+ [MonoTODO]
+ public BuildTask AddNewTask (string taskName)
+ {
+ if (taskName == null)
+ throw new ArgumentNullException ("taskName");
+
+ XmlElement task = project.XmlDocument.CreateElement (taskName, Project.XmlNamespace);
+ targetElement.AppendChild (task);
+ BuildTask bt = new BuildTask (task, this);
+ buildTasks.Add (bt);
+
+ return bt;
+ }
+
+ public IEnumerator GetEnumerator ()
+ {
+ foreach (BuildTask bt in buildTasks)
+ yield return bt;
+ }
+
+ // FIXME: shouldn't we remove it from XML?
+ public void RemoveTask (BuildTask buildTask)
+ {
+ if (buildTask == null)
+ throw new ArgumentNullException ("buildTask");
+ buildTasks.Remove (buildTask);
+ }
+
+ bool Build ()
+ {
+ return Build (null);
+ }
+
+ internal bool Build (string built_targets_key)
+ {
+ bool executeOnErrors;
+ return Build (built_targets_key, out executeOnErrors);
+ }
+
+ 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;
+
+ // built targets are keyed by the particular set of global
+ // properties. So, a different set could allow a target
+ // to run again
+ built_targets_key = project.GetKeyForTarget (Name);
+ 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;
+
+#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[]) OutputsAsITaskItems.Clone ();
+
+ return result;
+ }
+
+ bool BuildDependencies (out bool executeOnErrors)
+ {
+ 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 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;