/******************************************************************************* * Copyright (c) 2010, 2011 Wind River Systems 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: * Wind River Systems - Initial API and implementation * James Blackburn (Broadcom Corp.) *******************************************************************************/ package org.eclipse.cdt.managedbuilder.core; import java.io.IOException; import java.io.OutputStream; import java.net.URI; import java.util.ArrayList; import java.util.HashMap; import java.util.List; import java.util.Map; import java.util.Map.Entry; import org.eclipse.cdt.build.core.scannerconfig.CfgInfoContext; import org.eclipse.cdt.build.core.scannerconfig.ICfgScannerConfigBuilderInfo2Set; import org.eclipse.cdt.build.internal.core.scannerconfig2.CfgScannerConfigProfileManager; import org.eclipse.cdt.core.CCorePlugin; import org.eclipse.cdt.core.ErrorParserManager; import org.eclipse.cdt.core.ICommandLauncher; import org.eclipse.cdt.core.IMarkerGenerator; 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.core.ConsoleOutputSniffer; import org.eclipse.cdt.make.core.scannerconfig.IScannerConfigBuilderInfo2; import org.eclipse.cdt.make.core.scannerconfig.IScannerInfoCollector; import org.eclipse.cdt.make.core.scannerconfig.IScannerInfoConsoleParser; import org.eclipse.cdt.make.core.scannerconfig.InfoContext; import org.eclipse.cdt.make.internal.core.scannerconfig2.SCProfileInstance; import org.eclipse.cdt.make.internal.core.scannerconfig2.ScannerConfigProfile; import org.eclipse.cdt.make.internal.core.scannerconfig2.ScannerConfigProfileManager; import org.eclipse.cdt.managedbuilder.internal.core.ManagedMakeMessages; import org.eclipse.cdt.managedbuilder.macros.BuildMacroException; import org.eclipse.cdt.managedbuilder.macros.IBuildMacroProvider; import org.eclipse.cdt.newmake.internal.core.StreamMonitor; import org.eclipse.cdt.utils.CommandLineUtil; import org.eclipse.cdt.utils.EFSExtensionManager; import org.eclipse.cdt.utils.PathUtil; 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.CoreException; import org.eclipse.core.runtime.IPath; import org.eclipse.core.runtime.IProgressMonitor; import org.eclipse.core.runtime.IStatus; import org.eclipse.core.runtime.NullProgressMonitor; import org.eclipse.core.runtime.Path; import org.eclipse.core.runtime.QualifiedName; import org.eclipse.core.runtime.Status; import org.eclipse.core.runtime.SubProgressMonitor; /** * @author dschaefer * @since 8.0 */ public class ExternalBuildRunner extends AbstractBuildRunner { private static final String TYPE_CLEAN = "ManagedMakeBuilder.type.clean"; //$NON-NLS-1$ private static final String TYPE_INC = "ManagedMakeBuider.type.incremental"; //$NON-NLS-1$ private static final String CONSOLE_HEADER = "ManagedMakeBuilder.message.console.header"; //$NON-NLS-1$ private static final String WARNING_UNSUPPORTED_CONFIGURATION = "ManagedMakeBuilder.warning.unsupported.configuration"; //$NON-NLS-1$ private static final String NEWLINE = System.getProperty("line.separator", "\n"); //$NON-NLS-1$ //$NON-NLS-2$ private static final String PATH_ENV = "PATH"; //$NON-NLS-1$ @Override public boolean invokeBuild(int kind, IProject project, IConfiguration configuration, IBuilder builder, IConsole console, IMarkerGenerator markerGenerator, IncrementalProjectBuilder projectBuilder, IProgressMonitor monitor) throws CoreException { return invokeExternalBuild(kind, project, configuration, builder, console, markerGenerator, projectBuilder, monitor); } protected boolean invokeExternalBuild(int kind, IProject project, IConfiguration configuration, IBuilder builder, IConsole console, IMarkerGenerator markerGenerator, IncrementalProjectBuilder projectBuilder, IProgressMonitor monitor) throws CoreException { boolean isClean = false; if (monitor == null) { monitor = new NullProgressMonitor(); } monitor.beginTask(ManagedMakeMessages.getResourceString("MakeBuilder.Invoking_Make_Builder") + project.getName(), 100); //$NON-NLS-1$ try { IPath buildCommand = builder.getBuildCommand(); if (buildCommand != null) { OutputStream cos = console.getOutputStream(); StringBuffer buf = new StringBuffer(); String[] consoleHeader = new String[3]; switch (kind) { case IncrementalProjectBuilder.FULL_BUILD: case IncrementalProjectBuilder.INCREMENTAL_BUILD: case IncrementalProjectBuilder.AUTO_BUILD: consoleHeader[0] = ManagedMakeMessages.getResourceString(TYPE_INC); break; case IncrementalProjectBuilder.CLEAN_BUILD: consoleHeader[0] = ManagedMakeMessages.getResourceString(TYPE_CLEAN); break; } consoleHeader[1] = configuration.getName(); consoleHeader[2] = project.getName(); buf.append(NEWLINE); buf.append(ManagedMakeMessages.getFormattedString(CONSOLE_HEADER, consoleHeader)).append(NEWLINE); buf.append(NEWLINE); if(!configuration.isSupported()){ String unsupportedToolchainMsg = ManagedMakeMessages.getFormattedString(WARNING_UNSUPPORTED_CONFIGURATION, new String[] { configuration.getName(), configuration.getToolChain().getName() }); buf.append(unsupportedToolchainMsg).append(NEWLINE); buf.append(NEWLINE); } cos.write(buf.toString().getBytes()); cos.flush(); // remove all markers for this project IWorkspace workspace = project.getWorkspace(); IMarker[] markers = project.findMarkers(ICModelMarker.C_MODEL_PROBLEM_MARKER, true, IResource.DEPTH_INFINITE); if (markers != null) workspace.deleteMarkers(markers); URI workingDirectoryURI = ManagedBuildManager.getBuildLocationURI(configuration, builder); final String pathFromURI = EFSExtensionManager.getDefault().getPathFromURI(workingDirectoryURI); if(pathFromURI == null) { throw new CoreException(new Status(IStatus.ERROR, ManagedBuilderCorePlugin.PLUGIN_ID, ManagedMakeMessages.getString("ManagedMakeBuilder.message.error"), null)); //$NON-NLS-1$ } IPath workingDirectory = new Path(pathFromURI); String[] targets = getTargets(kind, builder); if (targets.length != 0 && targets[targets.length - 1].equals(builder.getCleanBuildTarget())) isClean = true; String errMsg = null; ICommandLauncher launcher = builder.getCommandLauncher(); launcher.setProject(project); // Print the command for visual interaction. launcher.showCommand(true); // Set the environment Map<String, String> envMap = getEnvironment(builder); String[] env = getEnvStrings(envMap); String[] buildArguments = targets; String[] newArgs = CommandLineUtil.argumentsToArray(builder.getBuildArguments()); buildArguments = new String[targets.length + newArgs.length]; System.arraycopy(newArgs, 0, buildArguments, 0, newArgs.length); System.arraycopy(targets, 0, buildArguments, newArgs.length, targets.length); QualifiedName qName = new QualifiedName(ManagedBuilderCorePlugin.getUniqueIdentifier(), "progressMonitor"); //$NON-NLS-1$ Integer last = (Integer)project.getSessionProperty(qName); if (last == null) { last = new Integer(100); } ErrorParserManager epm = new ErrorParserManager(project, workingDirectoryURI, markerGenerator, builder.getErrorParsers()); epm.setOutputStream(cos); StreamMonitor streamMon = new StreamMonitor(new SubProgressMonitor(monitor, 100), epm, last.intValue()); OutputStream stdout = streamMon; OutputStream stderr = streamMon; // Sniff console output for scanner info ConsoleOutputSniffer sniffer = createBuildOutputSniffer(stdout, stderr, project, configuration, workingDirectory, markerGenerator, null); OutputStream consoleOut = (sniffer == null ? stdout : sniffer.getOutputStream()); OutputStream consoleErr = (sniffer == null ? stderr : sniffer.getErrorStream()); Process p = launcher.execute(buildCommand, buildArguments, env, workingDirectory, monitor); if (p != null) { try { // Close the input of the Process explicitly. // We will never write to it. p.getOutputStream().close(); } catch (IOException e) { } // Before launching give visual cues via the monitor monitor.subTask(ManagedMakeMessages.getResourceString("MakeBuilder.Invoking_Command") + launcher.getCommandLine()); //$NON-NLS-1$ if (launcher.waitAndRead(consoleOut, consoleErr, new SubProgressMonitor(monitor, 0)) != ICommandLauncher.OK) errMsg = launcher.getErrorMessage(); monitor.subTask(ManagedMakeMessages.getResourceString("MakeBuilder.Updating_project")); //$NON-NLS-1$ try { // 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. // TODO should only refresh output folders //project.refreshLocal(IResource.DEPTH_INFINITE, null); // use the refresh scope manager to refresh RefreshScopeManager refreshManager = RefreshScopeManager.getInstance(); IWorkspaceRunnable runnable = refreshManager.getRefreshRunnable(project); ResourcesPlugin.getWorkspace().run(runnable, null, IWorkspace.AVOID_UPDATE, null); } catch (CoreException e) { } } else { buf = new StringBuffer(launcher.getCommandLine()).append(NEWLINE); errMsg = launcher.getErrorMessage(); } project.setSessionProperty(qName, !monitor.isCanceled() && !isClean ? new Integer(streamMon.getWorkDone()) : null); if (errMsg != null) { // Launching failed, trying to figure out possible cause String errorPrefix = ManagedMakeMessages.getResourceString("ManagedMakeBuilder.error.prefix"); //$NON-NLS-1$ String buildCommandStr = buildCommand.toString(); String envPath = envMap.get(PATH_ENV); if (envPath==null) { envPath = System.getenv(PATH_ENV); } if (PathUtil.findProgramLocation(buildCommandStr, envPath)==null) { buf.append(errMsg).append(NEWLINE); errMsg = ManagedMakeMessages.getFormattedString("ManagedMakeBuilder.message.program.not.in.path", buildCommandStr); //$NON-NLS-1$ buf.append(errorPrefix).append(errMsg).append(NEWLINE); buf.append(NEWLINE); buf.append(PATH_ENV+"=["+envPath+"]").append(NEWLINE); //$NON-NLS-1$//$NON-NLS-2$ } else { buf.append(errorPrefix).append(errMsg).append(NEWLINE); } consoleErr.write(buf.toString().getBytes()); consoleErr.flush(); } buf = new StringBuffer(NEWLINE); buf.append(ManagedMakeMessages.getResourceString("ManagedMakeBuilder.message.build.finished")).append(NEWLINE); //$NON-NLS-1$ consoleOut.write(buf.toString().getBytes()); stdout.close(); stderr.close(); monitor.subTask(ManagedMakeMessages.getResourceString("MakeBuilder.Creating_Markers")); //$NON-NLS-1$ consoleOut.close(); consoleErr.close(); cos.close(); } } catch (Exception e) { ManagedBuilderCorePlugin.log(e); throw new CoreException(new Status(IStatus.ERROR, ManagedBuilderCorePlugin.getUniqueIdentifier(), e.getLocalizedMessage(), e)); } finally { monitor.done(); } return (isClean); } protected String[] getTargets(int kind, IBuilder builder) { String targetsArray[] = null; if(kind != IncrementalProjectBuilder.CLEAN_BUILD && !builder.isCustomBuilder() && builder.isManagedBuildOn()){ IConfiguration cfg = builder.getParent().getParent(); String preBuildStep = cfg.getPrebuildStep(); try { preBuildStep = ManagedBuildManager.getBuildMacroProvider().resolveValueToMakefileFormat( preBuildStep, "", //$NON-NLS-1$ " ", //$NON-NLS-1$ IBuildMacroProvider.CONTEXT_CONFIGURATION, cfg); } catch (BuildMacroException e) { } if(preBuildStep != null && preBuildStep.length() != 0){ targetsArray = new String[]{"pre-build", "main-build"}; //$NON-NLS-1$ //$NON-NLS-2$ } } if(targetsArray == null){ String targets = ""; //$NON-NLS-1$ switch (kind) { case IncrementalProjectBuilder.AUTO_BUILD : targets = builder.getAutoBuildTarget(); break; case IncrementalProjectBuilder.INCREMENTAL_BUILD : // now treated as the same! case IncrementalProjectBuilder.FULL_BUILD : targets = builder.getIncrementalBuildTarget(); break; case IncrementalProjectBuilder.CLEAN_BUILD : targets = builder.getCleanBuildTarget(); break; } targetsArray = CommandLineUtil.argumentsToArray(targets); } return targetsArray; } protected Map<String, String> getEnvironment(IBuilder builder) throws CoreException { Map<String, String> envMap = new HashMap<String, String>(); if (builder.appendEnvironment()) { ICConfigurationDescription cfgDes = ManagedBuildManager.getDescriptionForConfiguration(builder.getParent().getParent()); IEnvironmentVariableManager mngr = CCorePlugin.getDefault().getBuildEnvironmentManager(); IEnvironmentVariable[] vars = mngr.getVariables(cfgDes, true); for (IEnvironmentVariable var : vars) { envMap.put(var.getName(), var.getValue()); } } // Add variables from build info Map<String, String> builderEnv = builder.getExpandedEnvironment(); if (builderEnv != null) envMap.putAll(builderEnv); return envMap; } protected static String[] getEnvStrings(Map<String, String> env) { // Convert into env strings List<String> strings= new ArrayList<String>(env.size()); for (Entry<String, String> entry : env.entrySet()) { StringBuffer buffer= new StringBuffer(entry.getKey()); buffer.append('=').append(entry.getValue()); strings.add(buffer.toString()); } return strings.toArray(new String[strings.size()]); } private ConsoleOutputSniffer createBuildOutputSniffer(OutputStream outputStream, OutputStream errorStream, IProject project, IConfiguration cfg, IPath workingDirectory, IMarkerGenerator markerGenerator, IScannerInfoCollector collector){ ICfgScannerConfigBuilderInfo2Set container = CfgScannerConfigProfileManager.getCfgScannerConfigBuildInfo(cfg); Map<CfgInfoContext, IScannerConfigBuilderInfo2> map = container.getInfoMap(); List<IScannerInfoConsoleParser> clParserList = new ArrayList<IScannerInfoConsoleParser>(); if(container.isPerRcTypeDiscovery()){ for (IResourceInfo rcInfo : cfg.getResourceInfos()) { ITool tools[]; if(rcInfo instanceof IFileInfo){ tools = ((IFileInfo)rcInfo).getToolsToInvoke(); } else { tools = ((IFolderInfo)rcInfo).getFilteredTools(); } for (ITool tool : tools) { IInputType[] types = tool.getInputTypes(); if(types.length != 0){ for (IInputType type : types) { CfgInfoContext c = new CfgInfoContext(rcInfo, tool, type); contributeToConsoleParserList(project, map, c, workingDirectory, markerGenerator, collector, clParserList); } } else { CfgInfoContext c = new CfgInfoContext(rcInfo, tool, null); contributeToConsoleParserList(project, map, c, workingDirectory, markerGenerator, collector, clParserList); } } } } if(clParserList.size() == 0){ contributeToConsoleParserList(project, map, new CfgInfoContext(cfg), workingDirectory, markerGenerator, collector, clParserList); } if(clParserList.size() != 0){ return new ConsoleOutputSniffer(outputStream, errorStream, clParserList.toArray(new IScannerInfoConsoleParser[clParserList.size()])); } return null; } private boolean contributeToConsoleParserList( IProject project, Map<CfgInfoContext, IScannerConfigBuilderInfo2> map, CfgInfoContext context, IPath workingDirectory, IMarkerGenerator markerGenerator, IScannerInfoCollector collector, List<IScannerInfoConsoleParser> parserList){ IScannerConfigBuilderInfo2 info = map.get(context); InfoContext ic = context.toInfoContext(); boolean added = false; if (info != null && info.isAutoDiscoveryEnabled() && info.isBuildOutputParserEnabled()) { String id = info.getSelectedProfileId(); ScannerConfigProfile profile = ScannerConfigProfileManager.getInstance().getSCProfileConfiguration(id); if(profile.getBuildOutputProviderElement() != null){ // get the make builder console parser SCProfileInstance profileInstance = ScannerConfigProfileManager.getInstance(). getSCProfileInstance(project, ic, id); IScannerInfoConsoleParser clParser = profileInstance.createBuildOutputParser(); if (collector == null) { collector = profileInstance.getScannerInfoCollector(); } if(clParser != null){ clParser.startup(project, workingDirectory, collector, info.isProblemReportingEnabled() ? markerGenerator : null); parserList.add(clParser); added = true; } } } return added; } }