# HG changeset patch # User Ed Swartz # Date 1262712230 21600 # Node ID 78fd666a897a47376e719d0662ac7c6bb155c904 # Parent 9fca333f8b5e374156c860b9d3ecba12a6cf4170 Fix filesystem caching performance as in bug #10318 diff -r 9fca333f8b5e -r 78fd666a897a builder/com.nokia.carbide.cdt.builder/src/com/nokia/carbide/cdt/builder/DefaultIncludeFileLocator.java --- a/builder/com.nokia.carbide.cdt.builder/src/com/nokia/carbide/cdt/builder/DefaultIncludeFileLocator.java Tue Jan 05 11:22:47 2010 -0600 +++ b/builder/com.nokia.carbide.cdt.builder/src/com/nokia/carbide/cdt/builder/DefaultIncludeFileLocator.java Tue Jan 05 11:23:50 2010 -0600 @@ -23,12 +23,12 @@ import java.util.*; import org.eclipse.core.resources.IProject; -import org.eclipse.core.runtime.IPath; import com.nokia.carbide.cdt.builder.project.ICarbideProjectInfo; -import com.nokia.carbide.cpp.epoc.engine.model.sbv.ISBVView; +import com.nokia.carbide.cpp.internal.api.sdk.SymbianBuildContext; import com.nokia.carbide.cpp.sdk.core.*; import com.nokia.carbide.internal.api.cpp.epoc.engine.preprocessor.BasicIncludeFileLocator; +import com.nokia.cpp.internal.api.utils.core.Check; public class DefaultIncludeFileLocator extends BasicIncludeFileLocator { /** @@ -40,7 +40,6 @@ */ public DefaultIncludeFileLocator(IProject project, ISymbianBuildContext buildContext) { super(null, null); - List systemPaths = new ArrayList(); if (buildContext != null && buildContext.getSDK() != null) { // search implicit bld.inf directory if known @@ -51,84 +50,11 @@ systemPaths.add(cpi.getAbsoluteBldInfPath().removeLastSegments(1).toFile()); } } + + // get info from context + Check.checkState(buildContext instanceof SymbianBuildContext); - IBSFPlatform bsfplatform = buildContext.getSDK().getBSFCatalog().findPlatform(buildContext.getPlatformString()); - ISBVPlatform sbvPlatform = buildContext.getSDK().getSBVCatalog().findPlatform(buildContext.getPlatformString()); - - // look in the epoc32 directory of the SDK - IPath includePath = buildContext.getSDK().getIncludePath(); - if (includePath != null) { - File includeDir = includePath.toFile().getAbsoluteFile(); - File dir; - - // get additional include directories from BSF platform, if defined - if (bsfplatform != null) { - IPath[] systemIncludePaths = bsfplatform.getSystemIncludePaths(); - for (IPath path : systemIncludePaths) { - dir = path.toFile(); - if (dir.exists() && dir.isDirectory()) { - systemPaths.add(dir); - } - } - } else if (sbvPlatform != null){ - - LinkedHashMap platPaths = sbvPlatform.getBuildIncludePaths(); - Set set = platPaths.keySet(); - for (IPath path : set) { - String pathType = platPaths.get(path); - if (pathType.equalsIgnoreCase(ISBVView.INCLUDE_FLAG_PREPEND) || pathType.equalsIgnoreCase(ISBVView.INCLUDE_FLAG_SET)){ - dir = path.toFile(); - systemPaths.add(dir); - } - } - } - else { - // legacy behavior - if (buildContext.getPlatformString().equals(ISymbianBuildContext.EMULATOR_PLATFORM)) { - dir = new File(includeDir, "wins"); //$NON-NLS-1$ - if (dir.exists() && dir.isDirectory()) { - systemPaths.add(dir); - } - } - } - - // add OEM dir - dir = new File(includeDir, "oem"); //$NON-NLS-1$ - if (dir.exists() && dir.isDirectory()) { - systemPaths.add(dir); - } - - // and finally the normal include dir - systemPaths.add(includeDir); - - // and finally, finally, if this is an SBV add any paths with the append flag - if (sbvPlatform != null){ - - Map platPaths = sbvPlatform.getBuildIncludePaths(); - Set set = platPaths.keySet(); - for (IPath path : set) { - String pathType = platPaths.get(path); - if (pathType.equalsIgnoreCase(ISBVView.INCLUDE_FLAG_APPEND)){ - dir = path.toFile(); - systemPaths.add(dir); - } - } - } - } - - // also search files in same folder as variant.hrh - File prefix = buildContext.getSDK().getPrefixFile(); - if (sbvPlatform != null){ - // might be an alternate HRH file to use - IPath varVarHRH = sbvPlatform.getBuildVariantHRHFile(); - if (!varVarHRH.toFile().equals(prefix) && varVarHRH.toFile().exists()){ - prefix = varVarHRH.toFile(); - } - } - if (prefix != null) { - systemPaths.add(prefix.getParentFile()); - } - + systemPaths.addAll(((SymbianBuildContext) buildContext).getCachedSystemIncludePaths()); } setPaths(null, (File[]) systemPaths.toArray(new File[systemPaths.size()])); } diff -r 9fca333f8b5e -r 78fd666a897a builder/com.nokia.carbide.cdt.builder/src/com/nokia/carbide/cdt/builder/EpocEngineHelper.java --- a/builder/com.nokia.carbide.cdt.builder/src/com/nokia/carbide/cdt/builder/EpocEngineHelper.java Tue Jan 05 11:22:47 2010 -0600 +++ b/builder/com.nokia.carbide.cdt.builder/src/com/nokia/carbide/cdt/builder/EpocEngineHelper.java Tue Jan 05 11:23:50 2010 -0600 @@ -27,6 +27,7 @@ import com.nokia.carbide.cpp.epoc.engine.preprocessor.AcceptedNodesViewFilter; import com.nokia.carbide.cpp.epoc.engine.preprocessor.AllNodesViewFilter; import com.nokia.carbide.cpp.internal.api.sdk.SymbianBuildContext; +import com.nokia.carbide.cpp.internal.api.sdk.SymbianBuildContextDataCache; import com.nokia.carbide.cpp.sdk.core.ISymbianBuildContext; import com.nokia.carbide.cpp.sdk.core.ISymbianSDK; import com.nokia.carbide.internal.api.cpp.epoc.engine.model.pkg.*; @@ -88,35 +89,41 @@ monitor.beginTask("Scanning bld.inf for mmp and make files", buildConfigs.size()); - for (final ISymbianBuildContext context : buildConfigs) { - EpocEnginePlugin.runWithBldInfData(bldInfFilePath, - new DefaultViewConfiguration(context, bldInfFilePath, new AcceptedNodesViewFilter()), - new BldInfDataRunnableAdapter() { - public Object run(IBldInfData data) { - for (IMakMakeReference ref : data.getMakMakeReferences()) { - normalFiles.add(getFullPath(data, ref.getPath())); - } - for (IMakMakeReference ref : data.getTestMakMakeReferences()) { - boolean ignore = false; - for (String att : ref.getAttributes()) { - if (att.equalsIgnoreCase("ignore")) { //$NON-NLS-1$ - ignore = true; - break; + try { + // let cache know we're iterating a lot + SymbianBuildContextDataCache.startProjectOperation(); + + for (final ISymbianBuildContext context : buildConfigs) { + EpocEnginePlugin.runWithBldInfData(bldInfFilePath, + new DefaultViewConfiguration(context, bldInfFilePath, new AcceptedNodesViewFilter()), + new BldInfDataRunnableAdapter() { + public Object run(IBldInfData data) { + for (IMakMakeReference ref : data.getMakMakeReferences()) { + normalFiles.add(getFullPath(data, ref.getPath())); + } + for (IMakMakeReference ref : data.getTestMakMakeReferences()) { + boolean ignore = false; + for (String att : ref.getAttributes()) { + if (att.equalsIgnoreCase("ignore")) { //$NON-NLS-1$ + ignore = true; + break; + } + } + if (!ignore) { + testFiles.add(getFullPath(data, ref.getPath())); } } - if (!ignore) { - testFiles.add(getFullPath(data, ref.getPath())); - } + return null; } - return null; - } - }); - - monitor.worked(1); + }); + + monitor.worked(1); + } + + monitor.done(); + } finally { + SymbianBuildContextDataCache.endProjectOperation(); } - - monitor.done(); - for (IPath normalPath : normalFiles){ normalMakMakePaths.add(normalPath); } @@ -145,29 +152,35 @@ monitor.beginTask("Scanning bld.inf project extensions", buildConfigs.size()); - for (final ISymbianBuildContext context : buildConfigs) { - EpocEnginePlugin.runWithBldInfData(bldInfFilePath, - new DefaultViewConfiguration(context, bldInfFilePath, new AcceptedNodesViewFilter()), - new BldInfDataRunnableAdapter() { - public Object run(IBldInfData data) { - BldInfViewPathHelper helper = new BldInfViewPathHelper(data, context); - for (IExtension extension : data.getExtensions()) { - IPath extensionMakefileBase = helper.convertExtensionTemplateToFilesystem(extension.getTemplatePath()); - normalFiles.add(extensionMakefileBase.addFileExtension("mk")); //$NON-NLS-1$ + try { + // let cache know we're iterating a lot + SymbianBuildContextDataCache.startProjectOperation(); + + for (final ISymbianBuildContext context : buildConfigs) { + EpocEnginePlugin.runWithBldInfData(bldInfFilePath, + new DefaultViewConfiguration(context, bldInfFilePath, new AcceptedNodesViewFilter()), + new BldInfDataRunnableAdapter() { + public Object run(IBldInfData data) { + BldInfViewPathHelper helper = new BldInfViewPathHelper(data, context); + for (IExtension extension : data.getExtensions()) { + IPath extensionMakefileBase = helper.convertExtensionTemplateToFilesystem(extension.getTemplatePath()); + normalFiles.add(extensionMakefileBase.addFileExtension("mk")); //$NON-NLS-1$ + } + for (IExtension extension : data.getTestExtensions()) { + IPath extensionMakefileBase = helper.convertExtensionTemplateToFilesystem(extension.getTemplatePath()); + testFiles.add(extensionMakefileBase.addFileExtension("mk")); //$NON-NLS-1$ + } + return null; } - for (IExtension extension : data.getTestExtensions()) { - IPath extensionMakefileBase = helper.convertExtensionTemplateToFilesystem(extension.getTemplatePath()); - testFiles.add(extensionMakefileBase.addFileExtension("mk")); //$NON-NLS-1$ - } - return null; - } - }); - - monitor.worked(1); + }); + + monitor.worked(1); + } + } finally { + SymbianBuildContextDataCache.endProjectOperation(); + monitor.done(); } - monitor.done(); - for (IPath normalPath : normalFiles){ normalExtensionPaths.add(normalPath); } @@ -195,30 +208,37 @@ monitor.beginTask("Scanning bld.inf project extensions", buildConfigs.size()); - for (final ISymbianBuildContext context : buildConfigs) { - EpocEnginePlugin.runWithBldInfData(bldInfFilePath, - new DefaultViewConfiguration(context, bldInfFilePath, new AcceptedNodesViewFilter()), - new BldInfDataRunnableAdapter() { - public Object run(IBldInfData data) { - for (IExtension extension : data.getExtensions()) { - if (extension.getName() != null) { - normalFiles.add(extension); + try { + // let cache know we're iterating a lot + SymbianBuildContextDataCache.startProjectOperation(); + + for (final ISymbianBuildContext context : buildConfigs) { + EpocEnginePlugin.runWithBldInfData(bldInfFilePath, + new DefaultViewConfiguration(context, bldInfFilePath, new AcceptedNodesViewFilter()), + new BldInfDataRunnableAdapter() { + public Object run(IBldInfData data) { + for (IExtension extension : data.getExtensions()) { + if (extension.getName() != null) { + normalFiles.add(extension); + } } + for (IExtension extension : data.getTestExtensions()) { + if (extension.getName() != null) { + testFiles.add(extension); + } + } + return null; } - for (IExtension extension : data.getTestExtensions()) { - if (extension.getName() != null) { - testFiles.add(extension); - } - } - return null; - } - }); - - monitor.worked(1); + }); + + monitor.worked(1); + } + } finally { + monitor.done(); + + SymbianBuildContextDataCache.endProjectOperation(); } - monitor.done(); - for (IExtension normal : normalFiles){ normalExtensions.add(normal); } @@ -240,29 +260,36 @@ monitor.beginTask("Scanning bld.inf project extensions", buildConfigs.size()); - for (final ISymbianBuildContext context : buildConfigs) { - EpocEnginePlugin.runWithBldInfData(bldInfFilePath, - new DefaultViewConfiguration(context, bldInfFilePath, new AcceptedNodesViewFilter()), - new BldInfDataRunnableAdapter() { - public Object run(IBldInfData data) { - for (IExtension extension : data.getExtensions()) { - if (extension.getName() == null) { - extensions.add(extension); + try { + // let cache know we're iterating a lot + SymbianBuildContextDataCache.startProjectOperation(); + + for (final ISymbianBuildContext context : buildConfigs) { + EpocEnginePlugin.runWithBldInfData(bldInfFilePath, + new DefaultViewConfiguration(context, bldInfFilePath, new AcceptedNodesViewFilter()), + new BldInfDataRunnableAdapter() { + public Object run(IBldInfData data) { + for (IExtension extension : data.getExtensions()) { + if (extension.getName() == null) { + extensions.add(extension); + } } + for (IExtension extension : data.getTestExtensions()) { + if (extension.getName() == null) { + extensions.add(extension); + } + } + return null; } - for (IExtension extension : data.getTestExtensions()) { - if (extension.getName() == null) { - extensions.add(extension); - } - } - return null; - } - }); + }); + + monitor.worked(1); + } + } finally { + monitor.done(); - monitor.worked(1); + SymbianBuildContextDataCache.endProjectOperation(); } - - monitor.done(); return extensions.size() > 0; } diff -r 9fca333f8b5e -r 78fd666a897a builder/com.nokia.carbide.cdt.builder/src/com/nokia/carbide/cdt/internal/builder/CarbideBuildManager.java --- a/builder/com.nokia.carbide.cdt.builder/src/com/nokia/carbide/cdt/internal/builder/CarbideBuildManager.java Tue Jan 05 11:22:47 2010 -0600 +++ b/builder/com.nokia.carbide.cdt.builder/src/com/nokia/carbide/cdt/internal/builder/CarbideBuildManager.java Tue Jan 05 11:23:50 2010 -0600 @@ -309,6 +309,7 @@ for (ICarbideBuildConfiguration config : cpi.getBuildConfigurations()) { // force a rebuild of the CarbideLanguageData cache + // TODO PERFORMANCE EJS: why??? We end up forcing a cache rebuild even when you just switch configurations... CLanguageData languageData = null; ICProjectDescription projDes = CoreModel.getDefault().getProjectDescription(config.getCarbideProject().getProject()); if (projDes != null) { diff -r 9fca333f8b5e -r 78fd666a897a builder/com.nokia.carbide.cdt.builder/src/com/nokia/carbide/cdt/internal/builder/CarbideProjectModifier.java --- a/builder/com.nokia.carbide.cdt.builder/src/com/nokia/carbide/cdt/internal/builder/CarbideProjectModifier.java Tue Jan 05 11:22:47 2010 -0600 +++ b/builder/com.nokia.carbide.cdt.builder/src/com/nokia/carbide/cdt/internal/builder/CarbideProjectModifier.java Tue Jan 05 11:23:50 2010 -0600 @@ -20,6 +20,7 @@ import com.nokia.carbide.cdt.builder.project.ICarbideBuildConfiguration; import com.nokia.carbide.cdt.builder.project.ICarbideProjectModifier; import com.nokia.carbide.cdt.internal.api.builder.CarbideConfigurationDataProvider; +import com.nokia.carbide.cpp.internal.api.sdk.SymbianBuildContextDataCache; import com.nokia.carbide.cpp.sdk.core.ISymbianBuildContext; import com.nokia.cpp.internal.api.utils.core.Logging; @@ -255,8 +256,17 @@ CarbideBuilderPlugin.getBuildManager().setProjectInfo(this); // save the CDT project description - CCorePlugin.getDefault().setProjectDescription(projectTracker.getProject(), projDes, true, new NullProgressMonitor()); + try { + // let the build context caches know we may be iterating them all + SymbianBuildContextDataCache.startProjectOperation(); + + // TODO PERFORMANCE: this can lead to CarbideLanguageData#buildCache(), which is an enormously expensive operation. + // So use a real progress monitor, say from a Job, so UI will be updated + CCorePlugin.getDefault().setProjectDescription(projectTracker.getProject(), projDes, true, new NullProgressMonitor()); + } finally { + SymbianBuildContextDataCache.endProjectOperation(); + } if (rebuildCacheAndReindex) { ICProject cproject = CoreModel.getDefault().create(projectTracker.getProject()); if (cproject != null) diff -r 9fca333f8b5e -r 78fd666a897a core/com.nokia.carbide.cpp.sdk.core/src/com/nokia/carbide/cpp/internal/api/sdk/SymbianBuildContext.java --- a/core/com.nokia.carbide.cpp.sdk.core/src/com/nokia/carbide/cpp/internal/api/sdk/SymbianBuildContext.java Tue Jan 05 11:22:47 2010 -0600 +++ b/core/com.nokia.carbide.cpp.sdk.core/src/com/nokia/carbide/cpp/internal/api/sdk/SymbianBuildContext.java Tue Jan 05 11:23:50 2010 -0600 @@ -18,13 +18,9 @@ import org.eclipse.core.runtime.IPath; import org.osgi.framework.Version; -import com.nokia.carbide.cpp.epoc.engine.model.sbv.ISBVView; import com.nokia.carbide.cpp.epoc.engine.preprocessor.*; import com.nokia.carbide.cpp.internal.sdk.core.model.SymbianMissingSDKFactory; -import com.nokia.carbide.cpp.internal.sdk.core.model.SymbianSDK; import com.nokia.carbide.cpp.sdk.core.*; -import com.nokia.carbide.internal.api.cpp.epoc.engine.preprocessor.BasicIncludeFileLocator; -import com.nokia.carbide.internal.api.cpp.epoc.engine.preprocessor.MacroScanner; public class SymbianBuildContext implements ISymbianBuildContext { @@ -43,22 +39,11 @@ // a copy of bad SDK default to fall back private static ISymbianSDK fallbackForBadSdk = SymbianMissingSDKFactory.createInstance("dummy_ID"); //$NON-NLS-1$ - // last time we checked the hrh file mod dates - only check if changed in last second - private static final long HRH_TIMESTAMP_CHECK_QUANTUM = 1000; // 1 sec - private static long lastHrhTimestampCheck; - - private File prefixFileParsed; - private List hrhFilesParsed = new ArrayList(); - private List variantHRHMacros = new ArrayList(); - private long hrhCacheTimestamp; - private List compilerPrefixMacros = new ArrayList(); - private long compilerCacheTimestamp; - - public SymbianBuildContext(ISymbianSDK theSDK, String thePlatform, String theTarget) { sdkId = theSDK.getUniqueId(); platform = thePlatform; target = theTarget; + getDisplayString(); } @@ -329,150 +314,11 @@ public List getVariantHRHDefines() { - // we parse the variant hrh file to gather macros. this can be time consuming so do it - // once and cache the values. only reset the cache when the hrh or any of its includes - // has changed. - - boolean buildCache = false; - - if (hrhCacheTimestamp == 0) { - // hasn't been built yet - buildCache = true; - } else { - // cache exists. see if any of the files have changed - ISymbianSDK sdk = getSDK(); - if (sdk != null) { - // the prefix may have been added, removed, or changed. in any case, - // we would need to reset the cache - File currentPrefixFile = sdk.getPrefixFile(); - if (currentPrefixFile == null) { - if (prefixFileParsed != null) { - // prefix file was removed from the SDK - buildCache = true; - } - } else { - if (prefixFileParsed == null) { - // prefix file was added to the SDK - buildCache = true; - } else { - // there was a prefix file before and now. see if it's the same file - // and if so, has it been modified? - if (!currentPrefixFile.equals(prefixFileParsed) || currentPrefixFile.lastModified() > hrhCacheTimestamp) { - buildCache = true; - } - } - } - } - - // now check to see if any of the included hrh files have changed - // we will do this at most once per quantum, because it is expensive and during import it was done 100 times per second - if (!buildCache && (System.currentTimeMillis() - lastHrhTimestampCheck) > HRH_TIMESTAMP_CHECK_QUANTUM) { - for (File file : hrhFilesParsed) { - if (file.lastModified() > hrhCacheTimestamp) { - buildCache = true; - break; - } - } - lastHrhTimestampCheck = System.currentTimeMillis(); - } - } - - if (buildCache) { - - variantHRHMacros.clear(); - - synchronized (this) { - - List macros = new ArrayList(); - Map namedMacros = new HashMap(); - File prefixFile = getSDK().getPrefixFile(); - - if (prefixFile == null){ - // Check that the prefix file may have become available since the SDK was scanned last. - // This can happen, for e.g., if the user opens the IDE _then_ does a subst on a drive that already has an SDK entry. - IPath prefixCheck = ((SymbianSDK)getSDK()).getPrefixFromVariantCfg(); - if (prefixCheck != null){ - prefixFile = prefixCheck.toFile(); - getSDK().setPrefixFile(prefixCheck); - } - } - - if (prefixFile != null) { - - // add any BSF/SBV includes so the headers are picked up from the correct location - List systemPaths = new ArrayList(); - IBSFPlatform bsfPlat = getSDK().getBSFCatalog().findPlatform(platform); - ISBVPlatform sbvPlat = getSDK().getSBVCatalog().findPlatform(platform); - if (bsfPlat != null) { - for (IPath path : bsfPlat.getSystemIncludePaths()) { - systemPaths.add(path.toFile()); - } - } else if (sbvPlat != null) { - LinkedHashMap platPaths = sbvPlat.getBuildIncludePaths(); - Set set = platPaths.keySet(); - for (IPath path : set) { - String pathType = platPaths.get(path); - if (pathType.equalsIgnoreCase(ISBVView.INCLUDE_FLAG_PREPEND) || pathType.equalsIgnoreCase(ISBVView.INCLUDE_FLAG_SET)){ - systemPaths.add(path.toFile()); - } - } - } - - MacroScanner scanner = new MacroScanner( - new BasicIncludeFileLocator(null, systemPaths.toArray(new File[systemPaths.size()])), - DefaultModelDocumentProvider.getInstance(), - DefaultTranslationUnitProvider.getInstance()); - scanner.scanFile(prefixFile); - - List scannedMacros = (List)scanner.getMacroDefinitions(); - for (IDefine scannedMacro : scannedMacros){ - // we don't want duplicate macros, so check to see if it's already there. - // if it is, remove it and then add the newer one. this is consistent with - // how it would be from a compiler standpoint. - IDefine macro = namedMacros.get(scannedMacro.getName()); - if (macro != null) { - macros.remove(macro); - } - - macros.add(scannedMacro); - namedMacros.put(scannedMacro.getName(), scannedMacro); - } - - hrhFilesParsed.clear(); - for (File inc : scanner.getIncludedFiles()) { - hrhFilesParsed.add(inc); - } - - List variantCFGMacros = new ArrayList(); - variantCFGMacros = getSDK().getVariantCFGMacros(); - for (String cfgMacros : variantCFGMacros){ - // we don't want duplicate macros, so check to see if it's already there. - IDefine existingMacro = namedMacros.get(cfgMacros); - if (existingMacro != null) { - macros.remove(existingMacro); - } - - IDefine macro = DefineFactory.createSimpleFreeformDefine(cfgMacros); - macros.add(macro); - namedMacros.put(macro.getName(), macro); - } - - // store off the time when we created the cache - hrhCacheTimestamp = System.currentTimeMillis(); - } - - // save the prefix file (which may be null) - prefixFileParsed = prefixFile; - - variantHRHMacros = macros; - } - } - - return variantHRHMacros; + return getCachedData().getVariantHRHDefines(); } public List getPrefixFileIncludes() { - return hrhFilesParsed; + return getCachedData().getPrefixFileIncludes(); } @@ -481,54 +327,11 @@ // once and cache the values. only reset the cache when the compiler prefix has changed. IPath prefixFile = getCompilerPrefixFile(); - if (prefixFile == null || !prefixFile.toFile().exists()) { - compilerPrefixMacros.clear(); - return compilerPrefixMacros; + if (prefixFile == null) { + return Collections.emptyList(); } - - if ((compilerCacheTimestamp == 0) || - (prefixFile.toFile().lastModified() > compilerCacheTimestamp)) { - - compilerPrefixMacros.clear(); - - synchronized (this) { - - List macros = new ArrayList(); - if (prefixFile != null) { - - List userPaths = new ArrayList(); - List systemPaths = new ArrayList(); - - userPaths.add(prefixFile.removeLastSegments(1).toFile()); - systemPaths.add(prefixFile.removeLastSegments(1).toFile()); - IPath includePath = getSDK().getIncludePath(); - if (includePath != null) { - File includeDir = includePath.toFile().getAbsoluteFile(); - userPaths.add(includeDir); - systemPaths.add(includeDir); - } - - - // get macros from the compiler prefix file: note, this is a stupid - // scan that will get the last version #defined, even if inside an #if. - MacroScanner scanner = new MacroScanner( - new BasicIncludeFileLocator(userPaths.toArray(new File[userPaths.size()]), systemPaths.toArray(new File[systemPaths.size()])), - DefaultModelDocumentProvider.getInstance(), - DefaultTranslationUnitProvider.getInstance()); - scanner.scanFile(prefixFile.toFile()); - for (IDefine define : scanner.getMacroDefinitions()) { - macros.add(define); - } - - // store off the time when we created the cache - compilerCacheTimestamp = System.currentTimeMillis(); - } - - compilerPrefixMacros = macros; - } - } - - return compilerPrefixMacros; + + return getCachedData().getCompilerMacros(prefixFile); } @@ -552,6 +355,16 @@ } } + /** + * Get the cache holding the data that applies to this build context globally. + * A build context is subclassed by CarbideBuildConfiguration, which has multiple + * instances at runtime, thus, a SymbianBuildContext instance should not hold a cache itself. + * @return cache, never null + */ + private SymbianBuildContextDataCache getCachedData() { + return SymbianBuildContextDataCache.getCache(this); + } + public String getBasePlatformForVariation() { String plat = ""; @@ -565,6 +378,14 @@ return plat; } - - + + + /** + * Get the list of #include paths detected for this context. + * @return List or null + */ + public List getCachedSystemIncludePaths() { + return getCachedData().getSystemIncludePaths(); + } + } diff -r 9fca333f8b5e -r 78fd666a897a core/com.nokia.carbide.cpp.sdk.core/src/com/nokia/carbide/cpp/internal/api/sdk/SymbianBuildContextDataCache.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/core/com.nokia.carbide.cpp.sdk.core/src/com/nokia/carbide/cpp/internal/api/sdk/SymbianBuildContextDataCache.java Tue Jan 05 11:23:50 2010 -0600 @@ -0,0 +1,702 @@ +/* +* Copyright (c) 2009 Nokia Corporation and/or its subsidiary(-ies). +* All rights reserved. +* This component and the accompanying materials are made available +* under the terms of the License "Eclipse Public License v1.0" +* which accompanies this distribution, and is available +* at the URL "http://www.eclipse.org/legal/epl-v10.html". +* +* Initial Contributors: +* Nokia Corporation - initial contribution. +* +*/ +package com.nokia.carbide.cpp.internal.api.sdk; + +import java.io.File; +import java.io.FileInputStream; +import java.io.FileOutputStream; +import java.io.IOException; +import java.io.ObjectInputStream; +import java.io.ObjectOutputStream; +import java.io.ObjectStreamClass; +import java.io.ObjectStreamException; +import java.util.*; + +import org.eclipse.core.runtime.IPath; +import org.eclipse.core.runtime.IStatus; +import org.eclipse.core.runtime.Path; +import org.eclipse.core.runtime.Platform; + +import com.nokia.carbide.cpp.epoc.engine.EpocEnginePlugin; +import com.nokia.carbide.cpp.epoc.engine.model.sbv.ISBVView; +import com.nokia.carbide.cpp.epoc.engine.preprocessor.*; +import com.nokia.carbide.cpp.internal.sdk.core.model.SymbianSDK; +import com.nokia.carbide.cpp.sdk.core.*; +import com.nokia.carbide.internal.api.cpp.epoc.engine.preprocessor.BasicIncludeFileLocator; +import com.nokia.carbide.internal.api.cpp.epoc.engine.preprocessor.MacroScanner; +import com.nokia.cpp.internal.api.utils.core.ExternalFileInfoCollection; +import com.nokia.cpp.internal.api.utils.core.FileUtils; +import com.nokia.cpp.internal.api.utils.core.Logging; +import com.nokia.cpp.internal.api.utils.core.ObjectUtils; + +/** + * This class holds the externally gathered data for a build context, + * such as the #include paths and macros defined in the SDK. + *

