1 /*
2 * Copyright (c) 2015, 2016, 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
93 if (mn.isEmpty()) {
94 throw taskHelper.newBadArgs("err.mods.must.be.specified",
95 "--limit-modules");
96 }
97 task.options.limitMods.add(mn);
98 }
99 }, "--limit-modules"),
100 new Option<JlinkTask>(true, (task, opt, arg) -> {
101 for (String mn : arg.split(",")) {
102 if (mn.isEmpty()) {
103 throw taskHelper.newBadArgs("err.mods.must.be.specified",
104 "--add-modules");
105 }
106 task.options.addMods.add(mn);
107 }
108 }, "--add-modules"),
109 new Option<JlinkTask>(true, (task, opt, arg) -> {
110 Path path = Paths.get(arg);
111 task.options.output = path;
112 }, "--output"),
113 new Option<JlinkTask>(true, (task, opt, arg) -> {
114 String[] values = arg.split("=");
115 // check values
116 if (values.length != 2 || values[0].isEmpty() || values[1].isEmpty()) {
117 throw taskHelper.newBadArgs("err.launcher.value.format", arg);
118 } else {
119 String commandName = values[0];
120 String moduleAndMain = values[1];
121 int idx = moduleAndMain.indexOf("/");
122 if (idx != -1) {
123 if (moduleAndMain.substring(0, idx).isEmpty()) {
124 throw taskHelper.newBadArgs("err.launcher.module.name.empty", arg);
125 }
126
127 if (moduleAndMain.substring(idx + 1).isEmpty()) {
128 throw taskHelper.newBadArgs("err.launcher.main.class.empty", arg);
129 }
130 }
131 task.options.launchers.put(commandName, moduleAndMain);
132 }
133 }, "--launcher"),
134 new Option<JlinkTask>(true, (task, opt, arg) -> {
135 if ("little".equals(arg)) {
136 task.options.endian = ByteOrder.LITTLE_ENDIAN;
137 } else if ("big".equals(arg)) {
138 task.options.endian = ByteOrder.BIG_ENDIAN;
139 } else {
140 throw taskHelper.newBadArgs("err.unknown.byte.order", arg);
141 }
142 }, "--endian"),
143 new Option<JlinkTask>(false, (task, opt, arg) -> {
144 task.options.version = true;
145 }, "--version"),
146 new Option<JlinkTask>(true, (task, opt, arg) -> {
147 Path path = Paths.get(arg);
148 if (Files.exists(path)) {
149 throw taskHelper.newBadArgs("err.dir.exists", path);
150 }
151 task.options.packagedModulesPath = path;
152 }, true, "--keep-packaged-modules"),
153 new Option<JlinkTask>(true, (task, opt, arg) -> {
154 task.options.saveoptsfile = arg;
155 }, "--save-opts"),
156 new Option<JlinkTask>(false, (task, opt, arg) -> {
157 task.options.fullVersion = true;
158 }, true, "--full-version"),
159 new Option<JlinkTask>(false, (task, opt, arg) -> {
160 task.options.ignoreSigning = true;
161 }, "--ignore-signing-information"),};
162
163 private static final String PROGNAME = "jlink";
168 private PrintWriter log;
169
170 void setLog(PrintWriter out, PrintWriter err) {
171 log = out;
172 taskHelper.setLog(log);
173 }
174
175 /**
176 * Result codes.
177 */
178 static final int
179 EXIT_OK = 0, // Completed with no errors.
180 EXIT_ERROR = 1, // Completed but reported errors.
181 EXIT_CMDERR = 2, // Bad command-line arguments
182 EXIT_SYSERR = 3, // System error or resource exhaustion.
183 EXIT_ABNORMAL = 4;// terminated abnormally
184
185 static class OptionsValues {
186 boolean help;
187 String saveoptsfile;
188 boolean version;
189 boolean fullVersion;
190 final List<Path> modulePath = new ArrayList<>();
191 final Set<String> limitMods = new HashSet<>();
192 final Set<String> addMods = new HashSet<>();
193 Path output;
194 final Map<String, String> launchers = new HashMap<>();
195 Path packagedModulesPath;
196 ByteOrder endian = ByteOrder.nativeOrder();
197 boolean ignoreSigning = false;
198 }
199
200 int run(String[] args) {
201 if (log == null) {
202 setLog(new PrintWriter(System.out, true),
203 new PrintWriter(System.err, true));
204 }
205 try {
206 optionsHelper.handleOptionsNoUnhandled(this, args);
207 if (options.help) {
208 optionsHelper.showHelp(PROGNAME);
209 return EXIT_OK;
210 }
211 if (optionsHelper.shouldListPlugins()) {
212 optionsHelper.listPlugins();
213 return EXIT_OK;
214 }
215 if (options.version || options.fullVersion) {
216 taskHelper.showVersion(options.fullVersion);
217 return EXIT_OK;
218 }
219
220 if (taskHelper.getExistingImage() == null) {
221 if (options.modulePath.isEmpty()) {
222 throw taskHelper.newBadArgs("err.modulepath.must.be.specified").showUsage(true);
223 }
224 createImage();
225 } else {
226 postProcessOnly(taskHelper.getExistingImage());
227 }
228
229 if (options.saveoptsfile != null) {
230 Files.write(Paths.get(options.saveoptsfile), getSaveOpts().getBytes());
231 }
232
233 return EXIT_OK;
234 } catch (PluginException | IllegalArgumentException |
235 UncheckedIOException |IOException | FindException | ResolutionException e) {
236 log.println(taskHelper.getMessage("error.prefix") + " " + e.getMessage());
237 if (DEBUG) {
238 e.printStackTrace(log);
239 }
240 return EXIT_ERROR;
241 } catch (BadArgs e) {
242 taskHelper.reportError(e.key, e.args);
243 if (e.showUsage) {
244 log.println(taskHelper.getMessage("main.usage.summary", PROGNAME));
245 }
246 if (DEBUG) {
247 e.printStackTrace(log);
248 }
249 return EXIT_CMDERR;
250 } catch (Throwable x) {
251 log.println(taskHelper.getMessage("error.prefix") + " " + x.getMessage());
252 x.printStackTrace(log);
253 return EXIT_ABNORMAL;
254 } finally {
255 log.flush();
256 }
257 }
258
259 /*
260 * Jlink API entry point.
261 */
262 public static void createImage(JlinkConfiguration config,
263 PluginsConfiguration plugins)
264 throws Exception {
265 Objects.requireNonNull(config);
266 Objects.requireNonNull(config.getOutput());
267 plugins = plugins == null ? new PluginsConfiguration() : plugins;
268
269 if (config.getModulepaths().isEmpty()) {
270 throw new IllegalArgumentException("Empty module paths");
271 }
272
273 ModuleFinder finder = newModuleFinder(config.getModulepaths(),
274 config.getLimitmods(),
275 config.getModules());
276
277 if (config.getModules().isEmpty()) {
278 throw new IllegalArgumentException("No modules to add");
279 }
280
281 // First create the image provider
282 ImageProvider imageProvider =
283 createImageProvider(finder,
284 config.getModules(),
285 config.getByteOrder(),
286 null,
287 IGNORE_SIGNING_DEFAULT,
288 null);
289
290 // Then create the Plugin Stack
291 ImagePluginStack stack = ImagePluginConfiguration.parseConfiguration(plugins);
292
293 //Ask the stack to proceed;
294 stack.operate(imageProvider);
295 }
296
297 /*
298 * Jlink API entry point.
299 */
300 public static void postProcessImage(ExecutableImage image, List<Plugin> postProcessorPlugins)
301 throws Exception {
302 Objects.requireNonNull(image);
303 Objects.requireNonNull(postProcessorPlugins);
304 PluginsConfiguration config = new PluginsConfiguration(postProcessorPlugins);
305 ImagePluginStack stack = ImagePluginConfiguration.
306 parseConfiguration(config);
307
308 stack.operate((ImagePluginStack stack1) -> image);
309 }
310
311 private void postProcessOnly(Path existingImage) throws Exception {
312 PluginsConfiguration config = taskHelper.getPluginsConfig(null, null);
313 ExecutableImage img = DefaultImageBuilder.getExecutableImage(existingImage);
314 if (img == null) {
315 throw taskHelper.newBadArgs("err.existing.image.invalid");
316 }
317 postProcessImage(img, config.getPlugins());
318 }
319
320 // the token for "all modules on the module path"
321 private static final String ALL_MODULE_PATH = "ALL-MODULE-PATH";
322 private void createImage() throws Exception {
323 if (options.output == null) {
324 throw taskHelper.newBadArgs("err.output.must.be.specified").showUsage(true);
325 }
326
327 if (options.addMods.isEmpty()) {
328 throw taskHelper.newBadArgs("err.mods.must.be.specified", "--add-modules")
329 .showUsage(true);
330 }
331
332 Set<String> roots = new HashSet<>();
333 for (String mod : options.addMods) {
334 if (mod.equals(ALL_MODULE_PATH)) {
335 ModuleFinder finder = modulePathFinder();
336 finder.findAll()
337 .stream()
338 .map(ModuleReference::descriptor)
339 .map(ModuleDescriptor::name)
340 .forEach(mn -> roots.add(mn));
341 } else {
342 roots.add(mod);
343 }
344 }
345
346 ModuleFinder finder = newModuleFinder(options.modulePath,
347 options.limitMods,
348 roots);
349
350
351 // First create the image provider
352 ImageProvider imageProvider = createImageProvider(finder,
353 roots,
354 options.endian,
355 options.packagedModulesPath,
356 options.ignoreSigning,
357 log);
358
359 // Then create the Plugin Stack
360 ImagePluginStack stack = ImagePluginConfiguration.
361 parseConfiguration(taskHelper.getPluginsConfig(options.output, options.launchers));
362
363 //Ask the stack to proceed
364 stack.operate(imageProvider);
365 }
366
367 /**
368 * Returns a module finder to find the observable modules specified in
369 * the --module-path and --limit-modules options
370 */
371 private ModuleFinder modulePathFinder() {
372 Path[] entries = options.modulePath.toArray(new Path[0]);
373 ModuleFinder finder = ModulePath.of(Runtime.version(), true, entries);
374 if (!options.limitMods.isEmpty()) {
375 finder = limitFinder(finder, options.limitMods, Collections.emptySet());
376 }
377 return finder;
378 }
379
380 /*
381 * Returns a module finder of the given module path that limits
382 * the observable modules to those in the transitive closure of
383 * the modules specified in {@code limitMods} plus other modules
384 * specified in the {@code roots} set.
385 */
386 public static ModuleFinder newModuleFinder(List<Path> paths,
387 Set<String> limitMods,
388 Set<String> roots)
389 {
390 Path[] entries = paths.toArray(new Path[0]);
391 ModuleFinder finder = ModulePath.of(Runtime.version(), true, entries);
392
393 // if limitmods is specified then limit the universe
394 if (!limitMods.isEmpty()) {
395 finder = limitFinder(finder, limitMods, roots);
396 }
397 return finder;
398 }
399
400 private static Path toPathLocation(ResolvedModule m) {
401 Optional<URI> ouri = m.reference().location();
402 if (!ouri.isPresent())
403 throw new InternalError(m + " does not have a location");
404 URI uri = ouri.get();
405 return Paths.get(uri);
406 }
407
408 private static ImageProvider createImageProvider(ModuleFinder finder,
409 Set<String> roots,
410 ByteOrder order,
411 Path retainModulesPath,
412 boolean ignoreSigning,
413 PrintWriter log)
414 throws IOException
415 {
416 if (roots.isEmpty()) {
417 throw new IllegalArgumentException("empty modules and limitmods");
418 }
419
420 Configuration cf = Configuration.empty()
421 .resolve(finder,
422 ModuleFinder.of(),
423 roots);
424
425 // emit a warning for any incubating modules in the configuration
426 if (log != null) {
427 String im = cf.modules()
428 .stream()
429 .map(ResolvedModule::reference)
430 .filter(ModuleResolution::hasIncubatingWarning)
431 .map(ModuleReference::descriptor)
432 .map(ModuleDescriptor::name)
433 .collect(Collectors.joining(", "));
434
435 if (!"".equals(im))
436 log.println("WARNING: Using incubator modules: " + im);
437 }
438
439 Map<String, Path> mods = cf.modules().stream()
440 .collect(Collectors.toMap(ResolvedModule::name, JlinkTask::toPathLocation));
441 return new ImageHelper(cf, mods, order, retainModulesPath, ignoreSigning);
442 }
443
444 /*
445 * Returns a ModuleFinder that limits observability to the given root
446 * modules, their transitive dependences, plus a set of other modules.
447 */
448 private static ModuleFinder limitFinder(ModuleFinder finder,
449 Set<String> roots,
450 Set<String> otherMods) {
451
452 // resolve all root modules
453 Configuration cf = Configuration.empty()
454 .resolve(finder,
455 ModuleFinder.of(),
456 roots);
457
458 // module name -> reference
459 Map<String, ModuleReference> map = new HashMap<>();
460 cf.modules().forEach(m -> {
461 ModuleReference mref = m.reference();
462 map.put(mref.descriptor().name(), mref);
463 });
464
465 // add the other modules
466 otherMods.stream()
467 .map(finder::find)
468 .flatMap(Optional::stream)
469 .forEach(mref -> map.putIfAbsent(mref.descriptor().name(), mref));
470
471 // set of modules that are observable
472 Set<ModuleReference> mrefs = new HashSet<>(map.values());
473
474 return new ModuleFinder() {
475 @Override
476 public Optional<ModuleReference> find(String name) {
477 return Optional.ofNullable(map.get(name));
478 }
479
480 @Override
481 public Set<ModuleReference> findAll() {
482 return mrefs;
483 }
484 };
485 }
486
487 private String getSaveOpts() {
488 StringBuilder sb = new StringBuilder();
489 sb.append('#').append(new Date()).append("\n");
490 for (String c : optionsHelper.getInputCommand()) {
491 sb.append(c).append(" ");
492 }
493
494 return sb.toString();
495 }
496
497 private static String getBomHeader() {
498 StringBuilder sb = new StringBuilder();
499 sb.append("#").append(new Date()).append("\n");
500 sb.append("#Please DO NOT Modify this file").append("\n");
501 return sb.toString();
502 }
503
504 private String genBOMContent() throws IOException {
505 StringBuilder sb = new StringBuilder();
506 sb.append(getBomHeader());
|
1 /*
2 * Copyright (c) 2015, 2017, 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
93 if (mn.isEmpty()) {
94 throw taskHelper.newBadArgs("err.mods.must.be.specified",
95 "--limit-modules");
96 }
97 task.options.limitMods.add(mn);
98 }
99 }, "--limit-modules"),
100 new Option<JlinkTask>(true, (task, opt, arg) -> {
101 for (String mn : arg.split(",")) {
102 if (mn.isEmpty()) {
103 throw taskHelper.newBadArgs("err.mods.must.be.specified",
104 "--add-modules");
105 }
106 task.options.addMods.add(mn);
107 }
108 }, "--add-modules"),
109 new Option<JlinkTask>(true, (task, opt, arg) -> {
110 Path path = Paths.get(arg);
111 task.options.output = path;
112 }, "--output"),
113 new Option<JlinkTask>(false, (task, opt, arg) -> {
114 task.options.bindServices = true;
115 }, "--bind-services"),
116 new Option<JlinkTask>(false, (task, opt, arg) -> {
117 task.options.suggestProviders = true;
118 }, "--suggest-providers", "", true),
119 new Option<JlinkTask>(true, (task, opt, arg) -> {
120 String[] values = arg.split("=");
121 // check values
122 if (values.length != 2 || values[0].isEmpty() || values[1].isEmpty()) {
123 throw taskHelper.newBadArgs("err.launcher.value.format", arg);
124 } else {
125 String commandName = values[0];
126 String moduleAndMain = values[1];
127 int idx = moduleAndMain.indexOf("/");
128 if (idx != -1) {
129 if (moduleAndMain.substring(0, idx).isEmpty()) {
130 throw taskHelper.newBadArgs("err.launcher.module.name.empty", arg);
131 }
132
133 if (moduleAndMain.substring(idx + 1).isEmpty()) {
134 throw taskHelper.newBadArgs("err.launcher.main.class.empty", arg);
135 }
136 }
137 task.options.launchers.put(commandName, moduleAndMain);
138 }
139 }, "--launcher"),
140 new Option<JlinkTask>(true, (task, opt, arg) -> {
141 if ("little".equals(arg)) {
142 task.options.endian = ByteOrder.LITTLE_ENDIAN;
143 } else if ("big".equals(arg)) {
144 task.options.endian = ByteOrder.BIG_ENDIAN;
145 } else {
146 throw taskHelper.newBadArgs("err.unknown.byte.order", arg);
147 }
148 }, "--endian"),
149 new Option<JlinkTask>(false, (task, opt, arg) -> {
150 task.options.verbose = true;
151 }, "--verbose", "-v"),
152 new Option<JlinkTask>(false, (task, opt, arg) -> {
153 task.options.version = true;
154 }, "--version"),
155 new Option<JlinkTask>(true, (task, opt, arg) -> {
156 Path path = Paths.get(arg);
157 if (Files.exists(path)) {
158 throw taskHelper.newBadArgs("err.dir.exists", path);
159 }
160 task.options.packagedModulesPath = path;
161 }, true, "--keep-packaged-modules"),
162 new Option<JlinkTask>(true, (task, opt, arg) -> {
163 task.options.saveoptsfile = arg;
164 }, "--save-opts"),
165 new Option<JlinkTask>(false, (task, opt, arg) -> {
166 task.options.fullVersion = true;
167 }, true, "--full-version"),
168 new Option<JlinkTask>(false, (task, opt, arg) -> {
169 task.options.ignoreSigning = true;
170 }, "--ignore-signing-information"),};
171
172 private static final String PROGNAME = "jlink";
177 private PrintWriter log;
178
179 void setLog(PrintWriter out, PrintWriter err) {
180 log = out;
181 taskHelper.setLog(log);
182 }
183
184 /**
185 * Result codes.
186 */
187 static final int
188 EXIT_OK = 0, // Completed with no errors.
189 EXIT_ERROR = 1, // Completed but reported errors.
190 EXIT_CMDERR = 2, // Bad command-line arguments
191 EXIT_SYSERR = 3, // System error or resource exhaustion.
192 EXIT_ABNORMAL = 4;// terminated abnormally
193
194 static class OptionsValues {
195 boolean help;
196 String saveoptsfile;
197 boolean verbose;
198 boolean version;
199 boolean fullVersion;
200 final List<Path> modulePath = new ArrayList<>();
201 final Set<String> limitMods = new HashSet<>();
202 final Set<String> addMods = new HashSet<>();
203 Path output;
204 final Map<String, String> launchers = new HashMap<>();
205 Path packagedModulesPath;
206 ByteOrder endian = ByteOrder.nativeOrder();
207 boolean ignoreSigning = false;
208 boolean bindServices = false;
209 boolean suggestProviders = false;
210 }
211
212 int run(String[] args) {
213 if (log == null) {
214 setLog(new PrintWriter(System.out, true),
215 new PrintWriter(System.err, true));
216 }
217 try {
218 List<String> remaining = optionsHelper.handleOptions(this, args);
219 if (remaining.size() > 0 && !options.suggestProviders) {
220 throw taskHelper.newBadArgs("err.orphan.arguments", toString(remaining))
221 .showUsage(true);
222 }
223 if (options.help) {
224 optionsHelper.showHelp(PROGNAME);
225 return EXIT_OK;
226 }
227 if (optionsHelper.shouldListPlugins()) {
228 optionsHelper.listPlugins();
229 return EXIT_OK;
230 }
231 if (options.version || options.fullVersion) {
232 taskHelper.showVersion(options.fullVersion);
233 return EXIT_OK;
234 }
235
236 if (taskHelper.getExistingImage() != null) {
237 postProcessOnly(taskHelper.getExistingImage());
238 return EXIT_OK;
239 }
240
241 if (options.modulePath.isEmpty()) {
242 throw taskHelper.newBadArgs("err.modulepath.must.be.specified")
243 .showUsage(true);
244 }
245
246 JlinkConfiguration config = initJlinkConfig();
247 if (options.suggestProviders) {
248 suggestProviders(config, remaining);
249 } else {
250 createImage(config);
251 if (options.saveoptsfile != null) {
252 Files.write(Paths.get(options.saveoptsfile), getSaveOpts().getBytes());
253 }
254 }
255
256 return EXIT_OK;
257 } catch (PluginException | IllegalArgumentException |
258 UncheckedIOException |IOException | FindException | ResolutionException e) {
259 log.println(taskHelper.getMessage("error.prefix") + " " + e.getMessage());
260 if (DEBUG) {
261 e.printStackTrace(log);
262 }
263 return EXIT_ERROR;
264 } catch (BadArgs e) {
265 taskHelper.reportError(e.key, e.args);
266 if (e.showUsage) {
267 log.println(taskHelper.getMessage("main.usage.summary", PROGNAME));
268 }
269 if (DEBUG) {
270 e.printStackTrace(log);
271 }
272 return EXIT_CMDERR;
273 } catch (Throwable x) {
274 log.println(taskHelper.getMessage("error.prefix") + " " + x.getMessage());
275 x.printStackTrace(log);
276 return EXIT_ABNORMAL;
277 } finally {
278 log.flush();
279 }
280 }
281
282 /*
283 * Jlink API entry point.
284 */
285 public static void createImage(JlinkConfiguration config,
286 PluginsConfiguration plugins)
287 throws Exception {
288 Objects.requireNonNull(config);
289 Objects.requireNonNull(config.getOutput());
290 plugins = plugins == null ? new PluginsConfiguration() : plugins;
291
292 // First create the image provider
293 ImageProvider imageProvider =
294 createImageProvider(config,
295 null,
296 IGNORE_SIGNING_DEFAULT,
297 false,
298 false,
299 null);
300
301 // Then create the Plugin Stack
302 ImagePluginStack stack = ImagePluginConfiguration.parseConfiguration(plugins);
303
304 //Ask the stack to proceed;
305 stack.operate(imageProvider);
306 }
307
308 /*
309 * Jlink API entry point.
310 */
311 public static void postProcessImage(ExecutableImage image, List<Plugin> postProcessorPlugins)
312 throws Exception {
313 Objects.requireNonNull(image);
314 Objects.requireNonNull(postProcessorPlugins);
315 PluginsConfiguration config = new PluginsConfiguration(postProcessorPlugins);
316 ImagePluginStack stack = ImagePluginConfiguration.
317 parseConfiguration(config);
318
319 stack.operate((ImagePluginStack stack1) -> image);
320 }
321
322 private void postProcessOnly(Path existingImage) throws Exception {
323 PluginsConfiguration config = taskHelper.getPluginsConfig(null, null);
324 ExecutableImage img = DefaultImageBuilder.getExecutableImage(existingImage);
325 if (img == null) {
326 throw taskHelper.newBadArgs("err.existing.image.invalid");
327 }
328 postProcessImage(img, config.getPlugins());
329 }
330
331 // the token for "all modules on the module path"
332 private static final String ALL_MODULE_PATH = "ALL-MODULE-PATH";
333 private JlinkConfiguration initJlinkConfig() throws BadArgs {
334 if (options.addMods.isEmpty()) {
335 throw taskHelper.newBadArgs("err.mods.must.be.specified", "--add-modules")
336 .showUsage(true);
337 }
338
339 Set<String> roots = new HashSet<>();
340 for (String mod : options.addMods) {
341 if (mod.equals(ALL_MODULE_PATH)) {
342 Path[] entries = options.modulePath.toArray(new Path[0]);
343 ModuleFinder finder = ModulePath.of(Runtime.version(), true, entries);
344 if (!options.limitMods.isEmpty()) {
345 // finder for the observable modules specified in
346 // the --module-path and --limit-modules options
347 finder = limitFinder(finder, options.limitMods, Collections.emptySet());
348 }
349
350 // all observable modules are roots
351 finder.findAll()
352 .stream()
353 .map(ModuleReference::descriptor)
354 .map(ModuleDescriptor::name)
355 .forEach(mn -> roots.add(mn));
356 } else {
357 roots.add(mod);
358 }
359 }
360
361 return new JlinkConfiguration(options.output,
362 options.modulePath,
363 roots,
364 options.limitMods,
365 options.endian);
366 }
367
368 private void createImage(JlinkConfiguration config) throws Exception {
369 if (options.output == null) {
370 throw taskHelper.newBadArgs("err.output.must.be.specified").showUsage(true);
371 }
372
373 // First create the image provider
374 ImageProvider imageProvider = createImageProvider(config,
375 options.packagedModulesPath,
376 options.ignoreSigning,
377 options.bindServices,
378 options.verbose,
379 log);
380
381 // Then create the Plugin Stack
382 ImagePluginStack stack = ImagePluginConfiguration.parseConfiguration(
383 taskHelper.getPluginsConfig(options.output, options.launchers));
384
385 //Ask the stack to proceed
386 stack.operate(imageProvider);
387 }
388
389 /*
390 * Returns a module finder of the given module path that limits
391 * the observable modules to those in the transitive closure of
392 * the modules specified in {@code limitMods} plus other modules
393 * specified in the {@code roots} set.
394 */
395 public static ModuleFinder newModuleFinder(List<Path> paths,
396 Set<String> limitMods,
397 Set<String> roots)
398 {
399 Path[] entries = paths.toArray(new Path[0]);
400 ModuleFinder finder = ModulePath.of(Runtime.version(), true, entries);
401
402 // if limitmods is specified then limit the universe
403 if (!limitMods.isEmpty()) {
404 finder = limitFinder(finder, limitMods, roots);
405 }
406 return finder;
407 }
408
409 private static Path toPathLocation(ResolvedModule m) {
410 Optional<URI> ouri = m.reference().location();
411 if (!ouri.isPresent())
412 throw new InternalError(m + " does not have a location");
413 URI uri = ouri.get();
414 return Paths.get(uri);
415 }
416
417
418 private static ImageProvider createImageProvider(JlinkConfiguration config,
419 Path retainModulesPath,
420 boolean ignoreSigning,
421 boolean bindService,
422 boolean verbose,
423 PrintWriter log)
424 throws IOException
425 {
426 Configuration cf = bindService ? config.resolveAndBind()
427 : config.resolve();
428
429 if (verbose && log != null) {
430 // print modules to be linked in
431 cf.modules().stream()
432 .sorted(Comparator.comparing(ResolvedModule::name))
433 .forEach(rm -> log.format("module %s (%s)%n",
434 rm.name(), rm.reference().location().get()));
435
436 // print provider info
437 Set<ModuleReference> references = cf.modules().stream()
438 .map(ResolvedModule::reference).collect(Collectors.toSet());
439
440 String msg = String.format("%n%s:", taskHelper.getMessage("providers.header"));
441 printProviders(log, msg, references);
442 }
443
444 // emit a warning for any incubating modules in the configuration
445 if (log != null) {
446 String im = cf.modules()
447 .stream()
448 .map(ResolvedModule::reference)
449 .filter(ModuleResolution::hasIncubatingWarning)
450 .map(ModuleReference::descriptor)
451 .map(ModuleDescriptor::name)
452 .collect(Collectors.joining(", "));
453
454 if (!"".equals(im))
455 log.println("WARNING: Using incubator modules: " + im);
456 }
457
458 Map<String, Path> mods = cf.modules().stream()
459 .collect(Collectors.toMap(ResolvedModule::name, JlinkTask::toPathLocation));
460 return new ImageHelper(cf, mods, config.getByteOrder(), retainModulesPath, ignoreSigning);
461 }
462
463 /*
464 * Returns a ModuleFinder that limits observability to the given root
465 * modules, their transitive dependences, plus a set of other modules.
466 */
467 public static ModuleFinder limitFinder(ModuleFinder finder,
468 Set<String> roots,
469 Set<String> otherMods) {
470
471 // resolve all root modules
472 Configuration cf = Configuration.empty()
473 .resolve(finder,
474 ModuleFinder.of(),
475 roots);
476
477 // module name -> reference
478 Map<String, ModuleReference> map = new HashMap<>();
479 cf.modules().forEach(m -> {
480 ModuleReference mref = m.reference();
481 map.put(mref.descriptor().name(), mref);
482 });
483
484 // add the other modules
485 otherMods.stream()
486 .map(finder::find)
487 .flatMap(Optional::stream)
488 .forEach(mref -> map.putIfAbsent(mref.descriptor().name(), mref));
489
490 // set of modules that are observable
491 Set<ModuleReference> mrefs = new HashSet<>(map.values());
492
493 return new ModuleFinder() {
494 @Override
495 public Optional<ModuleReference> find(String name) {
496 return Optional.ofNullable(map.get(name));
497 }
498
499 @Override
500 public Set<ModuleReference> findAll() {
501 return mrefs;
502 }
503 };
504 }
505
506 /*
507 * Returns a map of each service type to the modules that use it
508 */
509 private static Map<String, Set<String>> uses(Set<ModuleReference> modules) {
510 // collects the services used by the modules and print uses
511 Map<String, Set<String>> uses = new HashMap<>();
512 modules.stream()
513 .map(ModuleReference::descriptor)
514 .forEach(md -> md.uses().forEach(s ->
515 uses.computeIfAbsent(s, _k -> new HashSet<>()).add(md.name()))
516 );
517 return uses;
518 }
519
520 private static void printProviders(PrintWriter log,
521 String header,
522 Set<ModuleReference> modules) {
523 printProviders(log, header, modules, uses(modules));
524 }
525
526 /*
527 * Prints the providers that are used by the services specified in
528 * the given modules.
529 *
530 * The specified uses maps a service type name to the modules
531 * using the service type and that may or may not be present
532 * the given modules.
533 */
534 private static void printProviders(PrintWriter log,
535 String header,
536 Set<ModuleReference> modules,
537 Map<String, Set<String>> uses) {
538 if (modules.isEmpty())
539 return;
540
541 // Build a map of a service type to the provider modules
542 Map<String, Set<ModuleDescriptor>> providers = new HashMap<>();
543 modules.stream()
544 .map(ModuleReference::descriptor)
545 .forEach(md -> {
546 md.provides().stream()
547 .filter(p -> uses.containsKey(p.service()))
548 .forEach(p -> providers.computeIfAbsent(p.service(), _k -> new HashSet<>())
549 .add(md));
550 });
551
552 if (!providers.isEmpty()) {
553 log.println(header);
554 }
555
556 // print the providers of the service types used by the specified modules
557 // sorted by the service type name and then provider's module name
558 providers.entrySet().stream()
559 .sorted(Map.Entry.comparingByKey())
560 .forEach(e -> {
561 String service = e.getKey();
562 e.getValue().stream()
563 .sorted(Comparator.comparing(ModuleDescriptor::name))
564 .forEach(md ->
565 md.provides().stream()
566 .filter(p -> p.service().equals(service))
567 .forEach(p -> log.format(" module %s provides %s, used by %s%n",
568 md.name(), p.service(),
569 uses.get(p.service()).stream()
570 .sorted()
571 .collect(Collectors.joining(","))))
572 );
573 });
574 }
575
576 private void suggestProviders(JlinkConfiguration config, List<String> args)
577 throws BadArgs
578 {
579 if (args.size() > 1) {
580 throw taskHelper.newBadArgs("err.orphan.argument",
581 toString(args.subList(1, args.size())))
582 .showUsage(true);
583 }
584
585 if (options.bindServices) {
586 log.println(taskHelper.getMessage("no.suggested.providers"));
587 return;
588 }
589
590 ModuleFinder finder = config.finder();
591 if (args.isEmpty()) {
592 // print providers used by the modules resolved without service binding
593 Configuration cf = config.resolve();
594 Set<ModuleReference> mrefs = cf.modules().stream()
595 .map(ResolvedModule::reference)
596 .collect(Collectors.toSet());
597
598 // print uses of the modules that would be linked into the image
599 mrefs.stream()
600 .sorted(Comparator.comparing(mref -> mref.descriptor().name()))
601 .forEach(mref -> {
602 ModuleDescriptor md = mref.descriptor();
603 log.format("module %s located (%s)%n", md.name(),
604 mref.location().get());
605 md.uses().stream().sorted()
606 .forEach(s -> log.format(" uses %s%n", s));
607 });
608
609 String msg = String.format("%n%s:", taskHelper.getMessage("suggested.providers.header"));
610 printProviders(log, msg, finder.findAll(), uses(mrefs));
611
612 } else {
613 // comma-separated service types, if specified
614 Set<String> names = Stream.of(args.get(0).split(","))
615 .collect(Collectors.toSet());
616 // find the modules that provide the specified service
617 Set<ModuleReference> mrefs = finder.findAll().stream()
618 .filter(mref -> mref.descriptor().provides().stream()
619 .map(ModuleDescriptor.Provides::service)
620 .anyMatch(names::contains))
621 .collect(Collectors.toSet());
622
623 // the specified services may or may not be in the modules that
624 // would be linked in. So find uses declared in all observable modules
625 Map<String, Set<String>> uses = uses(finder.findAll());
626
627 // check if any name given on the command line are unused service
628 mrefs.stream()
629 .flatMap(mref -> mref.descriptor().provides().stream()
630 .map(ModuleDescriptor.Provides::service))
631 .forEach(names::remove);
632 if (!names.isEmpty()) {
633 log.println(taskHelper.getMessage("warn.unused.services",
634 toString(names)));
635 }
636
637 String msg = String.format("%n%s:", taskHelper.getMessage("suggested.providers.header"));
638 printProviders(log, msg, mrefs, uses);
639 }
640 }
641
642 private static String toString(Collection<String> collection) {
643 return collection.stream().sorted()
644 .collect(Collectors.joining(","));
645 }
646
647 private String getSaveOpts() {
648 StringBuilder sb = new StringBuilder();
649 sb.append('#').append(new Date()).append("\n");
650 for (String c : optionsHelper.getInputCommand()) {
651 sb.append(c).append(" ");
652 }
653
654 return sb.toString();
655 }
656
657 private static String getBomHeader() {
658 StringBuilder sb = new StringBuilder();
659 sb.append("#").append(new Date()).append("\n");
660 sb.append("#Please DO NOT Modify this file").append("\n");
661 return sb.toString();
662 }
663
664 private String genBOMContent() throws IOException {
665 StringBuilder sb = new StringBuilder();
666 sb.append(getBomHeader());
|