diff --git a/m2e-maven-runtime/pom.xml b/m2e-maven-runtime/pom.xml index 1df18714d3..fae76d80a0 100644 --- a/m2e-maven-runtime/pom.xml +++ b/m2e-maven-runtime/pom.xml @@ -68,7 +68,7 @@ <_nodefaultversion>true <_snapshot>qualifier - ${project.artifactId};singleton:=false + ${project.artifactId};singleton:=true JavaSE-11 ${project.name} Eclipse.org - m2e diff --git a/org.eclipse.m2e.core.ui.tests/src/org/eclipse/m2e/core/ui/tests/ConsoleTest.java b/org.eclipse.m2e.core.ui.tests/src/org/eclipse/m2e/core/ui/tests/ConsoleTest.java index b0c261389a..54a8cc76d8 100644 --- a/org.eclipse.m2e.core.ui.tests/src/org/eclipse/m2e/core/ui/tests/ConsoleTest.java +++ b/org.eclipse.m2e.core.ui.tests/src/org/eclipse/m2e/core/ui/tests/ConsoleTest.java @@ -177,7 +177,7 @@ private static void assertLinkActivationOpensPartWithTitle(ConsoleHyperlinkPosit } @Test - public void testConsole_automaticDebuggerAttachment() throws Exception { + public void testConsole_automaticDebuggerAttachment_explicit() throws Exception { importMavenProjectIntoWorkspace(SIMPLE_PROJECT); @@ -187,6 +187,16 @@ public void testConsole_automaticDebuggerAttachment() throws Exception { assertDebugeePrintOutAndDebuggerLaunch(document, SIMPLE_PROJECT); } + @Test + public void testConsole_automaticDebuggerAttachment_fromDebugLaunch() throws Exception { + + importMavenProjectIntoWorkspace(SIMPLE_PROJECT); + + IDocument document = runMavenBuild(projectLocVariable(SIMPLE_PROJECT), ILaunchManager.DEBUG_MODE); + + assertDebugeePrintOutAndDebuggerLaunch(document, SIMPLE_PROJECT); + } + private static void assertDebugeePrintOutAndDebuggerLaunch(IDocument document, String debugLaunchName) throws CoreException { // Check for Listening debugee print-out diff --git a/org.eclipse.m2e.debug/.classpath b/org.eclipse.m2e.debug/.classpath new file mode 100644 index 0000000000..e801ebfb46 --- /dev/null +++ b/org.eclipse.m2e.debug/.classpath @@ -0,0 +1,7 @@ + + + + + + + diff --git a/org.eclipse.m2e.debug/.project b/org.eclipse.m2e.debug/.project new file mode 100644 index 0000000000..386170634e --- /dev/null +++ b/org.eclipse.m2e.debug/.project @@ -0,0 +1,34 @@ + + + org.eclipse.m2e.debug + + + + + + org.eclipse.jdt.core.javabuilder + + + + + org.eclipse.pde.ManifestBuilder + + + + + org.eclipse.pde.SchemaBuilder + + + + + org.eclipse.m2e.core.maven2Builder + + + + + + org.eclipse.m2e.core.maven2Nature + org.eclipse.pde.PluginNature + org.eclipse.jdt.core.javanature + + diff --git a/org.eclipse.m2e.debug/.settings/org.eclipse.core.resources.prefs b/org.eclipse.m2e.debug/.settings/org.eclipse.core.resources.prefs new file mode 100644 index 0000000000..99f26c0203 --- /dev/null +++ b/org.eclipse.m2e.debug/.settings/org.eclipse.core.resources.prefs @@ -0,0 +1,2 @@ +eclipse.preferences.version=1 +encoding/=UTF-8 diff --git a/org.eclipse.m2e.debug/.settings/org.eclipse.jdt.core.prefs b/org.eclipse.m2e.debug/.settings/org.eclipse.jdt.core.prefs new file mode 100644 index 0000000000..052ee6edbe --- /dev/null +++ b/org.eclipse.m2e.debug/.settings/org.eclipse.jdt.core.prefs @@ -0,0 +1,10 @@ +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.forbiddenReference=warning +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.debug/.settings/org.eclipse.m2e.core.prefs b/org.eclipse.m2e.debug/.settings/org.eclipse.m2e.core.prefs new file mode 100644 index 0000000000..f897a7f1cb --- /dev/null +++ b/org.eclipse.m2e.debug/.settings/org.eclipse.m2e.core.prefs @@ -0,0 +1,4 @@ +activeProfiles= +eclipse.preferences.version=1 +resolveWorkspaceProjects=true +version=1 diff --git a/org.eclipse.m2e.debug/META-INF/MANIFEST.MF b/org.eclipse.m2e.debug/META-INF/MANIFEST.MF new file mode 100644 index 0000000000..a3e1cdc960 --- /dev/null +++ b/org.eclipse.m2e.debug/META-INF/MANIFEST.MF @@ -0,0 +1,14 @@ +Manifest-Version: 1.0 +Bundle-ManifestVersion: 2 +Bundle-Name: M2E Debug Support +Bundle-SymbolicName: org.eclipse.m2e.debug;singleton:=true +Bundle-Version: 1.0.0.qualifier +Bundle-Vendor: Eclipse.org - m2e +Fragment-Host: org.eclipse.m2e.maven.runtime +Automatic-Module-Name: org.eclipse.m2e.debug +Bundle-RequiredExecutionEnvironment: JavaSE-11 +Import-Package: javax.inject;version="1.0.0" +Require-Bundle: org.eclipse.m2e.launching, + org.eclipse.debug.core, + org.eclipse.equinox.common, + org.eclipse.m2e.core diff --git a/org.eclipse.m2e.debug/META-INF/sisu/javax.inject.Named b/org.eclipse.m2e.debug/META-INF/sisu/javax.inject.Named new file mode 100644 index 0000000000..bde4a9a1c5 --- /dev/null +++ b/org.eclipse.m2e.debug/META-INF/sisu/javax.inject.Named @@ -0,0 +1,2 @@ +org.eclipse.m2e.debug.runtime.DebugEventSpy +org.eclipse.m2e.debug.runtime.DebugExecutionListener diff --git a/org.eclipse.m2e.debug/build.properties b/org.eclipse.m2e.debug/build.properties new file mode 100644 index 0000000000..e3023e14e9 --- /dev/null +++ b/org.eclipse.m2e.debug/build.properties @@ -0,0 +1,5 @@ +source.. = src/ +output.. = bin/ +bin.includes = META-INF/,\ + .,\ + fragment.xml diff --git a/org.eclipse.m2e.debug/fragment.xml b/org.eclipse.m2e.debug/fragment.xml new file mode 100644 index 0000000000..8a80d5bfe0 --- /dev/null +++ b/org.eclipse.m2e.debug/fragment.xml @@ -0,0 +1,14 @@ + + + + + + + + + diff --git a/org.eclipse.m2e.debug/pom.xml b/org.eclipse.m2e.debug/pom.xml new file mode 100644 index 0000000000..394f522f07 --- /dev/null +++ b/org.eclipse.m2e.debug/pom.xml @@ -0,0 +1,48 @@ + + + + 4.0.0 + + + org.eclipse.m2e + m2e-core + 1.16.0-SNAPSHOT + + + org.eclipse.m2e.debug + 1.18.0-SNAPSHOT + eclipse-plugin + + M2E Debug Support for Maven Build Processes + + + + + org.eclipse.sisu + sisu-maven-plugin + 0.3.4 + + + + index-project + prepare-package + + index + + + ${project.basedir} + + + + + + + \ No newline at end of file diff --git a/org.eclipse.m2e.debug/src/org/eclipse/m2e/debug/runtime/DebugEventSpy.java b/org.eclipse.m2e.debug/src/org/eclipse/m2e/debug/runtime/DebugEventSpy.java new file mode 100644 index 0000000000..23af66ae80 --- /dev/null +++ b/org.eclipse.m2e.debug/src/org/eclipse/m2e/debug/runtime/DebugEventSpy.java @@ -0,0 +1,126 @@ +package org.eclipse.m2e.debug.runtime; + +import static java.util.Map.entry; + +import java.io.IOException; +import java.net.ServerSocket; +import java.util.Map; +import java.util.Map.Entry; +import java.util.Properties; +import java.util.function.UnaryOperator; + +import javax.inject.Named; +import javax.inject.Singleton; + +import org.apache.maven.eventspy.AbstractEventSpy; +import org.apache.maven.execution.ExecutionEvent; +import org.apache.maven.execution.ExecutionEvent.Type; +import org.apache.maven.plugin.MojoExecution; +import org.codehaus.plexus.util.xml.Xpp3Dom; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +@Named +@Singleton +public class DebugEventSpy extends AbstractEventSpy { + private static final Logger LOGGER = LoggerFactory.getLogger(DebugEventSpy.class); + + private static final String DEBUG_PORT_VARIABLE = "${debugPort}"; + public static final String DEBUG_DATA_PROPERTY = "m2e.debug.support"; //$NON-NLS-1$ + + public static String getEnableDebugProperty() { + return "-D" + DebugEventSpy.DEBUG_DATA_PROPERTY + "=" + "true"; + } + + // TODO: name this entire fragment m2e.debug.extension to reflect that it is an + // extension to the maven-runtime? + + // TODO: specify the debug setup of the supported plug-ins at a more suitable + // location. Ideally within the Maven-plugins directly (like for lifecycle + // mappings) LifecycleMappingFactory.readMavenPluginEmbeddedMetadata(), or with + // life-cylce mappins/connectors + // TODO: specify properties per Goal! + private static final Map>> ID_2_CONFIGURATION_INJECTOR = Map.of( + "org.apache.maven.plugins:maven-surefire-plugin", // + entry("debugForkedProcess", + v -> "-Xdebug -Xrunjdwp:transport=dt_socket,server=y,suspend=y,address=" + DEBUG_PORT_VARIABLE + + " -Xnoagent -Djava.compiler=NONE"), // TODO: use Java-9 approach: agentlib:jdwp ? + "org.eclipse.tycho:tycho-surefire-plugin", entry("debugPort", v -> DEBUG_PORT_VARIABLE), + "org.eclipse.tycho.extras:tycho-eclipserun-plugin", // + entry("argLine", // TODO: use not deprecated approach with nested elements in jvmArgs + v -> "-Xdebug -Xrunjdwp:transport=dt_socket,server=y,suspend=y,address=" + DEBUG_PORT_VARIABLE + + " -Xnoagent")); // TODO: use Java-9 approach: agentlib:jdwp ? + + private boolean isDebugLaunch = false; + + @Override + public void init(Context context) throws Exception { + Map data = context.getData(); + Object userProperties = data.get("userProperties"); + if (userProperties instanceof Properties) { + Properties properties = (Properties) userProperties; + isDebugLaunch = Boolean.parseBoolean(properties.getProperty(DEBUG_DATA_PROPERTY, "false")); + } + if (isDebugLaunch) { + LOGGER.debug("M2E - Automatic debugging of forked VMs enabled"); + } + } + + @Override + public void close() throws Exception { + isDebugLaunch = false; + } + + @Override + public void onEvent(Object event) throws Exception { + if (isDebugLaunch && event instanceof ExecutionEvent) { + ExecutionEvent executionEvent = (ExecutionEvent) event; + if (executionEvent.getType() == Type.MojoStarted) { + MojoExecution mojoExecution = executionEvent.getMojoExecution(); + String id = mojoExecution.getGroupId() + ":" + mojoExecution.getArtifactId(); + + Entry> injector = ID_2_CONFIGURATION_INJECTOR.get(id); + if (injector != null) { + Xpp3Dom configuration = mojoExecution.getConfiguration(); + int debugPort = getFreeDebugPort(); + String debugElementName = injector.getKey(); + Xpp3Dom debugElement = getOrCreateChild(configuration, debugElementName); + injectDebugSetupIntoConfiguration(debugElement, injector.getValue(), debugPort); + + LOGGER.debug("M2E - Injected debug-port {} into configuration element <{}>", debugPort, + debugElementName); + } + } + } + } + + private void injectDebugSetupIntoConfiguration(Xpp3Dom debugElement, UnaryOperator valueComputer, + int debugPort) { + String enhancedValue = valueComputer.apply(debugElement.getValue()); + enhancedValue = enhancedValue.replace(DEBUG_PORT_VARIABLE, Integer.toString(debugPort)); + debugElement.setValue(enhancedValue); + // TODO: handle other cases + // - nested elements + // - set(but do not overwrite), + // - append (check if already contained?) + } + + private static Xpp3Dom getOrCreateChild(Xpp3Dom configuration, String name) { + Xpp3Dom child = configuration.getChild(name); + if (child != null) { + child.setInputLocation(null); // disconnect from actual source + } else { + child = new Xpp3Dom(name); + configuration.addChild(child); + } + return child; + } + + private static int getFreeDebugPort() { + try (ServerSocket socket = new ServerSocket(0)) { + return socket.getLocalPort(); + } catch (IOException e) { + return 0; + } + } +} diff --git a/org.eclipse.m2e.debug/src/org/eclipse/m2e/debug/runtime/DebugExecutionListener.java b/org.eclipse.m2e.debug/src/org/eclipse/m2e/debug/runtime/DebugExecutionListener.java new file mode 100644 index 0000000000..8dc6ea121d --- /dev/null +++ b/org.eclipse.m2e.debug/src/org/eclipse/m2e/debug/runtime/DebugExecutionListener.java @@ -0,0 +1,31 @@ +package org.eclipse.m2e.debug.runtime; + +import javax.inject.Named; +import javax.inject.Singleton; + +import org.apache.maven.execution.MojoExecutionEvent; +import org.apache.maven.execution.MojoExecutionListener; +import org.apache.maven.plugin.Mojo; +import org.apache.maven.plugin.MojoExecutionException; + +@Named +@Singleton +public class DebugExecutionListener implements MojoExecutionListener { + // TODO: this class is likely not necessary anymore + + @Override + public void beforeMojoExecution(MojoExecutionEvent event) throws MojoExecutionException { + Mojo mojo = event.getMojo(); + } + + @Override + public void afterMojoExecutionSuccess(MojoExecutionEvent event) throws MojoExecutionException { + // TODO Auto-generated method stub + } + + @Override + public void afterExecutionFailure(MojoExecutionEvent event) { + // TODO Auto-generated method stub + } + +} diff --git a/org.eclipse.m2e.debug/src/org/eclipse/m2e/debug/setup/DebugLaunchParticipant.java b/org.eclipse.m2e.debug/src/org/eclipse/m2e/debug/setup/DebugLaunchParticipant.java new file mode 100644 index 0000000000..8e327acc60 --- /dev/null +++ b/org.eclipse.m2e.debug/src/org/eclipse/m2e/debug/setup/DebugLaunchParticipant.java @@ -0,0 +1,42 @@ +package org.eclipse.m2e.debug.setup; + +import java.util.List; + +import org.eclipse.core.runtime.CoreException; +import org.eclipse.core.runtime.IProgressMonitor; +import org.eclipse.debug.core.ILaunch; +import org.eclipse.debug.core.ILaunchConfiguration; +import org.eclipse.debug.core.sourcelookup.ISourceLookupParticipant; +import org.eclipse.m2e.core.internal.launch.MavenEmbeddedRuntime; +import org.eclipse.m2e.debug.runtime.DebugEventSpy; +import org.eclipse.m2e.internal.launch.IMavenLaunchParticipant; +import org.eclipse.m2e.internal.launch.MavenLaunchUtils; + +@SuppressWarnings("restriction") +public class DebugLaunchParticipant implements IMavenLaunchParticipant { + + @Override + public String getProgramArguments(ILaunchConfiguration configuration, ILaunch launch, IProgressMonitor monitor) { + try { + if (MavenLaunchUtils.getMavenRuntime(configuration) instanceof MavenEmbeddedRuntime) { + return DebugEventSpy.getEnableDebugProperty(); + } + } catch (CoreException e) { // assume false + } + return null; + } + + @Override + public String getVMArguments(ILaunchConfiguration configuration, ILaunch launch, IProgressMonitor monitor) { + // TODO Auto-generated method stub + return null; + } + + @Override + public List getSourceLookupParticipants(ILaunchConfiguration configuration, + ILaunch launch, IProgressMonitor monitor) { + // TODO Auto-generated method stub + return null; + } + +} diff --git a/org.eclipse.m2e.feature/feature.xml b/org.eclipse.m2e.feature/feature.xml index 4827f36b17..06500f24e3 100644 --- a/org.eclipse.m2e.feature/feature.xml +++ b/org.eclipse.m2e.feature/feature.xml @@ -179,4 +179,12 @@ version="0.0.0" unpack="false"/> + + diff --git a/org.eclipse.m2e.launching/src/org/eclipse/m2e/actions/ExecutePomAction.java b/org.eclipse.m2e.launching/src/org/eclipse/m2e/actions/ExecutePomAction.java index 74d5c8d166..1a751af1dc 100644 --- a/org.eclipse.m2e.launching/src/org/eclipse/m2e/actions/ExecutePomAction.java +++ b/org.eclipse.m2e.launching/src/org/eclipse/m2e/actions/ExecutePomAction.java @@ -215,10 +215,6 @@ private ILaunchConfiguration createLaunchConfiguration(IContainer basedir, Strin if(path != null) { workingCopy.setAttribute(IJavaLaunchConfigurationConstants.ATTR_JRE_CONTAINER_PATH, path.toPortableString()); } - - // TODO when launching Maven with debugger consider to add the following property - // -Dmaven.surefire.debug="-Xdebug -Xrunjdwp:transport=dt_socket,server=y,suspend=y,address=8000 -Xnoagent -Djava.compiler=NONE" - return workingCopy; } catch(CoreException ex) { log.error(ex.getMessage(), ex); diff --git a/pom.xml b/pom.xml index 2ecab5ae0f..853c1d9327 100644 --- a/pom.xml +++ b/pom.xml @@ -82,6 +82,7 @@ org.eclipse.m2e.pde org.eclipse.m2e.pde.ui org.eclipse.m2e.pde.connector + org.eclipse.m2e.debug org.eclipse.m2e.tests.common