Example #1
0
        /// <summary>
        /// Processes the "include" list and the "exclude" list for an item element, and returns
        /// the resultant virtual group of items. Ignores any condition on the item.
        /// </summary>
        /// <param name="baseDirectory">Where relative paths should be evaluated from</param>
        /// <param name="originalItem">The "mother" item that's being expanded</param>
        /// <param name="expander">Expander to evaluated items and properties</param>
        /// <param name="expandMetadata">Whether metadata expressions should be expanded, or left as literals</param>
        internal static BuildItemGroup ExpandItemIntoItems
        (
            string baseDirectory,
            BuildItem originalItem,
            Expander expander,
            bool expandMetadata
        )
        {
            ErrorUtilities.VerifyThrow(originalItem != null, "Can't add a null item to project.");
            ErrorUtilities.VerifyThrow(expander != null, "expander can't be null.");

            // Take the entire string specified in the "Include" attribute, and split
            // it up by semi-colons.  Then loop over the individual pieces.
            // 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.
            List <string>  itemIncludePieces = (new Expander(expander, ExpanderOptions.ExpandProperties).ExpandAllIntoStringListLeaveEscaped(originalItem.Include, originalItem.IncludeAttribute));
            BuildItemGroup itemsToInclude    = new BuildItemGroup();

            for (int i = 0; i < itemIncludePieces.Count; i++)
            {
                BuildItemGroup itemizedGroup = expander.ExpandSingleItemListExpressionIntoItemsLeaveEscaped(itemIncludePieces[i], originalItem.IncludeAttribute);
                if (itemizedGroup == null)
                {
                    // The expression did not represent a single @(...) item list reference.
                    if (expandMetadata)
                    {
                        // We're inside a target: metadata expressions like %(foo) are legal, so expand them now
                        itemIncludePieces[i] = expander.ExpandMetadataLeaveEscaped(itemIncludePieces[i]);
                    }
                    // Now it's a string constant, possibly with wildcards.
                    // Take each individual path or file expression, and expand any
                    // wildcards.  Then loop through each file returned.
                    if (itemIncludePieces[i].Length > 0)
                    {
                        string[] includeFileList = EngineFileUtilities.GetFileListEscaped(baseDirectory, itemIncludePieces[i]);
                        for (int j = 0; j < includeFileList.Length; j++)
                        {
                            BuildItem newItem = itemsToInclude.AddNewItem(originalItem.Name, originalItem.Include);
                            newItem.SetEvaluatedItemSpecEscaped(itemIncludePieces[i]); // comes from XML include --- "arbitrarily escaped"
                            newItem.SetFinalItemSpecEscaped(includeFileList[j]);       // comes from file system matcher -- "canonically escaped"
                        }
                    }
                }
                else
                {
                    itemsToInclude.ImportItems(itemizedGroup);
                }
            }

            List <BuildItem> matchingItems = FindItemsMatchingSpecification(itemsToInclude, originalItem.Exclude, originalItem.ExcludeAttribute, expander, baseDirectory);

            if (matchingItems != null)
            {
                foreach (BuildItem item in matchingItems)
                {
                    itemsToInclude.RemoveItem(item);
                }
            }

            return(itemsToInclude);
        }
Example #2
0
        /// <summary>
        /// This creates a shallow clone of the BuildItem.  If this is an xml-backed item,
        /// then the clone references the same XML element as the original, meaning
        /// that modifications to the clone will affect the original.
        /// </summary>
        /// <returns></returns>
        /// <owner>rgoel</owner>
        public BuildItem Clone()
        {
            BuildItem clonedItem;

            if (IsBackedByXml)
            {
                clonedItem = new BuildItem(xml.Element, this.importedFromAnotherProject, this.itemDefinitionLibrary);
                clonedItem.SetEvaluatedItemSpecEscaped(this.evaluatedItemSpecEscaped);
                clonedItem.SetFinalItemSpecEscaped(this.FinalItemSpecEscaped);
                clonedItem.itemSpecModifiers = this.itemSpecModifiers;
                clonedItem.recursivePortionOfFinalItemSpecDirectory = this.recursivePortionOfFinalItemSpecDirectory;
                clonedItem.evaluatedCustomMetadata = this.evaluatedCustomMetadata;
                clonedItem.unevaluatedCustomMetadata = this.unevaluatedCustomMetadata;
                clonedItem.isPartOfProjectManifest = this.isPartOfProjectManifest;
                clonedItem.itemDefinitionLibrary = this.itemDefinitionLibrary;
            }
            else
            {
                clonedItem = VirtualClone();
            }

            // Do not set the ParentPersistedItemGroup on the cloned item, because it isn't really
            // part of the item group.

            return clonedItem;
        }
