< prev index next >
src/jdk.jlink/share/classes/jdk/tools/jlink/internal/JlinkTask.java
Print this page
@@ -1,7 +1,7 @@
/*
- * Copyright (c) 2015, 2016, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2015, 2017, 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
@@ -108,10 +108,16 @@
}, "--add-modules"),
new Option<JlinkTask>(true, (task, opt, arg) -> {
Path path = Paths.get(arg);
task.options.output = path;
}, "--output"),
+ new Option<JlinkTask>(false, (task, opt, arg) -> {
+ task.options.bindServices = true;
+ }, "--bind-services"),
+ new Option<JlinkTask>(false, (task, opt, arg) -> {
+ task.options.suggestProviders = true;
+ }, "--suggest-providers", "", true),
new Option<JlinkTask>(true, (task, opt, arg) -> {
String[] values = arg.split("=");
// check values
if (values.length != 2 || values[0].isEmpty() || values[1].isEmpty()) {
throw taskHelper.newBadArgs("err.launcher.value.format", arg);
@@ -139,10 +145,13 @@
} else {
throw taskHelper.newBadArgs("err.unknown.byte.order", arg);
}
}, "--endian"),
new Option<JlinkTask>(false, (task, opt, arg) -> {
+ task.options.verbose = true;
+ }, "--verbose", "-v"),
+ new Option<JlinkTask>(false, (task, opt, arg) -> {
task.options.version = true;
}, "--version"),
new Option<JlinkTask>(true, (task, opt, arg) -> {
Path path = Paths.get(arg);
if (Files.exists(path)) {
@@ -183,29 +192,36 @@
EXIT_ABNORMAL = 4;// terminated abnormally
static class OptionsValues {
boolean help;
String saveoptsfile;
+ boolean verbose;
boolean version;
boolean fullVersion;
final List<Path> modulePath = new ArrayList<>();
final Set<String> limitMods = new HashSet<>();
final Set<String> addMods = new HashSet<>();
Path output;
final Map<String, String> launchers = new HashMap<>();
Path packagedModulesPath;
ByteOrder endian = ByteOrder.nativeOrder();
boolean ignoreSigning = false;
+ boolean bindServices = false;
+ boolean suggestProviders = false;
}
int run(String[] args) {
if (log == null) {
setLog(new PrintWriter(System.out, true),
new PrintWriter(System.err, true));
}
try {
- optionsHelper.handleOptionsNoUnhandled(this, args);
+ List<String> remaining = optionsHelper.handleOptions(this, args);
+ if (remaining.size() > 0 && !options.suggestProviders) {
+ throw taskHelper.newBadArgs("err.orphan.arguments", toString(remaining))
+ .showUsage(true);
+ }
if (options.help) {
optionsHelper.showHelp(PROGNAME);
return EXIT_OK;
}
if (optionsHelper.shouldListPlugins()) {
@@ -215,22 +231,29 @@
if (options.version || options.fullVersion) {
taskHelper.showVersion(options.fullVersion);
return EXIT_OK;
}
- if (taskHelper.getExistingImage() == null) {
- if (options.modulePath.isEmpty()) {
- throw taskHelper.newBadArgs("err.modulepath.must.be.specified").showUsage(true);
- }
- createImage();
- } else {
+ if (taskHelper.getExistingImage() != null) {
postProcessOnly(taskHelper.getExistingImage());
+ return EXIT_OK;
+ }
+
+ if (options.modulePath.isEmpty()) {
+ throw taskHelper.newBadArgs("err.modulepath.must.be.specified")
+ .showUsage(true);
}
+ JlinkConfiguration config = initJlinkConfig();
+ if (options.suggestProviders) {
+ suggestProviders(config, remaining);
+ } else {
+ createImage(config);
if (options.saveoptsfile != null) {
Files.write(Paths.get(options.saveoptsfile), getSaveOpts().getBytes());
}
+ }
return EXIT_OK;
} catch (PluginException | IllegalArgumentException |
UncheckedIOException |IOException | FindException | ResolutionException e) {
log.println(taskHelper.getMessage("error.prefix") + " " + e.getMessage());
@@ -264,29 +287,17 @@
throws Exception {
Objects.requireNonNull(config);
Objects.requireNonNull(config.getOutput());
plugins = plugins == null ? new PluginsConfiguration() : plugins;
- if (config.getModulepaths().isEmpty()) {
- throw new IllegalArgumentException("Empty module paths");
- }
-
- ModuleFinder finder = newModuleFinder(config.getModulepaths(),
- config.getLimitmods(),
- config.getModules());
-
- if (config.getModules().isEmpty()) {
- throw new IllegalArgumentException("No modules to add");
- }
-
// First create the image provider
ImageProvider imageProvider =
- createImageProvider(finder,
- config.getModules(),
- config.getByteOrder(),
+ createImageProvider(config,
null,
IGNORE_SIGNING_DEFAULT,
+ false,
+ false,
null);
// Then create the Plugin Stack
ImagePluginStack stack = ImagePluginConfiguration.parseConfiguration(plugins);
@@ -317,68 +328,66 @@
postProcessImage(img, config.getPlugins());
}
// the token for "all modules on the module path"
private static final String ALL_MODULE_PATH = "ALL-MODULE-PATH";
- private void createImage() throws Exception {
- if (options.output == null) {
- throw taskHelper.newBadArgs("err.output.must.be.specified").showUsage(true);
- }
-
+ private JlinkConfiguration initJlinkConfig() throws BadArgs {
if (options.addMods.isEmpty()) {
throw taskHelper.newBadArgs("err.mods.must.be.specified", "--add-modules")
.showUsage(true);
}
Set<String> roots = new HashSet<>();
for (String mod : options.addMods) {
if (mod.equals(ALL_MODULE_PATH)) {
- ModuleFinder finder = modulePathFinder();
+ Path[] entries = options.modulePath.toArray(new Path[0]);
+ ModuleFinder finder = ModulePath.of(Runtime.version(), true, entries);
+ if (!options.limitMods.isEmpty()) {
+ // finder for the observable modules specified in
+ // the --module-path and --limit-modules options
+ finder = limitFinder(finder, options.limitMods, Collections.emptySet());
+ }
+
+ // all observable modules are roots
finder.findAll()
.stream()
.map(ModuleReference::descriptor)
.map(ModuleDescriptor::name)
.forEach(mn -> roots.add(mn));
} else {
roots.add(mod);
}
}
- ModuleFinder finder = newModuleFinder(options.modulePath,
+ return new JlinkConfiguration(options.output,
+ options.modulePath,
+ roots,
options.limitMods,
- roots);
+ options.endian);
+ }
+ private void createImage(JlinkConfiguration config) throws Exception {
+ if (options.output == null) {
+ throw taskHelper.newBadArgs("err.output.must.be.specified").showUsage(true);
+ }
// First create the image provider
- ImageProvider imageProvider = createImageProvider(finder,
- roots,
- options.endian,
+ ImageProvider imageProvider = createImageProvider(config,
options.packagedModulesPath,
options.ignoreSigning,
+ options.bindServices,
+ options.verbose,
log);
// Then create the Plugin Stack
- ImagePluginStack stack = ImagePluginConfiguration.
- parseConfiguration(taskHelper.getPluginsConfig(options.output, options.launchers));
+ ImagePluginStack stack = ImagePluginConfiguration.parseConfiguration(
+ taskHelper.getPluginsConfig(options.output, options.launchers));
//Ask the stack to proceed
stack.operate(imageProvider);
}
- /**
- * Returns a module finder to find the observable modules specified in
- * the --module-path and --limit-modules options
- */
- private ModuleFinder modulePathFinder() {
- Path[] entries = options.modulePath.toArray(new Path[0]);
- ModuleFinder finder = ModulePath.of(Runtime.version(), true, entries);
- if (!options.limitMods.isEmpty()) {
- finder = limitFinder(finder, options.limitMods, Collections.emptySet());
- }
- return finder;
- }
-
/*
* Returns a module finder of the given module path that limits
* the observable modules to those in the transitive closure of
* the modules specified in {@code limitMods} plus other modules
* specified in the {@code roots} set.
@@ -403,26 +412,36 @@
throw new InternalError(m + " does not have a location");
URI uri = ouri.get();
return Paths.get(uri);
}
- private static ImageProvider createImageProvider(ModuleFinder finder,
- Set<String> roots,
- ByteOrder order,
+
+ private static ImageProvider createImageProvider(JlinkConfiguration config,
Path retainModulesPath,
boolean ignoreSigning,
+ boolean bindService,
+ boolean verbose,
PrintWriter log)
throws IOException
{
- if (roots.isEmpty()) {
- throw new IllegalArgumentException("empty modules and limitmods");
- }
+ Configuration cf = bindService ? config.resolveAndBind()
+ : config.resolve();
- Configuration cf = Configuration.empty()
- .resolve(finder,
- ModuleFinder.of(),
- roots);
+ if (verbose && log != null) {
+ // print modules to be linked in
+ cf.modules().stream()
+ .sorted(Comparator.comparing(ResolvedModule::name))
+ .forEach(rm -> log.format("module %s (%s)%n",
+ rm.name(), rm.reference().location().get()));
+
+ // print provider info
+ Set<ModuleReference> references = cf.modules().stream()
+ .map(ResolvedModule::reference).collect(Collectors.toSet());
+
+ String msg = String.format("%n%s:", taskHelper.getMessage("providers.header"));
+ printProviders(log, msg, references);
+ }
// emit a warning for any incubating modules in the configuration
if (log != null) {
String im = cf.modules()
.stream()
@@ -436,18 +455,18 @@
log.println("WARNING: Using incubator modules: " + im);
}
Map<String, Path> mods = cf.modules().stream()
.collect(Collectors.toMap(ResolvedModule::name, JlinkTask::toPathLocation));
- return new ImageHelper(cf, mods, order, retainModulesPath, ignoreSigning);
+ return new ImageHelper(cf, mods, config.getByteOrder(), retainModulesPath, ignoreSigning);
}
/*
* Returns a ModuleFinder that limits observability to the given root
* modules, their transitive dependences, plus a set of other modules.
*/
- private static ModuleFinder limitFinder(ModuleFinder finder,
+ public static ModuleFinder limitFinder(ModuleFinder finder,
Set<String> roots,
Set<String> otherMods) {
// resolve all root modules
Configuration cf = Configuration.empty()
@@ -482,10 +501,151 @@
return mrefs;
}
};
}
+ /*
+ * Returns a map of each service type to the modules that use it
+ */
+ private static Map<String, Set<String>> uses(Set<ModuleReference> modules) {
+ // collects the services used by the modules and print uses
+ Map<String, Set<String>> uses = new HashMap<>();
+ modules.stream()
+ .map(ModuleReference::descriptor)
+ .forEach(md -> md.uses().forEach(s ->
+ uses.computeIfAbsent(s, _k -> new HashSet<>()).add(md.name()))
+ );
+ return uses;
+ }
+
+ private static void printProviders(PrintWriter log,
+ String header,
+ Set<ModuleReference> modules) {
+ printProviders(log, header, modules, uses(modules));
+ }
+
+ /*
+ * Prints the providers that are used by the services specified in
+ * the given modules.
+ *
+ * The specified uses maps a service type name to the modules
+ * using the service type and that may or may not be present
+ * the given modules.
+ */
+ private static void printProviders(PrintWriter log,
+ String header,
+ Set<ModuleReference> modules,
+ Map<String, Set<String>> uses) {
+ if (modules.isEmpty())
+ return;
+
+ // Build a map of a service type to the provider modules
+ Map<String, Set<ModuleDescriptor>> providers = new HashMap<>();
+ modules.stream()
+ .map(ModuleReference::descriptor)
+ .forEach(md -> {
+ md.provides().stream()
+ .filter(p -> uses.containsKey(p.service()))
+ .forEach(p -> providers.computeIfAbsent(p.service(), _k -> new HashSet<>())
+ .add(md));
+ });
+
+ if (!providers.isEmpty()) {
+ log.println(header);
+ }
+
+ // print the providers of the service types used by the specified modules
+ // sorted by the service type name and then provider's module name
+ providers.entrySet().stream()
+ .sorted(Map.Entry.comparingByKey())
+ .forEach(e -> {
+ String service = e.getKey();
+ e.getValue().stream()
+ .sorted(Comparator.comparing(ModuleDescriptor::name))
+ .forEach(md ->
+ md.provides().stream()
+ .filter(p -> p.service().equals(service))
+ .forEach(p -> log.format(" module %s provides %s, used by %s%n",
+ md.name(), p.service(),
+ uses.get(p.service()).stream()
+ .sorted()
+ .collect(Collectors.joining(","))))
+ );
+ });
+ }
+
+ private void suggestProviders(JlinkConfiguration config, List<String> args)
+ throws BadArgs
+ {
+ if (args.size() > 1) {
+ throw taskHelper.newBadArgs("err.orphan.argument",
+ toString(args.subList(1, args.size())))
+ .showUsage(true);
+ }
+
+ if (options.bindServices) {
+ log.println(taskHelper.getMessage("no.suggested.providers"));
+ return;
+ }
+
+ ModuleFinder finder = config.finder();
+ if (args.isEmpty()) {
+ // print providers used by the modules resolved without service binding
+ Configuration cf = config.resolve();
+ Set<ModuleReference> mrefs = cf.modules().stream()
+ .map(ResolvedModule::reference)
+ .collect(Collectors.toSet());
+
+ // print uses of the modules that would be linked into the image
+ mrefs.stream()
+ .sorted(Comparator.comparing(mref -> mref.descriptor().name()))
+ .forEach(mref -> {
+ ModuleDescriptor md = mref.descriptor();
+ log.format("module %s located (%s)%n", md.name(),
+ mref.location().get());
+ md.uses().stream().sorted()
+ .forEach(s -> log.format(" uses %s%n", s));
+ });
+
+ String msg = String.format("%n%s:", taskHelper.getMessage("suggested.providers.header"));
+ printProviders(log, msg, finder.findAll(), uses(mrefs));
+
+ } else {
+ // comma-separated service types, if specified
+ Set<String> names = Stream.of(args.get(0).split(","))
+ .collect(Collectors.toSet());
+ // find the modules that provide the specified service
+ Set<ModuleReference> mrefs = finder.findAll().stream()
+ .filter(mref -> mref.descriptor().provides().stream()
+ .map(ModuleDescriptor.Provides::service)
+ .anyMatch(names::contains))
+ .collect(Collectors.toSet());
+
+ // the specified services may or may not be in the modules that
+ // would be linked in. So find uses declared in all observable modules
+ Map<String, Set<String>> uses = uses(finder.findAll());
+
+ // check if any name given on the command line are unused service
+ mrefs.stream()
+ .flatMap(mref -> mref.descriptor().provides().stream()
+ .map(ModuleDescriptor.Provides::service))
+ .forEach(names::remove);
+ if (!names.isEmpty()) {
+ log.println(taskHelper.getMessage("warn.unused.services",
+ toString(names)));
+ }
+
+ String msg = String.format("%n%s:", taskHelper.getMessage("suggested.providers.header"));
+ printProviders(log, msg, mrefs, uses);
+ }
+ }
+
+ private static String toString(Collection<String> collection) {
+ return collection.stream().sorted()
+ .collect(Collectors.joining(","));
+ }
+
private String getSaveOpts() {
StringBuilder sb = new StringBuilder();
sb.append('#').append(new Date()).append("\n");
for (String c : optionsHelper.getInputCommand()) {
sb.append(c).append(" ");
< prev index next >