Use BCL APIs to create symlinks and fix error reporting.#14143
Use BCL APIs to create symlinks and fix error reporting.#14143teo-tsirpanis wants to merge 5 commits into
Conversation
There was a problem hiding this comment.
Pull request overview
This PR updates MSBuild’s symlink creation to use the .NET BCL (System.IO.File.CreateSymbolicLink) when building for NET, and adjusts the Copy task’s link-creation plumbing to better propagate link-creation error text while removing an unused parameter.
Changes:
- Use
File.CreateSymbolicLinkinMicrosoft.Build.Framework.NativeMethods.MakeSymbolicLinkunder#if NET. - Refactor
Copy.TryCopyViaLinkto use aref string-based delegate for error propagation and remove an unused importance parameter.
Reviewed changes
Copilot reviewed 2 out of 2 changed files in this pull request and generated 3 comments.
| File | Description |
|---|---|
| src/Tasks/Copy.cs | Refactors link-copy helper to use a ref error message delegate and updates call sites. |
| src/Framework/NativeMethods.cs | Switches symlink creation to BCL API for NET builds and maps exceptions to error text. |
|
/review |
|
✅ Expert Code Review (command) completed successfully! Caution agentic threat detected Performance & Allocation — LGTM No concrete regression found:
|
| else | ||
| #endif | ||
| { | ||
| symbolicLinkCreated = symlink(existingFileName, newFileName) == 0; | ||
| errorMessage = symbolicLinkCreated ? null : Marshal.GetLastWin32Error().ToString(); | ||
| } |
There was a problem hiding this comment.
Do we need the path for .NET Framework on non-Windows?
|
Caution agentic threat detected 🔴 MAJOR — Test Coverage GapThe PR fixes a bug (error messages from failed link creation were silently not propagated) and introduces a new Gap 1 — No test invokes Gap 2 — Symlink failure error message in the The Windows hard-link tests ( Suggested additions:
|
|
Caution agentic threat detected
|
There was a problem hiding this comment.
Caution
agentic threat detected
Threat detection flagged this output in warn mode. Manual review is REQUIRED before any follow-up automation.
Error Message Quality — ISSUE
One concrete issue introduced by this PR: the #if NET switch to File.CreateSymbolicLink + e.Message makes the pre-existing Copy.NonWindowsLinkErrorMessage format string inaccurate in two ways ("symlink()" is the wrong function name; "error code" is the wrong label for a human-readable description). See inline comment.
Generated by Expert Code Review (command) for #14143 · 2.6K AIC · ⊞ 31.9K
Comment /review to run again
Comments that could not be inline-anchored
src/Tasks/Copy.cs:362
Error Message Quality — ISSUE introduced by this PR
The #if NET path in NativeMethods.MakeSymbolicLink now calls File.CreateSymbolicLink and surfaces e.Message (e.g. "Permission denied"), but line 362 still formats the message with:
Log.FormatResourceString("Copy.NonWindowsLinkErrorMessage", "symlink()", errorMessage)Copy.NonWindowsLinkErrorMessage reads: "The {0} library call failed with the following error code: {1}."
On .NET non-Windows a failure now prod…
src/Tasks/Copy.cs:400
Naming Precision — NIT
The Proc suffix is unused anywhere else in this codebase for delegate declarations. The pervasive convention here is the Delegate suffix (e.g., NodeShutdownDelegate, RequestCompleteDelegate, BuildRequestEntryStateChangedDelegate). Consider renaming to CreateLinkDelegate to stay consistent.
src/Framework/NativeMethods.cs:1
Idiomatic C# Patterns — ISSUE
catch (Exception e) swallows every exception kind, including OutOfMemoryException and StackOverflowException. The established pattern in Copy.cs uses a filtered catch:
catch (Exception e) when (ExceptionHandling.IsIoRelatedException(e))(See Copy.cs lines 995 and 1025.) A File.CreateSymbolicLink failure will always be IO-related, so the filter fits and keeps the codebase consistent while avoiding silently masking unexpected exceptio…
src/Framework/NativeMethods.cs:819
[MODERATE] Correctness & Edge Cases — overly broad exception catch
catch (Exception e) is too broad: it silently swallows fatal exceptions (OutOfMemoryException, ThreadAbortException, ExecutionEngineException) that should propagate and terminate the process.
File.CreateSymbolicLink fails in normal operation with IOException (file exists, path too long) or UnauthorizedAccessException (missing privilege / developer mode not enabled on Windows). These are the only exception typ…
src/Tasks/Copy.cs:400
[NIT] Naming Precision — Proc suffix is not idiomatic in C#
CreateLinkProc uses the Proc suffix, which originates from Win32 / WNDPROC conventions and is rarely seen in modern .NET codebases.
Since Func<> does not support ref parameters, a custom delegate is correct here. The idiomatic convention is to use the Delegate suffix or describe the shape without a suffix:
// Option A: explicit 'delegate' suffix
private delegate bool CreateLinkDelegate(string source, string…
</details>
<details><summary>src/Framework/NativeMethods.cs:819</summary>
**⚠️ MODERATE — Idiomatic C# / Correctness**
Bare `catch (Exception e)` silently swallows fatal exceptions (`OutOfMemoryException`, `AccessViolationException`, etc.). The established pattern throughout this assembly is:
```csharp
catch (Exception e) when (ExceptionHandling.IsIoRelatedException(e))ExceptionHandling lives in src/Framework/ (same assembly) and this exact pattern is used in FileMatcher.cs, FileUtilities.cs, FrameworkLocationHelper.cs, and others. The when filter…
src/Tasks/Copy.cs:400
NIT — Naming
CreateLinkProc is the only delegate type in the entire Tasks assembly using a Proc suffix (Win32 API nomenclature). No single naming convention dominates in the codebase, but the suffix is a clear outlier. Consider CreateLinkDelegate or simply dropping the suffix (e.g., CreateLink) to stay closer to existing patterns in Copy.cs and neighboring files.
Fixes #
Context
MSBuild has used platform-specific APIs to create symbolic links. However, since .NET 6 there is an API in
System.IO.Filefor this purpose. This PR makes use of this API when available. It also fixes a bug in theCopytask where the error message was not getting propagated, and removed an unused parameter.Changes Made
Testing
Existing testing coverage.
Notes
A BCL API to create hard links has been added in .NET 11. We can similarly use it when MSBuild targets this framework.