Example #3
0
        /// <summary>
        /// Clones the item to a virtual item i.e. an item with no backing XML.
        /// If removeReferences is specified, removes all references which hold on to Projects (or other heavyweight objects)
        /// in order to minimize the transitive size of the clone.
        /// </summary>
        /// <remarks>
        /// This method differs from Clone() in that it always produces a virtual item, even when given an item with backing XML.
        /// Decoupling an item from its XML allows modifications to the clone without affecting the original item.
        /// </remarks>
        internal BuildItem VirtualClone(bool removeReferences)
        {
            ItemDefinitionLibrary definitionLibraryToClone = this.itemDefinitionLibrary;

            if (removeReferences)
            {
                definitionLibraryToClone = null;
            }

           BuildItem virtualClone = 
               new BuildItem
               (
                   null /* this is a virtual item with no backing XML */,
                   name, Include,
                   false, /* PERF NOTE: don't waste time creating a new custom metadata
                           * cache, because we're going to clone the current item's cache */
                   definitionLibraryToClone
               );

            virtualClone.SetEvaluatedItemSpecEscaped(evaluatedItemSpecEscaped);
            virtualClone.SetFinalItemSpecEscaped(FinalItemSpecEscaped);

            // NOTE: itemSpecModifiers don't need to be deep-cloned because they are tied to the finalItemSpec -- when the
            // finalItemSpec changes, they will get reset
            virtualClone.itemSpecModifiers = itemSpecModifiers;
            virtualClone.recursivePortionOfFinalItemSpecDirectory = recursivePortionOfFinalItemSpecDirectory;

            ErrorUtilities.VerifyThrow(unevaluatedCustomMetadata != null && evaluatedCustomMetadata != null, "Item is not initialized properly.");
             
            if (removeReferences)
            {
                // The ItemDefinition is going to be cleared to remove a link between a project instance and the Item when it is in the cache of targetOutputs.
                // We need to merge the ItemDefinition metadata onto the builditemto preserve the metadata on the buildItem when the ItemDefinition
                // is cleared and the item is placed in the cache.
                virtualClone.unevaluatedCustomMetadata = (CopyOnWriteHashtable)GetAllCustomUnevaluatedMetadata();
                virtualClone.evaluatedCustomMetadata = (CopyOnWriteHashtable)GetAllCustomEvaluatedMetadata();
            }
            else
            {
                // Cloning is cheap for CopyOnWriteHashtable so just always do it.
                virtualClone.unevaluatedCustomMetadata = (CopyOnWriteHashtable)this.unevaluatedCustomMetadata.Clone();
                virtualClone.evaluatedCustomMetadata = (CopyOnWriteHashtable)this.evaluatedCustomMetadata.Clone();
           }
   
            return virtualClone;
        }
