Skip to content

Commit a23b83d

Browse files
committed
the last hand built suitesparse header and s imple cffi build script from grblas.
1 parent 43b1c77 commit a23b83d

File tree

7 files changed

+17415
-0
lines changed

7 files changed

+17415
-0
lines changed

setup.py

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
from setuptools import setup, find_packages
2+
3+
setup(
4+
name='suitesparse-graphblas',
5+
version='4.0.3',
6+
description='SuiteSparse:GraphBLAS Python bindings.',
7+
packages=find_packages(),
8+
author='Michel Pelletier, James Kitchen, Erik Welch',
9+
cffi_modules=["suitesparse/graphblas/build.py:ffibuilder"],
10+
install_requires=["cffi>=1.0.0"],
11+
)
12+

suitesparse/__init__.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+

suitesparse/graphblas/__init__.py

Whitespace-only changes.

suitesparse/graphblas/build.py

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
import os
2+
import sys
3+
from cffi import FFI
4+
5+
is_win = sys.platform.startswith("win")
6+
7+
ffibuilder = FFI()
8+
9+
ffibuilder.set_source(
10+
"suitesparse.graphblas",
11+
r"""#include "GraphBLAS.h" """,
12+
libraries=["graphblas"],
13+
include_dirs=[os.path.join(sys.prefix, "include")],
14+
)
15+
16+
thisdir = os.path.dirname(__file__)
17+
18+
header = "suitesparse_graphblas_4.0.3.h"
19+
if is_win:
20+
header = "suitesparse_graphblas_no_complex_4.0.3.h"
21+
gb_cdef = open(os.path.join(thisdir, header))
22+
23+
ffibuilder.cdef(gb_cdef.read())
24+
25+
if __name__ == "__main__":
26+
ffibuilder.compile(verbose=True)
Lines changed: 97 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,97 @@
1+
import sys
2+
import os
3+
import re
4+
5+
6+
def rename_defined_constants(text):
7+
# #define GB_PUBLIC extern
8+
text = text.replace("GB_PUBLIC", "extern")
9+
10+
return text
11+
12+
13+
def remove_directives(text):
14+
# There are a few cases of safe `#define` directives that we need to keep
15+
# - #define FOO 12
16+
# - #define BAR ...
17+
safe_define = re.compile(r"^#define\s+\w+\s+(\d+|\.{3})(\s|$)")
18+
19+
out = []
20+
multiline = False
21+
for line in text.splitlines():
22+
if not line:
23+
out.append(line)
24+
elif multiline:
25+
if line[-1] != "\\":
26+
multiline = False
27+
out.append(f"/* {line} */")
28+
elif line.lstrip()[0] == "#":
29+
if line[-1] == "\\":
30+
multiline = True
31+
if not multiline and safe_define.match(line):
32+
out.append(line)
33+
else:
34+
out.append(f"/* {line} */")
35+
else:
36+
out.append(line)
37+
return "\n".join(out)
38+
39+
40+
def remove_complex(text):
41+
out = []
42+
extern_block = None
43+
complex_extern = False
44+
for line in text.splitlines():
45+
if extern_block is not None:
46+
if "FC32" in line or "FC64" in line:
47+
complex_extern = True
48+
if line.replace(" ", "") == ");": # End of extern block
49+
extern_block.append(line)
50+
if complex_extern:
51+
for i in range(len(extern_block)):
52+
extern_block[i] = f"// {extern_block[i]}"
53+
out.extend(extern_block)
54+
extern_block = None
55+
complex_extern = False
56+
else:
57+
extern_block.append(line)
58+
elif not line:
59+
out.append(line)
60+
elif line.strip() == "extern":
61+
extern_block = [line]
62+
elif "FC32" in line or "FC64" in line:
63+
# Check if the line is a terminating line
64+
if re.search(r"FC(32|64)\s*;", line):
65+
# By commenting the terminating line out, we lose the closing semicolon
66+
# Walk up the lines, looking for the last non-commented out line
67+
# then replace the trailing comma with a semicolon
68+
for i in range(5): # it's never more than 5 away
69+
if out[-i].startswith("//"):
70+
continue
71+
last_comma_pos = out[-i].rfind(",")
72+
if last_comma_pos < 0:
73+
continue
74+
out[-i] = f"{out[-i][:last_comma_pos]};{out[-i][last_comma_pos+1:]}"
75+
break
76+
out.append(f"// {line}")
77+
else:
78+
out.append(line)
79+
return "\n".join(out)
80+
81+
82+
def main(filename):
83+
with open(filename, "r") as f:
84+
text = f.read()
85+
text = rename_defined_constants(text)
86+
text = remove_directives(text)
87+
if "_no_complex_" in filename:
88+
text = remove_complex(text)
89+
with open(filename, "w") as f:
90+
f.write(text)
91+
92+
93+
if __name__ == "__main__":
94+
filename = sys.argv[1]
95+
if not os.path.exists(filename):
96+
raise Exception(f'"{filename}" does not exist')
97+
main(filename)

0 commit comments

Comments
 (0)