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 
34 import java.io.BufferedWriter;
35 import java.io.File;
36 import java.io.IOException;
37 import java.io.PrintWriter;
38 import java.nio.file.FileVisitResult;
39 import java.nio.file.Files;
40 import java.nio.file.Path;
41 import java.nio.file.Paths;
42 import java.nio.file.SimpleFileVisitor;
43 import java.nio.file.attribute.BasicFileAttributes;
44 import java.util.ArrayList;
45 import java.util.HashSet;
46 import java.util.List;
47 import java.util.Set;
48 import java.util.spi.ToolProvider;
49 import java.util.stream.Collectors;
50 import java.util.stream.Stream;
51 import jdk.test.lib.compiler.CompilerUtils;
52 
53 import org.testng.annotations.BeforeTest;
54 import org.testng.annotations.DataProvider;
55 import org.testng.annotations.Test;
56 
57 import static org.testng.Assert.*;
58 
59 public 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
setup()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 
imageDir(String dir)96     private String imageDir(String dir) {
97         return IMAGES_DIR.resolve(dir).toString();
98     }
99 
100 
101     @DataProvider(name = "jlinkoptions")
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")
test(String[] opts, List<String> expectedFiles)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
testJavaBase()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 
createImage(String outputDir, List<String> options, List<String> expectedFiles)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 
deleteDirectory(Path dir)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 
JmodFileBuilder(String name)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 
manPage(String filename)265         JmodFileBuilder manPage(String filename) {
266             manPages.add(filename);
267             return this;
268         }
269 
headerFile(String filename)270         JmodFileBuilder headerFile(String filename) {
271             headerFiles.add(filename);
272             return this;
273         }
274 
build()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 
compileModule()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 
createJmodFile()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