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); }
public void GetBuckets() { ProjectInstance project = ProjectHelpers.CreateEmptyProjectInstance(); List<string> parameters = new List<string>(); parameters.Add("@(File);$(unittests)"); parameters.Add("$(obj)\\%(Filename).ext"); parameters.Add("@(File->'%(extension)')"); // attributes in transforms don't affect batching ItemDictionary<ProjectItemInstance> itemsByType = new ItemDictionary<ProjectItemInstance>(); IList<ProjectItemInstance> items = new List<ProjectItemInstance>(); items.Add(new ProjectItemInstance(project, "File", "a.foo", project.FullPath)); items.Add(new ProjectItemInstance(project, "File", "b.foo", project.FullPath)); items.Add(new ProjectItemInstance(project, "File", "c.foo", project.FullPath)); items.Add(new ProjectItemInstance(project, "File", "d.foo", project.FullPath)); items.Add(new ProjectItemInstance(project, "File", "e.foo", project.FullPath)); itemsByType.ImportItems(items); items = new List<ProjectItemInstance>(); items.Add(new ProjectItemInstance(project, "Doc", "a.doc", project.FullPath)); items.Add(new ProjectItemInstance(project, "Doc", "b.doc", project.FullPath)); items.Add(new ProjectItemInstance(project, "Doc", "c.doc", project.FullPath)); items.Add(new ProjectItemInstance(project, "Doc", "d.doc", project.FullPath)); items.Add(new ProjectItemInstance(project, "Doc", "e.doc", project.FullPath)); itemsByType.ImportItems(items); PropertyDictionary<ProjectPropertyInstance> properties = new PropertyDictionary<ProjectPropertyInstance>(); properties.Set(ProjectPropertyInstance.Create("UnitTests", "unittests.foo")); properties.Set(ProjectPropertyInstance.Create("OBJ", "obj")); List<ItemBucket> buckets = BatchingEngine.PrepareBatchingBuckets(parameters, CreateLookup(itemsByType, properties), MockElementLocation.Instance); Assert.AreEqual(5, buckets.Count); foreach (ItemBucket bucket in buckets) { // non-batching data -- same for all buckets XmlAttribute tempXmlAttribute = (new XmlDocument()).CreateAttribute("attrib"); tempXmlAttribute.Value = "'$(Obj)'=='obj'"; Assert.IsTrue(ConditionEvaluator.EvaluateCondition(tempXmlAttribute.Value, ParserOptions.AllowAll, bucket.Expander, ExpanderOptions.ExpandAll, Directory.GetCurrentDirectory(), MockElementLocation.Instance, null, new BuildEventContext(1, 2, 3, 4))); Assert.AreEqual("a.doc;b.doc;c.doc;d.doc;e.doc", bucket.Expander.ExpandIntoStringAndUnescape("@(doc)", ExpanderOptions.ExpandItems, MockElementLocation.Instance)); Assert.AreEqual("unittests.foo", bucket.Expander.ExpandIntoStringAndUnescape("$(bogus)$(UNITTESTS)", ExpanderOptions.ExpandPropertiesAndMetadata, MockElementLocation.Instance)); } Assert.AreEqual("a.foo", buckets[0].Expander.ExpandIntoStringAndUnescape("@(File)", ExpanderOptions.ExpandItems, MockElementLocation.Instance)); Assert.AreEqual(".foo", buckets[0].Expander.ExpandIntoStringAndUnescape("@(File->'%(Extension)')", ExpanderOptions.ExpandItems, MockElementLocation.Instance)); Assert.AreEqual("obj\\a.ext", buckets[0].Expander.ExpandIntoStringAndUnescape("$(obj)\\%(Filename).ext", ExpanderOptions.ExpandPropertiesAndMetadata, MockElementLocation.Instance)); // we weren't batching on this attribute, so it has no value Assert.AreEqual(String.Empty, buckets[0].Expander.ExpandIntoStringAndUnescape("%(Extension)", ExpanderOptions.ExpandAll, MockElementLocation.Instance)); ProjectItemInstanceFactory factory = new ProjectItemInstanceFactory(project, "i"); items = buckets[0].Expander.ExpandIntoItemsLeaveEscaped("@(file)", factory, ExpanderOptions.ExpandItems, MockElementLocation.Instance); Assert.IsNotNull(items); Assert.AreEqual(1, items.Count); int invalidProjectFileExceptions = 0; try { // This should throw because we don't allow item lists to be concatenated // with other strings. bool throwAway; items = buckets[0].Expander.ExpandSingleItemVectorExpressionIntoItems("@(file)$(unitests)", factory, ExpanderOptions.ExpandItems, false /* no nulls */, out throwAway, MockElementLocation.Instance); } catch (InvalidProjectFileException ex) { // check we don't lose error codes from IPFE's during build Assert.AreEqual(ex.ErrorCode, "MSB4012"); invalidProjectFileExceptions++; } // We do allow separators in item vectors, this results in an item group with a single flattened item items = buckets[0].Expander.ExpandIntoItemsLeaveEscaped("@(file, ',')", factory, ExpanderOptions.ExpandItems, MockElementLocation.Instance); Assert.IsNotNull(items); Assert.AreEqual(1, items.Count); Assert.AreEqual("a.foo", items[0].EvaluatedInclude); Assert.AreEqual(1, invalidProjectFileExceptions); }
public void GetBuckets() { ProjectInstance project = ProjectHelpers.CreateEmptyProjectInstance(); List <string> parameters = new List <string>(); parameters.Add("@(File);$(unittests)"); parameters.Add("$(obj)\\%(Filename).ext"); parameters.Add("@(File->'%(extension)')"); // attributes in transforms don't affect batching ItemDictionary <ProjectItemInstance> itemsByType = new ItemDictionary <ProjectItemInstance>(); IList <ProjectItemInstance> items = new List <ProjectItemInstance>(); items.Add(new ProjectItemInstance(project, "File", "a.foo", project.FullPath)); items.Add(new ProjectItemInstance(project, "File", "b.foo", project.FullPath)); items.Add(new ProjectItemInstance(project, "File", "c.foo", project.FullPath)); items.Add(new ProjectItemInstance(project, "File", "d.foo", project.FullPath)); items.Add(new ProjectItemInstance(project, "File", "e.foo", project.FullPath)); itemsByType.ImportItems(items); items = new List <ProjectItemInstance>(); items.Add(new ProjectItemInstance(project, "Doc", "a.doc", project.FullPath)); items.Add(new ProjectItemInstance(project, "Doc", "b.doc", project.FullPath)); items.Add(new ProjectItemInstance(project, "Doc", "c.doc", project.FullPath)); items.Add(new ProjectItemInstance(project, "Doc", "d.doc", project.FullPath)); items.Add(new ProjectItemInstance(project, "Doc", "e.doc", project.FullPath)); itemsByType.ImportItems(items); PropertyDictionary <ProjectPropertyInstance> properties = new PropertyDictionary <ProjectPropertyInstance>(); properties.Set(ProjectPropertyInstance.Create("UnitTests", "unittests.foo")); properties.Set(ProjectPropertyInstance.Create("OBJ", "obj")); List <ItemBucket> buckets = BatchingEngine.PrepareBatchingBuckets(parameters, CreateLookup(itemsByType, properties), MockElementLocation.Instance); Assert.Equal(5, buckets.Count); foreach (ItemBucket bucket in buckets) { // non-batching data -- same for all buckets XmlAttribute tempXmlAttribute = (new XmlDocument()).CreateAttribute("attrib"); tempXmlAttribute.Value = "'$(Obj)'=='obj'"; Assert.True(ConditionEvaluator.EvaluateCondition(tempXmlAttribute.Value, ParserOptions.AllowAll, bucket.Expander, ExpanderOptions.ExpandAll, Directory.GetCurrentDirectory(), MockElementLocation.Instance, null, new BuildEventContext(1, 2, 3, 4))); Assert.Equal("a.doc;b.doc;c.doc;d.doc;e.doc", bucket.Expander.ExpandIntoStringAndUnescape("@(doc)", ExpanderOptions.ExpandItems, MockElementLocation.Instance)); Assert.Equal("unittests.foo", bucket.Expander.ExpandIntoStringAndUnescape("$(bogus)$(UNITTESTS)", ExpanderOptions.ExpandPropertiesAndMetadata, MockElementLocation.Instance)); } Assert.Equal("a.foo", buckets[0].Expander.ExpandIntoStringAndUnescape("@(File)", ExpanderOptions.ExpandItems, MockElementLocation.Instance)); Assert.Equal(".foo", buckets[0].Expander.ExpandIntoStringAndUnescape("@(File->'%(Extension)')", ExpanderOptions.ExpandItems, MockElementLocation.Instance)); Assert.Equal("obj\\a.ext", buckets[0].Expander.ExpandIntoStringAndUnescape("$(obj)\\%(Filename).ext", ExpanderOptions.ExpandPropertiesAndMetadata, MockElementLocation.Instance)); // we weren't batching on this attribute, so it has no value Assert.Equal(String.Empty, buckets[0].Expander.ExpandIntoStringAndUnescape("%(Extension)", ExpanderOptions.ExpandAll, MockElementLocation.Instance)); ProjectItemInstanceFactory factory = new ProjectItemInstanceFactory(project, "i"); items = buckets[0].Expander.ExpandIntoItemsLeaveEscaped("@(file)", factory, ExpanderOptions.ExpandItems, MockElementLocation.Instance); Assert.NotNull(items); Assert.Equal(1, items.Count); int invalidProjectFileExceptions = 0; try { // This should throw because we don't allow item lists to be concatenated // with other strings. bool throwAway; items = buckets[0].Expander.ExpandSingleItemVectorExpressionIntoItems("@(file)$(unitests)", factory, ExpanderOptions.ExpandItems, false /* no nulls */, out throwAway, MockElementLocation.Instance); } catch (InvalidProjectFileException ex) { // check we don't lose error codes from IPFE's during build Assert.Equal(ex.ErrorCode, "MSB4012"); invalidProjectFileExceptions++; } // We do allow separators in item vectors, this results in an item group with a single flattened item items = buckets[0].Expander.ExpandIntoItemsLeaveEscaped("@(file, ',')", factory, ExpanderOptions.ExpandItems, MockElementLocation.Instance); Assert.NotNull(items); Assert.Equal(1, items.Count); Assert.Equal("a.foo", items[0].EvaluatedInclude); Assert.Equal(1, invalidProjectFileExceptions); }
/// <summary> /// Uses the given task output specification to (statically) infer the task's outputs. /// </summary> /// <param name="lookup">The lookup</param> /// <param name="taskOutputSpecification">The task output specification</param> /// <param name="taskParameterName">The task parameter name</param> /// <param name="itemName">can be null</param> /// <param name="propertyName">can be null</param> /// <param name="bucket">The bucket for the batch.</param> private void InferTaskOutputs ( Lookup lookup, ProjectTaskInstanceChild taskOutputSpecification, string taskParameterName, string itemName, string propertyName, ItemBucket bucket ) { string taskParameterAttribute = _taskNode.GetParameter(taskParameterName); if (null != taskParameterAttribute) { ProjectTaskOutputItemInstance taskItemInstance = taskOutputSpecification as ProjectTaskOutputItemInstance; if (taskItemInstance != null) { // This is an output item. // 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. IList<string> outputItemSpecs = bucket.Expander.ExpandIntoStringListLeaveEscaped(taskParameterAttribute, ExpanderOptions.ExpandPropertiesAndMetadata, taskItemInstance.TaskParameterLocation); ProjectItemInstanceFactory itemFactory = new ProjectItemInstanceFactory(_buildRequestEntry.RequestConfiguration.Project, itemName); foreach (string outputItemSpec in outputItemSpecs) { ICollection<ProjectItemInstance> items = bucket.Expander.ExpandIntoItemsLeaveEscaped(outputItemSpec, itemFactory, ExpanderOptions.ExpandItems, taskItemInstance.TaskParameterLocation); lookup.AddNewItemsOfItemType(itemName, items); } } else { // This is an output property. ProjectTaskOutputPropertyInstance taskPropertyInstance = (ProjectTaskOutputPropertyInstance)taskOutputSpecification; string taskParameterValue = bucket.Expander.ExpandIntoStringAndUnescape(taskParameterAttribute, ExpanderOptions.ExpandAll, taskPropertyInstance.TaskParameterLocation); if (!String.IsNullOrEmpty(taskParameterValue)) { lookup.SetProperty(ProjectPropertyInstance.Create(propertyName, taskParameterValue, taskPropertyInstance.TaskParameterLocation, _buildRequestEntry.RequestConfiguration.Project.IsImmutable)); } } } }