2006-02-26 Marek Sieradzki <marek.sieradzki@gmail.com>
[mono.git] / mcs / class / Microsoft.Build.Engine / Microsoft.Build.BuildEngine / Target.cs
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:
16 //
17 // The above copyright notice and this permission notice shall be
18 // included in all copies or substantial portions of the Software.
19 //
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.
27
28 #if NET_2_0
29
30 using System;
31 using System.Collections;
32 using System.Xml;
33 using Microsoft.Build.Framework;
34
35 namespace Microsoft.Build.BuildEngine {
36         public class Target : IEnumerable {
37         
38                 BatchingImpl    batchingImpl;
39                 BuildState      buildState;
40                 XmlAttribute    condition;
41                 XmlAttribute    dependsOnTargets;
42                 Engine          engine;
43                 bool            isImported;
44                 string          name;
45                 Project         project;
46                 XmlElement      targetElement;
47                 ArrayList       taskElements;
48                 ArrayList       onErrorElements;
49                 
50                 internal Target (Project project, string name)
51                 {
52                         if (project == null)
53                                 throw new ArgumentNullException ("project");
54                         if (name == null)
55                                 throw new ArgumentNullException ("name");
56                         this.buildState = BuildState.NotStarted;
57                         this.project = project;
58                         this.engine = project.ParentEngine;
59                         this.name = name;
60                         this.isImported = false;;
61                         taskElements = new ArrayList ();
62                         onErrorElements  = new ArrayList ();
63                 }
64                 
65                 internal void BindToXml (XmlElement targetElement)
66                 {
67                         if (targetElement == null)
68                                 throw new ArgumentNullException ("targetElement");
69                         this.targetElement = targetElement;
70                         // FIXME: check if Target element is valid
71                         this.condition = targetElement.GetAttributeNode ("Condition");
72                         this.dependsOnTargets = targetElement.GetAttributeNode ("DependsOnTargets");
73                         this.batchingImpl = new BatchingImpl (project, this.targetElement);
74                         foreach (XmlNode xn in targetElement.ChildNodes) {
75                                 if (xn is XmlElement) {
76                                         XmlElement xe = (XmlElement) xn;
77                                         if (xe.Name == "OnError") {
78                                                 onErrorElements.Add (xe);
79                                                 continue;
80                                         }
81                                         TaskElement te = new TaskElement ();
82                                         te.BindToXml (xe, this);
83                                         taskElements.Add (te);
84                                 }
85                         }
86                 }
87                 
88                 internal void Build ()
89                 {
90                         buildState = BuildState.Started;
91                         if (dependsOnTargets == null) {
92                                 ;
93                         } else if (dependsOnTargets.Value == "") {
94                                 ;
95                         } else {
96                                 Expression dependencies = new Expression (Project, dependsOnTargets.Value);
97                                 string[] targetsToBuildFirst = (string[]) dependencies.ToArray (typeof (string[]));
98                                 foreach (string target in targetsToBuildFirst) {
99                                         string trimmed = target.Trim ();
100                                         Target t = (Target) project.Targets [trimmed];
101                                         if (t == null)
102                                                 throw new InvalidProjectFileException (String.Format ("Target {0} not found.", trimmed));
103                                         if (t.BuildState == BuildState.NotStarted) {
104                                                 t.Build ();
105                                         }
106                                         if (t.BuildState == BuildState.Started)
107                                                 throw new InvalidProjectFileException ("Cycle in target dependencies detected.");
108                                 }
109                         }
110                         RealBuild ();
111                         buildState = BuildState.Finished;
112                 }
113                 
114                 private void RealBuild ()
115                 {
116                         bool executeOnErrors = false;
117                         bool result = true;
118                 
119                         LogTargetStarted ();
120                         
121                         if (this.batchingImpl.BuildNeeded ()) {
122                                 foreach (TaskElement te in taskElements) {
123                                         if (this.batchingImpl.BatchTaskElement (te) == false && te.ContinueOnError == false) {
124                                                 executeOnErrors = true;
125                                                 result = false;
126                                                 break;
127                                         }
128                                 }
129                         } else {
130                                 LogTargetSkipped ();
131                         }
132
133                         LogTargetFinished (result);
134                         
135                         if (executeOnErrors == true)
136                                 ExecuteOnErrors ();
137                 }
138                 
139                 private void ExecuteOnErrors ()
140                 {
141                         foreach (XmlElement onError in onErrorElements) {
142                                 // FIXME: add condition
143                                 if (onError.GetAttribute ("ExecuteTargets") == String.Empty)
144                                         throw new InvalidProjectFileException ("ExecuteTargets attribute is required in OnError element.");
145                                 string[] targetsToExecute = onError.GetAttribute ("ExecuteTargets").Split (';');
146                                 foreach (string t in targetsToExecute)
147                                         this.project.Targets [t].Build ();
148                         }
149                 }
150                 
151                 private void LogTargetSkipped ()
152                 {
153                         BuildMessageEventArgs bmea;
154                         bmea = new BuildMessageEventArgs (String.Format ("Skipping target \"{0}\" because its outputs are up-to-date.",
155                                 name), null, "MSBuild", MessageImportance.Normal);
156                         engine.EventSource.FireMessageRaised (this, bmea);
157                 }
158                 
159                 private void LogTargetStarted ()
160                 {
161                         TargetStartedEventArgs tsea;
162                         string projectFile = project.FullFileName;
163                         tsea = new TargetStartedEventArgs ("Target " + name + " started.", null, name, projectFile, null);
164                         engine.EventSource.FireTargetStarted (this, tsea);
165                 }
166                 
167                 private void LogTargetFinished (bool succeeded)
168                 {
169                         TargetFinishedEventArgs tfea;
170                         string projectFile = project.FullFileName;
171                         tfea = new TargetFinishedEventArgs ("Target " + name + " finished.", null, name, projectFile, null, succeeded);
172                         engine.EventSource.FireTargetFinished (this, tfea);
173                 }
174                 
175                 public TaskElement AddNewTaskElement (string taskName)
176                 {
177                         TaskElement te = new TaskElement ();
178                         taskElements.Add (te);
179                         return te;
180                 }
181
182                 public IEnumerator GetEnumerator ()
183                 {
184                         foreach (TaskElement te in taskElements) {
185                                 yield return te;
186                         }
187                 }
188
189                 public void RemoveTaskElement (TaskElement taskElement)
190                 {
191                         taskElements.Remove (taskElement);
192                 }
193
194                 public string Condition {
195                         get { return condition.Value; }
196                         set { condition.Value = value; }
197                 }
198
199                 public string DependsOnTargets {
200                         get {
201                                 if (dependsOnTargets == null)
202                                         return null;
203                                 else
204                                         return dependsOnTargets.Value;
205                         }
206                         set {
207                                 if (dependsOnTargets != null)
208                                         dependsOnTargets.Value = value;
209                         }
210                 }
211
212                 public bool IsImported {
213                         get { return isImported; }
214                         internal set { isImported = value; }
215                 }
216
217                 public string Name {
218                         get { return name; }
219                 }
220                 
221                 internal Project Project {
222                         get { return project; }
223                 }
224                 
225                 internal BuildState BuildState {
226                         get { return buildState; }
227                 }
228         }
229         
230         internal enum BuildState {
231                 NotStarted,
232                 Started,
233                 Finished,
234                 Skipped
235         }
236 }
237
238 #endif