示例#1
0
        private void ProcessMetadataElements(ProjectItemElement itemElement, OperationBuilderWithMetadata operationBuilder)
        {
            if (itemElement.HasMetadata)
            {
                operationBuilder.Metadata.AddRange(itemElement.Metadata);

                List <string> values = new List <string>(itemElement.Metadata.Count * 2);

                foreach (ProjectMetadataElement metadatumElement in itemElement.Metadata)
                {
                    values.Add(metadatumElement.Value);
                    values.Add(metadatumElement.Condition);
                }

                ItemsAndMetadataPair itemsAndMetadataFound = ExpressionShredder.GetReferencedItemNamesAndMetadata(values);
                if (itemsAndMetadataFound.Items != null)
                {
                    foreach (var itemType in itemsAndMetadataFound.Items)
                    {
                        var itemList = GetItemList(itemType);
                        if (itemList != null)
                        {
                            operationBuilder.ReferencedItemLists[itemType] = itemList;
                        }
                    }
                }
            }
        }
示例#2
0
        /// <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?.Count > 0);

            return(result);
        }
示例#3
0
        /// <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(IEnumerable <string> expressions)
        {
            ItemsAndMetadataPair pair = new ItemsAndMetadataPair(null, null);

            foreach (string expression in expressions)
            {
                GetReferencedItemNamesAndMetadata(expression, 0, expression.Length, ref pair, ShredderOptions.All);
            }

            return(pair);
        }
示例#4
0
        /// <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 ??= 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 ??= new Dictionary <string, MetadataReference>(MSBuildNameIgnoreCaseComparer.Default);
                        pair.Metadata[qualifiedMetadataName] = new MetadataReference(itemName, metadataName);
                    }

                    i--;
                }
            }
        }
