From 48631df5e9224437517dec12c7c5ed5026439471 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Christoph=20L=C3=A4ubrich?= Date: Thu, 9 Dec 2021 20:11:33 +0100 Subject: [PATCH 1/2] #439 - Adopt PDE configurations for maven-bundle-plugins MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit add initial code contribution and project setup Signed-off-by: Christoph Läubrich --- org.eclipse.m2e.pde.connector/.classpath | 7 + org.eclipse.m2e.pde.connector/.project | 28 ++ .../.settings/org.eclipse.jdt.core.prefs | 9 + .../META-INF/MANIFEST.MF | 17 ++ .../build.properties | 5 + .../lifecycle-mapping-metadata.xml | 38 +++ org.eclipse.m2e.pde.connector/plugin.xml | 16 ++ .../eclipse/m2e/pde/connector/Activator.java | 44 +++ .../PDEMavenBundlePluginConfigurator.java | 125 +++++++++ .../m2e/pde/connector/PDEProjectHelper.java | 260 ++++++++++++++++++ org.eclipse.m2e.pde.feature/feature.xml | 9 +- .../org.eclipse.core.resources.prefs | 2 + pom.xml | 1 + 13 files changed, 560 insertions(+), 1 deletion(-) create mode 100644 org.eclipse.m2e.pde.connector/.classpath create mode 100644 org.eclipse.m2e.pde.connector/.project create mode 100644 org.eclipse.m2e.pde.connector/.settings/org.eclipse.jdt.core.prefs create mode 100644 org.eclipse.m2e.pde.connector/META-INF/MANIFEST.MF create mode 100644 org.eclipse.m2e.pde.connector/build.properties create mode 100644 org.eclipse.m2e.pde.connector/lifecycle-mapping-metadata.xml create mode 100644 org.eclipse.m2e.pde.connector/plugin.xml create mode 100644 org.eclipse.m2e.pde.connector/src/org/eclipse/m2e/pde/connector/Activator.java create mode 100644 org.eclipse.m2e.pde.connector/src/org/eclipse/m2e/pde/connector/PDEMavenBundlePluginConfigurator.java create mode 100644 org.eclipse.m2e.pde.connector/src/org/eclipse/m2e/pde/connector/PDEProjectHelper.java create mode 100644 org.eclipse.m2e.site/.settings/org.eclipse.core.resources.prefs diff --git a/org.eclipse.m2e.pde.connector/.classpath b/org.eclipse.m2e.pde.connector/.classpath new file mode 100644 index 000000000..e801ebfb4 --- /dev/null +++ b/org.eclipse.m2e.pde.connector/.classpath @@ -0,0 +1,7 @@ + + + + + + + diff --git a/org.eclipse.m2e.pde.connector/.project b/org.eclipse.m2e.pde.connector/.project new file mode 100644 index 000000000..8d8090cb5 --- /dev/null +++ b/org.eclipse.m2e.pde.connector/.project @@ -0,0 +1,28 @@ + + + org.eclipse.m2e.pde.connector + + + + + + org.eclipse.jdt.core.javabuilder + + + + + org.eclipse.pde.ManifestBuilder + + + + + org.eclipse.pde.SchemaBuilder + + + + + + org.eclipse.pde.PluginNature + org.eclipse.jdt.core.javanature + + diff --git a/org.eclipse.m2e.pde.connector/.settings/org.eclipse.jdt.core.prefs b/org.eclipse.m2e.pde.connector/.settings/org.eclipse.jdt.core.prefs new file mode 100644 index 000000000..c9545f06a --- /dev/null +++ b/org.eclipse.m2e.pde.connector/.settings/org.eclipse.jdt.core.prefs @@ -0,0 +1,9 @@ +eclipse.preferences.version=1 +org.eclipse.jdt.core.compiler.codegen.targetPlatform=11 +org.eclipse.jdt.core.compiler.compliance=11 +org.eclipse.jdt.core.compiler.problem.assertIdentifier=error +org.eclipse.jdt.core.compiler.problem.enablePreviewFeatures=disabled +org.eclipse.jdt.core.compiler.problem.enumIdentifier=error +org.eclipse.jdt.core.compiler.problem.reportPreviewFeatures=warning +org.eclipse.jdt.core.compiler.release=enabled +org.eclipse.jdt.core.compiler.source=11 diff --git a/org.eclipse.m2e.pde.connector/META-INF/MANIFEST.MF b/org.eclipse.m2e.pde.connector/META-INF/MANIFEST.MF new file mode 100644 index 000000000..8d94d0d6f --- /dev/null +++ b/org.eclipse.m2e.pde.connector/META-INF/MANIFEST.MF @@ -0,0 +1,17 @@ +Manifest-Version: 1.0 +Bundle-ManifestVersion: 2 +Bundle-Name: M2E PDE Connector +Bundle-SymbolicName: org.eclipse.m2e.pde.connector;singleton:=true +Bundle-Version: 1.20.0.qualifier +Automatic-Module-Name: org.eclipse.m2e.pde.connector +Bundle-RequiredExecutionEnvironment: JavaSE-11 +Bundle-Vendor: Eclipse.org - m2e +Bundle-ActivationPolicy: lazy +Require-Bundle: org.eclipse.m2e.core;bundle-version="1.18.4", + org.eclipse.m2e.jdt;bundle-version="1.18.3", + org.eclipse.core.runtime;bundle-version="3.24.0", + org.eclipse.core.resources;bundle-version="3.16.0", + org.eclipse.m2e.maven.runtime;bundle-version="1.18.2", + org.eclipse.pde.core, + org.eclipse.jdt.core +Bundle-Activator: org.eclipse.m2e.pde.connector.Activator diff --git a/org.eclipse.m2e.pde.connector/build.properties b/org.eclipse.m2e.pde.connector/build.properties new file mode 100644 index 000000000..e9863e281 --- /dev/null +++ b/org.eclipse.m2e.pde.connector/build.properties @@ -0,0 +1,5 @@ +source.. = src/ +output.. = bin/ +bin.includes = META-INF/,\ + .,\ + plugin.xml diff --git a/org.eclipse.m2e.pde.connector/lifecycle-mapping-metadata.xml b/org.eclipse.m2e.pde.connector/lifecycle-mapping-metadata.xml new file mode 100644 index 000000000..85428db36 --- /dev/null +++ b/org.eclipse.m2e.pde.connector/lifecycle-mapping-metadata.xml @@ -0,0 +1,38 @@ + + + + + + + bundle + org.eclipse.m2e.jdt.JarLifecycleMapping + + + + + + + + org.apache.felix + maven-bundle-plugin + [3.2.0,) + + manifest + bundle + + + + + org.eclipse.m2e.pde.connector.configurator.bundle + + + + + + \ No newline at end of file diff --git a/org.eclipse.m2e.pde.connector/plugin.xml b/org.eclipse.m2e.pde.connector/plugin.xml new file mode 100644 index 000000000..98133496a --- /dev/null +++ b/org.eclipse.m2e.pde.connector/plugin.xml @@ -0,0 +1,16 @@ + + + + + + + + + + + diff --git a/org.eclipse.m2e.pde.connector/src/org/eclipse/m2e/pde/connector/Activator.java b/org.eclipse.m2e.pde.connector/src/org/eclipse/m2e/pde/connector/Activator.java new file mode 100644 index 000000000..0a49a4ced --- /dev/null +++ b/org.eclipse.m2e.pde.connector/src/org/eclipse/m2e/pde/connector/Activator.java @@ -0,0 +1,44 @@ +/******************************************************************************* + * Copyright (c) 2021 Christoph Läubrich + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v2.0 + * which accompanies this distribution, and is available at + * https://www.eclipse.org/legal/epl-v20.html + * + * SPDX-License-Identifier: EPL-2.0 + * + * Contributors: + * Christoph Läubrich - initial API and implementation + *******************************************************************************/ +package org.eclipse.m2e.pde.connector; + +import java.util.Optional; + +import org.eclipse.pde.core.project.IBundleProjectService; +import org.osgi.framework.BundleActivator; +import org.osgi.framework.BundleContext; +import org.osgi.util.tracker.ServiceTracker; + +public class Activator implements BundleActivator { + + private static volatile ServiceTracker bundleProjectServiceTracker; + + @Override + public void start(BundleContext context) throws Exception { + bundleProjectServiceTracker = new ServiceTracker<>(context, IBundleProjectService.class, null); + bundleProjectServiceTracker.open(); + } + + @Override + public void stop(BundleContext context) throws Exception { + bundleProjectServiceTracker.close(); + } + + public static Optional getBundleProjectService() { + if (bundleProjectServiceTracker == null) { + return Optional.empty(); + } + return Optional.ofNullable(bundleProjectServiceTracker.getService()); + } + +} diff --git a/org.eclipse.m2e.pde.connector/src/org/eclipse/m2e/pde/connector/PDEMavenBundlePluginConfigurator.java b/org.eclipse.m2e.pde.connector/src/org/eclipse/m2e/pde/connector/PDEMavenBundlePluginConfigurator.java new file mode 100644 index 000000000..63b0ae3af --- /dev/null +++ b/org.eclipse.m2e.pde.connector/src/org/eclipse/m2e/pde/connector/PDEMavenBundlePluginConfigurator.java @@ -0,0 +1,125 @@ +/******************************************************************************* + * Copyright (c) 2008 Sonatype, Inc. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/legal/epl-v10.html + *******************************************************************************/ +package org.eclipse.m2e.pde.connector; + + +import java.io.File; +import java.util.ArrayList; +import java.util.List; + +import org.apache.maven.plugin.MojoExecution; +import org.apache.maven.project.MavenProject; +import org.eclipse.core.resources.IMarker; +import org.eclipse.core.resources.IProject; +import org.eclipse.core.runtime.CoreException; +import org.eclipse.core.runtime.IPath; +import org.eclipse.core.runtime.IProgressMonitor; +import org.eclipse.m2e.core.MavenPlugin; +import org.eclipse.m2e.core.embedder.IMaven; +import org.eclipse.m2e.core.internal.IMavenConstants; +import org.eclipse.m2e.core.internal.markers.MavenProblemInfo; +import org.eclipse.m2e.core.internal.markers.SourceLocation; +import org.eclipse.m2e.core.internal.markers.SourceLocationHelper; +import org.eclipse.m2e.core.project.IMavenProjectFacade; +import org.eclipse.m2e.core.project.configurator.AbstractProjectConfigurator; +import org.eclipse.m2e.core.project.configurator.ILifecycleMappingConfiguration; +import org.eclipse.m2e.core.project.configurator.MojoExecutionKey; +import org.eclipse.m2e.core.project.configurator.ProjectConfigurationRequest; +import org.eclipse.m2e.jdt.IClasspathDescriptor; +import org.eclipse.m2e.jdt.IJavaProjectConfigurator; + +/** + * This configurator performs the following tasks: + *
    + *
  • Enable the PDE nature for this project to make PDE aware of this + * project
  • + *
  • Set the location of the "bundle-root" where PDE looks for the + * manifest
  • + *
