5 // Atsushi Enomoto (atsushi@xamarin.com)
7 // Copyright (C) 2013 Xamarin Inc. (http://www.xamarin.com)
9 // Permission is hereby granted, free of charge, to any person obtaining
10 // a copy of this software and associated documentation files (the
11 // "Software"), to deal in the Software without restriction, including
12 // without limitation the rights to use, copy, modify, merge, publish,
13 // distribute, sublicense, and/or sell copies of the Software, and to
14 // permit persons to whom the Software is furnished to do so, subject to
15 // the following conditions:
17 // The above copyright notice and this permission notice shall be
18 // included in all copies or substantial portions of the Software.
20 // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
21 // EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
22 // MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
23 // NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
24 // LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
25 // OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
26 // WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
29 using System.Collections;
30 using System.Collections.Generic;
31 using Microsoft.Build.Execution;
32 using Microsoft.Build.Framework;
33 using Microsoft.Build.Evaluation;
36 using Microsoft.Build.Exceptions;
37 using System.Globalization;
38 using Microsoft.Build.Construction;
39 using Microsoft.Build.Internal.Expressions;
41 namespace Microsoft.Build.Internal
50 public BuildEngine4 (BuildSubmission submission)
52 this.submission = submission;
53 event_source = new Microsoft.Build.BuildEngine.EventSource ();
54 if (submission.BuildManager.OngoingBuildParameters.Loggers != null)
55 foreach (var l in submission.BuildManager.OngoingBuildParameters.Loggers)
56 l.Initialize (event_source);
59 BuildSubmission submission;
60 ProjectInstance project;
61 ProjectTaskInstance current_task;
62 Microsoft.Build.BuildEngine.EventSource event_source;
64 public ProjectCollection Projects {
65 get { return submission.BuildManager.OngoingBuildParameters.ProjectCollection; }
69 // While we are not faced to implement those features, there are some modern task execution requirements.
71 // This will have to be available for "out of process" nodes (see NodeAffinity).
72 // NodeAffinity is set per project file at BuildManager.HostServices.
73 // When NodeAffinity is set to OutOfProc, it should probably launch different build host
74 // that runs separate build tasks. (.NET has MSBuildTaskHost.exe which I guess is about that.)
76 // Also note that the complete implementation has to support LoadInSeparateAppDomainAttribute
77 // (which is most likely derived from AppDomainIsolatedBuildTask) that marks a task to run
78 // in separate AppDomain.
80 public void BuildProject (Func<bool> checkCancel, BuildResult result, ProjectInstance project, IEnumerable<string> targetNames, IDictionary<string,string> globalProperties, IDictionary<string,string> targetOutputs, string toolsVersion)
82 if (toolsVersion == null)
83 throw new ArgumentNullException ("toolsVersion");
85 var parameters = submission.BuildManager.OngoingBuildParameters;
86 var toolset = parameters.GetToolset (toolsVersion);
88 throw new InvalidOperationException (string.Format ("Toolset version '{0}' was not resolved to valid toolset", toolsVersion));
89 LogMessageEvent (new BuildMessageEventArgs (string.Format ("Using Toolset version {0}.", toolsVersion), null, null, MessageImportance.Low));
90 var buildTaskFactory = new BuildTaskFactory (BuildTaskDatabase.GetDefaultTaskDatabase (toolset), new BuildTaskDatabase (this, submission.BuildRequest.ProjectInstance));
91 BuildProject (new InternalBuildArguments () { CheckCancel = checkCancel, Result = result, Project = project, TargetNames = targetNames, GlobalProperties = globalProperties, TargetOutputs = targetOutputs, ToolsVersion = toolsVersion, BuildTaskFactory = buildTaskFactory });
94 class InternalBuildArguments
96 public Func<bool> CheckCancel;
97 public BuildResult Result;
98 public ProjectInstance Project;
99 public IEnumerable<string> TargetNames;
100 public IDictionary<string,string> GlobalProperties;
101 public IDictionary<string,string> TargetOutputs;
102 public string ToolsVersion;
103 public BuildTaskFactory BuildTaskFactory;
105 public void AddTargetResult (string targetName, TargetResult targetResult)
107 if (!Result.HasResultsForTarget (targetName))
108 Result.AddResultsForTarget (targetName, targetResult);
112 void BuildProject (InternalBuildArguments args)
114 var request = submission.BuildRequest;
115 var parameters = submission.BuildManager.OngoingBuildParameters;
116 this.project = args.Project;
118 string directoryBackup = Directory.GetCurrentDirectory ();
119 Directory.SetCurrentDirectory (project.Directory);
120 event_source.FireBuildStarted (this, new BuildStartedEventArgs ("Build Started", null, DateTime.Now));
124 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 ());
125 LogMessageEvent (new BuildMessageEventArgs (initialPropertiesFormatted, null, null, MessageImportance.Low));
126 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 ());
127 LogMessageEvent (new BuildMessageEventArgs (initialItemsFormatted, null, null, MessageImportance.Low));
129 // null targets -> success. empty targets -> success(!)
130 if (request.TargetNames == null)
131 args.Result.OverallResult = BuildResultCode.Success;
133 foreach (var targetName in (args.TargetNames ?? request.TargetNames).Where (t => t != null))
134 BuildTargetByName (targetName, args);
136 // FIXME: check .NET behavior, whether cancellation always results in failure.
137 args.Result.OverallResult = args.CheckCancel () ? BuildResultCode.Failure : args.Result.ResultsByTarget.Any (p => p.Value.ResultCode == TargetResultCode.Failure) ? BuildResultCode.Failure : BuildResultCode.Success;
139 } catch (Exception ex) {
140 args.Result.OverallResult = BuildResultCode.Failure;
141 LogErrorEvent (new BuildErrorEventArgs (null, null, project.FullPath, 0, 0, 0, 0, "Unhandled exception occured during a build", null, null));
142 LogMessageEvent (new BuildMessageEventArgs ("Exception details: " + ex, null, null, MessageImportance.Low));
143 throw; // BuildSubmission re-catches this.
145 event_source.FireBuildFinished (this, new BuildFinishedEventArgs ("Build Finished.", null, args.Result.OverallResult == BuildResultCode.Success, DateTime.Now));
146 Directory.SetCurrentDirectory (directoryBackup);
150 bool BuildTargetByName (string targetName, InternalBuildArguments args)
152 var request = submission.BuildRequest;
153 var parameters = submission.BuildManager.OngoingBuildParameters;
154 ProjectTargetInstance target;
155 TargetResult dummyResult;
157 if (args.Result.ResultsByTarget.TryGetValue (targetName, out dummyResult) && dummyResult.ResultCode == TargetResultCode.Success) {
158 LogMessageEvent (new BuildMessageEventArgs (string.Format ("Target '{0}' was skipped because it was already built successfully.", targetName), null, null, MessageImportance.Low));
159 return true; // do not add result.
162 var targetResult = new TargetResult ();
164 // null key is allowed and regarded as blind success(!) (as long as it could retrieve target)
165 if (!request.ProjectInstance.Targets.TryGetValue (targetName, out target))
166 // FIXME: from MSBuild.exe it is given MSB4057. Can we assign a number too?
167 throw new InvalidOperationException (string.Format ("target '{0}' was not found in project '{1}'", targetName, project.FullPath));
168 else if (!args.Project.EvaluateCondition (target.Condition)) {
169 LogMessageEvent (new BuildMessageEventArgs (string.Format ("Target '{0}' was skipped because condition '{1}' was not met.", target.Name, target.Condition), null, null, MessageImportance.Low));
170 targetResult.Skip ();
172 // process DependsOnTargets first.
173 foreach (var dep in project.ExpandString (target.DependsOnTargets).Split (';').Select (s => s.Trim ()).Where (s => !string.IsNullOrEmpty (s))) {
174 LogMessageEvent (new BuildMessageEventArgs (string.Format ("Target '{0}' depends on '{1}'.", target.Name, dep), null, null, MessageImportance.Low));
175 if (!BuildTargetByName (dep, args)) {
176 LogMessageEvent (new BuildMessageEventArgs (string.Format ("Quit target '{0}', as dependency target '{1}' has failed.", target.Name, dep), null, null, MessageImportance.Low));
181 Func<string,ITaskItem> creator = s => new TargetOutputTaskItem () { ItemSpec = s };
183 event_source.FireTargetStarted (this, new TargetStartedEventArgs ("Target Started", null, target.Name, project.FullPath, target.FullPath));
185 // FIXME: examine in which scenario Inputs/Outputs inconsistency results in errors. Now it rather prevents csproj build.
186 /*if (!string.IsNullOrEmpty (target.Inputs) != !string.IsNullOrEmpty (target.Outputs)) {
187 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));
190 if (!string.IsNullOrEmpty (target.Inputs)) {
191 var inputs = args.Project.GetAllItems (target.Inputs, string.Empty, creator, creator, s => true, (t, s) => {
193 if (!inputs.Any ()) {
194 LogMessageEvent (new BuildMessageEventArgs (string.Format ("Target '{0}' was skipped because there is no input.", target.Name), null, null, MessageImportance.Low));
197 var outputs = args.Project.GetAllItems (target.Outputs, string.Empty, creator, creator, s => true, (t, s) => {
199 var needsUpdates = GetOlderOutputsThanInputs (inputs, outputs).FirstOrDefault ();
200 if (needsUpdates != null)
201 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));
203 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));
209 targetResult.Skip ();
211 if (DoBuildTarget (target, targetResult, args)) {
212 var items = args.Project.GetAllItems (target.Outputs, string.Empty, creator, creator, s => true, (t, s) => {
214 targetResult.Success (items);
219 event_source.FireTargetFinished (this, new TargetFinishedEventArgs ("Target Finished", null, targetName, project.FullPath, target.FullPath, targetResult.ResultCode != TargetResultCode.Failure));
222 args.AddTargetResult (targetName, targetResult);
224 return targetResult.ResultCode != TargetResultCode.Failure;
227 IEnumerable<ITaskItem> GetOlderOutputsThanInputs (IEnumerable<ITaskItem> inputs, IEnumerable<ITaskItem> outputs)
229 return outputs.Where (o => !File.Exists (o.GetMetadata ("FullPath")) || inputs.Any (i => string.CompareOrdinal (i.GetMetadata ("LastModifiedTime"), o.GetMetadata ("LastModifiedTime")) > 0));
232 // FIXME: Exception should be caught at caller site.
233 bool DoBuildTarget (ProjectTargetInstance target, TargetResult targetResult, InternalBuildArguments args)
235 var request = submission.BuildRequest;
237 // Here we check cancellation (only after TargetStarted event).
238 if (args.CheckCancel ()) {
239 targetResult.Failure (new BuildAbortedException ("Build has canceled"));
244 foreach (var child in target.Children) {
245 // Evaluate additional target properties
246 var tp = child as ProjectPropertyGroupTaskInstance;
248 if (!args.Project.EvaluateCondition (tp.Condition))
250 foreach (var p in tp.Properties) {
251 if (!args.Project.EvaluateCondition (p.Condition))
253 var value = args.Project.ExpandString (p.Value);
254 project.SetProperty (p.Name, value);
258 var ii = child as ProjectItemGroupTaskInstance;
260 if (!args.Project.EvaluateCondition (ii.Condition))
262 foreach (var item in ii.Items) {
263 if (!args.Project.EvaluateCondition (item.Condition))
265 project.AddItem (item.ItemType, project.ExpandString (item.Include));
269 var task = child as ProjectTaskInstance;
272 if (!args.Project.EvaluateCondition (task.Condition)) {
273 LogMessageEvent (new BuildMessageEventArgs (string.Format ("Task '{0}' was skipped because condition '{1}' wasn't met.", task.Name, task.Condition), null, null, MessageImportance.Low));
276 if (!RunBuildTask (target, task, targetResult, args))
280 } catch (Exception ex) {
281 // fallback task specified by OnError element
282 foreach (var c in target.Children.OfType<ProjectOnErrorInstance> ()) {
283 if (!args.Project.EvaluateCondition (c.Condition))
285 foreach (var fallbackTarget in project.ExpandString (c.ExecuteTargets).Split (';'))
286 BuildTargetByName (fallbackTarget, args);
288 int line = target.Location != null ? target.Location.Line : 0;
289 int col = target.Location != null ? target.Location.Column : 0;
290 LogErrorEvent (new BuildErrorEventArgs (null, null, target.FullPath, line, col, 0, 0, ex.Message, null, null));
291 targetResult.Failure (ex);
297 bool RunBuildTask (ProjectTargetInstance target, ProjectTaskInstance taskInstance, TargetResult targetResult, InternalBuildArguments args)
299 var request = submission.BuildRequest;
301 var host = request.HostServices == null ? null : request.HostServices.GetHostObject (request.ProjectFullPath, target.Name, taskInstance.Name);
303 // Create Task instance.
304 var factoryIdentityParameters = new Dictionary<string,string> ();
306 factoryIdentityParameters ["MSBuildRuntime"] = taskInstance.MSBuildRuntime;
307 factoryIdentityParameters ["MSBuildArchitecture"] = taskInstance.MSBuildArchitecture;
309 var task = args.BuildTaskFactory.CreateTask (taskInstance.Name, factoryIdentityParameters, this);
311 throw new InvalidOperationException (string.Format ("TaskFactory {0} returned null Task", args.BuildTaskFactory));
312 LogMessageEvent (new BuildMessageEventArgs (string.Format ("Using task {0} from {1}", taskInstance.Name, task.GetType ()), null, null, MessageImportance.Low));
313 task.HostObject = host;
314 task.BuildEngine = this;
316 // Prepare task parameters.
317 var evaluator = new ExpressionEvaluator (project);
318 var evaluatedTaskParams = taskInstance.Parameters.Select (p => new KeyValuePair<string,string> (p.Key, project.ExpandString (evaluator, p.Value)));
320 var requiredProps = task.GetType ().GetProperties ()
321 .Where (p => p.CanWrite && p.GetCustomAttributes (typeof (RequiredAttribute), true).Any ());
322 var missings = requiredProps.Where (p => !evaluatedTaskParams.Any (tp => tp.Key.Equals (p.Name, StringComparison.OrdinalIgnoreCase)));
324 throw new InvalidOperationException (string.Format ("Task {0} of type {1} is used without specifying mandatory property: {2}",
325 taskInstance.Name, task.GetType (), string.Join (", ", missings.Select (p => p.Name).ToArray ())));
327 foreach (var p in evaluatedTaskParams) {
328 switch (p.Key.ToLower ()) {
330 case "continueonerror":
333 var prop = task.GetType ().GetProperty (p.Key);
335 throw new InvalidOperationException (string.Format ("Task {0} does not have property {1}", taskInstance.Name, p.Key));
337 throw new InvalidOperationException (string.Format ("Task {0} has property {1} but it is read-only.", taskInstance.Name, p.Key));
338 if (string.IsNullOrEmpty (p.Value) && !requiredProps.Contains (prop))
341 prop.SetValue (task, ConvertTo (p.Value, prop.PropertyType, evaluator), null);
342 } catch (Exception ex) {
343 throw new InvalidOperationException (string.Format ("Failed to convert '{0}' for property '{1}' of type {2}", p.Value, prop.Name, prop.PropertyType), ex);
348 bool taskSuccess = false;
349 event_source.FireTaskStarted (this, new TaskStartedEventArgs ("Task Started", null, project.FullPath, taskInstance.FullPath, taskInstance.Name));
351 taskSuccess = task.Execute ();
354 targetResult.Failure (null);
355 if (!ContinueOnError) {
359 // Evaluate task output properties and items.
360 foreach (var to in taskInstance.Outputs) {
361 if (!project.EvaluateCondition (to.Condition))
363 var toItem = to as ProjectTaskOutputItemInstance;
364 var toProp = to as ProjectTaskOutputPropertyInstance;
365 string taskParameter = toItem != null ? toItem.TaskParameter : toProp.TaskParameter;
366 var pi = task.GetType ().GetProperty (taskParameter);
368 throw new InvalidOperationException (string.Format ("Task {0} does not have property {1} specified as TaskParameter", taskInstance.Name, toItem.TaskParameter));
370 throw new InvalidOperationException (string.Format ("Task {0} has property {1} specified as TaskParameter, but it is write-only", taskInstance.Name, toItem.TaskParameter));
371 var value = pi.GetValue (task, null);
372 var valueString = ConvertFrom (value);
373 if (toItem != null) {
374 LogMessageEvent (new BuildMessageEventArgs (string.Format ("Output Item {0} from TaskParameter {1}: {2}", toItem.ItemType, toItem.TaskParameter, valueString), null, null, MessageImportance.Low));
375 Action<ITaskItem> addItem = i => {
376 var metadata = new ArrayList (i.MetadataNames).ToArray ().Cast<string> ().Select (n => new KeyValuePair<string,string> (n, i.GetMetadata (n)));
377 args.Project.AddItem (toItem.ItemType, i.ItemSpec, metadata);
379 var taskItemArray = value as ITaskItem [];
380 if (taskItemArray != null) {
381 foreach (var ti in taskItemArray)
384 var taskItem = value as ITaskItem;
385 if (taskItem != null)
388 foreach (var item in valueString.Split (';'))
389 args.Project.AddItem (toItem.ItemType, item);
392 LogMessageEvent (new BuildMessageEventArgs (string.Format ("Output Property {0} from TaskParameter {1}: {2}", toProp.PropertyName, toProp.TaskParameter, valueString), null, null, MessageImportance.Low));
393 args.Project.SetProperty (toProp.PropertyName, valueString);
398 event_source.FireTaskFinished (this, new TaskFinishedEventArgs ("Task Finished", null, project.FullPath, taskInstance.FullPath, taskInstance.Name, taskSuccess));
403 object ConvertTo (string source, Type targetType, ExpressionEvaluator evaluator)
405 if (targetType == typeof (ITaskItem) || targetType.IsSubclassOf (typeof (ITaskItem))) {
406 var item = evaluator.EvaluatedTaskItems.FirstOrDefault (i => string.Equals (i.ItemSpec, source.Trim (), StringComparison.OrdinalIgnoreCase));
407 var ret = new TargetOutputTaskItem () { ItemSpec = source.Trim () };
409 foreach (string name in item.MetadataNames)
410 ret.SetMetadata (name, item.GetMetadata (name));
413 if (targetType.IsArray)
414 return new ArrayList (source.Split (';').Select (s => s.Trim ()).Where (s => !string.IsNullOrEmpty (s)).Select (s => ConvertTo (s, targetType.GetElementType (), evaluator)).ToArray ())
415 .ToArray (targetType.GetElementType ());
416 if (targetType == typeof (bool)) {
417 switch (source != null ? source.ToLower (CultureInfo.InvariantCulture) : string.Empty) {
429 return Convert.ChangeType (source == "" ? null : source, targetType);
432 string ConvertFrom (object source)
436 if (source is ITaskItem)
437 return ((ITaskItem) source).ItemSpec;
438 if (source.GetType ().IsArray)
439 return string.Join (";", ((Array) source).Cast<object> ().Select (o => ConvertFrom (o)).ToArray ());
441 return (string) Convert.ChangeType (source, typeof (string));
444 class TargetOutputTaskItem : ITaskItem2
446 Hashtable metadata = new Hashtable ();
448 #region ITaskItem2 implementation
449 public string GetMetadataValueEscaped (string metadataName)
451 return ProjectCollection.Escape ((string) metadata [metadataName]);
453 public void SetMetadataValueLiteral (string metadataName, string metadataValue)
455 metadata [metadataName] = ProjectCollection.Unescape (metadataValue);
457 public IDictionary CloneCustomMetadataEscaped ()
459 var ret = new Hashtable ();
460 foreach (DictionaryEntry e in metadata)
461 ret [e.Key] = ProjectCollection.Escape ((string) e.Value);
464 public string EvaluatedIncludeEscaped {
465 get { return ProjectCollection.Escape (ItemSpec); }
466 set { ItemSpec = ProjectCollection.Unescape (value); }
469 #region ITaskItem implementation
470 public IDictionary CloneCustomMetadata ()
472 return new Hashtable (metadata);
474 public void CopyMetadataTo (ITaskItem destinationItem)
476 foreach (DictionaryEntry e in metadata)
477 destinationItem.SetMetadata ((string) e.Key, (string) e.Value);
479 public string GetMetadata (string metadataName)
481 var wk = ProjectCollection.GetWellKnownMetadata (metadataName, ItemSpec, Path.GetFullPath, null);
484 var ret = (string) metadata [metadataName];
485 return ret ?? string.Empty;
487 public void RemoveMetadata (string metadataName)
489 metadata.Remove (metadataName);
491 public void SetMetadata (string metadataName, string metadataValue)
493 metadata [metadataName] = metadataValue;
495 public string ItemSpec { get; set; }
496 public int MetadataCount {
497 get { return metadata.Count; }
499 public ICollection MetadataNames {
500 get { return metadata.Keys; }
504 public override string ToString ()
511 #region IBuildEngine4 implementation
513 // task objects are not in use anyways though...
515 class TaskObjectRegistration
517 public TaskObjectRegistration (object key, object obj, RegisteredTaskObjectLifetime lifetime, bool allowEarlyCollection)
522 AllowEarlyCollection = allowEarlyCollection;
524 public object Key { get; private set; }
525 public object Object { get; private set; }
526 public RegisteredTaskObjectLifetime Lifetime { get; private set; }
527 public bool AllowEarlyCollection { get; private set; }
530 List<TaskObjectRegistration> task_objects = new List<TaskObjectRegistration> ();
532 public object GetRegisteredTaskObject (object key, RegisteredTaskObjectLifetime lifetime)
534 var reg = task_objects.FirstOrDefault (t => t.Key == key && t.Lifetime == lifetime);
535 return reg != null ? reg.Object : null;
538 public void RegisterTaskObject (object key, object obj, RegisteredTaskObjectLifetime lifetime, bool allowEarlyCollection)
540 task_objects.Add (new TaskObjectRegistration (key, obj, lifetime, allowEarlyCollection));
543 public object UnregisterTaskObject (object key, RegisteredTaskObjectLifetime lifetime)
545 var reg = task_objects.FirstOrDefault (t => t.Key == key && t.Lifetime == lifetime);
547 task_objects.Remove (reg);
553 #region IBuildEngine3 implementation
555 public BuildEngineResult BuildProjectFilesInParallel (string[] projectFileNames, string[] targetNames, IDictionary[] globalProperties, IList<string>[] removeGlobalProperties, string[] toolsVersion, bool returnTargetOutputs)
557 throw new NotImplementedException ();
560 public void Reacquire ()
562 throw new NotImplementedException ();
567 throw new NotImplementedException ();
572 #region IBuildEngine2 implementation
574 // To NOT reuse this IBuildEngine instance for different build, we create another BuildManager and BuildSubmisson and then run it.
575 public bool BuildProjectFile (string projectFileName, string[] targetNames, IDictionary globalProperties, IDictionary targetOutputs, string toolsVersion)
577 var globalPropertiesThatMakeSense = new Dictionary<string,string> ();
578 foreach (DictionaryEntry p in globalProperties)
579 globalPropertiesThatMakeSense [(string) p.Key] = (string) p.Value;
580 var result = new BuildManager ().Build (this.submission.BuildManager.OngoingBuildParameters.Clone (), new BuildRequestData (projectFileName, globalPropertiesThatMakeSense, toolsVersion, targetNames, null));
581 foreach (var p in result.ResultsByTarget)
582 targetOutputs [p.Key] = p.Value.Items;
583 return result.OverallResult == BuildResultCode.Success;
586 public bool BuildProjectFilesInParallel (string[] projectFileNames, string[] targetNames, IDictionary[] globalProperties, IDictionary[] targetOutputsPerProject, string[] toolsVersion, bool useResultsCache, bool unloadProjectsOnCompletion)
588 throw new NotImplementedException ();
591 public bool IsRunningMultipleNodes {
593 throw new NotImplementedException ();
597 ProjectInstance GetProjectInstance (string projectFileName, string toolsVersion)
599 string fullPath = Path.GetFullPath (projectFileName);
600 if (submission.BuildRequest.ProjectFullPath == fullPath)
601 return submission.BuildRequest.ProjectInstance;
602 // FIXME: could also be filtered by global properties
603 // http://msdn.microsoft.com/en-us/library/microsoft.build.evaluation.projectcollection.getloadedprojects.aspx
604 var project = Projects.GetLoadedProjects (projectFileName).FirstOrDefault (p => p.ToolsVersion == toolsVersion);
606 throw new InvalidOperationException (string.Format ("Project '{0}' is not loaded", projectFileName));
607 return submission.BuildManager.GetProjectInstanceForBuild (project);
612 #region IBuildEngine implementation
614 public bool BuildProjectFile (string projectFileName, string[] targetNames, IDictionary globalProperties, IDictionary targetOutputs)
616 return BuildProjectFile (projectFileName, targetNames, globalProperties, targetOutputs, Projects.DefaultToolsVersion);
619 public void LogCustomEvent (CustomBuildEventArgs e)
621 event_source.FireCustomEventRaised (this, e);
624 public void LogErrorEvent (BuildErrorEventArgs e)
626 event_source.FireErrorRaised (this, e);
629 public void LogMessageEvent (BuildMessageEventArgs e)
631 event_source.FireMessageRaised (this, e);
634 public void LogWarningEvent (BuildWarningEventArgs e)
636 event_source.FireWarningRaised (this, e);
639 public int ColumnNumberOfTaskNode {
640 get { return current_task.Location != null ? current_task.Location.Column : 0; }
643 public bool ContinueOnError {
644 get { return current_task != null && project.EvaluateCondition (current_task.Condition) && EvaluateContinueOnError (current_task.ContinueOnError); }
647 bool EvaluateContinueOnError (string value)
650 case "WarnAndContinue":
651 case "ErrorAndContinue":
656 // empty means "stop on error", so don't pass empty string to EvaluateCondition().
657 return !string.IsNullOrEmpty (value) && project.EvaluateCondition (value);
660 public int LineNumberOfTaskNode {
661 get { return current_task.Location != null ? current_task.Location.Line : 0; }
664 public string ProjectFileOfTaskNode {
665 get { return current_task.FullPath; }