summaryrefslogblamecommitdiffstats
path: root/org.rtems.cdt/src/org/rtems/cdt/Storage.java
blob: e0cc1a54afadf9a148e3cf870c138f6ee4e6eac7 (plain) (tree)







































                                                                                  
                                          
                                           
                                            
























                                                                                   
                










                                                                                            
        









































                                                                                                 
                                                       





























                                                                                                  







                                                                             
                                                             
                                            









                                                                                    









                                                                                  
                                                   




















                                                                                                   
                                                                
                                      











                                                                                      























                                                                                                                 
                                                                                                

                                                                                

                                              


                                                                                       
                                

                                                             
                                                     


                                              







                                                                                                                              


                                   
                                                                                                                 

                                          
                                    










                                                                                            















                                                                                          
                                                                                                         






                                                                                                         
                                                                                                         


                                                           
                                                                                            



                                                                   
                                                                                           






                                                                            
                                     




                                                                            
                           







                                                                          


                                 

                                    
                                                      
                                     


                                                                   
                                                                                                                                              

                          





                                                                                                                      







                                                                                                                             
                                                                                                                                    













                                                                          


                                                              

                                                                  









                                                                                                      










                                                                                                          



                                                                                                  



                                                                                       
        


























                                                                                                                                    




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