Skip to content

Commit e744b57

Browse files
committed
Generalize usage of Eclipse Applications in Tycho
Embedded Eclipse Application have proven to be a powerful tool in Tycho to reuse existing Eclipse codes in a dynamic way. Currently its still a bit of boilerplate code to start the application but always have the same semantics we want something with bundles resolved and cached and read from a location/target. This adds a generic EclipseApplicationManager that is able to manage many applications and caching these in a more effective way than single applications can do.
1 parent 1c37754 commit e744b57

File tree

12 files changed

+267
-210
lines changed

12 files changed

+267
-210
lines changed

tycho-apitools-plugin/src/main/java/org/eclipse/tycho/apitools/ApiAnalysisMojo.java

Lines changed: 1 addition & 68 deletions
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,6 @@
1616
import java.net.URI;
1717
import java.nio.file.Path;
1818
import java.util.Collection;
19-
import java.util.HashSet;
2019
import java.util.List;
2120
import java.util.Map;
2221
import java.util.Objects;
@@ -41,23 +40,13 @@
4140
import org.eclipse.pde.api.tools.internal.IApiCoreConstants;
4241
import org.eclipse.pde.api.tools.internal.provisional.ApiPlugin;
4342
import org.eclipse.pde.api.tools.internal.provisional.problems.IApiProblem;
44-
import org.eclipse.tycho.ArtifactDescriptor;
4543
import org.eclipse.tycho.ArtifactKey;
46-
import org.eclipse.tycho.ClasspathEntry;
4744
import org.eclipse.tycho.DependencyResolutionException;
4845
import org.eclipse.tycho.IllegalArtifactReferenceException;
4946
import org.eclipse.tycho.MavenRepositoryLocation;
50-
import org.eclipse.tycho.ReactorProject;
51-
import org.eclipse.tycho.ResolvedArtifactKey;
5247
import org.eclipse.tycho.TychoConstants;
53-
import org.eclipse.tycho.classpath.ClasspathContributor;
54-
import org.eclipse.tycho.core.TychoProject;
5548
import org.eclipse.tycho.core.TychoProjectManager;
5649
import org.eclipse.tycho.core.osgitools.DefaultReactorProject;
57-
import org.eclipse.tycho.core.osgitools.MavenBundleResolver;
58-
import org.eclipse.tycho.core.osgitools.OsgiBundleProject;
59-
import org.eclipse.tycho.core.utils.TychoProjectUtils;
60-
import org.eclipse.tycho.helper.PluginRealmHelper;
6150
import org.eclipse.tycho.model.project.EclipseProject;
6251
import org.eclipse.tycho.osgi.framework.EclipseApplication;
6352
import org.eclipse.tycho.osgi.framework.EclipseFramework;
@@ -122,12 +111,6 @@ public class ApiAnalysisMojo extends AbstractMojo {
122111
@Component
123112
private ApiApplicationResolver resolver;
124113

125-
@Component
126-
private PluginRealmHelper pluginRealmHelper;
127-
128-
@Component
129-
protected MavenBundleResolver mavenBundleResolver;
130-
131114
@Component
132115
private ApiApplicationResolver applicationResolver;
133116

@@ -158,7 +141,7 @@ public void execute() throws MojoExecutionException, MojoFailureException {
158141
}
159142
Collection<Path> dependencyBundles;
160143
try {
161-
dependencyBundles = getProjectDependencies();
144+
dependencyBundles = projectManager.getProjectDependencies(project);
162145
} catch (Exception e) {
163146
throw new MojoFailureException("Can't fetch dependencies!", e);
164147
}
@@ -279,56 +262,6 @@ private String time(long start) {
279262
return sec + " s";
280263
}
281264

282-
private Collection<Path> getProjectDependencies() throws Exception {
283-
Set<Path> dependencySet = new HashSet<>();
284-
TychoProject tychoProject = projectManager.getTychoProject(project).get();
285-
List<ArtifactDescriptor> dependencies = TychoProjectUtils
286-
.getDependencyArtifacts(DefaultReactorProject.adapt(project)).getArtifacts();
287-
for (ArtifactDescriptor descriptor : dependencies) {
288-
File location = descriptor.fetchArtifact().get();
289-
if (location.equals(project.getBasedir())) {
290-
continue;
291-
}
292-
ReactorProject reactorProject = descriptor.getMavenProject();
293-
if (reactorProject == null) {
294-
writeLocation(location, dependencySet);
295-
} else {
296-
ReactorProject otherProject = reactorProject;
297-
writeLocation(otherProject.getArtifact(descriptor.getClassifier()), dependencySet);
298-
}
299-
}
300-
if (tychoProject instanceof OsgiBundleProject bundleProject) {
301-
pluginRealmHelper.visitPluginExtensions(project, session, ClasspathContributor.class, cpc -> {
302-
List<ClasspathEntry> list = cpc.getAdditionalClasspathEntries(project, Artifact.SCOPE_COMPILE);
303-
if (list != null && !list.isEmpty()) {
304-
for (ClasspathEntry entry : list) {
305-
for (File locations : entry.getLocations()) {
306-
writeLocation(locations, dependencySet);
307-
}
308-
}
309-
}
310-
});
311-
// This is a hack because "org.eclipse.osgi.services" exports the annotation
312-
// package and might then be resolved by Tycho as a dependency, but then PDE
313-
// can't find the annotations here, so we always add this as a dependency
314-
// manually here, once "org.eclipse.osgi.services" is gone we can remove this
315-
// again!
316-
Optional<ResolvedArtifactKey> bundle = mavenBundleResolver.resolveMavenBundle(project, session, "org.osgi",
317-
"org.osgi.service.component.annotations", "1.3.0");
318-
bundle.ifPresent(key -> {
319-
writeLocation(key.getLocation(), dependencySet);
320-
});
321-
}
322-
return dependencySet;
323-
}
324-
325-
private void writeLocation(File location, Collection<Path> consumer) {
326-
if (location == null) {
327-
return;
328-
}
329-
consumer.add(location.getAbsoluteFile().toPath());
330-
}
331-
332265
private static final class ApiAppKey {
333266

334267
private URI key;

tycho-apitools-plugin/src/main/java/org/eclipse/tycho/apitools/ApiApplicationResolver.java

Lines changed: 12 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@
1818
import java.util.Collection;
1919
import java.util.List;
2020
import java.util.Map;
21+
import java.util.Set;
2122
import java.util.concurrent.ConcurrentHashMap;
2223

2324
import org.codehaus.plexus.component.annotations.Component;
@@ -31,8 +32,11 @@
3132
import org.eclipse.tycho.core.resolver.P2ResolutionResult;
3233
import org.eclipse.tycho.core.resolver.P2ResolutionResult.Entry;
3334
import org.eclipse.tycho.core.resolver.P2Resolver;
35+
import org.eclipse.tycho.osgi.framework.Bundles;
3436
import org.eclipse.tycho.osgi.framework.EclipseApplication;
3537
import org.eclipse.tycho.osgi.framework.EclipseApplicationFactory;
38+
import org.eclipse.tycho.osgi.framework.EclipseApplicationManager;
39+
import org.eclipse.tycho.osgi.framework.Features;
3640
import org.osgi.framework.BundleException;
3741
import org.osgi.service.log.LogEntry;
3842

@@ -43,14 +47,6 @@
4347
@Component(role = ApiApplicationResolver.class)
4448
public class ApiApplicationResolver {
4549

46-
private static final String FRAGMENT_COMPATIBILITY = "org.eclipse.osgi.compatibility.state";
47-
48-
private static final String BUNDLE_API_TOOLS = "org.eclipse.pde.api.tools";
49-
50-
private static final String BUNDLE_LAUNCHING_MACOS = "org.eclipse.jdt.launching.macosx";
51-
52-
private static final String FILTER_MACOS = "(osgi.os=macosx)";
53-
5450
private final Map<URI, EclipseApplication> cache = new ConcurrentHashMap<>();
5551

5652
@Requirement
@@ -59,6 +55,9 @@ public class ApiApplicationResolver {
5955
@Requirement
6056
private EclipseApplicationFactory applicationFactory;
6157

58+
@Requirement
59+
private EclipseApplicationManager applicationManager;
60+
6261
public Collection<Path> getApiBaselineBundles(Collection<MavenRepositoryLocation> baselineRepoLocations,
6362
ArtifactKey artifactKey) throws IllegalArtifactReferenceException {
6463
P2Resolver resolver = applicationFactory.createResolver();
@@ -77,16 +76,11 @@ public Collection<Path> getApiBaselineBundles(Collection<MavenRepositoryLocation
7776
}
7877

7978
public EclipseApplication getApiApplication(MavenRepositoryLocation apiToolsRepo) {
80-
return cache.computeIfAbsent(apiToolsRepo.getURL().normalize(), x -> {
81-
logger.info("Resolve API tools runtime from " + apiToolsRepo + "...");
82-
EclipseApplication application = applicationFactory.createEclipseApplication(apiToolsRepo,
83-
"ApiToolsApplication");
84-
application.addBundle(BUNDLE_API_TOOLS);
85-
application.addBundle(FRAGMENT_COMPATIBILITY);
86-
application.addConditionalBundle(BUNDLE_LAUNCHING_MACOS, FILTER_MACOS);
87-
application.setLoggingFilter(ApiApplicationResolver::isOnlyDebug);
88-
return application;
89-
});
79+
80+
EclipseApplication application = applicationManager.getApplication(apiToolsRepo, new Bundles(Set.of(Bundles.BUNDLE_API_TOOLS)),
81+
new Features(Set.of()), "Api Tools");
82+
application.setLoggingFilter(ApiApplicationResolver::isOnlyDebug);
83+
return application;
9084
}
9185

9286
private static boolean isOnlyDebug(LogEntry entry) {

tycho-core/src/main/java/org/eclipse/tycho/core/TychoProjectManager.java

Lines changed: 87 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,9 +14,14 @@
1414

1515
import java.io.File;
1616
import java.io.IOException;
17+
import java.nio.file.Path;
18+
import java.util.Collection;
19+
import java.util.HashSet;
20+
import java.util.List;
1721
import java.util.Map;
1822
import java.util.Objects;
1923
import java.util.Optional;
24+
import java.util.Set;
2025
import java.util.concurrent.ConcurrentHashMap;
2126

2227
import javax.inject.Inject;
@@ -31,18 +36,28 @@
3136
import org.codehaus.plexus.component.annotations.Component;
3237
import org.codehaus.plexus.component.annotations.Requirement;
3338
import org.codehaus.plexus.logging.Logger;
39+
import org.eclipse.tycho.ArtifactDescriptor;
3440
import org.eclipse.tycho.ArtifactKey;
41+
import org.eclipse.tycho.ClasspathEntry;
3542
import org.eclipse.tycho.DefaultArtifactKey;
3643
import org.eclipse.tycho.ExecutionEnvironmentConfiguration;
3744
import org.eclipse.tycho.ReactorProject;
45+
import org.eclipse.tycho.ResolvedArtifactKey;
46+
import org.eclipse.tycho.TargetPlatform;
47+
import org.eclipse.tycho.TargetPlatformService;
3848
import org.eclipse.tycho.TychoConstants;
49+
import org.eclipse.tycho.classpath.ClasspathContributor;
3950
import org.eclipse.tycho.core.ee.ExecutionEnvironmentConfigurationImpl;
4051
import org.eclipse.tycho.core.osgitools.AbstractTychoProject;
4152
import org.eclipse.tycho.core.osgitools.BundleReader;
4253
import org.eclipse.tycho.core.osgitools.DefaultReactorProject;
54+
import org.eclipse.tycho.core.osgitools.MavenBundleResolver;
55+
import org.eclipse.tycho.core.osgitools.OsgiBundleProject;
4356
import org.eclipse.tycho.core.osgitools.OsgiManifest;
4457
import org.eclipse.tycho.core.osgitools.OsgiManifestParserException;
4558
import org.eclipse.tycho.core.resolver.DefaultTargetPlatformConfigurationReader;
59+
import org.eclipse.tycho.core.utils.TychoProjectUtils;
60+
import org.eclipse.tycho.helper.PluginRealmHelper;
4661
import org.eclipse.tycho.model.project.EclipseProject;
4762
import org.eclipse.tycho.targetplatform.TargetDefinition;
4863

@@ -72,6 +87,15 @@ public class TychoProjectManager {
7287
@Requirement
7388
ToolchainManager toolchainManager;
7489

90+
@Requirement
91+
PluginRealmHelper pluginRealmHelper;
92+
93+
@Requirement
94+
MavenBundleResolver mavenBundleResolver;
95+
96+
@Requirement
97+
TargetPlatformService targetPlatformService;
98+
7599
private final Map<File, Optional<EclipseProject>> eclipseProjectCache = new ConcurrentHashMap<>();
76100

77101
private final MavenSession mavenSession;
@@ -203,4 +227,67 @@ public Optional<Processor> getBndTychoProject(MavenProject project) {
203227
return Optional.empty();
204228
}
205229

230+
/**
231+
* Determine the list of dependencies for a given project as a collection of path items
232+
*
233+
* @param project
234+
* the project to use to determine the dependencies
235+
* @return a Collection of pathes describing the project dependencies
236+
* @throws Exception
237+
*/
238+
public Collection<Path> getProjectDependencies(MavenProject project) throws Exception {
239+
Set<Path> dependencySet = new HashSet<>();
240+
TychoProject tychoProject = getTychoProject(project).get();
241+
List<ArtifactDescriptor> dependencies = TychoProjectUtils
242+
.getDependencyArtifacts(DefaultReactorProject.adapt(project)).getArtifacts();
243+
for (ArtifactDescriptor descriptor : dependencies) {
244+
File location = descriptor.fetchArtifact().get();
245+
if (location.equals(project.getBasedir())) {
246+
continue;
247+
}
248+
ReactorProject reactorProject = descriptor.getMavenProject();
249+
if (reactorProject == null) {
250+
writeLocation(location, dependencySet);
251+
} else {
252+
writeLocation(reactorProject.getArtifact(descriptor.getClassifier()), dependencySet);
253+
}
254+
}
255+
if (tychoProject instanceof OsgiBundleProject bundleProject) {
256+
MavenSession session = getMavenSession();
257+
pluginRealmHelper.visitPluginExtensions(project, session, ClasspathContributor.class, cpc -> {
258+
List<ClasspathEntry> list = cpc.getAdditionalClasspathEntries(project, Artifact.SCOPE_COMPILE);
259+
if (list != null && !list.isEmpty()) {
260+
for (ClasspathEntry entry : list) {
261+
for (File locations : entry.getLocations()) {
262+
writeLocation(locations, dependencySet);
263+
}
264+
}
265+
}
266+
});
267+
// This is a hack because "org.eclipse.osgi.services" exports the annotation
268+
// package and might then be resolved by Tycho as a dependency, but then PDE
269+
// can't find the annotations here, so we always add this as a dependency
270+
// manually here, once "org.eclipse.osgi.services" is gone we can remove this
271+
// again!
272+
Optional<ResolvedArtifactKey> bundle = mavenBundleResolver.resolveMavenBundle(project, session, "org.osgi",
273+
"org.osgi.service.component.annotations", "1.3.0");
274+
bundle.ifPresent(key -> {
275+
writeLocation(key.getLocation(), dependencySet);
276+
});
277+
}
278+
return dependencySet;
279+
}
280+
281+
private void writeLocation(File location, Collection<Path> consumer) {
282+
if (location == null) {
283+
return;
284+
}
285+
consumer.add(location.getAbsoluteFile().toPath());
286+
}
287+
288+
public Optional<TargetPlatform> getTargetPlatform(MavenProject project) {
289+
return targetPlatformService.getTargetPlatform(DefaultReactorProject.adapt(project));
290+
291+
}
292+
206293
}
Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
/*******************************************************************************
2+
* Copyright (c) 2023 Christoph Läubrich and others.
3+
* This program and the accompanying materials
4+
* are made available under the terms of the Eclipse Public License 2.0
5+
* which accompanies this distribution, and is available at
6+
* https://www.eclipse.org/legal/epl-2.0/
7+
*
8+
* SPDX-License-Identifier: EPL-2.0
9+
*
10+
* Contributors:
11+
* Christoph Läubrich - initial API and implementation
12+
*******************************************************************************/
13+
package org.eclipse.tycho.osgi.framework;
14+
15+
import java.util.Set;
16+
17+
public record Bundles(Set<String> bundles) {
18+
19+
public static final String BUNDLE_API_TOOLS = "org.eclipse.pde.api.tools";
20+
public static final String BUNDLE_ECLIPSE_HELP_BASE = "org.eclipse.help.base";
21+
public static final String BUNDLE_PDE_CORE = "org.eclipse.pde.core";
22+
static final String BUNDLE_LAUNCHING_MACOS = "org.eclipse.jdt.launching.macosx";
23+
static final String BUNDLE_APP = "org.eclipse.equinox.app";
24+
static final String BUNDLE_SCR = "org.apache.felix.scr";
25+
static final String BUNDLE_CORE = "org.eclipse.core.runtime";
26+
static final String BUNDLE_LAUNCHER = "org.eclipse.equinox.launcher";
27+
28+
public static Bundles of(String... bundles) {
29+
return new Bundles(Set.of(bundles));
30+
}
31+
}

tycho-core/src/main/java/org/eclipse/tycho/osgi/framework/EclipseApplication.java

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -61,8 +61,8 @@ public class EclipseApplication {
6161

6262
public static final String ARG_APPLICATION = "-application";
6363

64-
private static final Set<String> ALWAYS_START_BUNDLES = Set.of(EclipseApplicationFactory.BUNDLE_CORE,
65-
EclipseApplicationFactory.BUNDLE_SCR, EclipseApplicationFactory.BUNDLE_APP);
64+
private static final Set<String> ALWAYS_START_BUNDLES = Set.of(Bundles.BUNDLE_CORE,
65+
Bundles.BUNDLE_SCR, Bundles.BUNDLE_APP);
6666
private P2Resolver resolver;
6767
private TargetPlatform targetPlatform;
6868
private Logger logger;

tycho-core/src/main/java/org/eclipse/tycho/osgi/framework/EclipseApplicationFactory.java

Lines changed: 8 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -42,14 +42,6 @@
4242
@SessionScoped
4343
public class EclipseApplicationFactory {
4444

45-
static final String BUNDLE_APP = "org.eclipse.equinox.app";
46-
47-
static final String BUNDLE_SCR = "org.apache.felix.scr";
48-
49-
static final String BUNDLE_CORE = "org.eclipse.core.runtime";
50-
51-
private static final String BUNDLE_LAUNCHER = "org.eclipse.equinox.launcher";
52-
5345
@Requirement
5446
private ToolchainManager toolchainManager;
5547

@@ -67,15 +59,17 @@ public EclipseApplicationFactory(MavenSession mavenSession) {
6759
}
6860

6961
public EclipseApplication createEclipseApplication(MavenRepositoryLocation repositoryLocation, String name) {
62+
return createEclipseApplication(createTargetPlatform(List.of(repositoryLocation)), name);
63+
}
64+
65+
public EclipseApplication createEclipseApplication(TargetPlatform targetPlatform, String name) {
7066
P2Resolver resolver = createResolver();
71-
List<MavenRepositoryLocation> locations = List.of(repositoryLocation);
72-
TargetPlatform targetPlatform = createTargetPlatform(locations);
7367
EclipseApplication application = new EclipseApplication(name, resolver, targetPlatform, logger);
7468
//add the bare minimum required ...
75-
application.addBundle(BUNDLE_CORE);
76-
application.addBundle(BUNDLE_SCR);
77-
application.addBundle(BUNDLE_APP);
78-
application.addBundle(BUNDLE_LAUNCHER);
69+
application.addBundle(Bundles.BUNDLE_CORE);
70+
application.addBundle(Bundles.BUNDLE_SCR);
71+
application.addBundle(Bundles.BUNDLE_APP);
72+
application.addBundle(Bundles.BUNDLE_LAUNCHER);
7973
return application;
8074
}
8175

0 commit comments

Comments
 (0)