Browse Source

Add option to build only projects that were modified inside SharpDevelop to improve compilation time.

git-svn-id: svn://svn.sharpdevelop.net/sharpdevelop/trunk@3253 1ccf3a8d-04fe-1044-b7c0-cef0b8235c61
shortcuts
Daniel Grunwald 18 years ago
parent
commit
c49f7e8045
  1. 2
      src/AddIns/Misc/UnitTesting/Src/RunTestCommands.cs
  2. 1
      src/Main/Base/Project/ICSharpCode.SharpDevelop.csproj
  3. 20
      src/Main/Base/Project/Resources/ProjectAndSolutionOptionsPanel.xfrm
  4. 34
      src/Main/Base/Project/Src/Commands/BuildCommands.cs
  5. 2
      src/Main/Base/Project/Src/Commands/DebugCommands.cs
  6. 17
      src/Main/Base/Project/Src/Gui/Dialogs/OptionPanels/IDEOptions/ProjectAndSolutionOptionsPanel.cs
  7. 1
      src/Main/Base/Project/Src/Gui/WorkbenchSingleton.cs
  8. 26
      src/Main/Base/Project/Src/Project/IProject.cs
  9. 7
      src/Main/Base/Project/Src/Project/Solution/ISolutionFolder.cs
  10. 252
      src/Main/Base/Project/Src/Services/ProjectService/CompileModifiedProjectsOnly.cs
  11. 18
      src/Main/Base/Project/Src/Services/ProjectService/ProjectService.cs

2
src/AddIns/Misc/UnitTesting/Src/RunTestCommands.cs

@ -420,7 +420,7 @@ namespace ICSharpCode.UnitTesting @@ -420,7 +420,7 @@ namespace ICSharpCode.UnitTesting
/// </summary>
public override void AfterBuild()
{
ProjectService.RaiseEventEndBuild();
ProjectService.RaiseEventEndBuild(new BuildEventArgs(LastBuildResults));
}
}

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

@ -143,6 +143,7 @@ @@ -143,6 +143,7 @@
<Compile Include="Src\Internal\Doozers\IDialogPanelDescriptor.cs" />
<Compile Include="Src\Services\File\FileService.cs" />
<Compile Include="Src\Services\File\FileEventArgs.cs" />
<Compile Include="Src\Services\ProjectService\CompileModifiedProjectsOnly.cs" />
<Compile Include="Src\Services\RefactoringService\ExtractInterfaceOptions.cs" />
<Compile Include="Src\Services\Tasks\Task.cs" />
<Compile Include="Src\Services\Tasks\TaskService.cs" />

20
src/Main/Base/Project/Resources/ProjectAndSolutionOptionsPanel.xfrm

@ -49,10 +49,26 @@ @@ -49,10 +49,26 @@
<Name value="CreatedObject27" />
<Location value="8, 128" />
<Text value="${res:Dialog.Options.IDEOptions.ProjectAndSolutionOptions.BuildAndRunGroupBox}" />
<Size value="336, 105" />
<Size value="336, 145" />
<Anchor value="Top, Bottom, Left, Right" />
<TabIndex value="1" />
<Controls>
<System.Windows.Forms.ComboBox>
<Name value="onExecuteComboBox" />
<TabIndex value="15" />
<Location value="115, 92" />
<Anchor value="Top, Left, Right" />
<Size value="211, 21" />
<FormattingEnabled value="True" />
<DropDownStyle value="DropDownList" />
</System.Windows.Forms.ComboBox>
<System.Windows.Forms.Label>
<Name value="label2" />
<Location value="9, 95" />
<Text value="When running:" />
<Size value="100, 23" />
<TabIndex value="14" />
</System.Windows.Forms.Label>
<System.Windows.Forms.NumericUpDown>
<Name value="parallelBuildNumericUpDown" />
<Value value="1" />
@ -82,4 +98,4 @@ @@ -82,4 +98,4 @@
</System.Windows.Forms.GroupBox>
</Controls>
</System.Windows.Forms.UserControl>
</Components>
</Components>

34
src/Main/Base/Project/Src/Commands/BuildCommands.cs

