2
0
Fork 0
mirror of https://github.com/ethauvin/bld.git synced 2025-04-25 16:27:11 -07:00

Merge pull request #45 from ethauvin/main

implementations of JpackageOperation, JmodOperation and JlinkOperation
This commit is contained in:
Geert Bevin 2024-08-23 15:36:30 -04:00 committed by GitHub
commit c70b4f1c43
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
30 changed files with 2784 additions and 0 deletions

View file

@ -0,0 +1,218 @@
/*
* Copyright 2024 Erik C. Thauvin (https://erik.thauvin.net/)
* Licensed under the Apache License, Version 2.0 (the "License")
*/
package rife.bld.operations;
import rife.bld.operations.exceptions.ExitStatusException;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.Reader;
import java.nio.charset.Charset;
import java.nio.file.Files;
import java.nio.file.Paths;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.spi.ToolProvider;
/**
* Provides common features for tool providers.
*
* @author <a href="https://erik.thauvin.net/">Erik C. Thauvin</a>
* @since 2.0.2
*/
public abstract class AbstractToolProviderOperation<T extends AbstractToolProviderOperation<T>>
extends AbstractOperation<AbstractToolProviderOperation<T>> {
private final List<String> toolArgs_ = new ArrayList<>();
private final String toolName_;
/**
* Provides the name of the tool.
*
* @param toolName the tool name
*/
public AbstractToolProviderOperation(String toolName) {
toolName_ = toolName;
}
/**
* Runs an instance of the tool.
* <p>
* On success, command line arguments are automatically cleared.
*
* @throws Exception if an error occurred
*/
@Override
public void execute() throws Exception {
if (toolArgs_.isEmpty()) {
System.err.println("No " + toolName_ + " command line arguments specified.");
throw new ExitStatusException(ExitStatusException.EXIT_FAILURE);
}
var tool = ToolProvider.findFirst(toolName_).orElseThrow(() ->
new IllegalStateException("No " + toolName_ + " tool found."));
var status = tool.run(System.out, System.err, toolArgs_.toArray(new String[0]));
if (status != 0) {
System.out.println(tool.name() + ' ' + String.join(" ", toolArgs_));
}
ExitStatusException.throwOnFailure(status);
toolArgs_.clear();
}
/**
* Adds arguments to pass to the tool.
*
* @param arg one or more argument
* @return this operation
*/
@SuppressWarnings("unchecked")
public T toolArgs(String... arg) {
toolArgs(List.of(arg));
return (T) this;
}
/**
* Adds arguments to pass to the tool.
*
* @param args the argument to add
* @return this operation
*/
@SuppressWarnings({"unchecked", "UnusedReturnValue"})
public T toolArgs(List<String> args) {
toolArgs_.addAll(args);
return (T) this;
}
/**
* Returns the tool's arguments.
*
* @return the arguments
*/
public List<String> toolArgs() {
return toolArgs_;
}
/**
* Parses arguments to pass to the tool from the given files.
*
* @param files the list of files
* @return this operation instance
* @throws FileNotFoundException if a file cannot be found
*/
@SuppressWarnings({"unchecked", "UnusedReturnValue"})
public T toolArgsFromFile(List<String> files) throws IOException {
var args = new ArrayList<String>();
for (var file : files) {
try (var reader = Files.newBufferedReader(Paths.get(file), Charset.defaultCharset())) {
var tokenizer = new CommandLineTokenizer(reader);
String token;
while ((token = tokenizer.nextToken()) != null) {
args.add(token);
}
}
}
toolArgs(args);
return (T) this;
}
/**
* Adds arguments to pass to the tool.
*
* @param args the argument-value pairs to add
* @return this operation
*/
@SuppressWarnings({"unchecked", "UnusedReturnValue"})
protected T toolArgs(Map<String, String> args) {
args.forEach((k, v) -> {
toolArgs_.add(k);
if (v != null && !v.isEmpty()) {
toolArgs_.add(v);
}
});
return (T) this;
}
/**
* Tokenize command line arguments.
*
* <ul>
* <li>Arguments containing spaces should be quoted</li>
* <li>Escape sequences and comments are supported</li>
* </ul>
*/
public static class CommandLineTokenizer {
private final StringBuilder buf_ = new StringBuilder();
private final Reader input_;
private int ch_;
public CommandLineTokenizer(Reader input) throws IOException {
input_ = input;
ch_ = input.read();
}
public String nextToken() throws IOException {
trimWhitespaceOrComments();
if (ch_ == -1) {
return null;
}
buf_.setLength(0); // reset buffer
char quote = 0;
while (ch_ != -1) {
if (ch_ == '\'' || ch_ == '"') { // quotes
if (quote == 0) { // begin quote
quote = (char) ch_;
} else if (quote == ch_) { // end quote
quote = 0;
} else {
buf_.append((char) ch_);
}
} else if (ch_ == '\\') { // escaped
ch_ = input_.read();
buf_.append(handleEscapeSequence());
} else if (quote == 0 && Character.isWhitespace(ch_)) { // whitespaces
break;
} else {
buf_.append((char) ch_);
}
ch_ = input_.read();
}
return buf_.toString();
}
private char handleEscapeSequence() {
return switch (ch_) {
case -1 -> '\\';
case 'n' -> '\n';
case 'r' -> '\r';
case 't' -> '\t';
case 'f' -> '\f';
default -> (char) ch_;
};
}
private void trimWhitespaceOrComments() throws IOException {
while (ch_ != -1) {
if (Character.isWhitespace(ch_)) { // Skip whitespaces
ch_ = input_.read();
} else if (ch_ == '#') {
// Skip the entire comment until a new line or end of input
do {
ch_ = input_.read();
} while (ch_ != -1 && ch_ != '\n' && ch_ != '\r');
} else {
break;
}
}
}
}
}

View file

