X-Git-Url: http://wien.tomnetworks.com/gitweb/?a=blobdiff_plain;f=mcs%2Fclass%2FMicrosoft.Build%2FMicrosoft.Build.Internal%2FBuildEngine4.cs;h=4eebb8ac1e2a6a76f54e23dde4d859d91727e422;hb=a0173a7e76ad48889ade46116e516731b170e7c5;hp=249ec4ea768edca0a78d6e1c7fb878b435b60b4f;hpb=e3fe18533357ecc22e0b7d29e1c1a7820b383fe8;p=mono.git diff --git a/mcs/class/Microsoft.Build/Microsoft.Build.Internal/BuildEngine4.cs b/mcs/class/Microsoft.Build/Microsoft.Build.Internal/BuildEngine4.cs index 249ec4ea768..4eebb8ac1e2 100644 --- a/mcs/class/Microsoft.Build/Microsoft.Build.Internal/BuildEngine4.cs +++ b/mcs/class/Microsoft.Build/Microsoft.Build.Internal/BuildEngine4.cs @@ -28,13 +28,14 @@ using System; using System.Collections; using System.Collections.Generic; -using Microsoft.Build.BuildEngine; using Microsoft.Build.Execution; using Microsoft.Build.Framework; using Microsoft.Build.Evaluation; using System.Linq; using System.IO; using Microsoft.Build.Exceptions; +using System.Globalization; +using Microsoft.Build.Construction; namespace Microsoft.Build.Internal { @@ -48,7 +49,7 @@ namespace Microsoft.Build.Internal public BuildEngine4 (BuildSubmission submission) { this.submission = submission; - event_source = new EventSource (); + event_source = new Microsoft.Build.BuildEngine.EventSource (); if (submission.BuildManager.OngoingBuildParameters.Loggers != null) foreach (var l in submission.BuildManager.OngoingBuildParameters.Loggers) l.Initialize (event_source); @@ -56,9 +57,8 @@ namespace Microsoft.Build.Internal BuildSubmission submission; ProjectInstance project; - ProjectTargetInstance current_target; ProjectTaskInstance current_task; - EventSource event_source; + Microsoft.Build.BuildEngine.EventSource event_source; public ProjectCollection Projects { get { return submission.BuildManager.OngoingBuildParameters.ProjectCollection; } @@ -78,8 +78,15 @@ namespace Microsoft.Build.Internal // public void BuildProject (Func checkCancel, BuildResult result, ProjectInstance project, IEnumerable targetNames, IDictionary globalProperties, IDictionary targetOutputs, string toolsVersion) { + if (toolsVersion == null) + throw new ArgumentNullException ("toolsVersion"); + var parameters = submission.BuildManager.OngoingBuildParameters; - var buildTaskFactory = new BuildTaskFactory (BuildTaskDatabase.GetDefaultTaskDatabase (parameters.GetToolset (toolsVersion)), submission.BuildRequest.ProjectInstance.TaskDatabase); + var toolset = parameters.GetToolset (toolsVersion); + if (toolset == null) + throw new InvalidOperationException (string.Format ("Toolset version '{0}' was not resolved to valid toolset", toolsVersion)); + LogMessageEvent (new BuildMessageEventArgs (string.Format ("Using Toolset version {0}.", toolsVersion), null, null, MessageImportance.Low)); + var buildTaskFactory = new BuildTaskFactory (BuildTaskDatabase.GetDefaultTaskDatabase (toolset), submission.BuildRequest.ProjectInstance.TaskDatabase); BuildProject (new InternalBuildArguments () { CheckCancel = checkCancel, Result = result, Project = project, TargetNames = targetNames, GlobalProperties = globalProperties, TargetOutputs = targetOutputs, ToolsVersion = toolsVersion, BuildTaskFactory = buildTaskFactory }); } @@ -106,73 +113,126 @@ namespace Microsoft.Build.Internal var request = submission.BuildRequest; var parameters = submission.BuildManager.OngoingBuildParameters; this.project = args.Project; + + string directoryBackup = Directory.GetCurrentDirectory (); + Directory.SetCurrentDirectory (project.Directory); + event_source.FireBuildStarted (this, new BuildStartedEventArgs ("Build Started", null, DateTime.Now)); - event_source.FireBuildStarted (this, new BuildStartedEventArgs ("Build Started", null)); - - var initialPropertiesFormatted = "Initial Properties:\n" + string.Join (Environment.NewLine, project.Properties.OrderBy (p => p.Name).Select (p => string.Format ("{0} = {1}", p.Name, p.EvaluatedValue)).ToArray ()); - event_source.FireMessageRaised (this, new BuildMessageEventArgs (initialPropertiesFormatted, null, null, MessageImportance.Low)); + try { + + var initialPropertiesFormatted = "Initial Properties:\n" + string.Join (Environment.NewLine, project.Properties.OrderBy (p => p.Name).Select (p => string.Format ("{0} = {1}", p.Name, p.EvaluatedValue)).ToArray ()); + LogMessageEvent (new BuildMessageEventArgs (initialPropertiesFormatted, null, null, MessageImportance.Low)); + var initialItemsFormatted = "Initial Items:\n" + string.Join (Environment.NewLine, project.Items.OrderBy (i => i.ItemType).Select (i => string.Format ("{0} : {1}", i.ItemType, i.EvaluatedInclude)).ToArray ()); + LogMessageEvent (new BuildMessageEventArgs (initialItemsFormatted, null, null, MessageImportance.Low)); + + // null targets -> success. empty targets -> success(!) + if (request.TargetNames == null) + args.Result.OverallResult = BuildResultCode.Success; + else { + foreach (var targetName in (args.TargetNames ?? request.TargetNames).Where (t => t != null)) + BuildTargetByName (targetName, args); - // null targets -> success. empty targets -> success(!) - if (request.TargetNames == null) - args.Result.OverallResult = BuildResultCode.Success; - else { - foreach (var targetName in request.TargetNames.Where (t => t != null)) - args.AddTargetResult (targetName, BuildTargetByName (targetName, args)); - - // FIXME: check .NET behavior, whether cancellation always results in failure. - args.Result.OverallResult = args.CheckCancel () ? BuildResultCode.Failure : args.Result.ResultsByTarget.Select (p => p.Value).Any (r => r.ResultCode == TargetResultCode.Failure) ? BuildResultCode.Failure : BuildResultCode.Success; - } - event_source.FireBuildFinished (this, new BuildFinishedEventArgs ("Build Finished.", null, args.Result.OverallResult == BuildResultCode.Success)); + // FIXME: check .NET behavior, whether cancellation always results in failure. + args.Result.OverallResult = args.CheckCancel () ? BuildResultCode.Failure : args.Result.ResultsByTarget.Any (p => p.Value.ResultCode == TargetResultCode.Failure) ? BuildResultCode.Failure : BuildResultCode.Success; + } + } catch (Exception ex) { + args.Result.OverallResult = BuildResultCode.Failure; + LogErrorEvent (new BuildErrorEventArgs (null, null, project.FullPath, 0, 0, 0, 0, "Unhandled exception occured during a build", null, null)); + LogMessageEvent (new BuildMessageEventArgs ("Exception details: " + ex, null, null, MessageImportance.Low)); + throw; // BuildSubmission re-catches this. + } finally { + event_source.FireBuildFinished (this, new BuildFinishedEventArgs ("Build Finished.", null, args.Result.OverallResult == BuildResultCode.Success, DateTime.Now)); + Directory.SetCurrentDirectory (directoryBackup); + } } - TargetResult BuildTargetByName (string targetName, InternalBuildArguments args) + bool BuildTargetByName (string targetName, InternalBuildArguments args) { - var targetResult = new TargetResult (); - var request = submission.BuildRequest; var parameters = submission.BuildManager.OngoingBuildParameters; ProjectTargetInstance target; + TargetResult dummyResult; + + if (args.Result.ResultsByTarget.TryGetValue (targetName, out dummyResult) && dummyResult.ResultCode == TargetResultCode.Success) { + LogMessageEvent (new BuildMessageEventArgs (string.Format ("Target '{0}' was skipped because it was already built successfully.", targetName), null, null, MessageImportance.Low)); + return true; // do not add result. + } - // FIXME: check skip condition - if (false) - targetResult.Skip (); + var targetResult = new TargetResult (); + // null key is allowed and regarded as blind success(!) (as long as it could retrieve target) - else if (!request.ProjectInstance.Targets.TryGetValue (targetName, out target)) - targetResult.Failure (new InvalidOperationException (string.Format ("target '{0}' was not found in project '{1}'", targetName, project.FullPath))); - else { - current_target = target; + if (!request.ProjectInstance.Targets.TryGetValue (targetName, out target)) + // FIXME: from MSBuild.exe it is given MSB4057. Can we assign a number too? + throw new InvalidOperationException (string.Format ("target '{0}' was not found in project '{1}'", targetName, project.FullPath)); + else if (!args.Project.EvaluateCondition (target.Condition)) { + LogMessageEvent (new BuildMessageEventArgs (string.Format ("Target '{0}' was skipped because condition '{1}' was not met.", target.Name, target.Condition), null, null, MessageImportance.Low)); + targetResult.Skip (); + } else { + // process DependsOnTargets first. + foreach (var dep in project.ExpandString (target.DependsOnTargets).Split (';').Select (s => s.Trim ()).Where (s => !string.IsNullOrEmpty (s))) { + LogMessageEvent (new BuildMessageEventArgs (string.Format ("Target '{0}' depends on '{1}'.", target.Name, dep), null, null, MessageImportance.Low)); + if (!BuildTargetByName (dep, args)) { + LogMessageEvent (new BuildMessageEventArgs (string.Format ("Quit target '{0}', as dependency target '{1}' has failed.", target.Name, dep), null, null, MessageImportance.Low)); + return false; + } + } + + Func creator = s => new TargetOutputTaskItem () { ItemSpec = s }; + + event_source.FireTargetStarted (this, new TargetStartedEventArgs ("Target Started", null, target.Name, project.FullPath, target.FullPath)); try { - if (!DoBuildTarget (targetResult, args)) - return targetResult; + // FIXME: examine in which scenario Inputs/Outputs inconsistency results in errors. Now it rather prevents csproj build. + /*if (!string.IsNullOrEmpty (target.Inputs) != !string.IsNullOrEmpty (target.Outputs)) { + targetResult.Failure (new InvalidProjectFileException (target.Location, null, string.Format ("Target {0} has mismatching Inputs and Outputs specification. When one is specified, another one has to be specified too.", targetName), null, null, null)); + } else*/ { + bool skip = false; + if (!string.IsNullOrEmpty (target.Inputs)) { + var inputs = args.Project.GetAllItems (target.Inputs, string.Empty, creator, creator, s => true, (t, s) => { + }); + if (!inputs.Any ()) { + LogMessageEvent (new BuildMessageEventArgs (string.Format ("Target '{0}' was skipped because there is no input.", target.Name), null, null, MessageImportance.Low)); + skip = true; + } else { + var outputs = args.Project.GetAllItems (target.Outputs, string.Empty, creator, creator, s => true, (t, s) => { + }); + var needsUpdates = GetOlderOutputsThanInputs (inputs, outputs).FirstOrDefault (); + if (needsUpdates != null) + LogMessageEvent (new BuildMessageEventArgs (string.Format ("Target '{0}' needs to be built because new output {1} is needed.", target.Name, needsUpdates.ItemSpec), null, null, MessageImportance.Low)); + else { + LogMessageEvent (new BuildMessageEventArgs (string.Format ("Target '{0}' was skipped because all the outputs are newer than all the inputs.", target.Name), null, null, MessageImportance.Low)); + skip = true; + } + } + } + if (skip) { + targetResult.Skip (); + } else { + if (DoBuildTarget (target, targetResult, args)) { + var items = args.Project.GetAllItems (target.Outputs, string.Empty, creator, creator, s => true, (t, s) => { + }); + targetResult.Success (items); + } + } + } } finally { - current_target = null; + event_source.FireTargetFinished (this, new TargetFinishedEventArgs ("Target Finished", null, targetName, project.FullPath, target.FullPath, targetResult.ResultCode != TargetResultCode.Failure)); } - Func creator = s => new TargetOutputTaskItem () { ItemSpec = s }; - var items = args.Project.GetAllItems (target.Outputs, string.Empty, creator, creator, s => true, (t, s) => { - }); - targetResult.Success (items); - event_source.FireTargetFinished (this, new TargetFinishedEventArgs ("Target Finished", null, targetName, project.FullPath, target.FullPath, true)); } - return targetResult; + args.AddTargetResult (targetName, targetResult); + + return targetResult.ResultCode != TargetResultCode.Failure; } - bool DoBuildTarget (TargetResult targetResult, InternalBuildArguments args) + IEnumerable GetOlderOutputsThanInputs (IEnumerable inputs, IEnumerable outputs) + { + return outputs.Where (o => !File.Exists (o.GetMetadata ("FullPath")) || inputs.Any (i => string.CompareOrdinal (i.GetMetadata ("LastModifiedTime"), o.GetMetadata ("LastModifiedTime")) > 0)); + } + + // FIXME: Exception should be caught at caller site. + bool DoBuildTarget (ProjectTargetInstance target, TargetResult targetResult, InternalBuildArguments args) { var request = submission.BuildRequest; - var target = current_target; - // process DependsOnTargets first. - foreach (var dep in project.ExpandString (target.DependsOnTargets).Split (';').Where (s => !string.IsNullOrEmpty (s)).Select (s => s.Trim ())) { - var result = BuildTargetByName (dep, args); - args.AddTargetResult (dep, result); - if (result.ResultCode == TargetResultCode.Failure) { - targetResult.Failure (null); - return false; - } - } - - event_source.FireTargetStarted (this, new TargetStartedEventArgs ("Target Started", null, target.Name, project.FullPath, target.FullPath)); - // Here we check cancellation (only after TargetStarted event). if (args.CheckCancel ()) { targetResult.Failure (new BuildAbortedException ("Build has canceled")); @@ -200,28 +260,37 @@ namespace Microsoft.Build.Internal if (!args.Project.EvaluateCondition (c.Condition)) continue; foreach (var item in c.Items) { + if (!args.Project.EvaluateCondition (item.Condition)) + continue; Func creator = i => new ProjectItemInstance (project, item.ItemType, item.Metadata.Select (m => new KeyValuePair (m.Name, m.Value)), i); foreach (var ti in project.GetAllItems (item.Include, item.Exclude, creator, creator, s => s == item.ItemType, (ti, s) => ti.SetMetadata ("RecurseDir", s))) itemsToRemove.Add (ti); } } - foreach (var c in target.Children.OfType ()) { - if (!args.Project.EvaluateCondition (c.Condition)) - continue; - throw new NotImplementedException (); - } - // run tasks foreach (var ti in target.Children.OfType ()) { current_task = ti; if (!args.Project.EvaluateCondition (ti.Condition)) { - event_source.FireMessageRaised (this, new BuildMessageEventArgs (string.Format ("Task '{0}' was skipped because condition '{1}' wasn't met.", ti.Name, ti.Condition), null, null, MessageImportance.Low)); + LogMessageEvent (new BuildMessageEventArgs (string.Format ("Task '{0}' was skipped because condition '{1}' wasn't met.", ti.Name, ti.Condition), null, null, MessageImportance.Low)); continue; } - if (!RunBuildTask (ti, targetResult, args)) + if (!RunBuildTask (target, ti, targetResult, args)) return false; } + } catch (Exception ex) { + // fallback task specified by OnError element + foreach (var c in target.Children.OfType ()) { + if (!args.Project.EvaluateCondition (c.Condition)) + continue; + foreach (var fallbackTarget in project.ExpandString (c.ExecuteTargets).Split (';')) + BuildTargetByName (fallbackTarget, args); + } + int line = target.Location != null ? target.Location.Line : 0; + int col = target.Location != null ? target.Location.Column : 0; + LogErrorEvent (new BuildErrorEventArgs (null, null, target.FullPath, line, col, 0, 0, ex.Message, null, null)); + targetResult.Failure (ex); + return false; } finally { // restore temporary property state to the original state. foreach (var p in propsToRestore) { @@ -236,87 +305,116 @@ namespace Microsoft.Build.Internal return true; } - bool RunBuildTask (ProjectTaskInstance ti, TargetResult targetResult, InternalBuildArguments args) + bool RunBuildTask (ProjectTargetInstance target, ProjectTaskInstance taskInstance, TargetResult targetResult, InternalBuildArguments args) { var request = submission.BuildRequest; - var target = current_target; - var host = request.HostServices == null ? null : request.HostServices.GetHostObject (request.ProjectFullPath, target.Name, ti.Name); + var host = request.HostServices == null ? null : request.HostServices.GetHostObject (request.ProjectFullPath, target.Name, taskInstance.Name); // Create Task instance. var factoryIdentityParameters = new Dictionary (); #if NET_4_5 - factoryIdentityParameters ["MSBuildRuntime"] = ti.MSBuildRuntime; - factoryIdentityParameters ["MSBuildArchitecture"] = ti.MSBuildArchitecture; + factoryIdentityParameters ["MSBuildRuntime"] = taskInstance.MSBuildRuntime; + factoryIdentityParameters ["MSBuildArchitecture"] = taskInstance.MSBuildArchitecture; #endif - var task = args.BuildTaskFactory.CreateTask (ti.Name, factoryIdentityParameters, this); - event_source.FireMessageRaised (this, new BuildMessageEventArgs (string.Format ("Using task {0} from {1}", ti.Name, task.GetType ().AssemblyQualifiedName), null, null, MessageImportance.Low)); + var task = args.BuildTaskFactory.CreateTask (taskInstance.Name, factoryIdentityParameters, this); + LogMessageEvent (new BuildMessageEventArgs (string.Format ("Using task {0} from {1}", taskInstance.Name, task.GetType ().AssemblyQualifiedName), null, null, MessageImportance.Low)); task.HostObject = host; task.BuildEngine = this; // Prepare task parameters. - var props = task.GetType ().GetProperties () + var evaluatedTaskParams = taskInstance.Parameters.Select (p => new KeyValuePair (p.Key, project.ExpandString (p.Value))); + + var requiredProps = task.GetType ().GetProperties () .Where (p => p.CanWrite && p.GetCustomAttributes (typeof (RequiredAttribute), true).Any ()); - var missings = props.Where (p => !ti.Parameters.Any (tp => tp.Key.Equals (p.Name, StringComparison.OrdinalIgnoreCase))); + var missings = requiredProps.Where (p => !evaluatedTaskParams.Any (tp => tp.Key.Equals (p.Name, StringComparison.OrdinalIgnoreCase))); if (missings.Any ()) throw new InvalidOperationException (string.Format ("Task {0} of type {1} is used without specifying mandatory property: {2}", - ti.Name, task.GetType (), string.Join (", ", missings.Select (p => p.Name).ToArray ()))); + taskInstance.Name, task.GetType (), string.Join (", ", missings.Select (p => p.Name).ToArray ()))); - foreach (var p in ti.Parameters) { + foreach (var p in evaluatedTaskParams) { + switch (p.Key.ToLower ()) { + case "condition": + case "continueonerror": + continue; + } var prop = task.GetType ().GetProperty (p.Key); - var value = project.ExpandString (p.Value); if (prop == null) - throw new InvalidOperationException (string.Format ("Task {0} does not have property {1}", ti.Name, p.Key)); + throw new InvalidOperationException (string.Format ("Task {0} does not have property {1}", taskInstance.Name, p.Key)); if (!prop.CanWrite) - throw new InvalidOperationException (string.Format ("Task {0} has property {1} but it is read-only.", ti.Name, p.Key)); - var valueInstance = ConvertTo (value, prop.PropertyType); - prop.SetValue (task, valueInstance, null); + throw new InvalidOperationException (string.Format ("Task {0} has property {1} but it is read-only.", taskInstance.Name, p.Key)); + if (string.IsNullOrEmpty (p.Value) && !requiredProps.Contains (prop)) + continue; + try { + var valueInstance = ConvertTo (p.Value, prop.PropertyType); + prop.SetValue (task, valueInstance, null); + } catch (Exception ex) { + throw new InvalidOperationException (string.Format ("Failed to convert '{0}' for property '{1}' of type {2}", p.Value, prop.Name, prop.PropertyType), ex); + } } // Do execute task. - event_source.FireTaskStarted (this, new TaskStartedEventArgs ("Task Started", null, project.FullPath, ti.FullPath, ti.Name)); - var taskSuccess = task.Execute (); + bool taskSuccess = false; + event_source.FireTaskStarted (this, new TaskStartedEventArgs ("Task Started", null, project.FullPath, taskInstance.FullPath, taskInstance.Name)); + try { + taskSuccess = task.Execute (); - if (!taskSuccess) { - event_source.FireTaskFinished (this, new TaskFinishedEventArgs ("Task Finished", null, project.FullPath, ti.FullPath, ti.Name, false)); - targetResult.Failure (null); - if (!ContinueOnError) { - event_source.FireTargetFinished (this, new TargetFinishedEventArgs ("Target Failed", null, target.Name, project.FullPath, target.FullPath, false)); - return false; - } - } else { - // Evaluate task output properties and items. - event_source.FireTaskFinished (this, new TaskFinishedEventArgs ("Task Finished", null, project.FullPath, ti.FullPath, ti.Name, true)); - foreach (var to in ti.Outputs) { - if (!project.EvaluateCondition (to.Condition)) - continue; - var toItem = to as ProjectTaskOutputItemInstance; - var toProp = to as ProjectTaskOutputPropertyInstance; - string taskParameter = toItem != null ? toItem.TaskParameter : toProp.TaskParameter; - var pi = task.GetType ().GetProperty (taskParameter); - if (pi == null) - throw new InvalidOperationException (string.Format ("Task {0} does not have property {1} specified as TaskParameter", ti.Name, toItem.TaskParameter)); - if (!pi.CanRead) - throw new InvalidOperationException (string.Format ("Task {0} has property {1} specified as TaskParameter, but it is write-only", ti.Name, toItem.TaskParameter)); - if (toItem != null) - args.Project.AddItem (toItem.ItemType, ConvertFrom (pi.GetValue (task, null))); - else - args.Project.SetProperty (toProp.PropertyName, ConvertFrom (pi.GetValue (task, null))); + if (!taskSuccess) { + targetResult.Failure (null); + if (!ContinueOnError) { + return false; + } + } else { + // Evaluate task output properties and items. + foreach (var to in taskInstance.Outputs) { + if (!project.EvaluateCondition (to.Condition)) + continue; + var toItem = to as ProjectTaskOutputItemInstance; + var toProp = to as ProjectTaskOutputPropertyInstance; + string taskParameter = toItem != null ? toItem.TaskParameter : toProp.TaskParameter; + var pi = task.GetType ().GetProperty (taskParameter); + if (pi == null) + throw new InvalidOperationException (string.Format ("Task {0} does not have property {1} specified as TaskParameter", taskInstance.Name, toItem.TaskParameter)); + if (!pi.CanRead) + throw new InvalidOperationException (string.Format ("Task {0} has property {1} specified as TaskParameter, but it is write-only", taskInstance.Name, toItem.TaskParameter)); + var value = ConvertFrom (pi.GetValue (task, null)); + if (toItem != null) { + LogMessageEvent (new BuildMessageEventArgs (string.Format ("Output Item {0} from TaskParameter {1}: {2}", toItem.ItemType, toItem.TaskParameter, value), null, null, MessageImportance.Low)); + foreach (var item in value.Split (';')) + args.Project.AddItem (toItem.ItemType, item); + } else { + LogMessageEvent (new BuildMessageEventArgs (string.Format ("Output Property {0} from TaskParameter {1}: {2}", toProp.PropertyName, toProp.TaskParameter, value), null, null, MessageImportance.Low)); + args.Project.SetProperty (toProp.PropertyName, value); + } + } } + } finally { + event_source.FireTaskFinished (this, new TaskFinishedEventArgs ("Task Finished", null, project.FullPath, taskInstance.FullPath, taskInstance.Name, taskSuccess)); } - return true; } object ConvertTo (string source, Type targetType) { - if (targetType == typeof (ITaskItem) || targetType.IsSubclassOf (typeof (ITaskItem))) - return new TargetOutputTaskItem () { ItemSpec = WindowsCompatibilityExtensions.NormalizeFilePath (source) }; + if (targetType == typeof(ITaskItem) || targetType.IsSubclassOf (typeof(ITaskItem))) + return new TargetOutputTaskItem () { ItemSpec = WindowsCompatibilityExtensions.NormalizeFilePath (source.Trim ()) }; if (targetType.IsArray) - return new ArrayList (source.Split (';').Where (s => !string.IsNullOrEmpty (s)).Select (s => ConvertTo (s, targetType.GetElementType ())).ToArray ()) + return new ArrayList (source.Split (';').Select (s => s.Trim ()).Where (s => !string.IsNullOrEmpty (s)).Select (s => ConvertTo (s, targetType.GetElementType ())).ToArray ()) .ToArray (targetType.GetElementType ()); - else - return Convert.ChangeType (source, targetType); + if (targetType == typeof(bool)) { + switch (source != null ? source.ToLower (CultureInfo.InvariantCulture) : string.Empty) { + case "true": + case "yes": + case "on": + return true; + case "false": + case "no": + case "off": + case "": + return false; + } + } + return Convert.ChangeType (source == "" ? null : source, targetType); } string ConvertFrom (object source) @@ -326,7 +424,7 @@ namespace Microsoft.Build.Internal if (source is ITaskItem) return ((ITaskItem) source).ItemSpec; if (source.GetType ().IsArray) - return string.Join (":", ((Array) source).Cast ().Select (o => ConvertFrom (o)).ToArray ()); + return string.Join (";", ((Array) source).Cast ().Select (o => ConvertFrom (o)).ToArray ()); else return (string) Convert.ChangeType (source, typeof (string)); } @@ -455,17 +553,15 @@ namespace Microsoft.Build.Internal #region IBuildEngine2 implementation + // To NOT reuse this IBuildEngine instance for different build, we create another BuildManager and BuildSubmisson and then run it. public bool BuildProjectFile (string projectFileName, string[] targetNames, IDictionary globalProperties, IDictionary targetOutputs, string toolsVersion) { - var proj = GetProjectInstance (projectFileName, toolsVersion); var globalPropertiesThatMakeSense = new Dictionary (); foreach (DictionaryEntry p in globalProperties) globalPropertiesThatMakeSense [(string) p.Key] = (string) p.Value; - var result = new BuildResult (); - var outputs = new Dictionary (); - BuildProject (() => false, result, proj, targetNames, globalPropertiesThatMakeSense, outputs, toolsVersion); - foreach (var p in outputs) - targetOutputs [p.Key] = p.Value; + var result = new BuildManager ().Build (this.submission.BuildManager.OngoingBuildParameters.Clone (), new BuildRequestData (projectFileName, globalPropertiesThatMakeSense, toolsVersion, targetNames, null)); + foreach (var p in result.ResultsByTarget) + targetOutputs [p.Key] = p.Value.Items; return result.OverallResult == BuildResultCode.Success; }