diff --git a/DEVGUIDE.md b/DEVGUIDE.md index e9ce1eec7..c7187ae21 100644 --- a/DEVGUIDE.md +++ b/DEVGUIDE.md @@ -45,6 +45,8 @@ Requirements: Build with dotnet build /p:SkipNative=true + +By default, this will build for your system's architecture (x64 vs arm64/M1). To override this, add `/p:TargetArchitecture=x64` or `/p:TargetArchitecture=arm64` to your build command. ## Packages diff --git a/Directory.Build.props b/Directory.Build.props index 40f3e1c77..2316b0353 100644 --- a/Directory.Build.props +++ b/Directory.Build.props @@ -7,8 +7,9 @@ Debug Debug;Release - x64 - x64 + <_DefaultArchitecture>$([System.Runtime.InteropServices.RuntimeInformation]::OSArchitecture.ToString().ToLower()) + AnyCPU + $(_DefaultArchitecture) $(TargetArchitecture) $(Platform).$(Configuration) @@ -50,9 +51,12 @@ mac linux + $(TargetOS)-$(TargetArchitecture) + win-x64 linux-x64 osx-x64 + osx-arm64 -debug @@ -125,13 +129,18 @@ + pytorch + conda + osx-arm64 %252Bcpu + -py3.10_0 %252Bcu$(CudaVersionNoDot) cpu cu$(CudaVersionNoDot) libtorch-win-shared-with-deps$(LibTorchDebug) libtorch-shared-with-deps libtorch-macos + pytorch $(LibTorchArchiveCoreName)-$(LibTorchVersion)$(LibTorchCpuArchiveNameSuffix) $(LibTorchArchiveCoreName)-$(LibTorchVersion)$(LibTorchCudaArchiveNameSuffix) $(LibTorchArchiveCoreName)-$(LibTorchVersion)$(LibTorchCpuLocalNameSuffix) diff --git a/Directory.Build.targets b/Directory.Build.targets index d0bd1e93b..5d2ce4d53 100644 --- a/Directory.Build.targets +++ b/Directory.Build.targets @@ -1,7 +1,7 @@ - + - + @@ -62,7 +62,7 @@ - + @@ -78,6 +78,15 @@ + + + + + + + + + @@ -124,5 +133,5 @@ - + \ No newline at end of file diff --git a/README.md b/README.md index debaa1c3c..65df44e8d 100644 --- a/README.md +++ b/README.md @@ -99,9 +99,11 @@ To use TorchSharp, you also need one of the LibTorch backend packages: https://w * `libtorch-cpu-win-x64` (CPU, Windows) -* `libtorch-cpu-osx-x64` (CPU, OSX) +* `libtorch-cpu-osx-x64` (CPU, OSX-x64) -* `libtorch-cpu` (CPU, references all three, larger download but simpler) +* `libtorch-cpu-osx-arm64` (CPU, OSX-arm64) + +* `libtorch-cpu` (CPU, references all four, larger download but simpler) * `libtorch-cuda-11.7-linux-x64` (CPU/CUDA 11.3, Linux) diff --git a/azure-pipelines.yml b/azure-pipelines.yml index e020d3063..b6d73e2cd 100644 --- a/azure-pipelines.yml +++ b/azure-pipelines.yml @@ -71,6 +71,15 @@ jobs: pool: vmImage: 'macos-11' +- template: /build/ci/job-template.yml + parameters: + prepScript: echo "no prep needed" + name: MacOS_arm64 + buildScript: dotnet build /p:SkipCuda=true /p:TargetArchitecture=arm64 -c + testScript: echo "Azure Pipelines does not support arm64 yet, can't run tests" + pool: + vmImage: 'macos-11' + ################################################################################ # {Build} --> combine --> package to build native bits on multiple OS's ################################################################################ @@ -166,7 +175,7 @@ jobs: artifact: WindowsAssets # ################################################################################ -- job: MacOS_Native_Build_For_Packages +- job: MacOS_x64_Native_Build_For_Packages # ################################################################################ condition: and(ne(variables['system.pullrequest.isfork'], true), eq(variables['build.sourcebranchname'], 'main')) variables: @@ -182,7 +191,7 @@ jobs: displayName: Download libtorch native binaries - script: dotnet build -c $(BuildConfig) src/TorchSharp/TorchSharp.csproj /p:SkipCuda=true - displayName: Build mac + displayName: Build mac-x64 - script: dotnet build -c $(BuildConfig) src/TorchVision/TorchVision.csproj /p:SkipCuda=true displayName: Build TorchVision @@ -191,7 +200,35 @@ jobs: displayName: Build TorchAudio - publish: $(Build.SourcesDirectory)/bin/obj/packprep/$(BuildConfig) - artifact: MacAssets + artifact: MacAssets_x64 + + # ################################################################################ +- job: MacOS_arm64_Native_Build_For_Packages + # ################################################################################ + condition: and(ne(variables['system.pullrequest.isfork'], true), eq(variables['build.sourcebranchname'], 'main')) + variables: + BuildConfig: Release + OfficialBuildId: $(BUILD.BUILDNUMBER) + DOTNET_CLI_TELEMETRY_OPTOUT: 1 + DOTNET_SKIP_FIRST_TIME_EXPERIENCE: 1 + DOTNET_MULTILEVEL_LOOKUP: 0 + pool: + vmImage: 'macos-11' + steps: + - script: dotnet build -c $(BuildConfig) src/Redist/libtorch-cpu/libtorch-cpu.proj /p:UpdateSHA=true /p:TargetOS=mac /p:TargetArchitecture=arm64 /t:Build /p:IncludeLibTorchCpuPackages=true + displayName: Download libtorch native binaries + + - script: dotnet build -c $(BuildConfig) src/TorchSharp/TorchSharp.csproj /p:SkipCuda=true /p:TargetArchitecture=arm64 + displayName: Build mac-arm64 + + - script: dotnet build -c $(BuildConfig) src/TorchVision/TorchVision.csproj /p:SkipCuda=true /p:TargetArchitecture=arm64 + displayName: Build TorchVision + + - script: dotnet build -c $(BuildConfig) src/TorchAudio/TorchAudio.csproj /p:SkipCuda=true /p:TargetArchitecture=arm64 + displayName: Build TorchAudio + + - publish: $(Build.SourcesDirectory)/bin/obj/packprep/$(BuildConfig) + artifact: MacAssets_arm64 ################################################################################ @@ -201,7 +238,8 @@ jobs: dependsOn: - Linux_Native_Build_For_Packages - Windows_Native_Build_For_Packages - - MacOS_Native_Build_For_Packages + - MacOS_x64_Native_Build_For_Packages + - MacOS_arm64_Native_Build_For_Packages variables: BuildConfig: Release OfficialBuildId: $(BUILD.BUILDNUMBER) @@ -249,36 +287,67 @@ jobs: - script: rmdir /s /q $(Pipeline.Workspace)\LinuxAssets displayName: Free up space (LinuxAssets in workspace) - # Download all bits contributing to the packages from the Mac build + # Download all bits contributing to the packages from the Mac x64 build - download: current - artifact: MacAssets + artifact: MacAssets_x64 - task: CopyFiles@2 - displayName: Copy mac native assets (TorchSharp) to correct folder where the bits contributing to the packages are assembled + displayName: Copy mac-x64 native assets (TorchSharp) to correct folder where the bits contributing to the packages are assembled inputs: - sourceFolder: $(Pipeline.Workspace)/MacAssets/TorchSharp + sourceFolder: $(Pipeline.Workspace)/MacAssets_x64/TorchSharp targetFolder: $(Build.SourcesDirectory)/bin/obj/packprep/$(BuildConfig)/TorchSharp - task: CopyFiles@2 - displayName: Copy mac native assets (TorchAudio) to correct folder where the bits contributing to the packages are assembled + displayName: Copy mac-x64 native assets (TorchAudio) to correct folder where the bits contributing to the packages are assembled inputs: - sourceFolder: $(Pipeline.Workspace)/MacAssets/TorchAudio + sourceFolder: $(Pipeline.Workspace)/MacAssets_x64/TorchAudio targetFolder: $(Build.SourcesDirectory)/bin/obj/packprep/$(BuildConfig)/TorchAudio - task: CopyFiles@2 - displayName: Copy mac native assets (TorchVision) to correct folder where the bits contributing to the packages are assembled + displayName: Copy mac-x64 native assets (TorchVision) to correct folder where the bits contributing to the packages are assembled inputs: - sourceFolder: $(Pipeline.Workspace)/MacAssets/TorchVision + sourceFolder: $(Pipeline.Workspace)/MacAssets_x64/TorchVision targetFolder: $(Build.SourcesDirectory)/bin/obj/packprep/$(BuildConfig)/TorchVision - task: CopyFiles@2 - displayName: Copy mac native assets (libtorch-cpu) to correct folder where the bits contributing to the packages are assembled + displayName: Copy mac-x64 native assets (libtorch-cpu) to correct folder where the bits contributing to the packages are assembled inputs: - sourceFolder: $(Pipeline.Workspace)/MacAssets/libtorch-cpu-osx-x64 + sourceFolder: $(Pipeline.Workspace)/MacAssets_x64/libtorch-cpu-osx-x64 targetFolder: $(Build.SourcesDirectory)/bin/obj/packprep/$(BuildConfig)/libtorch-cpu-osx-x64 - - script: rmdir /s /q $(Pipeline.Workspace)\MacAssets - displayName: Free up space (MacAssets in workspace) + - script: rmdir /s /q $(Pipeline.Workspace)\MacAssets_x64 + displayName: Free up space (MacAssets_x64 in workspace) + + # Download all bits contributing to the packages from the Mac arm64 build + - download: current + artifact: MacAssets_arm64 + + - task: CopyFiles@2 + displayName: Copy mac-arm64 native assets (TorchSharp) to correct folder where the bits contributing to the packages are assembled + inputs: + sourceFolder: $(Pipeline.Workspace)/MacAssets_arm64/TorchSharp + targetFolder: $(Build.SourcesDirectory)/bin/obj/packprep/$(BuildConfig)/TorchSharp + + - task: CopyFiles@2 + displayName: Copy mac-arm64 native assets (TorchAudio) to correct folder where the bits contributing to the packages are assembled + inputs: + sourceFolder: $(Pipeline.Workspace)/MacAssets_arm64/TorchAudio + targetFolder: $(Build.SourcesDirectory)/bin/obj/packprep/$(BuildConfig)/TorchAudio + + - task: CopyFiles@2 + displayName: Copy mac-arm64 native assets (TorchVision) to correct folder where the bits contributing to the packages are assembled + inputs: + sourceFolder: $(Pipeline.Workspace)/MacAssets_arm64/TorchVision + targetFolder: $(Build.SourcesDirectory)/bin/obj/packprep/$(BuildConfig)/TorchVision + + - task: CopyFiles@2 + displayName: Copy mac-arm64 native assets (libtorch-cpu) to correct folder where the bits contributing to the packages are assembled + inputs: + sourceFolder: $(Pipeline.Workspace)/MacAssets_arm64/libtorch-cpu-osx-arm64 + targetFolder: $(Build.SourcesDirectory)/bin/obj/packprep/$(BuildConfig)/libtorch-cpu-osx-arm64 + + - script: rmdir /s /q $(Pipeline.Workspace)\MacAssets_arm64 + displayName: Free up space (MacAssets_arm64 in workspace) - download: current artifact: WindowsAssets diff --git a/pkg/libtorch-cpu-osx-arm64/libtorch-cpu-osx-arm64.nupkgproj b/pkg/libtorch-cpu-osx-arm64/libtorch-cpu-osx-arm64.nupkgproj new file mode 100644 index 000000000..d8ce731a6 --- /dev/null +++ b/pkg/libtorch-cpu-osx-arm64/libtorch-cpu-osx-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 32a70e59b..f8d75cb83 100644 --- a/pkg/libtorch-cpu/libtorch-cpu.nupkgproj +++ b/pkg/libtorch-cpu/libtorch-cpu.nupkgproj @@ -7,6 +7,7 @@ + diff --git a/src/Examples.Utils/Examples.Utils.csproj b/src/Examples.Utils/Examples.Utils.csproj index 1f6d5a081..794a09947 100644 --- a/src/Examples.Utils/Examples.Utils.csproj +++ b/src/Examples.Utils/Examples.Utils.csproj @@ -9,10 +9,6 @@ net6.0 - - x64 - - diff --git a/src/Examples/Examples.csproj b/src/Examples/Examples.csproj index 4bac9a142..38853cad4 100644 --- a/src/Examples/Examples.csproj +++ b/src/Examples/Examples.csproj @@ -15,7 +15,6 @@ false TorchSharp.Examples.Program false - x64 TorchSharp.Examples diff --git a/src/Native/LibTorchSharp/CMakeLists.txt b/src/Native/LibTorchSharp/CMakeLists.txt index d1f3b51a4..db3626b28 100644 --- a/src/Native/LibTorchSharp/CMakeLists.txt +++ b/src/Native/LibTorchSharp/CMakeLists.txt @@ -1,6 +1,6 @@ project(LibTorchSharp) -if(APPLE) +if(APPLE AND NOT LIBTORCH_ARCH STREQUAL "arm64") include_directories("/usr/local/include" "/usr/local/opt/llvm/include") link_directories("/usr/local/lib" "/usr/local/opt/llvm/lib") endif() @@ -56,6 +56,15 @@ if(NOT WIN32) endif() endif() +# Enable cross compilation for arm64/x64 on macOS +if(APPLE) + if(LIBTORCH_ARCH STREQUAL "arm64") + set(CMAKE_OSX_ARCHITECTURES "arm64") + else() + set(CMAKE_OSX_ARCHITECTURES "x86_64") + endif() +endif() + # Add libTorch bindings include_directories(${TORCH_INCLUDE_DIRS}) diff --git a/src/Native/LibTorchSharp/crc32c.c b/src/Native/LibTorchSharp/crc32c.c index 6f34e23b3..f044397ff 100644 --- a/src/Native/LibTorchSharp/crc32c.c +++ b/src/Native/LibTorchSharp/crc32c.c @@ -30,12 +30,15 @@ // Per #2 in the copyright notice above: // // The definition of 'NONMINMAX' below was altered from the original. +// cpuid.h and x86intrin.h are only included if we're on x86 or x64, fixing arm builds. //#define NOMINMAX #ifdef CRC32C_GCC +#if defined(__x86_64__) || defined(__i386__) #include #include +#endif #else #include #endif diff --git a/src/Native/build.sh b/src/Native/build.sh index d04636db1..fdc14f190 100755 --- a/src/Native/build.sh +++ b/src/Native/build.sh @@ -59,7 +59,7 @@ while [ "$1" != "" ]; do done # Force the build to be release since libtorch is in release. -__cmake_defines="-DCMAKE_BUILD_TYPE=${__configuration} ${__strip_argument} -DLIBTORCH_PATH=${__libtorchpath}" +__cmake_defines="-DCMAKE_BUILD_TYPE=${__configuration} ${__strip_argument} -DLIBTORCH_PATH=${__libtorchpath} -DLIBTORCH_ARCH=${__build_arch}" __IntermediatesDir="$__baseIntermediateOutputPath/$__build_arch.$__configuration/Native" __BinDir="$__rootBinPath/$__build_arch.$__configuration/Native" diff --git a/src/Redist/libtorch-cpu/libtorch-cpu.proj b/src/Redist/libtorch-cpu/libtorch-cpu.proj index 74d269950..33c853b3e 100644 --- a/src/Redist/libtorch-cpu/libtorch-cpu.proj +++ b/src/Redist/libtorch-cpu/libtorch-cpu.proj @@ -8,8 +8,13 @@ $(LibTorchCpuLocalBase) $(LibTorchCpuArchiveBase) + $(LibTorchArchiveSource) .zip https://download.pytorch.org/libtorch/cpu/$(ArchiveBaseName)$(ArchiveExtension) + .tar.bz2 + + https://conda.anaconda.org/pytorch/$(CondaArchivePlatformName)/$(ArchiveBaseName)$(ArchiveExtension) + $(MassiveDownloadRoot)$(MSBuildProjectName)\ $(ArchiveBaseName)$(ArchiveExtension) $(DownloadedArchiveFolder)$(DownloadedArchiveFileName) @@ -24,7 +29,7 @@ $(PackagePreparationPath)$(MSBuildProjectName)-$(TargetRuntimeID) $(MainPackageFolder)\.copied.SkipTests.$(SkipTests).IncludeLibTorchCpuPackages.$(IncludeLibTorchCpuPackages) - + @@ -38,7 +43,7 @@ - + @@ -52,6 +57,13 @@ + + + + + + + @@ -105,12 +117,20 @@ + + + + diff --git a/src/Redist/libtorch-cpu/pytorch-1.13.0-py3.10_0.tar.bz2.sha b/src/Redist/libtorch-cpu/pytorch-1.13.0-py3.10_0.tar.bz2.sha new file mode 100644 index 000000000..e2552c1d1 --- /dev/null +++ b/src/Redist/libtorch-cpu/pytorch-1.13.0-py3.10_0.tar.bz2.sha @@ -0,0 +1 @@ +3C22DC05D614895F583588E94ACCDB0922916D358CA9D6085376586EAB152519 diff --git a/src/TorchSharp/Torch.cs b/src/TorchSharp/Torch.cs index eaafda124..de05b91f4 100644 --- a/src/TorchSharp/Torch.cs +++ b/src/TorchSharp/Torch.cs @@ -30,6 +30,7 @@ public static partial class torch static string nativeRid => RuntimeInformation.IsOSPlatform(OSPlatform.Windows) ? "win-x64" : RuntimeInformation.IsOSPlatform(OSPlatform.Linux) ? "linux-x64" : + RuntimeInformation.IsOSPlatform(OSPlatform.OSX) && RuntimeInformation.OSArchitecture == Architecture.Arm64 ? "osx-arm64" : RuntimeInformation.IsOSPlatform(OSPlatform.OSX) ? "osx-x64" : "any"; diff --git a/test/TorchSharpTest.WithCudaBinaries/TorchSharpTest.WithCudaBinaries.csproj b/test/TorchSharpTest.WithCudaBinaries/TorchSharpTest.WithCudaBinaries.csproj index e097d0dc8..e6830d131 100644 --- a/test/TorchSharpTest.WithCudaBinaries/TorchSharpTest.WithCudaBinaries.csproj +++ b/test/TorchSharpTest.WithCudaBinaries/TorchSharpTest.WithCudaBinaries.csproj @@ -9,7 +9,6 @@ true true false - x64 false trx $(OutputPath) diff --git a/test/TorchSharpTest/TorchSharpTest.csproj b/test/TorchSharpTest/TorchSharpTest.csproj index f1c1b54e7..2343302fe 100644 --- a/test/TorchSharpTest/TorchSharpTest.csproj +++ b/test/TorchSharpTest/TorchSharpTest.csproj @@ -9,7 +9,6 @@ net6.0 true false - x64 false trx $(OutputPath)