@ -0,0 +1,89 @@
/*
* Copyright 2024 Erik C. Thauvin (https://erik.thauvin.net/)
* Licensed under the Apache License, Version 2.0 (the "License")
*/
package rife.bld.operations;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
/**
* Create run-time images using the jlink tool.
*
* @author <a href="https://erik.thauvin.net/">Erik C. Thauvin</a>
* @since 2.0.2
*/
public class JlinkOperation extends AbstractToolProviderOperation<JlinkOperation> {
private final List<String> cmdFiles_ = new ArrayList<>();
private final List<String> disabledPlugins_ = new ArrayList<>();
private final JlinkOptions jlinkOptions_ = new JlinkOptions();
public JlinkOperation() {
super("jlink");
}
/**
* Read options and/or mode from file(s).
*
* @param file one or more file
* @return this operation instance
*/
public JlinkOperation cmdFiles(String... file) {
cmdFiles_.addAll(List.of(file));
return this;
}
/**
* Retrieves the list of files containing options or mode.
*
* @return the list of files
*/
public List<String> cmdFiles() {
return cmdFiles_;
}
/**
* Disable the plugin mentioned.
*
* @param plugin the plugin name
* @return this map of options
*/
public JlinkOperation disablePlugin(String... plugin) {
disabledPlugins_.addAll(List.of(plugin));
return this;
}
@Override
public void execute() throws Exception {
toolArgsFromFile(cmdFiles_);
disabledPlugins_.forEach(plugin -> toolArgs("--disable-plugin", plugin));
toolArgs(jlinkOptions_);
super.execute();
}
/**
* Provides a list of options to provide to the jlink tool.
* <p>
* A copy will be created to allow this list to be independently modifiable.
*
* @param options the argument-value pairs
* @return this operation instance
*/
public JlinkOperation jlinkOptions(Map<String, String> options) {
jlinkOptions_.putAll(options);
return this;
}
/**
* Retrieves the list of options for the jlink tool.
* <p>
* This is a modifiable list that can be retrieved and changed.
*
* @return the map of jlink options
*/
public JlinkOptions jlinkOptions() {
return jlinkOptions_;
}
}

View file

@ -0,0 +1,329 @@
/*
* Copyright 2024 Erik C. Thauvin (https://erik.thauvin.net/)
* Licensed under the Apache License, Version 2.0 (the "License")
*/
package rife.bld.operations;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
/**
* Options for jlink tool.
*
* @author <a href="https://erik.thauvin.net/">Erik C. Thauvin</a>
* @since 2.0.2
*/
public class JlinkOptions extends HashMap<String, String> {
/**
* All Modules Path.
*/
@SuppressWarnings("unused")
public final static String ALL_MODULE_PATH = "ALL-MODULE-PATH";
/**
* Root modules to resolve in addition to the initial modules.
* <p>
* Module can also be {@link #ALL_MODULE_PATH}
*
* @param modules one or more module
* @return this map of options
*/
public JlinkOptions addModules(String... modules) {
put("--add-modules", String.join(",", modules));
return this;
}
/**
* Link in service provider modules and their dependencies.
*
* @param bindServices {@code true} to bind services, {@code false} otherwise
* @return this map of options
*/
public JlinkOptions bindServices(boolean bindServices) {
if (bindServices) {
put("--bind-services");
} else {
remove("--bind-services");
}
return this;
}
/**
* Compression to use in compressing resources.
* <p>
* <b>Requires Java 21 or higher</b>. Use {@link #compress(CompressionLevel)} for lower versions.
* <p>
* Where {@link ZipCompression#ZIP_0 ZIP_0} provides no compression and {@link ZipCompression#ZIP_9 ZIP_9} provides
* the best compression.
* <p>Default is {@link ZipCompression#ZIP_6 ZIP_6}
*
* @param compression the {@link ZipCompression compression} level
* @return this map of options
* @see #compress(ZipCompression)
*/
public JlinkOptions compress(ZipCompression compression) {
put("--compress", compression.level);
return this;
}
/**
* Enable compression of resources.
* <p>
* Use {@link #compress(ZipCompression)} on Java 21 or higher.
*
* @param compression the {@link CompressionLevel compression} level
* @return this map of options
* @see #compress(CompressionLevel)
*/
public JlinkOptions compress(CompressionLevel compression) {
put("--compress", compression.level);
return this;
}
/**
* Byte order of generated jimage.
* <p>
* Default: native
*
* @param endian the byte order
* @return this map of options
*/
public JlinkOptions endian(Endian endian) {
put("--endian", endian.byteOrder);
return this;
}
/**
* Suppress a fatal error when signed modular JARs are linked in the image.
*
* @param ignoreSigningInformation {@code true} to ignore signing information, {@code false} otherwise
* @return this map of options
*/
public JlinkOptions ignoreSigningInformation(boolean ignoreSigningInformation) {
if (ignoreSigningInformation) {
put("--ignore-signing-information");
} else {
remove("--ignore-signing-information");
}
return this;
}
/**
* Add a launcher command of the given name for the module.
*
* @param name the name
* @param module the module
* @return this map of options
*/
@SuppressWarnings("UnusedReturnValue")
public JlinkOptions launcher(String name, String module) {
put("--launcher", name + "=" + module);
return this;
}
/**
* Add a launcher command of the given name for the module and the main class.
*
* @param name the name
* @param module the module
* @param mainClass the main class
* @return this map of options
*/
public JlinkOptions launcher(String name, String module, String mainClass) {
put("--launcher", name + "=" + module + "/" + mainClass);
return this;
}
/**
* Limit the universe of observable modules.
*
* @param module one or more module
* @return this map of options
*/
public JlinkOptions limitModule(String... module) {
put("--limit-modules", String.join(",", module));
return this;
}
/**
* Module path.
* <p>
* If not specified, the JDKs jmods directory will be used, if it exists. If specified, but it does not contain the
* java.base module, the JDKs jmods directory will be added, if it exists.
*
* @param path the module path
* @return this map of options
*/
public JlinkOptions modulePath(String path) {
put("--module-path", path);
return this;
}
/**
* Exclude include header files.
*
* @param noHeaderFiles {@code true} to exclude header files, {@code false} otherwise
* @return this map of options
*/
public JlinkOptions noHeaderFiles(boolean noHeaderFiles) {
if (noHeaderFiles) {
put("--no-header-files");
} else {
remove("--no-header-files");
}
return this;
}
/**
* Exclude man pages.
*
* @param noManPages {@code true} to exclude man pages, {@code false} otherwise
* @return this map of options
*/
public JlinkOptions noManPages(boolean noManPages) {
if (noManPages) {
put("--no-man-pages");
} else {
remove("--no-man-pages");
}
return this;
}
/**
* Location of output path.
*
* @param path the output path
* @return this map of options
*/
public JlinkOptions output(String path) {
put("--output", path);
return this;
}
/**
* Associates {@code null} with the specified key in this map. If the map previously contained a mapping for the
* key, the old value is replaced.
*
* @param key key with which the specified value is to be associated
*/
public void put(String key) {
put(key, null);
}
/**
* Suggest providers that implement the given service types from the module path.
*
* @param filename the filename
* @return this map of options
*/
public JlinkOptions saveOpts(String filename) {
put("--save-opts", filename);
return this;
}
/**
* Strip debug information.
*
* @param stripDebug {@code true} to strip debug info, {@code false} otherwise
* @return this map of options
*/
public JlinkOptions stripDebug(boolean stripDebug) {
if (stripDebug) {
put("--strip-debug");
} else {
remove("--strip-debug");
}
return this;
}
/**
* Strip native commands.
*
* @param stripNativeCommands {@code true} to strip, {@code false} otherwise
* @return this map of options
*/
public JlinkOptions stripNativeCommands(boolean stripNativeCommands) {
if (stripNativeCommands) {
put("--strip-native-commands");
} else {
remove("--strip-native-commands");
}
return this;
}
/**
* Suggest providers that implement the given service types from the module path.
*
* @param name one or more provider name
* @return this map of options
*/
public JlinkOptions suggestProviders(String... name) {
put("--suggest-providers", String.join(",", name));
return this;
}
public List<String> toList() {
var list = new ArrayList<String>();
forEach((k, v) -> {
list.add(k);
if (v != null && !v.isEmpty()) {
list.add(v);
}
});
return list;
}
/**
* Enable verbose tracing.
*
* @param verbose {@code true} to enable verbose tracing, {@code false} otherwise.
* @return this map of options
*/
public JlinkOptions verbose(boolean verbose) {
if (verbose) {
put("--verbose");
} else {
remove("--verbose");
}
return this;
}
/**
* The byte orders.
*/
public enum Endian {
BIG("big"), LITTLE("little");
public final String byteOrder;
Endian(String byteOrder) {
this.byteOrder = byteOrder;
}
}
/**
* Resources compression levels.
*/
public enum CompressionLevel {
/**
* Level 0: No compression
*/
NO_COMPRESSION("0"),
/**
* Level 1: Constant string sharing
*/
CONSTANT_STRING_SHARING("1"),
/**
* Level 2: ZIP
*/
ZIP("2");
public final String level;
CompressionLevel(String level) {
this.level = level;
}
}
}

