/******************************************************************************* * 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.configuration; import java.util.Collection; import java.util.HashMap; import java.util.HashSet; import java.util.LinkedList; import java.util.List; import java.util.Map; import java.util.Set; import java.util.TreeMap; import java.util.TreeSet; import org.eclipse.core.runtime.PlatformObject; import com.arm.cmsis.pack.build.IBuildSettings; import com.arm.cmsis.pack.build.IMemorySettings; import com.arm.cmsis.pack.build.MemorySettings; import com.arm.cmsis.pack.common.CmsisConstants; import com.arm.cmsis.pack.data.CpCodeTemplate; import com.arm.cmsis.pack.data.ICpCodeTemplate; import com.arm.cmsis.pack.data.ICpComponent; import com.arm.cmsis.pack.data.ICpDebugConfiguration; import com.arm.cmsis.pack.data.ICpDeviceItem; import com.arm.cmsis.pack.data.ICpFile; import com.arm.cmsis.pack.data.ICpItem; import com.arm.cmsis.pack.data.ICpMemory; import com.arm.cmsis.pack.data.ICpPack; import com.arm.cmsis.pack.data.ICpPack.PackState; import com.arm.cmsis.pack.enums.EEvaluationResult; import com.arm.cmsis.pack.enums.EFileCategory; import com.arm.cmsis.pack.enums.EFileRole; import com.arm.cmsis.pack.info.ICpComponentInfo; import com.arm.cmsis.pack.info.ICpConfigurationInfo; import com.arm.cmsis.pack.info.ICpDeviceInfo; import com.arm.cmsis.pack.info.ICpFileInfo; import com.arm.cmsis.pack.info.ICpPackInfo; import com.arm.cmsis.pack.project.CpVariableResolver; import com.arm.cmsis.pack.project.Messages; import com.arm.cmsis.pack.project.utils.ProjectUtils; import com.arm.cmsis.pack.project.utils.RtePathComparator; import com.arm.cmsis.pack.rte.IRteModel; import com.arm.cmsis.pack.rte.RteModel; import com.arm.cmsis.pack.rte.dependencies.IRteDependencyItem; import com.arm.cmsis.pack.utils.AlnumComparator; import com.arm.cmsis.pack.utils.Utils; /** * Default implementation of IRteConfiguration interface */ public class RteConfiguration extends PlatformObject implements IRteConfiguration { protected IRteModel fModel = null; // underlying model that is source of information protected ICpConfigurationInfo fConfigInfo = null; // meta-information that is stored .rteconfig file and used to transfer information to/from model protected RteBuildSettings rteBuildSettings = new RteBuildSettings(); // source files included in project: project relative path -> absPath protected Map<String, ICpFileInfo> projectFiles = new HashMap<String, ICpFileInfo>(); // root of the code template hierarchy protected ICpCodeTemplate fCodeTemplateRoot = new CpCodeTemplate(null); // scvd files for component viewer: project relative path -> absPath protected Map<String, ICpFileInfo> fScvdFiles = new HashMap<String, ICpFileInfo>(); // paths to library sources (for debugger) protected Set<String> libSourcePaths = new TreeSet<String>(new RtePathComparator()); // pieces of code put into RTE_Components.h file protected List<String> rteComponentsH = new LinkedList<String>(); // header -> comment (for editor) protected Map<String, String> headers = new TreeMap<String, String>(new AlnumComparator(false)); // documentation files relevant to configuration protected Map<String, String> docs = new TreeMap<String, String>(new AlnumComparator(false)); protected String svdFile = null; protected ICpComponentInfo deviceStartupComponent = null; protected ICpComponentInfo cmsisCoreComponent = null; protected ICpComponentInfo cmsisRtosComponent = null; // device header name without path protected String deviceHeader = null; protected Collection<ICpPackInfo> fMissingPacks = new HashSet<ICpPackInfo>(); boolean valid = true; // flag that indicates that device and all required components are resolved public RteConfiguration() { } protected void clear() { rteBuildSettings.clear(); projectFiles.clear(); libSourcePaths.clear(); rteComponentsH.clear(); headers.clear(); docs.clear(); deviceHeader = null; deviceStartupComponent = null; cmsisCoreComponent = null; cmsisRtosComponent = null; valid = true; fMissingPacks.clear(); fScvdFiles.clear(); } @Override public ICpConfigurationInfo getConfigurationInfo() { return fConfigInfo; } @Override public ICpDeviceInfo getDeviceInfo() { return fConfigInfo != null ? fConfigInfo.getDeviceInfo() : null; } @Override public ICpDebugConfiguration getDebugConfiguration() { ICpDeviceInfo di = getDeviceInfo(); if(di != null) { return di.getDebugConfiguration(); } return null; } @Override public ICpPack getDfp() { if(fConfigInfo == null) { return null; } return fConfigInfo.getPack(); } @Override public String getDfpPath() { if(fConfigInfo == null) { return null; } return fConfigInfo.getDfpPath(); } @Override public IBuildSettings getBuildSettings() { return rteBuildSettings; } @Override public Map<String, ICpFileInfo> getProjectFiles() { return projectFiles; } @Override public ICpFileInfo getProjectFileInfo(String fileName) { return projectFiles.get(fileName); } @Override public ICpFileInfo[] getProjectFileInfos(String fileName) { Collection<ICpFileInfo> fileInfos = new LinkedList<>(); for (String key : projectFiles.keySet()) { if (key.matches(fileName)) { fileInfos.add(projectFiles.get(key)); } } ICpFileInfo[] infos = new ICpFileInfo[fileInfos.size()]; return fileInfos.toArray(infos); } @Override public Collection<String> getLibSourcePaths() { return libSourcePaths; } @Override public Collection<String> getRteComponentsHCode() { return rteComponentsH; } @Override public Map<String, String> getHeaders() { return headers; } @Override public Map<String, String> getDocs() { return docs; } @Override public String getDeviceHeader() { return deviceHeader; } @Override public String getSvdFile() { return svdFile; } @Override public ICpComponentInfo getDeviceStartupComponent() { return deviceStartupComponent; } @Override public ICpComponentInfo getCmsisCoreComponent() { return cmsisCoreComponent; } @Override public ICpComponentInfo getCmsisRtosComponent() { return cmsisRtosComponent; } @Override public ICpCodeTemplate getCmsisCodeTemplate() { return fCodeTemplateRoot; } @Override public Map<String, ICpFileInfo> getScvdFiles() { return fScvdFiles; } @Override public void setConfigurationInfo(ICpConfigurationInfo info) { if(fConfigInfo == info) { return; } fConfigInfo = info; if(fModel == null && fConfigInfo != null) { fModel = new RteModel(); } if(fModel != null) { fModel.setConfigurationInfo(info); fConfigInfo = fModel.getConfigurationInfo(); } collectSettings(); } protected void collectSettings() { clear(); if(getDeviceInfo() == null) { return; } // insert default settings rteBuildSettings.addStringListValue(IBuildSettings.RTE_INCLUDE_PATH, CmsisConstants.PROJECT_RTE_PATH); rteBuildSettings.addStringListValue(IBuildSettings.RTE_DEFINES, CmsisConstants._RTE_); headers.put(CmsisConstants.RTE_RTE_Components_h, Messages.RteConfiguration_ComponentSelection); ICpItem apisItem = fConfigInfo.getFirstChild(CmsisConstants.APIS_TAG); collectComponentSettings(apisItem); ICpItem componentsItem = fConfigInfo.getFirstChild(CmsisConstants.COMPONENTS_TAG); collectComponentSettings(componentsItem); collectDeviceSettings(getDeviceInfo()); } protected void collectDeviceSettings(ICpDeviceInfo di) { ICpDeviceItem d = di.getDevice(); if(d == null) { return; } rteBuildSettings.setDeviceAttributes(di.attributes()); ICpItem props = di.getEffectiveProperties(); if(props == null) { return; } Collection<? extends ICpItem> children = props.getChildren(); for(ICpItem p : children) { String tag = p.getTag(); if(tag.equals(CmsisConstants.COMPILE_TAG)) { String define = p.getAttribute(CmsisConstants.DEFINE); rteBuildSettings.addStringListValue(IBuildSettings.RTE_DEFINES, define); String pdefine = p.getAttribute(CmsisConstants.PDEFINE); rteBuildSettings.addStringListValue(IBuildSettings.RTE_DEFINES, pdefine); String header = p.getAttribute(CmsisConstants.HEADER); if(header != null && !header.isEmpty()) { deviceHeader = Utils.extractFileName(header); // check if header is already defined via device startup component boolean inserted = false; for(String h: headers.keySet()) { if(Utils.extractFileName(h).equals(deviceHeader)) { inserted = true; break; } } if(!inserted) { header = p.getAbsolutePath(header); header = CpVariableResolver.insertCmsisRootVariable(header); addFile(header, EFileCategory.HEADER, Messages.RteConfiguration_DeviceHeader); } } } } } protected void collectComponentSettings(ICpItem componentsParent) { if(componentsParent == null) { return; } Collection<? extends ICpItem> components = componentsParent.getChildren(); if(components == null || components.isEmpty()) { return; } for(ICpItem child : components) { if(child instanceof ICpComponentInfo) { collectComponentSettings((ICpComponentInfo)child); } } } protected void collectComponentSettings(ICpComponentInfo ci) { // collect specific components if(ci.isDeviceStartupComponent()) { deviceStartupComponent = ci; } else if(ci.isCmsisCoreComponent()) { cmsisCoreComponent = ci; } else if(ci.isCmsisRtosComponent()) { cmsisRtosComponent = ci; } ICpComponent c = ci.getComponent(); int count = ci.getInstanceCount(); if(c != null) { addRteComponentsHCode(c, count); } collectFiles(ci); } protected void collectFiles(ICpComponentInfo ci) { Collection<? extends ICpItem> children = ci.getChildren(); if( children == null || children.isEmpty()) { return; } boolean bMultiInstance = ci.isMultiInstance(); int count = ci.getInstanceCount(); for(ICpItem child : children) { if(!(child instanceof ICpFileInfo)) { continue; } ICpFileInfo fi = (ICpFileInfo)child; if(bMultiInstance && fi.getRole() == EFileRole.CONFIG) { for(int i = 0; i < count; i++) { collectFile(fi, ci, i); } } else { collectFile(fi, ci, -1); } } } /** * Collects file to configuration * @param fi ICpFileInfo * @param ci parent ICpComponentInfo * @param index for multi-instance components : instance index, for others -1 */ protected void collectFile(ICpFileInfo fi, ICpComponentInfo ci, int index) { String name = fi.getName(); ICpFile f = fi.getFile(); String absPath = null; String effectivePath = null; if(f != null) { absPath = f.getAbsolutePath(name); } EFileRole role = fi.getRole(); if(isAddToProject(fi)){ String className = ci.getAttribute(CmsisConstants.CCLASS); String deviceName = fConfigInfo.getDeviceInfo().getDeviceName(); effectivePath = getPathRelativeToProject(fi, className, deviceName, index); projectFiles.put(effectivePath, fi); if(fi.isGenerated() || (role != EFileRole.CONFIG && role != EFileRole.COPY)) { effectivePath = CpVariableResolver.insertCmsisRootVariable(absPath); } } else { effectivePath = CpVariableResolver.insertCmsisRootVariable(absPath); } EFileCategory cat = fi.getCategory(); if(cat == EFileCategory.LINKER_SCRIPT && !ci.isDeviceStartupComponent()) { return; } String componentName = ci.getName(); addFile(effectivePath, cat, componentName); if(cat == EFileCategory.LIBRARY) { addLibrarySourcePaths(f); } collectCodeTemplates(fi, ci); collectScvdFile(fi); } /** * Collects file of code templates * @param fi ICpFileInfo * @param ci parent ICpComponentInfo */ protected void collectCodeTemplates(ICpFileInfo fi, ICpComponentInfo ci) { ICpPack pack = ci.getPack(); if (fi.getRole() == EFileRole.TEMPLATE && pack != null && (pack.getPackState() == PackState.INSTALLED)) { String className = ci.getAttribute(CmsisConstants.CCLASS); ICpCodeTemplate component = (ICpCodeTemplate) fCodeTemplateRoot.getFirstChild(className); if (component == null) { component = new CpCodeTemplate(fCodeTemplateRoot, className, ci); fCodeTemplateRoot.addChild(component); } String selectName = fi.getAttribute(CmsisConstants.SELECT); ICpCodeTemplate codeTemplate = (ICpCodeTemplate) component.getFirstChild(selectName); if (codeTemplate == null) { codeTemplate = new CpCodeTemplate(component, selectName, fi); component.addChild(codeTemplate); } codeTemplate.addCodeTemplate(fi.getAttribute(CmsisConstants.NAME)); } } /** * Collects scvd files for component viewer * @param fi ICpFileInfo */ protected void collectScvdFile(ICpFileInfo fi) { ICpPack pack = fi.getPack(); if (fi.getCategory() == EFileCategory.OTHER && fi.getName().endsWith(CmsisConstants.EXT_SCVD) && pack != null && pack.getPackState() == PackState.INSTALLED) { fScvdFiles.put(pack.getAbsolutePath(fi.getName()), fi); } } /** * Adds {@link CmsisConstants#PROJECT_LOCAL_PATH} prefix if path is relative * @param path path to adjust * @return */ protected String adjustRelativePath(String path){ if(path == null || path.isEmpty()) { return path; } if(path.startsWith(CmsisConstants.RTE)) { return CmsisConstants.PROJECT_LOCAL_PATH + path; } if(path.startsWith(CmsisConstants.CMSIS_PACK_ROOT_VAR)) { return path; } if(path.startsWith(CmsisConstants.CMSIS_RTE_VAR)) { return path; } return CmsisConstants.CMSIS_RTE_VAR + path; } protected void addFile(String effectivePath, EFileCategory cat, String comment) { if(effectivePath == null || effectivePath.isEmpty()) { return; } switch(cat){ case DOC: docs.put(effectivePath, comment); break; case HEADER: headers.put(effectivePath, comment); effectivePath = ProjectUtils.removeLastPathSegment(effectivePath); case INCLUDE: if(!effectivePath.isEmpty()) { effectivePath = adjustRelativePath(effectivePath); rteBuildSettings.addStringListValue(IBuildSettings.RTE_INCLUDE_PATH, Utils.removeTrailingSlash(effectivePath)); } break; case IMAGE: break; case LIBRARY: effectivePath = adjustRelativePath(effectivePath); rteBuildSettings.addStringListValue(IBuildSettings.RTE_LIBRARIES, effectivePath); effectivePath = ProjectUtils.removeLastPathSegment(effectivePath); rteBuildSettings.addStringListValue(IBuildSettings.RTE_LIBRARY_PATHS, Utils.removeTrailingSlash(effectivePath)); break; case LINKER_SCRIPT: effectivePath = adjustRelativePath(effectivePath); rteBuildSettings.addStringListValue(IBuildSettings.RTE_LINKER_SCRIPT, effectivePath); break; case OBJECT: effectivePath = adjustRelativePath(effectivePath); rteBuildSettings.addStringListValue(IBuildSettings.RTE_OBJECTS, effectivePath); break; case OTHER: break; case SOURCE: break; case SOURCE_ASM: break; case SOURCE_C: break; case SOURCE_CPP: break; case UTILITY: break; case SVD: svdFile = effectivePath; default: break; } } protected void addLibrarySourcePaths(ICpFile f) { if(f == null) { return; } String src = f.getAttribute(CmsisConstants.SRC); if(src == null || src.isEmpty()) { return; } String[] paths = src.split(";"); //$NON-NLS-1$ if(paths == null || paths.length == 0) { return; } for(String p: paths){ if(p == null || p.isEmpty()) { continue; } String absPath = f.getAbsolutePath(p); String path = CpVariableResolver.insertCmsisRootVariable(absPath); libSourcePaths.add(path); } } /** * Adds piece of RteComponents.h code for the component * @param c ICpComponent * @param count number of component instances * @return code string */ protected void addRteComponentsHCode(ICpComponent c, int count) { String code = c.getRteComponentsHCode(); if(code == null || code.isEmpty()) { return ; } // convert all line endings to unix format code = code.replaceAll("\\\\r\\\\n", "\\\\n"); //$NON-NLS-1$ //$NON-NLS-2$ int index = code.indexOf(CmsisConstants.pINSTANCEp); if(index >= 0) { for(int i = 0; i < count; i++) { String instance = String.valueOf(i); String tmp = code.replaceAll(CmsisConstants.pINSTANCEp, instance); rteComponentsH.add(tmp); } } else { rteComponentsH.add(code); } } @Override public boolean isAddToProject(ICpFileInfo fi) { if(fi == null ) { return false; } if(isGeneratedAndRelativeToProject(fi)) { return true; } EFileRole role = fi.getRole(); boolean includeInProject = false; switch(role) { case INTERFACE: case TEMPLATE: return false; case CONFIG: case COPY: includeInProject = true; case NONE: default: break; } EFileCategory cat = fi.getCategory(); switch(cat){ case SOURCE: case SOURCE_ASM: case SOURCE_C: case SOURCE_CPP: case LINKER_SCRIPT: case LIBRARY: case OBJECT: return true; case INCLUDE: return false; case HEADER: default: break; } return includeInProject; } /** * Check if file is generated and relative to project (=> to config file directory) * @param fi {@link ICpFileInfo} to check * @return true if file is resolved to a generated file that is relative to project directory */ protected boolean isGeneratedAndRelativeToProject(ICpFileInfo fi) { ICpFile f= fi.getFile(); if(f != null && f.isGenerated()) { String abs = f.getAbsolutePath(f.getName()); String base = fConfigInfo.getDir(true); if(abs.startsWith(base)) { return true; } } return false; } /** * * @param fi {@link ICpFileInfo} * @param className * @param deviceName * @param index * @return */ protected String getPathRelativeToProject(ICpFileInfo fi, String className, String deviceName, int index){ if(fi == null) { return null; } if(fi.isGenerated()) { ICpFile f = fi.getFile(); String absPath = f.getAbsolutePath(f.getName()); String baseDir = fConfigInfo.getDir(false); if(absPath.startsWith(baseDir)) { // the file is within project return Utils.makePathRelative(absPath, baseDir); } } String path = CmsisConstants.RTE; path += '/'; if(className != null && !className.isEmpty()) { path += Utils.wildCardsToX(className) + '/'; // escape spaces with underscores } if(fi.isDeviceDependent() && deviceName != null && !deviceName.isEmpty()) { path += Utils.wildCardsToX(deviceName) + '/'; } String fileName = Utils.extractFileName(fi.getName()); if(index >= 0) { String ext = Utils.extractFileExtension(fileName); fileName = Utils.extractBaseFileName(fileName); fileName += "_" + String.valueOf(index); //$NON-NLS-1$ if(ext != null) { fileName += "." + ext; //$NON-NLS-1$ } } return path + fileName; } /** * Creates memory settings from device information * @param deviceInfo ICpDeviceInfo object */ public static IMemorySettings createMemorySettings(ICpDeviceInfo di) { ICpDeviceItem d = di.getDevice(); if(d == null) { return null; } ICpItem props = di.getEffectiveProperties(); if(props == null) { return null; } ICpDebugConfiguration dc = di.getDebugConfiguration(); Map<String, ICpMemory> memoryItems = dc.getMemoryItems(); return new MemorySettings(memoryItems); } @Override public Collection<? extends IRteDependencyItem> validate() { EEvaluationResult res = fModel.getEvaluationResult(); if(res.ordinal() >= EEvaluationResult.INSTALLED.ordinal()) { valid = true; return null; } return fModel.getDependencyItems(); } @Override public boolean isValid() { return valid; } @Override public EEvaluationResult getEvaluationResult() { if(fModel != null) { return fModel.getEvaluationResult(); } return EEvaluationResult.UNDEFINED; } @Override public void setEvaluationResult(EEvaluationResult result) { if(fModel != null) { fModel.setEvaluationResult(result); } } @Override public boolean isGeneratedPackUsed(String gpdsc) { if(fModel != null) { return fModel.isGeneratedPackUsed(gpdsc); } return false; } }