Skip to content

Commit 043d1a4

Browse files
committed
MSVC complex values
Fixes #64
1 parent 97fc4f9 commit 043d1a4

File tree

3 files changed

+90
-42
lines changed

3 files changed

+90
-42
lines changed

build.py

Lines changed: 76 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,76 @@
1+
import os
2+
import sys
3+
from pathlib import Path
4+
5+
from cffi import FFI
6+
from setuptools import Extension
7+
8+
is_win = sys.platform.startswith("win")
9+
ss_g = Path(__file__).parent / "suitesparse_graphblas"
10+
11+
ffibuilder = FFI()
12+
13+
include_dirs = [os.path.join(sys.prefix, "include")]
14+
library_dirs = [os.path.join(sys.prefix, "lib")]
15+
if is_win:
16+
include_dirs.append(os.path.join(sys.prefix, "Library", "include"))
17+
library_dirs.append(os.path.join(sys.prefix, "Library", "lib"))
18+
19+
# suitesparse.sh installs GraphBLAS.h here
20+
include_dirs.append(os.path.join("C:\\", "Program Files (x86)", "include"))
21+
# graphblas.lib and graphblas.dll.a
22+
library_dirs.append(os.path.join("C:\\", "Program Files (x86)", "lib"))
23+
# graphblas.dll
24+
library_dirs.append(os.path.join("C:\\", "Program Files (x86)", "bin"))
25+
26+
ffibuilder.set_source(
27+
"suitesparse_graphblas._graphblas",
28+
(ss_g / "source.c").read_text(),
29+
libraries=["graphblas"],
30+
include_dirs=include_dirs,
31+
library_dirs=library_dirs,
32+
)
33+
34+
ffibuilder.cdef((ss_g / "suitesparse_graphblas.h").read_text())
35+
36+
37+
def get_extension(apply_msvc_patch: bool = None, extra_compile_args=()):
38+
"""
39+
Get a setuptools.Extension version of this CFFI builder.
40+
41+
In other words, enables `setup(ext_modules=[get_extension()])`
42+
instead of `setup(cffi_modules=["build.py:ffibuilder"])`.
43+
44+
The main reason for this is to allow a patch for complex values when compiling on MSVC. MSVC famously lacks support
45+
for standard C complex types like `double _Complex` and `float _Complex`. Instead, MSVC has its own
46+
`_Dcomplex` and `_Fcomplex` types. Cffi's machinery cannot be made to work with these types, so we instead
47+
emit the regular standard C code and patch it manually.
48+
49+
:param apply_msvc_patch: whether to apply the MSVC patch. If None then auto-detect based on platform.
50+
:param extra_compile_args: forwarded to Extension constructor.
51+
"""
52+
code_path = ss_g / "_graphblas.c"
53+
ffibuilder.emit_c_code(str(code_path))
54+
55+
if apply_msvc_patch is None:
56+
apply_msvc_patch = is_win
57+
58+
if apply_msvc_patch:
59+
code = code_path.read_text()
60+
msvc_code = code.replace("float _Complex", "_Fcomplex").replace(
61+
"double _Complex", "_Dcomplex"
62+
)
63+
code_path.write_text(msvc_code)
64+
65+
return Extension(
66+
"suitesparse_graphblas._graphblas",
67+
[os.path.join("suitesparse_graphblas", "_graphblas.c")],
68+
libraries=["graphblas"],
69+
include_dirs=include_dirs,
70+
library_dirs=library_dirs,
71+
extra_compile_args=extra_compile_args,
72+
)
73+
74+
75+
if __name__ == "__main__":
76+
ffibuilder.compile(verbose=True)

setup.py

Lines changed: 14 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,10 @@
55
import numpy as np
66
from setuptools import Extension, setup
77

8+
# Add current directory to the Python path because it's not present when running `pip install .`
9+
sys.path.append(os.path.dirname(__file__))
10+
import build # noqa: E402
11+
812
try:
913
from Cython.Build import cythonize
1014
from Cython.Compiler.Options import get_directive_defaults
@@ -15,6 +19,13 @@
1519

1620
define_macros = [("NPY_NO_DEPRECATED_API", "NPY_1_7_API_VERSION")]
1721

22+
# /d2FH4- flag needed only for early Python 3.8 builds on Windows.
23+
# See https://cibuildwheel.readthedocs.io/en/stable/faq/
24+
# (Search for flag on page. Full link is long and causes the linter to fail the tests.)
25+
#
26+
# The /std:c11 flag is because the MSVC default is C89.
27+
extra_compile_args = ["/d2FH4-", "/std:c11"] if sys.platform == "win32" else []
28+
1829
if use_cython:
1930
suffix = ".pyx"
2031
directive_defaults = get_directive_defaults()
@@ -40,13 +51,15 @@
4051
[name],
4152
include_dirs=include_dirs,
4253
define_macros=define_macros,
54+
extra_compile_args=extra_compile_args,
4355
)
4456
for name in glob(f"suitesparse_graphblas/**/*{suffix}", recursive=True)
4557
]
4658
if use_cython:
4759
ext_modules = cythonize(ext_modules, include_path=include_dirs)
4860

61+
ext_modules.append(build.get_extension(extra_compile_args=extra_compile_args))
62+
4963
setup(
5064
ext_modules=ext_modules,
51-
cffi_modules=["suitesparse_graphblas/build.py:ffibuilder"],
5265
)

suitesparse_graphblas/build.py

Lines changed: 0 additions & 41 deletions
This file was deleted.

0 commit comments

Comments
 (0)