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 internal bool Build ()
118 if (!ConditionParser
.ParseAndEvaluate (Condition
, Project
)) {
119 LogMessage (MessageImportance
.Low
,
120 "Target {0} skipped due to false condition: {1}",
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 ());
140 List
<Target
> GetDependencies ()
142 List
<Target
> list
= new List
<Target
> ();
144 string [] targetNames
;
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 ()];
154 throw new InvalidProjectFileException (String
.Format ("Target '{0}' not found.", name
.Trim ()));
161 bool BuildDependencies (List
<Target
> deps
)
163 foreach (Target t
in deps
) {
164 if (t
.BuildState
== BuildState
.NotStarted
)
167 if (t
.BuildState
== BuildState
.Started
)
168 throw new InvalidProjectFileException ("Cycle in target dependencies detected");
176 bool executeOnErrors
;
179 if (BuildTasks
.Count
== 0)
184 result
= batchingImpl
.Build (this, out executeOnErrors
);
185 } catch (Exception e
) {
186 LogError ("Error building target {0}: {1}", Name
, e
.ToString ());
190 if (executeOnErrors
== true)
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
)
211 throw new ArgumentException ("message");
213 BuildErrorEventArgs beea
= new BuildErrorEventArgs (
214 null, null, null, 0, 0, 0, 0, String
.Format (message
, messageArgs
),
216 engine
.EventSource
.FireErrorRaised (this, beea
);
219 void LogMessage (MessageImportance importance
, string message
, params object [] messageArgs
)
222 throw new ArgumentNullException ("message");
224 BuildMessageEventArgs bmea
= new BuildMessageEventArgs (
225 String
.Format (message
, messageArgs
), null,
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; }
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
{
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
{