@ -56,6 +56,7 @@ namespace ICSharpCode.SharpDevelop.Project.Commands @@ -56,6 +56,7 @@ namespace ICSharpCode.SharpDevelop.Project.Commands
public BuildResults LastBuildResults {
get { return lastBuildResults; }
protected set { lastBuildResults = value; }
}
protected void CallbackMethod(BuildResults results)
@ -63,15 +64,19 @@ namespace ICSharpCode.SharpDevelop.Project.Commands @@ -63,15 +64,19 @@ namespace ICSharpCode.SharpDevelop.Project.Commands
lastBuildResults = results;
ShowResults(results);
AfterBuild();
if (BuildComplete != null)
BuildComplete(this, EventArgs.Empty);
OnBuildComplete(EventArgs.Empty);
}
public abstract void StartBuild();
public event EventHandler BuildComplete;
protected virtual void OnBuildComplete(EventArgs e)
{
if (BuildComplete != null) {
BuildComplete(this, e);
}
}
public static void ShowResults(BuildResults results)
{
@ -116,11 +121,30 @@ namespace ICSharpCode.SharpDevelop.Project.Commands @@ -116,11 +121,30 @@ namespace ICSharpCode.SharpDevelop.Project.Commands
public override void AfterBuild()
{
ProjectService.RaiseEventEndBuild();
ProjectService.RaiseEventEndBuild(new BuildEventArgs(LastBuildResults));
base.AfterBuild();
}
}
public class BuildBeforeExecute : Build
{
public override void Run()
{
if (BuildModifiedProjectsOnlyService.Setting == BuildOnExecuteSetting.DoNotBuild) {
LastBuildResults = new BuildResults { Result = BuildResultCode.Success };
OnBuildComplete(EventArgs.Empty);
} else {
base.Run();
}
}
public override void StartBuild()
{
BuildEngine.BuildInGui(BuildModifiedProjectsOnlyService.WrapBuildable(ProjectService.OpenSolution),
new BuildOptions(BuildTarget.Build, CallbackMethod));
}
}
public class Rebuild : Build
{
public override void StartBuild()
@ -175,7 +199,7 @@ namespace ICSharpCode.SharpDevelop.Project.Commands @@ -175,7 +199,7 @@ namespace ICSharpCode.SharpDevelop.Project.Commands
public override void AfterBuild()
{
ProjectService.RaiseEventEndBuild();
ProjectService.RaiseEventEndBuild(new BuildEventArgs(LastBuildResults));
base.AfterBuild();
}
}

2
src/Main/Base/Project/Src/Commands/DebugCommands.cs

@ -21,7 +21,7 @@ namespace ICSharpCode.SharpDevelop.Project.Commands @@ -21,7 +21,7 @@ namespace ICSharpCode.SharpDevelop.Project.Commands
public override void Run()
{
Build build = new Build();
Build build = new BuildBeforeExecute();
build.BuildComplete += delegate {
if (build.LastBuildResults.ErrorCount == 0) {
IProject startupProject = ProjectService.OpenSolution.StartupProject;

17
src/Main/Base/Project/Src/Gui/Dialogs/OptionPanels/IDEOptions/ProjectAndSolutionOptionsPanel.cs

@ -6,6 +6,7 @@ @@ -6,6 +6,7 @@
// </file>
using System;
using System.ComponentModel;
using System.IO;
using System.Windows.Forms;
@ -29,6 +30,20 @@ namespace ICSharpCode.SharpDevelop.Gui.OptionPanels @@ -29,6 +30,20 @@ namespace ICSharpCode.SharpDevelop.Gui.OptionPanels
((NumericUpDown)ControlDictionary["parallelBuildNumericUpDown"]).Value = Project.BuildOptions.DefaultParallelProjectCount;
((Button)ControlDictionary["selectProjectLocationButton"]).Click += new EventHandler(SelectProjectLocationButtonClicked);
ComboBox onExecuteComboBox = Get<ComboBox>("onExecute");
Type type = typeof(Project.BuildOnExecuteSetting);
foreach (Project.BuildOnExecuteSetting element in Enum.GetValues(type)) {
object[] attr = type.GetField(Enum.GetName(type, element)).GetCustomAttributes(typeof(DescriptionAttribute), false);
string description;
if (attr.Length > 0) {
description = StringParser.Parse((attr[0] as DescriptionAttribute).Description);
} else {
description = Enum.GetName(type, element);
}
onExecuteComboBox.Items.Add(description);
}
onExecuteComboBox.SelectedIndex = (int)Project.BuildModifiedProjectsOnlyService.Setting;
}
public override bool StorePanelContents()
@ -48,6 +63,8 @@ namespace ICSharpCode.SharpDevelop.Gui.OptionPanels @@ -48,6 +63,8 @@ namespace ICSharpCode.SharpDevelop.Gui.OptionPanels
Project.BuildOptions.ShowErrorListAfterBuild = ((CheckBox)ControlDictionary["showErrorListCheckBox"]).Checked;
Project.BuildOptions.DefaultParallelProjectCount = (int)((NumericUpDown)ControlDictionary["parallelBuildNumericUpDown"]).Value;
Project.BuildModifiedProjectsOnlyService.Setting = (Project.BuildOnExecuteSetting)Get<ComboBox>("onExecute").SelectedIndex;
return true;
}

1
src/Main/Base/Project/Src/Gui/WorkbenchSingleton.cs

@ -99,6 +99,7 @@ namespace ICSharpCode.SharpDevelop.Gui @@ -99,6 +99,7 @@ namespace ICSharpCode.SharpDevelop.Gui
ParserService.InitializeParserService();
Bookmarks.BookmarkManager.Initialize();
Project.CustomToolsService.Initialize();
Project.BuildModifiedProjectsOnlyService.Initialize();
MessageService.MainForm = workbench.MainForm;

26
src/Main/Base/Project/Src/Project/IProject.cs

@ -82,6 +82,19 @@ namespace ICSharpCode.SharpDevelop.Project @@ -82,6 +82,19 @@ namespace ICSharpCode.SharpDevelop.Project
get;
set;
}
/// <summary>
/// Gets/Sets the name of the project.
/// </summary>
/// <remarks>
/// Name already exists in ISolutionFolder, it's repeated here to prevent
/// the ambiguity with IBuildable.Name.
/// </remarks>
new string Name {
get;
set;
}
/// <summary>
/// Gets the directory of the project file.
/// This is equivalent to Path.GetDirectoryName(project.FileName);
@ -220,8 +233,9 @@ namespace ICSharpCode.SharpDevelop.Project @@ -220,8 +233,9 @@ namespace ICSharpCode.SharpDevelop.Project
/// <summary>
/// A project or solution.
/// The IBuildable interface members are thread-safe.
/// </summary>
public interface IBuildable : ISolutionFolder
public interface IBuildable
{
/// <summary>
/// Gets the list of projects on which this project depends.
@ -233,6 +247,16 @@ namespace ICSharpCode.SharpDevelop.Project @@ -233,6 +247,16 @@ namespace ICSharpCode.SharpDevelop.Project
/// This member must be implemented thread-safe.
/// </summary>
void StartBuild(ProjectBuildOptions buildOptions, IBuildFeedbackSink feedbackSink);
/// <summary>
/// Gets the name of the buildable item.
/// </summary>
string Name { get; }
/// <summary>
/// Gets the parent solution.
/// </summary>
Solution ParentSolution { get; }
}
/// <summary>

