Skip to content

Commit 5e4a66b

Browse files
committed
Bug 580178 - Unable to stop build process from launchbar
Switch from the standard Java ProcessBuilder to the CDT CommandLauncher for new style core build projects. The CommandLauncher uses a more sophiscated mechanism for watching the spawned process allowing us to interrupt the process when the user hits the stop button on the launchbar by properly listening to a monitor. The change adds new API to CBuildCongifuration that takes a progress monitor, and changes all the affected build configuration types to use this new API. Change-Id: I0c4225616ad8331c2cea28bcb502028455a8ea71
1 parent 4a95606 commit 5e4a66b

File tree

7 files changed

+72
-137
lines changed

7 files changed

+72
-137
lines changed

build/org.eclipse.cdt.core.autotools.core/src/org/eclipse/cdt/core/autotools/core/AutotoolsBuildConfiguration.java

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -106,7 +106,7 @@ protected void execute(List<String> command, IPath dir, IConsole console, IProgr
106106
try {
107107
// TODO Error parsers
108108
Process process = builder.start();
109-
watchProcess(process, console);
109+
watchProcess(console, monitor);
110110
} catch (IOException e) {
111111
throw new CoreException(Activator.errorStatus("Error executing: " + String.join(" ", command), e)); //$NON-NLS-1$ //$NON-NLS-2$
112112
}
@@ -153,7 +153,7 @@ protected void executeRemote(List<String> command, IPath dir, IConsole console,
153153
Activator.errorStatus("Error executing: " + String.join(" ", command), null)); //$NON-NLS-1$ //$NON-NLS-2$
154154
}
155155

156-
watchProcess(p, new IConsoleParser[] { epm });
156+
watchProcess(new IConsoleParser[] { epm }, monitor);
157157
}
158158

159159
project.refreshLocal(IResource.DEPTH_INFINITE, monitor);

build/org.eclipse.cdt.meson.core/src/org/eclipse/cdt/internal/meson/core/MesonBuildConfiguration.java

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*******************************************************************************
2-
* Copyright (c) 2015, 2018 QNX Software Systems and others.
2+
* Copyright (c) 2015, 2022 QNX Software Systems and others.
33
*
44
* This program and the accompanying materials
55
* are made available under the terms of the Eclipse Public License 2.0
@@ -183,7 +183,7 @@ public IProject[] build(int kind, Map<String, String> args, String[] ninjaEnv, S
183183
return null;
184184
}
185185

186-
watchProcess(p, console);
186+
watchProcess(console, monitor);
187187
}
188188

189189
if (!Files.exists(buildDir.resolve("build.ninja"))) { //$NON-NLS-1$
@@ -238,7 +238,7 @@ public IProject[] build(int kind, Map<String, String> args, String[] ninjaEnv, S
238238
return null;
239239
}
240240

241-
watchProcess(p, new IConsoleParser[] { epm });
241+
watchProcess(new IConsoleParser[] { epm }, monitor);
242242
}
243243

244244
project.refreshLocal(IResource.DEPTH_INFINITE, monitor);
@@ -298,7 +298,7 @@ public void clean(IConsole console, IProgressMonitor monitor) throws CoreExcepti
298298
return;
299299
}
300300

301-
watchProcess(p, console);
301+
watchProcess(console, monitor);
302302
}
303303

304304
outStream.write(String.format(Messages.MesonBuildConfiguration_BuildingComplete, buildDir.toString()));

cmake/org.eclipse.cdt.cmake.core/src/org/eclipse/cdt/cmake/core/internal/CMakeBuildConfiguration.java

