Skip to content

Commit 4ac503f

Browse files
committed
feat(python): let setup.py build abi3 wheels, fix python 2 usage of it
1 parent f370d1e commit 4ac503f

File tree

1 file changed

+44
-69
lines changed

1 file changed

+44
-69
lines changed

setup.py

Lines changed: 44 additions & 69 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@
77
import platform
88
import re
99
import unittest
10+
import sys
1011

1112
try:
1213
from setuptools import Extension
@@ -15,19 +16,21 @@
1516
from distutils.core import Extension
1617
from distutils.core import setup
1718
from distutils.command.build_ext import build_ext
18-
from distutils import errors
1919
from distutils import dep_util
2020
from distutils import log
2121

2222

23+
IS_PYTHON3 = sys.version_info[0] == 3
24+
BROTLI_PYTHON3_LIMITED_API = os.environ.get('BROTLI_PYTHON3_LIMITED_API', '0') == '1'
25+
2326
CURR_DIR = os.path.abspath(os.path.dirname(os.path.realpath(__file__)))
2427

2528

2629
def read_define(path, macro):
2730
""" Return macro value from the given file. """
2831
with open(path, 'r') as f:
2932
for line in f:
30-
m = re.match(rf'#define\s{macro}\s+(.+)', line)
33+
m = re.match('#define\\s{macro}\\s+(.+)'.format(macro=macro), line)
3134
if m:
3235
return m.group(1)
3336
return ''
@@ -41,7 +44,7 @@ def get_version():
4144
patch = read_define(version_file_path, 'BROTLI_VERSION_PATCH')
4245
if not major or not minor or not patch:
4346
return ''
44-
return f'{major}.{minor}.{patch}'
47+
return '{major}.{minor}.{patch}'.format(major=major, minor=minor, patch=patch)
4548

4649

4750
def get_test_suite():
@@ -51,20 +54,7 @@ def get_test_suite():
5154

5255

5356
class BuildExt(build_ext):
54-
55-
def get_source_files(self):
56-
filenames = build_ext.get_source_files(self)
57-
for ext in self.extensions:
58-
filenames.extend(ext.depends)
59-
return filenames
60-
6157
def build_extension(self, ext):
62-
if ext.sources is None or not isinstance(ext.sources, (list, tuple)):
63-
raise errors.DistutilsSetupError(
64-
"in 'ext_modules' option (extension '%s'), "
65-
"'sources' must be present and must be "
66-
"a list of source filenames" % ext.name)
67-
6858
ext_path = self.get_ext_fullpath(ext.name)
6959
depends = ext.sources + ext.depends
7060
if not (self.force or dep_util.newer_group(depends, ext_path, 'newer')):
@@ -73,59 +63,13 @@ def build_extension(self, ext):
7363
else:
7464
log.info("building '%s' extension", ext.name)
7565

76-
c_sources = []
77-
for source in ext.sources:
78-
if source.endswith('.c'):
79-
c_sources.append(source)
80-
extra_args = ext.extra_compile_args or []
81-
82-
objects = []
83-
84-
macros = ext.define_macros[:]
85-
if platform.system() == 'Darwin':
86-
macros.append(('OS_MACOSX', '1'))
87-
elif self.compiler.compiler_type == 'mingw32':
66+
if self.compiler.compiler_type == 'mingw32':
8867
# On Windows Python 2.7, pyconfig.h defines "hypot" as "_hypot",
8968
# This clashes with GCC's cmath, and causes compilation errors when
9069
# building under MinGW: http://bugs.python.org/issue11566
91-
macros.append(('_hypot', 'hypot'))
92-
for undef in ext.undef_macros:
93-
macros.append((undef,))
94-
95-
objs = self.compiler.compile(
96-
c_sources,
97-
output_dir=self.build_temp,
98-
macros=macros,
99-
include_dirs=ext.include_dirs,
100-
debug=self.debug,
101-
extra_postargs=extra_args,
102-
depends=ext.depends)
103-
objects.extend(objs)
104-
105-
self._built_objects = objects[:]
106-
if ext.extra_objects:
107-
objects.extend(ext.extra_objects)
108-
extra_args = ext.extra_link_args or []
109-
# when using GCC on Windows, we statically link libgcc and libstdc++,
110-
# so that we don't need to package extra DLLs
111-
if self.compiler.compiler_type == 'mingw32':
112-
extra_args.extend(['-static-libgcc', '-static-libstdc++'])
70+
ext.define_macros.append(('_hypot', 'hypot'))
11371

