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