Check Condition on task parameter before trying to apply it.
[mono.git] / mcs / class / Microsoft.Build / Microsoft.Build.Internal / BuildEngine4.cs
1 //
2 // BuildEngine4.cs
3 //
4 // Author:
5 //   Atsushi Enomoto (atsushi@xamarin.com)
6 //
7 // Copyright (C) 2013 Xamarin Inc. (http://www.xamarin.com)
8 //
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:
16 // 
17 // The above copyright notice and this permission notice shall be
18 // included in all copies or substantial portions of the Software.
19 // 
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.
27 //
28 using System;
29 using System.Collections;
30 using System.Collections.Generic;
31 using Microsoft.Build.BuildEngine;
32 using Microsoft.Build.Execution;
33 using Microsoft.Build.Framework;
34 using Microsoft.Build.Evaluation;
35 using System.Linq;
36 using System.IO;
37 using Microsoft.Build.Exceptions;
38
39 namespace Microsoft.Build.Internal
40 {
41         class BuildEngine4
42 #if NET_4_5
43                 : IBuildEngine4
44 #else
45                 : IBuildEngine3
46 #endif
47         {
48                 public BuildEngine4 (BuildSubmission submission)
49                 {
50                         this.submission = submission;
51                         event_source = new EventSource ();
52                         if (submission.BuildManager.OngoingBuildParameters.Loggers != null)
53                                 foreach (var l in submission.BuildManager.OngoingBuildParameters.Loggers)
54                                         l.Initialize (event_source);
55                 }
56
57                 BuildSubmission submission;
58                 ProjectInstance project;
59                 ProjectTargetInstance current_target;
60                 ProjectTaskInstance current_task;
61                 EventSource event_source;
62                 
63                 public ProjectCollection Projects {
64                         get { return submission.BuildManager.OngoingBuildParameters.ProjectCollection; }
65                 }
66
67                 // FIXME:
68                 // While we are not faced to implement those features, there are some modern task execution requirements.
69                 //
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.)
74                 //
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.
78                 //
79                 public void BuildProject (Func<bool> checkCancel, BuildResult result, ProjectInstance project, IEnumerable<string> targetNames, IDictionary<string,string> globalProperties, IDictionary<string,string> targetOutputs, string toolsVersion)
80                 {
81                         var parameters = submission.BuildManager.OngoingBuildParameters;
82                         var buildTaskFactory = new BuildTaskFactory (BuildTaskDatabase.GetDefaultTaskDatabase (parameters.GetToolset (toolsVersion)), submission.BuildRequest.ProjectInstance.TaskDatabase);
83                         BuildProject (new InternalBuildArguments () { CheckCancel = checkCancel, Result = result, Project = project, TargetNames = targetNames, GlobalProperties = globalProperties, TargetOutputs = targetOutputs, ToolsVersion = toolsVersion, BuildTaskFactory = buildTaskFactory });
84                 }
85
86                 class InternalBuildArguments
87                 {
88                         public Func<bool> CheckCancel;
89                         public BuildResult Result;
90                         public ProjectInstance Project;
91                         public IEnumerable<string> TargetNames;
92                         public IDictionary<string,string> GlobalProperties;
93                         public IDictionary<string,string> TargetOutputs;
94                         public string ToolsVersion;
95                         public BuildTaskFactory BuildTaskFactory;
96                         
97                         public void AddTargetResult (string targetName, TargetResult targetResult)
98                         {
99                                 if (!Result.HasResultsForTarget (targetName))
100                                         Result.AddResultsForTarget (targetName, targetResult);
101                         }
102                 }
103                 
104                 void BuildProject (InternalBuildArguments args)
105                 {
106                         var request = submission.BuildRequest;
107                         var parameters = submission.BuildManager.OngoingBuildParameters;
108                         this.project = args.Project;
109                         
110                         event_source.FireBuildStarted (this, new BuildStartedEventArgs ("Build Started", null));
111                         
112                         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 ());
113                         event_source.FireMessageRaised (this, new BuildMessageEventArgs (initialPropertiesFormatted, null, null, MessageImportance.Low));
114                         
115                         // null targets -> success. empty targets -> success(!)
116                         if (request.TargetNames == null)
117                                 args.Result.OverallResult = BuildResultCode.Success;
118                         else {
119                                 foreach (var targetName in request.TargetNames.Where (t => t != null))
120                                         args.AddTargetResult (targetName, BuildTargetByName (targetName, args));
121                 
122                                 // FIXME: check .NET behavior, whether cancellation always results in failure.
123                                 args.Result.OverallResult = args.CheckCancel () ? BuildResultCode.Failure : args.Result.ResultsByTarget.Select (p => p.Value).Any (r => r.ResultCode == TargetResultCode.Failure) ? BuildResultCode.Failure : BuildResultCode.Success;
124                         }                       
125                         event_source.FireBuildFinished (this, new BuildFinishedEventArgs ("Build Finished.", null, args.Result.OverallResult == BuildResultCode.Success));
126                 }
127                 
128                 TargetResult BuildTargetByName (string targetName, InternalBuildArguments args)
129                 {
130                         var targetResult = new TargetResult ();
131
132                         var request = submission.BuildRequest;
133                         var parameters = submission.BuildManager.OngoingBuildParameters;
134                         ProjectTargetInstance target;
135                         
136                         // FIXME: check skip condition
137                         if (false)
138                                 targetResult.Skip ();
139                         // null key is allowed and regarded as blind success(!) (as long as it could retrieve target)
140                         else if (!request.ProjectInstance.Targets.TryGetValue (targetName, out target))
141                                 targetResult.Failure (new InvalidOperationException (string.Format ("target '{0}' was not found in project '{1}'", targetName, project.FullPath)));
142                         else {
143                                 current_target = target;
144                                 try {
145                                         if (!DoBuildTarget (targetResult, args))
146                                                 return targetResult;
147                                 } finally {
148                                         current_target = null;
149                                 }
150                                 Func<string,ITaskItem> creator = s => new TargetOutputTaskItem () { ItemSpec = s };
151                                 var items = args.Project.GetAllItems (target.Outputs, string.Empty, creator, creator, s => true, (t, s) => {
152                                 });
153                                 targetResult.Success (items);
154                                 event_source.FireTargetFinished (this, new TargetFinishedEventArgs ("Target Finished", null, targetName, project.FullPath, target.FullPath, true));
155                         }
156                         return targetResult;
157                 }
158                 
159                 bool DoBuildTarget (TargetResult targetResult, InternalBuildArguments args)
160                 {
161                         var request = submission.BuildRequest;
162                         var target = current_target;
163         
164                         // process DependsOnTargets first.
165                         foreach (var dep in project.ExpandString (target.DependsOnTargets).Split (';').Where (s => !string.IsNullOrEmpty (s)).Select (s => s.Trim ())) {
166                                 var result = BuildTargetByName (dep, args);
167                                 args.AddTargetResult (dep, result);
168                                 if (result.ResultCode == TargetResultCode.Failure) {
169                                         targetResult.Failure (null);
170                                         return false;
171                                 }
172                         }
173                         
174                         event_source.FireTargetStarted (this, new TargetStartedEventArgs ("Target Started", null, target.Name, project.FullPath, target.FullPath));
175                         
176                         // Here we check cancellation (only after TargetStarted event).
177                         if (args.CheckCancel ()) {
178                                 targetResult.Failure (new BuildAbortedException ("Build has canceled"));
179                                 return false;
180                         }
181                         
182                         var propsToRestore = new Dictionary<string,string> ();
183                         var itemsToRemove = new List<ProjectItemInstance> ();
184                         try {
185                                 // Evaluate additional target properties
186                                 foreach (var c in target.Children.OfType<ProjectPropertyGroupTaskInstance> ()) {
187                                         if (!args.Project.EvaluateCondition (c.Condition))
188                                                 continue;
189                                         foreach (var p in c.Properties) {
190                                                 if (!args.Project.EvaluateCondition (p.Condition))
191                                                         continue;
192                                                 var value = args.Project.ExpandString (p.Value);
193                                                 propsToRestore.Add (p.Name, project.GetPropertyValue (value));
194                                                 project.SetProperty (p.Name, value);
195                                         }
196                                 }
197                                 
198                                 // Evaluate additional target items
199                                 foreach (var c in target.Children.OfType<ProjectItemGroupTaskInstance> ()) {
200                                         if (!args.Project.EvaluateCondition (c.Condition))
201                                                 continue;
202                                         foreach (var item in c.Items) {
203                                                 Func<string,ProjectItemInstance> creator = i => new ProjectItemInstance (project, item.ItemType, item.Metadata.Select (m => new KeyValuePair<string,string> (m.Name, m.Value)), i);
204                                                 foreach (var ti in project.GetAllItems (item.Include, item.Exclude, creator, creator, s => s == item.ItemType, (ti, s) => ti.SetMetadata ("RecurseDir", s)))
205                                                         itemsToRemove.Add (ti);
206                                         }
207                                 }
208                                 
209                                 foreach (var c in target.Children.OfType<ProjectOnErrorInstance> ()) {
210                                         if (!args.Project.EvaluateCondition (c.Condition))
211                                                 continue;
212                                         throw new NotImplementedException ();
213                                 }
214                                 
215                                 // run tasks
216                                 foreach (var ti in target.Children.OfType<ProjectTaskInstance> ()) {
217                                         current_task = ti;
218                                         if (!args.Project.EvaluateCondition (ti.Condition)) {
219                                                 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));
220                                                 continue;
221                                         }
222                                         if (!RunBuildTask (ti, targetResult, args))
223                                                 return false;
224                                 }
225                         } finally {
226                                 // restore temporary property state to the original state.
227                                 foreach (var p in propsToRestore) {
228                                         if (p.Value == string.Empty)
229                                                 project.RemoveProperty (p.Key);
230                                         else
231                                                 project.SetProperty (p.Key, p.Value);
232                                 }
233                                 foreach (var item in itemsToRemove)
234                                         project.RemoveItem (item);
235                         }
236                         return true;
237                 }
238                 
239                 bool RunBuildTask (ProjectTaskInstance ti, TargetResult targetResult, InternalBuildArguments args)
240                 {
241                         var request = submission.BuildRequest;
242                         var target = current_target;
243
244                         var host = request.HostServices == null ? null : request.HostServices.GetHostObject (request.ProjectFullPath, target.Name, ti.Name);
245                         
246                         // Create Task instance.
247                         var factoryIdentityParameters = new Dictionary<string,string> ();
248                         #if NET_4_5
249                         factoryIdentityParameters ["MSBuildRuntime"] = ti.MSBuildRuntime;
250                         factoryIdentityParameters ["MSBuildArchitecture"] = ti.MSBuildArchitecture;
251                         #endif
252                         var task = args.BuildTaskFactory.CreateTask (ti.Name, factoryIdentityParameters, this);
253                         event_source.FireMessageRaised (this, new BuildMessageEventArgs (string.Format ("Using task {0} from {1}", ti.Name, task.GetType ().AssemblyQualifiedName), null, null, MessageImportance.Low));
254                         task.HostObject = host;
255                         task.BuildEngine = this;
256                         
257                         // Prepare task parameters.
258                         var props = task.GetType ().GetProperties ()
259                                 .Where (p => p.CanWrite && p.GetCustomAttributes (typeof (RequiredAttribute), true).Any ());
260                         var missings = props.Where (p => !ti.Parameters.Any (tp => tp.Key.Equals (p.Name, StringComparison.OrdinalIgnoreCase)));
261                         if (missings.Any ())
262                                 throw new InvalidOperationException (string.Format ("Task {0} of type {1} is used without specifying mandatory property: {2}",
263                                         ti.Name, task.GetType (), string.Join (", ", missings.Select (p => p.Name).ToArray ())));
264                         
265                         foreach (var p in ti.Parameters) {
266                                 var prop = task.GetType ().GetProperty (p.Key);
267                                 var value = project.ExpandString (p.Value);
268                                 if (prop == null)
269                                         throw new InvalidOperationException (string.Format ("Task {0} does not have property {1}", ti.Name, p.Key));
270                                 if (!prop.CanWrite)
271                                         throw new InvalidOperationException (string.Format ("Task {0} has property {1} but it is read-only.", ti.Name, p.Key));
272                                 var valueInstance = ConvertTo (value, prop.PropertyType);
273                                 prop.SetValue (task, valueInstance, null);
274                         }
275                         
276                         // Do execute task.
277                         event_source.FireTaskStarted (this, new TaskStartedEventArgs ("Task Started", null, project.FullPath, ti.FullPath, ti.Name));
278                         var taskSuccess = task.Execute ();
279                         
280                         if (!taskSuccess) {
281                                 event_source.FireTaskFinished (this, new TaskFinishedEventArgs ("Task Finished", null, project.FullPath, ti.FullPath, ti.Name, false));
282                                 targetResult.Failure (null);
283                                 if (!ContinueOnError) {
284                                         event_source.FireTargetFinished (this, new TargetFinishedEventArgs ("Target Failed", null, target.Name, project.FullPath, target.FullPath, false));
285                                         return false;
286                                 }
287                         } else {
288                                 // Evaluate task output properties and items.
289                                 event_source.FireTaskFinished (this, new TaskFinishedEventArgs ("Task Finished", null, project.FullPath, ti.FullPath, ti.Name, true));
290                                 foreach (var to in ti.Outputs) {
291                                         if (!project.EvaluateCondition (to.Condition))
292                                                 continue;
293                                         var toItem = to as ProjectTaskOutputItemInstance;
294                                         var toProp = to as ProjectTaskOutputPropertyInstance;
295                                         string taskParameter = toItem != null ? toItem.TaskParameter : toProp.TaskParameter;
296                                         var pi = task.GetType ().GetProperty (taskParameter);
297                                         if (pi == null)
298                                                 throw new InvalidOperationException (string.Format ("Task {0} does not have property {1} specified as TaskParameter", ti.Name, toItem.TaskParameter));
299                                         if (!pi.CanRead)
300                                                 throw new InvalidOperationException (string.Format ("Task {0} has property {1} specified as TaskParameter, but it is write-only", ti.Name, toItem.TaskParameter));
301                                         if (toItem != null)
302                                                 args.Project.AddItem (toItem.ItemType, ConvertFrom (pi.GetValue (task, null)));
303                                         else
304                                                 args.Project.SetProperty (toProp.PropertyName, ConvertFrom (pi.GetValue (task, null)));
305                                 }
306                         }
307                         
308                         return true;
309                 }
310                 
311                 object ConvertTo (string source, Type targetType)
312                 {
313                         if (targetType == typeof (ITaskItem) || targetType.IsSubclassOf (typeof (ITaskItem)))
314                                 return new TargetOutputTaskItem () { ItemSpec = WindowsCompatibilityExtensions.NormalizeFilePath (source) };
315                         if (targetType.IsArray)
316                                 return new ArrayList (source.Split (';').Where (s => !string.IsNullOrEmpty (s)).Select (s => ConvertTo (s, targetType.GetElementType ())).ToArray ())
317                                                 .ToArray (targetType.GetElementType ());
318                         else
319                                 return Convert.ChangeType (source, targetType);
320                 }
321                 
322                 string ConvertFrom (object source)
323                 {
324                         if (source == null)
325                                 return string.Empty;
326                         if (source is ITaskItem)
327                                 return ((ITaskItem) source).ItemSpec;
328                         if (source.GetType ().IsArray)
329                                 return string.Join (":", ((Array) source).Cast<object> ().Select (o => ConvertFrom (o)).ToArray ());
330                         else
331                                 return (string) Convert.ChangeType (source, typeof (string));
332                 }
333                 
334                 class TargetOutputTaskItem : ITaskItem2
335                 {
336                         Hashtable metadata = new Hashtable ();
337                         
338                         #region ITaskItem2 implementation
339                         public string GetMetadataValueEscaped (string metadataName)
340                         {
341                                 return ProjectCollection.Escape ((string) metadata [metadataName]);
342                         }
343                         public void SetMetadataValueLiteral (string metadataName, string metadataValue)
344                         {
345                                 metadata [metadataName] = ProjectCollection.Unescape (metadataValue);
346                         }
347                         public IDictionary CloneCustomMetadataEscaped ()
348                         {
349                                 var ret = new Hashtable ();
350                                 foreach (DictionaryEntry e in metadata)
351                                         ret [e.Key] = ProjectCollection.Escape ((string) e.Value);
352                                 return ret;
353                         }
354                         public string EvaluatedIncludeEscaped {
355                                 get { return ProjectCollection.Escape (ItemSpec); }
356                                 set { ItemSpec = ProjectCollection.Unescape (value); }
357                         }
358                         #endregion
359                         #region ITaskItem implementation
360                         public IDictionary CloneCustomMetadata ()
361                         {
362                                 return new Hashtable (metadata);
363                         }
364                         public void CopyMetadataTo (ITaskItem destinationItem)
365                         {
366                                 foreach (DictionaryEntry e in metadata)
367                                         destinationItem.SetMetadata ((string) e.Key, (string) e.Value);
368                         }
369                         public string GetMetadata (string metadataName)
370                         {
371                                 var wk = ProjectCollection.GetWellKnownMetadata (metadataName, ItemSpec, Path.GetFullPath, null);
372                                 if (wk != null)
373                                         return wk;
374                                 return (string) metadata [metadataName];
375                         }
376                         public void RemoveMetadata (string metadataName)
377                         {
378                                 metadata.Remove (metadataName);
379                         }
380                         public void SetMetadata (string metadataName, string metadataValue)
381                         {
382                                 metadata [metadataName] = metadataValue;
383                         }
384                         public string ItemSpec { get; set; }
385                         public int MetadataCount {
386                                 get { return metadata.Count; }
387                         }
388                         public ICollection MetadataNames {
389                                 get { return metadata.Keys; }
390                         }
391                         #endregion
392                 }
393                 
394 #if NET_4_5
395                 #region IBuildEngine4 implementation
396                 
397                 // task objects are not in use anyways though...
398                 
399                 class TaskObjectRegistration
400                 {
401                         public TaskObjectRegistration (object key, object obj, RegisteredTaskObjectLifetime lifetime, bool allowEarlyCollection)
402                         {
403                                 Key = key;
404                                 Object = obj;
405                                 Lifetime = lifetime;
406                                 AllowEarlyCollection = allowEarlyCollection;
407                         }
408                         public object Key { get; private set; }
409                         public object Object { get; private set; }
410                         public RegisteredTaskObjectLifetime Lifetime { get; private set; }
411                         public bool AllowEarlyCollection { get; private set; }
412                 }
413                 
414                 List<TaskObjectRegistration> task_objects = new List<TaskObjectRegistration> ();
415
416                 public object GetRegisteredTaskObject (object key, RegisteredTaskObjectLifetime lifetime)
417                 {
418                         var reg = task_objects.FirstOrDefault (t => t.Key == key && t.Lifetime == lifetime);
419                         return reg != null ? reg.Object : null;
420                 }
421
422                 public void RegisterTaskObject (object key, object obj, RegisteredTaskObjectLifetime lifetime, bool allowEarlyCollection)
423                 {
424                         task_objects.Add (new TaskObjectRegistration (key, obj, lifetime, allowEarlyCollection));
425                 }
426
427                 public object UnregisterTaskObject (object key, RegisteredTaskObjectLifetime lifetime)
428                 {
429                         var reg = task_objects.FirstOrDefault (t => t.Key == key && t.Lifetime == lifetime);
430                         if (reg != null)
431                                 task_objects.Remove (reg);
432                         return reg.Object;
433                 }
434                 #endregion
435 #endif
436
437                 #region IBuildEngine3 implementation
438
439                 public BuildEngineResult BuildProjectFilesInParallel (string[] projectFileNames, string[] targetNames, IDictionary[] globalProperties, IList<string>[] removeGlobalProperties, string[] toolsVersion, bool returnTargetOutputs)
440                 {
441                         throw new NotImplementedException ();
442                 }
443
444                 public void Reacquire ()
445                 {
446                         throw new NotImplementedException ();
447                 }
448
449                 public void Yield ()
450                 {
451                         throw new NotImplementedException ();
452                 }
453
454                 #endregion
455
456                 #region IBuildEngine2 implementation
457
458                 public bool BuildProjectFile (string projectFileName, string[] targetNames, IDictionary globalProperties, IDictionary targetOutputs, string toolsVersion)
459                 {
460                         var proj = GetProjectInstance (projectFileName, toolsVersion);
461                         var globalPropertiesThatMakeSense = new Dictionary<string,string> ();
462                         foreach (DictionaryEntry p in globalProperties)
463                                 globalPropertiesThatMakeSense [(string) p.Key] = (string) p.Value;
464                         var result = new BuildResult ();
465                         var outputs = new Dictionary<string, string> ();
466                         BuildProject (() => false, result, proj, targetNames, globalPropertiesThatMakeSense, outputs, toolsVersion);
467                         foreach (var p in outputs)
468                                 targetOutputs [p.Key] = p.Value;
469                         return result.OverallResult == BuildResultCode.Success;
470                 }
471
472                 public bool BuildProjectFilesInParallel (string[] projectFileNames, string[] targetNames, IDictionary[] globalProperties, IDictionary[] targetOutputsPerProject, string[] toolsVersion, bool useResultsCache, bool unloadProjectsOnCompletion)
473                 {
474                         throw new NotImplementedException ();
475                 }
476
477                 public bool IsRunningMultipleNodes {
478                         get {
479                                 throw new NotImplementedException ();
480                         }
481                 }
482                 
483                 ProjectInstance GetProjectInstance (string projectFileName, string toolsVersion)
484                 {
485                         string fullPath = Path.GetFullPath (projectFileName);
486                         if (submission.BuildRequest.ProjectFullPath == fullPath)
487                                 return submission.BuildRequest.ProjectInstance;
488                         // FIXME: could also be filtered by global properties
489                         // http://msdn.microsoft.com/en-us/library/microsoft.build.evaluation.projectcollection.getloadedprojects.aspx
490                         var project = Projects.GetLoadedProjects (projectFileName).FirstOrDefault (p => p.ToolsVersion == toolsVersion);
491                         if (project == null)
492                                 throw new InvalidOperationException (string.Format ("Project '{0}' is not loaded", projectFileName));
493                         return submission.BuildManager.GetProjectInstanceForBuild (project);
494                 }
495
496                 #endregion
497
498                 #region IBuildEngine implementation
499
500                 public bool BuildProjectFile (string projectFileName, string[] targetNames, IDictionary globalProperties, IDictionary targetOutputs)
501                 {
502                         return BuildProjectFile (projectFileName, targetNames, globalProperties, targetOutputs, Projects.DefaultToolsVersion);
503                 }
504
505                 public void LogCustomEvent (CustomBuildEventArgs e)
506                 {
507                         event_source.FireCustomEventRaised (this, e);
508                 }
509
510                 public void LogErrorEvent (BuildErrorEventArgs e)
511                 {
512                         event_source.FireErrorRaised (this, e);
513                 }
514
515                 public void LogMessageEvent (BuildMessageEventArgs e)
516                 {
517                         event_source.FireMessageRaised (this, e);
518                 }
519
520                 public void LogWarningEvent (BuildWarningEventArgs e)
521                 {
522                         event_source.FireWarningRaised (this, e);
523                 }
524
525                 public int ColumnNumberOfTaskNode {
526                         get { return current_task.Location != null ? current_task.Location.Column : 0; }
527                 }
528
529                 public bool ContinueOnError {
530                         get { return current_task != null && project.EvaluateCondition (current_task.Condition) && EvaluateContinueOnError (current_task.ContinueOnError); }
531                 }
532                 
533                 bool EvaluateContinueOnError (string value)
534                 {
535                         switch (value) {
536                         case "WarnAndContinue":
537                         case "ErrorAndContinue":
538                                 return true;
539                         case "ErrorAndStop":
540                                 return false;
541                         }
542                         // empty means "stop on error", so don't pass empty string to EvaluateCondition().
543                         return !string.IsNullOrEmpty (value) && project.EvaluateCondition (value);
544                 }
545
546                 public int LineNumberOfTaskNode {
547                         get { return current_task.Location != null ? current_task.Location.Line : 0; }
548                 }
549
550                 public string ProjectFileOfTaskNode {
551                         get { return current_task.FullPath; }
552                 }
553
554                 #endregion
555         }
556 }
557