/******************************************************************************* * Copyright (c) 2015 ARM Ltd. and others * All rights reserved. This program and the accompanying materials * are made available under the terms of the Eclipse Public License v1.0 * which accompanies this distribution, and is available at * http://www.eclipse.org/legal/epl-v10.html * * Contributors: * ARM Ltd and ARM Germany GmbH - Initial API and implementation *******************************************************************************/ package com.arm.cmsis.pack.build.settings; import java.util.ArrayList; import java.util.Arrays; import java.util.Collection; import java.util.Iterator; import java.util.List; import org.eclipse.cdt.managedbuilder.core.BuildException; import org.eclipse.cdt.managedbuilder.core.IConfiguration; import org.eclipse.cdt.managedbuilder.core.IHoldsOptions; import org.eclipse.cdt.managedbuilder.core.IOption; import org.eclipse.cdt.managedbuilder.core.ITool; import org.eclipse.cdt.managedbuilder.core.IToolChain; import org.eclipse.cdt.managedbuilder.core.ManagedBuildManager; import org.eclipse.core.runtime.PlatformObject; import com.arm.cmsis.pack.build.IBuildSettings; import com.arm.cmsis.pack.common.CmsisConstants; import com.arm.cmsis.pack.utils.Utils; /** * Generic toolchain adapter implementation, uses PlatformObject's implementation of IAdaptable interface */ public class RteToolChainAdapter extends PlatformObject implements IRteToolChainAdapter { protected boolean bInitialUpdate = false; @Override public ILinkerScriptGenerator getLinkerScriptGenerator() { // generic adapter has no linker script generator return null; } @Override public void setToolChainOptions(IConfiguration configuration, IBuildSettings buildSettings) { if(configuration == null || buildSettings == null) { return; } if(bInitialUpdate) { updateBuildSteps(configuration, buildSettings, IBuildSettings.PRE_BUILD_STEPS); updateBuildSteps(configuration, buildSettings, IBuildSettings.POST_BUILD_STEPS); } IToolChain toolchain = configuration.getToolChain(); if(toolchain == null) { return; } // iterate over toolchain options updateOptions(configuration, toolchain, buildSettings); // iterate over tools ITool[] tools = toolchain.getTools(); for(ITool t : tools) { if(t == null || !t.isEnabled()) { continue; } updateOptions(configuration, t, buildSettings); } } @Override public void setInitialToolChainOptions(IConfiguration configuration, IBuildSettings buildSettings) { // default updates all options bInitialUpdate = true; setToolChainOptions(configuration, buildSettings); bInitialUpdate = false; } /** * Updates tollchain/tool options for given configuration * @param configuration option's parent IConfiguration * @param tool IHoldsOptions representing ITool or IToolChain * @param buildSettings IBuildSettings containing source RTE information */ protected void updateOptions(IConfiguration configuration, IHoldsOptions tool, IBuildSettings buildSettings) { IOption[] options = tool.getOptions(); for(IOption o : options) { try { if(o != null) { updateOption(configuration, tool, o, buildSettings); } } catch (BuildException e) { // TODO Auto-generated catch block e.printStackTrace(); } } } /** * Updates an option that contains preprocessor defines, libraries, include paths or library paths .<br> * Removes all options after <code>_RTE_</code> and adds new defines. * @param configuration option's parent IConfiguration * @param tool option's parent IHoldsOptions (ITool or IToolChain ) * @param option IOption to update * @param buildSettings IBuildSettings containing source RTE information * @throws BuildException */ protected void updateOption(IConfiguration configuration, IHoldsOptions tool, IOption option, IBuildSettings buildSettings) throws BuildException { int oType = getOptionType(option); if(!bInitialUpdate && isInitialOption(oType)) { return; // initial update only } if(oType > IBuildSettings.RTE_OPTION ) { updateRteOption(oType, configuration, tool, option, buildSettings); } } /** * Updates RTE option value * @param oType option's type : see getRteOptionType() * @param configuration option's parent IConfiguration * @param tool option's parent IHoldsOptions * @param option IOption to update * @param buildSettings IBuildSettings containing source RTE information * @throws BuildException */ protected void updateRteOption(int oType, IConfiguration configuration, IHoldsOptions tool, IOption option, IBuildSettings buildSettings) throws BuildException { int type = option.getBasicValueType(); if(type == IOption.STRING_LIST) { setStringListOptionValue(oType, configuration, tool, option, buildSettings); return; } String value = getRteOptionValue(oType, buildSettings, option); if(value != null) { if(type == IOption.BOOLEAN) { boolean bVal = value.equals("1") || value.equalsIgnoreCase("true"); //$NON-NLS-1$ //$NON-NLS-2$ ManagedBuildManager.setOption(configuration, tool, option, bVal); } else { ManagedBuildManager.setOption(configuration, tool, option, value); } } } /** * Returns RTE values for * @param buildSettings IBuildSettings to get value from * @param type option type returned by getOptionType(IOption option) * @return collection of strings for the option */ protected Collection<String> getStringListValue(IBuildSettings buildSettings, int type) { return buildSettings.getStringListValue(type); } /** * Updates string list option values * @param oType option's extended type: see getOptionType() * @param configuration option's parent IConfiguration * @param tool option's parent IHoldsOptions * @param option IOption to update * @throws BuildException */ protected void setStringListOptionValue(int oType, IConfiguration configuration, IHoldsOptions tool, IOption option, IBuildSettings buildSettings) throws BuildException { if (option.getBasicValueType() != IOption.STRING_LIST) { return; } List<String> value = getCurrentStringListValue(option); if (value == null) { return; } value = cleanStringList(value, oType); Collection<String> newValue = getStringListValue(buildSettings, oType); if (newValue != null) { value.addAll(newValue); } // copy to array and add quotes if needed String[] arrayValue = new String[value.size()]; int i = 0; for (String s : value) { if (isToQuoteOption(oType, option)) { arrayValue[i] = Utils.addQuotes(s); } else { arrayValue[i] = s; } i++; } ManagedBuildManager.setOption(configuration, tool, option, arrayValue); } /** * Checks if specified option type is for initial setting only * @param oType option type * @return true if option is an initial option */ protected boolean isInitialOption(int oType) { return oType > IBuildSettings.RTE_INITIAL_OPTION; } /** * Checks if option value shout be quoted * @param oType option's extended type: see getOptionType() * @param option IOption to check * @return true if surround with quotes */ protected boolean isToQuoteOption(int oType, IOption option) { if(oType == IBuildSettings.RTE_LINKER_SCRIPT) return true; int valueType; try { valueType = option.getValueType(); } catch (BuildException e) { e.printStackTrace(); return false; } switch(valueType){ case IOption.INCLUDE_PATH: case IOption.LIBRARY_PATHS: case IOption.LIBRARIES: case IOption.OBJECTS: return true; case IOption.PREPROCESSOR_SYMBOLS: default: return false; } } /** * Retrieves current string list value form IOption * @param option option from which to retrieve string list * @return string list or null if there is no value for this option * @throws BuildException */ protected List<String> getCurrentStringListValue(IOption option) throws BuildException { String[] array = null; int type = option.getValueType(); switch (type) { case IOption.PREPROCESSOR_SYMBOLS: array = option.getDefinedSymbols(); break; case IOption.INCLUDE_PATH: array = option.getIncludePaths(); break; case IOption.LIBRARY_PATHS: array = option.getLibraryPaths(); break; case IOption.LIBRARIES: array = option.getLibraries(); break; case IOption.OBJECTS: array = option.getUserObjects(); break; case IOption.STRING_LIST: array = option.getStringListValue(); break; default: break; } if(array == null) { return null; } return new ArrayList<String>(Arrays.asList(array)); } /** * Returns current string value stored by option of STRING base type * @param option option to get value from * @return option value as String, null if option base type is not STRING */ public String getCurrentStringValue(IOption option) { try { int type = option.getBasicValueType(); if(type == IOption.STRING) return option.getStringValue(); } catch (BuildException e) { // TODO Auto-generated catch block e.printStackTrace(); } return null; } /** * Removes entries from a string list for given option * @param value list of strings to clean * @param oType option's extended type: see getOptionType() */ protected List<String> cleanStringList(List<String> value, int oType) { switch(oType){ case IBuildSettings.RTE_DEFINES: value = truncateStringList(value, CmsisConstants._RTE_); break; case IBuildSettings.RTE_INCLUDE_PATH: case IBuildSettings.RTE_LIBRARY_PATHS: case IBuildSettings.RTE_LIBRARIES: case IBuildSettings.RTE_OBJECTS: case IBuildSettings.RTE_LINKER_SCRIPT: value = removeRtePathEntries(value); break; default: break; } return value; } /** * Removes all entries in the list after truncateFrom string (inclusive truncateFrom entry) * @param strings list of strings to truncate * @param truncateFrom * @return updated list */ static public List<String> truncateStringList(List<String> strings, String truncateFrom) { if(strings == null) { return null; } int index = strings.indexOf(truncateFrom); if(index >=0) { return strings.subList(0, index); } return strings; } /** * Removes all entries beginning with RTE or ${cmsis_pack_root} paths from supplied list * @param paths list of paths/files to process * @return updated list */ static public List<String> removeRtePathEntries(List<String> paths) { for (Iterator<String> iterator = paths.iterator(); iterator.hasNext();) { String s = iterator.next(); if(s.startsWith(CmsisConstants.PROJECT_RTE_PATH, 1) || s.startsWith(CmsisConstants.CMSIS_PACK_ROOT_VAR, 1) || s.startsWith(CmsisConstants.CMSIS_RTE_VAR, 1) ) { iterator.remove(); } } return paths; } /** * Returns if CPU can have FPU * @param cpu core name * @return true */ public boolean coreHasFpu(String cpu) { if(cpu == null) { return false; } switch(cpu) { case "SC000": //$NON-NLS-1$ case "SC300": //$NON-NLS-1$ case "Cortex-M0": //$NON-NLS-1$ case "Cortex-M0+": //$NON-NLS-1$ case "Cortex-M1": //$NON-NLS-1$ case "Cortex-M3": //$NON-NLS-1$ case "ARMV8MBL": //$NON-NLS-1$ return false; default: case "Cortex-M4": //$NON-NLS-1$ case "Cortex-M7": //$NON-NLS-1$ case "Cortex-R4": //$NON-NLS-1$ case "Cortex-R5": //$NON-NLS-1$ case "Cortex-A5": //$NON-NLS-1$ case "Cortex-A7": //$NON-NLS-1$ case "Cortex-A8": //$NON-NLS-1$ case "Cortex-A9": //$NON-NLS-1$ case "Cortex-A15": //$NON-NLS-1$ case "Cortex-A17": //$NON-NLS-1$ case "Cortex-A53": //$NON-NLS-1$ case "Cortex-A57": //$NON-NLS-1$ case "Cortex-A72": //$NON-NLS-1$ case "ARMV8MML": //$NON-NLS-1$ return true; } } /** * Return option type: base or RTE one * @param option IOption to get type * @return positive integer if it is a known option, -1 otherwise */ public int getOptionType(IOption option) { if(option == null) { return IBuildSettings.UNKNOWN_OPTION; } for(IOption o = option; o != null; o = o.getSuperClass()) { String id = o.getId(); int rteType = getRteOptionType(id); if( rteType > IBuildSettings.RTE_OPTION ) { return rteType; } id = o.getBaseId(); rteType = getRteOptionType(id); if( rteType > IBuildSettings.RTE_OPTION ) { return rteType; } } try { return getOptionType(option.getValueType()); } catch (BuildException e) { e.printStackTrace(); } return IBuildSettings.UNKNOWN_OPTION; } /** * Returns device option type for specified option ID * @param id ID or base ID to check * @return positive integer if it is a device-related option, 0 otherwise */ protected int getRteOptionType(String id) { // default has no idea about specific option IDs return IBuildSettings.UNKNOWN_OPTION; } protected int getOptionType(int valueType) { // default converts some base types to RTE ones switch(valueType){ case IOption.PREPROCESSOR_SYMBOLS: return IBuildSettings.RTE_DEFINES; case IOption.INCLUDE_PATH: return IBuildSettings.RTE_INCLUDE_PATH; case IOption.LIBRARIES: return IBuildSettings.RTE_LIBRARIES; case IOption.LIBRARY_PATHS: return IBuildSettings.RTE_LIBRARY_PATHS; case IOption.OBJECTS: return IBuildSettings.RTE_OBJECTS; default: break; } return valueType; } /** * Returns option value for given option type * @param oType RTE option type * @param buildSettings IBuildSettings containing source RTE information * @param option IOption for which to get new value * @return new option value as a string */ protected String getRteOptionValue(int oType, IBuildSettings buildSettings, IOption option) { if(oType > IBuildSettings.RTE_OPTION) { switch(oType) { case IBuildSettings.RTE_LINKER_SCRIPT: return getLinkerSrciptOptionValue(buildSettings); default: // default simply returns device attribute value return getDeviceAttribute(oType, buildSettings); } } return null; } /** * Returns device attribute for given option type * @return CPU option string */ protected String getDeviceAttribute(int oType, IBuildSettings buildSettings) { switch(oType) { case IBuildSettings.CPU_OPTION: return buildSettings.getDeviceAttribute("Dcore"); //$NON-NLS-1$ case IBuildSettings.FPU_OPTION: return buildSettings.getDeviceAttribute("Dfpu"); //$NON-NLS-1$ case IBuildSettings.ENDIAN_OPTION: return buildSettings.getDeviceAttribute("Dendian"); //$NON-NLS-1$ default: break; } return null; } /** * Returns single linker script file if it is only one * @param buildSettings IBuildSettings to get value from * @return single linker script file or null */ protected String getLinkerSrciptOptionValue(IBuildSettings buildSettings) { return buildSettings.getSingleLinkerScriptFile(); } /** * Updates pre- or post-build command for given configuration * @param configuration destination IConfiguration to set steps to * @param buildSettings source IBuildSettings * @param oType option type: PRE_BUILD_STEPS or POST_BUILD_STEPS */ protected void updateBuildSteps(IConfiguration configuration, IBuildSettings buildSettings, int oType) { String step; if(oType == IBuildSettings.PRE_BUILD_STEPS) step = configuration.getPrebuildStep(); else if(oType == IBuildSettings.POST_BUILD_STEPS) step = configuration.getPostbuildStep(); else return; if(step == null) // actually should not happen, but do not rely on CDT step = CmsisConstants.EMPTY_STRING; // find begin and end markers int beginPos = step.indexOf(CmsisConstants.CMSIS_RTE_BEGIN_VAR); if(beginPos < 0) beginPos = step.length(); int endPos = step.indexOf(CmsisConstants.CMSIS_RTE_END_VAR); if(endPos < 0) endPos = step.length(); else { if(beginPos > endPos) beginPos = endPos; // a marker has been removed by user endPos += CmsisConstants.CMSIS_RTE_END_VAR.length(); } String prefix = (beginPos > 0) ? step.substring(0, beginPos) : CmsisConstants.EMPTY_STRING; String suffix = (endPos >= 0) ? step.substring(endPos) : CmsisConstants.EMPTY_STRING; String rteCommand = getPrePostCommand(buildSettings, oType); String newStep = CmsisConstants.EMPTY_STRING; if(!prefix.isEmpty()) { if(prefix.endsWith(";")) //$NON-NLS-1$ prefix = prefix.substring(0, prefix.length() - 1); newStep += prefix; } if(rteCommand != null && !rteCommand.isEmpty()) { if(!newStep.isEmpty() && !newStep.endsWith(";")) //$NON-NLS-1$ newStep += ';'; newStep += rteCommand; } if(!suffix.isEmpty()) { if(!newStep.isEmpty() && !newStep.endsWith(";") && !suffix.startsWith(";")) //$NON-NLS-1$ //$NON-NLS-2$ newStep += ';'; newStep += suffix; } if(step.equals(newStep)) return; // nothing to do if(oType == IBuildSettings.PRE_BUILD_STEPS) configuration.setPrebuildStep(newStep); else if(oType == IBuildSettings.POST_BUILD_STEPS) configuration.setPostbuildStep(newStep); } /** * Returns assembled command of build steps * @param buildSettings source IBuildSettings * @param oType option type: PRE_BUILD_STEPS or POST_BUILD_STEPS * @return String containing assembled command of build steps */ protected String getPrePostCommand(IBuildSettings buildSettings, int oType) { Collection<String> steps = buildSettings.getStringListValue(oType); if(steps == null || steps.isEmpty()) return CmsisConstants.EMPTY_STRING; String cmd = CmsisConstants.CMSIS_RTE_BEGIN_VAR; for(String s : steps) { if(cmd.length() > CmsisConstants.CMSIS_RTE_BEGIN_VAR.length()) cmd += ';'; cmd += s; } cmd += CmsisConstants.CMSIS_RTE_END_VAR; return cmd; } }