Files
cantera/interfaces/cython/SConscript
Ray Speth 3c9af75f05 Bump vendored fmt library to version 9.1.0
This version properly exports all necessary DLL symbols, eliminating the
need to embed it separately in the Cython module.
2023-02-02 18:33:16 -06:00

141 lines
5.9 KiB
Python

"""Cython-based Python Module"""
import re
from pathlib import Path
from buildutils import *
Import('env', 'build', 'install')
localenv = env.Clone()
dataFiles = localenv.RecursiveInstall("cantera/data", "#build/data")
build(dataFiles)
# Install Python samples
install(localenv.RecursiveInstall, "$inst_sampledir/python", "#samples/python")
setup_python_env(localenv)
# Python module shouldn't explicitly link to Python library (added in other cases to
# support Python-based extensions), except when using MinGW
if localenv["toolchain"] != "mingw":
localenv["LIBS"] = [lib for lib in localenv["LIBS"] if not lib.startswith("python")]
setup_cfg = localenv.SubstFile("setup.cfg", "setup.cfg.in")
readme = localenv.Command("README.rst", "#README.rst", Copy("$TARGET", "$SOURCE"))
license = localenv.Command("LICENSE.txt", "#build/ext/LICENSE.txt",
Copy("$TARGET", "$SOURCE"))
localenv.Depends(license, localenv["license_target"])
directives = {"binding": True}
if env["coverage"]:
directives["linetrace"] = True
localenv.Append(CPPDEFINES={"CYTHON_TRACE": 1})
# Build the Python module
cython_obj = []
for pyxfile in multi_glob(localenv, "cantera", "pyx"):
cythonized = localenv.Command(
f"cantera/{pyxfile.name.replace('.pyx', '.cpp')}", pyxfile,
f'''${{python_cmd}} -c "import Cython.Build; Cython.Build.cythonize(r'${{SOURCE}}', compiler_directives={directives!r})"'''
)
for pxd in multi_glob(localenv, "cantera", "pxd"):
localenv.Depends(cythonized, pxd)
obj = localenv.SharedObject(
f"#build/temp-py/{pyxfile.name.split('.')[0]}", cythonized)
cython_obj.append(obj)
cython_obj.extend(env['python_ext_objects'])
module_ext = localenv["py_module_ext"]
ext = localenv.LoadableModule(f"cantera/_cantera{module_ext}",
cython_obj, LIBPREFIX="", SHLIBSUFFIX=module_ext,
SHLIBPREFIX="", LIBSUFFIXES=[module_ext])
build_cmd = ("$python_cmd_esc -m pip wheel -v --no-build-isolation --no-deps "
"--wheel-dir=build/python/dist build/python")
wheel_name = ("Cantera-${cantera_version}-cp${py_version_nodot}"
"-cp${py_version_nodot}-${py_plat}.whl")
mod = build(localenv.Command(f"#build/python/dist/{wheel_name}", "setup.cfg",
build_cmd))
env['python_module'] = mod
env['python_extension'] = ext
localenv.Depends(mod, [ext, dataFiles, setup_cfg, readme, license,
"setup.py", "pyproject.toml",
"cantera/test/README.txt", "cantera/examples/README.txt"])
if env['OS'] == 'Windows':
# On Windows, the cantera library directory is likely not to be on the path.
# However, Windows does search the directory containing a library (i.e. the
# Python extension module) for DLL dependencies.
dll = [f for f in localenv['cantera_shlib'] if f.name.endswith('.dll')][0]
copy_dll = localenv.Command(f'cantera/{dll.name}', dll, Copy("$TARGET", "$SOURCE"))
localenv.Depends(ext, copy_dll)
else:
localenv.Depends(ext, localenv['cantera_shlib'])
for f in (multi_glob(localenv, 'cantera', 'py') +
multi_glob(localenv, 'cantera/*', 'py')):
localenv.Depends(mod, f)
# Determine installation path and install the Python module
install_cmd = ["$python_cmd_esc", "-m", "pip", "install"]
user_install = False
python_prefix = None
if localenv['python_prefix'] == 'USER':
# Install to the OS-dependent user site-packages directory
install_cmd.append("--user")
user_install = True
elif localenv["python_prefix"]:
# A specific location for the Cantera python module has been given
install_cmd.append(f"--prefix={localenv.subst('$python_prefix')}")
python_prefix = localenv.subst("$python_prefix")
elif not env["default_prefix"]:
install_cmd.append(f"--prefix={env['prefix']}")
python_prefix = env["prefix"]
# Check for existing Python module installation. Allow pip to remove an existing
# installation only if we're installing to the same location. Also disable
# uninstallation if we're installing to a staging directory.
if env["stage_dir"]:
install_cmd.append("--ignore-installed")
else:
info = get_command_output(localenv["python_cmd"], "-m", "pip", "show", "cantera",
ignore_errors=True)
if user_install:
test_prefix = Path(localenv["user_site_packages"]).parents[2]
elif python_prefix is None:
test_prefix = Path(localenv["site_packages"][0]).parents[2]
else:
test_prefix = Path(python_prefix)
match = re.search(r"Location: (.*)\n", info, re.MULTILINE)
existing_prefix = Path(match.group(1)).parents[2] if match else None
if existing_prefix and existing_prefix != test_prefix:
install_cmd.append("--ignore-installed")
if env["stage_dir"]:
# Get the absolute path to the stage directory. If the stage directory is a relative
# path, consider it to be relative to the root of the Cantera source directory.
stage_dir = Path(env["stage_dir"])
if not stage_dir.is_absolute():
stage_dir = Path(Dir("#").abspath) / stage_dir
install_cmd.append(f"--root={stage_dir.resolve()}")
install_cmd.extend(("--no-build-isolation", "--no-deps", "-v", "--force-reinstall",
"build/python"))
if localenv['PYTHON_INSTALLER'] == 'direct':
mod_inst = install(localenv.Command, "dummy", mod, " ".join(install_cmd))
env["install_python_action"] = mod_inst
install_locs = get_pip_install_location(localenv["python_cmd"], user_install,
python_prefix)
env["python_module_loc"] = Path(install_locs["platlib"]).as_posix()
env["ct_pyscriptdir"] = Path(install_locs["scripts"]).as_posix()
elif localenv['PYTHON_INSTALLER'] == 'debian':
install(localenv.Command, 'dummy', mod,
'cd build/python && '
'$python_cmd_esc setup.py build --build-lib=. '
'install --install-layout=deb --no-compile --root=${python_prefix}')
env["python_module_loc"] = "<unspecified>"