示例#5
0
            protected void DecorateItemsWithMetadata(ImmutableList <I> items, ImmutableList <ProjectMetadataElement> metadata)
            {
                if (metadata.Count > 0)
                {
                    ////////////////////////////////////////////////////
                    // UNDONE: Implement batching here.
                    //
                    // We want to allow built-in metadata in metadata values here.
                    // For example, so that an Idl file can specify that its Tlb output should be named %(Filename).tlb.
                    //
                    // In other words, we want batching. However, we won't need to go to the trouble of using the regular batching code!
                    // That's because that code is all about grouping into buckets of similar items. In this context, we're not
                    // invoking a task, and it's fine to process each item individually, which will always give the correct results.
                    //
                    // For the CTP, to make the minimal change, we will not do this quite correctly.
                    //
                    // We will do this:
                    // -- check whether any metadata values or their conditions contain any bare built-in metadata expressions,
                    //    or whether they contain any custom metadata && the Include involved an @(itemlist) expression.
                    // -- if either case is found, we go ahead and evaluate all the metadata separately for each item.
                    // -- otherwise we can do the old thing (evaluating all metadata once then applying to all items)
                    //
                    // This algorithm gives the correct results except when:
                    // -- batchable expressions exist on the include, exclude, or condition on the item element itself
                    //
                    // It means that 99% of cases still go through the old code, which is best for the CTP.
                    // When we ultimately implement this correctly, we should make sure we optimize for the case of very many items
                    // and little metadata, none of which varies between items.

                    // Do not expand properties as they have been already expanded by the lazy evaluator upon item operation construction.
                    // Prior to lazy evaluation ExpanderOptions.ExpandAll was used.
                    const ExpanderOptions metadataExpansionOptions = ExpanderOptions.ExpandAll;

                    List <string> values = new List <string>(metadata.Count * 2);

                    foreach (var metadataElement in metadata)
                    {
                        values.Add(metadataElement.Value);
                        values.Add(metadataElement.Condition);
                    }

                    ItemsAndMetadataPair itemsAndMetadataFound = ExpressionShredder.GetReferencedItemNamesAndMetadata(values);

                    bool needToProcessItemsIndividually = 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)
                        {
                            needToProcessItemsIndividually = 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)
                            {
                                needToProcessItemsIndividually = true;
                            }
                        }
                    }

                    if (needToProcessItemsIndividually)
                    {
                        foreach (I item in items)
                        {
                            _expander.Metadata = item;

                            foreach (var metadataElement in metadata)
                            {
#if FEATURE_MSBUILD_DEBUGGER
                                //if (DebuggerManager.DebuggingEnabled)
                                //{
                                //    DebuggerManager.PulseState(metadataElementElement.Location, _itemPassLocals);
                                //}
#endif

                                if (!EvaluateCondition(metadataElement.Condition, metadataElement, metadataExpansionOptions, ParserOptions.AllowAll, _expander, _lazyEvaluator))
                                {
                                    continue;
                                }

                                string evaluatedValue = _expander.ExpandIntoStringLeaveEscaped(metadataElement.Value, metadataExpansionOptions, metadataElement.Location);

                                item.SetMetadata(metadataElement, FileUtilities.MaybeAdjustFilePath(evaluatedValue, metadataElement.ContainingProject.DirectoryPath));
                            }
                        }

                        // End of legal area for metadata expressions.
                        _expander.Metadata = null;
                    }

                    // End of pseudo batching
                    ////////////////////////////////////////////////////
                    // Start of old code
                    else
                    {
                        // Metadata expressions are allowed here.
                        // Temporarily gather and expand these in a table so they can reference other metadata elements above.
                        EvaluatorMetadataTable metadataTable = new EvaluatorMetadataTable(_itemType);
                        _expander.Metadata = metadataTable;

                        // Also keep a list of everything so we can get the predecessor objects correct.
                        List <Pair <ProjectMetadataElement, string> > metadataList = new List <Pair <ProjectMetadataElement, string> >(metadata.Count);

                        foreach (var metadataElement in metadata)
                        {
                            // Because of the checking above, it should be safe to expand metadata in conditions; the condition
                            // will be true for either all the items or none
                            if (!EvaluateCondition(metadataElement.Condition, metadataElement, metadataExpansionOptions, ParserOptions.AllowAll, _expander, _lazyEvaluator))
                            {
                                continue;
                            }

#if FEATURE_MSBUILD_DEBUGGER
                            //if (DebuggerManager.DebuggingEnabled)
                            //{
                            //    DebuggerManager.PulseState(metadataElementElement.Location, _itemPassLocals);
                            //}
#endif

                            string evaluatedValue = _expander.ExpandIntoStringLeaveEscaped(metadataElement.Value, metadataExpansionOptions, metadataElement.Location);
                            evaluatedValue = FileUtilities.MaybeAdjustFilePath(evaluatedValue, metadataElement.ContainingProject.DirectoryPath);

                            metadataTable.SetValue(metadataElement, evaluatedValue);
                            metadataList.Add(new Pair <ProjectMetadataElement, string>(metadataElement, evaluatedValue));
                        }

                        // Apply those metadata to each item
                        // Note that several items could share the same metadata objects

                        // Set all the items at once to make a potential copy-on-write optimization possible.
                        // This is valuable in the case where one item element evaluates to
                        // many items (either by semicolon or wildcards)
                        // and that item also has the same piece/s of metadata for each item.
                        _itemFactory.SetMetadata(metadataList, items);

                        // End of legal area for metadata expressions.
                        _expander.Metadata = null;
                    }
                }
            }
示例#6
0
            protected bool NeedToExpandMetadataForEachItem(ImmutableList <ProjectMetadataElement> metadata, out ItemsAndMetadataPair itemsAndMetadataFound)
            {
                itemsAndMetadataFound = ExpressionShredder.GetReferencedItemNamesAndMetadata(GetMetadataValuesAndConditions(metadata));

                bool needToExpandMetadataForEachItem = false;

                if (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);
            }
示例#7
0
        /// <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--;
                }
            }
        }
示例#8
0
        /// <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;
        }
示例#9
0
        /// <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;
        }