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 } |