2 // Target.cs: Represents a target.
5 // Marek Sieradzki (marek.sieradzki@gmail.com)
7 // (C) 2005 Marek Sieradzki
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:
17 // The above copyright notice and this permission notice shall be
18 // included in all copies or substantial portions of the Software.
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.
31 using System.Collections;
32 using System.Collections.Generic;
34 using Microsoft.Build.Framework;
35 using Microsoft.Build.Utilities;
37 namespace Microsoft.Build.BuildEngine {
38 public class Target : IEnumerable {
40 TargetBatchingImpl batchingImpl;
41 BuildState buildState;
43 ImportedProject importedProject;
46 XmlElement targetElement;
47 List <XmlElement> onErrorElements;
48 List <BuildTask> buildTasks;
50 internal Target (XmlElement targetElement, Project project, ImportedProject importedProject)
53 throw new ArgumentNullException ("project");
54 if (targetElement == null)
55 throw new ArgumentNullException ("targetElement");
57 this.targetElement = targetElement;
58 this.name = targetElement.GetAttribute ("Name");
60 this.project = project;
61 this.engine = project.ParentEngine;
62 this.importedProject = importedProject;
64 this.onErrorElements = new List <XmlElement> ();
65 this.buildState = BuildState.NotStarted;
66 this.buildTasks = new List <BuildTask> ();
67 this.batchingImpl = new TargetBatchingImpl (project, this.targetElement);
69 bool onErrorFound = false;
70 foreach (XmlNode xn in targetElement.ChildNodes) {
71 if (xn is XmlElement) {
72 XmlElement xe = (XmlElement) xn;
73 if (xe.Name == "OnError") {
74 onErrorElements.Add (xe);
76 } else if (onErrorFound)
77 throw new InvalidProjectFileException (
78 "The element <OnError> must be last under element <Target>. Found element <Error> instead.");
80 buildTasks.Add (new BuildTask (xe, this));
86 public BuildTask AddNewTask (string taskName)
89 throw new ArgumentNullException ("taskName");
91 XmlElement task = project.XmlDocument.CreateElement (taskName, Project.XmlNamespace);
92 targetElement.AppendChild (task);
93 BuildTask bt = new BuildTask (task, this);
99 public IEnumerator GetEnumerator ()
101 foreach (BuildTask bt in buildTasks)
105 // FIXME: shouldn't we remove it from XML?
106 public void RemoveTask (BuildTask buildTask)
108 if (buildTask == null)
109 throw new ArgumentNullException ("buildTask");
110 buildTasks.Remove (buildTask);
113 // FIXME: log errors instead of throwing exceptions
114 internal bool Build ()
119 // log that target is being skipped
120 if (!ConditionParser.ParseAndEvaluate (Condition, Project))
124 buildState = BuildState.Started;
125 deps = BuildDependencies (GetDependencies ());
127 result = deps ? DoBuild () : false;
129 buildState = BuildState.Finished;
131 } catch (Exception e) {
132 LogError ("Error building target {0}: {1}", Name, e.ToString ());
139 List <Target> GetDependencies ()
141 List <Target> list = new List <Target> ();
143 string [] targetNames;
146 if (DependsOnTargets != String.Empty) {
147 deps = new Expression ();
148 deps.Parse (DependsOnTargets, true);
149 targetNames = (string []) deps.ConvertTo (Project, typeof (string []));
150 foreach (string name in targetNames) {
151 t = project.Targets [name.Trim ()];
153 throw new InvalidProjectFileException (String.Format ("Target '{0}' not found.", name.Trim ()));
160 bool BuildDependencies (List <Target> deps)
162 foreach (Target t in deps) {
163 if (t.BuildState == BuildState.NotStarted)
166 if (t.BuildState == BuildState.Started)
167 throw new InvalidProjectFileException ("Cycle in target dependencies detected");
175 bool executeOnErrors;
178 if (BuildTasks.Count == 0)
183 result = batchingImpl.Build (this, out executeOnErrors);
184 } catch (Exception e) {
185 LogError ("Error building target {0}: {1}", Name, e.ToString ());
189 if (executeOnErrors == true)
195 void ExecuteOnErrors ()
197 foreach (XmlElement onError in onErrorElements) {
198 // FIXME: add condition
199 if (onError.GetAttribute ("ExecuteTargets") == String.Empty)
200 throw new InvalidProjectFileException ("ExecuteTargets attribute is required in OnError element.");
201 string[] targetsToExecute = onError.GetAttribute ("ExecuteTargets").Split (';');
202 foreach (string t in targetsToExecute)
203 this.project.Targets [t].Build ();
207 void LogError (string message, params object [] messageArgs)
210 throw new ArgumentException ("message");
212 BuildErrorEventArgs beea = new BuildErrorEventArgs (
213 null, null, null, 0, 0, 0, 0, String.Format (message, messageArgs),
215 engine.EventSource.FireErrorRaised (this, beea);
218 public string Condition {
219 get { return targetElement.GetAttribute ("Condition"); }
220 set { targetElement.SetAttribute ("Condition", value); }
223 public string DependsOnTargets {
224 get { return targetElement.GetAttribute ("DependsOnTargets"); }
225 set { targetElement.SetAttribute ("DependsOnTargets", value); }
228 public bool IsImported {
229 get { return importedProject != null; }
236 internal Project Project {
237 get { return project; }
240 internal List<BuildTask> BuildTasks {
241 get { return buildTasks; }
244 internal Engine Engine {
245 get { return engine; }
248 internal BuildState BuildState {
249 get { return buildState; }
252 internal ITaskItem [] Outputs {
254 string outputs = targetElement.GetAttribute ("Outputs");
255 if (outputs == String.Empty)
256 return new ITaskItem [0];
258 Expression e = new Expression ();
259 e.Parse (outputs, true);
261 return (ITaskItem []) e.ConvertTo (project, typeof (ITaskItem []));
266 internal enum BuildState {