diff --git a/repos/spack_repo/builtin/packages/acfl/detection_test.yaml b/repos/spack_repo/builtin/packages/acfl/detection_test.yaml index 23fc1930f22..b8452eed0ba 100644 --- a/repos/spack_repo/builtin/packages/acfl/detection_test.yaml +++ b/repos/spack_repo/builtin/packages/acfl/detection_test.yaml @@ -81,3 +81,22 @@ paths: echo "InstalledDir: /usr/bin" platforms: [linux] results: [] + +- layout: # does not detect ATfL + - executables: + - "bin/armclang" + - "bin/armclang++" + script: | + echo "Arm Toolchain for Linux 20.1 clang version 20.1.0" + echo "Target: aarch64--linux-gnu" + echo "Thread model: posix" + echo "InstalledDir: /opt/arm/arm-toolchain-for-linux-20.1/bin" + - executables: + - "bin/armflang" + script: | + echo "Arm Toolchain for Linux 20.1 flang version 20.1.0" + echo "Target: aarch64--linux-gnu" + echo "Thread model: posix" + echo "InstalledDir: /opt/arm/arm-toolchain-for-linux-20.1/bin" + platforms: [linux] + results: [] diff --git a/repos/spack_repo/builtin/packages/armpl_gcc/package.py b/repos/spack_repo/builtin/packages/armpl_gcc/package.py index 7a0a134833d..87f7993c8d3 100644 --- a/repos/spack_repo/builtin/packages/armpl_gcc/package.py +++ b/repos/spack_repo/builtin/packages/armpl_gcc/package.py @@ -403,6 +403,7 @@ class ArmplGcc(Package): conflicts("%msvc", msg="Not compatible with MSVC compiler.") + variant("examples", default=True, description="Build and run ArmPL examples after install") variant("ilp64", default=False, description="use ilp64 specific Armpl library") variant("shared", default=True, description="enable shared libs") variant( @@ -417,11 +418,11 @@ class ArmplGcc(Package): provides("lapack") provides("fftw-api@3") - depends_on("c", type="build") - depends_on("fortran", type="build") - requires("^[virtuals=c,fortran] gcc", msg="armpl-gcc is only compatible with the GCC compiler") - - depends_on("gmake", type="build") + with when("+examples"): + depends_on("c", type="build") + depends_on("fortran", type="build") + depends_on("gmake", type="build") + requires("^[virtuals=c,fortran] gcc", msg="armpl-gcc examples require GCC toolchain") # Run the installer with the desired install directory def install(self, spec, prefix): @@ -476,13 +477,16 @@ def blas_libs(self): recursive=True, ) - # Link the same libraries as the gcc used for Arm PL - armpl_libs += find_libraries( - ["libgomp", "libm"], - root=self["gcc"].prefix, - shared=self.spec.satisfies("+shared"), - recursive=True, - ) + # Link the same libraries as the gcc used for Arm PL, but only when + # building/running examples. Avoid injecting GCC runtimes by default + # to keep non-GCC toolchains (e.g., ATfL) conflict-free. + if self.spec.satisfies("+examples"): + armpl_libs += find_libraries( + ["libgomp", "libm"], + root=self["gcc"].prefix, + shared=self.spec.satisfies("+shared"), + recursive=True, + ) return armpl_libs @@ -520,7 +524,7 @@ def setup_run_environment(self, env: EnvironmentModifications) -> None: else: env.prepend_path("LD_LIBRARY_PATH", join_path(armpl_dir, "lib")) - @run_after("install") + @run_after("install", when="+examples") def check_install(self): armpl_dir = get_armpl_prefix(self.spec) suffix = get_armpl_suffix(self.spec) diff --git a/repos/spack_repo/builtin/packages/atfl/detection_test.yaml b/repos/spack_repo/builtin/packages/atfl/detection_test.yaml new file mode 100644 index 00000000000..2c6b6c6ffa3 --- /dev/null +++ b/repos/spack_repo/builtin/packages/atfl/detection_test.yaml @@ -0,0 +1,53 @@ +paths: +- layout: + - executables: + - "bin/armclang" + - "bin/armclang++" + script: | + echo "Arm Toolchain for Linux 20.1 clang version 20.1.0" + echo "Target: aarch64--linux-gnu" + echo "Thread model: posix" + echo "InstalledDir: /opt/arm/arm-toolchain-for-linux-20.1/bin" + - executables: + - "bin/armflang" + script: | + echo "Arm Toolchain for Linux 20.1 flang version 20.1.0" + echo "Target: aarch64--linux-gnu" + echo "Thread model: posix" + echo "InstalledDir: /opt/arm/arm-toolchain-for-linux-20.1/bin" + platforms: [linux] + results: + - spec: atfl@20.1.0 + extra_attributes: + compilers: + c: ".*/bin/armclang$" + cxx: ".*/bin/armclang[+][+]$" + fortran: ".*/bin/armflang$" + +# Negative tests: ensure ATfL does not detect other compilers +- layout: # does not detect upstream clang/LLVM + - executables: + - "bin/clang" + - "bin/clang++" + script: | + echo "clang version 20.1.0 (https://github.com/llvm/llvm-project abcdef)" + echo "Target: x86_64-unknown-linux-gnu" + echo "Thread model: posix" + echo "InstalledDir: /usr/bin" + platforms: [linux] + results: [] + +- layout: # does not detect ACfL + - executables: + - "bin/armclang" + - "bin/armclang++" + - "bin/armflang" + script: | + echo "Arm C/C++/Fortran Compiler version 19.3.1 (build number 75) (based on LLVM 7.0.2)" + echo "Target: aarch64--linux-gnu" + echo "Thread model: posix" + echo "InstalledDir:" + echo "/opt/arm/arm-hpc-compiler-19.3.5_Generic-AArch64_RHEL-7_aarch64-linux/bin" + platforms: [linux] + results: [] + diff --git a/repos/spack_repo/builtin/packages/atfl/package.py b/repos/spack_repo/builtin/packages/atfl/package.py new file mode 100644 index 00000000000..8b72e71dc8e --- /dev/null +++ b/repos/spack_repo/builtin/packages/atfl/package.py @@ -0,0 +1,337 @@ +# Copyright Spack Project Developers. See COPYRIGHT file. +# SPDX-License-Identifier: (Apache-2.0 OR MIT) + +"""Package for Arm Toolchain for Linux (ATfL).""" + +import os +from os import environ as env +from pathlib import Path +from typing import ClassVar, Dict, List, Optional, Sequence + +from spack_repo.builtin.build_systems.compiler import CompilerPackage +from spack_repo.builtin.build_systems.generic import Package + +from spack.package import * + +_VERSIONS = { + "21.1.1": { + "ubuntu22.04": ( + "8132ef95e4671c20a5f2b21dbe2d7ad8ae16137ea634e3e11096a8b87a3ffeee", + "https://developer.arm.com/packages/arm-toolchains%3Aubuntu-22/jammy/arm64/arm-toolchain-for-linux_21.1-81_arm64.deb", + ), + "ubuntu24.04": ( + "18f210fb04f27c50af932e1549deeda394a4ff351f9388957de0b096ab5f1db0", + "https://developer.arm.com/packages/arm-toolchains%3Aubuntu-24/noble/arm64/arm-toolchain-for-linux_21.1-81_arm64.deb", + ), + "rhel8": ( + "c5f0c3f7a25e269160aa3d684cb14edc57252bc38643929c67cda892f1c28dda", + "https://developer.arm.com/packages/arm-toolchains%3Arhel-8/el8/aarch64/arm-toolchain-for-linux-21.1-81.aarch64.rpm", + ), + "rhel9": ( + "3ead2888f8aa71b79a944270b1f7f723f56a7d161aeb2abaad14b3333f91f0e9", + "https://developer.arm.com/packages/arm-toolchains%3Arhel-9/el9/aarch64/arm-toolchain-for-linux-21.1-81.aarch64.rpm", + ), + "rhel10": ( + "a2fc22f18b7ee764ca11af48e6215b43ab47597cc67ba63fcccf0e40f9859a76", + "https://developer.arm.com/packages/arm-toolchains%3Arhel-10/el10/aarch64/arm-toolchain-for-linux-21.1-81.aarch64.rpm", + ), + "amzn2023": ( + "f7bc8c156aaa367dff1d6ced922e90581a04c8bac92e5675cc107eb3cd28428a", + "https://developer.arm.com/packages/arm-toolchains%3Aamzn-2023/al2023/aarch64/arm-toolchain-for-linux-21.1-81.aarch64.rpm", + ), + "sles15": ( + "8de2e35511d7f59a933c40e2e20ed2e01d018f51caba36dc5e9e0c6da71a3619", + "https://developer.arm.com/packages/arm-toolchains%3Asles-15/sl15/aarch64/arm-toolchain-for-linux-21.1-81.aarch64.rpm", + ), + }, + "20.1.0": { + "ubuntu22.04": ( + "944cf6420fb7b49c52d6c3d6f139fbb4896073ad401204a0cdf609faea360e73", + "https://developer.arm.com/packages/arm-toolchains%3Aubuntu-22/jammy/arm64/arm-toolchain-for-linux_20.1-65_arm64.deb", + ), + "ubuntu24.04": ( + "b9f7db08da8d579daad06999a65136e6b449b79c870a16264fbef1a7ab0cbe6b", + "https://developer.arm.com/packages/arm-toolchains%3Aubuntu-24/noble/arm64/arm-toolchain-for-linux_20.1-65_arm64.deb", + ), + "rhel8": ( + "795e6b74b2b538cc8431ff164c145aa7920ad9394ccf1d544a06667663b70f01", + "https://developer.arm.com/packages/arm-toolchains%3Arhel-8/el8/aarch64/arm-toolchain-for-linux-20.1-65.aarch64.rpm", + ), + "rhel9": ( + "453fc0f4d62968a833499f40a3d04deddbf292c9ff40fd3d246e84ba5b9d7d7a", + "https://developer.arm.com/packages/arm-toolchains%3Arhel-9/el9/aarch64/arm-toolchain-for-linux-20.1-65.aarch64.rpm", + ), + "amzn2023": ( + "87b2031290b9a48b0f2c700aff5f38560f7d1cb7d7f0b0f537ce18965260a54a", + "https://developer.arm.com/packages/arm-toolchains%3Aamzn-2023/al2023/aarch64/arm-toolchain-for-linux-20.1-65.aarch64.rpm", + ), + "sles15": ( + "506a9ddff6d7daf60e1d5b8cfed95f6f25e70db3cc9545d8ea3e9745056740f9", + "https://developer.arm.com/packages/arm-toolchains%3Asles-15/sl15/aarch64/arm-toolchain-for-linux-20.1-65.aarch64.rpm", + ), + }, +} + + +class Atfl(Package, CompilerPackage): + """Arm Toolchain for Linux (ATfL): LLVM-based AArch64 compilers. + + Installs the distro package (RPM/DEB) from Arm's repos, without root. + """ + + maintainers("pawosm-arm") + homepage = "https://developer.arm.com/documentation/110477" + + spack_os = host_platform().default_os + _alias: ClassVar[Dict[str, str]] = {"rocky10": "rhel10", "rocky9": "rhel9", "rocky8": "rhel8"} + os_key = _alias.get(spack_os, spack_os) + latest = max(_VERSIONS, key=Version) + for ver, per_os in _VERSIONS.items(): + pkg = per_os.get(os_key, None) + if pkg: + version(ver, sha256=pkg[0], url=pkg[1], expand=False, preferred=(ver == latest)) + + def archspec_name(self) -> str: + """Return the compiler name to use for archspec queries.""" + return "clang" + + # Linux AArch64 only + requires("platform=linux", msg="ATfL is only available on Linux") + conflicts("target=x86_64:", msg="ATfL provides AArch64-native compilers") + conflicts("target=ppc64:", msg="ATfL provides AArch64-native compilers") + conflicts("target=ppc64le:", msg="ATfL provides AArch64-native compilers") + + provides("c", "cxx") + provides("fortran") + + # Optional: allow opt-in to lld via the spec. Default is GNU ld for + # maximum compatibility; packages that prefer lld can request `%atfl+lld`. + variant("lld", default=False, description="Use lld linker (adds -fuse-ld=lld)") + + # Arm Performance Libraries are required: ATfL's default configuration + # links against ArmPL (e.g. -fveclib=ArmPL -> -lamath). Ensure it's + # present and wired into the environment so linking always succeeds. + depends_on("armpl-gcc~examples", type="run") + + def install(self, spec: Spec, prefix: Prefix) -> None: + """Install the package.""" + archive = self.stage.archive_file + extract_dir = Path(self.stage.path) / "extract" + extract_dir.mkdir(exist_ok=True) + if archive.endswith(".rpm"): + Executable("bsdtar")("-xf", archive, "-C", str(extract_dir)) + elif archive.endswith(".deb"): + Executable("dpkg-deb")("-x", archive, str(extract_dir)) + else: + raise InstallError(f"Unknown archive type: {archive}") + src_root = extract_dir / "opt" / "arm" / "arm-toolchain-for-linux" + + if not src_root.is_dir(): + raise InstallError(f"Expected payload at {src_root}") + + install_tree(str(src_root), prefix) + + def _toolroot(self) -> Path: + return Path(self.prefix) + + def _has_amath(self, d: Path) -> bool: + return (d / "libamath.a").exists() or any(d.glob("libamath.so*")) + + def _resolve_armpl_spec(self) -> Optional[Path]: + if "armpl-gcc" in self.spec: + armpl: Spec = self.spec["armpl-gcc"] # type: ignore[assignment] + parent = Path(armpl.prefix) + if parent.is_dir(): + # Typical armpl layout: /armpl__*/lib + for cand in parent.iterdir(): + if cand.is_dir() and cand.name.startswith("armpl_"): + libdir = cand / "lib" + if self._has_amath(libdir) or self._has_amath( + libdir / "aarch64-unknown-linux-gnu" + ): + return cand + # Fallback: parent already is the root + libdir = parent / "lib" + if ( + self._has_amath(libdir) + or self._has_amath(libdir / "aarch64-unknown-linux-gnu") + or self._has_amath(parent / "lib64") + ): + return parent + return None + + def _search_armpl_in_env(self) -> Optional[Path]: + for key in ("ATFL_ARMPL_ROOT", "ARMPL_ROOT", "ARMPL_DIR"): + val = env.get(key) + if val: + p = Path(val) + if p.exists(): + libdir = p / "lib" + if ( + self._has_amath(libdir) + or self._has_amath(libdir / "aarch64-unknown-linux-gnu") + or self._has_amath(p / "lib64") + ): + return p + return None + + def _detect_armpl_root(self) -> Optional[Path]: + tr = self._toolroot() # e.g. .../arm-toolchain-for-linux + if tr.exists(): + libdir = tr / "lib" + if ( + self._has_amath(libdir) + or self._has_amath(libdir / "aarch64-unknown-linux-gnu") + or self._has_amath(tr / "lib64") + ): + return tr + # Last resort: bounded search under the toolroot + for hit in list((tr).rglob("libamath.so*")) + list((tr).rglob("libamath.a")): + # Make the root be the first ancestor that has a 'lib' directory + # so setup_* can do root/'lib' and root/'lib/aarch64-unknown-linux-gnu'. + for ancestor in (hit.parent, hit.parent.parent, tr): + if (ancestor.parent / "lib").is_dir() or (ancestor / "lib").is_dir(): + return tr + return None + + def _find_armpl_root(self) -> Optional[Path]: + for method in ( + self._resolve_armpl_spec, + self._search_armpl_in_env, + self._detect_armpl_root, + ): + root = method() + if root: + return root + return None + + def _cc_path(self) -> str: + return str(self._toolroot() / "bin" / "armclang") + + def _cxx_path(self) -> str: + return str(self._toolroot() / "bin" / "armclang++") + + def _fortran_path(self) -> str: + return str(self._toolroot() / "bin" / "armflang") + + compiler_languages: Sequence[str] = ["c", "cxx", "fortran"] + c_names: ClassVar[List[str]] = ["armclang"] + cxx_names: ClassVar[List[str]] = ["armclang++"] + fortran_names: ClassVar[List[str]] = ["armflang"] + + compiler_version_argument = "--version" + compiler_version_regex = r"Arm Toolchain for Linux [\d\.]+ [cf]lang version ([\d\.]+)" + + debug_flags: ClassVar[Sequence[str]] = [ + "-gcodeview", + "-gdwarf-2", + "-gdwarf-3", + "-gdwarf-4", + "-gdwarf-5", + "-gline-tables-only", + "-gmodules", + "-g", + ] + + opt_flags: ClassVar[Sequence[str]] = [ + "-O0", + "-O1", + "-O2", + "-O3", + "-Ofast", + "-Os", + "-Oz", + "-Og", + "-O", + "-O4", + ] + + compiler_wrapper_link_paths: ClassVar[Dict[str, str]] = { + "c": str(Path("arm") / "armclang"), + "cxx": str(Path("arm") / "armclang++"), + "fortran": str(Path("arm") / "armflang"), + } + + implicit_rpath_libs: ClassVar[List[str]] = ["libclang"] + stdcxx_libs = ("-lstdc++",) + + def _standard_flag(self, *, language: str, standard: str) -> str: + flags = { + "cxx": {"11": "-std=c++11", "14": "-std=c++14", "17": "-std=c++17"}, + "c": {"99": "-std=c99", "11": "-std=c11"}, + } + return flags[language][standard] + + def setup_compiler_environment(self, env: EnvironmentModifications) -> None: + """Set up the build environment for this compiler package.""" + tr = self._toolroot() + armpl_root = self._find_armpl_root() + + env.prepend_path("PATH", Path(tr) / "bin") + env.prepend_path("CPATH", Path(tr) / "include") + env.prepend_path("LIBRARY_PATH", Path(tr) / "lib") + env.append_path("LIBRARY_PATH", Path(tr) / "lib" / "aarch64-unknown-linux-gnu") + env.prepend_path("MANPATH", Path(tr) / "share" / "man") + env.set("ARM_LINUX_COMPILER_DIR", str(tr)) + env.set("ATFL_ROOT", str(tr)) + env.set("ARM_LINUX_COMPILER_BUILD", str(self.spec.version)) + + # ArmPL runtime is required for the default toolchain config. + if armpl_root is not None: + libdir = armpl_root / "lib" + env.prepend_path("LIBRARY_PATH", str(libdir)) + env.prepend_path("LIBRARY_PATH", str(libdir / "aarch64-unknown-linux-gnu")) + env.prepend_path("LD_LIBRARY_PATH", str(libdir)) + env.prepend_path("LD_LIBRARY_PATH", str(libdir / "aarch64-unknown-linux-gnu")) + # Also advertise the root explicitly for any config that consults + # these variables. + env.set("ATFL_ARMPL_ROOT", str(armpl_root)) + env.set("ARMPL_ROOT", str(armpl_root)) + env.set("ARMPL_DIR", str(armpl_root)) + # If ArmPL is the OpenMP variant, prefer generic OpenMP flags + # only. Avoid pulling in GCC runtimes (libgomp/libgfortran) to + # keep ATfL functional without GCC and prevent runtime clashes. + if self.spec.satisfies("^armpl-gcc threads=openmp"): + env.append_flags("CFLAGS", "-fopenmp") + env.append_flags("CXXFLAGS", "-fopenmp") + env.append_flags("FFLAGS", "-fopenmp") + env.append_flags("FCFLAGS", "-fopenmp") + env.append_flags("LDFLAGS", "-fopenmp") + else: + raise InstallError( + "Arm Performance Libraries not found. Install 'armpl-gcc' or set ARMPL_ROOT." + ) + + # Prefer LLVM binutils; optionally enable lld if requested. + # Some system builds (e.g. ncurses with version scripts) are stricter + # under lld; make this opt-in to maximize compatibility. + use_lld = self.spec.satisfies("+lld") or ( + os.environ.get("ATFL_USE_LLD", "").lower() in {"1", "true", "yes", "on"} + ) + if use_lld: + env.append_flags("LDFLAGS", "-fuse-ld=lld") + + # Ensure ATfL's own Fortran runtime is available to dependents when + # a package's configure probes link small test programs. New Flang + # uses libFortranRuntime and libFortranDecimal; older toolchains used + # libflang_rt.runtime. Add them if present to avoid per-package tweaks. + rt_dir = tr / "lib" + if (rt_dir / "libFortranRuntime.so").exists() or (rt_dir / "libFortranRuntime.a").exists(): + env.append_flags("LDFLAGS", f"-L{rt_dir} -Wl,-rpath,{rt_dir}") + env.append_flags("LIBS", "-lFortranRuntime -lFortranDecimal") + elif (rt_dir / "libflang_rt.runtime.so").exists() or ( + rt_dir / "libflang_rt.runtime.a" + ).exists(): + env.append_flags("LDFLAGS", f"-L{rt_dir} -Wl,-rpath,{rt_dir}") + env.append_flags("LIBS", "-lflang_rt.runtime") + env.set("AR", str(tr / "bin" / "llvm-ar")) + env.set("NM", str(tr / "bin" / "llvm-nm")) + env.set("RANLIB", str(tr / "bin" / "llvm-ranlib")) + + def setup_run_environment(self, env: EnvironmentModifications) -> None: + """Set up the run environment for this package.""" + self.setup_compiler_environment(env) + + def setup_dependent_build_environment(self, env: EnvironmentModifications, _: Spec) -> None: + """Set up the build environment for packages that depend on this one.""" + self.setup_run_environment(env) diff --git a/repos/spack_repo/builtin/packages/llvm/detection_test.yaml b/repos/spack_repo/builtin/packages/llvm/detection_test.yaml index b8df8412f94..4cdf30a83e6 100644 --- a/repos/spack_repo/builtin/packages/llvm/detection_test.yaml +++ b/repos/spack_repo/builtin/packages/llvm/detection_test.yaml @@ -182,3 +182,16 @@ paths: echo "InstalledDir: /Library/Developer/CommandLineTools/usr/bin" platforms: ["darwin"] results: [] + +# ATfL should not be detected as upstream LLVM +- layout: + - executables: + - "bin/clang" + - "bin/clang++" + script: | + echo "Arm Toolchain for Linux 20.1 clang version 20.1.0" + echo "Target: aarch64--linux-gnu" + echo "Thread model: posix" + echo "InstalledDir: /opt/arm/arm-toolchain-for-linux-20.1/bin" + platforms: ["linux"] + results: [] diff --git a/repos/spack_repo/builtin/packages/llvm/package.py b/repos/spack_repo/builtin/packages/llvm/package.py index d727d595820..9a7e1c70e32 100644 --- a/repos/spack_repo/builtin/packages/llvm/package.py +++ b/repos/spack_repo/builtin/packages/llvm/package.py @@ -709,6 +709,8 @@ def determine_version(cls, exe): return None if "AMD" in output: return None + if "Arm Toolchain for Linux" in output: + return None match = re.search(cls.compiler_version_regex, output) if match: return match.group(match.lastindex)