package com.arm.cmsis.pack.project;
import java.io.File;
import java.io.FileNotFoundException;
import java.io.PrintWriter;
import java.text.SimpleDateFormat;
import java.util.Collection;
import java.util.Date;
import java.util.HashMap;
import java.util.Map;
import java.util.Map.Entry;
import java.util.stream.Collectors;
import org.eclipse.cdt.managedbuilder.core.IConfiguration;
import org.eclipse.cdt.managedbuilder.core.IManagedBuildInfo;
import org.eclipse.cdt.managedbuilder.core.ManagedBuildManager;
import org.eclipse.core.resources.IFile;
import org.eclipse.core.resources.IFolder;
import org.eclipse.core.resources.IMarker;
import org.eclipse.core.resources.IProject;
import org.eclipse.core.resources.IResource;
import org.eclipse.core.resources.IWorkspace;
import org.eclipse.core.resources.ResourcesPlugin;
import org.eclipse.core.resources.WorkspaceJob;
import org.eclipse.core.runtime.CoreException;
import org.eclipse.core.runtime.IPath;
import org.eclipse.core.runtime.IProgressMonitor;
import org.eclipse.core.runtime.IStatus;
import org.eclipse.core.runtime.Status;
import com.arm.cmsis.pack.CpPlugIn;
import com.arm.cmsis.pack.ICpEnvironmentProvider;
import com.arm.cmsis.pack.build.IBuildSettings;
import com.arm.cmsis.pack.build.IMemorySettings;
import com.arm.cmsis.pack.build.settings.ILinkerScriptGenerator;
import com.arm.cmsis.pack.build.settings.IRteToolChainAdapter;
import com.arm.cmsis.pack.common.CmsisConstants;
import com.arm.cmsis.pack.configuration.IRteConfiguration;
import com.arm.cmsis.pack.configuration.RteConfiguration;
import com.arm.cmsis.pack.data.ICpFile;
import com.arm.cmsis.pack.data.ICpItem;
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.generic.IAttributes;
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.parser.CpConfigParser;
import com.arm.cmsis.pack.project.ui.RteProjectDecorator;
import com.arm.cmsis.pack.project.utils.ProjectUtils;
import com.arm.cmsis.pack.rte.components.IRteComponentItem;
import com.arm.cmsis.pack.rte.dependencies.IRteDependencyItem;
import com.arm.cmsis.pack.ui.CpPlugInUI;
import com.arm.cmsis.pack.ui.console.RteConsole;
import com.arm.cmsis.pack.utils.Utils;
public class RteProjectUpdater extends WorkspaceJob {
public static final String RTE_PROBLEM_MARKER = CpPlugInUI.RTE_PROBLEM_MARKER;
public static final String RTE_PROBLEM_MARKER_DEP_ITEM = CpPlugInUI.RTE_PROBLEM_MARKER_DEP_ITEM;
public static final int LOAD_CONFIGS = 0x01;
public static final int UPDATE_TOOLCHAIN = 0x02; // forces update of all relevant toolchain settings
public static final int CLEANUP_RTE_FILES = 0x04; // delete excluded RTE config files
protected IRteProject rteProject;
protected IProject project;
protected IProgressMonitor monitor = null;
protected int updateFlags = 0;
protected boolean bLoadConfigs = false;
protected boolean bForceUpdateToolchain = false;
protected boolean bSaveProject = false;
protected boolean bDeleteConfigFiles = false;
protected RteConsole rteConsole = null;
public RteProjectUpdater(IRteProject rteProject, int updateFlags) {
super("RTE Project Updater"); //$NON-NLS-1$
this.rteProject = rteProject;
this.project = rteProject.getProject();
this.updateFlags = updateFlags;
bLoadConfigs = (updateFlags & LOAD_CONFIGS) == LOAD_CONFIGS;
bForceUpdateToolchain = (updateFlags & UPDATE_TOOLCHAIN) == UPDATE_TOOLCHAIN;
bDeleteConfigFiles = (updateFlags & CLEANUP_RTE_FILES) == CLEANUP_RTE_FILES;
IWorkspace workspace = ResourcesPlugin.getWorkspace();
setRule(workspace.getRoot()); // ensures synch update
rteConsole = RteConsole.openConsole(project);
}
@Override
public IStatus runInWorkspace(IProgressMonitor monitor) {
if (project == null || rteProject == null) {
Status status = new Status(IStatus.ERROR, CpPlugInUI.PLUGIN_ID,
Messages.RteProjectUpdater_ErrorProjectIsNull);
return status;
}
this.monitor = monitor;
bSaveProject = false;
Status status = null;
EEvaluationResult res = EEvaluationResult.FULFILLED;
try {
long startTime = System.currentTimeMillis();
String timestamp = new SimpleDateFormat("HH:mm:ss").format(new Date(startTime)); //$NON-NLS-1$
String msg = timestamp + " **** " + Messages.RteProjectUpdater_UpdatingProject + " " + project.getName(); //$NON-NLS-1$ //$NON-NLS-2$
rteConsole.outputInfo(msg, project);
String packRoot = CpVariableResolver.getCmsisPackRoot();
if(packRoot == null || packRoot.isEmpty()){
status = new Status(IStatus.WARNING, CpPlugInUI.PLUGIN_ID, Messages.RteProjectUpdater_ErrorCmisPackRootNotSet);
throw new CoreException(status);
}
if (bLoadConfigs) {
// rteConsole.outputInfo(Messages.RteProjectUpdater_LoadingRteConfiguration);
res = loadConfigFile();
}
// rteConsole.outputInfo(Messages.RteProjectUpdater_UpdatingResources);
addResources();
removeResources();
updateRteComponentsH();
//rteConsole.outputInfo(Messages.RteProjectUpdater_UpdatingBuildSettings);
updateBuildSettings(bForceUpdateToolchain);
if (bSaveProject) {
rteProject.save();
}
project.refreshLocal(IResource.DEPTH_INFINITE, monitor);
updateIndex();
} catch (CoreException e) {
status = new Status(e.getStatus().getSeverity(), CpPlugInUI.PLUGIN_ID,
Messages.RteProjectUpdater_ErrorUpdatingRteProject, e);
res = EEvaluationResult.FAILED;
} catch (Exception e) {
e.printStackTrace();
status = new Status(IStatus.ERROR, CpPlugInUI.PLUGIN_ID,
Messages.RteProjectUpdater_ErrorUpdatingRteProject, e);
res = EEvaluationResult.FAILED;
}
// Output the error message to the RTE console
if (res.ordinal() < EEvaluationResult.INSTALLED.ordinal() || status != null) {
rteConsole.outputError(Messages.RteProjectUpdater_Fail, project);
if(status != null) {
rteConsole.outputInfo(status.getMessage(), project);
IStatus[] statusArray = status.getChildren();
if (statusArray != null && statusArray.length > 0) {
for (IStatus s : statusArray) {
rteConsole.outputInfo(s.getMessage(), project);
}
}
}
} else {
rteConsole.outputInfo(Messages.RteProjectUpdater_Success, project);
}
rteConsole.output(CmsisConstants.EMPTY_STRING, project);
RteProjectDecorator.refresh();
if(status == null) {
status = new Status(IStatus.OK, CpPlugInUI.PLUGIN_ID, Messages.RteProjectUpdater_ProjectUpdated);
}
rteProject.setUpdateCompleted(true);
return status;
}
protected void collectErrors(Collection<? extends IRteDependencyItem> errors) throws CoreException {
IFile rteFile = rteProject.getProject().getFile(rteProject.getName() + CmsisConstants.DOT_RTECONFIG);
rteFile.deleteMarkers(RTE_PROBLEM_MARKER, true, IResource.DEPTH_INFINITE);
if (!rteFile.exists()) {
return;
}
if (errors == null || errors.isEmpty()) {
return;
}
IRteConfiguration conf = rteProject.getRteConfiguration();
if(conf == null) {
return;
}
for (IRteDependencyItem depItem : errors) {
IMarker marker = rteFile.createMarker(RTE_PROBLEM_MARKER);
Map<String, Object> attributes = new HashMap<>();
attributes.put(IMarker.SEVERITY, IMarker.SEVERITY_ERROR);
attributes.put(IMarker.MESSAGE, depItem.getName() + " - " + depItem.getDescription()); //$NON-NLS-1$
IRteComponentItem item = depItem.getComponentItem();
if (item != null) {
attributes.put(IMarker.LOCATION, String.join("->", //$NON-NLS-1$
item.getKeyPath().stream().filter(s -> !s.isEmpty()).collect(Collectors.toList())));
attributes.put(RTE_PROBLEM_MARKER_DEP_ITEM, depItem);
}
marker.setAttributes(attributes);
}
}
protected void updateIndex() {
rteProject.setUpdateCompleted(true);
CpProjectPlugIn.getRteProjectManager().updateIndex(project);
}
protected EEvaluationResult loadConfigFile() throws CoreException {
String savedRteConfigName = rteProject.getRteConfigurationName();
IRteConfiguration rteConf = loadRteConfiguration(savedRteConfigName);
Collection<? extends IRteDependencyItem> errors = rteConf.validate();
collectErrors(errors);
EEvaluationResult res = rteConf.getEvaluationResult();
if (errors == null || errors.isEmpty()) {
return res;
}
String msg = Messages.RteProjectUpdater_ErrorLoadinConfigFile + " '" + savedRteConfigName + "':"; //$NON-NLS-1$ //$NON-NLS-2$
rteConsole.outputError(msg, project);
for (IRteDependencyItem item : errors) {
String s = item.getName() + " - " + item.getDescription(); //$NON-NLS-1$
rteConsole.output(s, project);
msg += System.lineSeparator() + s;
}
if(res.ordinal() > EEvaluationResult.FAILED.ordinal()){
return res;
}
Status status = new Status(IStatus.WARNING, CpPlugInUI.PLUGIN_ID, msg);
throw new CoreException(status);
}
protected IRteConfiguration loadRteConfiguration(String savedRteConfigName) throws CoreException {
if (savedRteConfigName == null || savedRteConfigName.isEmpty()) {
String msg = Messages.RteProjectUpdater_ErrorLoadinConfigFile + " '' " + //$NON-NLS-1$
Messages.RteProjectUpdater_ErrorConfigFileNotExist;
Status status = new Status(IStatus.ERROR, CpPlugInUI.PLUGIN_ID, msg);
throw new CoreException(status);
}
String rteConfigName = project.getName() + CmsisConstants.DOT_RTECONFIG;
// moving project can still left the old file
IFile iFile = project.getFile(rteConfigName);
if (!iFile.exists() || iFile.getLocation() == null) {
// ensure file has the project name (e.g. after rename)
if (!rteConfigName.equals(savedRteConfigName)) {
iFile = project.getFile(savedRteConfigName);
if (!iFile.exists() || iFile.getLocation() == null) {
String msg = Messages.RteProjectUpdater_ErrorLoadinConfigFile + " '" + savedRteConfigName + "' " + //$NON-NLS-1$//$NON-NLS-2$
Messages.RteProjectUpdater_ErrorConfigFileNotExist;
Status status = new Status(IStatus.ERROR, CpPlugInUI.PLUGIN_ID, msg);
throw new CoreException(status);
}
rteConfigName = savedRteConfigName;
}
}
File file = iFile.getLocation().toFile();
CpConfigParser confParser = new CpConfigParser();
ICpItem root = confParser.parseFile(file.getAbsolutePath());
IRteConfiguration rteConf = null;
if (root instanceof ICpConfigurationInfo) {
ICpConfigurationInfo info = (ICpConfigurationInfo) root;
rteConf = new RteConfiguration();
rteConf.setConfigurationInfo(info);
rteProject.setRteConfiguration(rteConfigName, rteConf);
// finally update project storage and rename file if needed
if (!rteConfigName.equals(savedRteConfigName)) {
rteProject.setRteConfigurationName(rteConfigName);
bSaveProject = true;
}
} else {
String msg = Messages.RteProjectUpdater_ErrorLoadinConfigFile + " '" + rteConfigName + "' " + //$NON-NLS-1$//$NON-NLS-2$
Messages.RteProjectUpdater_ErrorParsingFailed;
Status status = new Status(IStatus.ERROR, CpPlugInUI.PLUGIN_ID, msg);
throw new CoreException(status);
}
return rteConf;
}
/**
* Removes resources that are no longer belong to project and refreshes remaining ones
* @throws CoreException
*/
protected void removeResources() throws CoreException {
IResource rteFolder = project.findMember(CmsisConstants.RTE);
removeResources(rteFolder);
}
protected void removeResources(IResource res) throws CoreException {
if (res == null) {
return;
}
int type = res.getType();
if (type == IResource.FILE) {
IPath path = res.getProjectRelativePath();
String dstFile = path.toString();
if (!isFileUsed(res)) {
if (res.isLinked()) {
res.delete(IResource.FORCE, monitor);
} else if (bDeleteConfigFiles) {
res.delete(IResource.FORCE | IResource.KEEP_HISTORY, monitor);
} else {
ProjectUtils.setExcludeFromBuild(project, dstFile, true);
}
} else if (ProjectUtils.isExcludedFromBuild(project, dstFile)) {
ProjectUtils.setExcludeFromBuild(project, dstFile, false);
}
} else if (res.getType() == IResource.FOLDER) {
IFolder f = (IFolder) res;
IResource[] members = f.members();
for (IResource r : members) {
removeResources(r);
}
f.refreshLocal(IResource.DEPTH_INFINITE, monitor);
if (!f.getName().equals(CmsisConstants.RTE) && f.members().length == 0) {
f.delete(true, true, null);
}
}
}
protected boolean isFileUsed(IResource res) {
IPath path = res.getProjectRelativePath();
String ext = path.getFileExtension();
if(CmsisConstants.GPDSC_TAG.equals(ext)) {
IPath gpdsc = res.getLocation();
IRteConfiguration rteConf = rteProject.getRteConfiguration();
return rteConf.isGeneratedPackUsed(gpdsc.toString());
}
return rteProject.isFileUsed(path.toString());
}
protected void addResources() throws CoreException {
IRteConfiguration rteConf = rteProject.getRteConfiguration();
addResources(rteConf);
project.refreshLocal(IResource.DEPTH_INFINITE, monitor);
}
protected void addResources(IRteConfiguration rteConf) throws CoreException {
if (rteConf == null) {
return;
}
Map<String, ICpFileInfo> fileMap = rteConf.getProjectFiles();
for (Entry<String, ICpFileInfo> e : fileMap.entrySet()) {
String projectRelativePath = e.getKey();
ICpFileInfo fi = e.getValue();
addFile(rteConf, projectRelativePath, fi);
}
}
protected void addFile(IRteConfiguration rteConf, String dstFile, ICpFileInfo fi) throws CoreException {
ICpFile f = fi.getFile();
if (f == null) {
return;
}
String srcFile = f.getAbsolutePath(f.getName());
if (srcFile == null) {
return;
}
boolean generated = f.isGenerated();
ICpConfigurationInfo cpConf = rteConf.getConfigurationInfo();
String base = cpConf.getDir(true);
boolean local = srcFile.startsWith(base);
EFileRole role = fi.getRole();
if(generated || local) {
role = EFileRole.NONE; // prevent generated files from copy
}
if (role == EFileRole.CONFIG) {
int index = -1;
EFileCategory cat = fi.getCategory();
if (cat.isHeader() || cat.isSource()) {
String baseSrc = Utils.extractBaseFileName(srcFile);
String baseDst = Utils.extractBaseFileName(dstFile);
int len = baseSrc.length() + 1;
if (baseDst.length() > len) {
String instance = baseDst.substring(len);
try {
index = Integer.decode(instance);
} catch (NumberFormatException e) {
// do nothing, use -1
}
}
}
int bCopied = ProjectUtils.copyFile(project, srcFile, dstFile, index, monitor, false);
if (bCopied == 1) {
updateFileVersion(dstFile, fi.getVersion(), true);
} else if (bCopied == -1) {
String savedVersion = getFileVersion(dstFile);
if (savedVersion != null) {
fi.setVersion(savedVersion);
}
}
} else if (role == EFileRole.COPY) {
int bCopied = ProjectUtils.copyFile(project, srcFile, dstFile, -1, monitor, false);
if (bCopied == 1) {
updateFileVersion(dstFile, fi.getVersion(), true);
} else if (bCopied == -1) {
String savedVersion = getFileVersion(dstFile);
if (savedVersion != null) {
fi.setVersion(savedVersion);
}
}
} else if(!local) {
srcFile = CpVariableResolver.insertCmsisRootVariable(srcFile);
if (srcFile != null) {
ProjectUtils.createLink(project, srcFile, dstFile, monitor);
}
}
if (ProjectUtils.isExcludedFromBuild(project, dstFile)) {
ProjectUtils.setExcludeFromBuild(project, dstFile, false);
}
}
public void updateFileVersion(String projectRelativePath, String version, boolean bForce) {
RteProjectStorage projectStorage = rteProject.getProjectStorage();
if (bForce || projectStorage.getConfigFileVersion(projectRelativePath) == null) {
projectStorage.setConfigFileVersion(projectRelativePath, version);
bSaveProject = true;
}
}
public String getFileVersion(String projectRelativePath) {
RteProjectStorage projectStorage = rteProject.getProjectStorage();
return projectStorage.getConfigFileVersion(projectRelativePath);
}
public void updateRteComponentsH() throws CoreException {
// ensure resource exists
try {
IFile f = ProjectUtils.createFile(project, CmsisConstants.RTE_RTE_Components_h, monitor);
IPath p = f.getLocation();
p.toFile().setWritable(true);
PrintWriter pw = new PrintWriter(p.toOSString());
writeRteComponentsHhead(pw);
writeRteComponentsHbody(pw);
writeRteComponentsHtail(pw);
pw.close();
} catch (FileNotFoundException e) {
e.printStackTrace();
}
}
protected void writeRteComponentsHbody(PrintWriter pw) {
IRteConfiguration rteConf = rteProject.getRteConfiguration();
if (rteConf == null) {
return;
}
// write #define CMSIS_device_header
String deviceHeader = rteConf.getDeviceHeader();
if (deviceHeader != null && !deviceHeader.isEmpty()) {
String s = "#define " + CmsisConstants.CMSIS_device_header + " \"" + deviceHeader + "\""; //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$
pw.println("/*"); //$NON-NLS-1$
pw.println(" * Define the Device Header File:"); //$NON-NLS-1$
pw.println("*/"); //$NON-NLS-1$
pw.println(s);
pw.println();
}
Collection<String> code = rteConf.getRteComponentsHCode();
for (String s : code) {
pw.println(s);
}
}
protected void writeRteComponentsHhead(PrintWriter pw) {
pw.println("/*"); //$NON-NLS-1$
pw.println(" * Auto generated Run-Time-Environment Component Configuration File"); //$NON-NLS-1$
pw.println(" * *** Do not modify ! ***"); //$NON-NLS-1$
pw.println(" *"); //$NON-NLS-1$
pw.println(" * Project: " + rteProject.getName()); //$NON-NLS-1$
pw.print(" * RTE configuration: "); //$NON-NLS-1$
pw.println(rteProject.getRteConfigurationName());
pw.println("*/"); //$NON-NLS-1$
pw.println("#ifndef RTE_COMPONENTS_H"); //$NON-NLS-1$
pw.println("#define RTE_COMPONENTS_H"); //$NON-NLS-1$
pw.println();
}
protected void writeRteComponentsHtail(PrintWriter pw) {
pw.println();
pw.println("#endif /* RTE_COMPONENTS_H */"); //$NON-NLS-1$
}
protected void updateBuildSettings(boolean bForceUpdateToolchain) {
RteProjectStorage ps = rteProject.getProjectStorage();
if (ps == null) {
return;
}
IRteToolChainAdapter adapter = ps.getToolChainAdapter();
if (adapter == null) {
return;
}
ICpEnvironmentProvider envProvider = CpPlugIn.getEnvironmentProvider();
IAttributes deviceAttributes = ps.getDeviceAttributes();
IRteConfiguration rteConfig = rteProject.getRteConfiguration();
ICpConfigurationInfo configInfo = rteConfig.getConfigurationInfo();
ICpDeviceInfo deviceInfo = rteConfig.getDeviceInfo();
IBuildSettings buildSettings = rteConfig.getBuildSettings();
boolean bInit = deviceInfo != null && !deviceInfo.attributes().matchCommonAttributes(deviceAttributes);
if (bInit || bForceUpdateToolchain) {
bSaveProject = true;
ps.setDeviceInfo(deviceInfo);
String linkerScriptFile = buildSettings.getSingleLinkerScriptFile();
if (linkerScriptFile == null) {
ILinkerScriptGenerator lsGen = adapter.getLinkerScriptGenerator();
if (lsGen != null) {
linkerScriptFile = getLinkerScriptFile(lsGen);
try {
IMemorySettings memorySettings = RteConfiguration.createMemorySettings(deviceInfo);
String script = lsGen.generate(memorySettings);
if (script != null && !script.isEmpty()) {
writeLinkerScriptFile(linkerScriptFile, script);
buildSettings.addStringListValue(IBuildSettings.RTE_LINKER_SCRIPT,
CmsisConstants.PROJECT_LOCAL_PATH + linkerScriptFile);
}
} catch (CoreException e) {
e.printStackTrace();
}
}
}
}
IManagedBuildInfo buildInfo = ManagedBuildManager.getBuildInfo(project);
String[] configNames = buildInfo.getConfigurationNames();
for (String name : configNames) {
IConfiguration config = ProjectUtils.getConfiguration(project, name);
if (bInit || bForceUpdateToolchain) {
envProvider.adjustInitialBuildSettings(buildSettings, configInfo);
adapter.setInitialToolChainOptions(config, buildSettings);
} else {
envProvider.adjustBuildSettings(buildSettings, configInfo);
adapter.setToolChainOptions(config, buildSettings);
}
}
ManagedBuildManager.saveBuildInfo(project, true);
}
protected String getLinkerScriptFile(ILinkerScriptGenerator lsGen) {
IRteConfiguration rteConfiguration = rteProject.getRteConfiguration();
String deviceName = rteConfiguration.getDeviceInfo().getDeviceName();
String fileName = Utils.wildCardsToX(deviceName) + "." + lsGen.getFileExtension(); //$NON-NLS-1$
return fileName;
}
protected void writeLinkerScriptFile(String fileName, String script) {
if (script == null || script.isEmpty()) {
return;
}
try {
IFile file = ProjectUtils.createFile(project, fileName, monitor);
IPath loc = file.getLocation();
File f = loc.toFile();
if (f != null && f.exists()) {
return; // destination file already exists
}
String osPath = loc.toOSString();
PrintWriter pw = new PrintWriter(osPath);
pw.write(script);
pw.close();
} catch (CoreException e) {
e.printStackTrace();
} catch (FileNotFoundException e) {
e.printStackTrace();
}
}
}