Bug 507795 - Functional dynamic dependencies
Add support for functional dynamic dependencies.
Change-Id: I38286e4bcb877613264cfc53cc75ff23d02483d9
Signed-off-by: Stefan Xenos <sxenos@gmail.com>
diff --git a/bundles/org.eclipse.core.resources/schema/builders.exsd b/bundles/org.eclipse.core.resources/schema/builders.exsd
index 8db1747..03a7a18 100644
--- a/bundles/org.eclipse.core.resources/schema/builders.exsd
+++ b/bundles/org.eclipse.core.resources/schema/builders.exsd
@@ -69,6 +69,7 @@
<element name="builder">
<complexType>
<sequence>
+ <element ref="dynamicReference" minOccurs="0" maxOccurs="1"/>
<element ref="run" minOccurs="0" maxOccurs="1"/>
</sequence>
<attribute name="hasNature" type="boolean">
@@ -154,6 +155,24 @@
</complexType>
</element>
+ <element name="dynamicReference">
+ <complexType>
+ <attribute name="class" type="string">
+ <annotation>
+ <documentation>
+ The fully-qualified name of a class that implements <samp>org.eclipse.core.resources.IDynamicReferenceProvider</samp>.
+ If supplied, the given class will asked to return a list of other projects that
+ must be built before this one when the build system is computing the build order.
+ </documentation>
+ <appInfo>
+ <meta.attribute kind="java" basedOn=":org.eclipse.core.resources.IDynamicReferenceProvider"/>
+ </appInfo>
+ </annotation>
+ </attribute>
+ </complexType>
+ </element>
+
+
<annotation>
<appInfo>
<meta.section type="examples"/>
@@ -188,7 +207,6 @@
</documentation>
</annotation>
-
<annotation>
<appInfo>
<meta.section type="implementation"/>
diff --git a/bundles/org.eclipse.core.resources/src/org/eclipse/core/internal/resources/Project.java b/bundles/org.eclipse.core.resources/src/org/eclipse/core/internal/resources/Project.java
index 89580954..0cc9d2d 100644
--- a/bundles/org.eclipse.core.resources/src/org/eclipse/core/internal/resources/Project.java
+++ b/bundles/org.eclipse.core.resources/src/org/eclipse/core/internal/resources/Project.java
@@ -470,6 +470,21 @@
}
@Override
+ public void clearCachedDynamicReferences() {
+ ResourceInfo info = getResourceInfo(false, false);
+ if (info == null) {
+ // If the project is not open there is no cached state and so there is nothing to do.
+ return;
+ }
+ ProjectDescription description = ((ProjectInfo) info).getDescription();
+ if (description == null) {
+ // If the project is in the process of being created there is no cached state and nothing to do.
+ return;
+ }
+ description.clearCachedDynamicReferences(null);
+ }
+
+ @Override
public IProject[] getReferencingProjects() {
IProject[] projects = workspace.getRoot().getProjects(IContainer.INCLUDE_HIDDEN);
List<IProject> result = new ArrayList<>(projects.length);
diff --git a/bundles/org.eclipse.core.resources/src/org/eclipse/core/internal/resources/ProjectDescription.java b/bundles/org.eclipse.core.resources/src/org/eclipse/core/internal/resources/ProjectDescription.java
index 7bbc9df..1af05fa 100644
--- a/bundles/org.eclipse.core.resources/src/org/eclipse/core/internal/resources/ProjectDescription.java
+++ b/bundles/org.eclipse.core.resources/src/org/eclipse/core/internal/resources/ProjectDescription.java
@@ -61,8 +61,21 @@
protected volatile IBuildConfiguration[] cachedBuildConfigs;
// Cached build configuration references. Not persisted.
protected Map<String, IBuildConfiguration[]> cachedConfigRefs = Collections.synchronizedMap(new HashMap<String, IBuildConfiguration[]>(1));
- // Cached project level references.
- protected volatile IProject[] cachedRefs = null;
+ /**
+ * Cached project level references. Synchronize on {@link #cachedRefsMutex} before reading or writing. Increment
+ * {@link #cachedRefsDirtyCount} whenever this is dirtied.
+ */
+ protected IProject[] cachedRefs;
+ /**
+ * Counts the number of times {@link #cachedRefs} has been dirtied. Can be used to determine if dynamic dependencies have
+ * changed during an operation that is intended to be atomic with respect to dynamic dependencies. Synchronize on
+ * {@link #cachedRefsMutex} before accessing.
+ */
+ protected int cachedRefsDirtyCount;
+ /**
+ * Mutex used to protect {@link #cachedRefs} and {@link #cachedRefsDirtyCount}.
+ */
+ protected final Object cachedRefsMutex = new Object();
/**
* Map of (IPath -> LinkDescription) pairs for each linked resource
@@ -103,7 +116,7 @@
clone.buildSpec = getBuildSpec(true);
clone.dynamicConfigRefs = (HashMap<String, IBuildConfiguration[]>) dynamicConfigRefs.clone();
clone.cachedConfigRefs = Collections.synchronizedMap(new HashMap<String, IBuildConfiguration[]>(1));
- clone.clearCachedReferences(null);
+ clone.clearCachedDynamicReferences(null);
return clone;
}
@@ -111,12 +124,15 @@
* Clear cached references for the specified build config name
* or all if configName is null.
*/
- private void clearCachedReferences(String configName) {
- if (configName == null)
- cachedConfigRefs.clear();
- else
- cachedConfigRefs.remove(configName);
- cachedRefs = null;
+ public void clearCachedDynamicReferences(String configName) {
+ synchronized (cachedRefsMutex) {
+ if (configName == null)
+ cachedConfigRefs.clear();
+ else
+ cachedConfigRefs.remove(configName);
+ cachedRefs = null;
+ cachedRefsDirtyCount++;
+ }
}
/**
@@ -189,8 +205,17 @@
* @see #getAllBuildConfigReferences(String, boolean)
*/
public IProject[] getAllReferences(boolean makeCopy) {
- IProject[] projRefs = cachedRefs;
- if (projRefs == null) {
+ int dirtyCount;
+ IProject[] projRefs;
+
+ synchronized (cachedRefsMutex) {
+ projRefs = cachedRefs;
+ dirtyCount = cachedRefsDirtyCount;
+ }
+ // Retry this computation until we're able to proceed to the end without someone dirtying the cache.
+ // This loop is here to prevent us from caching a stale result if someone dirties the cache between
+ // the time we invoke getAllBuildConfigReferences and the time we can write to cachedRefs.
+ while (projRefs == null) {
IBuildConfiguration[] refs;
if (hasBuildConfig(activeConfiguration))
refs = getAllBuildConfigReferences(activeConfiguration, false);
@@ -200,7 +225,16 @@
// No build configuration => fall-back to default
refs = getAllBuildConfigReferences(IBuildConfiguration.DEFAULT_CONFIG_NAME, false);
Collection<IProject> l = getProjectsFromBuildConfigRefs(refs);
- projRefs = cachedRefs = l.toArray(new IProject[l.size()]);
+
+ synchronized (cachedRefsMutex) {
+ // If nobody dirtied the cache since the start of this operation then we can cache the
+ // new result and end the loop.
+ if (cachedRefsDirtyCount == dirtyCount) {
+ cachedRefs = l.toArray(new IProject[l.size()]);
+ }
+ projRefs = cachedRefs;
+ dirtyCount = cachedRefsDirtyCount;
+ }
}
//still need to copy the result to prevent tampering with the cache
return makeCopy ? (IProject[]) projRefs.clone() : projRefs;
@@ -224,7 +258,15 @@
if (refs == null) {
Set<IBuildConfiguration> references = new LinkedHashSet<>();
IBuildConfiguration[] dynamicBuildConfigs = dynamicConfigRefs.containsKey(configName) ? dynamicConfigRefs.get(configName) : EMPTY_BUILD_CONFIG_REFERENCE_ARRAY;
- Collection<BuildConfiguration> dynamic = getBuildConfigReferencesFromProjects(dynamicRefs);
+ IProject project = ResourcesPlugin.getWorkspace().getRoot().getProject(getName());
+ Collection<BuildConfiguration> dynamic;
+ try {
+ IBuildConfiguration buildConfig = project.getBuildConfig(configName);
+ dynamic = getBuildConfigReferencesFromProjects(computeDynamicReferencesForProject(buildConfig, getBuildSpec()));
+ } catch (CoreException e) {
+ dynamic = Collections.emptyList();
+ }
+ Collection<BuildConfiguration> legacyDynamic = getBuildConfigReferencesFromProjects(dynamicRefs);
Collection<BuildConfiguration> statik = getBuildConfigReferencesFromProjects(staticRefs);
// Combine all references:
@@ -232,6 +274,7 @@
references.addAll(Arrays.asList(dynamicBuildConfigs));
// We preserve the previous order of static project references before dynamic project references
references.addAll(statik);
+ references.addAll(legacyDynamic);
references.addAll(dynamic);
refs = references.toArray(new IBuildConfiguration[references.size()]);
cachedConfigRefs.put(configName, refs);
@@ -539,7 +582,7 @@
public void setActiveBuildConfig(String configName) {
Assert.isNotNull(configName);
if (!configName.equals(activeConfiguration))
- clearCachedReferences(null);
+ clearCachedDynamicReferences(null);
activeConfiguration = configName;
}
@@ -567,16 +610,17 @@
comment = value;
}
+ @Deprecated
@Override
public void setDynamicReferences(IProject[] value) {
Assert.isLegal(value != null);
dynamicRefs = copyAndRemoveDuplicates(value);
- clearCachedReferences(null);
+ clearCachedDynamicReferences(null);
}
public void setBuildConfigReferences(HashMap<String, IBuildConfiguration[]> refs) {
dynamicConfigRefs = new HashMap<>(refs);
- clearCachedReferences(null);
+ clearCachedDynamicReferences(null);
}
@Override
@@ -586,7 +630,7 @@
if (!hasBuildConfig(configName))
return;
dynamicConfigRefs.put(configName, copyAndRemoveDuplicates(references));
- clearCachedReferences(configName);
+ clearCachedDynamicReferences(configName);
}
@Override
@@ -613,7 +657,7 @@
// Remove references for deleted buildConfigs
boolean modified = dynamicConfigRefs.keySet().retainAll(buildConfigNames);
if (modified)
- clearCachedReferences(null);
+ clearCachedDynamicReferences(null);
// Clear the cached IBuildConfiguration[]
cachedBuildConfigs = null;
}
@@ -813,7 +857,7 @@
public void setReferencedProjects(IProject[] value) {
Assert.isLegal(value != null);
staticRefs = copyAndRemoveDuplicates(value);
- clearCachedReferences(null);
+ clearCachedDynamicReferences(null);
}
/**
@@ -871,7 +915,46 @@
dynamicConfigRefs = new HashMap<>(description.dynamicConfigRefs);
}
if (changed)
- clearCachedReferences(null);
+ clearCachedDynamicReferences(null);
return changed;
}
+
+ /**
+ * Computes the dynamic references for the given project + configuration.
+ */
+ private static IProject[] computeDynamicReferencesForProject(IBuildConfiguration buildConfig, ICommand[] buildSpec) {
+ List<IProject> result = new ArrayList<>();
+ for (ICommand command : buildSpec) {
+ IExtension extension = Platform.getExtensionRegistry().getExtension(ResourcesPlugin.PI_RESOURCES, ResourcesPlugin.PT_BUILDERS, command.getBuilderName());
+
+ if (extension == null) {
+ continue;
+ }
+
+ IConfigurationElement[] configurationElements = extension.getConfigurationElements();
+
+ if (configurationElements.length == 0) {
+ continue;
+ }
+
+ IConfigurationElement element = configurationElements[0];
+
+ Object executableExtension;
+ try {
+ IConfigurationElement[] children = element.getChildren("dynamicReference"); //$NON-NLS-1$
+ if (children.length != 0) {
+ executableExtension = children[0].createExecutableExtension("class"); //$NON-NLS-1$
+ if (executableExtension instanceof IDynamicReferenceProvider) {
+ IDynamicReferenceProvider provider = (IDynamicReferenceProvider) executableExtension;
+
+ result.addAll(provider.getDependentProjects(buildConfig));
+ }
+ }
+ } catch (CoreException e) {
+ String problemElement = element.toString();
+ ResourcesPlugin.getPlugin().getLog().log(new Status(IStatus.ERROR, ResourcesPlugin.PI_RESOURCES, "Unable to load dynamic reference provider: " + problemElement, e)); //$NON-NLS-1$
+ }
+ }
+ return result.toArray(new IProject[0]);
+ }
}
diff --git a/bundles/org.eclipse.core.resources/src/org/eclipse/core/resources/IDynamicReferenceProvider.java b/bundles/org.eclipse.core.resources/src/org/eclipse/core/resources/IDynamicReferenceProvider.java
new file mode 100644
index 0000000..7392f8a
--- /dev/null
+++ b/bundles/org.eclipse.core.resources/src/org/eclipse/core/resources/IDynamicReferenceProvider.java
@@ -0,0 +1,34 @@
+/*******************************************************************************
+ * Copyright (c) 2016 IBM Corporation and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ * IBM Corporation - initial API and implementation
+ *******************************************************************************/
+package org.eclipse.core.resources;
+
+import java.util.List;
+import org.eclipse.core.runtime.CoreException;
+
+/**
+ * Implementations of this interface are capable of determining a set
+ * of projects which a given project depends upon. Unless otherwise stated,
+ * all arguments and return values are non-null.
+ *
+ * @since 3.12
+ */
+public interface IDynamicReferenceProvider {
+ /**
+ * Returns the set of projects which the given project depends upon. If the return
+ * value of a previous call to this method ever changes, it will fire an event to
+ * the listeners. This method my be invoked from any thread and may be invoked
+ * in parallel by multiple threads.
+ *
+ * @param buildConfiguration the build configuration being queried.
+ * @return the set of projects which the given projects depends upon.
+ */
+ public List<IProject> getDependentProjects(IBuildConfiguration buildConfiguration) throws CoreException;
+}
diff --git a/bundles/org.eclipse.core.resources/src/org/eclipse/core/resources/IProject.java b/bundles/org.eclipse.core.resources/src/org/eclipse/core/resources/IProject.java
index 9702a5b..0ea186a 100644
--- a/bundles/org.eclipse.core.resources/src/org/eclipse/core/resources/IProject.java
+++ b/bundles/org.eclipse.core.resources/src/org/eclipse/core/resources/IProject.java
@@ -101,7 +101,7 @@
* @see IncrementalProjectBuilder#CLEAN_BUILD
* @see IResourceRuleFactory#buildRule()
*/
- public void build(int kind, String builderName, Map<String,String> args, IProgressMonitor monitor) throws CoreException;
+ public void build(int kind, String builderName, Map<String, String> args, IProgressMonitor monitor) throws CoreException;
/**
* Builds this project. Does nothing if the project is closed.
@@ -611,6 +611,20 @@
public IProject[] getReferencedProjects() throws CoreException;
/**
+ * Clears the cache of dynamic project references for this project. Invoking this
+ * method will cause the dynamic project references to be recomputed the next time
+ * they are accessed (for example, in a call to {@link #getReferencedProjects()}.
+ * It is not necessary to hold the workspace lock when invoking this method. Plugins
+ * that provide an {@link IDynamicReferenceProvider} should invoke this method to
+ * inform the rest of the application when one or more dynamic project references
+ * may have changed. This will also clear any other cached data that is derived from
+ * the dynamic references.
+ *
+ * @since 3.12
+ */
+ public void clearCachedDynamicReferences();
+
+ /**
* Returns the list of all open projects which reference
* this project. This project may or may not exist. Returns
* an empty array if there are no referencing projects.
@@ -744,8 +758,7 @@
* @see #saveSnapshot(int, URI, IProgressMonitor)
* @since 3.6
*/
- public void loadSnapshot(int options, URI snapshotLocation,
- IProgressMonitor monitor) throws CoreException;
+ public void loadSnapshot(int options, URI snapshotLocation, IProgressMonitor monitor) throws CoreException;
/**
* Renames this project so that it is located at the name in
@@ -894,8 +907,7 @@
* @see #loadSnapshot(int, URI, IProgressMonitor)
* @since 3.6
*/
- public void saveSnapshot(int options, URI snapshotLocation,
- IProgressMonitor monitor) throws CoreException;
+ public void saveSnapshot(int options, URI snapshotLocation, IProgressMonitor monitor) throws CoreException;
/**
* Changes this project resource to match the given project
diff --git a/bundles/org.eclipse.core.resources/src/org/eclipse/core/resources/IProjectDescription.java b/bundles/org.eclipse.core.resources/src/org/eclipse/core/resources/IProjectDescription.java
index 8cb8f08..dbc4516 100644
--- a/bundles/org.eclipse.core.resources/src/org/eclipse/core/resources/IProjectDescription.java
+++ b/bundles/org.eclipse.core.resources/src/org/eclipse/core/resources/IProjectDescription.java
@@ -275,12 +275,14 @@
* Users must call {@link IProject#setDescription(IProjectDescription, int, IProgressMonitor)}
* before changes made to this description take effect.
* </p>
+ * @deprecated please use {@link IDynamicReferenceProvider} with the builders extension point to supply dynamic references
* @see #getDynamicReferences()
* @see #setBuildConfigReferences(String, IBuildConfiguration[])
* @see IProject#setDescription(IProjectDescription, int, IProgressMonitor)
* @param projects list of projects
* @since 3.0
*/
+ @Deprecated
public void setDynamicReferences(IProject[] projects);
/**