From 8c424e1622f59e25a667e806852c563e9d0ae4ec Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Christoph=20L=C3=A4ubrich?= Date: Sat, 10 Feb 2024 14:51:40 +0100 Subject: [PATCH] Automatically discover dependencies from a published product Currently the publis-products mojo is only fully usable in the eclipse-repository packaging type but there are other use-cases where it becomes interesting to publish the product metadata and further use it, for example in an p2 installed runtime. Even though it works to use the mojos it is quite inconvenient as one has to specify all its requirements manually as extra dependencies. This adds a new PublishProduct P2 unit providers that collect the dependencies from a product and supply them automatically as requirements to the project so the mojo can be used without having missed dependencies from the target. --- .../p2maven/InstallableUnitGenerator.java | 4 + .../tycho/p2maven/actions/ProductFile2.java | 7 +- .../eclipse/tycho/IDependencyMetadata.java | 7 +- .../tycho/UnmodifiableDependencyMetadata.java | 49 +++++++++++ .../tycho/core/bnd/BndP2MetadataProvider.java | 27 +----- .../osgitools/EclipseRepositoryProject.java | 6 +- .../p2resolver/P2DependencyResolver.java | 7 ++ .../resources/META-INF/maven/extension.xml | 1 + tycho-p2-publisher-plugin/pom.xml | 17 ++++ ...PublishProductInstallableUnitProvider.java | 83 +++++++++++++++++++ .../PublishProductMetadataProvider.java | 39 +++++++++ .../p2/publisher/PublishProductMojo.java | 6 +- .../resolver/InstallableUnitProvider.java | 2 +- 13 files changed, 223 insertions(+), 32 deletions(-) create mode 100644 tycho-api/src/main/java/org/eclipse/tycho/UnmodifiableDependencyMetadata.java create mode 100644 tycho-p2-publisher-plugin/src/main/java/org/eclipse/tycho/plugins/p2/publisher/PublishProductInstallableUnitProvider.java create mode 100644 tycho-p2-publisher-plugin/src/main/java/org/eclipse/tycho/plugins/p2/publisher/PublishProductMetadataProvider.java diff --git a/p2-maven-plugin/src/main/java/org/eclipse/tycho/p2maven/InstallableUnitGenerator.java b/p2-maven-plugin/src/main/java/org/eclipse/tycho/p2maven/InstallableUnitGenerator.java index 5d89c5edf9..4bd1b96a73 100644 --- a/p2-maven-plugin/src/main/java/org/eclipse/tycho/p2maven/InstallableUnitGenerator.java +++ b/p2-maven-plugin/src/main/java/org/eclipse/tycho/p2maven/InstallableUnitGenerator.java @@ -262,6 +262,10 @@ private List getPublisherActions(String packaging, File basedi return actions; } + public Collection getInstallableUnits(IProductDescriptor productDescriptor) throws CoreException { + return publisher.publishMetadata(List.of(new ProductDependenciesAction(productDescriptor))); + } + public Collection getInstallableUnits(Manifest manifest) { Attributes mainAttributes = manifest.getMainAttributes(); CaseInsensitiveDictionaryMap headers = new CaseInsensitiveDictionaryMap<>( diff --git a/p2-maven-plugin/src/main/java/org/eclipse/tycho/p2maven/actions/ProductFile2.java b/p2-maven-plugin/src/main/java/org/eclipse/tycho/p2maven/actions/ProductFile2.java index b2c55cfe61..a2ad68d153 100644 --- a/p2-maven-plugin/src/main/java/org/eclipse/tycho/p2maven/actions/ProductFile2.java +++ b/p2-maven-plugin/src/main/java/org/eclipse/tycho/p2maven/actions/ProductFile2.java @@ -12,6 +12,7 @@ *******************************************************************************/ package org.eclipse.tycho.p2maven.actions; +import java.io.File; import java.util.List; import org.eclipse.equinox.internal.p2.publisher.eclipse.ProductFile; @@ -26,7 +27,11 @@ public class ProductFile2 extends ProductFile { protected static final String ATTRIBUTE_ARCH = "arch"; - public ProductFile2(String location) throws Exception { + public ProductFile2(File location) throws Exception { + this(location.getAbsolutePath()); + } + + public ProductFile2(String location) throws Exception { super(location); } diff --git a/tycho-api/src/main/java/org/eclipse/tycho/IDependencyMetadata.java b/tycho-api/src/main/java/org/eclipse/tycho/IDependencyMetadata.java index ced8511a5c..6aaf61e402 100644 --- a/tycho-api/src/main/java/org/eclipse/tycho/IDependencyMetadata.java +++ b/tycho-api/src/main/java/org/eclipse/tycho/IDependencyMetadata.java @@ -20,7 +20,12 @@ public interface IDependencyMetadata { enum DependencyMetadataType { - INITIAL, SEED, RESOLVE; + INITIAL, SEED, RESOLVE, + /** + * Additional metadata describing requirements like defined in the targets platform + * dependency resolution + */ + ADDITIONAL; } Set getDependencyMetadata(DependencyMetadataType type); diff --git a/tycho-api/src/main/java/org/eclipse/tycho/UnmodifiableDependencyMetadata.java b/tycho-api/src/main/java/org/eclipse/tycho/UnmodifiableDependencyMetadata.java new file mode 100644 index 0000000000..4b2738d2a2 --- /dev/null +++ b/tycho-api/src/main/java/org/eclipse/tycho/UnmodifiableDependencyMetadata.java @@ -0,0 +1,49 @@ +/******************************************************************************* + * Copyright (c) 2024 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; + +import java.util.Collection; +import java.util.Collections; +import java.util.Set; + +import org.eclipse.equinox.p2.metadata.IInstallableUnit; + +public class UnmodifiableDependencyMetadata implements IDependencyMetadata { + + private Set units; + private DependencyMetadataType dependencyMetadataType; + + public UnmodifiableDependencyMetadata(Set units, DependencyMetadataType type) { + this.dependencyMetadataType = type; + this.units = Collections.unmodifiableSet(units); + } + + @Override + public Set getDependencyMetadata(DependencyMetadataType type) { + if (dependencyMetadataType == type) { + return getDependencyMetadata(); + } + return Set.of(); + } + + @Override + public Set getDependencyMetadata() { + return units; + } + + @Override + public void setDependencyMetadata(DependencyMetadataType type, Collection units) { + throw new UnsupportedOperationException(); + } + +} diff --git a/tycho-core/src/main/java/org/eclipse/tycho/core/bnd/BndP2MetadataProvider.java b/tycho-core/src/main/java/org/eclipse/tycho/core/bnd/BndP2MetadataProvider.java index 74b0e5324e..1a5de009b4 100644 --- a/tycho-core/src/main/java/org/eclipse/tycho/core/bnd/BndP2MetadataProvider.java +++ b/tycho-core/src/main/java/org/eclipse/tycho/core/bnd/BndP2MetadataProvider.java @@ -12,7 +12,6 @@ *******************************************************************************/ package org.eclipse.tycho.core.bnd; -import java.util.Collection; import java.util.Collections; import java.util.List; import java.util.Map; @@ -25,9 +24,11 @@ import org.eclipse.core.runtime.CoreException; import org.eclipse.equinox.p2.metadata.IInstallableUnit; import org.eclipse.tycho.IDependencyMetadata; +import org.eclipse.tycho.IDependencyMetadata.DependencyMetadataType; import org.eclipse.tycho.OptionalResolutionAction; import org.eclipse.tycho.TargetEnvironment; import org.eclipse.tycho.TychoConstants; +import org.eclipse.tycho.UnmodifiableDependencyMetadata; import org.eclipse.tycho.resolver.InstallableUnitProvider; import org.eclipse.tycho.resolver.P2MetadataProvider; @@ -49,28 +50,8 @@ public Map getDependencyMetadata(MavenSession sessi if (units.isEmpty()) { return Collections.emptyMap(); } - IDependencyMetadata metadata = new IDependencyMetadata() { - - @Override - public Set getDependencyMetadata(DependencyMetadataType type) { - if (type == DependencyMetadataType.INITIAL) { - return getDependencyMetadata(); - } - return Collections.emptySet(); - } - - @Override - public Set getDependencyMetadata() { - return units; - } - - @Override - public void setDependencyMetadata(DependencyMetadataType type, Collection units) { - throw new UnsupportedOperationException(); - } - - }; - return Map.of(TychoConstants.PDE_BND, metadata); + return Map.of(TychoConstants.PDE_BND, + new UnmodifiableDependencyMetadata(units, DependencyMetadataType.INITIAL)); } } diff --git a/tycho-core/src/main/java/org/eclipse/tycho/core/osgitools/EclipseRepositoryProject.java b/tycho-core/src/main/java/org/eclipse/tycho/core/osgitools/EclipseRepositoryProject.java index 0dd45d280d..7ace0a21f4 100644 --- a/tycho-core/src/main/java/org/eclipse/tycho/core/osgitools/EclipseRepositoryProject.java +++ b/tycho-core/src/main/java/org/eclipse/tycho/core/osgitools/EclipseRepositoryProject.java @@ -122,7 +122,7 @@ public List loadCategories(final File categoriesDirectory) { * @param project * @return */ - protected List loadProducts(final ReactorProject project) { + public static List loadProducts(final ReactorProject project) { List products = new ArrayList<>(); for (File file : getProductFiles(project)) { try { @@ -153,7 +153,7 @@ private List getCategoryFiles(final File basedir) { * the project containing the product files * @return The list of product files to parse for an eclipse-repository project */ - public List getProductFiles(final ReactorProject project) { + public static List getProductFiles(final ReactorProject project) { final File projectLocation = project.getBasedir(); return getProductFiles(projectLocation); } @@ -165,7 +165,7 @@ public List getProductFiles(final ReactorProject project) { * the directory containing the product files * @return The list of product files to parse for an eclipse-repository project */ - public List getProductFiles(final File basedir) { + public static List getProductFiles(final File basedir) { final List files = new ArrayList<>(); // noinspection ConstantConditions diff --git a/tycho-core/src/main/java/org/eclipse/tycho/p2resolver/P2DependencyResolver.java b/tycho-core/src/main/java/org/eclipse/tycho/p2resolver/P2DependencyResolver.java index 54f6d7473a..4242cf30d2 100644 --- a/tycho-core/src/main/java/org/eclipse/tycho/p2resolver/P2DependencyResolver.java +++ b/tycho-core/src/main/java/org/eclipse/tycho/p2resolver/P2DependencyResolver.java @@ -354,6 +354,13 @@ private DependencyArtifacts doResolveDependencies(MavenSession session, MavenPro for (IRequirement requirement : resolverConfiguration.getAdditionalRequirements()) { resolver.addRequirement(requirement); } + Set additionalDependencyMetadata = DefaultReactorProject.adapt(project) + .getDependencyMetadata(DependencyMetadataType.ADDITIONAL); + for (IInstallableUnit unit : additionalDependencyMetadata) { + for (IRequirement requirement : unit.getRequirements()) { + resolver.addRequirement(requirement); + } + } } BuildProperties buildProperties = buildPropertiesParser.parse(DefaultReactorProject.adapt(project)); diff --git a/tycho-maven-plugin/src/main/resources/META-INF/maven/extension.xml b/tycho-maven-plugin/src/main/resources/META-INF/maven/extension.xml index e90dd7588a..1648eaff0f 100644 --- a/tycho-maven-plugin/src/main/resources/META-INF/maven/extension.xml +++ b/tycho-maven-plugin/src/main/resources/META-INF/maven/extension.xml @@ -21,6 +21,7 @@ org.eclipse.equinox.p2.repository.artifact org.eclipse.equinox.p2.repository.metadata org.eclipse.equinox.internal.p2.metadata + org.eclipse.equinox.internal.p2.publisher.eclipse diff --git a/tycho-p2-publisher-plugin/pom.xml b/tycho-p2-publisher-plugin/pom.xml index c6c5c95e6c..62483c2782 100644 --- a/tycho-p2-publisher-plugin/pom.xml +++ b/tycho-p2-publisher-plugin/pom.xml @@ -56,4 +56,21 @@ + + + + + org.apache.maven.plugins + maven-plugin-plugin + + + tycho-p2-publisher + + + + org.codehaus.plexus + plexus-component-metadata + + + diff --git a/tycho-p2-publisher-plugin/src/main/java/org/eclipse/tycho/plugins/p2/publisher/PublishProductInstallableUnitProvider.java b/tycho-p2-publisher-plugin/src/main/java/org/eclipse/tycho/plugins/p2/publisher/PublishProductInstallableUnitProvider.java new file mode 100644 index 0000000000..b3ecfa3488 --- /dev/null +++ b/tycho-p2-publisher-plugin/src/main/java/org/eclipse/tycho/plugins/p2/publisher/PublishProductInstallableUnitProvider.java @@ -0,0 +1,83 @@ +/******************************************************************************* + * Copyright (c) 2024 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.publisher; + +import java.io.File; +import java.util.ArrayList; +import java.util.Collection; +import java.util.HashSet; +import java.util.List; +import java.util.Set; + +import org.apache.maven.execution.MavenSession; +import org.apache.maven.model.Plugin; +import org.apache.maven.project.MavenProject; +import org.codehaus.plexus.component.annotations.Component; +import org.codehaus.plexus.component.annotations.Requirement; +import org.eclipse.core.runtime.CoreException; +import org.eclipse.equinox.p2.metadata.IInstallableUnit; +import org.eclipse.equinox.p2.metadata.IRequirement; +import org.eclipse.tycho.PackagingType; +import org.eclipse.tycho.core.osgitools.EclipseRepositoryProject; +import org.eclipse.tycho.p2maven.InstallableUnitGenerator; +import org.eclipse.tycho.p2maven.actions.ProductFile2; +import org.eclipse.tycho.resolver.InstallableUnitProvider; + +@Component(role = InstallableUnitProvider.class, hint = PublishProductInstallableUnitProvider.HINT) +public class PublishProductInstallableUnitProvider implements InstallableUnitProvider { + + static final String HINT = "publish-products"; + + @Requirement + private InstallableUnitGenerator installableUnitGenerator; + + @Override + public Collection getInstallableUnits(MavenProject project, MavenSession session) + throws CoreException { + return getProductUnits(installableUnitGenerator, project); + } + + static Set getProductUnits(InstallableUnitGenerator installableUnitGenerator, + MavenProject project) { + if (PackagingType.TYPE_ECLIPSE_REPOSITORY.equals(project.getPackaging())) { + //This is already handled there... + //TODO can we merge the both ways to determine the requirements? + return Set.of(); + } + Plugin plugin = project.getPlugin("org.eclipse.tycho:tycho-p2-publisher-plugin"); + if (plugin == null || plugin.getExecutions().isEmpty()) { + return Set.of(); + } + List productFiles = EclipseRepositoryProject.getProductFiles(project.getBasedir()); + if (productFiles.isEmpty()) { + return Set.of(); + } + List requirements = new ArrayList<>(); + for (File file : productFiles) { + try { + Collection units = installableUnitGenerator + .getInstallableUnits(new ProductFile2(file)); + for (IInstallableUnit unit : units) { + requirements.addAll(unit.getRequirements()); + } + } catch (CoreException e) { + } catch (Exception e) { + } + } + if (requirements.isEmpty()) { + return Set.of(); + } + return new HashSet<>(InstallableUnitProvider.createIU(requirements, HINT)); + } + +} diff --git a/tycho-p2-publisher-plugin/src/main/java/org/eclipse/tycho/plugins/p2/publisher/PublishProductMetadataProvider.java b/tycho-p2-publisher-plugin/src/main/java/org/eclipse/tycho/plugins/p2/publisher/PublishProductMetadataProvider.java new file mode 100644 index 0000000000..46396a34c3 --- /dev/null +++ b/tycho-p2-publisher-plugin/src/main/java/org/eclipse/tycho/plugins/p2/publisher/PublishProductMetadataProvider.java @@ -0,0 +1,39 @@ +package org.eclipse.tycho.plugins.p2.publisher; + +import java.util.List; +import java.util.Map; +import java.util.Set; + +import org.apache.maven.execution.MavenSession; +import org.apache.maven.project.MavenProject; +import org.codehaus.plexus.component.annotations.Component; +import org.codehaus.plexus.component.annotations.Requirement; +import org.eclipse.equinox.p2.metadata.IInstallableUnit; +import org.eclipse.tycho.IDependencyMetadata; +import org.eclipse.tycho.IDependencyMetadata.DependencyMetadataType; +import org.eclipse.tycho.OptionalResolutionAction; +import org.eclipse.tycho.TargetEnvironment; +import org.eclipse.tycho.UnmodifiableDependencyMetadata; +import org.eclipse.tycho.p2maven.InstallableUnitGenerator; +import org.eclipse.tycho.resolver.P2MetadataProvider; + +@Component(role = P2MetadataProvider.class, hint = PublishProductInstallableUnitProvider.HINT) +public class PublishProductMetadataProvider implements P2MetadataProvider { + + @Requirement + private InstallableUnitGenerator installableUnitGenerator; + + @Override + public Map getDependencyMetadata(MavenSession session, MavenProject project, + List environments, OptionalResolutionAction optionalAction) { + + Set productUnits = PublishProductInstallableUnitProvider + .getProductUnits(installableUnitGenerator, project); + if (productUnits.isEmpty()) { + return Map.of(); + } + return Map.of(PublishProductInstallableUnitProvider.HINT, + new UnmodifiableDependencyMetadata(productUnits, DependencyMetadataType.ADDITIONAL)); + } + +} diff --git a/tycho-p2-publisher-plugin/src/main/java/org/eclipse/tycho/plugins/p2/publisher/PublishProductMojo.java b/tycho-p2-publisher-plugin/src/main/java/org/eclipse/tycho/plugins/p2/publisher/PublishProductMojo.java index a031d19d1a..33665bacbb 100644 --- a/tycho-p2-publisher-plugin/src/main/java/org/eclipse/tycho/plugins/p2/publisher/PublishProductMojo.java +++ b/tycho-p2-publisher-plugin/src/main/java/org/eclipse/tycho/plugins/p2/publisher/PublishProductMojo.java @@ -67,8 +67,8 @@ /** *

- * Publishes all product definitions files (*.product) that are present in the root of the - * project. + * Publishes all product definitions files (*.product) that are present in the root of + * the project. *

* * @see https://wiki.eclipse.org/Equinox/p2/Publisher @@ -113,7 +113,7 @@ protected Collection publishContent(PublisherServiceFactory publ List seeds = new ArrayList<>(); boolean hasLaunchers = false; - for (final File productFile : eclipseRepositoryProject.getProductFiles(productsDirectory)) { + for (final File productFile : EclipseRepositoryProject.getProductFiles(productsDirectory)) { try { ProductConfiguration productConfiguration = ProductConfiguration.read(productFile); if (productConfiguration.getId() == null || productConfiguration.getId().isEmpty()) { diff --git a/tycho-spi/src/main/java/org/eclipse/tycho/resolver/InstallableUnitProvider.java b/tycho-spi/src/main/java/org/eclipse/tycho/resolver/InstallableUnitProvider.java index 936f4a2906..1fc3581933 100644 --- a/tycho-spi/src/main/java/org/eclipse/tycho/resolver/InstallableUnitProvider.java +++ b/tycho-spi/src/main/java/org/eclipse/tycho/resolver/InstallableUnitProvider.java @@ -36,7 +36,7 @@ static Collection createIU(Stream requirements, return createIU(requirements.toList(), idPrefix); } - static Collection createIU(List requirements, String idPrefix) { + static Collection createIU(Collection requirements, String idPrefix) { if (requirements.isEmpty()) { return Collections.emptyList(); }