/// <summary> /// Parses the target's "Inputs" and "Outputs" attributes and gathers up referenced items. /// </summary> /// <param name="bucket"></param> /// <param name="itemVectorsInTargetInputs"></param> /// <param name="itemVectorTransformsInTargetInputs"></param> /// <param name="discreteItemsInTargetInputs"></param> /// <param name="itemVectorsInTargetOutputs"></param> /// <param name="discreteItemsInTargetOutputs"></param> /// <param name="targetOutputItemSpecs"></param> /// <owner>SumedhK</owner> private void ParseTargetInputOutputSpecifications ( ItemBucket bucket, out Hashtable itemVectorsInTargetInputs, out Hashtable itemVectorTransformsInTargetInputs, out Hashtable discreteItemsInTargetInputs, out Hashtable itemVectorsInTargetOutputs, out Hashtable discreteItemsInTargetOutputs, out ArrayList targetOutputItemSpecs ) { // break down the input/output specifications along the standard separator, after expanding all embedded properties // and item metadata Expander propertyAndMetadataExpander = new Expander(bucket.Expander, ExpanderOptions.ExpandPropertiesAndMetadata); List<string> targetInputs = propertyAndMetadataExpander.ExpandAllIntoStringListLeaveEscaped(TargetInputSpecification, this.targetInputsAttribute); List<string> targetOutputs = propertyAndMetadataExpander.ExpandAllIntoStringListLeaveEscaped(TargetOutputSpecification, this.targetOutputsAttribute); itemVectorTransformsInTargetInputs = new Hashtable(StringComparer.OrdinalIgnoreCase); // figure out which of the inputs are: // 1) item vectors // 2) item vectors with transforms // 3) "discrete" items i.e. items that do not reference item vectors SeparateItemVectorsFromDiscreteItems(this.targetInputsAttribute, targetInputs, bucket, out itemVectorsInTargetInputs, itemVectorTransformsInTargetInputs, out discreteItemsInTargetInputs); // figure out which of the outputs are: // 1) item vectors (with or without transforms) // 2) "discrete" items i.e. items that do not reference item vectors SeparateItemVectorsFromDiscreteItems(this.targetOutputsAttribute, targetOutputs, bucket, out itemVectorsInTargetOutputs, null /* don't want transforms separated */, out discreteItemsInTargetOutputs); // list out all the output item-specs targetOutputItemSpecs = GetItemSpecsFromItemVectors(itemVectorsInTargetOutputs); targetOutputItemSpecs.AddRange(discreteItemsInTargetOutputs.Values); }
/// <summary> /// Determines if the new item spec that the user is trying to add to the project /// already matches an existing wildcarded item declared in the project. We only /// consider it a "match" in very specific circumstances... if there's anything /// weird or not-mainline about the new item spec or the existing item, we fail /// the match in order to "play it safe". /// </summary> internal bool NewItemSpecMatchesExistingWildcard(string newItemSpec) { Project parentProject = GetParentProject(); ErrorUtilities.VerifyThrow(parentProject != null, "This method should only get called on persisted items."); BuildPropertyGroup evaluatedProperties = parentProject.evaluatedProperties; if ( // The original item spec should have had at least one "*" or "?" in it. FileMatcher.HasWildcards(this.Include) && // The original item should not have a Condition. (this.Condition.Length == 0) && // The original item should not have an Exclude. (this.Exclude.Length == 0) && // The new item spec should NOT have any wildcards. !FileMatcher.HasWildcards(newItemSpec) ) { Expander propertyExpander = new Expander(evaluatedProperties); string newItemSpecExpandedEscaped = propertyExpander.ExpandAllIntoStringLeaveEscaped(newItemSpec, null); // New item spec should not have any unescaped semicolons ... this can really mess us up. if (-1 == newItemSpecExpandedEscaped.IndexOf(';')) { // Expand any properties in the new item spec that the user gave us. string newItemSpecExpandedUnescaped = EscapingUtilities.UnescapeAll(newItemSpecExpandedEscaped); // Loop over each piece separated by semicolons. List<string> itemIncludePieces = propertyExpander.ExpandAllIntoStringListLeaveEscaped(this.Include, this.IncludeAttribute); foreach (string itemIncludePiece in itemIncludePieces) { bool containsEscapedWildcards = EscapingUtilities.ContainsEscapedWildcards(itemIncludePiece); bool containsRealWildcards = FileMatcher.HasWildcards(itemIncludePiece); // If this is the piece that has the wildcards ... if (containsRealWildcards && !containsEscapedWildcards) { string itemIncludePieceContainingWildcardUnescaped = EscapingUtilities.UnescapeAll(itemIncludePiece); FileMatcher.Result match = FileMatcher.FileMatch(itemIncludePieceContainingWildcardUnescaped, newItemSpecExpandedUnescaped); if (match.isLegalFileSpec && match.isMatch) { // The wildcard in the original item spec will match the new item that // user is trying to add. return true; } } } } } return false; }
/// <summary> /// Sets the project's default targets from the given list of semi-colon-separated target names after expanding all /// embedded properties in the list. /// </summary> /// <owner>SumedhK</owner> /// <param name="defaultTargetsList"></param> /// <param name="propertyBag"></param> private void SetDefaultTargets(string defaultTargetsList, BuildPropertyGroup propertyBag) { Expander propertyExpander = new Expander(propertyBag); this.defaultTargetNames = propertyExpander.ExpandAllIntoStringListLeaveEscaped(defaultTargetsList, null).ToArray(); BuildProperty defaultTargetsProperty = new BuildProperty(ReservedPropertyNames.projectDefaultTargets, propertyExpander.ExpandAllIntoStringLeaveEscaped(defaultTargetsList, null), PropertyType.ReservedProperty); this.ReservedProperties.SetProperty(defaultTargetsProperty); // we also need to push this property directly into the evaluatedProperties bucket // since this property was computed "late", i.e. after the initial evaluation. this.evaluatedProperties.SetProperty(defaultTargetsProperty); }
public void ExpandAllIntoStringListLeaveEscapedComplex() { ReadOnlyLookup lookup; Dictionary<string, string> itemMetadata; CreateComplexPropertiesItemsMetadata(out lookup, out itemMetadata); Expander expander = new Expander(lookup, itemMetadata); XmlAttribute xmlattribute = (new XmlDocument()).CreateAttribute("dummy"); xmlattribute.Value = "@(Resource->'%(Filename)') ; @(Content) ; @(NonExistent) ; $(NonExistent) ; %(NonExistent) ; " + "$(OutputPath) ; $(TargetPath) ; %(Language)_%(Culture)"; List<string> expanded = expander.ExpandAllIntoStringListLeaveEscaped(xmlattribute.Value, xmlattribute); Assertion.AssertEquals(9, expanded.Count); Assertion.AssertEquals(@"string$(p)", expanded[0]); Assertion.AssertEquals(@"dialogs%253b", expanded[1]); Assertion.AssertEquals(@"splash.bmp", expanded[2]); Assertion.AssertEquals(@"\jk", expanded[3]); Assertion.AssertEquals(@"l\mno%253bpqr\stu", expanded[4]); Assertion.AssertEquals(@"subdir1\", expanded[5]); Assertion.AssertEquals(@"subdir2\", expanded[6]); Assertion.AssertEquals(@"english_abc%253bdef", expanded[7]); Assertion.AssertEquals(@"ghi", expanded[8]); expanded = expander.ExpandAllIntoStringListLeaveEscaped(xmlattribute); Assertion.AssertEquals(9, expanded.Count); Assertion.AssertEquals(@"string$(p)", expanded[0]); Assertion.AssertEquals(@"dialogs%253b", expanded[1]); Assertion.AssertEquals(@"splash.bmp", expanded[2]); Assertion.AssertEquals(@"\jk", expanded[3]); Assertion.AssertEquals(@"l\mno%253bpqr\stu", expanded[4]); Assertion.AssertEquals(@"subdir1\", expanded[5]); Assertion.AssertEquals(@"subdir2\", expanded[6]); Assertion.AssertEquals(@"english_abc%253bdef", expanded[7]); Assertion.AssertEquals(@"ghi", expanded[8]); }
/// <summary> /// Returns a list of all items in the provided item group whose itemspecs match the specification, after it is split and any wildcards are expanded. /// If not items match, returns null. /// </summary> internal static List<BuildItem> FindItemsMatchingSpecification(BuildItemGroup items, string specification, XmlAttribute attribute, Expander expander, string baseDirectory) { if (items.Count == 0 || specification.Length == 0) { return null; } // This is a hashtable whose key is the filename for the individual items // in the Exclude list, after wildcard expansion. The value in the hash table // is just an empty string. Hashtable specificationsToFind = new Hashtable(StringComparer.OrdinalIgnoreCase); // Split by semicolons List<string> specificationPieces = expander.ExpandAllIntoStringListLeaveEscaped(specification, attribute); foreach (string piece in specificationPieces) { // Take each individual path or file expression, and expand any // wildcards. Then loop through each file returned, and add it // to our hashtable. // Don't unescape wildcards just yet - if there were any escaped, the caller wants to treat them // as literals. Everything else is safe to unescape at this point, since we're only matching // against the file system. string[] fileList = EngineFileUtilities.GetFileListEscaped(baseDirectory, piece); foreach (string file in fileList) { // Now unescape everything, because this is the end of the road for this filename. // We're just going to compare it to the unescaped include path to filter out the // file excludes. specificationsToFind[EscapingUtilities.UnescapeAll(file)] = String.Empty; } } if (specificationsToFind.Count == 0) { return null; } // Now loop through our list and filter out any that match a // filename in the remove list. List<BuildItem> itemsRemoved = new List<BuildItem>(); foreach (BuildItem item in items) { // Even if the case for the excluded files is different, they // will still get excluded, as expected. However, if the excluded path // references the same file in a different way, such as by relative // path instead of absolute path, we will not realize that they refer // to the same file, and thus we will not exclude it. if (specificationsToFind.ContainsKey(item.FinalItemSpec)) { itemsRemoved.Add(item); } } return itemsRemoved; }
/// <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)); } } } }