[xbuild] Set ProjectFile and TargetName metadata on target outputs.
[mono.git] / mcs / class / Microsoft.Build.Engine / Microsoft.Build.BuildEngine / Target.cs
index bfdd2768356bbe6cc94ef7d3ce8410559a185d22..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;
@@ -130,72 +141,115 @@ namespace Microsoft.Build.BuildEngine {
                        // properties. So, a different set could allow a target
                        // to run again
                        built_targets_key = project.GetKeyForTarget (Name);
-                       ITaskItem[] outputs;
                        if (project.ParentEngine.BuiltTargetsOutputByName.ContainsKey (built_targets_key)) {
                                LogTargetSkipped ();
                                return true;
                        }
 
+                       // Push a null/empty batch, effectively clearing it
+                       project.PushBatch (null, null);
                        if (!ConditionParser.ParseAndEvaluate (Condition, Project)) {
                                LogMessage (MessageImportance.Low,
                                                "Target {0} skipped due to false condition: {1}",
                                                Name, Condition);
+                               project.PopBatch ();
                                return true;
                        }
 
                        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) {
                                LogError ("Error building target {0}: {1}", Name, e.ToString ());
                                return false;
+                       } finally {
+                               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;
+               }
+
+#if NET_4_0
+               bool BuildBeforeThisTargets (out bool executeOnErrors)
+               {
+                       executeOnErrors = false;
+                       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 BuildDependencies (List <Target> deps, out bool executeOnErrors)
+               bool BuildOtherTargets (IEnumerable<string> targetNames, Action<string> missing_target, out bool executeOnErrors)
                {
                        executeOnErrors = false;
-                       foreach (Target t in deps) {
+                       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");
                        }
@@ -308,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; }
                }
@@ -320,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)