2 // TaskEngine.cs: Class that executes each task.
5 // Marek Sieradzki (marek.sieradzki@gmail.com)
6 // Ankit Jain (jankit@novell.com)
8 // (C) 2005 Marek Sieradzki
9 // Copyright 2009 Novell, Inc (http://www.novell.com)
11 // Permission is hereby granted, free of charge, to any person obtaining
12 // a copy of this software and associated documentation files (the
13 // "Software"), to deal in the Software without restriction, including
14 // without limitation the rights to use, copy, modify, merge, publish,
15 // distribute, sublicense, and/or sell copies of the Software, and to
16 // permit persons to whom the Software is furnished to do so, subject to
17 // the following conditions:
19 // The above copyright notice and this permission notice shall be
20 // included in all copies or substantial portions of the Software.
22 // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
23 // EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
24 // MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
25 // NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
26 // LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
27 // OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
28 // WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
33 using System
.Collections
.Generic
;
34 using System
.Collections
.Specialized
;
35 using System
.Reflection
;
37 using Microsoft
.Build
.Framework
;
38 using Microsoft
.Build
.Utilities
;
40 namespace Microsoft
.Build
.BuildEngine
{
41 internal class TaskEngine
{
44 XmlElement taskElement
;
46 Project parentProject
;
48 static Type requiredAttribute
;
49 static Type outputAttribute
;
53 requiredAttribute
= typeof (Microsoft
.Build
.Framework
.RequiredAttribute
);
54 outputAttribute
= typeof (Microsoft
.Build
.Framework
.OutputAttribute
);
57 public TaskEngine (Project project
)
59 parentProject
= project
;
62 // Rules (inferred) for property values incase of empty data
64 // Prop Type Argument Final Value Required
65 // string empty string null Yes/no
66 // string only whitespace @arg Yes/no
69 // ITaskItem[] empty/whitespace empty array Yes
72 // ITaskItem[] empty/whitespace null No
74 public void Prepare (ITask task
, XmlElement taskElement
,
75 IDictionary
<string, string> parameters
, Type taskType
)
77 Dictionary
<string, object> values
;
78 PropertyInfo currentProperty
;
79 PropertyInfo
[] properties
;
83 this.taskElement
= taskElement
;
84 this.taskType
= taskType
;
85 values
= new Dictionary
<string, object> (StringComparer
.InvariantCultureIgnoreCase
);
87 foreach (KeyValuePair
<string, string> de
in parameters
) {
88 currentProperty
= taskType
.GetProperty (de
.Key
, BindingFlags
.Public
| BindingFlags
.Instance
89 | BindingFlags
.IgnoreCase
);
90 if (currentProperty
== null)
91 throw new InvalidProjectFileException (String
.Format ("Task does not have property \"{0}\" defined",
95 if (TryGetObjectFromString (de
.Value
, currentProperty
.PropertyType
, out value))
96 values
.Add (de
.Key
, value);
97 } catch (Exception e
) {
98 throw new Exception (String
.Format (
99 "Error converting Property named '{0}' with value '{1}' to type {2}: {3}",
100 de
.Key
, de
.Value
, currentProperty
.PropertyType
, e
.Message
), e
);
104 properties
= taskType
.GetProperties ();
105 foreach (PropertyInfo pi
in properties
) {
106 bool is_required
= pi
.IsDefined (requiredAttribute
, false);
108 if (is_required
&& values
.ContainsKey (pi
.Name
) == false)
109 throw new InvalidProjectFileException (String
.Format ("Required property '{0}' not set.",
112 if (!values
.ContainsKey (pi
.Name
))
115 Type prop_type
= pi
.PropertyType
;
116 if (prop_type
.IsArray
)
117 prop_type
= prop_type
.GetElementType ();
119 // Valid data types: primitive types, DateTime, string and ITaskItem, and their arrays
120 if (!prop_type
.IsPrimitive
&& prop_type
!= typeof (string) && prop_type
!= typeof (ITaskItem
))
121 throw new InvalidProjectFileException (String
.Format (
122 "{0} is not a supported type for properties for msbuild tasks.",
125 object val
= values
[pi
.Name
];
126 if (val
== null && pi
.PropertyType
.IsArray
&& is_required
) {
127 if (pi
.PropertyType
== typeof (ITaskItem
[]))
128 val
= new ITaskItem
[0];
129 else if (pi
.PropertyType
== typeof (string[]))
130 val
= new string [0];
133 InitializeParameter (pi
, val
);
137 public bool Execute ()
139 return task
.Execute ();
142 public void PublishOutput ()
144 XmlElement xmlElement
;
145 PropertyInfo propertyInfo
;
147 string taskParameter
;
151 foreach (XmlNode xmlNode
in taskElement
.ChildNodes
) {
152 if (!(xmlNode
is XmlElement
))
155 xmlElement
= (XmlElement
) xmlNode
;
157 if (xmlElement
.Name
!= "Output")
158 throw new InvalidProjectFileException ("Only Output elements can be Task's child nodes.");
159 if (xmlElement
.GetAttribute ("ItemName") != String
.Empty
&& xmlElement
.GetAttribute ("PropertyName") != String
.Empty
)
160 throw new InvalidProjectFileException ("Only one of ItemName and PropertyName attributes can be specified.");
161 if (xmlElement
.GetAttribute ("TaskParameter") == String
.Empty
)
162 throw new InvalidProjectFileException ("TaskParameter attribute must be specified.");
164 if (!ConditionParser
.ParseAndEvaluate (xmlElement
.GetAttribute ("Condition"), parentProject
))
167 taskParameter
= xmlElement
.GetAttribute ("TaskParameter");
168 itemName
= xmlElement
.GetAttribute ("ItemName");
169 propertyName
= xmlElement
.GetAttribute ("PropertyName");
171 propertyInfo
= taskType
.GetProperty (taskParameter
, BindingFlags
.Public
| BindingFlags
.Instance
|
172 BindingFlags
.IgnoreCase
);
173 if (propertyInfo
== null)
174 throw new Exception (String
.Format (
175 "The parameter '{0}' was not found for the '{1}' task.", taskParameter
, taskElement
.Name
));
176 if (!propertyInfo
.IsDefined (outputAttribute
, false))
177 throw new Exception ("This is not output property.");
179 o
= propertyInfo
.GetValue (task
, null);
180 // FIXME: maybe we should throw an exception here?
184 if (itemName
!= String
.Empty
) {
185 PublishItemGroup (propertyInfo
, o
, itemName
);
187 PublishProperty (propertyInfo
, o
, propertyName
);
192 void InitializeParameter (PropertyInfo propertyInfo
, object value)
194 propertyInfo
.SetValue (task
, value, null);
197 void PublishProperty (PropertyInfo propertyInfo
,
201 BuildProperty bp
= ChangeType
.ToBuildProperty (o
, propertyInfo
.PropertyType
, propertyName
);
202 parentProject
.EvaluatedProperties
.AddProperty (bp
);
205 // FIXME: cleanup + test
206 void PublishItemGroup (PropertyInfo propertyInfo
,
210 BuildItemGroup newItems
= ChangeType
.ToBuildItemGroup (o
, propertyInfo
.PropertyType
, itemName
);
211 newItems
.ParentProject
= parentProject
;
213 if (parentProject
.EvaluatedItemsByName
.ContainsKey (itemName
)) {
214 BuildItemGroup big
= parentProject
.EvaluatedItemsByName
[itemName
];
215 foreach (BuildItem item
in newItems
)
218 parentProject
.EvaluatedItemsByName
.Add (itemName
, newItems
);
220 foreach (BuildItem bi
in newItems
)
221 parentProject
.EvaluatedItems
.AddItem (bi
);
224 // returns true, if the @result should be included in the values list
225 bool TryGetObjectFromString (string raw
, Type type
, out object result
)
230 e
= new Expression ();
231 e
.Parse (raw
, ParseOptions
.AllowItemsMetadataAndSplit
);
233 // See rules in comment for 'Prepare'
234 string str
= (string) e
.ConvertTo (parentProject
, typeof (string));
235 if (!type
.IsArray
&& str
== String
.Empty
)
238 if (str
.Trim ().Length
== 0 && type
.IsArray
&&
239 (type
.GetElementType () == typeof (string) || type
.GetElementType () == typeof (ITaskItem
)))
242 result
= e
.ConvertTo (parentProject
, type
, ExpressionOptions
.ExpandItemRefs
);