diff --git a/var/spack/repos/builtin/packages/rust/detection_test.yaml b/var/spack/repos/builtin/packages/rust/detection_test.yaml new file mode 100644 index 00000000000..e0729e89752 --- /dev/null +++ b/var/spack/repos/builtin/packages/rust/detection_test.yaml @@ -0,0 +1,55 @@ +paths: +- layout: + - executables: + - "bin/rustc" + script: | + echo "rustc 1.75.0 (82e1608df 2023-12-21) (built from a source tarball)" + - executables: + - "bin/cargo" + script: | + echo "cargo 1.75.0" + platforms: ["darwin", "linux"] + results: + - spec: 'rust@1.75.0' + extra_attributes: + compilers: + rust: ".*/bin/rustc" + cargo: ".*/bin/cargo" +# If rustc is missing, then we don't detect specs +- layout: + - executables: + - "bin/cargo" + script: | + echo "cargo 1.75.0" + platforms: ["darwin", "linux"] + results: [] +# Check we can detect 2 different versions in the same folder +- layout: + - executables: + - "bin/rustc" + script: | + echo "rustc 1.75.0 (82e1608df 2023-12-21) (built from a source tarball)" + - executables: + - "bin/rustc-1.80" + script: | + echo "rustc 1.80.1 (3f5fd8dd4 2024-08-06) (built from a source tarball)" + - executables: + - "bin/cargo" + script: | + echo "cargo 1.75.0" + - executables: + - "bin/cargo-1.80" + script: | + echo "cargo 1.80.1 (376290515 2024-07-16)" + platforms: ["darwin", "linux"] + results: + - spec: 'rust@1.75.0' + extra_attributes: + compilers: + rust: ".*/bin/rustc" + cargo: ".*/bin/cargo" + - spec: 'rust@1.80.1' + extra_attributes: + compilers: + rust: ".*/bin/rustc-1.80" + cargo: ".*/bin/cargo-1.80" diff --git a/var/spack/repos/builtin/packages/rust/package.py b/var/spack/repos/builtin/packages/rust/package.py index dda63f60677..d25dafeccc3 100644 --- a/var/spack/repos/builtin/packages/rust/package.py +++ b/var/spack/repos/builtin/packages/rust/package.py @@ -102,22 +102,36 @@ class Rust(Package): conflicts("%oneapi", msg="Rust not compatible with Intel oneAPI compilers") extendable = True - executables = ["^rustc$", "^cargo$"] + executables = [r"^rustc(-[\d.]*)?$", r"^cargo(-[\d.]*)?$"] phases = ["configure", "build", "install"] @classmethod - def determine_spec_details(cls, prefix, exes_in_prefix): - rustc_candidates = [x for x in exes_in_prefix if os.path.basename(x) == "rustc"] - cargo_candidates = [x for x in exes_in_prefix if os.path.basename(x) == "cargo"] - # Both rustc and cargo must be present - if not (rustc_candidates and cargo_candidates): - return - output = Executable(rustc_candidates[0])("--version", output=str, error=str) - match = re.match(r"rustc (\S+)", output) - if match: - version_str = match.group(1) - return Spec.from_detection(f"rust@{version_str}", external_path=prefix) + def determine_version(csl, exe): + output = Executable(exe)("--version", output=str, error=str) + match = re.match(r"(rustc|cargo) (\S+)", output) + return match.group(2) if match else None + + @classmethod + def determine_variants(cls, exes, version_str): + rustc_candidates = [x for x in exes if "rustc" in os.path.basename(x)] + cargo_candidates = [x for x in exes if "cargo" in os.path.basename(x)] + extra_attributes = {} + if rustc_candidates: + extra_attributes.setdefault("compilers", {})["rust"] = rustc_candidates[0] + + if cargo_candidates: + extra_attributes["cargo"] = cargo_candidates[0] + + return "", extra_attributes + + @classmethod + def validate_detected_spec(cls, spec, extra_attributes): + if "cargo" not in extra_attributes: + raise RuntimeError(f"discarding {spec} since 'cargo' is missing") + + if not extra_attributes.get("compilers", {}).get("rust"): + raise RuntimeError(f"discarding {spec} since 'rustc' is missing") def setup_dependent_package(self, module, dependent_spec): module.cargo = Executable(os.path.join(self.spec.prefix.bin, "cargo"))