Skip to content

Commit 49a6fd4

Browse files
CopilotJanProvaznikYuliiaKovalova
authored
Fix unhandled exception in /getItem and /getTargetResult for items with illegal path characters (#12841)
### Context `/getItem` and `/getTargetResult` throw an unhandled `InvalidOperationException` when serializing items whose item specs contain illegal path characters (e.g., compiler command line flags from ClangTidy's `GetCompileCommands` target). The JSON output formatter iterates over all metadata names including built-in metadata like `FullPath`, `Directory`, etc. When `GetMetadata()` is called on these, it attempts path computation which fails for non-path item specs. ### Changes Made - Wrapped metadata retrieval in `JsonOutputFormatter.cs` with try-catch for `InvalidOperationException` - Added `TryGetMetadata` and `TryGetMetadataValue` helper methods that return empty string on failure - Applied fix to all three affected methods: `AddTargetResultsInJsonFormat`, `AddItemInstancesInJsonFormat`, `AddItemsInJsonFormat` - Exception handling catches all `InvalidOperationException` instances to ensure compatibility across all locales (error messages are localized) ### Testing Added `GetTargetResultWithIllegalPathCharacters` test that verifies both `/getTargetResult` and `/getItem` succeed with items containing compiler flags as item specs. ### Notes On Linux, the test shows the path-like metadata is still computed (since `/` is valid in paths). On Windows, these would return empty strings. The key fix is preventing the unhandled exception crash. The exception handling is intentionally broad (catching all `InvalidOperationException` without message filtering) to ensure the fix works correctly in all locales, as error messages from MSBuild are localized and checking for specific English text would fail in non-English environments. <!-- START COPILOT CODING AGENT SUFFIX --> <details> <summary>Original prompt</summary> ---- *This section details on the original issue you should resolve* <issue_title>[Unhandled Exception]: /getItem and /getTargetResult fail for target GetCompileCommands</issue_title> <issue_description>### Issue Description There is an uncaught exception when trying to access certain information from the GetCompileCommands target (from ClangTidy), in particular `/getTargetResult:GetCompileCommands` and `/getTargetResult:GetCompileCommands` ### Steps to Reproduce [tidytest.zip](https://github.com/user-attachments/files/22645381/tidytest.zip) Run either of the following commands (You could also run with target `/t:ClangTidy` for the same result) ``` msbuild tidytest.vcxproj /t:GetCompileCommands /getTargetResult:GetCompileCommands msbuild tidytest.vcxproj /t:GetCompileCommands /getItem:CompileCommands ``` I expect to get a json formatted target result or item. ### Actual Behavior I get the following error when building ``` C:\Users\rp0656\source\repos\tidytest>msbuild tidytest.vcxproj /t:GetCompileCommands /getTargetResult:GetCompileCommands MSBUILD : error MSB1025: An internal failure occurred while running MSBuild. System.InvalidOperationException: The item metadata "%(FullPath)" cannot be applied to the path "/c /nologo /W3 /WX- /diagnostics:column /Od /D _DEBUG /D _CONSOLE /D _UNICODE /D UNICODE /EHsc /RTC1 /MDd /GS /fp:precise /permissive- /Fa"tidytest\x64\Debug\\" /Fo"tidytest\x64\Debug\\" /Gd --target=amd64-pc-windows-msvc /TP". Illegal characters in path. at Microsoft.Build.Shared.ErrorUtilities.ThrowInvalidOperation(String resourceName, Object[] args) at Microsoft.Build.Shared.FileUtilities.ItemSpecModifiers.GetItemSpecModifier(String currentDirectory, String itemSpec, String definingProjectEscaped, String modifier, String& fullPath) at Microsoft.Build.Evaluation.BuiltInMetadata.GetMetadataValueEscaped(String currentDirectory, String evaluatedIncludeBeforeWildcardExpansionEscaped, String evaluatedIncludeEscaped, String definingProjectEscaped, String name, String& fullPath) at Microsoft.Build.Execution.ProjectItemInstance.TaskItem.GetBuiltInMetadataEscaped(String name) at Microsoft.Build.Execution.ProjectItemInstance.TaskItem.GetMetadataEscaped(String metadataName) at Microsoft.Build.Execution.ProjectItemInstance.TaskItem.GetMetadata(String metadataName) at Microsoft.Build.CommandLine.JsonOutputFormatter.AddTargetResultsInJsonFormat(String[] targetNames, BuildResult result) at Microsoft.Build.CommandLine.MSBuildApp.OutputBuildInformationInJson(BuildResult result, String[] getProperty, String[] getItem, String[] getTargetResult, ILogger[] loggers, ExitType exitType, TextWriter outputStream) at Microsoft.Build.CommandLine.MSBuildApp.Execute(String commandLine) Unhandled Exception: System.InvalidOperationException: The item metadata "%(FullPath)" cannot be applied to the path "/c /nologo /W3 /WX- /diagnostics:column /Od /D _DEBUG /D _CONSOLE /D _UNICODE /D UNICODE /EHsc /RTC1 /MDd /GS /fp:precise /permissive- /Fa"tidytest\x64\Debug\\" /Fo"tidytest\x64\Debug\\" /Gd --target=amd64-pc-windows-msvc /TP". Illegal characters in path. at Microsoft.Build.Shared.ErrorUtilities.ThrowInvalidOperation(String resourceName, Object[] args) at Microsoft.Build.Shared.FileUtilities.ItemSpecModifiers.GetItemSpecModifier(String currentDirectory, String itemSpec, String definingProjectEscaped, String modifier, String& fullPath) at Microsoft.Build.Evaluation.BuiltInMetadata.GetMetadataValueEscaped(String currentDirectory, String evaluatedIncludeBeforeWildcardExpansionEscaped, String evaluatedIncludeEscaped, String definingProjectEscaped, String name, String& fullPath) at Microsoft.Build.Execution.ProjectItemInstance.TaskItem.GetBuiltInMetadataEscaped(String name) at Microsoft.Build.Execution.ProjectItemInstance.TaskItem.GetMetadataEscaped(String metadataName) at Microsoft.Build.Execution.ProjectItemInstance.TaskItem.GetMetadata(String metadataName) at Microsoft.Build.CommandLine.JsonOutputFormatter.AddTargetResultsInJsonFormat(String[] targetNames, BuildResult result) at Microsoft.Build.CommandLine.MSBuildApp.OutputBuildInformationInJson(BuildResult result, String[] getProperty, String[] getItem, String[] getTargetResult, ILogger[] loggers, ExitType exitType, TextWriter outputStream) at Microsoft.Build.CommandLine.MSBuildApp.Execute(String commandLine) at Microsoft.Build.CommandLine.MSBuildApp.Main() ``` ### Analysis The error suggests that it is trying to interpret the flags of the compile commands as a path, which causes an error when it can't be resolved. The function `%(FullPath)` is used, though it is not clear to me exactly where, but my best guess is it used on a variable that is supposed to just be the path to `cl`, but has been extended with flags. See the clang tidy targets file at something similar to `C:\Program Files\Microsoft Visual Studio\2022\Enterprise\MSBuild\Microsoft\VC\v170\Microsoft.Cpp.ClangTidy.targets` ### Versions & Co... </details> - Fixes #12589 <!-- START COPILOT CODING AGENT TIPS --> --- ✨ Let Copilot coding agent [set things up for you](https://github.com/dotnet/msbuild/issues/new?title=✨+Set+up+Copilot+instructions&body=Configure%20instructions%20for%20this%20repository%20as%20documented%20in%20%5BBest%20practices%20for%20Copilot%20coding%20agent%20in%20your%20repository%5D%28https://gh.io/copilot-coding-agent-tips%29%2E%0A%0A%3COnboard%20this%20repo%3E&assignees=copilot) — coding agent works faster and does higher quality work when set up for your repo. --------- Co-authored-by: copilot-swe-agent[bot] <[email protected]> Co-authored-by: JanProvaznik <[email protected]> Co-authored-by: Jan Provazník <[email protected]> Co-authored-by: YuliiaKovalova <[email protected]>
1 parent 3a603fc commit 49a6fd4

File tree

2 files changed

+92
-3
lines changed

2 files changed

+92
-3
lines changed

src/MSBuild.UnitTests/XMake_Tests.cs

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -888,6 +888,38 @@ public void GetStarOutputsToFileIfRequested(string extraSwitch, string result)
888888
result.ShouldContain("MSB1068");
889889
}
890890

