2010-04-06 Jb Evain <jbevain@novell.com>
[mcs.git] / class / Microsoft.Build.Engine / Microsoft.Build.BuildEngine / TaskEngine.cs
blob485771de49243bef34cc970dcb18b7f353eb3d66
1 //
2 // TaskEngine.cs: Class that executes each task.
3 //
4 // Author:
5 // Marek Sieradzki (marek.sieradzki@gmail.com)
6 // Ankit Jain (jankit@novell.com)
7 //
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.
30 #if NET_2_0
32 using System;
33 using System.Collections.Generic;
34 using System.Collections.Specialized;
35 using System.Reflection;
36 using System.Xml;
37 using Microsoft.Build.Framework;
38 using Microsoft.Build.Utilities;
40 namespace Microsoft.Build.BuildEngine {
41 internal class TaskEngine {
43 ITask task;
44 XmlElement taskElement;
45 Type taskType;
46 Project parentProject;
48 static Type requiredAttribute;
49 static Type outputAttribute;
51 static TaskEngine ()
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
68 // string/
69 // ITaskItem[] empty/whitespace empty array Yes
71 // string/
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;
80 object value;
82 this.task = task;
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",
92 de.Key));
94 try {
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.",
110 pi.Name));
112 if (!values.ContainsKey (pi.Name))
113 continue;
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.",
123 pi.PropertyType));
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;
146 string propertyName;
147 string taskParameter;
148 string itemName;
149 object o;
151 foreach (XmlNode xmlNode in taskElement.ChildNodes) {
152 if (!(xmlNode is XmlElement))
153 continue;
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))
165 continue;
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?
181 if (o == null)
182 continue;
184 if (itemName != String.Empty) {
185 PublishItemGroup (propertyInfo, o, itemName);
186 } else {
187 PublishProperty (propertyInfo, o, propertyName);
192 void InitializeParameter (PropertyInfo propertyInfo, object value)
194 propertyInfo.SetValue (task, value, null);
197 void PublishProperty (PropertyInfo propertyInfo,
198 object o,
199 string propertyName)
201 BuildProperty bp = ChangeType.ToBuildProperty (o, propertyInfo.PropertyType, propertyName);
202 parentProject.EvaluatedProperties.AddProperty (bp);
205 // FIXME: cleanup + test
206 void PublishItemGroup (PropertyInfo propertyInfo,
207 object o,
208 string itemName)
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)
216 big.AddItem (item);
217 } else {
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)
227 Expression e;
228 result = null;
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)
236 return false;
238 if (str.Trim ().Length == 0 && type.IsArray &&
239 (type.GetElementType () == typeof (string) || type.GetElementType () == typeof (ITaskItem)))
240 return true;
242 result = e.ConvertTo (parentProject, type, ExpressionOptions.ExpandItemRefs);
244 return true;
249 #endif