Skip to content

Commit

Permalink
Detect meta-data from BND build maven projects
Browse files Browse the repository at this point in the history
currently we already generate a preliminary manifest for pde.bnd files
but the same would work as well for bundles build by the bnd maven
plugin. That way it would allow to participate even more close to the
usual Tycho dependency resolution mechanism.
  • Loading branch information
laeubi committed May 18, 2023
1 parent 0990f90 commit f542dbb
Show file tree
Hide file tree
Showing 6 changed files with 206 additions and 31 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@
import java.util.jar.Attributes;
import java.util.jar.JarFile;
import java.util.jar.Manifest;
import java.util.stream.Stream;

import org.apache.maven.artifact.Artifact;
import org.apache.maven.artifact.handler.manager.ArtifactHandlerManager;
Expand Down Expand Up @@ -58,6 +59,7 @@
import org.eclipse.osgi.framework.util.CaseInsensitiveDictionaryMap;
import org.eclipse.osgi.service.resolver.BundleDescription;
import org.eclipse.tycho.PackagingType;
import org.eclipse.tycho.ReactorProject;
import org.eclipse.tycho.helper.PluginRealmHelper;
import org.eclipse.tycho.p2maven.actions.AuthoredIUAction;
import org.eclipse.tycho.p2maven.actions.CategoryDependenciesAction;
Expand Down Expand Up @@ -280,6 +282,28 @@ public Collection<IInstallableUnit> getInstallableUnits(Artifact artifact) {
return artifactUnitMap.computeIfAbsent(artifact, x -> new ArtifactUnits()).getUnits(artifact);
}

/**
* Compute the additional provided units for a ReactorProject
*
* @param reactorProject
* @return a collection of units for the given reactor project
*/
public Collection<IInstallableUnit> getProvidedInstallableUnits(ReactorProject reactorProject) {
MavenProject mavenProject = reactorProject.adapt(MavenProject.class);
MavenSession mavenSession = reactorProject.adapt(MavenSession.class);
try {
return getProvider(mavenProject, mavenSession).stream().flatMap(provider -> {
try {
return provider.getInstallableUnits(mavenProject, mavenSession).stream();
} catch (CoreException e) {
return Stream.empty();
}
}).toList();
} catch (CoreException e) {
return List.of();
}
}