View file

@ -0,0 +1,153 @@
/*
* Copyright 2024 Erik C. Thauvin (https://erik.thauvin.net/)
* Licensed under the Apache License, Version 2.0 (the "License")
*/
package rife.bld.operations;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
/**
* Create JMOD files with the jmod tool.
*
* @author <a href="https://erik.thauvin.net/">Erik C. Thauvin</a>
* @since 2.0.2
*/
public class JmodOperation extends AbstractToolProviderOperation<JmodOperation> {
private final List<String> cmdFiles = new ArrayList<>();
private final JmodOptions jmodOptions_ = new JmodOptions();
private String jmodFile_;
private OperationMode operationMode_;
public JmodOperation() {
super("jmod");
}
/**
* Retrieves the list of files containing options or mode.
*
* @return the list of files
*/
public List<String> cmdFiles() {
return cmdFiles;
}
/**
* Read options and/or mode from file(s).
*
* @param file one or more file
* @return this operation instance
*/
public JmodOperation cmdFiles(String... file) {
cmdFiles.addAll(List.of(file));
return this;
}
@Override
public void execute() throws Exception {
if (operationMode_ != null) {
toolArgs(operationMode_.mode);
}
toolArgsFromFile(cmdFiles);
toolArgs(jmodOptions_);
if (jmodFile_ != null) {
toolArgs(jmodFile_);
}
super.execute();
}
/**
* Retrieves the name of the JMOD file to create or from which to retrieve information.
*
* @return the JMOD file
*/
public String jmodFile() {
return jmodFile_;
}
/**
* Specifies name of the JMOD file to create or from which to retrieve information.
* <p>
* The JMOD file is <b>required</b>.
*
* @param file the JMOD file
* @return this operation instance
*/
public JmodOperation jmodFile(String file) {
jmodFile_ = file;
return this;
}
/**
* Retrieves the list of options for the jmod tool.
* <p>
* This is a modifiable list that can be retrieved and changed.
*
* @return the map of jmod options
*/
public JmodOptions jmodOptions() {
return jmodOptions_;
}
/**
* Provides a list of options to provide to the jmod tool.
* <p>
* A copy will be created to allow this list to be independently modifiable.
*
* @param options the list of jmod options
* @return this operation instance
*/
public JmodOperation jmodOptions(Map<String, String> options) {
jmodOptions_.putAll(options);
return this;
}
/**
* Provides the {@link OperationMode operation mode}.
* <p>
* The operation mode is <b>required</b>.
*
* @param mode the mode
* @return this operation instance
*/
public JmodOperation operationMode(OperationMode mode) {
operationMode_ = mode;
return this;
}
/**
* The operation modes.
*/
public enum OperationMode {
/**
* Creates a new JMOD archive file.
*/
CREATE("create"),
/**
* Prints the module details.
*/
DESCRIBE("describe"),
/**
* Extracts all the files from the JMOD archive file.
*/
EXTRACT("extract"),
/**
* Determines leaf modules and records the hashes of the dependencies that directly and indirectly require them.
*/
HASH("hash"),
/**
* Prints the names of all the entries.
*/
LIST("list");
final String mode;
OperationMode(String mode) {
this.mode = mode;
}
}
}

