GenModuleInfo.java revision 3792:d516975e8110
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 --generate-module-info option 27 * @library ../lib 28 * @build CompilerUtils JdepsUtil JdepsRunner 29 * @modules jdk.jdeps/com.sun.tools.jdeps 30 * @run testng GenModuleInfo 31 */ 32 33import java.io.File; 34import java.io.IOException; 35import java.io.InputStream; 36import java.io.UncheckedIOException; 37import java.lang.module.ModuleDescriptor; 38 39import java.nio.file.Files; 40import java.nio.file.Path; 41import java.nio.file.Paths; 42 43import java.util.List; 44import java.util.Set; 45import java.util.stream.Collectors; 46import java.util.stream.Stream; 47 48import org.testng.annotations.BeforeTest; 49import org.testng.annotations.Test; 50 51import static org.testng.Assert.assertEquals; 52import static org.testng.Assert.assertTrue; 53 54public class GenModuleInfo { 55 private static final String MODULE_INFO = "module-info.class"; 56 private static final String TEST_SRC = System.getProperty("test.src"); 57 58 private static final Path SRC_DIR = Paths.get(TEST_SRC, "src"); 59 private static final Path MODS_DIR = Paths.get("mods"); 60 private static final Path LIBS_DIR = Paths.get("libs"); 61 private static final Path DEST_DIR = Paths.get("moduleinfosrc"); 62 private static final Path NEW_MODS_DIR = Paths.get("new_mods"); 63 64 // the names of the modules in this test 65 public static final String UNSUPPORTED = "unsupported"; 66 public static final Set<String> MODULES = Set.of( 67 "mI", "mII", "mIII", "provider", UNSUPPORTED 68 ); 69 70 /** 71 * Compile modules 72 */ 73 public static void compileModules(Path dest) { 74 assertTrue(CompilerUtils.compileModule(SRC_DIR, dest, UNSUPPORTED, 75 "--add-exports", "java.base/jdk.internal.perf=" + UNSUPPORTED)); 76 MODULES.stream() 77 .filter(mn -> !mn.equals(UNSUPPORTED)) 78 .forEach(mn -> assertTrue(CompilerUtils.compileModule(SRC_DIR, dest, mn))); 79 } 80 81 /** 82 * Create JAR files with no module-info.class 83 */ 84 public static List<Path> createJARFiles(Path mods, Path libs) throws IOException { 85 Files.createDirectory(libs); 86 87 for (String mn : MODULES) { 88 Path root = mods.resolve(mn); 89 Path msrc = SRC_DIR.resolve(mn); 90 Path metaInf = msrc.resolve("META-INF"); 91 if (Files.exists(metaInf)) { 92 try (Stream<Path> resources = Files.find(metaInf, Integer.MAX_VALUE, 93 (p, attr) -> { return attr.isRegularFile();})) { 94 resources.forEach(file -> { 95 try { 96 Path path = msrc.relativize(file); 97 Files.createDirectories(root.resolve(path).getParent()); 98 Files.copy(file, root.resolve(path)); 99 } catch (IOException e) { 100 throw new UncheckedIOException(e); 101 } 102 }); 103 } 104 } 105 // copy all entries except module-info.class 106 try (Stream<Path> stream = Files.find(root, Integer.MAX_VALUE, 107 (p, attr) -> { return attr.isRegularFile();})) { 108 Stream<Path> entries = stream.filter(f -> { 109 String fn = f.getFileName().toString(); 110 if (fn.endsWith(".class")) { 111 return !fn.equals("module-info.class"); 112 } else { 113 return true; 114 } 115 }); 116 117 JdepsUtil.createJar(libs.resolve(mn + ".jar"), root, entries); 118 } 119 } 120 121 return MODULES.stream() 122 .map(mn -> LIBS_DIR.resolve(mn + ".jar")) 123 .collect(Collectors.toList()); 124 } 125 126 /** 127 * compile the generated module-info.java 128 */ 129 public static void compileNewGenModuleInfo(Path source, Path dest) { 130 131 assertTrue(CompilerUtils.compileModule(source, dest, UNSUPPORTED, 132 "-p", dest.toString(), 133 "--add-exports", "java.base/jdk.internal.perf=" + UNSUPPORTED)); 134 135 MODULES.stream() 136 .filter(mn -> !mn.equals(UNSUPPORTED)) 137 .forEach(mn -> assertTrue( 138 CompilerUtils.compileModule(source, dest, 139 mn, "-p", dest.toString())) 140 ); 141 142 } 143 144 /** 145 * Compiles all modules used by the test 146 */ 147 @BeforeTest 148 public void compileAll() throws Exception { 149 CompilerUtils.cleanDir(MODS_DIR); 150 CompilerUtils.cleanDir(LIBS_DIR); 151 CompilerUtils.cleanDir(DEST_DIR); 152 CompilerUtils.cleanDir(NEW_MODS_DIR); 153 154 compileModules(MODS_DIR); 155 156 createJARFiles(MODS_DIR, LIBS_DIR); 157 } 158 159 @Test 160 public void automaticModules() throws IOException { 161 Stream<String> files = MODULES.stream() 162 .map(mn -> LIBS_DIR.resolve(mn + ".jar")) 163 .map(Path::toString); 164 JdepsRunner.run(Stream.concat(Stream.of("-cp"), files).toArray(String[]::new)); 165 } 166 167 @Test 168 public void test() throws IOException { 169 Files.createDirectory(DEST_DIR); 170 171 Stream<String> files = MODULES.stream() 172 .map(mn -> LIBS_DIR.resolve(mn + ".jar")) 173 .map(Path::toString); 174 175 Stream<String> options = Stream.concat( 176 Stream.of("--generate-module-info", DEST_DIR.toString()), files); 177 JdepsRunner.run(options.toArray(String[]::new)); 178 179 // check file exists 180 MODULES.stream() 181 .map(mn -> DEST_DIR.resolve(mn).resolve("module-info.java")) 182 .forEach(f -> assertTrue(Files.exists(f))); 183 184 // copy classes to a temporary directory 185 // and then compile new module-info.java 186 copyClasses(MODS_DIR, NEW_MODS_DIR); 187 compileNewGenModuleInfo(DEST_DIR, NEW_MODS_DIR); 188 189 for (String mn : MODULES) { 190 Path p1 = NEW_MODS_DIR.resolve(mn).resolve(MODULE_INFO); 191 Path p2 = MODS_DIR.resolve(mn).resolve(MODULE_INFO); 192 193 try (InputStream in1 = Files.newInputStream(p1); 194 InputStream in2 = Files.newInputStream(p2)) { 195 verify(ModuleDescriptor.read(in1), 196 ModuleDescriptor.read(in2, () -> packages(MODS_DIR.resolve(mn)))); 197 } 198 } 199 } 200 201 /** 202 * Copy classes except the module-info.class to the destination directory 203 */ 204 public static void copyClasses(Path from, Path dest) throws IOException { 205 try (Stream<Path> stream = Files.walk(from, Integer.MAX_VALUE)) { 206 stream.filter(path -> !path.getFileName().toString().equals(MODULE_INFO) && 207 path.getFileName().toString().endsWith(".class")) 208 .map(path -> from.relativize(path)) 209 .forEach(path -> { 210 try { 211 Path newFile = dest.resolve(path); 212 Files.createDirectories(newFile.getParent()); 213 Files.copy(from.resolve(path), newFile); 214 } catch (IOException e) { 215 throw new UncheckedIOException(e); 216 } 217 }); 218 } 219 } 220 221 /** 222 * Verify the generated module-info.java is equivalent to module-info.class 223 * compiled from source. 224 */ 225 private void verify(ModuleDescriptor md1, ModuleDescriptor md2) { 226 System.out.println("verifying: " + md1.name()); 227 assertEquals(md1.name(), md2.name()); 228 assertEquals(md1.requires(), md2.requires()); 229 // all packages are exported 230 assertEquals(md1.exports().stream() 231 .map(ModuleDescriptor.Exports::source) 232 .collect(Collectors.toSet()), md2.packages()); 233 if (!md1.opens().isEmpty()) { 234 throw new RuntimeException("unexpected opens: " + 235 md1.opens().stream() 236 .map(o -> o.toString()) 237 .collect(Collectors.joining(","))); 238 } 239 240 assertEquals(md1.provides(), md2.provides()); 241 } 242 243 private Set<String> packages(Path dir) { 244 try (Stream<Path> stream = Files.find(dir, Integer.MAX_VALUE, 245 ((path, attrs) -> attrs.isRegularFile() && 246 path.toString().endsWith(".class")))) { 247 return stream.map(path -> toPackageName(dir.relativize(path))) 248 .filter(pkg -> pkg.length() > 0) // module-info 249 .distinct() 250 .collect(Collectors.toSet()); 251 } catch (IOException x) { 252 throw new UncheckedIOException(x); 253 } 254 } 255 256 private String toPackageName(Path path) { 257 String name = path.toString(); 258 assert name.endsWith(".class"); 259 int index = name.lastIndexOf(File.separatorChar); 260 if (index != -1) { 261 return name.substring(0, index).replace(File.separatorChar, '.'); 262 } else { 263 return ""; 264 } 265 } 266 267} 268