* Target.cs (Build): Log a message if a target is skipped.
[mcs.git] / class / Microsoft.Build.Engine / Microsoft.Build.BuildEngine / Target.cs
blobde4648ee9f7c9850eb50cc6d0a1e1c22bec58b1e
1 //
2 // Target.cs: Represents a target.
3 //
4 // Author:
5 // Marek Sieradzki (marek.sieradzki@gmail.com)
6 //
7 // (C) 2005 Marek Sieradzki
8 //
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.
28 #if NET_2_0
30 using System;
31 using System.Collections;
32 using System.Collections.Generic;
33 using System.Xml;
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;
42 Engine engine;
43 ImportedProject importedProject;
44 string name;
45 Project project;
46 XmlElement targetElement;
47 List <XmlElement> onErrorElements;
48 List <BuildTask> buildTasks;
50 internal Target (XmlElement targetElement, Project project, ImportedProject importedProject)
52 if (project == null)
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);
75 onErrorFound = true;
76 } else if (onErrorFound)
77 throw new InvalidProjectFileException (
78 "The element <OnError> must be last under element <Target>. Found element <Error> instead.");
79 else
80 buildTasks.Add (new BuildTask (xe, this));
85 [MonoTODO]
86 public BuildTask AddNewTask (string taskName)
88 if (taskName == null)
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);
94 buildTasks.Add (bt);
96 return bt;
99 public IEnumerator GetEnumerator ()
101 foreach (BuildTask bt in buildTasks)
102 yield return bt;
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 internal bool Build ()
115 bool deps;
116 bool result;
118 if (!ConditionParser.ParseAndEvaluate (Condition, Project)) {
119 LogMessage (MessageImportance.Low,
120 "Target {0} skipped due to false condition: {1}",
121 Name, Condition);
122 return true;
125 try {
126 buildState = BuildState.Started;
127 deps = BuildDependencies (GetDependencies ());
129 result = deps ? DoBuild () : false;
131 buildState = BuildState.Finished;
132 } catch (Exception e) {
133 LogError ("Error building target {0}: {1}", Name, e.ToString ());
134 return false;
137 return result;
140 List <Target> GetDependencies ()
142 List <Target> list = new List <Target> ();
143 Target t;
144 string [] targetNames;
145 Expression deps;
147 if (DependsOnTargets != String.Empty) {
148 deps = new Expression ();
149 deps.Parse (DependsOnTargets, true);
150 targetNames = (string []) deps.ConvertTo (Project, typeof (string []));
151 foreach (string name in targetNames) {
152 t = project.Targets [name.Trim ()];
153 if (t == null)
154 throw new InvalidProjectFileException (String.Format ("Target '{0}' not found.", name.Trim ()));
155 list.Add (t);
158 return list;
161 bool BuildDependencies (List <Target> deps)
163 foreach (Target t in deps) {
164 if (t.BuildState == BuildState.NotStarted)
165 if (!t.Build ())
166 return false;
167 if (t.BuildState == BuildState.Started)
168 throw new InvalidProjectFileException ("Cycle in target dependencies detected");
171 return true;
174 bool DoBuild ()
176 bool executeOnErrors;
177 bool result = true;
179 if (BuildTasks.Count == 0)
180 // nothing to do
181 return true;
183 try {
184 result = batchingImpl.Build (this, out executeOnErrors);
185 } catch (Exception e) {
186 LogError ("Error building target {0}: {1}", Name, e.ToString ());
187 throw;
190 if (executeOnErrors == true)
191 ExecuteOnErrors ();
193 return result;
196 void ExecuteOnErrors ()
198 foreach (XmlElement onError in onErrorElements) {
199 // FIXME: add condition
200 if (onError.GetAttribute ("ExecuteTargets") == String.Empty)
201 throw new InvalidProjectFileException ("ExecuteTargets attribute is required in OnError element.");
202 string[] targetsToExecute = onError.GetAttribute ("ExecuteTargets").Split (';');
203 foreach (string t in targetsToExecute)
204 this.project.Targets [t].Build ();
208 void LogError (string message, params object [] messageArgs)
210 if (message == null)
211 throw new ArgumentException ("message");
213 BuildErrorEventArgs beea = new BuildErrorEventArgs (
214 null, null, null, 0, 0, 0, 0, String.Format (message, messageArgs),
215 null, null);
216 engine.EventSource.FireErrorRaised (this, beea);
219 void LogMessage (MessageImportance importance, string message, params object [] messageArgs)
221 if (message == null)
222 throw new ArgumentNullException ("message");
224 BuildMessageEventArgs bmea = new BuildMessageEventArgs (
225 String.Format (message, messageArgs), null,
226 null, importance);
227 engine.EventSource.FireMessageRaised (this, bmea);
230 public string Condition {
231 get { return targetElement.GetAttribute ("Condition"); }
232 set { targetElement.SetAttribute ("Condition", value); }
235 public string DependsOnTargets {
236 get { return targetElement.GetAttribute ("DependsOnTargets"); }
237 set { targetElement.SetAttribute ("DependsOnTargets", value); }
240 public bool IsImported {
241 get { return importedProject != null; }
244 public string Name {
245 get { return name; }
248 internal Project Project {
249 get { return project; }
252 internal List<BuildTask> BuildTasks {
253 get { return buildTasks; }
256 internal Engine Engine {
257 get { return engine; }
260 internal BuildState BuildState {
261 get { return buildState; }
264 internal ITaskItem [] Outputs {
265 get {
266 string outputs = targetElement.GetAttribute ("Outputs");
267 if (outputs == String.Empty)
268 return new ITaskItem [0];
270 Expression e = new Expression ();
271 e.Parse (outputs, true);
273 return (ITaskItem []) e.ConvertTo (project, typeof (ITaskItem []));
278 internal enum BuildState {
279 NotStarted,
280 Started,
281 Finished,
282 Skipped
286 #endif