diff --git a/src/AddIns/BackendBindings/VBNetBinding/Project/Src/VbcEncodingFixingLogger.cs b/src/AddIns/BackendBindings/VBNetBinding/Project/Src/VbcEncodingFixingLogger.cs
index 472a50875d..9e8bee7c10 100644
--- a/src/AddIns/BackendBindings/VBNetBinding/Project/Src/VbcEncodingFixingLogger.cs
+++ b/src/AddIns/BackendBindings/VBNetBinding/Project/Src/VbcEncodingFixingLogger.cs
@@ -18,18 +18,18 @@ namespace VBNetBinding
///
public class VbcEncodingFixingLogger : IMSBuildAdditionalLogger
{
- public ILogger CreateLogger(MSBuildEngine engine)
+ public ILogger CreateLogger(MSBuildEngineWorker engineWorker)
{
- return new VbcLoggerImpl(engine);
+ return new VbcLoggerImpl(engineWorker);
}
private class VbcLoggerImpl : ILogger
{
- MSBuildEngine engine;
+ MSBuildEngineWorker engineWorker;
- public VbcLoggerImpl(MSBuildEngine engine)
+ public VbcLoggerImpl(MSBuildEngineWorker engineWorker)
{
- this.engine = engine;
+ this.engineWorker = engineWorker;
}
public LoggerVerbosity Verbosity {
@@ -80,8 +80,8 @@ namespace VBNetBinding
void FixMessage()
{
- engine.CurrentErrorOrWarning.ErrorText = FixEncoding(engine.CurrentErrorOrWarning.ErrorText);
- engine.CurrentErrorOrWarning.FileName = FixEncoding(engine.CurrentErrorOrWarning.FileName);
+ engineWorker.CurrentErrorOrWarning.ErrorText = FixEncoding(engineWorker.CurrentErrorOrWarning.ErrorText);
+ engineWorker.CurrentErrorOrWarning.FileName = FixEncoding(engineWorker.CurrentErrorOrWarning.FileName);
}
static string FixEncoding(string encoding)
diff --git a/src/AddIns/Misc/CodeAnalysis/Src/AnalysisProjectOptions.cs b/src/AddIns/Misc/CodeAnalysis/Src/AnalysisProjectOptions.cs
index e7549c6aa3..3caa44489e 100644
--- a/src/AddIns/Misc/CodeAnalysis/Src/AnalysisProjectOptions.cs
+++ b/src/AddIns/Misc/CodeAnalysis/Src/AnalysisProjectOptions.cs
@@ -295,6 +295,7 @@ namespace ICSharpCode.CodeAnalysis
public ConfigBinding(AnalysisProjectOptions po)
{
this.po = po;
+ this.TreatPropertyValueAsLiteral = false;
po.OptionChanged += delegate {
Helper.IsDirty = true;
};
diff --git a/src/AddIns/Misc/CodeAnalysis/Src/FxCopLogger.cs b/src/AddIns/Misc/CodeAnalysis/Src/FxCopLogger.cs
index 3cb671c2d7..44e553075f 100644
--- a/src/AddIns/Misc/CodeAnalysis/Src/FxCopLogger.cs
+++ b/src/AddIns/Misc/CodeAnalysis/Src/FxCopLogger.cs
@@ -17,18 +17,18 @@ namespace ICSharpCode.CodeAnalysis
{
public class FxCopLogger : IMSBuildAdditionalLogger
{
- public ILogger CreateLogger(MSBuildEngine engine)
+ public ILogger CreateLogger(MSBuildEngineWorker engineWorker)
{
- return new FxCopLoggerImpl(engine);
+ return new FxCopLoggerImpl(engineWorker);
}
private class FxCopLoggerImpl : ILogger
{
- MSBuildEngine engine;
+ MSBuildEngineWorker engineWorker;
- public FxCopLoggerImpl(MSBuildEngine engine)
+ public FxCopLoggerImpl(MSBuildEngineWorker engineWorker)
{
- this.engine = engine;
+ this.engineWorker = engineWorker;
}
public LoggerVerbosity Verbosity {
@@ -54,7 +54,7 @@ namespace ICSharpCode.CodeAnalysis
public void Initialize(IEventSource eventSource)
{
this.eventSource = eventSource;
- engine.MessageView.AppendText("${res:ICSharpCode.CodeAnalysis.RunningFxCopOn} " + Path.GetFileNameWithoutExtension(engine.CurrentProjectFile) + "\r\n");
+ engineWorker.OutputText("${res:ICSharpCode.CodeAnalysis.RunningFxCopOn} " + Path.GetFileNameWithoutExtension(engineWorker.CurrentProjectFile) + "\r\n");
eventSource.ErrorRaised += OnError;
eventSource.WarningRaised += OnWarning;
}
@@ -85,12 +85,12 @@ namespace ICSharpCode.CodeAnalysis
string category, string checkId, string subcategory)
{
string[] moreData = (subcategory ?? "").Split('|');
- BuildError err = engine.CurrentErrorOrWarning;
+ BuildError err = engineWorker.CurrentErrorOrWarning;
if (FileUtility.IsValidFileName(file) &&
Path.GetFileName(file) == "SharpDevelop.CodeAnalysis.targets") {
err.FileName = null;
}
- IProject project = ProjectService.GetProject(engine.CurrentProjectFile);
+ IProject project = ProjectService.GetProject(engineWorker.CurrentProjectFile);
if (project != null) {
IProjectContent pc = ParserService.GetProjectContent(project);
if (pc != null) {
diff --git a/src/AddIns/Misc/UnitTesting/Src/RunTestCommands.cs b/src/AddIns/Misc/UnitTesting/Src/RunTestCommands.cs
index 04504ef2d3..5da690caa7 100644
--- a/src/AddIns/Misc/UnitTesting/Src/RunTestCommands.cs
+++ b/src/AddIns/Misc/UnitTesting/Src/RunTestCommands.cs
@@ -167,7 +167,7 @@ namespace ICSharpCode.UnitTesting
{
BuildProjectBeforeTestRun build = new BuildProjectBeforeTestRun(project);
build.BuildComplete += delegate {
- OnBuildComplete(project, fixture, test);
+ OnBuildComplete(build.LastBuildResults, project, fixture, test);
};
build.Run();
}
@@ -277,9 +277,9 @@ namespace ICSharpCode.UnitTesting
///
/// Runs the test for the project after a successful build.
///
- void OnBuildComplete(IProject project, IClass fixture, IMember test)
+ void OnBuildComplete(BuildResults results, IProject project, IClass fixture, IMember test)
{
- if (MSBuildEngine.LastErrorCount == 0 && IsRunningTest) {
+ if (results.ErrorCount == 0 && IsRunningTest) {
UnitTestApplicationStartHelper helper = new UnitTestApplicationStartHelper();
helper.Initialize(project, fixture, test);
helper.Results = Path.GetTempFileName();
diff --git a/src/Libraries/ICSharpCode.Build.Tasks/Project/ICSharpCode.Build.Tasks.csproj b/src/Libraries/ICSharpCode.Build.Tasks/Project/ICSharpCode.Build.Tasks.csproj
index 77cdf25e3d..b406742d61 100644
--- a/src/Libraries/ICSharpCode.Build.Tasks/Project/ICSharpCode.Build.Tasks.csproj
+++ b/src/Libraries/ICSharpCode.Build.Tasks/Project/ICSharpCode.Build.Tasks.csproj
@@ -16,6 +16,7 @@
4096
4
false
+ False
..\..\..\..\bin\
diff --git a/src/Main/Base/Project/ICSharpCode.SharpDevelop.csproj b/src/Main/Base/Project/ICSharpCode.SharpDevelop.csproj
index 4f9317d731..8d0eab92fb 100644
--- a/src/Main/Base/Project/ICSharpCode.SharpDevelop.csproj
+++ b/src/Main/Base/Project/ICSharpCode.SharpDevelop.csproj
@@ -64,6 +64,7 @@
+
diff --git a/src/Main/Base/Project/Src/Commands/BuildCommands.cs b/src/Main/Base/Project/Src/Commands/BuildCommands.cs
index a9cc3b108c..b8897d81ac 100644
--- a/src/Main/Base/Project/Src/Commands/BuildCommands.cs
+++ b/src/Main/Base/Project/Src/Commands/BuildCommands.cs
@@ -48,13 +48,20 @@ namespace ICSharpCode.SharpDevelop.Project.Commands
BeforeBuild();
StartBuild();
} else {
- MSBuildEngine.AddNoSingleFileCompilationError();
+ AddNoSingleFileCompilationError();
}
}
+ BuildResults lastBuildResults;
+
+ public BuildResults LastBuildResults {
+ get { return lastBuildResults; }
+ }
+
protected void CallbackMethod(BuildResults results)
{
- MSBuildEngine.ShowResults(results);
+ lastBuildResults = results;
+ ShowResults(results);
AfterBuild();
if (BuildComplete != null)
BuildComplete(this, EventArgs.Empty);
@@ -63,6 +70,35 @@ namespace ICSharpCode.SharpDevelop.Project.Commands
public abstract void StartBuild();
public event EventHandler BuildComplete;
+
+
+
+ public static void ShowResults(BuildResults results)
+ {
+ if (results != null) {
+ TaskService.InUpdate = true;
+ foreach (BuildError error in results.Errors) {
+ TaskService.Add(new Task(error));
+ }
+ TaskService.InUpdate = false;
+ if (results.Errors.Count > 0 && ErrorListPad.ShowAfterBuild) {
+ WorkbenchSingleton.Workbench.GetPad(typeof(ErrorListPad)).BringPadToFront();
+ }
+ }
+ }
+
+ ///
+ /// Notifies the user that #develp's internal MSBuildEngine
+ /// implementation only supports compiling solutions and projects;
+ /// it does not allow compiling individual files.
+ ///
+ /// Adds a message to the and
+ /// shows the .
+ public static void AddNoSingleFileCompilationError()
+ {
+ TaskService.Add(new Task(null, StringParser.Parse("${res:BackendBindings.ExecutionManager.NoSingleFileCompilation}"), 0, 0, TaskType.Error));
+ WorkbenchSingleton.Workbench.GetPad(typeof(ErrorListPad)).BringPadToFront();
+ }
}
public sealed class Build : AbstractBuildMenuCommand
diff --git a/src/Main/Base/Project/Src/Commands/DebugCommands.cs b/src/Main/Base/Project/Src/Commands/DebugCommands.cs
index 68a4b18ea6..968a278151 100644
--- a/src/Main/Base/Project/Src/Commands/DebugCommands.cs
+++ b/src/Main/Base/Project/Src/Commands/DebugCommands.cs
@@ -22,7 +22,7 @@ namespace ICSharpCode.SharpDevelop.Project.Commands
{
Build build = new Build();
build.BuildComplete += delegate {
- if (MSBuildEngine.LastErrorCount == 0) {
+ if (build.LastBuildResults.ErrorCount == 0) {
IProject startupProject = ProjectService.OpenSolution.StartupProject;
if (startupProject != null) {
startupProject.Start(withDebugger);
diff --git a/src/Main/Base/Project/Src/Project/BuildError.cs b/src/Main/Base/Project/Src/Project/BuildError.cs
index 70433cb946..27d231b666 100644
--- a/src/Main/Base/Project/Src/Project/BuildError.cs
+++ b/src/Main/Base/Project/Src/Project/BuildError.cs
@@ -11,17 +11,27 @@ using ICSharpCode.Core;
namespace ICSharpCode.SharpDevelop.Project
{
+ [Serializable]
public class BuildError
{
public BuildError()
{
- this.line = 0;
- this.column = 0;
+ this.line = -1;
+ this.column = -1;
this.errorCode = string.Empty;
this.errorText = string.Empty;
this.fileName = string.Empty;
}
+ public BuildError(string fileName, string errorText)
+ {
+ this.line = -1;
+ this.column = -1;
+ this.errorCode = string.Empty;
+ this.errorText = errorText;
+ this.fileName = fileName;
+ }
+
public BuildError(string fileName, int line, int column, string errorCode, string errorText)
{
this.line = line;
@@ -37,6 +47,7 @@ namespace ICSharpCode.SharpDevelop.Project
string fileName;
int line;
bool warning;
+ [NonSerialized]
object tag;
string contextMenuAddInTreeEntry;
@@ -94,6 +105,13 @@ namespace ICSharpCode.SharpDevelop.Project
}
}
+ ///
+ /// Allows to store any object with this error. An object might be attached by a custom
+ /// MSBuild logger and later read by the context menu command.
+ ///
+ /// The Tag property is [NonSerialized], which shouldn't be a problem
+ /// because both custom loggers and context menu commands are guaranteed to run
+ /// in the main AppDomain.
public object Tag {
get {
return tag;
diff --git a/src/Main/Base/Project/Src/Project/BuildResults.cs b/src/Main/Base/Project/Src/Project/BuildResults.cs
index a844fd7c91..bc96117587 100644
--- a/src/Main/Base/Project/Src/Project/BuildResults.cs
+++ b/src/Main/Base/Project/Src/Project/BuildResults.cs
@@ -7,6 +7,7 @@
using System;
using System.Collections.Generic;
+using System.Collections.ObjectModel;
namespace ICSharpCode.SharpDevelop.Project
{
@@ -29,21 +30,54 @@ namespace ICSharpCode.SharpDevelop.Project
public class BuildResults
{
List errors = new List();
+ ReadOnlyCollection readOnlyErrors;
BuildResultCode result;
+ int errorCount, warningCount;
- public List Errors {
- get {
- return errors;
+ ///
+ /// Adds a build error/warning to the results.
+ /// This method is thread-safe.
+ ///
+ public void Add(BuildError error)
+ {
+ if (error == null)
+ throw new ArgumentNullException("error");
+ lock (errors) {
+ readOnlyErrors = null;
+ errors.Add(error);
+ if (error.IsWarning)
+ warningCount++;
+ else
+ errorCount++;
}
}
- public BuildResultCode Result {
+ ///
+ /// Gets the list of build errors or warnings.
+ /// This property is thread-safe.
+ ///
+ public ReadOnlyCollection Errors {
get {
- return result;
- }
- set {
- result = value;
+ lock (errors) {
+ if (readOnlyErrors == null) {
+ readOnlyErrors = Array.AsReadOnly(errors.ToArray());
+ }
+ return readOnlyErrors;
+ }
}
}
+
+ public BuildResultCode Result {
+ get { return result; }
+ set { result = value; }
+ }
+
+ public int ErrorCount {
+ get { return errorCount; }
+ }
+
+ public int WarningCount {
+ get { return warningCount; }
+ }
}
}
diff --git a/src/Main/Base/Project/Src/Project/MSBuildAdditionalLogger.cs b/src/Main/Base/Project/Src/Project/MSBuildAdditionalLogger.cs
index 78aae06fe1..075bc934b0 100644
--- a/src/Main/Base/Project/Src/Project/MSBuildAdditionalLogger.cs
+++ b/src/Main/Base/Project/Src/Project/MSBuildAdditionalLogger.cs
@@ -16,7 +16,7 @@ namespace ICSharpCode.SharpDevelop.Project
///
public interface IMSBuildAdditionalLogger
{
- ILogger CreateLogger(MSBuildEngine engine);
+ ILogger CreateLogger(MSBuildEngineWorker engineWorker);
}
///
@@ -67,23 +67,23 @@ namespace ICSharpCode.SharpDevelop.Project
addIn = codon.AddIn;
}
- public ILogger CreateLogger(MSBuildEngine engine)
+ public ILogger CreateLogger(MSBuildEngineWorker engineWorker)
{
- return new TaskBoundAdditionalLogger(this, engine);
+ return new TaskBoundAdditionalLogger(this, engineWorker);
}
}
private class TaskBoundAdditionalLogger : ILogger
{
TaskBoundAdditionalLoggerDescriptor desc;
- MSBuildEngine engine;
+ MSBuildEngineWorker engineWorker;
ILogger baseLogger;
bool isActive;
- public TaskBoundAdditionalLogger(TaskBoundAdditionalLoggerDescriptor desc, MSBuildEngine engine)
+ public TaskBoundAdditionalLogger(TaskBoundAdditionalLoggerDescriptor desc, MSBuildEngineWorker engineWorker)
{
this.desc = desc;
- this.engine = engine;
+ this.engineWorker = engineWorker;
}
void CreateBaseLogger()
@@ -93,7 +93,7 @@ namespace ICSharpCode.SharpDevelop.Project
baseLogger = obj as ILogger;
IMSBuildAdditionalLogger addLog = obj as IMSBuildAdditionalLogger;
if (addLog != null) {
- baseLogger = addLog.CreateLogger(engine);
+ baseLogger = addLog.CreateLogger(engineWorker);
}
}
}
diff --git a/src/Main/Base/Project/Src/Project/MSBuildBasedProject.cs b/src/Main/Base/Project/Src/Project/MSBuildBasedProject.cs
index a668c77801..48ae564fee 100644
--- a/src/Main/Base/Project/Src/Project/MSBuildBasedProject.cs
+++ b/src/Main/Base/Project/Src/Project/MSBuildBasedProject.cs
@@ -737,34 +737,19 @@ namespace ICSharpCode.SharpDevelop.Project
#region Building
public override void StartBuild(BuildOptions options)
{
- RunMSBuild(this.FileName, options.Target.TargetName,
- this.ActiveConfiguration, this.ActivePlatform,
- true, options.Callback, options.AdditionalProperties);
+ RunMSBuild(this.ParentSolution, this,
+ this.ActiveConfiguration, this.ActivePlatform, options);
}
- internal static void RunMSBuild(string fileName, string target, string configuration, string platform, bool isSingleProject, BuildCallback callback, IDictionary additionalProperties)
+ internal static void RunMSBuild(Solution solution, IProject project,
+ string configuration, string platform, BuildOptions options)
{
WorkbenchSingleton.Workbench.GetPad(typeof(CompilerMessageView)).BringPadToFront();
MSBuildEngine engine = new MSBuildEngine();
- if (isSingleProject) {
- string dir = ProjectService.OpenSolution.Directory;
- if (!dir.EndsWith("/") && !dir.EndsWith("\\"))
- dir += Path.DirectorySeparatorChar;
- engine.AdditionalProperties.Add("SolutionDir", dir);
- }
- if (additionalProperties != null) {
- foreach (KeyValuePair pair in additionalProperties) {
- engine.AdditionalProperties.Add(pair.Key, pair.Value);
- }
- }
engine.Configuration = configuration;
engine.Platform = platform;
engine.MessageView = TaskService.BuildMessageViewCategory;
- if (target == null) {
- engine.Run(fileName, callback);
- } else {
- engine.Run(fileName, new string[] { target }, callback);
- }
+ engine.Run(solution, project, options);
}
#endregion
diff --git a/src/Main/Base/Project/Src/Project/MSBuildEngine.cs b/src/Main/Base/Project/Src/Project/MSBuildEngine.cs
index c7008e7e8f..d9ba515aca 100644
--- a/src/Main/Base/Project/Src/Project/MSBuildEngine.cs
+++ b/src/Main/Base/Project/Src/Project/MSBuildEngine.cs
@@ -9,6 +9,7 @@ using System;
using System.Collections.Generic;
using System.IO;
using System.Threading;
+using System.Text.RegularExpressions;
using ICSharpCode.Core;
using ICSharpCode.SharpDevelop.Gui;
@@ -17,18 +18,21 @@ using Microsoft.Build.Framework;
namespace ICSharpCode.SharpDevelop.Project
{
+ // let Project refer to the class, not to the namespace
+ using Project = Microsoft.Build.BuildEngine.Project;
+
///
/// Class responsible for building a project using MSBuild.
/// Is called by MSBuildProject.
///
- public class MSBuildEngine
+ public sealed class MSBuildEngine
{
///
/// Gets a list of the task names that cause a "Compiling ..." log message.
/// You can add items to this list by putting strings into
/// "/SharpDevelop/MSBuildEngine/CompileTaskNames".
///
- public static readonly List CompileTaskNames;
+ public static readonly ICollection CompileTaskNames;
///
/// Gets a list where addins can add additional properties for use in MsBuild.
@@ -51,10 +55,10 @@ namespace ICSharpCode.SharpDevelop.Project
static MSBuildEngine()
{
- CompileTaskNames = AddInTree.BuildItems("/SharpDevelop/MSBuildEngine/CompileTaskNames", null, false);
- for (int i = 0; i < CompileTaskNames.Count; i++) {
- CompileTaskNames[i] = CompileTaskNames[i].ToLowerInvariant();
- }
+ CompileTaskNames = new Set(
+ AddInTree.BuildItems("/SharpDevelop/MSBuildEngine/CompileTaskNames", null, false),
+ StringComparer.OrdinalIgnoreCase
+ );
AdditionalTargetFiles = AddInTree.BuildItems("/SharpDevelop/MSBuildEngine/AdditionalTargetFiles", null, false);
AdditionalMSBuildLoggers = AddInTree.BuildItems("/SharpDevelop/MSBuildEngine/AdditionalLoggers", null, false);
@@ -62,46 +66,7 @@ namespace ICSharpCode.SharpDevelop.Project
MSBuildProperties.Add("SharpDevelopBinPath", Path.GetDirectoryName(typeof(MSBuildEngine).Assembly.Location));
}
- #region relocated from ICSharpCode.SharpDevelop.Project.Commands.Build in BuildCommands.cs
- public static int LastErrorCount;
- public static int LastWarningCount;
-
- public static void ShowResults(BuildResults results)
- {
- if (results != null) {
- LastErrorCount = 0;
- LastWarningCount = 0;
- TaskService.InUpdate = true;
- foreach (BuildError error in results.Errors) {
- TaskService.Add(new Task(error));
- if (error.IsWarning)
- LastWarningCount++;
- else
- LastErrorCount++;
- }
- TaskService.InUpdate = false;
- if (results.Errors.Count > 0 && ErrorListPad.ShowAfterBuild) {
- WorkbenchSingleton.Workbench.GetPad(typeof(ErrorListPad)).BringPadToFront();
- }
- }
- }
-
- ///
- /// Notifies the user that #develp's internal MSBuildEngine
- /// implementation only supports compiling solutions and projects;
- /// it does not allow compiling individual files.
- ///
- /// Adds a message to the and
- /// shows the .
- public static void AddNoSingleFileCompilationError()
- {
- LastErrorCount = 1;
- LastWarningCount = 0;
- TaskService.Add(new Task(null, StringParser.Parse("${res:BackendBindings.ExecutionManager.NoSingleFileCompilation}"), 0, 0, TaskType.Error));
- WorkbenchSingleton.Workbench.GetPad(typeof(ErrorListPad)).BringPadToFront();
- }
- #endregion
-
+ #region Properties
MessageViewCategory messageView;
///
@@ -116,14 +81,6 @@ namespace ICSharpCode.SharpDevelop.Project
}
}
- SortedList additionalProperties = new SortedList();
-
- public IDictionary AdditionalProperties {
- get {
- return additionalProperties;
- }
- }
-
string configuration;
///
@@ -153,279 +110,538 @@ namespace ICSharpCode.SharpDevelop.Project
platform = value;
}
}
-
- BuildError currentErrorOrWarning;
-
- ///
- /// Gets the last build error/warning created by the default
- /// SharpDevelop logger.
- ///
- public BuildError CurrentErrorOrWarning {
- get {
- return currentErrorOrWarning;
- }
- }
-
- Stack projectFiles = new Stack();
-
- ///
- /// Gets the name of the currently building project file.
- ///
- public string CurrentProjectFile {
- get {
- if (projectFiles.Count == 0)
- return null;
- else
- return projectFiles.Peek();
- }
- }
-
- BuildResults currentResults;
-
- public BuildResults CurrentResults {
- get {
- return currentResults;
- }
- }
-
- public void Run(string buildFile, BuildCallback callback)
- {
- Run(buildFile, null, callback);
- }
+ #endregion
volatile static bool isRunning = false;
- public void Run(string buildFile, string[] targets, BuildCallback callback)
+ public void Run(Solution solution, IProject project, BuildOptions options)
{
if (isRunning) {
BuildResults results = new BuildResults();
results.Result = BuildResultCode.MSBuildAlreadyRunning;
- results.Errors.Add(new BuildError(null, 0, 0, null, ResourceService.GetString("MainWindow.CompilerMessages.MSBuildAlreadyRunning")));
- callback(results);
+ results.Add(new BuildError(null, ResourceService.GetString("MainWindow.CompilerMessages.MSBuildAlreadyRunning")));
+ if (options.Callback != null) {
+ options.Callback(results);
+ }
} else {
isRunning = true;
- Thread thread = new Thread(new ThreadStarter(buildFile, targets, this, callback).Run);
- thread.Name = "MSBuildEngine";
+ Thread thread = new Thread(new BuildRun(solution, project, options, this).RunMainBuild);
+ thread.Name = "MSBuildEngine main worker";
thread.SetApartmentState(ApartmentState.STA);
thread.Start();
}
}
- class ThreadStarter
+ internal sealed class BuildRun
{
- string buildFile;
- string[] targets;
- MSBuildEngine engine;
- BuildCallback callback;
+ Solution solution;
+ IProject project;
+ BuildOptions options;
+ MSBuildEngine parentEngine;
+ internal BuildResults currentResults = new BuildResults();
+ List projectsToBuild = new List();
- public ThreadStarter(string buildFile, string[] targets, MSBuildEngine engine, BuildCallback callback)
+ public BuildRun(Solution solution, IProject project, BuildOptions options, MSBuildEngine parentEngine)
{
- engine.currentResults = new BuildResults();
- this.buildFile = buildFile;
- this.targets = targets;
- this.engine = engine;
- this.callback = callback;
+ this.solution = solution;
+ this.project = project;
+ this.options = options;
+ this.parentEngine = parentEngine;
}
+ #region Main Build
[STAThread]
- public void Run()
+ internal void RunMainBuild()
{
- BuildResults results = this.engine.currentResults;
- LoggingService.Debug("Run MSBuild on " + buildFile);
-
- Engine engine = MSBuildInternals.CreateEngine();
- if (this.engine.Configuration != null) {
- engine.GlobalProperties.SetProperty("Configuration", this.engine.Configuration);
- }
- if (this.engine.Platform != null) {
- engine.GlobalProperties.SetProperty("Platform", this.engine.Platform);
+ try {
+ PrepareBuild();
+ } catch (Exception ex) {
+ MessageService.ShowError(ex);
}
- foreach (KeyValuePair entry in MSBuildProperties) {
- engine.GlobalProperties.SetProperty(entry.Key, entry.Value);
+ StartWorkerBuild();
+ }
+
+ void Finish()
+ {
+ LoggingService.Debug("MSBuild finished");
+ MSBuildEngine.isRunning = false;
+ if (currentResults.Result == BuildResultCode.None) {
+ currentResults.Result = BuildResultCode.Success;
}
- foreach (KeyValuePair entry in this.engine.additionalProperties) {
- engine.GlobalProperties.SetProperty(entry.Key, entry.Value);
+ if (currentResults.Result == BuildResultCode.Success) {
+ parentEngine.MessageView.AppendLine("${res:MainWindow.CompilerMessages.BuildFinished}");
+ StatusBarService.SetMessage("${res:MainWindow.CompilerMessages.BuildFinished}");
+ } else {
+ parentEngine.MessageView.AppendLine("${res:MainWindow.CompilerMessages.BuildFailed}");
+ StatusBarService.SetMessage("${res:MainWindow.CompilerMessages.BuildFailed}");
}
-
- SharpDevelopLogger logger = new SharpDevelopLogger(this.engine);
- engine.RegisterLogger(logger);
- foreach (IMSBuildAdditionalLogger loggerProvider in MSBuildEngine.AdditionalMSBuildLoggers) {
- engine.RegisterLogger(loggerProvider.CreateLogger(this.engine));
+ if (options.Callback != null) {
+ WorkbenchSingleton.MainForm.BeginInvoke(options.Callback, currentResults);
}
+ }
+
+ void PrepareBuild()
+ {
+ parentEngine.MessageView.AppendLine("${res:MainWindow.CompilerMessages.BuildStarted}");
- Microsoft.Build.BuildEngine.Project project = engine.CreateNewProject();
- try {
- project.Load(buildFile);
+ if (project == null) {
+ LoggingService.Debug("Parsing solution file " + solution.FileName);
- foreach (string targetFile in AdditionalTargetFiles) {
- project.AddNewImport(targetFile, null);
+ Engine engine = CreateEngine();
+ if (parentEngine.Configuration != null) {
+ engine.GlobalProperties.SetProperty("Configuration", parentEngine.Configuration);
+ }
+ if (parentEngine.Platform != null) {
+ engine.GlobalProperties.SetProperty("Platform", parentEngine.Platform);
+ }
+ Project solutionProject = LoadProject(engine, solution.FileName);
+ if (solutionProject == null) {
+ Finish();
+ return;
+ }
+ if (!ParseSolution(solutionProject)) {
+ Finish();
+ return;
+ }
+ } else {
+ if (ParseMSBuildProject(project) == null) {
+ Finish();
+ return;
}
-
- if (engine.BuildProject(project, targets))
- results.Result = BuildResultCode.Success;
- else
- results.Result = BuildResultCode.Error;
- } catch (ArgumentException ex) {
- results.Result = BuildResultCode.BuildFileError;
- results.Errors.Add(new BuildError(null, -1, -1, "", ex.Message));
- } catch (InvalidProjectFileException ex) {
- results.Result = BuildResultCode.BuildFileError;
- results.Errors.Add(new BuildError(ex.ProjectFile, ex.LineNumber, ex.ColumnNumber, ex.ErrorCode, ex.Message));
- }
-
- logger.FlushCurrentError();
-
- LoggingService.Debug("MSBuild finished");
- MSBuildEngine.isRunning = false;
- if (callback != null) {
- WorkbenchSingleton.MainForm.BeginInvoke(callback, results);
}
- engine.UnloadAllProjects();
- this.engine.currentResults = null;
+ SortProjectsToBuild();
}
- }
-
- class SharpDevelopLogger : ILogger
- {
- MSBuildEngine engine;
- BuildResults results;
+ #endregion
- public SharpDevelopLogger(MSBuildEngine engine)
- {
- this.engine = engine;
- this.results = engine.currentResults;
- }
+ #region Worker build
+ int workerCount;
+ int maxWorkerCount;
- void AppendText(string text)
+ ///
+ /// Runs the first worker on this thread and creates new threads if required
+ ///
+ void StartWorkerBuild()
{
- engine.MessageView.AppendText(text + "\r\n");
+ workerCount = 1;
+ // one build worker thread is maximum - MSBuild sets the working directory
+ // and thus cannot run multiple times in the same process
+ maxWorkerCount = 1;
+ RunWorkerBuild();
}
- internal void FlushCurrentError()
+ // Reuse worker objects: improves performance because MSBuild can cache Xml documents
+ Queue unusedWorkers = new Queue();
+
+ ///
+ /// Runs one worker thread
+ ///
+ [STAThread]
+ void RunWorkerBuild()
{
- if (engine.currentErrorOrWarning != null) {
- AppendText(engine.currentErrorOrWarning.ToString());
- engine.currentErrorOrWarning = null;
+ LoggingService.Debug("Build Worker thread started");
+ MSBuildEngineWorker worker = null;
+ try {
+ lock (projectsToBuild) {
+ if (unusedWorkers.Count > 0)
+ worker = unusedWorkers.Dequeue();
+ }
+ if (worker == null) {
+ worker = new MSBuildEngineWorker(parentEngine, this);
+ }
+ while (RunWorkerInternal(worker));
+ } catch (Exception ex) {
+ MessageService.ShowError(ex);
+ } finally {
+ bool wasLastWorker;
+ lock (projectsToBuild) {
+ workerCount--;
+ wasLastWorker = workerCount == 0;
+ if (worker != null) {
+ unusedWorkers.Enqueue(worker);
+ }
+ }
+ LoggingService.Debug("Build Worker thread finished");
+ if (wasLastWorker) {
+ Finish();
+ }
}
}
- void OnBuildStarted(object sender, BuildStartedEventArgs e)
- {
- AppendText("${res:MainWindow.CompilerMessages.BuildStarted}");
- }
+ int lastUniqueWorkerID;
- void OnBuildFinished(object sender, BuildFinishedEventArgs e)
+ ///
+ /// Find available work and run it on the specified worker.
+ ///
+ bool RunWorkerInternal(MSBuildEngineWorker worker)
{
- if (e.Succeeded) {
- AppendText("${res:MainWindow.CompilerMessages.BuildFinished}");
- StatusBarService.SetMessage("${res:MainWindow.CompilerMessages.BuildFinished}");
- } else {
- AppendText("${res:MainWindow.CompilerMessages.BuildFailed}");
- StatusBarService.SetMessage("${res:MainWindow.CompilerMessages.BuildFailed}");
+ ProjectToBuild nextFreeProject = null;
+ lock (projectsToBuild) {
+ foreach (ProjectToBuild ptb in projectsToBuild) {
+ if (ptb.buildStarted == false && ptb.DependenciesSatisfied()) {
+ if (nextFreeProject == null) {
+ nextFreeProject = ptb;
+
+ // all workers busy, don't look if there is more work available
+ if (workerCount == maxWorkerCount)
+ break;
+ } else {
+ // free workers available + additional work available:
+ // start a new worker
+ LoggingService.Debug("Starting a new worker");
+ workerCount++;
+ Thread thread = new Thread(RunWorkerBuild);
+ thread.Name = "MSBuildEngine worker " + (++lastUniqueWorkerID);
+ thread.SetApartmentState(ApartmentState.STA);
+ thread.Start();
+
+ // start at most one additional worker, the new worker can
+ // start more threads if desired
+ break;
+ }
+ }
+ }
+ if (nextFreeProject == null) {
+ // nothing to do for this worker thread
+ return false;
+ }
+ // now build nextFreeProject
+ nextFreeProject.buildStarted = true;
+ } // end lock
+
+ StatusBarService.SetMessage("${res:MainWindow.CompilerMessages.BuildVerb} " + Path.GetFileNameWithoutExtension(nextFreeProject.file) + "...");
+
+ // run the build:
+ if (worker.Build(nextFreeProject)) {
+ // build successful: mark it as finished
+ lock (projectsToBuild) {
+ nextFreeProject.buildFinished = true;
+ }
}
+ return true;
}
+ #endregion
- void OnProjectStarted(object sender, ProjectStartedEventArgs e)
+ #region Managing the output lock
+ ///
+ /// Queue of output text to write when lock is released.
+ ///
+ /// Also serves as object to ensure access to outputLockIsAquired is thread-safe.
+ ///
+ Queue queuedOutputText = new Queue();
+ volatile bool outputLockIsAquired;
+
+ internal bool TryAquireOutputLock()
{
- engine.projectFiles.Push(e.ProjectFile);
- StatusBarService.SetMessage("${res:MainWindow.CompilerMessages.BuildVerb} " + Path.GetFileNameWithoutExtension(e.ProjectFile) + "...");
+ lock (queuedOutputText) {
+ if (outputLockIsAquired) {
+ return false;
+ } else {
+ outputLockIsAquired = true;
+ return true;
+ }
+ }
}
- void OnProjectFinished(object sender, ProjectFinishedEventArgs e)
+ internal void ReleaseOutputLock()
{
- FlushCurrentError();
- engine.projectFiles.Pop();
- if (engine.projectFiles.Count > 0) {
- StatusBarService.SetMessage("${res:MainWindow.CompilerMessages.BuildVerb} " + Path.GetFileNameWithoutExtension(engine.CurrentProjectFile) + "...");
+ lock (queuedOutputText) {
+ outputLockIsAquired = false;
+ while (queuedOutputText.Count > 0) {
+ parentEngine.MessageView.AppendText(queuedOutputText.Dequeue());
+ }
}
}
- string activeTaskName;
-
- void OnTaskStarted(object sender, TaskStartedEventArgs e)
+ internal void EnqueueTextForAppendWhenOutputLockIsReleased(string text)
{
- activeTaskName = e.TaskName;
- if (CompileTaskNames.Contains(e.TaskName.ToLowerInvariant())) {
- AppendText("${res:MainWindow.CompilerMessages.CompileVerb} " + Path.GetFileNameWithoutExtension(e.ProjectFile));
+ lock (queuedOutputText) {
+ if (outputLockIsAquired) {
+ queuedOutputText.Enqueue(text);
+ } else {
+ parentEngine.MessageView.AppendText(text);
+ }
}
}
+ #endregion
- void OnTaskFinished(object sender, TaskFinishedEventArgs e)
+ #region CreateEngine / LoadProject
+ internal Engine CreateEngine()
{
- FlushCurrentError();
+ Engine engine = MSBuildInternals.CreateEngine();
+ foreach (KeyValuePair entry in MSBuildProperties) {
+ engine.GlobalProperties.SetProperty(entry.Key, entry.Value);
+ }
+ if (options.AdditionalProperties != null) {
+ foreach (KeyValuePair entry in options.AdditionalProperties) {
+ engine.GlobalProperties.SetProperty(entry.Key, entry.Value);
+ }
+ }
+ engine.GlobalProperties.SetProperty("SolutionDir", EnsureBackslash(solution.Directory));
+ engine.GlobalProperties.SetProperty("SolutionExt", ".sln");
+ engine.GlobalProperties.SetProperty("SolutionFileName", Path.GetFileName(solution.FileName));
+ engine.GlobalProperties.SetProperty("SolutionPath", solution.FileName);
+
+ return engine;
}
- void OnError(object sender, BuildErrorEventArgs e)
+ static string EnsureBackslash(string path)
{
- AppendError(e.File, e.LineNumber, e.ColumnNumber, e.Code, e.Message, false);
+ if (path.EndsWith("\\"))
+ return path;
+ else
+ return path + "\\";
}
- void OnWarning(object sender, BuildWarningEventArgs e)
+ internal Project LoadProject(Engine engine, string fileName)
{
- AppendError(e.File, e.LineNumber, e.ColumnNumber, e.Code, e.Message, true);
+ Project project = engine.CreateNewProject();
+ try {
+ project.Load(fileName);
+ return project;
+ } catch (ArgumentException ex) {
+ currentResults.Result = BuildResultCode.BuildFileError;
+ currentResults.Add(new BuildError("", ex.Message));
+ } catch (InvalidProjectFileException ex) {
+ currentResults.Result = BuildResultCode.BuildFileError;
+ currentResults.Add(new BuildError(ex.ProjectFile, ex.LineNumber, ex.ColumnNumber, ex.ErrorCode, ex.Message));
+ }
+ return null;
}
+ #endregion
- void AppendError(string file, int lineNumber, int columnNumber, string code, string message, bool isWarning)
+ #region ParseSolution
+ bool ParseSolution(Project solution)
{
- if (string.Equals(file, activeTaskName, StringComparison.InvariantCultureIgnoreCase)) {
- file = "";
- } else if (FileUtility.IsValidFileName(file)) {
- bool isShortFileName = file == Path.GetFileNameWithoutExtension(file);
- if (engine.CurrentProjectFile != null) {
- file = Path.Combine(Path.GetDirectoryName(engine.CurrentProjectFile), file);
+ // get the build target to call
+ Target mainBuildTarget = solution.Targets[options.Target.TargetName];
+ if (mainBuildTarget == null) {
+ currentResults.Result = BuildResultCode.BuildFileError;
+ currentResults.Add(new BuildError(this.solution.FileName, "Target '" + options.Target + "' not supported by solution."));
+ return false;
+ }
+ // example of mainBuildTarget:
+ //
+ //
+ //
+ List mainBuildTargetTasks = Linq.ToList(Linq.CastTo(mainBuildTarget));
+ if (mainBuildTargetTasks.Count != 1
+ || mainBuildTargetTasks[0].Name != "CallTarget")
+ {
+ return InvalidTarget(mainBuildTarget);
+ }
+
+ List solutionTargets = new List();
+ foreach (string solutionTargetName in mainBuildTargetTasks[0].GetParameterValue("Targets").Split(';'))
+ {
+ Target target = solution.Targets[solutionTargetName];
+ if (target != null) {
+ solutionTargets.Add(target);
}
- if (isShortFileName && !File.Exists(file)) {
- file = "";
+ }
+
+ // dictionary for fast lookup of ProjectToBuild elements
+ Dictionary projectsToBuildDict = new Dictionary();
+
+ // now look through targets that took like this:
+ //
+ //
+ //
+ //
+ // and add those targets to the "projectsToBuild" list.
+ foreach (Target target in solutionTargets) {
+ List tasks = Linq.ToList(Linq.CastTo(target));
+ if (tasks.Count == 0) {
+ return InvalidTarget(target);
+ }
+
+ // find task to run when this target is executed
+ BuildTask bestTask = null;
+ foreach (BuildTask task in tasks) {
+ if (task.Name != "MSBuild") {
+ return InvalidTarget(target);
+ }
+ if (MSBuildInternals.EvaluateCondition(solution, task.Condition)) {
+ bestTask = task;
+ }
+ }
+ if (bestTask == null) {
+ LoggingService.Warn("No matching condition for solution target " + target.Name);
+ bestTask = tasks[0];
+ }
+
+ // create projectToBuild entry and add it to list and dictionary
+ string projectFileName = Path.Combine(this.solution.Directory, bestTask.GetParameterValue("Projects"));
+ ProjectToBuild projectToBuild = new ProjectToBuild(Path.GetFullPath(projectFileName),
+ bestTask.GetParameterValue("Targets"));
+
+ // get project configuration and platform from properties section
+ string propertiesString = bestTask.GetParameterValue("Properties");
+ Match match = Regex.Match(propertiesString, @"\bConfiguration=([^;]+);");
+ if (match.Success) {
+ projectToBuild.configuration = match.Groups[1].Value;
+ } else {
+ projectToBuild.configuration = parentEngine.Configuration;
+ }
+ match = Regex.Match(propertiesString, @"\bPlatform=([^;]+);");
+ if (match.Success) {
+ projectToBuild.platform = match.Groups[1].Value;
+ } else {
+ projectToBuild.platform = parentEngine.Platform;
+ if (projectToBuild.platform == "Any CPU") {
+ projectToBuild.platform = "AnyCPU";
+ }
+ }
+
+ projectsToBuild.Add(projectToBuild);
+ projectsToBuildDict[target.Name] = projectToBuild;
+ }
+
+ // now create dependencies between projectsToBuild
+ foreach (Target target in solutionTargets) {
+ ProjectToBuild p1;
+ if (!projectsToBuildDict.TryGetValue(target.Name, out p1))
+ continue;
+ foreach (string dependency in target.DependsOnTargets.Split(';')) {
+ ProjectToBuild p2;
+ if (!projectsToBuildDict.TryGetValue(dependency, out p2))
+ continue;
+ p1.dependencies.Add(p2);
}
}
- FlushCurrentError();
- BuildError error = new BuildError(file, lineNumber, columnNumber, code, message);
- error.IsWarning = isWarning;
- results.Errors.Add(error);
- engine.currentErrorOrWarning = error;
+ return true;
}
- #region ILogger interface implementation
- LoggerVerbosity verbosity = LoggerVerbosity.Minimal;
+ ///
+ /// Adds an error message that the specified target is invalid and returns false.
+ ///
+ bool InvalidTarget(Target target)
+ {
+ currentResults.Result = BuildResultCode.BuildFileError;
+ currentResults.Add(new BuildError(this.solution.FileName, "Solution target '" + target.Name + "' is invalid."));
+ return false;
+ }
+ #endregion
- public LoggerVerbosity Verbosity {
- get {
- return verbosity;
+ #region ParseMSBuildProject
+ Dictionary parseMSBuildProjectProjectsToBuildDict = new Dictionary();
+
+ ///
+ /// Adds a ProjectToBuild item for the project and it's project references.
+ /// Returns the added item, or null if an error occured.
+ ///
+ ProjectToBuild ParseMSBuildProject(IProject project)
+ {
+ ProjectToBuild ptb;
+ if (parseMSBuildProjectProjectsToBuildDict.TryGetValue(project, out ptb)) {
+ // only add each project once, reuse existing ProjectToBuild
+ return ptb;
}
- set {
- verbosity = value;
+ ptb = new ProjectToBuild(project.FileName, options.Target.TargetName);
+ ptb.configuration = parentEngine.Configuration;
+ ptb.platform = parentEngine.Platform;
+
+ projectsToBuild.Add(ptb);
+ parseMSBuildProjectProjectsToBuildDict[project] = ptb;
+
+ foreach (ProjectItem item in project.GetItemsOfType(ItemType.ProjectReference)) {
+ ProjectReferenceProjectItem prpi = item as ProjectReferenceProjectItem;
+ if (prpi != null && prpi.ReferencedProject != null) {
+ ProjectToBuild referencedProject = ParseMSBuildProject(prpi.ReferencedProject);
+ if (referencedProject == null)
+ return null;
+ ptb.dependencies.Add(referencedProject);
+ }
}
+
+ return ptb;
}
+ #endregion
- string parameters;
+ #region SortProjectsToBuild
+ ///
+ /// Recursively count dependencies and sort projects (most important first).
+ /// This decreases the number of waiting workers on multi-processor builds
+ ///
+ void SortProjectsToBuild()
+ {
+ // count:
+ try {
+ foreach (ProjectToBuild ptb in projectsToBuild) {
+ projectsToBuild.ForEach(delegate(ProjectToBuild p) { p.visitFlag = 0; });
+ ptb.dependencies.ForEach(IncrementRequiredByCount);
+ }
+ } catch (DependencyCycleException) {
+ currentResults.Add(new BuildError(null, "Dependency cycle detected, cannot build!"));
+ return;
+ }
+ // sort by requiredByCount, decreasing
+ projectsToBuild.Sort(delegate (ProjectToBuild a, ProjectToBuild b) {
+ return -a.requiredByCount.CompareTo(b.requiredByCount);
+ });
+ }
- public string Parameters {
- get {
- return parameters;
+ ///
+ /// Recursively increment requiredByCount on ptb and all its dependencies
+ ///
+ static void IncrementRequiredByCount(ProjectToBuild ptb)
+ {
+ if (ptb.visitFlag == 1) {
+ return;
}
- set {
- parameters = value;
+ if (ptb.visitFlag == -1) {
+ throw new DependencyCycleException();
}
+ ptb.visitFlag = -1;
+ ptb.requiredByCount++;
+ ptb.dependencies.ForEach(IncrementRequiredByCount);
+ ptb.visitFlag = 1;
}
- public void Initialize(IEventSource eventSource)
+ class DependencyCycleException : Exception {}
+ #endregion
+ }
+
+ ///
+ /// node used for project dependency graph
+ ///
+ internal class ProjectToBuild
+ {
+ // information required to build the project
+ internal string file;
+ internal string targets;
+ internal string configuration, platform;
+
+ internal List dependencies = new List();
+
+ internal bool DependenciesSatisfied()
{
- eventSource.BuildStarted += new BuildStartedEventHandler(OnBuildStarted);
- eventSource.BuildFinished += new BuildFinishedEventHandler(OnBuildFinished);
- eventSource.ProjectStarted += new ProjectStartedEventHandler(OnProjectStarted);
- eventSource.ProjectFinished += new ProjectFinishedEventHandler(OnProjectFinished);
- eventSource.TaskStarted += new TaskStartedEventHandler(OnTaskStarted);
- eventSource.TaskFinished += new TaskFinishedEventHandler(OnTaskFinished);
-
- eventSource.ErrorRaised += new BuildErrorEventHandler(OnError);
- eventSource.WarningRaised += new BuildWarningEventHandler(OnWarning);
+ return dependencies.TrueForAll(delegate(ProjectToBuild p) { return p.buildFinished; });
}
- public void Shutdown()
+ ///
+ /// Number of projects that are directly or indirectly dependent on this project.
+ /// Used in SortProjectsToBuild step.
+ ///
+ internal int requiredByCount;
+
+ ///
+ /// Mark already visited nodes. 0 = not visited, -1 = visiting, 1 = visited
+ /// Used in SortProjectsToBuild step.
+ ///
+ internal int visitFlag;
+
+ // build status. Three possible values:
+ // buildStarted=buildFinished=false => build not yet started
+ // buildStarted=true, buildFinished=false => build running
+ // buildStarted=buildFinished=true => build finished
+ internal bool buildStarted;
+ internal bool buildFinished;
+
+ public ProjectToBuild(string file, string targets)
{
-
+ this.file = file;
+ this.targets = targets;
}
- #endregion
}
}
}
diff --git a/src/Main/Base/Project/Src/Project/MSBuildEngineWorker.cs b/src/Main/Base/Project/Src/Project/MSBuildEngineWorker.cs
new file mode 100644
index 0000000000..141ac103cf
--- /dev/null
+++ b/src/Main/Base/Project/Src/Project/MSBuildEngineWorker.cs
@@ -0,0 +1,251 @@
+//
+//
+//
+//
+// $Revision$
+//
+
+using System;
+using System.Text;
+using System.Collections.Generic;
+using Microsoft.Build.Framework;
+using Microsoft.Build.BuildEngine;
+using ICSharpCode.SharpDevelop.Gui;
+using System.IO;
+using ICSharpCode.Core;
+
+namespace ICSharpCode.SharpDevelop.Project
+{
+ public sealed class MSBuildEngineWorker
+ {
+ MSBuildEngine parentEngine;
+ MSBuildEngine.BuildRun buildRun;
+ Engine engine;
+ SharpDevelopLogger logger;
+
+ internal MSBuildEngineWorker(MSBuildEngine parentEngine, MSBuildEngine.BuildRun buildRun)
+ {
+ this.parentEngine = parentEngine;
+ this.buildRun = buildRun;
+ engine = buildRun.CreateEngine();
+
+ logger = new SharpDevelopLogger(this);
+ engine.RegisterLogger(logger);
+ foreach (IMSBuildAdditionalLogger loggerProvider in MSBuildEngine.AdditionalMSBuildLoggers) {
+ engine.RegisterLogger(loggerProvider.CreateLogger(this));
+ }
+ }
+
+ internal bool Build(MSBuildEngine.ProjectToBuild ptb)
+ {
+ LoggingService.Debug("Run MSBuild on " + ptb.file);
+
+ if (!string.IsNullOrEmpty(ptb.configuration)) {
+ engine.GlobalProperties.SetProperty("Configuration", ptb.configuration);
+ }
+ if (!string.IsNullOrEmpty(ptb.platform)) {
+ engine.GlobalProperties.SetProperty("Platform", ptb.platform);
+ }
+
+ Microsoft.Build.BuildEngine.Project project = buildRun.LoadProject(engine, ptb.file);
+ if (project == null) {
+ LoggingService.Debug("Error loading " + ptb.file);
+ return false;
+ }
+ foreach (string additionalTargetFile in MSBuildEngine.AdditionalTargetFiles) {
+ project.AddNewImport(additionalTargetFile, null);
+ }
+
+ bool success;
+ if (string.IsNullOrEmpty(ptb.targets)) {
+ success = engine.BuildProject(project);
+ } else {
+ success = engine.BuildProject(project, ptb.targets.Split(';'));
+ }
+
+ logger.FlushCurrentError();
+ ReleaseOutput();
+
+ LoggingService.Debug("MSBuild on " + ptb.file + " finished " + (success ? "successfully" : "with error"));
+ return success;
+ }
+
+ bool outputAcquired;
+ StringBuilder cachedOutput;
+
+ public void OutputText(string text)
+ {
+ if (outputAcquired == false && cachedOutput == null) {
+ outputAcquired = buildRun.TryAquireOutputLock();
+ if (!outputAcquired) {
+ cachedOutput = new StringBuilder();
+ }
+ }
+ if (outputAcquired) {
+ parentEngine.MessageView.AppendText(text);
+ } else {
+ cachedOutput.Append(text);
+ }
+ }
+
+ void ReleaseOutput()
+ {
+ if (cachedOutput != null) {
+ buildRun.EnqueueTextForAppendWhenOutputLockIsReleased(cachedOutput.ToString());
+ cachedOutput = null;
+ }
+ if (outputAcquired) {
+ buildRun.ReleaseOutputLock();
+ }
+ }
+
+ #region CurrentBuild properties
+ BuildError currentErrorOrWarning;
+
+ ///
+ /// Gets the last build error/warning created by the default
+ /// SharpDevelop logger.
+ ///
+ public BuildError CurrentErrorOrWarning {
+ get {
+ return currentErrorOrWarning;
+ }
+ }
+
+ Stack projectFiles = new Stack();
+
+ ///
+ /// Gets the name of the currently building project file.
+ ///
+ public string CurrentProjectFile {
+ get {
+ if (projectFiles.Count == 0)
+ return null;
+ else
+ return projectFiles.Peek();
+ }
+ }
+ #endregion
+
+ class SharpDevelopLogger : ILogger
+ {
+ MSBuildEngineWorker worker;
+ BuildResults results;
+
+ public SharpDevelopLogger(MSBuildEngineWorker worker)
+ {
+ this.worker = worker;
+ this.results = worker.buildRun.currentResults;
+ }
+
+ void AppendText(string text)
+ {
+ worker.OutputText(text + "\r\n");
+ }
+
+ internal void FlushCurrentError()
+ {
+ if (worker.currentErrorOrWarning != null) {
+ AppendText(worker.currentErrorOrWarning.ToString());
+ worker.currentErrorOrWarning = null;
+ }
+ }
+
+ void OnProjectStarted(object sender, ProjectStartedEventArgs e)
+ {
+ worker.projectFiles.Push(e.ProjectFile);
+ }
+
+ void OnProjectFinished(object sender, ProjectFinishedEventArgs e)
+ {
+ FlushCurrentError();
+ worker.projectFiles.Pop();
+ }
+
+ string activeTaskName;
+
+ void OnTaskStarted(object sender, TaskStartedEventArgs e)
+ {
+ activeTaskName = e.TaskName;
+ if (MSBuildEngine.CompileTaskNames.Contains(e.TaskName.ToLowerInvariant())) {
+ AppendText("${res:MainWindow.CompilerMessages.CompileVerb} " + Path.GetFileNameWithoutExtension(e.ProjectFile));
+ }
+ }
+
+ void OnTaskFinished(object sender, TaskFinishedEventArgs e)
+ {
+ FlushCurrentError();
+ }
+
+ void OnError(object sender, BuildErrorEventArgs e)
+ {
+ AppendError(e.File, e.LineNumber, e.ColumnNumber, e.Code, e.Message, false);
+ }
+
+ void OnWarning(object sender, BuildWarningEventArgs e)
+ {
+ AppendError(e.File, e.LineNumber, e.ColumnNumber, e.Code, e.Message, true);
+ }
+
+ void AppendError(string file, int lineNumber, int columnNumber, string code, string message, bool isWarning)
+ {
+ if (string.Equals(file, activeTaskName, StringComparison.InvariantCultureIgnoreCase)) {
+ file = "";
+ } else if (FileUtility.IsValidFileName(file)) {
+ bool isShortFileName = file == Path.GetFileNameWithoutExtension(file);
+ if (worker.CurrentProjectFile != null) {
+ file = Path.Combine(Path.GetDirectoryName(worker.CurrentProjectFile), file);
+ }
+ if (isShortFileName && !File.Exists(file)) {
+ file = "";
+ }
+ }
+ FlushCurrentError();
+ BuildError error = new BuildError(file, lineNumber, columnNumber, code, message);
+ error.IsWarning = isWarning;
+ results.Add(error);
+ worker.currentErrorOrWarning = error;
+ }
+
+ #region ILogger interface implementation
+ LoggerVerbosity verbosity = LoggerVerbosity.Minimal;
+
+ public LoggerVerbosity Verbosity {
+ get {
+ return verbosity;
+ }
+ set {
+ verbosity = value;
+ }
+ }
+
+ string parameters;
+
+ public string Parameters {
+ get {
+ return parameters;
+ }
+ set {
+ parameters = value;
+ }
+ }
+
+ public void Initialize(IEventSource eventSource)
+ {
+ eventSource.ProjectStarted += new ProjectStartedEventHandler(OnProjectStarted);
+ eventSource.ProjectFinished += new ProjectFinishedEventHandler(OnProjectFinished);
+ eventSource.TaskStarted += new TaskStartedEventHandler(OnTaskStarted);
+ eventSource.TaskFinished += new TaskFinishedEventHandler(OnTaskFinished);
+
+ eventSource.ErrorRaised += new BuildErrorEventHandler(OnError);
+ eventSource.WarningRaised += new BuildWarningEventHandler(OnWarning);
+ }
+
+ public void Shutdown()
+ {
+
+ }
+ #endregion
+ }
+ }
+}
diff --git a/src/Main/Base/Project/Src/Project/MSBuildInternals.cs b/src/Main/Base/Project/Src/Project/MSBuildInternals.cs
index 86f6601618..37d633f2aa 100644
--- a/src/Main/Base/Project/Src/Project/MSBuildInternals.cs
+++ b/src/Main/Base/Project/Src/Project/MSBuildInternals.cs
@@ -192,7 +192,7 @@ namespace ICSharpCode.SharpDevelop.Project
/// which invalidates enumerators over the list of property groups!
///
internal static bool EvaluateCondition(MSBuild.Project project,
- string condition)
+ string condition)
{
const string propertyName = "MSBuildInternalsEvaluateConditionDummyPropertyName";
MSBuild.BuildPropertyGroup pGroup = project.AddNewPropertyGroup(true);
diff --git a/src/Main/Base/Project/Src/Project/Solution/Solution.cs b/src/Main/Base/Project/Src/Project/Solution/Solution.cs
index 3a1f187b50..121844e016 100644
--- a/src/Main/Base/Project/Src/Project/Solution/Solution.cs
+++ b/src/Main/Base/Project/Src/Project/Solution/Solution.cs
@@ -831,10 +831,10 @@ namespace ICSharpCode.SharpDevelop.Project
public void StartBuild(BuildOptions options)
{
- MSBuildBasedProject.RunMSBuild(this.FileName, options.Target.TargetName,
+ MSBuildBasedProject.RunMSBuild(this, null,
this.Preferences.ActiveConfiguration,
this.Preferences.ActivePlatform,
- false, options.Callback, options.AdditionalProperties);
+ options);
}
}
}
diff --git a/src/Main/Base/Project/Src/Services/LanguageBinding/LanguageBindingService.cs b/src/Main/Base/Project/Src/Services/LanguageBinding/LanguageBindingService.cs
index 16fc511b4f..1104481b77 100644
--- a/src/Main/Base/Project/Src/Services/LanguageBinding/LanguageBindingService.cs
+++ b/src/Main/Base/Project/Src/Services/LanguageBinding/LanguageBindingService.cs
@@ -111,6 +111,9 @@ namespace ICSharpCode.SharpDevelop
} else {
ILanguageBinding binding = LanguageBindingService.GetBindingPerProjectFile(location);
if (binding != null) {
+ try {
+ location = Path.GetFullPath(location);
+ } catch (Exception) {}
try {
newProject = binding.LoadProject(provider, location, title);
} catch (XmlException ex) {
diff --git a/src/Main/Core/Project/Src/AddInTree/AddIn/DefaultDoozers/MenuItem/Gui/MenuCommand.cs b/src/Main/Core/Project/Src/AddInTree/AddIn/DefaultDoozers/MenuItem/Gui/MenuCommand.cs
index b02638e884..303603d4e7 100644
--- a/src/Main/Core/Project/Src/AddInTree/AddIn/DefaultDoozers/MenuItem/Gui/MenuCommand.cs
+++ b/src/Main/Core/Project/Src/AddInTree/AddIn/DefaultDoozers/MenuItem/Gui/MenuCommand.cs
@@ -153,7 +153,9 @@ namespace ICSharpCode.Core
{
if (codon != null) {
if (Image == null && codon.Properties.Contains("icon")) {
- Image = ResourceService.GetBitmap(codon.Properties["icon"]);
+ try {
+ Image = ResourceService.GetBitmap(codon.Properties["icon"]);
+ } catch (ResourceNotFoundException) {}
}
Visible = GetVisible();
}
diff --git a/src/SharpDevelop.sln b/src/SharpDevelop.sln
index b3197db26f..d601f062f3 100644
--- a/src/SharpDevelop.sln
+++ b/src/SharpDevelop.sln
@@ -1,6 +1,6 @@
Microsoft Visual Studio Solution File, Format Version 9.00
-# SharpDevelop 2.1.0.2001
+# SharpDevelop 2.1.0.2049
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "AddIns", "AddIns", "{14A277EE-7DF1-4529-B639-7D1EF334C1C5}"
ProjectSection(SolutionItems) = postProject
EndProjectSection
@@ -131,10 +131,10 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Main", "Main", "{5A3EBEBA-0
ProjectSection(SolutionItems) = postProject
EndProjectSection
EndProject
-Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "ICSharpCode.SharpDevelop.Widgets", "Main\ICSharpCode.SharpDevelop.Widgets\Project\ICSharpCode.SharpDevelop.Widgets.csproj", "{8035765F-D51F-4A0C-A746-2FD100E19419}"
-EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "ICSharpCode.SharpDevelop.Dom", "Main\ICSharpCode.SharpDevelop.Dom\Project\ICSharpCode.SharpDevelop.Dom.csproj", "{924EE450-603D-49C1-A8E5-4AFAA31CE6F3}"
EndProject
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "ICSharpCode.SharpDevelop.Widgets", "Main\ICSharpCode.SharpDevelop.Widgets\Project\ICSharpCode.SharpDevelop.Widgets.csproj", "{8035765F-D51F-4A0C-A746-2FD100E19419}"
+EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "ICSharpCode.SharpDevelop.Sda", "Main\ICSharpCode.SharpDevelop.Sda\ICSharpCode.SharpDevelop.Sda.csproj", "{80318B5F-A25D-45AB-8A95-EF31D2370A4C}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "ICSharpCode.SharpDevelop", "Main\Base\Project\ICSharpCode.SharpDevelop.csproj", "{2748AD25-9C63-4E12-877B-4DCE96FBED54}"
@@ -465,7 +465,7 @@ Global
{35CEF10F-2D4C-45F2-9DD1-161E0FEC583C} = {5A3EBEBA-0560-41C1-966B-23F7D03A5486}
{2748AD25-9C63-4E12-877B-4DCE96FBED54} = {5A3EBEBA-0560-41C1-966B-23F7D03A5486}
{80318B5F-A25D-45AB-8A95-EF31D2370A4C} = {5A3EBEBA-0560-41C1-966B-23F7D03A5486}
- {924EE450-603D-49C1-A8E5-4AFAA31CE6F3} = {5A3EBEBA-0560-41C1-966B-23F7D03A5486}
{8035765F-D51F-4A0C-A746-2FD100E19419} = {5A3EBEBA-0560-41C1-966B-23F7D03A5486}
+ {924EE450-603D-49C1-A8E5-4AFAA31CE6F3} = {5A3EBEBA-0560-41C1-966B-23F7D03A5486}
EndGlobalSection
EndGlobal