internal static ProjectItemGroupTaskItemInstance FactoryForDeserialization(ITranslator translator) { var instance = new ProjectItemGroupTaskItemInstance(); ((ITranslatable)instance).Translate(translator); return(instance); }
/// <summary> /// Cloning constructor /// </summary> private ProjectItemGroupTaskItemInstance(ProjectItemGroupTaskItemInstance that) { // All fields are immutable _itemType = that._itemType; _include = that._include; _exclude = that._exclude; _remove = that._remove; _keepMetadata = that._keepMetadata; _removeMetadata = that._removeMetadata; _keepDuplicates = that._keepDuplicates; _condition = that._condition; _metadata = that._metadata; }
/// <summary> /// Takes an item specification, evaluates it and expands it into a list of items /// </summary> /// <param name="originalItem">The original item data</param> /// <param name="expander">The expander to use.</param> /// <remarks> /// This code is very close to that which exists in the Evaluator.EvaluateItemXml method. However, because /// it invokes type constructors, and those constructors take arguments of fundamentally different types, it has not /// been refactored. /// </remarks> /// <returns>A list of items.</returns> private IList<ProjectItemInstance> ExpandItemIntoItems ( ProjectItemGroupTaskItemInstance originalItem, Expander<ProjectPropertyInstance, ProjectItemInstance> expander, ISet<string> keepMetadata, ISet<string> removeMetadata ) { ProjectErrorUtilities.VerifyThrowInvalidProject(!(keepMetadata != null && removeMetadata != null), originalItem.KeepMetadataLocation, "KeepAndRemoveMetadataMutuallyExclusive"); IList<ProjectItemInstance> items = new List<ProjectItemInstance>(); // UNDONE: (Refactor) This code also exists in largely the same form in Evaluator.CreateItemsFromInclude. // STEP 1: Expand properties and metadata in Include string evaluatedInclude = expander.ExpandIntoStringLeaveEscaped(originalItem.Include, ExpanderOptions.ExpandPropertiesAndMetadata, originalItem.IncludeLocation); // STEP 2: Split Include on any semicolons, and take each split in turn if (evaluatedInclude.Length > 0) { IList<string> includeSplits = ExpressionShredder.SplitSemiColonSeparatedList(evaluatedInclude); ProjectItemInstanceFactory itemFactory = new ProjectItemInstanceFactory(this.Project, originalItem.ItemType); foreach (string includeSplit in includeSplits) { // STEP 3: If expression is "@(x)" copy specified list with its metadata, otherwise just treat as string bool throwaway; IList<ProjectItemInstance> itemsFromSplit = expander.ExpandSingleItemVectorExpressionIntoItems(includeSplit, itemFactory, ExpanderOptions.ExpandItems, false /* do not include null expansion results */, out throwaway, originalItem.IncludeLocation); if (itemsFromSplit != null) { // Expression is in form "@(X)", so add these items directly. foreach (ProjectItemInstance item in itemsFromSplit) { items.Add(item); } } else { // The expression is not of the form "@(X)". Treat as string string[] includeSplitFiles = EngineFileUtilities.GetFileListEscaped(Project.Directory, includeSplit); foreach (string includeSplitFile in includeSplitFiles) { items.Add(new ProjectItemInstance(Project, originalItem.ItemType, includeSplitFile, includeSplit /* before wildcard expansion */, null, null, originalItem.Location.File)); } } } // STEP 4: Evaluate, split, expand and subtract any Exclude if (originalItem.Exclude.Length > 0) { string evaluatedExclude = expander.ExpandIntoStringLeaveEscaped(originalItem.Exclude, ExpanderOptions.ExpandAll, originalItem.ExcludeLocation); if (evaluatedExclude.Length > 0) { IList<string> excludeSplits = ExpressionShredder.SplitSemiColonSeparatedList(evaluatedExclude); HashSet<string> excludesUnescapedForComparison = new HashSet<string>(StringComparer.OrdinalIgnoreCase); foreach (string excludeSplit in excludeSplits) { string[] excludeSplitFiles = EngineFileUtilities.GetFileListUnescaped(Project.Directory, excludeSplit); foreach (string excludeSplitFile in excludeSplitFiles) { excludesUnescapedForComparison.Add(excludeSplitFile); } } List<ProjectItemInstance> remainingItems = new List<ProjectItemInstance>(); for (int i = 0; i < items.Count; i++) { if (!excludesUnescapedForComparison.Contains(((IItem)items[i]).EvaluatedInclude)) { remainingItems.Add(items[i]); } } items = remainingItems; } } } // Filter the metadata as appropriate if (keepMetadata != null) { foreach (var item in items) { var metadataToRemove = item.MetadataNames.Where(name => !keepMetadata.Contains(name)); foreach (var metadataName in metadataToRemove) { item.RemoveMetadata(metadataName); } } } else if (removeMetadata != null) { foreach (var item in items) { var metadataToRemove = item.MetadataNames.Where(name => removeMetadata.Contains(name)); foreach (var metadataName in metadataToRemove) { item.RemoveMetadata(metadataName); } } } return items; }
/// <summary> /// Adds batchable parameters from an item element into the list. If the item element was a task, these /// would be its raw parameter values. /// </summary> /// <param name="parameterValues">The list of batchable values</param> /// <param name="child">The item from which to find batchable values</param> private void GetBatchableValuesFromBuildItemGroupChild(List<string> parameterValues, ProjectItemGroupTaskItemInstance child) { AddIfNotEmptyString(parameterValues, child.Include); AddIfNotEmptyString(parameterValues, child.Exclude); AddIfNotEmptyString(parameterValues, child.Remove); AddIfNotEmptyString(parameterValues, child.Condition); foreach (ProjectItemGroupTaskMetadataInstance metadataElement in child.Metadata) { AddIfNotEmptyString(parameterValues, metadataElement.Value); AddIfNotEmptyString(parameterValues, metadataElement.Condition); } }
/// <summary> /// Modifies items in the world - specifically, changes their metadata. Changes to items that are part of the project manifest are backed up, so /// they can be reverted when the project is reset after the end of the build. /// </summary> /// <param name="child">The item specification to evaluate and modify.</param> /// <param name="bucket">The batching bucket.</param> private void ExecuteModify(ProjectItemGroupTaskItemInstance child, ItemBucket bucket, ISet<string> keepMetadata, ISet<string> removeMetadata) { ICollection<ProjectItemInstance> group = bucket.Lookup.GetItems(child.ItemType); if (group == null || group.Count == 0) { // No items of this type to modify return; } // Figure out what metadata names and values we need to set var metadataToSet = new Lookup.MetadataModifications(keepMetadata != null); // Filter the metadata as appropriate if (keepMetadata != null) { foreach (var metadataName in keepMetadata) { metadataToSet[metadataName] = Lookup.MetadataModification.CreateFromNoChange(); } } else if (removeMetadata != null) { foreach (var metadataName in removeMetadata) { metadataToSet[metadataName] = Lookup.MetadataModification.CreateFromRemove(); } } foreach (ProjectItemGroupTaskMetadataInstance metadataInstance in child.Metadata) { bool condition = ConditionEvaluator.EvaluateCondition ( metadataInstance.Condition, ParserOptions.AllowAll, bucket.Expander, ExpanderOptions.ExpandAll, Project.Directory, metadataInstance.ConditionLocation, LoggingContext.LoggingService, LoggingContext.BuildEventContext ); if (condition) { string evaluatedValue = bucket.Expander.ExpandIntoStringLeaveEscaped(metadataInstance.Value, ExpanderOptions.ExpandAll, metadataInstance.Location); metadataToSet[metadataInstance.Name] = Lookup.MetadataModification.CreateFromNewValue(evaluatedValue); } } // Now apply the changes. This must be done after filtering, since explicitly set metadata overrides filters. bucket.Lookup.ModifyItems(child.ItemType, group, metadataToSet); }
/// <summary> /// Remove items from the world. Removes to items that are part of the project manifest are backed up, so /// they can be reverted when the project is reset after the end of the build. /// </summary> /// <param name="child">The item specification to evaluate and remove.</param> /// <param name="bucket">The batching bucket.</param> private void ExecuteRemove(ProjectItemGroupTaskItemInstance child, ItemBucket bucket) { ICollection<ProjectItemInstance> group = bucket.Lookup.GetItems(child.ItemType); if (group == null) { // No items of this type to remove return; } List<ProjectItemInstance> itemsToRemove = FindItemsMatchingSpecification(group, child.Remove, child.RemoveLocation, bucket.Expander); if (itemsToRemove != null) { if (LogTaskInputs && !LoggingContext.LoggingService.OnlyLogCriticalEvents && itemsToRemove.Count > 0) { var itemGroupText = ItemGroupLoggingHelper.GetParameterText(ResourceUtilities.GetResourceString("ItemGroupRemoveLogMessage"), child.ItemType, itemsToRemove.ToArray()); LoggingContext.LogCommentFromText(MessageImportance.Low, itemGroupText); } bucket.Lookup.RemoveItems(itemsToRemove); } }
/// <summary> /// Add items to the world. This is the in-target equivalent of an item include expression outside of a target. /// </summary> /// <param name="child">The item specification to evaluate and add.</param> /// <param name="bucket">The batching bucket.</param> private void ExecuteAdd(ProjectItemGroupTaskItemInstance child, ItemBucket bucket, ISet<string> keepMetadata, ISet<string> removeMetadata) { // First, collect up the appropriate metadata collections. We need the one from the item definition, if any, and // the one we are using for this batching bucket. ProjectItemDefinitionInstance itemDefinition = null; Project.ItemDefinitions.TryGetValue(child.ItemType, out itemDefinition); // The NestedMetadataTable will handle the aggregation of the different metadata collections NestedMetadataTable metadataTable = new NestedMetadataTable(child.ItemType, bucket.Expander.Metadata, itemDefinition); IMetadataTable originalMetadataTable = bucket.Expander.Metadata; bucket.Expander.Metadata = metadataTable; // Second, expand the item include and exclude, and filter existing metadata as appropriate. IList<ProjectItemInstance> itemsToAdd = ExpandItemIntoItems(child, bucket.Expander, keepMetadata, removeMetadata); // Third, expand the metadata. foreach (ProjectItemGroupTaskMetadataInstance metadataInstance in child.Metadata) { bool condition = ConditionEvaluator.EvaluateCondition ( metadataInstance.Condition, ParserOptions.AllowAll, bucket.Expander, ExpanderOptions.ExpandAll, Project.Directory, metadataInstance.Location, LoggingContext.LoggingService, LoggingContext.BuildEventContext ); if (condition) { string evaluatedValue = bucket.Expander.ExpandIntoStringLeaveEscaped(metadataInstance.Value, ExpanderOptions.ExpandAll, metadataInstance.Location); // This both stores the metadata so we can add it to all the items we just created later, and // exposes this metadata to further metadata evaluations in subsequent loop iterations. metadataTable.SetValue(metadataInstance.Name, evaluatedValue); } } // Finally, copy the added metadata onto the new items. The set call is additive. ProjectItemInstance.SetMetadata(metadataTable.AddedMetadata, itemsToAdd); // Add in one operation for potential copy-on-write // Restore the original metadata table. bucket.Expander.Metadata = originalMetadataTable; // Determine if we should NOT add duplicate entries bool keepDuplicates = ConditionEvaluator.EvaluateCondition ( child.KeepDuplicates, ParserOptions.AllowAll, bucket.Expander, ExpanderOptions.ExpandAll, Project.Directory, child.KeepDuplicatesLocation, LoggingContext.LoggingService, LoggingContext.BuildEventContext ); if (LogTaskInputs && !LoggingContext.LoggingService.OnlyLogCriticalEvents && itemsToAdd != null && itemsToAdd.Count > 0) { var itemGroupText = ItemGroupLoggingHelper.GetParameterText(ResourceUtilities.GetResourceString("ItemGroupIncludeLogMessagePrefix"), child.ItemType, itemsToAdd.ToArray()); LoggingContext.LogCommentFromText(MessageImportance.Low, itemGroupText); } // Now add the items we created to the lookup. bucket.Lookup.AddNewItemsOfItemType(child.ItemType, itemsToAdd, !keepDuplicates); // Add in one operation for potential copy-on-write }