ModuleInfoBuilder.java revision 3792:d516975e8110
1183234Ssimon/* 2280297Sjkim * Copyright (c) 2015, Oracle and/or its affiliates. All rights reserved. 3280297Sjkim * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. 4183234Ssimon * 5183234Ssimon * This code is free software; you can redistribute it and/or modify it 6183234Ssimon * under the terms of the GNU General Public License version 2 only, as 7183234Ssimon * published by the Free Software Foundation. Oracle designates this 8183234Ssimon * particular file as subject to the "Classpath" exception as provided 9183234Ssimon * by Oracle in the LICENSE file that accompanied this code. 10183234Ssimon * 11183234Ssimon * This code is distributed in the hope that it will be useful, but WITHOUT 12183234Ssimon * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 13183234Ssimon * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License 14280297Sjkim * version 2 for more details (a copy is included in the LICENSE file that 15183234Ssimon * accompanied this code). 16183234Ssimon * 17183234Ssimon * You should have received a copy of the GNU General Public License version 18183234Ssimon * 2 along with this work; if not, write to the Free Software Foundation, 19183234Ssimon * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. 20183234Ssimon * 21183234Ssimon * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA 22183234Ssimon * or visit www.oracle.com if you need additional information or have any 23183234Ssimon * questions. 24183234Ssimon */ 25183234Ssimonpackage com.sun.tools.jdeps; 26183234Ssimon 27183234Ssimonimport static com.sun.tools.jdeps.JdepsTask.*; 28183234Ssimonimport static com.sun.tools.jdeps.Analyzer.*; 29183234Ssimonimport static com.sun.tools.jdeps.JdepsFilter.DEFAULT_FILTER; 30183234Ssimon 31183234Ssimonimport java.io.IOException; 32183234Ssimonimport java.io.PrintWriter; 33183234Ssimonimport java.io.UncheckedIOException; 34183234Ssimonimport java.lang.module.ModuleDescriptor; 35183234Ssimonimport java.lang.module.ModuleDescriptor.Exports; 36183234Ssimonimport java.lang.module.ModuleDescriptor.Provides; 37183234Ssimonimport java.lang.module.ModuleDescriptor.Requires; 38183234Ssimonimport java.lang.module.ModuleFinder; 39183234Ssimonimport java.nio.file.Files; 40183234Ssimonimport java.nio.file.Path; 41183234Ssimonimport java.nio.file.Paths; 42183234Ssimonimport java.util.Collections; 43183234Ssimonimport java.util.Comparator; 44183234Ssimonimport java.util.HashMap; 45183234Ssimonimport java.util.List; 46183234Ssimonimport java.util.Map; 47183234Ssimonimport java.util.Optional; 48183234Ssimonimport java.util.Set; 49183234Ssimonimport java.util.function.Function; 50183234Ssimonimport java.util.stream.Stream; 51183234Ssimonimport static java.util.stream.Collectors.*; 52183234Ssimon 53183234Ssimon 54183234Ssimonpublic class ModuleInfoBuilder { 55183234Ssimon final JdepsConfiguration configuration; 56183234Ssimon final Path outputdir; 57290207Sjkim final boolean open; 58290207Sjkim 59183234Ssimon final DependencyFinder dependencyFinder; 60183234Ssimon final Analyzer analyzer; 61183234Ssimon 62280297Sjkim // an input JAR file (loaded as an automatic module for analysis) 63183234Ssimon // maps to an explicit module to generate module-info.java 64290207Sjkim final Map<Module, Module> automaticToExplicitModule; 65290207Sjkim public ModuleInfoBuilder(JdepsConfiguration configuration, 66280297Sjkim List<String> args, 67183234Ssimon Path outputdir, 68280297Sjkim boolean open) { 69280297Sjkim this.configuration = configuration; 70280297Sjkim this.outputdir = outputdir; 71183234Ssimon this.open = open; 72290207Sjkim 73280297Sjkim this.dependencyFinder = new DependencyFinder(configuration, DEFAULT_FILTER); 74290207Sjkim this.analyzer = new Analyzer(configuration, Type.CLASS, DEFAULT_FILTER); 75290207Sjkim 76290207Sjkim // add targets to modulepath if it has module-info.class 77290207Sjkim List<Path> paths = args.stream() 78183234Ssimon .map(fn -> Paths.get(fn)) 79238405Sjkim .collect(toList()); 80238405Sjkim 81238405Sjkim // automatic module to convert to explicit module 82238405Sjkim this.automaticToExplicitModule = ModuleFinder.of(paths.toArray(new Path[0])) 83238405Sjkim .findAll().stream() 84238405Sjkim .map(configuration::toModule) 85238405Sjkim .collect(toMap(Function.identity(), Function.identity())); 86238405Sjkim 87238405Sjkim Optional<Module> om = automaticToExplicitModule.keySet().stream() 88238405Sjkim .filter(m -> !m.descriptor().isAutomatic()) 89238405Sjkim .findAny(); 90238405Sjkim if (om.isPresent()) { 91280297Sjkim throw new UncheckedBadArgs(new BadArgs("err.genmoduleinfo.not.jarfile", 92238405Sjkim om.get().getPathName())); 93238405Sjkim } 94280297Sjkim if (automaticToExplicitModule.isEmpty()) { 95280297Sjkim throw new UncheckedBadArgs(new BadArgs("err.invalid.path", args)); 96280297Sjkim } 97280297Sjkim } 98238405Sjkim 99238405Sjkim public boolean run() throws IOException { 100238405Sjkim try { 101280297Sjkim // pass 1: find API dependencies 102280297Sjkim Map<Archive, Set<Archive>> requiresTransitive = computeRequiresTransitive(); 103183234Ssimon 104183234Ssimon // pass 2: analyze all class dependences 105280297Sjkim dependencyFinder.parse(automaticModules().stream()); 106280297Sjkim 107280297Sjkim analyzer.run(automaticModules(), dependencyFinder.locationToArchive()); 108183234Ssimon 109280297Sjkim boolean missingDeps = false; 110280297Sjkim for (Module m : automaticModules()) { 111280297Sjkim Set<Archive> apiDeps = requiresTransitive.containsKey(m) 112183234Ssimon ? requiresTransitive.get(m) 113280297Sjkim : Collections.emptySet(); 114280297Sjkim 115280297Sjkim Path file = outputdir.resolve(m.name()).resolve("module-info.java"); 116206046Ssimon 117296279Sjkim // computes requires and requires transitive 118296279Sjkim Module explicitModule = toExplicitModule(m, apiDeps); 119296279Sjkim if (explicitModule != null) { 120296279Sjkim automaticToExplicitModule.put(m, explicitModule); 121296279Sjkim 122296279Sjkim // generate module-info.java 123296279Sjkim System.out.format("writing to %s%n", file); 124296279Sjkim writeModuleInfo(file, explicitModule.descriptor()); 125296279Sjkim } else { 126296279Sjkim // find missing dependences 127296279Sjkim System.out.format("Missing dependence: %s not generated%n", file); 128296279Sjkim missingDeps = true; 129296279Sjkim } 130296279Sjkim } 131296279Sjkim 132296279Sjkim return !missingDeps; 133296279Sjkim } finally { 134296279Sjkim dependencyFinder.shutdown(); 135296279Sjkim } 136296279Sjkim } 137280297Sjkim 138280297Sjkim boolean notFound(Archive m) { 139280297Sjkim return m == NOT_FOUND || m == REMOVED_JDK_INTERNALS; 140183234Ssimon } 141280297Sjkim 142280297Sjkim private Module toExplicitModule(Module module, Set<Archive> requiresTransitive) 143183234Ssimon throws IOException 144183234Ssimon { 145183234Ssimon // done analysis 146183234Ssimon module.close(); 147183234Ssimon 148183234Ssimon if (analyzer.requires(module).anyMatch(this::notFound)) { 149183234Ssimon // missing dependencies 150183234Ssimon return null; 151183234Ssimon } 152183234Ssimon 153280297Sjkim Map<String, Boolean> requires = new HashMap<>(); 154183234Ssimon requiresTransitive.stream() 155280297Sjkim .map(Archive::getModule) 156280297Sjkim .forEach(m -> requires.put(m.name(), Boolean.TRUE)); 157280297Sjkim 158280297Sjkim analyzer.requires(module) 159183234Ssimon .map(Archive::getModule) 160280297Sjkim .forEach(d -> requires.putIfAbsent(d.name(), Boolean.FALSE)); 161280297Sjkim 162183234Ssimon return module.toStrictModule(requires); 163280297Sjkim } 164183234Ssimon 165183234Ssimon /** 166280297Sjkim * Returns the stream of resulting modules 167280297Sjkim */ 168280297Sjkim Stream<Module> modules() { 169280297Sjkim return automaticToExplicitModule.values().stream(); 170183234Ssimon } 171280297Sjkim 172183234Ssimon /** 173280297Sjkim * Returns the stream of resulting ModuleDescriptors 174183234Ssimon */ 175183234Ssimon public Stream<ModuleDescriptor> descriptors() { 176183234Ssimon return automaticToExplicitModule.entrySet().stream() 177280297Sjkim .map(Map.Entry::getValue) 178183234Ssimon .map(Module::descriptor); 179183234Ssimon } 180183234Ssimon 181280297Sjkim void visitMissingDeps(Analyzer.Visitor visitor) { 182280297Sjkim automaticModules().stream() 183280297Sjkim .filter(m -> analyzer.requires(m).anyMatch(this::notFound)) 184280297Sjkim .forEach(m -> { 185183234Ssimon analyzer.visitDependences(m, visitor, Analyzer.Type.VERBOSE); 186183234Ssimon }); 187280297Sjkim } 188183234Ssimon 189280297Sjkim void writeModuleInfo(Path file, ModuleDescriptor md) { 190183234Ssimon try { 191280297Sjkim Files.createDirectories(file.getParent()); 192280297Sjkim try (PrintWriter pw = new PrintWriter(Files.newOutputStream(file))) { 193280297Sjkim printModuleInfo(pw, md); 194183234Ssimon } 195280297Sjkim } catch (IOException e) { 196280297Sjkim throw new UncheckedIOException(e); 197280297Sjkim } 198183234Ssimon } 199183234Ssimon 200280297Sjkim private void printModuleInfo(PrintWriter writer, ModuleDescriptor md) { 201280297Sjkim writer.format("%smodule %s {%n", open ? "open " : "", md.name()); 202183234Ssimon 203183234Ssimon Map<String, Module> modules = configuration.getModules(); 204280297Sjkim // first print the JDK modules 205280297Sjkim md.requires().stream() 206280297Sjkim .filter(req -> !req.name().equals("java.base")) // implicit requires 207280297Sjkim .sorted(Comparator.comparing(Requires::name)) 208280297Sjkim .forEach(req -> writer.format(" requires %s;%n", req)); 209280297Sjkim 210280297Sjkim if (!open) { 211280297Sjkim md.exports().stream() 212280297Sjkim .peek(exp -> { 213280297Sjkim if (exp.targets().size() > 0) 214280297Sjkim throw new InternalError(md.name() + " qualified exports: " + exp); 215183234Ssimon }) 216183234Ssimon .sorted(Comparator.comparing(Exports::source)) 217280297Sjkim .forEach(exp -> writer.format(" exports %s;%n", exp.source())); 218183234Ssimon } 219280297Sjkim 220183234Ssimon md.provides().stream() 221280297Sjkim .sorted(Comparator.comparing(Provides::service)) 222280297Sjkim .map(p -> p.providers().stream() 223183234Ssimon .map(impl -> " " + impl.replace('$', '.')) 224183234Ssimon .collect(joining(",\n", 225280297Sjkim String.format(" provides %s with%n", 226183234Ssimon p.service().replace('$', '.')), 227280297Sjkim ";"))) 228183234Ssimon .forEach(writer::println); 229280297Sjkim 230183234Ssimon writer.println("}"); 231280297Sjkim } 232183234Ssimon 233280297Sjkim private Set<Module> automaticModules() { 234183234Ssimon return automaticToExplicitModule.keySet(); 235280297Sjkim } 236280297Sjkim 237280297Sjkim /** 238280297Sjkim * Compute 'requires transitive' dependences by analyzing API dependencies 239280297Sjkim */ 240183234Ssimon private Map<Archive, Set<Archive>> computeRequiresTransitive() 241183234Ssimon throws IOException 242183234Ssimon { 243280297Sjkim // parse the input modules 244280297Sjkim dependencyFinder.parseExportedAPIs(automaticModules().stream()); 245280297Sjkim 246280297Sjkim return dependencyFinder.dependences(); 247183234Ssimon } 248280297Sjkim} 249280297Sjkim