View file

@ -0,0 +1,294 @@
/*
* Copyright 2024 Erik C. Thauvin (https://erik.thauvin.net/)
* Licensed under the Apache License, Version 2.0 (the "License")
*/
package rife.bld.operations;
import java.time.ZonedDateTime;
import java.time.format.DateTimeFormatter;
import java.time.temporal.ChronoUnit;
import java.util.ArrayList;
import java.util.HashMap;
/**
* Options for jmod tool.
*
* @author <a href="https://erik.thauvin.net/">Erik C. Thauvin</a>
* @since 2.0.2
*/
public class JmodOptions extends HashMap<String, String> {
/**
* Application jar files|dir containing classes.
*
* @param classpath the classpath
* @return this map of options
*/
public JmodOptions classpath(String classpath) {
put("--class-path", classpath);
return this;
}
/**
* Location of native commands.
*
* @param path the location
* @return this map of options
*/
public JmodOptions cmds(String path) {
put("--cmds", path);
return this;
}
/**
* Compression to use when creating the JMOD archive.
* <p>
* <b>Requires Java 20 or higher</b>.
* <p>
* Where {@link ZipCompression#ZIP_0 ZIP_0} provides no compression and {@link ZipCompression#ZIP_9 ZIP_9} provides the
* best compression.
* <p>
* Default is {@link ZipCompression#ZIP_6 ZIP_6}
*
* @param compression the {@link ZipCompression compression} level
* @return this map of options
*/
public JmodOptions compress(ZipCompression compression) {
put("--compress", compression.level);
return this;
}
/**
* Location of user-editable config files
*
* @param path the path to the config files
* @return this map of options
*/
public JmodOptions config(String path) {
put("--config", path);
return this;
}
/**
* Date and time for the timestamps of entries.
*
* @param date the date
* @return this map of options
*/
public JmodOptions date(ZonedDateTime date) {
put("--date", date.truncatedTo(ChronoUnit.SECONDS).format(DateTimeFormatter.ISO_INSTANT));
return this;
}
/**
* Target directory for extract
*
* @param path the directory path
* @return this map of options
*/
public JmodOptions dir(String path) {
put("--dir", path);
return this;
}
/**
* Exclude from the default root set of modules.
*
* @param doNotResolveByDefault {@code true} to not resolve, {@code false} otherwise
* @return this map of options
*/
public JmodOptions doNotResolveByDefault(boolean doNotResolveByDefault) {
if (doNotResolveByDefault) {
put("--do-not-resolve-by-default");
} else {
remove("--do-not-resolve-by-default");
}
return this;
}
/**
* Dry run of hash mode.
*
* @param dryRun {@code true} for dry run, {@code false} otherwise
* @return this list of operation
*/
public JmodOptions dryRun(boolean dryRun) {
if (dryRun) {
put("--dry-run");
} else {
remove("--dry-run");
}
return this;
}
/**
* Exclude files matching the supplied pattern list.
*
* @param pattern one or more pattern
* @return the map of options
*/
public JmodOptions exclude(FilePattern... pattern) {
var args = new ArrayList<String>();
for (var p : pattern) {
if (p.type == FilePatternType.GLOB) {
args.add("glob:" + p.pattern);
} else if (p.type == FilePatternType.REGEX) {
args.add("regex:" + p.pattern);
}
}
put("--exclude", String.join(",", args));
return this;
}
/**
* Compute and record hashes to tie a packaged module with modules matching the given regular expression pattern and
* depending upon it directly or indirectly. The hashes are recorded in the JMOD file being created, or a JMOD file
* or modular JAR on the module path specified the jmod hash command.
*
* @param regexPattern the regular expression pattern
* @return this map of options
*/
public JmodOptions hashModules(String regexPattern) {
put("--hash-modules", regexPattern);
return this;
}
/**
* Location of header files.
*
* @param path the location
* @return this map of options
*/
public JmodOptions headerFiles(String path) {
put("--header-files", path);
return this;
}
/**
* Location of legal notices.
*
* @param path the location
* @return this map of options
*/
public JmodOptions legalNotices(String path) {
put("--legal-notices", path);
return this;
}
/**
* Location of native libraries.
*
* @param path the location
* @return this map of options
*/
public JmodOptions libs(String path) {
put("--libs", path);
return this;
}
/**
* Main class.
*
* @param name the class name
* @return this list of operation
*/
public JmodOptions mainClass(String name) {
put("--main-class", name);
return this;
}
/**
* Location of man pages.
*
* @param path the location
* @return this map of options
*/
public JmodOptions manPages(String path) {
put("--man-pages", path);
return this;
}
/**
* Module path.
*
* @param path the module path
* @return this map of options
*/
public JmodOptions modulePath(String path) {
put("--module-path", path);
return this;
}
/**
* Module version.
*
* @param version the module version.
* @return this map of options
*/
public JmodOptions moduleVersion(String version) {
put("--module-version", version);
return this;
}
/**
* Associates {@code null} with the specified key in this map. If the map previously contained a mapping for the
* key, the old value is replaced.
*
* @param key key with which the specified value is to be associated
*/
public void put(String key) {
put(key, null);
}
/**
* Target platform.
*
* @param platform the platform
* @return this list of operation
*/
public JmodOptions targetPlatform(String platform) {
put("--target-platform", platform);
return this;
}
/**
* Hint for a tool to issue a warning if the module is resolved.
*
* @param reason the reason
* @return this map of options
*/
public JmodOptions warnIfResolved(ResolvedReason reason) {
put("--warn-if-resolved", reason.reason);
return this;
}
/**
* The resolved reasons.
*/
public enum ResolvedReason {
DEPRECATED("deprecated"),
DEPRECATED_FOR_REMOVAL("deprecated-for-removal"),
INCUBATING("incubating");
final String reason;
ResolvedReason(String reason) {
this.reason = reason;
}
}
/**
* The file pattern types.
*/
public enum FilePatternType {
GLOB, REGEX
}
/**
* Defines a file pattern and pattern type.
*
* @param type the pattern type
* @param pattern the pattern
*/
public record FilePattern(FilePatternType type, String pattern) {
}
}

