mirror of
https://github.com/Cantera/cantera.git
synced 2025-02-25 18:55:29 -06:00
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
165 lines
6.7 KiB
Python
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>"
|