Skip to main content
aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorJonah Graham2018-02-12 16:18:32 +0000
committerJonah Graham2018-02-12 17:31:31 +0000
commit2c82259f953af0608926071f9f46298e550675df (patch)
tree93ff53f576e140f48cac2c8e7a95c25f3bf0fa0a /core/org.eclipse.cdt.core/src/org/eclipse
parentfadd0a50156e36faae91d9bf185252e7898c8375 (diff)
downloadorg.eclipse.cdt-2c82259f953af0608926071f9f46298e550675df.tar.gz
org.eclipse.cdt-2c82259f953af0608926071f9f46298e550675df.tar.xz
org.eclipse.cdt-2c82259f953af0608926071f9f46298e550675df.zip
Fix newlines.
Diffstat (limited to 'core/org.eclipse.cdt.core/src/org/eclipse')
-rw-r--r--core/org.eclipse.cdt.core/src/org/eclipse/cdt/internal/core/BuildRunnerHelper.java1230
1 files changed, 615 insertions, 615 deletions
diff --git a/core/org.eclipse.cdt.core/src/org/eclipse/cdt/internal/core/BuildRunnerHelper.java b/core/org.eclipse.cdt.core/src/org/eclipse/cdt/internal/core/BuildRunnerHelper.java
index 7a6f76ff78d..d3ac8b46ea7 100644
--- a/core/org.eclipse.cdt.core/src/org/eclipse/cdt/internal/core/BuildRunnerHelper.java
+++ b/core/org.eclipse.cdt.core/src/org/eclipse/cdt/internal/core/BuildRunnerHelper.java
@@ -1,615 +1,615 @@
-/*******************************************************************************
- * Copyright (c) 2012, 2016 Andrew Gvozdev and others.
- * All rights reserved. This program and the accompanying materials
- * are made available under the terms of the Eclipse Public License v1.0
- * which accompanies this distribution, and is available at
- * http://www.eclipse.org/legal/epl-v10.html
- *
- * Contributors:
- * Andrew Gvozdev - initial API and implementation
- * IBM Corporation
- *******************************************************************************/
-
-package org.eclipse.cdt.internal.core;
-
-import java.io.Closeable;
-import java.io.IOException;
-import java.io.OutputStream;
-import java.net.URI;
-import java.text.SimpleDateFormat;
-import java.util.ArrayList;
-import java.util.Date;
-import java.util.List;
-import java.util.Map;
-import java.util.Map.Entry;
-import java.util.concurrent.TimeUnit;
-
-import org.eclipse.cdt.core.CCorePlugin;
-import org.eclipse.cdt.core.ErrorParserManager;
-import org.eclipse.cdt.core.ICommandLauncher;
-import org.eclipse.cdt.core.IConsoleParser;
-import org.eclipse.cdt.core.envvar.IEnvironmentVariable;
-import org.eclipse.cdt.core.envvar.IEnvironmentVariableManager;
-import org.eclipse.cdt.core.model.ICModelMarker;
-import org.eclipse.cdt.core.resources.IConsole;
-import org.eclipse.cdt.core.resources.RefreshScopeManager;
-import org.eclipse.cdt.core.settings.model.ICConfigurationDescription;
-import org.eclipse.cdt.internal.errorparsers.FixitManager;
-import org.eclipse.cdt.utils.EFSExtensionManager;
-import org.eclipse.core.resources.IMarker;
-import org.eclipse.core.resources.IProject;
-import org.eclipse.core.resources.IResource;
-import org.eclipse.core.resources.IWorkspace;
-import org.eclipse.core.resources.IWorkspaceRunnable;
-import org.eclipse.core.resources.IncrementalProjectBuilder;
-import org.eclipse.core.resources.ResourcesPlugin;
-import org.eclipse.core.runtime.Assert;
-import org.eclipse.core.runtime.CoreException;
-import org.eclipse.core.runtime.IPath;
-import org.eclipse.core.runtime.IProgressMonitor;
-import org.eclipse.core.runtime.NullProgressMonitor;
-import org.eclipse.core.runtime.Path;
-import org.eclipse.core.runtime.QualifiedName;
-
-/**
- * Helper class attempting to unify interactions with build console,
- * such as style of console output and handling of console output parsers.
- *
- * As of CDT 8.1, this class is experimental, internal and work in progress.
- * <strong>API is unstable and subject to change.</strong>
- */
-public class BuildRunnerHelper implements Closeable {
- private static final String PROGRESS_MONITOR_QUALIFIER = CCorePlugin.PLUGIN_ID + ".progressMonitor"; //$NON-NLS-1$
- private static final int PROGRESS_MONITOR_SCALE = 100;
- private static final int TICKS_STREAM_PROGRESS_MONITOR = 1 * PROGRESS_MONITOR_SCALE;
- private static final int TICKS_EXECUTE_PROGRAM = 1 * PROGRESS_MONITOR_SCALE;
- private static final int TICKS_PARSE_OUTPUT = 1 * PROGRESS_MONITOR_SCALE;
-
- private IProject project;
-
- private IConsole console = null;
- private ErrorParserManager errorParserManager = null;
- private StreamProgressMonitor streamProgressMonitor = null;
- private OutputStream stdout = null;
- private OutputStream stderr = null;
- private OutputStream consoleOut = null;
- private OutputStream consoleInfo = null;
-
- private long startTime = 0;
- private long endTime = 0;
-
- private QualifiedName progressPropertyName = null;
-
- private ICommandLauncher launcher;
- private IPath buildCommand;
- private String[] args;
- private URI workingDirectoryURI;
- String[] envp;
-
- private boolean isStreamsOpen = false;
- boolean isCancelled = false;
-
- /**
- * Constructor.
- */
- public BuildRunnerHelper(IProject project) {
- this.project = project;
- }
-
- /**
- * Set parameters for the launch.
- * @param envp - String[] array of environment variables in format "var=value" suitable for using
- * as "envp" with Runtime.exec(String[] cmdarray, String[] envp, File dir)
- */
- public void setLaunchParameters(ICommandLauncher launcher, IPath buildCommand, String[] args, URI workingDirectoryURI, String[] envp) {
- this.launcher = launcher;
- launcher.setProject(project);
- // Print the command for visual interaction.
- launcher.showCommand(true);
-
- this.buildCommand = buildCommand;
- this.args = args;
- this.workingDirectoryURI = workingDirectoryURI;
- this.envp = envp;
- }
-
- /**
- * Open and set up streams for use by {@link BuildRunnerHelper}.
- * This must be followed by {@link #close()} to close the streams. Use try...finally for that.
- *
- * @param epm - ErrorParserManger for error parsing and coloring errors on the console
- * @param buildOutputParsers - list of console output parsers or {@code null}.
- * @param con - the console.
- * @param monitor - progress monitor in the initial state where {@link IProgressMonitor#beginTask(String, int)}
- * has not been called yet.
- * @throws CoreException
- */
- public void prepareStreams(ErrorParserManager epm, List<IConsoleParser> buildOutputParsers, IConsole con, IProgressMonitor monitor) throws CoreException {
- errorParserManager = epm;
- console = con;
-
- // Visualize the flow of the streams:
- //
- // console <- EPM
- // ^
- // IConsoleParsers (includes EPM + other parsers)
- // ^
- // null <- StreamMomitor <= Sniffer <= Process (!!! the flow starts here!)
- //
-
- isStreamsOpen = true;
-
- consoleOut = console.getOutputStream();
- // stdout/stderr get to the console through ErrorParserManager
- errorParserManager.setOutputStream(consoleOut);
-
- List<IConsoleParser> parsers = new ArrayList<IConsoleParser>();
- // Using ErrorParserManager as console parser helps to avoid intermixing buffered streams
- // as ConsoleOutputSniffer waits for EOL to send a line to console parsers
- // separately for each stream.
- parsers.add(errorParserManager);
- if (buildOutputParsers != null) {
- parsers.addAll(buildOutputParsers);
- }
-
- Integer lastWork = null;
- if (buildCommand != null && project != null) {
- progressPropertyName = getProgressPropertyName(buildCommand, args);
- lastWork = (Integer)project.getSessionProperty(progressPropertyName);
- }
- if (lastWork == null) {
- lastWork = TICKS_STREAM_PROGRESS_MONITOR;
- }
-
- streamProgressMonitor = new StreamProgressMonitor(monitor, null, lastWork.intValue());
- ConsoleOutputSniffer sniffer = new ConsoleOutputSniffer(streamProgressMonitor, streamProgressMonitor, parsers.toArray(new IConsoleParser[parsers.size()]));
- stdout = sniffer.getOutputStream();
- stderr = sniffer.getErrorStream();
- }
-
- /**
- * @return the output stream to connect stdout of a process
- */
- public OutputStream getOutputStream() {
- return stdout;
- }
-
- /**
- * @return the output stream to connect stderr of a process
- */
- public OutputStream getErrorStream() {
- return stderr;
- }
-
- /**
- * Remove problem markers created for the resource by previous build.
- *
- * @param rc - resource to remove its markers.
- * @param monitor - progress monitor in the initial state where {@link IProgressMonitor#beginTask(String, int)}
- * has not been called yet.
- * @throws CoreException
- */
- public void removeOldMarkers(IResource rc, IProgressMonitor monitor) throws CoreException {
- if (monitor == null) {
- monitor = new NullProgressMonitor();
- }
- try {
- monitor.beginTask("", IProgressMonitor.UNKNOWN); //$NON-NLS-1$
- try {
- if (rc != null) {
- monitor.subTask(CCorePlugin.getFormattedString("BuildRunnerHelper.removingMarkers", rc.getFullPath().toString())); //$NON-NLS-1$
- rc.deleteMarkers(ICModelMarker.C_MODEL_PROBLEM_MARKER, false, IResource.DEPTH_INFINITE);
- }
- } catch (CoreException e) {
- // ignore
- }
- if (project != null) {
- // Remove markers which source is this project from other projects
- try {
- IWorkspace workspace = project.getWorkspace();
- IMarker[] markers = workspace.getRoot().findMarkers(ICModelMarker.C_MODEL_PROBLEM_MARKER, true, IResource.DEPTH_INFINITE);
- String projectName = project.getName();
- List<IMarker> markersList = new ArrayList<IMarker>();
- for (IMarker marker : markers) {
- if (projectName.equals(marker.getAttribute(IMarker.SOURCE_ID))) {
- markersList.add(marker);
- }
- }
- if (markersList.size() > 0) {
- workspace.deleteMarkers(markersList.toArray(new IMarker[markersList.size()]));
- FixitManager.getInstance().deleteMarkers(markersList.toArray(new IMarker[markersList.size()]));
- }
- } catch (CoreException e) {
- // ignore
- }
- }
-
- } finally {
- monitor.done();
- }
- }
-
- /**
- * Launch build command and process console output.
- *
- * @param monitor - progress monitor in the initial state where {@link IProgressMonitor#beginTask(String, int)}
- * has not been called yet.
- * @throws CoreException
- * @throws IOException
- */
- public int build(IProgressMonitor monitor) throws CoreException, IOException {
- Assert.isNotNull(launcher, "Launch parameters must be set before calling this method"); //$NON-NLS-1$
- Assert.isNotNull(errorParserManager, "Streams must be created and connected before calling this method"); //$NON-NLS-1$
-
- int status = ICommandLauncher.ILLEGAL_COMMAND;
-
- if (monitor == null) {
- monitor = new NullProgressMonitor();
- }
- try {
- monitor.beginTask("", TICKS_EXECUTE_PROGRAM + TICKS_PARSE_OUTPUT); //$NON-NLS-1$
-
- isCancelled = false;
- String pathFromURI = null;
- if (workingDirectoryURI != null) {
- pathFromURI = EFSExtensionManager.getDefault().getPathFromURI(workingDirectoryURI);
- }
- if (pathFromURI == null) {
- // fallback to CWD
- pathFromURI = System.getProperty("user.dir"); //$NON-NLS-1$
- }
- IPath workingDirectory = new Path(pathFromURI);
-
- String errMsg = null;
- monitor.subTask(CCorePlugin.getFormattedString("BuildRunnerHelper.invokingCommand", guessCommandLine(buildCommand.toString(), args))); //$NON-NLS-1$
- Process p = launcher.execute(buildCommand, args, envp, workingDirectory, monitor);
- monitor.worked(TICKS_EXECUTE_PROGRAM);
- if (p != null) {
- try {
- // Close the input of the Process explicitly.
- // We will never write to it.
- p.getOutputStream().close();
- } catch (IOException e) {
- }
-
- status = launcher.waitAndRead(stdout, stderr, monitor);
- monitor.worked(TICKS_PARSE_OUTPUT);
- if (status != ICommandLauncher.OK) {
- errMsg = launcher.getErrorMessage();
- }
- } else {
- errMsg = launcher.getErrorMessage();
- }
-
- if (errMsg != null && !errMsg.isEmpty()) {
- stderr.write(errMsg.getBytes());
- }
-
- isCancelled = monitor.isCanceled();
- if (!isCancelled && project != null) {
- project.setSessionProperty(progressPropertyName, Integer.valueOf(streamProgressMonitor.getWorkDone()));
- }
- } catch (Exception e) {
- CCorePlugin.log(e);
- } finally {
- monitor.done();
- }
- return status;
- }
-
- /**
- * Close all streams except console Info stream which is handled by {@link #greeting(String)}/{@link #goodbye()}.
- */
- @Override
- public void close() throws IOException {
- if (!isStreamsOpen)
- return;
-
- try {
- if (stdout != null)
- stdout.close();
- } catch (Exception e) {
- CCorePlugin.log(e);
- } finally {
- stdout = null;
- try {
- if (stderr != null)
- stderr.close();
- } catch (Exception e) {
- CCorePlugin.log(e);
- } finally {
- stderr = null;
- try {
- if (streamProgressMonitor != null)
- streamProgressMonitor.close();
- } catch (Exception e) {
- CCorePlugin.log(e);
- } finally {
- streamProgressMonitor = null;
- try {
- if (consoleOut != null)
- consoleOut.close();
- } catch (Exception e) {
- CCorePlugin.log(e);
- } finally {
- consoleOut = null;
- }
- }
- }
- }
- isStreamsOpen = false;
- }
-
- /**
- * Refresh project in the workspace.
- *
- * @param configName - the configuration to refresh
- * @param monitor - progress monitor in the initial state where {@link IProgressMonitor#beginTask(String, int)}
- * has not been called yet.
- */
- public void refreshProject(String configName, IProgressMonitor monitor) {
- if (monitor == null) {
- monitor = new NullProgressMonitor();
- }
- try {
- monitor.beginTask(CCorePlugin.getFormattedString("BuildRunnerHelper.refreshingProject", project.getName()), IProgressMonitor.UNKNOWN); //$NON-NLS-1$
- monitor.subTask(""); //$NON-NLS-1$
-
- // Do not allow the cancel of the refresh, since the builder is external
- // to Eclipse, files may have been created/modified and we will be out-of-sync.
- // The caveat is for huge projects, it may take sometimes at every build.
- // Use the refresh scope manager to refresh
- RefreshScopeManager refreshManager = RefreshScopeManager.getInstance();
- IWorkspaceRunnable runnable = refreshManager.getRefreshRunnable(project, configName);
- ResourcesPlugin.getWorkspace().run(runnable, null, IWorkspace.AVOID_UPDATE, null);
- } catch (CoreException e) {
- // ignore exceptions
- } finally {
- monitor.done();
- }
- }
-
- /**
- * Print a standard greeting to the console.
- * Note that start time of the build is recorded by this method.
- *
- * This method may open an Info stream which must be closed by call to {@link #goodbye()}
- * after all informational messages are printed.
- *
- * @param kind - kind of build. {@link IncrementalProjectBuilder} constants such as
- * {@link IncrementalProjectBuilder#FULL_BUILD} should be used.
- */
- public void greeting(int kind) {
- String msg = CCorePlugin.getFormattedString("BuildRunnerHelper.buildProject", //$NON-NLS-1$
- new String[] { buildKindToString(kind), project.getName() });
- greeting(msg);
- }
-
- /**
- * Print a standard greeting to the console.
- * Note that start time of the build is recorded by this method.
- *
- * This method may open an Info stream which must be closed by call to {@link #goodbye()}
- * after all informational messages are printed.
- *
- * @param kind - kind of build. {@link IncrementalProjectBuilder} constants such as
- * {@link IncrementalProjectBuilder#FULL_BUILD} should be used.
- * @param cfgName - configuration name.
- * @param toolchainName - tool-chain name.
- * @param isSupported - flag indicating if tool-chain is supported on the system.
- */
- public void greeting(int kind, String cfgName, String toolchainName, boolean isSupported) {
- greeting(buildKindToString(kind), cfgName, toolchainName, isSupported);
- }
-
- /**
- * Print a standard greeting to the console.
- * Note that start time of the build is recorded by this method.
- *
- * This method may open an Info stream which must be closed by call to {@link #goodbye()}
- * after all informational messages are printed.
- *
- * @param kind - kind of build as a String.
- * @param cfgName - configuration name.
- * @param toolchainName - tool-chain name.
- * @param isSupported - flag indicating if tool-chain is supported on the system.
- */
- public void greeting(String kind, String cfgName, String toolchainName, boolean isSupported) {
- String msg = CCorePlugin.getFormattedString("BuildRunnerHelper.buildProjectConfiguration", //$NON-NLS-1$
- new String[] { kind, cfgName, project.getName() });
- greeting(msg);
-
- if (!isSupported ){
- String errMsg = CCorePlugin.getFormattedString("BuildRunnerHelper.unsupportedConfiguration", //$NON-NLS-1$
- new String[] { cfgName, toolchainName });
- printLine(errMsg);
- }
- }
-
- /**
- * Print the specified greeting to the console.
- * Note that start time of the build is recorded by this method.
- *
- * This method may open an Info stream which must be closed by call to {@link #goodbye()}
- * after all informational messages are printed.
- */
- public void greeting(String msg) {
- startTime = System.currentTimeMillis();
- if (consoleInfo == null) {
- try {
- consoleInfo = console.getInfoStream();
- } catch (CoreException e) {
- CCorePlugin.log(e);
- }
- }
- toConsole(BuildRunnerHelper.timestamp(startTime) + "**** " + msg + " ****"); //$NON-NLS-1$ //$NON-NLS-2$
- }
-
- /**
- * Print a standard footer to the console and close Info stream (must be open with one of {@link #greeting(String)} calls).
- * That prints duration of the build determined by start time recorded in {@link #greeting(String)}.
- *
- * <br><strong>Important: {@link #close()} the streams BEFORE calling this method to properly flush all outputs</strong>
- */
- public void goodbye() {
- Assert.isTrue(startTime != 0, "Start time must be set before calling this method."); //$NON-NLS-1$
- Assert.isTrue(consoleInfo != null, "consoleInfo must be open with greetings(...) call before using this method."); //$NON-NLS-1$
-
- endTime = System.currentTimeMillis();
- String duration = durationToString(endTime - startTime);
- String msg = isCancelled ? CCorePlugin.getFormattedString("BuildRunnerHelper.buildCancelled", duration) //$NON-NLS-1$
- : CCorePlugin.getFormattedString("BuildRunnerHelper.buildFinished", duration); //$NON-NLS-1$
- String goodbye = '\n' + timestamp(endTime) + msg + '\n';
-
- try {
- toConsole(goodbye);
- } finally {
- try {
- consoleInfo.close();
- } catch (Exception e) {
- CCorePlugin.log(e);
- } finally {
- consoleInfo = null;
- }
- }
- }
-
- /**
- * Print the given message to the console.
- * @param msg - message to print.
- */
- public void printLine(String msg) {
- Assert.isNotNull(errorParserManager, "Streams must be created and connected before calling this method"); //$NON-NLS-1$
- errorParserManager.processLine(msg);
- }
-
- /**
- * Compose command line that presumably will be run by launcher.
- */
- private static String guessCommandLine(String command, String[] args) {
- StringBuilder buf = new StringBuilder(command + ' ');
- if (args != null) {
- for (String arg : args) {
- buf.append(arg);
- buf.append(' ');
- }
- }
- return buf.toString().trim();
- }
- /**
- * Print a message to the console info output. Note that this message is colored
- * with the color assigned to "Info" stream.
- * @param msg - message to print.
- */
- private void toConsole(String msg) {
- Assert.isNotNull(console, "Streams must be created and connected before calling this method"); //$NON-NLS-1$
- try {
- consoleInfo.write((msg+"\n").getBytes()); //$NON-NLS-1$
- } catch (Exception e) {
- CCorePlugin.log(e);
- }
- }
-
- /**
- * Qualified name to keep previous value of build duration in project session properties.
- */
- private static QualifiedName getProgressPropertyName(IPath buildCommand, String[] args) {
- String name = "buildCommand." + buildCommand.toString(); //$NON-NLS-1$
- if (args != null) {
- for (String arg : args) {
- name = name + ' ' + arg;
- }
- }
- return new QualifiedName(PROGRESS_MONITOR_QUALIFIER, name);
- }
-
- /**
- * Get environment variables from configuration as array of "var=value" suitable
- * for using as "envp" with Runtime.exec(String[] cmdarray, String[] envp, File dir)
- *
- * @param envMap - map of environment variables
- * @return String array of environment variables in format "var=value"
- */
- public static String[] envMapToEnvp(Map<String, String> envMap) {
- // Convert into envp strings
- List<String> strings = new ArrayList<String>(envMap.size());
- for (Entry<String, String> entry : envMap.entrySet()) {
- strings.add(entry.getKey() + '=' + entry.getValue());
- }
-
- return strings.toArray(new String[strings.size()]);
- }
-
- /**
- * Get environment variables from configuration as array of "var=value" suitable
- * for using as "envp" with Runtime.exec(String[] cmdarray, String[] envp, File dir)
- *
- * @param cfgDescription - configuration description.
- * @return String array of environment variables in format "var=value". Does not return {@code null}.
- */
- public static String[] getEnvp(ICConfigurationDescription cfgDescription) {
- IEnvironmentVariableManager mngr = CCorePlugin.getDefault().getBuildEnvironmentManager();
- IEnvironmentVariable[] vars = mngr.getVariables(cfgDescription, true);
- // Convert into envp strings
- List<String> strings = new ArrayList<String>(vars.length);
- for (IEnvironmentVariable var : vars) {
- strings.add(var.getName() + '=' + var.getValue());
- }
-
- return strings.toArray(new String[strings.size()]);
- }
-
- /**
- * Convert duration to human friendly format.
- */
- @SuppressWarnings("nls")
- private static String durationToString(long duration) {
- String result = "";
- long days = TimeUnit.MILLISECONDS.toDays(duration);
- if (days > 0) {
- result += days + "d,";
- }
- long hours = TimeUnit.MILLISECONDS.toHours(duration) % 24;
- if (hours > 0) {
- result += hours + "h:";
- }
- long minutes = TimeUnit.MILLISECONDS.toMinutes(duration) % 60;
- if (minutes > 0) {
- result += minutes + "m:";
- }
- long seconds = TimeUnit.MILLISECONDS.toSeconds(duration) % 60;
- if (seconds > 0) {
- result += seconds + "s.";
- }
- long milliseconds = TimeUnit.MILLISECONDS.toMillis(duration) % 1000;
- result += milliseconds + "ms";
-
- return result;
- }
-
- /**
- * Supply timestamp to prefix informational messages.
- */
- @SuppressWarnings("nls")
- private static String timestamp(long time) {
- return new SimpleDateFormat("HH:mm:ss").format(new Date(time)) + " ";
- }
-
- /**
- * Convert build kind to human friendly format.
- */
- private static String buildKindToString(int kind) {
- switch (kind) {
- case IncrementalProjectBuilder.FULL_BUILD:
- return CCorePlugin.getResourceString("BuildRunnerHelper.build"); //$NON-NLS-1$
- case IncrementalProjectBuilder.INCREMENTAL_BUILD:
- return CCorePlugin.getResourceString("BuildRunnerHelper.incrementalBuild"); //$NON-NLS-1$
- case IncrementalProjectBuilder.AUTO_BUILD:
- return CCorePlugin.getResourceString("BuildRunnerHelper.autoBuild"); //$NON-NLS-1$
- case IncrementalProjectBuilder.CLEAN_BUILD:
- return CCorePlugin.getResourceString("BuildRunnerHelper.cleanBuild"); //$NON-NLS-1$
- default:
- return CCorePlugin.getResourceString("BuildRunnerHelper.build"); //$NON-NLS-1$
- }
- }
-}
+/*******************************************************************************
+ * Copyright (c) 2012, 2016 Andrew Gvozdev and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ * Andrew Gvozdev - initial API and implementation
+ * IBM Corporation
+ *******************************************************************************/
+
+package org.eclipse.cdt.internal.core;
+
+import java.io.Closeable;
+import java.io.IOException;
+import java.io.OutputStream;
+import java.net.URI;
+import java.text.SimpleDateFormat;
+import java.util.ArrayList;
+import java.util.Date;
+import java.util.List;
+import java.util.Map;
+import java.util.Map.Entry;
+import java.util.concurrent.TimeUnit;
+
+import org.eclipse.cdt.core.CCorePlugin;
+import org.eclipse.cdt.core.ErrorParserManager;
+import org.eclipse.cdt.core.ICommandLauncher;
+import org.eclipse.cdt.core.IConsoleParser;
+import org.eclipse.cdt.core.envvar.IEnvironmentVariable;
+import org.eclipse.cdt.core.envvar.IEnvironmentVariableManager;
+import org.eclipse.cdt.core.model.ICModelMarker;
+import org.eclipse.cdt.core.resources.IConsole;
+import org.eclipse.cdt.core.resources.RefreshScopeManager;
+import org.eclipse.cdt.core.settings.model.ICConfigurationDescription;
+import org.eclipse.cdt.internal.errorparsers.FixitManager;
+import org.eclipse.cdt.utils.EFSExtensionManager;
+import org.eclipse.core.resources.IMarker;
+import org.eclipse.core.resources.IProject;
+import org.eclipse.core.resources.IResource;
+import org.eclipse.core.resources.IWorkspace;
+import org.eclipse.core.resources.IWorkspaceRunnable;
+import org.eclipse.core.resources.IncrementalProjectBuilder;
+import org.eclipse.core.resources.ResourcesPlugin;
+import org.eclipse.core.runtime.Assert;
+import org.eclipse.core.runtime.CoreException;
+import org.eclipse.core.runtime.IPath;
+import org.eclipse.core.runtime.IProgressMonitor;
+import org.eclipse.core.runtime.NullProgressMonitor;
+import org.eclipse.core.runtime.Path;
+import org.eclipse.core.runtime.QualifiedName;
+
+/**
+ * Helper class attempting to unify interactions with build console,
+ * such as style of console output and handling of console output parsers.
+ *
+ * As of CDT 8.1, this class is experimental, internal and work in progress.
+ * <strong>API is unstable and subject to change.</strong>
+ */
+public class BuildRunnerHelper implements Closeable {
+ private static final String PROGRESS_MONITOR_QUALIFIER = CCorePlugin.PLUGIN_ID + ".progressMonitor"; //$NON-NLS-1$
+ private static final int PROGRESS_MONITOR_SCALE = 100;
+ private static final int TICKS_STREAM_PROGRESS_MONITOR = 1 * PROGRESS_MONITOR_SCALE;
+ private static final int TICKS_EXECUTE_PROGRAM = 1 * PROGRESS_MONITOR_SCALE;
+ private static final int TICKS_PARSE_OUTPUT = 1 * PROGRESS_MONITOR_SCALE;
+
+ private IProject project;
+
+ private IConsole console = null;
+ private ErrorParserManager errorParserManager = null;
+ private StreamProgressMonitor streamProgressMonitor = null;
+ private OutputStream stdout = null;
+ private OutputStream stderr = null;
+ private OutputStream consoleOut = null;
+ private OutputStream consoleInfo = null;
+
+ private long startTime = 0;
+ private long endTime = 0;
+
+ private QualifiedName progressPropertyName = null;
+
+ private ICommandLauncher launcher;
+ private IPath buildCommand;
+ private String[] args;
+ private URI workingDirectoryURI;
+ String[] envp;
+
+ private boolean isStreamsOpen = false;
+ boolean isCancelled = false;
+
+ /**
+ * Constructor.
+ */
+ public BuildRunnerHelper(IProject project) {
+ this.project = project;
+ }
+
+ /**
+ * Set parameters for the launch.
+ * @param envp - String[] array of environment variables in format "var=value" suitable for using
+ * as "envp" with Runtime.exec(String[] cmdarray, String[] envp, File dir)
+ */
+ public void setLaunchParameters(ICommandLauncher launcher, IPath buildCommand, String[] args, URI workingDirectoryURI, String[] envp) {
+ this.launcher = launcher;
+ launcher.setProject(project);
+ // Print the command for visual interaction.
+ launcher.showCommand(true);
+
+ this.buildCommand = buildCommand;
+ this.args = args;
+ this.workingDirectoryURI = workingDirectoryURI;
+ this.envp = envp;
+ }
+
+ /**
+ * Open and set up streams for use by {@link BuildRunnerHelper}.
+ * This must be followed by {@link #close()} to close the streams. Use try...finally for that.
+ *
+ * @param epm - ErrorParserManger for error parsing and coloring errors on the console
+ * @param buildOutputParsers - list of console output parsers or {@code null}.
+ * @param con - the console.
+ * @param monitor - progress monitor in the initial state where {@link IProgressMonitor#beginTask(String, int)}
+ * has not been called yet.
+ * @throws CoreException
+ */
+ public void prepareStreams(ErrorParserManager epm, List<IConsoleParser> buildOutputParsers, IConsole con, IProgressMonitor monitor) throws CoreException {
+ errorParserManager = epm;
+ console = con;
+
+ // Visualize the flow of the streams:
+ //
+ // console <- EPM
+ // ^
+ // IConsoleParsers (includes EPM + other parsers)
+ // ^
+ // null <- StreamMomitor <= Sniffer <= Process (!!! the flow starts here!)
+ //
+
+ isStreamsOpen = true;
+
+ consoleOut = console.getOutputStream();
+ // stdout/stderr get to the console through ErrorParserManager
+ errorParserManager.setOutputStream(consoleOut);
+
+ List<IConsoleParser> parsers = new ArrayList<IConsoleParser>();
+ // Using ErrorParserManager as console parser helps to avoid intermixing buffered streams
+ // as ConsoleOutputSniffer waits for EOL to send a line to console parsers
+ // separately for each stream.
+ parsers.add(errorParserManager);
+ if (buildOutputParsers != null) {
+ parsers.addAll(buildOutputParsers);
+ }
+
+ Integer lastWork = null;
+ if (buildCommand != null && project != null) {
+ progressPropertyName = getProgressPropertyName(buildCommand, args);
+ lastWork = (Integer)project.getSessionProperty(progressPropertyName);
+ }
+ if (lastWork == null) {
+ lastWork = TICKS_STREAM_PROGRESS_MONITOR;
+ }
+
+ streamProgressMonitor = new StreamProgressMonitor(monitor, null, lastWork.intValue());
+ ConsoleOutputSniffer sniffer = new ConsoleOutputSniffer(streamProgressMonitor, streamProgressMonitor, parsers.toArray(new IConsoleParser[parsers.size()]));
+ stdout = sniffer.getOutputStream();
+ stderr = sniffer.getErrorStream();
+ }
+
+ /**
+ * @return the output stream to connect stdout of a process
+ */
+ public OutputStream getOutputStream() {
+ return stdout;
+ }
+
+ /**
+ * @return the output stream to connect stderr of a process
+ */
+ public OutputStream getErrorStream() {
+ return stderr;
+ }
+
+ /**
+ * Remove problem markers created for the resource by previous build.
+ *
+ * @param rc - resource to remove its markers.
+ * @param monitor - progress monitor in the initial state where {@link IProgressMonitor#beginTask(String, int)}
+ * has not been called yet.
+ * @throws CoreException
+ */
+ public void removeOldMarkers(IResource rc, IProgressMonitor monitor) throws CoreException {
+ if (monitor == null) {
+ monitor = new NullProgressMonitor();
+ }
+ try {
+ monitor.beginTask("", IProgressMonitor.UNKNOWN); //$NON-NLS-1$
+ try {
+ if (rc != null) {
+ monitor.subTask(CCorePlugin.getFormattedString("BuildRunnerHelper.removingMarkers", rc.getFullPath().toString())); //$NON-NLS-1$
+ rc.deleteMarkers(ICModelMarker.C_MODEL_PROBLEM_MARKER, false, IResource.DEPTH_INFINITE);
+ }
+ } catch (CoreException e) {
+ // ignore
+ }
+ if (project != null) {
+ // Remove markers which source is this project from other projects
+ try {
+ IWorkspace workspace = project.getWorkspace();
+ IMarker[] markers = workspace.getRoot().findMarkers(ICModelMarker.C_MODEL_PROBLEM_MARKER, true, IResource.DEPTH_INFINITE);
+ String projectName = project.getName();
+ List<IMarker> markersList = new ArrayList<IMarker>();
+ for (IMarker marker : markers) {
+ if (projectName.equals(marker.getAttribute(IMarker.SOURCE_ID))) {
+ markersList.add(marker);
+ }
+ }
+ if (markersList.size() > 0) {
+ workspace.deleteMarkers(markersList.toArray(new IMarker[markersList.size()]));
+ FixitManager.getInstance().deleteMarkers(markersList.toArray(new IMarker[markersList.size()]));
+ }
+ } catch (CoreException e) {
+ // ignore
+ }
+ }
+
+ } finally {
+ monitor.done();
+ }
+ }
+
+ /**
+ * Launch build command and process console output.
+ *
+ * @param monitor - progress monitor in the initial state where {@link IProgressMonitor#beginTask(String, int)}
+ * has not been called yet.
+ * @throws CoreException
+ * @throws IOException
+ */
+ public int build(IProgressMonitor monitor) throws CoreException, IOException {
+ Assert.isNotNull(launcher, "Launch parameters must be set before calling this method"); //$NON-NLS-1$
+ Assert.isNotNull(errorParserManager, "Streams must be created and connected before calling this method"); //$NON-NLS-1$
+
+ int status = ICommandLauncher.ILLEGAL_COMMAND;
+
+ if (monitor == null) {
+ monitor = new NullProgressMonitor();
+ }
+ try {
+ monitor.beginTask("", TICKS_EXECUTE_PROGRAM + TICKS_PARSE_OUTPUT); //$NON-NLS-1$
+
+ isCancelled = false;
+ String pathFromURI = null;
+ if (workingDirectoryURI != null) {
+ pathFromURI = EFSExtensionManager.getDefault().getPathFromURI(workingDirectoryURI);
+ }
+ if (pathFromURI == null) {
+ // fallback to CWD
+ pathFromURI = System.getProperty("user.dir"); //$NON-NLS-1$
+ }
+ IPath workingDirectory = new Path(pathFromURI);
+
+ String errMsg = null;
+ monitor.subTask(CCorePlugin.getFormattedString("BuildRunnerHelper.invokingCommand", guessCommandLine(buildCommand.toString(), args))); //$NON-NLS-1$
+ Process p = launcher.execute(buildCommand, args, envp, workingDirectory, monitor);
+ monitor.worked(TICKS_EXECUTE_PROGRAM);
+ if (p != null) {
+ try {
+ // Close the input of the Process explicitly.
+ // We will never write to it.
+ p.getOutputStream().close();
+ } catch (IOException e) {
+ }
+
+ status = launcher.waitAndRead(stdout, stderr, monitor);
+ monitor.worked(TICKS_PARSE_OUTPUT);
+ if (status != ICommandLauncher.OK) {
+ errMsg = launcher.getErrorMessage();
+ }
+ } else {
+ errMsg = launcher.getErrorMessage();
+ }
+
+ if (errMsg != null && !errMsg.isEmpty()) {
+ stderr.write(errMsg.getBytes());
+ }
+
+ isCancelled = monitor.isCanceled();
+ if (!isCancelled && project != null) {
+ project.setSessionProperty(progressPropertyName, Integer.valueOf(streamProgressMonitor.getWorkDone()));
+ }
+ } catch (Exception e) {
+ CCorePlugin.log(e);
+ } finally {
+ monitor.done();
+ }
+ return status;
+ }
+
+ /**
+ * Close all streams except console Info stream which is handled by {@link #greeting(String)}/{@link #goodbye()}.
+ */
+ @Override
+ public void close() throws IOException {
+ if (!isStreamsOpen)
+ return;
+
+ try {
+ if (stdout != null)
+ stdout.close();
+ } catch (Exception e) {
+ CCorePlugin.log(e);
+ } finally {
+ stdout = null;
+ try {
+ if (stderr != null)
+ stderr.close();
+ } catch (Exception e) {
+ CCorePlugin.log(e);
+ } finally {
+ stderr = null;
+ try {
+ if (streamProgressMonitor != null)
+ streamProgressMonitor.close();
+ } catch (Exception e) {
+ CCorePlugin.log(e);
+ } finally {
+ streamProgressMonitor = null;
+ try {
+ if (consoleOut != null)
+ consoleOut.close();
+ } catch (Exception e) {
+ CCorePlugin.log(e);
+ } finally {
+ consoleOut = null;
+ }
+ }
+ }
+ }
+ isStreamsOpen = false;
+ }
+
+ /**
+ * Refresh project in the workspace.
+ *
+ * @param configName - the configuration to refresh
+ * @param monitor - progress monitor in the initial state where {@link IProgressMonitor#beginTask(String, int)}
+ * has not been called yet.
+ */
+ public void refreshProject(String configName, IProgressMonitor monitor) {
+ if (monitor == null) {
+ monitor = new NullProgressMonitor();
+ }
+ try {
+ monitor.beginTask(CCorePlugin.getFormattedString("BuildRunnerHelper.refreshingProject", project.getName()), IProgressMonitor.UNKNOWN); //$NON-NLS-1$
+ monitor.subTask(""); //$NON-NLS-1$
+
+ // Do not allow the cancel of the refresh, since the builder is external
+ // to Eclipse, files may have been created/modified and we will be out-of-sync.
+ // The caveat is for huge projects, it may take sometimes at every build.
+ // Use the refresh scope manager to refresh
+ RefreshScopeManager refreshManager = RefreshScopeManager.getInstance();
+ IWorkspaceRunnable runnable = refreshManager.getRefreshRunnable(project, configName);
+ ResourcesPlugin.getWorkspace().run(runnable, null, IWorkspace.AVOID_UPDATE, null);
+ } catch (CoreException e) {
+ // ignore exceptions
+ } finally {
+ monitor.done();
+ }
+ }
+
+ /**
+ * Print a standard greeting to the console.
+ * Note that start time of the build is recorded by this method.
+ *
+ * This method may open an Info stream which must be closed by call to {@link #goodbye()}
+ * after all informational messages are printed.
+ *
+ * @param kind - kind of build. {@link IncrementalProjectBuilder} constants such as
+ * {@link IncrementalProjectBuilder#FULL_BUILD} should be used.
+ */
+ public void greeting(int kind) {
+ String msg = CCorePlugin.getFormattedString("BuildRunnerHelper.buildProject", //$NON-NLS-1$
+ new String[] { buildKindToString(kind), project.getName() });
+ greeting(msg);
+ }
+
+ /**
+ * Print a standard greeting to the console.
+ * Note that start time of the build is recorded by this method.
+ *
+ * This method may open an Info stream which must be closed by call to {@link #goodbye()}
+ * after all informational messages are printed.
+ *
+ * @param kind - kind of build. {@link IncrementalProjectBuilder} constants such as
+ * {@link IncrementalProjectBuilder#FULL_BUILD} should be used.
+ * @param cfgName - configuration name.
+ * @param toolchainName - tool-chain name.
+ * @param isSupported - flag indicating if tool-chain is supported on the system.
+ */
+ public void greeting(int kind, String cfgName, String toolchainName, boolean isSupported) {
+ greeting(buildKindToString(kind), cfgName, toolchainName, isSupported);
+ }
+
+ /**
+ * Print a standard greeting to the console.
+ * Note that start time of the build is recorded by this method.
+ *
+ * This method may open an Info stream which must be closed by call to {@link #goodbye()}
+ * after all informational messages are printed.
+ *
+ * @param kind - kind of build as a String.
+ * @param cfgName - configuration name.
+ * @param toolchainName - tool-chain name.
+ * @param isSupported - flag indicating if tool-chain is supported on the system.
+ */
+ public void greeting(String kind, String cfgName, String toolchainName, boolean isSupported) {
+ String msg = CCorePlugin.getFormattedString("BuildRunnerHelper.buildProjectConfiguration", //$NON-NLS-1$
+ new String[] { kind, cfgName, project.getName() });
+ greeting(msg);
+
+ if (!isSupported ){
+ String errMsg = CCorePlugin.getFormattedString("BuildRunnerHelper.unsupportedConfiguration", //$NON-NLS-1$
+ new String[] { cfgName, toolchainName });
+ printLine(errMsg);
+ }
+ }
+
+ /**
+ * Print the specified greeting to the console.
+ * Note that start time of the build is recorded by this method.
+ *
+ * This method may open an Info stream which must be closed by call to {@link #goodbye()}
+ * after all informational messages are printed.
+ */
+ public void greeting(String msg) {
+ startTime = System.currentTimeMillis();
+ if (consoleInfo == null) {
+ try {
+ consoleInfo = console.getInfoStream();
+ } catch (CoreException e) {
+ CCorePlugin.log(e);
+ }
+ }
+ toConsole(BuildRunnerHelper.timestamp(startTime) + "**** " + msg + " ****"); //$NON-NLS-1$ //$NON-NLS-2$
+ }
+
+ /**
+ * Print a standard footer to the console and close Info stream (must be open with one of {@link #greeting(String)} calls).
+ * That prints duration of the build determined by start time recorded in {@link #greeting(String)}.
+ *
+ * <br><strong>Important: {@link #close()} the streams BEFORE calling this method to properly flush all outputs</strong>
+ */
+ public void goodbye() {
+ Assert.isTrue(startTime != 0, "Start time must be set before calling this method."); //$NON-NLS-1$
+ Assert.isTrue(consoleInfo != null, "consoleInfo must be open with greetings(...) call before using this method."); //$NON-NLS-1$
+
+ endTime = System.currentTimeMillis();
+ String duration = durationToString(endTime - startTime);
+ String msg = isCancelled ? CCorePlugin.getFormattedString("BuildRunnerHelper.buildCancelled", duration) //$NON-NLS-1$
+ : CCorePlugin.getFormattedString("BuildRunnerHelper.buildFinished", duration); //$NON-NLS-1$
+ String goodbye = '\n' + timestamp(endTime) + msg + '\n';
+
+ try {
+ toConsole(goodbye);
+ } finally {
+ try {
+ consoleInfo.close();
+ } catch (Exception e) {
+ CCorePlugin.log(e);
+ } finally {
+ consoleInfo = null;
+ }
+ }
+ }
+
+ /**
+ * Print the given message to the console.
+ * @param msg - message to print.
+ */
+ public void printLine(String msg) {
+ Assert.isNotNull(errorParserManager, "Streams must be created and connected before calling this method"); //$NON-NLS-1$
+ errorParserManager.processLine(msg);
+ }
+
+ /**
+ * Compose command line that presumably will be run by launcher.
+ */
+ private static String guessCommandLine(String command, String[] args) {
+ StringBuilder buf = new StringBuilder(command + ' ');
+ if (args != null) {
+ for (String arg : args) {
+ buf.append(arg);
+ buf.append(' ');
+ }
+ }
+ return buf.toString().trim();
+ }
+ /**
+ * Print a message to the console info output. Note that this message is colored
+ * with the color assigned to "Info" stream.
+ * @param msg - message to print.
+ */
+ private void toConsole(String msg) {
+ Assert.isNotNull(console, "Streams must be created and connected before calling this method"); //$NON-NLS-1$
+ try {
+ consoleInfo.write((msg+"\n").getBytes()); //$NON-NLS-1$
+ } catch (Exception e) {
+ CCorePlugin.log(e);
+ }
+ }
+
+ /**
+ * Qualified name to keep previous value of build duration in project session properties.
+ */
+ private static QualifiedName getProgressPropertyName(IPath buildCommand, String[] args) {
+ String name = "buildCommand." + buildCommand.toString(); //$NON-NLS-1$
+ if (args != null) {
+ for (String arg : args) {
+ name = name + ' ' + arg;
+ }
+ }
+ return new QualifiedName(PROGRESS_MONITOR_QUALIFIER, name);
+ }
+
+ /**
+ * Get environment variables from configuration as array of "var=value" suitable
+ * for using as "envp" with Runtime.exec(String[] cmdarray, String[] envp, File dir)
+ *
+ * @param envMap - map of environment variables
+ * @return String array of environment variables in format "var=value"
+ */
+ public static String[] envMapToEnvp(Map<String, String> envMap) {
+ // Convert into envp strings
+ List<String> strings = new ArrayList<String>(envMap.size());
+ for (Entry<String, String> entry : envMap.entrySet()) {
+ strings.add(entry.getKey() + '=' + entry.getValue());
+ }
+
+ return strings.toArray(new String[strings.size()]);
+ }
+
+ /**
+ * Get environment variables from configuration as array of "var=value" suitable
+ * for using as "envp" with Runtime.exec(String[] cmdarray, String[] envp, File dir)
+ *
+ * @param cfgDescription - configuration description.
+ * @return String array of environment variables in format "var=value". Does not return {@code null}.
+ */
+ public static String[] getEnvp(ICConfigurationDescription cfgDescription) {
+ IEnvironmentVariableManager mngr = CCorePlugin.getDefault().getBuildEnvironmentManager();
+ IEnvironmentVariable[] vars = mngr.getVariables(cfgDescription, true);
+ // Convert into envp strings
+ List<String> strings = new ArrayList<String>(vars.length);
+ for (IEnvironmentVariable var : vars) {
+ strings.add(var.getName() + '=' + var.getValue());
+ }
+
+ return strings.toArray(new String[strings.size()]);
+ }
+
+ /**
+ * Convert duration to human friendly format.
+ */
+ @SuppressWarnings("nls")
+ private static String durationToString(long duration) {
+ String result = "";
+ long days = TimeUnit.MILLISECONDS.toDays(duration);
+ if (days > 0) {
+ result += days + "d,";
+ }
+ long hours = TimeUnit.MILLISECONDS.toHours(duration) % 24;
+ if (hours > 0) {
+ result += hours + "h:";
+ }
+ long minutes = TimeUnit.MILLISECONDS.toMinutes(duration) % 60;
+ if (minutes > 0) {
+ result += minutes + "m:";
+ }
+ long seconds = TimeUnit.MILLISECONDS.toSeconds(duration) % 60;
+ if (seconds > 0) {
+ result += seconds + "s.";
+ }
+ long milliseconds = TimeUnit.MILLISECONDS.toMillis(duration) % 1000;
+ result += milliseconds + "ms";
+
+ return result;
+ }
+
+ /**
+ * Supply timestamp to prefix informational messages.
+ */
+ @SuppressWarnings("nls")
+ private static String timestamp(long time) {
+ return new SimpleDateFormat("HH:mm:ss").format(new Date(time)) + " ";
+ }
+
+ /**
+ * Convert build kind to human friendly format.
+ */
+ private static String buildKindToString(int kind) {
+ switch (kind) {
+ case IncrementalProjectBuilder.FULL_BUILD:
+ return CCorePlugin.getResourceString("BuildRunnerHelper.build"); //$NON-NLS-1$
+ case IncrementalProjectBuilder.INCREMENTAL_BUILD:
+ return CCorePlugin.getResourceString("BuildRunnerHelper.incrementalBuild"); //$NON-NLS-1$
+ case IncrementalProjectBuilder.AUTO_BUILD:
+ return CCorePlugin.getResourceString("BuildRunnerHelper.autoBuild"); //$NON-NLS-1$
+ case IncrementalProjectBuilder.CLEAN_BUILD:
+ return CCorePlugin.getResourceString("BuildRunnerHelper.cleanBuild"); //$NON-NLS-1$
+ default:
+ return CCorePlugin.getResourceString("BuildRunnerHelper.build"); //$NON-NLS-1$
+ }
+ }
+}

Back to the top