diff --git a/src/Main/Base/Project/ICSharpCode.SharpDevelop.csproj b/src/Main/Base/Project/ICSharpCode.SharpDevelop.csproj
index 00cebf1b63..6530ecf132 100644
--- a/src/Main/Base/Project/ICSharpCode.SharpDevelop.csproj
+++ b/src/Main/Base/Project/ICSharpCode.SharpDevelop.csproj
@@ -648,7 +648,6 @@
-
diff --git a/src/Main/Base/Project/Src/Project/BuildEngine.cs b/src/Main/Base/Project/Src/Project/BuildEngine.cs
index c9e5359bb6..8c77acc14a 100644
--- a/src/Main/Base/Project/Src/Project/BuildEngine.cs
+++ b/src/Main/Base/Project/Src/Project/BuildEngine.cs
@@ -468,6 +468,7 @@ namespace ICSharpCode.SharpDevelop.Project
node.hasErrors = !success;
projectsCurrentlyBuilding.Remove(node);
+ results.AddBuiltProject(node.project);
if (progressMonitor != null) {
progressMonitor.WorkDone += 1;
}
diff --git a/src/Main/Base/Project/Src/Project/BuildResults.cs b/src/Main/Base/Project/Src/Project/BuildResults.cs
index 3a025eed2a..c2e67edc10 100644
--- a/src/Main/Base/Project/Src/Project/BuildResults.cs
+++ b/src/Main/Base/Project/Src/Project/BuildResults.cs
@@ -33,6 +33,10 @@ namespace ICSharpCode.SharpDevelop.Project
{
List errors = new List();
ReadOnlyCollection readOnlyErrors;
+
+ List builtProjects = new List();
+ ReadOnlyCollection readOnlyBuiltProjects;
+
BuildResultCode result;
int errorCount, warningCount;
@@ -54,6 +58,20 @@ namespace ICSharpCode.SharpDevelop.Project
}
}
+ ///
+ /// Adds a project to the list of built projects.
+ /// This method is thread-sage.
+ ///
+ public void AddBuiltProject(IBuildable buildable)
+ {
+ if (buildable == null)
+ throw new ArgumentNullException("buildable");
+ lock (builtProjects) {
+ readOnlyBuiltProjects = null;
+ builtProjects.Add(buildable);
+ }
+ }
+
///
/// Gets the list of build errors or warnings.
/// This property is thread-safe.
@@ -69,6 +87,20 @@ namespace ICSharpCode.SharpDevelop.Project
}
}
+ ///
+ /// Gets the list of projects that were built. This property is thread-safe.
+ ///
+ public ReadOnlyCollection BuiltProjects {
+ get {
+ lock (builtProjects) {
+ if (readOnlyBuiltProjects == null) {
+ readOnlyBuiltProjects = Array.AsReadOnly(builtProjects.ToArray());
+ }
+ return readOnlyBuiltProjects;
+ }
+ }
+ }
+
public BuildResultCode Result {
get { return result; }
set { result = value; }
diff --git a/src/Main/Base/Project/Src/Services/ProjectService/CompileModifiedProjectsOnly.cs b/src/Main/Base/Project/Src/Services/ProjectService/CompileModifiedProjectsOnly.cs
index 53e00287b4..0ec673f40e 100644
--- a/src/Main/Base/Project/Src/Services/ProjectService/CompileModifiedProjectsOnly.cs
+++ b/src/Main/Base/Project/Src/Services/ProjectService/CompileModifiedProjectsOnly.cs
@@ -37,7 +37,7 @@ namespace ICSharpCode.SharpDevelop.Project
set { PropertyService.Set("BuildOnExecute", value); }
}
- static readonly HashSet unmodifiedProjects = new HashSet();
+ static readonly Dictionary unmodifiedProjects = new Dictionary();
static BuildModifiedProjectsOnlyService()
{
@@ -57,16 +57,27 @@ namespace ICSharpCode.SharpDevelop.Project
static void ProjectService_EndBuild(object sender, BuildEventArgs e)
{
- // at the end of an successful build, mark all projects as unmodified
+ // at the end of an successful build, mark all built projects as unmodified
if (e.Results.Result == BuildResultCode.Success) {
- if (ProjectService.OpenSolution != null) {
- lock (unmodifiedProjects) {
- unmodifiedProjects.AddRange(ProjectService.OpenSolution.Projects);
+ lock (unmodifiedProjects) {
+ CompilationPass pass = new CompilationPass();
+ foreach (IBuildable b in e.Results.BuiltProjects) {
+ IProject p = GetProjectFromBuildable(b);
+ if (p != null) {
+ unmodifiedProjects[p] = pass;
+ }
}
}
}
}
+ static IProject GetProjectFromBuildable(IBuildable b)
+ {
+ while (b is Wrapper)
+ b = ((Wrapper)b).wrapped;
+ return b as IProject;
+ }
+
static void MarkAllForRecompilation(object sender, EventArgs e)
{
lock (unmodifiedProjects) {
@@ -94,6 +105,11 @@ namespace ICSharpCode.SharpDevelop.Project
return new DummyBuildable(buildable);
case BuildOnExecuteSetting.BuildModifiedAndDependent:
case BuildOnExecuteSetting.BuildOnlyModified:
+ lock (unmodifiedProjects) {
+ foreach (var pair in unmodifiedProjects) {
+ LoggingService.Debug(pair.Key.Name + ": " + pair.Value);
+ }
+ }
return new WrapperFactory().GetWrapper(buildable);
case BuildOnExecuteSetting.RegularBuild:
return buildable;
@@ -129,8 +145,26 @@ namespace ICSharpCode.SharpDevelop.Project
}
}
+ sealed class CompilationPass
+ {
+ public readonly int Index;
+
+ static int nextIndex;
+
+ public CompilationPass()
+ {
+ Index = System.Threading.Interlocked.Increment(ref nextIndex);
+ }
+
+ public override string ToString()
+ {
+ return "[CompilationPass " + Index + "]";
+ }
+ }
+
sealed class WrapperFactory
{
+ public readonly CompilationPass CurrentPass = new CompilationPass();
readonly Dictionary dict = new Dictionary();
public IBuildable GetWrapper(IBuildable wrapped)
@@ -146,8 +180,8 @@ namespace ICSharpCode.SharpDevelop.Project
sealed class Wrapper : IBuildable
{
- IBuildable wrapped;
- WrapperFactory factory;
+ internal readonly IBuildable wrapped;
+ internal readonly WrapperFactory factory;
public Wrapper(IBuildable wrapped, WrapperFactory factory)
{
@@ -177,7 +211,19 @@ namespace ICSharpCode.SharpDevelop.Project
return result;
}
- internal bool wasRecompiled;
+ CompilationPass lastCompilationPass;
+
+ ///
+ /// Returns true if "this" was recompiled after "comparisonPass".
+ ///
+ internal bool WasRecompiledAfter(CompilationPass comparisonPass)
+ {
+ Debug.Assert(comparisonPass != null);
+
+ if (lastCompilationPass == null)
+ return true;
+ return lastCompilationPass.Index > comparisonPass.Index;
+ }
public void StartBuild(ProjectBuildOptions buildOptions, IBuildFeedbackSink feedbackSink)
{
@@ -185,28 +231,27 @@ namespace ICSharpCode.SharpDevelop.Project
if (p == null) {
wrapped.StartBuild(buildOptions, feedbackSink);
} else {
- bool isUnmodified;
lock (unmodifiedProjects) {
- isUnmodified = unmodifiedProjects.Contains(p);
- // mark project as unmodified
- unmodifiedProjects.Add(p);
+ if (!unmodifiedProjects.TryGetValue(p, out lastCompilationPass)) {
+ lastCompilationPass = null;
+ }
}
- if (isUnmodified && Setting == BuildOnExecuteSetting.BuildModifiedAndDependent) {
+ if (lastCompilationPass != null && Setting == BuildOnExecuteSetting.BuildModifiedAndDependent) {
lock (cachedBuildDependencies) {
- if (cachedBuildDependencies[buildOptions].OfType().Any(w=>w.wasRecompiled)) {
- isUnmodified = false;
+ if (cachedBuildDependencies[buildOptions].OfType().Any(w=>w.WasRecompiledAfter(lastCompilationPass))) {
+ lastCompilationPass = null;
}
}
}
- if (isUnmodified) {
+ if (lastCompilationPass != null) {
feedbackSink.ReportMessage(
StringParser.Parse("${res:MainWindow.CompilerMessages.SkipProjectNoChanges}",
new string[,] {{ "Name", p.Name }})
);
feedbackSink.Done(true);
} else {
- wasRecompiled = true;
- wrapped.StartBuild(buildOptions, new BuildFeedbackSink(p, feedbackSink));
+ lastCompilationPass = factory.CurrentPass;
+ wrapped.StartBuild(buildOptions, new BuildFeedbackSink(p, feedbackSink, factory.CurrentPass));
}
}
}
@@ -219,13 +264,16 @@ namespace ICSharpCode.SharpDevelop.Project
{
IProject project;
IBuildFeedbackSink sink;
+ CompilationPass currentPass;
- public BuildFeedbackSink(IProject p, IBuildFeedbackSink sink)
+ public BuildFeedbackSink(IProject p, IBuildFeedbackSink sink, CompilationPass currentPass)
{
Debug.Assert(p != null);
Debug.Assert(sink != null);
+ Debug.Assert(currentPass != null);
this.project = p;
this.sink = sink;
+ this.currentPass = currentPass;
}
public void ReportError(BuildError error)
@@ -240,10 +288,9 @@ namespace ICSharpCode.SharpDevelop.Project
public void Done(bool success)
{
- if (!success) {
- // force recompilation if there was a build error
+ if (success) {
lock (unmodifiedProjects) {
- unmodifiedProjects.Remove(project);
+ unmodifiedProjects[project] = currentPass;
}
}
sink.Done(success);
diff --git a/src/Main/Base/Project/Src/Services/StatusBar/StatusBarService.cs b/src/Main/Base/Project/Src/Services/StatusBar/StatusBarService.cs
index d90037c5ce..4d8bf20649 100644
--- a/src/Main/Base/Project/Src/Services/StatusBar/StatusBarService.cs
+++ b/src/Main/Base/Project/Src/Services/StatusBar/StatusBarService.cs
@@ -6,8 +6,9 @@
//
using System;
-using System.Linq;
+using System.Collections.Generic;
using System.Drawing;
+using System.Linq;
using System.Windows.Forms;
using ICSharpCode.Core;
diff --git a/src/Main/Base/Project/Src/Util/HashSet.cs b/src/Main/Base/Project/Src/Util/HashSet.cs
deleted file mode 100644
index 8d25f9bc45..0000000000
--- a/src/Main/Base/Project/Src/Util/HashSet.cs
+++ /dev/null
@@ -1,194 +0,0 @@
-//
-//
-//
-//
-// $Revision$
-//
-
-using System;
-using System.Collections;
-using System.Collections.Generic;
-using System.Collections.Specialized;
-
-namespace ICSharpCode.SharpDevelop
-{
- ///
- /// Represents a set of items. The set does not preserve the order of items and does not allow items to
- /// be added twice.
- /// It is cloned by sharing the underlying data structure and delaying the actual copy until the next change.
- ///
- [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Naming", "CA1710:IdentifiersShouldHaveCorrectSuffix")]
- public sealed class HashSet : ICollection, ICollection, ICloneable
- where T : class
- {
- Dictionary _dict;
- bool _copyOnWrite;
-
- ///
- /// Creates a new, empty set.
- ///
- public HashSet()
- {
- _dict = new Dictionary();
- }
-
- ///
- /// Creates a copy of the existing set.
- ///
- public HashSet(HashSet existingSet)
- {
- existingSet._copyOnWrite = true;
- this._copyOnWrite = true;
- _dict = existingSet._dict;
- }
-
- ///
- /// Adds the item to the set.
- /// Trying to add null will return false without changing the collection.
- ///
- /// True when the item was added, false when it was not added because it already is in the set
- public bool Add(T item)
- {
- if (item == null)
- return false;
- if (_dict.ContainsKey(item)) {
- return false;
- } else {
- CopyIfRequired();
- _dict.Add(item, null);
- return true;
- }
- }
-
- ///
- /// Adds a list of items to the set. This is equivalent to calling for each item in .
- ///
- public void AddRange(IEnumerable items)
- {
- foreach (T item in items) {
- Add(item);
- }
- }
-
- private void CopyIfRequired()
- {
- if (_copyOnWrite) {
- _copyOnWrite = false;
- _dict = new Dictionary(_dict);
- }
- }
-
- ///
- /// Removes all items from the set.
- ///
- public void Clear()
- {
- _dict.Clear();
- }
-
- ///
- /// Tests if this set contains the specified item.
- /// Checking for null always returns false.
- ///
- public bool Contains(T item)
- {
- if (item == null)
- return false;
- else
- return _dict.ContainsKey(item);
- }
-
- ///
- /// Gets the number of items in the collection.
- ///
- public int Count
- {
- get { return _dict.Count; }
- }
-
- ///
- /// Removes an item from the set.
- /// Trying to remove null will return false without changing the collection.
- ///
- public bool Remove(T item)
- {
- if (item == null)
- return false;
- CopyIfRequired();
- if (_dict.Remove(item)) {
- return true;
- } else {
- return false;
- }
- }
-
- ///
- /// Copy all items to the specified array.
- ///
- void ICollection.CopyTo(T[] array, int arrayIndex)
- {
- _dict.Keys.CopyTo(array, arrayIndex);
- }
-
- void ICollection.Add(T item)
- {
- this.Add(item);
- }
-
- bool ICollection.IsReadOnly
- {
- get { return false; }
- }
-
- #region IEnumerable Members
- ///
- /// Gets an enumerator to enumerate the items in the set.
- ///
- public IEnumerator GetEnumerator()
- {
- return _dict.Keys.GetEnumerator();
- }
-
- IEnumerator IEnumerable.GetEnumerator()
- {
- return _dict.Keys.GetEnumerator();
- }
- #endregion
-
- #region ICollection Members
-
- void ICollection.CopyTo(Array array, int index)
- {
- ((ICollection)_dict).CopyTo(array, index);
- }
-
- bool ICollection.IsSynchronized
- {
- get { return false; }
- }
-
- object ICollection.SyncRoot
- {
- get { return null; }
- }
-
- #endregion
-
- #region ICloneable Members
-
- ///
- /// Create a copy of this set.
- ///
- public HashSet Clone()
- {
- return new HashSet(this);
- }
-
- object ICloneable.Clone()
- {
- return this.Clone();
- }
-
- #endregion
- }
-}