[SCons] Extract Python module-building environment setup

This commit is contained in:
Ray Speth
2022-08-12 15:26:51 -04:00
committed by Ray Speth
parent 9875cb49f4
commit 0958f7cf31
3 changed files with 87 additions and 72 deletions

View File

@@ -1,8 +1,6 @@
"""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')
@@ -15,70 +13,8 @@ build(dataFiles)
# Install Python samples
install(localenv.RecursiveInstall, "$inst_sampledir/python", "#samples/python")
# Get information needed to build the Python module
script = """\
from sysconfig import *
import numpy
import json
import site
vars = get_config_vars()
vars["plat"] = get_platform()
vars["numpy_include"] = numpy.get_include()
vars["site_packages"] = [d for d in site.getsitepackages() if d.endswith("-packages")]
vars["user_site_packages"] = site.getusersitepackages()
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"]
site_packages = info["site_packages"]
user_site_packages = info["user_site_packages"]
localenv.Prepend(CPPPATH=[Dir('#include'), inc, numpy_include])
localenv.Prepend(LIBS=localenv['cantera_libs'])
setup_python_env(localenv)
# 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"})
if "numpy_1_7_API" in localenv:
localenv.Append(CPPDEFINES="NPY_NO_DEPRECATED_API=NPY_1_7_API_VERSION")
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",
@@ -104,15 +40,15 @@ for pyxfile in multi_glob(localenv, "cantera", "pyx"):
f"#build/temp-py/{pyxfile.name.split('.')[0]}", cythonized)
cython_obj.append(obj)
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")
plat = info['plat'].replace('-', '_').replace('.', '_')
wheel_name = (f"Cantera-{env['cantera_version']}-cp{py_version_nodot}"
f"-cp{py_version_nodot}-{plat}.whl")
wheel_name = ("Cantera-${cantera_version}-cp${py_version_nodot}"
"-cp${py_version_nodot}-${plat}.whl")
mod = build(localenv.Command(f"#build/python/dist/{wheel_name}", "setup.cfg",
build_cmd))
env['python_module'] = mod
@@ -153,9 +89,9 @@ else:
ignore_errors=True)
if user_install:
test_prefix = Path(user_site_packages).parents[2]
test_prefix = Path(localenv["user_site_packages"]).parents[2]
elif python_prefix is None:
test_prefix = Path(site_packages[0]).parents[2]
test_prefix = Path(localenv["site_packages"][0]).parents[2]
else:
test_prefix = Path(python_prefix)

View File

@@ -51,7 +51,7 @@ packages =
# The module extension needs to be here since we don't want setuptools to compile
# the extension, so there are no ``source`` files in the setup.py ``extension`` and
# we have to treat the module as package data.
cantera = *.pxd, *@module_ext@, test/*.txt, examples/*.txt, data/*.*
cantera = *.pxd, *@py_module_ext@, test/*.txt, examples/*.txt, data/*.*
[options.extras_require]
hdf5 = h5py

View File

@@ -11,10 +11,12 @@ import time
import shutil
import enum
from pathlib import Path
from pkg_resources import parse_version
import logging
from typing import TYPE_CHECKING
from collections.abc import Mapping as MappingABC
from SCons.Variables import PathVariable, EnumVariable, BoolVariable
from SCons.Script import Dir
try:
import numpy as np
@@ -25,7 +27,7 @@ __all__ = ("Option", "PathOption", "BoolOption", "EnumOption", "Configuration",
"logger", "remove_directory", "remove_file", "test_results",
"add_RegressionTest", "get_command_output", "listify", "which",
"ConfigBuilder", "multi_glob", "get_spawn", "quoted",
"get_pip_install_location", "compiler_flag_list")
"get_pip_install_location", "compiler_flag_list", "setup_python_env")
if TYPE_CHECKING:
from typing import Iterable, TypeVar, Union, List, Dict, Tuple, Optional, \
@@ -1246,6 +1248,83 @@ def get_command_output(cmd: str, *args: str, ignore_errors=False):
)
return data.stdout.strip()
_python_info = None
def setup_python_env(env):
"""Set up an environment for compiling Python extension modules"""
global _python_info
if _python_info is None:
# Get information needed to build the Python module
script = textwrap.dedent("""\
from sysconfig import *
import numpy
import json
import site
vars = get_config_vars()
vars["plat"] = get_platform()
vars["numpy_include"] = numpy.get_include()
vars["site_packages"] = [d for d in site.getsitepackages() if d.endswith("-packages")]
vars["user_site_packages"] = site.getusersitepackages()
print(json.dumps(vars))
""")
_python_info = json.loads(get_command_output(env["python_cmd"], "-c", script))
info = _python_info
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"]
plat = info['plat'].replace('-', '_').replace('.', '_')
numpy_include = info["numpy_include"]
env.Prepend(CPPPATH=[Dir('#include'), inc, numpy_include])
env.Prepend(LIBS=env['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 env["OS"] == "Windows"
and module_ext == ".pyd"
):
module_ext = f".cp{py_version_nodot}-{info['plat'].replace('-', '_')}.pyd"
env["py_module_ext"] = module_ext
env["py_version_nodot"] = py_version_nodot
env["py_plat"] = plat
env["site_packages"] = info["site_packages"]
env["user_site_packages"] = info["user_site_packages"]
# 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 env["HAS_CLANG"] and py_version_short == parse_version("3.8"):
env.Append(CXXFLAGS='-Wno-deprecated-declarations')
if "icc" in env["CC"]:
env.Append(CPPDEFINES={"CYTHON_FALLTHROUGH": " __attribute__((fallthrough))"})
if env['OS'] == 'Darwin':
env.Append(LINKFLAGS='-undefined dynamic_lookup')
elif env['OS'] == 'Windows':
env.Append(LIBPATH=prefix + '/libs')
if env['toolchain'] == 'mingw':
env.Append(LIBS=f"python{py_version_nodot}")
if env['OS_BITS'] == 64:
env.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"):
env.Append(CPPDEFINES={"_hypot": "hypot"})
if "numpy_1_7_API" in env:
env.Append(CPPDEFINES="NPY_NO_DEPRECATED_API=NPY_1_7_API_VERSION")
return env
def get_pip_install_location(
python_cmd: str,