Merge pull request #439 from mono-soc-2012/garyb/iconfix
[mono.git] / mcs / class / Microsoft.Build.Engine / Microsoft.Build.BuildEngine / TaskEngine.cs
index ea4e29db8f88b721fa6647f99586194204e6d272..6013888ff872917ca019d41a568856ec638b35fd 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
@@ -56,6 +58,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 +82,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);
                        }
                }
                
@@ -107,139 +149,118 @@ namespace Microsoft.Build.BuildEngine {
                        object          o;
                
                        foreach (XmlNode xmlNode in taskElement.ChildNodes) {
-                               if (xmlNode is XmlElement) {
-                                       xmlElement = (XmlElement) xmlNode;
-                                       
-                                       if (xmlElement.Name != "Output")
-                                               throw new InvalidProjectFileException ("Only Output elements can be Task's child nodes.");
-                                       if (xmlElement.GetAttribute ("ItemName") != String.Empty && xmlElement.GetAttribute ("PropertyName") != String.Empty)
-                                               throw new InvalidProjectFileException ("Only one of ItemName and ProperytyName attributes can be specified.");
-                                       if (xmlElement.GetAttribute ("TaskParameter") == String.Empty)
-                                               throw new InvalidProjectFileException ("TaskParameter attribute must be specified.");
-                                               
-                                       taskParameter = xmlElement.GetAttribute ("TaskParameter");
-                                       itemName = xmlElement.GetAttribute ("ItemName");
-                                       propertyName = xmlElement.GetAttribute ("PropertyName");
-                                       
-                                       propertyInfo = taskType.GetProperty (taskParameter);
-                                       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.");
-                                       
-                                       o = propertyInfo.GetValue (task, null);
-                                       if (o == null)
-                                               continue;
+                               if (!(xmlNode is XmlElement))
+                                       continue;
+                       
+                               xmlElement = (XmlElement) xmlNode;
+                               
+                               if (xmlElement.Name != "Output")
+                                       throw new InvalidProjectFileException ("Only Output elements can be Task's child nodes.");
+                               if (xmlElement.GetAttribute ("ItemName") != String.Empty && xmlElement.GetAttribute ("PropertyName") != String.Empty)
+                                       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;
                                        
-                                       if (itemName != String.Empty) {
-                                               PublishItemGroup (propertyInfo, o, itemName);
-                                       } else {
-                                               PublishProperty (propertyInfo, o, propertyName);
-                                       }
+                               taskParameter = xmlElement.GetAttribute ("TaskParameter");
+                               itemName = xmlElement.GetAttribute ("ItemName");
+                               propertyName = xmlElement.GetAttribute ("PropertyName");
+                               
+                               propertyInfo = taskType.GetProperty (taskParameter, BindingFlags.Public | BindingFlags.Instance |
+                                                       BindingFlags.IgnoreCase);
+                               if (propertyInfo == null)
+                                       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 (itemName != String.Empty) {
+                                       PublishItemGroup (propertyInfo, o, itemName);
+                               } else {
+                                       PublishProperty (propertyInfo, o, propertyName);
                                }
                        }
                }
                
-               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.Contains (itemName)) {
-                               BuildItemGroup big = (BuildItemGroup) parentProject.EvaluatedItemsByName [itemName];
-                               big.Clear ();
-                               parentProject.EvaluatedItemsByName.Remove (itemName);
-                               parentProject.EvaluatedItemsByName.Add (itemName, newItems);
-                       } else {
-                               parentProject.EvaluatedItemsByName.Add (itemName, newItems);
-                       }
-               }
-               
-               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)
                {
-                       OldExpression e;
-                       object result;
-                       
-                       e = new OldExpression (parentProject);
-                       e.ParseSource (raw);
-                       
-                       if ((string) e.ConvertTo (typeof (string)) == String.Empty)
-                               return null;
+                       Expression e;
+                       result = null;
                        
-                       result = e.ConvertTo (type);
+                       e = new Expression ();
+                       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;
                }
        }
 }