gcc: add support for the D language (GDC) (#32330)

This commit is contained in:
Sergey Kosukhin 2022-09-12 11:27:35 +02:00 committed by GitHub
parent f7fbfc54b3
commit 7a93eddf1c
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23

View File

@ -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 @@ def determine_version(cls, exe):
@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 @@ def determine_variants(cls, exes, version_str):
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 @@ def validate_detected_spec(cls, spec, extra_attributes):
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 @@ def configure_args(self):
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 @@ def setup_run_environment(self, env):
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
),
),
)