+ */ +@SuppressWarnings( "restriction" ) +public class PDEMavenBundlePluginConfigurator + extends AbstractProjectConfigurator + implements IJavaProjectConfigurator +{ + + public static final String FELIX_PARAM_MANIFESTLOCATION = "manifestLocation"; + + @Override + public void configure( ProjectConfigurationRequest request, IProgressMonitor monitor ) + throws CoreException + { + List executions = getMojoExecutions( request, monitor ); + + if ( executions.size() > 1 ) + { + List errors = new ArrayList(); + + for ( MojoExecution mojoExecution : executions ) + { + SourceLocation location = SourceLocationHelper.findLocation( mojoExecution.getPlugin(), "executions" ); + MavenProblemInfo problem = + new MavenProblemInfo( "Duplicate " + mojoExecution.getGoal() + + " executions found. Please remove any explicitly defined " + mojoExecution.getGoal() + + " executions in your pom.xml.", IMarker.SEVERITY_ERROR, location ); + errors.add( problem ); + } + + this.markerManager.addErrorMarkers( request.getPom(), IMavenConstants.MARKER_LIFECYCLEMAPPING_ID, errors ); + } + + // bundle manifest is generated in #configureRawClasspath, which is invoked earlier during project configuration + + IProject project = request.getProject(); + IMavenProjectFacade facade = request.getMavenProjectFacade(); + + IPath metainfPath = getMetainfPath( facade, executions, monitor ); + + PDEProjectHelper.addPDENature( project, metainfPath, monitor ); + } + + @Override + public void configureClasspath( IMavenProjectFacade facade, IClasspathDescriptor classpath, IProgressMonitor monitor ) + throws CoreException + { + } + + @Override + public void configureRawClasspath( ProjectConfigurationRequest request, IClasspathDescriptor classpath, + IProgressMonitor monitor ) + throws CoreException + { + } + + private IPath getMetainfPath( IMavenProjectFacade facade, List executions, IProgressMonitor monitor ) + throws CoreException + { + IMaven maven = MavenPlugin.getMaven(); + for ( MojoExecution execution : executions ) + { + MavenProject mavenProject = facade.getMavenProject( monitor ); + File location = + maven.getMojoParameterValue(mavenProject, execution, FELIX_PARAM_MANIFESTLOCATION, File.class, + monitor); + if ( location != null ) + { + return facade.getProjectRelativePath( location.getAbsolutePath() ); + } + } + + return null; + } + + @Override + public boolean hasConfigurationChanged( IMavenProjectFacade newFacade, + ILifecycleMappingConfiguration oldProjectConfiguration, + MojoExecutionKey key, IProgressMonitor monitor ) + { + return false; + } +} diff --git a/org.eclipse.m2e.pde.connector/src/org/eclipse/m2e/pde/connector/PDEProjectHelper.java b/org.eclipse.m2e.pde.connector/src/org/eclipse/m2e/pde/connector/PDEProjectHelper.java new file mode 100644 index 000000000..d0e9f8543 --- /dev/null +++ b/org.eclipse.m2e.pde.connector/src/org/eclipse/m2e/pde/connector/PDEProjectHelper.java @@ -0,0 +1,260 @@ +/******************************************************************************* + * Copyright (c) 2011 Sonatype, Inc. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/legal/epl-v10.html + *******************************************************************************/ +package org.eclipse.m2e.pde.connector; + +import java.io.File; +import java.util.ArrayList; +import java.util.Iterator; +import java.util.List; + +import org.apache.maven.project.MavenProject; +import org.eclipse.core.resources.ICommand; +import org.eclipse.core.resources.IContainer; +import org.eclipse.core.resources.IFile; +import org.eclipse.core.resources.IFolder; +import org.eclipse.core.resources.IProject; +import org.eclipse.core.resources.IProjectDescription; +import org.eclipse.core.resources.IResource; +import org.eclipse.core.resources.WorkspaceJob; +import org.eclipse.core.runtime.CoreException; +import org.eclipse.core.runtime.IPath; +import org.eclipse.core.runtime.IProgressMonitor; +import org.eclipse.core.runtime.IStatus; +import org.eclipse.core.runtime.Path; +import org.eclipse.core.runtime.Status; +import org.eclipse.jdt.core.IClasspathEntry; +import org.eclipse.jdt.core.IJavaProject; +import org.eclipse.jdt.core.JavaCore; +import org.eclipse.m2e.core.project.MavenProjectUtils; +import org.eclipse.m2e.core.project.configurator.AbstractProjectConfigurator; +import org.eclipse.pde.core.plugin.IPluginModelBase; +import org.eclipse.pde.core.plugin.PluginRegistry; +import org.eclipse.pde.core.project.IBundleProjectService; +import org.eclipse.pde.internal.core.ClasspathComputer; +import org.eclipse.pde.internal.core.IPluginModelListener; +import org.eclipse.pde.internal.core.PDECore; +import org.eclipse.pde.internal.core.PluginModelDelta; +import org.eclipse.pde.internal.core.natures.PDE; +import org.eclipse.pde.internal.core.project.PDEProject; +import org.eclipse.pde.internal.core.util.CoreUtility; + +@SuppressWarnings( "restriction" ) +public class PDEProjectHelper +{ + private static boolean isListeningForPluginModelChanges = false; + + private static final List projectsForUpdateClasspath = new ArrayList(); + + private static final PDEProjectHelper INSTANCE = new PDEProjectHelper(); + + private PDEProjectHelper() + { + + } + + public static PDEProjectHelper getInstance() + { + return INSTANCE; + } + + private static final IPluginModelListener classpathUpdater = new IPluginModelListener() + { + @Override + public void modelsChanged( PluginModelDelta delta ) + { + synchronized ( projectsForUpdateClasspath ) + { + if ( projectsForUpdateClasspath.size() == 0 ) + { + return; + } + + Iterator projectsIter = projectsForUpdateClasspath.iterator(); + while ( projectsIter.hasNext() ) + { + IProject project = projectsIter.next(); + IPluginModelBase model = PluginRegistry.findModel( project ); + if ( model == null ) + { + continue; + } + + UpdateClasspathWorkspaceJob job = new UpdateClasspathWorkspaceJob( project, model ); + job.schedule(); + projectsIter.remove(); + } + } + } + }; + + private static class UpdateClasspathWorkspaceJob + extends WorkspaceJob + { + private final IProject project; + + private final IPluginModelBase model; + + public UpdateClasspathWorkspaceJob( IProject project, IPluginModelBase model ) + { + super( "Updating classpath" ); + this.project = project; + this.model = model; + } + + @Override + public IStatus runInWorkspace( IProgressMonitor monitor ) + throws CoreException + { + setClasspath( project, model, monitor ); + return Status.OK_STATUS; + } + } + + public void configurePDEBundleProject( IProject project, MavenProject mavenProject, IProgressMonitor monitor ) + throws CoreException + { + // see org.eclipse.pde.internal.ui.wizards.plugin.NewProjectCreationOperation + + if ( !project.hasNature( PDE.PLUGIN_NATURE ) ) + { + CoreUtility.addNatureToProject( project, PDE.PLUGIN_NATURE, null ); + } + + if ( !project.hasNature( JavaCore.NATURE_ID ) ) + { + CoreUtility.addNatureToProject( project, JavaCore.NATURE_ID, null ); + } + + // PDE can't handle default JDT classpath + IJavaProject javaProject = JavaCore.create( project ); + javaProject.setOutputLocation( getOutputLocation( project, mavenProject, monitor ), monitor ); + + // see org.eclipse.pde.internal.ui.wizards.tools.UpdateClasspathJob + // PDE populates the model cache lazily from WorkspacePluginModelManager.visit() ResourceChangeListenter + // That means the model may be available or not at this point in the lifecycle. + // If it is, update its classpath right away. + // If not add the project to the list to be updated later based on model change events. + IPluginModelBase model = PluginRegistry.findModel( project ); + if ( model != null ) + { + setClasspath( project, model, monitor ); + } + else + { + addProjectForUpdateClasspath( project ); + } + } + + private void addProjectForUpdateClasspath( IProject project ) + { + synchronized ( projectsForUpdateClasspath ) + { + projectsForUpdateClasspath.add( project ); + if ( !isListeningForPluginModelChanges ) + { + PDECore.getDefault().getModelManager().addPluginModelListener( classpathUpdater ); + isListeningForPluginModelChanges = true; + } + } + } + + private IPath getOutputLocation( IProject project, MavenProject mavenProject, IProgressMonitor monitor ) + throws CoreException + { + File outputDirectory = new File( mavenProject.getBuild().getOutputDirectory() ); + outputDirectory.mkdirs(); + IPath relPath = + MavenProjectUtils.getProjectRelativePath( project, mavenProject.getBuild().getOutputDirectory() ); + IFolder folder = project.getFolder( relPath ); + folder.refreshLocal( IResource.DEPTH_INFINITE, monitor ); + return folder.getFullPath(); + } + + public static void addPDENature( IProject project, IPath manifestPath, IProgressMonitor monitor ) + throws CoreException + { + AbstractProjectConfigurator.addNature( project, PDE.PLUGIN_NATURE, monitor ); + IProjectDescription description = project.getDescription(); + ICommand[] prevBuilders = description.getBuildSpec(); + ArrayList newBuilders = new ArrayList(); + for ( ICommand builder : prevBuilders ) + { + if ( !builder.getBuilderName().startsWith( "org.eclipse.pde" ) ) + { + newBuilders.add( builder ); + } + } + description.setBuildSpec( newBuilders.toArray( new ICommand[newBuilders.size()] ) ); + project.setDescription( description, monitor ); + + setManifestLocaton( project, manifestPath, monitor ); + } + + protected static void setManifestLocaton( IProject project, IPath manifestPath, IProgressMonitor monitor ) + throws CoreException + { + IBundleProjectService projectService = Activator.getBundleProjectService().get(); + if ( manifestPath != null && manifestPath.segmentCount() > 1 ) + { + IPath metainfPath = manifestPath.removeLastSegments( 1 ); + project.getFile( metainfPath ).refreshLocal( IResource.DEPTH_INFINITE, monitor ); + projectService.setBundleRoot( project, metainfPath ); + } + else + { + // in case of configuration update, reset to the default value + projectService.setBundleRoot( project, null ); + } + } + + /** + * Returns bundle manifest as known to PDE project metadata. Returned file may not exist in the workspace or on the + * filesystem. Never returns null. + */ + public static IFile getBundleManifest( IProject project ) + throws CoreException + { + // PDE API is very inconvenient, lets use internal classes instead + IContainer metainf = PDEProject.getBundleRoot( project ); + if ( metainf == null || metainf instanceof IProject ) + { + metainf = project.getFolder( "META-INF" ); + } + else + { + metainf = metainf.getFolder( new Path( "META-INF" ) ); + } + + return metainf.getFile( new Path( "MANIFEST.MF" ) ); + } + + private static void setClasspath( IProject project, IPluginModelBase model, IProgressMonitor monitor ) + throws CoreException + { + IClasspathEntry[] entries = ClasspathComputer.getClasspath(project, model, null, true /*clear existing entries*/, true); + JavaCore.create(project).setRawClasspath(entries, null); + // workaround PDE sloppy model management during the first multimodule project import in eclipse session + // 1. m2e creates all modules as simple workspace projects without JDT or PDE natures + // 2. first call to org.eclipse.pde.internal.core.PluginModelManager.initializeTable() reads all workspace + // projects regardless of their natures (see https://bugs.eclipse.org/bugs/show_bug.cgi?id=319268) + // 3. going through all projects one by one + // 3.1. m2e enables JDE and PDE natures and adds PDE classpath container + // 3.2. org.eclipse.pde.internal.core.PDEClasspathContainer.addProjectEntry ignores all project's dependencies + // that do not have JAVA nature. at this point PDE classpath is missing some/all workspace dependencies. + // 4. PDE does not re-resolve classpath when dependencies get JAVA nature enabled + + // as a workaround, touch project bundle manifests to force PDE re-read the model, re-resolve dependencies + // and recalculate PDE classpath + + IFile manifest = PDEProjectHelper.getBundleManifest( project ); + if ( manifest.isAccessible() ) + { + manifest.touch( monitor ); + } + } +} diff --git a/org.eclipse.m2e.pde.feature/feature.xml b/org.eclipse.m2e.pde.feature/feature.xml index 8c1f4b726..610491969 100644 --- a/org.eclipse.m2e.pde.feature/feature.xml +++ b/org.eclipse.m2e.pde.feature/feature.xml @@ -2,7 +2,7 @@ + + diff --git a/org.eclipse.m2e.site/.settings/org.eclipse.core.resources.prefs b/org.eclipse.m2e.site/.settings/org.eclipse.core.resources.prefs new file mode 100644 index 000000000..99f26c020 --- /dev/null +++ b/org.eclipse.m2e.site/.settings/org.eclipse.core.resources.prefs @@ -0,0 +1,2 @@ +eclipse.preferences.version=1 +encoding/=UTF-8 diff --git a/pom.xml b/pom.xml index 91a9535e9..de3c24564 100644 --- a/pom.xml +++ b/pom.xml @@ -88,6 +88,7 @@ org.eclipse.m2e.editor.lemminx org.eclipse.m2e.pde org.eclipse.m2e.pde.ui + org.eclipse.m2e.pde.connector org.eclipse.m2e.tests.common From 671ff1decf552838acda1afef997ae544354e51a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Christoph=20L=C3=A4ubrich?= Date: Fri, 10 Dec 2021 17:40:12 +0100 Subject: [PATCH 2/2] #439 - Adopt PDE configurations for maven-bundle-plugins - add warnings about missing configuration options - support bnd-bundle plugin as well --- .../build.properties | 3 +- .../lifecycle-mapping-metadata.xml | 16 ++ .../PDEMavenBundlePluginConfigurator.java | 213 +++++++++++------- 3 files changed, 149 insertions(+), 83 deletions(-) diff --git a/org.eclipse.m2e.pde.connector/build.properties b/org.eclipse.m2e.pde.connector/build.properties index e9863e281..d4a907fb6 100644 --- a/org.eclipse.m2e.pde.connector/build.properties +++ b/org.eclipse.m2e.pde.connector/build.properties @@ -2,4 +2,5 @@ source.. = src/ output.. = bin/ bin.includes = META-INF/,\ .,\ - plugin.xml + plugin.xml,\ + lifecycle-mapping-metadata.xml diff --git a/org.eclipse.m2e.pde.connector/lifecycle-mapping-metadata.xml b/org.eclipse.m2e.pde.connector/lifecycle-mapping-metadata.xml index 85428db36..236241167 100644 --- a/org.eclipse.m2e.pde.connector/lifecycle-mapping-metadata.xml +++ b/org.eclipse.m2e.pde.connector/lifecycle-mapping-metadata.xml @@ -33,6 +33,22 @@ + + + biz.aQute.bnd + bnd-maven-plugin + [5.0.0,) + + jar + bnd-process + + + + + org.eclipse.m2e.pde.connector.configurator.bundle + + + \ No newline at end of file diff --git a/org.eclipse.m2e.pde.connector/src/org/eclipse/m2e/pde/connector/PDEMavenBundlePluginConfigurator.java b/org.eclipse.m2e.pde.connector/src/org/eclipse/m2e/pde/connector/PDEMavenBundlePluginConfigurator.java index 63b0ae3af..c42a4101a 100644 --- a/org.eclipse.m2e.pde.connector/src/org/eclipse/m2e/pde/connector/PDEMavenBundlePluginConfigurator.java +++ b/org.eclipse.m2e.pde.connector/src/org/eclipse/m2e/pde/connector/PDEMavenBundlePluginConfigurator.java @@ -1,5 +1,5 @@ /******************************************************************************* - * Copyright (c) 2008 Sonatype, Inc. + * Copyright (c) 2008, 2021 Sonatype, Inc. * All rights reserved. This program and the accompanying materials * are made available under the terms of the Eclipse Public License v1.0 * which accompanies this distribution, and is available at @@ -7,11 +7,12 @@ *******************************************************************************/ package org.eclipse.m2e.pde.connector; - import java.io.File; import java.util.ArrayList; +import java.util.Arrays; import java.util.List; +import org.apache.maven.model.Plugin; import org.apache.maven.plugin.MojoExecution; import org.apache.maven.project.MavenProject; import org.eclipse.core.resources.IMarker; @@ -25,9 +26,12 @@ import org.eclipse.m2e.core.internal.markers.MavenProblemInfo; import org.eclipse.m2e.core.internal.markers.SourceLocation; import org.eclipse.m2e.core.internal.markers.SourceLocationHelper; +import org.eclipse.m2e.core.lifecyclemapping.model.IPluginExecutionMetadata; import org.eclipse.m2e.core.project.IMavenProjectFacade; +import org.eclipse.m2e.core.project.configurator.AbstractBuildParticipant; import org.eclipse.m2e.core.project.configurator.AbstractProjectConfigurator; import org.eclipse.m2e.core.project.configurator.ILifecycleMappingConfiguration; +import org.eclipse.m2e.core.project.configurator.MojoExecutionBuildParticipant; import org.eclipse.m2e.core.project.configurator.MojoExecutionKey; import org.eclipse.m2e.core.project.configurator.ProjectConfigurationRequest; import org.eclipse.m2e.jdt.IClasspathDescriptor; @@ -42,84 +46,129 @@ * manifest * */ -@SuppressWarnings( "restriction" ) -public class PDEMavenBundlePluginConfigurator - extends AbstractProjectConfigurator - implements IJavaProjectConfigurator -{ - - public static final String FELIX_PARAM_MANIFESTLOCATION = "manifestLocation"; - - @Override - public void configure( ProjectConfigurationRequest request, IProgressMonitor monitor ) - throws CoreException - { - List executions = getMojoExecutions( request, monitor ); - - if ( executions.size() > 1 ) - { - List errors = new ArrayList(); - - for ( MojoExecution mojoExecution : executions ) - { - SourceLocation location = SourceLocationHelper.findLocation( mojoExecution.getPlugin(), "executions" ); - MavenProblemInfo problem = - new MavenProblemInfo( "Duplicate " + mojoExecution.getGoal() - + " executions found. Please remove any explicitly defined " + mojoExecution.getGoal() - + " executions in your pom.xml.", IMarker.SEVERITY_ERROR, location ); - errors.add( problem ); - } - - this.markerManager.addErrorMarkers( request.getPom(), IMavenConstants.MARKER_LIFECYCLEMAPPING_ID, errors ); - } - - // bundle manifest is generated in #configureRawClasspath, which is invoked earlier during project configuration - - IProject project = request.getProject(); - IMavenProjectFacade facade = request.getMavenProjectFacade(); - - IPath metainfPath = getMetainfPath( facade, executions, monitor ); - - PDEProjectHelper.addPDENature( project, metainfPath, monitor ); - } - - @Override - public void configureClasspath( IMavenProjectFacade facade, IClasspathDescriptor classpath, IProgressMonitor monitor ) - throws CoreException - { - } - - @Override - public void configureRawClasspath( ProjectConfigurationRequest request, IClasspathDescriptor classpath, - IProgressMonitor monitor ) - throws CoreException - { - } - - private IPath getMetainfPath( IMavenProjectFacade facade, List executions, IProgressMonitor monitor ) - throws CoreException - { - IMaven maven = MavenPlugin.getMaven(); - for ( MojoExecution execution : executions ) - { - MavenProject mavenProject = facade.getMavenProject( monitor ); - File location = - maven.getMojoParameterValue(mavenProject, execution, FELIX_PARAM_MANIFESTLOCATION, File.class, - monitor); - if ( location != null ) - { - return facade.getProjectRelativePath( location.getAbsolutePath() ); - } - } - - return null; - } - - @Override - public boolean hasConfigurationChanged( IMavenProjectFacade newFacade, - ILifecycleMappingConfiguration oldProjectConfiguration, - MojoExecutionKey key, IProgressMonitor monitor ) - { - return false; - } +@SuppressWarnings("restriction") +public class PDEMavenBundlePluginConfigurator extends AbstractProjectConfigurator implements IJavaProjectConfigurator { + + private static final String FELIX_PARAM_MANIFESTLOCATION = "manifestLocation"; + private static final String FELIX_PARAM_SUPPORTINCREMENTALBUILD = "supportIncrementalBuild"; + private static final String FELIX_MANIFEST_GOAL = "manifest"; + private static final String BND_PARAM_MANIFESTLOCATION = "manifestPath"; + private static final String[] BND_MANIFEST_GOALS = { "bnd-process", "bnd-process-tests", "jar", "test-jar" }; + + @Override + public void configure(ProjectConfigurationRequest request, IProgressMonitor monitor) throws CoreException { + List executions = getMojoExecutions(request, monitor); + List problems = new ArrayList(); + boolean hasManifestExecution = false; + for (MojoExecution execution : executions) { + Plugin plugin = execution.getPlugin(); + if (isFelix(plugin)) { + MavenProject mavenProject = request.getMavenProject(); + IMaven maven = MavenPlugin.getMaven(); + if (isFelixBundleGoal(execution)) { + Boolean supportIncremental = maven.getMojoParameterValue(mavenProject, execution, + FELIX_PARAM_SUPPORTINCREMENTALBUILD, Boolean.class, monitor); + if (supportIncremental == null || !supportIncremental.booleanValue()) { + SourceLocation location = SourceLocationHelper.findLocation(execution.getPlugin(), + "configuration"); + MavenProblemInfo problem = new MavenProblemInfo( + "Incremental updates are currently disabled, set supportIncrementalBuild=true to support automatic manifest updates for this project.", + IMarker.SEVERITY_WARNING, location); + problems.add(problem); + } + hasManifestExecution = true; + } + } else if (isBND(plugin)) { + if (isBNDBundleGoal(execution)) { + hasManifestExecution = true; + } + } + } + if (!hasManifestExecution) { + for (MojoExecution execution : executions) { + SourceLocation location = SourceLocationHelper.findLocation(execution.getPlugin(), "executions"); + MavenProblemInfo problem = new MavenProblemInfo( + "There is currently no execution that generates a manifest, consider adding an execution for one of the following goal: " + + (isFelix(execution.getPlugin()) ? FELIX_MANIFEST_GOAL + : Arrays.toString(BND_MANIFEST_GOALS)) + + ".", + IMarker.SEVERITY_WARNING, location); + problems.add(problem); + break; + } + } + if (problems.size() > 0) { + this.markerManager.addErrorMarkers(request.getPom(), IMavenConstants.MARKER_LIFECYCLEMAPPING_ID, problems); + } + IProject project = request.getProject(); + IMavenProjectFacade facade = request.getMavenProjectFacade(); + + IPath metainfPath = getMetainfPath(facade, executions, monitor); + + PDEProjectHelper.addPDENature(project, metainfPath, monitor); + } + + private boolean isFelixBundleGoal(MojoExecution execution) { + return FELIX_MANIFEST_GOAL.equals(execution.getGoal()); + } + + private boolean isBNDBundleGoal(MojoExecution execution) { + String executionGoal = execution.getGoal(); + for (String goal : BND_MANIFEST_GOALS) { + if (goal.equals(executionGoal)) { + return true; + } + } + return false; + } + + @Override + public void configureClasspath(IMavenProjectFacade facade, IClasspathDescriptor classpath, IProgressMonitor monitor) + throws CoreException { + } + + @Override + public void configureRawClasspath(ProjectConfigurationRequest request, IClasspathDescriptor classpath, + IProgressMonitor monitor) throws CoreException { + } + + private IPath getMetainfPath(IMavenProjectFacade facade, List executions, IProgressMonitor monitor) + throws CoreException { + IMaven maven = MavenPlugin.getMaven(); + for (MojoExecution execution : executions) { + Plugin plugin = execution.getPlugin(); + MavenProject mavenProject = facade.getMavenProject(monitor); + File location = maven.getMojoParameterValue(mavenProject, execution, + isBND(plugin) ? BND_PARAM_MANIFESTLOCATION : FELIX_PARAM_MANIFESTLOCATION, File.class, monitor); + if (location != null) { + return facade.getProjectRelativePath(location.getAbsolutePath()); + } + } + return null; + } + + private boolean isBND(Plugin plugin) { + return plugin != null && "bnd-maven-plugin".equals(plugin.getArtifactId()); + } + + private boolean isFelix(Plugin plugin) { + return plugin != null && "org.apache.felix".equals(plugin.getGroupId()) + && "maven-bundle-plugin".equals(plugin.getArtifactId()); + } + + @Override + public boolean hasConfigurationChanged(IMavenProjectFacade newFacade, + ILifecycleMappingConfiguration oldProjectConfiguration, MojoExecutionKey key, IProgressMonitor monitor) { + return false; + } + + @Override + public AbstractBuildParticipant getBuildParticipant(IMavenProjectFacade projectFacade, MojoExecution execution, + IPluginExecutionMetadata executionMetadata) { + if ((isFelix(execution.getPlugin()) && isFelixBundleGoal(execution)) + || (isBND(execution.getPlugin()) && isBNDBundleGoal(execution))) { + return new MojoExecutionBuildParticipant(execution, true, true); + } + return super.getBuildParticipant(projectFacade, execution, executionMetadata); + } }