public static TargetsToPropagate FromProjectAndEntryTargets(ProjectInstance project, ImmutableList <string> entryTargets) { var targetsForOuterBuild = ImmutableList.CreateBuilder <string>(); var targetsForInnerBuild = ImmutableList.CreateBuilder <string>(); var projectReferenceTargets = project.GetItems(ItemTypeNames.ProjectReferenceTargets); foreach (var entryTarget in entryTargets) { foreach (var projectReferenceTarget in projectReferenceTargets) { if (projectReferenceTarget.EvaluatedInclude.Equals(entryTarget, StringComparison.OrdinalIgnoreCase)) { var targetsMetadataValue = projectReferenceTarget.GetMetadataValue(ItemMetadataNames.ProjectReferenceTargetsMetadataName); var targetsAreForOuterBuild = projectReferenceTarget.GetMetadataValue(ProjectReferenceTargetIsOuterBuildMetadataName).Equals("true", StringComparison.OrdinalIgnoreCase); var targets = ExpressionShredder.SplitSemiColonSeparatedList(targetsMetadataValue).ToArray(); if (targetsAreForOuterBuild) { targetsForOuterBuild.AddRange(targets); } else { targetsForInnerBuild.AddRange(targets); } } } } return(new TargetsToPropagate(targetsForOuterBuild.ToImmutable(), targetsForInnerBuild.ToImmutable())); }
private static IEnumerable <ProjectItemInstance> GetInnerBuildReferences(ProjectInstance outerBuild) { var globalPropertyName = GetInnerBuildPropertyName(outerBuild); var globalPropertyValues = GetInnerBuildPropertyValues(outerBuild); ErrorUtilities.VerifyThrow(!String.IsNullOrWhiteSpace(globalPropertyName), "Must have an inner build property"); ErrorUtilities.VerifyThrow(!String.IsNullOrWhiteSpace(globalPropertyValues), "Must have values for the inner build property"); foreach (var globalPropertyValue in ExpressionShredder.SplitSemiColonSeparatedList(globalPropertyValues)) { yield return(new ProjectItemInstance( outerBuild, "_ProjectSelfReference", outerBuild.FullPath, new[] { new KeyValuePair <string, string>(ItemMetadataNames.PropertiesMetadataName, $"{globalPropertyName}={globalPropertyValue}") }, outerBuild.FullPath)); } }
private static ImmutableList <string> DetermineTargetsToPropagate(ProjectGraphNode node, ImmutableList <string> entryTargets) { var targetsToPropagate = ImmutableList <string> .Empty; ICollection <ProjectItemInstance> projectReferenceTargets = node.ProjectInstance.GetItems(ProjectReferenceTargetsItemType); foreach (var entryTarget in entryTargets) { foreach (var projectReferenceTarget in projectReferenceTargets) { if (projectReferenceTarget.EvaluatedInclude.Equals(entryTarget, StringComparison.OrdinalIgnoreCase)) { string targetsMetadataValue = projectReferenceTarget.GetMetadataValue(ProjectReferenceTargetsMetadataName); targetsToPropagate = targetsToPropagate.AddRange(ExpressionShredder.SplitSemiColonSeparatedList(targetsMetadataValue)); } } } return(targetsToPropagate); }
private void VerifyExpression(string test) { List <string> list = new List <string>(); list.Add(test); ItemsAndMetadataPair pair = ExpressionShredder.GetReferencedItemNamesAndMetadata(list); Hashtable actualItems = pair.Items; Dictionary <string, MetadataReference> actualMetadata = pair.Metadata; Hashtable expectedItems = GetConsumedItemReferences_OriginalImplementation(test); Console.WriteLine("verifying item names..."); VerifyAgainstCanonicalResults(test, actualItems, expectedItems); Hashtable expectedMetadata = GetConsumedMetadataReferences_OriginalImplementation(test); Console.WriteLine("verifying metadata ..."); VerifyAgainstCanonicalResults(test, actualMetadata, expectedMetadata); Console.WriteLine("===OK==="); }
internal static List <ItemBucket> PrepareBatchingBuckets ( List <string> batchableObjectParameters, Lookup lookup, string implicitBatchableItemType, ElementLocation elementLocation ) { if (batchableObjectParameters == null) { ErrorUtilities.ThrowInternalError("Need the parameters of the batchable object to determine if it can be batched."); } if (lookup == null) { ErrorUtilities.ThrowInternalError("Need to specify the lookup."); } ItemsAndMetadataPair pair = ExpressionShredder.GetReferencedItemNamesAndMetadata(batchableObjectParameters); // All the @(itemname) item list references in the tag, including transforms, etc. HashSet <string> consumedItemReferences = pair.Items; // All the %(itemname.metadataname) references in the tag (not counting those embedded // inside item transforms), and note that the itemname portion is optional. // The keys in the returned hash table are the qualified metadata names (e.g. "EmbeddedResource.Culture" // or just "Culture"). The values are MetadataReference structs, which simply split out the item // name (possibly null) and the actual metadata name. Dictionary <string, MetadataReference> consumedMetadataReferences = pair.Metadata; List <ItemBucket> buckets = null; if (consumedMetadataReferences?.Count > 0) { // Add any item types that we were explicitly told to assume. if (implicitBatchableItemType != null) { consumedItemReferences ??= new HashSet <string>(MSBuildNameIgnoreCaseComparer.Default); consumedItemReferences.Add(implicitBatchableItemType); } // This method goes through all the item list references and figures out which ones // will be participating in batching, and which ones won't. We get back a hashtable // where the key is the item name that will be participating in batching. The values // are all String.Empty (not used). This method may return additional item names // that weren't represented in "consumedItemReferences"... this would happen if there // were qualified metadata references in the consumedMetadataReferences table, such as // %(EmbeddedResource.Culture). Dictionary <string, ICollection <ProjectItemInstance> > itemListsToBeBatched = GetItemListsToBeBatched(consumedMetadataReferences, consumedItemReferences, lookup, elementLocation); // At this point, if there were any metadata references in the tag, but no item // references to batch on, we've got a problem because we can't figure out which // item lists the user wants us to batch. if (itemListsToBeBatched.Count == 0) { foreach (string unqualifiedMetadataName in consumedMetadataReferences.Keys) { // Of course, since this throws an exception, there's no way we're ever going // to really loop here... it's just that the foreach is the only way I can // figure out how to get data out of the hashtable without knowing any of the // keys! ProjectErrorUtilities.ThrowInvalidProject(elementLocation, "CannotReferenceItemMetadataWithoutItemName", unqualifiedMetadataName); } } else { // If the batchable object consumes item metadata as well as items to be batched, // we need to partition the items consumed by the object. buckets = BucketConsumedItems(lookup, itemListsToBeBatched, consumedMetadataReferences, elementLocation); } } // if the batchable object does not consume any item metadata or items, or if the item lists it consumes are all // empty, then the object does not need to be batched if ((buckets == null) || (buckets.Count == 0)) { // create a default bucket that references the project items and properties -- this way we always have a bucket buckets = new List <ItemBucket>(1); buckets.Add(new ItemBucket(null, null, lookup, buckets.Count)); } return(buckets); }
/// <summary> /// Retrieves the list of dependencies this target needs to have built and moves the target to the next state. /// Never returns null. /// </summary> /// <returns>A collection of targets on which this target depends.</returns> internal List <TargetSpecification> GetDependencies(ProjectLoggingContext projectLoggingContext) { VerifyState(_state, TargetEntryState.Dependencies); // Resolve the target now, since from this point on we are going to be doing work with the actual instance. GetTargetInstance(); // We first make sure no batching was attempted with the target's condition. // UNDONE: (Improvement) We want to allow this actually. In order to do this we need to determine what the // batching buckets are, and if there are any which aren't empty, return our list of dependencies. // Only in the case where all bucket conditions fail do we want to skip the target entirely (and // this skip building the dependencies.) if (ExpressionShredder.ContainsMetadataExpressionOutsideTransform(_target.Condition)) { ProjectErrorUtilities.ThrowInvalidProject(_target.ConditionLocation, "TargetConditionHasInvalidMetadataReference", _target.Name, _target.Condition); } // If condition is false (based on propertyBag), set this target's state to // "Skipped" since we won't actually build it. bool condition = ConditionEvaluator.EvaluateCondition ( _target.Condition, ParserOptions.AllowPropertiesAndItemLists, _expander, ExpanderOptions.ExpandPropertiesAndItems, _requestEntry.ProjectRootDirectory, _target.ConditionLocation, projectLoggingContext.LoggingService, projectLoggingContext.BuildEventContext ); if (!condition) { _targetResult = new TargetResult(new TaskItem[0] { }, new WorkUnitResult(WorkUnitResultCode.Skipped, WorkUnitActionCode.Continue, null)); _state = TargetEntryState.Completed; if (!projectLoggingContext.LoggingService.OnlyLogCriticalEvents) { // Expand the expression for the Log. string expanded = _expander.ExpandIntoStringAndUnescape(_target.Condition, ExpanderOptions.ExpandPropertiesAndItems, _target.ConditionLocation); // By design: Not building dependencies. This is what NAnt does too. // NOTE: In the original code, this was logged from the target logging context. However, the target // hadn't been "started" by then, so you'd get a target message outside the context of a started // target. In the Task builder (and original Task Engine), a Task Skipped message would be logged in // the context of the target, not the task. This should be the same, especially given that we // wish to allow batching on the condition of a target. projectLoggingContext.LogComment(MessageImportance.Low, "TargetSkippedFalseCondition", _target.Name, _target.Condition, expanded); } return(new List <TargetSpecification>()); } IList <string> dependencies = _expander.ExpandIntoStringListLeaveEscaped(_target.DependsOnTargets, ExpanderOptions.ExpandPropertiesAndItems, _target.DependsOnTargetsLocation); List <TargetSpecification> dependencyTargets = new List <TargetSpecification>(dependencies.Count); foreach (string escapedDependency in dependencies) { string dependencyTargetName = EscapingUtilities.UnescapeAll(escapedDependency); dependencyTargets.Add(new TargetSpecification(dependencyTargetName, _target.DependsOnTargetsLocation)); } _state = TargetEntryState.Execution; return(dependencyTargets); }
private List <ProjectItemInstance> ExpandItemIntoItems ( ProjectItemGroupTaskItemInstance originalItem, Expander <ProjectPropertyInstance, ProjectItemInstance> expander, ISet <string> keepMetadata, ISet <string> removeMetadata ) { // todo this is duplicated logic with the item computation logic from evaluation (in LazyIncludeOperation.SelectItems) ProjectErrorUtilities.VerifyThrowInvalidProject(!(keepMetadata != null && removeMetadata != null), originalItem.KeepMetadataLocation, "KeepAndRemoveMetadataMutuallyExclusive"); List <ProjectItemInstance> items = new List <ProjectItemInstance>(); // Expand properties and metadata in Include string evaluatedInclude = expander.ExpandIntoStringLeaveEscaped(originalItem.Include, ExpanderOptions.ExpandPropertiesAndMetadata, originalItem.IncludeLocation); if (evaluatedInclude.Length == 0) { return(items); } // Compute exclude fragments, without expanding wildcards var excludes = ImmutableList <string> .Empty.ToBuilder(); if (originalItem.Exclude.Length > 0) { string evaluatedExclude = expander.ExpandIntoStringLeaveEscaped(originalItem.Exclude, ExpanderOptions.ExpandAll, originalItem.ExcludeLocation); if (evaluatedExclude.Length > 0) { var excludeSplits = ExpressionShredder.SplitSemiColonSeparatedList(evaluatedExclude); foreach (string excludeSplit in excludeSplits) { excludes.Add(excludeSplit); } } } // Split Include on any semicolons, and take each split in turn var includeSplits = ExpressionShredder.SplitSemiColonSeparatedList(evaluatedInclude); ProjectItemInstanceFactory itemFactory = new ProjectItemInstanceFactory(this.Project, originalItem.ItemType); foreach (string includeSplit in includeSplits) { // 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 // Pass the non wildcard expanded excludes here to fix https://github.com/dotnet/msbuild/issues/2621 string[] includeSplitFiles = EngineFileUtilities.GetFileListEscaped( Project.Directory, includeSplit, excludes); foreach (string includeSplitFile in includeSplitFiles) { items.Add(new ProjectItemInstance( Project, originalItem.ItemType, includeSplitFile, includeSplit /* before wildcard expansion */, null, null, originalItem.Location.File)); } } } // Evaluate, split, expand and subtract any Exclude HashSet <string> excludesUnescapedForComparison = new HashSet <string>(StringComparer.OrdinalIgnoreCase); foreach (string excludeSplit in excludes) { 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> /// 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> /// <param name="keepMetadata">An <see cref="ISet{String}"/> of metadata names to keep.</param> /// <param name="removeMetadata">An <see cref="ISet{String}"/> of metadata names to remove.</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); }