/*
* 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
}
}