7
src/Main/Base/Project/Src/Project/Solution/ISolutionFolder.cs

@ -33,13 +33,6 @@ namespace ICSharpCode.SharpDevelop.Project @@ -33,13 +33,6 @@ namespace ICSharpCode.SharpDevelop.Project
set;
}
/// <summary>
/// Gets the solution the solution folder/project belongs to. This member is thread-safe.
/// </summary>
Solution ParentSolution {
get;
}
string TypeGuid {
get;
set;

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

@ -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);
}
}
}
}
}

18
src/Main/Base/Project/Src/Services/ProjectService/ProjectService.cs

@ -546,17 +546,19 @@ namespace ICSharpCode.SharpDevelop.Project @@ -546,17 +546,19 @@ namespace ICSharpCode.SharpDevelop.Project
public static void RaiseEventStartBuild()
{
WorkbenchSingleton.AssertMainThread();
building = true;
if (StartBuild != null) {
StartBuild(null, EventArgs.Empty);
}
}
public static void RaiseEventEndBuild()
public static void RaiseEventEndBuild(BuildEventArgs e)
{
WorkbenchSingleton.AssertMainThread();
building = false;
if (EndBuild != null) {
EndBuild(null, EventArgs.Empty);
EndBuild(null, e);
}
}
@ -623,7 +625,7 @@ namespace ICSharpCode.SharpDevelop.Project @@ -623,7 +625,7 @@ namespace ICSharpCode.SharpDevelop.Project
public static event SolutionFolderEventHandler SolutionFolderRemoved;
public static event EventHandler StartBuild;
public static event EventHandler EndBuild;
public static event EventHandler<BuildEventArgs> EndBuild;
public static event ProjectConfigurationEventHandler ProjectConfigurationChanged;
public static event SolutionConfigurationEventHandler SolutionConfigurationChanged;
@ -648,5 +650,15 @@ namespace ICSharpCode.SharpDevelop.Project @@ -648,5 +650,15 @@ namespace ICSharpCode.SharpDevelop.Project
public static event EventHandler<ProjectItemEventArgs> ProjectItemAdded;
public static event EventHandler<ProjectItemEventArgs> ProjectItemRemoved;
}
public class BuildEventArgs : EventArgs
{
public readonly BuildResults Results;
public BuildEventArgs(BuildResults results)
{
this.Results = results;
}
}
}

Loading…
Cancel
Save