diff options
Diffstat (limited to 'qt')
9 files changed, 420 insertions, 318 deletions
diff --git a/qt/org.eclipse.cdt.qt.core/src/org/eclipse/cdt/internal/qt/core/index/QMakeEnvProviderDescriptor.java b/qt/org.eclipse.cdt.qt.core/src/org/eclipse/cdt/internal/qt/core/index/QMakeEnvProviderDescriptor.java index 63f6b4aedfd..e24476deed5 100644 --- a/qt/org.eclipse.cdt.qt.core/src/org/eclipse/cdt/internal/qt/core/index/QMakeEnvProviderDescriptor.java +++ b/qt/org.eclipse.cdt.qt.core/src/org/eclipse/cdt/internal/qt/core/index/QMakeEnvProviderDescriptor.java @@ -39,8 +39,6 @@ public final class QMakeEnvProviderDescriptor implements Comparable<QMakeEnvProv private final AtomicReference<Boolean> evaluation = new AtomicReference<Boolean>(); private final Expression enablementExpression; - private IQMakeEnvProvider provider; - QMakeEnvProviderDescriptor(IConfigurationElement element) { this.element = element; @@ -99,17 +97,12 @@ public final class QMakeEnvProviderDescriptor implements Comparable<QMakeEnvProv if (! matches(controller)) { return null; } - if (provider == null) { - synchronized (this) { - if (provider == null) { - try { - provider = (IQMakeEnvProvider) element.createExecutableExtension(ATTR_CLASS); - } catch (CoreException e) { - QtPlugin.log("Error in class attribute of " + id, e); //$NON-NLS-1$ - return null; - } - } - } + IQMakeEnvProvider provider; + try { + provider = (IQMakeEnvProvider) element.createExecutableExtension(ATTR_CLASS); + } catch (CoreException e) { + QtPlugin.log("Error in class attribute of " + id, e); //$NON-NLS-1$ + return null; } return provider.createEnv(controller); } diff --git a/qt/org.eclipse.cdt.qt.core/src/org/eclipse/cdt/internal/qt/core/index/QMakeEnvProviderManager.java b/qt/org.eclipse.cdt.qt.core/src/org/eclipse/cdt/internal/qt/core/index/QMakeEnvProviderManager.java index f1758ef9348..8b4490922d0 100644 --- a/qt/org.eclipse.cdt.qt.core/src/org/eclipse/cdt/internal/qt/core/index/QMakeEnvProviderManager.java +++ b/qt/org.eclipse.cdt.qt.core/src/org/eclipse/cdt/internal/qt/core/index/QMakeEnvProviderManager.java @@ -16,6 +16,7 @@ import org.eclipse.cdt.core.envvar.IEnvironmentVariable; import org.eclipse.cdt.core.settings.model.ICConfigurationDescription; import org.eclipse.cdt.qt.core.QtPlugin; import org.eclipse.cdt.qt.core.index.IQMakeEnv; +import org.eclipse.cdt.qt.core.index.IQMakeEnv2; import org.eclipse.cdt.qt.core.index.IQMakeEnvProvider; import org.eclipse.cdt.qt.core.index.QMakeEnvInfo; import org.eclipse.core.resources.IFile; @@ -77,7 +78,7 @@ public final class QMakeEnvProviderManager { * Represents a fallback IQMakeEnvProvider that is used for a project that has QtNature * while there is no registered IQMakeEnvProvider that would provide any IQMakeEnv. */ - private static class ConfigurationQMakeEnv implements IQMakeEnv { + private static class ConfigurationQMakeEnv implements IQMakeEnv2 { private static final String PRO_FILE_SUFFIX = ".pro"; //$NON-NLS-1$ private static final String ENV_VAR_QMAKE = "QMAKE"; //$NON-NLS-1$ @@ -89,6 +90,10 @@ public final class QMakeEnvProviderManager { } @Override + public void init() { + } + + @Override public void destroy() { } diff --git a/qt/org.eclipse.cdt.qt.core/src/org/eclipse/cdt/internal/qt/core/index/QMakeProjectInfo.java b/qt/org.eclipse.cdt.qt.core/src/org/eclipse/cdt/internal/qt/core/index/QMakeProjectInfo.java index 305c29ccb25..c3cfa866aca 100644 --- a/qt/org.eclipse.cdt.qt.core/src/org/eclipse/cdt/internal/qt/core/index/QMakeProjectInfo.java +++ b/qt/org.eclipse.cdt.qt.core/src/org/eclipse/cdt/internal/qt/core/index/QMakeProjectInfo.java @@ -10,7 +10,6 @@ package org.eclipse.cdt.internal.qt.core.index; import java.io.File; import java.util.ArrayList; import java.util.Collections; -import java.util.HashMap; import java.util.HashSet; import java.util.Iterator; import java.util.List; @@ -19,157 +18,90 @@ import java.util.Set; import java.util.concurrent.CopyOnWriteArrayList; import org.eclipse.cdt.core.model.CoreModel; -import org.eclipse.cdt.core.settings.model.CProjectDescriptionEvent; import org.eclipse.cdt.core.settings.model.ICConfigurationDescription; -import org.eclipse.cdt.core.settings.model.ICDescriptionDelta; import org.eclipse.cdt.core.settings.model.ICProjectDescription; -import org.eclipse.cdt.core.settings.model.ICProjectDescriptionListener; import org.eclipse.cdt.qt.core.index.IQMakeEnv; -import org.eclipse.cdt.qt.core.index.IQMakeEnvProvider; +import org.eclipse.cdt.qt.core.index.IQMakeEnv2; +import org.eclipse.cdt.qt.core.index.IQMakeEnvProvider.IController; +import org.eclipse.cdt.qt.core.index.IQMakeInfo; import org.eclipse.cdt.qt.core.index.IQMakeProjectInfo; import org.eclipse.cdt.qt.core.index.IQMakeProjectInfoListener; import org.eclipse.cdt.qt.core.index.QMakeEnvInfo; -import org.eclipse.cdt.qt.core.index.IQMakeInfo; import org.eclipse.core.filesystem.URIUtil; import org.eclipse.core.resources.IFile; import org.eclipse.core.resources.IProject; -import org.eclipse.core.resources.IResource; -import org.eclipse.core.resources.IResourceChangeEvent; -import org.eclipse.core.resources.IResourceChangeListener; -import org.eclipse.core.resources.IResourceDelta; -import org.eclipse.core.resources.IResourceDeltaVisitor; import org.eclipse.core.resources.IWorkspaceRoot; import org.eclipse.core.resources.ResourcesPlugin; -import org.eclipse.core.runtime.CoreException; import org.eclipse.core.runtime.IPath; import org.eclipse.core.runtime.Path; /** * Represents a QMake project information that is based on an activate project configuration of a specified related IProject. * Allows to resolve actual information and listen its change. - * Manages life-cycle of all QMakeProjectInfo instances. */ -public final class QMakeProjectInfo implements IQMakeProjectInfo, ICProjectDescriptionListener { - - private static final RCListener LISTENER = new RCListener(); - // sync object for CACHE field - private static final Object SYNC = new Object(); - // a map of all QMakeProjectInfo instances - private static final Map<IProject,QMakeProjectInfo> CACHE = new HashMap<IProject,QMakeProjectInfo>(); - - // called by QtPlugin activator to setup this class - public static final void start() { - ResourcesPlugin.getWorkspace().addResourceChangeListener(LISTENER, IResourceChangeEvent.POST_CHANGE | IResourceChangeEvent.PRE_CLOSE | IResourceChangeEvent.PRE_DELETE); - } +public final class QMakeProjectInfo implements IQMakeProjectInfo { - // called by QtPlugin activator to clean up this class - public static final void stop() { - ResourcesPlugin.getWorkspace().removeResourceChangeListener(LISTENER); - List<QMakeProjectInfo> infos; - synchronized (SYNC) { - infos = new ArrayList<QMakeProjectInfo>(CACHE.values()); - CACHE.clear(); - } - for (QMakeProjectInfo info : infos) { - if (info != null) { - info.destroy(); - } - } - } - - /** - * Returns a QMakeProjectInfo for an active project configuration of a specified project. - * - * @param project the project - * @return the QMakeProjectInfo; or null if the project does not have QtNature - */ - public static QMakeProjectInfo getQMakeProjectInfoFor(IProject project) { - QMakeProjectInfo info; - synchronized (SYNC) { - info = CACHE.get(project); - if (info != null) { - return info; - } - info = new QMakeProjectInfo(project); - CACHE.put(project,info); - } - info.updateActiveConfiguration(); - return info; - } + private final State STATE_FREEZE = new State(); + private final State STATE_INVALID = new State(); - // removes the project from the CACHE - private static void removeProjectFromCache(IResource project) { - QMakeProjectInfo info; - synchronized (SYNC) { - info = CACHE.remove(project); - } - if (info != null) { - info.destroy(); - } - } + // listeners + private final List<IQMakeProjectInfoListener> listeners = new CopyOnWriteArrayList<IQMakeProjectInfoListener>(); private final IProject project; - // sync object for all mutable fields - private final Object sync = new Object(); - // true if this instance still registered in CACHE - private boolean live = true; - // a set of sensitive files that might affects actual QMake information - private final SensitiveSet sensitiveFilePathSet = new SensitiveSet(); - // an active project configuration - private ControllerImpl activeController; - // the last calculated QMake info; null if not calculated - private IQMakeInfo qmakeInfo = null; - // listeners - private final List<IQMakeProjectInfoListener> listeners = new CopyOnWriteArrayList<IQMakeProjectInfoListener>(); + private final Object stateSync = new Object(); - private QMakeProjectInfo(IProject project) { + // represents a current state of QMakeProjectInfo + private State state = STATE_INVALID; + + QMakeProjectInfo(IProject project) { this.project = project; - CoreModel.getDefault().addCProjectDescriptionListener(this, ICDescriptionDelta.ACTIVE_CFG); } - // called from removeProjectFromCache only - private void destroy() { - synchronized (sync) { - if (! live) { - return; - } - live = false; - CoreModel.getDefault().removeCProjectDescriptionListener(this); - setActiveConfiguration(null); - qmakeInfo = QMakeInfo.INVALID; - } + void destroy() { + setState(STATE_FREEZE); } - // must not be called under synchronized (SYNC) or synchronized (sync) - private void updateActiveConfiguration() { - synchronized (sync) { - if (! live) { + // must not be called under any QMake-related sync-lock, except for workspace lock + private void updateStateFrom(State fromState) { + synchronized (stateSync) { + if (state != fromState) { return; } - ICProjectDescription projectDescription = CoreModel.getDefault().getProjectDescriptionManager().getProjectDescription(project); - setActiveConfiguration(projectDescription != null ? projectDescription.getActiveConfiguration() : null); - qmakeInfo = null; } - notifyListeners(); + updateState(); } - // called under synchronized (sync) - private void setActiveConfiguration(ICConfigurationDescription configuration) { - ControllerImpl previous = activeController; - activeController = configuration != null ? new ControllerImpl(configuration) : null; - if (previous != null) { - previous.destroy(); - } + // must not be called under any QMake-related sync-lock, except for workspace lock + // we are running outside of synchronized block to prevent deadlock involving workspace lock + // this means that theoretically there might be multiple thread calculating the same results but only the last one wins + void updateState() { + // note that getProjectDescription might acquire workspace lock + ICProjectDescription projectDescription = CoreModel.getDefault().getProjectDescriptionManager().getProjectDescription(project); + ICConfigurationDescription configuration = projectDescription != null ? projectDescription.getActiveConfiguration() : null; + setState(configuration != null ? new State(configuration) : STATE_INVALID); } - // called on active project configuration change - @Override - public void handleEvent(CProjectDescriptionEvent event) { - if (event.getProject() != project) { - return; + private void setState(State newState) { + State oldState = null; + synchronized (stateSync) { + if (newState == null || state == newState) { + return; + } + if (state == STATE_FREEZE) { + newState.destroyBeforeInit(); + return; + } + oldState = state; + state = newState; + if (oldState != null) { + oldState.destroy(); + } + newState.init(); + } + for (IQMakeProjectInfoListener listener : listeners) { + listener.qmakeInfoChanged(); } - updateActiveConfiguration(); } @Override @@ -182,60 +114,14 @@ public final class QMakeProjectInfo implements IQMakeProjectInfo, ICProjectDescr listeners.remove(listener); } - private IProject getProject() { + IProject getProject() { return project; } - // calculates (if does not exist) and returns actual QMake info @Override public IQMakeInfo getActualInfo() { - synchronized (sync) { - if (! live) { - return QMakeInfo.INVALID; - } - if (qmakeInfo == null) { - fetchQMakeInfo(); - } - return qmakeInfo; - } - } - - // calculates actual QMake info - private void fetchQMakeInfo() { - // retrieves IQMakeEnvInfo from IQMakeEnv - IQMakeEnv qmakeEnv = activeController != null ? activeController.getQMakeEnv() : null; - QMakeEnvInfo qmakeEnvInfo = qmakeEnv != null ? qmakeEnv.getQMakeEnvInfo() : null; - - // retrieves .pro file path - String proFilePath = toFilePath(qmakeEnvInfo != null ? qmakeEnvInfo.getProFile() : null); - // retrieves qmake executable path - String qmakeFilePath = qmakeEnvInfo != null ? qmakeEnvInfo.getQMakeFilePath() : null; - // retries environment - List<String> envList = new ArrayList<String>(); - Map<String, String> envMap = qmakeEnvInfo != null ? qmakeEnvInfo.getEnvironment() : Collections.<String,String>emptyMap(); - for (Map.Entry<String,String> entry : envMap.entrySet()) { - envList.add(entry.getKey() + "=" + entry.getValue()); - } - - // calculates actual QMake info - qmakeInfo = QMakeInfo.create(proFilePath, qmakeFilePath, envList.toArray(new String[envList.size()])); - - // calculates a new set of sensitive file paths - sensitiveFilePathSet.clear(); - Set<IFile> envSensFiles = qmakeEnvInfo != null ? qmakeEnvInfo.getSensitiveFiles() : Collections.<IFile>emptySet(); - for (IFile sensitiveFile : envSensFiles) { - if (sensitiveFile != null) { - sensitiveFilePathSet.addSensitiveFile(sensitiveFile); - } - } - if (proFilePath != null) { - sensitiveFilePathSet.addSensitiveFile(proFilePath); - } - List<String> sensitiveFiles = qmakeInfo.getInvolvedQMakeFiles(); - if (sensitiveFiles != null) { - for (String sensitiveFile : sensitiveFiles) { - sensitiveFilePathSet.addSensitiveFile(sensitiveFile); - } + synchronized (stateSync) { + return state.getQMakeInfo(); } } @@ -257,174 +143,115 @@ public final class QMakeProjectInfo implements IQMakeProjectInfo, ICProjectDescr } // checks if any of the specified files is a sensitive file - private boolean containsAnySensitiveFile(Set<IPath> files) { - synchronized (sync) { - if (live) { - for (Iterator<IPath> iterator = files.iterator(); iterator.hasNext();) { - IPath path = iterator.next(); - if (sensitiveFilePathSet.contains(path)) { - return true; - } - } - } - return false; - } - } - - // resets actual QMake info and notifies all listeners that QMake information has changes - private void scheduleFetchQMakeInfo() { - synchronized (sync) { - if (! live) { - return; - } - qmakeInfo = null; - } - notifyListeners(); - } - - private void notifyListeners() { - for (IQMakeProjectInfoListener listener : listeners) { - listener.qmakeInfoChanged(); + boolean containsAnySensitiveFile(Set<IPath> files) { + synchronized (stateSync) { + return state.containsAnySensitiveFile(files); } } /** - * Represents a project configuration. + * Represents a state of QMakeInfo for a specific QMakeProjectInfo. */ - private final class ControllerImpl implements IQMakeEnvProvider.IController { + private final class State implements IController { + // an active project configuration private final ICConfigurationDescription configuration; + // an active project qmake env private final IQMakeEnv qmakeEnv; + // the last calculated QMake info; null if not calculated + private final IQMakeInfo qmakeInfo; + // a set of sensitive files that might affects actual QMake information + private final SensitiveSet sensitiveFilePathSet; + + State() { + configuration = null; + qmakeEnv = null; + qmakeInfo = QMakeInfo.INVALID; + sensitiveFilePathSet = new SensitiveSet(); + } - public ControllerImpl(ICConfigurationDescription configuration) { + State(ICConfigurationDescription configuration) { this.configuration = configuration; // qmakeEnv created from registry of qmakeEnvProvider extensions this.qmakeEnv = QMakeEnvProviderManager.getInstance().createEnv(this); - } - - public void destroy() { - qmakeEnv.destroy(); - } - - public IQMakeEnv getQMakeEnv() { - return qmakeEnv; - } - @Override - public ICConfigurationDescription getConfiguration() { - return configuration; - } - - @Override - public void scheduleUpdate() { - scheduleFetchQMakeInfo(); - } + // retrieves IQMakeEnvInfo from IQMakeEnv + QMakeEnvInfo qmakeEnvInfo = qmakeEnv.getQMakeEnvInfo(); + + // retrieves .pro file path + String proFilePath = toFilePath(qmakeEnvInfo != null ? qmakeEnvInfo.getProFile() : null); + // retrieves qmake executable path + String qmakeFilePath = qmakeEnvInfo != null ? qmakeEnvInfo.getQMakeFilePath() : null; + // retries environment + List<String> envList = new ArrayList<String>(); + Map<String, String> envMap = qmakeEnvInfo != null ? qmakeEnvInfo.getEnvironment() : Collections.<String,String>emptyMap(); + for (Map.Entry<String,String> entry : envMap.entrySet()) { + envList.add(entry.getKey() + "=" + entry.getValue()); + } - } + // calculates actual QMake info + qmakeInfo = QMakeInfo.create(proFilePath, qmakeFilePath, envList.toArray(new String[envList.size()])); - /** - * Listens on Eclipse file system changes. - */ - private static final class RCListener implements IResourceChangeListener { - - @Override - public void resourceChanged(IResourceChangeEvent event) { - RDVisitor visitor = new RDVisitor(); - - // collect project to delete and changed files - switch (event.getType()) { - case IResourceChangeEvent.PRE_CLOSE: - case IResourceChangeEvent.PRE_DELETE: - IResource project = event.getResource(); - if (project != null && project.getType() == IResource.PROJECT) { - visitor.addProjectToDelete(project); + // calculates a new set of sensitive file paths + sensitiveFilePathSet = new SensitiveSet(); + Set<IFile> envSensFiles = qmakeEnvInfo != null ? qmakeEnvInfo.getSensitiveFiles() : Collections.<IFile>emptySet(); + for (IFile sensitiveFile : envSensFiles) { + if (sensitiveFile != null) { + sensitiveFilePathSet.addSensitiveFile(sensitiveFile); } - break; - case IResourceChangeEvent.POST_CHANGE: - IResourceDelta delta = event.getDelta(); - if (delta != null) { - try { - delta.accept(visitor); - } catch (CoreException e) { - // empty - } + } + if (proFilePath != null) { + sensitiveFilePathSet.addSensitiveFile(proFilePath); + } + List<String> sensitiveFiles = qmakeInfo.getInvolvedQMakeFiles(); + if (sensitiveFiles != null) { + for (String sensitiveFile : sensitiveFiles) { + sensitiveFilePathSet.addSensitiveFile(sensitiveFile); } - break; } - - // process collected data - visitor.process(); } - } - - private static final class RDVisitor implements IResourceDeltaVisitor { - - private final Set<IResource> projectsToDelete = new HashSet<IResource>(); - private final Set<IResource> projectsToUpdate = new HashSet<IResource>(); - private final Set<IPath> changedFiles = new HashSet<IPath>(); - - @Override - public boolean visit(IResourceDelta delta) throws CoreException { - IResource resource = delta.getResource(); - if (resource != null) { - switch (resource.getType()) { - case IResource.FILE: - addChangedFile(resource); - return false; - case IResource.PROJECT: - switch (delta.getKind()) { - case IResourceDelta.CHANGED: - if ((delta.getFlags() & IResourceDelta.DESCRIPTION) != 0) { - addProjectToUpdate(resource); - } - return true; - case IResourceDelta.REMOVED: - addProjectToDelete(resource); - return false; - } - break; + private boolean containsAnySensitiveFile(Set<IPath> files) { + for (Iterator<IPath> iterator = files.iterator(); iterator.hasNext();) { + IPath path = iterator.next(); + if (sensitiveFilePathSet.contains(path)) { + return true; } } - return true; + return false; } - private void addProjectToUpdate(IResource project) { - projectsToUpdate.add(project); + @Override + public ICConfigurationDescription getConfiguration() { + return configuration; } - private void addProjectToDelete(IResource project) { - projectsToDelete.add(project); + public void destroyBeforeInit() { + // see IQMakeEnv JavaDoc for details + if (qmakeEnv != null && ! (qmakeEnv instanceof IQMakeEnv2)) { + qmakeEnv.destroy(); + } } - private void addChangedFile(IResource file) { - IPath fullPath = file.getFullPath(); - if (fullPath != null) { - changedFiles.add(fullPath); + public void init() { + if (qmakeEnv instanceof IQMakeEnv2) { + ((IQMakeEnv2) qmakeEnv).init(); } } - public void process() { - // removing projects from CACHE - for(IResource project : projectsToDelete) { - removeProjectFromCache(project); + public void destroy() { + if (qmakeEnv != null) { + qmakeEnv.destroy(); } + } - List<QMakeProjectInfo> infos; - synchronized (SYNC) { - infos = new ArrayList<QMakeProjectInfo>(CACHE.values()); - } - for (QMakeProjectInfo info : infos) { - // checking if any project description change affects QMakeProjectInfo - if (projectsToUpdate.contains(info.getProject())) { - info.updateActiveConfiguration(); - } - // checking if any of the changed files affects QMakeProjectInfo - if (info.containsAnySensitiveFile(changedFiles)) { - // if so then scheduling update - info.scheduleFetchQMakeInfo(); - } - } + @Override + public void scheduleUpdate() { + updateStateFrom(this); + } + + IQMakeInfo getQMakeInfo() { + return qmakeInfo; } } diff --git a/qt/org.eclipse.cdt.qt.core/src/org/eclipse/cdt/internal/qt/core/index/QMakeProjectInfoManager.java b/qt/org.eclipse.cdt.qt.core/src/org/eclipse/cdt/internal/qt/core/index/QMakeProjectInfoManager.java new file mode 100644 index 00000000000..4553bcbfcd2 --- /dev/null +++ b/qt/org.eclipse.cdt.qt.core/src/org/eclipse/cdt/internal/qt/core/index/QMakeProjectInfoManager.java @@ -0,0 +1,224 @@ +/* + * Copyright (c) 2014 QNX Software Systems 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 + */ +package org.eclipse.cdt.internal.qt.core.index; + +import java.util.ArrayList; +import java.util.HashMap; +import java.util.HashSet; +import java.util.List; +import java.util.Map; +import java.util.Set; + +import org.eclipse.cdt.core.model.CoreModel; +import org.eclipse.cdt.core.settings.model.CProjectDescriptionEvent; +import org.eclipse.cdt.core.settings.model.ICDescriptionDelta; +import org.eclipse.cdt.core.settings.model.ICProjectDescriptionListener; +import org.eclipse.core.resources.IProject; +import org.eclipse.core.resources.IResource; +import org.eclipse.core.resources.IResourceChangeEvent; +import org.eclipse.core.resources.IResourceChangeListener; +import org.eclipse.core.resources.IResourceDelta; +import org.eclipse.core.resources.IResourceDeltaVisitor; +import org.eclipse.core.resources.ResourcesPlugin; +import org.eclipse.core.runtime.CoreException; +import org.eclipse.core.runtime.IPath; + +/** + * Represents a management of QMakeProjectInfo instances and manages life-cycle of all QMakeProjectInfo instances. + */ +public class QMakeProjectInfoManager { + + private static final PDListener PD_LISTENER = new PDListener(); + private static final RCListener RC_LISTENER = new RCListener(); + + // sync object for CACHE field + private static final Object CACHE_SYNC = new Object(); + // a list of all QMakeProjectInfo instances + private static Map<IProject,QMakeProjectInfo> CACHE; + + // called by QtPlugin activator to setup this class + public static final void start() { + synchronized (CACHE_SYNC) { + CACHE = new HashMap<IProject,QMakeProjectInfo>(); + } + CoreModel.getDefault().addCProjectDescriptionListener(PD_LISTENER, ICDescriptionDelta.ACTIVE_CFG); + ResourcesPlugin.getWorkspace().addResourceChangeListener(RC_LISTENER, IResourceChangeEvent.POST_CHANGE | IResourceChangeEvent.PRE_CLOSE | IResourceChangeEvent.PRE_DELETE); + } + + // called by QtPlugin activator to clean up this class + public static final void stop() { + ResourcesPlugin.getWorkspace().removeResourceChangeListener(RC_LISTENER); + CoreModel.getDefault().removeCProjectDescriptionListener(PD_LISTENER); + List<QMakeProjectInfo> infos; + synchronized (CACHE_SYNC) { + infos = new ArrayList<QMakeProjectInfo>(CACHE.values()); + CACHE = null; + } + for (QMakeProjectInfo info : infos) { + if (info != null) { + info.destroy(); + } + } + } + + /** + * Returns a QMakeProjectInfo for an active project configuration of a specified project. + * + * @param project the project + * @return the QMakeProjectInfo; or null if the project does not have QtNature + */ + public static QMakeProjectInfo getQMakeProjectInfoFor(IProject project) { + return getQMakeProjectInfoFor(project, true); + } + + private static QMakeProjectInfo getQMakeProjectInfoFor(IProject project, boolean create) { + QMakeProjectInfo info; + synchronized (CACHE_SYNC) { + info = CACHE.get(project); + if (info != null) { + return info; + } + if (! create) { + // do not create, just return null + return null; + } + info = new QMakeProjectInfo(project); + CACHE.put(project, info); + } + info.updateState(); + return info; + } + + // removes the project from the CACHE + private static void removeProjectFromCache(IResource project) { + QMakeProjectInfo info; + synchronized (CACHE_SYNC) { + info = CACHE.remove(project); + } + if (info != null) { + info.destroy(); + } + } + + private static final class PDListener implements ICProjectDescriptionListener { + + // called on active project configuration change + @Override + public void handleEvent(CProjectDescriptionEvent event) { + QMakeProjectInfo info = getQMakeProjectInfoFor(event.getProject(), false); + if (info != null) { + info.updateState(); + } + } + + } + + /** + * Listens on Eclipse file system changes. + */ + private static final class RCListener implements IResourceChangeListener { + + @Override + public void resourceChanged(IResourceChangeEvent event) { + RDVisitor visitor = new RDVisitor(); + + // collect project to delete and changed files + switch (event.getType()) { + case IResourceChangeEvent.PRE_CLOSE: + case IResourceChangeEvent.PRE_DELETE: + IResource project = event.getResource(); + if (project != null && project.getType() == IResource.PROJECT) { + visitor.addProjectToDelete(project); + } + break; + case IResourceChangeEvent.POST_CHANGE: + IResourceDelta delta = event.getDelta(); + if (delta != null) { + try { + delta.accept(visitor); + } catch (CoreException e) { + // empty + } + } + break; + } + + // process collected data + visitor.process(); + } + + } + + private static final class RDVisitor implements IResourceDeltaVisitor { + + private final Set<IResource> projectsToDelete = new HashSet<IResource>(); + private final Set<IResource> projectsToUpdate = new HashSet<IResource>(); + private final Set<IPath> changedFiles = new HashSet<IPath>(); + + @Override + public boolean visit(IResourceDelta delta) throws CoreException { + IResource resource = delta.getResource(); + if (resource != null) { + switch (resource.getType()) { + case IResource.FILE: + addChangedFile(resource); + return false; + case IResource.PROJECT: + switch (delta.getKind()) { + case IResourceDelta.CHANGED: + if ((delta.getFlags() & IResourceDelta.DESCRIPTION) != 0) { + addProjectToUpdate(resource); + } + return true; + case IResourceDelta.REMOVED: + addProjectToDelete(resource); + return false; + } + break; + } + } + return true; + } + + private void addProjectToUpdate(IResource project) { + projectsToUpdate.add(project); + } + + private void addProjectToDelete(IResource project) { + projectsToDelete.add(project); + } + + private void addChangedFile(IResource file) { + IPath fullPath = file.getFullPath(); + if (fullPath != null) { + changedFiles.add(fullPath); + } + } + + public void process() { + // removing projects from CACHE + for(IResource project : projectsToDelete) { + removeProjectFromCache(project); + } + + List<QMakeProjectInfo> infos; + synchronized (CACHE_SYNC) { + infos = new ArrayList<QMakeProjectInfo>(CACHE.values()); + } + for (QMakeProjectInfo info : infos) { + // checking if any project description change or any of the changed files affect QMakeProjectInfo + if (projectsToUpdate.contains(info.getProject()) || info.containsAnySensitiveFile(changedFiles)) { + // if so then updating + info.updateState(); + } + } + } + + } + +} diff --git a/qt/org.eclipse.cdt.qt.core/src/org/eclipse/cdt/qt/core/QtPlugin.java b/qt/org.eclipse.cdt.qt.core/src/org/eclipse/cdt/qt/core/QtPlugin.java index 7649685cd21..130ec7a1e39 100644 --- a/qt/org.eclipse.cdt.qt.core/src/org/eclipse/cdt/qt/core/QtPlugin.java +++ b/qt/org.eclipse.cdt.qt.core/src/org/eclipse/cdt/qt/core/QtPlugin.java @@ -7,14 +7,18 @@ */ package org.eclipse.cdt.qt.core; +import java.util.concurrent.atomic.AtomicBoolean; + import org.eclipse.cdt.core.model.CModelException; -import org.eclipse.cdt.internal.qt.core.index.QMakeProjectInfo; +import org.eclipse.cdt.internal.qt.core.index.QMakeProjectInfoManager; import org.eclipse.core.runtime.CoreException; import org.eclipse.core.runtime.IStatus; import org.eclipse.core.runtime.Plugin; import org.eclipse.core.runtime.QualifiedName; import org.eclipse.core.runtime.Status; import org.osgi.framework.BundleContext; +import org.osgi.framework.BundleEvent; +import org.osgi.framework.BundleListener; public class QtPlugin extends Plugin { @@ -47,13 +51,27 @@ public class QtPlugin extends Plugin { @Override public void start(BundleContext context) throws Exception { + // have to wait for STARTED event because + // cannot access CoreModel.getDefault().addCProjectDescriptionListener() + // since the CoreModel is not completely initialized at this time + BundleListener bundleListener = new BundleListener() { + final AtomicBoolean initStarted = new AtomicBoolean(false); + @Override + public void bundleChanged(BundleEvent bundleEvent) { + if (bundleEvent.getType() == BundleEvent.STARTED) { + if (!initStarted.getAndSet(true)) { + QMakeProjectInfoManager.start(); + } + } + } + }; + context.addBundleListener(bundleListener); super.start(context); - QMakeProjectInfo.start(); } @Override public void stop(BundleContext context) throws Exception { - QMakeProjectInfo.stop(); + QMakeProjectInfoManager.stop(); super.stop(context); } diff --git a/qt/org.eclipse.cdt.qt.core/src/org/eclipse/cdt/qt/core/index/IQMakeEnv.java b/qt/org.eclipse.cdt.qt.core/src/org/eclipse/cdt/qt/core/index/IQMakeEnv.java index 574d3fb8c6c..adafb0680ca 100644 --- a/qt/org.eclipse.cdt.qt.core/src/org/eclipse/cdt/qt/core/index/IQMakeEnv.java +++ b/qt/org.eclipse.cdt.qt.core/src/org/eclipse/cdt/qt/core/index/IQMakeEnv.java @@ -9,11 +9,18 @@ package org.eclipse.cdt.qt.core.index; /** * Represents a QMake environment. It is usually created by IQMakeEnvProvider.createEnv() method for a specific project configuration. + * + * Note that IQMakeEnv has destroy method only and it is expected that the instance is already initialized in the constructor. + * This means that it may happen that IQMakeEnv instance is created and destroyed immediately. + * + * See IQMakeEnv2 interface if you want to get an explicit notification when IQMakeEnv gets really used. In that case, the instance initialization + * needs to be done in init method completely - not in the constructor. */ public interface IQMakeEnv { /** * Notifies that this environment is no longer used. + * This method should not use any workspace-lock or sync-locks that might call QMake-related structures. */ void destroy(); diff --git a/qt/org.eclipse.cdt.qt.core/src/org/eclipse/cdt/qt/core/index/IQMakeEnv2.java b/qt/org.eclipse.cdt.qt.core/src/org/eclipse/cdt/qt/core/index/IQMakeEnv2.java new file mode 100644 index 00000000000..9dc8839141c --- /dev/null +++ b/qt/org.eclipse.cdt.qt.core/src/org/eclipse/cdt/qt/core/index/IQMakeEnv2.java @@ -0,0 +1,26 @@ +/* + * Copyright (c) 2014 QNX Software Systems 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 + */ +package org.eclipse.cdt.qt.core.index; + +/** + * Represents a QMake environment similarly to IQMakeEnv but it has an explicit init method which is called to notify + * that the IQMakeEnv is used for active listening on change of QMakeEnvInfo and therefore it should initialize + * its listener for such changes. + * + * Note that it is expected that IQMakeEnv2 does complete initialization in init method - not in the constructor + * i.e. the IQMakeEnv2 instance should start listening on possible changes of its IQMakeEnvInfo. + */ +public interface IQMakeEnv2 extends IQMakeEnv { + + /** + * Notifies that this IQMakeEnv is used. + * This method should not use any workspace-lock or sync-locks that might call QMake-related structures. + */ + void init(); + +} diff --git a/qt/org.eclipse.cdt.qt.core/src/org/eclipse/cdt/qt/core/index/IQMakeEnvProvider.java b/qt/org.eclipse.cdt.qt.core/src/org/eclipse/cdt/qt/core/index/IQMakeEnvProvider.java index 2dd5053240e..deb851665d4 100644 --- a/qt/org.eclipse.cdt.qt.core/src/org/eclipse/cdt/qt/core/index/IQMakeEnvProvider.java +++ b/qt/org.eclipse.cdt.qt.core/src/org/eclipse/cdt/qt/core/index/IQMakeEnvProvider.java @@ -41,6 +41,8 @@ public interface IQMakeEnvProvider { /** * Request the controller to schedule a new qmake run to retrieve new QMake information. * This method should be called when there is any change in IQMakeEnv that might affect resulting IQMakeEnvInfo. + * + * Note that calculation of new QMakeInfo is done immediately if this controller is still active and used. */ void scheduleUpdate(); diff --git a/qt/org.eclipse.cdt.qt.core/src/org/eclipse/cdt/qt/core/index/QMakeProjectInfoFactory.java b/qt/org.eclipse.cdt.qt.core/src/org/eclipse/cdt/qt/core/index/QMakeProjectInfoFactory.java index 758b7135ff3..4337a6baed3 100644 --- a/qt/org.eclipse.cdt.qt.core/src/org/eclipse/cdt/qt/core/index/QMakeProjectInfoFactory.java +++ b/qt/org.eclipse.cdt.qt.core/src/org/eclipse/cdt/qt/core/index/QMakeProjectInfoFactory.java @@ -7,7 +7,7 @@ */ package org.eclipse.cdt.qt.core.index; -import org.eclipse.cdt.internal.qt.core.index.QMakeProjectInfo; +import org.eclipse.cdt.internal.qt.core.index.QMakeProjectInfoManager; import org.eclipse.core.resources.IProject; /** @@ -27,7 +27,7 @@ public final class QMakeProjectInfoFactory { * in the specified project. */ public static IQMakeProjectInfo getForActiveConfigurationIn(IProject project) { - return QMakeProjectInfo.getQMakeProjectInfoFor(project); + return QMakeProjectInfoManager.getQMakeProjectInfoFor(project); } } |