/******************************************************************************* * Copyright (c) 2007, 2012 Wind River Systems, Inc. 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: * Markus Schorn - initial API and implementation * Sergey Prigogin (Google) ******************************************************************************/ package org.eclipse.cdt.internal.core.pdom.indexer; import java.io.File; import java.util.ArrayList; import java.util.HashSet; import java.util.List; import org.eclipse.cdt.core.CCorePlugin; import org.eclipse.cdt.core.dom.IPDOMIndexer; import org.eclipse.cdt.core.dom.IPDOMIndexerTask; import org.eclipse.cdt.core.dom.IPDOMManager; import org.eclipse.cdt.core.index.IIndex; import org.eclipse.cdt.core.index.IIndexFile; import org.eclipse.cdt.core.index.IIndexFileLocation; import org.eclipse.cdt.core.index.IIndexInclude; import org.eclipse.cdt.core.index.IIndexManager; import org.eclipse.cdt.core.index.IndexLocationFactory; import org.eclipse.cdt.core.model.CoreModel; import org.eclipse.cdt.core.model.ICElement; import org.eclipse.cdt.core.model.ICProject; import org.eclipse.cdt.core.model.ITranslationUnit; import org.eclipse.cdt.core.parser.IScannerInfo; import org.eclipse.cdt.internal.core.model.ExternalTranslationUnit; import org.eclipse.cdt.internal.core.parser.scanner.CPreprocessor; import org.eclipse.cdt.internal.core.parser.scanner.IncludeSearchPath; import org.eclipse.cdt.internal.core.parser.scanner.IncludeSearchPathElement; import org.eclipse.cdt.internal.core.parser.scanner.ScannerUtility; import org.eclipse.cdt.internal.core.pdom.IndexerProgress; import org.eclipse.core.resources.IFile; import org.eclipse.core.resources.IResource; import org.eclipse.core.resources.ResourcesPlugin; import org.eclipse.core.runtime.CoreException; import org.eclipse.core.runtime.IPath; import org.eclipse.core.runtime.IProgressMonitor; import org.eclipse.osgi.util.NLS; /** * A task for updating an index, suitable for all indexers. */ public class PDOMUpdateTask implements IPDOMIndexerTask { private static final ITranslationUnit[] NO_TUS = {}; private final IPDOMIndexer fIndexer; private final int fUpdateOptions; private final IndexerProgress fProgress; private volatile IPDOMIndexerTask fDelegate; private ArrayList<ICElement> fFilesAndFolders; public PDOMUpdateTask(IPDOMIndexer indexer, int updateOptions) { fIndexer= indexer; fUpdateOptions= updateOptions; fProgress= createProgress(); } private IndexerProgress createProgress() { IndexerProgress progress= new IndexerProgress(); progress.fTimeEstimate= 1000; return progress; } @Override public IPDOMIndexer getIndexer() { return fIndexer; } @Override public void run(IProgressMonitor monitor) throws InterruptedException { monitor.subTask(NLS.bind(Messages.PDOMIndexerTask_collectingFilesTask, fIndexer.getProject().getElementName())); ICProject project= fIndexer.getProject(); if (project.getProject().isOpen()) { try { if (!IPDOMManager.ID_NO_INDEXER.equals(fIndexer.getID())) { createDelegate(project, monitor); } } catch (CoreException e) { CCorePlugin.log(e); } } if (fDelegate != null) { fDelegate.run(monitor); } } private void createDelegate(ICProject project, IProgressMonitor monitor) throws CoreException, InterruptedException { HashSet<ITranslationUnit> set= new HashSet<ITranslationUnit>(); if ((fUpdateOptions & (IIndexManager.UPDATE_ALL | IIndexManager.UPDATE_CHECK_TIMESTAMPS)) != 0) { TranslationUnitCollector collector= new TranslationUnitCollector(set, set, monitor); boolean haveProject= false; if (fFilesAndFolders == null) { project.accept(collector); } else { for (ICElement elem : fFilesAndFolders) { if (elem.getElementType() == ICElement.C_PROJECT) { haveProject= true; } elem.accept(collector); } } if (haveProject && (fUpdateOptions & IIndexManager.UPDATE_EXTERNAL_FILES_FOR_PROJECT) != 0) { final String projectPrefix= project.getProject().getFullPath().toString() + IPath.SEPARATOR; IIndex index= CCorePlugin.getIndexManager().getIndex(project); index.acquireReadLock(); try { IIndexFile[] files= index.getAllFiles(); for (IIndexFile indexFile : files) { IIndexFileLocation floc= indexFile.getLocation(); final String fullPath = floc.getFullPath(); if (fullPath == null || !fullPath.startsWith(projectPrefix)) { ITranslationUnit tu = getTranslationUnit(floc, project); if (tu != null) { set.add(tu); } } } } finally { index.releaseReadLock(); } } } if ((fUpdateOptions & IIndexManager.UPDATE_UNRESOLVED_INCLUDES) != 0) { IIndex index= CCorePlugin.getIndexManager().getIndex(project); index.acquireReadLock(); try { // Files that were indexed with I/O errors. IIndexFile[] files= index.getDefectiveFiles(); for (IIndexFile file : files) { ITranslationUnit tu = getTranslationUnit(file.getLocation(), project); if (tu != null) { set.add(tu); } } // Files with unresolved includes. files= index.getFilesWithUnresolvedIncludes(); if (files.length > 0) { ProjectIndexerInputAdapter inputAdapter = new ProjectIndexerInputAdapter(project, true); ProjectIndexerIncludeResolutionHeuristics includeResolutionHeuristics = new ProjectIndexerIncludeResolutionHeuristics(project.getProject(), inputAdapter); for (IIndexFile file : files) { ITranslationUnit tu = getTranslationUnit(file.getLocation(), project); if (tu != null) { IScannerInfo scannerInfo = tu.getScannerInfo(true); if (canResolveUnresolvedInclude(file, scannerInfo, includeResolutionHeuristics)) { set.add(tu); } } } } } finally { index.releaseReadLock(); } } ITranslationUnit[] tus= set.toArray(new ITranslationUnit[set.size()]); IPDOMIndexerTask delegate= fIndexer.createTask(NO_TUS, tus, NO_TUS); if (delegate instanceof PDOMIndexerTask) { final PDOMIndexerTask task = (PDOMIndexerTask) delegate; task.setUpdateFlags(fUpdateOptions); } setDelegate(delegate); } private ITranslationUnit getTranslationUnit(IIndexFileLocation location, ICProject project) { IPath path= IndexLocationFactory.getAbsolutePath(location); if (path == null) return null; ITranslationUnit tu= CoreModel.getDefault().createTranslationUnitFrom(project, path); if (tu != null) { final String fullPath = location.getFullPath(); if (fullPath != null) { if (tu instanceof ExternalTranslationUnit) { IResource file= ResourcesPlugin.getWorkspace().getRoot().findMember(fullPath); if (file instanceof IFile) { ((ExternalTranslationUnit) tu).setResource((IFile) file); } } } } return tu; } private static boolean canResolveUnresolvedInclude(IIndexFile file, IScannerInfo scannerInfo, ProjectIndexerIncludeResolutionHeuristics includeResolutionHeuristics) { try { String filePath = IndexLocationFactory.getAbsolutePath(file.getLocation()).toOSString(); long fileReadTime = file.getSourceReadTime(); IncludeSearchPath includeSearchPath = CPreprocessor.configureIncludeSearchPath(new File(filePath).getParentFile(), scannerInfo); for (IIndexInclude include : file.getIncludes()) { if (!include.isResolved() && include.isActive() && canResolveInclude(include, filePath, fileReadTime, includeSearchPath, includeResolutionHeuristics)) { return true; } } } catch (CoreException e) { CCorePlugin.log(e); } return false; } private static boolean canResolveInclude(IIndexInclude include, String currentFile, long timestamp, IncludeSearchPath includeSearchPath, ProjectIndexerIncludeResolutionHeuristics includeResolutionHeuristics) throws CoreException { String includeName = include.getFullName(); String filePath = CPreprocessor.getAbsoluteInclusionPath(includeName, currentFile); if (filePath != null && fileIsNotOlderThanTimestamp(filePath, timestamp)) { return true; } if (currentFile != null && !include.isSystemInclude() && !includeSearchPath.isInhibitUseOfCurrentFileDirectory()) { // Check to see if we find a match in the current directory final File currentDir= new File(currentFile).getParentFile(); if (currentDir != null) { filePath = ScannerUtility.createReconciledPath(currentDir.getAbsolutePath(), includeName); if (!filePath.equals(currentFile) && fileIsNotOlderThanTimestamp(filePath, timestamp)) { return true; } } } // Unlike CPreprocessor.findInclusion we are searching include path from the beginning. // This simplification may produce false positives, but by checking file modification time // we guarantee that any false positive won't be produced again when this task runs // next time. for (IncludeSearchPathElement path : includeSearchPath.getElements()) { if (!include.isSystemInclude() || !path.isForQuoteIncludesOnly()) { filePath = path.getLocation(includeName); if (filePath != null && fileIsNotOlderThanTimestamp(filePath, timestamp)) { return true; } } } if (includeResolutionHeuristics != null) { filePath= includeResolutionHeuristics.findInclusion(includeName, currentFile); if (filePath != null && fileIsNotOlderThanTimestamp(filePath, timestamp)) { return true; } } return false; } /** * Returns true if the file exists and is not older than the given timestamp. */ private static boolean fileIsNotOlderThanTimestamp(String filename, long timestamp) { // We are subtracting 1 second from the timestamp to account for limited precision // of File.lastModified() method and possible skew between clocks on a multi-CPU // system. This may produce false positives, but they are pretty harmless. return new File(filename).lastModified() >= timestamp - 1000; } private synchronized void setDelegate(IPDOMIndexerTask delegate) { fDelegate= delegate; } @Override public synchronized IndexerProgress getProgressInformation() { return fDelegate != null ? fDelegate.getProgressInformation() : fProgress; } @Override public synchronized boolean acceptUrgentTask(IPDOMIndexerTask task) { return fDelegate != null && fDelegate.acceptUrgentTask(task); } public void setTranslationUnitSelection(List<? extends ICElement> filesAndFolders) { fFilesAndFolders= new ArrayList<>(filesAndFolders); } @Override public void cancel() { if (fDelegate != null) fDelegate.cancel(); } }