Browse Source

Fixed SD2-1106: FxCop code analysis not run when building the entire solution

git-svn-id: svn://svn.sharpdevelop.net/sharpdevelop/trunk@2051 1ccf3a8d-04fe-1044-b7c0-cef0b8235c61
shortcuts
Daniel Grunwald 19 years ago
parent
commit
a725d631a5
  1. 14
      src/AddIns/BackendBindings/VBNetBinding/Project/Src/VbcEncodingFixingLogger.cs
  2. 1
      src/AddIns/Misc/CodeAnalysis/Src/AnalysisProjectOptions.cs
  3. 16
      src/AddIns/Misc/CodeAnalysis/Src/FxCopLogger.cs
  4. 6
      src/AddIns/Misc/UnitTesting/Src/RunTestCommands.cs
  5. 1
      src/Libraries/ICSharpCode.Build.Tasks/Project/ICSharpCode.Build.Tasks.csproj
  6. 1
      src/Main/Base/Project/ICSharpCode.SharpDevelop.csproj
  7. 40
      src/Main/Base/Project/Src/Commands/BuildCommands.cs
  8. 2
      src/Main/Base/Project/Src/Commands/DebugCommands.cs
  9. 22
      src/Main/Base/Project/Src/Project/BuildError.cs
  10. 50
      src/Main/Base/Project/Src/Project/BuildResults.cs
  11. 14
      src/Main/Base/Project/Src/Project/MSBuildAdditionalLogger.cs
  12. 25
      src/Main/Base/Project/Src/Project/MSBuildBasedProject.cs
  13. 700
      src/Main/Base/Project/Src/Project/MSBuildEngine.cs
  14. 251
      src/Main/Base/Project/Src/Project/MSBuildEngineWorker.cs
  15. 2
      src/Main/Base/Project/Src/Project/MSBuildInternals.cs
  16. 4
      src/Main/Base/Project/Src/Project/Solution/Solution.cs
  17. 3
      src/Main/Base/Project/Src/Services/LanguageBinding/LanguageBindingService.cs
  18. 4
      src/Main/Core/Project/Src/AddInTree/AddIn/DefaultDoozers/MenuItem/Gui/MenuCommand.cs
  19. 8
      src/SharpDevelop.sln

14
src/AddIns/BackendBindings/VBNetBinding/Project/Src/VbcEncodingFixingLogger.cs