private Collection<InstallableUnitProvider> getProvider(MavenProject project, MavenSession mavenSession)
throws CoreException {
Set<InstallableUnitProvider> unitProviders = new HashSet<>(additionalUnitProviders.values());
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -13,31 +13,47 @@
package org.eclipse.tycho.core.bnd;

import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.StringReader;
import java.nio.charset.StandardCharsets;
import java.nio.file.Path;
import java.util.Collection;
import java.util.Collections;
import java.util.HashMap;
import java.util.Map;
import java.util.Optional;
import java.util.Properties;
import java.util.Set;
import java.util.concurrent.ConcurrentHashMap;
import java.util.jar.Manifest;

import org.apache.maven.artifact.Artifact;
import org.apache.maven.execution.MavenSession;
import org.apache.maven.model.Dependency;
import org.apache.maven.model.Plugin;
import org.apache.maven.model.PluginExecution;
import org.apache.maven.project.MavenProject;
import org.codehaus.plexus.component.annotations.Component;
import org.codehaus.plexus.component.annotations.Requirement;
import org.codehaus.plexus.logging.Logger;
import org.codehaus.plexus.util.xml.Xpp3Dom;
import org.codehaus.plexus.util.xml.Xpp3DomBuilder;
import org.eclipse.core.runtime.CoreException;
import org.eclipse.equinox.p2.metadata.IInstallableUnit;
import org.eclipse.tycho.TychoConstants;
import org.eclipse.tycho.core.TychoProjectManager;
import org.eclipse.tycho.core.maven.MavenDependenciesResolver;
import org.eclipse.tycho.p2maven.InstallableUnitGenerator;
import org.eclipse.tycho.resolver.InstallableUnitProvider;

import aQute.bnd.osgi.Analyzer;
import aQute.bnd.osgi.Builder;
import aQute.bnd.osgi.Constants;
import aQute.bnd.osgi.Jar;
import aQute.bnd.osgi.Processor;
import aQute.bnd.version.MavenVersion;
import aQute.bnd.version.Version;
import aQute.lib.manifest.ManifestUtil;

/**
Expand All @@ -53,6 +69,8 @@ public class PdeInstallableUnitProvider implements InstallableUnitProvider {
private TychoProjectManager projectManager;
@Requirement
private InstallableUnitGenerator installableUnitGenerator;
@Requirement
private MavenDependenciesResolver mavenDependenciesResolver;

private Map<MavenProject, Collection<IInstallableUnit>> cache = new ConcurrentHashMap<>();

Expand All @@ -62,26 +80,140 @@ public Collection<IInstallableUnit> getInstallableUnits(MavenProject project, Ma
return cache.computeIfAbsent(project, p -> {
Optional<Processor> bndTychoProject = projectManager.getBndTychoProject(project);
if (bndTychoProject.isPresent()) {
try (Processor processor = bndTychoProject.get(); Analyzer analyzer = new Analyzer(processor)) {
Jar jar = new Jar(project.getArtifactId());
analyzer.setJar(jar);
analyzer.addBasicPlugin(new SourceCodeAnalyzerPlugin(
project.getCompileSourceRoots().stream().map(Path::of).toList()));
analyzer.setProperty(Constants.NOEXTRAHEADERS, "true");
Manifest manifest = analyzer.calcManifest();
if (logger.isDebugEnabled()) {
ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
ManifestUtil.write(manifest, outputStream);
String str = new String(outputStream.toByteArray(), StandardCharsets.UTF_8);
logger.info("Generated preliminary manifest for " + project.getId() + ":\r\n" + str);
}
return installableUnitGenerator.getInstallableUnits(manifest);
try (Processor pr = bndTychoProject.get()) {
return generateWithProcessor(project, pr, project.getArtifacts());
} catch (Exception e) {
logger.warn("Can't determine additional units for " + project.getId(), e);
}
} else {
//bnd maven project?
Optional<Processor> bndPluginProcessor = getProcessor(project);
if (bndPluginProcessor.isPresent()) {
try (Processor pr = bndPluginProcessor.get()) {
if (pr.getProperty(Constants.BUNDLE_SYMBOLICNAME) == null) {
pr.setProperty(Constants.BUNDLE_SYMBOLICNAME, project.getArtifactId());
}
if (pr.getProperty(Constants.BUNDLE_VERSION) == null) {
Version version = new MavenVersion(project.getVersion()).getOSGiVersion();
pr.setProperty(Constants.BUNDLE_VERSION, version.toString());
}
Collection<Dependency> dependencies = collectInitial(project, new HashMap<String, Dependency>())
.values();
return generateWithProcessor(project, pr, mavenDependenciesResolver.resolve(project,
dependencies, Set.of(Artifact.SCOPE_COMPILE, Artifact.SCOPE_TEST), session));
} catch (Exception e) {
logger.warn("Can't determine additional units for " + project.getId(), e);
}
}
}
return Collections.emptyList();
});
}

private Optional<Processor> getProcessor(MavenProject project) {
if ("jar".equals(project.getPackaging())) {
Plugin plugin = project.getPlugin(Plugin.constructKey("biz.aQute.bnd", "bnd-maven-plugin"));
if (plugin != null) {
for (PluginExecution execution : plugin.getExecutions()) {
if (execution.getGoals().contains("bnd-process")) {
Xpp3Dom configuration = getConfig(execution.getConfiguration());
if (configuration != null) {
Properties props = new Properties();
Properties pp = project.getProperties();
for (String k : pp.stringPropertyNames()) {
props.setProperty(k, pp.getProperty(k));
}
File bndFile = getBndFile(configuration, project);
if (bndFile.exists()) {
try (FileInputStream stream = new FileInputStream(bndFile)) {
props.load(stream);
} catch (IOException e) {
}
} else {
Xpp3Dom bnd = configuration.getChild("bnd");
if (bnd != null) {
try {
props.load(new StringReader(bnd.getValue()));
} catch (IOException e) {
}
}
}
Processor processor = new Processor(props, false);
processor.setBase(project.getBasedir());
return Optional.of(processor);
}
}
}
}
}
return Optional.empty();
}

private File getBndFile(Xpp3Dom configuration, MavenProject project) {
Xpp3Dom bndfile = configuration.getChild("bndfile");
if (bndfile != null) {
return new File(bndfile.getValue());
}
return new File(project.getBasedir(), "bnd.bnd");
}

private static Xpp3Dom getConfig(Object configuration) {
if (configuration == null) {
return null;
}
if (configuration instanceof Xpp3Dom) {
return (Xpp3Dom) configuration;
}
try {
return Xpp3DomBuilder.build(new StringReader(configuration.toString()));
} catch (Exception e) {
return null;
}
}

private static Map<String, Dependency> collectInitial(MavenProject project, Map<String, Dependency> map) {
for (Dependency dependency : project.getDependencies()) {
map.putIfAbsent(dependency.getManagementKey(), dependency);
}
MavenProject parent = project.getParent();
if (parent != null) {
return collectInitial(parent, map);
}
return map;
}

private Collection<IInstallableUnit> generateWithProcessor(MavenProject project, Processor processor,
Collection<Artifact> artifacts) throws Exception {
try (Builder analyzer = new Builder(processor)) {
analyzer.setBase(project.getBasedir());
Jar jar = new Jar(project.getArtifactId());
analyzer.setJar(jar);
for (Artifact artifact : artifacts) {
File file = artifact.getFile();
if (file != null) {
try {
analyzer.addClasspath(file);
} catch (Exception e) {
continue;
}
}
}
analyzer.addBasicPlugin(
new SourceCodeAnalyzerPlugin(project.getCompileSourceRoots().stream().map(Path::of).toList()));
analyzer.setProperty(Constants.NOEXTRAHEADERS, "true");
analyzer.build();
Manifest manifest = jar.getManifest();
if (manifest == null) {
return Collections.emptyList();
}
if (logger.isDebugEnabled()) {
ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
ManifestUtil.write(manifest, outputStream);
String str = new String(outputStream.toByteArray(), StandardCharsets.UTF_8);
logger.debug("Generated preliminary manifest for " + project.getId() + ":\r\n" + str);
}
return installableUnitGenerator.getInstallableUnits(manifest);
}
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -97,7 +97,19 @@ public void setupProject(MavenSession session, MavenProject project) {
ReactorProject reactorProject = DefaultReactorProject.adapt(project);
reactorProject.setContextValue(CTX_MAVEN_SESSION, session);
reactorProject.setContextValue(CTX_MAVEN_PROJECT, project);
reactorProject.setContextValue(CTX_INITIAL_MAVEN_DEPENDENCIES, List.copyOf(project.getDependencies()));
reactorProject.setContextValue(CTX_INITIAL_MAVEN_DEPENDENCIES,
List.copyOf(collectInitial(project, new HashMap<String, Dependency>()).values()));
}

private Map<String, Dependency> collectInitial(MavenProject project, Map<String, Dependency> map) {
for (Dependency dependency : project.getDependencies()) {
map.putIfAbsent(dependency.getManagementKey(), dependency);
}
MavenProject parent = project.getParent();
if (parent != null) {
return collectInitial(parent, map);
}
return map;
}

protected TargetEnvironment[] getEnvironments(ReactorProject project, TargetEnvironment environment) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -60,7 +60,6 @@
import org.eclipse.tycho.TargetEnvironment;
import org.eclipse.tycho.TargetPlatform;
import org.eclipse.tycho.TychoConstants;
import org.eclipse.tycho.core.TargetPlatformConfiguration;
import org.eclipse.tycho.core.ee.shared.ExecutionEnvironmentConfiguration;
import org.eclipse.tycho.core.ee.shared.ExecutionEnvironmentConfigurationStub;
import org.eclipse.tycho.core.resolver.DefaultP2ResolutionResult;
Expand Down Expand Up @@ -225,11 +224,7 @@ protected P2ResolutionResult resolveDependencies(Collection<IInstallableUnit> ro
Collection<IInstallableUnit> newState;
try {
if (project != null && p2ResolverFactoryImpl != null && pomDependencies != PomDependencies.ignore) {
TargetPlatformConfiguration configuration = p2ResolverFactoryImpl.getProjectManager()
.getTargetPlatformConfiguration(project);
if (!configuration.isRequireEagerResolve()) {
data.setAdditionalUnitStore(p2ResolverFactoryImpl.getPomUnits().createPomQueryable(project));
}
data.setAdditionalUnitStore(p2ResolverFactoryImpl.getPomUnits().createPomQueryable(project));
}
newState = strategy.resolve(environment, monitor);
} catch (ResolverException e) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -114,11 +114,26 @@ private IQueryable<IInstallableUnit> getPomIUs() {
for (Artifact artifact : initalArtifacts) {
IArtifactFacade facade = facadeMap.get(artifact);
getArtifactStream(artifact, facade).forEach(a -> {
if (a.getFile() == null || configuration.isExcluded(a.getGroupId(), a.getArtifactId())) {
logger.debug("Skipp artifact " + a);
Collection<IInstallableUnit> units;
if (a.getFile() == null) {
if (facadeMap.get(a) instanceof ReactorProjectFacade reactorFacade) {
ReactorProject prj = reactorFacade.getReactorProject();
units = generator.getProvidedInstallableUnits(prj);
if (units.isEmpty()) {
logger.debug("Skip artifact " + a
+ " because it is not resolved and can't gather any units for it...");
return;
}
} else {
logger.debug("Skip artifact " + a + " because it is not resolved!");
return;
}
} else if (configuration.isExcluded(a.getGroupId(), a.getArtifactId())) {
logger.debug("Skip artifact " + a + " because it is excluded...");
return;
} else {
units = generator.getInstallableUnits(a);
}
Collection<IInstallableUnit> units = generator.getInstallableUnits(a);
logger.debug("artifact " + a + " maps to " + units);
IArtifactFacade artifactFacade;
if (a.hasClassifier()) {
Expand Down Expand Up @@ -195,11 +210,7 @@ private void addBuildReactorProjects(Collection<Artifact> initalArtifacts) {
if (mavenSession != null) {
for (MavenProject mavenProject : mavenSession.getProjects()) {
if ("jar".equals(mavenProject.getPackaging())) {
Artifact artifact = mavenProject.getArtifact();
File file = artifact.getFile();
if (file != null && file.exists()) {
initalArtifacts.add(artifact);
}
initalArtifacts.add(mavenProject.getArtifact());
}
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -372,8 +372,9 @@ private Map<IInstallableUnit, ReactorProjectIdentities> getPreliminaryReactorPro
for (ReactorProject project : reactorProjects) {
Set<IInstallableUnit> projectIUs = project.getDependencyMetadata(DependencyMetadataType.INITIAL);

if (projectIUs == null)
if (projectIUs == null) {
continue;
}
for (IInstallableUnit iu : projectIUs) {
ReactorProjectIdentities identities = project.getIdentities();
ReactorProjectIdentities otherOrigin = reactorUIs.put(iu, identities);
Expand Down

0 comments on commit f542dbb

Please sign in to comment.