[xbuild] Set ProjectFile and TargetName metadata on target outputs.
[mono.git] / mcs / class / Microsoft.Build.Engine / Microsoft.Build.BuildEngine / Target.cs
index 158ffdef94bc40a0894d7721b18996d587fb8653..92e369b888b5ba56542aaa6c14f89511f044f252 100644 (file)
@@ -30,6 +30,7 @@
 using System;
 using System.Collections;
 using System.Collections.Generic;
+using System.Linq;
 using System.Xml;
 using Microsoft.Build.Framework;
 using Microsoft.Build.Utilities;
@@ -122,6 +123,16 @@ namespace Microsoft.Build.BuildEngine {
                }
 
                bool Build (string built_targets_key, out bool executeOnErrors)
+               {
+                       project.PushThisFileProperty (TargetFile);
+                       try {
+                               return BuildActual (built_targets_key, out executeOnErrors);
+                       } finally {
+                               project.PopThisFileProperty ();
+                       }
+               }
+
+               bool BuildActual (string built_targets_key, out bool executeOnErrors)
                {
                        bool result = false;
                        executeOnErrors = false;
@@ -147,14 +158,15 @@ namespace Microsoft.Build.BuildEngine {
 
                        try {
                                buildState = BuildState.Started;
-                               result = BuildDependencies (GetDependencies (), out executeOnErrors);
 
-                               if (!result && executeOnErrors)
-                                       ExecuteOnErrors ();
-
-                               if (result)
-                                       // deps built fine, do main build
-                                       result = DoBuild (out executeOnErrors);
+#if NET_4_0
+                               result = BuildDependencies (out executeOnErrors) &&
+                                               BuildBeforeThisTargets (out executeOnErrors) &&
+                                               DoBuild (out executeOnErrors) && // deps & Before targets built fine, do main build
+                                               BuildAfterThisTargets (out executeOnErrors);
+#else
+                               result = BuildDependencies (out executeOnErrors) && DoBuild (out executeOnErrors);
+#endif
 
                                buildState = BuildState.Finished;
                        } catch (Exception e) {
@@ -164,42 +176,80 @@ namespace Microsoft.Build.BuildEngine {
                                project.PopBatch ();
                        }
 
-                       project.ParentEngine.BuiltTargetsOutputByName [built_targets_key] = (ITaskItem[]) Outputs.Clone ();
-                       project.BuiltTargetKeys.Add (built_targets_key);
+                       ITaskItem[] outputs = (ITaskItem[]) OutputsAsITaskItems.Clone ();
+                       foreach (ITaskItem item in outputs) {
+                               item.SetMetadata ("MSBuildProjectFile", TargetFile);
+                               item.SetMetadata ("MSBuildTargetName", Name);
+                       }
+                       project.ParentEngine.BuiltTargetsOutputByName [built_targets_key] = outputs;
 
                        return result;
                }
 
-               List <Target> GetDependencies ()
+               bool BuildDependencies (out bool executeOnErrors)
                {
-                       List <Target> list = new List <Target> ();
-                       Target t;
-                       string [] targetNames;
-                       Expression deps;
-
-                       if (DependsOnTargets != String.Empty) {
-                               deps = new Expression ();
-                               deps.Parse (DependsOnTargets, ParseOptions.AllowItemsNoMetadataAndSplit);
-                               targetNames = (string []) deps.ConvertTo (Project, typeof (string []));
-                               foreach (string dep_name in targetNames) {
-                                       t = project.Targets [dep_name.Trim ()];
-                                       if (t == null)
-                                               throw new InvalidProjectFileException (String.Format (
-                                                               "Target '{0}', a dependency of target '{1}', not found.",
-                                                               dep_name.Trim (), Name));
-                                       list.Add (t);
-                               }
-                       }
-                       return list;
+                       executeOnErrors = false;
+
+                       if (String.IsNullOrEmpty (DependsOnTargets))
+                               return true;
+
+                       var expr = new Expression ();
+                       expr.Parse (DependsOnTargets, ParseOptions.AllowItemsNoMetadataAndSplit);
+                       string [] targetNames = (string []) expr.ConvertTo (Project, typeof (string []));
+
+                       bool result = BuildOtherTargets (targetNames,
+                                                       tname => engine.LogError ("Target '{0}', a dependency of target '{1}', not found.",
+                                                                               tname, Name),
+                                                       out executeOnErrors);
+                       if (!result && executeOnErrors)
+                               ExecuteOnErrors ();
+
+                       return result;
                }
 
-               bool BuildDependencies (List <Target> deps, out bool executeOnErrors)
+#if NET_4_0
+               bool BuildBeforeThisTargets (out bool executeOnErrors)
                {
                        executeOnErrors = false;
-                       foreach (Target t in deps) {
+                       bool result = BuildOtherTargets (BeforeThisTargets, null, out executeOnErrors);
+                       if (!result && executeOnErrors)
+                               ExecuteOnErrors ();
+
+                       return result;
+               }
+
+               bool BuildAfterThisTargets (out bool executeOnErrors)
+               {
+                       executeOnErrors = false;
+                       //missing_target handler not required as these are picked from actual target's
+                       //"Before/AfterTargets attributes!
+                       bool result = BuildOtherTargets (AfterThisTargets, null, out executeOnErrors);
+                       if (!result && executeOnErrors)
+                               ExecuteOnErrors ();
+
+                       return result;
+               }
+#endif
+
+               bool BuildOtherTargets (IEnumerable<string> targetNames, Action<string> missing_target, out bool executeOnErrors)
+               {
+                       executeOnErrors = false;
+                       if (targetNames == null)
+                               // nothing to build
+                               return true;
+
+                       foreach (string target_name in targetNames) {
+                               var t = project.Targets [target_name.Trim ()];
+                               if (t == null) {
+                                       if (missing_target != null)
+                                               missing_target (target_name);
+                                       return false;
+                               }
+
                                if (t.BuildState == BuildState.NotStarted)
                                        if (!t.Build (null, out executeOnErrors))
                                                return false;
+
                                if (t.BuildState == BuildState.Started)
                                        throw new InvalidProjectFileException ("Cycle in target dependencies detected");
                        }
@@ -312,6 +362,19 @@ namespace Microsoft.Build.BuildEngine {
                        }
                }
 
+#if NET_4_0
+               internal string BeforeTargets {
+                       get { return targetElement.GetAttribute ("BeforeTargets"); }
+               }
+
+               internal string AfterTargets {
+                       get { return targetElement.GetAttribute ("AfterTargets"); }
+               }
+
+               internal List<string> BeforeThisTargets { get; set; }
+               internal List<string> AfterThisTargets { get; set; }
+#endif
+
                internal List<BuildTask> BuildTasks {
                        get { return buildTasks; }
                }
@@ -324,7 +387,12 @@ namespace Microsoft.Build.BuildEngine {
                        get { return buildState; }
                }
 
-               internal ITaskItem [] Outputs {
+               public string Outputs {
+                       get { return targetElement.GetAttribute ("Outputs"); }
+                       set { targetElement.SetAttribute ("Outputs", value); }
+               }
+
+               ITaskItem [] OutputsAsITaskItems {
                        get {
                                string outputs = targetElement.GetAttribute ("Outputs");
                                if (outputs == String.Empty)