From f7d89236fc0fc0ce48a0d972d0e686440aef21bd Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Christoph=20L=C3=A4ubrich?= Date: Sat, 18 Nov 2023 11:27:07 +0100 Subject: [PATCH] Add a tycho-eclipse-plugin Tycho already offers to run an eclipse application with the tycho-extras/tycho-eclipserun-plugin, usually used to run an eclipse application. Recently there was a demand to even execute a project "like in eclipse", and there is even an (ant based) solution to run "tests in eclipse". Because of this it seem suitable to collect all these demands in a plugin dedicated to eclipse task --- RELEASE_NOTES.md | 26 +++ pom.xml | 1 + ...leRequirementsInstallableUnitProvider.java | 18 +- .../osgi/framework/EclipseApplication.java | 36 +++- .../framework/EclipseApplicationFactory.java | 6 +- .../osgi/framework/EclipseFramework.java | 9 + .../.settings/org.eclipse.jdt.core.prefs | 8 + tycho-eclipse-plugin/pom.xml | 80 ++++++++ .../BundleListTargetLocation.java | 83 ++++++++ .../tycho/eclipsebuild/EclipseBuild.java | 142 +++++++++++++ .../EclipseBuildInstallableUnitProvider.java | 55 +++++ .../tycho/eclipsebuild/EclipseBuildMojo.java | 189 ++++++++++++++++++ .../eclipsebuild/EclipseBuildResult.java | 47 +++++ .../eclipse/tycho/eclipsebuild/MarkerDTO.java | 174 ++++++++++++++++ .../tycho/eclipsebuild/SetTargetPlatform.java | 76 +++++++ .../tycho}/eclipserun/EclipseRunMojo.java | 7 +- .../eclipse/tycho/eclipserun/Repository.java | 96 +++++++++ .../m2e/lifecycle-mapping-metadata.xml | 0 .../extras/eclipserun/EclipseRunMojoTest.java | 3 +- tycho-extras/tycho-eclipserun-plugin/pom.xml | 18 +- .../helper/PluginConfigurationHelper.java | 24 ++- .../resolver/InstallableUnitProvider.java | 45 ++++- 22 files changed, 1089 insertions(+), 54 deletions(-) create mode 100644 tycho-eclipse-plugin/.settings/org.eclipse.jdt.core.prefs create mode 100644 tycho-eclipse-plugin/pom.xml create mode 100644 tycho-eclipse-plugin/src/main/java/org/eclipse/tycho/eclipsebuild/BundleListTargetLocation.java create mode 100644 tycho-eclipse-plugin/src/main/java/org/eclipse/tycho/eclipsebuild/EclipseBuild.java create mode 100644 tycho-eclipse-plugin/src/main/java/org/eclipse/tycho/eclipsebuild/EclipseBuildInstallableUnitProvider.java create mode 100644 tycho-eclipse-plugin/src/main/java/org/eclipse/tycho/eclipsebuild/EclipseBuildMojo.java create mode 100644 tycho-eclipse-plugin/src/main/java/org/eclipse/tycho/eclipsebuild/EclipseBuildResult.java create mode 100644 tycho-eclipse-plugin/src/main/java/org/eclipse/tycho/eclipsebuild/MarkerDTO.java create mode 100644 tycho-eclipse-plugin/src/main/java/org/eclipse/tycho/eclipsebuild/SetTargetPlatform.java rename {tycho-extras/tycho-eclipserun-plugin/src/main/java/org/eclipse/tycho/extras => tycho-eclipse-plugin/src/main/java/org/eclipse/tycho}/eclipserun/EclipseRunMojo.java (98%) create mode 100644 tycho-eclipse-plugin/src/main/java/org/eclipse/tycho/eclipserun/Repository.java rename {tycho-extras/tycho-eclipserun-plugin => tycho-eclipse-plugin}/src/main/resources/META-INF/m2e/lifecycle-mapping-metadata.xml (100%) rename {tycho-extras/tycho-eclipserun-plugin => tycho-eclipse-plugin}/src/test/java/org/eclipse/tycho/extras/eclipserun/EclipseRunMojoTest.java (98%) diff --git a/RELEASE_NOTES.md b/RELEASE_NOTES.md index 0d2cb2c38e..0e0d7dbf48 100644 --- a/RELEASE_NOTES.md +++ b/RELEASE_NOTES.md @@ -6,6 +6,32 @@ If you are reading this in the browser, then you can quickly jump to specific ve ## 5.0.0 (under development) +### new `tycho-eclipse-plugin` + +Tycho now contains a new `tycho-eclipse-plugin` that is dedicated to executing "tasks like eclipse", this currently includes +- the former tycho-extras `tycho-eclipserun-plugin` and its mojos +- a new `eclipse-build` mojo that allows to take a literal eclipse project and execute the build on it + +#### new `eclipse-build` mojo + +The `eclipse-build` mojo can be used like this + +```xml + + org.eclipse.tycho + tycho-eclipse-plugin + ${tycho-version} + + + eclipse-build + + eclipse-build + + + + +``` + ### support for PDE Api Tools Annotations Tycho now supports PDE Api Tools Annotations to be added to the project automatically. diff --git a/pom.xml b/pom.xml index 405a9bb328..fcbdccc557 100644 --- a/pom.xml +++ b/pom.xml @@ -557,6 +557,7 @@ tycho-targetplatform tycho-bnd-plugin tycho-repository-plugin + tycho-eclipse-plugin diff --git a/tycho-core/src/main/java/org/eclipse/tycho/core/resolver/AdditionalBundleRequirementsInstallableUnitProvider.java b/tycho-core/src/main/java/org/eclipse/tycho/core/resolver/AdditionalBundleRequirementsInstallableUnitProvider.java index f066bedd81..187d8991ac 100644 --- a/tycho-core/src/main/java/org/eclipse/tycho/core/resolver/AdditionalBundleRequirementsInstallableUnitProvider.java +++ b/tycho-core/src/main/java/org/eclipse/tycho/core/resolver/AdditionalBundleRequirementsInstallableUnitProvider.java @@ -18,7 +18,6 @@ import java.util.Collections; import java.util.List; import java.util.Optional; -import java.util.UUID; import org.apache.maven.execution.MavenSession; import org.apache.maven.project.MavenProject; @@ -29,8 +28,6 @@ import org.eclipse.equinox.p2.metadata.IInstallableUnit; import org.eclipse.equinox.p2.metadata.IRequirement; import org.eclipse.equinox.p2.metadata.MetadataFactory; -import org.eclipse.equinox.p2.metadata.MetadataFactory.InstallableUnitDescription; -import org.eclipse.equinox.p2.metadata.Version; import org.eclipse.equinox.p2.metadata.VersionRange; import org.eclipse.equinox.p2.publisher.eclipse.BundlesAction; import org.eclipse.tycho.BuildProperties; @@ -67,7 +64,7 @@ public Collection getInstallableUnits(MavenProject project, Ma try (Processor processor = bndTychoProject.get()) { List requirements = getBndClasspathRequirements(processor); if (!requirements.isEmpty()) { - return createIU(requirements); + return InstallableUnitProvider.createIU(requirements, "bnd-classpath-requirements"); } } catch (IOException e) { logger.warn("Can't determine classpath requirements from " + project.getId(), e); @@ -80,7 +77,7 @@ public Collection getInstallableUnits(MavenProject project, Ma .map(bundleName -> MetadataFactory.createRequirement(BundlesAction.CAPABILITY_NS_OSGI_BUNDLE, bundleName, VersionRange.emptyRange, null, true, true)) .toList(); - return createIU(additionalBundleRequirements); + return InstallableUnitProvider.createIU(additionalBundleRequirements, "additional-bundle-requirements"); } return Collections.emptyList(); } @@ -97,15 +94,4 @@ public static List getBndClasspathRequirements(Processor processor return Collections.emptyList(); } - private Collection createIU(List additionalBundleRequirements) { - if (additionalBundleRequirements.isEmpty()) { - return Collections.emptyList(); - } - InstallableUnitDescription result = new MetadataFactory.InstallableUnitDescription(); - result.setId("additional-bundle-requirements-" + UUID.randomUUID()); - result.setVersion(Version.createOSGi(0, 0, 0, String.valueOf(System.currentTimeMillis()))); - result.addRequirements(additionalBundleRequirements); - return List.of(MetadataFactory.createInstallableUnit(result)); - } - } diff --git a/tycho-core/src/main/java/org/eclipse/tycho/osgi/framework/EclipseApplication.java b/tycho-core/src/main/java/org/eclipse/tycho/osgi/framework/EclipseApplication.java index 236e66f6e0..6bca6000d9 100644 --- a/tycho-core/src/main/java/org/eclipse/tycho/osgi/framework/EclipseApplication.java +++ b/tycho-core/src/main/java/org/eclipse/tycho/osgi/framework/EclipseApplication.java @@ -12,6 +12,7 @@ *******************************************************************************/ package org.eclipse.tycho.osgi.framework; +import java.io.File; import java.io.IOException; import java.io.InputStream; import java.nio.file.Files; @@ -27,6 +28,7 @@ import java.util.Set; import java.util.function.Predicate; +import org.apache.maven.project.MavenProject; import org.codehaus.plexus.logging.Logger; import org.eclipse.equinox.p2.metadata.IInstallableUnit; import org.eclipse.equinox.p2.metadata.IRequirement; @@ -61,8 +63,8 @@ public class EclipseApplication { public static final String ARG_APPLICATION = "-application"; - private static final Set ALWAYS_START_BUNDLES = Set.of(Bundles.BUNDLE_CORE, - Bundles.BUNDLE_SCR, Bundles.BUNDLE_APP); + private static final Set ALWAYS_START_BUNDLES = Set.of(Bundles.BUNDLE_CORE, Bundles.BUNDLE_SCR, + Bundles.BUNDLE_APP); private P2Resolver resolver; private TargetPlatform targetPlatform; private Logger logger; @@ -72,12 +74,15 @@ public class EclipseApplication { private Map frameworkProperties = new LinkedHashMap<>(); private Predicate loggingFilter = always -> true; private Set startBundles = new HashSet<>(ALWAYS_START_BUNDLES); + private Map baseDirMap; - EclipseApplication(String name, P2Resolver resolver, TargetPlatform targetPlatform, Logger logger) { + EclipseApplication(String name, P2Resolver resolver, TargetPlatform targetPlatform, Logger logger, + Map baseDirMap) { this.name = name; this.resolver = resolver; this.targetPlatform = targetPlatform; this.logger = logger; + this.baseDirMap = baseDirMap; } public synchronized Collection getApplicationBundles() { @@ -100,7 +105,18 @@ private List resolveBundles(P2Resolver resolver) { for (Entry entry : result.getArtifacts()) { if (ArtifactType.TYPE_ECLIPSE_PLUGIN.equals(entry.getType()) && !"org.eclipse.osgi".equals(entry.getId())) { - resolvedBundles.add(entry.getLocation(true).toPath()); + File location = entry.getLocation(true); + Path path = location.toPath(); + if (location.isDirectory()) { + MavenProject mavenProject = baseDirMap.get(location); + if (mavenProject != null) { + File artifactFile = mavenProject.getArtifact().getFile(); + if (artifactFile != null && artifactFile.exists()) { + path = artifactFile.toPath(); + } + } + } + resolvedBundles.add(path); } } } @@ -187,10 +203,14 @@ public EclipseFramework startFramework(EclipseWorkspace workspace, List + 4.0.0 + + org.eclipse.tycho + tycho + 5.0.0-SNAPSHOT + + tycho-eclipse-plugin + Tycho Eclipse Plugin + maven-plugin + + ${minimal-maven-version} + + Maven Plugins for working with Eclipse + + + org.apache.maven + maven-core + + + org.apache.maven + maven-plugin-api + + + org.apache.maven.plugin-tools + maven-plugin-annotations + + + org.codehaus.plexus + plexus-utils + + + org.eclipse.tycho + tycho-core + ${project.version} + + + org.eclipse.pde + org.eclipse.pde.core + 3.17.100 + jar + + + * + * + + + + + org.eclipse.tycho + sisu-equinox-launching + ${project.version} + + + org.eclipse.tycho + tycho-testing-harness + test + + + org.mockito + mockito-core + test + + + org.apache.maven + maven-compat + test + + + + + + org.codehaus.plexus + plexus-component-metadata + + + + \ No newline at end of file diff --git a/tycho-eclipse-plugin/src/main/java/org/eclipse/tycho/eclipsebuild/BundleListTargetLocation.java b/tycho-eclipse-plugin/src/main/java/org/eclipse/tycho/eclipsebuild/BundleListTargetLocation.java new file mode 100644 index 0000000000..cdb3c4735c --- /dev/null +++ b/tycho-eclipse-plugin/src/main/java/org/eclipse/tycho/eclipsebuild/BundleListTargetLocation.java @@ -0,0 +1,83 @@ +/******************************************************************************* + * Copyright (c) 2019 Red Hat 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 + * https://www.eclipse.org/legal/epl-2.0/ + * + * SPDX-License-Identifier: EPL-2.0 + * + * Contributors: + * - Mickael Istria (Red Hat Inc.) + *******************************************************************************/ +package org.eclipse.tycho.eclipsebuild; + +import org.eclipse.core.runtime.CoreException; +import org.eclipse.core.runtime.IProgressMonitor; +import org.eclipse.core.runtime.IStatus; +import org.eclipse.core.runtime.Status; +import org.eclipse.pde.core.target.ITargetDefinition; +import org.eclipse.pde.core.target.ITargetLocation; +import org.eclipse.pde.core.target.TargetBundle; +import org.eclipse.pde.core.target.TargetFeature; + +class BundleListTargetLocation implements ITargetLocation { + + private TargetBundle[] bundles; + + public BundleListTargetLocation(TargetBundle[] bundles) { + this.bundles = bundles; + } + + @Override + public T getAdapter(Class adapter) { + return null; + } + + @Override + public IStatus resolve(ITargetDefinition definition, IProgressMonitor monitor) { + return Status.OK_STATUS; + } + + @Override + public boolean isResolved() { + return true; + } + + @Override + public IStatus getStatus() { + return Status.OK_STATUS; + } + + @Override + public String getType() { + return "BundleList"; //$NON-NLS-1$ + } + + @Override + public String getLocation(boolean resolve) throws CoreException { + return null; + } + + @Override + public TargetBundle[] getBundles() { + return this.bundles; + } + + @Override + public TargetFeature[] getFeatures() { + return null; + } + + @Override + public String[] getVMArguments() { + return null; + } + + @Override + public String serialize() { + return null; + } + +} diff --git a/tycho-eclipse-plugin/src/main/java/org/eclipse/tycho/eclipsebuild/EclipseBuild.java b/tycho-eclipse-plugin/src/main/java/org/eclipse/tycho/eclipsebuild/EclipseBuild.java new file mode 100644 index 0000000000..58a210cf56 --- /dev/null +++ b/tycho-eclipse-plugin/src/main/java/org/eclipse/tycho/eclipsebuild/EclipseBuild.java @@ -0,0 +1,142 @@ +/******************************************************************************* + * Copyright (c) 2023 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.eclipsebuild; + +import java.io.IOException; +import java.io.Serializable; +import java.nio.file.Path; +import java.util.concurrent.Callable; + +import org.eclipse.core.resources.IMarker; +import org.eclipse.core.resources.IProject; +import org.eclipse.core.resources.IProjectDescription; +import org.eclipse.core.resources.IResource; +import org.eclipse.core.resources.IWorkspace; +import org.eclipse.core.resources.IWorkspaceDescription; +import org.eclipse.core.resources.IncrementalProjectBuilder; +import org.eclipse.core.resources.ResourcesPlugin; +import org.eclipse.core.runtime.CoreException; +import org.eclipse.core.runtime.IPath; +import org.eclipse.core.runtime.IProgressMonitor; +import org.eclipse.core.runtime.NullProgressMonitor; +import org.eclipse.core.runtime.Platform; + +public class EclipseBuild implements Callable, Serializable { + + private boolean debug; + private String baseDir; + + EclipseBuild(Path projectDir, boolean debug) { + this.debug = debug; + this.baseDir = pathAsString(projectDir); + } + + @Override + public EclipseBuildResult call() throws Exception { + EclipseBuildResult result = new EclipseBuildResult(); + Platform.addLogListener((status, plugin) -> debug(status.toString())); + disableAutoBuild(); + deleteAllProjects(); + IProject project = importProject(); + IProgressMonitor debugMonitor = new IProgressMonitor() { + + @Override + public void worked(int work) { + + } + + @Override + public void subTask(String name) { + debug("SubTask: " + name); + } + + @Override + public void setTaskName(String name) { + debug("Task: " + name); + } + + @Override + public void setCanceled(boolean value) { + + } + + @Override + public boolean isCanceled() { + return false; + } + + @Override + public void internalWorked(double work) { + + } + + @Override + public void done() { + + } + + @Override + public void beginTask(String name, int totalWork) { + setTaskName(name); + } + }; + project.build(IncrementalProjectBuilder.CLEAN_BUILD, debugMonitor); + project.build(IncrementalProjectBuilder.FULL_BUILD, debugMonitor); + for (IMarker marker : project.findMarkers(IMarker.PROBLEM, true, IResource.DEPTH_INFINITE)) { + result.addMarker(marker); + debug(marker.toString()); + } + ResourcesPlugin.getWorkspace().save(true, new NullProgressMonitor()); + return result; + } + + static void disableAutoBuild() throws CoreException { + IWorkspace workspace = ResourcesPlugin.getWorkspace(); + IWorkspaceDescription desc = workspace.getDescription(); + desc.setAutoBuilding(false); + workspace.setDescription(desc); + } + + private void deleteAllProjects() throws CoreException { + for (IProject project : ResourcesPlugin.getWorkspace().getRoot().getProjects()) { + project.delete(IResource.NEVER_DELETE_PROJECT_CONTENT | IResource.FORCE, new NullProgressMonitor()); + } + } + + private IProject importProject() throws CoreException, IOException { + IPath projectPath = IPath.fromOSString(baseDir); + IPath projectDescriptionFile = projectPath.append(IProjectDescription.DESCRIPTION_FILE_NAME); + IProjectDescription projectDescription = ResourcesPlugin.getWorkspace() + .loadProjectDescription(projectDescriptionFile); + projectDescription.setLocation(projectPath); +// projectDescription.setBuildSpec(new ICommand[0]); + IProject project = ResourcesPlugin.getWorkspace().getRoot().getProject(projectDescription.getName()); + project.create(projectDescription, new NullProgressMonitor()); + project.open(new NullProgressMonitor()); + return project; + } + + private void debug(String string) { + if (debug) { + System.out.println(string); + } + } + + static String pathAsString(Path path) { + if (path != null) { + return path.toAbsolutePath().toString(); + } + return null; + } + +} diff --git a/tycho-eclipse-plugin/src/main/java/org/eclipse/tycho/eclipsebuild/EclipseBuildInstallableUnitProvider.java b/tycho-eclipse-plugin/src/main/java/org/eclipse/tycho/eclipsebuild/EclipseBuildInstallableUnitProvider.java new file mode 100644 index 0000000000..f904e5aeec --- /dev/null +++ b/tycho-eclipse-plugin/src/main/java/org/eclipse/tycho/eclipsebuild/EclipseBuildInstallableUnitProvider.java @@ -0,0 +1,55 @@ +/******************************************************************************* + * Copyright (c) 2023 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.eclipsebuild; + +import java.util.Collection; +import java.util.List; +import java.util.Optional; + +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.core.runtime.CoreException; +import org.eclipse.equinox.p2.metadata.IInstallableUnit; +import org.eclipse.equinox.p2.metadata.MetadataFactory; +import org.eclipse.equinox.p2.metadata.VersionRange; +import org.eclipse.equinox.p2.publisher.eclipse.BundlesAction; +import org.eclipse.tycho.helper.PluginConfigurationHelper; +import org.eclipse.tycho.helper.PluginConfigurationHelper.Configuration; +import org.eclipse.tycho.resolver.InstallableUnitProvider; + +@Component(role = InstallableUnitProvider.class, hint = "eclipse-build") +public class EclipseBuildInstallableUnitProvider implements InstallableUnitProvider { + + @Requirement + PluginConfigurationHelper configurationHelper; + + @Override + public Collection getInstallableUnits(MavenProject project, MavenSession session) + throws CoreException { + Configuration configuration = configurationHelper.getConfiguration(EclipseBuildMojo.class, project, session); + Optional local = configuration.getBoolean(EclipseBuildMojo.PARAMETER_LOCAL); + if (local.isPresent() && local.get()) { + // for local target resolution the bundles become requirements... + Optional> list = configuration.getStringList("bundles"); + return InstallableUnitProvider.createIU(list.stream().flatMap(Collection::stream) + .map(bundleName -> MetadataFactory.createRequirement(BundlesAction.CAPABILITY_NS_OSGI_BUNDLE, + bundleName, + VersionRange.emptyRange, null, false, true)), + "eclipse-build-bundles"); + } + return List.of(); + } + +} diff --git a/tycho-eclipse-plugin/src/main/java/org/eclipse/tycho/eclipsebuild/EclipseBuildMojo.java b/tycho-eclipse-plugin/src/main/java/org/eclipse/tycho/eclipsebuild/EclipseBuildMojo.java new file mode 100644 index 0000000000..fa90c29f4c --- /dev/null +++ b/tycho-eclipse-plugin/src/main/java/org/eclipse/tycho/eclipsebuild/EclipseBuildMojo.java @@ -0,0 +1,189 @@ +/******************************************************************************* + * Copyright (c) 2023 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.eclipsebuild; + +import java.lang.reflect.InvocationTargetException; +import java.nio.file.Path; +import java.util.Collection; +import java.util.HashSet; +import java.util.List; +import java.util.Set; +import java.util.function.Consumer; +import java.util.stream.Collectors; + +import org.apache.maven.model.Repository; +import org.apache.maven.plugin.AbstractMojo; +import org.apache.maven.plugin.MojoExecutionException; +import org.apache.maven.plugin.MojoFailureException; +import org.apache.maven.plugins.annotations.Component; +import org.apache.maven.plugins.annotations.LifecyclePhase; +import org.apache.maven.plugins.annotations.Mojo; +import org.apache.maven.plugins.annotations.Parameter; +import org.apache.maven.plugins.annotations.ResolutionScope; +import org.apache.maven.project.MavenProject; +import org.eclipse.core.resources.IMarker; +import org.eclipse.core.runtime.CoreException; +import org.eclipse.tycho.TargetPlatform; +import org.eclipse.tycho.core.TychoProjectManager; +import org.eclipse.tycho.osgi.framework.Bundles; +import org.eclipse.tycho.osgi.framework.EclipseApplication; +import org.eclipse.tycho.osgi.framework.EclipseApplicationManager; +import org.eclipse.tycho.osgi.framework.EclipseFramework; +import org.eclipse.tycho.osgi.framework.EclipseWorkspaceManager; +import org.eclipse.tycho.osgi.framework.Features; +import org.osgi.framework.BundleException; + +/** + * This mojo allows to perform an eclipse-build on a project like it would be performed inside the + * IDE, this can be useful in cases where there are very special builders that are not part of + * Tycho. + */ +@Mojo(name = "eclipse-build", defaultPhase = LifecyclePhase.COMPILE, threadSafe = true, requiresDependencyCollection = ResolutionScope.COMPILE_PLUS_RUNTIME) +public class EclipseBuildMojo extends AbstractMojo { + + static final String PARAMETER_LOCAL = "local"; + + private static final String NAME = "Eclipse Build Project"; + + @Parameter() + private Repository eclipseRepository; + + @Parameter(defaultValue = "false", property = "tycho.eclipsebuild.skip") + private boolean skip; + + @Parameter(defaultValue = "false", property = "tycho.eclipsebuild.debug") + private boolean debug; + + /** + * Controls if the local target platform of the project should be used to resolve the eclipse + * application + */ + @Parameter(defaultValue = "false", property = "tycho.eclipsebuild.local", name = PARAMETER_LOCAL) + private boolean local; + + @Parameter(defaultValue = "true", property = "tycho.eclipsebuild.failOnError") + private boolean failOnError; + + @Parameter + private List bundles; + + @Parameter(property = "project", readonly = true) + private MavenProject project; + + @Component + private EclipseWorkspaceManager workspaceManager; + + @Component + private EclipseApplicationManager eclipseApplicationManager; + + @Component + private TychoProjectManager projectManager; + + @Override + public void execute() throws MojoExecutionException, MojoFailureException { + + Collection projectDependencies; + try { + projectDependencies = projectManager.getProjectDependencies(project); + } catch (Exception e) { + throw new MojoFailureException("Can't resolve project dependencies", e); + } + EclipseApplication application; + //TODO configureable by parameters! + Bundles bundles = new Bundles(getBundles()); + Features features = new Features(Set.of()); + if (local) { + TargetPlatform targetPlatform = projectManager.getTargetPlatform(project).orElseThrow( + () -> new MojoFailureException("Can't get target platform for project " + project.getId())); + application = eclipseApplicationManager.getApplication(targetPlatform, bundles, features, NAME); + } else { + application = eclipseApplicationManager.getApplication(eclipseRepository, bundles, features, NAME); + } + try (EclipseFramework framework = application.startFramework(workspaceManager + .getWorkspace(EclipseApplicationManager.getRepository(eclipseRepository).getURL(), this), List.of())) { + if (debug) { + framework.printState(); + } + if (framework.hasBundle(Bundles.BUNDLE_PDE_CORE)) { + framework.execute(new SetTargetPlatform(projectDependencies, debug)); + } else { + getLog().info("Skip set Target Platform because " + Bundles.BUNDLE_PDE_CORE + + " is not part of the framework..."); + } + EclipseBuildResult result = framework + .execute(new EclipseBuild(project.getBasedir().toPath(), debug)); + result.markers().filter(marker -> marker.getAttribute(IMarker.SEVERITY, -1) == IMarker.SEVERITY_INFO) + .forEach(info -> printMarker(info, result, getLog()::info)); + result.markers().filter(marker -> marker.getAttribute(IMarker.SEVERITY, -1) == IMarker.SEVERITY_WARNING) + .forEach(warn -> printMarker(warn, result, getLog()::warn)); + List errors = result.markers() + .filter(marker -> marker.getAttribute(IMarker.SEVERITY, -1) == IMarker.SEVERITY_ERROR).toList(); + errors.forEach(error -> printMarker(error, result, getLog()::error)); + if (failOnError && errors.size() > 0) { + String msg = errors.stream().map(problem -> asString(problem, result)) + .collect(Collectors.joining(System.lineSeparator())); + throw new MojoFailureException("There are Build errors:" + System.lineSeparator() + msg); + } + } catch (BundleException e) { + throw new MojoFailureException("Can't start framework!", e); + } catch (InvocationTargetException e) { + Throwable cause = e.getCause(); + if (cause.getClass().getName().equals(CoreException.class.getName())) { + throw new MojoFailureException(cause.getMessage(), cause); + } + throw new MojoExecutionException(cause); + } + } + + private Set getBundles() { + HashSet set = new HashSet<>(); + set.add("org.eclipse.core.resources"); + set.add("org.eclipse.core.runtime"); + set.add("org.eclipse.core.jobs"); + if (bundles != null) { + set.addAll(bundles); + } + return set; + } + + private void printMarker(IMarker marker, EclipseBuildResult result, Consumer consumer) { + StringBuilder sb = asString(marker, result); + consumer.accept(sb.toString().trim()); + } + + private StringBuilder asString(IMarker marker, EclipseBuildResult result) { + StringBuilder sb = new StringBuilder(); + String path = result.getMarkerPath(marker); + if (path != null) { + sb.append(path); + int line = marker.getAttribute("lineNumber", -1); + if (line > -1) { + sb.append(":"); + sb.append(line); + } + sb.append(" "); + } + String message = marker.getAttribute("message", ""); + if (!message.isBlank()) { + sb.append(message); + sb.append(" "); + } + String sourceId = marker.getAttribute("sourceId", ""); + if (!sourceId.isBlank()) { + sb.append(sourceId); + sb.append(" "); + } + return sb; + } + +} diff --git a/tycho-eclipse-plugin/src/main/java/org/eclipse/tycho/eclipsebuild/EclipseBuildResult.java b/tycho-eclipse-plugin/src/main/java/org/eclipse/tycho/eclipsebuild/EclipseBuildResult.java new file mode 100644 index 0000000000..3ec70742d8 --- /dev/null +++ b/tycho-eclipse-plugin/src/main/java/org/eclipse/tycho/eclipsebuild/EclipseBuildResult.java @@ -0,0 +1,47 @@ +/******************************************************************************* + * Copyright (c) 2023 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.eclipsebuild; + +import java.io.Serializable; +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.stream.Stream; + +import org.eclipse.core.resources.IMarker; +import org.eclipse.core.resources.IResource; + +public class EclipseBuildResult implements Serializable { + + private List markers = new ArrayList(); + private Map resourceMap = new HashMap(); + + public void addMarker(IMarker marker) { + + MarkerDTO wrapper = new MarkerDTO(marker); + markers.add(wrapper); + IResource resource = marker.getResource(); + if (resource != null) { + resourceMap.put(wrapper, resource.getProjectRelativePath().toString()); + } + } + + public Stream markers() { + return markers.stream(); + } + + public String getMarkerPath(IMarker marker) { + return resourceMap.get(marker); + } +} diff --git a/tycho-eclipse-plugin/src/main/java/org/eclipse/tycho/eclipsebuild/MarkerDTO.java b/tycho-eclipse-plugin/src/main/java/org/eclipse/tycho/eclipsebuild/MarkerDTO.java new file mode 100644 index 0000000000..411b86f83e --- /dev/null +++ b/tycho-eclipse-plugin/src/main/java/org/eclipse/tycho/eclipsebuild/MarkerDTO.java @@ -0,0 +1,174 @@ +/******************************************************************************* + * Copyright (c) 2023 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.eclipsebuild; + +import java.io.Serializable; +import java.util.HashMap; +import java.util.Map; + +import org.eclipse.core.resources.IMarker; +import org.eclipse.core.resources.IResource; +import org.eclipse.core.runtime.CoreException; +import org.eclipse.core.runtime.Status; + +class MarkerDTO implements IMarker, Serializable { + + private long id; + private long creationTime; + private String type; + private String string; + private HashMap attributes; + private boolean exits; + + MarkerDTO(IMarker marker) { + id = marker.getId(); + string = marker.toString(); + attributes = new HashMap(); + exits = marker.exists(); + try { + creationTime = marker.getCreationTime(); + } catch (CoreException e) { + } + try { + type = marker.getType(); + } catch (CoreException e) { + } + try { + Map map = marker.getAttributes(); + if (map != null) { + map.forEach((k, v) -> { + if (v == null) { + return; + } + attributes.put(k, v.toString()); + }); + } + } catch (CoreException e) { + } + } + + @Override + public Object getAttribute(String attributeName) throws CoreException { + return getAttribute(attributeName, null); + } + + @Override + public int getAttribute(String attributeName, int defaultValue) { + try { + return Integer.parseInt(getAttribute(attributeName, String.valueOf(defaultValue))); + } catch (NumberFormatException e) { + return defaultValue; + } + } + + @Override + public String getAttribute(String attributeName, String defaultValue) { + return (String) attributes.getOrDefault(attributeName, defaultValue); + } + + @Override + public boolean getAttribute(String attributeName, boolean defaultValue) { + return Boolean.parseBoolean(getAttribute(attributeName, String.valueOf(defaultValue))); + } + + @Override + public Map getAttributes() throws CoreException { + checkExsits(); + return attributes; + } + + @Override + public Object[] getAttributes(String[] attributeNames) throws CoreException { + throw new CoreException(Status.error("Not implemented")); + } + + @Override + public long getCreationTime() throws CoreException { + checkExsits(); + return creationTime; + } + + @Override + public long getId() { + return id; + } + + @Override + public String getType() throws CoreException { + checkExsits(); + return type; + } + + @Override + public IResource getResource() { + return null; + } + + @Override + public T getAdapter(Class adapter) { + return null; + } + + @Override + public void delete() throws CoreException { + throw new CoreException(Status.error("Not implemented")); + } + + @Override + public boolean exists() { + return exits; + } + + @Override + public boolean isSubtypeOf(String superType) throws CoreException { + throw new CoreException(Status.error("Not implemented")); + } + + @Override + public void setAttribute(String attributeName, int value) throws CoreException { + throw new CoreException(Status.error("Not implemented")); + + } + + @Override + public void setAttribute(String attributeName, Object value) throws CoreException { + throw new CoreException(Status.error("Not implemented")); + } + + @Override + public void setAttribute(String attributeName, boolean value) throws CoreException { + throw new CoreException(Status.error("Not implemented")); + } + + @Override + public void setAttributes(String[] attributeNames, Object[] values) throws CoreException { + throw new CoreException(Status.error("Not implemented")); + } + + @Override + public void setAttributes(Map attributes) throws CoreException { + throw new CoreException(Status.error("Not implemented")); + } + + private void checkExsits() throws CoreException { + if (!exits) { + throw new CoreException(Status.error("DO not exits")); + } + + } + + @Override + public String toString() { + return string; + } +} diff --git a/tycho-eclipse-plugin/src/main/java/org/eclipse/tycho/eclipsebuild/SetTargetPlatform.java b/tycho-eclipse-plugin/src/main/java/org/eclipse/tycho/eclipsebuild/SetTargetPlatform.java new file mode 100644 index 0000000000..616326345a --- /dev/null +++ b/tycho-eclipse-plugin/src/main/java/org/eclipse/tycho/eclipsebuild/SetTargetPlatform.java @@ -0,0 +1,76 @@ +/******************************************************************************* + * Copyright (c) 2023 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.eclipsebuild; + +import java.io.File; +import java.io.Serializable; +import java.nio.file.Path; +import java.util.Collection; +import java.util.List; +import java.util.Objects; +import java.util.concurrent.Callable; + +import org.eclipse.core.runtime.CoreException; +import org.eclipse.core.runtime.ILogListener; +import org.eclipse.core.runtime.Platform; +import org.eclipse.core.runtime.jobs.Job; +import org.eclipse.pde.core.target.ITargetDefinition; +import org.eclipse.pde.core.target.ITargetLocation; +import org.eclipse.pde.core.target.ITargetPlatformService; +import org.eclipse.pde.core.target.LoadTargetDefinitionJob; +import org.eclipse.pde.core.target.TargetBundle; +import org.eclipse.pde.internal.core.target.TargetPlatformService; + +public class SetTargetPlatform implements Callable, Serializable { + + private boolean debug; + private List targetBundles; + + SetTargetPlatform(Collection dependencyBundles, boolean debug) { + this.debug = debug; + this.targetBundles = dependencyBundles.stream().map(EclipseBuild::pathAsString).toList(); + } + + @Override + public Serializable call() throws Exception { + ILogListener listener = (status, plugin) -> debug(status.toString()); + Platform.addLogListener(listener); + EclipseBuild.disableAutoBuild(); + ITargetPlatformService service = TargetPlatformService.getDefault(); + ITargetDefinition target = service.newTarget(); + target.setName("buildpath"); + debug("== Target Platform Bundles =="); + TargetBundle[] bundles = targetBundles.stream().peek(this::debug).map(absoluteFile -> { + try { + return new TargetBundle(new File(absoluteFile)); + } catch (CoreException e) { + debug(e.toString()); + return null; + } + }).filter(Objects::nonNull)// + .toArray(TargetBundle[]::new); + target.setTargetLocations(new ITargetLocation[] { new BundleListTargetLocation(bundles) }); + service.saveTargetDefinition(target); + Job job = new LoadTargetDefinitionJob(target); + job.schedule(); + job.join(); + return null; + } + + private void debug(String string) { + if (debug) { + System.out.println(string); + } + } + +} diff --git a/tycho-extras/tycho-eclipserun-plugin/src/main/java/org/eclipse/tycho/extras/eclipserun/EclipseRunMojo.java b/tycho-eclipse-plugin/src/main/java/org/eclipse/tycho/eclipserun/EclipseRunMojo.java similarity index 98% rename from tycho-extras/tycho-eclipserun-plugin/src/main/java/org/eclipse/tycho/extras/eclipserun/EclipseRunMojo.java rename to tycho-eclipse-plugin/src/main/java/org/eclipse/tycho/eclipserun/EclipseRunMojo.java index ac3d619b63..323a857298 100644 --- a/tycho-extras/tycho-eclipserun-plugin/src/main/java/org/eclipse/tycho/extras/eclipserun/EclipseRunMojo.java +++ b/tycho-eclipse-plugin/src/main/java/org/eclipse/tycho/eclipserun/EclipseRunMojo.java @@ -11,7 +11,7 @@ * Sonatype Inc. - initial API and implementation * Marc-Andre Laperle - EclipseRunMojo inspired by TestMojo *******************************************************************************/ -package org.eclipse.tycho.extras.eclipserun; +package org.eclipse.tycho.eclipserun; import java.io.File; import java.util.ArrayList; @@ -57,7 +57,6 @@ import org.eclipse.tycho.core.resolver.P2Resolver; import org.eclipse.tycho.core.resolver.P2ResolverFactory; import org.eclipse.tycho.p2.target.facade.TargetPlatformConfigurationStub; -import org.eclipse.tycho.plugins.p2.extras.Repository; /** * Launch an eclipse process with arbitrary commandline arguments. The eclipse @@ -371,7 +370,7 @@ private EquinoxInstallation createEclipseInstallation() throws MojoFailureExcept return installationFactory.createInstallation(installationDesc, work); } - void runEclipse(EquinoxInstallation runtime) throws MojoExecutionException { + public void runEclipse(EquinoxInstallation runtime) throws MojoExecutionException { try { File workspace = new File(work, "data").getAbsoluteFile(); synchronized (WORKSPACE_LOCKS.computeIfAbsent(workspace.getAbsolutePath(), k -> new Object())) { @@ -392,7 +391,7 @@ void runEclipse(EquinoxInstallation runtime) throws MojoExecutionException { } } - LaunchConfiguration createCommandLine(EquinoxInstallation runtime) throws MojoExecutionException { + public LaunchConfiguration createCommandLine(EquinoxInstallation runtime) throws MojoExecutionException { EquinoxLaunchConfiguration cli = new EquinoxLaunchConfiguration(runtime); String executable = null; diff --git a/tycho-eclipse-plugin/src/main/java/org/eclipse/tycho/eclipserun/Repository.java b/tycho-eclipse-plugin/src/main/java/org/eclipse/tycho/eclipserun/Repository.java new file mode 100644 index 0000000000..b7f6579ae8 --- /dev/null +++ b/tycho-eclipse-plugin/src/main/java/org/eclipse/tycho/eclipserun/Repository.java @@ -0,0 +1,96 @@ +/******************************************************************************* + * Copyright (c) 2011 SAP AG 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: + * Tobias Oberlies - initial API and implementation + *******************************************************************************/ +package org.eclipse.tycho.eclipserun; + +import java.net.URI; +import java.util.Arrays; +import java.util.stream.Collectors; + +public final class Repository { + + public enum Layout { + BOTH("p2"), METADATA("p2-metadata"), ARTIFACTS("p2-artifacts"); + + private String literal; + + private Layout(String literal) { + this.literal = literal; + } + + boolean matches(String value) { + return literal.equals(value); + } + + public boolean hasMetadata() { + return this != ARTIFACTS; + } + + public boolean hasArtifacts() { + return this != METADATA; + } + + @Override + public String toString() { + return literal; + } + } + + private String id; + + private URI url; + + private Layout layout = Layout.BOTH; + + public Repository() { + } + + public Repository(URI location) { + this.url = location; + } + + /** + * @return never null + */ + public URI getLocation() { + if (url == null) + throw new IllegalStateException("Attribute 'url' is required for source repositories"); + return url; + } + + /** + * @return never null + */ + public Layout getLayout() { + return layout; + } + + /** + * @return may be null + */ + public String getId() { + return id; + } + + public void setLayout(String value) { + for (Layout layout : Layout.values()) { + if (layout.matches(value)) { + this.layout = layout; + return; + } + } + String values = Arrays.stream(Layout.values()).map(Object::toString).collect(Collectors.joining(", ")); + throw new IllegalArgumentException( + "Unrecognized value for attribute 'layout': \"" + value + "\". Valid values are: " + values); + } + +} diff --git a/tycho-extras/tycho-eclipserun-plugin/src/main/resources/META-INF/m2e/lifecycle-mapping-metadata.xml b/tycho-eclipse-plugin/src/main/resources/META-INF/m2e/lifecycle-mapping-metadata.xml similarity index 100% rename from tycho-extras/tycho-eclipserun-plugin/src/main/resources/META-INF/m2e/lifecycle-mapping-metadata.xml rename to tycho-eclipse-plugin/src/main/resources/META-INF/m2e/lifecycle-mapping-metadata.xml diff --git a/tycho-extras/tycho-eclipserun-plugin/src/test/java/org/eclipse/tycho/extras/eclipserun/EclipseRunMojoTest.java b/tycho-eclipse-plugin/src/test/java/org/eclipse/tycho/extras/eclipserun/EclipseRunMojoTest.java similarity index 98% rename from tycho-extras/tycho-eclipserun-plugin/src/test/java/org/eclipse/tycho/extras/eclipserun/EclipseRunMojoTest.java rename to tycho-eclipse-plugin/src/test/java/org/eclipse/tycho/extras/eclipserun/EclipseRunMojoTest.java index 76b8f0fca1..8460d243a1 100644 --- a/tycho-extras/tycho-eclipserun-plugin/src/test/java/org/eclipse/tycho/extras/eclipserun/EclipseRunMojoTest.java +++ b/tycho-eclipse-plugin/src/test/java/org/eclipse/tycho/extras/eclipserun/EclipseRunMojoTest.java @@ -36,6 +36,7 @@ import org.eclipse.tycho.core.maven.ToolchainProvider; import org.eclipse.tycho.core.resolver.P2Resolver; import org.eclipse.tycho.core.resolver.P2ResolverFactory; +import org.eclipse.tycho.eclipserun.EclipseRunMojo; import org.eclipse.tycho.p2.target.facade.TargetPlatformFactory; import org.eclipse.tycho.testing.AbstractTychoMojoTestCase; import org.eclipse.tycho.version.TychoVersion; @@ -53,7 +54,7 @@ public class EclipseRunMojoTest extends AbstractTychoMojoTestCase { public void setUp() throws Exception { super.setUp(); - runMojo = (EclipseRunMojo) lookupMojo("org.eclipse.tycho.extras", "tycho-eclipserun-plugin", + runMojo = (EclipseRunMojo) lookupMojo("org.eclipse.tycho", "tycho-eclipse-plugin", TychoVersion.getTychoVersion(), "eclipse-run", null); runMojo.setLog(new SilentLog()); MavenSession mavenSession = newMavenSession(mock(MavenProject.class)); diff --git a/tycho-extras/tycho-eclipserun-plugin/pom.xml b/tycho-extras/tycho-eclipserun-plugin/pom.xml index dfa7727018..c7a33be4a2 100644 --- a/tycho-extras/tycho-eclipserun-plugin/pom.xml +++ b/tycho-extras/tycho-eclipserun-plugin/pom.xml @@ -23,6 +23,13 @@ ${minimal-maven-version} + + + org.eclipse.tycho + tycho-eclipse-plugin + The eclipse-run mojo is now part of the tycho-eclipse-plugin + + Tycho Eclipserun Plugin @@ -57,15 +64,10 @@ org.eclipse.tycho - tycho-testing-harness - test - - - org.mockito - mockito-core - test + tycho-eclipse-plugin + ${project.version} - + its diff --git a/tycho-spi/src/main/java/org/eclipse/tycho/helper/PluginConfigurationHelper.java b/tycho-spi/src/main/java/org/eclipse/tycho/helper/PluginConfigurationHelper.java index e8dce66a84..1ffeafa52d 100644 --- a/tycho-spi/src/main/java/org/eclipse/tycho/helper/PluginConfigurationHelper.java +++ b/tycho-spi/src/main/java/org/eclipse/tycho/helper/PluginConfigurationHelper.java @@ -15,6 +15,8 @@ import java.io.BufferedReader; import java.io.InputStreamReader; import java.net.URL; +import java.util.Arrays; +import java.util.List; import java.util.Map; import java.util.Optional; import java.util.concurrent.ConcurrentHashMap; @@ -129,11 +131,7 @@ public Optional getChild(String name) { public Optional getString(String name) { return getChild(name).map(child -> { - String value = child.configuration.getValue(); - if (value == null) { - return child.configuration.getAttribute("default-value"); - } - return value; + return getValue(child.configuration); }); } @@ -158,6 +156,22 @@ public String toString() { return configuration == null ? "-empty configuration-" : String.valueOf(configuration); } + public Optional> getStringList(String name) { + return getChild(name).map(child -> { + return Arrays.stream(child.configuration.getChildren()).map(PluginConfigurationHelper::getValue) + .toList(); + }); + + } + + } + + private static String getValue(Xpp3Dom dom) { + String value = dom.getValue(); + if (value == null) { + return dom.getAttribute("default-value"); + } + return value; } } diff --git a/tycho-spi/src/main/java/org/eclipse/tycho/resolver/InstallableUnitProvider.java b/tycho-spi/src/main/java/org/eclipse/tycho/resolver/InstallableUnitProvider.java index 4813bde1b2..936f4a2906 100644 --- a/tycho-spi/src/main/java/org/eclipse/tycho/resolver/InstallableUnitProvider.java +++ b/tycho-spi/src/main/java/org/eclipse/tycho/resolver/InstallableUnitProvider.java @@ -1,26 +1,49 @@ package org.eclipse.tycho.resolver; import java.util.Collection; +import java.util.Collections; +import java.util.List; +import java.util.UUID; +import java.util.stream.Stream; import org.apache.maven.execution.MavenSession; import org.apache.maven.project.MavenProject; import org.eclipse.core.runtime.CoreException; import org.eclipse.equinox.p2.metadata.IInstallableUnit; +import org.eclipse.equinox.p2.metadata.IRequirement; +import org.eclipse.equinox.p2.metadata.MetadataFactory; +import org.eclipse.equinox.p2.metadata.MetadataFactory.InstallableUnitDescription; +import org.eclipse.equinox.p2.metadata.Version; /** - * Components implementing this interface can provide additional project units, - * for example source features/bundles. + * Components implementing this interface can provide additional project units, for example source + * features/bundles. */ public interface InstallableUnitProvider { - /** - * Computes the {@link IInstallableUnit}s for the given maven project - * - * @param project - * @param session - * @return the collection of units, probably empty but never null - * @throws CoreException if anything goes wrong - */ - Collection getInstallableUnits(MavenProject project, MavenSession session) throws CoreException; + /** + * Computes the {@link IInstallableUnit}s for the given maven project + * + * @param project + * @param session + * @return the collection of units, probably empty but never null + * @throws CoreException + * if anything goes wrong + */ + Collection getInstallableUnits(MavenProject project, MavenSession session) throws CoreException; + static Collection createIU(Stream requirements, String idPrefix) { + return createIU(requirements.toList(), idPrefix); + } + + static Collection createIU(List requirements, String idPrefix) { + if (requirements.isEmpty()) { + return Collections.emptyList(); + } + InstallableUnitDescription result = new MetadataFactory.InstallableUnitDescription(); + result.setId(idPrefix + "-" + UUID.randomUUID()); + result.setVersion(Version.createOSGi(0, 0, 0, String.valueOf(System.currentTimeMillis()))); + result.addRequirements(requirements); + return List.of(MetadataFactory.createInstallableUnit(result)); + } }