From 70db7a985cb22563650162f0e141f7527daae1c8 Mon Sep 17 00:00:00 2001 From: alinpahontu2912 Date: Fri, 27 Feb 2026 14:41:04 +0200 Subject: [PATCH 01/11] Add Windows ARM64 CPU support for TorchSharp Add support for building, packaging, and distributing TorchSharp on Windows ARM64 (CPU-only, no CUDA). This enables .NET developers on Windows ARM64 devices to use TorchSharp with the stable LibTorch 2.10.0 release. Key changes: - MSBuild: Add win-arm64 RID mapping, archive name, and cmake path (ARM64 archive has different layout: lib/ instead of libtorch/lib/) - Native build: Enable ARM64 cross-compilation from x64 via MSVC amd64_arm64 toolchain and CMake -A ARM64 - Runtime: Detect win-arm64 in Torch.cs nativeRid for correct native library loading - NuGet: Add libtorch-cpu-win-arm64 package and update libtorch-cpu meta-package - CI: Add Windows ARM64 PR validation and native build pipeline jobs (cross-compiled on x64 agents) - LibTorch: ARM64 uses armpl_lp64.dll (ARM Performance Libraries) instead of Intel OpenMP (libiomp5md.dll) Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> --- Directory.Build.props | 7 +- Directory.Build.targets | 10 ++ azure-pipelines.yml | 118 ++++++++++++++++++ .../libtorch-cpu-win-arm64.nupkgproj | 16 +++ pkg/libtorch-cpu/libtorch-cpu.nupkgproj | 1 + pkg/pack.proj | 2 + src/Native/build.cmd | 30 ++--- src/Native/gen-buildsys-win.bat | 3 +- src/Redist/libtorch-cpu/libtorch-cpu.proj | 11 +- ...rm64-shared-with-deps-2.10.0%2Bcpu.zip.sha | 1 + src/TorchSharp/Torch.cs | 3 +- 11 files changed, 178 insertions(+), 24 deletions(-) create mode 100644 pkg/libtorch-cpu-win-arm64/libtorch-cpu-win-arm64.nupkgproj create mode 100644 src/Redist/libtorch-cpu/libtorch-win-arm64-shared-with-deps-2.10.0%2Bcpu.zip.sha diff --git a/Directory.Build.props b/Directory.Build.props index e8e44ee50..014fd9495 100644 --- a/Directory.Build.props +++ b/Directory.Build.props @@ -55,10 +55,12 @@ $(TargetOS)-$(TargetArchitecture) win-x64 + win-arm64 linux-x64 osx-arm64 win-x64 + win-arm64 linux-x64 osx-$(TargetArchitecture) @@ -141,6 +143,7 @@ cpu cu$(CudaVersionNoDot) libtorch-win-shared-with-deps$(LibTorchDebug) + libtorch-win-arm64-shared-with-deps$(LibTorchDebug) libtorch-shared-with-deps libtorch-macos-x86_64 libtorch-macos-arm64 @@ -148,7 +151,9 @@ $(LibTorchArchiveCoreName)-$(LibTorchVersion)$(LibTorchCudaArchiveNameSuffix) $(LibTorchArchiveCoreName)-$(LibTorchVersion)$(LibTorchCpuLocalNameSuffix) $(LibTorchArchiveCoreName)-$(LibTorchVersion)$(LibTorchCudaLocalNameSuffix) - $(IntermediateOutputRootPath)libtorch-cpu\$(LibTorchCpuLocalBase)\libtorch\share\cmake\Torch + $(IntermediateOutputRootPath)libtorch-cpu\$(LibTorchCpuLocalBase)\libtorch\share\cmake\Torch + + $(IntermediateOutputRootPath)libtorch-cpu\$(LibTorchCpuLocalBase)\share\cmake\Torch diff --git a/Directory.Build.targets b/Directory.Build.targets index 4ab3c814c..cc6bb3d4e 100644 --- a/Directory.Build.targets +++ b/Directory.Build.targets @@ -18,6 +18,16 @@ + + + + + + + + + + diff --git a/azure-pipelines.yml b/azure-pipelines.yml index 9df773f77..850cc59cd 100644 --- a/azure-pipelines.yml +++ b/azure-pipelines.yml @@ -89,6 +89,15 @@ jobs: pool: vmImage: 'macos-latest' +- template: /build/ci/job-template.yml + parameters: + prepScript: echo "no prep needed" + name: Windows_arm64 + buildScript: dotnet build /p:SkipCuda=true /p:TargetArchitecture=arm64 /p:SkipNetFxBuild=true -c + testScript: echo "Cannot run ARM64 tests on x64 Azure Pipelines agent" + pool: + vmImage: 'windows-latest' + ################################################################################ # {Build} --> combine --> package to build native bits on multiple OS's ################################################################################ @@ -285,6 +294,64 @@ jobs: - publish: $(Build.SourcesDirectory)/bin/obj/packprep/$(BuildConfig) artifact: MacAssets_arm64 +################################################################################ +- job: Windows_arm64_Native_Build_For_Packages +################################################################################ + condition: and(ne(variables['system.pullrequest.isfork'], true), eq(variables['build.sourcebranchname'], '${{ parameters.SourceBranchName }}')) + variables: + BuildConfig: Release + OfficialBuildId: $(BUILD.BUILDNUMBER) + DOTNET_CLI_TELEMETRY_OPTOUT: 1 + DOTNET_SKIP_FIRST_TIME_EXPERIENCE: 1 + DOTNET_MULTILEVEL_LOOKUP: 0 + pool: + vmImage: 'windows-latest' + steps: + # Initial cleanup + - script: | + rmdir /s /q .git 2>nul + dotnet nuget locals all --clear + dir + displayName: Initial cleanup + continueOnError: true + + - task: UseDotNet@2 + displayName: 'Use .NET Core sdk' + inputs: + packageType: sdk + version: 8.0.x + installationPath: $(Agent.ToolsDirectory)/dotnet + + # Download ARM64 LibTorch and clean immediately + - script: | + dotnet build -c $(BuildConfig) src/Redist/libtorch-cpu/libtorch-cpu.proj /p:UpdateSHA=true /p:SkipTests=true /p:TargetOS=windows /p:TargetArchitecture=arm64 /t:Build /p:IncludeLibTorchCpuPackages=true + del /s /q *.zip 2>nul + del /s /q *.tar.gz 2>nul + displayName: Download ARM64 libtorch native binaries and cleanup + condition: eq('${{ parameters.BuildLibTorchPackages }}', true) + + # Cross-compile LibTorchSharp for ARM64 on x64 host + - script: dotnet build -c $(BuildConfig) src/TorchSharp/TorchSharp.csproj /p:SkipCuda=true /p:SkipTests=true /p:TargetArchitecture=arm64 + condition: eq('${{ parameters.BuildLibTorchPackages }}', true) + displayName: Build TorchSharp win-arm64 + + - script: dotnet build -c $(BuildConfig) src/TorchVision/TorchVision.csproj /p:SkipCuda=true /p:SkipTests=true /p:TargetArchitecture=arm64 + displayName: Build TorchVision + + - script: dotnet build -c $(BuildConfig) src/TorchAudio/TorchAudio.csproj /p:SkipCuda=true /p:SkipTests=true /p:TargetArchitecture=arm64 + displayName: Build TorchAudio + + # Clean up unnecessary files before publishing + - script: | + del /s /q $(Build.SourcesDirectory)\bin\*.pdb 2>nul + del /s /q $(Build.SourcesDirectory)\bin\*.xml 2>nul + del /s /q $(Build.SourcesDirectory)\bin\obj\packprep\$(BuildConfig)\*.lib 2>nul + displayName: Clean up unnecessary files + continueOnError: true + + - publish: $(Build.SourcesDirectory)/bin/obj/packprep/$(BuildConfig) + artifact: WindowsAssets_arm64 + ################################################################################ - job: Build_TorchSharp_And_libtorch_cpu_Packages ################################################################################ @@ -292,6 +359,7 @@ jobs: dependsOn: - Linux_Native_Build_For_Packages - Windows_Native_Build_For_Packages + - Windows_arm64_Native_Build_For_Packages - MacOS_arm64_Native_Build_For_Packages timeoutInMinutes: 90 variables: @@ -496,6 +564,56 @@ jobs: displayName: Clean WindowsAssets immediately continueOnError: true + # Process Windows ARM64 assets + - task: DownloadPipelineArtifact@2 + displayName: Download Windows ARM64 TorchSharp assets + inputs: + artifact: WindowsAssets_arm64 + patterns: | + TorchSharp/** + path: $(Pipeline.Workspace)/WindowsAssets_arm64 + retryCountOnTaskFailure: 3 + + - task: DownloadPipelineArtifact@2 + displayName: Download Windows ARM64 TorchAudio assets + inputs: + artifact: WindowsAssets_arm64 + patterns: | + TorchAudio/** + path: $(Pipeline.Workspace)/WindowsAssets_arm64 + retryCountOnTaskFailure: 3 + + - task: DownloadPipelineArtifact@2 + displayName: Download Windows ARM64 TorchVision assets + inputs: + artifact: WindowsAssets_arm64 + patterns: | + TorchVision/** + path: $(Pipeline.Workspace)/WindowsAssets_arm64 + retryCountOnTaskFailure: 3 + + - task: DownloadPipelineArtifact@2 + displayName: Download Windows ARM64 libtorch-cpu assets + condition: eq('${{ parameters.BuildLibTorchPackages }}', true) + inputs: + artifact: WindowsAssets_arm64 + patterns: | + libtorch-cpu-win-arm64/** + path: $(Pipeline.Workspace)/WindowsAssets_arm64 + retryCountOnTaskFailure: 3 + continueOnError: true + + - task: CopyFiles@2 + displayName: Copy Windows ARM64 native assets (batch) + inputs: + sourceFolder: $(Pipeline.Workspace)/WindowsAssets_arm64 + targetFolder: $(Build.SourcesDirectory)/bin/obj/packprep/$(BuildConfig) + cleanTargetFolder: false + + - script: rmdir /s /q $(Pipeline.Workspace)\WindowsAssets_arm64 + displayName: Clean WindowsAssets_arm64 immediately + continueOnError: true + # Restore and pack - script: dotnet restore pkg/pack.proj /p:Configuration=Release --nologo displayName: Restore package projects diff --git a/pkg/libtorch-cpu-win-arm64/libtorch-cpu-win-arm64.nupkgproj b/pkg/libtorch-cpu-win-arm64/libtorch-cpu-win-arm64.nupkgproj new file mode 100644 index 000000000..d8ce731a6 --- /dev/null +++ b/pkg/libtorch-cpu-win-arm64/libtorch-cpu-win-arm64.nupkgproj @@ -0,0 +1,16 @@ + + + + netstandard2.0 + + + + + + + + + + + + diff --git a/pkg/libtorch-cpu/libtorch-cpu.nupkgproj b/pkg/libtorch-cpu/libtorch-cpu.nupkgproj index 97c3ffe67..c35a3d5fc 100644 --- a/pkg/libtorch-cpu/libtorch-cpu.nupkgproj +++ b/pkg/libtorch-cpu/libtorch-cpu.nupkgproj @@ -7,6 +7,7 @@ + diff --git a/pkg/pack.proj b/pkg/pack.proj index 3c9db2f98..55474fdc6 100644 --- a/pkg/pack.proj +++ b/pkg/pack.proj @@ -24,6 +24,8 @@ Condition="'$(IncludeTorchSharpPackage)' == 'true' AND !Exists('$(PackagePreparationPath)\TorchSharp\runtimes\linux-x64\native\libLibTorchSharp.so')" /> + diff --git a/src/Native/build.cmd b/src/Native/build.cmd index c0c26c600..ef2b1bd84 100644 --- a/src/Native/build.cmd +++ b/src/Native/build.cmd @@ -23,6 +23,7 @@ if /i [%1] == [Debug] ( set CMAKE_BUILD_TYPE=Debug&&shift&goto Arg_Loop) if /i [%1] == [x86] ( set __BuildArch=x86&&set __VCBuildArch=x86&&shift&goto Arg_Loop) if /i [%1] == [x64] ( set __BuildArch=x64&&set __VCBuildArch=x86_amd64&&shift&goto Arg_Loop) if /i [%1] == [amd64] ( set __BuildArch=x64&&set __VCBuildArch=x86_amd64&&shift&goto Arg_Loop) +if /i [%1] == [arm64] ( set __BuildArch=ARM64&&set __VCBuildArch=amd64_arm64&&shift&goto Arg_Loop) if /i [%1] == [--libtorchpath] ( set LIBTORCH_PATH=%2&&shift&goto Arg_Loop) @@ -66,50 +67,39 @@ exit /b 1 :: Setup vars for VS2026 set __PlatformToolset=v145 set __VSVersion=18 2026 -if NOT "%__BuildArch%" == "arm64" ( - :: Set the environment for the native build - call "%VS180COMNTOOLS%..\..\VC\Auxiliary\Build\vcvarsall.bat" %__VCBuildArch% -) +:: Set the environment for the native build (including cross-compilation for ARM64) +call "%VS180COMNTOOLS%..\..\VC\Auxiliary\Build\vcvarsall.bat" %__VCBuildArch% goto :SetupDirs :VS2022 :: Setup vars for VS2022 set __PlatformToolset=v143 set __VSVersion=17 2022 -if NOT "%__BuildArch%" == "arm64" ( - :: Set the environment for the native build - call "%VS170COMNTOOLS%..\..\VC\Auxiliary\Build\vcvarsall.bat" %__VCBuildArch% -) +:: Set the environment for the native build (including cross-compilation for ARM64) +call "%VS170COMNTOOLS%..\..\VC\Auxiliary\Build\vcvarsall.bat" %__VCBuildArch% goto :SetupDirs :VS2019 :: Setup vars for VS2019 set __PlatformToolset=v142 set __VSVersion=16 2019 -if NOT "%__BuildArch%" == "arm64" ( - :: Set the environment for the native build - call "%VS160COMNTOOLS%..\..\VC\Auxiliary\Build\vcvarsall.bat" %__VCBuildArch% -) +:: Set the environment for the native build (including cross-compilation for ARM64) +call "%VS160COMNTOOLS%..\..\VC\Auxiliary\Build\vcvarsall.bat" %__VCBuildArch% goto :SetupDirs :VS2017 :: Setup vars for VS2017 set __PlatformToolset=v141 set __VSVersion=15 2017 -if NOT "%__BuildArch%" == "arm64" ( - :: Set the environment for the native build - call "%VS150COMNTOOLS%..\..\VC\Auxiliary\Build\vcvarsall.bat" %__VCBuildArch% -) +:: Set the environment for the native build (including cross-compilation for ARM64) +call "%VS150COMNTOOLS%..\..\VC\Auxiliary\Build\vcvarsall.bat" %__VCBuildArch% goto :SetupDirs :VS2015 :: Setup vars for VS2015build set __PlatformToolset=v140 set __VSVersion=14 2015 -if NOT "%__BuildArch%" == "arm64" ( - :: Set the environment for the native build - call "%VS140COMNTOOLS%..\..\VC\vcvarsall.bat" %__VCBuildArch% -) +call "%VS140COMNTOOLS%..\..\VC\vcvarsall.bat" %__VCBuildArch% :SetupDirs :: Setup to cmake the native components diff --git a/src/Native/gen-buildsys-win.bat b/src/Native/gen-buildsys-win.bat index b3870c171..c4a76bd4f 100644 --- a/src/Native/gen-buildsys-win.bat +++ b/src/Native/gen-buildsys-win.bat @@ -30,6 +30,7 @@ popd :: Set the target architecture to a format cmake understands. if /i "%3" == "x64" (set __ExtraCmakeParams=%__ExtraCmakeParams% -A x64) if /i "%3" == "x86" (set __ExtraCmakeParams=%__ExtraCmakeParams% -A Win32) +if /i "%3" == "ARM64" (set __ExtraCmakeParams=%__ExtraCmakeParams% -A ARM64) echo "%CMakePath%" "-DCMAKE_BUILD_TYPE=%CMAKE_BUILD_TYPE%" "-DCMAKE_INSTALL_PREFIX=%__CMakeBinDir%" "-DLIBTORCH_PATH=%LIBTORCH_PATH%" -G "Visual Studio %__VSString%" %__ExtraCmakeParams% -B. -H%1 "%CMakePath%" "-DCMAKE_BUILD_TYPE=%CMAKE_BUILD_TYPE%" "-DCMAKE_INSTALL_PREFIX=%__CMakeBinDir%" "-DLIBTORCH_PATH=%LIBTORCH_PATH%" -G "Visual Studio %__VSString%" %__ExtraCmakeParams% -B. -H%1 @@ -40,7 +41,7 @@ GOTO :DONE echo "Usage..." echo "gen-buildsys-win.bat " echo "Specify the VSVersion to be used - VS2015, VS2017 or VS2019" - echo "Specify the Target Architecture - x86, or x64." + echo "Specify the Target Architecture - x86, x64, or ARM64." EXIT /B 1 :DONE diff --git a/src/Redist/libtorch-cpu/libtorch-cpu.proj b/src/Redist/libtorch-cpu/libtorch-cpu.proj index adc8ba013..c082675ee 100644 --- a/src/Redist/libtorch-cpu/libtorch-cpu.proj +++ b/src/Redist/libtorch-cpu/libtorch-cpu.proj @@ -30,7 +30,7 @@ $(MainPackageFolder)\.copied.SkipTests.$(SkipTests).IncludeLibTorchCpuPackages.$(IncludeLibTorchCpuPackages) - + @@ -39,6 +39,15 @@ + + + + + + + + + diff --git a/src/Redist/libtorch-cpu/libtorch-win-arm64-shared-with-deps-2.10.0%2Bcpu.zip.sha b/src/Redist/libtorch-cpu/libtorch-win-arm64-shared-with-deps-2.10.0%2Bcpu.zip.sha new file mode 100644 index 000000000..f50bc08ff --- /dev/null +++ b/src/Redist/libtorch-cpu/libtorch-win-arm64-shared-with-deps-2.10.0%2Bcpu.zip.sha @@ -0,0 +1 @@ +38d666a9030ba098d1ac5dabfd995cf3d113a12d512252080978b0cc206af205 \ No newline at end of file diff --git a/src/TorchSharp/Torch.cs b/src/TorchSharp/Torch.cs index dd7a07689..c7e6dd608 100644 --- a/src/TorchSharp/Torch.cs +++ b/src/TorchSharp/Torch.cs @@ -38,7 +38,8 @@ public static partial class torch RuntimeInformation.OSArchitecture == Architecture.Arm64; static string nativeRid => - RuntimeInformation.IsOSPlatform(OSPlatform.Windows) ? $"win-x64" : + RuntimeInformation.IsOSPlatform(OSPlatform.Windows) + ? (RuntimeInformation.OSArchitecture == Architecture.Arm64 ? "win-arm64" : "win-x64") : RuntimeInformation.IsOSPlatform(OSPlatform.Linux) ? $"linux-x64" : isAppleSilicon ? "osx-arm64" : "any"; From 6e410de9e1ad721042ca8a4eb876a17c25918594 Mon Sep 17 00:00:00 2001 From: alinpahontu2912 Date: Wed, 11 Mar 2026 15:47:51 +0100 Subject: [PATCH 02/11] Address PR review feedback for ARM64 support - Use ProcessArchitecture instead of OSArchitecture for nativeRid to correctly handle x64 emulation on ARM64 Windows - Add missing '-c Release' configuration to Windows_arm64 CI job - Consolidate duplicate TargetRuntimeID blocks into single block with mutually exclusive conditions - Update VS version list in gen-buildsys-win.bat usage text to include VS2022 and VS2026 Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> --- Directory.Build.props | 7 +------ azure-pipelines.yml | 2 +- src/Native/gen-buildsys-win.bat | 2 +- src/TorchSharp/Torch.cs | 2 +- 4 files changed, 4 insertions(+), 9 deletions(-) diff --git a/Directory.Build.props b/Directory.Build.props index 014fd9495..b4cf70432 100644 --- a/Directory.Build.props +++ b/Directory.Build.props @@ -54,12 +54,7 @@ $(TargetOS)-$(TargetArchitecture) - win-x64 - win-arm64 - linux-x64 - osx-arm64 - - win-x64 + win-x64 win-arm64 linux-x64 osx-$(TargetArchitecture) diff --git a/azure-pipelines.yml b/azure-pipelines.yml index 850cc59cd..a3303ff0c 100644 --- a/azure-pipelines.yml +++ b/azure-pipelines.yml @@ -93,7 +93,7 @@ jobs: parameters: prepScript: echo "no prep needed" name: Windows_arm64 - buildScript: dotnet build /p:SkipCuda=true /p:TargetArchitecture=arm64 /p:SkipNetFxBuild=true -c + buildScript: dotnet build /p:SkipCuda=true /p:TargetArchitecture=arm64 /p:SkipNetFxBuild=true -c Release testScript: echo "Cannot run ARM64 tests on x64 Azure Pipelines agent" pool: vmImage: 'windows-latest' diff --git a/src/Native/gen-buildsys-win.bat b/src/Native/gen-buildsys-win.bat index c4a76bd4f..f58139e92 100644 --- a/src/Native/gen-buildsys-win.bat +++ b/src/Native/gen-buildsys-win.bat @@ -40,7 +40,7 @@ GOTO :DONE :USAGE echo "Usage..." echo "gen-buildsys-win.bat " - echo "Specify the VSVersion to be used - VS2015, VS2017 or VS2019" + echo "Specify the VSVersion to be used - VS2015, VS2017, VS2019, VS2022, or VS2026" echo "Specify the Target Architecture - x86, x64, or ARM64." EXIT /B 1 diff --git a/src/TorchSharp/Torch.cs b/src/TorchSharp/Torch.cs index c7e6dd608..7dbc42ecd 100644 --- a/src/TorchSharp/Torch.cs +++ b/src/TorchSharp/Torch.cs @@ -39,7 +39,7 @@ public static partial class torch static string nativeRid => RuntimeInformation.IsOSPlatform(OSPlatform.Windows) - ? (RuntimeInformation.OSArchitecture == Architecture.Arm64 ? "win-arm64" : "win-x64") : + ? (RuntimeInformation.ProcessArchitecture == Architecture.Arm64 ? "win-arm64" : "win-x64") : RuntimeInformation.IsOSPlatform(OSPlatform.Linux) ? $"linux-x64" : isAppleSilicon ? "osx-arm64" : "any"; From 5b689132d5f2c2c46494b1ba0b4c506531443450 Mon Sep 17 00:00:00 2001 From: alinpahontu2912 Date: Thu, 12 Mar 2026 10:11:32 +0100 Subject: [PATCH 03/11] Fix Windows ARM64 CI build: remove duplicate Release config The job template appends \ to buildScript, so including 'Release' after '-c' resulted in 'dotnet build ... -c Release Release', where the trailing 'Release' was interpreted as a project path (MSB1009). Remove the explicit 'Release' so the template provides it, matching the pattern used by the MacOS_arm64 job. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> --- azure-pipelines.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/azure-pipelines.yml b/azure-pipelines.yml index a3303ff0c..850cc59cd 100644 --- a/azure-pipelines.yml +++ b/azure-pipelines.yml @@ -93,7 +93,7 @@ jobs: parameters: prepScript: echo "no prep needed" name: Windows_arm64 - buildScript: dotnet build /p:SkipCuda=true /p:TargetArchitecture=arm64 /p:SkipNetFxBuild=true -c Release + buildScript: dotnet build /p:SkipCuda=true /p:TargetArchitecture=arm64 /p:SkipNetFxBuild=true -c testScript: echo "Cannot run ARM64 tests on x64 Azure Pipelines agent" pool: vmImage: 'windows-latest' From c80365aad977672000a30247838f7c4bfce3e7cb Mon Sep 17 00:00:00 2001 From: alinpahontu2912 Date: Wed, 18 Mar 2026 14:09:32 +0100 Subject: [PATCH 04/11] Preload ARM64 native dependencies for NuGet package loading Add preloading of ARM Performance Libraries and other libtorch dependencies on Windows ARM64 before loading torch_cpu.dll and LibTorchSharp.dll. This fixes native library resolution when consuming TorchSharp from NuGet packages, where implicit DLL dependencies cannot be found in the NuGet cache directory. Follows the same pattern as the existing CUDA DLL preloading. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> --- src/TorchSharp/Torch.cs | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/src/TorchSharp/Torch.cs b/src/TorchSharp/Torch.cs index 7dbc42ecd..86ec78d7a 100644 --- a/src/TorchSharp/Torch.cs +++ b/src/TorchSharp/Torch.cs @@ -154,6 +154,19 @@ private static void LoadNativeBackend(bool useCudaBackend, out StringBuilder? tr ok = TryLoadNativeLibraryByName("torch_cuda", typeof(torch).Assembly, trace); ok = TryLoadNativeLibraryByName("LibTorchSharp", typeof(torch).Assembly, trace); } else { + var isWindowsArm64 = RuntimeInformation.IsOSPlatform(OSPlatform.Windows) && + RuntimeInformation.ProcessArchitecture == Architecture.Arm64; + if (isWindowsArm64) { + trace.AppendLine($" Try loading Windows ARM64 native components"); + // Preloading these DLLs on Windows ARM64 ensures dependencies are resolved + // when loading from NuGet package directories, similar to the CUDA preloading above. + // ARM64 libtorch uses ARM Performance Libraries instead of Intel OpenMP. + ok = TryLoadNativeLibraryByName("armpl_lp64", typeof(torch).Assembly, trace); + ok = TryLoadNativeLibraryByName("uv", typeof(torch).Assembly, trace); + ok = TryLoadNativeLibraryByName("c10", typeof(torch).Assembly, trace); + ok = TryLoadNativeLibraryByName("torch_global_deps", typeof(torch).Assembly, trace); + } + ok = TryLoadNativeLibraryByName("torch_cpu", typeof(torch).Assembly, trace); ok = TryLoadNativeLibraryByName("LibTorchSharp", typeof(torch).Assembly, trace); } From bf24a3f56dfeaf76dcd943b2fcecf0d351a1545f Mon Sep 17 00:00:00 2001 From: alinpahontu2912 Date: Thu, 19 Mar 2026 14:51:23 +0100 Subject: [PATCH 05/11] Fix CI disk space exhaustion during CUDA libtorch download Add aggressive disk cleanup steps to prevent out-of-memory failures when downloading and extracting large PyTorch 2.10.0 CUDA binaries: - Linux: Remove .git dir and clear NuGet cache in initial cleanup (matching existing Windows behavior) - Both platforms: Delete CPU intermediate extraction dir and downloads before starting CUDA download - Both platforms: Delete CUDA intermediate extraction dir and downloads after CUDA build completes Only intermediate build directories are removed; the final packprep output used for publish artifacts is preserved. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> --- azure-pipelines.yml | 39 ++++++++++++++++++++++++++++++++++++++- 1 file changed, 38 insertions(+), 1 deletion(-) diff --git a/azure-pipelines.yml b/azure-pipelines.yml index 850cc59cd..40f69359b 100644 --- a/azure-pipelines.yml +++ b/azure-pipelines.yml @@ -117,8 +117,10 @@ jobs: vmImage: 'ubuntu-latest' container: UbuntuContainer steps: - # Initial cleanup + # Initial cleanup - free as much disk space as possible - script: | + rm -rf .git + dotnet nuget locals all --clear 2>/dev/null || true rm -rf bin/obj find . -name "*.pdb" -type f -delete find . -name "*.xml" -type f -delete @@ -145,6 +147,15 @@ jobs: condition: eq('${{ parameters.BuildLibTorchPackages }}', true) displayName: Download libtorch native binaries and cleanup archives + # Free intermediate space before CUDA download + - script: | + rm -rf bin/obj/AnyCPU.$(BuildConfig)/libtorch-cpu + rm -rf bin/downloads + df -h + condition: eq('${{ parameters.BuildLibTorchPackages }}', true) + displayName: Free disk space before CUDA download + continueOnError: true + # Build libtorch CUDA and clean immediately - script: | dotnet build -c $(BuildConfig) src/Redist/libtorch-cuda-12.8/libtorch-cuda-12.8.proj /p:UpdateSHA=true /p:SkipTests=true /p:TargetOS=linux /t:Build /p:IncludeLibTorchCudaPackages=true @@ -154,6 +165,15 @@ jobs: condition: eq('${{ parameters.BuildLibTorchPackages }}', true) displayName: Download libtorch CUDA binaries and cleanup archives + # Free intermediate space after CUDA build + - script: | + rm -rf bin/obj/AnyCPU.$(BuildConfig)/libtorch-cuda-12.8 + rm -rf bin/downloads + df -h + condition: eq('${{ parameters.BuildLibTorchPackages }}', true) + displayName: Free disk space after CUDA build + continueOnError: true + - script: dotnet build -c $(BuildConfig) src/TorchSharp/TorchSharp.csproj /p:SkipCuda=true /p:SkipTests=true displayName: Build TorchSharp @@ -211,6 +231,15 @@ jobs: displayName: Download libtorch native binaries and cleanup condition: eq('${{ parameters.BuildLibTorchPackages }}', true) + # Free intermediate space before CUDA download + - script: | + rmdir /s /q bin\obj\AnyCPU.$(BuildConfig)\libtorch-cpu 2>nul + rmdir /s /q bin\downloads 2>nul + dir + condition: eq('${{ parameters.BuildLibTorchPackages }}', true) + displayName: Free disk space before CUDA download + continueOnError: true + # Build libtorch CUDA and clean immediately - script: | dotnet build -c $(BuildConfig) src/Redist/libtorch-cuda-12.8/libtorch-cuda-12.8.proj /p:UpdateSHA=true /p:SkipTests=true /p:TargetOS=windows /t:Build /p:IncludeLibTorchCudaPackages=true @@ -219,6 +248,14 @@ jobs: condition: eq('${{ parameters.BuildLibTorchPackages }}', true) displayName: Download libtorch CUDA binaries and cleanup + # Free intermediate space after CUDA build + - script: | + rmdir /s /q bin\obj\AnyCPU.$(BuildConfig)\libtorch-cuda-12.8 2>nul + rmdir /s /q bin\downloads 2>nul + condition: eq('${{ parameters.BuildLibTorchPackages }}', true) + displayName: Free disk space after CUDA build + continueOnError: true + - script: dotnet build -c $(BuildConfig) src/TorchSharp/TorchSharp.csproj /p:SkipCuda=true /p:SkipTests=true condition: eq('${{ parameters.BuildLibTorchPackages }}', true) displayName: Build TorchSharp From 8fcff09a5fcaf02b0846ec2f783649e7bc86c40c Mon Sep 17 00:00:00 2001 From: alinpahontu2912 Date: Mon, 30 Mar 2026 16:59:45 +0200 Subject: [PATCH 06/11] Fix native library loading from NuGet packages on netstandard2.0 consumers The netstandard2.0 build of TorchSharp uses a custom NativeLibrary polyfill (in netstandard.cs) that calls LoadLibraryEx directly without probing deps.json. This means native DLLs in the NuGet package cache are invisible to Step 1 loading. Step 3's fallback only worked for F# Interactive scenarios where TorchSharp.dll was loaded from the NuGet cache path. This fix extends Step 3 with a second resolution path for regular projects: when TorchSharp.dll is in the app output directory (not the NuGet cache), it independently locates the NuGet global packages folder via the NUGET_PACKAGES environment variable or the default ~/.nuget/packages path, then consolidates native DLLs into a single directory for loading. This fixes the issue where consuming CI-produced NuGet packages on Windows ARM64 (targeting net6.0) would fail with: 'doesn't contain a reference to libtorch-cpu-win-arm64' Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> --- src/TorchSharp/Torch.cs | 75 ++++++++++++++++++++++++++++++++--------- 1 file changed, 60 insertions(+), 15 deletions(-) diff --git a/src/TorchSharp/Torch.cs b/src/TorchSharp/Torch.cs index 86ec78d7a..a7137a88f 100644 --- a/src/TorchSharp/Torch.cs +++ b/src/TorchSharp/Torch.cs @@ -189,31 +189,76 @@ private static void LoadNativeBackend(bool useCudaBackend, out StringBuilder? tr // See https://github.com/dotnet/TorchSharp/issues/169 // - // If we are loading in .NET Interactive or F# Interactive, these are in packages in separate - // package directories. For managed DLLs this works OK, but native DLLs do not load transitive dependencies. + // Native DLLs from NuGet packages may be in separate package directories. + // For managed DLLs this works OK, but native DLLs do not load transitive dependencies + // across different directories. // - // So we shadow copy the DLLs into the TorchSharp package, make a copy of the native DLL and continue - // with the dynamic load + // Additionally, on netstandard2.0 builds the NativeLibrary polyfill uses LoadLibraryEx + // directly and does not probe deps.json, so native DLLs in the NuGet cache are not found. // - // Assumed to be in ...\packages\torchsharp\0.3.0-local-debug-20200918\lib\net6.0\TorchSharp.dll + // We consolidate (shadow copy) all native DLLs into a single directory so that + // inter-DLL dependencies can be resolved by the OS loader. // // TODO: on linux make these copies link not shadow-copy var torchsharpLoc = Path.GetDirectoryName(typeof(torch).Assembly.Location); - var packagesDir = Path.GetFullPath(Path.Combine(torchsharpLoc!, "..", "..", "..", "..")); + + // Try to find the NuGet global packages folder + string? packagesDir = null; + string? torchSharpVersion = null; + + // Path 1: F# Interactive / .NET Interactive - TorchSharp.dll is inside the NuGet cache + // e.g. .../packages/torchsharp/0.106.1/lib/net6.0/TorchSharp.dll + var interactivePackagesDir = Path.GetFullPath(Path.Combine(torchsharpLoc!, "..", "..", "..", "..")); var torchsharpHome = Path.GetFullPath(Path.Combine(torchsharpLoc!, "..", "..")); trace.AppendLine($" torchsharpLoc = {torchsharpLoc}"); - trace.AppendLine($" packagesDir = {packagesDir}"); - trace.AppendLine($" torchsharpHome = {torchsharpHome}"); - if (torchsharpLoc!.Contains("torchsharp") && torchsharpLoc.Contains("lib") && Directory.Exists(packagesDir) && Directory.Exists(torchsharpHome)) { + if (torchsharpLoc!.Contains("torchsharp") && torchsharpLoc.Contains("lib") && Directory.Exists(interactivePackagesDir) && Directory.Exists(torchsharpHome)) { + packagesDir = interactivePackagesDir; + torchSharpVersion = NormalizeNuGetVersion(Path.GetFileName(torchsharpHome)); + trace.AppendLine($" Detected interactive scenario, packagesDir = {packagesDir}, torchSharpVersion = {torchSharpVersion}"); + } + + // Path 2: Regular project - TorchSharp.dll is in the output directory + // Find the NuGet global packages folder independently + if (packagesDir == null) { + trace.AppendLine(" TorchSharp.dll not in NuGet cache, probing NuGet global packages folder..."); + + var nugetPackagesPath = Environment.GetEnvironmentVariable("NUGET_PACKAGES"); + if (string.IsNullOrEmpty(nugetPackagesPath)) { + nugetPackagesPath = Path.Combine( + Environment.GetFolderPath(Environment.SpecialFolder.UserProfile), + ".nuget", "packages"); + } + + trace.AppendLine($" nugetPackagesPath = {nugetPackagesPath}"); + + if (Directory.Exists(nugetPackagesPath)) { + packagesDir = nugetPackagesPath; + // Determine TorchSharp NuGet package version from the assembly metadata + var asm = typeof(torch).Assembly; + var infoVersionAttr = asm.GetCustomAttributes(typeof(System.Reflection.AssemblyInformationalVersionAttribute), false); + if (infoVersionAttr.Length > 0) { + var rawVersion = ((System.Reflection.AssemblyInformationalVersionAttribute)infoVersionAttr[0]).InformationalVersion; + // Strip source hash suffix (e.g. "0.106.1+abc123def") + var versionPart = rawVersion.Split('+')[0]; + torchSharpVersion = NormalizeNuGetVersion(versionPart); + } else { + torchSharpVersion = NormalizeNuGetVersion(asm.GetName().Version!.ToString()); + } + trace.AppendLine($" Resolved packagesDir = {packagesDir}, torchSharpVersion = {torchSharpVersion}"); + } else { + trace.AppendLine($" NuGet packages folder not found at {nugetPackagesPath}"); + } + } + + if (packagesDir != null && torchSharpVersion != null && Directory.Exists(packagesDir)) { - var torchSharpVersion = NormalizeNuGetVersion(Path.GetFileName(torchsharpHome)); var normalizedLibtorchPackageVersion = NormalizeNuGetVersion(libtorchPackageVersion); if (useCudaBackend) { - var consolidatedDir = Path.Combine(torchsharpLoc, $"cuda-{cudaVersion}"); + var consolidatedDir = Path.Combine(torchsharpLoc!, $"cuda-{cudaVersion}"); - trace.AppendLine($" Trying dynamic load for .NET/F# Interactive by consolidating native {cudaRootPackage}-* binaries to {consolidatedDir}..."); + trace.AppendLine($" Trying dynamic load by consolidating native {cudaRootPackage}-* binaries to {consolidatedDir}..."); var cudaOk = CopyNativeComponentsIntoSingleDirectory(packagesDir, $"{cudaRootPackage}-*", normalizedLibtorchPackageVersion, consolidatedDir, trace); if (cudaOk) { @@ -229,9 +274,9 @@ private static void LoadNativeBackend(bool useCudaBackend, out StringBuilder? tr throw new NotSupportedException(message); } } else { - var consolidatedDir = Path.Combine(torchsharpLoc, $"cpu"); + var consolidatedDir = Path.Combine(torchsharpLoc!, $"cpu"); - trace.AppendLine($" Trying dynamic load for .NET/F# Interactive by consolidating native {cpuRootPackage}-* binaries to {consolidatedDir}..."); + trace.AppendLine($" Trying dynamic load by consolidating native {cpuRootPackage} binaries to {consolidatedDir}..."); var cpuOk = CopyNativeComponentsIntoSingleDirectory(packagesDir, cpuRootPackage, normalizedLibtorchPackageVersion, consolidatedDir, trace); if (cpuOk) { @@ -249,7 +294,7 @@ private static void LoadNativeBackend(bool useCudaBackend, out StringBuilder? tr } } else { - trace.AppendLine(" Giving up, TorchSharp.dll does not appear to have been loaded from package directories"); + trace.AppendLine(" Giving up, could not locate NuGet packages directory"); } if (!ok) { var message = $"This application or script uses TorchSharp but doesn't contain a reference to {(useCudaBackend ? cudaRootPackage : cpuRootPackage)}, Version={libtorchPackageVersion}.\n\nConsider referencing one of the combination packages TorchSharp-cpu, TorchSharp-cuda-linux, TorchSharp-cuda-windows or call System.Runtime.InteropServices.NativeLibrary.Load(path-to-{target}) explicitly for a Python install of pytorch. See https://github.com/dotnet/TorchSharp/issues/169.\".\n\nFor CUDA, you may need to call 'TorchSharp.torch.InitializeDeviceType(TorchSharp.DeviceType.CUDA)' before any use of TorchSharp CUDA packages from scripts or notebooks.\n\nTrace from LoadNativeBackend:\n{trace}"; From 220d8e1ca21f456151adad28b08ab1883535c51e Mon Sep 17 00:00:00 2001 From: alinpahontu2912 Date: Thu, 2 Apr 2026 15:27:35 +0200 Subject: [PATCH 07/11] Revert ARM64 preloading - issue was TFM mismatch, not native loading The native DLL loading failure was caused by using net8.0 NuGet packages with a net6.0 consuming project, not a missing preload. Reverts the ARM64 preloading added in 33b4855b. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> --- src/TorchSharp/Torch.cs | 13 ------------- 1 file changed, 13 deletions(-) diff --git a/src/TorchSharp/Torch.cs b/src/TorchSharp/Torch.cs index a7137a88f..4dc323c93 100644 --- a/src/TorchSharp/Torch.cs +++ b/src/TorchSharp/Torch.cs @@ -154,19 +154,6 @@ private static void LoadNativeBackend(bool useCudaBackend, out StringBuilder? tr ok = TryLoadNativeLibraryByName("torch_cuda", typeof(torch).Assembly, trace); ok = TryLoadNativeLibraryByName("LibTorchSharp", typeof(torch).Assembly, trace); } else { - var isWindowsArm64 = RuntimeInformation.IsOSPlatform(OSPlatform.Windows) && - RuntimeInformation.ProcessArchitecture == Architecture.Arm64; - if (isWindowsArm64) { - trace.AppendLine($" Try loading Windows ARM64 native components"); - // Preloading these DLLs on Windows ARM64 ensures dependencies are resolved - // when loading from NuGet package directories, similar to the CUDA preloading above. - // ARM64 libtorch uses ARM Performance Libraries instead of Intel OpenMP. - ok = TryLoadNativeLibraryByName("armpl_lp64", typeof(torch).Assembly, trace); - ok = TryLoadNativeLibraryByName("uv", typeof(torch).Assembly, trace); - ok = TryLoadNativeLibraryByName("c10", typeof(torch).Assembly, trace); - ok = TryLoadNativeLibraryByName("torch_global_deps", typeof(torch).Assembly, trace); - } - ok = TryLoadNativeLibraryByName("torch_cpu", typeof(torch).Assembly, trace); ok = TryLoadNativeLibraryByName("LibTorchSharp", typeof(torch).Assembly, trace); } From 5bbb6cfedc6df52306412254e8292defcd4734a0 Mon Sep 17 00:00:00 2001 From: Stefan-Alin Pahontu <56953855+alinpahontu2912@users.noreply.github.com> Date: Tue, 7 Apr 2026 16:01:26 +0200 Subject: [PATCH 08/11] Update src/TorchSharp/Torch.cs MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: Jiri Cincura ↹ --- src/TorchSharp/Torch.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/TorchSharp/Torch.cs b/src/TorchSharp/Torch.cs index 4dc323c93..0db4ab4cb 100644 --- a/src/TorchSharp/Torch.cs +++ b/src/TorchSharp/Torch.cs @@ -187,7 +187,7 @@ private static void LoadNativeBackend(bool useCudaBackend, out StringBuilder? tr // inter-DLL dependencies can be resolved by the OS loader. // // TODO: on linux make these copies link not shadow-copy - var torchsharpLoc = Path.GetDirectoryName(typeof(torch).Assembly.Location); + var torchsharpLoc = Path.GetDirectoryName(typeof(torch).Assembly.Location)!; // Try to find the NuGet global packages folder string? packagesDir = null; From 7ea42ec1b782a273a17119a0a883abd46922c07b Mon Sep 17 00:00:00 2001 From: alinpahontu2912 Date: Wed, 8 Apr 2026 11:26:10 +0200 Subject: [PATCH 09/11] add local arm64 build properties --- Directory.Build.props | 4 +++- TorchSharp.sln | 44 +++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 47 insertions(+), 1 deletion(-) diff --git a/Directory.Build.props b/Directory.Build.props index b4cf70432..313c39566 100644 --- a/Directory.Build.props +++ b/Directory.Build.props @@ -93,7 +93,9 @@ true - false + + true + false false diff --git a/TorchSharp.sln b/TorchSharp.sln index b27ac7e8a..df741e198 100644 --- a/TorchSharp.sln +++ b/TorchSharp.sln @@ -78,8 +78,10 @@ EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Any CPU = Debug|Any CPU + Debug|arm64 = Debug|arm64 Debug|x64 = Debug|x64 Release|Any CPU = Release|Any CPU + Release|arm64 = Release|arm64 Release|x64 = Release|x64 EndGlobalSection GlobalSection(ProjectConfigurationPlatforms) = postSolution @@ -166,6 +168,48 @@ Global {B3AAC8E8-9CA4-4B01-96CF-206AE7327DDE}.Release|Any CPU.Build.0 = Release|Any CPU {B3AAC8E8-9CA4-4B01-96CF-206AE7327DDE}.Release|x64.ActiveCfg = Release|Any CPU {B3AAC8E8-9CA4-4B01-96CF-206AE7327DDE}.Release|x64.Build.0 = Release|Any CPU + {061CCBA1-A859-4392-8F45-249E5DAF1C88}.Debug|arm64.ActiveCfg = Debug|Any CPU + {061CCBA1-A859-4392-8F45-249E5DAF1C88}.Debug|arm64.Build.0 = Debug|Any CPU + {061CCBA1-A859-4392-8F45-249E5DAF1C88}.Release|arm64.ActiveCfg = Release|Any CPU + {061CCBA1-A859-4392-8F45-249E5DAF1C88}.Release|arm64.Build.0 = Release|Any CPU + {6C323B05-9028-4B09-911C-3C03AE058BEE}.Debug|arm64.ActiveCfg = Debug|Any CPU + {6C323B05-9028-4B09-911C-3C03AE058BEE}.Debug|arm64.Build.0 = Debug|Any CPU + {6C323B05-9028-4B09-911C-3C03AE058BEE}.Release|arm64.ActiveCfg = Release|Any CPU + {6C323B05-9028-4B09-911C-3C03AE058BEE}.Release|arm64.Build.0 = Release|Any CPU + {42B45168-476D-4BFA-87B8-81A34E6295CD}.Debug|arm64.ActiveCfg = Debug|Any CPU + {42B45168-476D-4BFA-87B8-81A34E6295CD}.Debug|arm64.Build.0 = Debug|Any CPU + {42B45168-476D-4BFA-87B8-81A34E6295CD}.Release|arm64.ActiveCfg = Release|Any CPU + {42B45168-476D-4BFA-87B8-81A34E6295CD}.Release|arm64.Build.0 = Release|Any CPU + {E7467DDF-893C-38A8-8E19-6B4E3FB10F55}.Debug|arm64.ActiveCfg = Debug|x64 + {E7467DDF-893C-38A8-8E19-6B4E3FB10F55}.Release|arm64.ActiveCfg = Release|x64 + {DD652544-711E-4029-83FF-DA4A9600E6E7}.Debug|arm64.ActiveCfg = Debug|Any CPU + {DD652544-711E-4029-83FF-DA4A9600E6E7}.Debug|arm64.Build.0 = Debug|Any CPU + {DD652544-711E-4029-83FF-DA4A9600E6E7}.Release|arm64.ActiveCfg = Release|Any CPU + {DD652544-711E-4029-83FF-DA4A9600E6E7}.Release|arm64.Build.0 = Release|Any CPU + {05031D1C-D0B2-4BF3-A6AF-3339A78437E3}.Debug|arm64.ActiveCfg = Debug|Any CPU + {05031D1C-D0B2-4BF3-A6AF-3339A78437E3}.Debug|arm64.Build.0 = Debug|Any CPU + {05031D1C-D0B2-4BF3-A6AF-3339A78437E3}.Release|arm64.ActiveCfg = Release|Any CPU + {05031D1C-D0B2-4BF3-A6AF-3339A78437E3}.Release|arm64.Build.0 = Release|Any CPU + {AACEAE55-804D-45BC-BC3D-1AB8E856E0E8}.Debug|arm64.ActiveCfg = Debug|Any CPU + {AACEAE55-804D-45BC-BC3D-1AB8E856E0E8}.Debug|arm64.Build.0 = Debug|Any CPU + {AACEAE55-804D-45BC-BC3D-1AB8E856E0E8}.Release|arm64.ActiveCfg = Release|Any CPU + {AACEAE55-804D-45BC-BC3D-1AB8E856E0E8}.Release|arm64.Build.0 = Release|Any CPU + {95493944-D1AE-414E-964B-B58AEAE672E5}.Debug|arm64.ActiveCfg = Debug|Any CPU + {95493944-D1AE-414E-964B-B58AEAE672E5}.Debug|arm64.Build.0 = Debug|Any CPU + {95493944-D1AE-414E-964B-B58AEAE672E5}.Release|arm64.ActiveCfg = Release|Any CPU + {95493944-D1AE-414E-964B-B58AEAE672E5}.Release|arm64.Build.0 = Release|Any CPU + {6D3CE8AA-F369-4D2D-BDA7-9F89D6BE1B2E}.Debug|arm64.ActiveCfg = Debug|Any CPU + {6D3CE8AA-F369-4D2D-BDA7-9F89D6BE1B2E}.Debug|arm64.Build.0 = Debug|Any CPU + {6D3CE8AA-F369-4D2D-BDA7-9F89D6BE1B2E}.Release|arm64.ActiveCfg = Release|Any CPU + {6D3CE8AA-F369-4D2D-BDA7-9F89D6BE1B2E}.Release|arm64.Build.0 = Release|Any CPU + {DCF01EE5-6431-4115-85E0-1FC4C3DE86A2}.Debug|arm64.ActiveCfg = Debug|Any CPU + {DCF01EE5-6431-4115-85E0-1FC4C3DE86A2}.Debug|arm64.Build.0 = Debug|Any CPU + {DCF01EE5-6431-4115-85E0-1FC4C3DE86A2}.Release|arm64.ActiveCfg = Release|Any CPU + {DCF01EE5-6431-4115-85E0-1FC4C3DE86A2}.Release|arm64.Build.0 = Release|Any CPU + {B3AAC8E8-9CA4-4B01-96CF-206AE7327DDE}.Debug|arm64.ActiveCfg = Debug|Any CPU + {B3AAC8E8-9CA4-4B01-96CF-206AE7327DDE}.Debug|arm64.Build.0 = Debug|Any CPU + {B3AAC8E8-9CA4-4B01-96CF-206AE7327DDE}.Release|arm64.ActiveCfg = Release|Any CPU + {B3AAC8E8-9CA4-4B01-96CF-206AE7327DDE}.Release|arm64.Build.0 = Release|Any CPU EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE From 3e49536eebba4408d6ac117c65e4f588fca8763f Mon Sep 17 00:00:00 2001 From: alinpahontu2912 Date: Wed, 8 Apr 2026 12:59:15 +0200 Subject: [PATCH 10/11] Add sha file for winarm64 libtorch --- ...ibtorch-win-arm64-shared-with-deps-debug-2.10.0%2Bcpu.zip.sha | 1 + 1 file changed, 1 insertion(+) create mode 100644 src/Redist/libtorch-cpu/libtorch-win-arm64-shared-with-deps-debug-2.10.0%2Bcpu.zip.sha diff --git a/src/Redist/libtorch-cpu/libtorch-win-arm64-shared-with-deps-debug-2.10.0%2Bcpu.zip.sha b/src/Redist/libtorch-cpu/libtorch-win-arm64-shared-with-deps-debug-2.10.0%2Bcpu.zip.sha new file mode 100644 index 000000000..eff08849d --- /dev/null +++ b/src/Redist/libtorch-cpu/libtorch-win-arm64-shared-with-deps-debug-2.10.0%2Bcpu.zip.sha @@ -0,0 +1 @@ +A6B6C3D9FF25113F8E48F6C7A5DD14EFCA5BCA6DE300DE0BD003FCC309439F2C From dc4fd8147a5216320d1a06a292036df934dae35d Mon Sep 17 00:00:00 2001 From: Stefan-Alin Pahontu <56953855+alinpahontu2912@users.noreply.github.com> Date: Wed, 8 Apr 2026 12:54:56 +0200 Subject: [PATCH 11/11] Apply suggestions from code review MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: Jiri Cincura ↹ --- src/TorchSharp/Torch.cs | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/src/TorchSharp/Torch.cs b/src/TorchSharp/Torch.cs index 0db4ab4cb..32dbdb4b6 100644 --- a/src/TorchSharp/Torch.cs +++ b/src/TorchSharp/Torch.cs @@ -195,12 +195,12 @@ private static void LoadNativeBackend(bool useCudaBackend, out StringBuilder? tr // Path 1: F# Interactive / .NET Interactive - TorchSharp.dll is inside the NuGet cache // e.g. .../packages/torchsharp/0.106.1/lib/net6.0/TorchSharp.dll - var interactivePackagesDir = Path.GetFullPath(Path.Combine(torchsharpLoc!, "..", "..", "..", "..")); - var torchsharpHome = Path.GetFullPath(Path.Combine(torchsharpLoc!, "..", "..")); + var interactivePackagesDir = Path.GetFullPath(Path.Combine(torchsharpLoc, "..", "..", "..", "..")); + var torchsharpHome = Path.GetFullPath(Path.Combine(torchsharpLoc, "..", "..")); trace.AppendLine($" torchsharpLoc = {torchsharpLoc}"); - if (torchsharpLoc!.Contains("torchsharp") && torchsharpLoc.Contains("lib") && Directory.Exists(interactivePackagesDir) && Directory.Exists(torchsharpHome)) { + if (torchsharpLoc.Contains("torchsharp") && torchsharpLoc.Contains("lib") && Directory.Exists(interactivePackagesDir) && Directory.Exists(torchsharpHome)) { packagesDir = interactivePackagesDir; torchSharpVersion = NormalizeNuGetVersion(Path.GetFileName(torchsharpHome)); trace.AppendLine($" Detected interactive scenario, packagesDir = {packagesDir}, torchSharpVersion = {torchSharpVersion}"); @@ -243,7 +243,7 @@ private static void LoadNativeBackend(bool useCudaBackend, out StringBuilder? tr var normalizedLibtorchPackageVersion = NormalizeNuGetVersion(libtorchPackageVersion); if (useCudaBackend) { - var consolidatedDir = Path.Combine(torchsharpLoc!, $"cuda-{cudaVersion}"); + var consolidatedDir = Path.Combine(torchsharpLoc, $"cuda-{cudaVersion}"); trace.AppendLine($" Trying dynamic load by consolidating native {cudaRootPackage}-* binaries to {consolidatedDir}..."); @@ -261,7 +261,7 @@ private static void LoadNativeBackend(bool useCudaBackend, out StringBuilder? tr throw new NotSupportedException(message); } } else { - var consolidatedDir = Path.Combine(torchsharpLoc!, $"cpu"); + var consolidatedDir = Path.Combine(torchsharpLoc, $"cpu"); trace.AppendLine($" Trying dynamic load by consolidating native {cpuRootPackage} binaries to {consolidatedDir}...");