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-jmod-section=headers:modules=java.base",
 107                     "--exclude-jmod-section=man:modules=java.base"
 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-jmod-section=man:modules=java.base"
 128                 },
 129                 List.of("man/tool2.1",
 130                         "man/tool3.1") },
 131 
 132             {   new String [] {
 133                     "test4",
 134                     "--no-man-pages",
 135                     "--exclude-jmod-section=headers:modules=java.base,m2",
 136                 },
 137                 List.of("include/m1a.h",
 138                         "include/m1b.h")
 139             },
 140 
 141             {   new String [] {
 142                     "test5",
 143                     "--exclude-jmod-section=man:modules=java.base,m1,m2",
 144                     "--no-header-files",
 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         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 
 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 }