View file

@ -0,0 +1,75 @@
/*
* Copyright 2024 Erik C. Thauvin (https://erik.thauvin.net/)
* Licensed under the Apache License, Version 2.0 (the "License")
*/
package rife.bld.operations;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
/**
* Package self-contained Java applications with the jpackage tool.
*
* @author <a href="https://erik.thauvin.net/">Erik C. Thauvin</a>
* @since 2.0.2
*/
public class JpackageOperation extends AbstractToolProviderOperation<JpackageOperation> {
private final List<String> cmdFiles_ = new ArrayList<>();
private final JpackageOptions jpackageOptions_ = new JpackageOptions();
public JpackageOperation() {
super("jpackage");
}
@Override
public void execute() throws Exception {
toolArgs(cmdFiles_.stream().map(opt -> '@' + opt).toList());
toolArgs(jpackageOptions_);
super.execute();
}
/**
* Retrieves the list of files containing options or mode.
*
* @return the list of files
*/
public List<String> fileOptions() {
return cmdFiles_;
}
/**
* Read options and/or mode from file(s).
*
* @param file one or more file
* @return this operation instance
*/
public JpackageOperation cmdFiles(String... file) {
cmdFiles_.addAll(List.of(file));
return this;
}
/**
* Retrieves the list of options for the jpackage tool.
* <p>
* This is a modifiable list that can be retrieved and changed.
*
* @return the map of jpackage options
*/
public JpackageOptions jpackageOptions() {
return jpackageOptions_;
}
/**
* Provides a list of options to provide to the jpackage tool.
* <p>
* A copy will be created to allow this list to be independently modifiable.
*
* @param options the map of jpackage options
* @return this operation instance
*/
public JpackageOperation jpackageOptions(Map<String, String> options) {
jpackageOptions_.putAll(options);
return this;
}
}

View file

