/*
* Copyright (c) 2008 Embedded Brains GmbH and others.
*
* Embedded Brains GmbH
* Obere Lagerstr. 30
* D-82178 Puchheim
* Germany
* rtems@embedded-brains.de
*
* All rights reserved. This program and the accompanying materials are made
* available under the terms of the Eclipse Public License Version 1.0 ("EPL")
* which accompanies this distribution and is available at
*
* http://www.eclipse.org/legal/epl-v10.html
*
* For purposes of the EPL, "Program" will mean the Content.
*
* Contributors:
*
* Sebastian Huber (Embedded Brains GmbH) - Initial API and implementation.
*/
package org.rtems.cdt;
import java.io.BufferedReader;
import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import org.eclipse.cdt.build.core.scannerconfig.CfgInfoContext;
import org.eclipse.cdt.build.internal.core.scannerconfig.CfgDiscoveredPathManager;
import org.eclipse.cdt.core.model.CoreModel;
import org.eclipse.cdt.core.settings.model.ICConfigurationDescription;
import org.eclipse.cdt.core.settings.model.ICProjectDescription;
import org.eclipse.cdt.managedbuilder.core.IConfiguration;
import org.eclipse.cdt.managedbuilder.core.ManagedBuildManager;
import org.eclipse.core.resources.IMarker;
import org.eclipse.core.resources.IProject;
import org.eclipse.core.resources.IResource;
import org.eclipse.core.runtime.CoreException;
import org.eclipse.core.runtime.IPath;
import org.eclipse.core.runtime.Path;
import org.eclipse.core.runtime.Platform;
import org.eclipse.core.runtime.QualifiedName;
public class Storage {
private static final String OPTION_SEPARATOR = "\0";
private static final String VALUE_START_TOKEN = "\t";
private static final int EXPECT_OPTION = 0;
private static final int EXPECT_COMMAND = 1;
private static final int EXPECT_KEY = 2;
private static final int TOOL_COMPLETE = 3;
public static String getPreference( String key) {
return Activator.getDefault().getPreferenceStore().getString( key);
}
public static String getPristineProperty( IProject project, String key) {
String value = null;
try {
value = project.getPersistentProperty( new QualifiedName( "", key));
} catch (CoreException e) {
e.printStackTrace();
}
return value;
}
public static String getProperty( IProject project, String key) {
String value = getPristineProperty( project, key);
if (value == null) {
if (key.startsWith( Constants.TOOL_KEY_PREFIX)) {
changePlatform( project, Constants.PLATFORM_DEFAULT);
} else {
value = getPreference( key);
setProperty( project, key, value);
}
}
return value;
}
public static void setProperty( IProject project, String key, String value) {
try {
project.setPersistentProperty( new QualifiedName( "", key), value);
} catch (CoreException e) {
e.printStackTrace();
}
}
public static IConfiguration [] getConfigurations( IProject project) {
ICProjectDescription pd = CoreModel.getDefault().getProjectDescription( project);
ICConfigurationDescription cds [] = pd.getConfigurations();
IConfiguration cfgs [] = new IConfiguration [cds.length];
for (int i = 0; i < cds.length; ++i) {
cfgs [i] = ManagedBuildManager.getConfigurationForDescription( cds [i]);
}
return cfgs;
}
public static IConfiguration getActiveConfiguration( IProject project) {
ICProjectDescription pd = CoreModel.getDefault().getProjectDescription( project);
ICConfigurationDescription cd = pd.getActiveConfiguration();
IConfiguration cfg = ManagedBuildManager.getConfigurationForDescription( cd);
return cfg;
}
public static String prependToPath( String path, String part) {
if (path == null || path.length()==0) {
return part;
} else {
return part + Constants.PATH_SEPARATOR + path;
}
}
public static String prependToPathByPreference( String path, String key) {
String basePath = getPreference( key);
if (basePath != null) {
IPath part = new Path( basePath).append( "bin");
return prependToPath( path, part.toOSString());
}
return path;
}
public static String prependToPathByProperty( IProject project, String path, String key) {
String basePath = getProperty( project, key);
if (basePath != null) {
IPath part = new Path( basePath).append( "bin");
return prependToPath( path, part.toOSString());
}
return path;
}
public static String getPlatform( IProject project) {
return getPristineProperty( project, Constants.PLATFORM_KEY);
}
public static void setPlatform( IProject project, String platform) {
setProperty( project, Constants.PLATFORM_KEY, platform);
}
public static void clearPlatform( IProject project) {
setPlatform( project, null);
// Delete discovered paths for all configurations of the project
for (IConfiguration cfg : getConfigurations( project)) {
CfgDiscoveredPathManager.getInstance().removeDiscoveredInfo(
project,
new CfgInfoContext( cfg)
);
}
}
public static void changePlatform( IProject project, String newPlatform) {
String platform = getPlatform( project);
// Check if we have already the requested platform
if (platform != null && platform == newPlatform) {
// Nothing to do
return;
}
// Set new platform
setPlatform( project, newPlatform);
// Update path prepends
String path = null;
if (Platform.getOS().equals( Platform.OS_WIN32)) {
if (newPlatform.equals( Constants.PLATFORM_CYGWIN)) {
path = prependToPathByPreference( path, Constants.CYGWIN_PATH_KEY);
} else {
path = prependToPathByPreference( path, Constants.MINGW_PATH_KEY);
path = prependToPathByPreference( path, Constants.MSYS_PATH_KEY);
}
}
path = prependToPathByProperty( project, path, Constants.BASE_PATH_KEY);
setProperty( project, Constants.PATH_PREPEND_KEY, path);
// Update tools
updateTools( project, newPlatform);
}
private static void updateTools( IProject project, String platform) {
String bspPath = getProperty( project, Constants.BSP_PATH_KEY);
IPath make = new Path( "make");
List<String> options = new LinkedList<String>();
boolean error = false;
// Set tools to default values
updateTool( project, Constants.TOOL_ARCHIVER_KEY, "ar", options);
updateTool( project, Constants.TOOL_ASSEMBLER_KEY, "as", options);
updateTool( project, Constants.TOOL_COMPILER_CPP_KEY, "g++", options);
updateTool( project, Constants.TOOL_COMPILER_C_KEY, "gcc", options);
updateTool( project, Constants.TOOL_LINKER_CPP_KEY, "g++", options);
updateTool( project, Constants.TOOL_LINKER_C_KEY, "gcc", options);
// Delete markers for this unit
deleteMarkers( project, Constants.MARKER_ID_TOOL_DISCOVERY);
// Translate path if necessary
if (Platform.getOS().equals( Platform.OS_WIN32)) {
if (platform.equals( Constants.PLATFORM_CYGWIN)) {
String s [] = bspPath.split( ":");
if (s.length > 0) {
bspPath = bspPath.replaceFirst( "^" + s [0] + ":", "/cygdrive/" + s [0]);
}
}
bspPath = bspPath.replaceAll( "\\\\", "/");
}
// Create make process builder
ProcessBuilder pb = new ProcessBuilder();
// Change working directory to the Makefile location
pb.directory( Activator.getDefault().getMakefileLocation().toFile());
// Update path environment variable
Map<String, String> env = pb.environment();
String path = env.get( Constants.PATH_VARIABLE_NAME);
String part = getProperty( project, Constants.PATH_PREPEND_KEY);
path = Storage.prependToPath( path, part);
env.put( Constants.PATH_VARIABLE_NAME, path);
// On Windows we have to search for the make program in the new path environment
if (Platform.getOS().equals( Platform.OS_WIN32)) {
String parts [] = path.split( Constants.PATH_SEPARATOR);
boolean found = false;
for (String p : parts) {
IPath makeCandidate = new Path( p).append( "make.exe");
File file = new File( makeCandidate.toOSString());
if (file.exists()) {
make = makeCandidate;
found = true;
break;
}
}
if (!found) {
createMarker(
project,
Constants.MARKER_ID_TOOL_DISCOVERY,
"make program not found, check your Cygwin or MinGW settings in the RTEMS preferences"
);
}
}
// Set command line
String makeArgument = Constants.BSP_PATH_MAKE_VARIABLE + "=" + bspPath.replaceAll( " ", "\\\\ ");
pb.command(
make.toOSString(),
makeArgument
);
// Start make process and parse its output
Process p = null;
try {
p = pb.start();
InputStream is = p.getInputStream();
BufferedReader br = new BufferedReader( new InputStreamReader( is));
String line = br.readLine();
String key = null;
String command = null;
int state = EXPECT_KEY;
while (line != null) {
switch (state) {
case EXPECT_OPTION:
if (line.startsWith( VALUE_START_TOKEN)) {
options.add( line.substring( 1));
} else {
state = TOOL_COMPLETE;
continue;
}
break;
case EXPECT_COMMAND:
if (line.startsWith( VALUE_START_TOKEN)) {
command = line.substring( 1);
state = EXPECT_OPTION;
} else {
throw new IOException( "unexpected line format");
}
break;
case EXPECT_KEY:
if (line.length() > Constants.TOOL_KEY_PREFIX.length()) {
key = line;
state = EXPECT_COMMAND;
} else {
throw new IOException( "unexpected line format");
}
break;
case TOOL_COMPLETE:
updateTool( project, key, command, options);
options.clear();
state = EXPECT_KEY;
continue;
default:
throw new IOException( "unexpected state");
}
line = br.readLine();
}
if (state == EXPECT_OPTION) {
updateTool( project, key, command, options);
}
} catch (IOException e) {
error = true;
createMarker(
project,
Constants.MARKER_ID_TOOL_DISCOVERY,
"make output parse error: " + e.getMessage()
);
} finally {
if (p != null) {
while (true) {
try {
p.waitFor();
break;
} catch (InterruptedException e) {
continue;
}
}
}
}
// Check exit status
if (p != null && p.exitValue() != 0) {
error = true;
createMarker(
project,
Constants.MARKER_ID_TOOL_DISCOVERY,
"make invokation `" + make.toOSString() + " " + makeArgument + "' returned with error status " + p.exitValue()
);
}
// Check error
if (error) {
// Clear platform to trigger an update again if someone changed a preference or property value
setPlatform( project, null);
}
}
private static void updateTool( IProject project, String toolKey, String command, List<String> options) {
List<String> filteredOptions = new LinkedList<String>();
// Filter options
if (toolKey.startsWith( Constants.COMPILER_KEY_PREFIX) || toolKey.startsWith( Constants.LINKER_KEY_PREFIX)) {
for (String option : options) {
if (!(option.length() == 0 || option.trim().matches( "^(-c|-O[0123s]|-g.*|-W[\\w-]*|[-]*pipe)$"))) {
filteredOptions.add( option);
}
}
} else {
filteredOptions = options;
}
// Transform filtered option list into option string value
String optionsValue = new String();
if (!options.isEmpty()) {
optionsValue = filteredOptions.get( 0);
filteredOptions.remove( 0);
}
for (String option : filteredOptions) {
if (option.indexOf( ' ') != -1) {
option = "\"" + option + "\"";
}
optionsValue += OPTION_SEPARATOR + option;
}
// FIXME: This is a workaround for tools with long filenames under Windows.
// It is also a workaround for the internal builder since this one does not care about
// the environment provider to fix the $PATH.
IPath commandPath = new Path(
Storage.getProperty( project, Constants.BASE_PATH_KEY)
)
.append( "bin")
.append( command);
command = commandPath.toString();
// Set properties
setProperty( project, toolKey, command);
setProperty( project, toolKey + Constants.TOOL_OPTIONS_KEY_POSTFIX, optionsValue);
}
public static String [] getToolOptions( IProject project, String toolKey) {
String optionsValue = getProperty( project, toolKey + Constants.TOOL_OPTIONS_KEY_POSTFIX);
return optionsValue.split( OPTION_SEPARATOR);
}
public static boolean areToolOptionsEnabled( IProject project) {
return !getProperty( project, Constants.DISABLE_TOOL_OPTIONS_KEY).equals( "true");
}
public static void createMarker( IProject project, String id, String message) {
createMarker( project, id, message, IMarker.SEVERITY_ERROR);
}
public static void createMarker( IProject project, String id, String message, int severity) {
try {
IMarker marker = project.createMarker( IMarker.PROBLEM);
marker.setAttribute( IMarker.LOCATION, id);
marker.setAttribute( IMarker.MESSAGE, message);
marker.setAttribute( IMarker.SEVERITY, severity);
} catch (CoreException e) {
e.printStackTrace();
}
}
public static void deleteMarkers( IProject project, String id) {
try {
IMarker[] markers = project.findMarkers( IMarker.PROBLEM, true, IResource.DEPTH_INFINITE);
if (markers != null) {
for (IMarker m : markers) {
if (m.getResource().equals( project) && m.getAttribute( IMarker.LOCATION, "").equals( id)) {
m.delete();
}
}
}
} catch (CoreException e) {
e.printStackTrace();
}
}
private Storage() {
// Do nothing
}
}