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;
40 namespace Microsoft.Build.Internal
49 public BuildEngine4 (BuildSubmission submission)
51 this.submission = submission;
52 event_source = new Microsoft.Build.BuildEngine.EventSource ();
53 if (submission.BuildManager.OngoingBuildParameters.Loggers != null)
54 foreach (var l in submission.BuildManager.OngoingBuildParameters.Loggers)
55 l.Initialize (event_source);
58 BuildSubmission submission;
59 ProjectInstance project;
60 ProjectTaskInstance current_task;
61 Microsoft.Build.BuildEngine.EventSource event_source;
63 public ProjectCollection Projects {
64 get { return submission.BuildManager.OngoingBuildParameters.ProjectCollection; }
68 // While we are not faced to implement those features, there are some modern task execution requirements.
70 // This will have to be available for "out of process" nodes (see NodeAffinity).
71 // NodeAffinity is set per project file at BuildManager.HostServices.
72 // When NodeAffinity is set to OutOfProc, it should probably launch different build host
73 // that runs separate build tasks. (.NET has MSBuildTaskHost.exe which I guess is about that.)
75 // Also note that the complete implementation has to support LoadInSeparateAppDomainAttribute
76 // (which is most likely derived from AppDomainIsolatedBuildTask) that marks a task to run
77 // in separate AppDomain.
79 public void BuildProject (Func<bool> checkCancel, BuildResult result, ProjectInstance project, IEnumerable<string> targetNames, IDictionary<string,string> globalProperties, IDictionary<string,string> targetOutputs, string toolsVersion)
81 if (toolsVersion == null)
82 throw new ArgumentNullException ("toolsVersion");
84 var parameters = submission.BuildManager.OngoingBuildParameters;
85 var toolset = parameters.GetToolset (toolsVersion);
87 throw new InvalidOperationException (string.Format ("Toolset version '{0}' was not resolved to valid toolset", toolsVersion));
88 LogMessageEvent (new BuildMessageEventArgs (string.Format ("Using Toolset version {0}.", toolsVersion), null, null, MessageImportance.Low));
89 var buildTaskFactory = new BuildTaskFactory (BuildTaskDatabase.GetDefaultTaskDatabase (toolset), submission.BuildRequest.ProjectInstance.TaskDatabase);
90 BuildProject (new InternalBuildArguments () { CheckCancel = checkCancel, Result = result, Project = project, TargetNames = targetNames, GlobalProperties = globalProperties, TargetOutputs = targetOutputs, ToolsVersion = toolsVersion, BuildTaskFactory = buildTaskFactory });
93 class InternalBuildArguments
95 public Func<bool> CheckCancel;
96 public BuildResult Result;
97 public ProjectInstance Project;
98 public IEnumerable<string> TargetNames;
99 public IDictionary<string,string> GlobalProperties;
100 public IDictionary<string,string> TargetOutputs;
101 public string ToolsVersion;
102 public BuildTaskFactory BuildTaskFactory;
104 public void AddTargetResult (string targetName, TargetResult targetResult)
106 if (!Result.HasResultsForTarget (targetName))
107 Result.AddResultsForTarget (targetName, targetResult);
111 void BuildProject (InternalBuildArguments args)
113 var request = submission.BuildRequest;
114 var parameters = submission.BuildManager.OngoingBuildParameters;
115 this.project = args.Project;
117 string directoryBackup = Directory.GetCurrentDirectory ();
118 Directory.SetCurrentDirectory (project.Directory);
119 event_source.FireBuildStarted (this, new BuildStartedEventArgs ("Build Started", null, DateTime.Now));
123 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 ());
124 LogMessageEvent (new BuildMessageEventArgs (initialPropertiesFormatted, null, null, MessageImportance.Low));
125 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 ());
126 LogMessageEvent (new BuildMessageEventArgs (initialItemsFormatted, null, null, MessageImportance.Low));
128 // null targets -> success. empty targets -> success(!)
129 if (request.TargetNames == null)
130 args.Result.OverallResult = BuildResultCode.Success;
132 foreach (var targetName in (args.TargetNames ?? request.TargetNames).Where (t => t != null))
133 BuildTargetByName (targetName, args);
135 // FIXME: check .NET behavior, whether cancellation always results in failure.
136 args.Result.OverallResult = args.CheckCancel () ? BuildResultCode.Failure : args.Result.ResultsByTarget.Any (p => p.Value.ResultCode == TargetResultCode.Failure) ? BuildResultCode.Failure : BuildResultCode.Success;
138 } catch (Exception ex) {
139 args.Result.OverallResult = BuildResultCode.Failure;
140 LogErrorEvent (new BuildErrorEventArgs (null, null, project.FullPath, 0, 0, 0, 0, "Unhandled exception occured during a build", null, null));
141 LogMessageEvent (new BuildMessageEventArgs ("Exception details: " + ex, null, null, MessageImportance.Low));
142 throw; // BuildSubmission re-catches this.
144 event_source.FireBuildFinished (this, new BuildFinishedEventArgs ("Build Finished.", null, args.Result.OverallResult == BuildResultCode.Success, DateTime.Now));
145 Directory.SetCurrentDirectory (directoryBackup);
149 bool BuildTargetByName (string targetName, InternalBuildArguments args)
151 var request = submission.BuildRequest;
152 var parameters = submission.BuildManager.OngoingBuildParameters;
153 ProjectTargetInstance target;
154 TargetResult dummyResult;
156 if (args.Result.ResultsByTarget.TryGetValue (targetName, out dummyResult) && dummyResult.ResultCode == TargetResultCode.Success) {
157 LogMessageEvent (new BuildMessageEventArgs (string.Format ("Target '{0}' was skipped because it was already built successfully.", targetName), null, null, MessageImportance.Low));
158 return true; // do not add result.
161 var targetResult = new TargetResult ();
163 // null key is allowed and regarded as blind success(!) (as long as it could retrieve target)
164 if (!request.ProjectInstance.Targets.TryGetValue (targetName, out target))
165 // FIXME: from MSBuild.exe it is given MSB4057. Can we assign a number too?
166 throw new InvalidOperationException (string.Format ("target '{0}' was not found in project '{1}'", targetName, project.FullPath));
167 else if (!args.Project.EvaluateCondition (target.Condition)) {
168 LogMessageEvent (new BuildMessageEventArgs (string.Format ("Target '{0}' was skipped because condition '{1}' was not met.", target.Name, target.Condition), null, null, MessageImportance.Low));
169 targetResult.Skip ();
171 // process DependsOnTargets first.
172 foreach (var dep in project.ExpandString (target.DependsOnTargets).Split (';').Select (s => s.Trim ()).Where (s => !string.IsNullOrEmpty (s))) {
173 LogMessageEvent (new BuildMessageEventArgs (string.Format ("Target '{0}' depends on '{1}'.", target.Name, dep), null, null, MessageImportance.Low));
174 if (!BuildTargetByName (dep, args)) {
175 LogMessageEvent (new BuildMessageEventArgs (string.Format ("Quit target '{0}', as dependency target '{1}' has failed.", target.Name, dep), null, null, MessageImportance.Low));
180 Func<string,ITaskItem> creator = s => new TargetOutputTaskItem () { ItemSpec = s };
182 event_source.FireTargetStarted (this, new TargetStartedEventArgs ("Target Started", null, target.Name, project.FullPath, target.FullPath));
184 // FIXME: examine in which scenario Inputs/Outputs inconsistency results in errors. Now it rather prevents csproj build.
185 /*if (!string.IsNullOrEmpty (target.Inputs) != !string.IsNullOrEmpty (target.Outputs)) {
186 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));
189 if (!string.IsNullOrEmpty (target.Inputs)) {
190 var inputs = args.Project.GetAllItems (target.Inputs, string.Empty, creator, creator, s => true, (t, s) => {
192 if (!inputs.Any ()) {
193 LogMessageEvent (new BuildMessageEventArgs (string.Format ("Target '{0}' was skipped because there is no input.", target.Name), null, null, MessageImportance.Low));
196 var outputs = args.Project.GetAllItems (target.Outputs, string.Empty, creator, creator, s => true, (t, s) => {
198 var needsUpdates = GetOlderOutputsThanInputs (inputs, outputs).FirstOrDefault ();
199 if (needsUpdates != null)
200 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));
202 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));
208 targetResult.Skip ();
210 if (DoBuildTarget (target, targetResult, args)) {
211 var items = args.Project.GetAllItems (target.Outputs, string.Empty, creator, creator, s => true, (t, s) => {
213 targetResult.Success (items);
218 event_source.FireTargetFinished (this, new TargetFinishedEventArgs ("Target Finished", null, targetName, project.FullPath, target.FullPath, targetResult.ResultCode != TargetResultCode.Failure));
221 args.AddTargetResult (targetName, targetResult);
223 return targetResult.ResultCode != TargetResultCode.Failure;
226 IEnumerable<ITaskItem> GetOlderOutputsThanInputs (IEnumerable<ITaskItem> inputs, IEnumerable<ITaskItem> outputs)
228 return outputs.Where (o => !File.Exists (o.GetMetadata ("FullPath")) || inputs.Any (i => string.CompareOrdinal (i.GetMetadata ("LastModifiedTime"), o.GetMetadata ("LastModifiedTime")) > 0));
231 // FIXME: Exception should be caught at caller site.
232 bool DoBuildTarget (ProjectTargetInstance target, TargetResult targetResult, InternalBuildArguments args)
234 var request = submission.BuildRequest;
236 // Here we check cancellation (only after TargetStarted event).
237 if (args.CheckCancel ()) {
238 targetResult.Failure (new BuildAbortedException ("Build has canceled"));
242 var propsToRestore = new Dictionary<string,string> ();
243 var itemsToRemove = new List<ProjectItemInstance> ();
245 // Evaluate additional target properties
246 foreach (var c in target.Children.OfType<ProjectPropertyGroupTaskInstance> ()) {
247 if (!args.Project.EvaluateCondition (c.Condition))
249 foreach (var p in c.Properties) {
250 if (!args.Project.EvaluateCondition (p.Condition))
252 var value = args.Project.ExpandString (p.Value);
253 propsToRestore.Add (p.Name, project.GetPropertyValue (value));
254 project.SetProperty (p.Name, value);
258 // Evaluate additional target items
259 foreach (var c in target.Children.OfType<ProjectItemGroupTaskInstance> ()) {
260 if (!args.Project.EvaluateCondition (c.Condition))
262 foreach (var item in c.Items) {
263 if (!args.Project.EvaluateCondition (item.Condition))
265 Func<string,ProjectItemInstance> creator = i => new ProjectItemInstance (project, item.ItemType, item.Metadata.Select (m => new KeyValuePair<string,string> (m.Name, m.Value)), i);
266 foreach (var ti in project.GetAllItems (item.Include, item.Exclude, creator, creator, s => s == item.ItemType, (ti, s) => ti.SetMetadata ("RecurseDir", s)))
267 itemsToRemove.Add (ti);
272 foreach (var ti in target.Children.OfType<ProjectTaskInstance> ()) {
274 if (!args.Project.EvaluateCondition (ti.Condition)) {
275 LogMessageEvent (new BuildMessageEventArgs (string.Format ("Task '{0}' was skipped because condition '{1}' wasn't met.", ti.Name, ti.Condition), null, null, MessageImportance.Low));
278 if (!RunBuildTask (target, ti, targetResult, args))
281 } catch (Exception ex) {
282 // fallback task specified by OnError element
283 foreach (var c in target.Children.OfType<ProjectOnErrorInstance> ()) {
284 if (!args.Project.EvaluateCondition (c.Condition))
286 foreach (var fallbackTarget in project.ExpandString (c.ExecuteTargets).Split (';'))
287 BuildTargetByName (fallbackTarget, args);
289 int line = target.Location != null ? target.Location.Line : 0;
290 int col = target.Location != null ? target.Location.Column : 0;
291 LogErrorEvent (new BuildErrorEventArgs (null, null, target.FullPath, line, col, 0, 0, ex.Message, null, null));
292 targetResult.Failure (ex);
295 // restore temporary property state to the original state.
296 foreach (var p in propsToRestore) {
297 if (p.Value == string.Empty)
298 project.RemoveProperty (p.Key);
300 project.SetProperty (p.Key, p.Value);
302 foreach (var item in itemsToRemove)
303 project.RemoveItem (item);
308 bool RunBuildTask (ProjectTargetInstance target, ProjectTaskInstance taskInstance, TargetResult targetResult, InternalBuildArguments args)
310 var request = submission.BuildRequest;
312 var host = request.HostServices == null ? null : request.HostServices.GetHostObject (request.ProjectFullPath, target.Name, taskInstance.Name);
314 // Create Task instance.
315 var factoryIdentityParameters = new Dictionary<string,string> ();
317 factoryIdentityParameters ["MSBuildRuntime"] = taskInstance.MSBuildRuntime;
318 factoryIdentityParameters ["MSBuildArchitecture"] = taskInstance.MSBuildArchitecture;
320 var task = args.BuildTaskFactory.CreateTask (taskInstance.Name, factoryIdentityParameters, this);
321 LogMessageEvent (new BuildMessageEventArgs (string.Format ("Using task {0} from {1}", taskInstance.Name, task.GetType ().AssemblyQualifiedName), null, null, MessageImportance.Low));
322 task.HostObject = host;
323 task.BuildEngine = this;
325 // Prepare task parameters.
326 var evaluatedTaskParams = taskInstance.Parameters.Select (p => new KeyValuePair<string,string> (p.Key, project.ExpandString (p.Value)));
328 var requiredProps = task.GetType ().GetProperties ()
329 .Where (p => p.CanWrite && p.GetCustomAttributes (typeof (RequiredAttribute), true).Any ());
330 var missings = requiredProps.Where (p => !evaluatedTaskParams.Any (tp => tp.Key.Equals (p.Name, StringComparison.OrdinalIgnoreCase)));
332 throw new InvalidOperationException (string.Format ("Task {0} of type {1} is used without specifying mandatory property: {2}",
333 taskInstance.Name, task.GetType (), string.Join (", ", missings.Select (p => p.Name).ToArray ())));
335 foreach (var p in evaluatedTaskParams) {
336 switch (p.Key.ToLower ()) {
338 case "continueonerror":
341 var prop = task.GetType ().GetProperty (p.Key);
343 throw new InvalidOperationException (string.Format ("Task {0} does not have property {1}", taskInstance.Name, p.Key));
345 throw new InvalidOperationException (string.Format ("Task {0} has property {1} but it is read-only.", taskInstance.Name, p.Key));
346 if (string.IsNullOrEmpty (p.Value) && !requiredProps.Contains (prop))
349 var valueInstance = ConvertTo (p.Value, prop.PropertyType);
350 prop.SetValue (task, valueInstance, null);
351 } catch (Exception ex) {
352 throw new InvalidOperationException (string.Format ("Failed to convert '{0}' for property '{1}' of type {2}", p.Value, prop.Name, prop.PropertyType), ex);
357 bool taskSuccess = false;
358 event_source.FireTaskStarted (this, new TaskStartedEventArgs ("Task Started", null, project.FullPath, taskInstance.FullPath, taskInstance.Name));
360 taskSuccess = task.Execute ();
363 targetResult.Failure (null);
364 if (!ContinueOnError) {
368 // Evaluate task output properties and items.
369 foreach (var to in taskInstance.Outputs) {
370 if (!project.EvaluateCondition (to.Condition))
372 var toItem = to as ProjectTaskOutputItemInstance;
373 var toProp = to as ProjectTaskOutputPropertyInstance;
374 string taskParameter = toItem != null ? toItem.TaskParameter : toProp.TaskParameter;
375 var pi = task.GetType ().GetProperty (taskParameter);
377 throw new InvalidOperationException (string.Format ("Task {0} does not have property {1} specified as TaskParameter", taskInstance.Name, toItem.TaskParameter));
379 throw new InvalidOperationException (string.Format ("Task {0} has property {1} specified as TaskParameter, but it is write-only", taskInstance.Name, toItem.TaskParameter));
380 var value = ConvertFrom (pi.GetValue (task, null));
381 if (toItem != null) {
382 LogMessageEvent (new BuildMessageEventArgs (string.Format ("Output Item {0} from TaskParameter {1}: {2}", toItem.ItemType, toItem.TaskParameter, value), null, null, MessageImportance.Low));
383 foreach (var item in value.Split (';'))
384 args.Project.AddItem (toItem.ItemType, item);
386 LogMessageEvent (new BuildMessageEventArgs (string.Format ("Output Property {0} from TaskParameter {1}: {2}", toProp.PropertyName, toProp.TaskParameter, value), null, null, MessageImportance.Low));
387 args.Project.SetProperty (toProp.PropertyName, value);
392 event_source.FireTaskFinished (this, new TaskFinishedEventArgs ("Task Finished", null, project.FullPath, taskInstance.FullPath, taskInstance.Name, taskSuccess));
397 object ConvertTo (string source, Type targetType)
399 if (targetType == typeof(ITaskItem) || targetType.IsSubclassOf (typeof(ITaskItem)))
400 return new TargetOutputTaskItem () { ItemSpec = WindowsCompatibilityExtensions.NormalizeFilePath (source.Trim ()) };
401 if (targetType.IsArray)
402 return new ArrayList (source.Split (';').Select (s => s.Trim ()).Where (s => !string.IsNullOrEmpty (s)).Select (s => ConvertTo (s, targetType.GetElementType ())).ToArray ())
403 .ToArray (targetType.GetElementType ());
404 if (targetType == typeof(bool)) {
405 switch (source != null ? source.ToLower (CultureInfo.InvariantCulture) : string.Empty) {
417 return Convert.ChangeType (source == "" ? null : source, targetType);
420 string ConvertFrom (object source)
424 if (source is ITaskItem)
425 return ((ITaskItem) source).ItemSpec;
426 if (source.GetType ().IsArray)
427 return string.Join (";", ((Array) source).Cast<object> ().Select (o => ConvertFrom (o)).ToArray ());
429 return (string) Convert.ChangeType (source, typeof (string));
432 class TargetOutputTaskItem : ITaskItem2
434 Hashtable metadata = new Hashtable ();
436 #region ITaskItem2 implementation
437 public string GetMetadataValueEscaped (string metadataName)
439 return ProjectCollection.Escape ((string) metadata [metadataName]);
441 public void SetMetadataValueLiteral (string metadataName, string metadataValue)
443 metadata [metadataName] = ProjectCollection.Unescape (metadataValue);
445 public IDictionary CloneCustomMetadataEscaped ()
447 var ret = new Hashtable ();
448 foreach (DictionaryEntry e in metadata)
449 ret [e.Key] = ProjectCollection.Escape ((string) e.Value);
452 public string EvaluatedIncludeEscaped {
453 get { return ProjectCollection.Escape (ItemSpec); }
454 set { ItemSpec = ProjectCollection.Unescape (value); }
457 #region ITaskItem implementation
458 public IDictionary CloneCustomMetadata ()
460 return new Hashtable (metadata);
462 public void CopyMetadataTo (ITaskItem destinationItem)
464 foreach (DictionaryEntry e in metadata)
465 destinationItem.SetMetadata ((string) e.Key, (string) e.Value);
467 public string GetMetadata (string metadataName)
469 var wk = ProjectCollection.GetWellKnownMetadata (metadataName, ItemSpec, Path.GetFullPath, null);
472 return (string) metadata [metadataName];
474 public void RemoveMetadata (string metadataName)
476 metadata.Remove (metadataName);
478 public void SetMetadata (string metadataName, string metadataValue)
480 metadata [metadataName] = metadataValue;
482 public string ItemSpec { get; set; }
483 public int MetadataCount {
484 get { return metadata.Count; }
486 public ICollection MetadataNames {
487 get { return metadata.Keys; }
493 #region IBuildEngine4 implementation
495 // task objects are not in use anyways though...
497 class TaskObjectRegistration
499 public TaskObjectRegistration (object key, object obj, RegisteredTaskObjectLifetime lifetime, bool allowEarlyCollection)
504 AllowEarlyCollection = allowEarlyCollection;
506 public object Key { get; private set; }
507 public object Object { get; private set; }
508 public RegisteredTaskObjectLifetime Lifetime { get; private set; }
509 public bool AllowEarlyCollection { get; private set; }
512 List<TaskObjectRegistration> task_objects = new List<TaskObjectRegistration> ();
514 public object GetRegisteredTaskObject (object key, RegisteredTaskObjectLifetime lifetime)
516 var reg = task_objects.FirstOrDefault (t => t.Key == key && t.Lifetime == lifetime);
517 return reg != null ? reg.Object : null;
520 public void RegisterTaskObject (object key, object obj, RegisteredTaskObjectLifetime lifetime, bool allowEarlyCollection)
522 task_objects.Add (new TaskObjectRegistration (key, obj, lifetime, allowEarlyCollection));
525 public object UnregisterTaskObject (object key, RegisteredTaskObjectLifetime lifetime)
527 var reg = task_objects.FirstOrDefault (t => t.Key == key && t.Lifetime == lifetime);
529 task_objects.Remove (reg);
535 #region IBuildEngine3 implementation
537 public BuildEngineResult BuildProjectFilesInParallel (string[] projectFileNames, string[] targetNames, IDictionary[] globalProperties, IList<string>[] removeGlobalProperties, string[] toolsVersion, bool returnTargetOutputs)
539 throw new NotImplementedException ();
542 public void Reacquire ()
544 throw new NotImplementedException ();
549 throw new NotImplementedException ();
554 #region IBuildEngine2 implementation
556 // To NOT reuse this IBuildEngine instance for different build, we create another BuildManager and BuildSubmisson and then run it.
557 public bool BuildProjectFile (string projectFileName, string[] targetNames, IDictionary globalProperties, IDictionary targetOutputs, string toolsVersion)
559 var globalPropertiesThatMakeSense = new Dictionary<string,string> ();
560 foreach (DictionaryEntry p in globalProperties)
561 globalPropertiesThatMakeSense [(string) p.Key] = (string) p.Value;
562 var result = new BuildManager ().Build (this.submission.BuildManager.OngoingBuildParameters.Clone (), new BuildRequestData (projectFileName, globalPropertiesThatMakeSense, toolsVersion, targetNames, null));
563 foreach (var p in result.ResultsByTarget)
564 targetOutputs [p.Key] = p.Value.Items;
565 return result.OverallResult == BuildResultCode.Success;
568 public bool BuildProjectFilesInParallel (string[] projectFileNames, string[] targetNames, IDictionary[] globalProperties, IDictionary[] targetOutputsPerProject, string[] toolsVersion, bool useResultsCache, bool unloadProjectsOnCompletion)
570 throw new NotImplementedException ();
573 public bool IsRunningMultipleNodes {
575 throw new NotImplementedException ();
579 ProjectInstance GetProjectInstance (string projectFileName, string toolsVersion)
581 string fullPath = Path.GetFullPath (projectFileName);
582 if (submission.BuildRequest.ProjectFullPath == fullPath)
583 return submission.BuildRequest.ProjectInstance;
584 // FIXME: could also be filtered by global properties
585 // http://msdn.microsoft.com/en-us/library/microsoft.build.evaluation.projectcollection.getloadedprojects.aspx
586 var project = Projects.GetLoadedProjects (projectFileName).FirstOrDefault (p => p.ToolsVersion == toolsVersion);
588 throw new InvalidOperationException (string.Format ("Project '{0}' is not loaded", projectFileName));
589 return submission.BuildManager.GetProjectInstanceForBuild (project);
594 #region IBuildEngine implementation
596 public bool BuildProjectFile (string projectFileName, string[] targetNames, IDictionary globalProperties, IDictionary targetOutputs)
598 return BuildProjectFile (projectFileName, targetNames, globalProperties, targetOutputs, Projects.DefaultToolsVersion);
601 public void LogCustomEvent (CustomBuildEventArgs e)
603 event_source.FireCustomEventRaised (this, e);
606 public void LogErrorEvent (BuildErrorEventArgs e)
608 event_source.FireErrorRaised (this, e);
611 public void LogMessageEvent (BuildMessageEventArgs e)
613 event_source.FireMessageRaised (this, e);
616 public void LogWarningEvent (BuildWarningEventArgs e)
618 event_source.FireWarningRaised (this, e);
621 public int ColumnNumberOfTaskNode {
622 get { return current_task.Location != null ? current_task.Location.Column : 0; }
625 public bool ContinueOnError {
626 get { return current_task != null && project.EvaluateCondition (current_task.Condition) && EvaluateContinueOnError (current_task.ContinueOnError); }
629 bool EvaluateContinueOnError (string value)
632 case "WarnAndContinue":
633 case "ErrorAndContinue":
638 // empty means "stop on error", so don't pass empty string to EvaluateCondition().
639 return !string.IsNullOrEmpty (value) && project.EvaluateCondition (value);
642 public int LineNumberOfTaskNode {
643 get { return current_task.Location != null ? current_task.Location.Line : 0; }
646 public string ProjectFileOfTaskNode {
647 get { return current_task.FullPath; }