In class/Microsoft.Build.Engine/Microsoft.Build.BuildEngine:
authorAnkit Jain <radical@corewars.org>
Fri, 28 Aug 2009 21:55:29 +0000 (21:55 -0000)
committerAnkit Jain <radical@corewars.org>
Fri, 28 Aug 2009 21:55:29 +0000 (21:55 -0000)
* Expression.cs (ExpressionOptions): New.

Introduce a ExpressionOptions argument to all ConvertTo*
methods. Implement the rule: in evaluation phase, expand
all items completely, but dont expand item refs in properties.
At other times, expand the item refs in the properties.
See comments in Expression.cs, for a full explanation.
* BuildItem.cs: Track api changes.
* BuildItemGroup.cs: Likewise.
* BuildProperty.cs: Track api changes. Handle self-references.
* BuildTask.cs: Track api changes.
* IReference.cs: Add ExpressionOptions param.
* ItemReference.cs: Track api changes.
* PropertyReference.cs: Likewise.
* MetadataReference.cs: Likewise.
* ExpressionCollection.cs: Track api changes. Add support for
converting all primitive types and DateTime, instead of a fixed
few.
* Project.cs (GetMetadataBatched): Use the evaluated metadata.
* TaskBatchingImpl.cs: Handle the case when batching is required,
but no items are available.

* TaskEngine.cs: Throw on unsupported types. Correctly handle
properties in case of empty values.

In class/Microsoft.Build.Engine/Test/Microsoft.Build.BuildEngine:

* BuildItemTest.cs (TestSetMetadata5a): New.
* ProjectTest.cs: Add tests for different property types
with required attribute. Also, check the values - null or
empty array.
* TestTasks.cs: Add new tasks for above.

In class/Microsoft.Build.Engine/Test/resources:

* TestTasks.cs: Add new tasks for different property types
with required attribute. Also, emit whether the value was
null or a zero length array.

In class/Microsoft.Build.Engine/Test/various:

* Items.cs: Add tests for property/item evaluation.

svn path=/trunk/mcs/; revision=140911

22 files changed:
mcs/class/Microsoft.Build.Engine/Microsoft.Build.BuildEngine/BuildItem.cs
mcs/class/Microsoft.Build.Engine/Microsoft.Build.BuildEngine/BuildItemGroup.cs
mcs/class/Microsoft.Build.Engine/Microsoft.Build.BuildEngine/BuildProperty.cs
mcs/class/Microsoft.Build.Engine/Microsoft.Build.BuildEngine/BuildTask.cs
mcs/class/Microsoft.Build.Engine/Microsoft.Build.BuildEngine/ChangeLog
mcs/class/Microsoft.Build.Engine/Microsoft.Build.BuildEngine/Expression.cs
mcs/class/Microsoft.Build.Engine/Microsoft.Build.BuildEngine/ExpressionCollection.cs
mcs/class/Microsoft.Build.Engine/Microsoft.Build.BuildEngine/IReference.cs
mcs/class/Microsoft.Build.Engine/Microsoft.Build.BuildEngine/ItemReference.cs
mcs/class/Microsoft.Build.Engine/Microsoft.Build.BuildEngine/MetadataReference.cs
mcs/class/Microsoft.Build.Engine/Microsoft.Build.BuildEngine/Project.cs
mcs/class/Microsoft.Build.Engine/Microsoft.Build.BuildEngine/PropertyReference.cs
mcs/class/Microsoft.Build.Engine/Microsoft.Build.BuildEngine/TargetBatchingImpl.cs
mcs/class/Microsoft.Build.Engine/Microsoft.Build.BuildEngine/TaskBatchingImpl.cs
mcs/class/Microsoft.Build.Engine/Microsoft.Build.BuildEngine/TaskEngine.cs
mcs/class/Microsoft.Build.Engine/Test/Microsoft.Build.BuildEngine/BuildItemTest.cs
mcs/class/Microsoft.Build.Engine/Test/Microsoft.Build.BuildEngine/ChangeLog
mcs/class/Microsoft.Build.Engine/Test/Microsoft.Build.BuildEngine/ProjectTest.cs
mcs/class/Microsoft.Build.Engine/Test/resources/ChangeLog
mcs/class/Microsoft.Build.Engine/Test/resources/TestTasks.cs
mcs/class/Microsoft.Build.Engine/Test/various/ChangeLog
mcs/class/Microsoft.Build.Engine/Test/various/Items.cs