@ -18,18 +18,18 @@ namespace VBNetBinding
/// </summary> /// </summary>
public class VbcEncodingFixingLogger : IMSBuildAdditionalLogger 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 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 { public LoggerVerbosity Verbosity {
@ -80,8 +80,8 @@ namespace VBNetBinding
void FixMessage() void FixMessage()
{ {
engine.CurrentErrorOrWarning.ErrorText = FixEncoding(engine.CurrentErrorOrWarning.ErrorText); engineWorker.CurrentErrorOrWarning.ErrorText = FixEncoding(engineWorker.CurrentErrorOrWarning.ErrorText);
engine.CurrentErrorOrWarning.FileName = FixEncoding(engine.CurrentErrorOrWarning.FileName); engineWorker.CurrentErrorOrWarning.FileName = FixEncoding(engineWorker.CurrentErrorOrWarning.FileName);
} }
static string FixEncoding(string encoding) static string FixEncoding(string encoding)

1
src/AddIns/Misc/CodeAnalysis/Src/AnalysisProjectOptions.cs

@ -295,6 +295,7 @@ namespace ICSharpCode.CodeAnalysis
public ConfigBinding(AnalysisProjectOptions po) public ConfigBinding(AnalysisProjectOptions po)
{ {
this.po = po; this.po = po;
this.TreatPropertyValueAsLiteral = false;
po.OptionChanged += delegate { po.OptionChanged += delegate {
Helper.IsDirty = true; Helper.IsDirty = true;
}; };

16
src/AddIns/Misc/CodeAnalysis/Src/FxCopLogger.cs

@ -17,18 +17,18 @@ namespace ICSharpCode.CodeAnalysis
{ {
public class FxCopLogger : IMSBuildAdditionalLogger 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 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 { public LoggerVerbosity Verbosity {
@ -54,7 +54,7 @@ namespace ICSharpCode.CodeAnalysis
public void Initialize(IEventSource eventSource) public void Initialize(IEventSource eventSource)
{ {
this.eventSource = 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.ErrorRaised += OnError;
eventSource.WarningRaised += OnWarning; eventSource.WarningRaised += OnWarning;
} }
@ -85,12 +85,12 @@ namespace ICSharpCode.CodeAnalysis
string category, string checkId, string subcategory) string category, string checkId, string subcategory)
{ {
string[] moreData = (subcategory ?? "").Split('|'); string[] moreData = (subcategory ?? "").Split('|');
BuildError err = engine.CurrentErrorOrWarning; BuildError err = engineWorker.CurrentErrorOrWarning;
if (FileUtility.IsValidFileName(file) && if (FileUtility.IsValidFileName(file) &&
Path.GetFileName(file) == "SharpDevelop.CodeAnalysis.targets") { Path.GetFileName(file) == "SharpDevelop.CodeAnalysis.targets") {
err.FileName = null; err.FileName = null;
} }
IProject project = ProjectService.GetProject(engine.CurrentProjectFile); IProject project = ProjectService.GetProject(engineWorker.CurrentProjectFile);
if (project != null) { if (project != null) {
IProjectContent pc = ParserService.GetProjectContent(project); IProjectContent pc = ParserService.GetProjectContent(project);
if (pc != null) { if (pc != null) {

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

@ -167,7 +167,7 @@ namespace ICSharpCode.UnitTesting
{ {
BuildProjectBeforeTestRun build = new BuildProjectBeforeTestRun(project); BuildProjectBeforeTestRun build = new BuildProjectBeforeTestRun(project);
build.BuildComplete += delegate { build.BuildComplete += delegate {
OnBuildComplete(project, fixture, test); OnBuildComplete(build.LastBuildResults, project, fixture, test);
}; };
build.Run(); build.Run();
} }
@ -277,9 +277,9 @@ namespace ICSharpCode.UnitTesting
/// <summary> /// <summary>
/// Runs the test for the project after a successful build. /// Runs the test for the project after a successful build.
/// </summary> /// </summary>
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(); UnitTestApplicationStartHelper helper = new UnitTestApplicationStartHelper();
helper.Initialize(project, fixture, test); helper.Initialize(project, fixture, test);
helper.Results = Path.GetTempFileName(); helper.Results = Path.GetTempFileName();

1
src/Libraries/ICSharpCode.Build.Tasks/Project/ICSharpCode.Build.Tasks.csproj

@ -16,6 +16,7 @@
<FileAlignment>4096</FileAlignment> <FileAlignment>4096</FileAlignment>
<WarningLevel>4</WarningLevel> <WarningLevel>4</WarningLevel>
<TreatWarningsAsErrors>false</TreatWarningsAsErrors> <TreatWarningsAsErrors>false</TreatWarningsAsErrors>
<RunCodeAnalysis>False</RunCodeAnalysis>
</PropertyGroup> </PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' "> <PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' ">
<OutputPath>..\..\..\..\bin\</OutputPath> <OutputPath>..\..\..\..\bin\</OutputPath>

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

@ -64,6 +64,7 @@
<Compile Include="Src\Internal\Undo\IUndoableOperation.cs" /> <Compile Include="Src\Internal\Undo\IUndoableOperation.cs" />
<Compile Include="Src\Internal\Undo\UndoQueue.cs" /> <Compile Include="Src\Internal\Undo\UndoQueue.cs" />
<Compile Include="Src\Internal\Undo\UndoStack.cs" /> <Compile Include="Src\Internal\Undo\UndoStack.cs" />
<Compile Include="Src\Project\MSBuildEngineWorker.cs" />
<Compile Include="Src\Project\ProjectPropertyChangedEventArgs.cs" /> <Compile Include="Src\Project\ProjectPropertyChangedEventArgs.cs" />
<Compile Include="Src\Services\Debugger\BreakpointBookmark.cs" /> <Compile Include="Src\Services\Debugger\BreakpointBookmark.cs" />
<Compile Include="Src\Services\Debugger\BreakpointBookmarkEventArgs.cs" /> <Compile Include="Src\Services\Debugger\BreakpointBookmarkEventArgs.cs" />

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

@ -48,13 +48,20 @@ namespace ICSharpCode.SharpDevelop.Project.Commands
BeforeBuild(); BeforeBuild();
StartBuild(); StartBuild();
} else { } else {
MSBuildEngine.AddNoSingleFileCompilationError(); AddNoSingleFileCompilationError();
} }
} }
BuildResults lastBuildResults;
public BuildResults LastBuildResults {
get { return lastBuildResults; }
}
protected void CallbackMethod(BuildResults results) protected void CallbackMethod(BuildResults results)
{ {
MSBuildEngine.ShowResults(results); lastBuildResults = results;
ShowResults(results);
AfterBuild(); AfterBuild();
if (BuildComplete != null) if (BuildComplete != null)
BuildComplete(this, EventArgs.Empty); BuildComplete(this, EventArgs.Empty);
@ -63,6 +70,35 @@ namespace ICSharpCode.SharpDevelop.Project.Commands
public abstract void StartBuild(); public abstract void StartBuild();
public event EventHandler BuildComplete; 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();
}
}
}
/// <summary>
/// Notifies the user that #develp's internal MSBuildEngine
/// implementation only supports compiling solutions and projects;
/// it does not allow compiling individual files.
/// </summary>
/// <remarks>Adds a message to the <see cref="TaskService"/> and
/// shows the <see cref="ErrorListPad"/>.</remarks>
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 public sealed class Build : AbstractBuildMenuCommand

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

@ -22,7 +22,7 @@ namespace ICSharpCode.SharpDevelop.Project.Commands
{ {
Build build = new Build(); Build build = new Build();
build.BuildComplete += delegate { build.BuildComplete += delegate {
if (MSBuildEngine.LastErrorCount == 0) { if (build.LastBuildResults.ErrorCount == 0) {
IProject startupProject = ProjectService.OpenSolution.StartupProject; IProject startupProject = ProjectService.OpenSolution.StartupProject;
if (startupProject != null) { if (startupProject != null) {
startupProject.Start(withDebugger); startupProject.Start(withDebugger);

22
src/Main/Base/Project/Src/Project/BuildError.cs

@ -11,17 +11,27 @@ using ICSharpCode.Core;
namespace ICSharpCode.SharpDevelop.Project namespace ICSharpCode.SharpDevelop.Project
{ {
[Serializable]
public class BuildError public class BuildError
{ {
public BuildError() public BuildError()
{ {
this.line = 0; this.line = -1;
this.column = 0; this.column = -1;
this.errorCode = string.Empty; this.errorCode = string.Empty;
this.errorText = string.Empty; this.errorText = string.Empty;
this.fileName = 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) public BuildError(string fileName, int line, int column, string errorCode, string errorText)
{ {
this.line = line; this.line = line;
@ -37,6 +47,7 @@ namespace ICSharpCode.SharpDevelop.Project
string fileName; string fileName;
int line; int line;
bool warning; bool warning;
[NonSerialized]
object tag; object tag;
string contextMenuAddInTreeEntry; string contextMenuAddInTreeEntry;
@ -94,6 +105,13 @@ namespace ICSharpCode.SharpDevelop.Project
} }
} }
/// <summary>
/// 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.
/// </summary>
/// <remarks>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.</remarks>
public object Tag { public object Tag {
get { get {
return tag; return tag;

50
src/Main/Base/Project/Src/Project/BuildResults.cs

@ -7,6 +7,7 @@
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Collections.ObjectModel;
namespace ICSharpCode.SharpDevelop.Project namespace ICSharpCode.SharpDevelop.Project
{ {
@ -29,21 +30,54 @@ namespace ICSharpCode.SharpDevelop.Project
public class BuildResults public class BuildResults
{ {
List<BuildError> errors = new List<BuildError>(); List<BuildError> errors = new List<BuildError>();
ReadOnlyCollection<BuildError> readOnlyErrors;
BuildResultCode result; BuildResultCode result;
int errorCount, warningCount;
public List<BuildError> Errors { /// <summary>
get { /// Adds a build error/warning to the results.
return errors; /// This method is thread-safe.
/// </summary>
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 { /// <summary>
/// Gets the list of build errors or warnings.
/// This property is thread-safe.
/// </summary>
public ReadOnlyCollection<BuildError> Errors {
get { get {
return result; lock (errors) {
} if (readOnlyErrors == null) {
set { readOnlyErrors = Array.AsReadOnly(errors.ToArray());
result = value; }
return readOnlyErrors;
}
} }
} }
public BuildResultCode Result {
get { return result; }
set { result = value; }
}
public int ErrorCount {
get { return errorCount; }
}
public int WarningCount {
get { return warningCount; }
}
} }
} }

14
src/Main/Base/Project/Src/Project/MSBuildAdditionalLogger.cs

@ -16,7 +16,7 @@ namespace ICSharpCode.SharpDevelop.Project
/// </summary> /// </summary>
public interface IMSBuildAdditionalLogger public interface IMSBuildAdditionalLogger
{ {
ILogger CreateLogger(MSBuildEngine engine); ILogger CreateLogger(MSBuildEngineWorker engineWorker);
} }
/// <summary> /// <summary>
@ -67,23 +67,23 @@ namespace ICSharpCode.SharpDevelop.Project
addIn = codon.AddIn; 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 private class TaskBoundAdditionalLogger : ILogger
{ {
TaskBoundAdditionalLoggerDescriptor desc; TaskBoundAdditionalLoggerDescriptor desc;
MSBuildEngine engine; MSBuildEngineWorker engineWorker;
ILogger baseLogger; ILogger baseLogger;
bool isActive; bool isActive;
public TaskBoundAdditionalLogger(TaskBoundAdditionalLoggerDescriptor desc, MSBuildEngine engine) public TaskBoundAdditionalLogger(TaskBoundAdditionalLoggerDescriptor desc, MSBuildEngineWorker engineWorker)
{ {
this.desc = desc; this.desc = desc;
this.engine = engine; this.engineWorker = engineWorker;
} }
void CreateBaseLogger() void CreateBaseLogger()
@ -93,7 +93,7 @@ namespace ICSharpCode.SharpDevelop.Project
baseLogger = obj as ILogger; baseLogger = obj as ILogger;
IMSBuildAdditionalLogger addLog = obj as IMSBuildAdditionalLogger; IMSBuildAdditionalLogger addLog = obj as IMSBuildAdditionalLogger;
if (addLog != null) { if (addLog != null) {
baseLogger = addLog.CreateLogger(engine); baseLogger = addLog.CreateLogger(engineWorker);
} }
} }
} }

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

@ -737,34 +737,19 @@ namespace ICSharpCode.SharpDevelop.Project
#region Building #region Building
public override void StartBuild(BuildOptions options) public override void StartBuild(BuildOptions options)
{ {
RunMSBuild(this.FileName, options.Target.TargetName, RunMSBuild(this.ParentSolution, this,
this.ActiveConfiguration, this.ActivePlatform, this.ActiveConfiguration, this.ActivePlatform, options);
true, options.Callback, options.AdditionalProperties);
} }
internal static void RunMSBuild(string fileName, string target, string configuration, string platform, bool isSingleProject, BuildCallback callback, IDictionary<string, string> additionalProperties) internal static void RunMSBuild(Solution solution, IProject project,
string configuration, string platform, BuildOptions options)
{ {
WorkbenchSingleton.Workbench.GetPad(typeof(CompilerMessageView)).BringPadToFront(); WorkbenchSingleton.Workbench.GetPad(typeof(CompilerMessageView)).BringPadToFront();
MSBuildEngine engine = new MSBuildEngine(); 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<string, string> pair in additionalProperties) {
engine.AdditionalProperties.Add(pair.Key, pair.Value);
}
}
engine.Configuration = configuration; engine.Configuration = configuration;
engine.Platform = platform; engine.Platform = platform;
engine.MessageView = TaskService.BuildMessageViewCategory; engine.MessageView = TaskService.BuildMessageViewCategory;
if (target == null) { engine.Run(solution, project, options);
engine.Run(fileName, callback);
} else {
engine.Run(fileName, new string[] { target }, callback);
}
} }
#endregion #endregion

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

@ -9,6 +9,7 @@ using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.IO; using System.IO;
using System.Threading; using System.Threading;
using System.Text.RegularExpressions;
using ICSharpCode.Core; using ICSharpCode.Core;
using ICSharpCode.SharpDevelop.Gui; using ICSharpCode.SharpDevelop.Gui;
@ -17,18 +18,21 @@ using Microsoft.Build.Framework;
namespace ICSharpCode.SharpDevelop.Project namespace ICSharpCode.SharpDevelop.Project
{ {
// let Project refer to the class, not to the namespace
using Project = Microsoft.Build.BuildEngine.Project;
/// <summary> /// <summary>
/// Class responsible for building a project using MSBuild. /// Class responsible for building a project using MSBuild.
/// Is called by MSBuildProject. /// Is called by MSBuildProject.
/// </summary> /// </summary>
public class MSBuildEngine public sealed class MSBuildEngine
{ {
/// <summary> /// <summary>
/// Gets a list of the task names that cause a "Compiling ..." log message. /// Gets a list of the task names that cause a "Compiling ..." log message.
/// You can add items to this list by putting strings into /// You can add items to this list by putting strings into
/// "/SharpDevelop/MSBuildEngine/CompileTaskNames". /// "/SharpDevelop/MSBuildEngine/CompileTaskNames".
/// </summary> /// </summary>
public static readonly List<string> CompileTaskNames; public static readonly ICollection<string> CompileTaskNames;
/// <summary> /// <summary>
/// Gets a list where addins can add additional properties for use in MsBuild. /// Gets a list where addins can add additional properties for use in MsBuild.
@ -51,10 +55,10 @@ namespace ICSharpCode.SharpDevelop.Project
static MSBuildEngine() static MSBuildEngine()
{ {
CompileTaskNames = AddInTree.BuildItems<string>("/SharpDevelop/MSBuildEngine/CompileTaskNames", null, false); CompileTaskNames = new Set<string>(
for (int i = 0; i < CompileTaskNames.Count; i++) { AddInTree.BuildItems<string>("/SharpDevelop/MSBuildEngine/CompileTaskNames", null, false),
CompileTaskNames[i] = CompileTaskNames[i].ToLowerInvariant(); StringComparer.OrdinalIgnoreCase
} );
AdditionalTargetFiles = AddInTree.BuildItems<string>("/SharpDevelop/MSBuildEngine/AdditionalTargetFiles", null, false); AdditionalTargetFiles = AddInTree.BuildItems<string>("/SharpDevelop/MSBuildEngine/AdditionalTargetFiles", null, false);
AdditionalMSBuildLoggers = AddInTree.BuildItems<IMSBuildAdditionalLogger>("/SharpDevelop/MSBuildEngine/AdditionalLoggers", null, false); AdditionalMSBuildLoggers = AddInTree.BuildItems<IMSBuildAdditionalLogger>("/SharpDevelop/MSBuildEngine/AdditionalLoggers", null, false);
@ -62,46 +66,7 @@ namespace ICSharpCode.SharpDevelop.Project
MSBuildProperties.Add("SharpDevelopBinPath", Path.GetDirectoryName(typeof(MSBuildEngine).Assembly.Location)); MSBuildProperties.Add("SharpDevelopBinPath", Path.GetDirectoryName(typeof(MSBuildEngine).Assembly.Location));
} }
#region relocated from ICSharpCode.SharpDevelop.Project.Commands.Build in BuildCommands.cs #region Properties
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();
}
}
}
/// <summary>
/// Notifies the user that #develp's internal MSBuildEngine
/// implementation only supports compiling solutions and projects;
/// it does not allow compiling individual files.
/// </summary>
/// <remarks>Adds a message to the <see cref="TaskService"/> and
/// shows the <see cref="ErrorListPad"/>.</remarks>
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
MessageViewCategory messageView; MessageViewCategory messageView;
/// <summary> /// <summary>
@ -116,14 +81,6 @@ namespace ICSharpCode.SharpDevelop.Project
} }
} }
SortedList<string, string> additionalProperties = new SortedList<string, string>();
public IDictionary<string, string> AdditionalProperties {
get {
return additionalProperties;
}
}
string configuration; string configuration;
/// <summary> /// <summary>
@ -153,279 +110,538 @@ namespace ICSharpCode.SharpDevelop.Project
platform = value; platform = value;
} }
} }
#endregion
BuildError currentErrorOrWarning;
/// <summary>
/// Gets the last build error/warning created by the default
/// SharpDevelop logger.
/// </summary>
public BuildError CurrentErrorOrWarning {
get {
return currentErrorOrWarning;
}
}
Stack<string> projectFiles = new Stack<string>();
/// <summary>
/// Gets the name of the currently building project file.
/// </summary>
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);
}
volatile static bool isRunning = false; 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) { if (isRunning) {
BuildResults results = new BuildResults(); BuildResults results = new BuildResults();
results.Result = BuildResultCode.MSBuildAlreadyRunning; results.Result = BuildResultCode.MSBuildAlreadyRunning;
results.Errors.Add(new BuildError(null, 0, 0, null, ResourceService.GetString("MainWindow.CompilerMessages.MSBuildAlreadyRunning"))); results.Add(new BuildError(null, ResourceService.GetString("MainWindow.CompilerMessages.MSBuildAlreadyRunning")));
callback(results); if (options.Callback != null) {
options.Callback(results);
}
} else { } else {
isRunning = true; isRunning = true;
Thread thread = new Thread(new ThreadStarter(buildFile, targets, this, callback).Run); Thread thread = new Thread(new BuildRun(solution, project, options, this).RunMainBuild);
thread.Name = "MSBuildEngine"; thread.Name = "MSBuildEngine main worker";
thread.SetApartmentState(ApartmentState.STA); thread.SetApartmentState(ApartmentState.STA);
thread.Start(); thread.Start();
} }
} }
class ThreadStarter internal sealed class BuildRun
{ {
string buildFile; Solution solution;
string[] targets; IProject project;
MSBuildEngine engine; BuildOptions options;
BuildCallback callback; MSBuildEngine parentEngine;
internal BuildResults currentResults = new BuildResults();
List<ProjectToBuild> projectsToBuild = new List<ProjectToBuild>();
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.solution = solution;
this.buildFile = buildFile; this.project = project;
this.targets = targets; this.options = options;
this.engine = engine; this.parentEngine = parentEngine;
this.callback = callback;
} }
#region Main Build
[STAThread] [STAThread]
public void Run() internal void RunMainBuild()
{ {
BuildResults results = this.engine.currentResults; try {
LoggingService.Debug("Run MSBuild on " + buildFile); PrepareBuild();
} catch (Exception ex) {
Engine engine = MSBuildInternals.CreateEngine(); MessageService.ShowError(ex);
if (this.engine.Configuration != null) {
engine.GlobalProperties.SetProperty("Configuration", this.engine.Configuration);
}
if (this.engine.Platform != null) {
engine.GlobalProperties.SetProperty("Platform", this.engine.Platform);
} }
foreach (KeyValuePair<string, string> entry in MSBuildProperties) { StartWorkerBuild();
engine.GlobalProperties.SetProperty(entry.Key, entry.Value); }
void Finish()
{
LoggingService.Debug("MSBuild finished");
MSBuildEngine.isRunning = false;
if (currentResults.Result == BuildResultCode.None) {
currentResults.Result = BuildResultCode.Success;
} }
foreach (KeyValuePair<string, string> entry in this.engine.additionalProperties) { if (currentResults.Result == BuildResultCode.Success) {
engine.GlobalProperties.SetProperty(entry.Key, entry.Value); 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}");
} }
if (options.Callback != null) {
SharpDevelopLogger logger = new SharpDevelopLogger(this.engine); WorkbenchSingleton.MainForm.BeginInvoke(options.Callback, currentResults);
engine.RegisterLogger(logger);
foreach (IMSBuildAdditionalLogger loggerProvider in MSBuildEngine.AdditionalMSBuildLoggers) {
engine.RegisterLogger(loggerProvider.CreateLogger(this.engine));
} }
}
void PrepareBuild()
{
parentEngine.MessageView.AppendLine("${res:MainWindow.CompilerMessages.BuildStarted}");
Microsoft.Build.BuildEngine.Project project = engine.CreateNewProject(); if (project == null) {
try { LoggingService.Debug("Parsing solution file " + solution.FileName);
project.Load(buildFile);
foreach (string targetFile in AdditionalTargetFiles) { Engine engine = CreateEngine();
project.AddNewImport(targetFile, null); 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(); SortProjectsToBuild();
this.engine.currentResults = null;
} }
} #endregion
class SharpDevelopLogger : ILogger
{
MSBuildEngine engine;
BuildResults results;
public SharpDevelopLogger(MSBuildEngine engine) #region Worker build
{ int workerCount;
this.engine = engine; int maxWorkerCount;
this.results = engine.currentResults;
}
void AppendText(string text) /// <summary>
/// Runs the first worker on this thread and creates new threads if required
/// </summary>
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<MSBuildEngineWorker> unusedWorkers = new Queue<MSBuildEngineWorker>();
/// <summary>
/// Runs one worker thread
/// </summary>
[STAThread]
void RunWorkerBuild()
{ {
if (engine.currentErrorOrWarning != null) { LoggingService.Debug("Build Worker thread started");
AppendText(engine.currentErrorOrWarning.ToString()); MSBuildEngineWorker worker = null;
engine.currentErrorOrWarning = 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) int lastUniqueWorkerID;
{
AppendText("${res:MainWindow.CompilerMessages.BuildStarted}");
}
void OnBuildFinished(object sender, BuildFinishedEventArgs e) /// <summary>
/// Find available work and run it on the specified worker.
/// </summary>
bool RunWorkerInternal(MSBuildEngineWorker worker)
{ {
if (e.Succeeded) { ProjectToBuild nextFreeProject = null;
AppendText("${res:MainWindow.CompilerMessages.BuildFinished}"); lock (projectsToBuild) {
StatusBarService.SetMessage("${res:MainWindow.CompilerMessages.BuildFinished}"); foreach (ProjectToBuild ptb in projectsToBuild) {
} else { if (ptb.buildStarted == false && ptb.DependenciesSatisfied()) {
AppendText("${res:MainWindow.CompilerMessages.BuildFailed}"); if (nextFreeProject == null) {
StatusBarService.SetMessage("${res:MainWindow.CompilerMessages.BuildFailed}"); 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
/// <summary>
/// Queue of output text to write when lock is released.
///
/// Also serves as object to ensure access to outputLockIsAquired is thread-safe.
/// </summary>
Queue<string> queuedOutputText = new Queue<string>();
volatile bool outputLockIsAquired;
internal bool TryAquireOutputLock()
{ {
engine.projectFiles.Push(e.ProjectFile); lock (queuedOutputText) {
StatusBarService.SetMessage("${res:MainWindow.CompilerMessages.BuildVerb} " + Path.GetFileNameWithoutExtension(e.ProjectFile) + "..."); if (outputLockIsAquired) {
return false;
} else {
outputLockIsAquired = true;
return true;
}
}
} }
void OnProjectFinished(object sender, ProjectFinishedEventArgs e) internal void ReleaseOutputLock()
{ {
FlushCurrentError(); lock (queuedOutputText) {
engine.projectFiles.Pop(); outputLockIsAquired = false;
if (engine.projectFiles.Count > 0) { while (queuedOutputText.Count > 0) {
StatusBarService.SetMessage("${res:MainWindow.CompilerMessages.BuildVerb} " + Path.GetFileNameWithoutExtension(engine.CurrentProjectFile) + "..."); parentEngine.MessageView.AppendText(queuedOutputText.Dequeue());
}
} }
} }
string activeTaskName; internal void EnqueueTextForAppendWhenOutputLockIsReleased(string text)
void OnTaskStarted(object sender, TaskStartedEventArgs e)
{ {
activeTaskName = e.TaskName; lock (queuedOutputText) {
if (CompileTaskNames.Contains(e.TaskName.ToLowerInvariant())) { if (outputLockIsAquired) {
AppendText("${res:MainWindow.CompilerMessages.CompileVerb} " + Path.GetFileNameWithoutExtension(e.ProjectFile)); 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<string, string> entry in MSBuildProperties) {
engine.GlobalProperties.SetProperty(entry.Key, entry.Value);
}
if (options.AdditionalProperties != null) {
foreach (KeyValuePair<string, string> 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)) { // get the build target to call
file = ""; Target mainBuildTarget = solution.Targets[options.Target.TargetName];
} else if (FileUtility.IsValidFileName(file)) { if (mainBuildTarget == null) {
bool isShortFileName = file == Path.GetFileNameWithoutExtension(file); currentResults.Result = BuildResultCode.BuildFileError;
if (engine.CurrentProjectFile != null) { currentResults.Add(new BuildError(this.solution.FileName, "Target '" + options.Target + "' not supported by solution."));
file = Path.Combine(Path.GetDirectoryName(engine.CurrentProjectFile), file); return false;
}
// example of mainBuildTarget:
// <Target Name="Build" Condition="'$(CurrentSolutionConfigurationContents)' != ''">
// <CallTarget Targets="Main\ICSharpCode_SharpDevelop;Main\ICSharpCode_Core;Main\StartUp;Tools" RunEachTargetSeparately="true" />
// </Target>
List<BuildTask> mainBuildTargetTasks = Linq.ToList(Linq.CastTo<BuildTask>(mainBuildTarget));
if (mainBuildTargetTasks.Count != 1
|| mainBuildTargetTasks[0].Name != "CallTarget")
{
return InvalidTarget(mainBuildTarget);
}
List<Target> solutionTargets = new List<Target>();
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<string, ProjectToBuild> projectsToBuildDict = new Dictionary<string, ProjectToBuild>();
// now look through targets that took like this:
// <Target Name="Main\ICSharpCode_Core" Condition="'$(CurrentSolutionConfigurationContents)' != ''">
// <MSBuild Projects="Main\Core\Project\ICSharpCode.Core.csproj" Properties="Configuration=Debug; Platform=AnyCPU; BuildingSolutionFile=true; CurrentSolutionConfigurationContents=$(CurrentSolutionConfigurationContents); SolutionDir=$(SolutionDir); SolutionExt=$(SolutionExt); SolutionFileName=$(SolutionFileName); SolutionName=$(SolutionName); SolutionPath=$(SolutionPath)" Condition=" ('$(Configuration)' == 'Debug') and ('$(Platform)' == 'Any CPU') " />
// <MSBuild Projects="Main\Core\Project\ICSharpCode.Core.csproj" Properties="Configuration=Release; Platform=AnyCPU; BuildingSolutionFile=true; CurrentSolutionConfigurationContents=$(CurrentSolutionConfigurationContents); SolutionDir=$(SolutionDir); SolutionExt=$(SolutionExt); SolutionFileName=$(SolutionFileName); SolutionName=$(SolutionName); SolutionPath=$(SolutionPath)" Condition=" ('$(Configuration)' == 'Release') and ('$(Platform)' == 'Any CPU') " />
// </Target>
// and add those targets to the "projectsToBuild" list.
foreach (Target target in solutionTargets) {
List<BuildTask> tasks = Linq.ToList(Linq.CastTo<BuildTask>(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(); return true;
BuildError error = new BuildError(file, lineNumber, columnNumber, code, message);
error.IsWarning = isWarning;
results.Errors.Add(error);
engine.currentErrorOrWarning = error;
} }
#region ILogger interface implementation /// <summary>
LoggerVerbosity verbosity = LoggerVerbosity.Minimal; /// Adds an error message that the specified target is invalid and returns false.
/// </summary>
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 { #region ParseMSBuildProject
get { Dictionary<IProject, ProjectToBuild> parseMSBuildProjectProjectsToBuildDict = new Dictionary<IProject, ProjectToBuild>();
return verbosity;
/// <summary>
/// Adds a ProjectToBuild item for the project and it's project references.
/// Returns the added item, or null if an error occured.
/// </summary>
ProjectToBuild ParseMSBuildProject(IProject project)
{
ProjectToBuild ptb;
if (parseMSBuildProjectProjectsToBuildDict.TryGetValue(project, out ptb)) {
// only add each project once, reuse existing ProjectToBuild
return ptb;
} }
set { ptb = new ProjectToBuild(project.FileName, options.Target.TargetName);
verbosity = value; 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
/// <summary>
/// Recursively count dependencies and sort projects (most important first).
/// This decreases the number of waiting workers on multi-processor builds
/// </summary>
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 { /// <summary>
get { /// Recursively increment requiredByCount on ptb and all its dependencies
return parameters; /// </summary>
static void IncrementRequiredByCount(ProjectToBuild ptb)
{
if (ptb.visitFlag == 1) {
return;
} }
set { if (ptb.visitFlag == -1) {
parameters = value; throw new DependencyCycleException();
} }
ptb.visitFlag = -1;
ptb.requiredByCount++;
ptb.dependencies.ForEach(IncrementRequiredByCount);
ptb.visitFlag = 1;
} }
public void Initialize(IEventSource eventSource) class DependencyCycleException : Exception {}
#endregion
}
/// <summary>
/// node used for project dependency graph
/// </summary>
internal class ProjectToBuild
{
// information required to build the project
internal string file;
internal string targets;
internal string configuration, platform;
internal List<ProjectToBuild> dependencies = new List<ProjectToBuild>();
internal bool DependenciesSatisfied()
{ {
eventSource.BuildStarted += new BuildStartedEventHandler(OnBuildStarted); return dependencies.TrueForAll(delegate(ProjectToBuild p) { return p.buildFinished; });
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);
} }
public void Shutdown() /// <summary>
/// Number of projects that are directly or indirectly dependent on this project.
/// Used in SortProjectsToBuild step.
/// </summary>
internal int requiredByCount;
/// <summary>
/// Mark already visited nodes. 0 = not visited, -1 = visiting, 1 = visited
/// Used in SortProjectsToBuild step.
/// </summary>
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
} }
} }
} }

251
src/Main/Base/Project/Src/Project/MSBuildEngineWorker.cs

@ -0,0 +1,251 @@
// <file>
// <copyright see="prj:///doc/copyright.txt"/>
// <license see="prj:///doc/license.txt"/>
// <owner name="Daniel Grunwald" email="daniel@danielgrunwald.de"/>
// <version>$Revision$</version>
// </file>
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;
/// <summary>
/// Gets the last build error/warning created by the default
/// SharpDevelop logger.
/// </summary>
public BuildError CurrentErrorOrWarning {
get {
return currentErrorOrWarning;
}
}
Stack<string> projectFiles = new Stack<string>();
/// <summary>
/// Gets the name of the currently building project file.
/// </summary>
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
}
}
}

2
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! /// which invalidates enumerators over the list of property groups!
/// </summary> /// </summary>
internal static bool EvaluateCondition(MSBuild.Project project, internal static bool EvaluateCondition(MSBuild.Project project,
string condition) string condition)
{ {
const string propertyName = "MSBuildInternalsEvaluateConditionDummyPropertyName"; const string propertyName = "MSBuildInternalsEvaluateConditionDummyPropertyName";
MSBuild.BuildPropertyGroup pGroup = project.AddNewPropertyGroup(true); MSBuild.BuildPropertyGroup pGroup = project.AddNewPropertyGroup(true);

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

@ -831,10 +831,10 @@ namespace ICSharpCode.SharpDevelop.Project
public void StartBuild(BuildOptions options) public void StartBuild(BuildOptions options)
{ {
MSBuildBasedProject.RunMSBuild(this.FileName, options.Target.TargetName, MSBuildBasedProject.RunMSBuild(this, null,
this.Preferences.ActiveConfiguration, this.Preferences.ActiveConfiguration,
this.Preferences.ActivePlatform, this.Preferences.ActivePlatform,
false, options.Callback, options.AdditionalProperties); options);
} }
} }
} }

3
src/Main/Base/Project/Src/Services/LanguageBinding/LanguageBindingService.cs

@ -111,6 +111,9 @@ namespace ICSharpCode.SharpDevelop
} else { } else {
ILanguageBinding binding = LanguageBindingService.GetBindingPerProjectFile(location); ILanguageBinding binding = LanguageBindingService.GetBindingPerProjectFile(location);
if (binding != null) { if (binding != null) {
try {
location = Path.GetFullPath(location);
} catch (Exception) {}
try { try {
newProject = binding.LoadProject(provider, location, title); newProject = binding.LoadProject(provider, location, title);
} catch (XmlException ex) { } catch (XmlException ex) {

4
src/Main/Core/Project/Src/AddInTree/AddIn/DefaultDoozers/MenuItem/Gui/MenuCommand.cs

@ -153,7 +153,9 @@ namespace ICSharpCode.Core
{ {
if (codon != null) { if (codon != null) {
if (Image == null && codon.Properties.Contains("icon")) { if (Image == null && codon.Properties.Contains("icon")) {
Image = ResourceService.GetBitmap(codon.Properties["icon"]); try {
Image = ResourceService.GetBitmap(codon.Properties["icon"]);
} catch (ResourceNotFoundException) {}
} }
Visible = GetVisible(); Visible = GetVisible();
} }

8
src/SharpDevelop.sln

@ -1,6 +1,6 @@
 
Microsoft Visual Studio Solution File, Format Version 9.00 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}" Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "AddIns", "AddIns", "{14A277EE-7DF1-4529-B639-7D1EF334C1C5}"
ProjectSection(SolutionItems) = postProject ProjectSection(SolutionItems) = postProject
EndProjectSection EndProjectSection
@ -131,10 +131,10 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Main", "Main", "{5A3EBEBA-0
ProjectSection(SolutionItems) = postProject ProjectSection(SolutionItems) = postProject
EndProjectSection EndProjectSection
EndProject 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}" Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "ICSharpCode.SharpDevelop.Dom", "Main\ICSharpCode.SharpDevelop.Dom\Project\ICSharpCode.SharpDevelop.Dom.csproj", "{924EE450-603D-49C1-A8E5-4AFAA31CE6F3}"
EndProject 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}" Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "ICSharpCode.SharpDevelop.Sda", "Main\ICSharpCode.SharpDevelop.Sda\ICSharpCode.SharpDevelop.Sda.csproj", "{80318B5F-A25D-45AB-8A95-EF31D2370A4C}"
EndProject EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "ICSharpCode.SharpDevelop", "Main\Base\Project\ICSharpCode.SharpDevelop.csproj", "{2748AD25-9C63-4E12-877B-4DCE96FBED54}" 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} {35CEF10F-2D4C-45F2-9DD1-161E0FEC583C} = {5A3EBEBA-0560-41C1-966B-23F7D03A5486}
{2748AD25-9C63-4E12-877B-4DCE96FBED54} = {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} {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} {8035765F-D51F-4A0C-A746-2FD100E19419} = {5A3EBEBA-0560-41C1-966B-23F7D03A5486}
{924EE450-603D-49C1-A8E5-4AFAA31CE6F3} = {5A3EBEBA-0560-41C1-966B-23F7D03A5486}
EndGlobalSection EndGlobalSection
EndGlobal EndGlobal

Loading…
Cancel
Save