jdk/test/tools/jlink/plugins/ExcludeJmodSectionPluginTest.java
author alanb
Fri, 07 Apr 2017 08:05:54 +0000
changeset 44545 83b611b88ac8
parent 41561 0c6942d13f2e
child 45393 de4e1efc8eec
permissions -rw-r--r--
8177530: Module system implementation refresh (4/2017) Reviewed-by: mchung, alanb Contributed-by: alan.bateman@oracle.com, mandy.chung@oracle.com

/**
 * Copyright (c) 2016, Oracle and/or its affiliates. All rights reserved.
 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
 *
 * This code is free software; you can redistribute it and/or modify it
 * under the terms of the GNU General Public License version 2 only, as
 * published by the Free Software Foundation.
 *
 * This code is distributed in the hope that it will be useful, but WITHOUT
 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
 * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
 * version 2 for more details (a copy is included in the LICENSE file that
 * accompanied this code).
 *
 * You should have received a copy of the GNU General Public License version
 * 2 along with this work; if not, write to the Free Software Foundation,
 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
 *
 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
 * or visit www.oracle.com if you need additional information or have any
 * questions.
 */

/*
 * @test
 * @summary Test --no-man-pages and --no-header-files
 * @library /lib/testlibrary
 * @modules jdk.compiler
 *          jdk.jlink
 * @build CompilerUtils
 * @run testng ExcludeJmodSectionPluginTest
 */

import java.io.BufferedWriter;
import java.io.File;
import java.io.IOException;
import java.io.PrintWriter;
import java.nio.file.FileVisitResult;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.nio.file.SimpleFileVisitor;
import java.nio.file.attribute.BasicFileAttributes;
import java.util.ArrayList;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
import java.util.spi.ToolProvider;
import java.util.stream.Collectors;
import java.util.stream.Stream;

import org.testng.annotations.BeforeTest;
import org.testng.annotations.DataProvider;
import org.testng.annotations.Test;

import static org.testng.Assert.*;

public class ExcludeJmodSectionPluginTest {
    static final ToolProvider JMOD_TOOL = ToolProvider.findFirst("jmod")
        .orElseThrow(() ->
            new RuntimeException("jmod tool not found")
        );

    static final ToolProvider JLINK_TOOL = ToolProvider.findFirst("jlink")
        .orElseThrow(() ->
            new RuntimeException("jlink tool not found")
        );

    static final Path MODULE_PATH = Paths.get(System.getProperty("java.home"), "jmods");
    static final Path SRC_DIR = Paths.get("src");
    static final Path MODS_DIR = Paths.get("mods");
    static final Path JMODS_DIR = Paths.get("jmods");
    static final Path MAN_DIR = Paths.get("man");
    static final Path INCLUDE_DIR = Paths.get("include");
    static final Path IMAGES_DIR = Paths.get("images");

    @BeforeTest
    private void setup() throws Exception {
        // build jmod files
        JmodFileBuilder m1 = new JmodFileBuilder("m1");
        m1.headerFile("m1a.h");
        m1.headerFile("m1b.h");
        m1.build();

        JmodFileBuilder m2 = new JmodFileBuilder("m2");
        m2.headerFile("m2.h");
        m2.manPage("tool2.1");
        m2.build();

        JmodFileBuilder m3 = new JmodFileBuilder("m3");
        m3.manPage("tool3.1");
        m3.build();
    }

    private String imageDir(String dir) {
        return IMAGES_DIR.resolve(dir).toString();
    }


    @DataProvider(name = "jlinkoptions")
    public Object[][] jlinkoptions() {
        // options and expected header files & man pages
        return new Object[][] {
            {   new String [] {
                    "test1",
                    "--exclude-files=/java.base/include/**,/java.base/man/**",
                },
                List.of("include/m1a.h",
                        "include/m1b.h",
                        "include/m2.h",
                        "man/tool2.1",
                        "man/tool3.1")
            },

            {   new String [] {
                    "test2",
                    "--no-man-pages",
                    "--no-header-files",
                },
                List.of()
            },

            {   new String[] {
                    "test3",
                    "--no-header-files",
                    "--exclude-files=/java.base/man/**"
                },
                List.of("man/tool2.1",
                        "man/tool3.1") },

            {   new String [] {
                    "test4",
                    "--no-man-pages",
                    "--exclude-files=/java.base/include/**,/m2/include/**",
                },
                List.of("include/m1a.h",
                        "include/m1b.h")
            },

            {   new String [] {
                    "test5",
                    "--no-header-files",
                    "--exclude-files=/java.base/man/**,/m2/man/**"
                },
                List.of("man/tool3.1")
            },
        };
    }

