From d2b4270e7a7740bc18958fb88a3d60626d5bf699 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Christoph=20L=C3=A4ubrich?= Date: Sun, 19 Jan 2025 14:18:15 +0100 Subject: [PATCH] Extract common code to AbstractEclipseBuildMojo so it can be reused Currently EclipseBuildMojo performs the boilerplate to perform an eclipse like build inside tycho build, but this can be useful for other scenarios as well. This now extracts the common code into an AbstractEclipseBuildMojo so it can be reused. (cherry picked from commit 82559f8a5b6526d3a76a1a7e7f9de8d84e2a5735) --- .../AbstractEclipseBuildMojo.java | 198 +++++++++++++++++ .../EclipseBuildInstallableUnitProvider.java | 4 +- .../tycho/eclipsebuild/EclipseBuildMojo.java | 207 ------------------ .../eclipsebuild/EclipseBuildProjectMojo.java | 59 +++++ 4 files changed, 259 insertions(+), 209 deletions(-) create mode 100644 tycho-eclipse-plugin/src/main/java/org/eclipse/tycho/eclipsebuild/AbstractEclipseBuildMojo.java delete 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/EclipseBuildProjectMojo.java diff --git a/tycho-eclipse-plugin/src/main/java/org/eclipse/tycho/eclipsebuild/AbstractEclipseBuildMojo.java b/tycho-eclipse-plugin/src/main/java/org/eclipse/tycho/eclipsebuild/AbstractEclipseBuildMojo.java new file mode 100644 index 0000000000..b22dfe9193 --- /dev/null +++ b/tycho-eclipse-plugin/src/main/java/org/eclipse/tycho/eclipsebuild/AbstractEclipseBuildMojo.java @@ -0,0 +1,198 @@ +/******************************************************************************* + * Copyright (c) 2025 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.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.concurrent.Callable; +import java.util.function.Consumer; + +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.plugin.logging.Log; +import org.apache.maven.plugins.annotations.Component; +import org.apache.maven.plugins.annotations.Parameter; +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; + +/** + * An abstract mojo baseclass that can be used to perform actions on an eclipse + * project that requires the build infrastructure. + * + * @param the rsult type + */ +public abstract class AbstractEclipseBuildMojo extends AbstractMojo { + + static final String PARAMETER_LOCAL = "local"; + + @Parameter() + private Repository eclipseRepository; + + @Parameter(defaultValue = "false", property = "tycho.eclipsebuild.skip") + private boolean skip; + + @Parameter(defaultValue = "false", property = "tycho.eclipsebuild.debug") + protected 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.printMarker") + private boolean printMarker; + + @Parameter + private List bundles; + + @Parameter + private List features; + + @Parameter(property = "project", readonly = true) + protected MavenProject project; + + @Component + private EclipseWorkspaceManager workspaceManager; + + @Component + private EclipseApplicationManager eclipseApplicationManager; + + @Component + private TychoProjectManager projectManager; + + @Override + public final 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; + Bundles bundles = new Bundles(getBundles()); + Features features = new Features(getFeatures()); + 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, getName()); + } else { + application = eclipseApplicationManager.getApplication(eclipseRepository, bundles, features, getName()); + } + 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..."); + } + Result result = framework.execute(createExecutable()); + if (printMarker) { + Log log = getLog(); + result.markers().filter(marker -> marker.getAttribute(IMarker.SEVERITY, -1) == IMarker.SEVERITY_INFO) + .forEach(info -> printMarker(info, result, log::info)); + result.markers().filter(marker -> marker.getAttribute(IMarker.SEVERITY, -1) == IMarker.SEVERITY_WARNING) + .forEach(warn -> printMarker(warn, result, log::warn)); + result.markers().filter(marker -> marker.getAttribute(IMarker.SEVERITY, -1) == IMarker.SEVERITY_ERROR) + .forEach(error -> printMarker(error, result, log::error)); + } + handleResult(result); + } 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); + } + } + + protected abstract void handleResult(Result result) throws MojoFailureException; + + protected abstract & Serializable, R extends Serializable> R createExecutable(); + + protected Set getFeatures() { + Set set = new HashSet<>(); + if (features != null) { + set.addAll(features); + } + return set; + } + + protected Set getBundles() { + Set 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 static void printMarker(IMarker marker, EclipseBuildResult result, Consumer consumer) { + consumer.accept(asString(marker, result).toString().trim()); + } + + protected static 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; + } + + protected abstract String getName(); + +} 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 index 037eedc360..5299163281 100644 --- 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 @@ -38,8 +38,8 @@ public class EclipseBuildInstallableUnitProvider implements InstallableUnitProvi @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); + Configuration configuration = configurationHelper.getConfiguration(EclipseBuildProjectMojo.class, project, session); + Optional local = configuration.getBoolean(EclipseBuildProjectMojo.PARAMETER_LOCAL); if (local.isPresent() && local.get()) { // for local target resolution the bundles become requirements... Optional> list = configuration.getStringList("bundles"); 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 deleted file mode 100644 index 883e196016..0000000000 --- a/tycho-eclipse-plugin/src/main/java/org/eclipse/tycho/eclipsebuild/EclipseBuildMojo.java +++ /dev/null @@ -1,207 +0,0 @@ -/******************************************************************************* - * 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.plugin.logging.Log; -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(defaultValue = "true", property = "tycho.eclipsebuild.printMarker") - private boolean printMarker; - - @Parameter - private List bundles; - - @Parameter - private List features; - - @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(getFeatures()); - 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)); - List errors = result.markers() - .filter(marker -> marker.getAttribute(IMarker.SEVERITY, -1) == IMarker.SEVERITY_ERROR).toList(); - if (printMarker) { - Log log = getLog(); - result.markers().filter(marker -> marker.getAttribute(IMarker.SEVERITY, -1) == IMarker.SEVERITY_INFO) - .forEach(info -> printMarker(info, result, log::info)); - result.markers().filter(marker -> marker.getAttribute(IMarker.SEVERITY, -1) == IMarker.SEVERITY_WARNING) - .forEach(warn -> printMarker(warn, result, log::warn)); - errors.forEach(error -> printMarker(error, result, log::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 getFeatures() { - Set set = new HashSet<>(); - if (features != null) { - set.addAll(features); - } - return set; - } - - private Set getBundles() { - Set 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/EclipseBuildProjectMojo.java b/tycho-eclipse-plugin/src/main/java/org/eclipse/tycho/eclipsebuild/EclipseBuildProjectMojo.java new file mode 100644 index 0000000000..b57242572c --- /dev/null +++ b/tycho-eclipse-plugin/src/main/java/org/eclipse/tycho/eclipsebuild/EclipseBuildProjectMojo.java @@ -0,0 +1,59 @@ +/******************************************************************************* + * 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.List; +import java.util.stream.Collectors; + +import org.apache.maven.plugin.MojoFailureException; +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.eclipse.core.resources.IMarker; + +/** + * 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 EclipseBuildProjectMojo extends AbstractEclipseBuildMojo { + + @Parameter(defaultValue = "true", property = "tycho.eclipsebuild.failOnError") + private boolean failOnError; + + @Override + protected String getName() { + return "Eclipse Project Build"; + } + + @SuppressWarnings("unchecked") + @Override + protected EclipseBuild createExecutable() { + return new EclipseBuild(project.getBasedir().toPath(), debug); + } + + @Override + protected void handleResult(EclipseBuildResult result) throws MojoFailureException { + if (failOnError) { + List errorMarkers = result.markers() + .filter(marker -> marker.getAttribute(IMarker.SEVERITY, -1) == IMarker.SEVERITY_ERROR).toList(); + if (errorMarkers.size() > 0) { + String msg = errorMarkers.stream().map(problem -> asString(problem, result)) + .collect(Collectors.joining(System.lineSeparator())); + throw new MojoFailureException("There are Build errors:" + System.lineSeparator() + msg); + } + } + } +}