1/**
2 * Copyright (c) 2016, 2017, Oracle and/or its affiliates. All rights reserved.
3 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
4 *
5 * This code is free software; you can redistribute it and/or modify it
6 * under the terms of the GNU General Public License version 2 only, as
7 * published by the Free Software Foundation.
8 *
9 * This code is distributed in the hope that it will be useful, but WITHOUT
10 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
11 * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
12 * version 2 for more details (a copy is included in the LICENSE file that
13 * accompanied this code).
14 *
15 * You should have received a copy of the GNU General Public License version
16 * 2 along with this work; if not, write to the Free Software Foundation,
17 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
18 *
19 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
20 * or visit www.oracle.com if you need additional information or have any
21 * questions.
22 */
23
24/*
25 * @test
26 * @summary Test --no-man-pages and --no-header-files
27 * @library /test/lib
28 * @modules jdk.compiler
29 *          jdk.jlink
30 * @build jdk.test.lib.compiler.CompilerUtils
31 * @run testng ExcludeJmodSectionPluginTest
32 */
33
34import java.io.BufferedWriter;
35import java.io.File;
36import java.io.IOException;
37import java.io.PrintWriter;
38import java.nio.file.FileVisitResult;
39import java.nio.file.Files;
40import java.nio.file.Path;
41import java.nio.file.Paths;
42import java.nio.file.SimpleFileVisitor;
43import java.nio.file.attribute.BasicFileAttributes;
44import java.util.ArrayList;
45import java.util.HashSet;
46import java.util.List;
47import java.util.Set;
48import java.util.spi.ToolProvider;
49import java.util.stream.Collectors;
50import java.util.stream.Stream;
51import jdk.test.lib.compiler.CompilerUtils;
52
53import org.testng.annotations.BeforeTest;
54import org.testng.annotations.DataProvider;
55import org.testng.annotations.Test;
56
57import static org.testng.Assert.*;
58
59public class ExcludeJmodSectionPluginTest {
60    static final ToolProvider JMOD_TOOL = ToolProvider.findFirst("jmod")
61        .orElseThrow(() ->
62            new RuntimeException("jmod tool not found")
63        );
64
65    static final ToolProvider JLINK_TOOL = ToolProvider.findFirst("jlink")
66        .orElseThrow(() ->
67            new RuntimeException("jlink tool not found")
68        );
69
70    static final Path MODULE_PATH = Paths.get(System.getProperty("java.home"), "jmods");
71    static final Path SRC_DIR = Paths.get("src");
72    static final Path MODS_DIR = Paths.get("mods");
73    static final Path JMODS_DIR = Paths.get("jmods");
74    static final Path MAN_DIR = Paths.get("man");
75    static final Path INCLUDE_DIR = Paths.get("include");
76    static final Path IMAGES_DIR = Paths.get("images");
77
78    @BeforeTest
79    private void setup() throws Exception {
80        // build jmod files
81        JmodFileBuilder m1 = new JmodFileBuilder("m1");
82        m1.headerFile("m1a.h");
83        m1.headerFile("m1b.h");
84        m1.build();
85
86        JmodFileBuilder m2 = new JmodFileBuilder("m2");
87        m2.headerFile("m2.h");
88        m2.manPage("tool2.1");
89        m2.build();
90
91        JmodFileBuilder m3 = new JmodFileBuilder("m3");
92        m3.manPage("tool3.1");
93        m3.build();
94    }
95
96    private String imageDir(String dir) {
97        return IMAGES_DIR.resolve(dir).toString();
98    }
99
100
101    @DataProvider(name = "jlinkoptions")
102    public Object[][] jlinkoptions() {
103        // options and expected header files & man pages
104        return new Object[][] {
105            {   new String [] {
106                    "test1",
107                    "--exclude-files=/java.base/include/**,/java.base/man/**",
108                },
109                List.of("include/m1a.h",
110                        "include/m1b.h",
111                        "include/m2.h",
112                        "man/tool2.1",
113                        "man/tool3.1")
114            },
115
116            {   new String [] {
117                    "test2",
118                    "--no-man-pages",
119                    "--no-header-files",
120                },
121                List.of()
122            },
123
124            {   new String[] {
125                    "test3",
126                    "--no-header-files",
127                    "--exclude-files=/java.base/man/**"
128                },
129                List.of("man/tool2.1",
130                        "man/tool3.1") },
131
132            {   new String [] {
133                    "test4",
134                    "--no-man-pages",
135                    "--exclude-files=/java.base/include/**,/m2/include/**",
136                },
137                List.of("include/m1a.h",
138                        "include/m1b.h")
139            },
140
141            {   new String [] {
142                    "test5",
143                    "--no-header-files",
144                    "--exclude-files=/java.base/man/**,/m2/man/**"
145                },
146                List.of("man/tool3.1")
147            },
148        };
149    }
150
151    @Test(dataProvider = "jlinkoptions")
152    public void test(String[] opts, List<String> expectedFiles) throws Exception {
153        if (Files.notExists(MODULE_PATH)) {
154            // exploded image
155            return;
156        }
157
158        String dir = opts[0];
159        List<String> options = new ArrayList<>();
160        for (int i = 1; i < opts.length; i++) {
161            options.add(opts[i]);
162        }
163
164        String mpath = MODULE_PATH.toString() + File.pathSeparator +
165                       JMODS_DIR.toString();
166        Stream.of("--module-path", mpath,
167                  "--add-modules", "m1,m2,m3",
168                  "--output", imageDir(dir))
169              .forEach(options::add);
170
171        Path image = createImage(dir, options, expectedFiles);
172
173        // check if any unexpected header file or man page
174        Set<Path> extraFiles = Files.walk(image, Integer.MAX_VALUE)
175            .filter(p -> Files.isRegularFile(p))
176            .filter(p -> p.getParent().endsWith("include") ||
177                         p.getParent().endsWith("man"))
178            .filter(p -> {
179                String fn = String.format("%s/%s",
180                    p.getParent().getFileName().toString(),
181                    p.getFileName().toString());
182                return !expectedFiles.contains(fn);
183            })
184            .collect(Collectors.toSet());
185
186        if (extraFiles.size() > 0) {
187            System.out.println("Unexpected files: " + extraFiles.toString());
188            assertTrue(extraFiles.isEmpty());
189        }
190    }
191
192    /**
193     * Test java.base's include header files
194     */
195    @Test
196    public void testJavaBase() {
197        if (Files.notExists(MODULE_PATH)) {
198            // exploded image
199            return;
200        }
201        List<String> options = List.of("--module-path",
202                                       MODULE_PATH.toString(),
203                                        "--add-modules", "java.base",
204                                        "--output", imageDir("base"));
205        createImage("base", options,
206                    List.of("include/jni.h", "include/jvmti.h"));
207
208    }
209
210    private Path createImage(String outputDir, List<String> options,
211                             List<String> expectedFiles) {
212        System.out.println("jlink " + options.toString());
213        int rc = JLINK_TOOL.run(System.out, System.out,
214                                options.toArray(new String[0]));
215        assertTrue(rc == 0);
216
217        Path d = IMAGES_DIR.resolve(outputDir);
218        for (String fn : expectedFiles) {
219            Path path = d.resolve(fn);
220            if (Files.notExists(path)) {
221                throw new RuntimeException(path + " not found");
222            }
223        }
224        return d;
225    }
226
227    private void deleteDirectory(Path dir) throws IOException {
228        Files.walkFileTree(dir, new SimpleFileVisitor<Path>() {
229            @Override
230            public FileVisitResult visitFile(Path file, BasicFileAttributes attrs)
231                throws IOException
232            {
233                Files.delete(file);
234                return FileVisitResult.CONTINUE;
235            }
236
237            @Override
238            public FileVisitResult postVisitDirectory(Path dir, IOException exc)
239                throws IOException
240            {
241                Files.delete(dir);
242                return FileVisitResult.CONTINUE;
243            }
244        });
245    }
246
247    /**
248     * Builder to create JMOD file
249     */
250    class JmodFileBuilder {
251
252        final String name;
253        final Set<String> manPages = new HashSet<>();
254        final Set<String> headerFiles = new HashSet<>();
255
256        JmodFileBuilder(String name) throws IOException {
257            this.name = name;
258
259            Path msrc = SRC_DIR.resolve(name);
260            if (Files.exists(msrc)) {
261                deleteDirectory(msrc);
262            }
263        }
264
265        JmodFileBuilder manPage(String filename) {
266            manPages.add(filename);
267            return this;
268        }
269
270        JmodFileBuilder headerFile(String filename) {
271            headerFiles.add(filename);
272            return this;
273        }
274
275        Path build() throws IOException {
276            compileModule();
277            // create man pages
278            Path mdir = MAN_DIR.resolve(name);
279            for (String filename : manPages) {
280                Files.createDirectories(mdir);
281                Files.createFile(mdir.resolve(filename));
282            }
283            // create header files
284            mdir = INCLUDE_DIR.resolve(name);
285            for (String filename : headerFiles) {
286                Files.createDirectories(mdir);
287                Files.createFile(mdir.resolve(filename));
288            }
289            return createJmodFile();
290        }
291
292        void compileModule() throws IOException  {
293            Path msrc = SRC_DIR.resolve(name);
294            Files.createDirectories(msrc);
295            Path minfo = msrc.resolve("module-info.java");
296            try (BufferedWriter bw = Files.newBufferedWriter(minfo);
297                 PrintWriter writer = new PrintWriter(bw)) {
298                writer.format("module %s { }%n", name);
299            }
300
301            assertTrue(CompilerUtils.compile(msrc, MODS_DIR,
302                                             "--module-source-path",
303                                             SRC_DIR.toString()));
304        }
305
306        Path createJmodFile() throws IOException {
307            Path mclasses = MODS_DIR.resolve(name);
308            Files.createDirectories(JMODS_DIR);
309            Path outfile = JMODS_DIR.resolve(name + ".jmod");
310            List<String> args = new ArrayList<>();
311            args.add("create");
312            // add classes
313            args.add("--class-path");
314            args.add(mclasses.toString());
315            // man pages
316            if (manPages.size() > 0) {
317                args.add("--man-pages");
318                args.add(MAN_DIR.resolve(name).toString());
319            }
320            // header files
321            if (headerFiles.size() > 0) {
322                args.add("--header-files");
323                args.add(INCLUDE_DIR.resolve(name).toString());
324            }
325            args.add(outfile.toString());
326
327            if (Files.exists(outfile))
328                Files.delete(outfile);
329
330            System.out.println("jmod " +
331                args.stream().collect(Collectors.joining(" ")));
332
333            int rc = JMOD_TOOL.run(System.out, System.out,
334                                   args.toArray(new String[args.size()]));
335            if (rc != 0) {
336                throw new AssertionError("jmod failed: rc = " + rc);
337            }
338            return outfile;
339        }
340    }
341}
342