Skip to content

Commit

Permalink
Automatically discover dependencies from a published product
Browse files Browse the repository at this point in the history
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.
  • Loading branch information
laeubi committed Feb 10, 2024
1 parent 62788b4 commit 8edc7b4
Show file tree
Hide file tree
Showing 13 changed files with 223 additions and 32 deletions.
Expand Up @@ -262,6 +262,10 @@ private List<IPublisherAction> getPublisherActions(String packaging, File basedi
return actions;
}

public Collection<IInstallableUnit> getInstallableUnits(IProductDescriptor productDescriptor) throws CoreException {
return publisher.publishMetadata(List.of(new ProductDependenciesAction(productDescriptor)));
}

public Collection<IInstallableUnit> getInstallableUnits(Manifest manifest) {
Attributes mainAttributes = manifest.getMainAttributes();
CaseInsensitiveDictionaryMap<String, String> headers = new CaseInsensitiveDictionaryMap<>(
Expand Down
Expand Up @@ -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;
Expand All @@ -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);
}

Expand Down
Expand Up @@ -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<IInstallableUnit> getDependencyMetadata(DependencyMetadataType type);
Expand Down
@@ -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<IInstallableUnit> units;
private DependencyMetadataType dependencyMetadataType;

public UnmodifiableDependencyMetadata(Set<IInstallableUnit> units, DependencyMetadataType type) {
this.dependencyMetadataType = type;
this.units = Collections.unmodifiableSet(units);
}

@Override
public Set<IInstallableUnit> getDependencyMetadata(DependencyMetadataType type) {
if (dependencyMetadataType == type) {
return getDependencyMetadata();
}
return Set.of();
}

@Override
public Set<IInstallableUnit> getDependencyMetadata() {
return units;
}

@Override
public void setDependencyMetadata(DependencyMetadataType type, Collection<IInstallableUnit> units) {
throw new UnsupportedOperationException();
}

}
Expand Up @@ -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;
Expand All @@ -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;

Expand All @@ -49,28 +50,8 @@ public Map<String, IDependencyMetadata> getDependencyMetadata(MavenSession sessi
if (units.isEmpty()) {
return Collections.emptyMap();
}
IDependencyMetadata metadata = new IDependencyMetadata() {

@Override
public Set<IInstallableUnit> getDependencyMetadata(DependencyMetadataType type) {
if (type == DependencyMetadataType.INITIAL) {
return getDependencyMetadata();
}
return Collections.emptySet();
}

@Override
public Set<IInstallableUnit> getDependencyMetadata() {
return units;
}

@Override
public void setDependencyMetadata(DependencyMetadataType type, Collection<IInstallableUnit> units) {
throw new UnsupportedOperationException();
}

};
return Map.of(TychoConstants.PDE_BND, metadata);
return Map.of(TychoConstants.PDE_BND,
new UnmodifiableDependencyMetadata(units, DependencyMetadataType.INITIAL));
}

}
Expand Up @@ -122,7 +122,7 @@ public List<Category> loadCategories(final File categoriesDirectory) {
* @param project
* @return
*/
protected List<ProductConfiguration> loadProducts(final ReactorProject project) {
public static List<ProductConfiguration> loadProducts(final ReactorProject project) {
List<ProductConfiguration> products = new ArrayList<>();
for (File file : getProductFiles(project)) {
try {
Expand Down Expand Up @@ -153,7 +153,7 @@ private List<File> 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<File> getProductFiles(final ReactorProject project) {
public static List<File> getProductFiles(final ReactorProject project) {
final File projectLocation = project.getBasedir();
return getProductFiles(projectLocation);
}
Expand All @@ -165,7 +165,7 @@ public List<File> 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<File> getProductFiles(final File basedir) {
public static List<File> getProductFiles(final File basedir) {
final List<File> files = new ArrayList<>();

// noinspection ConstantConditions
Expand Down
Expand Up @@ -354,6 +354,13 @@ private DependencyArtifacts doResolveDependencies(MavenSession session, MavenPro
for (IRequirement requirement : resolverConfiguration.getAdditionalRequirements()) {
resolver.addRequirement(requirement);
}
Set<IInstallableUnit> 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));
Expand Down
Expand Up @@ -21,6 +21,7 @@
<exportedPackage>org.eclipse.equinox.p2.repository.artifact</exportedPackage>
<exportedPackage>org.eclipse.equinox.p2.repository.metadata</exportedPackage>
<exportedPackage>org.eclipse.equinox.internal.p2.metadata</exportedPackage>
<exportedPackage>org.eclipse.equinox.internal.p2.publisher.eclipse</exportedPackage>
<!-- <exportedPackage>org.eclipse.core.runtime</exportedPackage>
<exportedPackage>org.eclipse.core.resources</exportedPackage> -->
<!-- other -->
Expand Down
17 changes: 17 additions & 0 deletions tycho-p2-publisher-plugin/pom.xml
Expand Up @@ -56,4 +56,21 @@
</dependency>
</dependencies>


<build>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-plugin-plugin</artifactId>
<!-- workaround for https://issues.apache.org/jira/browse/MPLUGIN-450 -->
<configuration>
<goalPrefix>tycho-p2-publisher</goalPrefix>
</configuration>
</plugin>
<plugin>
<groupId>org.codehaus.plexus</groupId>
<artifactId>plexus-component-metadata</artifactId>
</plugin>
</plugins>
</build>
</project>
@@ -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<IInstallableUnit> getInstallableUnits(MavenProject project, MavenSession session)
throws CoreException {
return getProductUnits(installableUnitGenerator, project);
}

static Set<IInstallableUnit> 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<File> productFiles = EclipseRepositoryProject.getProductFiles(project.getBasedir());
if (productFiles.isEmpty()) {
return Set.of();
}
List<IRequirement> requirements = new ArrayList<>();
for (File file : productFiles) {
try {
Collection<IInstallableUnit> 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));
}

}
@@ -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<String, IDependencyMetadata> getDependencyMetadata(MavenSession session, MavenProject project,
List<TargetEnvironment> environments, OptionalResolutionAction optionalAction) {

Set<IInstallableUnit> productUnits = PublishProductInstallableUnitProvider
.getProductUnits(installableUnitGenerator, project);
if (productUnits.isEmpty()) {
return Map.of();
}
return Map.of(PublishProductInstallableUnitProvider.HINT,
new UnmodifiableDependencyMetadata(productUnits, DependencyMetadataType.ADDITIONAL));
}

}
Expand Up @@ -67,8 +67,8 @@

/**
* <p>
* Publishes all product definitions files (<code>*.product</code>) that are present in the root of the
* project.
* Publishes all product definitions files (<code>*.product</code>) that are present in the root of
* the project.
* </p>
*
* @see https://wiki.eclipse.org/Equinox/p2/Publisher
Expand Down Expand Up @@ -113,7 +113,7 @@ protected Collection<DependencySeed> publishContent(PublisherServiceFactory publ

List<DependencySeed> 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()) {
Expand Down
Expand Up @@ -36,7 +36,7 @@ static Collection<IInstallableUnit> createIU(Stream<IRequirement> requirements,
return createIU(requirements.toList(), idPrefix);
}

static Collection<IInstallableUnit> createIU(List<IRequirement> requirements, String idPrefix) {
static Collection<IInstallableUnit> createIU(Collection<IRequirement> requirements, String idPrefix) {
if (requirements.isEmpty()) {
return Collections.emptyList();
}
Expand Down

0 comments on commit 8edc7b4

Please sign in to comment.