From 7001a2a65ac8643c05a986b03e821fe22ef196ff Mon Sep 17 00:00:00 2001 From: Massimiliano Culpo Date: Tue, 13 Aug 2024 10:19:26 +0200 Subject: [PATCH] Fix a bug with automatic tag detection (#45696) Extracted from #45638 When adding the "detectable" tag to a package class that has the "tag" attribute inherited from a base class, we need to copy it to avoid modifying the base class. --- lib/spack/spack/package_base.py | 11 ++++--- lib/spack/spack/test/cmd/external.py | 28 ++++++++++++++--- lib/spack/spack/test/tag.py | 2 +- .../builtin.mock/packages/gcc/package.py | 6 +++- .../intel-oneapi-compilers/package.py | 6 +++- .../builtin.mock/packages/llvm/package.py | 30 +++++++++++++++++++ 6 files changed, 70 insertions(+), 13 deletions(-) create mode 100644 var/spack/repos/builtin.mock/packages/llvm/package.py diff --git a/lib/spack/spack/package_base.py b/lib/spack/spack/package_base.py index 6f2d4406bca..63aa02cc62a 100644 --- a/lib/spack/spack/package_base.py +++ b/lib/spack/spack/package_base.py @@ -197,13 +197,12 @@ def __init__(cls, name, bases, attr_dict): # that "foo" was a possible executable. # If a package has the executables or libraries attribute then it's - # assumed to be detectable + # assumed to be detectable. Add a tag, so finding them is faster if hasattr(cls, "executables") or hasattr(cls, "libraries"): - # Append a tag to each detectable package, so that finding them is faster - if not hasattr(cls, "tags"): - setattr(cls, "tags", [DetectablePackageMeta.TAG]) - elif DetectablePackageMeta.TAG not in cls.tags: - cls.tags.append(DetectablePackageMeta.TAG) + # To add the tag, we need to copy the tags attribute, and attach it to + # the current class. We don't use append, since it might modify base classes, + # if "tags" is retrieved following the MRO. + cls.tags = getattr(cls, "tags", []) + [DetectablePackageMeta.TAG] @classmethod def platform_executables(cls): diff --git a/lib/spack/spack/test/cmd/external.py b/lib/spack/spack/test/cmd/external.py index 2de6f18b506..0573bc04ae1 100644 --- a/lib/spack/spack/test/cmd/external.py +++ b/lib/spack/spack/test/cmd/external.py @@ -114,11 +114,31 @@ def test_find_external_cmd_not_buildable(mutable_config, working_env, mock_execu @pytest.mark.parametrize( "names,tags,exclude,expected", [ - # find --all - (None, ["detectable"], [], ["builtin.mock.find-externals1"]), + # find -all + ( + None, + ["detectable"], + [], + [ + "builtin.mock.find-externals1", + "builtin.mock.gcc", + "builtin.mock.llvm", + "builtin.mock.intel-oneapi-compilers", + ], + ), # find --all --exclude find-externals1 - (None, ["detectable"], ["builtin.mock.find-externals1"], []), - (None, ["detectable"], ["find-externals1"], []), + ( + None, + ["detectable"], + ["builtin.mock.find-externals1"], + ["builtin.mock.gcc", "builtin.mock.llvm", "builtin.mock.intel-oneapi-compilers"], + ), + ( + None, + ["detectable"], + ["find-externals1"], + ["builtin.mock.gcc", "builtin.mock.llvm", "builtin.mock.intel-oneapi-compilers"], + ), # find cmake (and cmake is not detectable) (["cmake"], ["detectable"], [], []), ], diff --git a/lib/spack/spack/test/tag.py b/lib/spack/spack/test/tag.py index 1f5affc549c..6a979eca4b6 100644 --- a/lib/spack/spack/test/tag.py +++ b/lib/spack/spack/test/tag.py @@ -153,7 +153,7 @@ def test_tag_no_tags(mock_packages): def test_tag_update_package(mock_packages): - mock_index = spack.repo.PATH.tag_index + mock_index = mock_packages.tag_index index = spack.tag.TagIndex(repository=mock_packages) for name in spack.repo.all_package_names(): index.update_package(name) diff --git a/var/spack/repos/builtin.mock/packages/gcc/package.py b/var/spack/repos/builtin.mock/packages/gcc/package.py index 31f7c95b536..05518419ddb 100644 --- a/var/spack/repos/builtin.mock/packages/gcc/package.py +++ b/var/spack/repos/builtin.mock/packages/gcc/package.py @@ -6,7 +6,7 @@ from spack.package import * -class Gcc(Package): +class Gcc(CompilerPackage, Package): """Simple compiler package.""" homepage = "http://www.example.com" @@ -18,6 +18,10 @@ class Gcc(Package): depends_on("conflict", when="@3.0") + c_names = ["gcc"] + cxx_names = ["g++"] + fortran_names = ["gfortran"] + def install(self, spec, prefix): # Create the minimal compiler that will fool `spack compiler find` mkdirp(prefix.bin) diff --git a/var/spack/repos/builtin.mock/packages/intel-oneapi-compilers/package.py b/var/spack/repos/builtin.mock/packages/intel-oneapi-compilers/package.py index f7c7dd67e52..78fdbe056c0 100644 --- a/var/spack/repos/builtin.mock/packages/intel-oneapi-compilers/package.py +++ b/var/spack/repos/builtin.mock/packages/intel-oneapi-compilers/package.py @@ -8,7 +8,7 @@ from spack.package import * -class IntelOneapiCompilers(Package): +class IntelOneapiCompilers(Package, CompilerPackage): """Simple compiler package.""" homepage = "http://www.example.com" @@ -18,6 +18,10 @@ class IntelOneapiCompilers(Package): version("2.0", md5="abcdef0123456789abcdef0123456789") version("3.0", md5="def0123456789abcdef0123456789abc") + c_names = ["icx"] + cxx_names = ["icpx"] + fortran_names = ["ifx"] + @property def compiler_search_prefix(self): return self.prefix.foo.bar.baz.bin diff --git a/var/spack/repos/builtin.mock/packages/llvm/package.py b/var/spack/repos/builtin.mock/packages/llvm/package.py new file mode 100644 index 00000000000..135ae5ebfa2 --- /dev/null +++ b/var/spack/repos/builtin.mock/packages/llvm/package.py @@ -0,0 +1,30 @@ +# Copyright 2013-2024 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 Llvm(Package, CompilerPackage): + """Simple compiler package.""" + + homepage = "http://www.example.com" + url = "http://www.example.com/gcc-1.0.tar.gz" + + version("18.1.8", md5="0123456789abcdef0123456789abcdef") + + variant( + "clang", default=True, description="Build the LLVM C/C++/Objective-C compiler frontend" + ) + + c_names = ["clang"] + cxx_names = ["clang++"] + fortran_names = ["flang"] + + def install(self, spec, prefix): + # Create the minimal compiler that will fool `spack compiler find` + mkdirp(prefix.bin) + with open(prefix.bin.gcc, "w") as f: + f.write('#!/bin/bash\necho "%s"' % str(spec.version)) + set_executable(prefix.bin.gcc)