    @Test(dataProvider = "jlinkoptions")
    public void test(String[] opts, List<String> expectedFiles) throws Exception {
        if (Files.notExists(MODULE_PATH)) {
            // exploded image
            return;
        }

        String dir = opts[0];
        List<String> options = new ArrayList<>();
        for (int i = 1; i < opts.length; i++) {
            options.add(opts[i]);
        }

        String mpath = MODULE_PATH.toString() + File.pathSeparator +
                       JMODS_DIR.toString();
        Stream.of("--module-path", mpath,
                  "--add-modules", "m1,m2,m3",
                  "--output", imageDir(dir))
              .forEach(options::add);

        Path image = createImage(dir, options, expectedFiles);

        // check if any unexpected header file or man page
        Set<Path> extraFiles = Files.walk(image, Integer.MAX_VALUE)
            .filter(p -> Files.isRegularFile(p))
            .filter(p -> p.getParent().endsWith("include") ||
                         p.getParent().endsWith("man"))
            .filter(p -> {
                String fn = String.format("%s/%s",
                    p.getParent().getFileName().toString(),
                    p.getFileName().toString());
                return !expectedFiles.contains(fn);
            })
            .collect(Collectors.toSet());

        if (extraFiles.size() > 0) {
            System.out.println("Unexpected files: " + extraFiles.toString());
            assertTrue(extraFiles.isEmpty());
        }
    }

    /**
     * Test java.base's include header files
     */
    @Test
    public void testJavaBase() {
        if (Files.notExists(MODULE_PATH)) {
            // exploded image
            return;
        }
        List<String> options = List.of("--module-path",
                                       MODULE_PATH.toString(),
                                        "--add-modules", "java.base",
                                        "--output", imageDir("base"));
        createImage("base", options,
                    List.of("include/jni.h", "include/jvmti.h"));

    }

    private Path createImage(String outputDir, List<String> options,
                             List<String> expectedFiles) {
        System.out.println("jlink " + options.toString());
        int rc = JLINK_TOOL.run(System.out, System.out,
                                options.toArray(new String[0]));
        assertTrue(rc == 0);

        Path d = IMAGES_DIR.resolve(outputDir);
        for (String fn : expectedFiles) {
            Path path = d.resolve(fn);
            if (Files.notExists(path)) {
                throw new RuntimeException(path + " not found");
            }
        }
        return d;
    }

    private void deleteDirectory(Path dir) throws IOException {
        Files.walkFileTree(dir, new SimpleFileVisitor<Path>() {
            @Override
            public FileVisitResult visitFile(Path file, BasicFileAttributes attrs)
                throws IOException
            {
                Files.delete(file);
                return FileVisitResult.CONTINUE;
            }

            @Override
            public FileVisitResult postVisitDirectory(Path dir, IOException exc)
                throws IOException
            {
                Files.delete(dir);
                return FileVisitResult.CONTINUE;
            }
        });
    }

    /**
     * Builder to create JMOD file
     */
    class JmodFileBuilder {

        final String name;
        final Set<String> manPages = new HashSet<>();
        final Set<String> headerFiles = new HashSet<>();

        JmodFileBuilder(String name) throws IOException {
            this.name = name;

            Path msrc = SRC_DIR.resolve(name);
            if (Files.exists(msrc)) {
                deleteDirectory(msrc);
            }
        }

        JmodFileBuilder manPage(String filename) {
            manPages.add(filename);
            return this;
        }

        JmodFileBuilder headerFile(String filename) {
            headerFiles.add(filename);
            return this;
        }

        Path build() throws IOException {
            compileModule();
            // create man pages
            Path mdir = MAN_DIR.resolve(name);
            for (String filename : manPages) {
                Files.createDirectories(mdir);
                Files.createFile(mdir.resolve(filename));
            }
            // create header files
            mdir = INCLUDE_DIR.resolve(name);
            for (String filename : headerFiles) {
                Files.createDirectories(mdir);
                Files.createFile(mdir.resolve(filename));
            }
            return createJmodFile();
        }

        void compileModule() throws IOException  {
            Path msrc = SRC_DIR.resolve(name);
            Files.createDirectories(msrc);
            Path minfo = msrc.resolve("module-info.java");
            try (BufferedWriter bw = Files.newBufferedWriter(minfo);
                 PrintWriter writer = new PrintWriter(bw)) {
                writer.format("module %s { }%n", name);
            }

            assertTrue(CompilerUtils.compile(msrc, MODS_DIR,
                                             "--module-source-path",
                                             SRC_DIR.toString()));
        }

        Path createJmodFile() throws IOException {
            Path mclasses = MODS_DIR.resolve(name);
            Files.createDirectories(JMODS_DIR);
            Path outfile = JMODS_DIR.resolve(name + ".jmod");
            List<String> args = new ArrayList<>();
            args.add("create");
            // add classes
            args.add("--class-path");
            args.add(mclasses.toString());
            // man pages
            if (manPages.size() > 0) {
                args.add("--man-pages");
                args.add(MAN_DIR.resolve(name).toString());
            }
            // header files
            if (headerFiles.size() > 0) {
                args.add("--header-files");
                args.add(INCLUDE_DIR.resolve(name).toString());
            }
            args.add(outfile.toString());

            if (Files.exists(outfile))
                Files.delete(outfile);

            System.out.println("jmod " +
                args.stream().collect(Collectors.joining(" ")));

            int rc = JMOD_TOOL.run(System.out, System.out,
                                   args.toArray(new String[args.size()]));
            if (rc != 0) {
                throw new AssertionError("jmod failed: rc = " + rc);
            }
            return outfile;
        }
    }
}