/******************************************************************************* * Copyright (c) 2004, 2011 IBM Corporation 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: * IBM - Initial API and implementation * Anton Leherbauer (Wind River Systems) * Markus Schorn (Wind River Systems) *******************************************************************************/ package org.eclipse.cdt.make.internal.core.scannerconfig2; import java.net.URI; import java.util.ArrayList; import java.util.Arrays; import java.util.HashMap; import java.util.LinkedHashMap; import java.util.List; import java.util.Map; import org.eclipse.cdt.core.CCProjectNature; import org.eclipse.cdt.core.CProjectNature; import org.eclipse.cdt.core.model.CModelException; import org.eclipse.cdt.core.model.CoreModel; import org.eclipse.cdt.core.model.ICProject; import org.eclipse.cdt.core.model.IPathEntry; import org.eclipse.cdt.make.core.MakeCorePlugin; import org.eclipse.cdt.make.core.scannerconfig.IExternalScannerInfoProvider; import org.eclipse.cdt.make.core.scannerconfig.IScannerConfigBuilderInfo2; import org.eclipse.cdt.make.core.scannerconfig.IScannerInfoCollector; import org.eclipse.cdt.make.core.scannerconfig.IScannerInfoCollector2; import org.eclipse.cdt.make.core.scannerconfig.IScannerInfoCollector3; import org.eclipse.cdt.make.core.scannerconfig.IScannerInfoCollectorCleaner; import org.eclipse.cdt.make.core.scannerconfig.InfoContext; import org.eclipse.cdt.make.core.scannerconfig.ScannerInfoTypes; import org.eclipse.cdt.make.core.scannerconfig.IDiscoveredPathManager.IDiscoveredPathInfo; import org.eclipse.cdt.make.core.scannerconfig.IDiscoveredPathManager.IPerProjectDiscoveredPathInfo; import org.eclipse.cdt.make.internal.core.MakeMessages; import org.eclipse.cdt.make.internal.core.scannerconfig.DiscoveredPathContainer; import org.eclipse.cdt.make.internal.core.scannerconfig.DiscoveredPathInfo; import org.eclipse.cdt.make.internal.core.scannerconfig.DiscoveredScannerInfoStore; import org.eclipse.cdt.make.internal.core.scannerconfig.ScannerConfigUtil; import org.eclipse.cdt.make.internal.core.scannerconfig.util.CygpathTranslator; import org.eclipse.cdt.make.internal.core.scannerconfig.util.SymbolEntry; import org.eclipse.cdt.make.internal.core.scannerconfig.util.TraceUtil; import org.eclipse.cdt.utils.EFSExtensionManager; import org.eclipse.core.filesystem.EFS; import org.eclipse.core.filesystem.IFileInfo; import org.eclipse.core.filesystem.IFileStore; import org.eclipse.core.resources.IProject; import org.eclipse.core.resources.IResource; import org.eclipse.core.runtime.CoreException; import org.eclipse.core.runtime.IProgressMonitor; import org.eclipse.core.runtime.ISafeRunnable; import org.eclipse.core.runtime.NullProgressMonitor; import org.eclipse.core.runtime.OperationCanceledException; import org.eclipse.core.runtime.SafeRunner; import org.w3c.dom.Element; /** * New per project scanner info collector * * @since 3.0 * @author vhirsl */ public class PerProjectSICollector implements IScannerInfoCollector3, IScannerInfoCollectorCleaner { public static final String COLLECTOR_ID = MakeCorePlugin.getUniqueIdentifier() + ".PerProjectSICollector"; //$NON-NLS-1$ protected IProject project; protected InfoContext context; private boolean isBuiltinConfig= false; protected Map<ScannerInfoTypes, List<String>> discoveredSI; // private List discoveredIncludes; // private List discoveredSymbols; // private List discoveredTSO; // target specific options // cumulative values protected List<String> sumDiscoveredIncludes; private Map<String, SymbolEntry> sumDiscoveredSymbols; protected boolean scPersisted = false; public PerProjectSICollector() { discoveredSI = new HashMap<ScannerInfoTypes, List<String>>(); // discoveredIncludes = new ArrayList(); // discoveredSymbols = new ArrayList(); // discoveredTSO = new ArrayList(); // sumDiscoveredIncludes = new ArrayList<String>(); sumDiscoveredSymbols = new LinkedHashMap<String, SymbolEntry>(); } /* (non-Javadoc) * @see org.eclipse.cdt.make.core.scannerconfig.IScannerInfoCollector2#setProject(org.eclipse.core.resources.IProject) */ public void setProject(IProject project) { this.project = project; this.context = new InfoContext(project); } public synchronized void contributeToScannerConfig(Object resource, @SuppressWarnings("rawtypes") Map scannerInfo, boolean isBuiltinConfig) { this.isBuiltinConfig= isBuiltinConfig; try { contributeToScannerConfig(resource, scannerInfo); } finally { this.isBuiltinConfig= false; } } /* (non-Javadoc) * @see org.eclipse.cdt.make.core.scannerconfig.IScannerInfoCollector#contributeToScannerConfig(java.lang.Object, java.util.Map) */ public synchronized void contributeToScannerConfig(Object resource, @SuppressWarnings("rawtypes") Map scannerInfo) { // check the resource String errorMessage = null; if (resource == null) { errorMessage = "resource is null";//$NON-NLS-1$ } else if (!(resource instanceof IResource)) { errorMessage = "resource is not an IResource";//$NON-NLS-1$ } else if (((IResource) resource).getProject() == null) { errorMessage = "project is null";//$NON-NLS-1$ } else if (!((IResource) resource).getProject().equals(project)) { errorMessage = "wrong project";//$NON-NLS-1$ } if (errorMessage != null) { TraceUtil.outputError("PerProjectSICollector.contributeToScannerConfig : ", errorMessage); //$NON-NLS-1$ return; } if (scPersisted) { // delete discovered scanner config discoveredSI.clear(); // new collection cycle scPersisted = false; } try { if (/*project.hasNature(MakeProjectNature.NATURE_ID) && */// limits to StandardMake projects (project.hasNature(CProjectNature.C_NATURE_ID) || project.hasNature(CCProjectNature.CC_NATURE_ID))) { for (Object name : scannerInfo.keySet()) { ScannerInfoTypes siType = (ScannerInfoTypes) name; @SuppressWarnings("unchecked") List<String> delta = (List<String>) scannerInfo.get(siType); List<String> discovered = discoveredSI.get(siType); if (discovered == null) { discovered = new ArrayList<String>(delta); discoveredSI.put(siType, discovered); } else { final boolean addSorted= !isBuiltinConfig && siType.equals(ScannerInfoTypes.INCLUDE_PATHS); contribute(discovered, delta, addSorted); } } } } catch (CoreException e) { MakeCorePlugin.log(e); } } /** * @param discovered symbols | includes | targetSpecificOptions * @param delta symbols | includes | targetSpecificOptions * @param ordered - to preserve order or append at the end * @return true if there is a change in discovered symbols | includes | targetSpecificOptions */ private boolean contribute(List<String> discovered, List<String> delta, boolean ordered) { if (delta == null || delta.isEmpty()) return false; return addItemsWithOrder(discovered, delta, ordered); } /** * Adds new items to the already accumulated ones preserving order * * @param sumIncludes - previously accumulated items * @param includes - items to be added * @param ordered - to preserve order or append at the end * @return boolean - true if added */ protected boolean addItemsWithOrder(List<String> sumIncludes, List<String> includes, boolean ordered) { if (includes.isEmpty()) return false; boolean addedIncludes = false; int insertionPoint= ordered ? 0 : sumIncludes.size(); for (String item : includes) { int pos= sumIncludes.indexOf(item); if (pos >= 0) { if (ordered) { insertionPoint= pos+1; } } else { sumIncludes.add(insertionPoint++, item); addedIncludes = true; } } return addedIncludes; } /* (non-Javadoc) * @see org.eclipse.cdt.make.core.scannerconfig.IScannerInfoCollector2#updateScannerConfiguration(org.eclipse.core.resources.IProject, org.eclipse.core.runtime.IProgressMonitor) */ public synchronized void updateScannerConfiguration(IProgressMonitor monitor) throws CoreException { if (monitor == null) { monitor = new NullProgressMonitor(); } IDiscoveredPathInfo pathInfo = MakeCorePlugin.getDefault().getDiscoveryManager().getDiscoveredInfo(project, context); if (pathInfo instanceof IPerProjectDiscoveredPathInfo) { IPerProjectDiscoveredPathInfo projectPathInfo = (IPerProjectDiscoveredPathInfo) pathInfo; monitor.beginTask(MakeMessages.getString("ScannerInfoCollector.Processing"), 100); //$NON-NLS-1$ monitor.subTask(MakeMessages.getString("ScannerInfoCollector.Processing")); //$NON-NLS-1$ if (scannerConfigNeedsUpdate(projectPathInfo)) { monitor.worked(50); monitor.subTask(MakeMessages.getString("ScannerInfoCollector.Updating") + project.getName()); //$NON-NLS-1$ try { // update scanner configuration List<IResource> resourceDelta = new ArrayList<IResource>(1); resourceDelta.add(project); MakeCorePlugin.getDefault().getDiscoveryManager().updateDiscoveredInfo(context, pathInfo, context.isDefaultContext(), resourceDelta); monitor.worked(50); } catch (CoreException e) { MakeCorePlugin.log(e); } } monitor.done(); scPersisted = true; } } /** * Compare discovered include paths and symbol definitions with the ones from scanInfo. */ private boolean scannerConfigNeedsUpdate(IPerProjectDiscoveredPathInfo discPathInfo) { boolean addedIncludes = includePathsNeedUpdate(discPathInfo); boolean addedSymbols = definedSymbolsNeedUpdate(discPathInfo); return (addedIncludes | addedSymbols); } /** * Compare include paths with already discovered. */ private boolean includePathsNeedUpdate(IPerProjectDiscoveredPathInfo discPathInfo) { boolean addedIncludes = false; List<String> discoveredIncludes = discoveredSI.get(ScannerInfoTypes.INCLUDE_PATHS); if (discoveredIncludes != null) { // Step 1. Add discovered scanner config to the existing discovered scanner config // add the includes from the latest discovery // if (sumDiscoveredIncludes == null) { // sumDiscoveredIncludes = new ArrayList(discoveredIncludes); // addedIncludes = true; // } // else { // addedIncludes = addItemsWithOrder(sumDiscoveredIncludes, discoveredIncludes, true); // } // instead addedIncludes = addItemsWithOrder(sumDiscoveredIncludes, discoveredIncludes, true); // try to translate cygpaths to absolute paths List<String> finalSumIncludes = CygpathTranslator.translateIncludePaths(project, sumDiscoveredIncludes); // Step 2. Get project's scanner config LinkedHashMap<String, Boolean> persistedIncludes = discPathInfo.getIncludeMap(); // Step 3. Merge scanner config from steps 1 and 2 // order is important, use list to preserve it ArrayList<String> persistedKeyList = new ArrayList<String>(persistedIncludes.keySet()); addedIncludes = addItemsWithOrder(persistedKeyList, finalSumIncludes, true); LinkedHashMap<String, Boolean> newPersistedIncludes; if (addedIncludes) { newPersistedIncludes = new LinkedHashMap<String, Boolean>(persistedKeyList.size()); for (String include : persistedKeyList) { if (persistedIncludes.containsKey(include)) { newPersistedIncludes.put(include, persistedIncludes.get(include)); } else { // the paths may be on EFS resources, not local Boolean includePathExists = true; URI projectLocationURI = discPathInfo.getProject().getLocationURI(); // use the project's location... create a URI that uses the same provider but that points to the include path URI includeURI = EFSExtensionManager.getDefault().createNewURIFromPath(projectLocationURI, include); // ask EFS if the path exists try { IFileStore fileStore = EFS.getStore(includeURI); IFileInfo info = fileStore.fetchInfo(); if(!info.exists()) { includePathExists = false; } } catch (CoreException e) { MakeCorePlugin.log(e); } // if the include path doesn't exist, then we tell the scanner config system that the folder // has been "removed", and thus it won't show up in the UI newPersistedIncludes.put(include, !includePathExists); } } } else { newPersistedIncludes = persistedIncludes; } // Step 4. Set resulting scanner config discPathInfo.setIncludeMap(newPersistedIncludes); } return addedIncludes; } /** * Compare symbol definitions with already discovered. */ protected boolean definedSymbolsNeedUpdate(IPerProjectDiscoveredPathInfo discPathInfo) { boolean addedSymbols = false; List<String> discoveredSymbols = discoveredSI.get(ScannerInfoTypes.SYMBOL_DEFINITIONS); if (discoveredSymbols != null) { // Step 1. Add discovered scanner config to the existing discovered scanner config // add the symbols from the latest discovery // if (sumDiscoveredSymbols == null) { // sumDiscoveredSymbols = new LinkedHashMap(); // } addedSymbols = ScannerConfigUtil.scAddSymbolsList2SymbolEntryMap(sumDiscoveredSymbols, discoveredSymbols, true); // Step 2. Get project's scanner config LinkedHashMap<String, SymbolEntry> persistedSymbols = discPathInfo.getSymbolMap(); // Step 3. Merge scanner config from steps 1 and 2 LinkedHashMap<String, SymbolEntry> candidateSymbols = new LinkedHashMap<String, SymbolEntry>(persistedSymbols); addedSymbols |= ScannerConfigUtil.scAddSymbolEntryMap2SymbolEntryMap(candidateSymbols, sumDiscoveredSymbols); // Step 4. Set resulting scanner config discPathInfo.setSymbolMap(candidateSymbols); } return addedSymbols; } /* (non-Javadoc) * @see org.eclipse.cdt.make.core.scannerconfig.IScannerInfoCollector#getCollectedScannerInfo(java.lang.Object, org.eclipse.cdt.make.core.scannerconfig.ScannerInfoTypes) */ public List<String> getCollectedScannerInfo(Object resource, ScannerInfoTypes type) { List<String> rv = null; // check the resource String errorMessage = null; if (resource == null) { errorMessage = "resource is null";//$NON-NLS-1$ } else if (!(resource instanceof IResource)) { errorMessage = "resource is not an IResource";//$NON-NLS-1$ } else if (((IResource) resource).getProject() == null) { errorMessage = "project is null";//$NON-NLS-1$ } else if (((IResource) resource).getProject() != project) { errorMessage = "wrong project";//$NON-NLS-1$ } if (errorMessage != null) { TraceUtil.outputError("PerProjectSICollector.getCollectedScannerInfo : ", errorMessage); //$NON-NLS-1$ } else if (resource!=null && project.equals(((IResource)resource).getProject())) { rv = discoveredSI.get(type); } return rv; } /* (non-Javadoc) * @see org.eclipse.cdt.make.core.scannerconfig.IScannerInfoCollector2#getDefinedSymbols() */ public Map<String, String> getDefinedSymbols() { Map<String, String> definedSymbols = ScannerConfigUtil.scSymbolEntryMap2Map(sumDiscoveredSymbols); return definedSymbols; } /* (non-Javadoc) * @see org.eclipse.cdt.make.core.scannerconfig.IScannerInfoCollector2#getIncludePaths() */ public List<String> getIncludePaths() { return sumDiscoveredIncludes; } /* (non-Javadoc) * @see org.eclipse.cdt.make.core.scannerconfig.IScannerInfoCollectorUtil#serialize(org.w3c.dom.Element) */ public void serialize(Element root) { // not supported in PerProject collector } /* (non-Javadoc) * @see org.eclipse.cdt.make.core.scannerconfig.IScannerInfoCollectorUtil#deserialize(org.w3c.dom.Element) */ public void deserialize(Element root) { // not supported in PerProject collector } /* (non-Javadoc) * @see org.eclipse.cdt.make.core.scannerconfig.IScannerInfoCollectorUtil#deleteAllPaths(org.eclipse.core.resources.IResource) */ public void deleteAllPaths(IResource resource) { IProject project = resource.getProject(); if (project != null && project.equals(this.project)) { sumDiscoveredIncludes.clear(); } } /* (non-Javadoc) * @see org.eclipse.cdt.make.core.scannerconfig.IScannerInfoCollectorUtil#deleteAllSymbols(org.eclipse.core.resources.IResource) */ public void deleteAllSymbols(IResource resource) { IProject project = resource.getProject(); if (project != null && project.equals(this.project)) { sumDiscoveredSymbols.clear(); } } /* (non-Javadoc) * @see org.eclipse.cdt.make.core.scannerconfig.IScannerInfoCollectorUtil#deletePath(org.eclipse.core.resources.IResource, java.lang.String) */ public void deletePath(IResource resource, String path) { IProject project = resource.getProject(); if (project != null && project.equals(this.project)) { sumDiscoveredIncludes.remove(path); } } /* (non-Javadoc) * @see org.eclipse.cdt.make.core.scannerconfig.IScannerInfoCollectorUtil#deleteSymbol(org.eclipse.core.resources.IResource, java.lang.String) */ public void deleteSymbol(IResource resource, String symbol) { IProject project = resource.getProject(); if (project != null && project.equals(this.project)) { // remove it from the Map of SymbolEntries ScannerConfigUtil.removeSymbolEntryValue(symbol, sumDiscoveredSymbols); } } /* (non-Javadoc) * @see org.eclipse.cdt.make.core.scannerconfig.IScannerInfoCollectorCleaner#deleteAll(org.eclipse.core.resources.IResource) */ public void deleteAll(IResource resource) { deleteAllPaths(resource); deleteAllSymbols(resource); } /* (non-Javadoc) * @see org.eclipse.cdt.make.core.scannerconfig.IScannerInfoCollector2#createPathInfoObject() */ public IDiscoveredPathInfo createPathInfoObject() { DiscoveredPathInfo pathInfo = new DiscoveredPathInfo(project); try { DiscoveredScannerInfoStore.getInstance().loadDiscoveredScannerInfoFromState(project, context, pathInfo); } catch (CoreException e) { MakeCorePlugin.log(e); } return pathInfo; } /** * Static method to return compiler built-in scanner info. * Preconditions: resource has to be contained by a project that has following natures: * C nature, CC nature (for C++ projects), Make nature and ScannerConfig nature */ public static void calculateCompilerBuiltins(final IProject project) throws CModelException { createDiscoveredPathContainer(project, new NullProgressMonitor()); String scdProfileId = ScannerConfigProfileManager.PER_PROJECT_PROFILE_ID; SCProfileInstance profileInstance = ScannerConfigProfileManager.getInstance(). getSCProfileInstance(project, scdProfileId); final IScannerConfigBuilderInfo2 buildInfo = ScannerConfigProfileManager. createScannerConfigBuildInfo2(MakeCorePlugin.getDefault().getPluginPreferences(), scdProfileId, true); final IScannerInfoCollector collector = profileInstance.getScannerInfoCollector(); if (collector instanceof IScannerInfoCollectorCleaner) { ((IScannerInfoCollectorCleaner) collector).deleteAll(project); } final IExternalScannerInfoProvider esiProvider = profileInstance.createExternalScannerInfoProvider("specsFile");//$NON-NLS-1$ // Set the arguments for the provider ISafeRunnable runnable = new ISafeRunnable() { public void run() throws CoreException { IProgressMonitor monitor = new NullProgressMonitor(); esiProvider.invokeProvider(monitor, project, "specsFile", buildInfo, collector);//$NON-NLS-1$ if (collector instanceof IScannerInfoCollector2) { IScannerInfoCollector2 collector2 = (IScannerInfoCollector2) collector; collector2.updateScannerConfiguration(monitor); } } public void handleException(Throwable exception) { if (exception instanceof OperationCanceledException) { throw (OperationCanceledException) exception; } } }; SafeRunner.run(runnable); } private static void createDiscoveredPathContainer(IProject project, IProgressMonitor monitor) throws CModelException { IPathEntry container = CoreModel.newContainerEntry(DiscoveredPathContainer.CONTAINER_ID); ICProject cProject = CoreModel.getDefault().create(project); if (cProject != null) { IPathEntry[] entries = cProject.getRawPathEntries(); List<IPathEntry> newEntries = new ArrayList<IPathEntry>(Arrays.asList(entries)); if (!newEntries.contains(container)) { newEntries.add(container); cProject.setRawPathEntries(newEntries.toArray(new IPathEntry[newEntries.size()]), monitor); } } // create a new discovered scanner config store MakeCorePlugin.getDefault().getDiscoveryManager().removeDiscoveredInfo(project); } public void setInfoContext(InfoContext context) { this.context = context; this.project = context.getProject(); } public InfoContext getContext(){ return this.context; } }