From c482534c1d3168da8053c4c47a203da300054a71 Mon Sep 17 00:00:00 2001 From: Alec Scott Date: Mon, 27 Nov 2023 13:15:16 -0700 Subject: CargoPackage: add new build system for Cargo packages (#41192) Co-authored-by: Tom Scogland --- lib/spack/spack/build_systems/cargo.py | 89 ++++++++++++++++++++++++ lib/spack/spack/cmd/create.py | 38 ++++++---- lib/spack/spack/package.py | 1 + share/spack/spack-completion.fish | 2 +- var/spack/repos/builtin/packages/exa/package.py | 25 ++++--- var/spack/repos/builtin/packages/eza/package.py | 19 +++++ var/spack/repos/builtin/packages/rust/package.py | 3 + 7 files changed, 152 insertions(+), 25 deletions(-) create mode 100644 lib/spack/spack/build_systems/cargo.py create mode 100644 var/spack/repos/builtin/packages/eza/package.py diff --git a/lib/spack/spack/build_systems/cargo.py b/lib/spack/spack/build_systems/cargo.py new file mode 100644 index 0000000000..28da475956 --- /dev/null +++ b/lib/spack/spack/build_systems/cargo.py @@ -0,0 +1,89 @@ +# Copyright 2013-2023 Lawrence Livermore National Security, LLC and other +# Spack Project Developers. See the top-level COPYRIGHT file for details. +# +# SPDX-License-Identifier: (Apache-2.0 OR MIT) + +import inspect + +import llnl.util.filesystem as fs + +import spack.builder +import spack.package_base +from spack.directives import build_system, depends_on +from spack.multimethod import when + +from ._checks import BaseBuilder, execute_install_time_tests + + +class CargoPackage(spack.package_base.PackageBase): + """Specialized class for packages built using a Makefiles.""" + + #: This attribute is used in UI queries that need to know the build + #: system base class + build_system_class = "CargoPackage" + + build_system("cargo") + + with when("build_system=cargo"): + depends_on("rust", type="build") + + +@spack.builder.builder("cargo") +class CargoBuilder(BaseBuilder): + """The Cargo builder encodes the most common way of building software with + a rust Cargo.toml file. It has two phases that can be overridden, if need be: + + 1. :py:meth:`~.CargoBuilder.build` + 2. :py:meth:`~.CargoBuilder.install` + + For a finer tuning you may override: + + +-----------------------------------------------+----------------------+ + | **Method** | **Purpose** | + +===============================================+======================+ + | :py:meth:`~.CargoBuilder.build_args` | Specify arguments | + | | to ``cargo install`` | + +-----------------------------------------------+----------------------+ + | :py:meth:`~.CargoBuilder.check_args` | Specify arguments | + | | to ``cargo test`` | + +-----------------------------------------------+----------------------+ + """ + + phases = ("build", "install") + + #: Callback names for install-time test + install_time_test_callbacks = ["check"] + + @property + def build_directory(self): + """Return the directory containing the main Cargo.toml.""" + return self.pkg.stage.source_path + + @property + def build_args(self): + """Arguments for ``cargo build``.""" + return [] + + @property + def check_args(self): + """Argument for ``cargo test`` during check phase""" + return [] + + def build(self, pkg, spec, prefix): + """Runs ``cargo install`` in the source directory""" + with fs.working_dir(self.build_directory): + inspect.getmodule(pkg).cargo( + "install", "--root", "out", "--path", ".", *self.build_args + ) + + def install(self, pkg, spec, prefix): + """Copy build files into package prefix.""" + with fs.working_dir(self.build_directory): + fs.install_tree("out", prefix) + + spack.builder.run_after("install")(execute_install_time_tests) + + def check(self): + """Run "cargo test".""" + with fs.working_dir(self.build_directory): + inspect.getmodule(self.pkg).cargo("test", *self.check_args) diff --git a/lib/spack/spack/cmd/create.py b/lib/spack/spack/cmd/create.py index 946e9bc8b9..130242a8b1 100644 --- a/lib/spack/spack/cmd/create.py +++ b/lib/spack/spack/cmd/create.py @@ -172,6 +172,14 @@ class AutoreconfPackageTemplate(PackageTemplate): return args""" +class CargoPackageTemplate(PackageTemplate): + """Provides appropriate overrides for cargo-based packages""" + + base_class_name = "CargoPackage" + + body_def = "" + + class CMakePackageTemplate(PackageTemplate): """Provides appropriate overrides for CMake-based packages""" @@ -575,28 +583,29 @@ class SIPPackageTemplate(PackageTemplate): templates = { - "autotools": AutotoolsPackageTemplate, "autoreconf": AutoreconfPackageTemplate, - "cmake": CMakePackageTemplate, + "autotools": AutotoolsPackageTemplate, + "bazel": BazelPackageTemplate, "bundle": BundlePackageTemplate, - "qmake": QMakePackageTemplate, + "cargo": CargoPackageTemplate, + "cmake": CMakePackageTemplate, + "generic": PackageTemplate, + "intel": IntelPackageTemplate, + "lua": LuaPackageTemplate, + "makefile": MakefilePackageTemplate, "maven": MavenPackageTemplate, - "scons": SconsPackageTemplate, - "waf": WafPackageTemplate, - "bazel": BazelPackageTemplate, + "meson": MesonPackageTemplate, + "octave": OctavePackageTemplate, + "perlbuild": PerlbuildPackageTemplate, + "perlmake": PerlmakePackageTemplate, "python": PythonPackageTemplate, + "qmake": QMakePackageTemplate, "r": RPackageTemplate, "racket": RacketPackageTemplate, - "perlmake": PerlmakePackageTemplate, - "perlbuild": PerlbuildPackageTemplate, - "octave": OctavePackageTemplate, "ruby": RubyPackageTemplate, - "makefile": MakefilePackageTemplate, - "intel": IntelPackageTemplate, - "meson": MesonPackageTemplate, - "lua": LuaPackageTemplate, + "scons": SconsPackageTemplate, "sip": SIPPackageTemplate, - "generic": PackageTemplate, + "waf": WafPackageTemplate, } @@ -679,6 +688,7 @@ class BuildSystemGuesser: clues = [ (r"/CMakeLists\.txt$", "cmake"), (r"/NAMESPACE$", "r"), + (r"/Cargo\.toml$", "cargo"), (r"/configure$", "autotools"), (r"/configure\.(in|ac)$", "autoreconf"), (r"/Makefile\.am$", "autoreconf"), diff --git a/lib/spack/spack/package.py b/lib/spack/spack/package.py index f38ebec299..0fdfffc177 100644 --- a/lib/spack/spack/package.py +++ b/lib/spack/spack/package.py @@ -37,6 +37,7 @@ from spack.build_systems.cached_cmake import ( cmake_cache_path, cmake_cache_string, ) +from spack.build_systems.cargo import CargoPackage from spack.build_systems.cmake import CMakePackage, generator from spack.build_systems.cuda import CudaPackage from spack.build_systems.generic import Package diff --git a/share/spack/spack-completion.fish b/share/spack/spack-completion.fish index 1029fa6b45..08df9825b6 100755 --- a/share/spack/spack-completion.fish +++ b/share/spack/spack-completion.fish @@ -1261,7 +1261,7 @@ complete -c spack -n '__fish_spack_using_command create' -l keep-stage -f -a kee complete -c spack -n '__fish_spack_using_command create' -l keep-stage -d 'don\'t clean up staging area when command completes' complete -c spack -n '__fish_spack_using_command create' -s n -l name -r -f -a name complete -c spack -n '__fish_spack_using_command create' -s n -l name -r -d 'name of the package to create' -complete -c spack -n '__fish_spack_using_command create' -s t -l template -r -f -a 'autoreconf autotools bazel bundle cmake generic intel lua makefile maven meson octave perlbuild perlmake python qmake r racket ruby scons sip waf' +complete -c spack -n '__fish_spack_using_command create' -s t -l template -r -f -a 'autoreconf autotools bazel bundle cargo cmake generic intel lua makefile maven meson octave perlbuild perlmake python qmake r racket ruby scons sip waf' complete -c spack -n '__fish_spack_using_command create' -s t -l template -r -d 'build system template to use' complete -c spack -n '__fish_spack_using_command create' -s r -l repo -r -f -a repo complete -c spack -n '__fish_spack_using_command create' -s r -l repo -r -d 'path to a repository where the package should be created' diff --git a/var/spack/repos/builtin/packages/exa/package.py b/var/spack/repos/builtin/packages/exa/package.py index 8488f3b3a1..21be99414a 100644 --- a/var/spack/repos/builtin/packages/exa/package.py +++ b/var/spack/repos/builtin/packages/exa/package.py @@ -6,17 +6,22 @@ from spack.package import * -class Exa(Package): - """exa is a replacement for ls written in Rust.""" +class Exa(CargoPackage): + """DEPRECATED: The exa upstream is no longer maintained, see the eza package for a + replacement. + + exa is a replacement for ls written in Rust.""" homepage = "https://the.exa.website" url = "https://github.com/ogham/exa/archive/v0.9.0.tar.gz" - version("0.10.1", sha256="ff0fa0bfc4edef8bdbbb3cabe6fdbd5481a71abbbcc2159f402dea515353ae7c") - version("0.9.0", sha256="96e743ffac0512a278de9ca3277183536ee8b691a46ff200ec27e28108fef783") - - depends_on("rust") - - def install(self, spec, prefix): - cargo = which("cargo") - cargo("install", "--root", prefix, "--path", ".") + version( + "0.10.1", + sha256="ff0fa0bfc4edef8bdbbb3cabe6fdbd5481a71abbbcc2159f402dea515353ae7c", + deprecated=True, + ) + version( + "0.9.0", + sha256="96e743ffac0512a278de9ca3277183536ee8b691a46ff200ec27e28108fef783", + deprecated=True, + ) diff --git a/var/spack/repos/builtin/packages/eza/package.py b/var/spack/repos/builtin/packages/eza/package.py new file mode 100644 index 0000000000..df9dd5aa8b --- /dev/null +++ b/var/spack/repos/builtin/packages/eza/package.py @@ -0,0 +1,19 @@ +# Copyright 2013-2023 Lawrence Livermore National Security, LLC and other +# Spack Project Developers. See the top-level COPYRIGHT file for details. +# +# SPDX-License-Identifier: (Apache-2.0 OR MIT) + +from spack.package import * + + +class Eza(CargoPackage): + """A modern, maintained replacement for ls.""" + + homepage = "https://github.com/eza-community/eza" + url = "https://github.com/eza-community/eza/archive/refs/tags/v0.15.3.tar.gz" + + maintainers("trws") + + license("MIT") + + version("0.15.3", sha256="09093e565913104acb7a8eba974f8067c95566b6fbedf31138c9923a8cfde42f") diff --git a/var/spack/repos/builtin/packages/rust/package.py b/var/spack/repos/builtin/packages/rust/package.py index 8d0784d95a..a9b7a7530f 100644 --- a/var/spack/repos/builtin/packages/rust/package.py +++ b/var/spack/repos/builtin/packages/rust/package.py @@ -89,6 +89,9 @@ class Rust(Package): match = re.match(r"rustc (\S+)", output) return match.group(1) if match else None + def setup_dependent_package(self, module, dependent_spec): + module.cargo = Executable(os.path.join(self.spec.prefix.bin, "cargo")) + def setup_build_environment(self, env): # Manually inject the path of ar for build. ar = which("ar", required=True) -- cgit v1.2.3-60-g2f50