Lines changed: 4 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*******************************************************************************
2-
* Copyright (c) 2015, 2016 QNX Software Systems and others.
2+
* Copyright (c) 2015, 2022 QNX Software Systems and others.
33
*
44
* This program and the accompanying materials
55
* are made available under the terms of the Eclipse Public License 2.0
@@ -175,9 +175,6 @@ public IProject[] build(int kind, Map<String, String> args, IConsole console, IP
175175
ParsingConsoleOutputStream errStream = new ParsingConsoleOutputStream(console.getErrorStream(),
176176
errorParser);
177177
IConsole errConsole = new CMakeConsoleWrapper(console, errStream);
178-
// TODO startBuildProcess() calls java.lang.ProcessBuilder.
179-
// Use org.eclipse.cdt.core.ICommandLauncher
180-
// in order to run builds in a container.
181178
Process p = startBuildProcess(command.getArguments(), new IEnvironmentVariable[0], workingDir,
182179
errConsole, monitor);
183180
String arg0 = command.getArguments().get(0);
@@ -190,7 +187,7 @@ public IProject[] build(int kind, Map<String, String> args, IConsole console, IP
190187
}
191188

192189
// check cmake exit status
193-
final int exitValue = watchProcess(p, errConsole);
190+
final int exitValue = watchProcess(errConsole, monitor);
194191
if (exitValue != 0) {
195192
// cmake had errors...
196193
String msg = String.format(Messages.CMakeBuildConfiguration_ExitFailure, arg0, exitValue);
@@ -230,8 +227,6 @@ public IProject[] build(int kind, Map<String, String> args, IConsole console, IP
230227

231228
org.eclipse.core.runtime.Path workingDir = new org.eclipse.core.runtime.Path(
232229
getBuildDirectory().toString());
233-
// TODO startBuildProcess() calls java.lang.ProcessBuilder. Use org.eclipse.cdt.core.ICommandLauncher
234-
// in order to run builds in a container.
235230
// TODO pass envvars from CommandDescriptor once we use ICommandLauncher
236231
Process p = startBuildProcess(command, envVars.toArray(new IEnvironmentVariable[0]), workingDir,
237232
console, monitor);
@@ -241,7 +236,7 @@ public IProject[] build(int kind, Map<String, String> args, IConsole console, IP
241236
}
242237

243238
// check exit status
244-
final int exitValue = watchProcess(p, new IConsoleParser[] { epm });
239+
final int exitValue = watchProcess(new IConsoleParser[] { epm }, monitor);
245240
if (exitValue != 0) {
246241
// had errors...
247242
String msg2 = String.format(Messages.CMakeBuildConfiguration_ExitFailure, command.get(0),
@@ -286,8 +281,6 @@ public void clean(IConsole console, IProgressMonitor monitor) throws CoreExcepti
286281

287282
org.eclipse.core.runtime.Path workingDir = new org.eclipse.core.runtime.Path(
288283
getBuildDirectory().toString());
289-
// TODO startBuildProcess() calls java.lang.ProcessBuilder. Use org.eclipse.cdt.core.ICommandLauncher
290-
// in order to run builds in a container.
291284
Process p = startBuildProcess(command.getArguments(), new IEnvironmentVariable[0], workingDir, console,
292285
monitor);
293286
if (p == null) {
@@ -299,7 +292,7 @@ public void clean(IConsole console, IProgressMonitor monitor) throws CoreExcepti
299292
}
300293

301294
// check exit status
302-
final int exitValue = watchProcess(p, console);
295+
final int exitValue = watchProcess(console, monitor);
303296
if (exitValue != 0) {
304297
// had errors...
305298
String msg = String.format(Messages.CMakeBuildConfiguration_ExitFailure, command.getArguments().get(0),

core/org.eclipse.cdt.core/META-INF/MANIFEST.MF

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@ Manifest-Version: 1.0
22
Bundle-ManifestVersion: 2
33
Bundle-Name: %pluginName
44
Bundle-SymbolicName: org.eclipse.cdt.core; singleton:=true
5-
Bundle-Version: 7.4.200.qualifier
5+
Bundle-Version: 7.5.0.qualifier
66
Bundle-Activator: org.eclipse.cdt.core.CCorePlugin
77
Bundle-Vendor: %providerName
88
Bundle-Localization: plugin

core/org.eclipse.cdt.core/src/org/eclipse/cdt/core/build/CBuildConfiguration.java

Lines changed: 47 additions & 103 deletions
Original file line numberDiff line numberDiff line change
@@ -10,15 +10,10 @@
1010
*******************************************************************************/
1111
package org.eclipse.cdt.core.build;
1212

13-
import java.io.BufferedReader;
1413
import java.io.File;
1514
import java.io.FileReader;
1615
import java.io.FileWriter;
1716
import java.io.IOException;
18-
import java.io.InputStream;
19-
import java.io.InputStreamReader;
20-
import java.io.OutputStream;
21-
import java.io.PrintStream;
2217
import java.net.URI;
2318
import java.nio.file.Files;
2419
import java.nio.file.InvalidPathException;
@@ -33,9 +28,11 @@
3328
import java.util.Map.Entry;
3429
import java.util.Properties;
3530
import java.util.Set;
31+
import java.util.stream.Collectors;
3632

3733
import org.eclipse.cdt.core.CCorePlugin;
3834
import org.eclipse.cdt.core.CommandLauncherManager;
35+
import org.eclipse.cdt.core.ICommandLauncher;
3936
import org.eclipse.cdt.core.IConsoleParser;
4037
import org.eclipse.cdt.core.IConsoleParser2;
4138
import org.eclipse.cdt.core.IMarkerGenerator;
@@ -60,6 +57,8 @@
6057
import org.eclipse.cdt.core.parser.IScannerInfo;
6158
import org.eclipse.cdt.core.parser.IScannerInfoChangeListener;
6259
import org.eclipse.cdt.core.resources.IConsole;
60+
import org.eclipse.cdt.internal.core.BuildRunnerHelper;
61+
import org.eclipse.cdt.internal.core.ConsoleOutputSniffer;
6362
import org.eclipse.cdt.internal.core.build.Messages;
6463
import org.eclipse.cdt.internal.core.model.BinaryRunner;
6564
import org.eclipse.cdt.internal.core.model.CModelManager;
@@ -116,6 +115,8 @@ public abstract class CBuildConfiguration extends PlatformObject implements ICBu
116115
private final Map<IResource, List<IScannerInfoChangeListener>> scannerInfoListeners = new HashMap<>();
117116
private ScannerInfoCache scannerInfoCache;
118117

118+
private ICommandLauncher launcher;
119+
119120
protected CBuildConfiguration(IBuildConfiguration config, String name) throws CoreException {
120121
this.config = config;
121122
this.name = name;
@@ -488,7 +489,8 @@ public Process startBuildProcess(List<String> commands, IEnvironmentVariable[] e
488489
.write(String.format(Messages.CBuildConfiguration_CommandNotFound, commands.get(0)));
489490
return null;
490491
}
491-
commands.set(0, commandPath.toString());
492+
IPath cmd = new org.eclipse.core.runtime.Path(commandPath.toString());
493+
List<String> args = commands.subList(1, commands.size());
492494

493495
// check if includes have been removed/refreshed and scanner info refresh is needed
494496
boolean needRefresh = CommandLauncherManager.getInstance().checkIfIncludesChanged(this);
@@ -497,18 +499,28 @@ public Process startBuildProcess(List<String> commands, IEnvironmentVariable[] e
497499
t.setProperty(NEED_REFRESH, Boolean.valueOf(needRefresh).toString());
498500
}
499501

500-
ProcessBuilder processBuilder = new ProcessBuilder(commands).directory(buildDirectory.toFile());
501-
// Override environment variables
502-
Map<String, String> environment = processBuilder.environment();
502+
// Generate environment block before launching process
503+
launcher = CommandLauncherManager.getInstance().getCommandLauncher(this);
504+
Properties envProps = launcher.getEnvironment();
505+
HashMap<String, String> environment = envProps.entrySet().stream()
506+
.collect(Collectors.toMap(e -> String.valueOf(e.getKey()), e -> String.valueOf(e.getValue()),
507+
(prev, next) -> next, HashMap::new));
503508
for (IEnvironmentVariable envVar : envVars) {
504509
environment.put(envVar.getName(), envVar.getValue());
505510
}
506511
setBuildEnvironment(environment);
507-
process = processBuilder.start();
512+
launcher.setProject(getProject());
513+
process = launcher.execute(cmd, args.toArray(new String[0]), BuildRunnerHelper.envMapToEnvp(environment),
514+
buildDirectory, monitor);
508515
}
509516
return process;
510517
}
511518

519+
/**
520+
* @return The exit code of the build process.
521+
*
522+
* @deprecated use {@link #watchProcess(IConsole, IProgressMonitor)} or {@link #watchProcess(IConsoleParser[], IProgressMonitor)} instead
523+
*/
512524
@Deprecated
513525
protected int watchProcess(Process process, IConsoleParser[] consoleParsers, IConsole console)
514526
throws CoreException {
@@ -520,110 +532,42 @@ protected int watchProcess(Process process, IConsoleParser[] consoleParsers, ICo
520532
}
521533

522534
/**
535+
* @return The exit code of the build process.
523536
* @since 6.4
537+
*
538+
* @deprecated use {@link #watchProcess(IConsole, IProgressMonitor)} instead and pass in a monitor
524539
*/
540+
@Deprecated
525541
protected int watchProcess(Process process, IConsole console) throws CoreException {
526-
Thread t1 = new ReaderThread(process.getInputStream(), console.getOutputStream());
527-
t1.start();
528-
Thread t2 = new ReaderThread(process.getErrorStream(), console.getErrorStream());
529-
t2.start();
530-
try {
531-
int rc = process.waitFor();
532-
// Allow reader threads the chance to process all output to console
533-
while (t1.isAlive()) {
534-
Thread.sleep(100);
535-
}
536-
while (t2.isAlive()) {
537-
Thread.sleep(100);
538-
}
539-
return rc;
540-
} catch (InterruptedException e) {
541-
CCorePlugin.log(e);
542-
return -1;
543-
}
542+
return watchProcess(console, new NullProgressMonitor());
544543
}
545544

546545
/**
546+
* @return The exit code of the build process.
547+
* @since 7.5
548+
*/
549+
protected int watchProcess(IConsole console, IProgressMonitor monitor) throws CoreException {
550+
return launcher.waitAndRead(console.getInfoStream(), console.getErrorStream(), monitor);
551+
}
552+
553+
/**
554+
* @return The exit code of the build process.
547555
* @since 6.4
556+
*
557+
* @deprecated use {@link #watchProcess(IConsoleParser[], IProgressMonitor)} instead and pass in a monitor
548558
*/
559+
@Deprecated
549560
protected int watchProcess(Process process, IConsoleParser[] consoleParsers) throws CoreException {
550-
Thread t1 = new ReaderThread(this, process.getInputStream(), consoleParsers);
551-
t1.start();
552-
Thread t2 = new ReaderThread(this, process.getErrorStream(), consoleParsers);
553-
t2.start();
554-
try {
555-
int rc = process.waitFor();
556-
// Allow reader threads the chance to process all output to console
557-
while (t1.isAlive()) {
558-
Thread.sleep(100);
559-
}
560-
while (t2.isAlive()) {
561-
Thread.sleep(100);
562-
}
563-
return rc;
564-
} catch (InterruptedException e) {
565-
CCorePlugin.log(e);
566-
return -1;
567-
}
561+
return watchProcess(consoleParsers, new NullProgressMonitor());
568562
}
569563

570-
private static class ReaderThread extends Thread {
571-
CBuildConfiguration config;
572-
private final BufferedReader in;
573-
private final IConsoleParser[] consoleParsers;
574-
private final PrintStream out;
575-
576-
public ReaderThread(CBuildConfiguration config, InputStream in, IConsoleParser[] consoleParsers) {
577-
this.config = config;
578-
this.in = new BufferedReader(new InputStreamReader(in));
579-
this.out = null;
580-
this.consoleParsers = consoleParsers;
581-
}
582-
583-
public ReaderThread(InputStream in, OutputStream out) {
584-
this.in = new BufferedReader(new InputStreamReader(in));
585-
this.out = new PrintStream(out);
586-
this.consoleParsers = null;
587-
this.config = null;
588-
}
589-
590-
@Override
591-
public void run() {
592-
List<Job> jobList = new ArrayList<>();
593-
try {
594-
for (String line = in.readLine(); line != null; line = in.readLine()) {
595-
if (consoleParsers != null) {
596-
for (IConsoleParser consoleParser : consoleParsers) {
597-
// Synchronize to avoid interleaving of lines
598-
synchronized (consoleParser) {
599-
// if we have an IConsoleParser2, use the processLine method that
600-
// takes a job list (Container Build support)
601-
if (consoleParser instanceof IConsoleParser2) {
602-
((IConsoleParser2) consoleParser).processLine(line, jobList);
603-
} else {
604-
consoleParser.processLine(line);
605-
}
606-
}
607-
}
608-
}
609-
if (out != null) {
610-
out.println(line);
611-
}
612-
}
613-
for (Job j : jobList) {
614-
try {
615-
j.join();
616-
} catch (InterruptedException e) {
617-
// ignore
618-
}
619-
}
620-
if (config != null) {
621-
config.shutdown();
622-
}
623-
} catch (IOException e) {
624-
CCorePlugin.log(e);
625-
}
626-
}
564+
/**
565+
* @return The exit code of the build process.
566+
* @since 7.5
567+
*/
568+
protected int watchProcess(IConsoleParser[] consoleParsers, IProgressMonitor monitor) throws CoreException {
569+
ConsoleOutputSniffer sniffer = new ConsoleOutputSniffer(consoleParsers);
570+
return launcher.waitAndRead(sniffer.getOutputStream(), sniffer.getErrorStream(), monitor);
627571
}
628572

629573
private File getScannerInfoCacheFile() {

core/org.eclipse.cdt.core/src/org/eclipse/cdt/core/build/StandardBuildConfiguration.java

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*******************************************************************************
2-
* Copyright (c) 2016 QNX Software Systems and others.
2+
* Copyright (c) 2016, 2022 QNX Software Systems and others.
33
*
44
* This program and the accompanying materials
55
* are made available under the terms of the Eclipse Public License 2.0
@@ -267,7 +267,7 @@ public IProject[] build(int kind, Map<String, String> args, IConsole console, IP
267267
}
268268

269269
IConsoleParser[] consoleParsers = new IConsoleParser[] { epm, this };
270-
watchProcess(p, consoleParsers);
270+
watchProcess(consoleParsers, monitor);
271271

272272
project.refreshLocal(IResource.DEPTH_INFINITE, monitor);
273273

@@ -326,7 +326,7 @@ public void clean(IConsole console, IProgressMonitor monitor) throws CoreExcepti
326326
return;
327327
}
328328

329-
watchProcess(p, console);
329+
watchProcess(console, monitor);
330330

331331
outStream.write(Messages.CBuildConfiguration_BuildComplete);
332332

0 commit comments

Comments
 (0)