Merge pull request #485 from mtausig/master
[mono.git] / mcs / class / Microsoft.Build.Engine / Microsoft.Build.BuildEngine / TaskEngine.cs
index d4365bbd52565dfc965a882f56bda2c3752f7a25..335c66b459ac232976876bd4059ad96ea04a1376 100644 (file)
@@ -3,8 +3,10 @@
 //
 // Author:
 //   Marek Sieradzki (marek.sieradzki@gmail.com)
+//   Ankit Jain (jankit@novell.com)
 // 
 // (C) 2005 Marek Sieradzki
+// Copyright 2009 Novell, Inc (http://www.novell.com)
 //
 // Permission is hereby granted, free of charge, to any person obtaining
 // a copy of this software and associated documentation files (the
@@ -25,8 +27,6 @@
 // OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
 // WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
 
-#if NET_2_0
-
 using System;
 using System.Collections.Generic;
 using System.Collections.Specialized;
@@ -56,6 +56,18 @@ namespace Microsoft.Build.BuildEngine {
                {
                        parentProject = project;
                }
+
+               // Rules (inferred) for property values incase of empty data
+               //
+               // Prop Type         Argument         Final Value     Required
+               // string            empty string       null           Yes/no
+               // string            only whitespace    @arg           Yes/no
+               //
+               // string/
+               //   ITaskItem[]     empty/whitespace   empty array     Yes
+               //
+               // string/
+               //   ITaskItem[]     empty/whitespace   null            No
                
                public void Prepare (ITask task, XmlElement taskElement,
                                     IDictionary <string, string> parameters, Type taskType)
@@ -68,27 +80,55 @@ namespace Microsoft.Build.BuildEngine {
                        this.task = task;
                        this.taskElement = taskElement;
                        this.taskType = taskType;
-                       values = new Dictionary <string, object> ();
+                       values = new Dictionary <string, object> (StringComparer.OrdinalIgnoreCase);
                        
                        foreach (KeyValuePair <string, string> de in parameters) {
-                               currentProperty = taskType.GetProperty (de.Key);
+                               currentProperty = taskType.GetProperty (de.Key, BindingFlags.Public | BindingFlags.Instance
+                                               | BindingFlags.IgnoreCase);
                                if (currentProperty == null)
                                        throw new InvalidProjectFileException (String.Format ("Task does not have property \"{0}\" defined",
                                                de.Key));
                                
-                               value = GetObjectFromString (de.Value, currentProperty.PropertyType);           
-                               
-                               if (value != null)
-                                       values.Add (de.Key, value);
+                               try {
+                                       if (TryGetObjectFromString (de.Value, currentProperty.PropertyType, out value))
+                                               values.Add (de.Key, value);
+                               } catch (Exception e) {
+                                       throw new InvalidProjectFileException (String.Format (
+                                                       "Error converting Property named '{0}' with value '{1}' to type {2}: {3}",
+                                                       de.Key, de.Value, currentProperty.PropertyType, e.Message), e);
+                               }
                        }
                        
                        properties = taskType.GetProperties ();
                        foreach (PropertyInfo pi in properties) {
-                               if (pi.IsDefined (requiredAttribute, false) && values.ContainsKey (pi.Name) == false)
-                                       throw new InvalidProjectFileException ("Required property not set.");
-                               
-                               if (values.ContainsKey (pi.Name))
-                                       InitializeParameter (pi, values [pi.Name]);
+                               bool is_required = pi.IsDefined (requiredAttribute, false);
+
+                               if (is_required && values.ContainsKey (pi.Name) == false)
+                                       throw new InvalidProjectFileException (String.Format ("Required property '{0}' not set.",
+                                               pi.Name));
+
+                               if (!values.ContainsKey (pi.Name))
+                                       continue;
+
+                               Type prop_type = pi.PropertyType;
+                               if (prop_type.IsArray)
+                                       prop_type = prop_type.GetElementType ();
+
+                               // Valid data types: primitive types, DateTime, string and ITaskItem, and their arrays
+                               if (!prop_type.IsPrimitive && prop_type != typeof (string) && prop_type != typeof (ITaskItem))
+                                       throw new InvalidProjectFileException (String.Format (
+                                                       "{0} is not a supported type for properties for msbuild tasks.",
+                                                       pi.PropertyType));
+
+                               object val = values [pi.Name];
+                               if (val == null && pi.PropertyType.IsArray && is_required) {
+                                       if (pi.PropertyType == typeof (ITaskItem[]))
+                                               val = new ITaskItem [0];
+                                       else if (pi.PropertyType == typeof (string[]))
+                                               val = new string [0];
+                               }
+
+                               InitializeParameter (pi, val);
                        }
                }
                
@@ -118,21 +158,23 @@ namespace Microsoft.Build.BuildEngine {
                                        throw new InvalidProjectFileException ("Only one of ItemName and PropertyName attributes can be specified.");
                                if (xmlElement.GetAttribute ("TaskParameter") == String.Empty)
                                        throw new InvalidProjectFileException ("TaskParameter attribute must be specified.");
+
+                               if (!ConditionParser.ParseAndEvaluate (xmlElement.GetAttribute ("Condition"), parentProject))
+                                       continue;
                                        
                                taskParameter = xmlElement.GetAttribute ("TaskParameter");
                                itemName = xmlElement.GetAttribute ("ItemName");
                                propertyName = xmlElement.GetAttribute ("PropertyName");
                                
-                               propertyInfo = taskType.GetProperty (taskParameter);
+                               propertyInfo = taskType.GetProperty (taskParameter, BindingFlags.Public | BindingFlags.Instance |
+                                                       BindingFlags.IgnoreCase);
                                if (propertyInfo == null)
-                                       throw new Exception ("Could not get property info.");
-                               if (propertyInfo.IsDefined (outputAttribute, false) == false)
-                                       throw new Exception ("This is not output property.");
+                                       throw new InvalidProjectFileException (String.Format (
+                                               "The parameter '{0}' was not found for the '{1}' task.", taskParameter, taskElement.Name));
+                               if (!propertyInfo.IsDefined (outputAttribute, false))
+                                       throw new InvalidProjectFileException ("This is not output property.");
                                
                                o = propertyInfo.GetValue (task, null);
-                               if (o == null)
-                                       continue;
-                               
                                if (itemName != String.Empty) {
                                        PublishItemGroup (propertyInfo, o, itemName);
                                } else {
@@ -141,111 +183,82 @@ namespace Microsoft.Build.BuildEngine {
                        }
                }
                
-               private void InitializeParameter (PropertyInfo propertyInfo, object value)
+               void InitializeParameter (PropertyInfo propertyInfo, object value)
                {
                        propertyInfo.SetValue (task, value, null);
                }
 
-               private void PublishItemGroup (PropertyInfo propertyInfo,
-                                              object o,
-                                              string itemName)
-               {
-                       BuildItemGroup newItems = CollectItemGroup (propertyInfo, o, itemName);
-                       
-                       if (parentProject.EvaluatedItemsByName.ContainsKey (itemName)) {
-                               BuildItemGroup big = parentProject.EvaluatedItemsByName [itemName];
-                               big.Clear ();
-                               parentProject.EvaluatedItemsByName.Remove (itemName);
-                               parentProject.EvaluatedItemsByName.Add (itemName, newItems);
-                       } else {
-                               parentProject.EvaluatedItemsByName.Add (itemName, newItems);
-                       }
-                       foreach (BuildItem bi in newItems)
-                               parentProject.EvaluatedItems.AddItem (bi);
-               }
-               
-               private void PublishProperty (PropertyInfo propertyInfo,
+               void PublishProperty (PropertyInfo propertyInfo,
                                              object o,
                                              string propertyName)
                {
-                       BuildProperty bp = CollectProperty (propertyInfo, o, propertyName);
+                       if (o == null) {
+                               parentProject.EvaluatedProperties.RemoveProperty (propertyName);
+                               return;
+                       }
+
+                       BuildProperty bp;
+                       try {
+                               bp = ChangeType.ToBuildProperty (o, propertyInfo.PropertyType, propertyName);
+                       } catch (Exception e) {
+                               throw new Exception (String.Format ("Error publishing Output from task property '{0} {1}' to property named '{2}' : {3}",
+                                                       propertyInfo.PropertyType, propertyInfo.Name, propertyName, e.Message),
+                                                       e);
+                       }
                        parentProject.EvaluatedProperties.AddProperty (bp);
                }
-               
-               private BuildProperty CollectProperty (PropertyInfo propertyInfo, object o, string name)
+
+               // FIXME: cleanup + test
+               void PublishItemGroup (PropertyInfo propertyInfo,
+                                              object o,
+                                              string itemName)
                {
-                       string output = null;
-                       BuildProperty bp;
-                       
-                       if (propertyInfo == null)
-                               throw new ArgumentNullException ("propertyInfo");
                        if (o == null)
-                               throw new ArgumentNullException ("o");
-                       if (name == null)
-                               throw new ArgumentNullException ("name");
-                       
-                       if (propertyInfo.PropertyType == typeof (ITaskItem)) {
-                               ITaskItem item = (ITaskItem) o;
-                               bp = ChangeType.TransformToBuildProperty (name, item);
-                       } else if (propertyInfo.PropertyType == typeof (ITaskItem[])) {
-                               ITaskItem[] items = (ITaskItem[]) o;
-                               bp = ChangeType.TransformToBuildProperty (name, items);
-                       } else {
-                               if (propertyInfo.PropertyType.IsArray == true) {
-                                       output = ChangeType.TransformToString ((object[])o, propertyInfo.PropertyType);
-                       } else {
-                                       output = ChangeType.TransformToString (o, propertyInfo.PropertyType);
-                               }
-                               bp = ChangeType.TransformToBuildProperty (name, output);
+                               return;
+
+                       BuildItemGroup newItems;
+                       try {
+                               newItems = ChangeType.ToBuildItemGroup (o, propertyInfo.PropertyType, itemName);
+                       } catch (Exception e) {
+                               throw new Exception (String.Format ("Error publishing Output from task property '{0} {1}' to item named '{2}' : {3}",
+                                                       propertyInfo.PropertyType, propertyInfo.Name, itemName, e.Message),
+                                                       e);
                        }
-                       return bp;
-               }
-               
-               private BuildItemGroup CollectItemGroup (PropertyInfo propertyInfo, object o, string name)
-               {
-                       BuildItemGroup big;
-                       string temp;
+
+                       newItems.ParentProject = parentProject;
                        
-                       if (propertyInfo == null)
-                               throw new ArgumentNullException ("propertyInfo");
-                       if (o == null)
-                               throw new ArgumentNullException ("o");
-                       if (name == null)
-                               throw new ArgumentNullException ("name");
-                               
-                       if (propertyInfo.PropertyType == typeof (ITaskItem)) {
-                               ITaskItem item = (ITaskItem) o;
-                               big = ChangeType.TransformToBuildItemGroup (name, item);
-                       } else if (propertyInfo.PropertyType == typeof (ITaskItem[])) {
-                               ITaskItem[] items = (ITaskItem[]) o;
-                               big = ChangeType.TransformToBuildItemGroup (name, items);
+                       if (parentProject.EvaluatedItemsByName.ContainsKey (itemName)) {
+                               BuildItemGroup big = parentProject.EvaluatedItemsByName [itemName];
+                               foreach (BuildItem item in newItems)
+                                       big.AddItem (item);
                        } else {
-                               if (propertyInfo.PropertyType.IsArray == true) {
-                                       temp = ChangeType.TransformToString ((object[]) o, propertyInfo.PropertyType);
-                               } else {
-                                       temp = ChangeType.TransformToString (o, propertyInfo.PropertyType);
-                               }
-                               big = ChangeType.TransformToBuildItemGroup (name, temp);
+                               parentProject.EvaluatedItemsByName.Add (itemName, newItems);
                        }
-                       return big;     
+                       foreach (BuildItem bi in newItems)
+                               parentProject.EvaluatedItems.AddItem (bi);
                }
-                               
-               private object GetObjectFromString (string raw, Type type)
+
+               // returns true, if the @result should be included in the values list
+               bool TryGetObjectFromString (string raw, Type type, out object result)
                {
                        Expression e;
-                       object result;
+                       result = null;
                        
                        e = new Expression ();
-                       e.Parse (raw);
-                       
-                       if ((string) e.ConvertTo (parentProject, typeof (string)) == String.Empty)
-                               return null;
-                       
-                       result = e.ConvertTo (parentProject, type);
+                       e.Parse (raw, ParseOptions.AllowItemsMetadataAndSplit);
+
+                       // See rules in comment for 'Prepare'
+                       string str = (string) e.ConvertTo (parentProject, typeof (string));
+                       if (!type.IsArray && str == String.Empty)
+                               return false;
+
+                       if (str.Trim ().Length == 0 && type.IsArray &&
+                               (type.GetElementType () == typeof (string) || type.GetElementType () == typeof (ITaskItem)))
+                               return true;
+
+                       result = e.ConvertTo (parentProject, type, ExpressionOptions.ExpandItemRefs);
                        
-                       return result;
+                       return true;
                }
        }
 }
-
-#endif