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