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 *
class Esmf(MakefilePackage):
class Esmf(MakefilePackage, PythonExtension):
"""The Earth System Modeling Framework (ESMF) is high-performance, flexible
software infrastructure for building and coupling weather, climate, and
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')",
)
# 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
depends_on("mpi", when="+mpi")
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("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
depends_on("perl", type="test")
@ -143,6 +161,29 @@ class Esmf(MakefilePackage):
# https://github.com/spack/spack/issues/35957
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
# the Cray compiler wrappers, where we need to swap
# 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
# 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")
with self.compiler.compiler_environment():
with self.pkg.compiler.compiler_environment():
gfortran_major_version = int(
spack.compiler.get_compiler_version_output(
self.compiler.fc, "-dumpversion"
self.pkg.compiler.fc, "-dumpversion"
).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")
elif self.compiler.name in ["clang", "apple-clang"]:
elif self.pkg.compiler.name in ["clang", "apple-clang"]:
env.set("ESMF_COMPILER", "gfortranclang")
with self.compiler.compiler_environment():
with self.pkg.compiler.compiler_environment():
gfortran_major_version = int(
spack.compiler.get_compiler_version_output(
self.compiler.fc, "-dumpversion"
self.pkg.compiler.fc, "-dumpversion"
).split(".")[0]
)
elif self.compiler.name == "nag":
elif self.pkg.compiler.name == "nag":
env.set("ESMF_COMPILER", "nag")
elif self.compiler.name == "pgi":
elif self.pkg.compiler.name == "pgi":
env.set("ESMF_COMPILER", "pgi")
elif self.compiler.name == "nvhpc":
elif self.pkg.compiler.name == "nvhpc":
env.set("ESMF_COMPILER", "nvhpc")
elif self.compiler.name == "cce":
elif self.pkg.compiler.name == "cce":
env.set("ESMF_COMPILER", "cce")
elif self.compiler.name == "aocc":
elif self.pkg.compiler.name == "aocc":
env.set("ESMF_COMPILER", "aocc")
else:
msg = "The compiler you are building with, "
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:
env.set("ESMF_CXX", spec["mpi"].mpicxx)
@ -265,7 +306,7 @@ def setup_build_environment(self, env):
env.set("ESMF_BOPT", "O")
if (
self.compiler.name in ["gcc", "clang", "apple-clang"]
self.pkg.compiler.name in ["gcc", "clang", "apple-clang"]
and gfortran_major_version >= 10
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
# 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")
# Allow override of ESMF_OS:
@ -430,5 +471,10 @@ def check(self):
def setup_dependent_build_environment(self, env, dependent_spec):
env.set("ESMFMKFILE", os.path.join(self.prefix.lib, "esmf.mk"))
def setup_run_environment(self, env):
env.set("ESMFMKFILE", os.path.join(self.prefix.lib, "esmf.mk"))
def install(self, pkg, spec, prefix):
make("install")
if "+python" in spec:
# build the python library
python_builder = PythonPipBuilder(pkg)
python_builder.install(pkg, spec, prefix)