Browse Source

Fixed bug in CompileModifiedProjectsOnly that could cause projects to not recompile even if they were changed.

git-svn-id: svn://svn.sharpdevelop.net/sharpdevelop/branches/3.0@3622 1ccf3a8d-04fe-1044-b7c0-cef0b8235c61
shortcuts
Daniel Grunwald 18 years ago
parent
commit
994ffb1812
  1. 1
      src/Main/Base/Project/ICSharpCode.SharpDevelop.csproj
  2. 1
      src/Main/Base/Project/Src/Project/BuildEngine.cs
  3. 32
      src/Main/Base/Project/Src/Project/BuildResults.cs
  4. 91
      src/Main/Base/Project/Src/Services/ProjectService/CompileModifiedProjectsOnly.cs
  5. 3
      src/Main/Base/Project/Src/Services/StatusBar/StatusBarService.cs
  6. 194
      src/Main/Base/Project/Src/Util/HashSet.cs

1
src/Main/Base/Project/ICSharpCode.SharpDevelop.csproj

@ -648,7 +648,6 @@ @@ -648,7 +648,6 @@
<Compile Include="Src\Gui\Dialogs\ReferenceDialog\AsyncDiscoveryState.cs" />
<Compile Include="Src\Gui\Dialogs\ReferenceDialog\DiscoveryNetworkCredential.cs" />
<Compile Include="Src\Services\ProjectService\ProjectLoader.cs" />
<Compile Include="Src\Util\HashSet.cs" />
<Compile Include="Src\Util\NativeMethods.cs" />
<Compile Include="Src\Util\ProcessRunnerException.cs" />
<Compile Include="Src\Util\LineReceivedEventArgs.cs" />

1
src/Main/Base/Project/Src/Project/BuildEngine.cs

@ -468,6 +468,7 @@ namespace ICSharpCode.SharpDevelop.Project @@ -468,6 +468,7 @@ namespace ICSharpCode.SharpDevelop.Project
node.hasErrors = !success;
projectsCurrentlyBuilding.Remove(node);
results.AddBuiltProject(node.project);
if (progressMonitor != null) {
progressMonitor.WorkDone += 1;
}

32
src/Main/Base/Project/Src/Project/BuildResults.cs

