return WindowsCompatibilityExtensions.NormalizeFilePath (new ExpressionEvaluator (this).Evaluate (unexpandedValue));
}
+ internal IEnumerable<object> EvaluateAsStringOrItems (string unexpandedValue)
+ {
+ return new ExpressionEvaluator (this).EvaluateAsStringOrItems (unexpandedValue);
+ }
+
public ICollection<ProjectItemInstance> GetItems (string itemType)
{
return new CollectionFromEnumerable<ProjectItemInstance> (Items.Where (p => p.ItemType.Equals (itemType, StringComparison.OrdinalIgnoreCase)));
task.BuildEngine = this;
// Prepare task parameters.
- var evaluatedTaskParams = taskInstance.Parameters.Select (p => new KeyValuePair<string,string> (p.Key, project.ExpandString (p.Value)));
+ var evaluatedTaskParams = taskInstance.Parameters.Select (p => new KeyValuePair<string,object[]> (p.Key, project.EvaluateAsStringOrItems (p.Value).ToArray ()));
var requiredProps = task.GetType ().GetProperties ()
.Where (p => p.CanWrite && p.GetCustomAttributes (typeof (RequiredAttribute), true).Any ());
continue;
}
var prop = task.GetType ().GetProperty (p.Key);
+ object valueInstance = null;
if (prop == null)
throw new InvalidOperationException (string.Format ("Task {0} does not have property {1}", taskInstance.Name, p.Key));
if (!prop.CanWrite)
throw new InvalidOperationException (string.Format ("Task {0} has property {1} but it is read-only.", taskInstance.Name, p.Key));
- if (string.IsNullOrEmpty (p.Value) && !requiredProps.Contains (prop))
- continue;
try {
- var valueInstance = ConvertTo (p.Value, prop.PropertyType);
- prop.SetValue (task, valueInstance, null);
+ if (p.Value.All (o => o is ITaskItem)) {
+ valueInstance = ConvertTo (p.Value, prop.PropertyType);
+ prop.SetValue (task, valueInstance, null);
+ } else if (p.Value.Any ()) {
+ string valueString = string.Join (";", p.Value.Where (o => o != null).Select (o => ConvertTo (o, typeof (string))));
+ if (string.IsNullOrEmpty (valueString) && !requiredProps.Contains (prop))
+ continue;
+ valueInstance = ConvertTo (valueString, prop.PropertyType);
+ prop.SetValue (task, valueInstance, null);
+ }
} catch (Exception ex) {
throw new InvalidOperationException (string.Format ("Failed to convert '{0}' for property '{1}' of type {2}", p.Value, prop.Name, prop.PropertyType), ex);
}
return true;
}
- object ConvertTo (string source, Type targetType)
+ object ConvertTo (object sourceObject, Type targetType)
{
- if (targetType == typeof(ITaskItem) || targetType.IsSubclassOf (typeof(ITaskItem)))
+ if (sourceObject == null)
+ return null;
+ if (sourceObject.GetType () == targetType)
+ return sourceObject;
+ var arr = sourceObject is IEnumerable<object> ? ((IEnumerable<object>) sourceObject).ToArray ()
+ : sourceObject is ICollection
+ ? (object []) new ArrayList ((ICollection) sourceObject).ToArray (typeof(object)) : null;
+ if (targetType.IsArray && sourceObject.GetType ().IsArray)
+ return new ArrayList (arr.Select (o => ConvertTo (o, targetType.GetElementType ())).Where (o => o != null).ToArray ())
+ .ToArray (targetType.GetElementType ());
+ var source = sourceObject is string ? (string) sourceObject : arr == null || !arr.Any () ? string.Empty : string.Join (";", arr.Select (o => ConvertTo (o, typeof (string))).Where (s => s != null));
+ if (targetType == typeof (ITaskItem) || targetType.IsSubclassOf (typeof (ITaskItem)))
return new TargetOutputTaskItem () { ItemSpec = WindowsCompatibilityExtensions.FindMatchingPath (source.Trim ()) };
if (targetType.IsArray)
return new ArrayList (source.Split (';').Select (s => s.Trim ()).Where (s => !string.IsNullOrEmpty (s)).Select (s => ConvertTo (s, targetType.GetElementType ())).ToArray ())
.ToArray (targetType.GetElementType ());
- if (targetType == typeof(bool)) {
+ if (targetType == typeof (bool)) {
switch (source != null ? source.ToLower (CultureInfo.InvariantCulture) : string.Empty) {
case "true":
case "yes":
throw new InvalidProjectFileException (string.Format ("failed to evaluate expression as boolean: '{0}': {1}", source, ex.Message), ex);
}
}
+
+ public IEnumerable<object> EvaluateAsStringOrItems (string unexpandedValue)
+ {
+ var exprList = new ExpressionParserManual (unexpandedValue ?? string.Empty, ExpressionValidationType.LaxString).Parse ();
+ if (exprList == null)
+ throw new ArgumentNullException ("exprList");
+ return exprList.Select (e => e.EvaluateAsStringOrItems (CreateContext (unexpandedValue)));
+ }
}
class EvaluationContext
Stack<object> evaluating_items = new Stack<object> ();
Stack<object> evaluating_props = new Stack<object> ();
+
+ List<ITaskItem> evaluated_task_items = new List<ITaskItem> ();
+
+ public IList<ITaskItem> EvaluatedTaskItems {
+ get { return evaluated_task_items; }
+ }
public IEnumerable<object> GetItems (string name)
{
var eval = item as ProjectItem;
if (eval != null)
return Evaluator.Evaluate (eval.EvaluatedInclude);
- else
- return Evaluator.Evaluate (((ProjectItemInstance) item).EvaluatedInclude);
+ else {
+ var inst = (ProjectItemInstance) item;
+ if (!evaluated_task_items.Contains (inst))
+ evaluated_task_items.Add (inst);
+ return Evaluator.Evaluate (inst.EvaluatedInclude);
+ }
} finally {
evaluating_items.Pop ();
}
}
throw new InvalidProjectFileException (this.Location, string.Format ("Condition '{0}' is evaluated as '{1}' and cannot be converted to boolean", context.Source, ret));
}
+
+ public virtual object EvaluateAsStringOrItems (EvaluationContext context)
+ {
+ return EvaluateAsString (context);
+ }
}
partial class BinaryExpression : Expression
public override string EvaluateAsString (EvaluationContext context)
{
+ // FIXME: enable this and fix regressions.
+ /*
+ var ret = EvaluateAsStringOrItems (context);
+ if (ret == null)
+ return null;
+ if (ret is string)
+ return (string) ret;
+ string itemType = Application.Name.Name;
+ return string.Join (";", ((IEnumerable<object>) ret).Select (item => context.EvaluateItem (itemType, item)));
+ */
string itemType = Application.Name.Name;
var items = context.GetItems (itemType);
if (!items.Any ())
var ret = string.Concat (Application.Expressions.Select (e => e.EvaluateAsString (context)));
context.ContextItem = null;
return ret;
- }));
+ }));
}
public override object EvaluateAsObject (EvaluationContext context)
{
return EvaluateAsString (context);
}
+
+ // FIXME: enable this and fix regressions.
+ /*
+ public override object EvaluateAsStringOrItems (EvaluationContext context)
+ {
+ string itemType = Application.Name.Name;
+ var items = context.GetItems (itemType);
+ if (!items.Any ())
+ return null;
+ if (Application.Expressions == null)
+ return items;
+ else
+ return string.Join (";", items.Select (item => {
+ context.ContextItem = item;
+ var ret = string.Concat (Application.Expressions.Select (e => e.EvaluateAsString (context)));
+ context.ContextItem = null;
+ return ret;
+ }));
+ }
+ */
}
partial class MetadataAccessExpression : Expression
[Test]
public void ExpandStringWithMetadata ()
{
- string thisAssembly = new Uri (GetType ().Assembly.CodeBase).LocalPath;
- string project_xml = string.Format (@"<Project xmlns='http://schemas.microsoft.com/developer/msbuild/2003'>
+ string project_xml = @"<Project xmlns='http://schemas.microsoft.com/developer/msbuild/2003'>
<ItemGroup>
<Foo Include='xxx'><M>x</M></Foo>
<Foo Include='yyy'><M>y</M></Foo>
</ItemGroup>
-</Project>", thisAssembly);
+</Project>";
var xml = XmlReader.Create (new StringReader (project_xml));
var root = ProjectRootElement.Create (xml);
root.FullPath = "ProjectInstanceTest.ExpandStringWithMetadata.proj";