Files
cantera/interfaces/python_sdist/SConscript
2023-04-25 18:14:19 -04:00

218 lines
7.5 KiB
Python

"""SDist of the Python Module"""
from pathlib import Path
import re
import shutil
from textwrap import dedent
from build import ProjectBuilder
from build.env import IsolatedEnvBuilder
from buildutils import logger
Import("env", "configh")
configh = configh.copy()
localenv = env.Clone()
sdist_targets = []
def sdist(targets):
sdist_targets.extend(targets)
return targets
def copy_ext_src(target, source, env):
target_ext = Path(target[0].abspath)
source_ext = Path(source[0].abspath)
if target_ext.is_dir():
shutil.rmtree(target_ext)
error_message = ("Please run 'git submodule update --init --recursive' to clone "
"these files.")
# fmt library
FMT_ROOT = target_ext / "fmt"
FMT_ROOT.mkdir(parents=True) # Needed to be able to use copy2
FMT_SOURCE = source_ext / "fmt"
if not FMT_SOURCE.is_dir():
raise ValueError("Missing fmt submodule. " + error_message)
for cc_file in (FMT_SOURCE / "src").glob("*.cc"):
shutil.copy2(cc_file, FMT_ROOT)
shutil.copytree(FMT_SOURCE / "include" / "fmt", FMT_ROOT / "fmt")
# yaml-cpp library
YAML_ROOT = target_ext / "yaml-cpp"
YAML_SOURCE = source_ext / "yaml-cpp"
if not YAML_SOURCE.is_dir():
raise ValueError("Missing yaml-cpp submodule. " + error_message)
shutil.copytree(YAML_SOURCE / "src", YAML_ROOT)
shutil.copytree(YAML_SOURCE / "include" / "yaml-cpp", YAML_ROOT / "yaml-cpp")
# SUNDIALS library
SUNDIALS_ROOT = target_ext / "sundials"
SUNDIALS_SOURCE = source_ext / "sundials"
if not SUNDIALS_SOURCE.is_dir():
raise ValueError("Missing sundials submodule. " + error_message)
subdirs = ["sundials", "nvector/serial", "cvodes", "ida", "sunmatrix/band",
"sunmatrix/dense", "sunmatrix/sparse", "sunlinsol/dense",
"sunlinsol/band", "sunlinsol/spgmr", "sunnonlinsol/newton"]
ignores = shutil.ignore_patterns("fsun*", "CMake*", "fmod", "fcmix")
for subdir in subdirs:
shutil.copytree(
SUNDIALS_SOURCE / "src" / subdir,
SUNDIALS_ROOT / subdir,
ignore=ignores,
)
def replace_git_hash(target, source, env):
# Avoid having to set a C preprocessor define at compile time, since
# the git commit is unknown from the sdist
target = Path(target[0].abspath)
source = Path(source[0].abspath)
git_commit_replaced = re.sub("#ifdef GIT_COMMIT.*?#endif",
f""" return "{env['git_commit']}";""",
source.read_text(),
flags=re.DOTALL)
target.write_text(git_commit_replaced)
# Use RecursiveInstall to be able to exclude files and folders.
sdist(localenv.RecursiveInstall(
"src",
"#src",
exclude=["fortran", "matlab", "clib", r"global\.cpp", "SCons.*"],
))
sdist(localenv.Command("src/base/global.cpp", "#src/base/global.cpp",
replace_git_hash))
# This is the only bit of the clib that we need for the Python interface
clib_defs_target = sdist(localenv.Command(
"include/cantera/clib/clib_defs.h",
"#include/cantera/clib/clib_defs.h",
Copy("$TARGET", "$SOURCE")
))
# The clib is handled by clib_defs_target. The include/cantera/ext folder is handled by
# ext_include_target. config.h needs to be filled on the user's machine by setup.py and
# the template file is handled by config_h_in_target.
include_target = sdist(localenv.RecursiveInstall(
"include",
"#include",
exclude=[
"clib$",
"ext$",
r"config\.h\.in",
r"config\.h",
],
))
localenv.Depends(clib_defs_target, include_target)
ext_include_target = sdist(localenv.Command(
"include/cantera/ext",
"#include/cantera/ext",
Copy("$TARGET", "$SOURCE"),
))
localenv.Depends(ext_include_target, env["ext_include_copies_target"])
localenv.Depends(ext_include_target, include_target)
ext_target = sdist(localenv.Command("ext", "#ext", copy_ext_src))
# Disable Lapack for the sdist
configh["CT_USE_LAPACK"] = None
configh["CT_SUNDIALS_USE_LAPACK"] = None
class DefineDict:
"""dict-like class used to fill config.h.in partially.
We cannot use buildutils.DefineDict because missing keys in self.data are replaced
with /* #undef {key} */, but here we want to leave those as template strings for
setup.py to fill in on the final build machine.
"""
def __init__(self, data):
self.data = data
def __getitem__(self, key):
if key not in self.data:
return f"{{{key!s}!s}}"
elif self.data[key] is None:
return f"/* #undef {key!s} */"
else:
return f"#define {key!s} {self.data[key]!s}"
def config_builder(target, source, env):
configh = env["configh"]
config_h = Path(str(target[0]))
config_h_in = Path(str(source[0])).read_text()
config_h.write_text(config_h_in.format_map(configh))
localenv["configh"] = DefineDict(configh)
config_h_in_target = sdist(localenv.Command(
"config.h.in",
"#include/cantera/base/config.h.in",
config_builder,
))
sdist(localenv.UnitsInterfaceBuilder(
"cantera/with_units/solution.py",
"#interfaces/cython/cantera/with_units/solution.py.in",
))
# Use RecursiveInstall to make sure that files are not overwritten during the copy.
# A normal Copy Action would fail because of the existing directories.
sdist(localenv.RecursiveInstall("cantera",
"#interfaces/cython/cantera",
exclude=["__pycache__"]))
sdist(localenv.RecursiveInstall("cantera/data",
"#build/data"))
# Copy the minimal Sundials configuration template into the sdist so that
# it can be filled in at compile time on the user's machine
sdist(localenv.Command("sundials_config.h.in", "#ext/sundials_config.h.in",
Copy("$TARGET", "$SOURCE")))
license = sdist(localenv.Command("LICENSE.txt", "#build/ext/LICENSE.txt",
Copy("$TARGET", "$SOURCE")))
localenv.Depends(license, localenv["license_target"])
sdist(localenv.SubstFile("setup.cfg", "setup.cfg.in"))
sdist(localenv.Command("README.rst", "#README.rst", Copy("$TARGET", "$SOURCE")))
def build_sdist(target, source, env):
build_dir = Path(source[0].abspath).parent
builder = ProjectBuilder(str(build_dir))
with IsolatedEnvBuilder() as build_env:
builder.python_executable = build_env.executable
builder.scripts_dir = build_env.scripts_dir
# first install the build dependencies
build_env.install(builder.build_system_requires)
# then get the extra required dependencies from the backend
build_env.install(builder.get_requires_for_build("sdist"))
builder.build("sdist", str(build_dir / "dist"), {})
def finish_sdist_message(target, source, env):
sdist = Path(source[0].path).name
message = dedent(f"""
*******************************************************
Python sdist '{sdist}' created successfully.
The sdist file is in the 'build/python_sdist/dist'
directory.
*******************************************************
""")
logger.info(message, print_level=False)
sdist_target = f"dist/Cantera-{env['cantera_version']}.tar.gz"
sdist_sources = ("setup.py", "pyproject.toml", "MANIFEST.in")
built_sdist = localenv.Command(sdist_target, sdist_sources, build_sdist)
finish_sdist = localenv.Command("finish_sdist", sdist_target, finish_sdist_message)
localenv.Depends(built_sdist, sdist_targets)
env.Alias("sdist", finish_sdist)