/******************************************************************************* * Copyright (c) 2004, 2010 IBM Corporation 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: * IBM - Initial API and implementation * Tianchao Li (tianchao.li@gmail.com) - arbitrary build directory (bug #136136) *******************************************************************************/ package org.eclipse.cdt.make.internal.core.scannerconfig2; import java.io.IOException; import java.io.OutputStream; import java.net.URI; import java.util.ArrayList; import java.util.Enumeration; import java.util.Properties; 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.ILanguage; import org.eclipse.cdt.core.resources.IConsole; import org.eclipse.cdt.internal.core.ConsoleOutputSniffer; import org.eclipse.cdt.make.core.MakeBuilder; import org.eclipse.cdt.make.core.MakeBuilderUtil; import org.eclipse.cdt.make.core.MakeCorePlugin; import org.eclipse.cdt.make.core.scannerconfig.IExternalScannerInfoProvider; import org.eclipse.cdt.make.core.scannerconfig.IScannerConfigBuilderInfo2; import org.eclipse.cdt.make.core.scannerconfig.IScannerInfoCollector; import org.eclipse.cdt.make.core.scannerconfig.InfoContext; import org.eclipse.cdt.make.internal.core.MakeMessages; import org.eclipse.cdt.make.internal.core.StreamMonitor; import org.eclipse.cdt.make.internal.core.scannerconfig.ScannerConfigUtil; import org.eclipse.cdt.make.internal.core.scannerconfig.ScannerInfoConsoleParserFactory; import org.eclipse.cdt.utils.EFSExtensionManager; import org.eclipse.cdt.utils.PathUtil; import org.eclipse.core.resources.IProject; import org.eclipse.core.resources.IResource; 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.SubProgressMonitor; import org.eclipse.core.runtime.preferences.IEclipsePreferences; import org.eclipse.core.runtime.preferences.InstanceScope; import org.osgi.service.prefs.BackingStoreException; /** * New default external scanner info provider of type 'run' * * @author vhirsl */ public class DefaultRunSIProvider implements IExternalScannerInfoProvider { private static final String EXTERNAL_SI_PROVIDER_ERROR = "ExternalScannerInfoProvider.Provider_Error"; //$NON-NLS-1$ private static final String GMAKE_ERROR_PARSER_ID = "org.eclipse.cdt.core.GmakeErrorParser"; //$NON-NLS-1$ private static final String PREF_CONSOLE_ENABLED = "org.eclipse.cdt.make.core.scanner.discovery.console.enabled"; //$NON-NLS-1$ private static final String LANG_ENV_VAR = "LANG"; //$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$ protected IResource resource; protected String providerId; protected IScannerConfigBuilderInfo2 buildInfo; protected IScannerInfoCollector collector; // To be initialized by a subclass protected IPath fWorkingDirectory; protected IPath fCompileCommand; protected String[] fCompileArguments; private SCMarkerGenerator markerGenerator = new SCMarkerGenerator(); public boolean invokeProvider(IProgressMonitor monitor, IResource resource, String providerId, IScannerConfigBuilderInfo2 buildInfo, IScannerInfoCollector collector) { return invokeProvider(monitor, resource, new InfoContext(resource.getProject()), providerId, buildInfo, collector, null); } public boolean invokeProvider(IProgressMonitor monitor, IResource resource, InfoContext context, String providerId, IScannerConfigBuilderInfo2 buildInfo, IScannerInfoCollector collector, Properties env) { // initialize fields this.resource = resource; this.providerId = providerId; this.buildInfo = buildInfo; this.collector = collector; IProject currentProject = resource.getProject(); // call a subclass to initialize protected fields if (!initialize()) { return false; } if (monitor == null) { monitor = new NullProgressMonitor(); } monitor.beginTask(MakeMessages.getString("ExternalScannerInfoProvider.Reading_Specs"), 100); //$NON-NLS-1$ try { ILanguage language = context.getLanguage(); IConsole console; if (language!=null && isConsoleEnabled()) { String consoleId = MakeCorePlugin.PLUGIN_ID + '.' + providerId + '.' + language.getId(); String consoleName = MakeMessages.getFormattedString("ExternalScannerInfoProvider.Console_Name", language.getName()); //$NON-NLS-1$ console = CCorePlugin.getDefault().getBuildConsole(consoleId, consoleName, null); } else { // that looks in extension points registry and won't find the id console = CCorePlugin.getDefault().getConsole(MakeCorePlugin.PLUGIN_ID + ".console.hidden"); //$NON-NLS-1$ } console.start(currentProject); OutputStream cos = console.getOutputStream(); // Before launching give visual cues via the monitor monitor.subTask(MakeMessages.getString("ExternalScannerInfoProvider.Reading_Specs")); //$NON-NLS-1$ String errMsg = null; ICommandLauncher launcher = new CommandLauncher(); launcher.setProject(currentProject); // Print the command for visual interaction. launcher.showCommand(true); String[] comandLineOptions = getCommandLineOptions(); String params = coligate(comandLineOptions); monitor.subTask(MakeMessages.getString("ExternalScannerInfoProvider.Invoking_Command") //$NON-NLS-1$ + getCommandToLaunch() + params); ErrorParserManager epm = new ErrorParserManager(currentProject, markerGenerator, new String[] {GMAKE_ERROR_PARSER_ID}); epm.setOutputStream(cos); StreamMonitor streamMon = new StreamMonitor(new SubProgressMonitor(monitor, 70), epm, 100); OutputStream stdout = streamMon; OutputStream stderr = streamMon; ConsoleOutputSniffer sniffer = ScannerInfoConsoleParserFactory.getESIProviderOutputSniffer( stdout, stderr, currentProject, context, providerId, buildInfo, collector, markerGenerator); OutputStream consoleOut = (sniffer == null ? cos : sniffer.getOutputStream()); OutputStream consoleErr = (sniffer == null ? cos : sniffer.getErrorStream()); Process p = launcher.execute(getCommandToLaunch(), comandLineOptions, setEnvironment(launcher, env), fWorkingDirectory, monitor); if (p != null) { try { // Close the input of the Process explicitely. // We will never write to it. p.getOutputStream().close(); } catch (IOException e) { } if (launcher.waitAndRead(consoleOut, consoleErr, new SubProgressMonitor(monitor, 0)) != ICommandLauncher.OK) { errMsg = launcher.getErrorMessage(); } monitor.subTask(MakeMessages.getString("ExternalScannerInfoProvider.Parsing_Output")); //$NON-NLS-1$ } else { errMsg = launcher.getErrorMessage(); } if (errMsg != null) { String errorPrefix = MakeMessages.getString("ExternalScannerInfoProvider.Error_Prefix"); //$NON-NLS-1$ String program = fCompileCommand.toString(); String msg = MakeMessages.getFormattedString(EXTERNAL_SI_PROVIDER_ERROR, program+params); printLine(consoleErr, errorPrefix + msg + NEWLINE); // Launching failed, trying to figure out possible cause Properties envMap = getEnvMap(launcher, env); String envPath = envMap.getProperty(PATH_ENV); if (envPath == null) { envPath = System.getenv(PATH_ENV); } if (!fCompileCommand.isAbsolute() && PathUtil.findProgramLocation(program, envPath) == null) { printLine(consoleErr, errMsg); msg = MakeMessages.getFormattedString("ExternalScannerInfoProvider.Working_Directory", fWorkingDirectory); //$NON-NLS-1$ msg = MakeMessages.getFormattedString("ExternalScannerInfoProvider.Program_Not_In_Path", program); //$NON-NLS-1$ printLine(consoleErr, errorPrefix + msg + NEWLINE); printLine(consoleErr, PATH_ENV + "=[" + envPath + "]" + NEWLINE); //$NON-NLS-1$ //$NON-NLS-2$ } else { printLine(consoleErr, errorPrefix + errMsg); msg = MakeMessages.getFormattedString("ExternalScannerInfoProvider.Working_Directory", fWorkingDirectory); //$NON-NLS-1$ printLine(consoleErr, PATH_ENV + "=[" + envPath + "]" + NEWLINE); //$NON-NLS-1$ //$NON-NLS-2$ } monitor.subTask(MakeMessages.getString("ExternalScannerInfoProvider.Creating_Markers")); //$NON-NLS-1$ } consoleOut.close(); consoleErr.close(); cos.close(); } catch (Exception e) { MakeCorePlugin.log(e); } finally { monitor.done(); } return true; } protected IPath getCommandToLaunch() { return fCompileCommand; } protected String[] getCommandLineOptions() { // add additional arguments // subclass can change default behavior return prepareArguments( buildInfo.isUseDefaultProviderCommand(providerId)); } private void printLine(OutputStream stream, String msg) throws IOException { stream.write((msg + NEWLINE).getBytes()); stream.flush(); } /** * Initialization of protected fields. * Subclasses are most likely to override default implementation. */ protected boolean initialize() { IProject currProject = resource.getProject(); //fWorkingDirectory = resource.getProject().getLocation(); URI workingDirURI = MakeBuilderUtil.getBuildDirectoryURI(currProject, MakeBuilder.BUILDER_ID); String pathString = EFSExtensionManager.getDefault().getPathFromURI(workingDirURI); if(pathString != null) { fWorkingDirectory = new Path(pathString); } else { // blow up throw new IllegalStateException(); } fCompileCommand = new Path(buildInfo.getProviderRunCommand(providerId)); fCompileArguments = ScannerConfigUtil.tokenizeStringWithQuotes(buildInfo.getProviderRunArguments(providerId), "\"");//$NON-NLS-1$ return (fCompileCommand != null); } /** * Add additional arguments. For example: tso - target specific options * Base class implementation returns compileArguments. * Subclasses are most likely to override default implementation. */ protected String[] prepareArguments(boolean isDefaultCommand) { return fCompileArguments; } private String coligate(String[] array) { StringBuffer sb = new StringBuffer(128); for (int i = 0; i < array.length; ++i) { sb.append(' '); sb.append(array[i]); } String ca = sb.toString(); return ca; } private Properties getEnvMap(ICommandLauncher launcher, Properties initialEnv) { // Set the environmennt, some scripts may need the CWD var to be set. Properties props = initialEnv != null ? initialEnv : launcher.getEnvironment(); if (fWorkingDirectory != null) { props.put("CWD", fWorkingDirectory.toOSString()); //$NON-NLS-1$ props.put("PWD", fWorkingDirectory.toOSString()); //$NON-NLS-1$ } // On POSIX (Linux, UNIX) systems reset LANG variable to English with // UTF-8 encoding // since GNU compilers can handle only UTF-8 characters. English language is chosen // beacuse GNU compilers inconsistently handle different locales when generating // output of the 'gcc -v' command. Include paths with locale characters will be // handled properly regardless of the language as long as the encoding is set to UTF-8. if (props.containsKey(LANG_ENV_VAR)) { props.put(LANG_ENV_VAR, "en_US.UTF-8"); //$NON-NLS-1$ } return props; } protected String[] setEnvironment(ICommandLauncher launcher, Properties initialEnv) { Properties props = getEnvMap(launcher, initialEnv); String[] env = null; ArrayList<String> envList = new ArrayList<String>(); Enumeration<?> names = props.propertyNames(); if (names != null) { while (names.hasMoreElements()) { String key = (String) names.nextElement(); envList.add(key + "=" + props.getProperty(key)); //$NON-NLS-1$ } env = envList.toArray(new String[envList.size()]); } return env; } /** * Set preference to stream output of scanner discovery to a console. */ public static void setConsoleEnabled(boolean value) { IEclipsePreferences node = InstanceScope.INSTANCE.getNode(MakeCorePlugin.PLUGIN_ID); node.putBoolean(PREF_CONSOLE_ENABLED, value); try { node.flush(); } catch (BackingStoreException e) { MakeCorePlugin.log(e); } } /** * Check preference to stream output of scanner discovery to a console. * * @return boolean preference value */ public static boolean isConsoleEnabled() { boolean value = InstanceScope.INSTANCE.getNode(MakeCorePlugin.PLUGIN_ID) .getBoolean(PREF_CONSOLE_ENABLED, false); return value; } }