+ * Unlike CarbideBuildConfiguration (which, unfortunately extends + * SymbianBuildContext), this will not be created multiple times for + * multiple projects, but only once for a different build context + * in an SDK or devkit. + */ +public class SymbianBuildContextDataCache { + + public static boolean DEBUG = false; + + // by default, only check HRH-included files if changed in last second (for ordinary operations) + // or the last minute (when doing a long project operation). see #startThrottle() and #stopThrottle() + private static final long DEFAULT_HRH_INFO_CHECK_QUANTUM = 1000; // 1 sec + private static final long THROTTLED_HRH_INFO_CHECK_QUANTUM = 60000; // 60 sec + + // compiler prefixes are very unlikely to change, but we need to check + // occasionally in case a user installs a new one... + private static final long DEFAULT_COMPILER_PREFIX_INFO_CHECK_QUANTUM = 15 * 60 * 1000; // 15 minutes + + // This is a count of times #startProjectOperation() was called without + // balancing #endProjectOperation(). + private static int inProjectOperationCount; + + private static Map cacheMap = new HashMap(); + + public static synchronized SymbianBuildContextDataCache getCache(ISymbianBuildContext context) { + // don't hash on ISymbianBuildContext itself since it is sometimes a ICarbideBuildConfiguration + String key = getBuildContextKey(context); + + SymbianBuildContextDataCache cache = cacheMap.get(key); + if (cache == null) { + cache = new SymbianBuildContextDataCache(context); + cache.loadCacheFile(); + cacheMap.put(key, cache); + } + return cache; + } + + /** + * @param context + * @return + */ + private static String getBuildContextKey(ISymbianBuildContext context) { + String key = context.getPlatformString() + "/" + context.getTargetString() + "/"; + ISymbianSDK sdk = context.getSDK(); + if (sdk != null) + key += sdk.getEPOCROOT(); + return key; + } + + //private File prefixFileParsed; + private List hrhFilesParsed = new ArrayList(); + private ExternalFileInfoCollection hrhFileInfo = null; + private List variantHRHMacros = new ArrayList(); + private List compilerPrefixMacros = new ArrayList(); + private ExternalFileInfoCollection compilerPrefixFileInfo = null; + private List systemIncludes; + private ISymbianSDK sdk; + private IPath compilerPrefixFile; + + private String platformString; + + private String displayString; + + private String contextKey; + + private boolean changed; + + private File cacheFile; + + private SymbianBuildContextDataCache(ISymbianBuildContext context) { + if (DEBUG) System.out.println("Creating cache for " + context.getDisplayString()); + this.platformString = context.getPlatformString(); + this.displayString = context.getDisplayString(); + this.sdk = context.getSDK(); + this.contextKey = getBuildContextKey(context); + } + + /* (non-Javadoc) + * @see java.lang.Object#toString() + */ + @Override + public String toString() { + return "Cache for " + displayString; + } + + public List getVariantHRHDefines() { + + // we parse the variant hrh file to gather macros. this can be time consuming so do it + // once and cache the values. only reset the cache when the hrh or any of its includes + // has changed. + + boolean buildCache = false; + + if (hrhFileInfo == null) { + // hasn't been built yet, or was flushed + buildCache = true; + } else { + // Cache exists. See if any of the files have changed + if (sdk != null && hrhFileInfo.anyChanged()) { + buildCache = true; + } + } + + if (buildCache) { + gatherVariantHRHDefines(); + } + + return variantHRHMacros; + } + + /** + * Re-gather the #defines from the variant HRH file + */ + private void gatherVariantHRHDefines() { + changed = true; + variantHRHMacros.clear(); + + synchronized (this) { + + List macros = new ArrayList(); + Map namedMacros = new HashMap(); + File prefixFile = sdk.getPrefixFile(); + + if (prefixFile == null){ + // Check that the prefix file may have become available since the SDK was scanned last. + // This can happen, for e.g., if the user opens the IDE _then_ does a subst on a drive that already has an SDK entry. + IPath prefixCheck = ((SymbianSDK)sdk).getPrefixFromVariantCfg(); + if (prefixCheck != null){ + prefixFile = prefixCheck.toFile(); + sdk.setPrefixFile(prefixCheck); + } + } + + File[] includedFiles = null; + + if (prefixFile != null) { + + // add any BSF/SBV includes so the headers are picked up from the correct location + List systemPaths = new ArrayList(); + IBSFPlatform bsfPlat = sdk.getBSFCatalog().findPlatform(platformString); + ISBVPlatform sbvPlat = sdk.getSBVCatalog().findPlatform(platformString); + if (bsfPlat != null) { + for (IPath path : bsfPlat.getSystemIncludePaths()) { + systemPaths.add(path.toFile()); + } + } else if (sbvPlat != null) { + LinkedHashMap platPaths = sbvPlat.getBuildIncludePaths(); + Set set = platPaths.keySet(); + for (IPath path : set) { + String pathType = platPaths.get(path); + if (pathType.equalsIgnoreCase(ISBVView.INCLUDE_FLAG_PREPEND) || pathType.equalsIgnoreCase(ISBVView.INCLUDE_FLAG_SET)){ + systemPaths.add(path.toFile()); + } + } + } + + MacroScanner scanner = new MacroScanner( + new BasicIncludeFileLocator(null, systemPaths.toArray(new File[systemPaths.size()])), + DefaultModelDocumentProvider.getInstance(), + DefaultTranslationUnitProvider.getInstance()); + scanner.scanFile(prefixFile); + + List scannedMacros = (List)scanner.getMacroDefinitions(); + for (IDefine scannedMacro : scannedMacros){ + // we don't want duplicate macros, so check to see if it's already there. + // if it is, remove it and then add the newer one. this is consistent with + // how it would be from a compiler standpoint. + IDefine macro = namedMacros.get(scannedMacro.getName()); + if (macro != null) { + macros.remove(macro); + } + + macros.add(scannedMacro); + namedMacros.put(scannedMacro.getName(), scannedMacro); + } + + hrhFilesParsed.clear(); + includedFiles = scanner.getIncludedFiles(); + for (File inc : includedFiles) { + hrhFilesParsed.add(inc); + } + + List variantCFGMacros = new ArrayList(); + variantCFGMacros = sdk.getVariantCFGMacros(); + for (String cfgMacros : variantCFGMacros){ + // we don't want duplicate macros, so check to see if it's already there. + IDefine existingMacro = namedMacros.get(cfgMacros); + if (existingMacro != null) { + macros.remove(existingMacro); + } + + IDefine macro = DefineFactory.createSimpleFreeformDefine(cfgMacros); + macros.add(macro); + namedMacros.put(macro.getName(), macro); + } + } + + // cache the info about when we created the cache + if (hrhFileInfo == null) { + hrhFileInfo = new ExternalFileInfoCollection( + EpocEnginePlugin.getExternalFileInfoCache(), + includedFiles, + DEFAULT_HRH_INFO_CHECK_QUANTUM); + } else { + hrhFileInfo.setFiles(includedFiles); + } + + variantHRHMacros = macros; + saveCacheFile(); + } + } + + public List getPrefixFileIncludes() { + return hrhFilesParsed; + } + + + public synchronized List getCompilerMacros(IPath prefixFile) { + + // we assume that the prefix file will not change often, + // (if at all) for a build context, so dump the cache if the prefix file changes. + + if (compilerPrefixFile != null && !compilerPrefixFile.equals(prefixFile)) { + compilerPrefixFileInfo = null; + } + + compilerPrefixFile = prefixFile; + + if (compilerPrefixFileInfo == null || + compilerPrefixFileInfo.anyChanged()) { + + changed = true; + + compilerPrefixMacros.clear(); + + synchronized (this) { + + List macros = new ArrayList(); + if (prefixFile != null) { + + List userPaths = new ArrayList(); + List systemPaths = new ArrayList(); + + userPaths.add(prefixFile.removeLastSegments(1).toFile()); + systemPaths.add(prefixFile.removeLastSegments(1).toFile()); + IPath includePath = sdk.getIncludePath(); + if (includePath != null) { + File includeDir = includePath.toFile().getAbsoluteFile(); + userPaths.add(includeDir); + systemPaths.add(includeDir); + } + + + // get macros from the compiler prefix file: note, this is a stupid + // scan that will get the last version #defined, even if inside an #if. + MacroScanner scanner = new MacroScanner( + new BasicIncludeFileLocator(userPaths.toArray(new File[userPaths.size()]), systemPaths.toArray(new File[systemPaths.size()])), + DefaultModelDocumentProvider.getInstance(), + DefaultTranslationUnitProvider.getInstance()); + scanner.scanFile(prefixFile.toFile()); + for (IDefine define : scanner.getMacroDefinitions()) { + macros.add(define); + } + + // store off the info about what we read for this cache + File[] files = scanner.getIncludedFiles(); + + if (compilerPrefixFileInfo == null) + compilerPrefixFileInfo = new ExternalFileInfoCollection( + EpocEnginePlugin.getExternalFileInfoCache(), + files, + DEFAULT_COMPILER_PREFIX_INFO_CHECK_QUANTUM); + else + compilerPrefixFileInfo.setFiles(files); + } + + compilerPrefixMacros = macros; + + saveCacheFile(); + } + } + + return compilerPrefixMacros; + } + + /** + * Get the list of #include paths detected for this context. + * @return List or null + */ + public synchronized List getSystemIncludePaths() { + if (systemIncludes == null) { + gatherBuildContextSystemIncludePaths(); + } + return systemIncludes; + } + + /** + * Fetch the list of include paths for the build context + */ + private void gatherBuildContextSystemIncludePaths() { + changed = true; + + systemIncludes = new ArrayList(); + + if (DEBUG) System.out.println("Scanning include paths for " + displayString); + + IBSFPlatform bsfplatform = sdk.getBSFCatalog().findPlatform(platformString); + ISBVPlatform sbvPlatform = sdk.getSBVCatalog().findPlatform(platformString); + + // look in the epoc32 directory of the SDK + IPath includePath = sdk.getIncludePath(); + if (includePath != null) { + File includeDir = includePath.toFile().getAbsoluteFile(); + File dir; + + // get additional include directories from BSF platform, if defined + if (bsfplatform != null) { + IPath[] systemIncludePaths = bsfplatform.getSystemIncludePaths(); + for (IPath path : systemIncludePaths) { + dir = path.toFile(); + if (dir.exists() && dir.isDirectory()) { + systemIncludes.add(dir); + } + } + } else if (sbvPlatform != null){ + + LinkedHashMap platPaths = sbvPlatform.getBuildIncludePaths(); + Set set = platPaths.keySet(); + for (IPath path : set) { + String pathType = platPaths.get(path); + if (pathType.equalsIgnoreCase(ISBVView.INCLUDE_FLAG_PREPEND) || pathType.equalsIgnoreCase(ISBVView.INCLUDE_FLAG_SET)){ + dir = path.toFile(); + systemIncludes.add(dir); + } + } + } + else { + // legacy behavior + if (platformString.equals(ISymbianBuildContext.EMULATOR_PLATFORM)) { + dir = new File(includeDir, "wins"); //$NON-NLS-1$ + if (dir.exists() && dir.isDirectory()) { + systemIncludes.add(dir); + } + } + } + + // add OEM dir + dir = new File(includeDir, "oem"); //$NON-NLS-1$ + if (dir.exists() && dir.isDirectory()) { + systemIncludes.add(dir); + } + + // and finally the normal include dir + systemIncludes.add(includeDir); + + // and finally, finally, if this is an SBV add any paths with the append flag + if (sbvPlatform != null){ + + Map platPaths = sbvPlatform.getBuildIncludePaths(); + Set set = platPaths.keySet(); + for (IPath path : set) { + String pathType = platPaths.get(path); + if (pathType.equalsIgnoreCase(ISBVView.INCLUDE_FLAG_APPEND)){ + dir = path.toFile(); + systemIncludes.add(dir); + } + } + } + } + + // also search files in same folder as variant.hrh + File prefix = sdk.getPrefixFile(); + if (sbvPlatform != null){ + // might be an alternate HRH file to use + IPath varVarHRH = sbvPlatform.getBuildVariantHRHFile(); + if (!varVarHRH.toFile().equals(prefix) && varVarHRH.toFile().exists()){ + prefix = varVarHRH.toFile(); + } + } + if (prefix != null) { + systemIncludes.add(prefix.getParentFile()); + } + + saveCacheFile(); + } + + /** + * Let cache know that the client is beginning an operation that + * will iterate all the build contexts. We use this information + * to optimize file info checks. + *

+ * Each call must be balanced by an {@link #endProjectOperation()} call. + * Use a try ... finally block to make sure. + */ + public static synchronized void startProjectOperation() { + inProjectOperationCount++; + if (inProjectOperationCount == 1) { + for (SymbianBuildContextDataCache cache : cacheMap.values()) { + cache.startThrottle(); + } + } + } + + /** + * Let cache know that a project-wide operation is done, so + * we can resume normal info checking behavior. + */ + public static synchronized void endProjectOperation() { + inProjectOperationCount--; + if (inProjectOperationCount < 0) { + Logging.log(SDKCorePlugin.getDefault(), + Logging.newStatus(SDKCorePlugin.getDefault(), + new IllegalStateException("project operation count not balanced"))); + inProjectOperationCount = 0; + } + if (inProjectOperationCount == 0) { + for (SymbianBuildContextDataCache cache : cacheMap.values()) { + cache.stopThrottle(); + cache.saveCacheFile(); + } + } + } + + /** + * Throttle file info checks on this cache if we suspect the same + * files will be checked over and over again in a short time (e.g. through + * multiple contexts on the same SDK). + */ + private void startThrottle() { + if (hrhFileInfo != null) { + hrhFileInfo.setRecheckQuantum(THROTTLED_HRH_INFO_CHECK_QUANTUM); + } + // note: compiler prefix infos already have a long delay, but + // this is a good place to refresh + if (compilerPrefixFileInfo != null) { + compilerPrefixFileInfo.refresh(); + } + } + + /** + * End file info throttling. + */ + private void stopThrottle() { + if (hrhFileInfo != null) + hrhFileInfo.setRecheckQuantum(DEFAULT_HRH_INFO_CHECK_QUANTUM); + // note: compiler prefix infos already have a long delay + } + + /** + * Refresh the cached data when there are substantial changes in the given SDKs. + * + * @param sdks SDKs for whose contexts the caches should be removed, or null for all of them + */ + public static synchronized void refreshForSDKs(ISymbianSDK[] sdks) { + // refresh each context cache, meaning, delete its memory and disk values + Collection values = cacheMap.keySet(); + String[] keyArray = (String[]) values.toArray(new String[values.size()]); + for (String key : keyArray) { + SymbianBuildContextDataCache cache = cacheMap.get(key); + boolean forSDK = sdkInArray(sdks, cache.sdk); + if (forSDK) { + cache.reset(); + cacheMap.remove(key); + } + } + } + + private static boolean sdkInArray(ISymbianSDK[] sdks, ISymbianSDK anSDK) { + if (sdks == null) + return true; + if (anSDK == null) + return false; + // object identity is not appropriate; user may have renamed or moved an SDK + for (ISymbianSDK sdk : sdks) { + if (ObjectUtils.equals(sdk.getEPOCROOT(), anSDK.getEPOCROOT()) + || ObjectUtils.equals(sdk.getUniqueId(), anSDK.getUniqueId())) + return true; + } + return false; + } + + /** + * Reset the cached data for this context, ensuring it will be + * freshly gathered. This deletes anything stored on disk. + */ + public synchronized void reset() { + hrhFileInfo = null; + compilerPrefixFileInfo = null; + systemIncludes = null; + getCacheFile().delete(); + } + + /** + * Get the file where we store cached data + * @return File in workspace plugin state storage + */ + protected File getCacheFile() { + if (cacheFile == null) { + IPath statePath; + if (Platform.isRunning()) + statePath = Platform.getStateLocation(SDKCorePlugin.getDefault().getBundle()); + else + statePath = new Path(FileUtils.getTemporaryDirectory().getAbsolutePath()); + cacheFile = statePath.append(contextKey.replaceAll("[^A-Za-z0-9_]", "_") + ".dat").toFile(); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$ + } + return cacheFile; + } + + /** + * Save cache to disk if changed. + */ + protected void saveCacheFile() { + if (!changed) + return; + + ObjectOutputStream os = null; + try { + File cacheFile = getCacheFile(); + if (DEBUG) System.out.println("Saving to " + cacheFile); //$NON-NLS-1$ + os = new ObjectOutputStream(new FileOutputStream(cacheFile)); + doSaveCache(os); + changed = false; + } catch (ObjectStreamException e) { + Logging.log(SDKCorePlugin.getDefault(), + Logging.newStatus(SDKCorePlugin.getDefault(), + IStatus.WARNING, + "Tried to save uncacheable data for " + displayString, //$NON-NLS-1$ + e)); + } catch (IOException e) { + // oh well + Logging.log(SDKCorePlugin.getDefault(), + Logging.newStatus(SDKCorePlugin.getDefault(), + IStatus.WARNING, + "Failed to save cache state for " + displayString, //$NON-NLS-1$ + e)); + } finally { + if (os != null) { + try { + os.close(); + } catch (IOException e) { + } + } + } + } + + protected void loadCacheFile() { + ObjectInputStream is = null; + // Use the class loader that knows how to span plugins + final ClassLoader classLoader = SDKCorePlugin.getDefault().getClass().getClassLoader(); + File cacheFile = getCacheFile(); + try { + is = new ObjectInputStream(new FileInputStream(cacheFile)) { + /* (non-Javadoc) + * @see java.io.ObjectInputStream#resolveClass(java.io.ObjectStreamClass) + */ + @Override + protected Class resolveClass(ObjectStreamClass desc) + throws IOException, ClassNotFoundException { + String name = desc.getName(); + try { + return classLoader.loadClass(name); + } catch (ClassNotFoundException e) { + return super.resolveClass(desc); + } + } + }; + doLoadCache(is); + if (DEBUG) System.out.println("Loaded cache from " + cacheFile); //$NON-NLS-1$ + } catch (ClassNotFoundException e) { + // this is probably a bug + e.printStackTrace(); + } catch (ObjectStreamException e) { + // assume it's just out of sync (e.g. object signatures changed), so nothing in Error Log + e.printStackTrace(); + cacheFile.delete(); + } catch (IOException e) { + // oh well, not made yet + } finally { + if (is != null) { + try { + is.close(); + } catch (IOException e) { + } + } + changed = false; + } + } + + /** + * @param os + */ + private void doSaveCache(ObjectOutputStream os) throws IOException { + os.writeObject(systemIncludes); + savePath(os, compilerPrefixFile); + os.writeObject(compilerPrefixFileInfo); + os.writeObject(compilerPrefixMacros); + os.writeObject(hrhFileInfo); + os.writeObject(hrhFilesParsed); + os.writeObject(variantHRHMacros); + } + + + /** + * Load entries from the cache. This must match the ordering in + * {@link #doSaveCache(ObjectOutputStream)}. + * @param is + */ + private void doLoadCache(ObjectInputStream is) throws IOException, ClassCastException, ClassNotFoundException { + systemIncludes = loadList(is, File.class); + compilerPrefixFile = loadPath(is); + compilerPrefixFileInfo = (ExternalFileInfoCollection) is.readObject(); + compilerPrefixMacros = loadList(is, IDefine.class); + hrhFileInfo = (ExternalFileInfoCollection) is.readObject(); + hrhFilesParsed = loadList(is, File.class); + variantHRHMacros = loadList(is, IDefine.class); + } + + /** + * Read an IPath from a portable string + * @param is + * @return an IPath constructed from a String + */ + private IPath loadPath(ObjectInputStream is) throws IOException, ClassCastException, ClassNotFoundException { + String path; + path = (String) is.readObject(); + if (path != null) + return Path.fromPortableString(path); + return null; + } + + /** + * Save an IPath as a portable string + * @param os + * @param path + * @throws IOException + */ + private void savePath(ObjectOutputStream os, IPath path) throws IOException { + os.writeObject(path != null ? path.toPortableString() : null); + } + + /** + * Load a list from the object stream, throwing if it appears to contain + * the wrong kind of data. + * @param is + * @param klass + * @return a List or null + */ + @SuppressWarnings("unchecked") + private List loadList(ObjectInputStream is, Class klass) throws IOException, ClassCastException, ClassNotFoundException { + List list = (List) is.readObject(); + if (list == null || klass == null || list.size() == 0) + return list; + if (!klass.isInstance(list.get(0))) + throw new IOException("Class contains " + list.get(0).getClass() + ", not " + klass); //$NON-NLS-1$ //$NON-NLS-2$ + return list; + } +} diff -r 9fca333f8b5e -r 78fd666a897a core/com.nokia.carbide.cpp.sdk.core/src/com/nokia/carbide/cpp/internal/sdk/core/model/AbstractSDKManager.java --- a/core/com.nokia.carbide.cpp.sdk.core/src/com/nokia/carbide/cpp/internal/sdk/core/model/AbstractSDKManager.java Tue Jan 05 11:22:47 2010 -0600 +++ b/core/com.nokia.carbide.cpp.sdk.core/src/com/nokia/carbide/cpp/internal/sdk/core/model/AbstractSDKManager.java Tue Jan 05 11:23:50 2010 -0600 @@ -52,6 +52,7 @@ import com.nokia.carbide.cpp.internal.api.sdk.ISDKManagerInternal; import com.nokia.carbide.cpp.internal.api.sdk.SBSv2Utils; import com.nokia.carbide.cpp.internal.api.sdk.SDKManagerInternalAPI; +import com.nokia.carbide.cpp.internal.api.sdk.SymbianBuildContextDataCache; import com.nokia.carbide.cpp.internal.api.sdk.SymbianMacroStore; import com.nokia.carbide.cpp.sdk.core.ICarbideInstalledSDKChangeListener; import com.nokia.carbide.cpp.sdk.core.IRVCTToolChainInfo; @@ -122,7 +123,7 @@ public void scanSDKs() { synchronized (sdkList) { - ArrayList oldSDkList = new ArrayList(sdkList); + ArrayList oldSDKList = new ArrayList(sdkList); getSBSv2Version(true); @@ -143,7 +144,7 @@ // now these SDK's are removed from the old list, add to // internal list - for (ISymbianSDK oldSdk : oldSDkList) { + for (ISymbianSDK oldSdk : oldSDKList) { boolean found = false; for (ISymbianSDK sdk : sdkList) { if (sdk.getUniqueId().equals(oldSdk.getUniqueId())) { @@ -154,6 +155,8 @@ if (found == false) { SDKManagerInternalAPI.addMissingSdk(oldSdk .getUniqueId()); + // flush cache + SymbianBuildContextDataCache.refreshForSDKs(new ISymbianSDK[] { oldSdk }); } } @@ -172,7 +175,7 @@ * Actually scan the SDKs available and populate sdkList. * @param oldSDkList old list of SDKs available for reference, e.g., detecting * when SDKs are newly missing - * @return + * @return true if scan succeeded */ abstract protected boolean doScanSDKs(); diff -r 9fca333f8b5e -r 78fd666a897a core/com.nokia.carbide.cpp.sdk.ui/src/com/nokia/carbide/cpp/internal/sdk/ui/SDKPreferencePage.java --- a/core/com.nokia.carbide.cpp.sdk.ui/src/com/nokia/carbide/cpp/internal/sdk/ui/SDKPreferencePage.java Tue Jan 05 11:22:47 2010 -0600 +++ b/core/com.nokia.carbide.cpp.sdk.ui/src/com/nokia/carbide/cpp/internal/sdk/ui/SDKPreferencePage.java Tue Jan 05 11:23:50 2010 -0600 @@ -33,6 +33,7 @@ import org.eclipse.swt.widgets.*; import org.eclipse.ui.*; +import com.nokia.carbide.cpp.internal.api.sdk.SymbianBuildContextDataCache; import com.nokia.carbide.cpp.internal.sdk.core.model.SDKManager; import com.nokia.carbide.cpp.sdk.core.*; import com.nokia.carbide.cpp.sdk.ui.SDKUIPlugin; @@ -370,6 +371,8 @@ if (sdkPropDlg.open() == SDKPropertiesDialog.OK){ sdkListTableViewer.refresh(); setSelectedSDKInfoText(sdk); + // forcible rescan; dump cache + SymbianBuildContextDataCache.refreshForSDKs(new ISymbianSDK[] { sdk }); rescanSDKs(false); } } else { @@ -405,6 +408,8 @@ } private void rescanNowButtonAction(){ + // forcible rescan; dump cache + SymbianBuildContextDataCache.refreshForSDKs(null); rescanSDKs(true); } diff -r 9fca333f8b5e -r 78fd666a897a core/com.nokia.cpp.utils.core/src/com/nokia/cpp/internal/api/utils/core/ExternalFileInfoCache.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/core/com.nokia.cpp.utils.core/src/com/nokia/cpp/internal/api/utils/core/ExternalFileInfoCache.java Tue Jan 05 11:23:50 2010 -0600 @@ -0,0 +1,183 @@ +/* +* Copyright (c) 2009 Nokia Corporation and/or its subsidiary(-ies). +* All rights reserved. +* This component and the accompanying materials are made available +* under the terms of the License "Eclipse Public License v1.0" +* which accompanies this distribution, and is available +* at the URL "http://www.eclipse.org/legal/epl-v10.html". +* +* Initial Contributors: +* Nokia Corporation - initial contribution. +* +* Contributors: +* +* Description: +* +*/ + +package com.nokia.cpp.internal.api.utils.core; + +import java.io.File; +import java.io.Serializable; +import java.util.HashMap; +import java.util.Map; + +/** + * Use this object to cache the timestamps and sizes for files which + * are often queried. On some OSes or types of filesystems, attribute checking + * is very slow, and on others, the resolution of a timestamp is so low that + * checking too often is likely to be a waste of effort. + *

+ * We only recommend using this tracker for files outside the Eclipse + * workspace. For workspace resources, use resource listeners to avoid + * polling for file changes. + */ +public class ExternalFileInfoCache implements Serializable { + + private static final long serialVersionUID = -2810901674805567105L; + + public static boolean DEBUG = false; + public static boolean DEBUG_VERBOSE = false; + + // time in ms between checks of any given file + private int quantum; + + static class FileInfo extends Tuple implements Serializable { + + private static final long serialVersionUID = -1226913205921924292L; + + protected FileInfo() { + // empty for serialization + } + public FileInfo(long lastModified, long lastQueried, long size) { + super(lastModified, lastQueried, size); + } + public long getLastModified() { + return (Long) get(0); + } + public long getLastQueried() { + return (Long) get(1); + } + public long getLength() { + return (Long) get(2); + } + + public boolean isChangedFrom(FileInfo other) { + long origTime = getLastModified(); + long origSize = getLength(); + + long newTime = other.getLastModified(); + long newSize = other.getLength(); + + if (newTime == 0) { // 0 if deleted + return true; + } + if (origTime != newTime + || origSize != newSize) + return true; + + return false; + } + } + + /** map of file to file size + last queried timestamp + time of last query + * (use File, not IPath, so we canonicalize for the OS) */ + private Map info = new HashMap(); + + + /** + * Create a cache to track the states of file timestamps and sizes. + * @param quantumMs time in ms between updating the status of any given file + */ + public ExternalFileInfoCache(int quantumMs) { + this.quantum = quantumMs; + } + + /** + * Create a cache to track the states of file timestamps and sizes, + * using an OS-dependent default quantum. + */ + public ExternalFileInfoCache() { + // this is based on the expensiveness of the attribute lookup, not the + // resolution of the timestamps + this.quantum = HostOS.IS_WIN32 ? 50 : 10; + } + + /** + * Tell if the file's timestamp or size changed since the basis time, + * or otherwise changed in the past quantum, + * and update the record. + *

+ * If a file does not exist, we always consider it to have changed + * (since otherwise, we cannot tell if a client saw the transition + * from existing to not existing). + * @param basis the system time in ms from which to judge the change + * @return true if file size or timestamp changed since basis, or + * the file does not exist + */ + public FileInfo getFileInfo(File file, long basis) { + FileInfo finfo = info.get(file); + if (finfo == null) { + finfo = new FileInfo(file.lastModified(), basis, file.length()); + synchronized(info) { + info.put(file, finfo); + } + if (DEBUG) System.out.println("First info for " + file + ": " + finfo); + return finfo; + } else if (finfo.getLastQueried() + quantum < basis) { + // note: if a file no longer exists, these return distinct values + // that also appear as changes + finfo = new FileInfo(file.lastModified(), basis, file.length()); + synchronized(info) { + info.put(file, finfo); + } + return finfo; + } else { + return finfo; + } + } + + /** + * Tell if the file's timestamp or size changed in the past quantum + * and update the record + * @return true if file size or timestamp changed. + */ + public boolean isChanged(File file) { + return isChanged(file, System.currentTimeMillis()); + } + + /** + * Tell if the file's timestamp or size changed since the basis time, + * or otherwise changed in the past quantum, + * and update the record. + *

+ * If a file does not exist, we always consider it to have changed + * (since otherwise, we cannot tell if a client saw the transition + * from existing to not existing). + * @param basis the system time in ms from which to judge the change + * @return true if file size or timestamp changed since basis, or + * the file does not exist + */ + public boolean isChanged(File file, long basis) { + FileInfo finfo = info.get(file); + FileInfo newInfo = getFileInfo(file, basis); + if (finfo == newInfo) + return false; + + // update info + info.put(file, newInfo); + + return finfo.isChangedFrom(newInfo); + } + + /** + * Force a refresh of the given file's status on the next #isChanged() call. + * @param file + */ + public void refresh(File file) { + synchronized(info) { + info.remove(file); + } + } + +} diff -r 9fca333f8b5e -r 78fd666a897a core/com.nokia.cpp.utils.core/src/com/nokia/cpp/internal/api/utils/core/ExternalFileInfoCollection.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/core/com.nokia.cpp.utils.core/src/com/nokia/cpp/internal/api/utils/core/ExternalFileInfoCollection.java Tue Jan 05 11:23:50 2010 -0600 @@ -0,0 +1,153 @@ +/* +* Copyright (c) 2009 Nokia Corporation and/or its subsidiary(-ies). +* All rights reserved. +* This component and the accompanying materials are made available +* under the terms of the License "Eclipse Public License v1.0" +* which accompanies this distribution, and is available +* at the URL "http://www.eclipse.org/legal/epl-v10.html". +* +* Initial Contributors: +* Nokia Corporation - initial contribution. +* +* Contributors: +* +* Description: +* +*/ + +package com.nokia.cpp.internal.api.utils.core; + +import java.io.File; +import java.io.Serializable; +import java.util.HashMap; +import java.util.Map; +import java.util.Set; + +import com.nokia.cpp.internal.api.utils.core.ExternalFileInfoCache.FileInfo; + +/** + * This object corrals a set of timestamp checks against a collection + * of related files (using an {@link ExternalFileInfoCache} for + * storage). + *

+ * Multiple instances of this object can (and should) share a cache. + *

+ * As with {@link ExternalFileInfoCache}, we recommend using this + * tracker only for files outside the Eclipse workspace. For workspace + * resources, use resource listeners to avoid polling for file changes. + */ +public class ExternalFileInfoCollection implements Serializable { + + private static final long serialVersionUID = 3685308336593621639L; + + // the files to check and the last info we found + private Map fileMap = new HashMap(); + + // time in ms between checks of all the files + private long quantum; + + // last time we checked + private long lastCheckTime; + + private ExternalFileInfoCache cache; + + protected ExternalFileInfoCollection() { + // for serialization + } + + /** + * Create an object to check the states of related file timestamps and sizes. + * @param quantumMs time in ms between checking all the files + */ + public ExternalFileInfoCollection(ExternalFileInfoCache cache, + File[] files, long quantumMs) { + Check.checkArg(cache); + this.cache = cache; + this.quantum = quantumMs; + setFiles(files); + this.lastCheckTime = System.currentTimeMillis(); + } + + /** + * If the collection's time quantum has passed, check all the files + * to see if any have changed. + *

+ * Note that by {@link ExternalFileInfoCache#isChanged(File, long)}, + * a deleted file always appears to have changed, so it is assumed that if a + * change is detected, a client will invoke {@link #setFiles(File[])} to + * ensure deleted files are removed from the collection. + * @return true if any file in the collection has appeared to change + * since the last quantum + */ + public synchronized boolean anyChanged() { + boolean changed = false; + if (System.currentTimeMillis() > lastCheckTime + quantum) { + for (File file : fileMap.keySet()) { + if (checkForChange(file)) { + changed = true; + // continue to check all the files, so we update the info uniformly + } + } + lastCheckTime = System.currentTimeMillis(); + } + return changed; + } + + /** + * Recheck one file in the collection and update data. + * @param file + * @return + */ + protected boolean checkForChange(File file) { + FileInfo currentInfo = fileMap.get(file); + FileInfo newInfo = cache.getFileInfo(file, lastCheckTime); + + fileMap.put(file, newInfo); + + return currentInfo == null || currentInfo.isChangedFrom(newInfo); + } + + /** + * Update the files incorporated in the timestamp/size check. + * @param files the files to set + */ + public synchronized void setFiles(File[] files) { + this.fileMap.clear(); + + // prime the cache so every file's current timestamp and size is known + if (files != null) { + for (File file : files) { + checkForChange(file); + } + } + } + + /** + * Get the files this collection checks. + * @return the files + */ + public File[] getFiles() { + Set files = fileMap.keySet(); + return (File[]) files.toArray(new File[files.size()]); + } + + /** + * Refresh the timestamps for the affected files, ensuring that + * changes on disk are immediately detected for the next {@link #anyChanged()} + * call. + */ + public synchronized void refresh() { + for (File file : fileMap.keySet()) { + cache.refresh(file); + } + this.lastCheckTime = 0; + } + + /** + * Set the quantum for the timestamp collection. + * @param quantumMs time is ms between checks of files + */ + public void setRecheckQuantum(long quantum) { + this.quantum = quantum; + } +} diff -r 9fca333f8b5e -r 78fd666a897a core/com.nokia.cpp.utils.core/src/com/nokia/cpp/internal/api/utils/core/Tuple.java --- a/core/com.nokia.cpp.utils.core/src/com/nokia/cpp/internal/api/utils/core/Tuple.java Tue Jan 05 11:22:47 2010 -0600 +++ b/core/com.nokia.cpp.utils.core/src/com/nokia/cpp/internal/api/utils/core/Tuple.java Tue Jan 05 11:23:50 2010 -0600 @@ -16,13 +16,20 @@ */ package com.nokia.cpp.internal.api.utils.core; +import java.io.Serializable; + /** * A tuple of zero or more items. * */ -public class Tuple { +public class Tuple implements Serializable { + private static final long serialVersionUID = -3117335085610864101L; + private Object[] args; + protected Tuple() { + // for serialization + } public Tuple(Object... args) { this.args = args; } diff -r 9fca333f8b5e -r 78fd666a897a project/com.nokia.carbide.cpp.epoc.engine/src/com/nokia/carbide/cpp/epoc/engine/EpocEnginePlugin.java --- a/project/com.nokia.carbide.cpp.epoc.engine/src/com/nokia/carbide/cpp/epoc/engine/EpocEnginePlugin.java Tue Jan 05 11:22:47 2010 -0600 +++ b/project/com.nokia.carbide.cpp.epoc.engine/src/com/nokia/carbide/cpp/epoc/engine/EpocEnginePlugin.java Tue Jan 05 11:23:50 2010 -0600 @@ -29,6 +29,8 @@ import com.nokia.carbide.cpp.epoc.engine.model.sbv.*; import com.nokia.carbide.internal.cpp.epoc.engine.model.SBVModelFactory; import com.nokia.carbide.internal.cpp.epoc.engine.model.ViewDataCache; +import com.nokia.cpp.internal.api.utils.core.ExternalFileInfoCache; +import com.nokia.cpp.internal.api.utils.core.ExternalFileInfoCollection; import com.nokia.cpp.internal.api.utils.core.Logging; import com.nokia.cpp.internal.api.utils.core.MultiResourceChangeListenerDispatcher; @@ -61,6 +63,10 @@ private static ViewDataCache bldInfViewDataCache; private static ViewDataCache imageMakefileViewDataCache; + + private static ExternalFileInfoCache externalFileTimestampSizeCache = + new ExternalFileInfoCache(); + /** * The constructor. */ @@ -506,6 +512,16 @@ return runnable.run(data); } + + /** + * Get the global cache of file timestamps and sizes associated with the EPOC engine. + * This is used to track the status of files in SDKs which are unlikely to change + * often and for which we don't want to waste OS time checking over and over. + * @return {@link ExternalFileInfoCache} + */ + public static ExternalFileInfoCache getExternalFileInfoCache() { + return externalFileTimestampSizeCache; + } } diff -r 9fca333f8b5e -r 78fd666a897a project/com.nokia.carbide.cpp.epoc.engine/src/com/nokia/carbide/cpp/epoc/engine/preprocessor/DefaultTranslationUnitProvider.java --- a/project/com.nokia.carbide.cpp.epoc.engine/src/com/nokia/carbide/cpp/epoc/engine/preprocessor/DefaultTranslationUnitProvider.java Tue Jan 05 11:22:47 2010 -0600 +++ b/project/com.nokia.carbide.cpp.epoc.engine/src/com/nokia/carbide/cpp/epoc/engine/preprocessor/DefaultTranslationUnitProvider.java Tue Jan 05 11:23:50 2010 -0600 @@ -42,7 +42,7 @@ private boolean DUMP = false; /** count of entries allowed */ - private static final int DEFAULT_MAX_CACHE_SIZE = 32; + private static final int DEFAULT_MAX_CACHE_SIZE = 256; /** the minimum number of hits (accesses) to the TU to keep it when flushing cache. */ private static final int DEFAULT_MINIMUM_HITS_TO_KEEP = 8; @@ -156,7 +156,8 @@ if (DUMP) System.out.println("Releasing TU for " + file); //$NON-NLS-1$ tuCache.remove(file); - cacheHits.remove(file); + // do not lose info about file importance + //cacheHits.remove(file); cacheTimes.remove(file); cacheOrder.remove(file); } diff -r 9fca333f8b5e -r 78fd666a897a project/com.nokia.carbide.cpp.epoc.engine/src/com/nokia/carbide/cpp/epoc/engine/preprocessor/IDefine.java --- a/project/com.nokia.carbide.cpp.epoc.engine/src/com/nokia/carbide/cpp/epoc/engine/preprocessor/IDefine.java Tue Jan 05 11:22:47 2010 -0600 +++ b/project/com.nokia.carbide.cpp.epoc.engine/src/com/nokia/carbide/cpp/epoc/engine/preprocessor/IDefine.java Tue Jan 05 11:23:50 2010 -0600 @@ -17,11 +17,13 @@ package com.nokia.carbide.cpp.epoc.engine.preprocessor; +import java.io.Serializable; + /** * High-level information about a macro definition. * */ -public interface IDefine { +public interface IDefine extends Serializable { /** * Get the macro name (never null) */ diff -r 9fca333f8b5e -r 78fd666a897a project/com.nokia.carbide.cpp.epoc.engine/src/com/nokia/carbide/internal/api/cpp/epoc/engine/preprocessor/MacroScanner.java --- a/project/com.nokia.carbide.cpp.epoc.engine/src/com/nokia/carbide/internal/api/cpp/epoc/engine/preprocessor/MacroScanner.java Tue Jan 05 11:22:47 2010 -0600 +++ b/project/com.nokia.carbide.cpp.epoc.engine/src/com/nokia/carbide/internal/api/cpp/epoc/engine/preprocessor/MacroScanner.java Tue Jan 05 11:23:50 2010 -0600 @@ -96,13 +96,16 @@ if (DUMP) System.out.println("Scanning #defines for " + file); //$NON-NLS-1$ - includes.add(file); - ITranslationUnit tu_ = tuProvider.getTranslationUnit(file, documentProvider); if (!(tu_ instanceof IASTTranslationUnit)) { EpocEnginePlugin.log(new FileNotFoundException("Failed to scan #defines for " + file)); //$NON-NLS-1$ return; } + + // only add file if it exists, otherwise, its absence makes clients + // think something has changed + includes.add(file); + IASTTranslationUnit tu = (IASTTranslationUnit) tu_; for (IASTTopLevelNode node : tu.getNodes()) { if (node instanceof IASTPreprocessorDefineStatement) { @@ -163,8 +166,9 @@ } /** - * Get the list of all header files that were scanned - * @return + * Get the list of all header files that were scanned (includes the + * top level file) + * @return non-null array of files */ public File[] getIncludedFiles() { return includes.toArray(new File[includes.size()]); diff -r 9fca333f8b5e -r 78fd666a897a project/com.nokia.carbide.cpp.epoc.engine/src/com/nokia/carbide/internal/cpp/epoc/engine/model/ViewDataCache.java --- a/project/com.nokia.carbide.cpp.epoc.engine/src/com/nokia/carbide/internal/cpp/epoc/engine/model/ViewDataCache.java Tue Jan 05 11:22:47 2010 -0600 +++ b/project/com.nokia.carbide.cpp.epoc.engine/src/com/nokia/carbide/internal/cpp/epoc/engine/model/ViewDataCache.java Tue Jan 05 11:23:50 2010 -0600 @@ -17,6 +17,7 @@ package com.nokia.carbide.internal.cpp.epoc.engine.model; +import com.nokia.carbide.cpp.epoc.engine.EpocEnginePlugin; import com.nokia.carbide.cpp.epoc.engine.model.*; import com.nokia.carbide.cpp.epoc.engine.preprocessor.*; import com.nokia.cpp.internal.api.utils.core.*; @@ -38,6 +39,7 @@ public class ViewDataCache> { public static boolean DEBUG = false; + public static boolean DEBUG_VERBOSE = false; /** A key which can retrieve the current state of a model for a given filter. */ static class ViewConfigKey extends Pair { @@ -60,7 +62,7 @@ */ /*private*/ Object getMD5(String uniqueKey) { try { - MessageDigest md = MessageDigest.getInstance("MD5"); + MessageDigest md = MessageDigest.getInstance("MD5"); //$NON-NLS-1$ return md.digest(uniqueKey.getBytes()); } catch (NoSuchAlgorithmException e) { return uniqueKey; @@ -114,7 +116,7 @@ IViewParserConfiguration parserConfig = viewConfiguration.getViewParserConfiguration(); String includeState = getIncludeState(parserConfig.getIncludeFileLocator()); String projectState = parserConfig.getProjectLocation().toOSString(); - return filterState + "/" + projectState + "/" + includeState + "/" + macroState; + return filterState + "/" + projectState + "/" + includeState + "/" + macroState; //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$ } /** @@ -164,110 +166,12 @@ } - static class FileTimestampSizeCollection { - static FileTimestampSizeCollection INSTANCE = new FileTimestampSizeCollection(); - /** Check timestamps only once in this number of milliseconds */ - final int QUANTUM = 0; - - static class FileInfo extends Tuple { - public FileInfo(long lastModified, long lastQueried, long size) { - super(lastModified, lastQueried, size); - } - public long getLastModified() { - return (Long) get(0); - } - public long getLastQueried() { - return (Long) get(0); - } - public long getLength() { - return (Long) get(2); - } - } - /** map of file to file size + last queried timestamp + time of last query - * (use File, not IPath, so we canonicalize for the OS) */ - private Map info = new HashMap(); - - - /** Tell if the file's timestamp changed in the past quantum - * and update the record */ - public boolean changed(File file) { - long now = System.currentTimeMillis(); - FileInfo finfo = info.get(file); - if (finfo == null) { - finfo = new FileInfo(file.lastModified(), now, file.length()); - info.put(file, finfo); - if (DEBUG) System.out.println("First info for " + file + ": " + finfo); - return true; - } else if (finfo.getLastQueried() + QUANTUM < now) { - // don't check times more than QUANTUM - long origTime = finfo.getLastModified(); - long origSize = finfo.getLength(); - finfo = new FileInfo(file.lastModified(), now, file.length()); - info.put(file, finfo); - if (DEBUG) System.out.println("Updated info for " + file + ": " + origTime + "/" + origSize + " <=> " - + finfo.getLastModified() + "/" + finfo.getLength()); - return origTime != finfo.getLastModified() || finfo.getLastModified() == 0 // 0 if deleted - || origSize != finfo.getLength(); - } else { - // not changed, as far as we assume - if (DEBUG) System.out.println("Assuming no change for " + file); - return false; - } - } - } - - public static class ModelFileTimestampCollection { - /** - * Delay in ms between successive checks of the filesystem, to avoid wasting time - * when such checks are slow, and in cases where it's unlikely the human will edit files - * fast enough to care. - */ - public static final long QUANTUM = HostOS.IS_WIN32 ? 50 : 10; - private File[] files; - private long lastQuery; - - public ModelFileTimestampCollection(IView view) { - IPath[] paths = view.getReferencedFiles(); - this.files = new File[paths.length]; - int idx = 0; - for (IPath path : paths) { - files[idx] = path.toFile(); - // prime the cache - FileTimestampSizeCollection.INSTANCE.changed(files[idx]); - idx++; - } - this.lastQuery = System.currentTimeMillis(); - } - - /** - * Tell if any of the files have changed - * @return - */ - public boolean changed() { - long prevQuery = lastQuery; - lastQuery = System.currentTimeMillis(); - - // don't check more often than the resolution of the file allows - if (prevQuery + QUANTUM > lastQuery) { - if (DEBUG) System.out.println("Skipping fileinfo check"); - return false; - } - - for (File file : files) { - if (FileTimestampSizeCollection.INSTANCE.changed(file)) { - return true; - } - } - return false; - } - } - /** the minimum number of hits (accesses) to the entry to keep it when flushing cache. */ private static final int DEFAULT_MINIMUM_HITS_TO_KEEP = 8; private IModelProvider modelProvider; private Map> cachedData; - private Map cachedTimestamps; + private Map cachedFileInfo; private Map cacheHits; private List cacheOrder; @@ -281,7 +185,7 @@ this.modelProvider = provider; this.cachedData = new HashMap>(); - this.cachedTimestamps = new HashMap(); + this.cachedFileInfo = new HashMap(); this.cacheHits = new HashMap(); this.cacheOrder = new LinkedList(); } @@ -366,11 +270,11 @@ } statefulData = cachedData.get(key); if (statefulData != null) { - // now check that the file timestamps are valid. - ModelFileTimestampCollection timestamps = cachedTimestamps.get(key); - if (timestamps.changed()) { + // now check that the file info is valid. + ExternalFileInfoCollection fileinfo = cachedFileInfo.get(key); + if (fileinfo.anyChanged()) { if (DEBUG) { - System.out.println("One or more relevant files changed for " + modelPath); + System.out.println("One or more relevant files changed for " + modelPath); //$NON-NLS-1$ } removeAllEntriesForModel(modelPath); statefulData = null; @@ -380,9 +284,9 @@ if (DEBUG) { String orig = statefulData.first.toString(); String curr = state.toString(); - System.out.println("State changed (from:\n" - + orig.substring(0, Math.min(100, orig.length())) + "\nto:\n" + - curr.substring(0, Math.min(100, curr.length())) + ")\n"); + System.out.println("State changed (from:\n" //$NON-NLS-1$ + + orig.substring(0, Math.min(100, orig.length())) + "\nto:\n" + //$NON-NLS-1$ + curr.substring(0, Math.min(100, curr.length())) + ")\n"); //$NON-NLS-1$ } removeEntry(key); cachedData.remove(key); @@ -392,8 +296,8 @@ } if (statefulData != null) { - if (DEBUG) { - System.out.println("Found entry for " + key); + if (DEBUG_VERBOSE) { + System.out.println("Found entry for " + key); //$NON-NLS-1$ } cacheHits.put(key, cacheHits.get(key) + 1); data = statefulData.second; @@ -422,7 +326,7 @@ ViewConfigState state, ViewConfigKey key) throws CoreException { Data data; if (DEBUG) { - System.out.println("Fetching view data for " + key); + System.out.println("Fetching view data for " + key); //$NON-NLS-1$ } Model model = modelProvider.getSharedModel(modelPath); if (model == null) @@ -437,11 +341,21 @@ if (data == null) return null; - ModelFileTimestampCollection timestamps = new ModelFileTimestampCollection(view); + IPath[] referencedFiles = view.getReferencedFiles(); + File[] files = new File[referencedFiles.length]; + for (int idx = 0; idx < referencedFiles.length; idx++) { + files[idx] = referencedFiles[idx].toFile(); + } + + ExternalFileInfoCollection fileinfo = + new ExternalFileInfoCollection( + EpocEnginePlugin.getExternalFileInfoCache(), + files, + FileUtils.getMinimumFileTimestampResolution(modelPath)); synchronized (cachedData) { // the data may have already been registered... oh well cachedData.put(key, new Pair(state, data)); - cachedTimestamps.put(key, timestamps); + cachedFileInfo.put(key, fileinfo); cacheOrder.add(0, key); cacheHits.put(key, 0); } @@ -470,8 +384,10 @@ * @param key */ private void removeEntry(ViewConfigKey key) { - cachedTimestamps.remove(key); - cacheHits.remove(key); + cachedFileInfo.remove(key); + + // do not lose info about file importance + //cacheHits.remove(key); cacheOrder.remove(key); cachedData.remove(key); } @@ -497,7 +413,7 @@ Integer hits = cacheHits.get(key); if (hits == null || hits < minimumHitsToKeep) { if (DEBUG) { - System.out.println("*** Flushing " + key); + System.out.println("*** Flushing " + key); //$NON-NLS-1$ } removeEntry(key); toRemove--; @@ -512,12 +428,11 @@ if (key.equals(retainKey)) continue; if (DEBUG) { - System.out.println("*** Flushing " + key); + System.out.println("*** Flushing " + key); //$NON-NLS-1$ } removeEntry(key); toRemove--; } } - }