diff --git a/RELEASE_NOTES.md b/RELEASE_NOTES.md index af2cc85180..d5b7f7ca34 100644 --- a/RELEASE_NOTES.md +++ b/RELEASE_NOTES.md @@ -5,8 +5,10 @@ This page describes the noteworthy improvements provided by each release of Ecli ## 4.0.5 ### backports: + - support for PDE Api Tools Annotations - api tools fixes +- new `tycho-eclipse-plugin` ## 4.0.4 diff --git a/pom.xml b/pom.xml index 9a7ff1a6a8..7edd1f7623 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 + 4.0.5-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-eclipse-plugin/src/main/java/org/eclipse/tycho/eclipserun/EclipseRunMojo.java b/tycho-eclipse-plugin/src/main/java/org/eclipse/tycho/eclipserun/EclipseRunMojo.java new file mode 100644 index 0000000000..323a857298 --- /dev/null +++ b/tycho-eclipse-plugin/src/main/java/org/eclipse/tycho/eclipserun/EclipseRunMojo.java @@ -0,0 +1,461 @@ +/******************************************************************************* + * Copyright (c) 2011, 2016 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 + * https://www.eclipse.org/legal/epl-2.0/ + * + * SPDX-License-Identifier: EPL-2.0 + * + * Contributors: + * Sonatype Inc. - initial API and implementation + * Marc-Andre Laperle - EclipseRunMojo inspired by TestMojo + *******************************************************************************/ +package org.eclipse.tycho.eclipserun; + +import java.io.File; +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; +import java.util.Map; +import java.util.Objects; +import java.util.concurrent.ConcurrentHashMap; +import java.util.concurrent.ConcurrentMap; + +import org.apache.maven.execution.MavenSession; +import org.apache.maven.model.Dependency; +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.Mojo; +import org.apache.maven.plugins.annotations.Parameter; +import org.apache.maven.project.MavenProject; +import org.apache.maven.toolchain.Toolchain; +import org.apache.maven.toolchain.ToolchainManager; +import org.codehaus.plexus.logging.Logger; +import org.codehaus.plexus.util.FileUtils; +import org.codehaus.plexus.util.cli.CommandLineUtils; +import org.eclipse.sisu.equinox.launching.BundleStartLevel; +import org.eclipse.sisu.equinox.launching.DefaultEquinoxInstallationDescription; +import org.eclipse.sisu.equinox.launching.EquinoxInstallation; +import org.eclipse.sisu.equinox.launching.EquinoxInstallationDescription; +import org.eclipse.sisu.equinox.launching.EquinoxInstallationFactory; +import org.eclipse.sisu.equinox.launching.EquinoxLauncher; +import org.eclipse.sisu.equinox.launching.LaunchConfiguration; +import org.eclipse.sisu.equinox.launching.internal.EquinoxLaunchConfiguration; +import org.eclipse.tycho.ArtifactType; +import org.eclipse.tycho.ExecutionEnvironmentConfiguration; +import org.eclipse.tycho.IllegalArtifactReferenceException; +import org.eclipse.tycho.MavenRepositoryLocation; +import org.eclipse.tycho.TargetEnvironment; +import org.eclipse.tycho.TargetPlatform; +import org.eclipse.tycho.core.ee.ExecutionEnvironmentConfigurationImpl; +import org.eclipse.tycho.core.maven.ToolchainProvider; +import org.eclipse.tycho.core.resolver.P2ResolutionResult; +import org.eclipse.tycho.core.resolver.P2ResolutionResult.Entry; +import org.eclipse.tycho.core.resolver.P2Resolver; +import org.eclipse.tycho.core.resolver.P2ResolverFactory; +import org.eclipse.tycho.p2.target.facade.TargetPlatformConfigurationStub; + +/** + * Launch an eclipse process with arbitrary commandline arguments. The eclipse + * installation is defined by the dependencies to bundles specified. + */ +@Mojo(name = "eclipse-run", threadSafe = true) +public class EclipseRunMojo extends AbstractMojo { + + /** + * Lock object to ensure thread-safety + */ + private static final Object CREATE_LOCK = new Object(); + + private static final ConcurrentMap WORKSPACE_LOCKS = new ConcurrentHashMap<>(); + + /** + * Work area. This includes: + *
    + *
  • <work>/configuration: The configuration area + * (-configuration) + *
  • <work>/data: The data ('workspace') area (-data) + *