@ -33,6 +33,10 @@ namespace ICSharpCode.SharpDevelop.Project @@ -33,6 +33,10 @@ namespace ICSharpCode.SharpDevelop.Project
{
List<BuildError> errors = new List<BuildError>();
ReadOnlyCollection<BuildError> readOnlyErrors;
List<IBuildable> builtProjects = new List<IBuildable>();
ReadOnlyCollection<IBuildable> readOnlyBuiltProjects;
BuildResultCode result;
int errorCount, warningCount;
@ -54,6 +58,20 @@ namespace ICSharpCode.SharpDevelop.Project @@ -54,6 +58,20 @@ namespace ICSharpCode.SharpDevelop.Project
}
}
/// <summary>
/// Adds a project to the list of built projects.
/// This method is thread-sage.
/// </summary>
public void AddBuiltProject(IBuildable buildable)
{
if (buildable == null)
throw new ArgumentNullException("buildable");
lock (builtProjects) {
readOnlyBuiltProjects = null;
builtProjects.Add(buildable);
}
}
/// <summary>
/// Gets the list of build errors or warnings.
/// This property is thread-safe.
@ -69,6 +87,20 @@ namespace ICSharpCode.SharpDevelop.Project @@ -69,6 +87,20 @@ namespace ICSharpCode.SharpDevelop.Project
}
}
/// <summary>
/// Gets the list of projects that were built. This property is thread-safe.
/// </summary>
public ReadOnlyCollection<IBuildable> BuiltProjects {
get {
lock (builtProjects) {
if (readOnlyBuiltProjects == null) {
readOnlyBuiltProjects = Array.AsReadOnly(builtProjects.ToArray());
}
return readOnlyBuiltProjects;
}
}
}
public BuildResultCode Result {
get { return result; }
set { result = value; }

91
src/Main/Base/Project/Src/Services/ProjectService/CompileModifiedProjectsOnly.cs

@ -37,7 +37,7 @@ namespace ICSharpCode.SharpDevelop.Project @@ -37,7 +37,7 @@ namespace ICSharpCode.SharpDevelop.Project
set { PropertyService.Set("BuildOnExecute", value); }
}
static readonly HashSet<IProject> unmodifiedProjects = new HashSet<IProject>();
static readonly Dictionary<IProject, CompilationPass> unmodifiedProjects = new Dictionary<IProject, CompilationPass>();
static BuildModifiedProjectsOnlyService()
{
@ -57,16 +57,27 @@ namespace ICSharpCode.SharpDevelop.Project @@ -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 @@ -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 @@ -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<IBuildable, IBuildable> dict = new Dictionary<IBuildable, IBuildable>();
public IBuildable GetWrapper(IBuildable wrapped)
@ -146,8 +180,8 @@ namespace ICSharpCode.SharpDevelop.Project @@ -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 @@ -177,7 +211,19 @@ namespace ICSharpCode.SharpDevelop.Project
return result;
}
internal bool wasRecompiled;
CompilationPass lastCompilationPass;
/// <summary>
/// Returns true if "this" was recompiled after "comparisonPass".
/// </summary>
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 @@ -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<Wrapper>().Any(w=>w.wasRecompiled)) {
isUnmodified = false;
if (cachedBuildDependencies[buildOptions].OfType<Wrapper>().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 @@ -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 @@ -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);

3
src/Main/Base/Project/Src/Services/StatusBar/StatusBarService.cs

@ -6,8 +6,9 @@ @@ -6,8 +6,9 @@
// </file>
using System;
using System.Linq;
using System.Collections.Generic;
using System.Drawing;
using System.Linq;
using System.Windows.Forms;
using ICSharpCode.Core;

194
src/Main/Base/Project/Src/Util/HashSet.cs

@ -1,194 +0,0 @@ @@ -1,194 +0,0 @@
// <file>
// <copyright see="prj:///doc/copyright.txt"/>
// <license see="prj:///doc/license.txt"/>
// <owner name="Daniel Grunwald" email="daniel@danielgrunwald.de"/>
// <version>$Revision$</version>
// </file>
using System;
using System.Collections;
using System.Collections.Generic;
using System.Collections.Specialized;
namespace ICSharpCode.SharpDevelop
{
/// <summary>
/// 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.
/// </summary>
[System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Naming", "CA1710:IdentifiersShouldHaveCorrectSuffix")]
public sealed class HashSet<T> : ICollection<T>, ICollection, ICloneable
where T : class
{
Dictionary<T, object> _dict;
bool _copyOnWrite;
/// <summary>
/// Creates a new, empty set.
/// </summary>
public HashSet()
{
_dict = new Dictionary<T, object>();
}
/// <summary>
/// Creates a copy of the existing set.
/// </summary>
public HashSet(HashSet<T> existingSet)
{
existingSet._copyOnWrite = true;
this._copyOnWrite = true;
_dict = existingSet._dict;
}
/// <summary>
/// Adds the item to the set.
/// Trying to add <c>null</c> will return false without changing the collection.
/// </summary>
/// <returns>True when the item was added, false when it was not added because it already is in the set</returns>
public bool Add(T item)
{
if (item == null)
return false;
if (_dict.ContainsKey(item)) {
return false;
} else {
CopyIfRequired();
_dict.Add(item, null);
return true;
}
}
/// <summary>
/// Adds a list of items to the set. This is equivalent to calling <see cref="Add"/> for each item in <paramref name="items"/>.
/// </summary>
public void AddRange(IEnumerable<T> items)
{
foreach (T item in items) {
Add(item);
}
}
private void CopyIfRequired()
{
if (_copyOnWrite) {
_copyOnWrite = false;
_dict = new Dictionary<T, object>(_dict);
}
}
/// <summary>
/// Removes all items from the set.
/// </summary>
public void Clear()
{
_dict.Clear();
}
/// <summary>
/// Tests if this set contains the specified item.
/// Checking for <c>null</c> always returns false.
/// </summary>
public bool Contains(T item)
{
if (item == null)
return false;
else
return _dict.ContainsKey(item);
}
/// <summary>
/// Gets the number of items in the collection.
/// </summary>
public int Count
{
get { return _dict.Count; }
}
/// <summary>
/// Removes an item from the set.
/// Trying to remove <c>null</c> will return false without changing the collection.
/// </summary>
public bool Remove(T item)
{
if (item == null)
return false;
CopyIfRequired();
if (_dict.Remove(item)) {
return true;
} else {
return false;
}
}
/// <summary>
/// Copy all items to the specified array.
/// </summary>
void ICollection<T>.CopyTo(T[] array, int arrayIndex)
{
_dict.Keys.CopyTo(array, arrayIndex);
}
void ICollection<T>.Add(T item)
{
this.Add(item);
}
bool ICollection<T>.IsReadOnly
{
get { return false; }
}
#region IEnumerable Members
/// <summary>
/// Gets an enumerator to enumerate the items in the set.
/// </summary>
public IEnumerator<T> 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
/// <summary>
/// Create a copy of this set.
/// </summary>
public HashSet<T> Clone()
{
return new HashSet<T>(this);
}
object ICloneable.Clone()
{
return this.Clone();
}
#endregion
}
}
Loading…
Cancel
Save