/******************************************************************************* * Copyright (c) 2000, 2016 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) * IBM Corporation *******************************************************************************/ package org.eclipse.cdt.make.core; import java.io.IOException; import java.net.URI; import java.util.ArrayList; import java.util.HashMap; import java.util.List; import java.util.Map; 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.IConsoleParser; import org.eclipse.cdt.core.language.settings.providers.ICBuildOutputParser; import org.eclipse.cdt.core.language.settings.providers.ILanguageSettingsProvider; import org.eclipse.cdt.core.language.settings.providers.ILanguageSettingsProvidersKeeper; import org.eclipse.cdt.core.language.settings.providers.IWorkingDirectoryTracker; import org.eclipse.cdt.core.language.settings.providers.LanguageSettingsManager; import org.eclipse.cdt.core.language.settings.providers.ScannerDiscoveryLegacySupport; import org.eclipse.cdt.core.model.CoreModel; import org.eclipse.cdt.core.resources.ACBuilder; import org.eclipse.cdt.core.resources.IConsole; import org.eclipse.cdt.core.settings.model.ICConfigurationDescription; import org.eclipse.cdt.core.settings.model.ICProjectDescription; import org.eclipse.cdt.internal.core.BuildRunnerHelper; import org.eclipse.cdt.make.core.scannerconfig.IScannerInfoConsoleParser; import org.eclipse.cdt.make.internal.core.MakeMessages; import org.eclipse.cdt.make.internal.core.scannerconfig.ScannerInfoConsoleParserFactory; import org.eclipse.cdt.utils.CommandLineUtil; 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.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$ 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_DELETE_MARKERS = 1 * PROGRESS_MONITOR_SCALE; private static final int TICKS_EXECUTE_COMMAND = 1 * PROGRESS_MONITOR_SCALE; private BuildRunnerHelper buildRunnerHelper = null; 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() { @Override 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(); } } /** * Run build command. * * @param kind - kind of build, constant defined by {@link IncrementalProjectBuilder} * @param info - builder info * @param monitor - progress monitor in the initial state where {@link IProgressMonitor#beginTask(String, int)} * has not been called yet. * @return {@code true} if the build purpose is to clean, {@code false} otherwise. */ protected boolean invokeMake(int kind, IMakeBuilderInfo info, IProgressMonitor monitor) { boolean isClean = false; IProject project = getProject(); buildRunnerHelper = new BuildRunnerHelper(project); try { if (monitor == null) { monitor = new NullProgressMonitor(); } monitor.beginTask(MakeMessages.getString("MakeBuilder.Invoking_Make_Builder") + project.getName(), //$NON-NLS-1$ TICKS_STREAM_PROGRESS_MONITOR + TICKS_DELETE_MARKERS + TICKS_EXECUTE_COMMAND); IPath buildCommand = info.getBuildCommand(); if (buildCommand != null) { IConsole console = CCorePlugin.getDefault().getConsole(); console.start(project); // Prepare launch parameters for BuildRunnerHelper ICommandLauncher launcher = new CommandLauncher(); String[] targets = getTargets(kind, info); if (targets.length != 0 && targets[targets.length - 1].equals(info.getCleanBuildTarget())) isClean = true; boolean isOnlyClean = isClean && (targets.length == 1); String[] args = getCommandArguments(info, targets); URI workingDirectoryURI = MakeBuilderUtil.getBuildDirectoryURI(project, info); HashMap<String, String> envMap = getEnvironment(launcher, info); String[] envp = BuildRunnerHelper.envMapToEnvp(envMap); String[] errorParsers = info.getErrorParsers(); ErrorParserManager epm = new ErrorParserManager(getProject(), workingDirectoryURI, this, errorParsers); List<IConsoleParser> parsers = new ArrayList<IConsoleParser>(); if (!isOnlyClean) { ICProjectDescription prjDescription = CoreModel.getDefault().getProjectDescription(project); if (prjDescription != null) { ICConfigurationDescription cfgDescription = prjDescription.getActiveConfiguration(); collectLanguageSettingsConsoleParsers(cfgDescription, epm, parsers); } if (ScannerDiscoveryLegacySupport.isLegacyScannerDiscoveryOn(project)) { IScannerInfoConsoleParser parserSD = ScannerInfoConsoleParserFactory.getScannerInfoConsoleParser(project, workingDirectoryURI, this); parsers.add(parserSD); } } buildRunnerHelper.setLaunchParameters(launcher, buildCommand, args, workingDirectoryURI, envp); buildRunnerHelper.prepareStreams(epm, parsers, console, new SubProgressMonitor(monitor, TICKS_STREAM_PROGRESS_MONITOR, SubProgressMonitor.PREPEND_MAIN_LABEL_TO_SUBTASK)); buildRunnerHelper.removeOldMarkers(project, new SubProgressMonitor(monitor, TICKS_DELETE_MARKERS, SubProgressMonitor.PREPEND_MAIN_LABEL_TO_SUBTASK)); buildRunnerHelper.greeting(kind); int state = buildRunnerHelper.build(new SubProgressMonitor(monitor, TICKS_EXECUTE_COMMAND, SubProgressMonitor.PREPEND_MAIN_LABEL_TO_SUBTASK)); buildRunnerHelper.close(); buildRunnerHelper.goodbye(); if (state != ICommandLauncher.ILLEGAL_COMMAND) { refreshProject(project); } } else { String msg = MakeMessages.getFormattedString("MakeBuilder.message.undefined.build.command", project.getName()); //$NON-NLS-1$ throw new CoreException(new Status(IStatus.ERROR, MakeCorePlugin.PLUGIN_ID, msg, new Exception())); } } catch (Exception e) { MakeCorePlugin.log(e); } finally { try { buildRunnerHelper.close(); } catch (IOException e) { MakeCorePlugin.log(e); } monitor.done(); } return isClean; } private HashMap<String, String> getEnvironment(ICommandLauncher launcher, IMakeBuilderInfo info) throws CoreException { HashMap<String, String> envMap = new HashMap<String, String>(); if (info.appendEnvironment()) { @SuppressWarnings({"unchecked", "rawtypes"}) Map<String, String> env = (Map)launcher.getEnvironment(); envMap.putAll(env); } envMap.putAll(info.getExpandedEnvironment()); return envMap; } private String[] getCommandArguments(IMakeBuilderInfo info, String[] targets) { String[] args = targets; if (info.isDefaultBuildCmd()) { if (!info.isStopOnError()) { args = new String[targets.length + 1]; args[0] = "-k"; //$NON-NLS-1$ System.arraycopy(targets, 0, args, 1, targets.length); } } else { String argsStr = info.getBuildArguments(); if (argsStr != null && !argsStr.isEmpty()) { String[] newArgs = makeArray(argsStr); args = new String[targets.length + newArgs.length]; System.arraycopy(newArgs, 0, args, 0, newArgs.length); System.arraycopy(targets, 0, args, newArgs.length, targets.length); } } return args; } /** * 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) { if (buildRunnerHelper != null) { buildRunnerHelper.refreshProject(null, null); } } /** * 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 static void collectLanguageSettingsConsoleParsers(ICConfigurationDescription cfgDescription, IWorkingDirectoryTracker cwdTracker, List<IConsoleParser> parsers) { if (cfgDescription instanceof ILanguageSettingsProvidersKeeper) { List<ILanguageSettingsProvider> lsProviders = ((ILanguageSettingsProvidersKeeper) cfgDescription).getLanguageSettingProviders(); for (ILanguageSettingsProvider lsProvider : lsProviders) { ILanguageSettingsProvider rawProvider = LanguageSettingsManager.getRawProvider(lsProvider); if (rawProvider instanceof ICBuildOutputParser) { ICBuildOutputParser consoleParser = (ICBuildOutputParser) rawProvider; try { consoleParser.startup(cfgDescription, cwdTracker); parsers.add(consoleParser); } catch (CoreException e) { MakeCorePlugin.log(new Status(IStatus.ERROR, MakeCorePlugin.PLUGIN_ID, "Language Settings Provider failed to start up", e)); //$NON-NLS-1$ } } } } } }