--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/langtools/src/jdk.jdeps/share/classes/com/sun/tools/jdeps/ModuleInfoBuilder.java Thu Mar 17 19:04:28 2016 +0000
@@ -0,0 +1,207 @@
+/*
+ * Copyright (c) 2015, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation. Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the LICENSE file that accompanied this code.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+package com.sun.tools.jdeps;
+
+import static com.sun.tools.jdeps.Analyzer.Type.CLASS;
+import static com.sun.tools.jdeps.Analyzer.NOT_FOUND;
+import static com.sun.tools.jdeps.Module.trace;
+
+import java.io.IOException;
+import java.io.PrintWriter;
+import java.nio.file.Files;
+import java.nio.file.Path;
+import java.util.HashMap;
+import java.util.Map;
+import java.util.Optional;
+import java.util.Set;
+import java.util.function.Function;
+import java.util.stream.Collectors;
+import java.util.stream.Stream;
+
+public class ModuleInfoBuilder {
+ final ModulePaths modulePaths;
+ final DependencyFinder dependencyFinder;
+ final JdepsFilter filter;
+ final Analyzer analyzer;
+ final Map<Module, Module> strictModules = new HashMap<>();
+ ModuleInfoBuilder(ModulePaths modulePaths, DependencyFinder finder) {
+ this.modulePaths = modulePaths;
+ this.dependencyFinder = finder;
+ this.filter = new JdepsFilter.Builder().filter(true, true).build();
+ this.analyzer = new Analyzer(CLASS, filter);
+ }
+
+ private Stream<Module> automaticModules() {
+ return modulePaths.getModules().values()
+ .stream()
+ .filter(Module::isAutomatic);
+ }
+
+ /**
+ * Compute 'requires public' dependences by analyzing API dependencies
+ */
+ Map<Module, Set<Module>> computeRequiresPublic() throws IOException {
+ dependencyFinder.findDependencies(filter, true /* api only */, 1);
+ Analyzer pass1 = new Analyzer(Analyzer.Type.CLASS, filter);
+
+ pass1.run(dependencyFinder.archives());
+
+ return automaticModules().collect(Collectors.toMap(Function.identity(),
+ source -> pass1.requires(source)
+ .map(Archive::getModule)
+ .collect(Collectors.toSet())));
+ }
+
+ boolean run(Analyzer.Type verbose, boolean quiet) throws IOException {
+ // add all automatic modules to the root set
+ automaticModules().forEach(dependencyFinder::addRoot);
+
+ // pass 1: find API dependencies
+ Map<Module, Set<Module>> requiresPublic = computeRequiresPublic();
+
+ // pass 2: analyze all class dependences
+ dependencyFinder.findDependencies(filter, false /* all classes */, 1);
+ analyzer.run(dependencyFinder.archives());
+
+ // computes requires and requires public
+ automaticModules().forEach(m -> {
+ Map<String, Boolean> requires;
+ if (requiresPublic.containsKey(m)) {
+ requires = requiresPublic.get(m)
+ .stream()
+ .collect(Collectors.toMap(Archive::getName, (v) -> Boolean.TRUE));
+ } else {
+ requires = new HashMap<>();
+ }
+ analyzer.requires(m)
+ .forEach(d -> requires.putIfAbsent(d.getName(), Boolean.FALSE));
+
+ trace("strict module %s requires %s%n", m.name(), requires);
+ strictModules.put(m, m.toStrictModule(requires));
+ });
+
+ // find any missing dependences
+ Optional<Module> missingDeps = automaticModules()
+ .filter(this::missingDep)
+ .findAny();
+ if (missingDeps.isPresent()) {
+ automaticModules()
+ .filter(this::missingDep)
+ .forEach(m -> {
+ System.err.format("Missing dependencies from %s%n", m.name());
+ analyzer.visitDependences(m,
+ new Analyzer.Visitor() {
+ @Override
+ public void visitDependence(String origin, Archive originArchive,
+ String target, Archive targetArchive) {
+ if (targetArchive == NOT_FOUND)
+ System.err.format(" %-50s -> %-50s %s%n",
+ origin, target, targetArchive.getName());
+ }
+ }, verbose);
+ System.err.println();
+ });
+
+ System.err.println("ERROR: missing dependencies (check \"requires NOT_FOUND;\")");
+ }
+ return missingDeps.isPresent() ? false : true;
+ }
+
+ private boolean missingDep(Archive m) {
+ return analyzer.requires(m).filter(a -> a.equals(NOT_FOUND))
+ .findAny().isPresent();
+ }
+
+ void build(Path dir) throws IOException {
+ ModuleInfoWriter writer = new ModuleInfoWriter(dir);
+ writer.generateOutput(strictModules.values(), analyzer);
+ }
+
+ private class ModuleInfoWriter {
+ private final Path outputDir;
+ ModuleInfoWriter(Path dir) {
+ this.outputDir = dir;
+ }
+
+ void generateOutput(Iterable<Module> modules, Analyzer analyzer) throws IOException {
+ // generate module-info.java file for each archive
+ for (Module m : modules) {
+ if (m.packages().contains("")) {
+ System.err.format("ERROR: %s contains unnamed package. " +
+ "module-info.java not generated%n", m.getPathName());
+ continue;
+ }
+
+ String mn = m.getName();
+ Path srcFile = outputDir.resolve(mn).resolve("module-info.java");
+ Files.createDirectories(srcFile.getParent());
+ System.out.println("writing to " + srcFile);
+ try (PrintWriter pw = new PrintWriter(Files.newOutputStream(srcFile))) {
+ printModuleInfo(pw, m);
+ }
+ }
+ }
+
+ private void printModuleInfo(PrintWriter writer, Module m) {
+ writer.format("module %s {%n", m.name());
+
+ Map<String, Module> modules = modulePaths.getModules();
+ Map<String, Boolean> requires = m.requires();
+ // first print the JDK modules
+ requires.keySet().stream()
+ .filter(mn -> !mn.equals("java.base")) // implicit requires
+ .filter(mn -> modules.containsKey(mn) && modules.get(mn).isJDK())
+ .sorted()
+ .forEach(mn -> {
+ String modifier = requires.get(mn) ? "public " : "";
+ writer.format(" requires %s%s;%n", modifier, mn);
+ });
+
+ // print requires non-JDK modules
+ requires.keySet().stream()
+ .filter(mn -> !modules.containsKey(mn) || !modules.get(mn).isJDK())
+ .sorted()
+ .forEach(mn -> {
+ String modifier = requires.get(mn) ? "public " : "";
+ writer.format(" requires %s%s;%n", modifier, mn);
+ });
+
+ m.packages().stream()
+ .sorted()
+ .forEach(pn -> writer.format(" exports %s;%n", pn));
+
+ m.provides().entrySet().stream()
+ .sorted(Map.Entry.comparingByKey())
+ .forEach(e -> {
+ String service = e.getKey();
+ e.getValue().stream()
+ .sorted()
+ .forEach(impl -> writer.format(" provides %s with %s;%n", service, impl));
+ });
+
+ writer.println("}");
+ }
+ }
+}