8b9b85e7f5
Former-commit-id: 172c8e3c300b39d5785c7a3e8dfb08ebdbc1a99b
266 lines
8.9 KiB
C#
266 lines
8.9 KiB
C#
//
|
|
// TaskEngine.cs: Class that executes each task.
|
|
//
|
|
// 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
|
|
// "Software"), to deal in the Software without restriction, including
|
|
// without limitation the rights to use, copy, modify, merge, publish,
|
|
// distribute, sublicense, and/or sell copies of the Software, and to
|
|
// permit persons to whom the Software is furnished to do so, subject to
|
|
// the following conditions:
|
|
//
|
|
// The above copyright notice and this permission notice shall be
|
|
// included in all copies or substantial portions of the Software.
|
|
//
|
|
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
|
// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
|
// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
|
// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
|
// LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
|
// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
|
// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
|
|
|
using System;
|
|
using System.Collections.Generic;
|
|
using System.Collections.Specialized;
|
|
using System.Reflection;
|
|
using System.Xml;
|
|
using Microsoft.Build.Framework;
|
|
using Microsoft.Build.Utilities;
|
|
|
|
namespace Microsoft.Build.BuildEngine {
|
|
internal class TaskEngine {
|
|
|
|
Type taskType;
|
|
Project parentProject;
|
|
ITask task;
|
|
|
|
static Type requiredAttribute;
|
|
static Type outputAttribute;
|
|
|
|
static TaskEngine ()
|
|
{
|
|
requiredAttribute = typeof (Microsoft.Build.Framework.RequiredAttribute);
|
|
outputAttribute = typeof (Microsoft.Build.Framework.OutputAttribute);
|
|
}
|
|
|
|
public TaskEngine (Project project, ITask task, Type taskType)
|
|
{
|
|
parentProject = project;
|
|
this.task = task;
|
|
this.taskType = taskType;
|
|
}
|
|
|
|
// 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 (IDictionary <string, string> parameters)
|
|
{
|
|
Dictionary <string, object> values;
|
|
PropertyInfo currentProperty;
|
|
PropertyInfo[] properties;
|
|
object value;
|
|
|
|
values = new Dictionary <string, object> (StringComparer.OrdinalIgnoreCase);
|
|
|
|
foreach (KeyValuePair <string, string> de in parameters) {
|
|
currentProperty = taskType.GetProperty (de.Key, BindingFlags.Public | BindingFlags.Instance
|
|
| BindingFlags.IgnoreCase);
|
|
if (currentProperty == null)
|
|
throw new InvalidProjectFileException (String.Format ("Task does not have property \"{0}\" defined",
|
|
de.Key));
|
|
|
|
try {
|
|
if (TryGetObjectFromString (de.Value, currentProperty.PropertyType, out value))
|
|
values.Add (de.Key, value);
|
|
} catch (Exception e) {
|
|
throw new InvalidProjectFileException (String.Format (
|
|
"Error converting Property named '{0}' with value '{1}' to type {2}: {3}",
|
|
de.Key, de.Value, currentProperty.PropertyType, e.Message), e);
|
|
}
|
|
}
|
|
|
|
properties = taskType.GetProperties ();
|
|
foreach (PropertyInfo pi in properties) {
|
|
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);
|
|
}
|
|
}
|
|
|
|
public bool Execute ()
|
|
{
|
|
return task.Execute ();
|
|
}
|
|
|
|
public void PublishOutput (XmlElement taskElement, Func<PropertyInfo, object> valueProvider)
|
|
{
|
|
XmlElement xmlElement;
|
|
string propertyName;
|
|
string taskParameter;
|
|
string itemName;
|
|
|
|
foreach (XmlNode xmlNode in taskElement.ChildNodes) {
|
|
if (!(xmlNode is XmlElement))
|
|
continue;
|
|
|
|
xmlElement = (XmlElement) xmlNode;
|
|
|
|
if (xmlElement.Name != "Output")
|
|
throw new InvalidProjectFileException ("Only Output elements can be Task's child nodes.");
|
|
if (xmlElement.GetAttribute ("ItemName") != String.Empty && xmlElement.GetAttribute ("PropertyName") != String.Empty)
|
|
throw new InvalidProjectFileException ("Only one of ItemName and PropertyName attributes can be specified.");
|
|
if (xmlElement.GetAttribute ("TaskParameter") == String.Empty)
|
|
throw new InvalidProjectFileException ("TaskParameter attribute must be specified.");
|
|
|
|
if (!ConditionParser.ParseAndEvaluate (xmlElement.GetAttribute ("Condition"), parentProject))
|
|
continue;
|
|
|
|
taskParameter = xmlElement.GetAttribute ("TaskParameter");
|
|
itemName = xmlElement.GetAttribute ("ItemName");
|
|
propertyName = xmlElement.GetAttribute ("PropertyName");
|
|
|
|
var propertyInfo = taskType.GetProperty (taskParameter, BindingFlags.Public | BindingFlags.Instance | BindingFlags.IgnoreCase);
|
|
|
|
if (propertyInfo == null)
|
|
throw new InvalidProjectFileException (String.Format ("The parameter '{0}' was not found for the '{1}' task.", taskParameter, taskElement.Name));
|
|
|
|
if (!propertyInfo.IsDefined (outputAttribute, false))
|
|
throw new InvalidProjectFileException ("This is not output property.");
|
|
|
|
var o = valueProvider (propertyInfo);
|
|
|
|
if (itemName != String.Empty) {
|
|
PublishItemGroup (propertyInfo, o, itemName);
|
|
} else {
|
|
PublishProperty (propertyInfo, o, propertyName);
|
|
}
|
|
}
|
|
}
|
|
|
|
void InitializeParameter (PropertyInfo propertyInfo, object value)
|
|
{
|
|
propertyInfo.SetValue (task, value, null);
|
|
}
|
|
|
|
void PublishProperty (PropertyInfo propertyInfo,
|
|
object o,
|
|
string propertyName)
|
|
{
|
|
if (o == null) {
|
|
parentProject.EvaluatedProperties.RemoveProperty (propertyName);
|
|
return;
|
|
}
|
|
|
|
BuildProperty bp;
|
|
try {
|
|
bp = ChangeType.ToBuildProperty (o, propertyInfo.PropertyType, propertyName);
|
|
} catch (Exception e) {
|
|
throw new Exception (String.Format ("Error publishing Output from task property '{0} {1}' to property named '{2}' : {3}",
|
|
propertyInfo.PropertyType, propertyInfo.Name, propertyName, e.Message),
|
|
e);
|
|
}
|
|
parentProject.EvaluatedProperties.AddProperty (bp);
|
|
}
|
|
|
|
// FIXME: cleanup + test
|
|
void PublishItemGroup (PropertyInfo propertyInfo,
|
|
object o,
|
|
string itemName)
|
|
{
|
|
if (o == null)
|
|
return;
|
|
|
|
BuildItemGroup newItems;
|
|
try {
|
|
newItems = ChangeType.ToBuildItemGroup (o, propertyInfo.PropertyType, itemName);
|
|
} catch (Exception e) {
|
|
throw new Exception (String.Format ("Error publishing Output from task property '{0} {1}' to item named '{2}' : {3}",
|
|
propertyInfo.PropertyType, propertyInfo.Name, itemName, e.Message),
|
|
e);
|
|
}
|
|
|
|
newItems.ParentProject = parentProject;
|
|
|
|
if (parentProject.EvaluatedItemsByName.ContainsKey (itemName)) {
|
|
BuildItemGroup big = parentProject.EvaluatedItemsByName [itemName];
|
|
foreach (BuildItem item in newItems)
|
|
big.AddItem (item);
|
|
} else {
|
|
parentProject.EvaluatedItemsByName.Add (itemName, newItems);
|
|
}
|
|
foreach (BuildItem bi in newItems)
|
|
parentProject.EvaluatedItems.AddItem (bi);
|
|
}
|
|
|
|
// returns true, if the @result should be included in the values list
|
|
bool TryGetObjectFromString (string raw, Type type, out object result)
|
|
{
|
|
Expression e;
|
|
result = null;
|
|
|
|
e = new Expression ();
|
|
e.Parse (raw, ParseOptions.AllowItemsMetadataAndSplit);
|
|
|
|
// See rules in comment for 'Prepare'
|
|
string str = (string) e.ConvertTo (parentProject, typeof (string));
|
|
if (!type.IsArray && str == String.Empty)
|
|
return false;
|
|
|
|
if (str.Trim ().Length == 0 && type.IsArray &&
|
|
(type.GetElementType () == typeof (string) || type.GetElementType () == typeof (ITaskItem)))
|
|
return true;
|
|
|
|
result = e.ConvertTo (parentProject, type, ExpressionOptions.ExpandItemRefs);
|
|
|
|
return true;
|
|
}
|
|
|
|
public object ValueFromExecution (PropertyInfo propertyInfo)
|
|
{
|
|
return propertyInfo.GetValue (task, null);
|
|
}
|
|
}
|
|
}
|