Merge pull request #439 from mono-soc-2012/garyb/iconfix
[mono.git] / mcs / class / Microsoft.Build.Engine / Microsoft.Build.BuildEngine / TaskEngine.cs
index 9ca947952d24d1b39a54cde3edbaf0ed9972a840..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 (String.Format ("Required property '{0}'not set.", pi.Name));
-                               
-                               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,23 +160,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 (String.Format (
+                                       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 Exception ("This is not output property.");
+                                       throw new InvalidProjectFileException ("This is not output property.");
                                
                                o = propertyInfo.GetValue (task, null);
-                               // FIXME: maybe we should throw an exception here?
-                               if (o == null)
-                                       continue;
-                               
                                if (itemName != String.Empty) {
                                        PublishItemGroup (propertyInfo, o, itemName);
                                } else {
@@ -152,7 +194,19 @@ namespace Microsoft.Build.BuildEngine {
                                              object o,
                                              string propertyName)
                {
-                       BuildProperty bp = ChangeType.ToBuildProperty (o, propertyInfo.PropertyType, 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);
                }
 
@@ -161,7 +215,19 @@ namespace Microsoft.Build.BuildEngine {
                                               object o,
                                               string itemName)
                {
-                       BuildItemGroup newItems = ChangeType.ToBuildItemGroup (o, propertyInfo.PropertyType, itemName);
+                       if (o == null)
+                               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);
+                       }
+
+                       newItems.ParentProject = parentProject;
                        
                        if (parentProject.EvaluatedItemsByName.ContainsKey (itemName)) {
                                BuildItemGroup big = parentProject.EvaluatedItemsByName [itemName];
@@ -173,21 +239,28 @@ namespace Microsoft.Build.BuildEngine {
                        foreach (BuildItem bi in newItems)
                                parentProject.EvaluatedItems.AddItem (bi);
                }
-                               
-               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, true);
-                       
-                       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;
                }
        }
 }