setup.py: optionally build cython extension

If Cython is already pre-installed, we opportunistically try to compile the
cu2qu extension module, and if it fails (e.g. compiler is not available), then
we silently fall back to pure-python mode.

If user explicitly requests --with-cython (or sets 'FONTTOOLS_WITH_CYTHON=1' env
variable), then we add cython to setup_requires, and raise an error if
compilation fails.
This commit is contained in:
Cosimo Lupo 2020-04-02 13:23:58 +01:00
parent 84b06022ed
commit c327dc6f0d
No known key found for this signature in database
GPG Key ID: 179A8F0895A02F4F

107
setup.py
View File

@ -6,7 +6,8 @@ import sys
import os
from os.path import isfile, join as pjoin
from glob import glob
from setuptools import setup, find_packages, Command
from setuptools import setup, find_packages, Command, Extension
from setuptools.command.build_ext import build_ext as _build_ext
from distutils import log
from distutils.util import convert_path
import subprocess as sp
@ -23,10 +24,50 @@ def doraise_py_compile(file, cfile=None, dfile=None, doraise=False):
py_compile.compile = doraise_py_compile
needs_wheel = {'bdist_wheel'}.intersection(sys.argv)
wheel = ['wheel'] if needs_wheel else []
needs_bumpversion = {'release'}.intersection(sys.argv)
bumpversion = ['bump2version'] if needs_bumpversion else []
setup_requires = []
if {'bdist_wheel'}.intersection(sys.argv):
setup_requires.append('wheel')
if {'release'}.intersection(sys.argv):
setup_requires.append('bump2version')
try:
__import__("cython")
except ImportError:
has_cython = False
else:
has_cython = True
env_with_cython = os.environ.get("FONTTOOLS_WITH_CYTHON")
with_cython = (
True if env_with_cython in {"1", "true", "yes"}
else False if env_with_cython in {"0", "false", "no"}
else None
)
# --with-cython/--without-cython options override environment variables
opt_with_cython = {'--with-cython'}.intersection(sys.argv)
opt_without_cython = {'--without-cython'}.intersection(sys.argv)
if opt_with_cython and opt_without_cython:
sys.exit(
"error: the options '--with-cython' and '--without-cython' are "
"mutually exclusive"
)
elif opt_with_cython:
sys.argv.remove("--with-cython")
with_cython = True
elif opt_without_cython:
sys.argv.remove("--without-cython")
with_cython = False
if with_cython and not has_cython:
setup_requires.append("cython")
ext_modules = []
if with_cython is True or (with_cython is None and has_cython):
ext_modules.append(
Extension("fontTools.cu2qu.cu2qu", ["Lib/fontTools/cu2qu/cu2qu.py"]),
)
extras_require = {
# for fontTools.ufoLib: to read/write UFO fonts
@ -343,7 +384,53 @@ def find_data_files(manpath="share/man"):
return data_files
setup(
class cython_build_ext(_build_ext):
"""Compile *.pyx source files to *.c using cythonize if Cython is
installed and there is a working C compiler, else fall back to pure python dist.
"""
def finalize_options(self):
from Cython.Build import cythonize
# optionally enable line tracing for test coverage support
linetrace = os.environ.get("CYTHON_TRACE") == "1"
self.distribution.ext_modules[:] = cythonize(
self.distribution.ext_modules,
force=linetrace or self.force,
annotate=os.environ.get("CYTHON_ANNOTATE") == "1",
quiet=not self.verbose,
compiler_directives={
"linetrace": linetrace,
"language_level": 3,
"embedsignature": True,
},
)
_build_ext.finalize_options(self)
def build_extensions(self):
try:
_build_ext.build_extensions(self)
except Exception as e:
if with_cython:
raise
from distutils.errors import DistutilsModuleError
# optional compilation failed: we delete 'ext_modules' and make sure
# the generated wheel is 'pure'
del self.distribution.ext_modules[:]
try:
bdist_wheel = self.get_finalized_command("bdist_wheel")
except DistutilsModuleError:
# 'bdist_wheel' command not available as wheel is not installed
pass
else:
bdist_wheel.root_is_pure = True
log.error('error: building extensions failed: %s' % e)
setup_params = dict(
name="fonttools",
version="4.6.1.dev0",
description="Tools to manipulate font files",
@ -360,7 +447,8 @@ setup(
packages=find_packages("Lib"),
include_package_data=True,
data_files=find_data_files(),
setup_requires=wheel + bumpversion,
ext_modules=ext_modules,
setup_requires=setup_requires,
extras_require=extras_require,
entry_points={
'console_scripts': [
@ -372,6 +460,11 @@ setup(
},
cmdclass={
"release": release,
"build_ext": cython_build_ext,
},
**classifiers
)
if __name__ == "__main__":
setup(**setup_params)