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