< prev index next >

make/src/classes/build/tools/module/GenModuleInfoSource.java

Print this page




   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 package build.tools.module;
  26 
  27 import java.io.BufferedWriter;


  28 import java.nio.file.Files;
  29 import java.nio.file.Path;
  30 import java.nio.file.Paths;
  31 import java.util.HashMap;
  32 import java.util.HashSet;

  33 import java.util.Map;

  34 import java.util.Set;

  35 
  36 /**
  37  * A build tool to extend the module-info.java in the source tree
  38  * for platform-specific exports, uses, and provides and write
  39  * to the specified output file.
  40  *
  41  * GenModulesList build tool currently generates the modules.list from
  42  * the module-info.java from the source tree that will be used for
  43  * the make target and dependences.
  44  *
  45  * The build currently invokes gensrc-$MODULE.gmk after modules.list
  46  * is generated.  Hence, platform-specific requires is not supported.
  47  */
  48 public class GenModuleInfoSource {
  49     private final static String USAGE =
  50         "Usage: GenModuleInfoSource [option] -o <output file> <module-info-java>\n" +
  51         "Options are:\n" +
  52         "  -exports  <package-name>\n" +
  53         "  -exports  <package-name>/<module-name>\n" +
  54         "  -uses     <service>\n" +
  55         "  -provides <service>/<provider-impl-classname>\n";
  56 
  57     public static void main(String... args) throws Exception {
  58         Path outfile = null;
  59         Path moduleInfoJava = null;
  60         Map<String, Set<String>> options = new HashMap<>();
  61 
  62         // validate input arguments
  63         for (int i = 0; i < args.length; i++){
  64             String option = args[i];
  65             if (option.startsWith("-")) {
  66                 String arg = args[++i];
  67                 if (option.equals("-exports") ||
  68                         option.equals("-uses") ||
  69                         option.equals("-provides")) {
  70                     options.computeIfAbsent(option, _k -> new HashSet<>()).add(arg);















  71                 } else if (option.equals("-o")) {
  72                     outfile = Paths.get(arg);
  73                 } else {
  74                     throw new IllegalArgumentException("invalid option: " + option);
  75                 }
  76             } else if (moduleInfoJava != null) {
  77                 throw new IllegalArgumentException("more than one module-info.java");
  78             } else {
  79                 moduleInfoJava = Paths.get(option);
  80                 if (Files.notExists(moduleInfoJava)) {
  81                     throw new IllegalArgumentException(option + " not exist");
  82                 }
  83             }
  84         }
  85 
  86         if (moduleInfoJava == null || outfile == null) {
  87             System.err.println(USAGE);
  88             System.exit(-1);
  89         }
  90         // read module-info.java
  91         Module.Builder builder = ModuleInfoReader.builder(moduleInfoJava);
  92         augment(builder, options);
  93 
  94         // generate new module-info.java
  95         Module module = builder.build();


































  96         Path parent = outfile.getParent();
  97         if (parent != null)
  98             Files.createDirectories(parent);
  99 
 100         try (BufferedWriter writer = Files.newBufferedWriter(outfile)) {
 101             writer.write(module.toString());






















 102         }
 103     }
 104 
 105     private static void augment(Module.Builder builder, Map<String, Set<String>> options) {
 106         for (String opt : options.keySet()) {
 107             if (opt.equals("-exports")) {
 108                 for (String arg : options.get(opt)) {
 109                     int index = arg.indexOf('/');
 110                     if (index > 0) {
 111                         String pn = arg.substring(0, index);
 112                         String mn = arg.substring(index + 1, arg.length());
 113                         builder.exportTo(pn, mn);
 114                     } else {
 115                         builder.export(arg);
 116                     }
















 117                 }
 118             } else if (opt.equals("-uses")) {
 119                 options.get(opt).stream()
 120                         .forEach(builder::use);
 121             } else if (opt.equals("-provides")) {
 122                 for (String arg : options.get(opt)) {
 123                     int index = arg.indexOf('/');
 124                     if (index <= 0) {
 125                         throw new IllegalArgumentException("invalid -provide argument: " + arg);
 126                     }
 127                     String service = arg.substring(0, index);
 128                     String impl = arg.substring(index + 1, arg.length());
 129                     builder.provide(service, impl);
 130                 }
 131             }

















 132         }
 133     }


























 134 }


   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 package build.tools.module;
  26 
  27 import java.io.BufferedWriter;
  28 import java.io.IOException;
  29 import java.io.PrintWriter;
  30 import java.nio.file.Files;
  31 import java.nio.file.Path;
  32 import java.nio.file.Paths;
  33 import java.util.HashMap;
  34 import java.util.HashSet;
  35 import java.util.List;
  36 import java.util.Map;
  37 import java.util.Objects;
  38 import java.util.Set;
  39 import java.util.stream.Collectors;
  40 
  41 /**
  42  * A build tool to extend the module-info.java in the source tree for
  43  * platform-specific exports, uses, and provides and write to the specified
  44  * output file. Injecting platform-specific requires is not supported.
  45  *
  46  * The extra exports, uses, provides can be specified in module-info.java.extra
  47  * files and GenModuleInfoSource will be invoked for each module that has
  48  * module-info.java.extra in the source directory.



  49  */
  50 public class GenModuleInfoSource {
  51     private final static String USAGE =
  52         "Usage: GenModuleInfoSource [option] -o <output file> <module-info-java>\n" +
  53         "Options are:\n" +
  54         "  -exports  <package-name>\n" +
  55         "  -exports  <package-name>/<module-name>\n" +
  56         "  -uses     <service>\n" +
  57         "  -provides <service>/<provider-impl-classname>\n";
  58 
  59     public static void main(String... args) throws Exception {
  60         Path outfile = null;
  61         Path moduleInfoJava = null;
  62         GenModuleInfoSource genModuleInfo = new GenModuleInfoSource();
  63 
  64         // validate input arguments
  65         for (int i = 0; i < args.length; i++){
  66             String option = args[i];
  67             if (option.startsWith("-")) {
  68                 String arg = args[++i];
  69                 if (option.equals("-exports")) {
  70                     int index = arg.indexOf('/');
  71                         if (index > 0) {
  72                             String pn = arg.substring(0, index);
  73                             String mn = arg.substring(index + 1, arg.length());
  74                             genModuleInfo.exportTo(pn, mn);
  75                         } else {
  76                             genModuleInfo.export(arg);
  77                         }
  78                 } else if (option.equals("-uses")) {
  79                     genModuleInfo.use(arg);
  80                 } else if (option.equals("-provides")) {
  81                         int index = arg.indexOf('/');
  82                         if (index <= 0) {
  83                             throw new IllegalArgumentException("invalid -provide argument: " + arg);
  84                         }
  85                         String service = arg.substring(0, index);
  86                         String impl = arg.substring(index + 1, arg.length());
  87                         genModuleInfo.provide(service, impl);
  88                 } else if (option.equals("-o")) {
  89                     outfile = Paths.get(arg);
  90                 } else {
  91                     throw new IllegalArgumentException("invalid option: " + option);
  92                 }
  93             } else if (moduleInfoJava != null) {
  94                 throw new IllegalArgumentException("more than one module-info.java");
  95             } else {
  96                 moduleInfoJava = Paths.get(option);
  97                 if (Files.notExists(moduleInfoJava)) {
  98                     throw new IllegalArgumentException(option + " not exist");
  99                 }
 100             }
 101         }
 102 
 103         if (moduleInfoJava == null || outfile == null) {
 104             System.err.println(USAGE);
 105             System.exit(-1);
 106         }



 107 
 108         // generate new module-info.java
 109         genModuleInfo.generate(moduleInfoJava, outfile);
 110     }
 111 
 112     private final Set<String> exports = new HashSet<>();
 113     private final Map<String, Set<String>> exportsTo = new HashMap<>();
 114     private final Set<String> uses = new HashSet<>();
 115     private final Map<String, Set<String>> provides = new HashMap<>();
 116     GenModuleInfoSource() {
 117     }
 118 
 119     private void export(String p) {
 120         Objects.requireNonNull(p);
 121         if (exports.contains(p) || exportsTo.containsKey(p)) {
 122             throw new RuntimeException("duplicated exports: " + p);
 123         }
 124         exports.add(p);
 125     }
 126     private void exportTo(String p, String mn) {
 127         Objects.requireNonNull(p);
 128         Objects.requireNonNull(mn);
 129         if (exports.contains(p)) {
 130             throw new RuntimeException("unqualified exports already exists: " + p);
 131         }
 132         exportsTo.computeIfAbsent(p, _k -> new HashSet<>()).add(mn);
 133     }
 134 
 135     private void use(String service) {
 136         uses.add(service);
 137     }
 138 
 139     private void provide(String s, String impl) {
 140         provides.computeIfAbsent(s, _k -> new HashSet<>()).add(impl);
 141     }
 142 
 143     private void generate(Path sourcefile, Path outfile) throws IOException {
 144         Path parent = outfile.getParent();
 145         if (parent != null)
 146             Files.createDirectories(parent);
 147 
 148         List<String> lines = Files.readAllLines(sourcefile);
 149         try (BufferedWriter bw = Files.newBufferedWriter(outfile);
 150              PrintWriter writer = new PrintWriter(bw)) {
 151             int lineNumber = 0;
 152             for (String l : lines) {
 153                 lineNumber++;
 154                 String[] s = l.trim().split("\\s+");
 155                 String keyword = s[0].trim();
 156                 int nextIndex = keyword.length();
 157                 String exp = null;
 158                 int n = l.length();
 159                 switch (keyword) {
 160                     case "exports":
 161                         boolean inExportsTo = false;
 162                         // assume package name immediately after exports
 163                         exp = s[1].trim();
 164                         if (s.length >= 3) {
 165                             nextIndex = l.indexOf(exp, nextIndex) + exp.length();
 166                             if (s[2].trim().equals("to")) {
 167                                 inExportsTo = true;
 168                                 n = l.indexOf("to", nextIndex) + "to".length();
 169                             } else {
 170                                 throw new RuntimeException(sourcefile + ", line " +
 171                                     lineNumber + ", is malformed: " + s[2]);
 172                             }
 173                         }
 174 
 175                         // inject the extra targets after "to"
 176                         if (inExportsTo) {
 177                             writer.println(injectExportTargets(exp, l, n));






 178                         } else {
 179                             writer.println(l);
 180                         }
 181                         break;
 182                     case "to":
 183                         if (exp == null) {
 184                             throw new RuntimeException(sourcefile + ", line " +
 185                                 lineNumber + ", is malformed");
 186                         }
 187                         n = l.indexOf("to", nextIndex) + "to".length();
 188                         writer.println(injectExportTargets(exp, l, n));
 189                         break;
 190                     case "}":
 191                         doAugments(writer);
 192                         // fall through
 193                     default:
 194                         writer.println(l);
 195                         // reset exports
 196                         exp = null;
 197                 }








 198             }



 199         }
 200     }
 201 
 202     private String injectExportTargets(String pn, String exp, int pos) {
 203         Set<String> targets = exportsTo.remove(pn);
 204         if (targets != null) {
 205             StringBuilder sb = new StringBuilder();
 206             // inject the extra targets after the given pos
 207             sb.append(exp.substring(0, pos))
 208               .append(targets.stream()
 209                              .collect(Collectors.joining(", ", " ", ",")))
 210               .append(" /* injected */");
 211             if (pos < exp.length()) {
 212                 // print the remaining statement followed "to"
 213                 sb.append(exp.substring(pos+1, exp.length()));
 214             }
 215             return sb.toString();
 216         } else {
 217             return exp;
 218         }
 219     }
 220 
 221     private void doAugments(PrintWriter writer) {
 222         if ((exports.size() + exportsTo.size() + uses.size() + provides.size()) == 0)
 223             return;
 224 
 225         writer.println("    // augmented from module-info.java.extra");
 226         exports.stream()
 227             .sorted()
 228             .forEach(e -> writer.format("    exports %s;%n", e));
 229         // remaining injected qualified exports
 230         exportsTo.entrySet().stream()
 231             .sorted(Map.Entry.comparingByKey())
 232             .map(e -> String.format("    exports %s to%n%s;", e.getKey(),
 233                                     e.getValue().stream().sorted()
 234                                         .map(mn -> String.format("        %s", mn))
 235                                         .collect(Collectors.joining(",\n"))))
 236             .forEach(writer::println);
 237         uses.stream().sorted()
 238             .forEach(s -> writer.format("    uses %s;%n", s));
 239         provides.entrySet().stream()
 240             .sorted(Map.Entry.comparingByKey())
 241             .flatMap(e -> e.getValue().stream().sorted()
 242                            .map(impl -> String.format("    provides %s with %s;",
 243                                                       e.getKey(), impl)))
 244             .forEach(writer::println);
 245     }
 246 }
< prev index next >