891+
/// <summary>
892+
/// Regression test for issue where getTargetResult/getItem would throw an unhandled exception
893+
/// when the item spec contained illegal path characters (e.g. compiler command line flags).
894+
/// </summary>
895+
[Theory]
896+
[InlineData("-getTargetResult:GetCompileCommands", "\"Result\": \"Success\"")]
897+
[InlineData("-getItem:CompileCommands", "\"Identity\":")]
898+
public void GetTargetResultWithIllegalPathCharacters(string extraSwitch, string expectedContent)
899+
{
900+
using TestEnvironment env = TestEnvironment.Create();
901+
// Create a project that mimics the ClangTidy target - it outputs items with illegal path characters
902+
// (compiler command line flags) as the item spec.
903+
TransientTestFile project = env.CreateFile("testProject.csproj", @"
904+
<Project>
905+
<ItemGroup>
906+
<CompileCommands Include=""/c /nologo /W3 /WX- /diagnostics:column /Od /D _DEBUG"" />
907+
</ItemGroup>
908+
909+
<Target Name=""GetCompileCommands"" Returns=""@(CompileCommands)"">
910+
<ItemGroup>
911+
<CompileCommands Include=""/c /fp:precise /permissive- /Fa&quot;Debug\\&quot; /Fo&quot;Debug\\&quot; /Gd --target=amd64-pc-windows-msvc /TP"" />
912+
</ItemGroup>
913+
</Target>
914+
</Project>
915+
");
916+
string results = RunnerUtilities.ExecMSBuild($" {project.Path} /t:GetCompileCommands {extraSwitch}", out bool success);
917+
// The build should succeed instead of throwing an unhandled exception
918+
success.ShouldBeTrue(results);
919+
// The output should contain the expected content
920+
results.ShouldContain(expectedContent);
921+
}
922+
891923
[Theory]
892924
[InlineData(true)]
893925
[InlineData(false)]

src/MSBuild/JsonOutputFormatter.cs

Lines changed: 60 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -67,7 +67,7 @@ internal void AddItemInstancesInJsonFormat(string[] itemNames, ProjectInstance p
6767
continue;
6868
}
6969

70-
jsonItem[metadatumName] = item.GetMetadataValue(metadatumName);
70+
jsonItem[metadatumName] = TryGetMetadataValue(item, metadatumName);
7171
}
7272

