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 Tests jdeps --gen-module-info option
  27  * @library ../lib
  28  * @build CompilerUtils JdepsUtil
  29  * @modules jdk.jdeps/com.sun.tools.jdeps
  30  * @run testng GenModuleInfo
  31  */
  32 
  33 import java.io.*;
  34 import java.lang.module.ModuleDescriptor;
  35 
  36 import java.nio.file.Files;
  37 import java.nio.file.Path;
  38 import java.nio.file.Paths;
  39 
  40 import java.util.Arrays;
  41 import java.util.Set;
  42 import java.util.stream.Collectors;
  43 import java.util.stream.Stream;
  44 
  45 import org.testng.annotations.BeforeTest;
  46 import org.testng.annotations.Test;
  47 
  48 import static org.testng.Assert.assertEquals;
  49 import static org.testng.Assert.assertTrue;
  50 
  51 public class GenModuleInfo {
  52     private static final String MODULE_INFO = "module-info.class";
  53     private static final String TEST_SRC = System.getProperty("test.src");
  54     private static final String TEST_CLASSES = System.getProperty("test.classes");
  55 
  56     private static final Path SRC_DIR = Paths.get(TEST_SRC, "src");
  57     private static final Path MODS_DIR = Paths.get("mods");
  58     private static final Path LIBS_DIR = Paths.get("libs");
  59     private static final Path DEST_DIR = Paths.get("moduleinfosrc");
  60     private static final Path NEW_MODS_DIR = Paths.get("new_mods");
  61 
  62     // the names of the modules in this test
  63     private static final String UNSUPPORTED = "unsupported";
  64     private static String[] modules = new String[] {"m1", "m2", "m3", UNSUPPORTED};
  65     /**
  66      * Compiles all modules used by the test
  67      */
  68     @BeforeTest
  69     public void compileAll() throws Exception {
  70         CompilerUtils.cleanDir(MODS_DIR);
  71         CompilerUtils.cleanDir(LIBS_DIR);
  72         CompilerUtils.cleanDir(DEST_DIR);
  73         CompilerUtils.cleanDir(NEW_MODS_DIR);
  74 
  75         assertTrue(CompilerUtils.compileModule(SRC_DIR, MODS_DIR, UNSUPPORTED,
  76                                                "--add-exports", "java.base/jdk.internal.perf=" + UNSUPPORTED));
  77         Arrays.asList("m1", "m2", "m3")
  78               .forEach(mn -> assertTrue(CompilerUtils.compileModule(SRC_DIR, MODS_DIR, mn)));
  79 
  80         Files.createDirectory(LIBS_DIR);
  81         Files.createDirectory(DEST_DIR);
  82 
  83         for (String mn : modules) {
  84             Path root = MODS_DIR.resolve(mn);
  85             try (Stream<Path> stream = Files.walk(root, Integer.MAX_VALUE)) {
  86                 Stream<Path> entries = stream.filter(f -> {
  87                     String fn = f.getFileName().toString();
  88                     return fn.endsWith(".class") && !fn.equals("module-info.class");
  89                 });
  90                 JdepsUtil.createJar(LIBS_DIR.resolve(mn + ".jar"), root, entries);
  91             }
  92         }
  93     }
  94 
  95     @Test
  96     public void jdeps() throws IOException {
  97         Stream<String> files = Arrays.stream(modules)
  98                 .map(mn -> LIBS_DIR.resolve(mn + ".jar"))
  99                 .map(Path::toString);
 100         JdepsUtil.jdeps(Stream.concat(Stream.of("-cp"), files).toArray(String[]::new));
 101     }
 102 
 103     @Test
 104     public void test() throws IOException {
 105         Stream<String> files = Arrays.stream(modules)
 106                 .map(mn -> LIBS_DIR.resolve(mn + ".jar"))
 107                 .map(Path::toString);
 108 
 109         JdepsUtil.jdeps(Stream.concat(Stream.of("--gen-module-info", DEST_DIR.toString()),
 110                                       files).toArray(String[]::new));
 111 
 112         // check file exists
 113         Arrays.stream(modules)
 114                 .map(mn -> DEST_DIR.resolve(mn).resolve("module-info.java"))
 115                 .forEach(f -> assertTrue(Files.exists(f)));
 116 
 117         // copy classes except the original module-info.class
 118         try (Stream<Path> stream = Files.walk(MODS_DIR, Integer.MAX_VALUE)) {
 119             stream.filter(path -> !path.getFileName().toString().equals(MODULE_INFO) &&
 120                                     path.getFileName().toString().endsWith(".class"))
 121                   .map(path -> MODS_DIR.relativize(path))
 122                   .forEach(path -> {
 123                       try {
 124                           Path newFile = NEW_MODS_DIR.resolve(path);
 125                           Files.createDirectories(newFile.getParent());
 126                           Files.copy(MODS_DIR.resolve(path), newFile);
 127                       } catch (IOException e) {
 128                           throw new UncheckedIOException(e);
 129                       }
 130                   });
 131         }
 132 
 133         // compile new module-info.java
 134         assertTrue(CompilerUtils.compileModule(DEST_DIR, NEW_MODS_DIR, UNSUPPORTED,
 135                         "-p", NEW_MODS_DIR.toString(), "-verbose",
 136                         "--add-exports", "java.base/jdk.internal.perf=" + UNSUPPORTED));
 137         Arrays.asList("m1", "m2", "m3")
 138               .forEach(mn -> assertTrue(CompilerUtils.compileModule(DEST_DIR, NEW_MODS_DIR,
 139                                         mn, "-p", NEW_MODS_DIR.toString())));
 140 
 141         for (String mn : modules) {
 142             Path p1 = NEW_MODS_DIR.resolve(mn).resolve(MODULE_INFO);
 143             Path p2 = MODS_DIR.resolve(mn).resolve(MODULE_INFO);
 144 
 145             try (InputStream in1 = Files.newInputStream(p1);
 146                  InputStream in2 = Files.newInputStream(p2)) {
 147                 verify(ModuleDescriptor.read(in1),
 148                        ModuleDescriptor.read(in2, () -> packages(MODS_DIR.resolve(mn))));
 149             }
 150         }
 151     }
 152 
 153     private void verify(ModuleDescriptor md1, ModuleDescriptor md2) {
 154         System.out.println("verifying: " + md1.name());
 155         assertEquals(md1.name(), md2.name());
 156         assertEquals(md1.requires(), md2.requires());
 157         // all packages are exported
 158         assertEquals(md1.exports().stream()
 159                                   .map(ModuleDescriptor.Exports::source)
 160                                   .collect(Collectors.toSet()), md2.packages());
 161     }
 162 
 163     private Set<String> packages(Path dir) {
 164         try (Stream<Path> stream = Files.find(dir, Integer.MAX_VALUE,
 165                              ((path, attrs) -> attrs.isRegularFile() &&
 166                                                path.toString().endsWith(".class")))) {
 167             return stream.map(path -> toPackageName(dir.relativize(path)))
 168                          .filter(pkg -> pkg.length() > 0)   // module-info
 169                          .distinct()
 170                          .collect(Collectors.toSet());
 171         } catch (IOException x) {
 172             throw new UncheckedIOException(x);
 173         }
 174     }
 175 
 176     private String toPackageName(Path path) {
 177         String name = path.toString();
 178         assert name.endsWith(".class");
 179         int index = name.lastIndexOf(File.separatorChar);
 180         if (index != -1) {
 181             return name.substring(0, index).replace(File.separatorChar, '.');
 182         } else {
 183             return "";
 184         }
 185     }
 186 
 187 }