X-Git-Url: http://wien.tomnetworks.com/gitweb/?a=blobdiff_plain;f=mcs%2Fclass%2FMicrosoft.Build.Engine%2FMicrosoft.Build.BuildEngine%2FTarget.cs;h=439c56fd94b14b4887d57ace200ec0d5f5e82e71;hb=3bc6741c3037000eebd46b91efad54e5e6915c74;hp=2d3457b078c81b32cabd05268650d139ab6391d7;hpb=a8b22e0e864c03b8cfd2f2cb5a8075b6611c5553;p=mono.git diff --git a/mcs/class/Microsoft.Build.Engine/Microsoft.Build.BuildEngine/Target.cs b/mcs/class/Microsoft.Build.Engine/Microsoft.Build.BuildEngine/Target.cs index 2d3457b078c..439c56fd94b 100644 --- a/mcs/class/Microsoft.Build.Engine/Microsoft.Build.BuildEngine/Target.cs +++ b/mcs/class/Microsoft.Build.Engine/Microsoft.Build.BuildEngine/Target.cs @@ -32,23 +32,22 @@ using System.Collections; using System.Collections.Generic; using System.Xml; using Microsoft.Build.Framework; +using Microsoft.Build.Utilities; namespace Microsoft.Build.BuildEngine { public class Target : IEnumerable { - BatchingImpl batchingImpl; + TargetBatchingImpl batchingImpl; BuildState buildState; - XmlAttribute condition; - XmlAttribute dependsOnTargets; Engine engine; - bool isImported; + ImportedProject importedProject; string name; Project project; XmlElement targetElement; List onErrorElements; List buildTasks; - internal Target (XmlElement targetElement, Project project) + internal Target (XmlElement targetElement, Project project, ImportedProject importedProject) { if (project == null) throw new ArgumentNullException ("project"); @@ -57,166 +56,253 @@ namespace Microsoft.Build.BuildEngine { this.targetElement = targetElement; this.name = targetElement.GetAttribute ("Name"); - this.condition = targetElement.GetAttributeNode ("Condition"); - this.dependsOnTargets = targetElement.GetAttributeNode ("DependsOnTargets"); this.project = project; this.engine = project.ParentEngine; - this.isImported = false;; + this.importedProject = importedProject; this.onErrorElements = new List (); this.buildState = BuildState.NotStarted; this.buildTasks = new List (); - this.batchingImpl = new BatchingImpl (project, this.targetElement); + this.batchingImpl = new TargetBatchingImpl (project, this.targetElement); + bool onErrorFound = false; foreach (XmlNode xn in targetElement.ChildNodes) { if (xn is XmlElement) { XmlElement xe = (XmlElement) xn; if (xe.Name == "OnError") { onErrorElements.Add (xe); - continue; - } - buildTasks.Add (new BuildTask (xe, this)); + onErrorFound = true; + } else if (onErrorFound) + throw new InvalidProjectFileException ( + "The element must be last under element . Found element instead."); + else + buildTasks.Add (new BuildTask (xe, this)); } } } - internal bool Build () + [MonoTODO] + public BuildTask AddNewTask (string taskName) { - bool result; + if (taskName == null) + throw new ArgumentNullException ("taskName"); - buildState = BuildState.Started; - if (dependsOnTargets == null) { - ; - } else if (dependsOnTargets.Value == String.Empty) { - ; - } else { - OldExpression dependencies = new OldExpression (Project); - dependencies.ParseSource (dependsOnTargets.Value); - - string[] targetsToBuildFirst = (string[]) dependencies.ConvertTo (typeof (string[])); - foreach (string target in targetsToBuildFirst) { - string trimmed = target.Trim (); - Target t = (Target) project.Targets [trimmed]; + 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; + result = BuildDependencies (GetDependencies (), out executeOnErrors); + + if (!result && executeOnErrors) + ExecuteOnErrors (); + + if (result) + // deps built fine, do main build + result = DoBuild (out executeOnErrors); + + 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 (); + + return result; + } + + List GetDependencies () + { + List list = new List (); + 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} not found.", trimmed)); - if (t.BuildState == BuildState.NotStarted) { - t.Build (); - } - if (t.BuildState == BuildState.Started) - throw new InvalidProjectFileException ("Cycle in target dependencies detected."); + throw new InvalidProjectFileException (String.Format ( + "Target '{0}', a dependency of target '{1}', not found.", + dep_name.Trim (), Name)); + list.Add (t); } } - - result = RealBuild (); - buildState = BuildState.Finished; - - return result; + return list; + } + + bool BuildDependencies (List deps, out bool executeOnErrors) + { + executeOnErrors = false; + foreach (Target t in deps) { + 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"); + } + + return true; } - private bool RealBuild () + bool DoBuild (out bool executeOnErrors) { - bool executeOnErrors = false; + executeOnErrors = false; bool result = true; + + if (BuildTasks.Count == 0) + // nothing to do + return true; - LogTargetStarted (); - - if (this.batchingImpl.BuildNeeded ()) { - foreach (BuildTask bt in buildTasks) { - result = batchingImpl.BatchBuildTask (bt); - - if (!result && !bt.ContinueOnError) { - executeOnErrors = true; - break; - } - } - } else { - LogTargetSkipped (); + try { + result = batchingImpl.Build (this, out executeOnErrors); + } catch (Exception e) { + LogError ("Error building target {0}: {1}", Name, e.Message); + LogMessage (MessageImportance.Low, "Error building target {0}: {1}", Name, e.ToString ()); + return false; } - LogTargetFinished (result); - if (executeOnErrors == true) ExecuteOnErrors (); return result; } - private void ExecuteOnErrors () + void ExecuteOnErrors () { foreach (XmlElement onError in onErrorElements) { - // FIXME: add condition if (onError.GetAttribute ("ExecuteTargets") == String.Empty) throw new InvalidProjectFileException ("ExecuteTargets attribute is required in OnError element."); + + string on_error_condition = onError.GetAttribute ("Condition"); + if (!ConditionParser.ParseAndEvaluate (on_error_condition, Project)) { + LogMessage (MessageImportance.Low, + "OnError for target {0} skipped due to false condition: {1}", + Name, on_error_condition); + continue; + } + string[] targetsToExecute = onError.GetAttribute ("ExecuteTargets").Split (';'); foreach (string t in targetsToExecute) this.project.Targets [t].Build (); } } - - private void LogTargetSkipped () + + void LogTargetSkipped () { BuildMessageEventArgs bmea; - bmea = new BuildMessageEventArgs (String.Format ("Skipping target \"{0}\" because its outputs are up-to-date.", - name), null, "MSBuild", MessageImportance.Normal); - engine.EventSource.FireMessageRaised (this, bmea); - } - - private void LogTargetStarted () - { - TargetStartedEventArgs tsea; - string projectFile = project.FullFileName; - tsea = new TargetStartedEventArgs ("Target " + name + " started.", null, name, projectFile, null); - engine.EventSource.FireTargetStarted (this, tsea); - } - - private void LogTargetFinished (bool succeeded) - { - TargetFinishedEventArgs tfea; - string projectFile = project.FullFileName; - tfea = new TargetFinishedEventArgs ("Target " + name + " finished.", null, name, projectFile, null, succeeded); - engine.EventSource.FireTargetFinished (this, tfea); - } - - [MonoTODO] - public BuildTask AddNewTask (string taskName) - { - throw new NotImplementedException (); + bmea = new BuildMessageEventArgs (String.Format ( + "Target {0} skipped, as it has already been built.", Name), + null, null, MessageImportance.Low); + + project.ParentEngine.EventSource.FireMessageRaised (this, bmea); } - public IEnumerator GetEnumerator () + void LogError (string message, params object [] messageArgs) { - foreach (BuildTask bt in buildTasks) { - yield return bt; - } + if (message == null) + throw new ArgumentException ("message"); + + BuildErrorEventArgs beea = new BuildErrorEventArgs ( + null, null, null, 0, 0, 0, 0, String.Format (message, messageArgs), + null, null); + engine.EventSource.FireErrorRaised (this, beea); } - public void RemoveTask (BuildTask buildTask) + void LogMessage (MessageImportance importance, string message, params object [] messageArgs) { - buildTasks.Remove (buildTask); - } + if (message == null) + throw new ArgumentNullException ("message"); + BuildMessageEventArgs bmea = new BuildMessageEventArgs ( + String.Format (message, messageArgs), null, + null, importance); + engine.EventSource.FireMessageRaised (this, bmea); + } + public string Condition { - get { return condition.Value; } - set { condition.Value = value; } + get { return targetElement.GetAttribute ("Condition"); } + set { targetElement.SetAttribute ("Condition", value); } } public string DependsOnTargets { - get { - if (dependsOnTargets == null) - return null; - else - return dependsOnTargets.Value; - } - set { - if (dependsOnTargets != null) - dependsOnTargets.Value = value; - } + get { return targetElement.GetAttribute ("DependsOnTargets"); } + set { targetElement.SetAttribute ("DependsOnTargets", value); } } public bool IsImported { - get { return isImported; } - internal set { isImported = value; } + get { return importedProject != null; } } public string Name { @@ -226,10 +312,39 @@ namespace Microsoft.Build.BuildEngine { internal Project Project { get { return project; } } + + internal string TargetFile { + get { + if (importedProject != null) + return importedProject.FullFileName; + return project != null ? project.FullFileName : String.Empty; + } + } + + internal List BuildTasks { + get { return buildTasks; } + } + + internal Engine Engine { + get { return engine; } + } internal BuildState BuildState { get { return buildState; } } + + internal ITaskItem [] Outputs { + get { + string outputs = targetElement.GetAttribute ("Outputs"); + if (outputs == String.Empty) + return new ITaskItem [0]; + + Expression e = new Expression (); + e.Parse (outputs, ParseOptions.AllowItemsNoMetadataAndSplit); + + return (ITaskItem []) e.ConvertTo (project, typeof (ITaskItem [])); + } + } } internal enum BuildState {