@ -0,0 +1,888 @@
/*
* Copyright 2024 Erik C. Thauvin (https://erik.thauvin.net/)
* Licensed under the Apache License, Version 2.0 (the "License")
*/
package rife.bld.operations;
import java.util.HashMap;
/**
* Options for jpackage tool.
*
* @author <a href="https://erik.thauvin.net/">Erik C. Thauvin</a>
* @since 2.0.2
*/
public class JpackageOptions extends HashMap<String, String> {
/**
* URL of the application's home page.
*
* @param url the URL
* @return this map of options
*/
public JpackageOptions aboutUrl(String url) {
put("--about-url", url);
return this;
}
/**
* List of application launchers.
* <p>
* The main application launcher will be built from the command line options.
* <p>
* Additional alternative launchers can be built using this option, and this option can be used to build multiple
* additional launchers.
*
* @param launcher one or more {@link Launcher}
* @return this map of options
*/
public JpackageOptions addLauncher(Launcher... launcher) {
for (var l : launcher) {
put("--add-launcher", l.name + '=' + l.path);
}
return this;
}
/**
* List of modules to add.
* <p>
* This module list, along with the main module (if specified) will be passed to jlink as the
* {@link JlinkOptions#addModules(String...) addModules} argument. If not specified, either just the main module
* (if {@link #module(String, String) module} is specified), or the default set of modules (if
* {@link #mainJar(String) mainJar} is specified) are used.
*
* @param modules one or more module
* @return this map of options
*/
public JpackageOptions addModules(String... modules) {
put("--add-modules", String.join(",", modules));
return this;
}
/**
* List of paths to files and/or directories to add to the application payload.
* <p>
* <b>Requires Java 20 or higher</b>.
*
* @param additionalContent one or more path
* @return this map of options
*/
public JpackageOptions appContent(String... additionalContent) {
put("--app-content", String.join(",", additionalContent));
return this;
}
/**
* Location of the predefined application image that is used to build an installable package.
*
* @param path absolute path or relative to the current directory
* @return this map of options
*/
public JpackageOptions appImage(String path) {
put("--app-image", path);
return this;
}
/**
* Version of the application and/or package.
*
* @param version the version
* @return this map of options
*/
public JpackageOptions appVersion(String version) {
put("--app-version", version);
return this;
}
/**
* Command line arguments to pass to main class if no command line arguments are given to the launcher.
*
* @param argument one or more argument
* @return this map of options
*/
public JpackageOptions arguments(String... argument) {
put("--arguments", String.join(" ", argument));
return this;
}
/**
* Copyright of the application.
*
* @param copyright the copyright
* @return this map of options
*/
public JpackageOptions copyright(String copyright) {
put("--copyright", copyright);
return this;
}
/**
* Description of the application.
*
* @param description the description
* @return this map of options
*/
public JpackageOptions description(String description) {
put("--description", description);
return this;
}
/**
* Path where generated output file is placed.
* <p>
* Defaults to the current working directory.
*
* @param path absolute path or relative to the current directory
* @return this map of options
*/
public JpackageOptions dest(String path) {
put("--dest", path);
return this;
}
/**
* Path to a Properties file that contains list of key, value pairs.
* <p>
* The keys {@code extension}, {@code mime-type}, {@code icon}, and {@code description} can be used to describe the
* association.
*
* @param path absolute path or relative to the current directory
* @return this map of options
*/
public JpackageOptions fileAssociations(String... path) {
put("--file-associations", String.join(",", path));
return this;
}
/**
* Path of the icon of the application package.
*
* @param path absolute path or relative to the current directory
* @return this map of options
*/
public JpackageOptions icon(String path) {
put("--icon", path);
return this;
}
/**
* Path of the input directory that contains the files to be packaged.
* <p>
* All files in the input directory will be packaged into the application image.
*
* @param path absolute path or relative to the current directory
* @return this map of options
*/
public JpackageOptions input(String path) {
put("--input", path);
return this;
}
/**
* Absolute path of the installation directory of the application.
*
* @param path the absolute directory path
* @return this map of options
*/
public JpackageOptions installDir(String path) {
put("--install-dir", path);
return this;
}
/**
* Options to pass to the Java runtime.
*
* @param options the options
* @return this map of options
*/
public JpackageOptions javaOptions(String... options) {
put("--java-options", String.join(" ", options));
return this;
}
/**
* List of options to pass to jlink.
* <p>
* If not specified, defaults to {@link JlinkOptions#stripNativeCommands(boolean) stripNativeCommands}
* {@link JlinkOptions#stripDebug(boolean) stripDebug} {@link JlinkOptions#noManPages(boolean) noManPages}
* {@link JlinkOptions#noHeaderFiles(boolean) noHeaderFiles}.
*
* @param options the {@link JlinkOptions}
* @return this map of options
*/
public JpackageOptions jlinkOptions(JlinkOptions options) {
put("--jlink-options", String.join(" ", options.toList()));
return this;
}
/**
* Request to create an installer that will register the main application launcher as a background service-type
* application.
* <p>
* <b>Requires Java 20 or higher</b>.
*
* @param launcherAsService {@code true} to register the launcher as a service; {@code false} otherwise
* @return this map of options
*/
public JpackageOptions launcherAsService(boolean launcherAsService) {
if (launcherAsService) {
put("--launcher-as-service");
} else {
remove("--launcher-as-service");
}
return this;
}
/**
* Path to the license file.
*
* @param path absolute path or relative to the current directory
* @return this map of options
*/
public JpackageOptions licenseFile(String path) {
put("--license-file", path);
return this;
}
/**
* Group value of the RPM {@code <name>.spec} file or Section value of DEB control file.
*
* @param appCategory the application category
* @return this map of options
*/
public JpackageOptions linuxAppCategory(String appCategory) {
put("--linux-app-category", appCategory);
return this;
}
/**
* Release value of the RPM {@code <name>.spec} file or Debian revision value of the DEB control file.
*
* @param appRelease the release value
* @return this map of options
*/
public JpackageOptions linuxAppRelease(String appRelease) {
put("--linux-app-release", appRelease);
return this;
}
/**
* Maintainer for {@code .deb} package.
*
* @param maintainer the maintainer
* @return this map of options
*/
public JpackageOptions linuxDebMaintainer(String maintainer) {
put("--linux-deb-maintainer", maintainer);
return this;
}
/**
* Menu group this application is placed in.
*
* @param menuGroup the menu group
* @return this map of options
*/
public JpackageOptions linuxMenuGroup(String menuGroup) {
put("--linux-menu-group", menuGroup);
return this;
}
/**
* Required packages or capabilities for the application.
*
* @param packageDeps {@code true} if required, {@code false} otherwise
* @return this map of options
*/
public JpackageOptions linuxPackageDeps(boolean packageDeps) {
if (packageDeps) {
put("--linux-package-deps");
} else {
remove("--linux-package-deps");
}
return this;
}
/**
* Name for Linux package, defaults to the application name.
*
* @param packageName the package name
* @return this map of options
*/
public JpackageOptions linuxPackageName(String packageName) {
put("--linux-package-name", packageName);
return this;
}
/**
* Type of the license.
* <p>
* {@code License: <value>} of the RPM {@code .spec}
*
* @param licenseType the license type
* @return this map of options
*/
public JpackageOptions linuxRpmLicenseType(String licenseType) {
put("--linux-rpm-license-type", licenseType);
return this;
}
/**
* Creates a shortcut for the application.
*
* @param shortcut {@code true| to create a shortcut, {@code false} otherwise
* @return this map of options
*/
public JpackageOptions linuxShortcut(boolean shortcut) {
if (shortcut) {
put("--linux-shortcut");
} else {
remove("--linux-shortcut");
}
return this;
}
/**
* String used to construct {@code LSApplicationCategoryType} in application plist.
* <p>
* The default value is {@code utilities}.
*
* @param appCategory the category
* @return this map of options
*/
public JpackageOptions macAppCategory(String appCategory) {
put("--mac-app-category", appCategory);
return this;
}
/**
* Identity used to sign application image.
* <p>
* This value will be passed directly to {@code --sign} option of {@code codesign} tool.
* <p>
* This option cannot be combined with {@link #macSigningKeyUserName(String) macSignKeyUserName}.
*
* @param identity the identity
* @return this map of options
*/
public JpackageOptions macAppImageSignIdentity(String identity) {
put("--mac-app-image-sign-identity", identity);
return this;
}
/**
* Indicates that the jpackage output is intended for the Mac App Store.
*
* @param appStore {@code true} if intended for the Mac App Store, {@code false} otherwise
* @return this map of options
*/
public JpackageOptions macAppStore(boolean appStore) {
if (appStore) {
put("--mac-app-store");
} else {
remove("--mac-app-store");
}
return this;
}
/**
* Include all the referenced content in the dmg.
*
* @param additionalContent one or more path
* @return this map of options
*/
public JpackageOptions macDmgContent(String... additionalContent) {
put("--mac-dmg-content", String.join(",", additionalContent));
return this;
}
/**
* Path to file containing entitlements to use when signing executables and libraries in the bundle.
*
* @param path the fie path
* @return this map of options
*/
public JpackageOptions macEntitlements(String path) {
put("--mac-entitlements", path);
return this;
}
/**
* Identity used to sign "pkg" installer.
* <p>
* This value will be passed directly to {@code --sign} option of {@code productbuild} tool.
* <p>
* This option cannot be combined with {@link #macSigningKeyUserName(String) macSignKeyUserName}.
*
* @param identity the identity
* @return this map of options
*/
public JpackageOptions macInstallerSignIdentity(String identity) {
put("--mac-installer-sign-identity", identity);
return this;
}
/**
* An identifier that uniquely identifies the application for macOS.
* <p>
* Defaults to the main class name.
* <p>
* May only use alphanumeric ({@code A-Z,a-z,0-9}), hyphen ({@code -}), and period ({@code .}) characters.
*
* @param packageIdentifier the package identifier
* @return this map of options
*/
public JpackageOptions macPackageIdentifier(String packageIdentifier) {
put("--mac-package-identifier", packageIdentifier);
return this;
}
/**
* Name of the application as it appears in the Menu Bar.
* <p>
* This can be different from the application name.
* <p>
* This name must be less than 16 characters long and be suitable for displaying in the menu bar and the application
* Info window.
* <p>
* Defaults to the application name.
*
* @param name the package name
* @return this map of options
*/
public JpackageOptions macPackageName(String name) {
put("--mac-package-name", name);
return this;
}
/**
* When signing the application package, this value is prefixed to all components that need to be signed that don't
* have an existing package identifier.
*
* @param prefix the signing prefix
* @return this map of options
*/
public JpackageOptions macPackageSigningPrefix(String prefix) {
put("--mac-package-signing-prefix", prefix);
return this;
}
/**
* Request that the package or the predefined application image be signed.
*
* @param sign {@code true} to sign, {@code false} otherwise
* @return this map of options
*/
public JpackageOptions macSign(boolean sign) {
if (sign) {
put("--mac-sign");
} else {
remove("--mac-sign");
}
return this;
}
/**
* Team or user name portion in Apple signing identities.
* <p>
* For direct control of the signing identity used to sign application images or installers use
* {@link #macAppImageSignIdentity(String) macAppImageSignIdentity} and/or
* {@link #macInstallerSignIdentity(String) macInstallerSignIdentity}.
* <p>
* This option cannot be combined with {@link #macAppImageSignIdentity(String) macAppImageSignIdentity} or
* {@link #macInstallerSignIdentity(String) macInstallerSignIdentity}.
*
* @param username the username
* @return this map of options
*/
public JpackageOptions macSigningKeyUserName(String username) {
put("--mac-signing-key-user-name", username);
return this;
}
/**
* Name of the keychain to search for the signing identity.
* <p>
* If not specified, the standard keychains are used.
*
* @param keychain the keychain name
* @return this map of options
*/
public JpackageOptions macSigningKeychain(String keychain) {
put("--mac-signing-keychain", keychain);
return this;
}
/**
* Qualified name of the application main class to execute.
* <p>
* This option can only be used if {@link #mainJar(String) mainJar} is specified.
*
* @param mainClass the main class
* @return this map of options
*/
public JpackageOptions mainClass(String mainClass) {
put("--main-class", mainClass);
return this;
}
/**
* The main JAR of the application; containing the main class.
* <p>
* Either {@link #module(String, String) module} or {@link #mainJar(String) mainJar} option can be specified but
* not both.
*
* @param jar the path relative to the input path
* @return this map of options
*/
@SuppressWarnings("JavadocDeclaration")
public JpackageOptions mainJar(String jar) {
put("--main-jar", jar);
return this;
}
/**
* The main module and main class of the application.
* <p>
* This module must be located on the {@link #modulePath(String...) module path}.
* <p>
* When this option is specified, the main module will be linked in the Java runtime image.
* <p>
* Either {@link #module(String, String) module} or {@link #mainJar(String) mainJar} option can be specified but
* not both.
*
* @param name the module name
* @return this map of options
*/
public JpackageOptions module(String name) {
put("--module", name);
return this;
}
/**
* The main module and main class of the application.
* <p>
* This module must be located on the {@link #modulePath(String...) module path}.
* <p>
* When this option is specified, the main module will be linked in the Java runtime image.
* <p>
* Either {@link #module(String, String) module} or {@link #mainJar(String) mainJar} option can be specified but
* not both.
*
* @param name the module name
* @param mainClass the main class
* @return this map of options
*/
@SuppressWarnings("JavadocDeclaration")
public JpackageOptions module(String name, String mainClass) {
put("--module-name", name + "/" + mainClass);
return this;
}
/**
* List of module paths.
* <p>
* Each path is either a directory of modules or the path to a modular jar.
* <p>
* Each path is absolute or relative to the current directory.
*
* @param path one or more path
* @return this map of options
*/
public JpackageOptions modulePath(String... path) {
put("--module-path", String.join(":", path));
return this;
}
/**
* Name of the application and/or package.
*
* @param name the name
* @return this map of options
*/
public JpackageOptions name(String name) {
put("--name", name);
return this;
}
/**
* Associates {@code null} with the specified key in this map. If the map previously contained a mapping for the
* key, the old value is replaced.
*
* @param key key with which the specified value is to be associated
*/
public void put(String key) {
put(key, null);
}
/**
* Path to override jpackage resources.
* <p>
* Icons, template files, and other resources of jpackage can be over-ridden by adding replacement resources to
* this directory.
*
* @param path absolute path or relative to the current directory
* @return this map of options
*/
public JpackageOptions resourceDir(String path) {
put("--resource-dir", path);
return this;
}
/**
* Path of the predefined runtime image that will be copied into the application image.
* <p>
* If not specified, jpackage will run jlink to create the runtime image using options:
* {@link JlinkOptions#stripNativeCommands(boolean) stripNativeCommands}
* {@link JlinkOptions#stripDebug(boolean) stripDebug} {@link JlinkOptions#noManPages(boolean) noManPages}
* {@link JlinkOptions#noHeaderFiles(boolean) noHeaderFiles}
* <p>
* Option is required when creating a runtime package.
*
* @param path absolute path or relative to the current directory
* @return this map of options
*/
public JpackageOptions runtimeImage(String path) {
put("--runtime-image", path);
return this;
}
/**
* Strip debug information.
*
* @param stripDebug {@code true} to strip debug info, {@code false} otherwise
* @return this map of options
*/
public JpackageOptions stripDebug(boolean stripDebug) {
if (stripDebug) {
put("--strip-debug");
} else {
remove("--strip-debug");
}
return this;
}
/**
* Path of a new or empty directory used to create temporary files.
* <p>
* If specified, the temp dir will not be removed upon the task completion and must be removed manually.
* <p>
* If not specified, a temporary directory will be created and removed upon the task completion.
*
* @param path absolute path or relative to the current directory
* @return this map of options
*/
public JpackageOptions temp(String path) {
put("--temp", path);
return this;
}
/**
* The type of package to create.
* <p>
* If this option is not specified a platform dependent default type will be created.
*
* @param type the package type
* @return this map of options
*/
public JpackageOptions type(PackageType type) {
put("--type", type.type);
return this;
}
/**
* Vendor of the application.
*
* @param vendor the vendor
* @return this map of options
*/
public JpackageOptions vendor(String vendor) {
put("--vendor", vendor);
return this;
}
/**
* Enables verbose output.
*
* @param verbose {@code true} to enable verbose tracing, {@code false} otherwise.
* @return this map of options
*/
public JpackageOptions verbose(boolean verbose) {
if (verbose) {
put("--verbose");
} else {
remove("--verbose");
}
return this;
}
/**
* Creates a console launcher for the application, should be specified for application which requires console
* interactions.
*
* @param winConsole {@code true} to create a console launcher, {@code false} otherwise
* @return this map of options
*/
public JpackageOptions winConsole(boolean winConsole) {
if (winConsole) {
put("--win-console");
} else {
remove("--win-console");
}
return this;
}
/**
* Adds a dialog to enable the user to choose a directory in which the product is installed.
*
* @param winDirChooser {@code true} to let the user choose a directory, {@code false} otherwise
* @return this map of options
*/
public JpackageOptions winDirChooser(boolean winDirChooser) {
if (winDirChooser) {
put("--win-dir-chooser");
} else {
remove("--win-dir-chooser");
}
return this;
}
/**
* URL where user can obtain further information or technical support.
*
* @param helpUrl the help URL
* @return this map of options
*/
public JpackageOptions winHelpUrl(String helpUrl) {
put("--win-help-url", helpUrl);
return this;
}
/**
* Request to add a Start Menu shortcut for this application.
*
* @param winMenu {@code true} to add a start menu shortcut, {@code false} otherwise
* @return this map of options
*/
public JpackageOptions winMenu(boolean winMenu) {
if (winMenu) {
put("--win-menu");
} else {
remove("--win-menu");
}
return this;
}
/**
* Start Menu group this application is placed in.
*
* @param menuGroup the menu group
* @return this map of options
*/
public JpackageOptions winMenuGroup(String menuGroup) {
put("--win-menu-group", menuGroup);
return this;
}
/**
* Request to perform an install on a per-user basis.
*
* @param winPerUserInstall {@code true} for per-user install, {@code false} otherwise
* @return this map of options
*/
public JpackageOptions winPerUserInstall(boolean winPerUserInstall) {
if (winPerUserInstall) {
put("--win-per-user-install");
} else {
remove("--win-per-user-install");
}
return this;
}
/**
* Request to create a desktop shortcut for this application.
*
* @param winShortcut {@code true} to create a shortcut, {@code false} otherwise
* @return this map of options
*/
public JpackageOptions winShortcut(boolean winShortcut) {
if (winShortcut) {
put("--win-shortcut");
} else {
remove("--win-shortcut");
}
return this;
}
/**
* Adds a dialog to enable the user to choose if shortcuts will be created by installer.
*
* @param shortcutPrompt {@code true} to add a prompt; {@code false} otherwise
* @return this map of options
*/
public JpackageOptions winShortcutPrompt(boolean shortcutPrompt) {
if (shortcutPrompt) {
put("--win-shortcut-prompt");
} else {
remove("--win-shortcut-prompt");
}
return this;
}
/**
* URL of available application update information.
*
* @param url the URL
* @return this map of options
*/
public JpackageOptions winUpdateUrl(String url) {
put("--win-update-url", url);
return this;
}
/**
* UUID associated with upgrades for this package.
*
* @param uuid the uuid
* @return this map of options
*/
public JpackageOptions winUpgradeUuid(String uuid) {
put("--win-upgrade-uuid", uuid);
return this;
}
/**
* The package types.
*/
public enum PackageType {
APP_IMAGE("app_image"),
DEB("deb"),
DMG("dmg"),
EXE("exe"),
MSI("msi"),
PKG("pkg"),
RPM("rpm");
final String type;
PackageType(String type) {
this.type = type;
}
}
/**
* Name of launcher, and a path to a Properties file that contains a list of key, value pairs.
* <p>
* The keys {@code module}, {@code main-jar}, {@code main-class}, {@code description},
* {@code arguments}, {@code java-options}, {@code app-version}, {@code icon},
* {@code launcher-as-service}, {@code win-console}, {@code win-shortcut}, {@code win-menu},
* {@code linux-app-category}, and {@code linux-shortcut} can be used.
* <p>
* These options are added to, or used to overwrite, the original command line options to build an additional
* alternative launcher.
*
* @param name the name
* @param path absolute path or relative to the current directory
*/
public record Launcher(String name, String path) {
}
}

View file

@ -0,0 +1,30 @@
/**
* Copyright 2024 Erik C. Thauvin (https://erik.thauvin.net/)
* Licensed under the Apache License, Version 2.0 (the "License")
*/
package rife.bld.operations;
/**
* The zip compression levels for jlink and jmod.
*
* @author <a href="https://erik.thauvin.net/">Erik C. Thauvin</a>
* @since 2.0.2
*/
public enum ZipCompression {
ZIP_0("zip-0"),
ZIP_1("zip-1"),
ZIP_2("zip-2"),
ZIP_3("zip-3"),
ZIP_4("zip-4"),
ZIP_5("zip-5"),
ZIP_6("zip-6"),
ZIP_7("zip-7"),
ZIP_8("zip-8"),
ZIP_9("zip-9");
public final String level;
ZipCompression(String level) {
this.level = level;
}
}