-
Notifications
You must be signed in to change notification settings - Fork 0
/
build_extension.py
121 lines (106 loc) · 3.68 KB
/
build_extension.py
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
import os
import platform
import subprocess
import warnings
from typing import Any, Dict, List
from setuptools import Extension
from setuptools.command.build_ext import build_ext
from setuptools.errors import CompileError
extension = Extension(
name="kmsr._core",
sources=[
"kmsr/_core.cpp",
"kmsr/cpp/ball.cpp",
"kmsr/cpp/cluster.cpp",
"kmsr/cpp/gonzalez.cpp",
"kmsr/cpp/heuristic.cpp",
"kmsr/cpp/util.cpp",
"kmsr/cpp/k_MSR.cpp",
"kmsr/cpp/point.cpp",
"kmsr/cpp/welzl.cpp",
"kmsr/cpp/yildirim.cpp",
],
include_dirs=["kmsr"],
)
def check_openmp_support() -> List[str]:
if platform.system() == "Windows":
return []
openmp_test_code = """
#include <omp.h>
#include <stdio.h>
int main() {
int nthreads;
#pragma omp parallel
{
nthreads = omp_get_num_threads();
}
printf("Number of threads = %d\\n", nthreads);
return 0;
}
"""
with open("test_openmp.c", "w") as f:
f.write(openmp_test_code)
try:
if platform.system() == "Darwin":
cmd_params = [
"-Xclang",
"-fopenmp",
"-I/usr/local/opt/libomp/include",
"-L/usr/local/opt/libomp/lib",
"-lomp",
]
else:
cmd_params = ["-fopenmp"]
# Try to compile the code with OpenMP support
result = subprocess.run(
["gcc"] + cmd_params + ["test_openmp.c", "-o", "test_openmp"],
stdout=subprocess.PIPE,
stderr=subprocess.PIPE,
)
if result.returncode != 0:
return []
# Run the compiled program
result = subprocess.run(
["./test_openmp"], stdout=subprocess.PIPE, stderr=subprocess.PIPE
)
if result.returncode == 0:
return cmd_params
else:
return []
finally:
os.remove("test_openmp.c")
if os.path.exists("test_openmp"):
os.remove("test_openmp")
class BuildExt(build_ext):
"""A custom build extension for adding -stdlib arguments for clang++."""
def build_extensions(self) -> None:
openmp_params = check_openmp_support()
# '-std=c++11' is added to `extra_compile_args` so the code can compile
# with clang++. This works across compilers (ignored by MSVC).
for extension in self.extensions:
extension.extra_compile_args.append("-std=c++11")
if len(openmp_params) == 0:
warnings.warn(
"\x1b[31;20m OpenMP is not installed on this system. "
"Please install it to have all the benefits from the program.\x1b[0m"
)
else:
for param in openmp_params:
extension.extra_compile_args.append(param)
if platform.system() == "Linux":
extension.extra_link_args.append("-fopenmp")
extension.extra_link_args.append("-lgomp")
try:
build_ext.build_extensions(self)
except CompileError:
# Workaround Issue #2.
# '-stdlib=libc++' is added to `extra_compile_args` and `extra_link_args`
# so the code can compile on macOS with Anaconda.
for extension in self.extensions:
extension.extra_compile_args.append("-stdlib=libc++")
extension.extra_link_args.append("-stdlib=libc++")
build_ext.build_extensions(self)
def build(setup_kwargs: Dict[str, Any]) -> None:
setup_kwargs.update(
{"ext_modules": [extension], "cmdclass": {"build_ext": BuildExt}}
)