/*******************************************************************************
* Copyright (c) 2004, 2012 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.net.URI;
import java.util.ArrayList;
import java.util.Enumeration;
import java.util.List;
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.IConsoleParser;
import org.eclipse.cdt.core.model.ILanguage;
import org.eclipse.cdt.core.resources.IConsole;
import org.eclipse.cdt.internal.core.BuildRunnerHelper;
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.scannerconfig.ScannerConfigUtil;
import org.eclipse.cdt.make.internal.core.scannerconfig.ScannerInfoConsoleParserFactory;
import org.eclipse.cdt.utils.EFSExtensionManager;
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 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 int PROGRESS_MONITOR_SCALE = 100;
private static final int TICKS_STREAM_PROGRESS_MONITOR = 1 * PROGRESS_MONITOR_SCALE;
private static final int TICKS_EXECUTE_PROGRAM = 1 * PROGRESS_MONITOR_SCALE;
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();
@Override
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);
}
@Override
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 project = resource.getProject();
BuildRunnerHelper buildRunnerHelper = new BuildRunnerHelper(project);
try {
if (monitor == null) {
monitor = new NullProgressMonitor();
}
monitor.beginTask(MakeMessages.getString("ExternalScannerInfoProvider.Reading_Specs"), //$NON-NLS-1$
TICKS_STREAM_PROGRESS_MONITOR + TICKS_EXECUTE_PROGRAM);
// call a subclass to initialize protected fields
if (!initialize()) {
return false;
}
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(project);
ICommandLauncher launcher = new CommandLauncher();
launcher.setProject(project);
IPath program = getCommandToLaunch();
if (! program.isEmpty()) {
String[] comandLineOptions = getCommandLineOptions();
URI workingDirectoryURI = MakeBuilderUtil.getBuildDirectoryURI(project, MakeBuilder.BUILDER_ID);
String[] envp = setEnvironment(launcher, env);
ErrorParserManager epm = new ErrorParserManager(project, markerGenerator, new String[] {GMAKE_ERROR_PARSER_ID});
List<IConsoleParser> parsers = new ArrayList<IConsoleParser>();
IConsoleParser parser = ScannerInfoConsoleParserFactory.getESIConsoleParser(project, context, providerId, buildInfo, collector, markerGenerator);
if (parser != null) {
parsers.add(parser);
}
buildRunnerHelper.setLaunchParameters(launcher, program, comandLineOptions, workingDirectoryURI, envp );
buildRunnerHelper.prepareStreams(epm, parsers, console, new SubProgressMonitor(monitor, TICKS_STREAM_PROGRESS_MONITOR, SubProgressMonitor.PREPEND_MAIN_LABEL_TO_SUBTASK));
buildRunnerHelper.greeting(MakeMessages.getFormattedString("ExternalScannerInfoProvider.Greeting", project.getName())); //$NON-NLS-1$
buildRunnerHelper.build(new SubProgressMonitor(monitor, TICKS_EXECUTE_PROGRAM, SubProgressMonitor.PREPEND_MAIN_LABEL_TO_SUBTASK));
buildRunnerHelper.close();
buildRunnerHelper.goodbye();
}
} catch (Exception e) {
MakeCorePlugin.log(e);
} finally {
try {
buildRunnerHelper.close();
} catch (IOException e) {
MakeCorePlugin.log(e);
}
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));
}
/**
* 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 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.
// 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 chosen because parser relies on English messages
// in the output of the 'gcc -v' command.
props.put("LANGUAGE", "en"); // override for GNU gettext //$NON-NLS-1$ //$NON-NLS-2$
props.put("LC_ALL", "en_US.UTF-8"); // for other parts of the system libraries //$NON-NLS-1$ //$NON-NLS-2$
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;
}
}