From 7a93eddf1c69e59a3f0f33729f9c57eb50d81df1 Mon Sep 17 00:00:00 2001 From: Sergey Kosukhin Date: Mon, 12 Sep 2022 11:27:35 +0200 Subject: gcc: add support for the D language (GDC) (#32330) --- var/spack/repos/builtin/packages/gcc/package.py | 160 +++++++++++++++++++++++- 1 file changed, 157 insertions(+), 3 deletions(-) (limited to 'var') diff --git a/var/spack/repos/builtin/packages/gcc/package.py b/var/spack/repos/builtin/packages/gcc/package.py index 44e5c8f531..c7d0b0b0bf 100644 --- a/var/spack/repos/builtin/packages/gcc/package.py +++ b/var/spack/repos/builtin/packages/gcc/package.py @@ -87,13 +87,17 @@ class Gcc(AutotoolsPackage, GNUMirrorPackage): version("4.5.4", sha256="eef3f0456db8c3d992cbb51d5d32558190bc14f3bc19383dd93acc27acc6befc") # We specifically do not add 'all' variant here because: - # (i) Ada, Go, Jit, and Objective-C++ are not default languages. + # (i) Ada, D, Go, Jit, and Objective-C++ are not default languages. # In that respect, the name 'all' is rather misleading. # (ii) Languages other than c,c++,fortran are prone to configure bug in GCC # For example, 'java' appears to ignore custom location of zlib # (iii) meaning of 'all' changes with GCC version, i.e. 'java' is not part # of gcc7. Correctly specifying conflicts() and depends_on() in such a # case is a PITA. + # + # Also note that some languages get enabled by the configure scripts even if not listed in the + # arguments. For example, c++ is enabled when the bootstrapping is enabled and lto is enabled + # when the link time optimization support is enabled. variant( "languages", default="c,c++,fortran", @@ -102,6 +106,7 @@ class Gcc(AutotoolsPackage, GNUMirrorPackage): "brig", "c", "c++", + "d", "fortran", "go", "java", @@ -234,6 +239,45 @@ class Gcc(AutotoolsPackage, GNUMirrorPackage): # See https://gcc.gnu.org/gcc-5/changes.html conflicts("languages=jit", when="@:4") + with when("languages=d"): + # The very first version of GDC that became part of GCC already supported version 2.076 of + # the language and runtime. + # See https://wiki.dlang.org/GDC#Status + provides("D@2") + + # Support for the D programming language has been added to GCC 9. + # See https://gcc.gnu.org/gcc-9/changes.html#d + conflicts("@:8", msg="support for D has been added in GCC 9.1") + + # Versions of GDC prior to 12 can be built with an ISO C++11 compiler. Starting version 12, + # the D frontend requires a working GDC. Moreover, it is strongly recommended to use an + # older version of GDC to build GDC. + # See https://gcc.gnu.org/install/prerequisites.html#GDC-prerequisite + with when("@12:"): + # All versions starting 12 have to be built GCC: + for c in spack.compilers.supported_compilers(): + if c != "gcc": + conflicts("%{0}".format(c)) + + # And it has to be GCC older than the version we build: + vv = ["11", "12.1.0", "12.2.0"] + for prev_v, curr_v in zip(vv, vv[1:]): + conflicts( + "%gcc@{0}:".format(curr_v), + when="@{0}".format(curr_v), + msg="'gcc@{0} languages=d' requires '%gcc@:{1}' " + "with the D language support".format(curr_v, prev_v), + ) + + # In principle, it is possible to have GDC even with GCC 5. + # See https://github.com/D-Programming-GDC/gdc + # We, however, require at least the oldest version that officially supports GDC. It is + # also a good opportunity to tell the users that they need a working GDC: + conflicts( + "%gcc@:8", + msg="'gcc@12: languages=d' requires '%gcc@9:' with the D language support", + ) + with when("+nvptx"): depends_on("cuda") resource( @@ -260,6 +304,7 @@ class Gcc(AutotoolsPackage, GNUMirrorPackage): conflicts("languages=jit") conflicts("languages=objc") conflicts("languages=obj-c++") + conflicts("languages=d") # NVPTX build disables bootstrap conflicts("+bootstrap") @@ -383,7 +428,7 @@ class Gcc(AutotoolsPackage, GNUMirrorPackage): @classproperty def executables(cls): - names = [r"gcc", r"[^\w]?g\+\+", r"gfortran"] + names = [r"gcc", r"[^\w]?g\+\+", r"gfortran", r"gdc"] suffixes = [r"", r"-mp-\d+\.\d", r"-\d+\.\d", r"-\d+", r"\d\d"] return [r"".join(x) for x in itertools.product(names, suffixes)] @@ -443,7 +488,14 @@ class Gcc(AutotoolsPackage, GNUMirrorPackage): @classmethod def determine_variants(cls, exes, version_str): languages, compilers = set(), {} - for exe in exes: + # There are often at least two copies (not symlinks) of each compiler executable in the + # same directory: one with a canonical name, e.g. "gfortran", and another one with the + # target prefix, e.g. "x86_64-pc-linux-gnu-gfortran". There also might be a copy of "gcc" + # with the version suffix, e.g. "x86_64-pc-linux-gnu-gcc-6.3.0". To ensure the consistency + # of values in the "compilers" dictionary (i.e. we prefer all of them to reference copies + # with canonical names if possible), we iterate over the executables in the reversed sorted + # order: + for exe in sorted(exes, reverse=True): basename = os.path.basename(exe) if "g++" in basename: languages.add("c++") @@ -454,6 +506,9 @@ class Gcc(AutotoolsPackage, GNUMirrorPackage): elif "gcc" in basename: languages.add("c") compilers["c"] = exe + elif "gdc" in basename: + languages.add("d") + compilers["d"] = exe variant_str = "languages={0}".format(",".join(languages)) return variant_str, {"compilers": compilers} @@ -469,6 +524,7 @@ class Gcc(AutotoolsPackage, GNUMirrorPackage): for constraint, key in { "languages=c": "c", "languages=c++": "cxx", + "languages=d": "d", "languages=fortran": "fortran", }.items(): if spec.satisfies(constraint, strict=True): @@ -720,6 +776,18 @@ class Gcc(AutotoolsPackage, GNUMirrorPackage): options.append("--with-boot-ldflags=" + boot_ldflags) options.append("--with-build-config=spack") + if "languages=d" in spec: + # Phobos is the standard library for the D Programming Language. The documentation says + # that on some targets, 'libphobos' is not enabled by default, but compiles and works + # if '--enable-libphobos' is used. Specifics are documented for affected targets. + # See https://gcc.gnu.org/install/prerequisites.html#GDC-prerequisite + # Unfortunately, it is unclear where exactly the aforementioned specifics are + # documented but GDC seems to be unusable without the library, therefore we enable it + # explicitly: + options.append("--enable-libphobos") + if spec.satisfies("@12:"): + options.append("GDC={0}".format(self.detect_gdc())) + return options # run configure/make/make(install) for the nvptx-none target @@ -893,3 +961,89 @@ class Gcc(AutotoolsPackage, GNUMirrorPackage): env.set(lang.upper(), abspath) # Stop searching filename/regex combos for this language break + + def detect_gdc(self): + """Detect and return the path to GDC that belongs to the same instance of GCC that is used + by self.compiler. + + If the path cannot be detected, raise InstallError with recommendations for the users on + how to circumvent the problem. + + Should be use only if self.spec.satisfies("@12: languages=d") + """ + # Detect GCC package in the directory of the GCC compiler + # or in the $PATH if self.compiler.cc is not an absolute path: + from spack.detection import by_executable + + compiler_dir = os.path.dirname(self.compiler.cc) + detected_packages = by_executable( + [self.__class__], path_hints=([compiler_dir] if os.path.isdir(compiler_dir) else None) + ) + + # We consider only packages that satisfy the following constraint: + required_spec = Spec("languages=c,c++,d") + candidate_specs = [ + p.spec + for p in filter( + lambda p: p.spec.satisfies(required_spec), detected_packages.get(self.name, ()) + ) + ] + + if candidate_specs: + # We now need to filter specs that match the compiler version: + compiler_spec = Spec(repr(self.compiler.spec)) + + # First, try to filter specs that satisfy the compiler spec: + new_candidate_specs = list( + filter(lambda s: s.satisfies(compiler_spec), candidate_specs) + ) + + # The compiler version might be more specific than what we can detect. For example, the + # user might have "gcc@10.2.1-sys" as the compiler spec in compilers.yaml. In that + # case, we end up with an empty list of candidates. To circumvent the problem, we try + # to filter specs that are satisfied by the compiler spec: + if not new_candidate_specs: + new_candidate_specs = list( + filter(lambda s: compiler_spec.satisfies(s), candidate_specs) + ) + + candidate_specs = new_candidate_specs + + error_nl = "\n " # see SpackError.__str__() + + if not candidate_specs: + raise InstallError( + "Cannot detect GDC", + long_msg="Starting version 12, the D frontend requires a working GDC." + "{0}You can install it with Spack by running:" + "{0}{0}spack install gcc@9:11 languages=c,c++,d" + "{0}{0}Once that has finished, you will need to add it to your compilers.yaml file" + "{0}and use it to install this spec (i.e. {1} ...).".format( + error_nl, self.spec.format("{name}{@version} {variants.languages}") + ), + ) + elif len(candidate_specs) == 0: + return candidate_specs[0].extra_attributes["compilers"]["d"] + else: + # It is rather unlikely to end up here but let us try to resolve the ambiguity: + candidate_gdc = candidate_specs[0].extra_attributes["compilers"]["d"] + if all( + candidate_gdc == s.extra_attributes["compilers"]["d"] for s in candidate_specs[1:] + ): + # It does not matter which one we take if they are all the same: + return candidate_gdc + else: + raise InstallError( + "Cannot resolve ambiguity when detecting GDC that belongs to " + "%{0}".format(self.compiler.spec), + long_msg="The candidates are:{0}{0}{1}{0}".format( + error_nl, + error_nl.join( + "{0} (cc: {1})".format( + s.extra_attributes["compilers"]["d"], + s.extra_attributes["compilers"]["c"], + ) + for s in candidate_specs + ), + ), + ) -- cgit v1.2.3-70-g09d2