From bfabab9c712041b6695852aef60c8648b7dc4bc7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Christoph=20L=C3=A4ubrich?= Date: Fri, 8 Dec 2023 18:24:10 +0100 Subject: [PATCH] Add new director mojo --- RELEASE_NOTES.md | 28 ++ .../tycho/p2/CommandLineArguments.java | 89 ++++ .../AbstractDirectorApplicationCommand.java | 24 +- tycho-p2-director-plugin/pom.xml | 5 + .../plugins/p2/director/DirectorMojo.java | 426 ++++++++++++++++++ 5 files changed, 549 insertions(+), 23 deletions(-) create mode 100644 tycho-core/src/main/java/org/eclipse/tycho/p2/CommandLineArguments.java create mode 100644 tycho-p2-director-plugin/src/main/java/org/eclipse/tycho/plugins/p2/director/DirectorMojo.java diff --git a/RELEASE_NOTES.md b/RELEASE_NOTES.md index 0e0d7dbf48..3c663eb99e 100644 --- a/RELEASE_NOTES.md +++ b/RELEASE_NOTES.md @@ -6,6 +6,34 @@ If you are reading this in the browser, then you can quickly jump to specific ve ## 5.0.0 (under development) +### new `director` mojo + +This mojo can be used in two ways: + +1. As a commandline invocation passing arguments as properties using `mvn org.eclipse.tycho:tycho-p2-director-plugin:director -Ddestination=[target] ... -D...` +2. as an execution inside a pom + +```xml + + org.eclipse.tycho + tycho-p2-director-plugin + ${tycho-version} + + + + director + + package + + ... + ... other arguments ... + + + + + ``` + + ### new `tycho-eclipse-plugin` Tycho now contains a new `tycho-eclipse-plugin` that is dedicated to executing "tasks like eclipse", this currently includes diff --git a/tycho-core/src/main/java/org/eclipse/tycho/p2/CommandLineArguments.java b/tycho-core/src/main/java/org/eclipse/tycho/p2/CommandLineArguments.java new file mode 100644 index 0000000000..a3c1f4aef1 --- /dev/null +++ b/tycho-core/src/main/java/org/eclipse/tycho/p2/CommandLineArguments.java @@ -0,0 +1,89 @@ +/******************************************************************************* + * Copyright (c) 2012, 2014 SAP SE and others. + * This program and the accompanying materials + * are made available under the terms of the Eclipse Public License 2.0 + * which accompanies this distribution, and is available at + * https://www.eclipse.org/legal/epl-2.0/ + * + * SPDX-License-Identifier: EPL-2.0 + * + * Contributors: + * SAP SE - initial API and implementation + *******************************************************************************/ +package org.eclipse.tycho.p2; + +import java.io.File; +import java.util.ArrayList; +import java.util.List; +import java.util.Map; +import java.util.StringJoiner; +import java.util.stream.Collectors; + +public class CommandLineArguments { + List arguments = new ArrayList<>(); + + public void add(String flag) { + arguments.add(flag); + } + + public void add(String parameterName, String parameterValue) { + arguments.add(parameterName); + arguments.add(parameterValue); + } + + public void addNonNull(String parameterName, String parameterValue) { + if (parameterValue != null) { + arguments.add(parameterName); + arguments.add(parameterValue); + } + } + + public void addUnlessEmpty(String parameterName, StringJoiner parameterValue) { + if (parameterValue.length() > 0) { + add(parameterName, parameterValue.toString()); + } + } + + public void addNotEmpty(String parameterName, List list, CharSequence seperator) { + if (list.isEmpty()) { + return; + } + add(parameterName, list.stream().collect(Collectors.joining(seperator))); + } + + public void addNotEmpty(String parameterName, Map propertyMap, CharSequence keyValueSeparator, + CharSequence seperator) { + if (propertyMap.isEmpty()) { + return; + } + add(parameterName, + propertyMap.entrySet().stream().map(entry -> entry.getKey() + keyValueSeparator + entry.getValue()) + .collect(Collectors.joining(seperator))); + } + + public void addFlagIfTrue(String flag, boolean value) { + if (value) { + add(flag); + } + } + + public void addNonNull(String parameterName, File file) { + if (file != null) { + add(parameterName, file.getAbsolutePath()); + } + } + + public List asList() { + return new ArrayList<>(arguments); + } + + public String[] toArray() { + return arguments.toArray(String[]::new); + } + + @Override + public String toString() { + return arguments.stream().collect(Collectors.joining(" ")); + } + +} diff --git a/tycho-core/src/main/java/org/eclipse/tycho/p2/tools/director/shared/AbstractDirectorApplicationCommand.java b/tycho-core/src/main/java/org/eclipse/tycho/p2/tools/director/shared/AbstractDirectorApplicationCommand.java index 44420e7c46..9af260c91e 100644 --- a/tycho-core/src/main/java/org/eclipse/tycho/p2/tools/director/shared/AbstractDirectorApplicationCommand.java +++ b/tycho-core/src/main/java/org/eclipse/tycho/p2/tools/director/shared/AbstractDirectorApplicationCommand.java @@ -14,7 +14,6 @@ import java.io.File; import java.net.URI; -import java.util.ArrayList; import java.util.HashMap; import java.util.List; import java.util.Map; @@ -24,6 +23,7 @@ import org.eclipse.tycho.ArtifactType; import org.eclipse.tycho.TargetEnvironment; import org.eclipse.tycho.core.resolver.shared.DependencySeed; +import org.eclipse.tycho.p2.CommandLineArguments; /** * Base class for calling a p2 director via command line arguments. @@ -142,26 +142,4 @@ protected List getDirectorApplicationArguments() { return args.asList(); } - private static class CommandLineArguments { - List arguments = new ArrayList<>(); - - void add(String flag) { - arguments.add(flag); - } - - void add(String parameterName, String parameterValue) { - arguments.add(parameterName); - arguments.add(parameterValue); - } - - void addUnlessEmpty(String parameterName, StringJoiner parameterValue) { - if (parameterValue.length() > 0) { - add(parameterName, parameterValue.toString()); - } - } - - public List asList() { - return new ArrayList<>(arguments); - } - } } diff --git a/tycho-p2-director-plugin/pom.xml b/tycho-p2-director-plugin/pom.xml index 990fd37e4a..4739df34c1 100644 --- a/tycho-p2-director-plugin/pom.xml +++ b/tycho-p2-director-plugin/pom.xml @@ -87,6 +87,11 @@ tycho-core ${project.version} + + org.eclipse.tycho + tycho-p2-plugin + ${project.version} + diff --git a/tycho-p2-director-plugin/src/main/java/org/eclipse/tycho/plugins/p2/director/DirectorMojo.java b/tycho-p2-director-plugin/src/main/java/org/eclipse/tycho/plugins/p2/director/DirectorMojo.java new file mode 100644 index 0000000000..8ea217ffdc --- /dev/null +++ b/tycho-p2-director-plugin/src/main/java/org/eclipse/tycho/plugins/p2/director/DirectorMojo.java @@ -0,0 +1,426 @@ +/******************************************************************************* + * Copyright (c) 2023 Christoph Läubrich and others. + * This program and the accompanying materials + * are made available under the terms of the Eclipse Public License 2.0 + * which accompanies this distribution, and is available at + * https://www.eclipse.org/legal/epl-2.0/ + * + * SPDX-License-Identifier: EPL-2.0 + * + * Contributors: + * Christoph Läubrich - initial API and implementation + *******************************************************************************/ +package org.eclipse.tycho.plugins.p2.director; + +import java.io.File; +import java.util.ArrayList; +import java.util.LinkedHashMap; +import java.util.List; +import java.util.Map; + +import org.apache.maven.plugin.AbstractMojo; +import org.apache.maven.plugin.MojoExecutionException; +import org.apache.maven.plugin.MojoFailureException; +import org.apache.maven.plugins.annotations.Component; +import org.apache.maven.plugins.annotations.LifecyclePhase; +import org.apache.maven.plugins.annotations.Mojo; +import org.apache.maven.plugins.annotations.Parameter; +import org.eclipse.equinox.app.IApplication; +import org.eclipse.equinox.internal.p2.director.app.DirectorApplication; +import org.eclipse.equinox.p2.core.IProvisioningAgent; +import org.eclipse.equinox.p2.repository.artifact.IArtifactRepositoryManager; +import org.eclipse.tycho.p2.CommandLineArguments; + +/** + * Allows to run the director + * application to manage Eclipse Installations. This mojo can be used in two ways + * + *
    + *
  1. As a commandline invocation passing arguments as properties using + * mvn org.eclipse.tycho:tycho-p2-director-plugin:director -Ddestination=[target] ... -D... + *
  2. + *
  3. as an execution inside a pom + * + *
    + * <plugin>
    + *    <groupId>org.eclipse.tycho</groupId>
    + *    <artifactId>tycho-p2-director-plugin</artifactId>
    + *    <version>${tycho-version}</version>
    + *    <executions>
    + *       <execution>
    + *          <goals>
    + *             <goal>director</goal>
    + *          </goals>
    + *          <phase>package</phase>
    + *          <configuration>
    + *             <destination>...</destination>
    + *             ... other arguments ...
    + *          </configuration>
    + *       </execution>
    + *    </executions>
    + * </plugin>
    + * 
    + * + *
  4. + *