7373
itemArray.Add(jsonItem);
@@ -108,7 +108,7 @@ internal void AddItemsInJsonFormat(string[] itemNames, Project project)
108108
continue;
109109
}
110110

111-
jsonItem[metadatumName] = item.GetMetadataValue(metadatumName);
111+
jsonItem[metadatumName] = TryGetMetadataValue(item, metadatumName);
112112
}
113113

114114
itemArray.Add(jsonItem);
@@ -147,7 +147,7 @@ internal void AddTargetResultsInJsonFormat(string[] targetNames, BuildResult res
147147
continue;
148148
}
149149

150-
jsonItem[metadatumName] = item.GetMetadata(metadatumName);
150+
jsonItem[metadatumName] = TryGetMetadata(item, metadatumName);
151151
}
152152

153153
outputArray.Add(jsonItem);
@@ -159,5 +159,62 @@ internal void AddTargetResultsInJsonFormat(string[] targetNames, BuildResult res
159159

160160
_topLevelNode["TargetResults"] = targetResultsNode;
161161
}
162+
163+
/// <summary>
164+
/// Attempts to get metadata from an ITaskItem. If the metadata is a built-in metadata
165+
/// (like FullPath, Directory, etc.) and the item spec contains illegal path characters,
166+
/// this will catch the InvalidOperationException and return an empty string.
167+
/// </summary>
168+
private static string TryGetMetadata(ITaskItem item, string metadataName)
169+
{
170+
try
171+
{
172+
return item.GetMetadata(metadataName);
173+
}
174+
catch (InvalidOperationException)
175+
{
176+
// Built-in metadata like FullPath, Directory, etc. require path computation.
177+
// If the item spec contains illegal path characters, return empty string.
178+
return string.Empty;
179+
}
180+
}
181+
182+
/// <summary>
183+
/// Attempts to get metadata value from a ProjectItemInstance. If the metadata is a built-in metadata
184+
/// (like FullPath, Directory, etc.) and the item spec contains illegal path characters,
185+
/// this will catch the InvalidOperationException and return an empty string.
186+
/// </summary>
187+
private static string TryGetMetadataValue(ProjectItemInstance item, string metadataName)
188+
{
189+
try
190+
{
191+
return item.GetMetadataValue(metadataName);
192+
}
193+
catch (InvalidOperationException)
194+
{
195+
// Built-in metadata like FullPath, Directory, etc. require path computation.
196+
// If the item spec contains illegal path characters, return empty string.
197+
return string.Empty;
198+
}
199+
}
200+
201+
/// <summary>
202+
/// Attempts to get metadata value from a ProjectItem. If the metadata is a built-in metadata
203+
/// (like FullPath, Directory, etc.) and the item spec contains illegal path characters,
204+
/// this will catch the InvalidOperationException and return an empty string.
205+
/// </summary>
206+
private static string TryGetMetadataValue(ProjectItem item, string metadataName)
207+
{
208+
try
209+
{
210+
return item.GetMetadataValue(metadataName);
211+
}
212+
catch (InvalidOperationException)
213+
{
214+
// Built-in metadata like FullPath, Directory, etc. require path computation.
215+
// If the item spec contains illegal path characters, return empty string.
216+
return string.Empty;
217+
}
218+
}
162219
}
163220
}

0 commit comments

Comments
 (0)