index 08b300c385d08f6fbb9075facd047ab8c6eac9e1..b966638ad1a57f996dd1b80f4d9891a7f18457bd 100644 (file)
@@ -228,7 +228,8 @@ namespace Microsoft.Build.BuildEngine {
                        if (parent_item_group != null) {
                                Expression e = new Expression ();
                                e.Parse (value, true);
-                               evaluatedMetadata [name] = (string) e.ConvertTo (parent_item_group.ParentProject, typeof (string));
+                               evaluatedMetadata [name] = (string) e.ConvertTo (parent_item_group.ParentProject,
+                                               typeof (string), ExpressionOptions.ExpandItemRefs);
                        } else
                                evaluatedMetadata [name] = Utilities.Unescape (value);
                                
@@ -267,8 +268,8 @@ namespace Microsoft.Build.BuildEngine {
                        excludeExpr = new Expression ();
                        excludeExpr.Parse (Exclude, true);
                        
-                       includes = (string) includeExpr.ConvertTo (project, typeof (string));
-                       excludes = (string) excludeExpr.ConvertTo (project, typeof (string));
+                       includes = (string) includeExpr.ConvertTo (project, typeof (string), ExpressionOptions.ExpandItemRefs);
+                       excludes = (string) excludeExpr.ConvertTo (project, typeof (string), ExpressionOptions.ExpandItemRefs);
 
                        this.finalItemSpec = includes;
                        
@@ -319,15 +320,22 @@ namespace Microsoft.Build.BuildEngine {
                        big.AddItem (bi);
                }
                
-               internal string ConvertToString (Expression transform)
+               // during item's eval phase, any item refs in this item, have either
+               // already been expanded or are non-existant, so expand can be _false_
+               //
+               // during prop's eval phase, this isn't reached, as it parses expressions
+               // with allowItems=false, so no ItemReferences are created at all
+               //
+               // at other times, item refs have already been expanded, so expand: false
+               internal string ConvertToString (Expression transform, ExpressionOptions options)
                {
-                       return GetItemSpecFromTransform (transform);
+                       return GetItemSpecFromTransform (transform, options);
                }
                
-               internal ITaskItem ConvertToITaskItem (Expression transform)
+               internal ITaskItem ConvertToITaskItem (Expression transform, ExpressionOptions options)
                {
                        TaskItem taskItem;
-                       taskItem = new TaskItem (GetItemSpecFromTransform (transform), evaluatedMetadata);
+                       taskItem = new TaskItem (GetItemSpecFromTransform (transform, options), evaluatedMetadata);
                        return taskItem;
                }
 
@@ -342,21 +350,37 @@ namespace Microsoft.Build.BuildEngine {
                        }
                }
 
-               string GetItemSpecFromTransform (Expression transform)
+               string GetItemSpecFromTransform (Expression transform, ExpressionOptions options)
                {
                        StringBuilder sb;
                
-                       if (transform == null)
-                               return finalItemSpec;
-                       else {
+                       if (transform == null) {
+                               if (options == ExpressionOptions.ExpandItemRefs) {
+                                       // With usual code paths, this will never execute,
+                                       // but letting this be here, incase BI.ConvertTo*
+                                       // is called directly
+                                       Expression expr = new Expression ();
+                                       expr.Parse (finalItemSpec, true);
+
+                                       return (string) expr.ConvertTo (parent_item_group.ParentProject,
+                                                       typeof (string), ExpressionOptions.ExpandItemRefs);
+                               } else {
+                                       return finalItemSpec;
+                               }
+                       } else {
+                               // Transform, _DONT_ expand itemrefs
                                sb = new StringBuilder ();
                                foreach (object o in transform.Collection) {
                                        if (o is string) {
                                                sb.Append ((string)o);
                                        } else if (o is PropertyReference) {
-                                               sb.Append (((PropertyReference)o).ConvertToString (parent_item_group.ParentProject));
+                                               sb.Append (((PropertyReference)o).ConvertToString (
+                                                                       parent_item_group.ParentProject,
+                                                                       ExpressionOptions.DoNotExpandItemRefs));
                                        } else if (o is ItemReference) {
-                                               sb.Append (((ItemReference)o).ConvertToString (parent_item_group.ParentProject));
+                                               sb.Append (((ItemReference)o).ConvertToString (
+                                                                       parent_item_group.ParentProject,
+                                                                       ExpressionOptions.DoNotExpandItemRefs));
                                        } else if (o is MetadataReference) {
                                                sb.Append (GetMetadata (((MetadataReference)o).MetadataName));
                                        }
index c3ce1237b23af047692670daea7d2a5c841eabba..08c6276594823974c58043f2d560789008fb67df 100644 (file)
@@ -185,29 +185,39 @@ namespace Microsoft.Build.BuildEngine {
                        buildItems.Add (buildItem);
                }
 
+               // In eval phase, any ref'ed item would've already been expanded
+               // or it doesnt exist, so dont expand again
+               // In non-eval, items have _already_ been expanded, so dont expand again
+               // So, ignore @options
                internal string ConvertToString (Expression transform,
-                                                Expression separator)
+                                                Expression separator, ExpressionOptions options)
                {
                        string separatorString;
                        
+                       // Item refs are not expanded for separator or transform
                        if (separator == null)
                                separatorString = ";";
                        else
-                               separatorString = (string) separator.ConvertTo (parentProject, typeof (string));
+                               separatorString = (string) separator.ConvertTo (parentProject, typeof (string),
+                                                               ExpressionOptions.DoNotExpandItemRefs);
                
                        string[] items = new string [buildItems.Count];
                        int i = 0;
                        foreach (BuildItem bi in  buildItems)
-                               items [i++] = bi.ConvertToString (transform);
+                               items [i++] = bi.ConvertToString (transform, ExpressionOptions.DoNotExpandItemRefs);
                        return String.Join (separatorString, items);
                }
 
-               internal ITaskItem[] ConvertToITaskItemArray (Expression transform)
+               // In eval phase, any ref'ed item would've already been expanded
+               // or it doesnt exist, so dont expand again
+               // In non-eval, items have _already_ been expanded, so dont expand again
+               // So, ignore @options
+               internal ITaskItem[] ConvertToITaskItemArray (Expression transform, ExpressionOptions options)
                {
                        ITaskItem[] array = new ITaskItem [buildItems.Count];
                        int i = 0;
                        foreach (BuildItem item in buildItems)
-                               array [i++] = item.ConvertToITaskItem (transform);
+                               array [i++] = item.ConvertToITaskItem (transform, ExpressionOptions.DoNotExpandItemRefs);
                        return array;
                }
 
index ff75d989e34cb84a6d2b8110596e4f6254a3d3ec..a6f842538fec833e41cddb36ad1f4176e34c5dad 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
@@ -32,6 +34,7 @@ using System.Text;
 using System.Xml;
 
 using Microsoft.Build.Framework;
+using Microsoft.Build.Utilities;
 
 namespace Microsoft.Build.BuildEngine {
        public class BuildProperty {
@@ -43,6 +46,7 @@ namespace Microsoft.Build.BuildEngine {
                string          name;
                Project         parentProject;
                PropertyType    propertyType;
+               bool            converting;
 
                BuildProperty ()
                {
@@ -116,27 +120,63 @@ namespace Microsoft.Build.BuildEngine {
                {
                        BuildProperty evaluated = new BuildProperty (Name, Value);
 
+                       // In evaluate phase, properties are not expanded
                        Expression exp = new Expression ();
                        exp.Parse (Value, false, false);
-                       evaluated.finalValue = (string) exp.ConvertTo (parentProject, typeof (string));
+                       evaluated.finalValue = (string) exp.ConvertTo (parentProject, typeof (string),
+                                       ExpressionOptions.DoNotExpandItemRefs);
 
                        parentProject.EvaluatedProperties.AddProperty (evaluated);
                }
 
-               internal string ConvertToString (Project project)
+               // during property's eval phase, this is never reached, as PropertyReference
+               // handles the eval case
+               //
+               // during item's eval phase, we have expand: true, that's what we
+               // do here..
+               //
+               // during non-eval, expand: true
+               // So, its always true here
+               internal string ConvertToString (Project project, ExpressionOptions options)
                {
-                       Expression exp = new Expression ();
-                       exp.Parse (Value, true, false);
+                       if (converting) {
+                               // found ref to @this while trying to ConvertToString
+                               // for @this!
+                               return FinalValue;
+                       }
 
-                       return (string) exp.ConvertTo (project, typeof (string));
+                       converting = true;
+                       try {
+                               Expression exp = new Expression ();
+
+                               // in non-evaluation phase, properties are always expanded
+                               exp.Parse (FinalValue, options == ExpressionOptions.ExpandItemRefs, false);
+                               return (string) exp.ConvertTo (project, typeof (string), options);
+                       } finally {
+                               converting = false;
+                       }
                }
 
-               internal ITaskItem[] ConvertToITaskItemArray (Project project)
+               internal ITaskItem[] ConvertToITaskItemArray (Project project, ExpressionOptions options)
                {
-                       Expression exp = new Expression ();
-                       exp.Parse (Value, true, false);
+                       if (converting) {
+                               // found ref to @this while trying to ConvertToITaskItemArray
+                               // for @this!
+                               ITaskItem []items = new ITaskItem [1];
+                               items [0] = new TaskItem (FinalValue);
+                               return items;
+                       }
 
-                       return (ITaskItem[]) exp.ConvertTo (project, typeof (ITaskItem[]));
+                       converting = true;
+                       try {
+                               Expression exp = new Expression ();
+
+                               // in non-evaluation phase, properties are always expanded
+                               exp.Parse (FinalValue, options == ExpressionOptions.ExpandItemRefs, false);
+                               return (ITaskItem[]) exp.ConvertTo (project, typeof (ITaskItem[]), options);
+                       } finally {
+                               converting = false;
+                       }
                }
 
                internal bool FromXml {
index feba056d22e69b1cede8b97d9409adade05238e6..0a3b7058b153a8e7a0461f9100db63fa57556fbd 100644 (file)
@@ -231,7 +231,8 @@ namespace Microsoft.Build.BuildEngine {
                                else {
                                        Expression exp = new Expression ();
                                        exp.Parse (str, true);
-                                       return (bool) exp.ConvertTo (parentTarget.Project, typeof (bool));
+                                       return (bool) exp.ConvertTo (parentTarget.Project, typeof (bool),
+                                                       ExpressionOptions.ExpandItemRefs);
                                }
                        }
                        set {
index b9e59b3b5da946612ad9c2c0e6fd6fa0569265e2..70a21b65e0c2aa2cf3b58bd0c6c779ac4788c92a 100644 (file)
@@ -1,3 +1,30 @@
+2009-08-29  Ankit Jain  <jankit@novell.com>
+
+       * Expression.cs (ExpressionOptions): New.
+
+       Introduce a ExpressionOptions argument to all ConvertTo*
+       methods. Implement the rule: in evaluation phase, expand
+       all items completely, but dont expand item refs in properties.
+       At other times, expand the item refs in the properties.
+       See comments in Expression.cs, for a full explanation.
+       * BuildItem.cs: Track api changes.
+       * BuildItemGroup.cs: Likewise.
+       * BuildProperty.cs: Track api changes. Handle self-references.
+       * BuildTask.cs: Track api changes.
+       * IReference.cs: Add ExpressionOptions param.
+       * ItemReference.cs: Track api changes.
+       * PropertyReference.cs: Likewise.
+       * MetadataReference.cs: Likewise.
+       * ExpressionCollection.cs: Track api changes. Add support for
+       converting all primitive types and DateTime, instead of a fixed
+       few.
+       * Project.cs (GetMetadataBatched): Use the evaluated metadata.
+       * TaskBatchingImpl.cs: Handle the case when batching is required,
+       but no items are available.
+
+       * TaskEngine.cs: Throw on unsupported types. Correctly handle
+       properties in case of empty values.
+
 2009-08-26  Ankit Jain  <jankit@novell.com>
 
        * Expression.cs: Correctly handle a item reference with transform
index 6ac39d30f0ff5630738d74d04f23537ddbe91e97..c09d1fdbc6d2252b649c4d921062afeaba7ddff7 100644 (file)
@@ -35,6 +35,23 @@ using System.Text;
 using System.Text.RegularExpressions;
 
 namespace Microsoft.Build.BuildEngine {
+
+       // Properties and items are processed in two ways
+       // 1. Evaluate, Project calls evaluate on all the item and property groups.
+       //    At this time, the items are fully expanded, all item and property
+       //    references are expanded to get the item's value.
+       //    Properties on the other hand, expand property refs, but _not_
+       //    item references.
+       //
+       // 2. After the 'evaluation' phase, this could be when executing a target/task,
+       //    - Items : no expansion required, as they are already at final value
+       //    - Properties: Item references get expanded now, in the context of the
+       //      batching
+       //
+       // The enum ExpressionOptions is for specifying this expansion of item references.
+       //
+       // GroupingCollection.Evaluate, evaluates all properties and then items
+
        internal class Expression {
        
                ExpressionCollection expressionCollection;
@@ -55,6 +72,9 @@ namespace Microsoft.Build.BuildEngine {
 
                // @split: Split on ';'
                //         Eg. Property values don't need to be split
+               //
+               // @allowItems: item refs should not be treated as item refs!
+               //              it converts them to strings in the final expressionCollection
                public void Parse (string expression, bool allowItems, bool split)
                {
                        expression = expression.Replace ('/', Path.DirectorySeparatorChar);
@@ -254,10 +274,15 @@ namespace Microsoft.Build.BuildEngine {
 
                        return phase2;
                }
-               
+
                public object ConvertTo (Project project, Type type)
                {
-                       return expressionCollection.ConvertTo (project, type);
+                       return ConvertTo (project, type, ExpressionOptions.ExpandItemRefs);
+               }
+
+               public object ConvertTo (Project project, Type type, ExpressionOptions options)
+               {
+                       return expressionCollection.ConvertTo (project, type, options);
                }
 
                public ExpressionCollection Collection {
@@ -300,6 +325,11 @@ namespace Microsoft.Build.BuildEngine {
                        }
                }
        }
+
+       enum ExpressionOptions {
+               ExpandItemRefs,
+               DoNotExpandItemRefs
+       }
 }
 
 #endif
index eb2929576b01fb0d0a3e73f64abf87a2f0ab0649..dd314aed9a8ed9f1fcd6416b7084cbd891a35c03 100644 (file)
@@ -74,18 +74,18 @@ namespace Microsoft.Build.BuildEngine {
                        objects.Add (s);
                }
                
-               public object ConvertTo (Project project, Type type)
+               public object ConvertTo (Project project, Type type, ExpressionOptions options)
                {
                        if (type.IsArray) {
                                if (type == typeof (ITaskItem[]))
-                                       return ConvertToITaskItemArray (project);
+                                       return ConvertToITaskItemArray (project, options);
                                else
-                                       return ConvertToArray (project, type);
+                                       return ConvertToArray (project, type, options);
                        } else {
                                if (type == typeof (ITaskItem))
-                                       return ConvertToITaskItem (project);
+                                       return ConvertToITaskItem (project, options);
                                else
-                                       return ConvertToNonArray (project, type);
+                                       return ConvertToNonArray (project, type, options);
                        }
                }
                
@@ -95,23 +95,23 @@ namespace Microsoft.Build.BuildEngine {
                                yield return o;
                }
                
-               object ConvertToNonArray (Project project, Type type)
+               object ConvertToNonArray (Project project, Type type, ExpressionOptions options)
                {
-                       return ConvertToObject (ConvertToString (project), type);
+                       return ConvertToObject (ConvertToString (project, options), type, options);
                }
 
-               object ConvertToArray (Project project, Type type)
+               object ConvertToArray (Project project, Type type, ExpressionOptions options)
                {
-                       ITaskItem[] items = ConvertToITaskItemArray (project);
+                       ITaskItem[] items = ConvertToITaskItemArray (project, options);
 
                        Type element_type = type.GetElementType ();
                        Array arr = Array.CreateInstance (element_type, items.Length);
                        for (int i = 0; i < arr.Length; i ++)
-                               arr.SetValue (ConvertToObject (items [i].ItemSpec, element_type), i);
+                               arr.SetValue (ConvertToObject (items [i].ItemSpec, element_type, options), i);
                        return arr;
                }
 
-               object ConvertToObject (string raw, Type type)
+               object ConvertToObject (string raw, Type type, ExpressionOptions options)
                {
                        if (type == typeof (bool)) {
                                bool value;
@@ -123,17 +123,17 @@ namespace Microsoft.Build.BuildEngine {
 
                        if (type == typeof (string))
                                return raw;
-                       else if (type == typeof (int))
-                               return Int32.Parse (raw);
-                       else if (type == typeof (uint))
-                               return UInt32.Parse (raw);
-                       else if (type == typeof (DateTime))
+
+                       if (type.IsPrimitive)
+                               return Convert.ChangeType (raw, type);
+
+                       if (type == typeof (DateTime))
                                return DateTime.Parse (raw);
-                       else
-                               throw new Exception (String.Format ("Unknown type: {0}", type.ToString ()));
+
+                       throw new Exception (String.Format ("Unsupported type: {0}", type));
                }
 
-               string ConvertToString (Project project)
+               string ConvertToString (Project project, ExpressionOptions options)
                {
                        StringBuilder sb = new StringBuilder ();
                        
@@ -146,25 +146,26 @@ namespace Microsoft.Build.BuildEngine {
 
                                IReference br = o as IReference;
                                if (br != null)
-                                       sb.Append (br.ConvertToString (project));
+                                       sb.Append (br.ConvertToString (project, options));
                                else
                                        throw new Exception ("BUG: Invalid type in objects collection.");
                        }
                        return sb.ToString ();
                }
 
-               ITaskItem ConvertToITaskItem (Project project)
+               ITaskItem ConvertToITaskItem (Project project, ExpressionOptions options)
                {
                        ITaskItem item;
                        
                        if (objects == null)
                                throw new Exception ("Cannot cast empty expression to ITaskItem.");
 
-                       ITaskItem[] items = ConvertToITaskItemArray (project);
-                       if (items.Length != 1)
+                       ITaskItem[] items = ConvertToITaskItemArray (project, options);
+                       if (items.Length > 1)
                                //FIXME: msbuild gives better errors
-                               throw new Exception (String.Format ("Too many items: {0}", items.Length));
+                               throw new Exception (String.Format ("Exactly one item required, but got: {0}", items.Length));
 
+                       if (items.Length == 0) return null;
                        return items [0];
                }
                
@@ -175,7 +176,7 @@ namespace Microsoft.Build.BuildEngine {
                //   PropertyRef concats if it doesn't end in ';'
                // - string cannot concat with ItemRef unless it is ';'.
                //   string concats if it ends in ';'
-               ITaskItem[] ConvertToITaskItemArray (Project project)
+               ITaskItem[] ConvertToITaskItemArray (Project project, ExpressionOptions options)
                {
                        List <ITaskItem> finalItems = new List <ITaskItem> ();
                        
@@ -228,7 +229,7 @@ namespace Microsoft.Build.BuildEngine {
                                        prev_can_concat = !(value.Length > 0 && value [value.Length - 1] == ';');
                                }
 
-                               AddItemsToArray (finalItems, br.ConvertToITaskItemArray (project), can_concat);
+                               AddItemsToArray (finalItems, br.ConvertToITaskItemArray (project, options), can_concat);
 
                                prev = o;
                        }
index a501ec7462b0200a2e2e06efe61ed0cfe00520f7..30104edd7ed816e236db75d856019a4f20985cc4 100644 (file)
@@ -31,8 +31,8 @@ using Microsoft.Build.Framework;
 
 namespace Microsoft.Build.BuildEngine {
        internal interface IReference {
-               string ConvertToString (Project project);
-               ITaskItem[] ConvertToITaskItemArray (Project project);
+               string ConvertToString (Project project, ExpressionOptions options);
+               ITaskItem[] ConvertToITaskItemArray (Project project, ExpressionOptions options);
 
                int Start { get; }
                int End { get; }
index ee827a4132b780c50c5c3b4944f1523eca995d62..7d0ff5265b53a91185dee12ef21a5983f4be20f2 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
@@ -48,6 +50,7 @@ namespace Microsoft.Build.BuildEngine {
                        this.length = length;
                        this.original_string = original_string;
 
+                       // Transform and separator are never expanded for item refs
                        if (transform != null) {
                                this.transform = new Expression ();
                                this.transform.Parse (transform, false);
@@ -59,23 +62,26 @@ namespace Microsoft.Build.BuildEngine {
                        }
                }
                
-               public string ConvertToString (Project project)
+               // when evaluating property, allowItems=false, so,
+               // ItemRef will _not_ get created, so this wont get hit
+               // when evaluating items, expand: true
+               // other cases, expand: true
+               public string ConvertToString (Project project, ExpressionOptions options)
                {
                        BuildItemGroup group;
                        if (project.TryGetEvaluatedItemByNameBatched (itemName, out group))
-                               return group.ConvertToString (transform, separator);
+                               return group.ConvertToString (transform, separator, options);
                        else
                                return String.Empty;
                }
                
-               public ITaskItem [] ConvertToITaskItemArray (Project project)
+               public ITaskItem [] ConvertToITaskItemArray (Project project, ExpressionOptions options)
                {
                        BuildItemGroup group;
                        if (project.TryGetEvaluatedItemByNameBatched (itemName, out group))
-                               return group.ConvertToITaskItemArray (transform);
+                               return group.ConvertToITaskItemArray (transform, options);
                        else
                                return null;
-
                }
 
                public string ItemName {
index 3e245ab1c9deca3ad1b59b117c4690a60936a98b..096fa214fc783cd9f6adee5bf0c9677a9e85b066 100644 (file)
@@ -69,12 +69,12 @@ namespace Microsoft.Build.BuildEngine {
                        get { return start + length - 1; }
                }
 
-               public string ConvertToString (Project project)
+               public string ConvertToString (Project project, ExpressionOptions options)
                {
                        return project.GetMetadataBatched (itemName, metadataName);
                }
 
-               public ITaskItem [] ConvertToITaskItemArray (Project project)
+               public ITaskItem [] ConvertToITaskItemArray (Project project, ExpressionOptions options)
                {
                        List<ITaskItem> items = new List<ITaskItem> ();
                        if (IsQualified) {
index 4bd8dfe36aaa67184cc80a3afaf9cff3c290b335..620eacfa9080db5e0bff4e8f87df95fffcd26642 100644 (file)
@@ -1063,7 +1063,7 @@ namespace Microsoft.Build.BuildEngine {
                        if (group != null) {
                                foreach (BuildItem item in group) {
                                        if (item.HasMetadata (metadataName))
-                                               return item.GetMetadata (metadataName);
+                                               return item.GetEvaluatedMetadata (metadataName);
                                }
                        }
                        return String.Empty;
index 3b857fa5ab20b2be750cb874bcca254975bd9c72..285808ed5952c70fc1bfde7159c6bc2fb322f617 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
@@ -47,16 +49,39 @@ namespace Microsoft.Build.BuildEngine {
                        this.length = length;
                }
                
-               public string ConvertToString (Project project)
+
+               // when evaluating items: expand: true
+               // all other times, expand: true
+               // so, always true, ignore @options
+               public string ConvertToString (Project project, ExpressionOptions options)
                {
                        BuildProperty bp = project.EvaluatedProperties [name];
-                       return bp != null ? bp.ConvertToString (project) : String.Empty;
+                       if (bp == null)
+                               return String.Empty;
+
+                       if (options == ExpressionOptions.DoNotExpandItemRefs)
+                               return bp.FinalValue;
+
+                       return bp.ConvertToString (project, ExpressionOptions.ExpandItemRefs);
                }
 
-               public ITaskItem[] ConvertToITaskItemArray (Project project)
+               // when evaluating items: expand: true
+               // all other times, expand: true
+               // so, always true, ignore @options
+               public ITaskItem[] ConvertToITaskItemArray (Project project, ExpressionOptions options)
                {
                        BuildProperty bp = project.EvaluatedProperties [name];
-                       return bp != null ? bp.ConvertToITaskItemArray (project) : null;
+                       if (bp == null)
+                               return null;
+
+                       if (options == ExpressionOptions.DoNotExpandItemRefs) {
+                               List<ITaskItem> list = new List<ITaskItem> ();
+                               foreach (string s in bp.FinalValue.Split (new char[] {';'}, StringSplitOptions.RemoveEmptyEntries))
+                                       list.Add (new TaskItem (s));
+                               return list.ToArray ();
+                       }
+
+                       return bp.ConvertToITaskItemArray (project, ExpressionOptions.ExpandItemRefs);
                }
                
                public string Name {
index fb394bf1731e449cbc882bc2d1bf3449094ad506..c299755bce70f6bb58ebdd5401b9666fe156f0e8 100644 (file)
@@ -177,11 +177,11 @@ namespace Microsoft.Build.BuildEngine {
 
                        Expression e = new Expression ();
                        e.Parse (inputs, true);
-                       inputFiles = (ITaskItem[]) e.ConvertTo (project, typeof (ITaskItem[]));
+                       inputFiles = (ITaskItem[]) e.ConvertTo (project, typeof (ITaskItem[]), ExpressionOptions.ExpandItemRefs);
 
                        e = new Expression ();
                        e.Parse (outputs, true);
-                       outputFiles = (ITaskItem[]) e.ConvertTo (project, typeof (ITaskItem[]));
+                       outputFiles = (ITaskItem[]) e.ConvertTo (project, typeof (ITaskItem[]), ExpressionOptions.ExpandItemRefs);
 
                        if (inputFiles == null || inputFiles.Length == 0)
                                return false;
index 2c10c76238deb227068f737340de7ac801adc82f..7ee3e93dd4097d46afb3bee5285a94d20a0c82fa 100644 (file)
@@ -76,17 +76,26 @@ namespace Microsoft.Build.BuildEngine {
 
                        // Run the task in batches
                        bool retval = true;
-                       foreach (Dictionary<string, BuildItemGroup> bucket in buckets) {
-                               project.SetBatchedItems (bucket, commonItemsByName);
+                       if (buckets.Count > 0) {
+                               foreach (Dictionary<string, BuildItemGroup> bucket in buckets) {
+                                       project.SetBatchedItems (bucket, commonItemsByName);
+                                       if (ConditionParser.ParseAndEvaluate (buildTask.Condition, project)) {
+                                                retval = buildTask.Execute ();
+                                                if (!retval && !buildTask.ContinueOnError) {
+                                                        executeOnErrors = true;
+                                                        break;
+                                                }
+                                       }
+                               }
+                               project.SetBatchedItems (null, null);
+                       } else {
+                               // batched mode, but no values in the corresponding items!
                                if (ConditionParser.ParseAndEvaluate (buildTask.Condition, project)) {
-                                        retval = buildTask.Execute ();
-                                        if (!retval && !buildTask.ContinueOnError) {
-                                                executeOnErrors = true;
-                                                break;
-                                        }
+                                       retval = buildTask.Execute ();
+                                       if (!retval && !buildTask.ContinueOnError)
+                                               executeOnErrors = true;
                                }
                        }
-                       project.SetBatchedItems (null, null);
 
                        return retval;
                }
index 569e1e30c7555eb204288a8b3d22e37f3749c7aa..bced7047949743410279796f20acb96c614c5461 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)
@@ -78,24 +92,45 @@ namespace Microsoft.Build.BuildEngine {
                                                de.Key));
                                
                                try {
-                                       value = GetObjectFromString (de.Value, currentProperty.PropertyType);
+                                       if (TryGetObjectFromString (de.Value, currentProperty.PropertyType, out value))
+                                               values.Add (de.Key, value);
                                } catch (Exception e) {
                                        throw new Exception (String.Format (
                                                        "Error converting Property named '{0}' with value '{1}' to type {2}: {3}",
                                                        de.Key, de.Value, currentProperty.PropertyType, e.Message), e);
                                }
-                               
-                               if (value != null)
-                                       values.Add (de.Key, value);
                        }
                        
                        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);
                        }
                }
                
@@ -182,24 +217,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);
 
-                       // Empty contents allowed only for arrays
-                       // See TestRequiredTask_*
-                       if (!type.IsArray &&
-                               (string) e.ConvertTo (parentProject, typeof (string)) == String.Empty)
-                               return null;
-                       
-                       result = e.ConvertTo (parentProject, type);
+                       // 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;
                }
        }
 }
index 696e7f42fe47452523b18557847a2d3c60913bcd..57467ad5a0cbd025f8d33cbbc370c1203b1d9dec 100644 (file)
@@ -716,6 +716,83 @@ namespace MonoTests.Microsoft.Build.BuildEngine {
                        Assert.AreEqual ("A", project.EvaluatedItems [1].GetEvaluatedMetadata ("Meta"), "A6");
                }
 
+               [Test]
+               public void TestSetMetadata5a () {
+                       Engine engine;
+                       Project project;
+                       BuildItemGroup[] groups = new BuildItemGroup[1];
+
+                       string documentString = @"
+                               <Project xmlns='http://schemas.microsoft.com/developer/msbuild/2003'>
+                                       <PropertyGroup>
+                                               <A>A</A>
+                                               <C>@(D)</C>
+                                       </PropertyGroup>
+                                       <ItemGroup>
+                                               <D Include='D'/>
+                                               <C Include='$(C)'/>
+                                               <A Include='a;b'>
+                                                       <Md>@(C)</Md>
+                                               </A>
+                                               <B Include='$(A)'/>
+                                       </ItemGroup>
+                                       <Target Name='main'>
+                                               <Message Text=""a.md: %(A.Md)""/>
+                                               <Message Text=""a.md: %(A.Meta)""/>
+                                       </Target>
+                               </Project>
+                       ";
+
+                       engine = new Engine (Consts.BinPath);
+                       project = engine.CreateNewProject ();
+                       MonoTests.Microsoft.Build.Tasks.TestMessageLogger logger = new MonoTests.Microsoft.Build.Tasks.TestMessageLogger ();
+                       engine.RegisterLogger (logger);
+                       project.LoadXml (documentString);
+
+                       CheckMetadata (project, "A", "Md", new string [] {"@(C)", "@(C)"}, "G1");
+                       CheckEvaluatedMetadata (project, "A", "Md", new string[] { "D", "D" }, "G2");
+
+                       //@(B)
+                       Assert.AreEqual ("A", project.GetEvaluatedItemsByName ("B")[0].FinalItemSpec, "B2");
+
+                       project.ItemGroups.CopyTo (groups, 0);
+                       /*Broken right now:
+                         CheckBuildItemGroup (groups[0], new string[] {
+                               "D", "D",
+                               "C", "$(C)",
+                               "A", "a;b",
+                               "B", "$(A)"
+                       }, "H1");*/
+
+                       CheckBuildItemGroup (project.GetEvaluatedItemsByName ("C"), new string[] {
+                               "C", "D"
+                       }, "H2");
+
+                       CheckBuildItemGroup (project.GetEvaluatedItemsByName ("C"), new string[] {
+                               "C", "D"
+                       }, "I");
+
+                       project.GetEvaluatedItemsByName ("A")[0].SetMetadata ("Meta", "@(B)");
+
+                       Assert.AreEqual (5, project.EvaluatedItems.Count, "A0");
+                       Assert.AreEqual (2, project.GetEvaluatedItemsByName ("A").Count, "A7");
+
+                       CheckMetadata (project, "A", "Meta", new string[] { "@(B)", "" }, "J");
+
+                       if (!project.Build ()) {
+                               logger.DumpMessages ();
+                               Assert.Fail ("Build failed");
+                       }
+                       logger.DumpMessages ();
+
+                       CheckMetadata (project, "A", "Meta", new string[] { "@(B)", "" }, "K1");
+                       CheckEvaluatedMetadata (project, "A", "Meta", new string[] { "", "" }, "K2");
+
+                       logger.CheckLoggedMessageHead ("a.md: D", "E10");
+                       logger.CheckLoggedMessageHead ("a.md: ", "E11");
+                       Assert.AreEqual (0, logger.NormalMessageCount, "Unexpected messages left");
+               }
+
                [Test]
                public void TestSetMetadata6 ()
                {
@@ -848,5 +925,44 @@ namespace MonoTests.Microsoft.Build.BuildEngine {
                        Assert.AreEqual ("PropValue/abc.txt", grp [0].FinalItemSpec, "A2");
                        Assert.AreEqual ("PropValue/def.txt", grp [1].FinalItemSpec, "A3");
                }
+
+               void CheckMetadata (Project p, string itemname, string metadataname, string[] values, string prefix)
+               {
+                       BuildItemGroup group = p.GetEvaluatedItemsByName (itemname);
+
+                       Assert.AreEqual (values.Length, group.Count, "Number of items for itemname " + itemname);
+
+                       for (int i = 0; i < values.Length; i++) {
+                               Assert.AreEqual (values[i], group [i].GetMetadata (metadataname), prefix + "#" + i.ToString ());
+                       }
+               }
+
+               void CheckEvaluatedMetadata (Project p, string itemname, string metadataname, string[] values, string prefix)
+               {
+                       BuildItemGroup group = p.GetEvaluatedItemsByName (itemname);
+
+                       Assert.AreEqual (values.Length, group.Count, "Number of items for itemname " + itemname);
+
+                       for (int i = 0; i < values.Length; i++) {
+                               Assert.AreEqual (values[i], group [i].GetEvaluatedMetadata (metadataname), prefix + "#" + i.ToString ());
+                       }
+               }
+
+               void CheckBuildItemGroup (BuildItemGroup group, string[] names, string prefix)
+               {
+                       try {
+                               Assert.AreEqual (group.Count, names.Length / 2, "Number of items in group");
+                               for (int i = 0; i < group.Count; i++) {
+                                       Assert.AreEqual (names[i * 2], group[i].Name, String.Format ("{0}#{1} : item name", prefix, i));
+                                       Assert.AreEqual (names[(i * 2) + 1], group[i].FinalItemSpec, String.Format ("{0}#{1} : FinalItemSpec", prefix, i));
+                               }
+                       } catch (AssertionException) {
+                               for (int i = 0; i < group.Count; i++) {
+                                       Console.WriteLine ("group[{0}] = {1}", i, group[i].Name);
+                                       Console.WriteLine ("group[{0}] = {1}", i, group[i].FinalItemSpec);
+                               }
+                               throw;
+                       }
+               }
        }
 }
index e80067631e1b0c2b1f1474a1657b2a08ec92a1f4..7a7fe9f92f00150e43879552fce91e881d449f91 100644 (file)
@@ -1,3 +1,11 @@
+2009-08-29  Ankit Jain  <jankit@novell.com>
+
+       * BuildItemTest.cs (TestSetMetadata5a): New.
+       * ProjectTest.cs: Add tests for different property types
+       with required attribute. Also, check the values - null or
+       empty array.
+       * TestTasks.cs: Add new tasks for above.
+
 2009-08-18  Ankit Jain  <jankit@novell.com>
 
        * ProjectTest.cs (TestCaseSensitivityOfProjectElements): New.
index 106637267c82d902fefbf537f8d15a39f5196af5..d287de88b6912d2d090fb2ae19fafb4b55b27bf3 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
@@ -97,6 +99,7 @@ namespace MonoTests.Microsoft.Build.BuildEngine {
                                "<Project></Project>";
                        
                        engine = new Engine (Consts.BinPath);
+
                        DateTime time = DateTime.Now;
                        project = engine.CreateNewProject ();
                        try {
@@ -1524,35 +1527,89 @@ namespace MonoTests.Microsoft.Build.BuildEngine {
                public void TestRequiredTask_String1 ()
                {
                        CheckProjectForRequiredTests ("RequiredTestTask_String", "@(NonExistant)",
-                               false, "Should've failed: No value specified for required field - 'Property' of RequiredTestTask_String");
+                               false, "Should've failed: No value specified for required field - 'Property' of RequiredTestTask_String", null);
                }
 
                [Test]
                public void TestRequiredTask_String2 ()
                {
                        CheckProjectForRequiredTests ("RequiredTestTask_String", "$(NonExistant)",
-                               false, "Should've failed: No value specified for required field - 'Property' of RequiredTestTask_String");
+                               false, "Should've failed: No value specified for required field - 'Property' of RequiredTestTask_String", null);
+               }
+
+               [Test]
+               public void TestRequiredTask_Strings1 () {
+                       CheckProjectForRequiredTests ("RequiredTestTask_Strings", "@(NonExistant)",
+                               true, "Build failed", "0");
+               }
+
+               [Test]
+               public void TestRequiredTask_Strings2 () {
+                       CheckProjectForRequiredTests ("RequiredTestTask_Strings", "$(NonExistant)",
+                               true, "Build failed", "0");
+               }
+
+               [Test]
+               public void TestRequiredTask_Strings3 () {
+                       CheckProjectForRequiredTests ("RequiredTestTask_Strings", "%(NonExistant.Md)",
+                               true, "Build failed", "0");
+               }
+
+               [Test]
+               public void TestRequiredTask_Strings4 () {
+                       CheckProjectForRequiredTests ("RequiredTestTask_Strings", "  %(NonExistant.Md)",
+                               true, "Build failed", "0");
+               }
+
+               [Test]
+               public void TestRequiredTask_Ints1 () {
+                       CheckProjectForRequiredTests ("RequiredTestTask_IntArray", "@(NonExistant)",
+                               true, "Build failed", "count: 0");
+               }
+
+               [Test]
+               public void TestRequiredTask_Ints2 () {
+                       CheckProjectForRequiredTests ("RequiredTestTask_IntArray", "$(NonExistant)",
+                               true, "Build failed", "count: 0");
+               }
+
+               [Test]
+               public void TestRequiredTask_OtherObjectsArray () {
+                       CheckProjectForRequiredTests ("RequiredTestTask_OtherObjectArray", "@(NonExistant)",
+                               false, "Should've failed: ObjectArray type not supported as a property type", null);
+               }
+
+               [Test]
+               public void TestRequiredTask_OtherObject () {
+                       CheckProjectForRequiredTests ("RequiredTestTask_OtherObjectArray", "@(NonExistant)",
+                               false, "Should've failed: ObjectArray type not supported as a property type", null);
+               }
+
+               [Test]
+               public void TestRequiredTask_MyTaskItems1 () {
+                       CheckProjectForRequiredTests ("RequiredTestTask_MyTaskItemArray", "@(NonExistant)",
+                               false, "Should've failed: ObjectArray type not supported as a property type", null);
                }
 
                [Test]
                public void TestRequiredTask_TaskItem1 ()
                {
                        Project p = CheckProjectForRequiredTests ("RequiredTestTask_TaskItem", "@(NonExistant)",
-                               false, "Should've failed: No value specified for required field - 'Property' of RequiredTestTask_TaskItem");
+                               false, "Should've failed: No value specified for required field - 'Property' of RequiredTestTask_TaskItem", null);
                }
 
                [Test]
                public void TestRequiredTask_TaskItem2 ()
                {
                        Project p = CheckProjectForRequiredTests ("RequiredTestTask_TaskItem", "$(NonExistant)",
-                               false, "Should've failed: No value specified for required field - 'Property' of RequiredTestTask_TaskItem");
+                               false, "Should've failed: No value specified for required field - 'Property' of RequiredTestTask_TaskItem", null);
                }
 
                [Test]
                public void TestRequiredTask_TaskItemArray1 ()
                {
                        Project p = CheckProjectForRequiredTests ("RequiredTestTask_TaskItems", "@(NonExistant)",
-                               true, "Build failed");
+                               true, "Build failed", "count: 0");
 
                        BuildItemGroup group = p.GetEvaluatedItemsByName ("OutItem");
                        Assert.AreEqual (1, group.Count, "A2");
@@ -1563,7 +1620,7 @@ namespace MonoTests.Microsoft.Build.BuildEngine {
                public void TestRequiredTask_TaskItemArray2 ()
                {
                        Project p = CheckProjectForRequiredTests ("RequiredTestTask_TaskItems", "$(NonExistant)",
-                               true, "Build failed");
+                               true, "Build failed", "count: 0");
 
                        BuildItemGroup group = p.GetEvaluatedItemsByName ("OutItem");
                        Assert.AreEqual (1, group.Count, "A2");
@@ -1574,13 +1631,35 @@ namespace MonoTests.Microsoft.Build.BuildEngine {
                public void TestRequiredTask_TaskItemArray3 ()
                {
                        Project p = CheckProjectForRequiredTests ("RequiredTestTask_IntArray", "$(NonExistant)",
-                               true, "Build failed");
+                               true, "Build failed", "count: 0");
 
                        BuildItemGroup group = p.GetEvaluatedItemsByName ("OutItem");
                        Assert.AreEqual (1, group.Count, "A2");
                        Assert.AreEqual ("count: 0", group [0].FinalItemSpec, "A3");
                }
 
+               [Test]
+               public void TestRequiredTask_TaskItemArray4 () {
+                       Project p = CheckProjectForRequiredTests ("RequiredTestTask_IntArray", "%(NonExistant.Md)",
+                               true, "Build failed", "count: 0");
+
+                       BuildItemGroup group = p.GetEvaluatedItemsByName ("OutItem");
+                       Assert.AreEqual (1, group.Count, "A2");
+                       Assert.AreEqual ("count: 0", group[0].FinalItemSpec, "A3");
+               }
+
+               [Test]
+               public void TestRequiredTask_TaskItemArray5 () {
+                       // with extra space in prop value
+                       Project p = CheckProjectForRequiredTests ("RequiredTestTask_IntArray", "  %(NonExistant.Md)",
+                               true, "Build failed", "count: 0");
+
+                       BuildItemGroup group = p.GetEvaluatedItemsByName ("OutItem");
+                       Assert.AreEqual (1, group.Count, "A2");
+                       Assert.AreEqual ("count: 0", group[0].FinalItemSpec, "A3");
+               }
+
+
                [Test]
                public void TestCaseSensitivityOfProjectElements ()
                {
@@ -1761,7 +1840,8 @@ namespace MonoTests.Microsoft.Build.BuildEngine {
                        }
                }
 
-               Project CheckProjectForRequiredTests (string taskname, string property_arg, bool expected_result, string error_msg)
+               Project CheckProjectForRequiredTests (string taskname, string property_arg, bool expected_result, string error_msg,
+                       string expected_output_msg)
                {
                        string projectString = String.Format (@"<Project xmlns=""http://schemas.microsoft.com/developer/msbuild/2003"">
                                <UsingTask TaskName=""{0}"" AssemblyFile=""Test/resources/TestTasks.dll"" />
@@ -1769,6 +1849,7 @@ namespace MonoTests.Microsoft.Build.BuildEngine {
                                        <{0} Property=""{1}"">
                                                <Output TaskParameter=""Output"" ItemName=""OutItem""/>
                                        </{0}>
+                                       <Message Text='@(OutItem)'/>
                                </Target>
                        </Project>", taskname, property_arg);
 
@@ -1778,9 +1859,12 @@ namespace MonoTests.Microsoft.Build.BuildEngine {
                        engine.RegisterLogger (logger);
                        Project project = engine.CreateNewProject ();
                        project.LoadXml (projectString);
-
                        try {
                                Assert.AreEqual (expected_result, project.Build (), error_msg);
+                               if (expected_result) {
+                                       logger.CheckLoggedMessageHead (expected_output_msg, "A");
+                                       Assert.AreEqual (0, logger.NormalMessageCount, "Unexpected messages found");
+                               }
                        } finally {
                                logger.DumpMessages ();
                        }
index b7f6cde0cdaf4ff26ba0788175bd04d860fa3624..42b4b7c7d257744b2ddc99664e76442fad131d63 100644 (file)
@@ -1,3 +1,9 @@
+2009-08-29  Ankit Jain  <jankit@novell.com>
+
+       * TestTasks.cs: Add new tasks for different property types
+       with required attribute. Also, emit whether the value was
+       null or a zero length array.
+
 2009-03-26  Jonathan Chambers  <joncham@gmail.com>
 
        * TestTasks.cs (NamespacedOutputTestTask): New.
index 46b032fe559e64176d88d870744a9bd2adcc730c..ac87e56dfb976c95fc2ef22faef205b267c89418 100644 (file)
@@ -28,6 +28,7 @@
 using System;
 using Microsoft.Build.Framework;
 using Microsoft.Build.Utilities;
+using System.Text;
 
 namespace Foo
 {
@@ -58,6 +59,45 @@ public class OutputTestTask : Task {
        }
 }
 
+public class TestTask_TaskItems : Task
+{
+       string output;
+       public override bool Execute () {
+               output = items == null ? "null" : "count: " + items.Length.ToString ();
+               return true;
+       }
+
+       ITaskItem[] items;
+       public ITaskItem[] Property {
+               set { items = value; }
+       }
+
+       [Output]
+       public string Output {
+               get { return output; }
+       }
+}
+
+public class TestTask_TaskItem : Task
+{
+       string output;
+       public override bool Execute () {
+               output = item == null ? "null" : "not null: " + item.ItemSpec;
+               return true;
+       }
+
+       ITaskItem item;
+       public ITaskItem Property {
+               set { item = value; }
+       }
+
+       [Output]
+       public string Output {
+               get { return output; }
+       }
+}
+
+
 public class RequiredTestTask_TaskItems : Task {
        string output;
        public override bool Execute ()
@@ -125,6 +165,134 @@ public class RequiredTestTask_String : Task
        }
 }
 
+public class RequiredTestTask_Strings : Task
+{
+       string output;
+       public override bool Execute () {
+               output = property == null ? "null" : property.Length.ToString ();
+               return true;
+       }
+
+       string []property;
+       [Required]
+       public string[] Property {
+               set { property = value; }
+       }
+
+       [Output]
+       public string Output {
+               get { return output; }
+       }
+}
+
+public class RequiredTestTask_OtherObjectArray : Task
+{
+       string output;
+       public override bool Execute () {
+               output = property == null ? "null" : property.Length.ToString ();
+               return true;
+       }
+
+       OtherClass[] property;
+       [Required]
+       public OtherClass[] Property {
+               set { property = value; }
+       }
+
+       [Output]
+       public string Output {
+               get { return output; }
+       }
+}
+
+public class RequiredTestTask_OtherObject : Task
+{
+       string output;
+       public override bool Execute () {
+               output = property == null ? "null" : "not null";
+               return true;
+       }
+
+       OtherClass property;
+       [Required]
+       public OtherClass Property {
+               set { property = value; }
+       }
+
+       [Output]
+       public string Output {
+               get { return output; }
+       }
+}
+
+public class RequiredTestTask_MyTaskItemArray : Task
+{
+       string output;
+       public override bool Execute () {
+               output = property == null ? "null" : property.Length.ToString ();
+               return true;
+       }
+
+       MyTaskItem[] property;
+       [Required]
+       public MyTaskItem[] Property {
+               set { property = value; }
+       }
+
+       [Output]
+       public string Output {
+               get { return output; }
+       }
+}
+
+public class OtherClass
+{
+}
+
+public class MyTaskItem : ITaskItem
+{
+       #region ITaskItem Members
+
+       public System.Collections.IDictionary CloneCustomMetadata () {
+               throw new NotImplementedException ();
+       }
+
+       public void CopyMetadataTo (ITaskItem destinationItem) {
+               throw new NotImplementedException ();
+       }
+
+       public string GetMetadata (string metadataName) {
+               throw new NotImplementedException ();
+       }
+
+       public string ItemSpec {
+               get {
+                       throw new NotImplementedException ();
+               }
+               set {
+                       throw new NotImplementedException ();
+               }
+       }
+
+       public int MetadataCount {
+               get { throw new NotImplementedException (); }
+       }
+
+       public System.Collections.ICollection MetadataNames {
+               get { throw new NotImplementedException (); }
+       }
+
+       public void RemoveMetadata (string metadataName) {
+               throw new NotImplementedException ();
+       }
+
+       public void SetMetadata (string metadataName, string metadataValue) {
+               throw new NotImplementedException ();
+       }
+
+       #endregion
+}
+
 public class RequiredTestTask_IntArray: Task
 {
        string output;
@@ -164,8 +332,15 @@ public class FalseTestTask : Task {
 }
 
 public class StringTestTask : Task {
+       string output;
+
        public override bool Execute ()
        {
+               StringBuilder sb = new StringBuilder ();
+               sb.AppendFormat ("property: {0} ## ", property == null ? "null" : property);
+               sb.AppendFormat ("array: {0}", array == null ? "null" : array.Length.ToString ());
+
+               output = sb.ToString ();
                return true;
        }
 
@@ -182,7 +357,12 @@ public class StringTestTask : Task {
        public string[] Array {
                get { return array; }
                set { array = value; }
-       }       
+       }
+
+       [Output]
+       public string OutputString {
+               get { return output; }
+       }
 }
 
 public class PublishTestTask : Task {
index 0a2badbdd94f80bd9fef7d9e7860a4ca2c96a7d9..b6084c513a75586328ad8c1555e6cab522cab519 100644 (file)
@@ -1,3 +1,7 @@
+2009-08-29  Ankit Jain  <jankit@novell.com>
+
+       * Items.cs: Add tests for property/item evaluation.
+
 2009-06-12  Ankit Jain  <jankit@novell.com>
 
        * Items.cs (TestItemsInTarget3a): Add another case for valid whitespace
index 15311420b4a0a67cc18d12bfdafa312a10803666..36ae351311337b02d4eaac2006408795996f3c6e 100644 (file)
@@ -160,25 +160,38 @@ namespace MonoTests.Microsoft.Build.BuildEngine.Various {
 
                        string documentString = @"
                                <Project xmlns=""http://schemas.microsoft.com/developer/msbuild/2003"">
+                                       <PropertyGroup>
+                                               <Prop1>@(Item0)</Prop1>
+                                       </PropertyGroup>
                                        <ItemGroup>
+                                               <Item0 Include=""Foo""/>
                                                <Item1 Include='A;B;C' />
                                                <Item2 Include=""A\B.txt;A\C.txt;B\B.zip;B\C.zip"" />
+                                               <ItemT0 Include=""@(Item1)"" />
                                                <ItemT1 Include=""@(Item1->'%(Identity)')"" />
                                                <ItemT2 Include=""@(Item1->'%(Identity)%(Identity)')"" />
                                                <ItemT3 Include=""@(Item1->'(-%(Identity)-)')"" />
                                                <ItemT4 Include=""@(Item2->'%(Extension)')"" />
                                                <ItemT5 Include=""@(Item2->'%(Filename)/%(Extension)')"" />
+                                               <ItemT6 Include=""@(Item2->'%(Extension)/$(Prop1)')"" />
                                        </ItemGroup>
                                </Project>
                        ";
 
                        proj.LoadXml (documentString);
 
+                       //Assert.IsTrue (proj.Build (), "Build failed");
+
+                       Assert.AreEqual ("@(Item0)", proj.EvaluatedProperties["Prop1"].FinalValue, "A0");
+                       //Assert.AreEqual ("@(Item2->'%(Extension)/$(Prop1)')", proj.EvaluatedItems [7].FinalItemSpec, "B0");
+
+                       CheckItems (proj, "ItemT0", "A1", "A", "B", "C");
                        CheckItems (proj, "ItemT1", "A1", "A", "B", "C");
                        CheckItems (proj, "ItemT2", "A2", "AA", "BB", "CC");
                        CheckItems (proj, "ItemT3", "A3", "(-A-)", "(-B-)", "(-C-)");
                        CheckItems (proj, "ItemT4", "A4", ".txt", ".txt", ".zip", ".zip");
                        CheckItems (proj, "ItemT5", "A5", "B/.txt", "C/.txt", "B/.zip", "C/.zip");
+                       CheckItems (proj, "ItemT6", "A6", ".txt/@(Item0)", ".txt/@(Item0)", ".zip/@(Item0)", ".zip/@(Item0)");
                }
 
                [Test]
@@ -306,6 +319,323 @@ namespace MonoTests.Microsoft.Build.BuildEngine.Various {
                        proj.LoadXml (documentString);
                }
 
+               [Test]
+               // test item metadata
+               public void TestItems10 ()
+               {
+                       string project_xml = @"<Project xmlns=""http://schemas.microsoft.com/developer/msbuild/2003"">
+       <PropertyGroup>
+               <Prop1>@(Item0)</Prop1>
+               <Prop2>@(Ref1)</Prop2>
+       </PropertyGroup>
+       <ItemGroup>
+               <Item0 Include=""Foo""/>
+               <Ref1 Include=""File1"" />
+               <IWithM Include=""A"">
+                       <Md>@(Item0)</Md>
+                       <Md2>$(Prop2)</Md2>
+               </IWithM>
+       </ItemGroup>
+
+       <Target Name=""1"">
+               <Message Text=""IWithM.md: %(IWithM.Md)""/>
+               <Message Text=""IWithM.md2: %(IWithM.Md2)""/>
+
+               <CreateItem Include=""Bar;Xyz"">
+                       <Output TaskParameter=""Include"" ItemName=""Item0""/>
+               </CreateItem>
+               
+               <Message Text=""IWithM.md: %(IWithM.Md)""/>
+               <Message Text=""IWithM.md2: %(IWithM.Md2)""/>
+       </Target>
+</Project>
+";
+
+                       Engine engine = new Engine (Consts.BinPath);
+                       Project proj = engine.CreateNewProject ();
+                       MonoTests.Microsoft.Build.Tasks.TestMessageLogger logger =
+                               new MonoTests.Microsoft.Build.Tasks.TestMessageLogger ();
+                       proj.LoadXml (project_xml);
+                       engine.RegisterLogger (logger);
+
+                       if (!proj.Build ("1")) {
+                               logger.DumpMessages ();
+                               Assert.Fail ("Build failed");
+                       }
+
+                       logger.CheckLoggedMessageHead ("IWithM.md: Foo", "A1");
+                       logger.CheckLoggedMessageHead ("IWithM.md2: File1", "A2");
+
+                       logger.CheckLoggedMessageHead ("IWithM.md: Foo", "A3");
+                       logger.CheckLoggedMessageHead ("IWithM.md2: File1", "A4");
+                       Assert.AreEqual (0, logger.NormalMessageCount, "unexpected messages found");
+               }
+
+               [Test]
+               // Test Item/prop references in conditions
+               public void TestItems11 () {
+                       string project_xml = @"<Project xmlns=""http://schemas.microsoft.com/developer/msbuild/2003"">
+       <PropertyGroup>
+               <Prop1>@(Item0)</Prop1>
+       </PropertyGroup>
+       <ItemGroup>
+               <Item0 Include=""Foo""/>
+               <Item1 Include=""@(Item0)""/>
+
+               <CondItem Condition=""'@(Item1)' == '@(Item0)'"" Include=""Equal to item0""/>
+               <CondItem Condition=""'@(Item1)' == 'Foo'"" Include=""Equal to item0's value""/>
+
+               <CondItem1 Condition=""'$(Prop1)' == '@(Item0)'"" Include=""Equal to item0""/>
+               <CondItem1 Condition=""'$(Prop1)' == 'Foo'"" Include=""Equal to item0's value""/>
+       </ItemGroup>
+
+       <Target Name=""1"">
+               <Message Text = ""CondItem: %(CondItem.Identity)""/>
+               <Message Text = ""CondItem1: %(CondItem1.Identity)""/>
+       </Target>
+</Project>
+";
+
+                       Engine engine = new Engine (Consts.BinPath);
+                       Project proj = engine.CreateNewProject ();
+                       MonoTests.Microsoft.Build.Tasks.TestMessageLogger logger =
+                               new MonoTests.Microsoft.Build.Tasks.TestMessageLogger ();
+                       proj.LoadXml (project_xml);
+                       engine.RegisterLogger (logger);
+
+                       if (!proj.Build ("1")) {
+                               logger.DumpMessages ();
+                               Assert.Fail ("Build failed");
+                       }
+
+                       logger.CheckLoggedMessageHead ("CondItem: Equal to item0", "A1");
+                       logger.CheckLoggedMessageHead ("CondItem: Equal to item0's value", "A2");
+                       logger.CheckLoggedMessageHead ("CondItem1: Equal to item0", "A3");
+                       logger.CheckLoggedMessageHead ("CondItem1: Equal to item0's value", "A4");
+                       Assert.AreEqual (0, logger.NormalMessageCount, "unexpected messages found");
+               }
+
+               [Test]
+               // test properties and item refs, with dynamic properties/items
+               public void TestItems12 ()
+               {
+                       string project_xml = @"<Project xmlns=""http://schemas.microsoft.com/developer/msbuild/2003"">
+       <PropertyGroup>
+               <Prop2>@(Ref1)</Prop2>
+       </PropertyGroup>
+       <ItemGroup>
+               <Ref1 Include=""File1"" />
+               <Files Include=""@(Ref1)""/>
+       </ItemGroup>
+
+       <Target Name=""1"">
+               <Message Text=""Prop2: $(Prop2)""/>
+               
+               <Message Text=""Files: @(Files)""/>
+               <CreateItem Include=""foobar"">
+                       <Output TaskParameter=""Include"" ItemName=""Ref1""/>
+               </CreateItem>
+               <Message Text=""Files: @(Files)""/>
+
+               <Message Text=""Prop2: $(Prop2)""/>
+               <CreateProperty Value=""NewValue"">
+                       <Output TaskParameter=""Value"" PropertyName=""Prop2""/>
+               </CreateProperty>
+               <Message Text=""Prop2: $(Prop2)""/>
+       </Target>
+</Project>
+";
+
+                       Engine engine = new Engine (Consts.BinPath);
+                       Project proj = engine.CreateNewProject ();
+                       MonoTests.Microsoft.Build.Tasks.TestMessageLogger logger =
+                               new MonoTests.Microsoft.Build.Tasks.TestMessageLogger ();
+                       proj.LoadXml (project_xml);
+                       engine.RegisterLogger (logger);
+
+                       if (!proj.Build ("1")) {
+                               logger.DumpMessages ();
+                               Assert.Fail ("Build failed");
+                       }
+
+                       logger.DumpMessages ();
+                       logger.CheckLoggedMessageHead ("Prop2: File1", "A1");
+                       logger.CheckLoggedMessageHead ("Files: File1", "A1");
+                       logger.CheckLoggedMessageHead ("Files: File1", "A1");
+                       logger.CheckLoggedMessageHead ("Prop2: File1;foobar", "A1");
+                       logger.CheckLoggedMessageHead ("Prop2: NewValue", "A1");
+                       Assert.AreEqual (0, logger.NormalMessageCount, "unexpected messages found");
+               }
+
+               [Test]
+               // test item refs in properties
+               public void TestItems13 () {
+                       string project_xml = @"<Project xmlns=""http://schemas.microsoft.com/developer/msbuild/2003"">
+       <PropertyGroup>
+               <Prop1>@(Item0)</Prop1>
+       </PropertyGroup>
+       <ItemGroup>
+               <Item0 Include=""Foo""/>
+               <Item1 Include=""A\B.txt;A\C.txt;B\B.zip;B\C.zip"" />
+               <Item2 Include=""@(Item1->'%(Extension)/$(Prop1)')"" />
+       </ItemGroup>
+
+       <Target Name='1'>
+               <Message Text=""Item2: @(Item2)""/>
+       </Target>
+</Project>";
+
+                       Engine engine = new Engine (Consts.BinPath);
+                       Project proj = engine.CreateNewProject ();
+                       MonoTests.Microsoft.Build.Tasks.TestMessageLogger logger =
+                               new MonoTests.Microsoft.Build.Tasks.TestMessageLogger ();
+                       proj.LoadXml (project_xml);
+                       engine.RegisterLogger (logger);
+
+                       if (!proj.Build ("1")) {
+                               logger.DumpMessages ();
+                               Assert.Fail ("Build failed");
+                       }
+
+                       logger.CheckLoggedMessageHead ("Item2: .txt/@(Item0);.txt/@(Item0);.zip/@(Item0);.zip/@(Item0)", "A1");
+                       Assert.AreEqual (0, logger.NormalMessageCount, "unexpected messages found");
+               }
+
+               [Test]
+               public void TestSelfRefrentialItems ()
+               {
+                       string project_xml = @"<Project xmlns=""http://schemas.microsoft.com/developer/msbuild/2003"">
+       <PropertyGroup>
+               <Prop1>@(Item1);Val</Prop1>
+               <Prop2>@(Item2)</Prop2>
+               <Prop3>@(Item3)</Prop3>
+       </PropertyGroup>
+       <ItemGroup>
+               <Item1 Include=""Item1OldVal""/>
+               <Item1 Include=""@(Item1);$(Prop1)""/>
+
+               <Item2 Include=""Item2OldVal""/>
+               <Item2 Include=""@(Item2->'%(Identity)');$(Prop2)""/>
+
+               <Item3 Include=""Item3OldVal""/>
+               <Item3 Include=""@(Item3, '_');$(Prop3)""/>
+
+               <Item4 Include=""@(Item4)""/>
+       </ItemGroup>
+
+       <Target Name=""1"">
+               <Message Text=""Item1: %(Item1.Identity)""/>
+               <Message Text=""Item2: %(Item2.Identity)""/>
+               <Message Text=""Item3: %(Item3.Identity)""/>
+               <Message Text=""%(Item4.Identity)""/>
+               <Message Text=""Item4: %(Item4.Identity)""/>
+       </Target>
+</Project>
+";
+
+                       Engine engine = new Engine (Consts.BinPath);
+                       Project proj = engine.CreateNewProject ();
+                       MonoTests.Microsoft.Build.Tasks.TestMessageLogger logger =
+                               new MonoTests.Microsoft.Build.Tasks.TestMessageLogger ();
+                       proj.LoadXml (project_xml);
+                       engine.RegisterLogger (logger);
+
+                       if (!proj.Build ("1")) {
+                               logger.DumpMessages ();
+                               Assert.Fail ("Build failed");
+                       }
+
+                       logger.DumpMessages ();
+                       logger.CheckLoggedMessageHead ("Item1: Item1OldVal", "A1");
+                       logger.CheckLoggedMessageHead ("Item1: Val", "A2");
+                       logger.CheckLoggedMessageHead ("Item2: Item2OldVal", "A3");
+                       logger.CheckLoggedMessageHead ("Item3: Item3OldVal", "A4");
+                       logger.CheckLoggedMessageHead ("Item4: ", "A5");
+                       Assert.AreEqual (0, logger.NormalMessageCount, "unexpected messages found");
+               }
+
+               [Test]
+               public void TestEmptyItemsWithBatching ()
+               {
+                       string project_xml = @"<Project xmlns=""http://schemas.microsoft.com/developer/msbuild/2003"">
+                       <UsingTask TaskName='StringTestTask' AssemblyFile='Test\resources\TestTasks.dll' />
+                       <UsingTask TaskName='TestTask_TaskItem' AssemblyFile='Test\resources\TestTasks.dll' />
+                       <UsingTask TaskName='TestTask_TaskItems' AssemblyFile='Test\resources\TestTasks.dll' />
+       <Target Name=""1"">
+               <StringTestTask Property=""%(Item4.Identity)"">
+                       <Output TaskParameter=""OutputString"" PropertyName=""OutputString""/>
+               </StringTestTask>
+               <Message Text='output1: $(OutputString)'/>
+
+               <StringTestTask Property=""  %(Item4.Identity)"">
+                       <Output TaskParameter=""OutputString"" PropertyName=""OutputString""/>
+               </StringTestTask>
+               <Message Text='output2: $(OutputString)'/>
+
+               <StringTestTask Array=""%(Item4.Identity)"">
+                       <Output TaskParameter=""OutputString"" PropertyName=""OutputString""/>
+               </StringTestTask>
+               <Message Text='output3: $(OutputString)'/>
+
+               <StringTestTask Array=""  %(Item4.Identity)"">
+                       <Output TaskParameter=""OutputString"" PropertyName=""OutputString""/>
+               </StringTestTask>
+               <Message Text='output4: $(OutputString)'/>
+
+
+               <TestTask_TaskItem Property=""%(Item4.Identity)"">
+                       <Output TaskParameter=""Output"" PropertyName=""OutputString""/>
+               </TestTask_TaskItem>
+               <Message Text='output5: $(OutputString)'/>
+
+               <TestTask_TaskItem Property=""  %(Item4.Identity)"">
+                       <Output TaskParameter=""Output"" PropertyName=""OutputString""/>
+               </TestTask_TaskItem>
+               <Message Text='output6: $(OutputString)'/>
+
+
+               <TestTask_TaskItems Property=""  %(Item4.Identity)"">
+                       <Output TaskParameter=""Output"" PropertyName=""OutputString""/>
+               </TestTask_TaskItems>
+               <Message Text='output7: $(OutputString)'/>
+       
+
+               <!-- no space in property -->
+               <TestTask_TaskItems Property=""%(Item4.Identity)"">
+                       <Output TaskParameter=""Output"" PropertyName=""OutputString""/>
+               </TestTask_TaskItems>
+               <Message Text='output8: $(OutputString)'/>
+
+       </Target>
+</Project>
+";
+
+                       Engine engine = new Engine (Consts.BinPath);
+                       Project proj = engine.CreateNewProject ();
+                       MonoTests.Microsoft.Build.Tasks.TestMessageLogger logger =
+                               new MonoTests.Microsoft.Build.Tasks.TestMessageLogger ();
+                       proj.LoadXml (project_xml);
+                       engine.RegisterLogger (logger);
+
+                       if (!proj.Build ("1")) {
+                               logger.DumpMessages ();
+                               Assert.Fail ("Build failed");
+                       }
+
+                       logger.DumpMessages ();
+                       logger.CheckLoggedMessageHead ("output1: property: null ## array: null", "A1");
+                       logger.CheckLoggedMessageHead ("output2: property:    ## array: null", "A2");
+                       logger.CheckLoggedMessageHead ("output3: property: null ## array: null", "A3");
+                       logger.CheckLoggedMessageHead ("output4: property: null ## array: null", "A4");
+
+                       logger.CheckLoggedMessageHead ("output5: null", "A5");
+                       logger.CheckLoggedMessageHead ("output6: null", "A6");
+                       logger.CheckLoggedMessageHead ("output7: null", "A7");
+                       logger.CheckLoggedMessageHead ("output8: null", "A8");
+
+                       Assert.AreEqual (0, logger.NormalMessageCount, "unexpected messages found");
+               }
+
                [Test]
                public void TestItemsInTarget1 ()
                {
@@ -317,6 +647,7 @@ namespace MonoTests.Microsoft.Build.BuildEngine.Various {
                                        <UsingTask TaskName='StringTestTask' AssemblyFile='Test\resources\TestTasks.dll' />
                                        <PropertyGroup>
                                                <A>A</A>
+                                               <B>@(A)g</B>
                                        </PropertyGroup>
                                        <ItemGroup>
                                                <A Include='A;B;C' />
@@ -338,6 +669,9 @@ namespace MonoTests.Microsoft.Build.BuildEngine.Various {
                                                <StringTestTask Property=""@(A,'@(A,'')')"">
                                                        <Output TaskParameter='Property' PropertyName='P5' />
                                                </StringTestTask>
+                                               <StringTestTask Property=""@(A,'$(B)')"">
+                                                       <Output TaskParameter='Property' PropertyName='P6' />
+                                               </StringTestTask>
                                                <StringTestTask Property=""%(A.Filename)"">
                                                        <Output TaskParameter='Property' ItemName='I1' />
                                                </StringTestTask>
@@ -348,14 +682,20 @@ namespace MonoTests.Microsoft.Build.BuildEngine.Various {
                                </Project>
                        ";
 
+                       MonoTests.Microsoft.Build.Tasks.TestMessageLogger logger = new MonoTests.Microsoft.Build.Tasks.TestMessageLogger ();
+                       engine.RegisterLogger (logger);
                        proj.LoadXml (documentString);
-                       Assert.IsTrue (proj.Build ("1"), "A0, Build failed");
+                       if (!proj.Build ("1")) {
+                               logger.DumpMessages ();
+                               Assert.Fail ("build failed");
+                       }
 
                        Assert.AreEqual ("A;B;C", proj.GetEvaluatedProperty ("P1"), "A1");
                        Assert.AreEqual ("ABC", proj.GetEvaluatedProperty ("P2"), "A2");
                        Assert.AreEqual ("A@(A)B@(A)C", proj.GetEvaluatedProperty ("P3"), "A3");
                        Assert.AreEqual ("AABAC", proj.GetEvaluatedProperty ("P4"), "A4");
                        Assert.AreEqual ("@(A,'ABC')", proj.GetEvaluatedProperty ("P5"), "A5");
+                       Assert.AreEqual ("A@(A)gB@(A)gC", proj.GetEvaluatedProperty ("P6"), "A6");
                        CheckItems (proj, "I1", "A6", "A", "B", "C");
                        CheckItems (proj, "I2", "A7", "A A", "B B", "C C");
                }
@@ -474,7 +814,7 @@ namespace MonoTests.Microsoft.Build.BuildEngine.Various {
                        CheckItems (proj, "I5", "A5", "A", "Foo    A", "Bar", "A", "B");
                        CheckItems (proj, "I6", "A6", "A", "B", "C", "A");
                        CheckItems (proj, "I7", "A7", "A", "B", "C", "A", "B", "C");
-                       CheckItems (proj, "I8", "A8", "abc", "A", "B", "C", "A", "foo");
+                       CheckItems(proj, "I8", "A8", "abc", "A", "B", "C", "A", "foo");
                }
 
                [Test]
@@ -525,6 +865,9 @@ namespace MonoTests.Microsoft.Build.BuildEngine.Various {
                {
                        Engine engine = new Engine (Consts.BinPath);
                        Project proj = engine.CreateNewProject ();
+                       MonoTests.Microsoft.Build.Tasks.TestMessageLogger logger =
+                               new MonoTests.Microsoft.Build.Tasks.TestMessageLogger();
+                       engine.RegisterLogger(logger);
 
                        string documentString = @"
                                <Project xmlns=""http://schemas.microsoft.com/developer/msbuild/2003"">
@@ -548,11 +891,14 @@ namespace MonoTests.Microsoft.Build.BuildEngine.Various {
                                                "$(C) $(C)",
                                                "@(A);$(C)",
                                                "@(A);A;B;C",
-                                               "@(A) $(C) @(A)"
+                                               "@(A) $(C) @(A)",
                                        }) + "</Project>";
 
                        proj.LoadXml (documentString);
-                       Assert.IsTrue (proj.Build ("1"), "Build failed");
+                       if (!proj.Build("1")) {
+                               logger.DumpMessages();
+                               Assert.Fail("Build failed");
+                       }
 
                        BuildProperty bp = proj.EvaluatedProperties ["D"];
                        Assert.AreEqual ("$(C);Foo", bp.Value, "B0");