Files
cantera/interfaces/cython/SConscript
Ray Speth d30b76a96a Fix errors due to setuptools 61.0
Something changed in setuptools that causes the old method of forcing
platform-specific wheels to be built to break for certain configurations
(specifically, Windows builds with Python 3.7). This alternative approach
appears to be more robust.

Also increase the logging from 'pip wheel' to help with debugging
2022-03-29 22:42:02 -04:00

165 lines
6.7 KiB
Python

"""Cython-based Python Module"""
import re
from pathlib import Path
from pkg_resources import parse_version
import json
from buildutils import *
Import('env', 'build', 'install')
localenv = env.Clone()
cythonized = localenv.Command(
'cantera/_cantera.cpp',
'cantera/_cantera.pyx',
'''${python_cmd} -c "import Cython.Build; Cython.Build.cythonize('${SOURCE}')"''')
for f in multi_glob(localenv, 'cantera', 'pyx', 'pxd'):
localenv.Depends(cythonized, f)
# This must be the path to the real pxd file, not a file node pointing at the
# possibly non-existent file in the build directory
pxd_file = File("#interfaces/cython/cantera/_cantera.pxd").abspath
for line in Path(pxd_file).read_text().splitlines():
m = re.search(r'from "(cantera.*?)"', line)
if m:
localenv.Depends('cantera/_cantera.cpp', '#include/' + m.group(1))
dataFiles = localenv.RecursiveInstall("cantera/data", "#build/data")
build(dataFiles)
testFiles = localenv.RecursiveInstall("cantera/test/data", "#test/data")
build(testFiles)
# Get information needed to build the Python module
script = """\
from sysconfig import *
import numpy
import json
vars = get_config_vars()
vars["plat"] = get_platform()
vars["numpy_include"] = numpy.get_include()
print(json.dumps(vars))
"""
info = json.loads(get_command_output(localenv["python_cmd"], "-c", script))
module_ext = info["EXT_SUFFIX"]
inc = info["INCLUDEPY"]
pylib = info.get("LDLIBRARY")
prefix = info["prefix"]
py_version_short = parse_version(info["py_version_short"])
py_version_full = parse_version(info["py_version"])
py_version_nodot = info["py_version_nodot"]
numpy_include = info["numpy_include"]
localenv.Prepend(CPPPATH=[Dir('#include'), inc, numpy_include])
localenv.Prepend(LIBS=localenv['cantera_libs'])
# Fix the module extension for Windows from the sysconfig library.
# See https://github.com/python/cpython/pull/22088 and
# https://bugs.python.org/issue39825
if (
py_version_full < parse_version("3.8.7")
and localenv["OS"] == "Windows"
and module_ext == ".pyd"
):
module_ext = f".cp{py_version_nodot}-{info['plat'].replace('-', '_')}.pyd"
# Don't print deprecation warnings for internal Python changes.
# Only applies to Python 3.8. The field that is deprecated in Python 3.8
# and causes the warnings to appear will be removed in Python 3.9 so no
# further warnings should be issued.
if localenv["HAS_CLANG"] and py_version_short == parse_version("3.8"):
localenv.Append(CXXFLAGS='-Wno-deprecated-declarations')
if "icc" in localenv["CC"]:
localenv.Append(CPPDEFINES={"CYTHON_FALLTHROUGH": " __attribute__((fallthrough))"})
if localenv['OS'] == 'Darwin':
localenv.Append(LINKFLAGS='-undefined dynamic_lookup')
elif localenv['OS'] == 'Windows':
localenv.Append(LIBPATH=prefix + '/libs')
if localenv['toolchain'] == 'mingw':
localenv.Append(LIBS=f"python{py_version_nodot}")
if localenv['OS_BITS'] == 64:
localenv.Append(CPPDEFINES='MS_WIN64')
# Fix for https://bugs.python.org/issue11566. Fixed in 3.7.3 and higher.
# See https://github.com/python/cpython/pull/11283
if py_version_full < parse_version("3.7.3"):
localenv.Append(CPPDEFINES={"_hypot": "hypot"})
elif localenv['OS'] == 'Cygwin':
# extract 'pythonX.Y' from 'libpythonX.Y.dll.a'
localenv.Append(LIBS=pylib[3:-6])
localenv["module_ext"] = module_ext
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"])
# Build the Python module
obj = localenv.SharedObject('#build/temp-py/_cantera', 'cantera/_cantera.cpp')
ext = localenv.LoadableModule(f"cantera/_cantera{module_ext}",
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")
plat = info['plat'].replace('-', '_').replace('.', '_')
wheel_name = (f"Cantera-{env['cantera_version']}-cp{py_version_nodot}"
f"-cp{py_version_nodot}-{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, testFiles, setup_cfg, readme, license,
"setup.py", "pyproject.toml"])
localenv.Depends(ext, localenv['cantera_staticlib'])
for f in (multi_glob(localenv, 'cantera', 'py') +
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"]
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"] = install_locs["platlib"]
env["ct_pyscriptdir"] = install_locs["scripts"]
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>"