esmf package: add (optional) Python bindings (#45504)

* Add `+python` variant
* `esmf` package installs Python bindings when `+python` is set

Note: this does not inherit `PythonPackage`, which force an either/or
choice between the Makefile and Pip builder: it instantiates a
`PythonPipBuilder` as needed (when `+python` is set).
This commit is contained in:
Chris Marsh 2024-08-16 16:02:50 -06:00 committed by GitHub
parent 9ec8eaa0d3
commit f0f9a16e4f
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194

View File

@ -10,7 +10,7 @@
from spack.package import * from spack.package import *
class Esmf(MakefilePackage): class Esmf(MakefilePackage, PythonExtension):
"""The Earth System Modeling Framework (ESMF) is high-performance, flexible """The Earth System Modeling Framework (ESMF) is high-performance, flexible
software infrastructure for building and coupling weather, climate, and software infrastructure for building and coupling weather, climate, and
related Earth science applications. The ESMF defines an architecture for related Earth science applications. The ESMF defines an architecture for
@ -97,6 +97,10 @@ class Esmf(MakefilePackage):
description="Named variant for snapshots versions (e.g., 'b09')", description="Named variant for snapshots versions (e.g., 'b09')",
) )
# The way python is handled here is only avialable >=8.4.0
# https://github.com/esmf-org/esmf/releases/tag/v8.4.0
variant("python", default=False, description="Build python bindings", when="@8.4.0:")
# Optional dependencies # Optional dependencies
depends_on("mpi", when="+mpi") depends_on("mpi", when="+mpi")
depends_on("lapack@3:", when="+external-lapack") depends_on("lapack@3:", when="+external-lapack")
@ -112,6 +116,20 @@ class Esmf(MakefilePackage):
depends_on("parallelio@2.5.10: ~mpi", when="@8.5:+external-parallelio~mpi") depends_on("parallelio@2.5.10: ~mpi", when="@8.5:+external-parallelio~mpi")
depends_on("cmake@3.5.2:", type="build", when="~external-parallelio") depends_on("cmake@3.5.2:", type="build", when="~external-parallelio")
# python library
with when("+python"):
extends("python")
depends_on("py-pip")
depends_on("py-setuptools", type="build")
depends_on("py-wheel", type="build")
depends_on("py-mpi4py", when="+mpi")
depends_on("py-numpy")
# In esmf@8.4.0, esmx was introduced which depends on py-pyyaml
with when("@8.4.0:"):
depends_on("python", type="run")
depends_on("py-pyyaml", type="run")
# Testing dependencies # Testing dependencies
depends_on("perl", type="test") depends_on("perl", type="test")
@ -143,6 +161,29 @@ class Esmf(MakefilePackage):
# https://github.com/spack/spack/issues/35957 # https://github.com/spack/spack/issues/35957
patch("esmf_cpp_info.patch") patch("esmf_cpp_info.patch")
@when("+python")
def patch(self):
# The pyproject.toml file uses a dynamically generated version from git
# However, this results in a version of 0.0.0 and a mismatch with the loaded version
# so this hardcodes it to match the library's version
filter_file(
"""dynamic = \\[\\s+"version"\\s+\\]""",
f"""version = "{self.version}" """,
os.path.join("src/addon/esmpy/pyproject.toml"),
)
def setup_run_environment(self, env):
env.set("ESMFMKFILE", os.path.join(self.prefix.lib, "esmf.mk"))
class PythonPipBuilder(spack.build_systems.python.PythonPipBuilder):
@property
def build_directory(self):
return os.path.join(self.stage.source_path, "src/addon/esmpy")
class MakefileBuilder(spack.build_systems.makefile.MakefileBuilder):
# This is strictly required on Cray systems that use # This is strictly required on Cray systems that use
# the Cray compiler wrappers, where we need to swap # the Cray compiler wrappers, where we need to swap
# out the spack compiler wrappers in esmf.mk with the # out the spack compiler wrappers in esmf.mk with the
@ -214,38 +255,38 @@ def setup_build_environment(self, env):
# ESMF_COMPILER must be set to select which Fortran and # ESMF_COMPILER must be set to select which Fortran and
# C++ compilers are being used to build the ESMF library. # C++ compilers are being used to build the ESMF library.
if self.compiler.name == "gcc": if self.pkg.compiler.name == "gcc":
env.set("ESMF_COMPILER", "gfortran") env.set("ESMF_COMPILER", "gfortran")
with self.compiler.compiler_environment(): with self.pkg.compiler.compiler_environment():
gfortran_major_version = int( gfortran_major_version = int(
spack.compiler.get_compiler_version_output( spack.compiler.get_compiler_version_output(
self.compiler.fc, "-dumpversion" self.pkg.compiler.fc, "-dumpversion"
).split(".")[0] ).split(".")[0]
) )
elif self.compiler.name == "intel" or self.compiler.name == "oneapi": elif self.pkg.compiler.name == "intel" or self.pkg.compiler.name == "oneapi":
env.set("ESMF_COMPILER", "intel") env.set("ESMF_COMPILER", "intel")
elif self.compiler.name in ["clang", "apple-clang"]: elif self.pkg.compiler.name in ["clang", "apple-clang"]:
env.set("ESMF_COMPILER", "gfortranclang") env.set("ESMF_COMPILER", "gfortranclang")
with self.compiler.compiler_environment(): with self.pkg.compiler.compiler_environment():
gfortran_major_version = int( gfortran_major_version = int(
spack.compiler.get_compiler_version_output( spack.compiler.get_compiler_version_output(
self.compiler.fc, "-dumpversion" self.pkg.compiler.fc, "-dumpversion"
).split(".")[0] ).split(".")[0]
) )
elif self.compiler.name == "nag": elif self.pkg.compiler.name == "nag":
env.set("ESMF_COMPILER", "nag") env.set("ESMF_COMPILER", "nag")
elif self.compiler.name == "pgi": elif self.pkg.compiler.name == "pgi":
env.set("ESMF_COMPILER", "pgi") env.set("ESMF_COMPILER", "pgi")
elif self.compiler.name == "nvhpc": elif self.pkg.compiler.name == "nvhpc":
env.set("ESMF_COMPILER", "nvhpc") env.set("ESMF_COMPILER", "nvhpc")
elif self.compiler.name == "cce": elif self.pkg.compiler.name == "cce":
env.set("ESMF_COMPILER", "cce") env.set("ESMF_COMPILER", "cce")
elif self.compiler.name == "aocc": elif self.pkg.compiler.name == "aocc":
env.set("ESMF_COMPILER", "aocc") env.set("ESMF_COMPILER", "aocc")
else: else:
msg = "The compiler you are building with, " msg = "The compiler you are building with, "
msg += '"{0}", is not supported by ESMF.' msg += '"{0}", is not supported by ESMF.'
raise InstallError(msg.format(self.compiler.name)) raise InstallError(msg.format(self.pkg.compiler.name))
if "+mpi" in spec: if "+mpi" in spec:
env.set("ESMF_CXX", spec["mpi"].mpicxx) env.set("ESMF_CXX", spec["mpi"].mpicxx)
@ -265,7 +306,7 @@ def setup_build_environment(self, env):
env.set("ESMF_BOPT", "O") env.set("ESMF_BOPT", "O")
if ( if (
self.compiler.name in ["gcc", "clang", "apple-clang"] self.pkg.compiler.name in ["gcc", "clang", "apple-clang"]
and gfortran_major_version >= 10 and gfortran_major_version >= 10
and (self.spec.satisfies("@:8.2.99") or self.spec.satisfies("@8.3.0b09")) and (self.spec.satisfies("@:8.2.99") or self.spec.satisfies("@8.3.0b09"))
): ):
@ -277,7 +318,7 @@ def setup_build_environment(self, env):
# ESMF_OS must be set for Cray systems # ESMF_OS must be set for Cray systems
# But spack no longer gives arch == cray # But spack no longer gives arch == cray
if self.compiler.name == "cce" or "^cray-mpich" in self.spec: if self.pkg.compiler.name == "cce" or "^cray-mpich" in self.spec:
env.set("ESMF_OS", "Unicos") env.set("ESMF_OS", "Unicos")
# Allow override of ESMF_OS: # Allow override of ESMF_OS:
@ -430,5 +471,10 @@ def check(self):
def setup_dependent_build_environment(self, env, dependent_spec): def setup_dependent_build_environment(self, env, dependent_spec):
env.set("ESMFMKFILE", os.path.join(self.prefix.lib, "esmf.mk")) env.set("ESMFMKFILE", os.path.join(self.prefix.lib, "esmf.mk"))
def setup_run_environment(self, env): def install(self, pkg, spec, prefix):
env.set("ESMFMKFILE", os.path.join(self.prefix.lib, "esmf.mk")) make("install")
if "+python" in spec:
# build the python library
python_builder = PythonPipBuilder(pkg)
python_builder.install(pkg, spec, prefix)