Browse Source

Trying to reimplement building (now building using a separate ProjectCollection)

git-svn-id: svn://svn.sharpdevelop.net/sharpdevelop/branches/dotnet4@4287 1ccf3a8d-04fe-1044-b7c0-cef0b8235c61
shortcuts
Daniel Grunwald 17 years ago
parent
commit
8910f68d47
  1. 9
      src/AddIns/BackendBindings/CSharpBinding/Project/Src/Project/CSharpProject.cs
  2. 5
      src/AddIns/BackendBindings/VBNetBinding/Project/Src/Project/VBNetProject.cs
  3. 1
      src/Main/Base/Project/ICSharpCode.SharpDevelop.csproj
  4. 2
      src/Main/Base/Project/Src/Project/AbstractProject.cs
  5. 4
      src/Main/Base/Project/Src/Project/BuildEngine.cs
  6. 2
      src/Main/Base/Project/Src/Project/IProject.cs
  7. 23
      src/Main/Base/Project/Src/Project/MSBuildBasedProject.cs
  8. 21
      src/Main/Base/Project/Src/Project/MSBuildEngine/MSBuildEngine.cs
  9. 177
      src/Main/Base/Project/Src/Project/MSBuildEngine/ParallelMSBuildManager.cs
  10. 4
      src/Main/Base/Project/Src/Project/MSBuildFileProject.cs
  11. 31
      src/Main/Base/Project/Src/Project/MSBuildInternals.cs
  12. 2
      src/Main/Base/Project/Src/Project/Solution/Solution.cs
  13. 24
      src/Main/Base/Project/Src/Services/ParserService/ParserService.cs
  14. 8
      src/Main/Base/Project/Src/Services/ProjectService/CompileModifiedProjectsOnly.cs
  15. 61
      src/Main/Base/Project/Src/Util/ThreadSafeServiceProvider.cs

9
src/AddIns/BackendBindings/CSharpBinding/Project/Src/Project/CSharpProject.cs

@ -5,11 +5,11 @@
// <version>$Revision$</version> // <version>$Revision$</version>
// </file> // </file>
using ICSharpCode.SharpDevelop;
using System; using System;
using System.ComponentModel; using System.ComponentModel;
using System.Linq;
using System.IO; using System.IO;
using System.Linq;
using ICSharpCode.SharpDevelop.Dom; using ICSharpCode.SharpDevelop.Dom;
using ICSharpCode.SharpDevelop.Dom.CSharp; using ICSharpCode.SharpDevelop.Dom.CSharp;
using ICSharpCode.SharpDevelop.Internal.Templates; using ICSharpCode.SharpDevelop.Internal.Templates;
@ -76,16 +76,17 @@ namespace CSharpBinding
return base.GetDefaultItemType(fileName); return base.GetDefaultItemType(fileName);
} }
public override void StartBuild(ProjectBuildOptions options, IBuildFeedbackSink feedbackSink) public override void StartBuild(ThreadSafeServiceContainer buildServices, ProjectBuildOptions options, IBuildFeedbackSink feedbackSink)
{ {
if (this.MinimumSolutionVersion == Solution.SolutionVersionVS2005) { if (this.MinimumSolutionVersion == Solution.SolutionVersionVS2005) {
MSBuildEngine.StartBuild(this, MSBuildEngine.StartBuild(this,
buildServices,
options, options,
feedbackSink, feedbackSink,
MSBuildEngine.AdditionalTargetFiles.Concat( MSBuildEngine.AdditionalTargetFiles.Concat(
new [] { "$(SharpDevelopBinPath)/SharpDevelop.CheckMSBuild35Features.targets" })); new [] { "$(SharpDevelopBinPath)/SharpDevelop.CheckMSBuild35Features.targets" }));
} else { } else {
base.StartBuild(options, feedbackSink); base.StartBuild(buildServices, options, feedbackSink);
} }
} }

5
src/AddIns/BackendBindings/VBNetBinding/Project/Src/Project/VBNetProject.cs

@ -101,16 +101,17 @@ namespace VBNetBinding
return base.GetDefaultItemType(fileName); return base.GetDefaultItemType(fileName);
} }
public override void StartBuild(ProjectBuildOptions options, IBuildFeedbackSink feedbackSink) public override void StartBuild(ThreadSafeServiceContainer buildServices, ProjectBuildOptions options, IBuildFeedbackSink feedbackSink)
{ {
if (this.MinimumSolutionVersion == Solution.SolutionVersionVS2005) { if (this.MinimumSolutionVersion == Solution.SolutionVersionVS2005) {
MSBuildEngine.StartBuild(this, MSBuildEngine.StartBuild(this,
buildServices,
options, options,
feedbackSink, feedbackSink,
MSBuildEngine.AdditionalTargetFiles.Concat( MSBuildEngine.AdditionalTargetFiles.Concat(
new [] { "$(SharpDevelopBinPath)/SharpDevelop.CheckMSBuild35Features.targets" })); new [] { "$(SharpDevelopBinPath)/SharpDevelop.CheckMSBuild35Features.targets" }));
} else { } else {
base.StartBuild(options, feedbackSink); base.StartBuild(buildServices, options, feedbackSink);
} }
} }

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