+ */ +@Mojo(name = "director", defaultPhase = LifecyclePhase.NONE, threadSafe = true, requiresProject = false) +public class DirectorMojo extends AbstractMojo { + + @Component + private IProvisioningAgent agent; + + /** + * The folder in which the targeted product is located. + */ + @Parameter(property = "destination", required = true) + private File destination; + + /** + * comma separated list of URLs denoting meta-data repositories + */ + @Parameter(property = "metadatarepositories", alias = "metadatarepository") + private String metadatarepositories; + + /** + * comma separated list of URLs denoting artifact repositories. + */ + @Parameter(property = "artifactrepositories", alias = "artifactrepository") + private String artifactrepositories; + + /** + * comma separated list denoting co-located meta-data and artifact repositories + */ + @Parameter(property = "repositories", alias = "repository") + private String repositories; + + /** + * comma separated list of IUs to install, each entry in the list is in the form [ '/' + * ]. + */ + @Parameter(property = "installIUs", alias = "installIU") + private String installIUs; + + /** + * Alternative way to specify the IU to install in a more declarative (but also verbose) way, + * example: + * + *
+     * <install>
+     *    <iu>
+     *       <id>...</id>
+     *       <version>...optional version...</id>
+     *       <feature>true/false</feature> <!-- optional if true .feature.group is automatically added to the id  -->
+     * </install>
+     * 
+ */ + @Parameter + private List install; + + /** + * comma separated list of IUs to install, each entry in the list is in the form [ '/' + * ]. + */ + @Parameter(property = "uninstallIUs", alias = "uninstallIU") + private String uninstallIUs; + + /** + * Alternative way to specify the IU to uninstall in a more declarative (but also verbose) way, + * example: + * + *
+     * <install>
+     *    <iu>
+     *       <id>...</id>
+     *       <version>...optional version...</id>
+     *       <feature>true/false</feature> <!-- optional if true .feature.group is automatically added to the id  -->
+     * </install>
+     * 
+ */ + @Parameter + private List uninstall; + + /** + * comma separated list of numbers, revert the installation to a previous state. The number + * representing the previous state of the profile as found in + * p2/org.eclipse.equinox.p2.engine//. + */ + @Parameter(property = "revert") + private String revert; + + /** + * Remove the history of the profile registry. + */ + @Parameter(property = "purgeHistory") + private boolean purgeHistory; + + /** + * Lists all IUs found in the given repositories. IUs can optionally be listed. Each entry in + * the list is in the form [ '/' ]. + */ + @Parameter(property = "list") + private boolean list; + + /** + * List the tags available + */ + @Parameter(property = "listTags") + private boolean listTags; + + /** + * Lists all root IUs found in the given profile. Each entry in the list is in the form [ + * '/' ]. + */ + @Parameter(property = "listInstalledRoots") + private boolean listInstalledRoots; + + /** + * Formats the list of IUs according to the given string. Use ${property} for variable parts, + * e.g. ${org.eclipse.equinox.p2.name} for the IU's name. ID and version of an IU are available + * through ${id} and ${version}. + */ + @Parameter(property = "listFormat") + private String listFormat; + + /** + * Defines what profile to use for the actions. + */ + @Parameter(property = "profile") + private String profile; + + /** + * A list of properties in the form key=value pairs. Effective only when a new profile is + * created. For example org.eclipse.update.install.features=true to install the + * features into the product. + */ + @Parameter(property = "profileproperties") + private String profileproperties; + + @Parameter(property = "installFeatures") + private boolean installFeatures; + + /** + * Additional profile properties to set when materializing the product + */ + @Parameter + private Map properties; + + /** + * Path to a properties file containing a list of IU profile properties to set. + */ + @Parameter(property = "iuProfileproperties") + private File iuProfileproperties; + + /** + * Defines what flavor to use for a newly created profile. + */ + @Parameter(property = "flavor") + private String flavor; + + /** + * The location where the plug-ins and features will be stored. Effective only when a new + * profile is created. + */ + @Parameter(property = "bundlepool") + private File bundlepool; + + /** + * The OS to use when the profile is created. + */ + @Parameter(property = "p2.os") + private String p2os; + + /** + * The windowing system to use when the profile is created. + */ + @Parameter(property = "p2.ws") + private String p2ws; + + /** + * The architecture to use when the profile is created. + */ + @Parameter(property = "p2.arch") + private String p2arch; + + /** + * The language to use when the profile is created. + */ + @Parameter(property = "p2.nl") + private String p2nl; + + /** + * Indicates that the product resulting from the installation can be moved. Effective only when + * a new profile is created. + */ + @Parameter(property = "roaming") + private boolean roaming; + + /** + * Use a shared location for the install. The defaults to ${user.home}/.p2 + */ + @Parameter(property = "shared") + private String shared; + + /** + * Tag the provisioning operation for easy referencing when reverting. + */ + @Parameter(property = "tag") + private String tag; + + /** + * Only verify that the actions can be performed. Don't actually install or remove anything. + */ + @Parameter(property = "verifyOnly") + private boolean verifyOnly; + + /** + * Only download the artifacts. + */ + @Parameter(property = "downloadOnly") + private boolean downloadOnly; + + /** + * Follow repository references. + */ + @Parameter(property = "followReferences") + private boolean followReferences; + + /** + * Whether to print detailed information about the content trust. + */ + @Parameter(property = "verboseTrust") + private boolean verboseTrust; + + /** + * Whether to trust each artifact only if it is jar-signed or PGP-signed. + */ + @Parameter(property = "trustSignedContentOnly") + private boolean trustSignedContentOnly; + + /** + * comma separated list of the authorities from which repository content, including repository + * metadata, is trusted. An empty value will reject all remote connections. + */ + @Parameter(property = "trustedAuthorities") + private String trustedAuthorities; + + /** + * comma separated list of the fingerprints of PGP keys to trust as signers of artifacts. An + * empty value will reject all PGP keys. + */ + @Parameter(property = "trustedPGPKeys") + private String trustedPGPKeys; + + /** + * The SHA-256 'fingerprints' of unanchored certficates to trust as signers of artifacts. An + * empty value will reject all unanchored certificates. + */ + @Parameter(property = "trustedCertificates") + private String trustedCertificates; + + @Override + public void execute() throws MojoExecutionException, MojoFailureException { + //TODO should be able to control agent creation see https://github.com/eclipse-equinox/p2/pull/398 + //Until now we need to fetch a service to trigger loading of the internal osgi framework... + agent.getService(IArtifactRepositoryManager.class); + CommandLineArguments args = new CommandLineArguments(); + args.addNonNull("-destination", destination); + args.addNonNull("-metadatarepository", metadatarepositories); + args.addNonNull("-artifactrepository", artifactrepositories); + args.addNonNull("-repository", repositories); + args.addNotEmpty("-installIU", getUnitParameterList(installIUs, install), ","); + args.addNotEmpty("-uninstallIU", getUnitParameterList(uninstallIUs, uninstall), ","); + args.addNonNull("-revert", revert); + args.addFlagIfTrue("-purgeHistory", purgeHistory); + args.addFlagIfTrue("-list", list); + args.addFlagIfTrue("-listTags", listTags); + args.addFlagIfTrue("-listInstalledRoots", listInstalledRoots); + args.addNonNull("-listFormat", listFormat); + args.addNonNull("-profile", profile); + args.addNotEmpty("-profileproperties", getPropertyMap(profileproperties, properties), "=", ","); + args.addNonNull("-iuProfileproperties", iuProfileproperties); + args.addNonNull("-flavor", flavor); + args.addNonNull("-bundlepool", bundlepool); + args.addNonNull("-p2.os", p2os); + args.addNonNull("-p2.ws", p2ws); + args.addNonNull("-p2.arch", p2arch); + args.addNonNull("-p2.nl", p2nl); + args.addFlagIfTrue("-roaming", roaming); + args.addNonNull("-trustedAuthorities", trustedAuthorities); + if (shared != null) { + if (shared.isEmpty()) { + args.add("-shared"); + } else { + args.addNonNull("-shared", new File(shared)); + } + } + args.addNonNull("-tag", tag); + args.addFlagIfTrue("-verifyOnly", verifyOnly); + args.addFlagIfTrue("-downloadOnly", downloadOnly); + args.addFlagIfTrue("-followReferences", followReferences); + args.addFlagIfTrue("-verboseTrust", verboseTrust); + args.addFlagIfTrue("-trustSignedContentOnly", trustSignedContentOnly); + args.addNonNull("-trustedAuthorities", trustedAuthorities); + args.addNonNull("-trustedPGPKeys", trustedPGPKeys); + args.addNonNull("-trustedCertificates", trustedCertificates); + Object exitCode = new DirectorApplication().run(args.toArray()); + if (!(IApplication.EXIT_OK.equals(exitCode))) { + throw new MojoFailureException("Call to p2 director application failed with exit code " + exitCode + + ". Program arguments were: '" + args + "'."); + } + } + + private Map getPropertyMap(String csvPropertiesMap, Map properties) { + LinkedHashMap map = new LinkedHashMap(); + if (csvPropertiesMap != null) { + for (String keyValue : csvPropertiesMap.split(",")) { + String[] split = keyValue.split("="); + map.put(split[0].trim(), split[1].trim()); + } + } + if (properties != null) { + map.putAll(properties); + } + if (installFeatures) { + map.put("org.eclipse.update.install.features", "true"); + } + return map; + } + + private List getUnitParameterList(String csvlist, List units) { + List list = new ArrayList(); + if (csvlist != null) { + for (String iu : csvlist.split(",")) { + list.add(iu.trim()); + } + } + if (install != null) { + for (IU iu : install) { + String id = iu.id; + if (iu.feature) { + id += ".feature.group"; + } + if (iu.version != null) { + id += "/" + iu.version; + } + list.add(id); + } + } + return list; + } + + private void add(String key, String value, List args) { + if (metadatarepositories != null) { + args.add("-metadatarepository"); + args.add(metadatarepositories); + } + } + + public static final class IU { + String id; + String version; + boolean feature; + } + +}