Skip to content

Commit

Permalink
Fix #626 - Classpath computation must take fragments into account
Browse files Browse the repository at this point in the history
- pass fragments of selected dependencies to the classpath computation
- add an integration test to proof the problem is fixed
  • Loading branch information
laeubi committed Feb 4, 2022
1 parent 1f15e7e commit 774e135
Show file tree
Hide file tree
Showing 17 changed files with 317 additions and 31 deletions.
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
/*******************************************************************************
* Copyright (c) 2008, 2011 Sonatype Inc. and others.
* Copyright (c) 2008, 2022 Sonatype Inc. 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
Expand All @@ -9,6 +9,7 @@
*
* Contributors:
* Sonatype Inc. - initial API and implementation
* Christoph Läubrich - Issue #626 - Classpath computation must take fragments into account
*******************************************************************************/
package org.eclipse.tycho.artifacts;

Expand Down Expand Up @@ -44,6 +45,12 @@ public interface DependencyArtifacts {
*/
public List<ArtifactDescriptor> getArtifacts();

/**
*
* @return additional fragments that might be attached to the artifacts
*/
Collection<ArtifactDescriptor> getFragments();

/**
* Returns all artifacts of the given type.
*/
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@
public final class ArtifactType {

public static final String TYPE_ECLIPSE_PLUGIN = "eclipse-plugin";
public static final String TYPE_BUNDLE_FRAGMENT = "bundle-fragment";
public static final String TYPE_ECLIPSE_TEST_PLUGIN = "eclipse-test-plugin";
public static final String TYPE_ECLIPSE_FEATURE = "eclipse-feature";
public static final String TYPE_ECLIPSE_PRODUCT = "eclipse-product";
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
/*******************************************************************************
* Copyright (c) 2011, 2014 Sonatype Inc. and others.
* Copyright (c) 2011, 2022 Sonatype Inc. 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
Expand All @@ -9,22 +9,27 @@
*
* Contributors:
* Sonatype Inc. - initial API and implementation
* Christoph Läubrich - Issue #626 - Classpath computation must take fragments into account
*******************************************************************************/
package org.eclipse.tycho.p2.resolver;

import java.io.File;
import java.util.Collection;
import java.util.LinkedHashMap;
import java.util.LinkedHashSet;
import java.util.List;
import java.util.Map;
import java.util.Optional;
import java.util.Set;
import java.util.stream.Collectors;

import org.eclipse.equinox.p2.metadata.IArtifactKey;
import org.eclipse.equinox.p2.metadata.IInstallableUnit;
import org.eclipse.tycho.ArtifactKey;
import org.eclipse.tycho.ArtifactType;
import org.eclipse.tycho.DefaultArtifactKey;
import org.eclipse.tycho.p2.resolver.facade.P2ResolutionResult;
import org.eclipse.tycho.p2.target.ArtifactTypeHelper;
import org.eclipse.tycho.p2.target.P2TargetPlatform;

public class DefaultP2ResolutionResult implements P2ResolutionResult {
Expand All @@ -36,15 +41,41 @@ public class DefaultP2ResolutionResult implements P2ResolutionResult {
* Set of installable unit in the target platform of the module that do not come from the local
* reactor.
*/
private final Set<Object/* IInstallableUnit */> nonReactorUnits = new LinkedHashSet<>();
private final Set<IInstallableUnit> nonReactorUnits = new LinkedHashSet<>();
private P2TargetPlatform resolutionContext;
private List<Entry> fragments;

public DefaultP2ResolutionResult(Collection<IInstallableUnit> dependencyFragments,
P2TargetPlatform resolutionContext) {
this.resolutionContext = resolutionContext;
fragments = dependencyFragments.stream().map(iu -> {
DefaultArtifactKey artifactKey = new DefaultArtifactKey(ArtifactType.TYPE_BUNDLE_FRAGMENT, iu.getId(),
iu.getVersion().toString());
final DefaultP2ResolutionResultEntry entry;
if (resolutionContext.isFileAlreadyAvailable(artifactKey)) {
entry = new DefaultP2ResolutionResultEntry(artifactKey.getType(), artifactKey.getId(),
artifactKey.getVersion(), null, resolutionContext.getArtifactLocation(artifactKey));
} else {
entry = new DefaultP2ResolutionResultEntry(artifactKey.getType(), artifactKey.getId(),
artifactKey.getVersion(), null, () -> {
File res = resolutionContext
.getLocalArtifactFile(ArtifactTypeHelper.toP2ArtifactKey(artifactKey));
resolutionContext.saveLocalMavenRepository(); // store just downloaded artifacts in local Maven repo index
return res;
});
}
entry.addInstallableUnit(iu);
return entry;
}).collect(Collectors.toList());
}

@Override
public Collection<Entry> getArtifacts() {
return entries.values();
}

public void addArtifact(ArtifactKey artifactKey, String classifier, IInstallableUnit installableUnit,
IArtifactKey p2ArtifactKey, P2TargetPlatform resolutionContext) {
IArtifactKey p2ArtifactKey) {
if (resolutionContext.isFileAlreadyAvailable(artifactKey)) {
addResolvedArtifact(Optional.of(artifactKey), classifier, installableUnit,
resolutionContext.getArtifactLocation(artifactKey));
Expand Down Expand Up @@ -141,4 +172,9 @@ public void addNonReactorUnits(Set<IInstallableUnit> installableUnits) {
public Set<?> getNonReactorUnits() {
return nonReactorUnits;
}

@Override
public Collection<Entry> getDependencyFragments() {
return fragments;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -72,4 +72,9 @@ public Collection<Entry> getArtifacts() {
public Set<?> getNonReactorUnits() {
return Collections.emptySet();
}

@Override
public Collection<Entry> getDependencyFragments() {
return Collections.emptyList();
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@
* Christoph Läubrich - Bug 572481 - Tycho does not understand "additional.bundles" directive in build.properties
* - Issue #82 - Support resolving of non-project IUs in P2Resolver
* - Issue #462 - Delay Pom considered items to the final Target Platform calculation
* - Issue #626 - Classpath computation must take fragments into account
*******************************************************************************/
package org.eclipse.tycho.p2.resolver;

Expand All @@ -34,6 +35,7 @@
import java.util.function.Consumer;

import org.eclipse.core.runtime.IProgressMonitor;
import org.eclipse.equinox.internal.p2.metadata.IRequiredCapability;
import org.eclipse.equinox.internal.p2.metadata.InstallableUnit;
import org.eclipse.equinox.p2.metadata.IArtifactKey;
import org.eclipse.equinox.p2.metadata.IInstallableUnit;
Expand Down Expand Up @@ -94,8 +96,6 @@ public class P2ResolverImpl implements P2Resolver {

private TargetPlatformFactoryImpl targetPlatformFactory;

private Set<IInstallableUnit> usedTargetPlatformUnits;

private PomDependencies pomDependencies = PomDependencies.ignore;

public P2ResolverImpl(TargetPlatformFactoryImpl targetPlatformFactory, MavenLogger logger) {
Expand All @@ -118,24 +118,22 @@ public Map<TargetEnvironment, P2ResolutionResult> resolveTargetDependencies(Targ

// we need a linked hashmap to maintain iteration-order, some of the code relies on it!
Map<TargetEnvironment, P2ResolutionResult> results = new LinkedHashMap<>();
usedTargetPlatformUnits = new LinkedHashSet<>();
Set<IInstallableUnit> usedTargetPlatformUnits = new LinkedHashSet<>();
Set<?> metadata = project != null ? project.getDependencyMetadata(DependencyMetadataType.SEED)
: Collections.emptySet();
for (TargetEnvironment environment : environments) {
if (isMatchingEnv(metadata, environment, logger::debug)) {
results.put(environment, resolveDependencies(Collections.<IInstallableUnit> emptySet(), project,
new ProjectorResolutionStrategy(logger), environment, targetPlatform));
new ProjectorResolutionStrategy(logger), environment, targetPlatform, usedTargetPlatformUnits));
} else {
logger.info(MessageFormat.format(
"Project {0}:{1}:{2} does not match environment {3} skipp dependecy resolution",
"Project {0}:{1}:{2} does not match environment {3} skip dependency resolution",
project.getGroupId(), project.getArtifactId(), project.getVersion(),
environment.toFilterExpression()));
}
}

targetPlatform.reportUsedLocalIUs(usedTargetPlatformUnits);
usedTargetPlatformUnits = null;

return results;
}

Expand All @@ -157,15 +155,15 @@ public Map<TargetEnvironment, P2ResolutionResult> resolveArtifactDependencies(Ta
Map<TargetEnvironment, P2ResolutionResult> results = new LinkedHashMap<>();
for (TargetEnvironment environment : environments) {
results.put(environment, resolveDependencies(roots, null, new ProjectorResolutionStrategy(logger),
environment, targetPlatform));
environment, targetPlatform, null));
}
return results;
}

@Override
public P2ResolutionResult collectProjectDependencies(TargetPlatform context, ReactorProject project) {
return resolveDependencies(Collections.<IInstallableUnit> emptySet(), project, new DependencyCollector(logger),
new TargetEnvironment(null, null, null), getTargetFromContext(context));
new TargetEnvironment(null, null, null), getTargetFromContext(context), null);
}

@Override
Expand Down Expand Up @@ -210,7 +208,8 @@ public P2ResolutionResult getTargetPlatformAsResolutionResult(TargetPlatformConf

@SuppressWarnings("unchecked")
protected P2ResolutionResult resolveDependencies(Collection<IInstallableUnit> rootUIs, ReactorProject project,
AbstractResolutionStrategy strategy, TargetEnvironment environment, P2TargetPlatform targetPlatform) {
AbstractResolutionStrategy strategy, TargetEnvironment environment, P2TargetPlatform targetPlatform,
Set<IInstallableUnit> usedTargetPlatformUnits) {
ResolutionDataImpl data = new ResolutionDataImpl(targetPlatform.getEEResolutionHints());

Set<IInstallableUnit> availableUnits = targetPlatform.getInstallableUnits();
Expand Down Expand Up @@ -256,16 +255,46 @@ protected P2ResolutionResult resolveDependencies(Collection<IInstallableUnit> ro
if (usedTargetPlatformUnits != null) {
usedTargetPlatformUnits.addAll(newState);
}
Set<IInstallableUnit> dependencyFragments = new HashSet<>();
for (IInstallableUnit iu : availableUnits) {
for (IProvidedCapability capability : iu.getProvidedCapabilities()) {
String nameSpace = capability.getNamespace();
if (BundlesAction.CAPABILITY_NS_OSGI_FRAGMENT.equals(nameSpace)) {
String fragmentName = capability.getName();
IRequiredCapability fragmentHost = findFragmentHostRequirement(iu, fragmentName);
if (fragmentHost != null) {
for (IInstallableUnit resolved : newState) {
if (fragmentHost.isMatch(resolved)) {
dependencyFragments.add(iu);
break;
}
}
}
}
}

}
return toResolutionResult(newState, dependencyFragments, project, targetPlatform);
}

return toResolutionResult(newState, project, targetPlatform);
private static IRequiredCapability findFragmentHostRequirement(IInstallableUnit unit, String fragmentName) {
for (IRequirement requirement : unit.getRequirements()) {
if (requirement instanceof IRequiredCapability) {
IRequiredCapability requiredCapability = (IRequiredCapability) requirement;
if (fragmentName.equals(requiredCapability.getName())) {
return requiredCapability;
}
}
}
return null;
}

private P2ResolutionResult toResolutionResult(Collection<IInstallableUnit> newState, ReactorProject project,
P2TargetPlatform targetPlatform) {
private P2ResolutionResult toResolutionResult(Collection<IInstallableUnit> resolvedUnits,
Collection<IInstallableUnit> dependencyFragments, ReactorProject project, P2TargetPlatform targetPlatform) {
Set<IInstallableUnit> currentProjectUnits = getProjectUnits(project);
DefaultP2ResolutionResult result = new DefaultP2ResolutionResult();
DefaultP2ResolutionResult result = new DefaultP2ResolutionResult(dependencyFragments, targetPlatform);

for (IInstallableUnit iu : newState) {
for (IInstallableUnit iu : resolvedUnits) {
addUnit(result, iu, project, targetPlatform, currentProjectUnits);
}
// remove entries for which there were only "additional" IUs, but none with a recognized type
Expand All @@ -275,7 +304,7 @@ private P2ResolutionResult toResolutionResult(Collection<IInstallableUnit> newSt
targetPlatform.saveLocalMavenRepository();

// TODO 372780 remove; no longer needed when aggregation uses frozen target platform as source
collectNonReactorIUs(result, newState, targetPlatform, currentProjectUnits);
collectNonReactorIUs(result, resolvedUnits, targetPlatform, currentProjectUnits);
return result;
}

Expand Down Expand Up @@ -355,7 +384,7 @@ public P2ResolutionResult resolveInstallableUnit(TargetPlatform context, String

Set<IInstallableUnit> newState = result.toUnmodifiableSet();

return toResolutionResult(newState, null, targetPlatform);
return toResolutionResult(newState, Collections.emptyList(), null, targetPlatform);
}

private static P2TargetPlatform getTargetFromContext(TargetPlatform context) {
Expand Down Expand Up @@ -481,16 +510,16 @@ private static void addArtifactFile(DefaultP2ResolutionResult result, IInstallab

if (PublisherHelper.OSGI_BUNDLE_CLASSIFIER.equals(p2ArtifactKey.getClassifier())) {
ArtifactKey artifactKey = new DefaultArtifactKey(ArtifactType.TYPE_ECLIPSE_PLUGIN, id, version);
result.addArtifact(artifactKey, mavenClassifier, iu, p2ArtifactKey, context);
result.addArtifact(artifactKey, mavenClassifier, iu, p2ArtifactKey);
} else if (PublisherHelper.ECLIPSE_FEATURE_CLASSIFIER.equals(p2ArtifactKey.getClassifier())) {
String featureId = getFeatureId(iu);
if (featureId != null) {
ArtifactKey artifactKey = new DefaultArtifactKey(ArtifactType.TYPE_ECLIPSE_FEATURE, featureId, version);
result.addArtifact(artifactKey, mavenClassifier, iu, p2ArtifactKey, context);
result.addArtifact(artifactKey, mavenClassifier, iu, p2ArtifactKey);
}
} else {
ArtifactKey key = new DefaultArtifactKey(ArtifactType.TYPE_INSTALLABLE_UNIT, id, version);
result.addArtifact(key, mavenClassifier, iu, p2ArtifactKey, context);
result.addArtifact(key, mavenClassifier, iu, p2ArtifactKey);
}

// ignore other/unknown artifacts, like binary blobs for now.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@
*******************************************************************************/
package org.eclipse.tycho.p2.target;

import static org.eclipse.tycho.ArtifactType.TYPE_BUNDLE_FRAGMENT;
import static org.eclipse.tycho.ArtifactType.TYPE_ECLIPSE_FEATURE;
import static org.eclipse.tycho.ArtifactType.TYPE_ECLIPSE_PLUGIN;

Expand Down Expand Up @@ -86,8 +87,8 @@ public static IRequirement createRequirementFor(String type, String id, VersionR
}

private static IRequirement createBundleRequirement(String id, VersionRange versionRange) {
return MetadataFactory.createRequirement(BundlesAction.CAPABILITY_NS_OSGI_BUNDLE, id, versionRange, null,
false, true); // optional=false, multiple=true
return MetadataFactory.createRequirement(BundlesAction.CAPABILITY_NS_OSGI_BUNDLE, id, versionRange, null, false,
true); // optional=false, multiple=true
}

private static IRequirement createFeatureRequirement(String id, VersionRange versionRange) {
Expand Down Expand Up @@ -119,7 +120,7 @@ public static org.eclipse.tycho.ArtifactKey toTychoArtifact(IInstallableUnit uni
// p2 artifacts

public static IArtifactKey toP2ArtifactKey(org.eclipse.tycho.ArtifactKey artifact) {
if (TYPE_ECLIPSE_PLUGIN.equals(artifact.getType())) {
if (TYPE_ECLIPSE_PLUGIN.equals(artifact.getType()) || TYPE_BUNDLE_FRAGMENT.equals(artifact.getType())) {
return createP2ArtifactKey(PublisherHelper.OSGI_BUNDLE_CLASSIFIER, artifact);

} else if (TYPE_ECLIPSE_FEATURE.equals(artifact.getType())) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -40,4 +40,10 @@ public static interface Entry extends ArtifactKey {
public Collection<Entry> getArtifacts();

public Set<?> getNonReactorUnits();

/**
*
* @return a list of fragments that belong to the resolved state of this result
*/
Collection<Entry> getDependencyFragments();
}
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
/*******************************************************************************
* Copyright (c) 2008, 2021 Sonatype Inc. and others.
* Copyright (c) 2008, 2022 Sonatype Inc. and others.
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* which accompanies this distribution, and is available at
Expand All @@ -10,6 +10,7 @@
* Christoph Läubrich - [Bug 572416] Tycho does not understand "additional.bundles" directive in build.properties
* [Bug 572416] Compile all source folders contained in .classpath
* [Issue #460] Delay classpath resolution to the compile phase
* [Issue #626] Classpath computation must take fragments into account
*******************************************************************************/
package org.eclipse.tycho.core.osgitools;

Expand Down Expand Up @@ -498,6 +499,15 @@ private void addExtraClasspathEntries(List<ClasspathEntry> classpath, ReactorPro
Collections.singletonList(libraryClasspathEntry.getLibraryPath()), null));
}
}
//Fragments are like embedded depdnecies...
for (ArtifactDescriptor fragment : artifacts.getFragments()) {
ArtifactKey projectKey = getArtifactKey(project);
File location = fragment.getLocation(true);
if (location != null) {
classpath
.add(new DefaultClasspathEntry(project, projectKey, Collections.singletonList(location), null));
}
}
}

protected DefaultClasspathEntry addBundleToClasspath(ArtifactDescriptor matchingBundle, String path) {
Expand Down
Loading

0 comments on commit 774e135

Please sign in to comment.