Skip to content

Commit

Permalink
Pass the profile properties so they can be considered in the resolve
Browse files Browse the repository at this point in the history
  • Loading branch information
laeubi committed Dec 10, 2023
1 parent 579e03b commit 2c43192
Show file tree
Hide file tree
Showing 7 changed files with 90 additions and 33 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -12,10 +12,14 @@
*******************************************************************************/
package org.eclipse.tycho.p2maven;

import java.util.ArrayList;
import java.util.Collection;
import java.util.HashMap;
import java.util.Iterator;
import java.util.LinkedHashSet;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;

import org.codehaus.plexus.component.annotations.Component;
import org.codehaus.plexus.component.annotations.Requirement;
Expand All @@ -27,6 +31,7 @@
import org.eclipse.equinox.internal.p2.metadata.InstallableUnit;
import org.eclipse.equinox.p2.metadata.IInstallableUnit;
import org.eclipse.equinox.p2.metadata.IRequirement;
import org.eclipse.equinox.p2.metadata.expression.IMatchExpression;
import org.eclipse.equinox.p2.query.CollectionResult;
import org.eclipse.equinox.p2.query.IQueryResult;
import org.eclipse.equinox.p2.query.IQueryable;
Expand Down Expand Up @@ -76,38 +81,59 @@ public IQueryResult<IInstallableUnit> computeDependencies(Collection<IInstallabl
* @param rootIus the root {@link InstallableUnit}s to take into account
* @param avaiableIUs the {@link IQueryable} of all units that could be used for
* fulfilling a requirement
* @param contextIUs context IUs that represent the the profile properties to
* consider during resolution, can be empty in which case a
* filter is always considered a match
* @return the result of the slicing
* @throws CoreException if there is any error
*/
public IQueryResult<IInstallableUnit> computeDirectDependencies(Collection<IInstallableUnit> rootIus,
IQueryable<IInstallableUnit> avaiableIUs) throws CoreException {
IQueryable<IInstallableUnit> avaiableIUs, Collection<IInstallableUnit> contextIUs) throws CoreException {
Collection<IInstallableUnit> result = new LinkedHashSet<>();
List<IRequirement> collect = rootIus.stream().flatMap(iu -> iu.getRequirements().stream()).filter(req -> {
for (IInstallableUnit unit : rootIus) {
if (unit.satisfies(req)) {
// self full filled requirement
return false;
}
}
return true;
}).toList();
Map<Boolean, List<IRequirement>> collect = rootIus.stream().flatMap(iu -> iu.getRequirements().stream())
.filter(req -> {
for (IInstallableUnit unit : rootIus) {
if (unit.satisfies(req)) {
// self full filled requirement
return false;
}
}
return isMatch(req.getFilter(), contextIUs);
}).collect(Collectors.groupingBy(req -> req.getMax() == 0));
List<IRequirement> negativeRequirements = collect.get(true);
List<IRequirement> requirements = new ArrayList<>(collect.get(false));

for (IInstallableUnit iu : avaiableIUs.query(QueryUtil.ALL_UNITS, new NullProgressMonitor()).toSet()) {
for (IRequirement requirement : collect) {
// Negative requirements should not create a dependency.
// If there is a filter, we need more context, e.g, see
// org.eclipse.equinox.internal.p2.director.Slicer.isApplicable(IRequirement)
// Failing that, we need to assume the filter isn't applicable.
if (requirement.getMax() != 0 && requirement.getFilter() == null && iu.satisfies(requirement)) {
for (Iterator<IRequirement> iterator = requirements.iterator(); iterator.hasNext();) {
IRequirement requirement = iterator.next();
if (iu.satisfies(requirement)) {
result.add(iu);
// TODO remove the requirement from the set so we only collect exactly one
// provider for a requirement?
if (requirement.getMax() == 1) {
// only one provider allowed
iterator.remove();
}
break;
}
}
// now check if the IU satisfies any negative one, then we need to remove it
// from the set...
for (IRequirement requirement : negativeRequirements) {
if (iu.satisfies(requirement)) {
result.remove(iu);
break;
}
}
}
return new CollectionResult<>(result);
}

private boolean isMatch(IMatchExpression<IInstallableUnit> filter, Collection<IInstallableUnit> contextIUs) {
if (filter == null || contextIUs.isEmpty()) {
return true;
}
return contextIUs.stream().anyMatch(contextIU -> filter.isMatch(contextIU));
}

private final class TychoSlicer extends PermissiveSlicer {
private TychoSlicer(IQueryable<IInstallableUnit> input) {
super(input, new HashMap<>(), //
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@
import java.util.Set;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.CopyOnWriteArrayList;
import java.util.function.Function;
import java.util.stream.Collectors;
import java.util.stream.Stream;

Expand Down Expand Up @@ -72,19 +73,25 @@ public class MavenProjectDependencyProcessor {
* Computes the {@link ProjectDependencyClosure} of the given collection of
* projects.
*
* @param projects the projects to include in the closure
* @param session the maven session for this request
* @param projects the projects to include in the closure
* @param session the maven session for this request
* @param profilePropertiesSupplier supplier of context IUs for a project that
* represent the the profile properties to
* consider during resolution, can be empty in
* which case a filter is always considered a
* match
* @return the computed {@link ProjectDependencyClosure}
* @throws CoreException if computation failed
*/
public ProjectDependencyClosure computeProjectDependencyClosure(Collection<MavenProject> projects,
MavenSession session) throws CoreException {
MavenSession session, Function<MavenProject, Collection<IInstallableUnit>> profilePropertiesSupplier)
throws CoreException {
Objects.requireNonNull(session);
Map<MavenProject, Collection<IInstallableUnit>> projectIUMap = generator.getInstallableUnits(projects, session);
Collection<IInstallableUnit> availableIUs = projectIUMap.values().stream().flatMap(Collection::stream)
.collect(Collectors.toSet());
Map<MavenProject, ProjectDependencies> projectDependenciesMap = computeProjectDependencies(projects,
new CollectionResult<>(availableIUs), projectIUMap);
new CollectionResult<>(availableIUs), projectIUMap, profilePropertiesSupplier);
Map<IInstallableUnit, MavenProject> iuProjectMap = new HashMap<>();
for (var entry : projectIUMap.entrySet()) {
MavenProject mavenProject = entry.getKey();
Expand Down Expand Up @@ -138,14 +145,15 @@ public Collection<IInstallableUnit> getProjectUnits(MavenProject mavenProject) {
* @throws CoreException if computation failed
*/
private Map<MavenProject, ProjectDependencies> computeProjectDependencies(Collection<MavenProject> projects,
IQueryable<IInstallableUnit> avaiableIUs, Map<MavenProject, Collection<IInstallableUnit>> projectIUMap)
IQueryable<IInstallableUnit> avaiableIUs, Map<MavenProject, Collection<IInstallableUnit>> projectIUMap,
Function<MavenProject, Collection<IInstallableUnit>> profilePropertiesSupplier)
throws CoreException {
List<CoreException> errors = new CopyOnWriteArrayList<>();
Map<MavenProject, ProjectDependencies> result = new ConcurrentHashMap<>();
projects.parallelStream().unordered().takeWhile(nil -> errors.isEmpty()).forEach(project -> {
try {
ProjectDependencies projectDependencies = computeProjectDependencies(projectIUMap.get(project),
avaiableIUs);
avaiableIUs, profilePropertiesSupplier.apply(project));
result.put(project, projectDependencies);
if (DUMP_DATA) {
File file = new File(project.getBasedir(), "project-dependencies.xml");
Expand Down Expand Up @@ -182,12 +190,13 @@ private Map<MavenProject, ProjectDependencies> computeProjectDependencies(Collec
* @throws CoreException if computation failed
*/
private ProjectDependencies computeProjectDependencies(Collection<IInstallableUnit> projectUnits,
IQueryable<IInstallableUnit> avaiableIUs) throws CoreException {
IQueryable<IInstallableUnit> avaiableIUs, Collection<IInstallableUnit> profileProperties)
throws CoreException {
if (projectUnits.isEmpty()) {
return EMPTY_DEPENDENCIES;
}
Set<IInstallableUnit> resolved = new LinkedHashSet<>(
slicer.computeDirectDependencies(projectUnits, avaiableIUs).toSet());
slicer.computeDirectDependencies(projectUnits, avaiableIUs, profileProperties).toSet());
resolved.removeAll(projectUnits);
// now we need to filter all fragments that we are a host!
// for example SWT creates an explicit requirement to its fragments and we don't
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,6 @@
package org.eclipse.tycho;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.LinkedHashMap;
import java.util.Map;
import java.util.Objects;
Expand Down Expand Up @@ -88,7 +87,7 @@ public boolean isWindows() {
* Returns the target environment as map. The keys are "osgi.ws", "osgi.os", and "osgi.arch".
* This format is used by the p2 slicer to filter installable units by environments.
*
* @return a new instance of {@link HashMap} with the target environment set
* @return a new instance of {@link LinkedHashMap} with the target environment set
*/
public Map<String, String> toFilterProperties() {
//for nicer debug output, use an ordered map here
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -152,7 +152,10 @@ public Result<ProjectDependencyGraph> build(MavenSession session) {
try {
ProjectDependencyClosure dependencyClosure;
try {
dependencyClosure = dependencyProcessor.computeProjectDependencyClosure(projects, session);
// here we don't use filters, the worst that can happen is that we get more
// projects to consider...
dependencyClosure = dependencyProcessor.computeProjectDependencyClosure(projects, session,
always -> List.of());
} catch (CoreException e) {
log.error("Cannot resolve projects", e);
return Result.error(graph, toProblems(e.getStatus(), new ArrayList<>()));
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -36,13 +36,16 @@
import org.codehaus.plexus.component.annotations.Component;
import org.codehaus.plexus.component.annotations.Requirement;
import org.codehaus.plexus.logging.Logger;
import org.eclipse.equinox.internal.p2.metadata.InstallableUnit;
import org.eclipse.equinox.p2.metadata.IInstallableUnit;
import org.eclipse.tycho.ArtifactDescriptor;
import org.eclipse.tycho.ArtifactKey;
import org.eclipse.tycho.ClasspathEntry;
import org.eclipse.tycho.DefaultArtifactKey;
import org.eclipse.tycho.ExecutionEnvironmentConfiguration;
import org.eclipse.tycho.ReactorProject;
import org.eclipse.tycho.ResolvedArtifactKey;
import org.eclipse.tycho.TargetEnvironment;
import org.eclipse.tycho.TargetPlatform;
import org.eclipse.tycho.TargetPlatformService;
import org.eclipse.tycho.TychoConstants;
Expand Down Expand Up @@ -143,6 +146,25 @@ public void readExecutionEnvironmentConfiguration(ReactorProject project, Execut
}
}

public List<IInstallableUnit> getContextIUs(MavenProject project) {
TargetPlatformConfiguration configuration = getTargetPlatformConfiguration(project);
return configuration.getEnvironments().stream().map(env -> getProfileProperties(env, configuration))
.map(InstallableUnit::contextIU).toList();
}

public Map<String, String> getProfileProperties(MavenProject project, TargetEnvironment environment) {
TargetPlatformConfiguration configuration = getTargetPlatformConfiguration(project);
return getProfileProperties(environment, configuration);
}

private Map<String, String> getProfileProperties(TargetEnvironment environment,
TargetPlatformConfiguration configuration) {
Map<String, String> properties = environment.toFilterProperties();
properties.put("org.eclipse.update.install.features", "true");
properties.putAll(configuration.getProfileProperties());
return properties;
}

public TargetPlatformConfiguration getTargetPlatformConfiguration(MavenProject project) {
ReactorProject reactorProject = DefaultReactorProject.adapt(project);
return reactorProject.computeContextValue(CTX_TARGET_PLATFORM_CONFIGURATION,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -148,7 +148,7 @@ public void afterProjectsRead(MavenSession session) throws MavenExecutionExcepti
if (lazyProjects.size() > 0) {
try {
ProjectDependencyClosure closure = dependencyProcessor.computeProjectDependencyClosure(projects,
session);
session, projectManager::getContextIUs);
for (MavenProject project : lazyProjects) {
if (projectManager.getTychoProject(project).isEmpty()) {
//do not inject additional dependencies for non Tycho managed projects!
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -21,9 +21,7 @@ public class JustJCycleTest extends AbstractTychoIntegrationTest {
@Test
public void testCustomBundleParent() throws Exception {
Verifier verifier = getVerifier("justj-cycle");
verifier.setForkJvm(false);
verifier.setSystemProperty("user.home", System.getProperty("user.home"));
verifier.executeGoal("verify");
verifier.executeGoal("initialize");
verifier.verifyErrorFreeLog();
}
}

0 comments on commit 2c43192

Please sign in to comment.