/******************************************************************************* * Copyright (c) 2000, 2010 QNX Software 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: * QNX Software Systems - Initial API and implementation * Tianchao Li (tianchao.li@gmail.com) - arbitrary build directory (bug #136136) * Dmitry Kozlov (CodeSourcery) - Build error highlighting and navigation * Save build output (bug 294106) * Andrew Gvozdev (Quoin Inc) - Saving build output implemented in different way (bug 306222) *******************************************************************************/ package org.eclipse.cdt.make.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.Set; import java.util.Map.Entry; import org.eclipse.cdt.core.CCorePlugin; import org.eclipse.cdt.core.CommandLauncher; import org.eclipse.cdt.core.ErrorParserManager; import org.eclipse.cdt.core.ICommandLauncher; import org.eclipse.cdt.core.model.ICModelMarker; import org.eclipse.cdt.core.resources.ACBuilder; import org.eclipse.cdt.core.resources.IConsole; import org.eclipse.cdt.core.resources.RefreshScopeManager; import org.eclipse.cdt.internal.core.ConsoleOutputSniffer; import org.eclipse.cdt.make.internal.core.MakeMessages; import org.eclipse.cdt.make.internal.core.StreamMonitor; import org.eclipse.cdt.make.internal.core.scannerconfig.ScannerInfoConsoleParserFactory; import org.eclipse.cdt.utils.CommandLineUtil; 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.IResourceDelta; import org.eclipse.core.resources.IResourceRuleFactory; 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.OperationCanceledException; import org.eclipse.core.runtime.Path; import org.eclipse.core.runtime.QualifiedName; import org.eclipse.core.runtime.Status; import org.eclipse.core.runtime.SubProgressMonitor; import org.eclipse.core.runtime.jobs.ISchedulingRule; import org.eclipse.core.runtime.jobs.Job; /** * @noextend This class is not intended to be subclassed by clients. * @noinstantiate This class is not intended to be instantiated by clients. */ public class MakeBuilder extends ACBuilder { public final static String BUILDER_ID = MakeCorePlugin.getUniqueIdentifier() + ".makeBuilder"; //$NON-NLS-1$ public MakeBuilder() { } /** * @see IncrementalProjectBuilder#build */ @Override protected IProject[] build(int kind, @SuppressWarnings("rawtypes") Map args0, IProgressMonitor monitor) throws CoreException { @SuppressWarnings("unchecked") Map<String, String> args = args0; if (DEBUG_EVENTS) printEvent(kind, args); boolean bPerformBuild = true; IMakeBuilderInfo info = MakeCorePlugin.createBuildInfo(args, MakeBuilder.BUILDER_ID); if (!shouldBuild(kind, info)) { return new IProject[0]; } if (kind == IncrementalProjectBuilder.AUTO_BUILD) { IResourceDelta delta = getDelta(getProject()); if (delta != null) { IResource res = delta.getResource(); if (res != null) { bPerformBuild = res.getProject().equals(getProject()); } } else { bPerformBuild = false; } } if (bPerformBuild) { boolean isClean = invokeMake(kind, info, monitor); if (isClean) { forgetLastBuiltState(); } } checkCancel(monitor); return getProject().getReferencedProjects(); } @Override protected void clean(IProgressMonitor monitor) throws CoreException { if (DEBUG_EVENTS) printEvent(IncrementalProjectBuilder.CLEAN_BUILD, null); final IMakeBuilderInfo info = MakeCorePlugin.createBuildInfo(getProject(), BUILDER_ID); if (shouldBuild(CLEAN_BUILD, info)) { IResourceRuleFactory ruleFactory= ResourcesPlugin.getWorkspace().getRuleFactory(); final ISchedulingRule rule = ruleFactory.modifyRule(getProject()); Job backgroundJob = new Job("Standard Make Builder"){ //$NON-NLS-1$ /* (non-Javadoc) * @see org.eclipse.core.runtime.jobs.Job#run(org.eclipse.core.runtime.IProgressMonitor) */ @Override protected IStatus run(IProgressMonitor monitor) { try { ResourcesPlugin.getWorkspace().run(new IWorkspaceRunnable() { public void run(IProgressMonitor monitor) { invokeMake(CLEAN_BUILD, info, monitor); } }, rule, IWorkspace.AVOID_UPDATE, monitor); } catch (CoreException e) { return e.getStatus(); } IStatus returnStatus = Status.OK_STATUS; return returnStatus; } }; backgroundJob.setRule(rule); backgroundJob.schedule(); } } protected boolean invokeMake(int kind, IMakeBuilderInfo info, IProgressMonitor monitor) { boolean isClean = false; IProject currProject = getProject(); if (monitor == null) { monitor = new NullProgressMonitor(); } monitor.beginTask(MakeMessages.getString("MakeBuilder.Invoking_Make_Builder") + currProject.getName(), 100); //$NON-NLS-1$ try { IPath buildCommand = info.getBuildCommand(); if (buildCommand != null) { IConsole console = CCorePlugin.getDefault().getConsole(); console.start(currProject); OutputStream cos = console.getOutputStream(); // remove all markers for this project removeAllMarkers(currProject); URI workingDirectoryURI = MakeBuilderUtil.getBuildDirectoryURI(currProject, info); final String pathFromURI = EFSExtensionManager.getDefault().getPathFromURI(workingDirectoryURI); if(pathFromURI == null) { throw new CoreException(new Status(IStatus.ERROR, MakeCorePlugin.PLUGIN_ID, MakeMessages.getString("MakeBuilder.ErrorWorkingDirectory"), null)); //$NON-NLS-1$ } IPath workingDirectory = new Path(pathFromURI); String[] targets = getTargets(kind, info); if (targets.length != 0 && targets[targets.length - 1].equals(info.getCleanBuildTarget())) isClean = true; String errMsg = null; ICommandLauncher launcher = new CommandLauncher(); launcher.setProject(currProject); // Print the command for visual interaction. launcher.showCommand(true); // Set the environment HashMap<String, String> envMap = new HashMap<String, String>(); if (info.appendEnvironment()) { @SuppressWarnings({"unchecked", "rawtypes"}) Map<String, String> env = (Map)launcher.getEnvironment(); envMap.putAll(env); } // Add variables from build info envMap.putAll(info.getExpandedEnvironment()); List<String> strings= new ArrayList<String>(envMap.size()); Set<Entry<String, String>> entrySet = envMap.entrySet(); for (Entry<String, String> entry : entrySet) { StringBuffer buffer= new StringBuffer(entry.getKey()); buffer.append('=').append(entry.getValue()); strings.add(buffer.toString()); } String[] env = strings.toArray(new String[strings.size()]); String[] buildArguments = targets; if (info.isDefaultBuildCmd()) { if (!info.isStopOnError()) { buildArguments = new String[targets.length + 1]; buildArguments[0] = "-k"; //$NON-NLS-1$ System.arraycopy(targets, 0, buildArguments, 1, targets.length); } } else { String args = info.getBuildArguments(); if (args != null && !args.equals("")) { //$NON-NLS-1$ String[] newArgs = makeArray(args); buildArguments = new String[targets.length + newArgs.length]; System.arraycopy(newArgs, 0, buildArguments, 0, newArgs.length); System.arraycopy(targets, 0, buildArguments, newArgs.length, targets.length); } } // MakeRecon recon = new MakeRecon(buildCommand, buildArguments, env, workingDirectory, makeMonitor, cos); // recon.invokeMakeRecon(); // cos = recon; QualifiedName qName = new QualifiedName(MakeCorePlugin.getUniqueIdentifier(), "progressMonitor"); //$NON-NLS-1$ Integer last = (Integer)getProject().getSessionProperty(qName); if (last == null) { last = new Integer(100); } ErrorParserManager epm = new ErrorParserManager(getProject(), workingDirectoryURI, this, info.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 = ScannerInfoConsoleParserFactory.getMakeBuilderOutputSniffer( stdout, stderr, getProject(), workingDirectory, null, this, 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(MakeMessages.getString("MakeBuilder.Invoking_Command") + launcher.getCommandLine()); //$NON-NLS-1$ if (launcher.waitAndRead(consoleOut, consoleErr, new SubProgressMonitor(monitor, 0)) != ICommandLauncher.OK) errMsg = launcher.getErrorMessage(); monitor.subTask(MakeMessages.getString("MakeBuilder.Updating_project")); //$NON-NLS-1$ refreshProject(currProject); } else { errMsg = launcher.getErrorMessage(); } getProject().setSessionProperty(qName, !monitor.isCanceled() && !isClean ? new Integer(streamMon.getWorkDone()) : null); if (errMsg != null) { StringBuffer buf = new StringBuffer(buildCommand.toString() + " "); //$NON-NLS-1$ for (String buildArgument : buildArguments) { buf.append(buildArgument); buf.append(' '); } String errorDesc = MakeMessages.getFormattedString("MakeBuilder.buildError", buf.toString()); //$NON-NLS-1$ buf = new StringBuffer(errorDesc); buf.append(System.getProperty("line.separator", "\n")); //$NON-NLS-1$ //$NON-NLS-2$ buf.append("(").append(errMsg).append(")"); //$NON-NLS-1$ //$NON-NLS-2$ cos.write(buf.toString().getBytes()); cos.flush(); } stdout.close(); stderr.close(); monitor.subTask(MakeMessages.getString("MakeBuilder.Creating_Markers")); //$NON-NLS-1$ consoleOut.close(); consoleErr.close(); cos.close(); } } catch (Exception e) { MakeCorePlugin.log(e); } finally { monitor.done(); } return (isClean); } /** * Refresh project. Can be overridden to not call actual refresh or to do something else. * Method is called after build is complete. * * @since 6.0 */ protected void refreshProject(IProject project) { 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. // 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) { MakeCorePlugin.log(e); } } /** * Check whether the build has been canceled. */ public void checkCancel(IProgressMonitor monitor) { if (monitor != null && monitor.isCanceled()) throw new OperationCanceledException(); } protected boolean shouldBuild(int kind, IMakeBuilderInfo info) { switch (kind) { case IncrementalProjectBuilder.AUTO_BUILD : return info.isAutoBuildEnable(); case IncrementalProjectBuilder.INCREMENTAL_BUILD : // now treated as the same! case IncrementalProjectBuilder.FULL_BUILD : return info.isFullBuildEnabled() | info.isIncrementalBuildEnabled() ; case IncrementalProjectBuilder.CLEAN_BUILD : return info.isCleanBuildEnabled(); } return true; } protected String[] getTargets(int kind, IMakeBuilderInfo info) { String targets = ""; //$NON-NLS-1$ switch (kind) { case IncrementalProjectBuilder.AUTO_BUILD : targets = info.getAutoBuildTarget(); break; case IncrementalProjectBuilder.INCREMENTAL_BUILD : // now treated as the same! case IncrementalProjectBuilder.FULL_BUILD : targets = info.getIncrementalBuildTarget(); break; case IncrementalProjectBuilder.CLEAN_BUILD : targets = info.getCleanBuildTarget(); break; } return makeArray(targets); } // Turn the string into an array. private String[] makeArray(String string) { return CommandLineUtil.argumentsToArray(string); } private void removeAllMarkers(IProject currProject) throws CoreException { IWorkspace workspace = currProject.getWorkspace(); // remove all markers IMarker[] markers = currProject.findMarkers(ICModelMarker.C_MODEL_PROBLEM_MARKER, true, IResource.DEPTH_INFINITE); if (markers != null) { workspace.deleteMarkers(markers); } } }