You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@netbeans.apache.org by en...@apache.org on 2021/09/30 10:56:41 UTC
[netbeans] branch master updated: Support of setting the current
working directory and environment variables for Java8+ VSCode projects.
This is an automated email from the ASF dual-hosted git repository.
entl pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/netbeans.git
The following commit(s) were added to refs/heads/master by this push:
new 03bd150 Support of setting the current working directory and environment variables for Java8+ VSCode projects.
03bd150 is described below
commit 03bd150b8b96fde89f653c9f581a24ba1af411e2
Author: Martin Entlicher <ma...@oracle.com>
AuthorDate: Tue Sep 21 03:03:14 2021 +0200
Support of setting the current working directory and environment variables for Java8+ VSCode projects.
---
cpplite/cpplite.debugger/nbproject/project.xml | 11 +-
.../modules/cpplite/debugger/CPPLiteDebugger.java | 36 +++-
.../cpplite/debugger/CPPLiteDebuggerConfig.java | 35 +++-
.../modules/cpplite/debugger/api/Debugger.java | 4 +-
.../debugger/ni/NIDebuggerProviderImpl.java | 20 ++-
.../cpplite/debugger/AbstractDebugTest.java | 53 +++++-
.../cpplite/debugger/ProcessParametersTest.java | 192 +++++++++++++++++++++
extide/gradle/apichanges.xml | 17 ++
extide/gradle/arch.xml | 14 ++
.../gradle/tooling/NetBeansRunSinglePlugin.groovy | 104 ++++++++++-
.../base/ExplicitProcessParameters.java | 106 +++++++++++-
.../nativeimage/api/debug/EvaluateException.java | 7 +-
.../modules/nativeimage/api/debug/NIDebugger.java | 56 ++++--
.../modules/nativeimage/api/debug/NIFrame.java | 16 +-
.../api/debug/NILineBreakpointDescriptor.java | 26 +--
.../modules/nativeimage/api/debug/NIVariable.java | 20 +--
.../api/debug/StartDebugParameters.java | 188 ++++++++++++++++++++
.../nativeimage/spi/debug/NIDebuggerProvider.java | 56 ++++--
java/gradle.java/arch.xml | 12 ++
java/gradle.java/nbproject/project.xml | 4 +-
.../modules/gradle/java/action-mapping.xml | 12 +-
.../modules/gradle/java/api/ProjectActions.java | 23 ++-
.../gradle/java/execute/JavaExecTokenProvider.java | 58 ++++++-
.../java/execute/JavaExecTokenProviderTest.java | 34 ++++
java/java.lsp.server/nbproject/project.xml | 4 +-
.../server/debugging/launch/NbLaunchDelegate.java | 89 ++++++++--
java/java.lsp.server/vscode/package.json | 15 ++
.../nbproject/project.xml | 2 +-
.../debugger/actions/NIAttachCustomizer.java | 1 +
.../nativeimage/debugger/api/NIDebugRunner.java | 25 +++
java/maven/nbproject/project.xml | 2 +-
.../modules/maven/debug/DebuggerChecker.java | 18 +-
.../maven/execute/DefaultReplaceTokenProvider.java | 1 +
.../maven/execute/MavenCommandLineExecutor.java | 14 +-
.../maven/runjar/LaunchArgPrereqsChecker.java | 15 ++
.../modules/maven/runjar/MavenExecuteUtils.java | 7 +
.../modules/maven/runjar/RunJarStartupArgs.java | 18 +-
.../maven/execute/MavenExecutionTestBase.java | 6 +-
.../maven/runjar/ExecutionEnvHelperTest.java | 52 ++++++
39 files changed, 1242 insertions(+), 131 deletions(-)
diff --git a/cpplite/cpplite.debugger/nbproject/project.xml b/cpplite/cpplite.debugger/nbproject/project.xml
index 7574c06..55bb7e8 100644
--- a/cpplite/cpplite.debugger/nbproject/project.xml
+++ b/cpplite/cpplite.debugger/nbproject/project.xml
@@ -61,12 +61,21 @@
</run-dependency>
</dependency>
<dependency>
+ <code-name-base>org.netbeans.modules.extexecution.base</code-name-base>
+ <build-prerequisite/>
+ <compile-dependency/>
+ <run-dependency>
+ <release-version>2</release-version>
+ <specification-version>1.20</specification-version>
+ </run-dependency>
+ </dependency>
+ <dependency>
<code-name-base>org.netbeans.modules.nativeimage.api</code-name-base>
<build-prerequisite/>
<compile-dependency/>
<run-dependency>
<release-version>0</release-version>
- <specification-version>0.4</specification-version>
+ <specification-version>0.5</specification-version>
</run-dependency>
</dependency>
<dependency>
diff --git a/cpplite/cpplite.debugger/src/org/netbeans/modules/cpplite/debugger/CPPLiteDebugger.java b/cpplite/cpplite.debugger/src/org/netbeans/modules/cpplite/debugger/CPPLiteDebugger.java
index 946f047..7c30f7c 100644
--- a/cpplite/cpplite.debugger/src/org/netbeans/modules/cpplite/debugger/CPPLiteDebugger.java
+++ b/cpplite/cpplite.debugger/src/org/netbeans/modules/cpplite/debugger/CPPLiteDebugger.java
@@ -50,6 +50,7 @@ import org.netbeans.api.debugger.DebuggerEngine;
import org.netbeans.api.debugger.DebuggerInfo;
import org.netbeans.api.debugger.DebuggerManager;
import org.netbeans.api.debugger.DebuggerManagerAdapter;
+import org.netbeans.api.extexecution.base.ExplicitProcessParameters;
import org.netbeans.modules.cnd.debugger.gdb2.mi.MICommand;
import org.netbeans.modules.cnd.debugger.gdb2.mi.MICommandInjector;
import org.netbeans.modules.cnd.debugger.gdb2.mi.MIConst;
@@ -75,6 +76,7 @@ import org.openide.filesystems.FileUtil;
import org.openide.text.Annotatable;
import org.openide.text.Line;
import org.openide.util.Exceptions;
+import org.openide.util.Lookup;
import org.openide.util.Pair;
import org.openide.util.RequestProcessor;
@@ -811,15 +813,20 @@ public final class CPPLiteDebugger {
CPPLiteDebugger debugger = es[0].lookupFirst(null, CPPLiteDebugger.class);
List<String> executable = new ArrayList<>();
executable.add(configuration.getDebugger());
- executable.add("--interpreter=mi");
- executable.add("--tty=" + pty.getSlaveName());
+ executable.add("--interpreter=mi"); // NOI18N
+ executable.add("--tty=" + pty.getSlaveName()); // NOI18N
if (configuration.isAttach()) {
- executable.add("-p");
+ executable.add("-p"); // NOI18N
executable.add(Long.toString(configuration.getAttachProcessId()));
}
+ if (configuration.getExecutable().size() > 1) {
+ executable.add("--args"); // NOI18N
+ }
executable.addAll(configuration.getExecutable());
- Process debuggee = new ProcessBuilder(executable).directory(configuration.getDirectory()).start();
- new RequestProcessor(configuration.getDisplayName() + " (pty deallocator)").post(() -> {
+ ProcessBuilder processBuilder = new ProcessBuilder(executable);
+ setParameters(processBuilder, configuration);
+ Process debuggee = processBuilder.start();
+ new RequestProcessor(configuration.getDisplayName() + " (pty deallocator)").post(() -> { // NOI18N
try {
while (debuggee.isAlive()) {
try {
@@ -878,6 +885,25 @@ public final class CPPLiteDebugger {
};
}
+ private static void setParameters(ProcessBuilder processBuilder, CPPLiteDebuggerConfig configuration) {
+ ExplicitProcessParameters processParameters = configuration.getProcessParameters();
+ if (processParameters.getWorkingDirectory() != null) {
+ processBuilder.directory(processParameters.getWorkingDirectory());
+ }
+ if (!processParameters.getEnvironmentVariables().isEmpty()) {
+ Map<String, String> environment = processBuilder.environment();
+ for (Map.Entry<String, String> entry : processParameters.getEnvironmentVariables().entrySet()) {
+ String env = entry.getKey();
+ String val = entry.getValue();
+ if (val != null) {
+ environment.put(env, val);
+ } else {
+ environment.remove(env);
+ }
+ }
+ }
+ }
+
private class BreakpointsHandler extends DebuggerManagerAdapter implements PropertyChangeListener {
private final Map<String, CPPLiteBreakpoint> breakpointsById = new ConcurrentHashMap<>();
diff --git a/cpplite/cpplite.debugger/src/org/netbeans/modules/cpplite/debugger/CPPLiteDebuggerConfig.java b/cpplite/cpplite.debugger/src/org/netbeans/modules/cpplite/debugger/CPPLiteDebuggerConfig.java
index 3a8ac18..7ea6634 100644
--- a/cpplite/cpplite.debugger/src/org/netbeans/modules/cpplite/debugger/CPPLiteDebuggerConfig.java
+++ b/cpplite/cpplite.debugger/src/org/netbeans/modules/cpplite/debugger/CPPLiteDebuggerConfig.java
@@ -19,10 +19,12 @@
package org.netbeans.modules.cpplite.debugger;
-import java.io.File;
+import java.util.ArrayList;
import java.util.List;
import org.netbeans.api.annotations.common.NullAllowed;
+import org.netbeans.api.extexecution.base.ExplicitProcessParameters;
+import org.openide.util.Exceptions;
/**
*
@@ -31,16 +33,33 @@ import org.netbeans.api.annotations.common.NullAllowed;
public final class CPPLiteDebuggerConfig {
private final List<String> executable;
- private final File directory;
+ private final ExplicitProcessParameters processParameters;
@NullAllowed
private final Long processId;
private final String debugger;
- public CPPLiteDebuggerConfig(List<String> executable, File directory, @NullAllowed Long processId, String debugger) {
- this.executable = executable;
- this.directory = directory;
+ public CPPLiteDebuggerConfig(List<String> executable, ExplicitProcessParameters processParameters, @NullAllowed Long processId, String debugger) {
+ this.processParameters = processParameters;
this.processId = processId;
this.debugger = debugger;
+ if (processParameters.isArgReplacement()) {
+ this.executable = new ArrayList<>();
+ this.executable.add(executable.get(0));
+ if (processParameters.getArguments() != null) {
+ this.executable.addAll(processParameters.getArguments());
+ }
+ } else {
+ if (processParameters.getArguments() != null) {
+ this.executable = new ArrayList<>();
+ this.executable.addAll(executable);
+ this.executable.addAll(processParameters.getArguments());
+ } else {
+ this.executable = executable;
+ }
+ }
+ if (processParameters.getLauncherArguments() != null) {
+ Exceptions.printStackTrace(new IllegalStateException("Launcher arguments " + processParameters.getLauncherArguments() + " can not be accepted by CPPLite debugger"));
+ }
}
public String getDisplayName() {
@@ -55,10 +74,10 @@ public final class CPPLiteDebuggerConfig {
}
/**
- * Get the directory in which the executable command is to be launched.
+ * Get the parameters which the executable command is to be launched with.
*/
- public File getDirectory() {
- return directory;
+ public ExplicitProcessParameters getProcessParameters() {
+ return processParameters;
}
/**
diff --git a/cpplite/cpplite.debugger/src/org/netbeans/modules/cpplite/debugger/api/Debugger.java b/cpplite/cpplite.debugger/src/org/netbeans/modules/cpplite/debugger/api/Debugger.java
index fc8e741..ef98531 100644
--- a/cpplite/cpplite.debugger/src/org/netbeans/modules/cpplite/debugger/api/Debugger.java
+++ b/cpplite/cpplite.debugger/src/org/netbeans/modules/cpplite/debugger/api/Debugger.java
@@ -21,6 +21,7 @@ package org.netbeans.modules.cpplite.debugger.api;
import java.io.File;
import java.io.IOException;
import java.util.List;
+import org.netbeans.api.extexecution.base.ExplicitProcessParameters;
import org.netbeans.modules.cpplite.debugger.CPPLiteDebugger;
import org.netbeans.modules.cpplite.debugger.CPPLiteDebuggerConfig;
@@ -38,8 +39,9 @@ public class Debugger {
public static Process startInDebugger(List<String> command, File directory) throws IOException {
CPPLiteDebugger[] debugger = new CPPLiteDebugger[] { null };
+ ExplicitProcessParameters processParameters = ExplicitProcessParameters.builder().workingDirectory(directory).build();
Process engineProcess = CPPLiteDebugger.startDebugging(
- new CPPLiteDebuggerConfig(command, directory, null, "gdb"),
+ new CPPLiteDebuggerConfig(command, processParameters, null, "gdb"),
engine -> {
debugger[0] = engine.lookupFirst(null, CPPLiteDebugger.class);
});
diff --git a/cpplite/cpplite.debugger/src/org/netbeans/modules/cpplite/debugger/ni/NIDebuggerProviderImpl.java b/cpplite/cpplite.debugger/src/org/netbeans/modules/cpplite/debugger/ni/NIDebuggerProviderImpl.java
index 7d71aaf..552107e 100644
--- a/cpplite/cpplite.debugger/src/org/netbeans/modules/cpplite/debugger/ni/NIDebuggerProviderImpl.java
+++ b/cpplite/cpplite.debugger/src/org/netbeans/modules/cpplite/debugger/ni/NIDebuggerProviderImpl.java
@@ -31,6 +31,7 @@ import org.netbeans.api.debugger.Breakpoint;
import org.netbeans.api.debugger.DebuggerEngine;
import org.netbeans.api.extexecution.ExecutionDescriptor;
import org.netbeans.api.extexecution.ExecutionService;
+import org.netbeans.api.extexecution.base.ExplicitProcessParameters;
import org.netbeans.modules.cpplite.debugger.CPPFrame;
import org.netbeans.modules.cpplite.debugger.CPPLiteDebugger;
import org.netbeans.modules.cpplite.debugger.CPPLiteDebuggerConfig;
@@ -42,12 +43,14 @@ import org.netbeans.modules.nativeimage.api.debug.EvaluateException;
import org.netbeans.modules.nativeimage.api.debug.NIFrame;
import org.netbeans.modules.nativeimage.api.debug.NILineBreakpointDescriptor;
import org.netbeans.modules.nativeimage.api.debug.NIVariable;
+import org.netbeans.modules.nativeimage.api.debug.StartDebugParameters;
import org.netbeans.modules.nativeimage.spi.debug.NIDebuggerProvider;
import org.netbeans.modules.nativeimage.spi.debug.filters.FrameDisplayer;
import org.openide.LifecycleManager;
import org.openide.util.RequestProcessor;
import org.netbeans.modules.nativeimage.spi.debug.filters.VariableDisplayer;
+import org.openide.util.Lookup;
/**
*
@@ -85,10 +88,20 @@ public class NIDebuggerProviderImpl implements NIDebuggerProvider {
}
@Override
- public CompletableFuture<Void> start(List<String> command, File workingDirectory, String miDebugger, String displayName, ExecutionDescriptor executionDescriptor, Consumer<DebuggerEngine> startedEngine) {
+ public CompletableFuture<Void> start(StartDebugParameters debugParameters, Consumer<DebuggerEngine> startedEngine) {
if (debugger != null) {
throw new IllegalStateException("Debugger has started already.");
}
+ List<String> command = debugParameters.getCommand();
+ String miDebugger = debugParameters.getDebugger();
+ String displayName = debugParameters.getDisplayName();
+ ExecutionDescriptor executionDescriptor = debugParameters.getExecutionDescriptor();
+ Lookup contextLookup = debugParameters.getContextLookup();
+ ExplicitProcessParameters explicitParameters = contextLookup != null ? ExplicitProcessParameters.buildExplicitParameters(contextLookup) : null;
+ if (explicitParameters == null) {
+ explicitParameters = ExplicitProcessParameters.builder().workingDirectory(debugParameters.getWorkingDirectory()).build();
+ }
+ final ExplicitProcessParameters processParameters = explicitParameters;
if (executionDescriptor == null) {
executionDescriptor = new ExecutionDescriptor()
.showProgress(true)
@@ -104,7 +117,7 @@ public class NIDebuggerProviderImpl implements NIDebuggerProvider {
try {
LifecycleManager.getDefault().saveAll();
engineProcess = CPPLiteDebugger.startDebugging(
- new CPPLiteDebuggerConfig(command, workingDirectory, null, miDebugger),
+ new CPPLiteDebuggerConfig(command, processParameters, null, miDebugger),
engine -> {
debugger[0] = engine.lookupFirst(null, CPPLiteDebugger.class);
this.debugger = debugger[0];
@@ -147,10 +160,11 @@ public class NIDebuggerProviderImpl implements NIDebuggerProvider {
if (debugger != null) {
throw new IllegalStateException("Debugger has started already.");
}
+ ExplicitProcessParameters processParameters = ExplicitProcessParameters.builder().workingDirectory(new File(System.getProperty("user.dir", ""))).build(); // NOI18N
CompletableFuture<Void> completed = new CompletableFuture<>();
try {
CPPLiteDebugger.startDebugging(
- new CPPLiteDebuggerConfig(Collections.singletonList(executablePath), new File(System.getProperty("user.dir")), processId, miDebugger),
+ new CPPLiteDebuggerConfig(Collections.singletonList(executablePath), processParameters, processId, miDebugger),
engine -> {
CPPLiteDebugger debugger = engine.lookupFirst(null, CPPLiteDebugger.class);
this.debugger = debugger;
diff --git a/cpplite/cpplite.debugger/test/unit/src/org/netbeans/modules/cpplite/debugger/AbstractDebugTest.java b/cpplite/cpplite.debugger/test/unit/src/org/netbeans/modules/cpplite/debugger/AbstractDebugTest.java
index d3aca0c..26aff70 100644
--- a/cpplite/cpplite.debugger/test/unit/src/org/netbeans/modules/cpplite/debugger/AbstractDebugTest.java
+++ b/cpplite/cpplite.debugger/test/unit/src/org/netbeans/modules/cpplite/debugger/AbstractDebugTest.java
@@ -18,21 +18,34 @@
*/
package org.netbeans.modules.cpplite.debugger;
+import java.io.BufferedReader;
import java.io.File;
import java.io.IOException;
+import java.io.InputStream;
+import java.io.InputStreamReader;
+import java.io.OutputStream;
+import java.io.OutputStreamWriter;
+import java.io.StringWriter;
+import java.io.Writer;
import java.net.URI;
import java.util.Arrays;
+import java.util.List;
import junit.framework.Test;
import org.netbeans.api.debugger.DebuggerEngine;
+import org.netbeans.api.extexecution.base.ExplicitProcessParameters;
import org.netbeans.junit.NbModuleSuite;
import org.netbeans.junit.NbTestCase;
+import org.openide.filesystems.FileObject;
+import org.openide.filesystems.FileUtil;
public abstract class AbstractDebugTest extends NbTestCase {
protected DebuggerEngine engine;
protected CPPLiteDebugger debugger;
protected Process process;
+ protected StringBuffer stdOut;
+ protected StringBuffer stdErr;
private final int[] suspendCount = new int[]{0};
private final int[] resumeCount = new int[]{0};
@@ -41,6 +54,14 @@ public abstract class AbstractDebugTest extends NbTestCase {
super(s);
}
+ protected final static void createSourceFile(String fileName, File wd, String content) throws IOException {
+ FileObject source = FileUtil.createData(FileUtil.toFileObject(wd), "main.cpp");
+ try (OutputStream os = source.getOutputStream();
+ Writer w = new OutputStreamWriter(os)) {
+ w.append(content);
+ }
+ }
+
protected final static void compileC(String name, File wd) throws IOException, InterruptedException {
Process compile = new ProcessBuilder("gcc", "-o", name, "-g", name + ".c").directory(wd).start();
assertEquals(0, compile.waitFor());
@@ -52,9 +73,20 @@ public abstract class AbstractDebugTest extends NbTestCase {
}
protected final void startDebugging(String name, File wd) throws IOException {
+ ExplicitProcessParameters processParameters = ExplicitProcessParameters.builder().workingDirectory(wd).build();
+ startDebugging(name, wd, processParameters);
+ }
+
+ protected final void startDebugging(String name, File wd, ExplicitProcessParameters processParameters) throws IOException {
+ startDebugging(Arrays.asList(new File(wd, name).getAbsolutePath()), processParameters);
+ }
+
+ protected final void startDebugging(List<String> executable, ExplicitProcessParameters processParameters) throws IOException {
this.process = CPPLiteDebugger.startDebugging(
- new CPPLiteDebuggerConfig(Arrays.asList(new File(wd, name).getAbsolutePath()), wd, null, "gdb"),
+ new CPPLiteDebuggerConfig(executable, processParameters, null, "gdb"),
engine -> this.engine = engine);
+ stdOut = outputFrom(process.getInputStream());
+ stdErr = outputFrom(process.getErrorStream());
debugger = engine.lookupFirst(null, CPPLiteDebugger.class);
debugger.addStateListener(new CPPLiteDebugger.StateListener() {
@Override
@@ -120,4 +152,23 @@ public abstract class AbstractDebugTest extends NbTestCase {
public static Test suite() {
return NbModuleSuite.emptyConfiguration().gui(false).suite();
}
+
+ private static StringBuffer outputFrom(InputStream inputStream) {
+ StringBuffer buffer = new StringBuffer();
+ new Thread("Process output") {
+ @Override
+ public void run() {
+ try (BufferedReader br = new BufferedReader(new InputStreamReader(inputStream))) {
+ String line;
+ while ((line = br.readLine()) != null) {
+ buffer.append(line);
+ buffer.append('\n');
+ }
+ } catch (IOException ex) {
+ buffer.append(ex);
+ }
+ }
+ }.start();
+ return buffer;
+ }
}
diff --git a/cpplite/cpplite.debugger/test/unit/src/org/netbeans/modules/cpplite/debugger/ProcessParametersTest.java b/cpplite/cpplite.debugger/test/unit/src/org/netbeans/modules/cpplite/debugger/ProcessParametersTest.java
new file mode 100644
index 0000000..fd5eb18
--- /dev/null
+++ b/cpplite/cpplite.debugger/test/unit/src/org/netbeans/modules/cpplite/debugger/ProcessParametersTest.java
@@ -0,0 +1,192 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.netbeans.modules.cpplite.debugger;
+
+import java.io.File;
+import java.io.OutputStream;
+import java.io.OutputStreamWriter;
+import java.io.Writer;
+import java.nio.file.Files;
+import java.util.Arrays;
+import java.util.HashMap;
+import java.util.Map;
+import junit.framework.Test;
+import org.netbeans.api.extexecution.base.ExplicitProcessParameters;
+import org.netbeans.junit.NbModuleSuite;
+import org.openide.filesystems.FileObject;
+import org.openide.filesystems.FileUtil;
+
+/**
+ * Tests application parameters set through {@link ExplicitProcessParameters}.
+ *
+ * @author Martin Entlicher
+ */
+public class ProcessParametersTest extends AbstractDebugTest {
+
+ private static final String CPP_APP =
+ "#include <unistd.h>\n" +
+ "#include <stdio.h>\n" +
+ "#include <limits.h>\n" +
+ "\n" +
+ "int main(int argc, char **argv, char **envp) {\n" +
+ " char buffer[PATH_MAX];\n" +
+ " if (getcwd(buffer, sizeof(buffer)) != NULL) {\n" +
+ " printf(\"CWD:%s\\n\", buffer);\n" +
+ " } else {\n" +
+ " perror(\"getcwd() error\");\n" +
+ " return 1;\n" +
+ " }\n" +
+ " while (--argc) {\n" +
+ " printf(\"ARG:%s\\n\", *(++argv));\n" +
+ " }\n" +
+ " for (char **e = envp; *e; e++) {\n" +
+ " printf(\"ENV:%s\\n\", *e);\n" +
+ " }\n" +
+ " return 0;\n" +
+ "}";
+
+ public ProcessParametersTest(String s) {
+ super(s);
+ }
+
+ @Override
+ protected void setUp() throws Exception {
+ clearWorkDir();
+ }
+
+ public void testWorkingDir() throws Exception {
+ File wd = getWorkDir();
+ createSourceFile("main.cpp", wd, CPP_APP);
+ compileCPP("main", wd);
+ File appWD = Files.createTempDirectory("TestCWD").toFile();
+ try {
+ ExplicitProcessParameters params = ExplicitProcessParameters.builder().
+ workingDirectory(appWD).
+ build();
+ startDebugging("main", wd, params);
+ assertEquals(0, waitAppProcessExit());
+ String output = stdOut.toString();
+ assertEquals("CWD:" + appWD.getAbsolutePath(), output.substring(0, output.indexOf('\n')));
+ } finally {
+ appWD.delete();
+ }
+ }
+
+ public void testArgumentsOwn() throws Exception {
+ File wd = getWorkDir();
+ createSourceFile("main.cpp", wd, CPP_APP);
+ compileCPP("main", wd);
+ ExplicitProcessParameters params = ExplicitProcessParameters.empty();
+ startDebugging(Arrays.asList(new File(wd, "main").getAbsolutePath(), "Param1", "Param 2"), params);
+ assertEquals(0, waitAppProcessExit());
+ assertArguments("Param1", "Param 2");
+ }
+
+ public void testArgumentsExplicit() throws Exception {
+ File wd = getWorkDir();
+ createSourceFile("main.cpp", wd, CPP_APP);
+ compileCPP("main", wd);
+ ExplicitProcessParameters params = ExplicitProcessParameters.builder().
+ arg("ExpParam1").
+ arg("ExpParam 2").
+ build();
+ startDebugging(Arrays.asList(new File(wd, "main").getAbsolutePath()), params);
+ assertEquals(0, waitAppProcessExit());
+ assertArguments("ExpParam1", "ExpParam 2");
+ }
+
+ public void testArgumentsCombinedReplace() throws Exception {
+ File wd = getWorkDir();
+ createSourceFile("main.cpp", wd, CPP_APP);
+ compileCPP("main", wd);
+ ExplicitProcessParameters params = ExplicitProcessParameters.builder().
+ arg("ExpParam1").
+ arg("ExpParam 2").
+ build();
+ startDebugging(Arrays.asList(new File(wd, "main").getAbsolutePath(), "Param1", "Param 2"), params);
+ assertEquals(0, waitAppProcessExit());
+ assertArguments("ExpParam1", "ExpParam 2");
+ }
+
+ public void testArgumentsCombinedAppend() throws Exception {
+ File wd = getWorkDir();
+ createSourceFile("main.cpp", wd, CPP_APP);
+ compileCPP("main", wd);
+ ExplicitProcessParameters params = ExplicitProcessParameters.builder().
+ arg("ExpParam1").
+ arg("ExpParam 2").
+ replaceArgs(false).
+ build();
+ startDebugging(Arrays.asList(new File(wd, "main").getAbsolutePath(), "Param1", "Param 2"), params);
+ assertEquals(0, waitAppProcessExit());
+ assertArguments("Param1", "Param 2", "ExpParam1", "ExpParam 2");
+ }
+
+ public void testEnvironmentVariables() throws Exception {
+ File wd = getWorkDir();
+ createSourceFile("main.cpp", wd, CPP_APP);
+ compileCPP("main", wd);
+ ExplicitProcessParameters params = ExplicitProcessParameters.builder().
+ environmentVariable("TEST_VAR1", "test variable value 1").
+ environmentVariable("TEST_VAR2", "test variable value 2").
+ environmentVariable("USER", null).
+ build();
+ startDebugging("main", wd, params);
+ assertEquals(0, waitAppProcessExit());
+ String output = stdOut.toString();
+ Map<String, String> env = new HashMap<>();
+ String[] lines = output.split("\n");
+ for (String line : lines) {
+ if (line.startsWith("ENV:")) {
+ line = line.substring("ENV:".length());
+ String[] var = line.split("=");
+ env.put(var[0], var[1]);
+ }
+ }
+ assertEquals("test variable value 1", env.get("TEST_VAR1"));
+ assertEquals("test variable value 2", env.get("TEST_VAR2"));
+ assertFalse("Removed variable is present", env.containsKey("USER"));
+ }
+
+ public static Test suite() {
+ return NbModuleSuite.emptyConfiguration()
+ .addTest(ProcessParametersTest.class)
+ .enableModules(".*", ".*")
+ .gui(false)
+ .suite();
+ }
+
+ private void assertArguments(String... args) {
+ String output = stdOut.toString();
+ int index1 = 0;
+ int index2 = 0;
+ for (int i = 0; i < args.length; i++) {
+ index1 = output.indexOf("ARG:", index2);
+ if (index1 < 0) {
+ fail("Missing argument " + i + " : " + args[i]);
+ }
+ index2 = output.indexOf('\n', index1);
+ assertEquals("ARG:" + args[i] , output.substring(index1, index2));
+ }
+ index1 = output.indexOf("ARG:", index2);
+ if (index1 >= 0) {
+ fail("An extra argument: " + output.substring(index1));
+ }
+ }
+}
diff --git a/extide/gradle/apichanges.xml b/extide/gradle/apichanges.xml
index b21982e..a9592af 100644
--- a/extide/gradle/apichanges.xml
+++ b/extide/gradle/apichanges.xml
@@ -83,6 +83,23 @@ is the proper place.
<!-- ACTUAL CHANGES BEGIN HERE: -->
<changes>
+ <change id="tooling-runWorkingDir-runEnvironment">
+ <api name="general"/>
+ <summary>NetBeans Tooling plugin recognizes "runWorkingDir" and "runEnvironment" properties.</summary>
+ <version major="2" minor="21"/>
+ <date day="23" month="9" year="2021"/>
+ <author login="entlicher"/>
+ <compatibility semantic="compatible" addition="yes"/>
+ <description>
+ Introduced <code>runWorkingDir</code> and <code>runEnvironment</code> project properties that can be passed
+ to NetBeans Tooling plugin in order to inject user parameters into gradle JavaExec task configurations.
+ The property <code>runWorkingDir</code> shall contain an absolute path of the working directory.
+ The property <code>runEnvironment</code> shall contain possibly quoted space-separated expressions of
+ <code>VAR_NAME=VAR_VALUE</code>, or <code>!VAR_NAME</code>. The <code>VAR_NAME=VAR_VALUE</code> sets
+ environment variable <code>VAR_NAME</code> to value <code>VAR_VALUE</code> and <code>!VAR_NAME</code>
+ removes variable <code>VAR_NAME</code> from the set of environment variables.
+ </description>
+ </change>
<change id="gradle-init">
<api name="general"/>
<summary>
diff --git a/extide/gradle/arch.xml b/extide/gradle/arch.xml
index 1c686f1..78d3e8f 100644
--- a/extide/gradle/arch.xml
+++ b/extide/gradle/arch.xml
@@ -156,6 +156,20 @@
If passing more arguments, the whole -P must be properly quoted. Arguments that contain spaces must
also use quoting or escapes.
</api>
+ <api category="friend" group="property" name="runWorkingDir" type="export">
+ NetBeans Tooling plugin recognizes <code>runWorkingDir</code> project property, and injects
+ specified working directory into JavaExec executing the user code. Use <code>-PrunWorkingDir=...</code>.
+ If the working directory contain spaces, it must also use quoting or escapes.
+ </api>
+ <api category="friend" group="property" name="runEnvironment" type="export">
+ NetBeans Tooling plugin recognizes <code>runEnvironment</code> project property, and injects
+ specified environmental variables into JavaExec executing the user code. Use <code>-PrunEnvironment=...</code>.
+ The value consists of expressions of <code>VAR_NAME=VAR_VALUE</code>, or <code>!VAR_NAME</code>.
+ The <code>VAR_NAME=VAR_VALUE</code> sets environment variable <code>VAR_NAME</code> to value <code>VAR_VALUE</code>
+ and <code>!VAR_NAME</code> removes variable <code>VAR_NAME</code> from the set of environment variables.
+ If passing more arguments, the whole -P must be properly quoted. Variables whose values contain spaces must
+ also use quoting or escapes.
+ </api>
</answer>
<answer id="resources-file">
diff --git a/extide/gradle/netbeans-gradle-tooling/src/main/groovy/org/netbeans/modules/gradle/tooling/NetBeansRunSinglePlugin.groovy b/extide/gradle/netbeans-gradle-tooling/src/main/groovy/org/netbeans/modules/gradle/tooling/NetBeansRunSinglePlugin.groovy
index 0663334..8470da8 100644
--- a/extide/gradle/netbeans-gradle-tooling/src/main/groovy/org/netbeans/modules/gradle/tooling/NetBeansRunSinglePlugin.groovy
+++ b/extide/gradle/netbeans-gradle-tooling/src/main/groovy/org/netbeans/modules/gradle/tooling/NetBeansRunSinglePlugin.groovy
@@ -35,6 +35,8 @@ class NetBeansRunSinglePlugin implements Plugin<Project> {
static String RUN_SINGLE_MAIN = "runClassName"
static String RUN_SINGLE_ARGS = "runArgs"
static String RUN_SINGLE_JVM_ARGS = "runJvmArgs"
+ static String RUN_SINGLE_CWD = "runWorkingDir"
+ static String RUN_SINGLE_ENV = "runEnvironment"
void apply(Project project) {
project.afterEvaluate {
@@ -65,7 +67,107 @@ class NetBeansRunSinglePlugin implements Plugin<Project> {
if (project.hasProperty(RUN_SINGLE_ARGS)) {
args = project.getProperty(RUN_SINGLE_ARGS).tokenize(' ')
}
+ if (project.hasProperty(RUN_SINGLE_CWD)) {
+ workingDir = project.getProperty(RUN_SINGLE_CWD)
+ }
+ if (project.hasProperty(RUN_SINGLE_ENV)) {
+ // Quoted space-separated expressions of <ENV_VAR>=<ENV_VALUE>
+ // to set environment variables, or !<ENV_VAR>
+ // to remove environment variables
+ unescapeParameters(project.getProperty(RUN_SINGLE_ENV)).each {
+ env ->
+ if (env.startsWith("!")) {
+ environment.remove(env.substring(1))
+ } else {
+ def i = env.indexOf("=")
+ if (i > 0) {
+ environment[env.substring(0, i)] = env.substring(i + 1)
+ }
+ }
+ }
+ }
}
}
-
+
+ private String[] unescapeParameters(String s) {
+ final int NULL = 0x0;
+ final int IN_PARAM = 0x1;
+ final int IN_DOUBLE_QUOTE = 0x2;
+ final int IN_SINGLE_QUOTE = 0x3;
+ ArrayList<String> params = new ArrayList<>(5);
+ char c;
+
+ int state = NULL;
+ StringBuilder buff = new StringBuilder(20);
+ int slength = s.length();
+
+ for (int i = 0; i < slength; i++) {
+ c = s.charAt(i);
+ switch (state) {
+ case NULL:
+ switch (c) {
+ case '\'':
+ state = IN_SINGLE_QUOTE;
+ break;
+ case '"':
+ state = IN_DOUBLE_QUOTE;
+ break;
+ default:
+ if (!Character.isWhitespace(c)) {
+ buff.append(c);
+ state = IN_PARAM;
+ }
+ }
+ break;
+ case IN_SINGLE_QUOTE:
+ if (c != '\'') {
+ buff.append(c);
+ } else {
+ state = IN_PARAM;
+ }
+ break;
+ case IN_DOUBLE_QUOTE:
+ switch (c) {
+ case '\\':
+ char peek = (i < slength - 1) ? s.charAt(i+1) : Character.MIN_VALUE;
+ if (peek == '"' || peek =='\\') {
+ buff.append(peek);
+ i++;
+ } else {
+ buff.append(c);
+ }
+ break;
+ case '"':
+ state = IN_PARAM;
+ break;
+ default:
+ buff.append(c);
+ }
+ break;
+ case IN_PARAM:
+ switch (c) {
+ case '\'':
+ state = IN_SINGLE_QUOTE;
+ break;
+ case '"':
+ state = IN_DOUBLE_QUOTE;
+ break;
+ default:
+ if (Character.isWhitespace(c)) {
+ params.add(buff.toString());
+ buff.setLength(0);
+ state = NULL;
+ } else {
+ buff.append(c);
+ }
+ }
+ break;
+ }
+ }
+ if (buff.length() > 0) {
+ params.add(buff.toString());
+ }
+
+ return params.toArray(new String[params.size()])
+ }
}
diff --git a/ide/extexecution.base/src/org/netbeans/api/extexecution/base/ExplicitProcessParameters.java b/ide/extexecution.base/src/org/netbeans/api/extexecution/base/ExplicitProcessParameters.java
index 9aa9d03..a25e199 100644
--- a/ide/extexecution.base/src/org/netbeans/api/extexecution/base/ExplicitProcessParameters.java
+++ b/ide/extexecution.base/src/org/netbeans/api/extexecution/base/ExplicitProcessParameters.java
@@ -18,11 +18,15 @@
*/
package org.netbeans.api.extexecution.base;
+import java.io.File;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.Collections;
+import java.util.HashMap;
import java.util.List;
+import java.util.Map;
+import org.netbeans.api.annotations.common.CheckForNull;
import org.netbeans.api.annotations.common.NonNull;
import org.netbeans.api.annotations.common.NullAllowed;
import org.openide.util.Lookup;
@@ -45,8 +49,7 @@ import org.openide.util.Lookup;
* process arguments.
* <div class="nonnormative">
* For <b>java applications</b> when {@code java} executable is used to launch the application, or even Maven project (see below), the <b>launcherArgs</b> should correspond to VM
- * arguments, and <b>args</b> correspond to the main class' arguments (passed to the main class). Environment variables for the new process are not
- * supported at the moment.
+ * arguments, and <b>args</b> correspond to the main class' arguments (passed to the main class). Additional environment variables can be specified.
* </div>
* <p>
* If the object is marked as {@link #isArgReplacement()}, the launcher implementor SHOULD replace all
@@ -92,17 +95,22 @@ public final class ExplicitProcessParameters {
private final List<String> arguments;
private final boolean replaceArgs;
private final boolean replaceLauncherArgs;
+ private final File workingDirectory;
+ private final Map<String, String> environmentVars;
private ExplicitProcessParameters(int position, List<String> launcherArguments,
- List<String> arguments, boolean appendArgs, boolean appendLauncherArgs) {
+ List<String> arguments, boolean appendArgs, boolean appendLauncherArgs,
+ File workingDirectory, Map<String, String> environmentVars) {
this.position = position;
this.launcherArguments = launcherArguments == null ? null : Collections.unmodifiableList(launcherArguments);
this.arguments = arguments == null ? null : Collections.unmodifiableList(arguments);
this.replaceArgs = appendArgs;
this.replaceLauncherArgs = appendLauncherArgs;
+ this.workingDirectory = workingDirectory;
+ this.environmentVars = environmentVars == null ? null : Collections.unmodifiableMap(environmentVars);
}
- private static final ExplicitProcessParameters EMPTY = new ExplicitProcessParameters(0, null, null, false, false);
+ private static final ExplicitProcessParameters EMPTY = new ExplicitProcessParameters(0, null, null, false, false, null, null);
/**
* Returns an empty instance of parameters that has no effect. DO NOT check for emptiness by
@@ -122,7 +130,10 @@ public final class ExplicitProcessParameters {
if (isArgReplacement() || isLauncherArgReplacement()) {
return false;
}
- return (((arguments == null) || arguments.isEmpty()) && (launcherArguments == null || launcherArguments.isEmpty()));
+ return ((arguments == null) || arguments.isEmpty()) &&
+ (launcherArguments == null || launcherArguments.isEmpty()) &&
+ workingDirectory == null &&
+ (environmentVars == null || environmentVars.isEmpty());
}
/**
@@ -190,6 +201,29 @@ public final class ExplicitProcessParameters {
}
/**
+ * Returns working directory to be set for the process.
+ *
+ * @return working directory, or <code>nul</code>
+ * @since 1.20
+ */
+ public @CheckForNull File getWorkingDirectory() {
+ return workingDirectory;
+ }
+
+ /**
+ * Returns a map of additional environment variables to be set for the process.
+ * Always non-null. Values of existing environment variables are overridden.
+ * A <code>null</code> value of a variable should be interpreted as a removal
+ * of that variable from the environment.
+ *
+ * @return map of additional environment variables
+ * @since 1.20
+ */
+ public @NonNull Map<String, String> getEnvironmentVariables() {
+ return environmentVars != null ? environmentVars : Collections.emptyMap();
+ }
+
+ /**
* Merges ExplicitProcessParameters instructions found in the Lookup. See {@link #buildExplicitParameters(java.util.Collection)}
* for more details.
* @param context context for the execution
@@ -214,6 +248,8 @@ public final class ExplicitProcessParameters {
* <i>Note:</i> if a replacement instruction and all the following (if any) have {@link #getArguments()} {@code null} (= no change),
* the result will report <b>no change</b>. It is therefore possible to <b>discard all contributions</b> by appending a no-change replacement
* last.
+ * <p>
+ * Environment variables are overridden by newly set variables.
*
* @param items individual instructions.
* @return combined instructions.
@@ -238,7 +274,7 @@ public final class ExplicitProcessParameters {
* <li><b>appends</b> launcher arguments
* <li><b>replaces</b> (normal) arguments
* </ul>
- * and the mode can be overriden for each group.
+ * and the mode can be overridden for each group.
*/
public final static class Builder {
private int position = 0;
@@ -246,6 +282,8 @@ public final class ExplicitProcessParameters {
private List<String> arguments = null;
private Boolean replaceArgs;
private Boolean replaceLauncherArgs;
+ private File workingDirectory = null;
+ private Map<String, String> environmentVars;
private void initArgs() {
if (arguments == null) {
@@ -371,6 +409,54 @@ public final class ExplicitProcessParameters {
}
/**
+ * Sets working directory to be used for the process.
+ *
+ * @param workingDirectory the working directory
+ * @return the builder
+ * @since 1.20
+ */
+ public Builder workingDirectory(File workingDirectory) {
+ this.workingDirectory = workingDirectory;
+ return this;
+ }
+
+ /**
+ * Provide additional environment variables for the process. Values of
+ * existing environment variables are overridden. <code>null</code> values
+ * are interpreted as removal of the respective variables from the environment.
+ *
+ * @param env a map of additional environment variables
+ * @return the builder
+ * @since 1.20
+ */
+ public Builder environmentVariables(Map<String, String> env) {
+ if (!env.isEmpty()) {
+ if (this.environmentVars == null) {
+ this.environmentVars = new HashMap<>();
+ }
+ this.environmentVars.putAll(env);
+ }
+ return this;
+ }
+
+ /**
+ * Provide an additional environment variable for the process. If the variable
+ * already exists, it's overridden with the new value.
+ *
+ * @param name name of the environment variable
+ * @param value value of the environment variable, or <code>null</code> in which case an existing variable is to be removed.
+ * @return the builder
+ * @since 1.20
+ */
+ public Builder environmentVariable(String name, String value) {
+ if (this.environmentVars == null) {
+ this.environmentVars = new HashMap<>();
+ }
+ this.environmentVars.put(name, value);
+ return this;
+ }
+
+ /**
* Defines a position for combining. The default rank is {@code 0}. When used in a collection in
* {@link ExplicitProcessParameters#buildExplicitParameters(java.util.Collection)}, instances are sorted
* by their position, in ascending order (lowest first).
@@ -416,6 +502,12 @@ public final class ExplicitProcessParameters {
if (p.getArguments() != null) {
args(p.getArguments());
}
+ if (p.getWorkingDirectory() != null) {
+ workingDirectory(p.getWorkingDirectory());
+ }
+ if (!p.getEnvironmentVariables().isEmpty()) {
+ environmentVariables(p.getEnvironmentVariables());
+ }
return this;
}
@@ -430,7 +522,7 @@ public final class ExplicitProcessParameters {
return new ExplicitProcessParameters(position, launcherArguments, arguments,
// if no args / launcher args given and no explicit instruction on append,
// make the args appending.
- aa, apa);
+ aa, apa, workingDirectory, environmentVars);
}
}
}
diff --git a/ide/nativeimage.api/src/org/netbeans/modules/nativeimage/api/debug/EvaluateException.java b/ide/nativeimage.api/src/org/netbeans/modules/nativeimage/api/debug/EvaluateException.java
index 1d542bb..88a2cd8 100644
--- a/ide/nativeimage.api/src/org/netbeans/modules/nativeimage/api/debug/EvaluateException.java
+++ b/ide/nativeimage.api/src/org/netbeans/modules/nativeimage/api/debug/EvaluateException.java
@@ -21,7 +21,7 @@ package org.netbeans.modules.nativeimage.api.debug;
/**
* Thrown when evaluation in the native debugger fails.
*
- * @since 1.0
+ * @since 0.1
*/
public class EvaluateException extends Exception {
@@ -30,7 +30,7 @@ public class EvaluateException extends Exception {
* specified detail message.
*
* @param msg the detail message.
- * @since 1.0
+ * @since 0.1
*/
public EvaluateException(String msg) {
super(msg);
@@ -41,10 +41,9 @@ public class EvaluateException extends Exception {
* existing exception.
*
* @param t exception.
- * @since 1.0
+ * @since 0.1
*/
public EvaluateException(Throwable t) {
super(t.getLocalizedMessage(), t);
-
}
}
diff --git a/ide/nativeimage.api/src/org/netbeans/modules/nativeimage/api/debug/NIDebugger.java b/ide/nativeimage.api/src/org/netbeans/modules/nativeimage/api/debug/NIDebugger.java
index bdd0245..a66b353 100644
--- a/ide/nativeimage.api/src/org/netbeans/modules/nativeimage/api/debug/NIDebugger.java
+++ b/ide/nativeimage.api/src/org/netbeans/modules/nativeimage/api/debug/NIDebugger.java
@@ -21,6 +21,7 @@ package org.netbeans.modules.nativeimage.api.debug;
import java.io.File;
import java.util.List;
import java.util.Map;
+import java.util.Objects;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.ExecutionException;
import java.util.function.Consumer;
@@ -42,7 +43,7 @@ import org.openide.util.NbBundle;
/**
* Representation of a native image debugger.
- * @since 1.0
+ * @since 0.1
*/
public final class NIDebugger {
@@ -59,7 +60,7 @@ public final class NIDebugger {
* @throws IllegalStateException when the native debugger is not available
* (there is not an implementation of {@link NIDebuggerServiceProvider}
* registered in the default lookup).
- * @since 1.0
+ * @since 0.1
*/
@NbBundle.Messages({"MSG_NoNativeDebug=No native debugger is available. Please install native debugger module."})
public static Builder newBuilder() throws IllegalStateException {
@@ -79,7 +80,7 @@ public final class NIDebugger {
* @param id a unique ID of the breakpoint
* @param breakpointDescriptor the breakpoint descriptor
* @return an instance of the native breakpoint
- * @since 1.0
+ * @since 0.1
*/
public Breakpoint addLineBreakpoint(Object id, NILineBreakpointDescriptor breakpointDescriptor) {
Breakpoint breakpoint = provider.addLineBreakpoint(id, breakpointDescriptor);
@@ -91,7 +92,7 @@ public final class NIDebugger {
* Remove breakpoint with the given id.
*
* @param id the ID of the breakpoint to remove
- * @since 1.0
+ * @since 0.1
*/
public void removeBreakpoint(Object id) {
provider.removeBreakpoint(id);
@@ -107,13 +108,42 @@ public final class NIDebugger {
* @param executionDescriptor execution descriptor that describes the runtime attributes
* @param startedEngine the corresponding DebuggerEngine is passed to this consumer
* @return future that completes on the execution finish
- * @since 1.0
+ * @since 0.1
+ * @deprecated Use {@link #start(org.netbeans.modules.nativeimage.api.debug.StartDebugParameters, java.util.function.Consumer)}
*/
+ @Deprecated
public CompletableFuture<Void> start(List<String> command, File workingDirectory, String debugger, String displayName, ExecutionDescriptor executionDescriptor, Consumer<DebuggerEngine> startedEngine) {
return provider.start(command, workingDirectory, debugger, displayName, executionDescriptor, startedEngine);
}
/**
+ * Start the actual debugging session. Call this typically after breakpoints are added.
+ *
+ * @param debugParameters parameters to start the debugging with
+ * @param startedEngine the corresponding DebuggerEngine is passed to this consumer
+ * @return a future which is completed when the started debugger session finishes
+ * @since 0.5
+ */
+ public CompletableFuture<Void> start(StartDebugParameters debugParameters, Consumer<DebuggerEngine> startedEngine) {
+ Objects.requireNonNull(debugParameters);
+ CompletableFuture<Void> start = provider.start(debugParameters, startedEngine);
+ if (start.isCompletedExceptionally()) {
+ try {
+ start.get();
+ } catch (ExecutionException ex) {
+ if (ex.getCause() instanceof UnsupportedOperationException) {
+ return startLegacy(debugParameters, startedEngine);
+ }
+ } catch (Throwable ex) {}
+ }
+ return start;
+ }
+
+ private CompletableFuture<Void> startLegacy(StartDebugParameters debugParameters, Consumer<DebuggerEngine> startedEngine) {
+ return provider.start(debugParameters.getCommand(), debugParameters.getWorkingDirectory(), debugParameters.getDebugger(), debugParameters.getDisplayName(), debugParameters.getExecutionDescriptor(), startedEngine);
+ }
+
+ /**
* Attach to a process and create a debugging session. Call this typically after breakpoints are added.
*
* @param executablePath path to an executable representing the native image
@@ -135,7 +165,7 @@ public final class NIDebugger {
* when <code>null</code> the expression is used as the name
* @param frame the frame to evaluate at
* @return the completable future with the evaluation result
- * @since 1.0
+ * @since 0.1
*/
public CompletableFuture<NIVariable> evaluateAsync(String expression, String resultName, NIFrame frame) {
return provider.evaluateAsync(expression, resultName, frame);
@@ -151,7 +181,7 @@ public final class NIDebugger {
* @param frame the frame to evaluate at
* @return the evaluation result
* @throws EvaluateException when evaluation fails
- * @since 1.0
+ * @since 0.1
*/
public NIVariable evaluate(String expression, String resultName, NIFrame frame) throws EvaluateException {
try {
@@ -169,7 +199,7 @@ public final class NIDebugger {
* @param length number of bytes to read
* @return hexadecimal representation of the memory content, or <code>null</code>
* when the read is not successful
- * @since 1.0
+ * @since 0.1
*/
public String readMemory(String address, long offset, int length) {
return provider.readMemory(address, offset, length);
@@ -178,7 +208,7 @@ public final class NIDebugger {
/**
* Get version of the underlying native debugger.
*
- * @since 1.0
+ * @since 0.1
*/
public String getVersion() {
return provider.getVersion();
@@ -230,7 +260,7 @@ public final class NIDebugger {
/**
* A builder that creates a Native Image debugger with optional displayers.
*
- * @since 1.0
+ * @since 0.1
*/
public static final class Builder {
@@ -244,7 +274,7 @@ public final class NIDebugger {
* Displayer of native frames.
*
* @param frameDisplayer translator of the native frame to it's displayed information
- * @since 1.0
+ * @since 0.1
*/
public Builder frameDisplayer(FrameDisplayer frameDisplayer) {
this.debuggerProvider.setFrameDisplayer(frameDisplayer);
@@ -255,7 +285,7 @@ public final class NIDebugger {
* Displayer of native variables.
*
* @param variablesDisplayer translator of native variables to displayed variables.
- * @since 1.0
+ * @since 0.1
*/
public Builder variablesDisplayer(VariableDisplayer variablesDisplayer) {
this.debuggerProvider.setVariablesDisplayer(variablesDisplayer);
@@ -264,7 +294,7 @@ public final class NIDebugger {
/**
* Create the debugger instance.
- * @since 1.0
+ * @since 0.1
*/
public NIDebugger build() {
return new NIDebugger(debuggerProvider);
diff --git a/ide/nativeimage.api/src/org/netbeans/modules/nativeimage/api/debug/NIFrame.java b/ide/nativeimage.api/src/org/netbeans/modules/nativeimage/api/debug/NIFrame.java
index 4ffc48c..9cc338c 100644
--- a/ide/nativeimage.api/src/org/netbeans/modules/nativeimage/api/debug/NIFrame.java
+++ b/ide/nativeimage.api/src/org/netbeans/modules/nativeimage/api/debug/NIFrame.java
@@ -21,49 +21,49 @@ package org.netbeans.modules.nativeimage.api.debug;
/**
* Representation of a native stack frame.
*
- * @since 1.0
+ * @since 0.1
*/
public interface NIFrame {
/**
* Frame's thread ID.
- * @since 1.0
+ * @since 0.1
*/
String getThreadId();
/**
* Frame's depth level. The top frame has level 0.
- * @since 1.0
+ * @since 0.1
*/
int getLevel();
/**
* Frame's native address.
- * @since 1.0
+ * @since 0.1
*/
String getAddress();
/**
* A short name of the file associated with the frame.
- * @since 1.0
+ * @since 0.1
*/
String getShortFileName();
/**
* A full name of the file associated with the frame.
- * @since 1.0
+ * @since 0.1
*/
String getFullFileName();
/**
* Name of the function associated with the frame.
- * @since 1.0
+ * @since 0.1
*/
String getFunctionName();
/**
* 1-based line of the frame location.
- * @since 1.0
+ * @since 0.1
*/
int getLine();
}
diff --git a/ide/nativeimage.api/src/org/netbeans/modules/nativeimage/api/debug/NILineBreakpointDescriptor.java b/ide/nativeimage.api/src/org/netbeans/modules/nativeimage/api/debug/NILineBreakpointDescriptor.java
index 192be78..2033f39 100644
--- a/ide/nativeimage.api/src/org/netbeans/modules/nativeimage/api/debug/NILineBreakpointDescriptor.java
+++ b/ide/nativeimage.api/src/org/netbeans/modules/nativeimage/api/debug/NILineBreakpointDescriptor.java
@@ -23,7 +23,7 @@ import org.netbeans.api.annotations.common.CheckForNull;
/**
* Description of a line native breakpoint.
*
- * @since 1.0
+ * @since 0.1
*/
public final class NILineBreakpointDescriptor {
@@ -46,7 +46,7 @@ public final class NILineBreakpointDescriptor {
*
* @param filePath file path of the breakpoint
* @param lineNumber 1-based line number
- * @since 1.0
+ * @since 0.1
*/
public static Builder newBuilder(String filePath, int lineNumber) {
return new Builder(filePath, lineNumber);
@@ -55,7 +55,7 @@ public final class NILineBreakpointDescriptor {
/**
* Get path of the file.
*
- * @since 1.0
+ * @since 0.1
*/
public String getFilePath() {
return filePath;
@@ -64,7 +64,7 @@ public final class NILineBreakpointDescriptor {
/**
* Get 1-based line number.
*
- * @since 1.0
+ * @since 0.1
*/
public int getLine() {
return line;
@@ -73,7 +73,7 @@ public final class NILineBreakpointDescriptor {
/**
* Check if the breakpoint is to be enabled.
*
- * @since 1.0
+ * @since 0.1
*/
public boolean isEnabled() {
return enabled;
@@ -83,7 +83,7 @@ public final class NILineBreakpointDescriptor {
* Get the breakpoint condition.
*
* @return the condition, or <code>null</code> when the breakpoint does not have any condition.
- * @since 1.0
+ * @since 0.1
*/
@CheckForNull
public String getCondition() {
@@ -93,7 +93,7 @@ public final class NILineBreakpointDescriptor {
/**
* Check if the breakpoint is to be hidden (not user-visible).
*
- * @since 1.0
+ * @since 0.1
*/
public boolean isHidden() {
return hidden;
@@ -103,7 +103,7 @@ public final class NILineBreakpointDescriptor {
* Builder of a line native breakpoint descriptor. The builder is reusable
* and the built breakpoint descriptor can be used to update existing breakpoints.
*
- * @since 1.0
+ * @since 0.1
*/
public static final class Builder {
@@ -121,7 +121,7 @@ public final class NILineBreakpointDescriptor {
/**
* Set a file path.
*
- * @since 1.0
+ * @since 0.1
*/
public Builder filePath(String filePath) {
this.filePath = filePath;
@@ -131,7 +131,7 @@ public final class NILineBreakpointDescriptor {
/**
* Set a 1-based line number.
*
- * @since 1.0
+ * @since 0.1
*/
public Builder line(int lineNumber) {
this.line = lineNumber;
@@ -141,7 +141,7 @@ public final class NILineBreakpointDescriptor {
/**
* Set a condition.
*
- * @since 1.0
+ * @since 0.1
*/
public Builder condition(String condition) {
this.condition = condition;
@@ -151,7 +151,7 @@ public final class NILineBreakpointDescriptor {
/**
* Set an enabled state of the breakpoint. The breakpoint is enabled by default.
*
- * @since 1.0
+ * @since 0.1
*/
public Builder enabled(boolean enabled) {
this.enabled = enabled;
@@ -162,7 +162,7 @@ public final class NILineBreakpointDescriptor {
* Set a hidden state of the breakpoint. Hidden breakpoints are not visible
* to users. The breakpoint is not hidden by default.
*
- * @since 1.0
+ * @since 0.1
*/
public Builder hidden(boolean hidden) {
this.hidden = hidden;
diff --git a/ide/nativeimage.api/src/org/netbeans/modules/nativeimage/api/debug/NIVariable.java b/ide/nativeimage.api/src/org/netbeans/modules/nativeimage/api/debug/NIVariable.java
index cd46e7c..d200192 100644
--- a/ide/nativeimage.api/src/org/netbeans/modules/nativeimage/api/debug/NIVariable.java
+++ b/ide/nativeimage.api/src/org/netbeans/modules/nativeimage/api/debug/NIVariable.java
@@ -20,28 +20,28 @@ package org.netbeans.modules.nativeimage.api.debug;
/**
* Representation of a native variable.
- * @since 1.0
+ * @since 0.1
*/
public interface NIVariable {
/**
* Name of the variable.
*
- * @since 1.0
+ * @since 0.1
*/
String getName();
/**
* Type of the variable value.
*
- * @since 1.0
+ * @since 0.1
*/
String getType();
/**
* String representation of the variable value.
*
- * @since 1.0
+ * @since 0.1
*/
String getValue();
@@ -50,14 +50,14 @@ public interface NIVariable {
* variable.
*
* @return the parent variable, or <code>null</code>.
- * @since 1.0
+ * @since 0.1
*/
NIVariable getParent();
/**
* Number of child variables (properties, or array elements) of this variable.
*
- * @since 1.0
+ * @since 0.1
*/
int getNumChildren();
@@ -66,14 +66,14 @@ public interface NIVariable {
* <code>from</code> index and up to and excluding <code>to</code> index will
* be returned. If <code>from</code> is less than zero, all children are returned.
*
- * @since 1.0
+ * @since 0.1
*/
NIVariable[] getChildren(int from, int to);
/**
* Get all variable's children.
*
- * @since 1.0
+ * @since 0.1
*/
default NIVariable[] getChildren() {
return getChildren(0, Integer.MAX_VALUE);
@@ -82,7 +82,7 @@ public interface NIVariable {
/**
* Get the full expression that this variable object represents.
*
- * @since 1.0
+ * @since 0.1
*/
String getExpressionPath();
@@ -90,7 +90,7 @@ public interface NIVariable {
* Get the frame this variable is associated with.
*
* @return the frame, or <code>null</code>.
- * @since 1.0
+ * @since 0.1
*/
NIFrame getFrame();
}
diff --git a/ide/nativeimage.api/src/org/netbeans/modules/nativeimage/api/debug/StartDebugParameters.java b/ide/nativeimage.api/src/org/netbeans/modules/nativeimage/api/debug/StartDebugParameters.java
new file mode 100644
index 0000000..2716714
--- /dev/null
+++ b/ide/nativeimage.api/src/org/netbeans/modules/nativeimage/api/debug/StartDebugParameters.java
@@ -0,0 +1,188 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.netbeans.modules.nativeimage.api.debug;
+
+import java.io.File;
+import java.util.List;
+import org.netbeans.api.extexecution.ExecutionDescriptor;
+import org.openide.util.Lookup;
+
+/**
+ * Parameters passed to {@link NIDebugger#start(org.netbeans.modules.nativeimage.api.debug.StartDebugParameters, java.util.function.Consumer)}.
+ *
+ * @since 0.5
+ */
+public final class StartDebugParameters {
+
+ private final List<String> command;
+ private final File workingDirectory;
+ private final String debugger;
+ private final String displayName;
+ private final ExecutionDescriptor executionDescriptor;
+ private final Lookup contextLookup;
+
+ private StartDebugParameters(List<String> command, File workingDirectory, String debugger, String displayName, ExecutionDescriptor executionDescriptor, Lookup contextLookup) {
+ this.command = command;
+ this.workingDirectory = workingDirectory;
+ this.debugger = debugger;
+ this.displayName = displayName;
+ this.executionDescriptor = executionDescriptor;
+ this.contextLookup = contextLookup;
+ }
+
+ /**
+ * The command to run the native image.
+ *
+ * @return the command with arguments.
+ */
+ public List<String> getCommand() {
+ return command;
+ }
+
+ /**
+ * Working directory of the process.
+ *
+ * @return the working directory
+ */
+ public File getWorkingDirectory() {
+ return workingDirectory;
+ }
+
+ /**
+ * The native debugger command.
+ *
+ * @return the debugger command
+ */
+ public String getDebugger() {
+ return debugger;
+ }
+
+ /**
+ * Display name of the debugger task.
+ *
+ * @return the display name of debugger task
+ */
+ public String getDisplayName() {
+ return displayName;
+ }
+
+ /**
+ * Execution descriptor that describes the runtime attributes.
+ *
+ * @return the execution descriptor
+ */
+ public ExecutionDescriptor getExecutionDescriptor() {
+ return executionDescriptor;
+ }
+
+ /**
+ * Context lookup. The lookup may contain other parameters, like ExplicitProcessParameters.
+ *
+ * @return the context lookup
+ */
+ public Lookup getContextLookup() {
+ return contextLookup;
+ }
+
+ /**
+ * Create a new debug parameters builder.
+ *
+ * @param command the command to run the native image.
+ * @return a new builder
+ */
+ public static Builder newBuilder(List<String> command) {
+ return new Builder(command);
+ }
+
+ /**
+ * Builder of start debug parameters.
+ *
+ * @since 0.5
+ */
+ public static final class Builder {
+
+ private final List<String> command;
+ private File workingDirectory;
+ private String debugger;
+ private String displayName;
+ private ExecutionDescriptor executionDescriptor;
+ private Lookup contextLookup;
+
+ Builder(List<String> command) {
+ this.command = command;
+ }
+
+ /**
+ * Set the working directory.
+ * @return the builder
+ */
+ public Builder workingDirectory(File workingDirectory) {
+ this.workingDirectory = workingDirectory;
+ return this;
+ }
+
+ /**
+ * Set the native debugger command.
+ * @return the builder
+ */
+ public Builder debugger(String debugger) {
+ this.debugger = debugger;
+ return this;
+ }
+
+ /**
+ * Set display name of the debugger task.
+ *
+ * @return the builder
+ */
+ public Builder displayName(String displayName) {
+ this.displayName = displayName;
+ return this;
+ }
+
+ /**
+ * Set execution descriptor that describes the runtime attributes.
+ *
+ * @return the builder
+ */
+ public Builder executionDescriptor(ExecutionDescriptor executionDescriptor) {
+ this.executionDescriptor = executionDescriptor;
+ return this;
+ }
+
+ /**
+ * Context lookup. The lookup may contain other parameters, like ExplicitProcessParameters.
+ *
+ * @return the builder
+ */
+ public Builder lookup(Lookup contextLookup) {
+ this.contextLookup = contextLookup;
+ return this;
+ }
+
+ /**
+ * Build the {@link StartDebugParameters} based on the properties set.
+ *
+ * @return a new instance of {@link StartDebugParameters}.
+ */
+ public StartDebugParameters build() {
+ return new StartDebugParameters(command, workingDirectory, debugger, displayName, executionDescriptor, contextLookup);
+ }
+ }
+}
diff --git a/ide/nativeimage.api/src/org/netbeans/modules/nativeimage/spi/debug/NIDebuggerProvider.java b/ide/nativeimage.api/src/org/netbeans/modules/nativeimage/spi/debug/NIDebuggerProvider.java
index 10a5b80..893f839 100644
--- a/ide/nativeimage.api/src/org/netbeans/modules/nativeimage/spi/debug/NIDebuggerProvider.java
+++ b/ide/nativeimage.api/src/org/netbeans/modules/nativeimage/spi/debug/NIDebuggerProvider.java
@@ -33,6 +33,7 @@ import org.netbeans.modules.nativeimage.api.Symbol;
import org.netbeans.modules.nativeimage.api.debug.NIFrame;
import org.netbeans.modules.nativeimage.api.debug.NILineBreakpointDescriptor;
import org.netbeans.modules.nativeimage.api.debug.NIVariable;
+import org.netbeans.modules.nativeimage.api.debug.StartDebugParameters;
import org.netbeans.modules.nativeimage.spi.debug.filters.FrameDisplayer;
import org.netbeans.modules.nativeimage.spi.debug.filters.VariableDisplayer;
@@ -40,7 +41,7 @@ import org.netbeans.modules.nativeimage.spi.debug.filters.VariableDisplayer;
* Provider of the native image debugger.
*
* @author martin
- * @since 1.0
+ * @since 0.1
*/
public interface NIDebuggerProvider {
@@ -52,7 +53,7 @@ public interface NIDebuggerProvider {
* @param id a unique ID of the breakpoint
* @param breakpointDescriptor the breakpoint descriptor
* @return an instance of the native breakpoint
- * @since 1.0
+ * @since 0.1
*/
Breakpoint addLineBreakpoint(Object id, NILineBreakpointDescriptor breakpointDescriptor);
@@ -60,7 +61,7 @@ public interface NIDebuggerProvider {
* Remove breakpoint with the given id.
*
* @param id the ID of the breakpoint to remove
- * @since 1.0
+ * @since 0.1
*/
void removeBreakpoint(Object id);
@@ -68,7 +69,7 @@ public interface NIDebuggerProvider {
* Set a displayer of native frames.
*
* @param frameDisplayer translator of the native frame to it's displayed information
- * @since 1.0
+ * @since 0.1
*/
void setFrameDisplayer(FrameDisplayer frameDisplayer);
@@ -76,7 +77,7 @@ public interface NIDebuggerProvider {
* Set a displayer of native variables.
*
* @param variablesDisplayer translator of native variables to displayed variables.
- * @since 1.0
+ * @since 0.1
*/
void setVariablesDisplayer(VariableDisplayer variablesDisplayer);
@@ -89,10 +90,33 @@ public interface NIDebuggerProvider {
* @param displayName display name of the debugger task
* @param executionDescriptor execution descriptor that describes the runtime attributes
* @param startedEngine the corresponding DebuggerEngine is passed to this consumer
- * @param finishedCallback notification of the execution finish
- * @since 1.0
+ * @since 0.1
+ * @deprecated Use {@link #start(org.netbeans.modules.nativeimage.api.debug.StartDebugParameters, java.util.function.Consumer)}
*/
- CompletableFuture<Void> start(List<String> command, File workingDirectory, String debugger, String displayName, ExecutionDescriptor executionDescriptor, Consumer<DebuggerEngine> startedEngine);
+ @Deprecated
+ default CompletableFuture<Void> start(List<String> command, File workingDirectory, String debugger, String displayName, ExecutionDescriptor executionDescriptor, Consumer<DebuggerEngine> startedEngine) {
+ StartDebugParameters parameters = StartDebugParameters.newBuilder(command)
+ .workingDirectory(workingDirectory)
+ .debugger(debugger)
+ .displayName(displayName)
+ .executionDescriptor(executionDescriptor)
+ .build();
+ return start(parameters, startedEngine);
+ }
+
+ /**
+ * Start the actual debugging session. Called typically after breakpoints are added.
+ *
+ * @param debugParameters parameters to start the debugging with
+ * @param startedEngine the corresponding DebuggerEngine is passed to this consumer
+ * @return a future which is completed when the started debugger session finishes
+ * @since 0.5
+ */
+ default CompletableFuture<Void> start(StartDebugParameters debugParameters, Consumer<DebuggerEngine> startedEngine) {
+ CompletableFuture cf = new CompletableFuture();
+ cf.completeExceptionally(new UnsupportedOperationException());
+ return cf;
+ }
/**
* Attach to a process and create a debugging session. Called typically after breakpoints are added.
@@ -118,7 +142,7 @@ public interface NIDebuggerProvider {
* when <code>null</code> the expression is used as the name
* @param frame the frame to evaluate at
* @return the completable future with the evaluation result
- * @since 1.0
+ * @since 0.1
*/
CompletableFuture<NIVariable> evaluateAsync(String expression, String resultName, NIFrame frame);
@@ -130,14 +154,14 @@ public interface NIDebuggerProvider {
* @param length number of bytes to read
* @return hexadecimal representation of the memory content, or <code>null</code>
* when the read is not successful
- * @since 1.0
+ * @since 0.1
*/
String readMemory(String address, long offset, int length);
/**
* Get version of the underlying native debugger.
*
- * @since 1.0
+ * @since 0.1
*/
String getVersion();
@@ -153,10 +177,20 @@ public interface NIDebuggerProvider {
return null;
}
+ /**
+ * Provide a list of functions in the debuggee.
+ *
+ * @since 0.2
+ */
default Map<SourceInfo, List<Symbol>> listFunctions(String name, boolean includeNondebug, int maxResults) {
return null;
}
+ /**
+ * Provide a list of global variables in the debuggee.
+ *
+ * @since 0.2
+ */
default Map<SourceInfo, List<Symbol>> listVariables(String name, boolean includeNondebug, int maxResults) {
return null;
}
diff --git a/java/gradle.java/arch.xml b/java/gradle.java/arch.xml
index f9d2db2..7c06a5f 100644
--- a/java/gradle.java/arch.xml
+++ b/java/gradle.java/arch.xml
@@ -110,6 +110,18 @@
in action's context Lookup. See <a href="@TOP@/org/netbeans/modules/gradle/java/api/ProjectActions.html#TOKEN_JAVAEXEC_ARGS">ProjectActions.TOKEN_JAVAEXEC_ARGS</a>
for more details.
</api>
+ <api category="stable" group="property" name="javaExec.runWorkingDir" type="export">
+ {@code ${javaExec.runWorkingDir}} token is replaced in action mappings with working directory retrieved
+ from <a href="@org-netbeans-modules-extexecution-base@/org/netbeans/api/extexecution/base/ExplicitProcessParameters.html">ExplicitProcessParameters</a>
+ in action's context Lookup. See <a href="@TOP@/org/netbeans/modules/gradle/java/api/ProjectActions.html#TOKEN_JAVAEXEC_CWD">ProjectActions.TOKEN_JAVAEXEC_CWD</a>
+ for more details.
+ </api>
+ <api category="stable" group="property" name="javaExec.runEnvironment" type="export">
+ {@code ${javaExec.runEnvironment}} token is replaced in action mappings with environment variables retrieved
+ from <a href="@org-netbeans-modules-extexecution-base@/org/netbeans/api/extexecution/base/ExplicitProcessParameters.html">ExplicitProcessParameters</a>
+ in action's context Lookup. See <a href="@TOP@/org/netbeans/modules/gradle/java/api/ProjectActions.html#TOKEN_JAVAEXEC_ENV">ProjectActions.TOKEN_JAVAEXEC_ENV</a>
+ for more details.
+ </api>
</answer>
<answer id="resources-file">
diff --git a/java/gradle.java/nbproject/project.xml b/java/gradle.java/nbproject/project.xml
index b8ba6b9..30877a7 100644
--- a/java/gradle.java/nbproject/project.xml
+++ b/java/gradle.java/nbproject/project.xml
@@ -31,7 +31,7 @@
<compile-dependency/>
<run-dependency>
<release-version>2</release-version>
- <specification-version>2.14</specification-version>
+ <specification-version>2.21</specification-version>
</run-dependency>
</dependency>
<dependency>
@@ -110,7 +110,7 @@
<compile-dependency/>
<run-dependency>
<release-version>2</release-version>
- <specification-version>1.18</specification-version>
+ <specification-version>1.20</specification-version>
</run-dependency>
</dependency>
<dependency>
diff --git a/java/gradle.java/src/org/netbeans/modules/gradle/java/action-mapping.xml b/java/gradle.java/src/org/netbeans/modules/gradle/java/action-mapping.xml
index 77de6e4..6a3b210 100644
--- a/java/gradle.java/src/org/netbeans/modules/gradle/java/action-mapping.xml
+++ b/java/gradle.java/src/org/netbeans/modules/gradle/java/action-mapping.xml
@@ -50,21 +50,21 @@
</action>
<action name="run.single">
- <args>-PrunClassName=${selectedClass} runSingle ${javaExec.jvmArgs} ${javaExec.args}</args>
+ <args>-PrunClassName=${selectedClass} ${javaExec.workingDir} ${javaExec.environment} runSingle --stacktrace ${javaExec.jvmArgs} ${javaExec.args}</args>
</action>
<action name="debug.single">
- <args>-PrunClassName=${selectedClass} runSingle --debug-jvm ${javaExec.jvmArgs} ${javaExec.args}</args>
+ <args>-PrunClassName=${selectedClass} ${javaExec.workingDir} ${javaExec.environment} runSingle --stacktrace --debug-jvm ${javaExec.jvmArgs} ${javaExec.args}</args>
</action>
</apply-for>
<apply-for plugins="application">
<action name="run">
- <args>run ${javaExec.jvmArgs} ${javaExec.args}</args>
+ <args>${javaExec.workingDir} ${javaExec.environment} run ${javaExec.jvmArgs} ${javaExec.args}</args>
</action>
<action name="debug">
- <args>run --debug-jvm ${javaExec.jvmArgs} ${javaExec.args}</args>
+ <args>${javaExec.workingDir} ${javaExec.environment} run --debug-jvm ${javaExec.jvmArgs} ${javaExec.args}</args>
</action>
</apply-for>
@@ -88,7 +88,7 @@
<actions>
<apply-for plugins="java">
<action name="run.single">
- <args>-PrunClassName=${selectedClass} runSingle --continuous ${javaExec.jvmArgs} ${javaExec.args}</args>
+ <args>-PrunClassName=${selectedClass} ${javaExec.workingDir} ${javaExec.environment} runSingle --continuous ${javaExec.jvmArgs} ${javaExec.args}</args>
</action>
<action name="test.single">
<args>cleanTest test --tests "${selectedClass}" --continuous</args>
@@ -111,7 +111,7 @@
</apply-for>
<apply-for plugins="application">
<action name="run">
- <args>run --continuous ${javaExec.jvmArgs} ${javaExec.args}</args>
+ <args>${javaExec.workingDir} ${javaExec.environment} run --continuous ${javaExec.jvmArgs} ${javaExec.args}</args>
</action>
<action name="debug">
<args></args>
diff --git a/java/gradle.java/src/org/netbeans/modules/gradle/java/api/ProjectActions.java b/java/gradle.java/src/org/netbeans/modules/gradle/java/api/ProjectActions.java
index a382e74..b4cafee 100644
--- a/java/gradle.java/src/org/netbeans/modules/gradle/java/api/ProjectActions.java
+++ b/java/gradle.java/src/org/netbeans/modules/gradle/java/api/ProjectActions.java
@@ -41,15 +41,34 @@ public final class ProjectActions {
* {@codesnippet JavaExecTokenProviderTest#testExamplePassJvmAndArguments}
* </div>
*/
- public static String TOKEN_JAVAEXEC_JVMARGS = "javaExec.jvmArgs";
+ public static String TOKEN_JAVAEXEC_JVMARGS = "javaExec.jvmArgs"; // NOI18N
/**
* Replaceable token for program parameters as a commandline option. Generates --args <i><parameter-list></i>, if the extra parameters are present, otherwise
* generates an empty String. See {@link #TOKEN_JAVAEXEC_JVMARGS} for code examples.
* @since 1.9
*/
- public static String TOKEN_JAVAEXEC_ARGS = "javaExec.args";
+ public static String TOKEN_JAVAEXEC_ARGS = "javaExec.args"; // NOI18N
+ /**
+ * Replaceable token for program working directory. Generates project property for NB Tooling Gradle plugin, which is used in <code>action-mapping.xml</code>
+ * and can be customized by the user. This feature cooperates with NetBeans Tooling Gradle plugin provided by org.netbeans.gradle module.
+ * The Gradle Java project support consumes {@link ExplicitProcessParameters} from the action's context Lookup, and populates the replaceable token mapping
+ * from {@link ExplicitProcessParameters#getWorkingDirectory()}.
+ *
+ * @since 1.15
+ */
+ public static String TOKEN_JAVAEXEC_CWD = "javaExec.workingDir"; // NOI18N
+
+ /**
+ * Replaceable token for program environment variables. Generates project property for NB Tooling Gradle plugin, which is used in <code>action-mapping.xml</code>
+ * and can be customized by the user. This feature cooperates with NetBeans Tooling Gradle plugin provided by org.netbeans.gradle module.
+ * The Gradle Java project support consumes {@link ExplicitProcessParameters} from the action's context Lookup, and populates the replaceable token mapping
+ * from {@link ExplicitProcessParameters#getEnvironmentVariables()}.
+ *
+ * @since 1.15
+ */
+ public static String TOKEN_JAVAEXEC_ENV = "javaExec.environment"; // NOI18N
private ProjectActions() {}
}
diff --git a/java/gradle.java/src/org/netbeans/modules/gradle/java/execute/JavaExecTokenProvider.java b/java/gradle.java/src/org/netbeans/modules/gradle/java/execute/JavaExecTokenProvider.java
index f477056..f0096e1 100644
--- a/java/gradle.java/src/org/netbeans/modules/gradle/java/execute/JavaExecTokenProvider.java
+++ b/java/gradle.java/src/org/netbeans/modules/gradle/java/execute/JavaExecTokenProvider.java
@@ -64,6 +64,20 @@ public class JavaExecTokenProvider implements ReplaceTokenProvider {
public static String TOKEN_JAVAEXEC_ARGS = ProjectActions.TOKEN_JAVAEXEC_ARGS;
/**
+ * Replaceable token for working directory project property. Generates project property for NB Tooling Gradle plugin, if the working directory is present, otherwise
+ * generates an empty String.
+ * @see #TOKEN_JAVA_CWD
+ */
+ public static String TOKEN_JAVAEXEC_CWD = ProjectActions.TOKEN_JAVAEXEC_CWD;
+
+ /**
+ * Replaceable token for environment variables project property. Generates project property for NB Tooling Gradle plugin, if the environment variables are present,
+ * otherwise generates an empty String.
+ * @see #TOKEN_JAVA_ENV
+ */
+ public static String TOKEN_JAVAEXEC_ENV = ProjectActions.TOKEN_JAVAEXEC_ENV;
+
+ /**
* Replaceable token for JVM arguments. Generates escaped / quoted arguments as a single String.
*/
public static String TOKEN_JAVA_ARGS = "java.args"; // NOI18N
@@ -73,7 +87,19 @@ public class JavaExecTokenProvider implements ReplaceTokenProvider {
* a space-delimited String.
*/
public static String TOKEN_JAVA_JVMARGS = "java.jvmArgs"; // NOI18N
-
+
+ /**
+ * Replaceable token for application working directory.
+ */
+ public static String TOKEN_JAVA_CWD = "java.workingDir"; // NOI18N
+
+ /**
+ * Replaceable token for environment variables. Generates escaped / quoted variable assignments as a single String.
+ * Expression <code>VAR_NAME=VAR_VALUE</code> sets environment variable of name <code>VAR_NAME</code> to value
+ * <code>VAR_VALUE</code>, expression <code>!VAR_NAME</code> unsets environment variable <code>VAR_NAME</code>.
+ */
+ public static String TOKEN_JAVA_ENV = "java.environment"; // NOI18N
+
private static final Set<String> TOKENS = Collections.unmodifiableSet(new HashSet<>(Arrays.asList(
TOKEN_JAVAEXEC_ARGS, TOKEN_JAVAEXEC_JVMARGS,
TOKEN_JAVA_ARGS, TOKEN_JAVA_JVMARGS
@@ -144,8 +170,12 @@ public class JavaExecTokenProvider implements ReplaceTokenProvider {
Map<String, String> result = new HashMap<>();
result.put(TOKEN_JAVAEXEC_ARGS, ""); // NOI18N
result.put(TOKEN_JAVAEXEC_JVMARGS, ""); // NOI18N
+ result.put(TOKEN_JAVAEXEC_CWD, ""); // NOI18N
+ result.put(TOKEN_JAVAEXEC_ENV, ""); // NOI18N
result.put(TOKEN_JAVA_ARGS, ""); // NOI18N
result.put(TOKEN_JAVA_JVMARGS, ""); // NOI18N
+ result.put(TOKEN_JAVA_CWD, ""); // NOI18N
+ result.put(TOKEN_JAVA_ENV, ""); // NOI18N
if (extraArgs.isEmpty() && contextParams.isEmpty()) {
return result;
}
@@ -178,6 +208,32 @@ public class JavaExecTokenProvider implements ReplaceTokenProvider {
result.put(TOKEN_JAVA_ARGS, args);
result.put(TOKEN_JAVAEXEC_ARGS, "--args " + prop); // NOI18N
}
+ if (changedParams.getWorkingDirectory() != null) {
+ String wd = Utilities.escapeParameters(new String[] {
+ changedParams.getWorkingDirectory().getAbsolutePath()
+ });
+ result.put(TOKEN_JAVA_CWD, wd);
+ String prop = Utilities.escapeParameters(new String[] {
+ "-PrunWorkingDir=" + wd // NOI18N
+ });
+ result.put(TOKEN_JAVAEXEC_CWD, prop);
+ }
+ if (!changedParams.getEnvironmentVariables().isEmpty()) {
+ List<String> envParams = new ArrayList<>(changedParams.getEnvironmentVariables().size());
+ for (Map.Entry<String, String> entry : changedParams.getEnvironmentVariables().entrySet()) {
+ if (entry.getValue() != null) {
+ envParams.add(entry.getKey() + "=" + entry.getValue());
+ } else {
+ envParams.add("!" + entry.getKey());
+ }
+ }
+ String env = Utilities.escapeParameters(envParams.toArray(new String[envParams.size()]));
+ result.put(TOKEN_JAVA_ENV, env);
+ String prop = Utilities.escapeParameters(new String[] {
+ "-PrunEnvironment=" + env // NOI18N
+ });
+ result.put(TOKEN_JAVAEXEC_ENV, prop);
+ }
return result;
}
}
diff --git a/java/gradle.java/test/unit/src/org/netbeans/modules/gradle/java/execute/JavaExecTokenProviderTest.java b/java/gradle.java/test/unit/src/org/netbeans/modules/gradle/java/execute/JavaExecTokenProviderTest.java
index 50943f0..197924c 100644
--- a/java/gradle.java/test/unit/src/org/netbeans/modules/gradle/java/execute/JavaExecTokenProviderTest.java
+++ b/java/gradle.java/test/unit/src/org/netbeans/modules/gradle/java/execute/JavaExecTokenProviderTest.java
@@ -18,10 +18,12 @@
*/
package org.netbeans.modules.gradle.java.execute;
+import java.io.File;
import java.io.IOException;
import java.util.Arrays;
import java.util.Collections;
import java.util.HashSet;
+import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import java.util.Optional;
@@ -174,6 +176,38 @@ public class JavaExecTokenProviderTest extends NbTestCase {
assertParamList(map.get("java.args"), THREE_PROPS);
}
+ public void testAppCWD() throws Exception {
+ JavaExecTokenProvider jetp = new JavaExecTokenProvider(createSimpleJavaProject());
+
+ File wdf = new File("A test working/directory");
+ String wd = wdf.getAbsolutePath();
+ ExplicitProcessParameters params = ExplicitProcessParameters.builder().
+ workingDirectory(wdf).
+ build();
+
+ Map<String, String> map = jetp.createReplacements(ActionProvider.COMMAND_RUN, Lookups.singleton(params));
+ assertEquals(6, map.size());
+
+ assertParamList(map.get("java.workingDir"), wd);
+ assertParamList(map.get("javaExec.workingDir"), "-PrunWorkingDir=\"" + wd + "\"");
+ }
+
+ public void testAppEnvironment() throws Exception {
+ JavaExecTokenProvider jetp = new JavaExecTokenProvider(createSimpleJavaProject());
+
+ Map<String, String> envVars = new LinkedHashMap<>();
+ envVars.put("TestVar1", "Env Value 1");
+ envVars.put("TestVar2", null);
+ ExplicitProcessParameters params = ExplicitProcessParameters.builder().
+ environmentVariables(envVars).
+ build();
+
+ Map<String, String> map = jetp.createReplacements(ActionProvider.COMMAND_RUN, Lookups.singleton(params));
+ assertEquals(6, map.size());
+
+ assertParamList(map.get("java.environment"), new String[]{"TestVar1=Env Value 1", "!TestVar2"});
+ assertParamList(map.get("javaExec.environment"), "-PrunEnvironment=\"TestVar1=Env Value 1\" !TestVar2");
+ }
/**
* Note: this code is not actually run, as it would require an entire
diff --git a/java/java.lsp.server/nbproject/project.xml b/java/java.lsp.server/nbproject/project.xml
index ce9ea03..6e882ce 100644
--- a/java/java.lsp.server/nbproject/project.xml
+++ b/java/java.lsp.server/nbproject/project.xml
@@ -206,7 +206,7 @@
<compile-dependency/>
<run-dependency>
<release-version>2</release-version>
- <specification-version>1.17</specification-version>
+ <specification-version>1.20</specification-version>
</run-dependency>
</dependency>
<dependency>
@@ -353,7 +353,7 @@
<compile-dependency/>
<run-dependency>
<release-version>0</release-version>
- <specification-version>0.4</specification-version>
+ <specification-version>0.5</specification-version>
</run-dependency>
</dependency>
<dependency>
diff --git a/java/java.lsp.server/src/org/netbeans/modules/java/lsp/server/debugging/launch/NbLaunchDelegate.java b/java/java.lsp.server/src/org/netbeans/modules/java/lsp/server/debugging/launch/NbLaunchDelegate.java
index ccfcece..a30c6c2 100644
--- a/java/java.lsp.server/src/org/netbeans/modules/java/lsp/server/debugging/launch/NbLaunchDelegate.java
+++ b/java/java.lsp.server/src/org/netbeans/modules/java/lsp/server/debugging/launch/NbLaunchDelegate.java
@@ -29,6 +29,7 @@ import java.util.Collection;
import java.util.Collections;
import java.util.List;
import java.util.Map;
+import java.util.Objects;
import java.util.concurrent.CancellationException;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.ConcurrentHashMap;
@@ -72,6 +73,7 @@ import org.netbeans.modules.java.lsp.server.progress.ProgressOperationListener;
import org.netbeans.modules.java.lsp.server.progress.TestProgressHandler;
import org.netbeans.modules.java.nativeimage.debugger.api.NIDebugRunner;
import org.netbeans.modules.nativeimage.api.debug.NIDebugger;
+import org.netbeans.modules.nativeimage.api.debug.StartDebugParameters;
import org.netbeans.spi.project.ActionProgress;
import org.netbeans.spi.project.ActionProvider;
import org.netbeans.spi.project.ProjectConfiguration;
@@ -173,16 +175,7 @@ public abstract class NbLaunchDelegate {
W writer = new W();
CompletableFuture<Pair<ActionProvider, String>> commandFuture = findTargetWithPossibleRebuild(toRun, singleMethod, debug, testRun, ioContext);
commandFuture.thenAccept((providerAndCommand) -> {
- List<String> args = argsToStringList(launchArguments.get("args"));
- List<String> vmArgs = argsToStringList(launchArguments.get("vmArgs"));
- ExplicitProcessParameters params = ExplicitProcessParameters.empty();
- if (!(args.isEmpty() && vmArgs.isEmpty())) {
- ExplicitProcessParameters.Builder bld = ExplicitProcessParameters.builder();
- bld.launcherArgs(vmArgs);
- bld.args(args);
- bld.replaceArgs(false);
- params = bld.build();
- }
+ ExplicitProcessParameters params = createExplicitProcessParameters(launchArguments);
OperationContext ctx = OperationContext.find(Lookup.getDefault());
ctx.addProgressOperationListener(null, new ProgressOperationListener() {
@Override
@@ -320,6 +313,7 @@ public abstract class NbLaunchDelegate {
.showSuspended(true)
.frontWindowOnError(true)
.controllable(true);
+ ExplicitProcessParameters params = createExplicitProcessParameters(launchArguments);
Lookup launchCtx = new ProxyLookup(
Lookups.fixed(ioContext, progress),
Lookup.getDefault()
@@ -335,7 +329,7 @@ public abstract class NbLaunchDelegate {
});
Lookups.executeWith(execLookup, () -> {
String miDebugger = (String) launchArguments.get("miDebugger");
- startNativeDebug(nativeImageFile, args, miDebugger, context, ed, launchFuture, debugProgress);
+ startNativeDebug(nativeImageFile, args, miDebugger, context, ed, Lookups.fixed(params), launchFuture, debugProgress);
});
});
} else {
@@ -344,19 +338,64 @@ public abstract class NbLaunchDelegate {
notifyFinished(context, exitCode != null && exitCode == 0);
});
Lookups.executeWith(execLookup, () -> {
- execNative(nativeImageFile, args, context, ed, launchFuture);
+ execNative(nativeImageFile, args, context, ed, params, launchFuture);
});
}
}
return launchFuture;
}
- private static void execNative(File nativeImageFile, List<String> args, DebugAdapterContext context, ExecutionDescriptor executionDescriptor, CompletableFuture<Void> launchFuture) {
+ private static ExplicitProcessParameters createExplicitProcessParameters(Map<String, Object> launchArguments) {
+ List<String> args = argsToStringList(launchArguments.get("args"));
+ List<String> vmArgs = argsToStringList(launchArguments.get("vmArgs"));
+ String cwd = Objects.toString(launchArguments.get("cwd"), null);
+ Object envObj = launchArguments.get("env");
+ Map<String, String> env = envObj != null ? (Map<String, String>) envObj : Collections.emptyMap();
+ ExplicitProcessParameters.Builder bld = ExplicitProcessParameters.builder();
+ if (!args.isEmpty()) {
+ bld.launcherArgs(vmArgs);
+ }
+ if (!vmArgs.isEmpty()) {
+ bld.args(args);
+ }
+ bld.replaceArgs(false);
+ if (cwd != null) {
+ bld.workingDirectory(new File(cwd));
+ }
+ if (!env.isEmpty()) {
+ bld.environmentVariables(env);
+ }
+ ExplicitProcessParameters params = bld.build();
+ return params;
+ }
+
+ private static void execNative(File nativeImageFile, List<String> args,
+ DebugAdapterContext context,
+ ExecutionDescriptor executionDescriptor,
+ ExplicitProcessParameters params,
+ CompletableFuture<Void> launchFuture) {
ExecutionService.newService(() -> {
launchFuture.complete(null);
- List<String> command = args.isEmpty() ? Collections.singletonList(nativeImageFile.getAbsolutePath()) : join(nativeImageFile.getAbsolutePath(), args);
+ List<String> command = join(nativeImageFile.getAbsolutePath(), args);
try {
- return new ProcessBuilder(command).start();
+ ProcessBuilder pb = new ProcessBuilder(command);
+ File workingDirectory = params.getWorkingDirectory();
+ if (workingDirectory != null) {
+ pb.directory(workingDirectory);
+ }
+ if (!params.getEnvironmentVariables().isEmpty()) {
+ Map<String, String> environment = pb.environment();
+ for (Map.Entry<String, String> entry : params.getEnvironmentVariables().entrySet()) {
+ String env = entry.getKey();
+ String val = entry.getValue();
+ if (val != null) {
+ environment.put(env, val);
+ } else {
+ environment.remove(env);
+ }
+ }
+ }
+ return pb.start();
} catch (IOException ex) {
ErrorUtilities.completeExceptionally(launchFuture,
"Failed to run debuggee native image: " + ex.getLocalizedMessage(),
@@ -367,18 +406,32 @@ public abstract class NbLaunchDelegate {
}
private static List<String> join(String first, List<String> next) {
+ if (next.isEmpty()) {
+ return Collections.singletonList(first);
+ }
List<String> joined = new ArrayList<>(next.size() + 1);
joined.add(first);
joined.addAll(next);
return joined;
}
- private static void startNativeDebug(File nativeImageFile, List<String> args, String miDebugger, DebugAdapterContext context, ExecutionDescriptor executionDescriptor, CompletableFuture<Void> launchFuture, ActionProgress debugProgress) {
+ private static void startNativeDebug(File nativeImageFile, List<String> args,
+ String miDebugger, DebugAdapterContext context,
+ ExecutionDescriptor executionDescriptor,
+ Lookup contextLookup,
+ CompletableFuture<Void> launchFuture,
+ ActionProgress debugProgress) {
AtomicReference<NbDebugSession> debugSessionRef = new AtomicReference<>();
CompletableFuture<Void> finished = new CompletableFuture<>();
+ List<String> command = join(nativeImageFile.getAbsolutePath(), args);
+ StartDebugParameters.Builder parametersBuilder = StartDebugParameters.newBuilder(command)
+ .debugger(miDebugger)
+ .executionDescriptor(executionDescriptor)
+ .lookup(contextLookup);
+ StartDebugParameters parameters = parametersBuilder.build();
NIDebugger niDebugger;
try {
- niDebugger = NIDebugRunner.start(nativeImageFile, args, miDebugger, null, null, executionDescriptor, engine -> {
+ niDebugger = NIDebugRunner.start(nativeImageFile, parameters, null, engine -> {
Session session = engine.lookupFirst(null, Session.class);
NbDebugSession debugSession = new NbDebugSession(session);
debugSessionRef.set(debugSession);
@@ -405,7 +458,7 @@ public abstract class NbLaunchDelegate {
}
@NonNull
- private List<String> argsToStringList(Object o) {
+ private static List<String> argsToStringList(Object o) {
if (o == null) {
return Collections.emptyList();
}
diff --git a/java/java.lsp.server/vscode/package.json b/java/java.lsp.server/vscode/package.json
index 92bb6f0..c25d787 100644
--- a/java/java.lsp.server/vscode/package.json
+++ b/java/java.lsp.server/vscode/package.json
@@ -153,6 +153,21 @@
"description": "Arguments for the Java VM",
"default": null
},
+ "cwd": {
+ "type": [
+ "string",
+ "null"
+ ],
+ "description": "Working directory for the program execution",
+ "default": null
+ },
+ "env": {
+ "type": [
+ "object"
+ ],
+ "description": "Environment variables for the program execution",
+ "default": {}
+ },
"launchConfiguration": {
"type": [
"string",
diff --git a/java/java.nativeimage.debugger/nbproject/project.xml b/java/java.nativeimage.debugger/nbproject/project.xml
index b54add0..5e7fb4b 100644
--- a/java/java.nativeimage.debugger/nbproject/project.xml
+++ b/java/java.nativeimage.debugger/nbproject/project.xml
@@ -85,7 +85,7 @@
<compile-dependency/>
<run-dependency>
<release-version>0</release-version>
- <specification-version>0.4</specification-version>
+ <specification-version>0.5</specification-version>
</run-dependency>
</dependency>
<dependency>
diff --git a/java/java.nativeimage.debugger/src/org/netbeans/modules/java/nativeimage/debugger/actions/NIAttachCustomizer.java b/java/java.nativeimage.debugger/src/org/netbeans/modules/java/nativeimage/debugger/actions/NIAttachCustomizer.java
index 6cbd10c..0b5eb01 100644
--- a/java/java.nativeimage.debugger/src/org/netbeans/modules/java/nativeimage/debugger/actions/NIAttachCustomizer.java
+++ b/java/java.nativeimage.debugger/src/org/netbeans/modules/java/nativeimage/debugger/actions/NIAttachCustomizer.java
@@ -39,6 +39,7 @@ import org.netbeans.api.project.FileOwnerQuery;
import org.netbeans.api.project.Project;
import org.netbeans.modules.java.nativeimage.debugger.actions.Processes.ProcessInfo;
import org.netbeans.modules.java.nativeimage.debugger.api.NIDebugRunner;
+import org.netbeans.modules.nativeimage.api.debug.StartDebugParameters;
import org.netbeans.spi.debugger.ui.Controller;
import static org.netbeans.spi.debugger.ui.Controller.PROP_VALID;
import org.netbeans.spi.debugger.ui.PersistentController;
diff --git a/java/java.nativeimage.debugger/src/org/netbeans/modules/java/nativeimage/debugger/api/NIDebugRunner.java b/java/java.nativeimage.debugger/src/org/netbeans/modules/java/nativeimage/debugger/api/NIDebugRunner.java
index 0db96e4..a1eee90 100644
--- a/java/java.nativeimage.debugger/src/org/netbeans/modules/java/nativeimage/debugger/api/NIDebugRunner.java
+++ b/java/java.nativeimage.debugger/src/org/netbeans/modules/java/nativeimage/debugger/api/NIDebugRunner.java
@@ -32,6 +32,7 @@ import org.netbeans.modules.nativeimage.api.debug.NIDebugger;
import org.netbeans.modules.java.nativeimage.debugger.breakpoints.JPDABreakpointsHandler;
import org.netbeans.modules.java.nativeimage.debugger.displayer.JavaFrameDisplayer;
import org.netbeans.modules.java.nativeimage.debugger.displayer.JavaVariablesDisplayer;
+import org.netbeans.modules.nativeimage.api.debug.StartDebugParameters;
import static org.netbeans.spi.project.ActionProvider.COMMAND_DEBUG;
import org.openide.DialogDisplayer;
@@ -61,7 +62,9 @@ public final class NIDebugRunner {
* @param startedEngine consumer of the started {@link DebuggerEngine}.
* @return an instance of {@link NIDebugger}.
* @throws IllegalStateException when the native debugger is not available.
+ * @deprecated Use {@link #start(File, StartDebugParameters, Project, Consumer)} instead.
*/
+ @Deprecated
public static NIDebugger start(File niFile, List<String> arguments, String debuggerCommand, Project project, String displayName, ExecutionDescriptor executionDescriptor, Consumer<DebuggerEngine> startedEngine) throws IllegalStateException {
JavaVariablesDisplayer variablesDisplayer = new JavaVariablesDisplayer();
JavaFrameDisplayer frameDisplayer = new JavaFrameDisplayer(project);
@@ -91,6 +94,28 @@ public final class NIDebugRunner {
return debugger;
}
+ public static NIDebugger start(File niFile, StartDebugParameters debugParameters, Project project, Consumer<DebuggerEngine> startedEngine) throws IllegalStateException {
+ JavaVariablesDisplayer variablesDisplayer = new JavaVariablesDisplayer();
+ JavaFrameDisplayer frameDisplayer = new JavaFrameDisplayer(project);
+ NIDebugger debugger = NIDebugger.newBuilder()
+ .frameDisplayer(frameDisplayer)
+ .variablesDisplayer(variablesDisplayer)
+ .build();
+ variablesDisplayer.setDebugger(debugger);
+ JPDABreakpointsHandler breakpointsHandler = new JPDABreakpointsHandler(niFile, debugger);
+ DialogDisplayer displayer = DialogDisplayer.getDefault(); // The launcher might provide a special displayer in the lookup. This is why we grab it eagerly.
+ debugger.start(debugParameters,
+ (engine) -> {
+ if (startedEngine != null) {
+ startedEngine.accept(engine);
+ }
+ }).thenRun(() -> {
+ breakpointsHandler.dispose();
+ });
+ checkVersion(debugger.getVersion(), displayer);
+ return debugger;
+ }
+
/**
* Attach debugger to a Native Image.
*
diff --git a/java/maven/nbproject/project.xml b/java/maven/nbproject/project.xml
index 9d105e2..f8fb496 100644
--- a/java/maven/nbproject/project.xml
+++ b/java/maven/nbproject/project.xml
@@ -140,7 +140,7 @@
<compile-dependency/>
<run-dependency>
<release-version>2</release-version>
- <specification-version>1.17</specification-version>
+ <specification-version>1.20</specification-version>
</run-dependency>
</dependency>
<dependency>
diff --git a/java/maven/src/org/netbeans/modules/maven/debug/DebuggerChecker.java b/java/maven/src/org/netbeans/modules/maven/debug/DebuggerChecker.java
index 974f98e..8df8831 100644
--- a/java/maven/src/org/netbeans/modules/maven/debug/DebuggerChecker.java
+++ b/java/maven/src/org/netbeans/modules/maven/debug/DebuggerChecker.java
@@ -248,25 +248,35 @@ public class DebuggerChecker implements LateBoundPrerequisitesChecker, Execution
}
String val = start.execute(context.getInputOutput());
for (Map.Entry<String,String> entry : NbCollections.checkedMapByFilter(config.getProperties(), String.class, String.class, true).entrySet()) {
- StringBuilder buf = new StringBuilder(entry.getValue());
+ String value = entry.getValue();
+ StringBuilder buf = null;
String replaceItem = "${jpda.address}"; //NOI18N
- int index = buf.indexOf(replaceItem);
+ int index = value.indexOf(replaceItem);
while (index > -1) {
String newItem = val;
newItem = newItem == null ? "" : newItem; //NOI18N
+ if (buf == null) {
+ buf = new StringBuilder(value);
+ }
buf.replace(index, index + replaceItem.length(), newItem);
index = buf.indexOf(replaceItem);
}
// debug must properly update debug port in runconfig...
if(entry.getKey().equals(MAVENSUREFIREDEBUG)) {
String address = "address="; //NOI18N
- index = buf.indexOf(address);
+ index = value.indexOf(address);
if(index > -1) {
+ if (buf == null) {
+ buf = new StringBuilder(value);
+ }
buf.replace(index + 8, buf.length(), val);
}
}
+ if (buf != null) { // Change the value when necessary only, to keep it's identity otherwise.
+ value = buf.toString();
+ }
// System.out.println("setting property=" + key + "=" + buf.toString());
- config.setProperty(entry.getKey(), buf.toString());
+ config.setProperty(entry.getKey(), value);
}
config.setProperty("jpda.address", val); //NOI18N
} catch (Throwable th) {
diff --git a/java/maven/src/org/netbeans/modules/maven/execute/DefaultReplaceTokenProvider.java b/java/maven/src/org/netbeans/modules/maven/execute/DefaultReplaceTokenProvider.java
index 45027bb..18591d8 100644
--- a/java/maven/src/org/netbeans/modules/maven/execute/DefaultReplaceTokenProvider.java
+++ b/java/maven/src/org/netbeans/modules/maven/execute/DefaultReplaceTokenProvider.java
@@ -42,6 +42,7 @@ import org.netbeans.modules.maven.NbMavenProjectImpl;
import org.netbeans.modules.maven.api.NbMavenProject;
import org.netbeans.modules.maven.classpath.MavenSourcesImpl;
import org.netbeans.modules.maven.configurations.M2ConfigProvider;
+import org.netbeans.modules.maven.runjar.MavenExecuteUtils;
import org.netbeans.modules.maven.spi.actions.ActionConvertor;
import org.netbeans.modules.maven.spi.actions.ReplaceTokenProvider;
import org.netbeans.spi.project.ActionProvider;
diff --git a/java/maven/src/org/netbeans/modules/maven/execute/MavenCommandLineExecutor.java b/java/maven/src/org/netbeans/modules/maven/execute/MavenCommandLineExecutor.java
index d303f7f..471ac41 100644
--- a/java/maven/src/org/netbeans/modules/maven/execute/MavenCommandLineExecutor.java
+++ b/java/maven/src/org/netbeans/modules/maven/execute/MavenCommandLineExecutor.java
@@ -37,6 +37,7 @@ import java.util.LinkedHashMap;
import java.util.List;
import java.util.Locale;
import java.util.Map;
+import java.util.TreeMap;
import java.util.UUID;
import java.util.function.Consumer;
import java.util.logging.Level;
@@ -79,6 +80,7 @@ import org.netbeans.modules.maven.execute.cmd.ShellConstructor;
import org.netbeans.modules.maven.indexer.api.RepositoryIndexer;
import org.netbeans.modules.maven.indexer.api.RepositoryPreferences;
import org.netbeans.modules.maven.options.MavenSettings;
+import org.netbeans.modules.maven.runjar.MavenExecuteUtils;
import org.netbeans.spi.project.ui.support.BuildExecutionSupport;
import org.openide.LifecycleManager;
import org.openide.awt.HtmlBrowser;
@@ -123,9 +125,9 @@ import org.openide.windows.OutputListener;
* @author Svata Dedic (svatopluk.dedic@gmail.com)
*/
public class MavenCommandLineExecutor extends AbstractMavenExecutor {
- static final String ENV_PREFIX = "Env."; //NOI18N
+ static final String ENV_PREFIX = MavenExecuteUtils.ENV_PREFIX;
static final String INTERNAL_PREFIX = "NbIde."; //NOI18N
- static final String ENV_JAVAHOME = "Env.JAVA_HOME"; //NOI18N
+ static final String ENV_JAVAHOME = ENV_PREFIX + "JAVA_HOME"; //NOI18N
private static final String KEY_UUID = "NB_EXEC_MAVEN_PROCESS_UUID"; //NOI18N
@@ -363,7 +365,7 @@ public class MavenCommandLineExecutor extends AbstractMavenExecutor {
out.waitFor();
return executionresult;
}
-
+
private void kill(Process prcs, String uuid) {
Map<String, String> env = new HashMap<String, String>();
env.put(KEY_UUID, uuid);
@@ -674,7 +676,11 @@ public class MavenCommandLineExecutor extends AbstractMavenExecutor {
continue;// #191374: would prevent bin/mvn from using selected installation
}
// TODO: do we really put *all* the env vars there? maybe filter, M2_HOME and JDK_HOME?
- builder.environment().put(env, val);
+ if (MavenExecuteUtils.isEnvRemovedValue(val)) {
+ builder.environment().remove(env);
+ } else {
+ builder.environment().put(env, val);
+ }
if (!env.equals(CosChecker.NETBEANS_PROJECT_MAPPINGS)
&& !env.equals(NETBEANS_MAVEN_COMMAND_LINE)) { //don't show to user
display.append(Utilities.escapeParameters(new String[] {env + "=" + val})).append(' '); // NOI18N
diff --git a/java/maven/src/org/netbeans/modules/maven/runjar/LaunchArgPrereqsChecker.java b/java/maven/src/org/netbeans/modules/maven/runjar/LaunchArgPrereqsChecker.java
index a5438e0..234115b 100644
--- a/java/maven/src/org/netbeans/modules/maven/runjar/LaunchArgPrereqsChecker.java
+++ b/java/maven/src/org/netbeans/modules/maven/runjar/LaunchArgPrereqsChecker.java
@@ -18,6 +18,7 @@
*/
package org.netbeans.modules.maven.runjar;
+import java.io.File;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
@@ -122,6 +123,20 @@ public class LaunchArgPrereqsChecker implements LateBoundPrerequisitesChecker {
config.setProperty(MavenExecuteUtils.RUN_APP_PARAMS,
MavenExecuteUtils.joinParameters(appArgsValue));
}
+ File workingDirectory = injectParams.getWorkingDirectory();
+ if (workingDirectory != null) {
+ config.setProperty(MavenExecuteUtils.RUN_WORKDIR,
+ workingDirectory.getAbsolutePath());
+ }
+ Map<String, String> environmentVariables = injectParams.getEnvironmentVariables();
+ for (Map.Entry<String, String> env : environmentVariables.entrySet()) {
+ String value = env.getValue();
+ if (value == null) {
+ // The environment variable is to be removed when the value is null
+ value = MavenExecuteUtils.ENV_REMOVED;
+ }
+ config.setProperty(MavenExecuteUtils.ENV_PREFIX + env.getKey(), value);
+ }
return true;
}
}
diff --git a/java/maven/src/org/netbeans/modules/maven/runjar/MavenExecuteUtils.java b/java/maven/src/org/netbeans/modules/maven/runjar/MavenExecuteUtils.java
index 4367fa4..6c747af 100644
--- a/java/maven/src/org/netbeans/modules/maven/runjar/MavenExecuteUtils.java
+++ b/java/maven/src/org/netbeans/modules/maven/runjar/MavenExecuteUtils.java
@@ -84,6 +84,9 @@ public final class MavenExecuteUtils {
static final String DEFAULT_DEBUG_PARAMS = "-agentlib:jdwp=transport=dt_socket,server=n,address=${jpda.address}"; //NOI18N
static final String DEFAULT_EXEC_ARGS_CLASSPATH2 = "${exec.vmArgs} -classpath %classpath ${exec.mainClass} ${exec.appArgs}"; // NOI18N
+ public static final String ENV_PREFIX = "Env."; // NOI18N
+ public static final String ENV_REMOVED = new String("null"); // A special instance for env vars to be removed. // NOI18N
+
/**
* ID of the 'profile' action.
*/
@@ -513,6 +516,10 @@ public final class MavenExecuteUtils {
return null;
}
+ public static boolean isEnvRemovedValue(String value) {
+ return value == ENV_REMOVED; // It's crutial to test the instance identity
+ }
+
/**
* Creates a helper to edit the mapping instances. Individual settings can be
* inspected by getters and changed by setters on the helper, changes can be then
diff --git a/java/maven/src/org/netbeans/modules/maven/runjar/RunJarStartupArgs.java b/java/maven/src/org/netbeans/modules/maven/runjar/RunJarStartupArgs.java
index 811c713..3846bfd 100644
--- a/java/maven/src/org/netbeans/modules/maven/runjar/RunJarStartupArgs.java
+++ b/java/maven/src/org/netbeans/modules/maven/runjar/RunJarStartupArgs.java
@@ -19,13 +19,13 @@
package org.netbeans.modules.maven.runjar;
+import java.io.File;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.List;
import java.util.Map;
-import java.util.stream.Stream;
-import org.apache.maven.wagon.Streams;
+
import org.netbeans.api.extexecution.base.ExplicitProcessParameters;
import org.netbeans.api.extexecution.startup.StartupExtender;
import org.netbeans.api.project.Project;
@@ -206,6 +206,20 @@ public class RunJarStartupArgs implements LateBoundPrerequisitesChecker {
config.setProperty(MavenExecuteUtils.RUN_APP_PARAMS,
MavenExecuteUtils.joinParameters(appArgsValue));
}
+ File workingDirectory = injectParams.getWorkingDirectory();
+ if (workingDirectory != null) {
+ config.setProperty(MavenExecuteUtils.RUN_WORKDIR,
+ workingDirectory.getAbsolutePath());
+ }
+ Map<String, String> environmentVariables = injectParams.getEnvironmentVariables();
+ for (Map.Entry<String, String> env : environmentVariables.entrySet()) {
+ String value = env.getValue();
+ if (value == null) {
+ // The environment variable is to be removed when the value is null
+ value = MavenExecuteUtils.ENV_REMOVED;
+ }
+ config.setProperty(MavenExecuteUtils.ENV_PREFIX + env.getKey(), value);
+ }
if ("test".equals(props.get("exec.classpathScope"))) {
isTestScope = true;
diff --git a/java/maven/test/unit/src/org/netbeans/modules/maven/execute/MavenExecutionTestBase.java b/java/maven/test/unit/src/org/netbeans/modules/maven/execute/MavenExecutionTestBase.java
index 195a392..7543fc2 100644
--- a/java/maven/test/unit/src/org/netbeans/modules/maven/execute/MavenExecutionTestBase.java
+++ b/java/maven/test/unit/src/org/netbeans/modules/maven/execute/MavenExecutionTestBase.java
@@ -90,6 +90,7 @@ public class MavenExecutionTestBase extends NbTestCase {
protected String mavenAppArgs = ""; // NOI18N
protected Map<String, String> mavenExecutorDefines = new HashMap<>();
protected Map<String, String> mavenExecutorRawDefines = new HashMap<>();
+ protected Map<String, String> mavenExecutorEnvironment = new HashMap<>();
protected final InstanceContent actionData = new InstanceContent();
protected final Lookup actionLookup = new AbstractLookup(actionData);
@@ -240,7 +241,7 @@ public class MavenExecutionTestBase extends NbTestCase {
* Evaluates property references in arguments. Uses Maven evaluator for the project + properties defined in the
* ModelRunConfig (which are passed as -D to the maven executor). Collects all -D defines into {@link #mavenExecutorDefines}
*/
- protected List<String> substituteProperties(List<String> args, Project p, Map<? extends String, ? extends String> properties) {
+ protected List<String> substituteProperties(List<String> args, Map<String, String> environment, Project p, Map<? extends String, ? extends String> properties) {
Map<String, String> props = new HashMap<>(properties);
for (int i = 0; i < args.size(); i++) {
String a = args.get(i);
@@ -261,6 +262,7 @@ public class MavenExecutionTestBase extends NbTestCase {
pE.setValue(interpolate(pE.getValue(), e, props));
}
mavenExecutorDefines = props;
+ mavenExecutorEnvironment = new HashMap<>(environment);
return args;
}
@@ -327,7 +329,7 @@ public class MavenExecutionTestBase extends NbTestCase {
MavenCommandLineExecutor exec = new MavenCommandLineExecutor(cfg, InputOutput.NULL, null) {
@Override
int executeProcess(CommandLineOutputHandler out, ProcessBuilder builder, Consumer<Process> processSetter) throws IOException, InterruptedException {
- List<String> args = substituteProperties(builder.command(), project, cfg.getProperties());
+ List<String> args = substituteProperties(builder.command(), builder.environment(), project, cfg.getProperties());
commandLineAcceptor.accept(args);
return 0;
}
diff --git a/java/maven/test/unit/src/org/netbeans/modules/maven/runjar/ExecutionEnvHelperTest.java b/java/maven/test/unit/src/org/netbeans/modules/maven/runjar/ExecutionEnvHelperTest.java
index 796e2d3..9d53987 100644
--- a/java/maven/test/unit/src/org/netbeans/modules/maven/runjar/ExecutionEnvHelperTest.java
+++ b/java/maven/test/unit/src/org/netbeans/modules/maven/runjar/ExecutionEnvHelperTest.java
@@ -18,6 +18,7 @@
*/
package org.netbeans.modules.maven.runjar;
+import java.io.File;
import org.netbeans.modules.maven.execute.MavenExecutionTestBase;
import java.io.StringReader;
import java.util.Map;
@@ -63,6 +64,10 @@ public abstract class ExecutionEnvHelperTest extends MavenExecutionTestBase {
protected void assertActionCustomVMProperties(String vmArg, String mainClass, String appArg) throws Exception {}
+ protected void assertActionWorkingDir(String workingDir) throws Exception {}
+
+ protected void assertActionEnvVariable(String varName, String varValue) throws Exception {}
+
public static class NetBeans123Config extends ExecutionEnvHelperTest {
public NetBeans123Config(String name) {
@@ -159,6 +164,16 @@ public abstract class ExecutionEnvHelperTest extends MavenExecutionTestBase {
}
@Override
+ protected void assertActionWorkingDir(String workingDir) throws Exception {
+ assertEquals(workingDir, mavenExecutorDefines.get(MavenExecuteUtils.RUN_WORKDIR));
+ }
+
+ @Override
+ protected void assertActionEnvVariable(String varName, String varValue) throws Exception {
+ assertEquals(varValue, mavenExecutorEnvironment.get(varName));
+ }
+
+ @Override
protected String defaultCommandLineArgs() {
return MavenExecuteUtils.DEFAULT_EXEC_ARGS_CLASSPATH2;
}
@@ -463,6 +478,43 @@ public abstract class ExecutionEnvHelperTest extends MavenExecutionTestBase {
assertTrue(mavenVmArgs.contains("-Dbar=foo"));
}
+ /**
+ * Checks that pre-12.3 default actions will inject working directory from Lookup.
+ */
+ public void test123DefaultActionWithCWD() throws Exception {
+ initCustomizedProperties();
+ createNbActions(runP, debugP, profileP);
+ File wd = new File("WorkingDirectory");
+ ExplicitProcessParameters explicit = ExplicitProcessParameters.builder().
+ workingDirectory(wd).build();
+ actionData.add(explicit);
+ createPomWithArguments();
+ final Project project = ProjectManager.getDefault().findProject(pom.getParent());
+ loadActionMappings(project);
+ assertMavenRunAction(project, runMapping, "run", c -> {});
+ assertActionWorkingDir(wd.getAbsolutePath());
+ }
+
+ /**
+ * Checks that pre-12.3 default actions will inject environment variables from Lookup.
+ */
+ public void test123DefaultActionWithEnvVars() throws Exception {
+ initCustomizedProperties();
+ createNbActions(runP, debugP, profileP);
+ ExplicitProcessParameters explicit = ExplicitProcessParameters.builder().
+ environmentVariable("TEST_VAR1", "test value 1").
+ environmentVariable("TEST_VAR2", "test value 2").
+ environmentVariable("PATH", null).build();
+ actionData.add(explicit);
+ createPomWithArguments();
+ final Project project = ProjectManager.getDefault().findProject(pom.getParent());
+ loadActionMappings(project);
+ assertMavenRunAction(project, runMapping, "run", c -> {});
+ assertActionEnvVariable("TEST_VAR1", "test value 1");
+ assertActionEnvVariable("TEST_VAR2", "test value 2");
+ assertActionEnvVariable("PATH", null);
+ }
+
private ExecutionEnvHelper load123ProjectExecutionHelper(boolean defaultProps) throws Exception {
if (defaultProps) {
initDefaultProperties();
---------------------------------------------------------------------
To unsubscribe, e-mail: commits-unsubscribe@netbeans.apache.org
For additional commands, e-mail: commits-help@netbeans.apache.org
For further information about the NetBeans mailing lists, visit:
https://cwiki.apache.org/confluence/display/NETBEANS/Mailing+lists