1 /**
   2  * Copyright (c) 2016, 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 /lib/testlibrary
  28  * @modules jdk.compiler
  29  *          jdk.jlink
  30  * @build 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 
  52 import org.testng.annotations.BeforeTest;
  53 import org.testng.annotations.DataProvider;
  54 import org.testng.annotations.Test;
  55 
  56 import static org.testng.Assert.*;
  57 
  58 public class ExcludeJmodSectionPluginTest {
  59     static final ToolProvider JMOD_TOOL = ToolProvider.findFirst("jmod")
  60         .orElseThrow(() ->
  61             new RuntimeException("jmod tool not found")
  62         );
  63 
  64     static final ToolProvider JLINK_TOOL = ToolProvider.findFirst("jlink")
  65         .orElseThrow(() ->
  66             new RuntimeException("jlink tool not found")
  67         );
  68 
  69     static final Path MODULE_PATH = Paths.get(System.getProperty("java.home"), "jmods");
  70     static final Path SRC_DIR = Paths.get("src");
  71     static final Path MODS_DIR = Paths.get("mods");
  72     static final Path JMODS_DIR = Paths.get("jmods");
  73     static final Path MAN_DIR = Paths.get("man");
  74     static final Path INCLUDE_DIR = Paths.get("include");
  75     static final Path IMAGES_DIR = Paths.get("images");
  76 
  77     @BeforeTest
  78     private void setup() throws Exception {
  79         // build jmod files
  80         JmodFileBuilder m1 = new JmodFileBuilder("m1");
  81         m1.headerFile("m1a.h");
  82         m1.headerFile("m1b.h");
  83         m1.build();
  84 
  85         JmodFileBuilder m2 = new JmodFileBuilder("m2");
  86         m2.headerFile("m2.h");
  87         m2.manPage("tool2.1");
  88         m2.build();
  89 
  90         JmodFileBuilder m3 = new JmodFileBuilder("m3");
  91         m3.manPage("tool3.1");
  92         m3.build();
  93     }
  94 
  95     private String imageDir(String dir) {
  96         return IMAGES_DIR.resolve(dir).toString();
  97     }
  98 
  99 
 100     @DataProvider(name = "jlinkoptions")
 101     public Object[][] jlinkoptions() {
 102         // options and expected header files & man pages
 103         return new Object[][] {
 104             {   new String [] {
 105                     "test1",
 106                     "--exclude-files=/java.base/include/**,/java.base/man/**",
 107                 },
 108                 List.of("include/m1a.h",
 109                         "include/m1b.h",
 110                         "include/m2.h",
 111                         "man/tool2.1",
 112                         "man/tool3.1")
 113             },
 114 
 115             {   new String [] {
 116                     "test2",
 117                     "--no-man-pages",
 118                     "--no-header-files",
 119                 },
 120                 List.of()
 121             },
 122 
 123             {   new String[] {
 124                     "test3",
 125                     "--no-header-files",
 126                     "--exclude-files=/java.base/man/**"
 127                 },
 128                 List.of("man/tool2.1",
 129                         "man/tool3.1") },
 130 
 131             {   new String [] {
 132                     "test4",
 133                     "--no-man-pages",
 134                     "--exclude-files=/java.base/include/**,/m2/include/**",
 135                 },
 136                 List.of("include/m1a.h",
 137                         "include/m1b.h")
 138             },
 139 
 140             {   new String [] {
 141                     "test5",
 142                     "--no-header-files",
 143                     "--exclude-files=/java.base/man/**,/m2/man/**"
 144                 },
 145                 List.of("man/tool3.1")
 146             },
 147         };
 148     }
 149 
 150     @Test(dataProvider = "jlinkoptions")
 151     public void test(String[] opts, List<String> expectedFiles) throws Exception {
 152         if (Files.notExists(MODULE_PATH)) {
 153             // exploded image
 154             return;
 155         }
 156 
 157         String dir = opts[0];
 158         List<String> options = new ArrayList<>();
 159         for (int i = 1; i < opts.length; i++) {
 160             options.add(opts[i]);
 161         }
 162 
 163         String mpath = MODULE_PATH.toString() + File.pathSeparator +
 164                        JMODS_DIR.toString();
 165         Stream.of("--module-path", mpath,
 166                   "--add-modules", "m1,m2,m3",
 167                   "--output", imageDir(dir))
 168               .forEach(options::add);
 169 
 170         Path image = createImage(dir, options, expectedFiles);
 171 
 172         // check if any unexpected header file or man page
 173         Set<Path> extraFiles = Files.walk(image, Integer.MAX_VALUE)
 174             .filter(p -> Files.isRegularFile(p))
 175             .filter(p -> p.getParent().endsWith("include") ||
 176                          p.getParent().endsWith("man"))
 177             .filter(p -> {
 178                 String fn = String.format("%s/%s",
 179                     p.getParent().getFileName().toString(),
 180                     p.getFileName().toString());
 181                 return !expectedFiles.contains(fn);
 182             })
 183             .collect(Collectors.toSet());
 184 
 185         if (extraFiles.size() > 0) {
 186             System.out.println("Unexpected files: " + extraFiles.toString());
 187             assertTrue(extraFiles.isEmpty());
 188         }
 189     }
 190 
 191     /**
 192      * Test java.base's include header files
 193      */
 194     @Test
 195     public void testJavaBase() {
 196         if (Files.notExists(MODULE_PATH)) {
 197             // exploded image
 198             return;
 199         }
 200         List<String> options = List.of("--module-path",
 201                                        MODULE_PATH.toString(),
 202                                         "--add-modules", "java.base",
 203                                         "--output", imageDir("base"));
 204         createImage("base", options,
 205                     List.of("include/jni.h", "include/jvmti.h"));
 206 
 207     }
 208 
 209     private Path createImage(String outputDir, List<String> options,
 210                              List<String> expectedFiles) {
 211         System.out.println("jlink " + options.toString());
 212         int rc = JLINK_TOOL.run(System.out, System.out,
 213                                 options.toArray(new String[0]));
 214         assertTrue(rc == 0);
 215 
 216         Path d = IMAGES_DIR.resolve(outputDir);
 217         for (String fn : expectedFiles) {
 218             Path path = d.resolve(fn);
 219             if (Files.notExists(path)) {
 220                 throw new RuntimeException(path + " not found");
 221             }
 222         }
 223         return d;
 224     }
 225 
 226     private void deleteDirectory(Path dir) throws IOException {
 227         Files.walkFileTree(dir, new SimpleFileVisitor<Path>() {
 228             @Override
 229             public FileVisitResult visitFile(Path file, BasicFileAttributes attrs)
 230                 throws IOException
 231             {
 232                 Files.delete(file);
 233                 return FileVisitResult.CONTINUE;
 234             }
 235 
 236             @Override
 237             public FileVisitResult postVisitDirectory(Path dir, IOException exc)
 238                 throws IOException
 239             {
 240                 Files.delete(dir);
 241                 return FileVisitResult.CONTINUE;
 242             }
 243         });
 244     }
 245 
 246     /**
 247      * Builder to create JMOD file
 248      */
 249     class JmodFileBuilder {
 250 
 251         final String name;
 252         final Set<String> manPages = new HashSet<>();
 253         final Set<String> headerFiles = new HashSet<>();
 254 
 255         JmodFileBuilder(String name) throws IOException {
 256             this.name = name;
 257 
 258             Path msrc = SRC_DIR.resolve(name);
 259             if (Files.exists(msrc)) {
 260                 deleteDirectory(msrc);
 261             }
 262         }
 263 
 264         JmodFileBuilder manPage(String filename) {
 265             manPages.add(filename);
 266             return this;
 267         }
 268 
 269         JmodFileBuilder headerFile(String filename) {
 270             headerFiles.add(filename);
 271             return this;
 272         }
 273 
 274         Path build() throws IOException {
 275             compileModule();
 276             // create man pages
 277             Path mdir = MAN_DIR.resolve(name);
 278             for (String filename : manPages) {
 279                 Files.createDirectories(mdir);
 280                 Files.createFile(mdir.resolve(filename));
 281             }
 282             // create header files
 283             mdir = INCLUDE_DIR.resolve(name);
 284             for (String filename : headerFiles) {
 285                 Files.createDirectories(mdir);
 286                 Files.createFile(mdir.resolve(filename));
 287             }
 288             return createJmodFile();
 289         }
 290 
 291         void compileModule() throws IOException  {
 292             Path msrc = SRC_DIR.resolve(name);
 293             Files.createDirectories(msrc);
 294             Path minfo = msrc.resolve("module-info.java");
 295             try (BufferedWriter bw = Files.newBufferedWriter(minfo);
 296                  PrintWriter writer = new PrintWriter(bw)) {
 297                 writer.format("module %s { }%n", name);
 298             }
 299 
 300             assertTrue(CompilerUtils.compile(msrc, MODS_DIR,
 301                                              "--module-source-path",
 302                                              SRC_DIR.toString()));
 303         }
 304 
 305         Path createJmodFile() throws IOException {
 306             Path mclasses = MODS_DIR.resolve(name);
 307             Files.createDirectories(JMODS_DIR);
 308             Path outfile = JMODS_DIR.resolve(name + ".jmod");
 309             List<String> args = new ArrayList<>();
 310             args.add("create");
 311             // add classes
 312             args.add("--class-path");
 313             args.add(mclasses.toString());
 314             // man pages
 315             if (manPages.size() > 0) {
 316                 args.add("--man-pages");
 317                 args.add(MAN_DIR.resolve(name).toString());
 318             }
 319             // header files
 320             if (headerFiles.size() > 0) {
 321                 args.add("--header-files");
 322                 args.add(INCLUDE_DIR.resolve(name).toString());
 323             }
 324             args.add(outfile.toString());
 325 
 326             if (Files.exists(outfile))
 327                 Files.delete(outfile);
 328 
 329             System.out.println("jmod " +
 330                 args.stream().collect(Collectors.joining(" ")));
 331 
 332             int rc = JMOD_TOOL.run(System.out, System.out,
 333                                    args.toArray(new String[args.size()]));
 334             if (rc != 0) {
 335                 throw new AssertionError("jmod failed: rc = " + rc);
 336             }
 337             return outfile;
 338         }
 339     }
 340 }