2006-10-14 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.Collections.Generic;
33 using System.Xml;
34 using Microsoft.Build.Framework;
35
36 namespace Microsoft.Build.BuildEngine {
37         public class Target : IEnumerable {
38         
39                 BatchingImpl    batchingImpl;
40                 BuildState      buildState;
41                 XmlAttribute    condition;
42                 XmlAttribute    dependsOnTargets;
43                 Engine          engine;
44                 bool            isImported;
45                 string          name;
46                 Project         project;
47                 XmlElement      targetElement;
48                 List <XmlElement>       onErrorElements;
49                 List <BuildTask>        buildTasks;
50                 
51                 internal Target (XmlElement targetElement, Project project)
52                 {
53                         if (project == null)
54                                 throw new ArgumentNullException ("project");
55                         if (targetElement == null)
56                                 throw new ArgumentNullException ("targetElement");
57
58                         this.targetElement = targetElement;
59                         this.name = targetElement.GetAttribute ("Name");
60                         this.condition = targetElement.GetAttributeNode ("Condition");
61                         this.dependsOnTargets = targetElement.GetAttributeNode ("DependsOnTargets");
62
63                         this.project = project;
64                         this.engine = project.ParentEngine;
65                         this.isImported = false;;
66
67                         this.onErrorElements  = new List <XmlElement> ();
68                         this.buildState = BuildState.NotStarted;
69                         this.buildTasks = new List <BuildTask> ();
70                         this.batchingImpl = new BatchingImpl (project, this.targetElement);
71
72                         foreach (XmlNode xn in targetElement.ChildNodes) {
73                                 if (xn is XmlElement) {
74                                         XmlElement xe = (XmlElement) xn;
75                                         if (xe.Name == "OnError") {
76                                                 onErrorElements.Add (xe);
77                                                 continue;
78                                         }
79                                         buildTasks.Add (new BuildTask (xe, this));
80                                 }
81                         }
82                 }
83                 
84                 internal bool Build ()
85                 {
86                         bool result;
87                 
88                         buildState = BuildState.Started;
89                         if (dependsOnTargets == null) {
90                                 ;
91                         } else if (dependsOnTargets.Value == String.Empty) {
92                                 ;
93                         } else {
94                                 OldExpression dependencies = new OldExpression (Project);
95                                 dependencies.ParseSource (dependsOnTargets.Value);
96                                 
97                                 string[] targetsToBuildFirst = (string[]) dependencies.ConvertTo (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                         
111                         result = RealBuild ();
112                         buildState = BuildState.Finished;
113                         
114                         return result;
115                 }
116                 
117                 private bool RealBuild ()
118                 {
119                         bool executeOnErrors = false;
120                         bool result = true;
121                 
122                         LogTargetStarted ();
123                         
124                         if (this.batchingImpl.BuildNeeded ()) {
125                                 foreach (BuildTask bt in buildTasks) {
126                                         result = batchingImpl.BatchBuildTask (bt);
127                                 
128                                         if (!result && !bt.ContinueOnError) {
129                                                 executeOnErrors = true;
130                                                 break;
131                                         }
132                                 }
133                         } else {
134                                 LogTargetSkipped ();
135                         }
136
137                         LogTargetFinished (result);
138                         
139                         if (executeOnErrors == true)
140                                 ExecuteOnErrors ();
141                                 
142                         return result;
143                 }
144                 
145                 private void ExecuteOnErrors ()
146                 {
147                         foreach (XmlElement onError in onErrorElements) {
148                                 // FIXME: add condition
149                                 if (onError.GetAttribute ("ExecuteTargets") == String.Empty)
150                                         throw new InvalidProjectFileException ("ExecuteTargets attribute is required in OnError element.");
151                                 string[] targetsToExecute = onError.GetAttribute ("ExecuteTargets").Split (';');
152                                 foreach (string t in targetsToExecute)
153                                         this.project.Targets [t].Build ();
154                         }
155                 }
156                 
157                 private void LogTargetSkipped ()
158                 {
159                         BuildMessageEventArgs bmea;
160                         bmea = new BuildMessageEventArgs (String.Format ("Skipping target \"{0}\" because its outputs are up-to-date.",
161                                 name), null, "MSBuild", MessageImportance.Normal);
162                         engine.EventSource.FireMessageRaised (this, bmea);
163                 }
164                 
165                 private void LogTargetStarted ()
166                 {
167                         TargetStartedEventArgs tsea;
168                         string projectFile = project.FullFileName;
169                         tsea = new TargetStartedEventArgs ("Target " + name + " started.", null, name, projectFile, null);
170                         engine.EventSource.FireTargetStarted (this, tsea);
171                 }
172                 
173                 private void LogTargetFinished (bool succeeded)
174                 {
175                         TargetFinishedEventArgs tfea;
176                         string projectFile = project.FullFileName;
177                         tfea = new TargetFinishedEventArgs ("Target " + name + " finished.", null, name, projectFile, null, succeeded);
178                         engine.EventSource.FireTargetFinished (this, tfea);
179                 }
180         
181                 [MonoTODO]
182                 public BuildTask AddNewTask (string taskName)
183                 {
184                         throw new NotImplementedException ();
185                 }
186
187                 public IEnumerator GetEnumerator ()
188                 {
189                         foreach (BuildTask bt in buildTasks) {
190                                 yield return bt;
191                         }
192                 }
193
194                 public void RemoveTask (BuildTask buildTask)
195                 {
196                         buildTasks.Remove (buildTask);
197                 }
198
199                 public string Condition {
200                         get { return condition.Value; }
201                         set { condition.Value = value; }
202                 }
203
204                 public string DependsOnTargets {
205                         get {
206                                 if (dependsOnTargets == null)
207                                         return null;
208                                 else
209                                         return dependsOnTargets.Value;
210                         }
211                         set {
212                                 if (dependsOnTargets != null)
213                                         dependsOnTargets.Value = value;
214                         }
215                 }
216
217                 public bool IsImported {
218                         get { return isImported; }
219                         internal set { isImported = value; }
220                 }
221
222                 public string Name {
223                         get { return name; }
224                 }
225                 
226                 internal Project Project {
227                         get { return project; }
228                 }
229                 
230                 internal BuildState BuildState {
231                         get { return buildState; }
232                 }
233         }
234         
235         internal enum BuildState {
236                 NotStarted,
237                 Started,
238                 Finished,
239                 Skipped
240         }
241 }
242
243 #endif