+ */ + @Parameter(defaultValue = "${project.build.directory}/eclipserun-work") + private File work; + + /** + * Whether the workspace should be cleared before running eclipse. + *

+ * If {@code false} and a workspace from a previous run exists, that workspace + * is reused. + *

+ */ + @Parameter(defaultValue = "true") + private boolean clearWorkspaceBeforeLaunch; + + @Parameter(property = "project") + private MavenProject project; + + /** + * Dependencies which will be resolved transitively to make up the eclipse + * runtime. Example: + * + *
+	 * <dependencies>
+	 *  <dependency>
+	 *   <artifactId>org.eclipse.ant.core</artifactId>
+	 *   <type>eclipse-plugin</type>
+	 *  </dependency>
+	 * </dependencies>
+	 * 
+ */ + @Parameter + private List dependencies = new ArrayList<>(); + + /** + * Whether to add default dependencies to bundles org.eclipse.equinox.launcher, + * org.eclipse.osgi and org.eclipse.core.runtime. + */ + @Parameter(defaultValue = "true") + private boolean addDefaultDependencies; + + /** + * Execution environment profile name used to resolve dependencies. + */ + @Parameter(defaultValue = "JavaSE-17") + private String executionEnvironment; + + /** + * p2 repositories which will be used to resolve dependencies. Example: + * + *
+	 * <repositories>
+	 *  <repository>
+	 *   <id>juno</id>
+	 *   <layout>p2</layout>
+	 *   <url>https://download.eclipse.org/releases/juno</url>
+	 *  </repository>
+	 * </repositories>
+	 * 
+ */ + @Parameter(required = true) + private List repositories; + + @Parameter(property = "session", readonly = true, required = true) + private MavenSession session; + + /** + * Arbitrary JVM options to set on the command line. It is recommended to use + * {@link #jvmArgs} instead because it provides more explicit control over + * argument separation and content, avoiding the need to quote arguments that + * contain spaces. + * + * @see #jvmArgs + */ + @Parameter + private String argLine; + + /** + * List of JVM arguments set on the command line. Example: + * + * {@code + * + * -Xdebug + * -Xrunjdwp:transport=dt_socket,server=y,suspend=n,address=1044 + * + * } + * + * @since 0.25.0 + */ + @Parameter + private List jvmArgs; + + /** + * Whether to skip mojo execution. + */ + @Parameter(property = "eclipserun.skip", defaultValue = "false") + private boolean skip; + + /** + * Arbitrary applications arguments to set on the command line. It is + * recommended to use {@link #applicationArgs} instead because it provides more + * explicit control over argument separation and content, avoiding the need to + * quote arguments that contain spaces. + * + * @see #applicationArgs + */ + @Parameter + private String appArgLine; + + /** + * List of applications arguments set on the command line. Example: + * + * {@code + * + * -buildfile + * build-test.xml + * + * } + * + * @since 0.24.0 + */ + @Parameter(alias = "applicationsArgs") + private List applicationArgs; + + /** + * Kill the forked process after a certain number of seconds. If set to 0, wait + * forever for the process, never timing out. + */ + @Parameter(property = "eclipserun.timeout") + private int forkedProcessTimeoutInSeconds; + + /** + * Additional environments to set for the forked JVM. + */ + @Parameter + private Map environmentVariables; + + /** + * Bundle start level and auto start configuration used by the eclipse runtime. + * Example: + * + *
+	 * <bundleStartLevel>
+	 *   <bundle>
+	 *     <id>foo.bar.myplugin</id>
+	 *     <level>6</level>
+	 *     <autoStart>true</autoStart>
+	 *   </bundle>
+	 * </bundleStartLevel>
+	 * 
+ */ + @Parameter + private BundleStartLevel[] bundleStartLevel; + + /** + * The default bundle start level and auto start configuration used by the + * runtime for bundles where the start level/auto start is not configured in + * {@link #bundleStartLevel}. Example: + * + *
+	 *   <defaultStartLevel>
+	 *     <level>6</level>
+	 *     <autoStart>true</autoStart>
+	 *   </defaultStartLevel>
+	 * 
+ */ + @Parameter + private BundleStartLevel defaultStartLevel; + + @Component + private EquinoxInstallationFactory installationFactory; + + @Component + private EquinoxLauncher launcher; + + @Component + private ToolchainProvider toolchainProvider; + + @Component() + P2ResolverFactory resolverFactory; + + @Component + private Logger logger; + + @Component + private ToolchainManager toolchainManager; + + public EclipseRunMojo() { + // default constructor + } + + /** + * Constructor for use of EclipseRunMojo in other Mojos. + */ + public EclipseRunMojo(File work, boolean clearWorkspaceBeforeLaunch, MavenProject project, + List dependencies, boolean addDefaultDependencies, String executionEnvironment, + List repositories, MavenSession session, List jvmArgs, boolean skip, + List applicationArgs, int forkedProcessTimeoutInSeconds, Map environmentVariables, + EquinoxInstallationFactory installationFactory, EquinoxLauncher launcher, + ToolchainProvider toolchainProvider, P2ResolverFactory resolverFactory, Logger logger, + ToolchainManager toolchainManager) { + this.work = work; + this.clearWorkspaceBeforeLaunch = clearWorkspaceBeforeLaunch; + this.project = project; + this.dependencies = dependencies; + this.addDefaultDependencies = addDefaultDependencies; + this.executionEnvironment = executionEnvironment; + this.repositories = repositories; + this.session = session; + this.jvmArgs = jvmArgs; + this.skip = skip; + this.applicationArgs = applicationArgs; + this.forkedProcessTimeoutInSeconds = forkedProcessTimeoutInSeconds; + this.environmentVariables = environmentVariables; + this.installationFactory = installationFactory; + this.launcher = launcher; + this.toolchainProvider = toolchainProvider; + this.resolverFactory = resolverFactory; + this.logger = logger; + this.toolchainManager = toolchainManager; + } + + @Override + public void execute() throws MojoExecutionException, MojoFailureException { + if (skip) { + getLog().info("Execution was skipped"); + return; + } + EquinoxInstallation installation; + synchronized (CREATE_LOCK) { + installation = createEclipseInstallation(); + } + runEclipse(installation); + } + + private void addDefaultDependency(P2Resolver resolver, String bundleId) { + try { + resolver.addDependency(ArtifactType.TYPE_ECLIPSE_PLUGIN, bundleId, null); + } catch (IllegalArtifactReferenceException e) { + // shouldn't happen for the constant type and version + throw new RuntimeException(e); + } + } + + private void addDefaultDependencies(P2Resolver resolver) { + if (addDefaultDependencies) { + addDefaultDependency(resolver, "org.eclipse.osgi"); + addDefaultDependency(resolver, DefaultEquinoxInstallationDescription.EQUINOX_LAUNCHER); + addDefaultDependency(resolver, "org.eclipse.core.runtime"); + } + } + + private EquinoxInstallation createEclipseInstallation() throws MojoFailureException { + TargetPlatformConfigurationStub tpConfiguration = new TargetPlatformConfigurationStub(); + // we want to resolve from remote repos only + tpConfiguration.setIgnoreLocalArtifacts(true); + for (Repository repository : repositories) { + tpConfiguration.addP2Repository(new MavenRepositoryLocation(repository.getId(), repository.getLocation())); + } + ExecutionEnvironmentConfiguration eeConfiguration = new ExecutionEnvironmentConfigurationImpl(logger, false, + toolchainManager, session); + eeConfiguration.setProfileConfiguration(executionEnvironment, "tycho-eclipserun-plugin "); + TargetPlatform targetPlatform = resolverFactory.getTargetPlatformFactory().createTargetPlatform(tpConfiguration, + eeConfiguration, null); + P2Resolver resolver = resolverFactory + .createResolver(Collections.singletonList(TargetEnvironment.getRunningEnvironment())); + for (Dependency dependency : dependencies) { + try { + resolver.addDependency(dependency.getType(), dependency.getArtifactId(), dependency.getVersion()); + } catch (IllegalArtifactReferenceException e) { + throw new MojoFailureException("Invalid dependency " + dependency.getType() + ":" + + dependency.getArtifactId() + ":" + dependency.getVersion() + ": " + e.getMessage(), e); + } + } + addDefaultDependencies(resolver); + EquinoxInstallationDescription installationDesc = new DefaultEquinoxInstallationDescription(); + for (P2ResolutionResult result : resolver.resolveTargetDependencies(targetPlatform, null).values()) { + for (Entry entry : result.getArtifacts()) { + if (ArtifactType.TYPE_ECLIPSE_PLUGIN.equals(entry.getType())) { + installationDesc.addBundle(entry.getId(), entry.getVersion(), entry.getLocation(true)); + } + } + } + installationDesc.setDefaultBundleStartLevel(defaultStartLevel); + if (bundleStartLevel != null) { + for (BundleStartLevel level : bundleStartLevel) { + installationDesc.addBundleStartLevel(level); + } + } + return installationFactory.createInstallation(installationDesc, work); + } + + public void runEclipse(EquinoxInstallation runtime) throws MojoExecutionException { + try { + File workspace = new File(work, "data").getAbsoluteFile(); + synchronized (WORKSPACE_LOCKS.computeIfAbsent(workspace.getAbsolutePath(), k -> new Object())) { + if (clearWorkspaceBeforeLaunch) { + FileUtils.deleteDirectory(workspace); + } + LaunchConfiguration cli = createCommandLine(runtime); + File expectedLog = new File(workspace, ".metadata/.log"); + getLog().info("Expected Eclipse log file: " + expectedLog.getCanonicalPath()); + int returnCode = launcher.execute(cli, forkedProcessTimeoutInSeconds); + if (returnCode != 0) { + throw new MojoExecutionException("Error while executing platform: return code=" + returnCode + + ", see content of " + expectedLog + "for more details."); + } + } + } catch (Exception e) { + throw new MojoExecutionException("Error while executing platform", e); + } + } + + public LaunchConfiguration createCommandLine(EquinoxInstallation runtime) throws MojoExecutionException { + EquinoxLaunchConfiguration cli = new EquinoxLaunchConfiguration(runtime); + + String executable = null; + Toolchain tc = getToolchain(); + if (tc != null) { + getLog().info("Toolchain in tycho-eclipserun-plugin: " + tc); + executable = tc.findTool("java"); + if (executable == null) { + getLog().error("No 'java' executable was found in toolchain. Current Java runtime will be used"); + } + } else if (Objects.equals(executionEnvironment, "JavaSE-" + Runtime.version().feature())) { + getLog().debug("Using current Java runtime as it matches configured executionEnvironment"); + } else { + getLog().warn("No toolchain was found in tycho-eclipserun-plugin for: " + executionEnvironment + + ". Current Java runtime will be used"); + } + cli.setJvmExecutable(executable); + cli.setWorkingDirectory(project.getBasedir()); + + cli.addVMArguments(splitArgLine(argLine)); + if (jvmArgs != null) { + cli.addVMArguments(jvmArgs.toArray(new String[jvmArgs.size()])); + } + + addProgramArgs(cli, "-install", runtime.getLocation().getAbsolutePath(), "-configuration", + new File(work, "configuration").getAbsolutePath()); + + File workspace = new File(work, "data"); + addProgramArgs(cli, "-data", workspace.getAbsolutePath()); + + cli.addProgramArguments(splitArgLine(appArgLine)); + if (applicationArgs != null) { + for (String arg : applicationArgs) { + cli.addProgramArguments(arg); + } + } + + if (environmentVariables != null) { + cli.addEnvironmentVariables(environmentVariables); + } + + return cli; + } + + private String[] splitArgLine(String argumentLine) throws MojoExecutionException { + try { + return CommandLineUtils.translateCommandline(argumentLine); + } catch (Exception e) { + throw new MojoExecutionException("Error parsing commandline: " + e.getMessage(), e); + } + } + + private void addProgramArgs(EquinoxLaunchConfiguration cli, String... arguments) { + if (arguments != null) { + for (String argument : arguments) { + if (argument != null) { + cli.addProgramArguments(argument); + } + } + } + } + + private Toolchain getToolchain() throws MojoExecutionException { + return toolchainProvider.findMatchingJavaToolChain(session, executionEnvironment); + } + +} 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 95% 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 5880c7433d..853571bdb5 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 @@ -27,6 +27,7 @@ import org.apache.maven.plugin.MojoExecutionException; import org.apache.maven.plugin.testing.SilentLog; import org.apache.maven.project.MavenProject; +import org.apache.maven.toolchain.java.DefaultJavaToolChain; import org.eclipse.sisu.equinox.launching.EquinoxInstallation; import org.eclipse.sisu.equinox.launching.EquinoxLauncher; import org.eclipse.sisu.equinox.launching.LaunchConfiguration; @@ -35,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; @@ -52,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)); @@ -153,11 +155,10 @@ public void testExecutionEnvironmentIsRespectedDuringDependencyResolution() thro } public void testExecutionEnvironmentIsRespectedDuringEclipseExecution() throws Exception { - @SuppressWarnings("deprecation") - org.apache.maven.toolchain.java.DefaultJavaToolChain mockToolchainForCustomEE = mock( - org.apache.maven.toolchain.java.DefaultJavaToolChain.class); + DefaultJavaToolChain mockToolchainForCustomEE = mock(DefaultJavaToolChain.class); when(mockToolchainForCustomEE.findTool("java")).thenReturn("/path/to/custom-ee-jdk/bin/java"); - when(toolchainProvider.findMatchingJavaToolChain(any(), eq("custom-ee"))).thenReturn(mockToolchainForCustomEE); + when(toolchainProvider.findMatchingJavaToolChain(any(), eq("custom-ee"))) + .thenReturn(mockToolchainForCustomEE); setVariableValueToObject(runMojo, "executionEnvironment", "custom-ee"); diff --git a/tycho-extras/tycho-eclipserun-plugin/pom.xml b/tycho-extras/tycho-eclipserun-plugin/pom.xml index e3582080b7..9a5b2ab57c 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-extras/tycho-eclipserun-plugin/src/main/java/org/eclipse/tycho/extras/eclipserun/EclipseRunMojo.java b/tycho-extras/tycho-eclipserun-plugin/src/main/java/org/eclipse/tycho/extras/eclipserun/EclipseRunMojo.java index ac3d619b63..934d112b82 100644 --- a/tycho-extras/tycho-eclipserun-plugin/src/main/java/org/eclipse/tycho/extras/eclipserun/EclipseRunMojo.java +++ b/tycho-extras/tycho-eclipserun-plugin/src/main/java/org/eclipse/tycho/extras/eclipserun/EclipseRunMojo.java @@ -14,257 +14,28 @@ package org.eclipse.tycho.extras.eclipserun; import java.io.File; -import java.util.ArrayList; -import java.util.Collections; import java.util.List; import java.util.Map; -import java.util.Objects; -import java.util.concurrent.ConcurrentHashMap; -import java.util.concurrent.ConcurrentMap; import org.apache.maven.execution.MavenSession; import org.apache.maven.model.Dependency; -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.Mojo; -import org.apache.maven.plugins.annotations.Parameter; import org.apache.maven.project.MavenProject; -import org.apache.maven.toolchain.Toolchain; import org.apache.maven.toolchain.ToolchainManager; import org.codehaus.plexus.logging.Logger; -import org.codehaus.plexus.util.FileUtils; -import org.codehaus.plexus.util.cli.CommandLineUtils; -import org.eclipse.sisu.equinox.launching.BundleStartLevel; -import org.eclipse.sisu.equinox.launching.DefaultEquinoxInstallationDescription; -import org.eclipse.sisu.equinox.launching.EquinoxInstallation; -import org.eclipse.sisu.equinox.launching.EquinoxInstallationDescription; import org.eclipse.sisu.equinox.launching.EquinoxInstallationFactory; import org.eclipse.sisu.equinox.launching.EquinoxLauncher; -import org.eclipse.sisu.equinox.launching.LaunchConfiguration; -import org.eclipse.sisu.equinox.launching.internal.EquinoxLaunchConfiguration; -import org.eclipse.tycho.ArtifactType; -import org.eclipse.tycho.ExecutionEnvironmentConfiguration; -import org.eclipse.tycho.IllegalArtifactReferenceException; -import org.eclipse.tycho.MavenRepositoryLocation; -import org.eclipse.tycho.TargetEnvironment; -import org.eclipse.tycho.TargetPlatform; -import org.eclipse.tycho.core.ee.ExecutionEnvironmentConfigurationImpl; import org.eclipse.tycho.core.maven.ToolchainProvider; -import org.eclipse.tycho.core.resolver.P2ResolutionResult; -import org.eclipse.tycho.core.resolver.P2ResolutionResult.Entry; -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; +import org.eclipse.tycho.eclipserun.Repository; /** * Launch an eclipse process with arbitrary commandline arguments. The eclipse * installation is defined by the dependencies to bundles specified. */ @Mojo(name = "eclipse-run", threadSafe = true) -public class EclipseRunMojo extends AbstractMojo { - - /** - * Lock object to ensure thread-safety - */ - private static final Object CREATE_LOCK = new Object(); - - private static final ConcurrentMap WORKSPACE_LOCKS = new ConcurrentHashMap<>(); - - /** - * Work area. This includes: - *
    - *
  • <work>/configuration: The configuration area - * (-configuration) - *
  • <work>/data: The data ('workspace') area (-data) - *
- */ - @Parameter(defaultValue = "${project.build.directory}/eclipserun-work") - private File work; - - /** - * Whether the workspace should be cleared before running eclipse. - *

- * If {@code false} and a workspace from a previous run exists, that workspace - * is reused. - *

- */ - @Parameter(defaultValue = "true") - private boolean clearWorkspaceBeforeLaunch; - - @Parameter(property = "project") - private MavenProject project; - - /** - * Dependencies which will be resolved transitively to make up the eclipse - * runtime. Example: - * - *
-	 * <dependencies>
-	 *  <dependency>
-	 *   <artifactId>org.eclipse.ant.core</artifactId>
-	 *   <type>eclipse-plugin</type>
-	 *  </dependency>
-	 * </dependencies>
-	 * 
- */ - @Parameter - private List dependencies = new ArrayList<>(); - - /** - * Whether to add default dependencies to bundles org.eclipse.equinox.launcher, - * org.eclipse.osgi and org.eclipse.core.runtime. - */ - @Parameter(defaultValue = "true") - private boolean addDefaultDependencies; - - /** - * Execution environment profile name used to resolve dependencies. - */ - @Parameter(defaultValue = "JavaSE-17") - private String executionEnvironment; - - /** - * p2 repositories which will be used to resolve dependencies. Example: - * - *
-	 * <repositories>
-	 *  <repository>
-	 *   <id>juno</id>
-	 *   <layout>p2</layout>
-	 *   <url>https://download.eclipse.org/releases/juno</url>
-	 *  </repository>
-	 * </repositories>
-	 * 
- */ - @Parameter(required = true) - private List repositories; - - @Parameter(property = "session", readonly = true, required = true) - private MavenSession session; - - /** - * Arbitrary JVM options to set on the command line. It is recommended to use - * {@link #jvmArgs} instead because it provides more explicit control over - * argument separation and content, avoiding the need to quote arguments that - * contain spaces. - * - * @see #jvmArgs - */ - @Parameter - private String argLine; - - /** - * List of JVM arguments set on the command line. Example: - * - * {@code - * - * -Xdebug - * -Xrunjdwp:transport=dt_socket,server=y,suspend=n,address=1044 - * - * } - * - * @since 0.25.0 - */ - @Parameter - private List jvmArgs; - - /** - * Whether to skip mojo execution. - */ - @Parameter(property = "eclipserun.skip", defaultValue = "false") - private boolean skip; - - /** - * Arbitrary applications arguments to set on the command line. It is - * recommended to use {@link #applicationArgs} instead because it provides more - * explicit control over argument separation and content, avoiding the need to - * quote arguments that contain spaces. - * - * @see #applicationArgs - */ - @Parameter - private String appArgLine; - - /** - * List of applications arguments set on the command line. Example: - * - * {@code - * - * -buildfile - * build-test.xml - * - * } - * - * @since 0.24.0 - */ - @Parameter(alias = "applicationsArgs") - private List applicationArgs; - - /** - * Kill the forked process after a certain number of seconds. If set to 0, wait - * forever for the process, never timing out. - */ - @Parameter(property = "eclipserun.timeout") - private int forkedProcessTimeoutInSeconds; - - /** - * Additional environments to set for the forked JVM. - */ - @Parameter - private Map environmentVariables; - - /** - * Bundle start level and auto start configuration used by the eclipse runtime. - * Example: - * - *
-	 * <bundleStartLevel>
-	 *   <bundle>
-	 *     <id>foo.bar.myplugin</id>
-	 *     <level>6</level>
-	 *     <autoStart>true</autoStart>
-	 *   </bundle>
-	 * </bundleStartLevel>
-	 * 
- */ - @Parameter - private BundleStartLevel[] bundleStartLevel; - - /** - * The default bundle start level and auto start configuration used by the - * runtime for bundles where the start level/auto start is not configured in - * {@link #bundleStartLevel}. Example: - * - *
-	 *   <defaultStartLevel>
-	 *     <level>6</level>
-	 *     <autoStart>true</autoStart>
-	 *   </defaultStartLevel>
-	 * 
- */ - @Parameter - private BundleStartLevel defaultStartLevel; - - @Component - private EquinoxInstallationFactory installationFactory; - - @Component - private EquinoxLauncher launcher; - - @Component - private ToolchainProvider toolchainProvider; - - @Component() - P2ResolverFactory resolverFactory; - - @Component - private Logger logger; - - @Component - private ToolchainManager toolchainManager; +@Deprecated +public class EclipseRunMojo extends org.eclipse.tycho.eclipserun.EclipseRunMojo { public EclipseRunMojo() { // default constructor @@ -280,183 +51,9 @@ public EclipseRunMojo(File work, boolean clearWorkspaceBeforeLaunch, MavenProjec EquinoxInstallationFactory installationFactory, EquinoxLauncher launcher, ToolchainProvider toolchainProvider, P2ResolverFactory resolverFactory, Logger logger, ToolchainManager toolchainManager) { - this.work = work; - this.clearWorkspaceBeforeLaunch = clearWorkspaceBeforeLaunch; - this.project = project; - this.dependencies = dependencies; - this.addDefaultDependencies = addDefaultDependencies; - this.executionEnvironment = executionEnvironment; - this.repositories = repositories; - this.session = session; - this.jvmArgs = jvmArgs; - this.skip = skip; - this.applicationArgs = applicationArgs; - this.forkedProcessTimeoutInSeconds = forkedProcessTimeoutInSeconds; - this.environmentVariables = environmentVariables; - this.installationFactory = installationFactory; - this.launcher = launcher; - this.toolchainProvider = toolchainProvider; - this.resolverFactory = resolverFactory; - this.logger = logger; - this.toolchainManager = toolchainManager; - } - - @Override - public void execute() throws MojoExecutionException, MojoFailureException { - if (skip) { - getLog().info("Execution was skipped"); - return; - } - EquinoxInstallation installation; - synchronized (CREATE_LOCK) { - installation = createEclipseInstallation(); - } - runEclipse(installation); - } - - private void addDefaultDependency(P2Resolver resolver, String bundleId) { - try { - resolver.addDependency(ArtifactType.TYPE_ECLIPSE_PLUGIN, bundleId, null); - } catch (IllegalArtifactReferenceException e) { - // shouldn't happen for the constant type and version - throw new RuntimeException(e); - } - } - - private void addDefaultDependencies(P2Resolver resolver) { - if (addDefaultDependencies) { - addDefaultDependency(resolver, "org.eclipse.osgi"); - addDefaultDependency(resolver, DefaultEquinoxInstallationDescription.EQUINOX_LAUNCHER); - addDefaultDependency(resolver, "org.eclipse.core.runtime"); - } + super(work, clearWorkspaceBeforeLaunch, project, dependencies, addDefaultDependencies, executionEnvironment, + repositories, session, jvmArgs, skip, applicationArgs, forkedProcessTimeoutInSeconds, + environmentVariables, installationFactory, launcher, toolchainProvider, resolverFactory, logger, + toolchainManager); } - - private EquinoxInstallation createEclipseInstallation() throws MojoFailureException { - TargetPlatformConfigurationStub tpConfiguration = new TargetPlatformConfigurationStub(); - // we want to resolve from remote repos only - tpConfiguration.setIgnoreLocalArtifacts(true); - for (Repository repository : repositories) { - tpConfiguration.addP2Repository(new MavenRepositoryLocation(repository.getId(), repository.getLocation())); - } - ExecutionEnvironmentConfiguration eeConfiguration = new ExecutionEnvironmentConfigurationImpl(logger, false, - toolchainManager, session); - eeConfiguration.setProfileConfiguration(executionEnvironment, "tycho-eclipserun-plugin "); - TargetPlatform targetPlatform = resolverFactory.getTargetPlatformFactory().createTargetPlatform(tpConfiguration, - eeConfiguration, null); - P2Resolver resolver = resolverFactory - .createResolver(Collections.singletonList(TargetEnvironment.getRunningEnvironment())); - for (Dependency dependency : dependencies) { - try { - resolver.addDependency(dependency.getType(), dependency.getArtifactId(), dependency.getVersion()); - } catch (IllegalArtifactReferenceException e) { - throw new MojoFailureException("Invalid dependency " + dependency.getType() + ":" - + dependency.getArtifactId() + ":" + dependency.getVersion() + ": " + e.getMessage(), e); - } - } - addDefaultDependencies(resolver); - EquinoxInstallationDescription installationDesc = new DefaultEquinoxInstallationDescription(); - for (P2ResolutionResult result : resolver.resolveTargetDependencies(targetPlatform, null).values()) { - for (Entry entry : result.getArtifacts()) { - if (ArtifactType.TYPE_ECLIPSE_PLUGIN.equals(entry.getType())) { - installationDesc.addBundle(entry.getId(), entry.getVersion(), entry.getLocation(true)); - } - } - } - installationDesc.setDefaultBundleStartLevel(defaultStartLevel); - if (bundleStartLevel != null) { - for (BundleStartLevel level : bundleStartLevel) { - installationDesc.addBundleStartLevel(level); - } - } - return installationFactory.createInstallation(installationDesc, work); - } - - void runEclipse(EquinoxInstallation runtime) throws MojoExecutionException { - try { - File workspace = new File(work, "data").getAbsoluteFile(); - synchronized (WORKSPACE_LOCKS.computeIfAbsent(workspace.getAbsolutePath(), k -> new Object())) { - if (clearWorkspaceBeforeLaunch) { - FileUtils.deleteDirectory(workspace); - } - LaunchConfiguration cli = createCommandLine(runtime); - File expectedLog = new File(workspace, ".metadata/.log"); - getLog().info("Expected Eclipse log file: " + expectedLog.getCanonicalPath()); - int returnCode = launcher.execute(cli, forkedProcessTimeoutInSeconds); - if (returnCode != 0) { - throw new MojoExecutionException("Error while executing platform: return code=" + returnCode - + ", see content of " + expectedLog + "for more details."); - } - } - } catch (Exception e) { - throw new MojoExecutionException("Error while executing platform", e); - } - } - - LaunchConfiguration createCommandLine(EquinoxInstallation runtime) throws MojoExecutionException { - EquinoxLaunchConfiguration cli = new EquinoxLaunchConfiguration(runtime); - - String executable = null; - Toolchain tc = getToolchain(); - if (tc != null) { - getLog().info("Toolchain in tycho-eclipserun-plugin: " + tc); - executable = tc.findTool("java"); - if (executable == null) { - getLog().error("No 'java' executable was found in toolchain. Current Java runtime will be used"); - } - } else if (Objects.equals(executionEnvironment, "JavaSE-" + Runtime.version().feature())) { - getLog().debug("Using current Java runtime as it matches configured executionEnvironment"); - } else { - getLog().warn("No toolchain was found in tycho-eclipserun-plugin for: " + executionEnvironment - + ". Current Java runtime will be used"); - } - cli.setJvmExecutable(executable); - cli.setWorkingDirectory(project.getBasedir()); - - cli.addVMArguments(splitArgLine(argLine)); - if (jvmArgs != null) { - cli.addVMArguments(jvmArgs.toArray(new String[jvmArgs.size()])); - } - - addProgramArgs(cli, "-install", runtime.getLocation().getAbsolutePath(), "-configuration", - new File(work, "configuration").getAbsolutePath()); - - File workspace = new File(work, "data"); - addProgramArgs(cli, "-data", workspace.getAbsolutePath()); - - cli.addProgramArguments(splitArgLine(appArgLine)); - if (applicationArgs != null) { - for (String arg : applicationArgs) { - cli.addProgramArguments(arg); - } - } - - if (environmentVariables != null) { - cli.addEnvironmentVariables(environmentVariables); - } - - return cli; - } - - private String[] splitArgLine(String argumentLine) throws MojoExecutionException { - try { - return CommandLineUtils.translateCommandline(argumentLine); - } catch (Exception e) { - throw new MojoExecutionException("Error parsing commandline: " + e.getMessage(), e); - } - } - - private void addProgramArgs(EquinoxLaunchConfiguration cli, String... arguments) { - if (arguments != null) { - for (String argument : arguments) { - if (argument != null) { - cli.addProgramArguments(argument); - } - } - } - } - - private Toolchain getToolchain() throws MojoExecutionException { - return toolchainProvider.findMatchingJavaToolChain(session, executionEnvironment); - } - } 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)); + } }