| /* |
| * 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 |
| * particular file as subject to the "Classpath" exception as provided |
| * by Oracle in the LICENSE file that accompanied this code. |
| * |
| * This code is distributed in the hope that it will be useful, but WITHOUT |
| * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or |
| * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License |
| * version 2 for more details (a copy is included in the LICENSE file that |
| * accompanied this code). |
| * |
| * You should have received a copy of the GNU General Public License version |
| * 2 along with this work; if not, write to the Free Software Foundation, |
| * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. |
| * |
| * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA |
| * or visit www.oracle.com if you need additional information or have any |
| * questions. |
| */ |
| package jdk.tools.jlink.internal; |
| |
| import java.io.File; |
| import java.io.IOException; |
| import java.io.PrintWriter; |
| import java.io.UncheckedIOException; |
| import java.lang.module.Configuration; |
| import java.lang.module.FindException; |
| import java.lang.module.ModuleDescriptor; |
| import java.lang.module.ModuleFinder; |
| import java.lang.module.ModuleReference; |
| import java.lang.module.ResolutionException; |
| import java.lang.module.ResolvedModule; |
| import java.net.URI; |
| import java.nio.ByteOrder; |
| import java.nio.file.Files; |
| import java.nio.file.Path; |
| import java.nio.file.Paths; |
| import java.util.ArrayList; |
| import java.util.Arrays; |
| import java.util.Collection; |
| import java.util.Collections; |
| import java.util.Comparator; |
| import java.util.Date; |
| import java.util.HashMap; |
| import java.util.HashSet; |
| import java.util.List; |
| import java.util.Locale; |
| import java.util.Map; |
| import java.util.Objects; |
| import java.util.Optional; |
| import java.util.Set; |
| import java.util.stream.Collectors; |
| import java.util.stream.Stream; |
| |
| import jdk.tools.jlink.internal.TaskHelper.BadArgs; |
| import static jdk.tools.jlink.internal.TaskHelper.JLINK_BUNDLE; |
| import jdk.tools.jlink.internal.Jlink.JlinkConfiguration; |
| import jdk.tools.jlink.internal.Jlink.PluginsConfiguration; |
| import jdk.tools.jlink.internal.TaskHelper.Option; |
| import jdk.tools.jlink.internal.TaskHelper.OptionsHelper; |
| import jdk.tools.jlink.internal.ImagePluginStack.ImageProvider; |
| import jdk.tools.jlink.plugin.PluginException; |
| import jdk.tools.jlink.builder.DefaultImageBuilder; |
| import jdk.tools.jlink.plugin.Plugin; |
| import jdk.internal.module.ModulePath; |
| import jdk.internal.module.ModuleResolution; |
| |
| /** |
| * Implementation for the jlink tool. |
| * |
| * ## Should use jdk.joptsimple some day. |
| */ |
| public class JlinkTask { |
| static final boolean DEBUG = Boolean.getBoolean("jlink.debug"); |
| |
| // jlink API ignores by default. Remove when signing is implemented. |
| static final boolean IGNORE_SIGNING_DEFAULT = true; |
| |
| private static final TaskHelper taskHelper |
| = new TaskHelper(JLINK_BUNDLE); |
| |
| private static final Option<?>[] recognizedOptions = { |
| new Option<JlinkTask>(false, (task, opt, arg) -> { |
| task.options.help = true; |
| }, "--help", "-h"), |
| new Option<JlinkTask>(true, (task, opt, arg) -> { |
| // if used multiple times, the last one wins! |
| // So, clear previous values, if any. |
| task.options.modulePath.clear(); |
| String[] dirs = arg.split(File.pathSeparator); |
| int i = 0; |
| Arrays.stream(dirs) |
| .map(Paths::get) |
| .forEach(task.options.modulePath::add); |
| }, "--module-path", "-p"), |
| new Option<JlinkTask>(true, (task, opt, arg) -> { |
| // if used multiple times, the last one wins! |
| // So, clear previous values, if any. |
| task.options.limitMods.clear(); |
| for (String mn : arg.split(",")) { |
| if (mn.isEmpty()) { |
| throw taskHelper.newBadArgs("err.mods.must.be.specified", |
| "--limit-modules"); |
| } |
| task.options.limitMods.add(mn); |
| } |
| }, "--limit-modules"), |
| new Option<JlinkTask>(true, (task, opt, arg) -> { |
| for (String mn : arg.split(",")) { |
| if (mn.isEmpty()) { |
| throw taskHelper.newBadArgs("err.mods.must.be.specified", |
| "--add-modules"); |
| } |
| task.options.addMods.add(mn); |
| } |
| }, "--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); |
| } else { |
| String commandName = values[0]; |
| String moduleAndMain = values[1]; |
| int idx = moduleAndMain.indexOf("/"); |
| if (idx != -1) { |
| if (moduleAndMain.substring(0, idx).isEmpty()) { |
| throw taskHelper.newBadArgs("err.launcher.module.name.empty", arg); |
| } |
| |
| if (moduleAndMain.substring(idx + 1).isEmpty()) { |
| throw taskHelper.newBadArgs("err.launcher.main.class.empty", arg); |
| } |
| } |
| task.options.launchers.put(commandName, moduleAndMain); |
| } |
| }, "--launcher"), |
| new Option<JlinkTask>(true, (task, opt, arg) -> { |
| if ("little".equals(arg)) { |
| task.options.endian = ByteOrder.LITTLE_ENDIAN; |
| } else if ("big".equals(arg)) { |
| task.options.endian = ByteOrder.BIG_ENDIAN; |
| } 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)) { |
| throw taskHelper.newBadArgs("err.dir.exists", path); |
| } |
| task.options.packagedModulesPath = path; |
| }, true, "--keep-packaged-modules"), |
| new Option<JlinkTask>(true, (task, opt, arg) -> { |
| task.options.saveoptsfile = arg; |
| }, "--save-opts"), |
| new Option<JlinkTask>(false, (task, opt, arg) -> { |
| task.options.fullVersion = true; |
| }, true, "--full-version"), |
| new Option<JlinkTask>(false, (task, opt, arg) -> { |
| task.options.ignoreSigning = true; |
| }, "--ignore-signing-information"),}; |
| |
| private static final String PROGNAME = "jlink"; |
| private final OptionsValues options = new OptionsValues(); |
| |
| private static final OptionsHelper<JlinkTask> optionsHelper |
| = taskHelper.newOptionsHelper(JlinkTask.class, recognizedOptions); |
| private PrintWriter log; |
| |
| void setLog(PrintWriter out, PrintWriter err) { |
| log = out; |
| taskHelper.setLog(log); |
| } |
| |
| /** |
| * Result codes. |
| */ |
| static final int |
| EXIT_OK = 0, // Completed with no errors. |
| EXIT_ERROR = 1, // Completed but reported errors. |
| EXIT_CMDERR = 2, // Bad command-line arguments |
| EXIT_SYSERR = 3, // System error or resource exhaustion. |
| 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 { |
| 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()) { |
| optionsHelper.listPlugins(); |
| return EXIT_OK; |
| } |
| if (options.version || options.fullVersion) { |
| taskHelper.showVersion(options.fullVersion); |
| return EXIT_OK; |
| } |
| |
| 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()); |
| if (DEBUG) { |
| e.printStackTrace(log); |
| } |
| return EXIT_ERROR; |
| } catch (BadArgs e) { |
| taskHelper.reportError(e.key, e.args); |
| if (e.showUsage) { |
| log.println(taskHelper.getMessage("main.usage.summary", PROGNAME)); |
| } |
| if (DEBUG) { |
| e.printStackTrace(log); |
| } |
| return EXIT_CMDERR; |
| } catch (Throwable x) { |
| log.println(taskHelper.getMessage("error.prefix") + " " + x.getMessage()); |
| x.printStackTrace(log); |
| return EXIT_ABNORMAL; |
| } finally { |
| log.flush(); |
| } |
| } |
| |
| /* |
| * Jlink API entry point. |
| */ |
| public static void createImage(JlinkConfiguration config, |
| PluginsConfiguration plugins) |
| throws Exception { |
| Objects.requireNonNull(config); |
| Objects.requireNonNull(config.getOutput()); |
| plugins = plugins == null ? new PluginsConfiguration() : plugins; |
| |
| // First create the image provider |
| ImageProvider imageProvider = |
| createImageProvider(config, |
| null, |
| IGNORE_SIGNING_DEFAULT, |
| false, |
| false, |
| null); |
| |
| // Then create the Plugin Stack |
| ImagePluginStack stack = ImagePluginConfiguration.parseConfiguration(plugins); |
| |
| //Ask the stack to proceed; |
| stack.operate(imageProvider); |
| } |
| |
| /* |
| * Jlink API entry point. |
| */ |
| public static void postProcessImage(ExecutableImage image, List<Plugin> postProcessorPlugins) |
| throws Exception { |
| Objects.requireNonNull(image); |
| Objects.requireNonNull(postProcessorPlugins); |
| PluginsConfiguration config = new PluginsConfiguration(postProcessorPlugins); |
| ImagePluginStack stack = ImagePluginConfiguration. |
| parseConfiguration(config); |
| |
| stack.operate((ImagePluginStack stack1) -> image); |
| } |
| |
| private void postProcessOnly(Path existingImage) throws Exception { |
| PluginsConfiguration config = taskHelper.getPluginsConfig(null, null); |
| ExecutableImage img = DefaultImageBuilder.getExecutableImage(existingImage); |
| if (img == null) { |
| throw taskHelper.newBadArgs("err.existing.image.invalid"); |
| } |
| postProcessImage(img, config.getPlugins()); |
| } |
| |
| // the token for "all modules on the module path" |
| private static final String ALL_MODULE_PATH = "ALL-MODULE-PATH"; |
| private JlinkConfiguration initJlinkConfig() throws BadArgs { |
| Set<String> roots = new HashSet<>(); |
| for (String mod : options.addMods) { |
| if (mod.equals(ALL_MODULE_PATH)) { |
| 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); |
| } |
| } |
| |
| return new JlinkConfiguration(options.output, |
| options.modulePath, |
| roots, |
| options.limitMods, |
| options.endian); |
| } |
| |
| private void createImage(JlinkConfiguration config) throws Exception { |
| if (options.output == null) { |
| throw taskHelper.newBadArgs("err.output.must.be.specified").showUsage(true); |
| } |
| if (options.addMods.isEmpty()) { |
| throw taskHelper.newBadArgs("err.mods.must.be.specified", "--add-modules") |
| .showUsage(true); |
| } |
| |
| // First create the image provider |
| 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)); |
| |
| //Ask the stack to proceed |
| stack.operate(imageProvider); |
| } |
| |
| /* |
| * 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. |
| */ |
| public static ModuleFinder newModuleFinder(List<Path> paths, |
| Set<String> limitMods, |
| Set<String> roots) |
| { |
| Path[] entries = paths.toArray(new Path[0]); |
| ModuleFinder finder = ModulePath.of(Runtime.version(), true, entries); |
| |
| // if limitmods is specified then limit the universe |
| if (!limitMods.isEmpty()) { |
| finder = limitFinder(finder, limitMods, roots); |
| } |
| return finder; |
| } |
| |
| private static Path toPathLocation(ResolvedModule m) { |
| Optional<URI> ouri = m.reference().location(); |
| if (!ouri.isPresent()) |
| throw new InternalError(m + " does not have a location"); |
| URI uri = ouri.get(); |
| return Paths.get(uri); |
| } |
| |
| |
| private static ImageProvider createImageProvider(JlinkConfiguration config, |
| Path retainModulesPath, |
| boolean ignoreSigning, |
| boolean bindService, |
| boolean verbose, |
| PrintWriter log) |
| throws IOException |
| { |
| Configuration cf = bindService ? config.resolveAndBind() |
| : config.resolve(); |
| |
| if (verbose && log != null) { |
| // print modules to be linked in |
| cf.modules().stream() |
| .sorted(Comparator.comparing(ResolvedModule::name)) |
| .forEach(rm -> log.format("%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() |
| .map(ResolvedModule::reference) |
| .filter(ModuleResolution::hasIncubatingWarning) |
| .map(ModuleReference::descriptor) |
| .map(ModuleDescriptor::name) |
| .collect(Collectors.joining(", ")); |
| |
| if (!"".equals(im)) |
| 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, config.getByteOrder(), retainModulesPath, ignoreSigning); |
| } |
| |
| /* |
| * Returns a ModuleFinder that limits observability to the given root |
| * modules, their transitive dependences, plus a set of other modules. |
| */ |
| public static ModuleFinder limitFinder(ModuleFinder finder, |
| Set<String> roots, |
| Set<String> otherMods) { |
| |
| // resolve all root modules |
| Configuration cf = Configuration.empty() |
| .resolve(finder, |
| ModuleFinder.of(), |
| roots); |
| |
| // module name -> reference |
| Map<String, ModuleReference> map = new HashMap<>(); |
| cf.modules().forEach(m -> { |
| ModuleReference mref = m.reference(); |
| map.put(mref.descriptor().name(), mref); |
| }); |
| |
| // add the other modules |
| otherMods.stream() |
| .map(finder::find) |
| .flatMap(Optional::stream) |
| .forEach(mref -> map.putIfAbsent(mref.descriptor().name(), mref)); |
| |
| // set of modules that are observable |
| Set<ModuleReference> mrefs = new HashSet<>(map.values()); |
| |
| return new ModuleFinder() { |
| @Override |
| public Optional<ModuleReference> find(String name) { |
| return Optional.ofNullable(map.get(name)); |
| } |
| |
| @Override |
| public Set<ModuleReference> findAll() { |
| return mrefs; |
| } |
| }; |
| } |
| |
| /* |
| * Returns a map of each service type to the modules that use it |
| * It will include services that are provided by a module but may not used |
| * by any of the observable modules. |
| */ |
| private static Map<String, Set<String>> uses(Set<ModuleReference> modules) { |
| // collects the services used by the modules and print uses |
| Map<String, Set<String>> services = new HashMap<>(); |
| modules.stream() |
| .map(ModuleReference::descriptor) |
| .forEach(md -> { |
| // include services that may not be used by any observable modules |
| md.provides().forEach(p -> |
| services.computeIfAbsent(p.service(), _k -> new HashSet<>())); |
| md.uses().forEach(s -> services.computeIfAbsent(s, _k -> new HashSet<>()) |
| .add(md.name())); |
| }); |
| return services; |
| } |
| |
| 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 specified services. |
| * |
| * The specified services maps a service type name to the modules |
| * using the service type which may be empty if no observable module uses |
| * that service. |
| */ |
| private static void printProviders(PrintWriter log, |
| String header, |
| Set<ModuleReference> modules, |
| Map<String, Set<String>> serviceToUses) { |
| 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 -> serviceToUses.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 -> { |
| String usedBy; |
| if (serviceToUses.get(p.service()).isEmpty()) { |
| usedBy = "not used by any observable module"; |
| } else { |
| usedBy = serviceToUses.get(p.service()).stream() |
| .sorted() |
| .collect(Collectors.joining(",", "used by ", "")); |
| } |
| log.format(" %s provides %s %s%n", |
| md.name(), p.service(), usedBy); |
| }) |
| ); |
| }); |
| } |
| |
| 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 observable modules without service binding |
| Set<ModuleReference> mrefs = finder.findAll(); |
| // 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("%s %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, mrefs, 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()); |
| |
| // find the modules that uses the specified services |
| Map<String, Set<String>> uses = new HashMap<>(); |
| names.forEach(s -> uses.computeIfAbsent(s, _k -> new HashSet<>())); |
| finder.findAll().stream() |
| .map(ModuleReference::descriptor) |
| .forEach(md -> md.uses().stream() |
| .filter(names::contains) |
| .forEach(s -> uses.get(s).add(md.name()))); |
| |
| // check if any name given on the command line are not provided by any module |
| mrefs.stream() |
| .flatMap(mref -> mref.descriptor().provides().stream() |
| .map(ModuleDescriptor.Provides::service)) |
| .forEach(names::remove); |
| if (!names.isEmpty()) { |
| log.println(taskHelper.getMessage("warn.provider.notfound", |
| 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(" "); |
| } |
| |
| return sb.toString(); |
| } |
| |
| private static String getBomHeader() { |
| StringBuilder sb = new StringBuilder(); |
| sb.append("#").append(new Date()).append("\n"); |
| sb.append("#Please DO NOT Modify this file").append("\n"); |
| return sb.toString(); |
| } |
| |
| private String genBOMContent() throws IOException { |
| StringBuilder sb = new StringBuilder(); |
| sb.append(getBomHeader()); |
| StringBuilder command = new StringBuilder(); |
| for (String c : optionsHelper.getInputCommand()) { |
| command.append(c).append(" "); |
| } |
| sb.append("command").append(" = ").append(command); |
| sb.append("\n"); |
| |
| return sb.toString(); |
| } |
| |
| private static String genBOMContent(JlinkConfiguration config, |
| PluginsConfiguration plugins) |
| throws IOException { |
| StringBuilder sb = new StringBuilder(); |
| sb.append(getBomHeader()); |
| sb.append(config); |
| sb.append(plugins); |
| return sb.toString(); |
| } |
| |
| private static class ImageHelper implements ImageProvider { |
| final ByteOrder order; |
| final Path packagedModulesPath; |
| final boolean ignoreSigning; |
| final Set<Archive> archives; |
| |
| ImageHelper(Configuration cf, |
| Map<String, Path> modsPaths, |
| ByteOrder order, |
| Path packagedModulesPath, |
| boolean ignoreSigning) throws IOException { |
| this.order = order; |
| this.packagedModulesPath = packagedModulesPath; |
| this.ignoreSigning = ignoreSigning; |
| this.archives = modsPaths.entrySet().stream() |
| .map(e -> newArchive(e.getKey(), e.getValue())) |
| .collect(Collectors.toSet()); |
| } |
| |
| private Archive newArchive(String module, Path path) { |
| if (path.toString().endsWith(".jmod")) { |
| return new JmodArchive(module, path); |
| } else if (path.toString().endsWith(".jar")) { |
| ModularJarArchive modularJarArchive = new ModularJarArchive(module, path); |
| |
| Stream<Archive.Entry> signatures = modularJarArchive.entries().filter((entry) -> { |
| String name = entry.name().toUpperCase(Locale.ENGLISH); |
| |
| return name.startsWith("META-INF/") && name.indexOf('/', 9) == -1 && ( |
| name.endsWith(".SF") || |
| name.endsWith(".DSA") || |
| name.endsWith(".RSA") || |
| name.endsWith(".EC") || |
| name.startsWith("META-INF/SIG-") |
| ); |
| }); |
| |
| if (signatures.count() != 0) { |
| if (ignoreSigning) { |
| System.err.println(taskHelper.getMessage("warn.signing", path)); |
| } else { |
| throw new IllegalArgumentException(taskHelper.getMessage("err.signing", path)); |
| } |
| } |
| |
| return modularJarArchive; |
| } else if (Files.isDirectory(path)) { |
| return new DirArchive(path); |
| } else { |
| throw new IllegalArgumentException( |
| taskHelper.getMessage("err.not.modular.format", module, path)); |
| } |
| } |
| |
| @Override |
| public ExecutableImage retrieve(ImagePluginStack stack) throws IOException { |
| ExecutableImage image = ImageFileCreator.create(archives, order, stack); |
| if (packagedModulesPath != null) { |
| // copy the packaged modules to the given path |
| Files.createDirectories(packagedModulesPath); |
| for (Archive a : archives) { |
| Path file = a.getPath(); |
| Path dest = packagedModulesPath.resolve(file.getFileName()); |
| Files.copy(file, dest); |
| } |
| } |
| return image; |
| } |
| } |
| } |