Example #4
0
        /// <summary>
        /// Called from the IDE to add a new item of a particular type to the project file. This method tries to add the new item
        /// near the other items of the same type.
        /// </summary>
        /// <owner>RGoel</owner>
        /// <param name="itemName">The name of the item list this item belongs to.</param>
        /// <param name="itemInclude">The value of the item's <c>Include</c> attribute i.e. the item-spec</param>
        /// <returns>The new item after evaluation.</returns>
        public BuildItem AddNewItem
            (
            string itemName,
            string itemInclude
            )
        {
            ErrorUtilities.VerifyThrowArgumentLength(itemName, "itemName");
            ErrorUtilities.VerifyThrowArgumentLength(itemInclude, "itemInclude");

            BuildItemGroup matchingItemGroup = null;

            // Search all of our existing (persisted) ItemGroups for one that is:
            //      1.)  local to the main project file
            //      2.)  a top-level BuildItemGroup, as opposed to a nested BuildItemGroup.
            //      3.)  has no "Condition"
            //      4.)  contains at least one item of the same type as the new item being added.
            foreach (BuildItemGroup itemGroup in this.rawItemGroups)
            {
                if (
                    (!itemGroup.IsImported) &&
                    (itemGroup.Condition.Length == 0)
                )
                {
                    // Now loop through the Items in the BuildItemGroup, and see if there's one of
                    // the same type as the new item being added.
                    foreach (BuildItem originalItem in itemGroup)
                    {
                        if ( 0 == String.Compare( originalItem.Name, itemName, StringComparison.OrdinalIgnoreCase))
                        {
                            // If the new item that the user is trying to add is already covered by 
                            // a wildcard in an existing item of the project, then there's really
                            // no need to physically touch the project file.  As long as the new item
                            // is on disk, the next reevaluation will automatically pick it up.  When
                            // customers employ the use of wildcards in their project files, and then
                            // they add new items through the IDE, they would much prefer that the IDE
                            // does not touch their project files.
                            if (originalItem.NewItemSpecMatchesExistingWildcard(itemInclude))
                            {
                                BuildItem tempNewItem = new BuildItem(itemName, itemInclude);
                                tempNewItem.SetEvaluatedItemSpecEscaped(itemInclude);
                                tempNewItem.SetFinalItemSpecEscaped((new Expander(evaluatedProperties)).ExpandAllIntoStringLeaveEscaped(itemInclude, null));

                                // We didn't touch the project XML, but we still need to add the new
                                // item to the appropriate data structures, and we need to have something
                                // to hand back to the project system so it can modify the new item
                                // later if needed.
                                BuildItem newItem = BuildItem.CreateClonedParentedItem(tempNewItem, originalItem);

                                AddToItemListByNameIgnoringCondition(newItem);

                                // Set up the other half of the parent/child relationship.
                                newItem.ParentPersistedItem.ChildItems.AddItem(newItem);

                                // Don't bother adding to item lists by name, as we're going to have to evaluate the project as a whole later anyway

                                // We haven't actually changed the XML for the project, because we're 
                                // just piggybacking onto an existing item that was a wildcard.  However,
                                // we should reevaluate on the next build.
                                this.MarkProjectAsDirtyForReevaluation();

                                return newItem;
                            }

                            matchingItemGroup = itemGroup;
                            break;
                        }
                    }
                }
            }

            // If we didn't find a matching BuildItemGroup, create a new one.
            if (matchingItemGroup == null)
            {
                matchingItemGroup = this.AddNewItemGroup();
            }

            // Add the new item to the appropriate place within the BuildItemGroup.  This
            // will attempt to keep items of the same type physically contiguous.
            BuildItem itemToAdd = matchingItemGroup.AddNewItem(itemName, itemInclude);

            // Since we're re-evaluating the project, clear out the previous list of child items
            // for each persisted item tag.
            itemToAdd.ChildItems.Clear();

            // Add this new item into the appropriate evaluated item tables for this project.
            BuildItemGroup items = BuildItemGroup.ExpandItemIntoItems(ProjectDirectory, itemToAdd, new Expander(evaluatedProperties, evaluatedItemsByName), false /* do not expand metadata */);

            foreach (BuildItem item in items)
            {
                BuildItem newItem = BuildItem.CreateClonedParentedItem(item, itemToAdd);

                AddToItemListByNameIgnoringCondition(newItem);

                // Set up the other half of the parent/child relationship.
                newItem.ParentPersistedItem.ChildItems.AddItem(newItem);

                // Don't bother adding to item lists by name, as we're going to have to evaluate the project as a whole later anyway
            }

            this.MarkProjectAsDirty();

            // Return the *evaluated* item to the caller. This way he can ask for evaluated item metadata, etc.
            // It also makes it consistent, because the IDE at project-load asks for all evaluated items,
            // and caches those pointers. We know the IDE is going to cache this pointer as well, so we
            // should give back an evaluated item here as well.
            return (itemToAdd.ChildItems.Count > 0) ? itemToAdd.ChildItems[0] : null;
        }