diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 5ad184a..a7002de 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -14,14 +14,16 @@ jobs: include: - os: ubuntu-latest cuda-version: "12.9" + cross-arch: "armv8" # test x86_64 -> arm64 cross-building, in addition to native + run-cross-test: false # the x86_64 runner cannot execute arm64 binaries - os: ubuntu-latest cuda-version: "13.0" - os: ubuntu-latest cuda-version: "13.2" - - os: ubuntu-24.04-arm - cuda-version: "13.2" - os: ubuntu-latest cuda-version: "13.3" + - os: ubuntu-24.04-arm + cuda-version: "13.2" - os: ubuntu-24.04-arm cuda-version: "13.3" # Windows: @@ -36,12 +38,11 @@ jobs: - os: windows-latest cuda-version: "13.3" # 13.4.0-preview developer preview: Windows x86_64 and arm64 - - os: windows-latest - cuda-version: "13.4" - cross-arch: "armv8" # test x86_64 -> arm64 cross-building, in addition to native - os: windows-11-arm cuda-version: "13.4" cross-arch: "x86_64" # test arm64 -> x86_64 cross-building, in addition to native + run-cross-test: true # Windows arm64 can execute x86_64 binaries + package-ref: "cuda-toolkit/13.4.0-preview" runs-on: ${{ matrix.os }} @@ -63,13 +64,15 @@ jobs: - name: conan create cuda-toolkit run: conan create recipes/cuda-toolkit/${{ matrix.cuda-version }} && conan cache clean "*" + # test_package is skipped here: it is only built when a host compiler able to + # target ${{ matrix.cross-arch }} is available, in the dedicated step below - name: conan create cuda-toolkit (cross-building for ${{ matrix.cross-arch }}) if: matrix.cross-arch != '' - run: conan create recipes/cuda-toolkit/${{ matrix.cuda-version }} -s arch=${{ matrix.cross-arch }} && conan cache clean "*" + run: conan create recipes/cuda-toolkit/${{ matrix.cuda-version }} -s arch=${{ matrix.cross-arch }} --test-folder="" && conan cache clean "*" - name: conan test cuda-toolkit (cross-building for ${{ matrix.cross-arch }}) - if: matrix.cross-arch != '' - run: conan test recipes/cuda-toolkit/${{ matrix.cuda-version }}/test_package cuda-toolkit/13.4.0-preview -s arch=${{ matrix.cross-arch }} -c tools.cmake.cmaketoolchain:generator=Ninja + if: matrix.run-cross-test + run: conan test recipes/cuda-toolkit/${{ matrix.cuda-version }}/test_package ${{ matrix.package-ref }} -s arch=${{ matrix.cross-arch }} -c tools.cmake.cmaketoolchain:generator=Ninja - name: build cuda-samples if: (matrix.cuda-version == '13.2' || (matrix.cuda-version == '13.4' && startsWith(matrix.os, 'windows'))) diff --git a/recipes/cuda-toolkit/12.9/conanfile.py b/recipes/cuda-toolkit/12.9/conanfile.py index d4feeba..0dc64e3 100644 --- a/recipes/cuda-toolkit/12.9/conanfile.py +++ b/recipes/cuda-toolkit/12.9/conanfile.py @@ -4,7 +4,7 @@ from conan import ConanFile from conan.errors import ConanInvalidConfiguration -from conan.tools.files import copy, download, get, load, rename +from conan.tools.files import copy, download, get, load, rename, replace_in_file class CudaToolkitConan(ConanFile): name = "cuda-toolkit" @@ -15,13 +15,39 @@ class CudaToolkitConan(ConanFile): homepage = "https://developer.nvidia.com/cuda-toolkit" description = "A development environment for creating high performance GPU-accelerated applications" settings = "os", "arch", "compiler", "build_type" + options = { + # Linux armv8 only: SBSA (Arm servers) vs Jetson/Tegra (linux-aarch64) + "arm_target": ["sbsa", "jetson"], + } + default_options = { + "arm_target": "sbsa", + } + + def config_options(self): + if self.settings.os != "Linux" or self.settings.arch != "armv8": + del self.options.arm_target def validate(self): if self.settings.os not in ["Windows", "Linux"]: raise ConanInvalidConfiguration("Operating system not supported") - + + # CUDA 12.9 redistributables: linux-x86_64, linux-sbsa, linux-aarch64, windows-x86_64 + supported_archs = ["x86_64", "armv8"] if self.settings.os == "Linux" else ["x86_64"] + if self.settings.arch not in supported_archs: + raise ConanInvalidConfiguration(f"{self.settings.arch} is not supported on {self.settings.os}") + # TODO: Add checks for supported compilers as per https://docs.nvidia.com/cuda/cuda-installation-guide-linux/index.html#id56 + def _cuda_platform(self): + if self.settings.os == "Linux" and self.settings.arch == "armv8": + return "linux-aarch64" if self.options.arm_target == "jetson" else "linux-sbsa" + return f"{str(self.settings.os).lower()}-{self.settings.arch}" + + def _targets_arch(self): + if self.settings.os == "Linux" and self.settings.arch == "armv8": + return "aarch64" if self.options.arm_target == "jetson" else "sbsa" + return str(self.settings.arch) + def build(self): base_url = "https://developer.download.nvidia.com/compute/cuda/redist" distrib = f"redistrib_{self.version}.json" @@ -30,8 +56,7 @@ def build(self): json_content = load(self, distrib) cuda_distrib = json.loads(json_content) - arch = "x86_64" - cuda_platform = f"{str(self.settings.os).lower()}-{arch}" + cuda_platform = self._cuda_platform() self.output.info(f"About to download CUDA toolkit components for {cuda_platform}") components = [ @@ -58,7 +83,8 @@ def build(self): platform_components = { "linux-x86_64": ["libcufile", "cuda_sandbox_dev", "cuda_opencl"], - "linux-aarch64": ["libcufile", "cuda_compat", "libcudla",], + "linux-sbsa": ["libcufile"], + "linux-aarch64": ["libcufile", "cuda_compat", "libcudla"], "windows-x86_64": ["cuda_opencl", "visual_studio_integration"], } components += platform_components.get(cuda_platform, []) @@ -75,14 +101,20 @@ def build(self): get(self, url, sha256=checksum, destination="cuda", strip_root=True, keep_permissions=False) rename(self, f"{self.build_folder}/cuda/LICENSE", f"LICENSE_{component_name}") + if self.settings.os == "Linux": + # When crossbuilding, point nvcc to the target directory in the "host" package (for the target architecture), + # rather than have it look relative to itself (which is the current architecture of the build machine) + replace_in_file(self, "cuda/bin/nvcc.profile", '$(TOP)/$(_TARGET_DIR_)/', '$(CONAN_CUDA_TARGET_DIR)/') + replace_in_file(self, "cuda/bin/nvcc.profile", "lib$(_TARGET_SIZE_)", "lib" ) + def package(self): copy(self, "LICENSE*", src=self.build_folder, dst=os.path.join(self.package_folder, "licenses")) for folder in ["bin", "nvml", "nvvm", "compat"]: copy(self, "*", src=os.path.join(self.build_folder, "cuda", folder), dst=os.path.join(self.package_folder, folder)) - if self.settings.os == "Linux" and not self.settings_target: - arch = "x86_64" + if self.settings.os == "Linux": + arch = self._targets_arch() targets_folder = f"targets/{arch}-linux" copy(self, "*", src=os.path.join(self.build_folder, "cuda", "lib"), dst=os.path.join(self.package_folder, targets_folder, "lib")) copy(self, "*", src=os.path.join(self.build_folder, "cuda", "include"), dst=os.path.join(self.package_folder, targets_folder, "include")) @@ -105,7 +137,7 @@ def package(self): dst=os.path.join(self.package_folder, "extras", "visual_studio_integration") ) def package_id(self): - # only os and arch matter for this package + # only os, arch, and arm_target (when set) matter for this package self.info.settings.rm_safe("compiler") self.info.settings.rm_safe("build_type") @@ -118,7 +150,10 @@ def package_info(self): self.buildenv_info.define_path("CUDACXX", os.path.join(self.package_folder, "bin", nvcc)) if not self.settings_target: + arch = self._targets_arch() if self.settings.os == "Linux" else str(self.settings.arch) self.buildenv_info.define_path("CUDAToolkit_ROOT", self.package_folder) + if self.settings.os == "Linux": + self.buildenv_info.define_path("CONAN_CUDA_TARGET_DIR", os.path.join(self.package_folder, "targets", f"{arch}-linux")) if self.settings.os == "Linux": self.cpp_info.libdirs = ["lib64"]