Skip to content

Commit

Permalink
Clean up configuring for embedded CFFI modules.
Browse files Browse the repository at this point in the history
Fixes #1411.
  • Loading branch information
jamadden committed May 1, 2019
1 parent c604d1d commit 6843884
Show file tree
Hide file tree
Showing 6 changed files with 67 additions and 70 deletions.
1 change: 1 addition & 0 deletions .gitignore
Expand Up @@ -73,6 +73,7 @@ deps/libev/Makefile
deps/libev/config.log
deps/libev/config.h
deps/libev/config.status
deps/libev/configure-output.txt
deps/libev/libtool
deps/libev/stamp-h1
deps/libev/.libs
Expand Down
47 changes: 23 additions & 24 deletions _setuplibev.py
Expand Up @@ -14,6 +14,7 @@
from _setuputils import Extension

from _setuputils import system
from _setuputils import dep_abspath
from _setuputils import quoted_dep_abspath
from _setuputils import WIN
from _setuputils import make_universal_header
Expand All @@ -26,41 +27,38 @@

LIBEV_EMBED = should_embed('libev')

# Configure libev in place; but cp the config.h to the old directory;
# if we're building a CPython extension, the old directory will be
# the build/temp.XXX/libev/ directory. If we're building from a
# source checkout on pypy, OLDPWD will be the location of setup.py
# and the PyPy branch will clean it up.
# Configure libev in place
libev_configure_command = ' '.join([
"(cd ", quoted_dep_abspath('libev'),
" && sh ./configure ",
" && cp config.h \"$OLDPWD\"",
" && sh ./configure > configure-output.txt",
")",
'> configure-output.txt'
])


def configure_libev(bext, ext):
def configure_libev(build_command=None, extension=None): # pylint:disable=unused-argument
# build_command is an instance of ConfiguringBuildExt.
# extension is an instance of the setuptools Extension object.
#
# This is invoked while `build_command` is in the middle of its `run()`
# method.

# Both of these arguments are unused here so that we can use this function
# both from a build command and from libev/_corecffi_build.py

if WIN:
return

bdir = os.path.join(bext.build_temp, 'libev')
ext.include_dirs.insert(0, bdir)
libev_path = dep_abspath('libev')
config_path = os.path.join(libev_path, 'config.h')
if os.path.exists(config_path):
print("Not configuring libev, 'config.h' already exists")
return

if not os.path.isdir(bdir):
os.makedirs(bdir)
system(libev_configure_command)
if sys.platform == 'darwin':
make_universal_header(config_path,
'SIZEOF_LONG', 'SIZEOF_SIZE_T', 'SIZEOF_TIME_T')

cwd = os.getcwd()
os.chdir(bdir)
try:
if os.path.exists('config.h'):
return
system(libev_configure_command)
if sys.platform == 'darwin':
make_universal_header('config.h',
'SIZEOF_LONG', 'SIZEOF_SIZE_T', 'SIZEOF_TIME_T')
finally:
os.chdir(cwd)

def build_extension():
# Return the un-cythonized extension.
Expand Down Expand Up @@ -99,5 +97,6 @@ def build_extension():
else:
CORE.define_macros += [('LIBEV_EMBED', '0')]
CORE.libraries.append('ev')
CORE.configure = lambda *args: print("libev not embedded, not configuring")

return CORE
30 changes: 30 additions & 0 deletions _setuputils.py
Expand Up @@ -233,6 +233,28 @@ class BuildFailed(Exception):

class ConfiguringBuildExt(build_ext):

# CFFI subclasses this class with its own, that overrides run()
# and invokes a `pre_run` method, if defined. The run() method is
# called only once from setup.py (this class is only instantiated
# once per invocation of setup()); run() in turn calls
# `build_extension` for every defined extension.

# For extensions we control, we let them define a `configure`
# callable attribute, and we invoke that before building. But we
# can't control the Extension object that CFFI creates. The best
# we can do is provide a global hook that we can invoke in pre_run().

gevent_pre_run_actions = ()

@classmethod
def gevent_add_pre_run_action(cls, action):
# Actions should be idempotent.
cls.gevent_pre_run_actions += (action,)

def finalize_options(self):
self.parallel = True # pylint: disable=attribute-defined-outside-init
build_ext.finalize_options(self)

def gevent_prepare(self, ext):
configure = getattr(ext, 'configure', None)
if configure:
Expand All @@ -247,6 +269,13 @@ def build_extension(self, ext):
raise BuildFailed()
raise

def pre_run(self, *_args):
# Called only from CFFI.
# With mulitple extensions, this probably gets called multiple
# times.
for action in self.gevent_pre_run_actions:
action()


