/******************************************************************************* * Copyright (c) 2009, 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 * Liviu Ionescu - Bug 413678: trigger discovery after command line change *******************************************************************************/ package org.eclipse.cdt.managedbuilder.language.settings.providers; import java.io.IOException; import java.io.OutputStream; import java.net.URI; import java.net.URL; import java.util.ArrayList; import java.util.Arrays; import java.util.HashMap; import java.util.HashSet; import java.util.List; import java.util.Map; import java.util.Map.Entry; import java.util.Set; 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.IMarkerGenerator; import org.eclipse.cdt.core.ProblemMarkerInfo; import org.eclipse.cdt.core.cdtvariables.ICdtVariableManager; import org.eclipse.cdt.core.envvar.EnvironmentVariable; import org.eclipse.cdt.core.envvar.IEnvironmentVariable; import org.eclipse.cdt.core.envvar.IEnvironmentVariableManager; import org.eclipse.cdt.core.language.settings.providers.ICBuildOutputParser; import org.eclipse.cdt.core.language.settings.providers.ICListenerAgent; import org.eclipse.cdt.core.language.settings.providers.IWorkingDirectoryTracker; import org.eclipse.cdt.core.model.ILanguage; import org.eclipse.cdt.core.model.ILanguageDescriptor; import org.eclipse.cdt.core.model.LanguageManager; import org.eclipse.cdt.core.resources.IConsole; import org.eclipse.cdt.core.settings.model.ICConfigurationDescription; import org.eclipse.cdt.core.settings.model.ICLanguageSettingEntry; import org.eclipse.cdt.core.settings.model.ICProjectDescription; import org.eclipse.cdt.internal.core.BuildRunnerHelper; import org.eclipse.cdt.internal.core.XmlUtil; import org.eclipse.cdt.internal.core.envvar.EnvironmentVariableManager; import org.eclipse.cdt.managedbuilder.core.ManagedBuilderCorePlugin; import org.eclipse.cdt.managedbuilder.internal.core.ManagedMakeMessages; import org.eclipse.cdt.utils.CommandLineUtil; import org.eclipse.cdt.utils.PathUtil; import org.eclipse.cdt.utils.envvar.IEnvironmentChangeEvent; import org.eclipse.cdt.utils.envvar.IEnvironmentChangeListener; import org.eclipse.core.resources.IMarker; import org.eclipse.core.resources.IResource; import org.eclipse.core.resources.IWorkspaceRoot; import org.eclipse.core.resources.ResourcesPlugin; import org.eclipse.core.resources.WorkspaceJob; 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.MultiStatus; import org.eclipse.core.runtime.NullProgressMonitor; import org.eclipse.core.runtime.OperationCanceledException; import org.eclipse.core.runtime.Path; import org.eclipse.core.runtime.Platform; import org.eclipse.core.runtime.Status; import org.eclipse.core.runtime.SubProgressMonitor; import org.eclipse.core.runtime.content.IContentType; import org.eclipse.core.runtime.jobs.ISchedulingRule; import org.eclipse.core.runtime.jobs.Job; import org.w3c.dom.Element; /** * Abstract parser capable to execute compiler command printing built-in compiler * specs and parse built-in language settings out of it. * <p> * <strong>EXPERIMENTAL</strong>. This class interface is not stable yet as * it is not currently (CDT 8.1, Juno) clear how it may need to be used in future. * There is no guarantee that this API will work or that it will remain the same. * Please do not use this API without consulting with the CDT team. * </p> * @noextend This class is not intended to be subclassed by clients. * * @since 8.1 */ public abstract class AbstractBuiltinSpecsDetector extends AbstractLanguageSettingsOutputScanner implements ICListenerAgent, IEnvironmentChangeListener { public static final String JOB_FAMILY_BUILTIN_SPECS_DETECTOR = "org.eclipse.cdt.managedbuilder.AbstractBuiltinSpecsDetector"; //$NON-NLS-1$ protected static final String COMPILER_MACRO = "${COMMAND}"; //$NON-NLS-1$ /** @since 8.3 */ protected static final String FLAGS_MACRO = "${FLAGS}"; //$NON-NLS-1$ protected static final String SPEC_FILE_MACRO = "${INPUTS}"; //$NON-NLS-1$ protected static final String SPEC_EXT_MACRO = "${EXT}"; //$NON-NLS-1$ protected static final String SPEC_FILE_BASE = "spec"; //$NON-NLS-1$ private static final String CDT_MANAGEDBUILDER_UI_PLUGIN_ID = "org.eclipse.cdt.managedbuilder.ui"; //$NON-NLS-1$ private static final String SCANNER_DISCOVERY_CONSOLE = "org.eclipse.cdt.managedbuilder.ScannerDiscoveryConsole"; //$NON-NLS-1$ private static final String SCANNER_DISCOVERY_GLOBAL_CONSOLE = "org.eclipse.cdt.managedbuilder.ScannerDiscoveryGlobalConsole"; //$NON-NLS-1$ private static final String DEFAULT_CONSOLE_ICON = "icons/obj16/inspect_sys.gif"; //$NON-NLS-1$ private static final String GMAKE_ERROR_PARSER_ID = "org.eclipse.cdt.core.GmakeErrorParser"; //$NON-NLS-1$ private static final String ATTR_PARAMETER = "parameter"; //$NON-NLS-1$ private static final String ATTR_CONSOLE = "console"; //$NON-NLS-1$ private static final String ATTR_ENV_HASH = "env-hash"; //$NON-NLS-1$ private static final String ENV_LANGUAGE = "LANGUAGE"; //$NON-NLS-1$ private static final String ENV_LC_ALL = "LC_ALL"; //$NON-NLS-1$ private static final String ENV_PATH = "PATH"; //$NON-NLS-1$ private static final int MONITOR_SCALE = 100; private static final int TICKS_REMOVE_MARKERS = 1 * MONITOR_SCALE; private static final int TICKS_RUN_FOR_ONE_LANGUAGE = 10 * MONITOR_SCALE; private static final int TICKS_SERIALIZATION = 1 * MONITOR_SCALE; private static final int TICKS_OUTPUT_PARSING = 1 * MONITOR_SCALE; private static final int TICKS_EXECUTE_COMMAND = 1 * MONITOR_SCALE; protected URI mappedRootURI = null; protected URI buildDirURI = null; protected java.io.File specFile = null; protected boolean preserveSpecFile = false; /** @since 8.2 */ protected IEnvironmentVariableManager envMngr = null; /** @since 8.2 */ protected volatile Map<String, String> environmentMap = null; protected List<ICLanguageSettingEntry> detectedSettingEntries = null; protected int collected = 0; protected volatile boolean isExecuted = false; private static final int HASH_NOT_INITIALIZED = -1; private long envPathHash = HASH_NOT_INITIALIZED; private BuildRunnerHelper buildRunnerHelper; private SDMarkerGenerator markerGenerator = new SDMarkerGenerator(); private boolean isConsoleEnabled = false; private String currentCommandResolved = null; private class SDMarkerGenerator implements IMarkerGenerator { // Reuse scanner discovery markers defined in org.eclipse.cdt.managedbuilder.core plugin.xml protected static final String SCANNER_DISCOVERY_PROBLEM_MARKER = "org.eclipse.cdt.managedbuilder.core.scanner.discovery.problem"; //$NON-NLS-1$ protected static final String ATTR_PROVIDER = "provider"; //$NON-NLS-1$ @Override public void addMarker(IResource rc, int lineNumber, String errorDesc, int severity, String errorVar) { ProblemMarkerInfo info = new ProblemMarkerInfo(rc, lineNumber, errorDesc, severity, errorVar); addMarker(info); } @Override public void addMarker(final ProblemMarkerInfo problemMarkerInfo) { final String providerName = getName(); final String providerId = getId(); // Add markers in a job to avoid deadlocks Job markerJob = new Job(ManagedMakeMessages.getResourceString("AbstractBuiltinSpecsDetector.AddScannerDiscoveryMarkers")) { //$NON-NLS-1$ @Override protected IStatus run(IProgressMonitor monitor) { // Avoid duplicates as different languages can generate identical errors try { IMarker[] markers = problemMarkerInfo.file.findMarkers(SDMarkerGenerator.SCANNER_DISCOVERY_PROBLEM_MARKER, false, IResource.DEPTH_ZERO); for (IMarker marker : markers) { int sev = ((Integer) marker.getAttribute(IMarker.SEVERITY)).intValue(); if (sev == problemMarkerInfo.severity) { String msg = (String) marker.getAttribute(IMarker.MESSAGE); if (msg != null && msg.equals(problemMarkerInfo.description)) { return Status.OK_STATUS; } } } } catch (CoreException e) { return new Status(IStatus.ERROR, ManagedBuilderCorePlugin.PLUGIN_ID, "Error checking markers.", e); //$NON-NLS-1$ } try { IMarker marker = problemMarkerInfo.file.createMarker(SDMarkerGenerator.SCANNER_DISCOVERY_PROBLEM_MARKER); marker.setAttribute(IMarker.MESSAGE, problemMarkerInfo.description); marker.setAttribute(IMarker.SEVERITY, problemMarkerInfo.severity); marker.setAttribute(SDMarkerGenerator.ATTR_PROVIDER, providerId); if (problemMarkerInfo.file instanceof IWorkspaceRoot) { String msgPreferences = ManagedMakeMessages.getFormattedString("AbstractBuiltinSpecsDetector.ScannerDiscoveryMarkerLocationPreferences", providerName); //$NON-NLS-1$ marker.setAttribute(IMarker.LOCATION, msgPreferences); } else { String msgProperties = ManagedMakeMessages.getFormattedString("AbstractBuiltinSpecsDetector.ScannerDiscoveryMarkerLocationProperties", providerName); //$NON-NLS-1$ marker.setAttribute(IMarker.LOCATION, msgProperties); } } catch (CoreException e) { return new Status(IStatus.ERROR, ManagedBuilderCorePlugin.PLUGIN_ID, "Error adding markers.", e); //$NON-NLS-1$ } return Status.OK_STATUS; } }; markerJob.setRule(problemMarkerInfo.file); markerJob.schedule(); } /** * Delete markers previously set by this provider for the resource. * * @param rc - resource to check markers. */ public void deleteMarkers(IResource rc) { String providerId = getId(); try { IMarker[] markers = rc.findMarkers(SCANNER_DISCOVERY_PROBLEM_MARKER, false, IResource.DEPTH_ZERO); for (IMarker marker : markers) { if (providerId.equals(marker.getAttribute(ATTR_PROVIDER))) { marker.delete(); } } } catch (CoreException e) { ManagedBuilderCorePlugin.log(new Status(IStatus.ERROR, ManagedBuilderCorePlugin.PLUGIN_ID, "Error deleting markers.", e)); //$NON-NLS-1$ } } } /** * Internal ICConsoleParser to handle individual run for one language. */ private class ConsoleParserAdapter implements ICBuildOutputParser { @Override public void startup(ICConfigurationDescription cfgDescription, IWorkingDirectoryTracker cwdTracker) throws CoreException { AbstractBuiltinSpecsDetector.this.cwdTracker = cwdTracker; } @Override public boolean processLine(String line) { return AbstractBuiltinSpecsDetector.this.processLine(line); } @Override public void shutdown() { AbstractBuiltinSpecsDetector.this.cwdTracker = null; } } /** * Compiler command without arguments. This value is used to replace macro ${COMMAND}. * In particular, this method is implemented in {@link ToolchainBuiltinSpecsDetector} * which retrieves the command from tool-chain. * * @param languageId - language ID. * @return compiler command without arguments, i.e. compiler program. */ protected abstract String getCompilerCommand(String languageId); /** * The command to run. Some macros could be specified in there: * <ul> * <b>${COMMAND}</b> - compiler command without arguments (compiler program). * Normally would come from the tool-chain.<br> * <b>${INPUTS}</b> - path to spec file which will be placed in workspace area.<br> * <b>${EXT}</b> - file extension calculated from language ID. * </ul> * The parameter could be taken from the extension * in {@code plugin.xml} or from property file. * * @return the command to run or empty string if command is not defined. */ public String getCommand() { return getProperty(ATTR_PARAMETER); } /** * Set custom command for the provider. See {@link #getCommand()}. * @param command - value of custom command to set. */ public void setCommand(String command) { setProperty(ATTR_PARAMETER, command); } /** * @return {@code true} if console output is enabled for this provider, {@code false} otherwise. */ public boolean isConsoleEnabled() { return isConsoleEnabled; } /** * Enable or disable console output for this provider. * * @param enable - {@code true} to enable console output or {@code false} to disable. */ public void setConsoleEnabled(boolean enable) { isConsoleEnabled = enable; } /** * Expand macros specified in the compiler command. See {@link #getCommand()} for * the recognized list of macros. * * @param languageId - language ID. * @return - resolved command to run. * @throws CoreException if something goes wrong. */ protected String resolveCommand(String languageId) throws CoreException { String cmd = getCommand(); if (cmd != null) { if (cmd.contains(COMPILER_MACRO)) { String compiler = getCompilerCommand(languageId); if (compiler != null) cmd = cmd.replace(COMPILER_MACRO, compiler); } if (cmd.contains(FLAGS_MACRO)) { String flags = getToolOptions(languageId); if (flags != null) cmd = cmd.replace(FLAGS_MACRO, flags); } if (cmd.contains(SPEC_FILE_MACRO)) { String specFileName = getSpecFile(languageId); if (specFileName != null) cmd = cmd.replace(SPEC_FILE_MACRO, specFileName); } if (cmd.contains(SPEC_EXT_MACRO)) { String specFileExt = getSpecFileExtension(languageId); if (specFileExt != null) cmd = cmd.replace(SPEC_EXT_MACRO, specFileExt); } if (cmd.contains("${")) { //$NON-NLS-1$ ICdtVariableManager varManager = CCorePlugin.getDefault().getCdtVariableManager(); try { cmd = varManager.resolveValue(cmd, "", null, currentCfgDescription); //$NON-NLS-1$ } catch (Exception e) { ManagedBuilderCorePlugin.log(e); } } } return cmd; } @Override protected String parseResourceName(String line) { // Normally built-in specs detectors are per-language and the result applies for the whole workspace. // Returning null works workspace-wide here. return null; } @Override protected String determineLanguage() { // language id is supposed to be set by run(), just return it return currentLanguageId; } @Override protected URI getMappedRootURI(IResource sourceFile, String parsedResourceName) { // Do not calculate mappedRootURI for each line if (mappedRootURI == null) { mappedRootURI = super.getMappedRootURI(sourceFile, parsedResourceName); } return mappedRootURI; } @Override protected URI getBuildDirURI(URI mappedRootURI) { if (buildDirURI == null) { // do not use build directory from MBS which could be not-existent when the provider runs buildDirURI = currentProject != null ? currentProject.getLocationURI() : null; } return buildDirURI; } @Override public void registerListener(ICConfigurationDescription cfgDescription) { currentCfgDescription = cfgDescription; EnvironmentVariableManager.fUserSupplier.registerEnvironmentChangeListener(this); execute(); } @Override public void unregisterListener() { EnvironmentVariableManager.fUserSupplier.unregisterEnvironmentChangeListener(this); } /** @since 8.2 */ @Override public void handleEvent(IEnvironmentChangeEvent event) { // here comes workspace environment change execute(); } @Override public void startup(ICConfigurationDescription cfgDescription, IWorkingDirectoryTracker cwdTracker) throws CoreException { super.startup(cfgDescription, cwdTracker); mappedRootURI = null; buildDirURI = getBuildDirURI(mappedRootURI); environmentMap = createEnvironmentMap(currentCfgDescription); } @Override public void shutdown() { mappedRootURI = null; buildDirURI = null; super.shutdown(); } /** * Calculate hash code for the environment */ private long calculateEnvHash() { String envPathValue = environmentMap.get(ENV_PATH); long envHashNew = envPathValue != null ? envPathValue.hashCode() : 0; List<String> languageIds = getLanguageScope(); if (languageIds == null) { languageIds = new ArrayList<String>(1); // "null" language indicates that the provider provides for any language languageIds.add(null); } for (String languageId : languageIds) { try { String command = resolveCommand(languageId); if (command != null) { envHashNew = 31*envHashNew + command.hashCode(); } String[] cmdArray = CommandLineUtil.argumentsToArray(command); if (cmdArray != null && cmdArray.length > 0) { IPath location = new Path(cmdArray[0]); if (!location.isAbsolute()) { location = PathUtil.findProgramLocation(cmdArray[0], envPathValue); } if (location != null) { java.io.File file = new java.io.File(location.toString()); try { // handles symbolic links as java.io.File.getCanonicalPath() resolves symlinks on UNIX file = file.getCanonicalFile(); } catch (IOException e) { ManagedBuilderCorePlugin.log(e); } long lastModified = file.lastModified(); envHashNew = 31*envHashNew + location.hashCode(); envHashNew = 31*envHashNew + lastModified; } } } catch (CoreException e) { ManagedBuilderCorePlugin.log(e); } } return envHashNew; } /** * This method does 2 related things: * <br> * 1. Validate environment, i.e. check that environment for running the command has not changed. * If environment changed {@link #execute()} will rerun the command even if flag {@link #isExecuted} * suggests that it was run already. * <br> * 2. The relevant environment is cached here so the new one is validated against it at the next call. * {@link #validateEnvironment()} will be called right before running the job to execute the command. * * @since 8.2 */ protected boolean validateEnvironment() { long envHashNew = calculateEnvHash(); if (envHashNew != envPathHash) { envPathHash = envHashNew; return false; } return true; } /** * Execute provider's command which is expected to print built-in compiler options (specs) to build output. * The parser will parse output and generate language settings for corresponding resources. */ protected void execute() { environmentMap = createEnvironmentMap(currentCfgDescription); if (validateEnvironment() && isExecuted) { return; } WorkspaceJob job = new WorkspaceJob(ManagedMakeMessages.getResourceString("AbstractBuiltinSpecsDetector.DiscoverBuiltInSettingsJobName")) { //$NON-NLS-1$ @Override public IStatus runInWorkspace(IProgressMonitor monitor) throws CoreException { isExecuted = false; if (!isEmpty()) { clear(); serializeLanguageSettings(currentCfgDescription); } IStatus status; try { startup(currentCfgDescription, null); status = runForEachLanguage(monitor); } catch (CoreException e) { ManagedBuilderCorePlugin.log(e); status = new Status(IStatus.ERROR, ManagedBuilderCorePlugin.PLUGIN_ID, IStatus.ERROR, "Error running Builtin Specs Detector", e); //$NON-NLS-1$ } finally { isExecuted = true; shutdown(); } return status; } @Override public boolean belongsTo(Object family) { return family == JOB_FAMILY_BUILTIN_SPECS_DETECTOR; } }; ISchedulingRule rule = null; if (currentCfgDescription != null) { ICProjectDescription prjDescription = currentCfgDescription.getProjectDescription(); if (prjDescription != null) { rule = prjDescription.getProject(); } } if (rule == null) { rule = ResourcesPlugin.getWorkspace().getRoot(); } job.setRule(rule); job.schedule(); } /** * Run built-in specs command for each language. * * @param monitor - progress monitor in the initial state where {@link IProgressMonitor#beginTask(String, int)} * has not been called yet. * @return status of operation. */ protected IStatus runForEachLanguage(IProgressMonitor monitor) { MultiStatus status = new MultiStatus(ManagedBuilderCorePlugin.PLUGIN_ID, IStatus.OK, "Problem running CDT Scanner Discovery provider " + getId(), null); //$NON-NLS-1$ if (monitor == null) { monitor = new NullProgressMonitor(); } try { boolean isChanged = false; List<String> languageIds = getLanguageScope(); if (languageIds != null) { monitor.beginTask(ManagedMakeMessages.getResourceString("AbstractBuiltinSpecsDetector.ScannerDiscoveryTaskTitle"), //$NON-NLS-1$ TICKS_REMOVE_MARKERS + languageIds.size()*TICKS_RUN_FOR_ONE_LANGUAGE + TICKS_SERIALIZATION); IResource markersResource = currentProject != null ? currentProject : ResourcesPlugin.getWorkspace().getRoot(); monitor.subTask(ManagedMakeMessages.getFormattedString("AbstractBuiltinSpecsDetector.ClearingMarkers", markersResource.getFullPath().toString())); //$NON-NLS-1$ markerGenerator.deleteMarkers(markersResource); if (monitor.isCanceled()) throw new OperationCanceledException(); monitor.worked(TICKS_REMOVE_MARKERS); for (String languageId : languageIds) { List<ICLanguageSettingEntry> oldEntries = getSettingEntries(currentCfgDescription, null, languageId); try { startupForLanguage(languageId); runForLanguage(new SubProgressMonitor(monitor, TICKS_RUN_FOR_ONE_LANGUAGE)); } catch (Exception e) { IStatus s = new Status(IStatus.ERROR, ManagedBuilderCorePlugin.PLUGIN_ID, IStatus.ERROR, "Error running Builtin Specs Detector", e); //$NON-NLS-1$ ManagedBuilderCorePlugin.log(s); status.merge(s); } finally { shutdownForLanguage(); } if (!isChanged) { List<ICLanguageSettingEntry> newEntries = getSettingEntries(currentCfgDescription, null, languageId); isChanged = newEntries != oldEntries; } if (monitor.isCanceled()) throw new OperationCanceledException(); } } monitor.subTask(ManagedMakeMessages.getResourceString("AbstractBuiltinSpecsDetector.SerializingResults")); //$NON-NLS-1$ if (isChanged) { // avoids resource and settings change notifications IStatus s = serializeLanguageSettings(currentCfgDescription); status.merge(s); } monitor.worked(TICKS_SERIALIZATION); } catch (OperationCanceledException e) { // user chose to cancel operation, do not threaten them with red error signs } catch (Exception e) { status.merge(new Status(IStatus.ERROR, ManagedBuilderCorePlugin.PLUGIN_ID, IStatus.ERROR, "Error running Builtin Specs Detector", e)); //$NON-NLS-1$ ManagedBuilderCorePlugin.log(status); } finally { monitor.done(); } return status; } /** * Initialize provider before running for a language. * * @param languageId - language ID. * @throws CoreException if something goes wrong. */ protected void startupForLanguage(String languageId) throws CoreException { currentLanguageId = languageId; specFile = null; // init specFile *before* calling resolveCommand(), can be changed in there currentCommandResolved = resolveCommand(currentLanguageId); detectedSettingEntries = new ArrayList<ICLanguageSettingEntry>(); collected = 0; } /** * Save collected entries and dispose temporary data used during run for the language. */ protected void shutdownForLanguage() { if (detectedSettingEntries != null && detectedSettingEntries.size() > 0) { collected = detectedSettingEntries.size(); setSettingEntries(currentCfgDescription, currentResource, currentLanguageId, detectedSettingEntries); } detectedSettingEntries = null; currentCommandResolved = null; if (specFile!=null && !preserveSpecFile) { specFile.delete(); specFile = null; } currentLanguageId = null; } /** * Run built-in specs command for one language. * * @param monitor - progress monitor in the initial state where {@link IProgressMonitor#beginTask(String, int)} * has not been called yet. */ private void runForLanguage(IProgressMonitor monitor) throws CoreException { buildRunnerHelper = new BuildRunnerHelper(currentProject); if (monitor == null) { monitor = new NullProgressMonitor(); } try { monitor.beginTask(ManagedMakeMessages.getFormattedString("AbstractBuiltinSpecsDetector.RunningScannerDiscovery", getName()), //$NON-NLS-1$ TICKS_EXECUTE_COMMAND + TICKS_OUTPUT_PARSING); IConsole console; if (isConsoleEnabled) { console = startProviderConsole(); } else { // that looks in extension points registry and won't find the id, this console is not shown console = CCorePlugin.getDefault().getConsole(ManagedBuilderCorePlugin.PLUGIN_ID + ".console.hidden"); //$NON-NLS-1$ } console.start(currentProject); ICommandLauncher launcher = new CommandLauncher(); launcher.setProject(currentProject); IPath program = new Path(""); //$NON-NLS-1$ String[] args = new String[0]; String[] cmdArray = CommandLineUtil.argumentsToArray(currentCommandResolved); if (cmdArray != null && cmdArray.length > 0) { program = new Path(cmdArray[0]); if (cmdArray.length > 1) { args = new String[cmdArray.length-1]; System.arraycopy(cmdArray, 1, args, 0, args.length); } } String[] envp = toEnvp(environmentMap); // Using GMAKE_ERROR_PARSER_ID as it can handle generated error messages ErrorParserManager epm = new ErrorParserManager(currentProject, buildDirURI, markerGenerator, new String[] {GMAKE_ERROR_PARSER_ID}); ConsoleParserAdapter consoleParser = new ConsoleParserAdapter(); consoleParser.startup(currentCfgDescription, epm); List<IConsoleParser> parsers = new ArrayList<IConsoleParser>(); parsers.add(consoleParser); buildRunnerHelper.setLaunchParameters(launcher, program, args, buildDirURI, envp); buildRunnerHelper.prepareStreams(epm, parsers, console, new SubProgressMonitor(monitor, TICKS_OUTPUT_PARSING)); buildRunnerHelper.greeting(ManagedMakeMessages.getFormattedString("AbstractBuiltinSpecsDetector.RunningScannerDiscovery", getName())); //$NON-NLS-1$ OutputStream outStream = buildRunnerHelper.getOutputStream(); OutputStream errStream = buildRunnerHelper.getErrorStream(); runProgramForLanguage(currentLanguageId, currentCommandResolved, envp, buildDirURI, outStream, errStream, new SubProgressMonitor(monitor, TICKS_EXECUTE_COMMAND, SubProgressMonitor.PREPEND_MAIN_LABEL_TO_SUBTASK)); buildRunnerHelper.close(); buildRunnerHelper.goodbye(); } catch (Exception e) { ManagedBuilderCorePlugin.log(new CoreException(new Status(IStatus.ERROR, ManagedBuilderCorePlugin.PLUGIN_ID, "Error running Builtin Specs Detector" , e))); //$NON-NLS-1$ } finally { try { buildRunnerHelper.close(); } catch (IOException e) { ManagedBuilderCorePlugin.log(e); } monitor.done(); } } /** * Returns list of environment variables to be used during execution of provider's command. * Implementers are expected to add their variables to the end of the list. * * @return list of environment variables. * @since 8.2 */ protected List<IEnvironmentVariable> getEnvironmentVariables() { if (envMngr == null) { envMngr = CCorePlugin.getDefault().getBuildEnvironmentManager(); } List<IEnvironmentVariable> vars = new ArrayList<IEnvironmentVariable>(Arrays.asList(envMngr.getVariables(currentCfgDescription, true))); // On POSIX (Linux, UNIX) systems reset language variables to default (English) // with UTF-8 encoding since GNU compilers can handle only UTF-8 characters. // Include paths with locale characters will be handled properly regardless // of the language as long as the encoding is set to UTF-8. // English language is set for parser because it relies on English messages // in the output of the 'gcc -v' command. vars.add(new EnvironmentVariable(ENV_LANGUAGE, "en")); //$NON-NLS-1$ vars.add(new EnvironmentVariable(ENV_LC_ALL, "C.UTF-8")); //$NON-NLS-1$ return vars; } /** * Create a handy map of environment variables. */ private Map<String, String> createEnvironmentMap(ICConfigurationDescription cfgDescription) { Map<String, String> envMap = new HashMap<String, String>(); for (IEnvironmentVariable var : getEnvironmentVariables()) { String name = var.getName(); if (!envMngr.isVariableCaseSensitive()) { name = name.toUpperCase(); } envMap.put(name, var.getValue()); } return envMap; } /** * Convert map of environment variables to array in format "var=value". */ private String[] toEnvp(Map<String, String> environmentMap) { Set<String> envp = new HashSet<String>(); for (Entry<String, String> var: environmentMap.entrySet()) { envp.add(var.getKey() + '=' + var.getValue()); } return envp.toArray(new String[envp.size()]); } protected int runProgramForLanguage(String languageId, String command, String[] envp, URI workingDirectoryURI, OutputStream consoleOut, OutputStream consoleErr, IProgressMonitor monitor) throws CoreException, IOException { return buildRunnerHelper.build(monitor); } @Override protected void setSettingEntries(List<? extends ICLanguageSettingEntry> entries) { // Built-in specs detectors collect entries not per line but for the whole output // so collect them to save later when output finishes if (entries != null) { for (ICLanguageSettingEntry entry : entries) { if (!detectedSettingEntries.contains(entry)) { detectedSettingEntries.add(entry); } } } } /** * Create and start the provider console. * @return CDT console. */ private IConsole startProviderConsole() { IConsole console = null; if (isConsoleEnabled && currentLanguageId != null) { String extConsoleId; if (currentProject != null) { extConsoleId = SCANNER_DISCOVERY_CONSOLE; } else { extConsoleId = SCANNER_DISCOVERY_GLOBAL_CONSOLE; } ILanguage ld = LanguageManager.getInstance().getLanguage(currentLanguageId); if (ld != null) { String consoleId = ManagedBuilderCorePlugin.PLUGIN_ID + '.' + getId() + '.' + currentLanguageId; String consoleName = getName() + ", " + ld.getName(); //$NON-NLS-1$ URL defaultIcon = Platform.getBundle(CDT_MANAGEDBUILDER_UI_PLUGIN_ID).getEntry(DEFAULT_CONSOLE_ICON); if (defaultIcon == null) { @SuppressWarnings("nls") String msg = "Unable to find icon " + DEFAULT_CONSOLE_ICON + " in plugin " + CDT_MANAGEDBUILDER_UI_PLUGIN_ID; ManagedBuilderCorePlugin.log(new Status(IStatus.ERROR, ManagedBuilderCorePlugin.PLUGIN_ID, msg)); } console = CCorePlugin.getDefault().getConsole(extConsoleId, consoleId, consoleName, defaultIcon); } } if (console == null) { // that looks in extension points registry and won't find the id, this console is not shown console = CCorePlugin.getDefault().getConsole(ManagedBuilderCorePlugin.PLUGIN_ID + ".console.hidden"); //$NON-NLS-1$ } return console; } /** * Get path to spec file which normally would be placed in workspace area. * This value is used to replace macro ${INPUTS}. * * @param languageId - language ID. * @return full path to the specs file. */ protected String getSpecFile(String languageId) { String specExt = getSpecFileExtension(languageId); String ext = ""; //$NON-NLS-1$ if (specExt != null) { ext = '.' + specExt; } String specFileName = SPEC_FILE_BASE + ext; IPath workingLocation = ManagedBuilderCorePlugin.getDefault().getStateLocation(); IPath fileLocation = workingLocation.append(specFileName); specFile = new java.io.File(fileLocation.toOSString()); // will preserve spec file if it was already there otherwise will delete upon finishing preserveSpecFile = specFile.exists(); if (!preserveSpecFile) { try { // In the typical case it is sufficient to have an empty file. specFile.createNewFile(); } catch (IOException e) { ManagedBuilderCorePlugin.log(e); } } return fileLocation.toString(); } /** * Determine file extension by language id. This implementation retrieves first extension * from the list as there could be multiple extensions associated with the given language. * This value is used to replace macro ${EXT}. * * @param languageId - given language ID. * @return file extension associated with the language or {@code null} if not found. */ protected String getSpecFileExtension(String languageId) { String ext = null; ILanguageDescriptor langDescriptor = LanguageManager.getInstance().getLanguageDescriptor(languageId); if (langDescriptor != null) { IContentType[] contentTypes = langDescriptor.getContentTypes(); if (contentTypes != null && contentTypes.length > 0) { String[] fileExtensions = contentTypes[0].getFileSpecs(IContentType.FILE_EXTENSION_SPEC); if (fileExtensions != null && fileExtensions.length > 0) { ext = fileExtensions[0]; } } } if (ext == null) { ManagedBuilderCorePlugin.log(new Status(IStatus.ERROR, ManagedBuilderCorePlugin.PLUGIN_ID, "Unable to find file extension for language " + languageId)); //$NON-NLS-1$ } return ext; } /** * Determine additional options to pass to scanner discovery command. * These options are intended to come from the tool-chain. * * @param languageId - language ID. * @return additional options to pass to scanner discovery command. * * @since 8.3 */ protected String getToolOptions(String languageId) { return ""; //$NON-NLS-1$ } @Override public Element serializeAttributes(Element parentElement) { Element elementProvider = super.serializeAttributes(parentElement); elementProvider.setAttribute(ATTR_CONSOLE, Boolean.toString(isConsoleEnabled)); if (envPathHash != HASH_NOT_INITIALIZED) { elementProvider.setAttribute(ATTR_ENV_HASH, Long.toString(envPathHash)); } return elementProvider; } @Override public void loadAttributes(Element providerNode) { super.loadAttributes(providerNode); String consoleValue = XmlUtil.determineAttributeValue(providerNode, ATTR_CONSOLE); if (consoleValue != null) { isConsoleEnabled = Boolean.parseBoolean(consoleValue); } envPathHash = HASH_NOT_INITIALIZED; String envPathHashStr = XmlUtil.determineAttributeValue(providerNode, ATTR_ENV_HASH); if (envPathHashStr != null) { try { envPathHash = Long.parseLong(envPathHashStr); } catch (Exception e) { ManagedBuilderCorePlugin.log(new Status(IStatus.ERROR, ManagedBuilderCorePlugin.PLUGIN_ID, "Wrong integer format [" + envPathHashStr + "]", e)); //$NON-NLS-1$ //$NON-NLS-2$ } } } @Override public void loadEntries(Element providerNode) { super.loadEntries(providerNode); if (!isEmpty()) { isExecuted = true; } } @Override public boolean isEmpty() { // treat provider that has been executed as not empty // to let "Clear" button to restart the provider return !isExecuted && super.isEmpty(); } @Override public void clear() { super.clear(); isExecuted = false; } @Override protected AbstractBuiltinSpecsDetector cloneShallow() throws CloneNotSupportedException { AbstractBuiltinSpecsDetector clone = (AbstractBuiltinSpecsDetector) super.cloneShallow(); clone.isExecuted = false; clone.envMngr = null; clone.environmentMap = null; clone.envPathHash = HASH_NOT_INITIALIZED; this.properties.remove(ATTR_ENV_HASH); return clone; } @Override public int hashCode() { final int prime = 31; int result = super.hashCode(); result = prime * result + (isConsoleEnabled ? 1231 : 1237); result = prime * result + (isExecuted ? 1231 : 1237); result = prime * result + Long.valueOf(envPathHash).hashCode(); return result; } @Override public boolean equals(Object obj) { if (this == obj) return true; if (!super.equals(obj)) return false; if (getClass() != obj.getClass()) return false; AbstractBuiltinSpecsDetector other = (AbstractBuiltinSpecsDetector) obj; if (isConsoleEnabled != other.isConsoleEnabled) return false; if (isExecuted != other.isExecuted) return false; if (envPathHash != other.envPathHash) return false; return true; } }