diff --git a/Directory.Build.props b/Directory.Build.props
index e8e44ee50..b4cf70432 100644
--- a/Directory.Build.props
+++ b/Directory.Build.props
@@ -54,11 +54,8 @@
$(TargetOS)-$(TargetArchitecture)
- win-x64
- linux-x64
- osx-arm64
-
- win-x64
+ win-x64
+ win-arm64
linux-x64
osx-$(TargetArchitecture)
@@ -141,6 +138,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 +146,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 9577abcf5..2032427ca 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
################################################################################
@@ -108,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
@@ -136,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
@@ -145,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
@@ -202,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
@@ -210,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
@@ -285,6 +331,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 +396,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 +601,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..f58139e92 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
@@ -39,8 +40,8 @@ GOTO :DONE
:USAGE
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 VSVersion to be used - VS2015, VS2017, VS2019, VS2022, or VS2026"
+ 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..a7137a88f 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.ProcessArchitecture == Architecture.Arm64 ? "win-arm64" : "win-x64") :
RuntimeInformation.IsOSPlatform(OSPlatform.Linux) ? $"linux-x64" :
isAppleSilicon ? "osx-arm64" :
"any";
@@ -153,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);
}
@@ -175,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) {
@@ -215,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) {
@@ -235,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}";