From 6b101ba7a0c4dd5c321d2af096c01da220c03f43 Mon Sep 17 00:00:00 2001 From: Sergii Zolotarov Date: Thu, 15 Oct 2026 21:46:07 +0200 Subject: [PATCH 1/2] Let a launch override the standard input file of its configuration ProcessConsole took the file supplied to a process's standard input only from the launch configuration (Common tab > Input File). Nothing outside the configuration could change it, so a plugin that wants every launch to read the same input had to edit configurations or write to the process itself, and then mixed its input with a configuration's own file. ProcessConsole now first looks for ATTR_CAPTURE_STDIN_FILE among the attributes of the process's launch and uses the configuration's only if the launch has none. A listener can set it when the launch is added, before any process exists. A launch without a configuration can supply input this way too. Co-Authored-By: Claude Opus 5.5 --- .../tests/console/ProcessConsoleTests.java | 65 +++++++++++++++++++ .../ui/views/console/ProcessConsole.java | 14 ++-- .../eclipse/debug/ui/IDebugUIConstants.java | 7 ++ 3 files changed, 82 insertions(+), 4 deletions(-) diff --git a/debug/org.eclipse.debug.tests/src/org/eclipse/debug/tests/console/ProcessConsoleTests.java b/debug/org.eclipse.debug.tests/src/org/eclipse/debug/tests/console/ProcessConsoleTests.java index 3a6d09bd4e0..a50fc104071 100644 --- a/debug/org.eclipse.debug.tests/src/org/eclipse/debug/tests/console/ProcessConsoleTests.java +++ b/debug/org.eclipse.debug.tests/src/org/eclipse/debug/tests/console/ProcessConsoleTests.java @@ -619,6 +619,71 @@ public void testInputFromFileSendsEof() throws Exception { assertThat(receivedInput).as("received input").isEqualTo(input); } + /** + * Test that a process reads its standard input from a file set on its launch + * when it has no launch configuration. + */ + @Test + public void testInputFromLaunchAttribute() throws Exception { + byte[] input = "from launch\n".getBytes(StandardCharsets.US_ASCII); + String consoleEncoding = StandardCharsets.UTF_8.name(); + + final File inFile = createTmpFile("testinput-launch.txt"); + Files.write(inFile.toPath(), input); + final MockProcess mockProcess = new MockProcess(MockProcess.RUN_FOREVER); + try { + final IProcess process = mockProcess.toRuntimeProcess("inputFromLaunch"); + process.getLaunch().setAttribute(IDebugUIConstants.ATTR_CAPTURE_STDIN_FILE, inFile.getCanonicalPath()); + final ProcessConsole console = new ProcessConsole(process, new ConsoleColorProvider(), consoleEncoding); + try { + console.initialize(); + waitWhile(() -> !mockProcess.isStdinClosed(), () -> "Process stdin was not closed after input file was read."); + } finally { + console.destroy(); + } + } finally { + mockProcess.destroy(); + } + + assertThat(mockProcess.getReceivedInput()).as("received input").isEqualTo(input); + } + + /** + * Test that an input file set on the launch is supplied instead of the input + * file of its launch configuration. + */ + @Test + public void testInputFromLaunchAttributeOverridesConfiguration() throws Exception { + byte[] launchInput = "from launch\n".getBytes(StandardCharsets.US_ASCII); + byte[] configurationInput = "from configuration\n".getBytes(StandardCharsets.US_ASCII); + String consoleEncoding = StandardCharsets.UTF_8.name(); + + final File launchFile = createTmpFile("testinput-launch.txt"); + Files.write(launchFile.toPath(), launchInput); + final File configurationFile = createTmpFile("testinput-configuration.txt"); + Files.write(configurationFile.toPath(), configurationInput); + final MockProcess mockProcess = new MockProcess(MockProcess.RUN_FOREVER); + try { + Map launchConfigAttributes = new HashMap<>(); + launchConfigAttributes.put(DebugPlugin.ATTR_CONSOLE_ENCODING, consoleEncoding); + launchConfigAttributes.put(IDebugUIConstants.ATTR_CAPTURE_STDIN_FILE, configurationFile.getCanonicalPath()); + launchConfigAttributes.put(IDebugUIConstants.ATTR_CAPTURE_IN_CONSOLE, false); + final IProcess process = mockProcess.toRuntimeProcess("inputFromLaunchOverride", launchConfigAttributes); + process.getLaunch().setAttribute(IDebugUIConstants.ATTR_CAPTURE_STDIN_FILE, launchFile.getCanonicalPath()); + final ProcessConsole console = new ProcessConsole(process, new ConsoleColorProvider(), consoleEncoding); + try { + console.initialize(); + waitWhile(() -> !mockProcess.isStdinClosed(), () -> "Process stdin was not closed after input file was read."); + } finally { + console.destroy(); + } + } finally { + mockProcess.destroy(); + } + + assertThat(mockProcess.getReceivedInput()).as("received input").isEqualTo(launchInput); + } + /** * Test that console name updates (elapsed time) only happen for visible * consoles. Hidden consoles should not update their name. When a hidden diff --git a/debug/org.eclipse.debug.ui/ui/org/eclipse/debug/internal/ui/views/console/ProcessConsole.java b/debug/org.eclipse.debug.ui/ui/org/eclipse/debug/internal/ui/views/console/ProcessConsole.java index 7ae8c565346..45273c93792 100644 --- a/debug/org.eclipse.debug.ui/ui/org/eclipse/debug/internal/ui/views/console/ProcessConsole.java +++ b/debug/org.eclipse.debug.ui/ui/org/eclipse/debug/internal/ui/views/console/ProcessConsole.java @@ -183,10 +183,16 @@ public ProcessConsole(IProcess process, IConsoleColorProvider colorProvider, Str ILaunchConfiguration configuration = process.getLaunch().getLaunchConfiguration(); String file = null; boolean append = false; - if (configuration != null) { + // an input file set on the launch overrides the one of its configuration + fStdInFile = process.getLaunch().getAttribute(IDebugUIConstants.ATTR_CAPTURE_STDIN_FILE); + if (configuration != null || fStdInFile != null) { try { - file = configuration.getAttribute(IDebugUIConstants.ATTR_CAPTURE_IN_FILE, (String) null); - fStdInFile = configuration.getAttribute(IDebugUIConstants.ATTR_CAPTURE_STDIN_FILE, (String) null); + if (configuration != null) { + file = configuration.getAttribute(IDebugUIConstants.ATTR_CAPTURE_IN_FILE, (String) null); + if (fStdInFile == null) { + fStdInFile = configuration.getAttribute(IDebugUIConstants.ATTR_CAPTURE_STDIN_FILE, (String) null); + } + } if (file != null || fStdInFile != null) { IStringVariableManager stringVariableManager = VariablesPlugin.getDefault().getStringVariableManager(); if (file != null) { @@ -248,7 +254,7 @@ public ProcessConsole(IProcess process, IConsoleColorProvider colorProvider, Str } fFileOutputStream = outputStream; InputStream inputStream = null; - if (fStdInFile != null && configuration != null) { + if (fStdInFile != null) { String message = null; try { inputStream = new FileInputStream(new File(fStdInFile)); diff --git a/debug/org.eclipse.debug.ui/ui/org/eclipse/debug/ui/IDebugUIConstants.java b/debug/org.eclipse.debug.ui/ui/org/eclipse/debug/ui/IDebugUIConstants.java index c52ff715bca..cdf2de12416 100644 --- a/debug/org.eclipse.debug.ui/ui/org/eclipse/debug/ui/IDebugUIConstants.java +++ b/debug/org.eclipse.debug.ui/ui/org/eclipse/debug/ui/IDebugUIConstants.java @@ -970,6 +970,13 @@ public interface IDebugUIConstants { /** * Launch configuration attribute to specifying a file whose content is supplied * to the launched process input stream. Default value is null. + *

+ * The attribute may also be set on the {@link org.eclipse.debug.core.ILaunch} + * before its processes are created, for example by an + * {@link org.eclipse.debug.core.ILaunchListener} when the launch is added. A + * file set on the launch is supplied instead of the one of its launch + * configuration. + *

* * @since 3.11 */ From b31204aaa6dcb3407ce8b368bbd6292144d53a9a Mon Sep 17 00:00:00 2001 From: Sergii Zolotarov Date: Mon, 9 Nov 2026 22:23:13 +0100 Subject: [PATCH 2/2] Read the append attribute only when a configuration exists The compiler reported a potential null access of the configuration. Read ATTR_APPEND_TO_FILE where the configuration is not null. Co-Authored-By: Claude Opus 5.5 --- .../eclipse/debug/internal/ui/views/console/ProcessConsole.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/debug/org.eclipse.debug.ui/ui/org/eclipse/debug/internal/ui/views/console/ProcessConsole.java b/debug/org.eclipse.debug.ui/ui/org/eclipse/debug/internal/ui/views/console/ProcessConsole.java index 45273c93792..55f129d14c7 100644 --- a/debug/org.eclipse.debug.ui/ui/org/eclipse/debug/internal/ui/views/console/ProcessConsole.java +++ b/debug/org.eclipse.debug.ui/ui/org/eclipse/debug/internal/ui/views/console/ProcessConsole.java @@ -189,6 +189,7 @@ public ProcessConsole(IProcess process, IConsoleColorProvider colorProvider, Str try { if (configuration != null) { file = configuration.getAttribute(IDebugUIConstants.ATTR_CAPTURE_IN_FILE, (String) null); + append = configuration.getAttribute(IDebugUIConstants.ATTR_APPEND_TO_FILE, false); if (fStdInFile == null) { fStdInFile = configuration.getAttribute(IDebugUIConstants.ATTR_CAPTURE_STDIN_FILE, (String) null); } @@ -197,7 +198,6 @@ public ProcessConsole(IProcess process, IConsoleColorProvider colorProvider, Str IStringVariableManager stringVariableManager = VariablesPlugin.getDefault().getStringVariableManager(); if (file != null) { file = stringVariableManager.performStringSubstitution(file); - append = configuration.getAttribute(IDebugUIConstants.ATTR_APPEND_TO_FILE, false); } if (fStdInFile != null) {