/* * 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 options = new LinkedList(); 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 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 options) { List filteredOptions = new LinkedList(); // 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 } }