1 /*
   2  * Copyright (c) 2017, 2018, 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.  Oracle designates this
   8  * particular file as subject to the "Classpath" exception as provided
   9  * by Oracle in the LICENSE file that accompanied this code.
  10  *
  11  * This code is distributed in the hope that it will be useful, but WITHOUT
  12  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
  13  * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
  14  * version 2 for more details (a copy is included in the LICENSE file that
  15  * accompanied this code).
  16  *
  17  * You should have received a copy of the GNU General Public License version
  18  * 2 along with this work; if not, write to the Free Software Foundation,
  19  * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
  20  *
  21  * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
  22  * or visit www.oracle.com if you need additional information or have any
  23  * questions.
  24  */
  25 
  26 package build.tools.symbolgenerator;
  27 
  28 import java.io.IOException;
  29 import java.io.PrintWriter;
  30 import java.io.Writer;
  31 import java.nio.charset.Charset;
  32 import java.nio.file.Files;
  33 import java.nio.file.Path;
  34 import java.nio.file.Paths;
  35 import java.util.ArrayDeque;
  36 import java.util.Arrays;
  37 import java.util.Deque;
  38 import java.util.HashSet;
  39 import java.util.LinkedList;
  40 import java.util.List;
  41 import java.util.Locale;
  42 import java.util.Set;
  43 import java.util.stream.Collectors;
  44 
  45 import javax.lang.model.element.ModuleElement.RequiresDirective;
  46 import javax.lang.model.util.Elements;
  47 import javax.tools.JavaCompiler;
  48 
  49 import com.sun.tools.javac.api.JavacTaskImpl;
  50 import com.sun.tools.javac.api.JavacTool;
  51 import com.sun.tools.javac.code.Source;
  52 import com.sun.tools.javac.code.Symbol.ModuleSymbol;
  53 import com.sun.tools.javac.jvm.Target;
  54 
  55 /**
  56  * Write reflexive transitive closure of the given modules along their requires transitive edges into
  57  * file <version>/system-modules in the specified directory.
  58  */
  59 public class TransitiveDependencies {
  60 
  61     private static void help() {
  62         System.err.println("java TransitiveDependencies <target-directory> <module-source-path> <root-modules>");
  63     }
  64 
  65     public static void main(String... args) throws IOException {
  66         if (args.length < 2) {
  67             help();
  68             return ;
  69         }
  70 
  71         JavaCompiler compiler = JavacTool.create();
  72         List<String> options = List.of("-source", Source.DEFAULT.name,
  73                                        "-target", Target.DEFAULT.name,
  74                                        "-proc:only",
  75                                        "--system", "none",
  76                                        "--module-source-path", args[1],
  77                                        "--add-modules", Arrays.stream(args)
  78                                                               .skip(2)
  79                                                               .collect(Collectors.joining(",")));
  80         List<String> jlObjectList = List.of("java.lang.Object");
  81         JavacTaskImpl task = (JavacTaskImpl) compiler.getTask(null, null, null, options, jlObjectList, null);
  82         task.enter();
  83         Elements elements = task.getElements();
  84         Deque<String> todo = new ArrayDeque<>();
  85         Arrays.stream(args).skip(2).forEach(todo::add);
  86         Set<String> allModules = new HashSet<>();
  87 
  88         while (!todo.isEmpty()) {
  89             String current = todo.removeFirst();
  90 
  91             if (!allModules.add(current))
  92                 continue;
  93 
  94             ModuleSymbol mod = (ModuleSymbol) elements.getModuleElement(current);
  95 
  96             if (mod == null) {
  97                 throw new IllegalStateException("Missing: " + current);
  98             }
  99 
 100              //use the internal structure to avoid unnecesarily completing the symbol using the UsesProvidesVisitor:
 101             for (RequiresDirective rd : mod.requires) {
 102                 if (rd.isTransitive()) {
 103                     todo.offerLast(rd.getDependency().getQualifiedName().toString());
 104                 }
 105             }
 106         }
 107 
 108         allModules.add("java.base");
 109         allModules.add("jdk.unsupported");
 110 
 111         String version =
 112                 Integer.toString(Integer.parseInt(Source.DEFAULT.name), Character.MAX_RADIX);
 113         version = version.toUpperCase(Locale.ROOT);
 114 
 115         Path targetFile = Paths.get(args[0]).resolve(version).resolve("system-modules");
 116 
 117         Files.createDirectories(targetFile.getParent());
 118 
 119         try (Writer w = Files.newBufferedWriter(targetFile);
 120              PrintWriter out = new PrintWriter(w)) {
 121             allModules.stream()
 122                       .sorted()
 123                       .forEach(out::println);
 124         }
 125     }
 126 
 127 }