114-
ext_path = self.get_ext_fullpath(ext.name)
115-
# Detect target language, if not provided
116-
language = ext.language or self.compiler.detect_language(c_sources)
117-
118-
self.compiler.link_shared_object(
119-
objects,
120-
ext_path,
121-
libraries=self.get_libraries(ext),
122-
library_dirs=ext.library_dirs,
123-
runtime_library_dirs=ext.runtime_library_dirs,
124-
extra_postargs=extra_args,
125-
export_symbols=self.get_export_symbols(ext),
126-
debug=self.debug,
127-
build_temp=self.build_temp,
128-
target_lang=language)
72+
build_ext.build_extension(self, ext)
12973

13074

13175
NAME = 'Brotli'
@@ -170,11 +114,25 @@ def build_extension(self, ext):
170114

171115
PACKAGE_DIR = {'': 'python'}
172116

173-
PY_MODULES = ['brotli']
117+
class VersionedExtension(Extension):
118+
def __init__(self, *args, **kwargs):
119+
define_macros = []
120+
121+
if IS_PYTHON3 and BROTLI_PYTHON3_LIMITED_API:
122+
kwargs['py_limited_api'] = True
123+
define_macros.append(('Py_LIMITED_API', '0x03060000'))
124+
125+
if platform.system() == 'Darwin':
126+
define_macros.append(('OS_MACOSX', '1'))
127+
128+
kwargs['define_macros'] = kwargs.get('define_macros', []) + define_macros
129+
130+
Extension.__init__(self, *args, **kwargs)
131+
174132

175133
EXT_MODULES = [
176-
Extension(
177-
'_brotli',
134+
VersionedExtension(
135+
name='brotli._brotli',
178136
sources=[
179137
'python/_brotli.c',
180138
'c/common/constants.c',
@@ -276,6 +234,23 @@ def build_extension(self, ext):
276234
'build_ext': BuildExt,
277235
}
278236

237+
if IS_PYTHON3 and BROTLI_PYTHON3_LIMITED_API:
238+
from wheel.bdist_wheel import bdist_wheel
239+
# adopted from:
240+
# https://github.com/joerick/python-abi3-package-sample/blob/7f05b22b9e0cfb4e60293bc85252e95278a80720/setup.py
241+
class bdist_wheel_abi3(bdist_wheel):
242+
def get_tag(self):
243+
python, abi, plat = super().get_tag()
244+
245+
if python.startswith("cp"):
246+
# on CPython, our wheels are abi3 and compatible back to 3.6
247+
return "cp36", "abi3", plat
248+
249+
return python, abi, plat
250+
251+
CMD_CLASS["bdist_wheel"] = bdist_wheel_abi3
252+
253+
279254
with open("README.md", "r") as f:
280255
README = f.read()
281256

@@ -291,7 +266,7 @@ def build_extension(self, ext):
291266
platforms=PLATFORMS,
292267
classifiers=CLASSIFIERS,
293268
package_dir=PACKAGE_DIR,
294-
py_modules=PY_MODULES,
269+
packages=["brotli"],
295270
ext_modules=EXT_MODULES,
296271
test_suite=TEST_SUITE,
297272
cmdclass=CMD_CLASS)

0 commit comments

Comments
 (0)