@ -763,6 +763,7 @@
<Compile Include="Src\Project\BuildTarget.cs" /> <Compile Include="Src\Project\BuildTarget.cs" />
<Compile Include="Src\Util\GenericConverter.cs" /> <Compile Include="Src\Util\GenericConverter.cs" />
<Compile Include="Src\Internal\Templates\TemplateLoadException.cs" /> <Compile Include="Src\Internal\Templates\TemplateLoadException.cs" />
<Compile Include="Src\Util\ThreadSafeServiceProvider.cs" />
<Compile Include="Src\Util\UnclosableStream.cs" /> <Compile Include="Src\Util\UnclosableStream.cs" />
<Compile Include="Src\Util\WpfSynchronizeInvoke.cs" /> <Compile Include="Src\Util\WpfSynchronizeInvoke.cs" />
<Compile Include="Src\Util\WorkerThread.cs" /> <Compile Include="Src\Util\WorkerThread.cs" />

2
src/Main/Base/Project/Src/Project/AbstractProject.cs

@ -488,7 +488,7 @@ namespace ICSharpCode.SharpDevelop.Project
{ {
} }
public virtual void StartBuild(ProjectBuildOptions options, IBuildFeedbackSink feedbackSink) public virtual void StartBuild(ThreadSafeServiceContainer buildServices, ProjectBuildOptions options, IBuildFeedbackSink feedbackSink)
{ {
feedbackSink.ReportError(new BuildError { ErrorText = "Building project " + Name + " is not supported.", IsWarning = true }); feedbackSink.ReportError(new BuildError { ErrorText = "Building project " + Name + " is not supported.", IsWarning = true });
// we don't know how to build anything, report that we're done. // we don't know how to build anything, report that we're done.

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

@ -301,7 +301,7 @@ namespace ICSharpCode.SharpDevelop.Project
public void DoStartBuild(object state) public void DoStartBuild(object state)
{ {
project.StartBuild(options, this); project.StartBuild(engine.serviceContainer, options, this);
} }
public void ReportError(BuildError error) public void ReportError(BuildError error)
@ -328,6 +328,7 @@ namespace ICSharpCode.SharpDevelop.Project
BuildNode rootNode; BuildNode rootNode;
readonly IBuildable rootProject; readonly IBuildable rootProject;
readonly BuildResults results = new BuildResults(); readonly BuildResults results = new BuildResults();
readonly ThreadSafeServiceContainer serviceContainer = new ThreadSafeServiceContainer();
DateTime buildStart; DateTime buildStart;
readonly List<BuildNode> projectsCurrentlyBuilding = new List<BuildNode>(); readonly List<BuildNode> projectsCurrentlyBuilding = new List<BuildNode>();
@ -551,6 +552,7 @@ namespace ICSharpCode.SharpDevelop.Project
/// </summary> /// </summary>
void ReportDone() void ReportDone()
{ {
serviceContainer.Dispose();
if (combinedBuildFeedbackSink != null) { if (combinedBuildFeedbackSink != null) {
if (combinedBuildFeedbackSink is MessageViewSink) { if (combinedBuildFeedbackSink is MessageViewSink) {
// Special case GUI-builds so that they have more information available: // Special case GUI-builds so that they have more information available:

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

@ -272,7 +272,7 @@ namespace ICSharpCode.SharpDevelop.Project
/// Starts building the project using the specified options. /// Starts building the project using the specified options.
/// This member must be implemented thread-safe. /// This member must be implemented thread-safe.
/// </summary> /// </summary>
void StartBuild(ProjectBuildOptions buildOptions, IBuildFeedbackSink feedbackSink); void StartBuild(ThreadSafeServiceContainer buildServices, ProjectBuildOptions buildOptions, IBuildFeedbackSink feedbackSink);
/// <summary> /// <summary>
/// Gets the name of the buildable item. /// Gets the name of the buildable item.

23
src/Main/Base/Project/Src/Project/MSBuildBasedProject.cs

@ -36,6 +36,10 @@ namespace ICSharpCode.SharpDevelop.Project
/// </summary> /// </summary>
ProjectCollection projectCollection; ProjectCollection projectCollection;
internal ProjectCollection MSBuildProjectCollection {
get { return projectCollection; }
}
/// <summary> /// <summary>
/// The underlying MSBuild project. /// The underlying MSBuild project.
/// </summary> /// </summary>
@ -1004,23 +1008,10 @@ namespace ICSharpCode.SharpDevelop.Project
return result; return result;
} }
public override void StartBuild(ProjectBuildOptions options, IBuildFeedbackSink feedbackSink) public override void StartBuild(ThreadSafeServiceContainer buildServices, ProjectBuildOptions options, IBuildFeedbackSink feedbackSink)
{ {
MSBuildEngine.StartBuild(this, options, feedbackSink, MSBuildEngine.AdditionalTargetFiles); MSBuildEngine.StartBuild(this, buildServices, options, feedbackSink, MSBuildEngine.AdditionalTargetFiles);
} }
/*
internal static void RunMSBuild(Solution solution, IProject project,
string configuration, string platform, BuildOptions options)
{
WorkbenchSingleton.Workbench.GetPad(typeof(CompilerMessageView)).BringPadToFront();
oldMSBuildEngine engine = new oldMSBuildEngine();
engine.Configuration = configuration;
engine.Platform = platform;
engine.MessageView = TaskService.BuildMessageViewCategory;
engine.Run(solution, project, options);
}
*/
#endregion #endregion
#region Loading #region Loading
@ -1140,7 +1131,7 @@ namespace ICSharpCode.SharpDevelop.Project
lock (SyncRoot) { lock (SyncRoot) {
// we need the global lock - if the file is being renamed, // we need the global lock - if the file is being renamed,
// MSBuild will update the global project collection // MSBuild will update the global project collection
lock (MSBuildInternals.GlobalProjectCollectionLock) { lock (MSBuildInternals.SolutionProjectCollectionLock) {
projectFile.Save(fileName); projectFile.Save(fileName);
//bool userProjectDirty = userProjectFile.HasUnsavedChanges; //bool userProjectDirty = userProjectFile.HasUnsavedChanges;
string userFile = fileName + ".user"; string userFile = fileName + ".user";

21
src/Main/Base/Project/Src/Project/MSBuildEngine/MSBuildEngine.cs

@ -78,10 +78,12 @@ namespace ICSharpCode.SharpDevelop.Project
} }
public static void StartBuild(IProject project, ProjectBuildOptions options, IBuildFeedbackSink feedbackSink, IEnumerable<string> additionalTargetFiles) public static void StartBuild(IProject project, ThreadSafeServiceContainer serviceContainer, ProjectBuildOptions options, IBuildFeedbackSink feedbackSink, IEnumerable<string> additionalTargetFiles)
{ {
if (project == null) if (project == null)
throw new ArgumentNullException("project"); throw new ArgumentNullException("project");
if (serviceContainer == null)
throw new ArgumentNullException("serviceContainer");
if (options == null) if (options == null)
throw new ArgumentNullException("options"); throw new ArgumentNullException("options");
if (feedbackSink == null) if (feedbackSink == null)
@ -91,6 +93,7 @@ namespace ICSharpCode.SharpDevelop.Project
MSBuildEngine engine = new MSBuildEngine(project, options, feedbackSink); MSBuildEngine engine = new MSBuildEngine(project, options, feedbackSink);
engine.additionalTargetFiles = additionalTargetFiles; engine.additionalTargetFiles = additionalTargetFiles;
engine.serviceContainer = serviceContainer;
engine.StartBuild(); engine.StartBuild();
} }
@ -98,6 +101,7 @@ namespace ICSharpCode.SharpDevelop.Project
ProjectBuildOptions options; ProjectBuildOptions options;
IBuildFeedbackSink feedbackSink; IBuildFeedbackSink feedbackSink;
IEnumerable<string> additionalTargetFiles; IEnumerable<string> additionalTargetFiles;
ThreadSafeServiceContainer serviceContainer;
private MSBuildEngine(IProject project, ProjectBuildOptions options, IBuildFeedbackSink feedbackSink) private MSBuildEngine(IProject project, ProjectBuildOptions options, IBuildFeedbackSink feedbackSink)
{ {
@ -182,11 +186,18 @@ namespace ICSharpCode.SharpDevelop.Project
InterestingTasks.AddRange(MSBuildEngine.CompileTaskNames); InterestingTasks.AddRange(MSBuildEngine.CompileTaskNames);
/* // Get ParallelMSBuildManager (or create a new one if one doesn't exist already).
// The serviceContainer will automatically dispose it after the build has completed.
ParallelMSBuildManager manager = (ParallelMSBuildManager)serviceContainer.GetOrCreateService(
typeof(ParallelMSBuildManager),
delegate {
return new ParallelMSBuildManager(new MSBuild.ProjectCollection());
});
// Use a temporary project collection to prevent MSBuild from opening the element from the global collection // Use a temporary project collection to prevent MSBuild from opening the element from the global collection
// - we don't want to modify the ProjectRootElement opened as project because we don't want to save // - we don't want to modify the ProjectRootElement opened as project because we don't want to save
// back our changes to disk. // back our changes to disk.
ProjectRootElement projectFile = ProjectRootElement.Open(project.FileName, new MSBuild.ProjectCollection()); ProjectRootElement projectFile = ProjectRootElement.Open(project.FileName, manager.ProjectCollection);
foreach (string import in additionalTargetFiles) foreach (string import in additionalTargetFiles)
projectFile.AddImport(import); projectFile.AddImport(import);
@ -199,7 +210,7 @@ namespace ICSharpCode.SharpDevelop.Project
projectFile.AddTarget("_ComputeNonExistentFileProperty"); projectFile.AddTarget("_ComputeNonExistentFileProperty");
} }
ProjectInstance projectInstance = MSBuildInternals.LoadProjectInstance(projectFile, globalProperties); ProjectInstance projectInstance = MSBuildInternals.LoadProjectInstance(manager.ProjectCollection, projectFile, globalProperties);
string[] targets = { options.Target.TargetName }; string[] targets = { options.Target.TargetName };
BuildRequestData requestData = new BuildRequestData(projectInstance, targets, new HostServices()); BuildRequestData requestData = new BuildRequestData(projectInstance, targets, new HostServices());
@ -207,7 +218,7 @@ namespace ICSharpCode.SharpDevelop.Project
new SharpDevelopLogger(this), new SharpDevelopLogger(this),
new BuildLogFileLogger(projectFile.FullPath + ".log", LoggerVerbosity.Diagnostic) new BuildLogFileLogger(projectFile.FullPath + ".log", LoggerVerbosity.Diagnostic)
}; };
ParallelMSBuildManager.StartBuild(requestData, loggers, OnComplete);*/ manager.StartBuild(requestData, loggers, OnComplete);
} }
void OnComplete(BuildSubmission submission) void OnComplete(BuildSubmission submission)

177
src/Main/Base/Project/Src/Project/MSBuildEngine/ParallelMSBuildManager.cs

@ -7,11 +7,15 @@
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using ICSharpCode.Core; using ICSharpCode.Core;
using Microsoft.Build.Evaluation;
using Microsoft.Build.Execution; using Microsoft.Build.Execution;
using Microsoft.Build.Framework; using Microsoft.Build.Framework;
using Microsoft.Build.Logging; using Microsoft.Build.Logging;
using System.Linq; using System.Threading;
namespace ICSharpCode.SharpDevelop.Project namespace ICSharpCode.SharpDevelop.Project
{ {
@ -23,77 +27,114 @@ namespace ICSharpCode.SharpDevelop.Project
/// ///
/// This class allows to run multiple indepent builds concurrently. Logging events /// This class allows to run multiple indepent builds concurrently. Logging events
/// will be forwarded to the appropriate logger. /// will be forwarded to the appropriate logger.
///
/// Note: due to MSBuild limitations, all projects being built must be from the same ProjectCollection.
/// SharpDevelop simply uses the predefined ProjectCollection.GlobalProjectCollection.
/// Code accessing that collection (even if indirectly through MSBuild) should lock on
/// MSBuildInternals.GlobalProjectCollectionLock.
/// </summary> /// </summary>
public static class ParallelMSBuildManager public sealed class ParallelMSBuildManager : IDisposable
{ {
#region Manage StartBuild/EndBuild of BuildManager.DefaultBuildManager /// <summary>
static readonly object enableDisableLock = new object(); /// MSBuild only allows a single build to run at one time - so if multiple ParallelMSBuildManager
static bool buildIsRunning; /// are present, we synchronize them using this global lock.
static int enableCount; /// </summary>
static readonly object globalBuildEngineLock = new object();
static bool globalBuildEngineLockTaken;
static void EnterGlobalBuildEngineLock()
{
lock (globalBuildEngineLock) {
while (globalBuildEngineLockTaken)
Monitor.Wait(globalBuildEngineLock);
globalBuildEngineLockTaken = true;
}
}
static void LeaveGlobalBuildEngineLock()
{
lock (globalBuildEngineLock) {
Debug.Assert(globalBuildEngineLockTaken);
globalBuildEngineLockTaken = false;
Monitor.Pulse(globalBuildEngineLock);
}
}
public ProjectCollection ProjectCollection { get; private set; }
/// <summary> /// <summary>
/// Enables the underlying build engine. /// Creates a new ParallelMSBuildManager for the project collection.
/// Call this method only if you are requesting multiple builds in a series (e.g. building the solution). /// WARNING: only a single ParallelMSBuildManager can build at a time, others will wait.
/// A counter is increment to remember how many users of this class need the build engine. /// Ensure you dispose the ParallelMSBuildManager when you are done with it, otherwise all future
/// For each EnableBuildEngine() call, you need to call DisableBuildEngine() exactly once! /// ParallelMSBuildManagers will deadlock!
/// </summary> /// </summary>
/// <param name="beginBuild">Specifies whether the build engine should be started immediately. public ParallelMSBuildManager(ProjectCollection projectCollection)
/// If false (default), the build engine will start on the first StartBuild() call.</param> {
public static void EnableBuildEngine(bool beginBuild) if (projectCollection == null)
throw new ArgumentNullException("projectCollection");
this.ProjectCollection = projectCollection;
}
#region Manage StartBuild/EndBuild
readonly object enableDisableLock = new object();
bool buildIsRunning;
void StartupBuildEngine()
{ {
lock (enableDisableLock) { lock (enableDisableLock) {
if (!buildIsRunning && beginBuild) { if (buildIsRunning)
LoggingService.Info("ParallelMSBuildManager starting..."); return;
buildIsRunning = true; buildIsRunning = true;
BuildParameters parameters = new BuildParameters();
parameters.Loggers = new ILogger[] { LoggingService.Info("ParallelMSBuildManager: waiting for start permission...");
new CentralLogger(), EnterGlobalBuildEngineLock();
#if DEBUG
new ConsoleLogger(LoggerVerbosity.Normal), LoggingService.Info("ParallelMSBuildManager: got start permisson, starting...");
#endif BuildParameters parameters = new BuildParameters(this.ProjectCollection);
}; parameters.Loggers = new ILogger[] {
parameters.EnableNodeReuse = false; new CentralLogger(this),
// parallel build seems to break in-memory modifications of the project (additionalTargetFiles+_ComputeNonExistentFileProperty), #if DEBUG
// so we keep it disabled for the moment. new ConsoleLogger(LoggerVerbosity.Normal),
parameters.MaxNodeCount = 1; //BuildOptions.DefaultParallelProjectCount; #endif
BuildManager.DefaultBuildManager.BeginBuild(parameters); };
} parameters.EnableNodeReuse = false;
enableCount++; // parallel build seems to break in-memory modifications of the project (additionalTargetFiles+_ComputeNonExistentFileProperty),
// so we keep it disabled for the moment.
parameters.MaxNodeCount = BuildOptions.DefaultParallelProjectCount;
BuildManager.DefaultBuildManager.BeginBuild(parameters);
} }
} }
/// <summary> /// <summary>
/// Decrements the counter and disables the underlying build engine if the counter reaches 0. /// Shuts down the build engine and allows other managers to build.
/// </summary> /// </summary>
public static void DisableBuildEngine() public void Dispose()
{ {
lock (enableDisableLock) { lock (enableDisableLock) {
enableCount--; if (!buildIsRunning)
if (enableCount == 0 && buildIsRunning) { return;
buildIsRunning = false; buildIsRunning = false;
try {
LoggingService.Info("ParallelMSBuildManager shutting down...");
BuildManager.DefaultBuildManager.EndBuild(); BuildManager.DefaultBuildManager.EndBuild();
BuildManager.DefaultBuildManager.ResetCaches(); BuildManager.DefaultBuildManager.ResetCaches();
LoggingService.Info("ParallelMSBuildManager shut down!"); LoggingService.Info("ParallelMSBuildManager shut down!");
} finally {
LeaveGlobalBuildEngineLock();
} }
} }
} }
#endregion #endregion
static readonly Dictionary<int, EventSource> submissionEventSourceMapping = new Dictionary<int, EventSource>(); readonly Dictionary<int, EventSource> submissionEventSourceMapping = new Dictionary<int, EventSource>();
/// <summary> /// <summary>
/// Starts building. /// Starts building.
/// This method blocks if another ParallelMSBuildManager is currently building.
/// However, it doe
/// </summary> /// </summary>
/// <param name="requestData">The requested build.</param> /// <param name="requestData">The requested build.</param>
/// <param name="logger">The logger that received build output.</param> /// <param name="logger">The logger that received build output.</param>
/// <param name="callback">Callback that is run when the build is complete</param> /// <param name="callback">Callback that is run when the build is complete</param>
/// <returns>The build submission that was started.</returns> /// <returns>The build submission that was started.</returns>
public static BuildSubmission StartBuild(BuildRequestData requestData, IEnumerable<ILogger> loggers, BuildSubmissionCompleteCallback callback) public BuildSubmission StartBuild(BuildRequestData requestData, IEnumerable<ILogger> loggers, BuildSubmissionCompleteCallback callback)
{ {
if (requestData == null) if (requestData == null)
throw new ArgumentNullException("requestData"); throw new ArgumentNullException("requestData");
@ -102,35 +143,32 @@ namespace ICSharpCode.SharpDevelop.Project
loggersArray = new ILogger[0]; loggersArray = new ILogger[0];
else else
loggersArray = loggers.ToArray(); // iterate through logger enumerable once loggersArray = loggers.ToArray(); // iterate through logger enumerable once
EnableBuildEngine(true);
try { StartupBuildEngine();
BuildSubmission submission = BuildManager.DefaultBuildManager.PendBuildRequest(requestData); BuildSubmission submission = BuildManager.DefaultBuildManager.PendBuildRequest(requestData);
EventSource eventSource = new EventSource(); EventSource eventSource = new EventSource();
foreach (ILogger logger in loggersArray) { foreach (ILogger logger in loggersArray) {
if (logger != null) if (logger != null)
logger.Initialize(eventSource); logger.Initialize(eventSource);
}
lock (submissionEventSourceMapping) {
submissionEventSourceMapping.Add(submission.SubmissionId, eventSource);
}
RunningBuild build = new RunningBuild(eventSource, loggersArray, callback);
submission.ExecuteAsync(build.OnComplete, null);
return submission;
} catch (Exception ex) {
LoggingService.Warn("Got exception starting build (exception will be rethrown)", ex);
DisableBuildEngine();
throw;
} }
lock (submissionEventSourceMapping) {
submissionEventSourceMapping.Add(submission.SubmissionId, eventSource);
}
RunningBuild build = new RunningBuild(this, eventSource, loggersArray, callback);
submission.ExecuteAsync(build.OnComplete, null);
return submission;
} }
sealed class RunningBuild sealed class RunningBuild
{ {
ParallelMSBuildManager manager;
ILogger[] loggers; ILogger[] loggers;
BuildSubmissionCompleteCallback callback; BuildSubmissionCompleteCallback callback;
EventSource eventSource; EventSource eventSource;
public RunningBuild(EventSource eventSource, ILogger[] loggers, BuildSubmissionCompleteCallback callback) public RunningBuild(ParallelMSBuildManager manager, EventSource eventSource, ILogger[] loggers, BuildSubmissionCompleteCallback callback)
{ {
this.manager = manager;
this.eventSource = eventSource; this.eventSource = eventSource;
this.loggers = loggers; this.loggers = loggers;
this.callback = callback; this.callback = callback;
@ -138,10 +176,8 @@ namespace ICSharpCode.SharpDevelop.Project
internal void OnComplete(BuildSubmission submission) internal void OnComplete(BuildSubmission submission)
{ {
DisableBuildEngine(); lock (manager.submissionEventSourceMapping) {
manager.submissionEventSourceMapping.Remove(submission.SubmissionId);
lock (submissionEventSourceMapping) {
submissionEventSourceMapping.Remove(submission.SubmissionId);
} }
if (submission.BuildResult.Exception != null) { if (submission.BuildResult.Exception != null) {
LoggingService.Error(submission.BuildResult.Exception); LoggingService.Error(submission.BuildResult.Exception);
@ -158,6 +194,13 @@ namespace ICSharpCode.SharpDevelop.Project
sealed class CentralLogger : INodeLogger, IEventRedirector sealed class CentralLogger : INodeLogger, IEventRedirector
{ {
readonly ParallelMSBuildManager parentManager;
public CentralLogger(ParallelMSBuildManager parentManager)
{
this.parentManager = parentManager;
}
public void Initialize(IEventSource eventSource, int nodeCount) public void Initialize(IEventSource eventSource, int nodeCount)
{ {
Initialize(eventSource); Initialize(eventSource);
@ -188,8 +231,8 @@ namespace ICSharpCode.SharpDevelop.Project
} }
} }
EventSource redirector; EventSource redirector;
lock (submissionEventSourceMapping) { lock (parentManager.submissionEventSourceMapping) {
if (!submissionEventSourceMapping.TryGetValue(e.BuildEventContext.SubmissionId, out redirector)) { if (!parentManager.submissionEventSourceMapping.TryGetValue(e.BuildEventContext.SubmissionId, out redirector)) {
LoggingService.Warn("Could not deliver build event: " + e + ":\n" + e.Message); LoggingService.Warn("Could not deliver build event: " + e + ":\n" + e.Message);
} }
} }

4
src/Main/Base/Project/Src/Project/MSBuildFileProject.cs

@ -22,9 +22,9 @@ namespace ICSharpCode.SharpDevelop.Project
TypeGuid = "{00000000-0000-0000-0000-000000000000}"; TypeGuid = "{00000000-0000-0000-0000-000000000000}";
} }
public override void StartBuild(ProjectBuildOptions options, IBuildFeedbackSink feedbackSink) public override void StartBuild(ThreadSafeServiceContainer buildServices, ProjectBuildOptions options, IBuildFeedbackSink feedbackSink)
{ {
MSBuildEngine.StartBuild(this, options, feedbackSink, MSBuildEngine.AdditionalTargetFiles); MSBuildEngine.StartBuild(this, buildServices, options, feedbackSink, MSBuildEngine.AdditionalTargetFiles);
} }
} }
} }

31
src/Main/Base/Project/Src/Project/MSBuildInternals.cs

@ -25,23 +25,22 @@ namespace ICSharpCode.SharpDevelop.Project
public static class MSBuildInternals public static class MSBuildInternals
{ {
/// <summary> /// <summary>
/// Note: due to MSBuild limitations, all projects being built must be from the same ProjectCollection. /// SharpDevelop uses one project collection per solution.
/// SharpDevelop simply uses the predefined ProjectCollection.GlobalProjectCollection. /// Code accessing one of those collection (even if indirectly through MSBuild) should lock on
/// Code accessing that collection (even if indirectly through MSBuild) should lock on /// MSBuildInternals.SolutionProjectCollectionLock.
/// MSBuildInternals.GlobalProjectCollectionLock.
/// </summary> /// </summary>
public readonly static object GlobalProjectCollectionLock = new object(); public readonly static object SolutionProjectCollectionLock = new object();
internal static void UnloadProject(MSBuild.Evaluation.ProjectCollection projectCollection, MSBuild.Evaluation.Project project) internal static void UnloadProject(MSBuild.Evaluation.ProjectCollection projectCollection, MSBuild.Evaluation.Project project)
{ {
lock (GlobalProjectCollectionLock) { lock (SolutionProjectCollectionLock) {
projectCollection.UnloadProject(project); projectCollection.UnloadProject(project);
} }
} }
internal static MSBuild.Evaluation.Project LoadProject(MSBuild.Evaluation.ProjectCollection projectCollection, ProjectRootElement rootElement, IDictionary<string, string> globalProps) internal static MSBuild.Evaluation.Project LoadProject(MSBuild.Evaluation.ProjectCollection projectCollection, ProjectRootElement rootElement, IDictionary<string, string> globalProps)
{ {
lock (GlobalProjectCollectionLock) { lock (SolutionProjectCollectionLock) {
string toolsVersion = rootElement.ToolsVersion; string toolsVersion = rootElement.ToolsVersion;
if (string.IsNullOrEmpty(toolsVersion)) if (string.IsNullOrEmpty(toolsVersion))
toolsVersion = projectCollection.DefaultToolsVersion; toolsVersion = projectCollection.DefaultToolsVersion;
@ -51,7 +50,7 @@ namespace ICSharpCode.SharpDevelop.Project
internal static ProjectInstance LoadProjectInstance(MSBuild.Evaluation.ProjectCollection projectCollection, ProjectRootElement rootElement, IDictionary<string, string> globalProps) internal static ProjectInstance LoadProjectInstance(MSBuild.Evaluation.ProjectCollection projectCollection, ProjectRootElement rootElement, IDictionary<string, string> globalProps)
{ {
lock (GlobalProjectCollectionLock) { lock (SolutionProjectCollectionLock) {
string toolsVersion = rootElement.ToolsVersion; string toolsVersion = rootElement.ToolsVersion;
if (string.IsNullOrEmpty(toolsVersion)) if (string.IsNullOrEmpty(toolsVersion))
toolsVersion = projectCollection.DefaultToolsVersion; toolsVersion = projectCollection.DefaultToolsVersion;
@ -166,14 +165,16 @@ namespace ICSharpCode.SharpDevelop.Project
string[] targets = { "ResolveAssemblyReferences" }; string[] targets = { "ResolveAssemblyReferences" };
BuildRequestData requestData = new BuildRequestData(project, targets, new HostServices()); BuildRequestData requestData = new BuildRequestData(project, targets, new HostServices());
ILogger[] loggers = { new SimpleErrorLogger() }; ILogger[] loggers = { new SimpleErrorLogger() };
BuildSubmission submission = ParallelMSBuildManager.StartBuild(requestData, loggers, null);
LoggingService.Debug("Started build for ResolveAssemblyReferences");
submission.WaitHandle.WaitOne();
BuildResult result = submission.BuildResult;
if (result == null)
throw new InvalidOperationException("BuildResult is null");
LoggingService.Debug("Build for ResolveAssemblyReferences finished: " + result.OverallResult);
using (ParallelMSBuildManager buildManager = new ParallelMSBuildManager(baseProject.MSBuildProjectCollection)) {
BuildSubmission submission = buildManager.StartBuild(requestData, loggers, null);
LoggingService.Debug("Started build for ResolveAssemblyReferences");
submission.WaitHandle.WaitOne();
BuildResult result = submission.BuildResult;
if (result == null)
throw new InvalidOperationException("BuildResult is null");
LoggingService.Debug("Build for ResolveAssemblyReferences finished: " + result.OverallResult);
}
var referenceDict = new Dictionary<string, ReferenceProjectItem>(); var referenceDict = new Dictionary<string, ReferenceProjectItem>();
foreach (ReferenceProjectItem item in referenceProjectItems) { foreach (ReferenceProjectItem item in referenceProjectItems) {

2
src/Main/Base/Project/Src/Project/Solution/Solution.cs

@ -1178,7 +1178,7 @@ namespace ICSharpCode.SharpDevelop.Project
return result; return result;
} }
void IBuildable.StartBuild(ProjectBuildOptions buildOptions, IBuildFeedbackSink feedbackSink) void IBuildable.StartBuild(ThreadSafeServiceContainer buildServices, ProjectBuildOptions buildOptions, IBuildFeedbackSink feedbackSink)
{ {
// building a solution finishes immediately: we only care for the dependencies // building a solution finishes immediately: we only care for the dependencies
feedbackSink.Done(true); feedbackSink.Done(true);

24
src/Main/Base/Project/Src/Services/ParserService/ParserService.cs

@ -177,22 +177,18 @@ namespace ICSharpCode.SharpDevelop
// multiply Count with 2 so that the progress bar is only at 50% when references are done // multiply Count with 2 so that the progress bar is only at 50% when references are done
progressMonitor.BeginTask("Loading references...", createdContents.Count * 2, false); progressMonitor.BeginTask("Loading references...", createdContents.Count * 2, false);
ParallelMSBuildManager.EnableBuildEngine(false); for (int i = 0; i < createdContents.Count; i++) {
try { if (abortLoadSolutionProjectsThread) return;
for (int i = 0; i < createdContents.Count; i++) { ParseProjectContent newContent = createdContents[i];
if (abortLoadSolutionProjectsThread) return; progressMonitor.WorkDone = i;
ParseProjectContent newContent = createdContents[i]; try {
progressMonitor.WorkDone = i; newContent.Initialize1(progressMonitor);
try { workAmount += newContent.GetInitializationWorkAmount();
newContent.Initialize1(progressMonitor); } catch (Exception e) {
workAmount += newContent.GetInitializationWorkAmount(); MessageService.ShowError(e, "Error while initializing project references:" + newContent);
} catch (Exception e) {
MessageService.ShowError(e, "Error while initializing project references:" + newContent);
}
} }
} finally {
ParallelMSBuildManager.DisableBuildEngine();
} }
// multiply workamount with two and start at workAmount so that the progress bar continues // multiply workamount with two and start at workAmount so that the progress bar continues
// from 50% towards 100%. // from 50% towards 100%.
progressMonitor.BeginTask("${res:ICSharpCode.SharpDevelop.Internal.ParserService.Parsing}...", workAmount * 2, false); progressMonitor.BeginTask("${res:ICSharpCode.SharpDevelop.Internal.ParserService.Parsing}...", workAmount * 2, false);

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

@ -151,7 +151,7 @@ namespace ICSharpCode.SharpDevelop.Project
return new IBuildable[0]; return new IBuildable[0];
} }
public void StartBuild(ProjectBuildOptions buildOptions, IBuildFeedbackSink feedbackSink) public void StartBuild(ThreadSafeServiceContainer buildServices, ProjectBuildOptions buildOptions, IBuildFeedbackSink feedbackSink)
{ {
} }
} }
@ -245,11 +245,11 @@ namespace ICSharpCode.SharpDevelop.Project
return lastCompilationPass.Index > comparisonPass.Index; return lastCompilationPass.Index > comparisonPass.Index;
} }
public void StartBuild(ProjectBuildOptions buildOptions, IBuildFeedbackSink feedbackSink) public void StartBuild(ThreadSafeServiceContainer buildServices, ProjectBuildOptions buildOptions, IBuildFeedbackSink feedbackSink)
{ {
IProject p = wrapped as IProject; IProject p = wrapped as IProject;
if (p == null) { if (p == null) {
wrapped.StartBuild(buildOptions, feedbackSink); wrapped.StartBuild(buildServices, buildOptions, feedbackSink);
} else { } else {
lock (unmodifiedProjects) { lock (unmodifiedProjects) {
if (!unmodifiedProjects.TryGetValue(p, out lastCompilationPass)) { if (!unmodifiedProjects.TryGetValue(p, out lastCompilationPass)) {
@ -272,7 +272,7 @@ namespace ICSharpCode.SharpDevelop.Project
feedbackSink.Done(true); feedbackSink.Done(true);
} else { } else {
lastCompilationPass = factory.CurrentPass; lastCompilationPass = factory.CurrentPass;
wrapped.StartBuild(buildOptions, new BuildFeedbackSink(p, feedbackSink, factory.CurrentPass)); wrapped.StartBuild(buildServices, buildOptions, new BuildFeedbackSink(p, feedbackSink, factory.CurrentPass));
} }
} }
} }

61
src/Main/Base/Project/Src/Util/ThreadSafeServiceProvider.cs

@ -0,0 +1,61 @@
/*
* Created by SharpDevelop.
* User: Daniel
* Date: 12.06.2009
* Time: 22:28
*
* To change this template use Tools | Options | Coding | Edit Standard Headers.
*/
using System;
using System.Linq;
using System.Collections.Generic;
namespace ICSharpCode.SharpDevelop
{
/// <summary>
/// A thread-safe service container class.
/// </summary>
public class ThreadSafeServiceContainer : IServiceProvider, IDisposable
{
Dictionary<Type, object> services = new Dictionary<Type, object>();
public ThreadSafeServiceContainer()
{
services.Add(typeof(ThreadSafeServiceContainer), this);
}
public object GetOrCreateService(Type type, Func<object> serviceCreator)
{
lock (services) {
object instance;
if (!services.TryGetValue(type, out instance)) {
instance = serviceCreator();
services.Add(type, instance);
}
return instance;
}
}
public object GetService(Type type)
{
lock (services) {
object instance;
if (services.TryGetValue(type, out instance))
return instance;
else
return null;
}
}
public void Dispose()
{
IDisposable[] disposables;
lock (services) {
disposables = services.Values.OfType<IDisposable>().ToArray();
services.Clear();
}
foreach (IDisposable disposable in disposables)
disposable.Dispose();
}
}
}
Loading…
Cancel
Save