From e39303ce2000693540561b0eb53b1776b0677cd5 Mon Sep 17 00:00:00 2001 From: Tanner Gooding Date: Thu, 16 Jul 2026 12:08:20 -0700 Subject: [PATCH 1/3] Skip TemplateNameTest and DependentTemplateBaseTest on win-arm64 These tests exercise native libClangSharp shims that are broken in the win-arm64 22.1.8.2 prebuilt, causing Debug.Fail / NRE when the native lib returns CX_TNK_Invalid for template names. The existing DeclTest guard already covers other shim-dependent tests; extend the same pattern to TypeTest and the baseline generator tests. See https://github.com/dotnet/ClangSharp/issues/806. Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com> --- .../Baseline/StructDeclarationTest.cs | 6 ++++++ tests/ClangSharp.UnitTests/TypeTest.cs | 6 ++++++ 2 files changed, 12 insertions(+) diff --git a/tests/ClangSharp.PInvokeGenerator.UnitTests/Baseline/StructDeclarationTest.cs b/tests/ClangSharp.PInvokeGenerator.UnitTests/Baseline/StructDeclarationTest.cs index 3612d285..8da44024 100644 --- a/tests/ClangSharp.PInvokeGenerator.UnitTests/Baseline/StructDeclarationTest.cs +++ b/tests/ClangSharp.PInvokeGenerator.UnitTests/Baseline/StructDeclarationTest.cs @@ -2,6 +2,7 @@ using System; using System.Collections.Generic; +using System.Runtime.InteropServices; using System.Threading.Tasks; using NUnit.Framework; @@ -169,6 +170,11 @@ typedef struct [Test] public Task DependentTemplateBaseTest() { + if (OperatingSystem.IsWindows() && (RuntimeInformation.ProcessArchitecture == Architecture.Arm64)) + { + Assert.Ignore("The win-arm64 libClangSharp prebuilt ships broken clangsharp shims. Remove this guard once the native lib is rebuilt. See https://github.com/dotnet/ClangSharp/issues/806."); + } + // A dependent template-specialization base (`Base` within a class template) previously // crashed `GetRecordDecl` with an `InvalidCastException` when both template bindings and // empty-record exclusion were enabled, as the base resolves to a `ClassTemplateDecl` diff --git a/tests/ClangSharp.UnitTests/TypeTest.cs b/tests/ClangSharp.UnitTests/TypeTest.cs index 42973186..ef03033c 100644 --- a/tests/ClangSharp.UnitTests/TypeTest.cs +++ b/tests/ClangSharp.UnitTests/TypeTest.cs @@ -2,6 +2,7 @@ using System; using System.Linq; +using System.Runtime.InteropServices; using ClangSharp.Interop; using NUnit.Framework; @@ -170,6 +171,11 @@ struct S { int field; void method(); }; [Test] public void TemplateNameTest() { + if (OperatingSystem.IsWindows() && (RuntimeInformation.ProcessArchitecture == Architecture.Arm64)) + { + Assert.Ignore("The win-arm64 libClangSharp prebuilt ships broken clangsharp shims. Remove this guard once the native lib is rebuilt. See https://github.com/dotnet/ClangSharp/issues/806."); + } + var inputContents = """ template struct Tmpl { T value; }; using SpecT = Tmpl; From 85fbada03b2f9f37d1668e136d6d85c3e8bd80b4 Mon Sep 17 00:00:00 2001 From: Tanner Gooding Date: Thu, 16 Jul 2026 14:35:50 -0700 Subject: [PATCH 2/3] Build win-arm64 libClangSharp with clang-cl to match the LLVM release ABI The official LLVM release binaries are clang-built. On Arm64, clang and MSVC disagree on the record layout of over-aligned non-POD base classes such as clang::TemplateSpecializationType (clang rounds the over-aligned base up to a full slot; MSVC-Arm64 packs the next base into its tail padding), so an MSVC-built shim reads members of clang-built types at the wrong offset and returns garbage (e.g. getTemplateName yielding CX_TNK_Invalid). win-x64 layouts agree between the two compilers, so only win-arm64 is affected. Build the win-arm64 shim with the release's own clang-cl (via Ninja) so its layouts match the libs it links against. Generalize the diaguids.lib repoint in libClangSharp/CMakeLists.txt to also apply under Ninja + clang-cl, sourcing the Visual Studio root and target arch from VSINSTALLDIR and the compiler arch id when the VS-generator variables are absent. Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com> --- README.md | 2 + scripts/build.ps1 | 67 +++++++++++++++++++++++++--- sources/libClangSharp/CMakeLists.txt | 39 +++++++++++----- 3 files changed, 91 insertions(+), 17 deletions(-) diff --git a/README.md b/README.md index 698d064c..9b02d739 100644 --- a/README.md +++ b/README.md @@ -150,6 +150,8 @@ The change-detection and version-verification the workflow gates on live in the The staged binary is written to `artifacts/native//`. The LLVM release distribution bundles both the prebuilt `libclang` and the `lib/cmake/{llvm,clang}` config, headers, and import libraries used as `PATH_TO_LLVM` when building `libClangSharp`, so no from-source LLVM build is required (the manual [Building Native](#building-native) steps remain the way to reproduce a build locally). Because lifting `libclang` is just unpacking prebuilt binaries, the workflow does all five runtimes on a single Windows runner (its bundled `bsdtar` handles `.tar.xz`); `libClangSharp` is compiled per-runtime on native runners. +The `win-arm64` runtime is compiled with the LLVM release's own `clang-cl` (via `-G Ninja`) rather than MSVC. The official LLVM binaries are clang-built, and on Arm64 clang and MSVC disagree on the record layout of over-aligned non-POD base classes (e.g. `clang::TemplateSpecializationType`), so an MSVC-built shim reads members of clang-built types at the wrong offset. The other runtimes are unaffected (`win-x64` layouts agree between the two, and the Unix runtimes already use clang). + The jobs run when: * **libclang** — the tracked LLVM major/minor version changes, or the workflow is dispatched manually with the `libclang` input set. diff --git a/scripts/build.ps1 b/scripts/build.ps1 index 7177ba59..a0180ab2 100644 --- a/scripts/build.ps1 +++ b/scripts/build.ps1 @@ -162,17 +162,72 @@ function Extract-Libclang([string] $runtime, [string] $source, [string] $destina Copy-Item -Path $lib.FullName -Destination (Join-Path -Path $destination -ChildPath $name) } -function Build-Libclangsharp([string] $runtime, [string] $source, [string] $destination) { - $arch = switch ($runtime) { - "win-x64" { "x64" } - "win-arm64" { "ARM64" } - default { throw "'$runtime' cannot build libClangSharp on Windows; use build.sh on the matching runner" } +function Get-VisualStudioInstallPath() { + $vswhere = Join-Path -Path ${env:ProgramFiles(x86)} -ChildPath "Microsoft Visual Studio\Installer\vswhere.exe" + + if (-not (Test-Path -Path $vswhere)) { + throw "'vswhere.exe' was not found; a Visual Studio installation is required to build libClangSharp on Windows" + } + + $vsPath = & $vswhere -latest -prerelease -products * -property installationPath | Select-Object -First 1 + + if (-not $vsPath) { + throw "No Visual Studio installation was found via 'vswhere.exe'" + } + + return $vsPath +} + +function Import-VisualStudioEnvironment([string] $vsPath, [string] $arch) { + $vcvars = Join-Path -Path $vsPath -ChildPath "VC\Auxiliary\Build\vcvarsall.bat" + + if (-not (Test-Path -Path $vcvars)) { + throw "'vcvarsall.bat' was not found under '$vsPath'" + } + + # clang-cl relies on the MSVC toolchain, Windows SDK, and linker being on + # INCLUDE/LIB/PATH; import the developer environment into this process so the + # subsequent cmake invocation inherits it. + & cmd /c "call `"$vcvars`" $arch > nul && set" | ForEach-Object { + $pair = $_ -split "=", 2 + if ($pair.Count -eq 2) { + [System.Environment]::SetEnvironmentVariable($pair[0], $pair[1]) + } } +} +function Build-Libclangsharp([string] $runtime, [string] $source, [string] $destination) { $nativeBuildDir = Join-Path -Path $ArtifactsDir -ChildPath "bin\native\$runtime" $pathToLlvm = (Resolve-Path -Path $source).Path - & cmake -B "$nativeBuildDir" -S "$RepoRoot" -A "$arch" "-Thost=$arch" "-DPATH_TO_LLVM=$pathToLlvm" + if ($runtime -eq "win-arm64") { + # The official LLVM release binaries are built with clang. On win-arm64, + # clang and MSVC disagree on the record layout of over-aligned non-POD base + # classes (e.g. clang::TemplateSpecializationType), so an MSVC-built shim + # reads members of clang-built types at the wrong offset and returns garbage. + # Build the shim with the release's own clang-cl so the layouts match. + # win-x64 layouts agree between the two, so it keeps using MSVC. + $vsPath = Get-VisualStudioInstallPath + $hostArch = if ([System.Runtime.InteropServices.RuntimeInformation]::OSArchitecture -eq [System.Runtime.InteropServices.Architecture]::Arm64) { "arm64" } else { "amd64_arm64" } + Import-VisualStudioEnvironment -vsPath $vsPath -arch $hostArch + + $env:PATH = "$pathToLlvm\bin;$env:PATH" + + $ninja = (Get-Command -Name "ninja" -ErrorAction SilentlyContinue).Source + if (-not $ninja) { + $ninja = Join-Path -Path $vsPath -ChildPath "Common7\IDE\CommonExtensions\Microsoft\CMake\Ninja\ninja.exe" + } + + & cmake -G Ninja -B "$nativeBuildDir" -S "$RepoRoot" "-DCMAKE_MAKE_PROGRAM=$ninja" -DCMAKE_BUILD_TYPE=Release -DCMAKE_C_COMPILER=clang-cl -DCMAKE_CXX_COMPILER=clang-cl "-DPATH_TO_LLVM=$pathToLlvm" + } + else { + $arch = switch ($runtime) { + "win-x64" { "x64" } + default { throw "'$runtime' cannot build libClangSharp on Windows; use build.sh on the matching runner" } + } + + & cmake -B "$nativeBuildDir" -S "$RepoRoot" -A "$arch" "-Thost=$arch" "-DPATH_TO_LLVM=$pathToLlvm" + } if ($LastExitCode -ne 0) { throw "'cmake' configure failed for '$runtime'" diff --git a/sources/libClangSharp/CMakeLists.txt b/sources/libClangSharp/CMakeLists.txt index 09ab3d17..729b9a20 100644 --- a/sources/libClangSharp/CMakeLists.txt +++ b/sources/libClangSharp/CMakeLists.txt @@ -42,20 +42,37 @@ find_package(Clang REQUIRED CONFIG # into the imported LLVMDebugInfoPDB target in LLVMExports.cmake. That path # won't exist on another machine (or a CI runner) with a different Visual # Studio, causing LNK1181. Repoint it at the DIA SDK of the Visual Studio -# actually being used for this build. -if(MSVC AND CMAKE_GENERATOR_INSTANCE AND TARGET LLVMDebugInfoPDB) - if(CMAKE_GENERATOR_PLATFORM MATCHES "[Aa][Rr][Mm]64") - set(_clangsharp_diaguids "${CMAKE_GENERATOR_INSTANCE}/DIA SDK/lib/arm64/diaguids.lib") - elseif(CMAKE_GENERATOR_PLATFORM MATCHES "[Ww]in32") - set(_clangsharp_diaguids "${CMAKE_GENERATOR_INSTANCE}/DIA SDK/lib/diaguids.lib") +# actually being used for this build. The Visual Studio generator (win-x64) +# exposes its root and target as CMAKE_GENERATOR_INSTANCE/PLATFORM; Ninja + +# clang-cl (win-arm64) gets them from VSINSTALLDIR and the compiler arch id. +if(MSVC AND TARGET LLVMDebugInfoPDB) + if(CMAKE_GENERATOR_INSTANCE) + set(_clangsharp_vs "${CMAKE_GENERATOR_INSTANCE}") else() - set(_clangsharp_diaguids "${CMAKE_GENERATOR_INSTANCE}/DIA SDK/lib/amd64/diaguids.lib") + file(TO_CMAKE_PATH "$ENV{VSINSTALLDIR}" _clangsharp_vs) endif() + string(REGEX REPLACE "/+$" "" _clangsharp_vs "${_clangsharp_vs}") - get_target_property(_clangsharp_pdb_libs LLVMDebugInfoPDB INTERFACE_LINK_LIBRARIES) - if(EXISTS "${_clangsharp_diaguids}") - string(REGEX REPLACE "[^;]*[Dd][Ii][Aa] SDK[^;]*diaguids\\.lib" "${_clangsharp_diaguids}" _clangsharp_pdb_libs "${_clangsharp_pdb_libs}") - set_target_properties(LLVMDebugInfoPDB PROPERTIES INTERFACE_LINK_LIBRARIES "${_clangsharp_pdb_libs}") + if(CMAKE_GENERATOR_PLATFORM) + set(_clangsharp_arch "${CMAKE_GENERATOR_PLATFORM}") + else() + set(_clangsharp_arch "${CMAKE_CXX_COMPILER_ARCHITECTURE_ID}") + endif() + + if(_clangsharp_vs) + if(_clangsharp_arch MATCHES "[Aa][Rr][Mm]64") + set(_clangsharp_diaguids "${_clangsharp_vs}/DIA SDK/lib/arm64/diaguids.lib") + elseif(_clangsharp_arch MATCHES "[Ww]in32|[Xx]86") + set(_clangsharp_diaguids "${_clangsharp_vs}/DIA SDK/lib/diaguids.lib") + else() + set(_clangsharp_diaguids "${_clangsharp_vs}/DIA SDK/lib/amd64/diaguids.lib") + endif() + + get_target_property(_clangsharp_pdb_libs LLVMDebugInfoPDB INTERFACE_LINK_LIBRARIES) + if(EXISTS "${_clangsharp_diaguids}") + string(REGEX REPLACE "[^;]*[Dd][Ii][Aa] SDK[^;]*diaguids\\.lib" "${_clangsharp_diaguids}" _clangsharp_pdb_libs "${_clangsharp_pdb_libs}") + set_target_properties(LLVMDebugInfoPDB PROPERTIES INTERFACE_LINK_LIBRARIES "${_clangsharp_pdb_libs}") + endif() endif() endif() From 297f170d084189e651c686d2749f1b98f986f955 Mon Sep 17 00:00:00 2001 From: Tanner Gooding Date: Thu, 16 Jul 2026 14:50:39 -0700 Subject: [PATCH 3/3] Bump libClangSharp native package to 22.1.8.3 for the clang-cl win-arm64 rebuild The win-arm64 shim is now compiled with clang-cl, so the native package needs a new revision to republish. Bump the five runtime nuspecs, the meta nuspec, and runtime.json from 22.1.8.2 to 22.1.8.3. The managed libClangSharp pin in Directory.Packages.props stays at .2 until .3 is published, matching how prior native republishes were sequenced. Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com> --- .../libClangSharp.runtime.linux-arm64.nuspec | 2 +- .../libClangSharp.runtime.linux-x64.nuspec | 2 +- .../libClangSharp.runtime.osx-arm64.nuspec | 2 +- .../libClangSharp.runtime.win-arm64.nuspec | 2 +- .../libClangSharp.runtime.win-x64.nuspec | 2 +- .../libClangSharp/libClangSharp/libClangSharp.nuspec | 2 +- packages/libClangSharp/libClangSharp/runtime.json | 10 +++++----- 7 files changed, 11 insertions(+), 11 deletions(-) diff --git a/packages/libClangSharp/libClangSharp.runtime.linux-arm64/libClangSharp.runtime.linux-arm64.nuspec b/packages/libClangSharp/libClangSharp.runtime.linux-arm64/libClangSharp.runtime.linux-arm64.nuspec index cb33cb03..60edc18c 100644 --- a/packages/libClangSharp/libClangSharp.runtime.linux-arm64/libClangSharp.runtime.linux-arm64.nuspec +++ b/packages/libClangSharp/libClangSharp.runtime.linux-arm64/libClangSharp.runtime.linux-arm64.nuspec @@ -2,7 +2,7 @@ libClangSharp.runtime.linux-arm64 - 22.1.8.2 + 22.1.8.3 .NET Foundation and Contributors .NET Foundation and Contributors true diff --git a/packages/libClangSharp/libClangSharp.runtime.linux-x64/libClangSharp.runtime.linux-x64.nuspec b/packages/libClangSharp/libClangSharp.runtime.linux-x64/libClangSharp.runtime.linux-x64.nuspec index 4ef7d465..a053ccc6 100644 --- a/packages/libClangSharp/libClangSharp.runtime.linux-x64/libClangSharp.runtime.linux-x64.nuspec +++ b/packages/libClangSharp/libClangSharp.runtime.linux-x64/libClangSharp.runtime.linux-x64.nuspec @@ -2,7 +2,7 @@ libClangSharp.runtime.linux-x64 - 22.1.8.2 + 22.1.8.3 .NET Foundation and Contributors .NET Foundation and Contributors true diff --git a/packages/libClangSharp/libClangSharp.runtime.osx-arm64/libClangSharp.runtime.osx-arm64.nuspec b/packages/libClangSharp/libClangSharp.runtime.osx-arm64/libClangSharp.runtime.osx-arm64.nuspec index b1e813f4..f30fcf59 100644 --- a/packages/libClangSharp/libClangSharp.runtime.osx-arm64/libClangSharp.runtime.osx-arm64.nuspec +++ b/packages/libClangSharp/libClangSharp.runtime.osx-arm64/libClangSharp.runtime.osx-arm64.nuspec @@ -2,7 +2,7 @@ libClangSharp.runtime.osx-arm64 - 22.1.8.2 + 22.1.8.3 .NET Foundation and Contributors .NET Foundation and Contributors true diff --git a/packages/libClangSharp/libClangSharp.runtime.win-arm64/libClangSharp.runtime.win-arm64.nuspec b/packages/libClangSharp/libClangSharp.runtime.win-arm64/libClangSharp.runtime.win-arm64.nuspec index fabe3499..959c33a9 100644 --- a/packages/libClangSharp/libClangSharp.runtime.win-arm64/libClangSharp.runtime.win-arm64.nuspec +++ b/packages/libClangSharp/libClangSharp.runtime.win-arm64/libClangSharp.runtime.win-arm64.nuspec @@ -2,7 +2,7 @@ libClangSharp.runtime.win-arm64 - 22.1.8.2 + 22.1.8.3 .NET Foundation and Contributors .NET Foundation and Contributors true diff --git a/packages/libClangSharp/libClangSharp.runtime.win-x64/libClangSharp.runtime.win-x64.nuspec b/packages/libClangSharp/libClangSharp.runtime.win-x64/libClangSharp.runtime.win-x64.nuspec index ba12773b..14aeeeea 100644 --- a/packages/libClangSharp/libClangSharp.runtime.win-x64/libClangSharp.runtime.win-x64.nuspec +++ b/packages/libClangSharp/libClangSharp.runtime.win-x64/libClangSharp.runtime.win-x64.nuspec @@ -2,7 +2,7 @@ libClangSharp.runtime.win-x64 - 22.1.8.2 + 22.1.8.3 .NET Foundation and Contributors .NET Foundation and Contributors true diff --git a/packages/libClangSharp/libClangSharp/libClangSharp.nuspec b/packages/libClangSharp/libClangSharp/libClangSharp.nuspec index df8de877..e65a11f5 100644 --- a/packages/libClangSharp/libClangSharp/libClangSharp.nuspec +++ b/packages/libClangSharp/libClangSharp/libClangSharp.nuspec @@ -2,7 +2,7 @@ libClangSharp - 22.1.8.2 + 22.1.8.3 .NET Foundation and Contributors .NET Foundation and Contributors true diff --git a/packages/libClangSharp/libClangSharp/runtime.json b/packages/libClangSharp/libClangSharp/runtime.json index c74df919..7cc5d4c5 100644 --- a/packages/libClangSharp/libClangSharp/runtime.json +++ b/packages/libClangSharp/libClangSharp/runtime.json @@ -2,27 +2,27 @@ "runtimes": { "linux-arm64": { "libClangSharp": { - "libClangSharp.runtime.linux-arm64": "22.1.8.2" + "libClangSharp.runtime.linux-arm64": "22.1.8.3" } }, "linux-x64": { "libClangSharp": { - "libClangSharp.runtime.linux-x64": "22.1.8.2" + "libClangSharp.runtime.linux-x64": "22.1.8.3" } }, "osx-arm64": { "libClangSharp": { - "libClangSharp.runtime.osx-arm64": "22.1.8.2" + "libClangSharp.runtime.osx-arm64": "22.1.8.3" } }, "win-arm64": { "libClangSharp": { - "libClangSharp.runtime.win-arm64": "22.1.8.2" + "libClangSharp.runtime.win-arm64": "22.1.8.3" } }, "win-x64": { "libClangSharp": { - "libClangSharp.runtime.win-x64": "22.1.8.2" + "libClangSharp.runtime.win-x64": "22.1.8.3" } } }