/// <summary> /// Returns true if there is a metadata expression (outside of a transform) in the expression. /// </summary> internal static bool ContainsMetadataExpressionOutsideTransform(string expression) { ItemsAndMetadataPair pair = new ItemsAndMetadataPair(null, null); GetReferencedItemNamesAndMetadata(expression, 0, expression.Length, ref pair, ShredderOptions.MetadataOutsideTransforms); bool result = (pair.Metadata != null && pair.Metadata.Count > 0); return(result); }
/// <summary> /// Given a list of expressions that may contain item list expressions, /// returns a pair of tables of all item names found, as K=Name, V=String.Empty; /// and all metadata not in transforms, as K=Metadata key, V=MetadataReference, /// where metadata key is like "itemname.metadataname" or "metadataname". /// PERF: Tables are null if there are no entries, because this is quite a common case. /// </summary> internal static ItemsAndMetadataPair GetReferencedItemNamesAndMetadata(List <string> expressions) { ItemsAndMetadataPair pair = new ItemsAndMetadataPair(null, null); foreach (string expression in expressions) { GetReferencedItemNamesAndMetadata(expression, 0, expression.Length, ref pair, ShredderOptions.All); } return(pair); }
protected bool NeedToExpandMetadataForEachItem(ImmutableList <ProjectMetadataElement> metadata, out ItemsAndMetadataPair itemsAndMetadataFound) { List <string> values = new List <string>(metadata.Count * 2); foreach (var metadataElement in metadata) { values.Add(metadataElement.Value); values.Add(metadataElement.Condition); } itemsAndMetadataFound = ExpressionShredder.GetReferencedItemNamesAndMetadata(values); bool needToExpandMetadataForEachItem = false; if (itemsAndMetadataFound.Metadata != null && itemsAndMetadataFound.Metadata.Values.Count > 0) { // If there is bare metadata of any kind, and the Include involved an item list, we should // run items individually, as even non-built-in metadata might differ between items if (_referencedItemLists.Count >= 0) { needToExpandMetadataForEachItem = true; } else { // If there is bare built-in metadata, we must always run items individually, as that almost // always differs between items. // UNDONE: When batching is implemented for real, we need to make sure that // item definition metadata is included in all metadata operations during evaluation if (itemsAndMetadataFound.Metadata.Values.Count > 0) { needToExpandMetadataForEachItem = true; } } } return(needToExpandMetadataForEachItem); }
/// <summary> /// Given a subexpression, finds referenced item names and inserts them into the table /// as K=Name, V=String.Empty. /// </summary> /// <remarks> /// We can ignore any semicolons in the expression, since we're not itemizing it. /// </remarks> private static void GetReferencedItemNamesAndMetadata(string expression, int start, int end, ref ItemsAndMetadataPair pair, ShredderOptions whatToShredFor) { for (int i = start; i < end; i++) { int restartPoint; if (Sink(expression, ref i, end, '@', '(')) { // Start of a possible item list expression // Store the index to backtrack to if this doesn't turn out to be a well // formed metadata expression. (Subtract one for the increment when we loop around.) restartPoint = i - 1; SinkWhitespace(expression, ref i); int startOfName = i; if (!SinkValidName(expression, ref i, end)) { i = restartPoint; continue; } // '-' is a legitimate char in an item name, but we should match '->' as an arrow // in '@(foo->'x')' rather than as the last char of the item name. // The old regex accomplished this by being "greedy" if (end > i && expression[i - 1] == '-' && expression[i] == '>') { i--; } // Grab the name, but continue to verify it's a well-formed expression // before we store it. string name = expression.Substring(startOfName, i - startOfName); SinkWhitespace(expression, ref i); bool transformOrFunctionFound = true; // If there's an '->' eat it and the subsequent quoted expression or transform function while (Sink(expression, ref i, end, '-', '>') && transformOrFunctionFound) { SinkWhitespace(expression, ref i); int startTransform = i; bool isQuotedTransform = SinkSingleQuotedExpression(expression, ref i, end); if (isQuotedTransform) { continue; } ItemExpressionCapture functionCapture = SinkItemFunctionExpression(expression, startTransform, ref i, end); if (functionCapture != null) { continue; } if (!isQuotedTransform && functionCapture == null) { i = restartPoint; transformOrFunctionFound = false; } } if (!transformOrFunctionFound) { continue; } SinkWhitespace(expression, ref i); // If there's a ',', eat it and the subsequent quoted expression if (Sink(expression, ref i, ',')) { SinkWhitespace(expression, ref i); if (!Sink(expression, ref i, '\'')) { i = restartPoint; continue; } int closingQuote = expression.IndexOf('\'', i); if (closingQuote == -1) { i = restartPoint; continue; } // Look for metadata in the separator expression // e.g., @(foo, '%(bar)') contains batchable metadata 'bar' GetReferencedItemNamesAndMetadata(expression, i, closingQuote, ref pair, ShredderOptions.MetadataOutsideTransforms); i = closingQuote + 1; } SinkWhitespace(expression, ref i); if (!Sink(expression, ref i, ')')) { i = restartPoint; continue; } // If we've got this far, we know the item expression was // well formed, so make sure the name's in the table if ((whatToShredFor & ShredderOptions.ItemTypes) != 0) { pair.Items = pair.Items ?? new HashSet <string>(MSBuildNameIgnoreCaseComparer.Default); pair.Items.Add(name); } i--; continue; } if (Sink(expression, ref i, end, '%', '(')) { // Start of a possible metadata expression // Store the index to backtrack to if this doesn't turn out to be a well // formed metadata expression. (Subtract one for the increment when we loop around.) restartPoint = i - 1; SinkWhitespace(expression, ref i); int startOfText = i; if (!SinkValidName(expression, ref i, end)) { i = restartPoint; continue; } // Grab this, but we don't know if it's an item or metadata name yet string firstPart = expression.Substring(startOfText, i - startOfText); string itemName = null; string metadataName; string qualifiedMetadataName; SinkWhitespace(expression, ref i); bool qualified = Sink(expression, ref i, '.'); if (qualified) { SinkWhitespace(expression, ref i); startOfText = i; if (!SinkValidName(expression, ref i, end)) { i = restartPoint; continue; } itemName = firstPart; metadataName = expression.Substring(startOfText, i - startOfText); qualifiedMetadataName = itemName + "." + metadataName; } else { metadataName = firstPart; qualifiedMetadataName = metadataName; } SinkWhitespace(expression, ref i); if (!Sink(expression, ref i, ')')) { i = restartPoint; continue; } if ((whatToShredFor & ShredderOptions.MetadataOutsideTransforms) != 0) { pair.Metadata = pair.Metadata ?? new Dictionary <string, MetadataReference>(MSBuildNameIgnoreCaseComparer.Default); pair.Metadata[qualifiedMetadataName] = new MetadataReference(itemName, metadataName); } i--; } } }