public void TaskOutputsObjectArrayParameter() { XmlElement taskNode; TaskEngine taskEngine; MockTask mockTask; ItemBucket itemBucket; EngineProxy engineProxy; taskNode = CreateXmlTaskNode(); // Create a task output specification to satisfy GatherGeneratedTaskOutputs. XmlElement outputElement = taskNode.OwnerDocument.CreateElement("Output"); outputElement.SetAttribute("TaskParameter", "ObjectArrayOutputParameter"); outputElement.SetAttribute("ItemName", "MyItemList"); taskNode.AppendChild(outputElement); TaskOutput myTaskOutputSpecification = new TaskOutput(outputElement); InstantiateMockTaskHelper(taskNode, out taskEngine, out mockTask, out itemBucket, out engineProxy); taskEngine.GatherGeneratedTaskOutputs(LookupHelpers.CreateLookup(new Hashtable()), myTaskOutputSpecification, "ObjectArrayOutputParameter", "MyItemList", null, mockTask); }
public void TaskOutputsMyTaskItemArrayParameter() { XmlElement taskNode; TaskEngine taskEngine; MockTask mockTask; ItemBucket itemBucket; EngineProxy engineProxy; taskNode = CreateXmlTaskNode(); // Create a task output specification to satisfy GatherGeneratedTaskOutputs. XmlElement outputElement = taskNode.OwnerDocument.CreateElement("Output"); outputElement.SetAttribute("TaskParameter", "MyTaskItemArrayOutputParameter"); outputElement.SetAttribute("ItemName", "MyItemList"); taskNode.AppendChild(outputElement); TaskOutput myTaskOutputSpecification = new TaskOutput(outputElement); InstantiateMockTaskHelper(taskNode, out taskEngine, out mockTask, out itemBucket, out engineProxy); taskEngine.GatherGeneratedTaskOutputs(GetEnteredScopeLookup(), myTaskOutputSpecification, "MyTaskItemArrayOutputParameter", "MyItemList", null, mockTask); // Did not throw InvalidProjectFileException }
private void GatherArrayStringAndValueOutputs(Lookup lookup, TaskOutput taskOutputSpecification, string itemName, string propertyName, PropertyInfo parameter, object outputs) { // if the task has generated outputs (if it didn't, don't do anything) if (outputs != null) { Array convertibleOutputs = (parameter.PropertyType.IsArray) ? (Array)outputs : new object[] { outputs }; if (taskOutputSpecification.IsItemVector) { ErrorUtilities.VerifyThrow((itemName != null) && (itemName.Length > 0), "Need item type."); // to store the outputs as items, use the string representations of the outputs as item-specs foreach (object output in convertibleOutputs) { // if individual outputs in the array are null, ignore them if (output != null) { string stringValueFromTask = (string)Convert.ChangeType(output, typeof(string), CultureInfo.InvariantCulture); // attempting to put an empty string into an item is a no-op (bug #444501). if (stringValueFromTask.Length > 0) { lookup.AddNewItem(new BuildItem(itemName, EscapingUtilities.Escape(stringValueFromTask))); } } } } else { Debug.Assert(taskOutputSpecification.IsProperty); ErrorUtilities.VerifyThrow((propertyName != null) && (propertyName.Length > 0), "Need property name."); // to store an object array in a property, join all the string representations of the objects with // semi-colons to make the property value StringBuilder joinedOutputs = new StringBuilder(); foreach (object output in convertibleOutputs) { // if individual outputs in the array are null, ignore them if (output != null) { if (joinedOutputs.Length > 0) { joinedOutputs.Append(';'); } string stringValueFromTask = (string)Convert.ChangeType(output, typeof(string), CultureInfo.InvariantCulture); joinedOutputs.Append(EscapingUtilities.Escape(stringValueFromTask)); } } lookup.SetProperty(new BuildProperty(propertyName, joinedOutputs.ToString(), PropertyType.OutputProperty)); } } }
/// <summary> /// Uses the given task output specification to grab the task's outputs using .NET reflection. /// </summary> /// <remarks> /// This method is "internal" for unit-testing purposes only. /// </remarks> /// <param name="taskOutputSpecification"></param> /// <param name="taskParameterName"></param> /// <param name="itemName">can be null</param> /// <param name="propertyName">can be null</param> /// <param name="task"></param> /// <returns>true, if successful</returns> internal bool GatherGeneratedTaskOutputs ( Lookup lookup, TaskOutput taskOutputSpecification, string taskParameterName, string itemName, string propertyName, ITask task) { ErrorUtilities.VerifyThrow(task != null, "Need instantiated task to retrieve outputs from."); bool gatheredGeneratedOutputsSuccessfully = true; try { PropertyInfo parameter = TaskClass.GetProperty(taskParameterName); // flag an error if we find a parameter that has no .NET property equivalent ProjectErrorUtilities.VerifyThrowInvalidProject(parameter != null, taskOutputSpecification.TaskParameterAttribute, "UnexpectedTaskOutputAttribute", taskParameterName, TaskName); // output parameters must have their corresponding .NET properties marked with the Output attribute ProjectErrorUtilities.VerifyThrowInvalidProject(TaskClass.GetNamesOfPropertiesWithOutputAttribute().ContainsKey(taskParameterName), taskOutputSpecification.TaskParameterAttribute, "UnmarkedOutputTaskParameter", parameter.Name, TaskName); // grab the outputs from the task's designated output parameter (which is a .NET property) object outputs = parameter.GetValue(task, null); Type type = parameter.PropertyType; // don't use the C# "is" operator as it always returns false if the object is null if ( typeof(ITaskItem[]).IsAssignableFrom(type) || /* ITaskItem array or derived type, or */ typeof(ITaskItem).IsAssignableFrom(type) /* ITaskItem or derived type */ ) { GatherTaskItemOutputs(lookup, taskOutputSpecification, itemName, propertyName, outputs); } // don't use the C# "is" operator as it always returns false if the object is null else if ( (type.IsArray && type.GetElementType().IsValueType) || /* array of value types, or */ (type == typeof(string[])) || /* string array, or */ (type.IsValueType) || /* value type, or */ (type == typeof(string)) /* string */ ) { GatherArrayStringAndValueOutputs(lookup, taskOutputSpecification, itemName, propertyName, parameter, outputs); } else { ProjectErrorUtilities.VerifyThrowInvalidProject(false, taskOutputSpecification.TaskParameterAttribute, "UnsupportedTaskParameterTypeError", parameter.PropertyType, parameter.Name, TaskName); } } // handle invalid TaskItems in task outputs catch (InvalidOperationException e) { loggingServices.LogError(buildEventContext, Utilities.CreateBuildEventFileInfo(taskOutputSpecification.TaskParameterAttribute, projectFileOfTaskNode), "InvalidTaskItemsInTaskOutputs", TaskName, taskParameterName, e.Message); gatheredGeneratedOutputsSuccessfully = false; } // handle any exception thrown by the task's getter catch (TargetInvocationException e) { // Exception thrown by the called code itself // Log the stack, so the task vendor can fix their code // Log the task line number, whatever the value of ContinueOnError; // because this will be a hard error anyway. loggingServices.LogFatalTaskError(buildEventContext, e.InnerException, CreateBuildEventFileInfoForTask(), TaskName); // We do not recover from a task exception while getting outputs, // so do not merely set gatheredGeneratedOutputsSuccessfully = false; here ProjectErrorUtilities.VerifyThrowInvalidProject(false, taskOutputSpecification.TaskParameterAttribute, "FailedToRetrieveTaskOutputs", TaskName, taskParameterName, e.InnerException.Message); } catch (Exception e) // Catching Exception, but rethrowing unless it's a well-known exception. { if (ExceptionHandling.NotExpectedReflectionException(e)) throw; ProjectErrorUtilities.VerifyThrowInvalidProject(false, taskOutputSpecification.TaskParameterAttribute, "FailedToRetrieveTaskOutputs", TaskName, taskParameterName, e.Message); } return gatheredGeneratedOutputsSuccessfully; }
/// <summary> /// Parses the task element for its output specifications, which are declared using <Output> tags. /// </summary> private List<TaskOutput> GetTaskOutputSpecifications(bool showWarnings) { List<TaskOutput> taskOutputSpecifications = new List<TaskOutput>(); foreach (XmlNode childNode in taskNode.ChildNodes) { ProjectErrorUtilities.VerifyThrowInvalidProject(XMakeElements.IsValidTaskChildNode(childNode), childNode, "UnrecognizedChildElement", childNode.Name, TaskName); if (childNode.Name == XMakeElements.output) { TaskOutput taskOutputSpecification = new TaskOutput((XmlElement)childNode); // The "ItemName" attribute of the <Output> tag is usually just a straight // string representing the item name. If it contains any "@" signs, the // project author most likely made a mistake, and so we throw a warning here. XmlAttribute itemNameAttribute = taskOutputSpecification.ItemNameAttribute; if (showWarnings && taskOutputSpecification.IsItemVector && (-1 != itemNameAttribute.Value.IndexOf('@'))) { loggingServices.LogWarning(buildEventContext, Utilities.CreateBuildEventFileInfo(itemNameAttribute, projectFileOfTaskNode), "AtSignInTaskOutputItemName", itemNameAttribute.Value); } // The "PropertyName" attribute of the <Output> tag is usually just a straight // string representing the property name. If it contains any "$" signs, the // project author most likely made a mistake, and so we throw a warning here. XmlAttribute propertyNameAttribute = taskOutputSpecification.PropertyNameAttribute; if (showWarnings && taskOutputSpecification.IsProperty && (-1 != propertyNameAttribute.Value.IndexOf('$'))) { loggingServices.LogWarning(buildEventContext, Utilities.CreateBuildEventFileInfo(propertyNameAttribute, projectFileOfTaskNode), "DollarSignInTaskOutputPropertyName", propertyNameAttribute.Value); } taskOutputSpecifications.Add(taskOutputSpecification); } } return taskOutputSpecifications; }
/// <summary> /// Uses the given task output specification to (statically) infer the task's outputs. /// </summary> /// <param name="taskOutputSpecification"></param> /// <param name="taskParameterName"></param> /// <param name="itemName">can be null</param> /// <param name="propertyName">can be null</param> /// <param name="bucket"></param> private void InferTaskOutputs ( Lookup lookup, TaskOutput taskOutputSpecification, string taskParameterName, string itemName, string propertyName, ItemBucket bucket ) { // if the task has a value set for the output parameter, expand all embedded properties and item metadata in it XmlAttribute taskParameterAttribute = null; // Lookup attribute name needs to be case-insensitive // DevDiv bugs: 33981 foreach (XmlAttribute taskNodeAttribute in taskNode.Attributes) { if (String.Compare(taskNodeAttribute.Name, taskParameterName, StringComparison.OrdinalIgnoreCase) == 0) { taskParameterAttribute = taskNodeAttribute; break; } } if (taskParameterAttribute != null) { if (taskOutputSpecification.IsItemVector) { // This is an output item. ErrorUtilities.VerifyThrow((itemName != null) && (itemName.Length > 0), "Need item type."); // Expand only with properties first, so that expressions like Include="@(foo)" will transfer the metadata of the "foo" items as well, not just their item specs. Expander propertyAndMetadataExpander = new Expander(bucket.Expander, ExpanderOptions.ExpandPropertiesAndMetadata); List<string> outputItemSpecs = propertyAndMetadataExpander.ExpandAllIntoStringListLeaveEscaped(taskParameterAttribute); foreach (string outputItemSpec in outputItemSpecs) { BuildItemGroup items = bucket.Expander.ExpandSingleItemListExpressionIntoItemsLeaveEscaped(outputItemSpec, taskParameterAttribute); // if the output item-spec is an item vector, get the items in it if (items != null) { foreach (BuildItem item in items) { // we want to preserve the attributes on the item BuildItem clonedItem = item.VirtualClone(); // but we do need to change the item type clonedItem.Name = itemName; lookup.AddNewItem(clonedItem); } } else { // if the output item-spec is not an item vector, accept it as-is lookup.AddNewItem(new BuildItem(itemName, outputItemSpec)); } } } else { // This is an output property. Debug.Assert(taskOutputSpecification.IsProperty); ErrorUtilities.VerifyThrow((propertyName != null) && (propertyName.Length > 0), "Need property name."); string taskParameterValue = bucket.Expander.ExpandAllIntoString(taskParameterAttribute); if (taskParameterValue.Length > 0) { lookup.SetProperty(new BuildProperty(propertyName, taskParameterValue, PropertyType.OutputProperty)); } } } }
private void GatherTaskItemOutputs(Lookup lookup, TaskOutput taskOutputSpecification, string itemName, string propertyName, object outputs) { // if the task has generated outputs (if it didn't, don't do anything) if (outputs != null) { ITaskItem[] taskItemOutputs = (outputs is ITaskItem[]) ? (ITaskItem[])outputs : new ITaskItem[] { (ITaskItem)outputs }; if (taskOutputSpecification.IsItemVector) { ErrorUtilities.VerifyThrow((itemName != null) && (itemName.Length > 0), "Need item type."); foreach (ITaskItem output in taskItemOutputs) { // if individual items in the array are null, ignore them if (output != null) { lookup.AddNewItem(new BuildItem(itemName, output)); } } } else { Debug.Assert(taskOutputSpecification.IsProperty); ErrorUtilities.VerifyThrow((propertyName != null) && (propertyName.Length > 0), "Need property name."); // to store an ITaskItem array in a property, join all the item-specs with semi-colons to make the // property value, and ignore/discard the attributes on the ITaskItems StringBuilder joinedOutputs = new StringBuilder(); foreach (ITaskItem output in taskItemOutputs) { // if individual items in the array are null, ignore them if (output != null) { if (joinedOutputs.Length > 0) { joinedOutputs.Append(';'); } joinedOutputs.Append(EscapingUtilities.Escape(output.ItemSpec)); } } lookup.SetProperty(new BuildProperty(propertyName, joinedOutputs.ToString(), PropertyType.OutputProperty)); } } }