Browse Source
git-svn-id: svn://svn.sharpdevelop.net/sharpdevelop/trunk@3253 1ccf3a8d-04fe-1044-b7c0-cef0b8235c61shortcuts
11 changed files with 360 additions and 20 deletions
@ -0,0 +1,252 @@
@@ -0,0 +1,252 @@
|
||||
// <file>
|
||||
// <copyright see="prj:///doc/copyright.txt"/>
|
||||
// <license see="prj:///doc/license.txt"/>
|
||||
// <owner name="Daniel Grunwald"/>
|
||||
// <version>$Revision$</version>
|
||||
// </file>
|
||||
|
||||
using System; |
||||
using System.Collections.Generic; |
||||
using System.Linq; |
||||
using ICSharpCode.Core; |
||||
using System.Diagnostics; |
||||
using System.ComponentModel; |
||||
|
||||
namespace ICSharpCode.SharpDevelop.Project |
||||
{ |
||||
public enum BuildOnExecuteSetting |
||||
{ |
||||
// TODO: translate
|
||||
[Description("Do not build")] |
||||
DoNotBuild, |
||||
[Description("Build modified projects only")] |
||||
BuildOnlyModified, |
||||
[Description("Build modified projects and projects depending on them")] |
||||
BuildModifiedAndDependent, |
||||
[Description("Build all projects")] |
||||
RegularBuild |
||||
} |
||||
|
||||
/// <summary>
|
||||
/// Tracks changes to projects and causes only modified projects
|
||||
/// to be recompiled.
|
||||
/// </summary>
|
||||
static class BuildModifiedProjectsOnlyService |
||||
{ |
||||
public static BuildOnExecuteSetting Setting { |
||||
get { return PropertyService.Get("BuildOnExecute", BuildOnExecuteSetting.RegularBuild); } |
||||
set { PropertyService.Set("BuildOnExecute", value); } |
||||
} |
||||
|
||||
static readonly HashSet<IProject> unmodifiedProjects = new HashSet<IProject>(); |
||||
|
||||
static BuildModifiedProjectsOnlyService() |
||||
{ |
||||
// these actions cause a full recompilation:
|
||||
ProjectService.SolutionClosed += MarkAllForRecompilation; |
||||
ProjectService.SolutionConfigurationChanged += MarkAllForRecompilation; |
||||
ProjectService.SolutionSaved += MarkAllForRecompilation; |
||||
ProjectService.EndBuild += ProjectService_EndBuild; |
||||
|
||||
FileUtility.FileSaved += OnFileSaved; |
||||
} |
||||
|
||||
public static void Initialize() |
||||
{ |
||||
// first call to init causes static ctor calls
|
||||
} |
||||
|
||||
static void ProjectService_EndBuild(object sender, BuildEventArgs e) |
||||
{ |
||||
// at the end of an successful build, mark all projects as unmodified
|
||||
if (e.Results.Result == BuildResultCode.Success) { |
||||
if (ProjectService.OpenSolution != null) { |
||||
lock (unmodifiedProjects) { |
||||
unmodifiedProjects.AddRange(ProjectService.OpenSolution.Projects); |
||||
} |
||||
} |
||||
} |
||||
} |
||||
|
||||
static void MarkAllForRecompilation(object sender, EventArgs e) |
||||
{ |
||||
lock (unmodifiedProjects) { |
||||
unmodifiedProjects.Clear(); |
||||
} |
||||
} |
||||
|
||||
static void OnFileSaved(object sender, FileNameEventArgs e) |
||||
{ |
||||
if (ProjectService.OpenSolution != null) { |
||||
foreach (IProject p in ProjectService.OpenSolution.Projects) { |
||||
if (p.FindFile(e.FileName) != null) { |
||||
lock (unmodifiedProjects) { |
||||
unmodifiedProjects.Remove(p); |
||||
} |
||||
} |
||||
} |
||||
} |
||||
} |
||||
|
||||
public static IBuildable WrapBuildable(IBuildable buildable) |
||||
{ |
||||
switch (Setting) { |
||||
case BuildOnExecuteSetting.DoNotBuild: |
||||
return new DummyBuildable(buildable); |
||||
case BuildOnExecuteSetting.BuildModifiedAndDependent: |
||||
case BuildOnExecuteSetting.BuildOnlyModified: |
||||
return new WrapperFactory().GetWrapper(buildable); |
||||
case BuildOnExecuteSetting.RegularBuild: |
||||
return buildable; |
||||
default: |
||||
throw new NotSupportedException(); |
||||
} |
||||
} |
||||
|
||||
sealed class DummyBuildable : IBuildable |
||||
{ |
||||
IBuildable wrappedBuildable; |
||||
|
||||
public DummyBuildable(IBuildable wrappedBuildable) |
||||
{ |
||||
this.wrappedBuildable = wrappedBuildable; |
||||
} |
||||
|
||||
public string Name { |
||||
get { return wrappedBuildable.Name; } |
||||
} |
||||
|
||||
public Solution ParentSolution { |
||||
get { return wrappedBuildable.ParentSolution; } |
||||
} |
||||
|
||||
public ICollection<IBuildable> GetBuildDependencies(ProjectBuildOptions buildOptions) |
||||
{ |
||||
return new IBuildable[0]; |
||||
} |
||||
|
||||
public void StartBuild(ProjectBuildOptions buildOptions, IBuildFeedbackSink feedbackSink) |
||||
{ |
||||
} |
||||
} |
||||
|
||||
sealed class WrapperFactory |
||||
{ |
||||
readonly Dictionary<IBuildable, IBuildable> dict = new Dictionary<IBuildable, IBuildable>(); |
||||
|
||||
public IBuildable GetWrapper(IBuildable wrapped) |
||||
{ |
||||
IBuildable b; |
||||
lock (dict) { |
||||
if (!dict.TryGetValue(wrapped, out b)) |
||||
b = dict[wrapped] = new Wrapper(wrapped, this); |
||||
} |
||||
return b; |
||||
} |
||||
} |
||||
|
||||
sealed class Wrapper : IBuildable |
||||
{ |
||||
IBuildable wrapped; |
||||
WrapperFactory factory; |
||||
|
||||
public Wrapper(IBuildable wrapped, WrapperFactory factory) |
||||
{ |
||||
this.wrapped = wrapped; |
||||
this.factory = factory; |
||||
} |
||||
|
||||
public string Name { |
||||
get { return wrapped.Name; } |
||||
} |
||||
|
||||
public Solution ParentSolution { |
||||
get { return wrapped.ParentSolution; } |
||||
} |
||||
|
||||
Dictionary<ProjectBuildOptions, ICollection<IBuildable>> cachedBuildDependencies = new Dictionary<ProjectBuildOptions, ICollection<IBuildable>>(); |
||||
|
||||
public ICollection<IBuildable> GetBuildDependencies(ProjectBuildOptions buildOptions) |
||||
{ |
||||
List<IBuildable> result = new List<IBuildable>(); |
||||
foreach (IBuildable b in wrapped.GetBuildDependencies(buildOptions)) { |
||||
result.Add(factory.GetWrapper(b)); |
||||
} |
||||
lock (cachedBuildDependencies) { |
||||
cachedBuildDependencies[buildOptions] = result; |
||||
} |
||||
return result; |
||||
} |
||||
|
||||
internal bool wasRecompiled; |
||||
|
||||
public void StartBuild(ProjectBuildOptions buildOptions, IBuildFeedbackSink feedbackSink) |
||||
{ |
||||
IProject p = wrapped as IProject; |
||||
if (p == null) { |
||||
wrapped.StartBuild(buildOptions, feedbackSink); |
||||
} else { |
||||
bool isUnmodified; |
||||
lock (unmodifiedProjects) { |
||||
isUnmodified = unmodifiedProjects.Contains(p); |
||||
// mark project as unmodified
|
||||
unmodifiedProjects.Add(p); |
||||
} |
||||
if (isUnmodified && Setting == BuildOnExecuteSetting.BuildModifiedAndDependent) { |
||||
lock (cachedBuildDependencies) { |
||||
if (cachedBuildDependencies[buildOptions].OfType<Wrapper>().Any(w=>w.wasRecompiled)) { |
||||
isUnmodified = false; |
||||
} |
||||
} |
||||
} |
||||
if (isUnmodified) { |
||||
feedbackSink.ReportMessage("Skipped " + p.Name + " (no changes inside SharpDevelop)"); |
||||
feedbackSink.Done(true); |
||||
} else { |
||||
wasRecompiled = true; |
||||
wrapped.StartBuild(buildOptions, new BuildFeedbackSink(p, feedbackSink)); |
||||
} |
||||
} |
||||
} |
||||
|
||||
/// <summary>
|
||||
/// Wraps a build feedback sink and marks a project as requiring recompilation when
|
||||
/// compilation was not successful.
|
||||
/// </summary>
|
||||
sealed class BuildFeedbackSink : IBuildFeedbackSink |
||||
{ |
||||
IProject project; |
||||
IBuildFeedbackSink sink; |
||||
|
||||
public BuildFeedbackSink(IProject p, IBuildFeedbackSink sink) |
||||
{ |
||||
Debug.Assert(p != null); |
||||
Debug.Assert(sink != null); |
||||
this.project = p; |
||||
this.sink = sink; |
||||
} |
||||
|
||||
public void ReportError(BuildError error) |
||||
{ |
||||
sink.ReportError(error); |
||||
} |
||||
|
||||
public void ReportMessage(string message) |
||||
{ |
||||
sink.ReportMessage(message); |
||||
} |
||||
|
||||
public void Done(bool success) |
||||
{ |
||||
if (!success) { |
||||
// force recompilation if there was a build error
|
||||
lock (unmodifiedProjects) { |
||||
unmodifiedProjects.Remove(project); |
||||
} |
||||
} |
||||
sink.Done(success); |
||||
} |
||||
} |
||||
} |
||||
} |
||||
} |
||||
Loading…
Reference in new issue