Skip to content

Commit

Permalink
[SCons] Extract Python module-building environment setup
Browse files Browse the repository at this point in the history
  • Loading branch information
speth committed Sep 11, 2022
1 parent 9875cb4 commit 0958f7c
Show file tree
Hide file tree
Showing 3 changed files with 88 additions and 73 deletions.
78 changes: 7 additions & 71 deletions interfaces/cython/SConscript
Original file line number Diff line number Diff line change
@@ -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')
Expand All @@ -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'])

# 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_python_env(localenv)

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",
Expand All @@ -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
Expand Down Expand Up @@ -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)

Expand Down
2 changes: 1 addition & 1 deletion interfaces/cython/setup.cfg.in
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
81 changes: 80 additions & 1 deletion site_scons/buildutils.py
Original file line number Diff line number Diff line change
Expand Up @@ -11,10 +11,12 @@
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
Expand All @@ -25,7 +27,7 @@
"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, \
Expand Down Expand Up @@ -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,
Expand Down

0 comments on commit 0958f7c

Please sign in to comment.