diff --git a/src/Compilers/Core/MSBuildTask/ArgsTaskItem.cs b/src/Compilers/Core/MSBuildTask/ArgsTaskItem.cs new file mode 100644 index 0000000000000..dd56beb51e61b --- /dev/null +++ b/src/Compilers/Core/MSBuildTask/ArgsTaskItem.cs @@ -0,0 +1,98 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +using System; +using System.Collections; +using System.Collections.Generic; +using Microsoft.Build.Framework; + +namespace Microsoft.CodeAnalysis.BuildTasks +{ + internal sealed class ArgsTaskItem : ITaskItem + { + // This list is taken from https://github.com/dotnet/msbuild/blob/291a8108761ed347562228f2f8f25477996a5a93/src/Shared/Modifiers.cs#L36-L70 + private static readonly string[] WellKnownItemSpecMetadataNames = + [ + "FullPath", + "RootDir", + "Filename", + "Extension", + "RelativeDir", + "Directory", + "RecursiveDir", + "Identity", + "ModifiedTime", + "CreatedTime", + "AccessedTime", + "DefiningProjectFullPath", + "DefiningProjectDirectory", + "DefiningProjectName", + "DefiningProjectExtension", + ]; + + private readonly Dictionary _metadata = new Dictionary(StringComparer.OrdinalIgnoreCase); + + public ArgsTaskItem(string itemSpec) + { + ItemSpec = itemSpec; + } + + public string ItemSpec { get; set; } + + // Implementation notes that we should include the built-in metadata names as well as our custom ones. + public ICollection MetadataNames + { + get + { + var clone = new List(_metadata.Keys); + clone.AddRange(WellKnownItemSpecMetadataNames); + return clone; + } + } + + // Implementation notes that we should include the built-in metadata names as well as our custom ones. + public int MetadataCount => _metadata.Count + WellKnownItemSpecMetadataNames.Length; + + public IDictionary CloneCustomMetadata() + { + return new Dictionary(_metadata, StringComparer.OrdinalIgnoreCase); + } + + public void CopyMetadataTo(ITaskItem destinationItem) + { + // Implementation notes that we should not overwrite existing metadata on the destination. + var destinationMetadataNames = new HashSet(StringComparer.OrdinalIgnoreCase); + foreach (var name in destinationItem.MetadataNames) + { + destinationMetadataNames.Add((string)name); + } + + foreach (var metadataName in _metadata.Keys) + { + if (destinationMetadataNames.Contains(metadataName)) + { + continue; + } + + var metadataValue = _metadata[metadataName]; + destinationItem.SetMetadata(metadataName, metadataValue); + } + } + + public string GetMetadata(string metadataName) + { + return _metadata[metadataName]; + } + + public void RemoveMetadata(string metadataName) + { + _metadata.Remove(metadataName); + } + + public void SetMetadata(string metadataName, string metadataValue) + { + _metadata[metadataName] = metadataValue; + } + } +} diff --git a/src/Compilers/Core/MSBuildTask/ManagedToolTask.cs b/src/Compilers/Core/MSBuildTask/ManagedToolTask.cs index c12dfc3eb08cf..b18e12bf4a5c1 100644 --- a/src/Compilers/Core/MSBuildTask/ManagedToolTask.cs +++ b/src/Compilers/Core/MSBuildTask/ManagedToolTask.cs @@ -159,7 +159,7 @@ protected static ITaskItem[] GenerateCommandLineArgsTaskItems(List comma var items = new ITaskItem[commandLineArgs.Count]; for (var i = 0; i < commandLineArgs.Count; i++) { - items[i] = new TaskItem(commandLineArgs[i]); + items[i] = new ArgsTaskItem(commandLineArgs[i]); } return items; diff --git a/src/Compilers/Core/MSBuildTaskTests/VbcTests.cs b/src/Compilers/Core/MSBuildTaskTests/VbcTests.cs index 5af9cfbc241f2..c2990c3c31ea6 100644 --- a/src/Compilers/Core/MSBuildTaskTests/VbcTests.cs +++ b/src/Compilers/Core/MSBuildTaskTests/VbcTests.cs @@ -191,6 +191,19 @@ public void DefineConstantsSimple() test("D1 D2"); } + [Fact] + [WorkItem(72014, "https://github.com/dotnet/roslyn/issues/72014")] + public void DefineConstantsWithEscaping() + { + var vbc = new Vbc(); + vbc.DefineConstants = "CONFIG=\"DEBUG\""; + vbc.Sources = MSBuildUtil.CreateTaskItems("test.vb"); + var responseFileContents = vbc.GenerateResponseFileContents(); + var argTaskItems = vbc.GenerateCommandLineArgsTaskItems(responseFileContents); + var defineTaskItem = Assert.Single(argTaskItems, item => item.ItemSpec.StartsWith("/define:")); + Assert.Equal("/define:\"CONFIG=\\\"DEBUG\\\"\"", defineTaskItem.ItemSpec); + } + [Fact] public void Features() {