class Extension(_Extension):
# This class exists currently mostly to make pylint
Expand Down Expand Up @@ -362,6 +391,7 @@ def dep_configure_artifacts(dep):
'config.h',
'config.log',
'config.status',
'configure-output.txt',
'.libs'
):
yield os.path.join('deps', dep, f)
Expand Down
44 changes: 4 additions & 40 deletions setup.py
Expand Up @@ -15,7 +15,6 @@

from _setuputils import read
from _setuputils import read_version
from _setuputils import system
from _setuputils import PYPY, WIN
from _setuputils import ConfiguringBuildExt
from _setuputils import GeventClean
Expand Down Expand Up @@ -43,8 +42,6 @@
__version__ = read_version()


from _setuplibev import libev_configure_command
from _setuplibev import LIBEV_EMBED
from _setuplibev import build_extension as build_libev_extension
from _setupares import ARES

Expand Down Expand Up @@ -307,18 +304,6 @@
CFFI_DEP,
] + EXTRA_DNSPYTHON + EXTRA_EVENTS + EXTRA_MONITOR

# If we are running info / help commands, or we're being imported by
# tools like pyroma, we don't need to build anything
_BUILDING = True
if ((len(sys.argv) >= 2
and ('--help' in sys.argv[1:]
or sys.argv[1] in ('--help-commands',
'egg_info',
'--version',
'clean',
'--long-description')))
or __name__ != '__main__'):
_BUILDING = False

def make_long_description():
readme = read('README.rst')
Expand All @@ -332,27 +317,7 @@ def make_long_description():
return readme


def run_setup(ext_modules, run_make):
if run_make:
if (not LIBEV_EMBED and not WIN and cffi_modules) or PYPY:
# We're not embedding libev but we do want
# to build the CFFI module. We need to configure libev
# because the CORE Extension won't.
# TODO: Generalize this.
if LIBEV_CFFI_MODULE in cffi_modules and not WIN:
system(libev_configure_command)
# This changed to the libev directory, and ran configure .
# It then copied the generated config.h back to the previous
# directory, which happened to be beside us. In the embedded case,
# we're building in a different directory, so it copied it back to build
# directory, but here, we're building in the embedded directory, so
# it gave us useless files.
bad_file = None
for bad_file in ('config.h', 'configure-output.txt'):
if os.path.exists(bad_file):
os.remove(bad_file)
del bad_file

def run_setup(ext_modules):
setup(
name='gevent',
version=__version__,
Expand Down Expand Up @@ -452,11 +417,10 @@ def run_setup(ext_modules, run_make):
os.environ['PATH'] = new_path

try:
run_setup(EXT_MODULES, run_make=_BUILDING)
run_setup(EXT_MODULES)
except BuildFailed:
if ARES not in EXT_MODULES or not ARES.optional:
raise
EXT_MODULES.remove(ARES)
run_setup(EXT_MODULES, run_make=_BUILDING)
if ARES not in EXT_MODULES and __name__ == '__main__' and _BUILDING:
sys.stderr.write('\nWARNING: The gevent.ares extension has been disabled.\n')
EXT_MODULES.remove(ARES)
run_setup(EXT_MODULES)
13 changes: 8 additions & 5 deletions src/gevent/libev/_corecffi_build.py
Expand Up @@ -15,6 +15,7 @@
sys.path.append(".")
try:
import _setuplibev
import _setuputils
except ImportError:
print("This file must be imported with setup.py in the current working dir.")
raise
Expand All @@ -27,7 +28,7 @@


ffi = FFI()

distutils_ext = _setuplibev.build_extension()

def read_source(name):
with open(os.path.join(thisdir, name), 'r') as f:
Expand All @@ -47,13 +48,18 @@ def read_source(name):
_cdef = _cdef.replace('GEVENT_ST_NLINK_T', 'nlink_t')

if _setuplibev.LIBEV_EMBED:
# Arrange access to the loop internals
_cdef += """
struct ev_loop {
int backend_fd;
int activecnt;
...;
};
"""
"""

# arrange to be configured.
_setuputils.ConfiguringBuildExt.gevent_add_pre_run_action(distutils_ext.configure)


if sys.platform.startswith('win'):
# We must have the vfd_open, etc, functions on
Expand All @@ -71,9 +77,6 @@ def read_source(name):
# source goes to the C compiler
_source = read_source('_corecffi_source.c')


distutils_ext = _setuplibev.build_extension()

macros = list(distutils_ext.define_macros)
try:
# We need the data pointer.
Expand Down
2 changes: 1 addition & 1 deletion src/gevent/libuv/_corecffi_build.py
Expand Up @@ -26,7 +26,7 @@

WIN = sys.platform.startswith('win32')
LIBUV_EMBED = _setuputils.should_embed('libuv')
print("Embedding libuv?", LIBUV_EMBED)


ffi = FFI()

Expand Down

0 comments on commit 6843884

Please sign in to comment.