Skip to content

Commit 4c1e8ad

Browse files
committed
Move build logic to setup.py (closes #60)
1 parent dad0024 commit 4c1e8ad

File tree

2 files changed

+152
-93
lines changed

2 files changed

+152
-93
lines changed

Makefile

Lines changed: 9 additions & 83 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
.PHONY: check-env compile clean all distclean test debug sdist clean-libuv
1+
.PHONY: compile clean all distclean test debug sdist clean-libuv
22
.PHONY: release sdist-libuv docs
33

44

@@ -15,10 +15,6 @@ clean:
1515
find . -name '__pycache__' | xargs rm -rf
1616

1717

18-
check-env:
19-
$(PYTHON) -c "import cython; (cython.__version__ < '0.24') and exit(1)"
20-
21-
2218
clean-libuv:
2319
(cd vendor/libuv; git clean -dfX)
2420

@@ -30,16 +26,15 @@ sdist-libuv: clean-libuv
3026
distclean: clean clean-libuv
3127

3228

33-
compile: check-env clean
34-
$(PYTHON) -m cython -3 uvloop/loop.pyx
35-
@echo "$$UVLOOP_BUILD_PATCH_SCRIPT" | $(PYTHON)
36-
$(PYTHON) setup.py build_ext --inplace
29+
compile: clean
30+
$(PYTHON) setup.py build_ext --inplace --cython-always
3731

3832

39-
debug: check-env clean
40-
$(PYTHON) -m cython -3 -a -p uvloop/loop.pyx
41-
@echo "$$UVLOOP_BUILD_PATCH_SCRIPT" | $(PYTHON)
42-
$(PYTHON) setup.py build_ext --inplace \
33+
debug: clean
34+
$(PYTHON) setup.py build_ext --inplace --debug \
35+
--cython-always \
36+
--cython-annotate \
37+
--cython-directives="linetrace=True" \
4338
--define UVLOOP_DEBUG,CYTHON_TRACE,CYTHON_TRACE_NOGIL
4439

4540

@@ -56,74 +51,5 @@ sdist: clean compile test sdist-libuv
5651
$(PYTHON) setup.py sdist
5752

5853

59-
release: clean compile test sdist-libuv
54+
release: distclean compile test sdist-libuv
6055
$(PYTHON) setup.py sdist bdist_wheel upload
61-
62-
63-
# Script to patch Cython 'async def' coroutines to have a 'tp_iter' slot,
64-
# which makes them compatible with 'yield from' without the
65-
# `asyncio.coroutine` decorator.
66-
define UVLOOP_BUILD_PATCH_SCRIPT
67-
import re
68-
69-
with open('uvloop/loop.c', 'rt') as f:
70-
src = f.read()
71-
72-
src = re.sub(
73-
r'''
74-
\s* offsetof\(__pyx_CoroutineObject,\s*gi_weakreflist\),
75-
\s* 0,
76-
\s* 0,
77-
\s* __pyx_Coroutine_methods,
78-
\s* __pyx_Coroutine_memberlist,
79-
\s* __pyx_Coroutine_getsets,
80-
''',
81-
82-
r'''
83-
offsetof(__pyx_CoroutineObject, gi_weakreflist),
84-
__Pyx_Coroutine_await, /* tp_iter */
85-
0,
86-
__pyx_Coroutine_methods,
87-
__pyx_Coroutine_memberlist,
88-
__pyx_Coroutine_getsets,
89-
''',
90-
91-
src, flags=re.X)
92-
93-
# Fix a segfault in Cython.
94-
src = re.sub(
95-
r'''
96-
\s* __Pyx_Coroutine_get_qualname\(__pyx_CoroutineObject\s+\*self\)
97-
\s* {
98-
\s* Py_INCREF\(self->gi_qualname\);
99-
''',
100-
101-
r'''
102-
__Pyx_Coroutine_get_qualname(__pyx_CoroutineObject *self)
103-
{
104-
if (self->gi_qualname == NULL) { return __pyx_empty_unicode; }
105-
Py_INCREF(self->gi_qualname);
106-
''',
107-
108-
src, flags=re.X)
109-
110-
src = re.sub(
111-
r'''
112-
\s* __Pyx_Coroutine_get_name\(__pyx_CoroutineObject\s+\*self\)
113-
\s* {
114-
\s* Py_INCREF\(self->gi_name\);
115-
''',
116-
117-
r'''
118-
__Pyx_Coroutine_get_name(__pyx_CoroutineObject *self)
119-
{
120-
if (self->gi_name == NULL) { return __pyx_empty_unicode; }
121-
Py_INCREF(self->gi_name);
122-
''',
123-
124-
src, flags=re.X)
125-
126-
with open('uvloop/loop.c', 'wt') as f:
127-
f.write(src)
128-
endef
129-
export UVLOOP_BUILD_PATCH_SCRIPT

setup.py

Lines changed: 143 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@
2020
from setuptools.command.build_ext import build_ext
2121

2222

23+
CFLAGS = ['-O2']
2324
LIBUV_DIR = os.path.join(os.path.dirname(__file__), 'vendor', 'libuv')
2425

2526

@@ -30,17 +31,149 @@ def discover_tests():
3031

3132

3233
class libuv_build_ext(build_ext):
33-
build_ext.user_options.extend([
34-
("use-system-libuv", None,
35-
"Use the system provided libuv, instead of the bundled one")
36-
])
34+
user_options = build_ext.user_options + [
35+
('cython-always', None,
36+
'run cythonize() even if .c files are present'),
37+
('cython-annotate', None,
38+
'Produce a colorized HTML version of the Cython source.'),
39+
('cython-directives=', None,
40+
'Cythion compiler directives'),
41+
('use-system-libuv', None,
42+
'Use the system provided libuv, instead of the bundled one'),
43+
]
3744

38-
build_ext.boolean_options.extend(["use-system-libuv"])
45+
boolean_options = build_ext.boolean_options + [
46+
'cython-always',
47+
'cython-annotate',
48+
'use-system-libuv',
49+
]
3950

4051
def initialize_options(self):
41-
build_ext.initialize_options(self)
42-
if getattr(self, 'use_system_libuv', None) is None:
43-
self.use_system_libuv = 0
52+
super().initialize_options()
53+
self.use_system_libuv = False
54+
self.cython_always = False
55+
self.cython_annotate = None
56+
self.cython_directives = None
57+
58+
def finalize_options(self):
59+
need_cythonize = self.cython_always
60+
cfiles = {}
61+
62+
for extension in self.distribution.ext_modules:
63+
for i, sfile in enumerate(extension.sources):
64+
if sfile.endswith('.pyx'):
65+
prefix, ext = os.path.splitext(sfile)
66+
cfile = prefix + '.c'
67+
68+
if os.path.exists(cfile) and not self.cython_always:
69+
extension.sources[i] = cfile
70+
else:
71+
if os.path.exists(cfile):
72+
cfiles[cfile] = os.path.getmtime(cfile)
73+
else:
74+
cfiles[cfile] = 0
75+
need_cythonize = True
76+
77+
if need_cythonize:
78+
try:
79+
import Cython
80+
except ImportError:
81+
raise RuntimeError(
82+
'please install Cython to compile asyncpg from source')
83+
84+
if Cython.__version__ < '0.24':
85+
raise RuntimeError(
86+
'uvloop requires Cython version 0.24 or greater')
87+
88+
from Cython.Build import cythonize
89+
90+
directives = {}
91+
if self.cython_directives:
92+
for directive in self.cython_directives.split(','):
93+
k, _, v = directive.partition('=')
94+
if v.lower() == 'false':
95+
v = False
96+
if v.lower() == 'true':
97+
v = True
98+
99+
directives[k] = v
100+
101+
self.distribution.ext_modules[:] = cythonize(
102+
self.distribution.ext_modules,
103+
compiler_directives=directives,
104+
annotate=self.cython_annotate)
105+
106+
for cfile, timestamp in cfiles.items():
107+
if os.path.getmtime(cfile) != timestamp:
108+
# The file was recompiled, patch
109+
self._patch_cfile(cfile)
110+
111+
super().finalize_options()
112+
113+
def _patch_cfile(self, cfile):
114+
# Patch Cython 'async def' coroutines to have a 'tp_iter'
115+
# slot, which makes them compatible with 'yield from' without
116+
# the `asyncio.coroutine` decorator.
117+
118+
with open(cfile, 'rt') as f:
119+
src = f.read()
120+
121+
src = re.sub(
122+
r'''
123+
\s* offsetof\(__pyx_CoroutineObject,\s*gi_weakreflist\),
124+
\s* 0,
125+
\s* 0,
126+
\s* __pyx_Coroutine_methods,
127+
\s* __pyx_Coroutine_memberlist,
128+
\s* __pyx_Coroutine_getsets,
129+
''',
130+
131+
r'''
132+
offsetof(__pyx_CoroutineObject, gi_weakreflist),
133+
__Pyx_Coroutine_await, /* tp_iter */
134+
0,
135+
__pyx_Coroutine_methods,
136+
__pyx_Coroutine_memberlist,
137+
__pyx_Coroutine_getsets,
138+
''',
139+
140+
src, flags=re.X)
141+
142+
# Fix a segfault in Cython.
143+
src = re.sub(
144+
r'''
145+
\s* __Pyx_Coroutine_get_qualname\(__pyx_CoroutineObject\s+\*self\)
146+
\s* {
147+
\s* Py_INCREF\(self->gi_qualname\);
148+
''',
149+
150+
r'''
151+
__Pyx_Coroutine_get_qualname(__pyx_CoroutineObject *self)
152+
{
153+
if (self->gi_qualname == NULL) { return __pyx_empty_unicode; }
154+
Py_INCREF(self->gi_qualname);
155+
''',
156+
157+
src, flags=re.X)
158+
159+
src = re.sub(
160+
r'''
161+
\s* __Pyx_Coroutine_get_name\(__pyx_CoroutineObject\s+\*self\)
162+
\s* {
163+
\s* Py_INCREF\(self->gi_name\);
164+
''',
165+
166+
r'''
167+
__Pyx_Coroutine_get_name(__pyx_CoroutineObject *self)
168+
{
169+
if (self->gi_name == NULL) { return __pyx_empty_unicode; }
170+
Py_INCREF(self->gi_name);
171+
''',
172+
173+
src, flags=re.X)
174+
175+
with open(cfile, 'wt') as f:
176+
f.write(src)
44177

45178
def build_libuv(self):
46179
env = os.environ.copy()
@@ -112,9 +245,9 @@ def build_extensions(self):
112245
Extension(
113246
"uvloop.loop",
114247
sources=[
115-
"uvloop/loop.c",
248+
"uvloop/loop.pyx",
116249
],
117-
extra_compile_args=['-O2']
250+
extra_compile_args=CFLAGS
118251
),
119252
],
120253
classifiers=[

0 commit comments

Comments
 (0)