[pypy-commit] cffi default: Trying a different hack: stop patching SO and EXT_SUFFIX in

arigo pypy.commits at gmail.com
Sat Jan 16 05:13:45 EST 2016


Author: Armin Rigo <arigo at tunes.org>
Branch: 
Changeset: r2592:9be92594ef14
Date: 2016-01-16 11:13 +0100
http://bitbucket.org/cffi/cffi/changeset/9be92594ef14/

Log:	Trying a different hack: stop patching SO and EXT_SUFFIX in
	sysconfigdata, and instead patch directly a method in
	distutils.command.build_ext. Motivation: Windows, where the previous
	solution makes it add the wrong 'init...' in exports_symbols

diff --git a/cffi/ffiplatform.py b/cffi/ffiplatform.py
--- a/cffi/ffiplatform.py
+++ b/cffi/ffiplatform.py
@@ -21,14 +21,12 @@
         allsources.append(os.path.normpath(src))
     return Extension(name=modname, sources=allsources, **kwds)
 
-def compile(tmpdir, ext, compiler_verbose=0, target_extension=None,
-            embedding=False):
+def compile(tmpdir, ext, compiler_verbose=0):
     """Compile a C extension module using distutils."""
 
     saved_environ = os.environ.copy()
     try:
-        outputfilename = _build(tmpdir, ext, compiler_verbose,
-                                target_extension, embedding)
+        outputfilename = _build(tmpdir, ext, compiler_verbose)
         outputfilename = os.path.abspath(outputfilename)
     finally:
         # workaround for a distutils bugs where some env vars can
@@ -38,32 +36,7 @@
                 os.environ[key] = value
     return outputfilename
 
-def _save_val(name):
-    import distutils.sysconfig
-    config_vars = distutils.sysconfig.get_config_vars()
-    return config_vars.get(name, Ellipsis)
-
-def _restore_val(name, value):
-    import distutils.sysconfig
-    config_vars = distutils.sysconfig.get_config_vars()
-    config_vars[name] = value
-    if value is Ellipsis:
-        del config_vars[name]
-
-def _win32_hack_for_embedding():
-    from distutils.msvc9compiler import MSVCCompiler
-    if not hasattr(MSVCCompiler, '_remove_visual_c_ref_CFFI_BAK'):
-        MSVCCompiler._remove_visual_c_ref_CFFI_BAK = \
-            MSVCCompiler._remove_visual_c_ref
-    MSVCCompiler._remove_visual_c_ref = lambda self,manifest_file: manifest_file
-
-def _win32_unhack_for_embedding():
-    from distutils.msvc9compiler import MSVCCompiler
-    MSVCCompiler._remove_visual_c_ref = \
-        MSVCCompiler._remove_visual_c_ref_CFFI_BAK
-
-def _build(tmpdir, ext, compiler_verbose=0, target_extension=None,
-           embedding=False):
+def _build(tmpdir, ext, compiler_verbose=0):
     # XXX compact but horrible :-(
     from distutils.core import Distribution
     import distutils.errors, distutils.log
@@ -76,25 +49,14 @@
     options['build_temp'] = ('ffiplatform', tmpdir)
     #
     try:
-        if sys.platform == 'win32' and embedding:
-            _win32_hack_for_embedding()
         old_level = distutils.log.set_threshold(0) or 0
-        old_SO = _save_val('SO')
-        old_EXT_SUFFIX = _save_val('EXT_SUFFIX')
         try:
-            if target_extension is not None:
-                _restore_val('SO', target_extension)
-                _restore_val('EXT_SUFFIX', target_extension)
             distutils.log.set_verbosity(compiler_verbose)
             dist.run_command('build_ext')
             cmd_obj = dist.get_command_obj('build_ext')
             [soname] = cmd_obj.get_outputs()
         finally:
             distutils.log.set_threshold(old_level)
-            _restore_val('SO', old_SO)
-            _restore_val('EXT_SUFFIX', old_EXT_SUFFIX)
-            if sys.platform == 'win32' and embedding:
-                _win32_unhack_for_embedding()
     except (distutils.errors.CompileError,
             distutils.errors.LinkError) as e:
         raise VerificationError('%s: %s' % (e.__class__.__name__, e))
diff --git a/cffi/recompiler.py b/cffi/recompiler.py
--- a/cffi/recompiler.py
+++ b/cffi/recompiler.py
@@ -1357,6 +1357,40 @@
     parts[-1] += extension
     return os.path.join(outputdir, *parts), parts
 
+
+# Aaargh.  Distutils is not tested at all for the purpose of compiling
+# DLLs that are not extension modules.  Here are some hacks to work
+# around that, in the _patch_for_*() functions...
+
+def _patch_meth(patchlist, cls, name, new_meth):
+    patchlist.append((cls, name, getattr(cls, name)))
+    setattr(cls, name, new_meth)
+
+def _unpatch_meths(patchlist):
+    for cls, name, old_meth in reversed(patchlist):
+        setattr(cls, name, old_meth)
+
+def _patch_for_embedding_win32(patchlist):
+    from distutils.msvc9compiler import MSVCCompiler
+    # we must not remove the manifest when building for embedding!
+    _patch_meth(patchlist, MSVCCompiler, '_remove_visual_c_ref',
+                lambda self, manifest_file: manifest_file)
+
+def _patch_for_target(patchlist, target):
+    from distutils.command.build_ext import build_ext
+    # if 'target' is different from '*', we need to patch some internal
+    # method to just return this 'target' value, instead of having it
+    # built from module_name
+    if target.endswith('.*'):
+        target = target[:-2]
+        if sys.platform == 'win32':
+            target += '.dll'
+        else:
+            target += '.so'
+    _patch_meth(patchlist, build_ext, 'get_ext_filename',
+                lambda self, ext_name: target)
+
+
 def recompile(ffi, module_name, preamble, tmpdir='.', call_c_compiler=True,
               c_file=None, source_extension='.c', extradir=None,
               compiler_verbose=1, target=None, **kwds):
@@ -1382,36 +1416,22 @@
                 target = '%s.*' % module_name
             else:
                 target = '*'
-        if target == '*':
-            target_module_name = module_name
-            target_extension = None      # use default
-        else:
-            if target.endswith('.*'):
-                target = target[:-2]
-                if sys.platform == 'win32':
-                    target += '.dll'
-                else:
-                    target += '.so'
-            # split along the first '.' (not the last one, otherwise the
-            # preceeding dots are interpreted as splitting package names)
-            index = target.find('.')
-            if index < 0:
-                raise ValueError("target argument %r should be a file name "
-                                 "containing a '.'" % (target,))
-            target_module_name = target[:index]
-            target_extension = target[index:]
         #
-        ext = ffiplatform.get_extension(ext_c_file, target_module_name, **kwds)
+        ext = ffiplatform.get_extension(ext_c_file, module_name, **kwds)
         updated = make_c_source(ffi, module_name, preamble, c_file)
         if call_c_compiler:
+            patchlist = []
             cwd = os.getcwd()
             try:
+                if embedding and sys.platform == 'win32':
+                    _patch_for_embedding_win32(patchlist)
+                if target != '*':
+                    _patch_for_target(patchlist, target)
                 os.chdir(tmpdir)
-                outputfilename = ffiplatform.compile('.', ext, compiler_verbose,
-                                                     target_extension,
-                                                     embedding=embedding)
+                outputfilename = ffiplatform.compile('.', ext, compiler_verbose)
             finally:
                 os.chdir(cwd)
+                _unpatch_meths(patchlist)
             return outputfilename
         else:
             return ext, updated
diff --git a/testing/cffi1/test_zdist.py b/testing/cffi1/test_zdist.py
--- a/testing/cffi1/test_zdist.py
+++ b/testing/cffi1/test_zdist.py
@@ -219,23 +219,6 @@
         x = ffi.compile(target="foo.bar.*")
         if sys.platform != 'win32':
             sofile = self.check_produced_files({
-                'foo.bar.SO': None,
-                'mod_name_in_package': {'mymod.c': None,
-                                        'mymod.o': None}})
-            assert os.path.isabs(x) and os.path.samefile(x, sofile)
-        else:
-            self.check_produced_files({
-                'foo.bar.SO': None,
-                'mod_name_in_package': {'mymod.c': None},
-                'Release': '?'})
-
-    @chdir_to_tmp
-    def test_api_compile_explicit_target_2(self):
-        ffi = cffi.FFI()
-        ffi.set_source("mod_name_in_package.mymod", "/*code would be here*/")
-        x = ffi.compile(target=os.path.join("mod_name_in_package", "foo.bar.*"))
-        if sys.platform != 'win32':
-            sofile = self.check_produced_files({
                 'mod_name_in_package': {'foo.bar.SO': None,
                                         'mymod.c': None,
                                         'mymod.o': None}})
@@ -253,15 +236,16 @@
         x = ffi.compile(target="foo.bar.baz")
         if sys.platform != 'win32':
             self.check_produced_files({
-                'foo.bar.baz': None,
-                'mod_name_in_package': {'mymod.c': None,
+                'mod_name_in_package': {'foo.bar.baz': None,
+                                        'mymod.c': None,
                                         'mymod.o': None}})
-            sofile = os.path.join(str(self.udir), 'foo.bar.baz')
+            sofile = os.path.join(str(self.udir),
+                                  'mod_name_in_package', 'foo.bar.baz')
             assert os.path.isabs(x) and os.path.samefile(x, sofile)
         else:
             self.check_produced_files({
-                'foo.bar.baz': None,
-                'mod_name_in_package': {'mymod.c': None},
+                'mod_name_in_package': {'foo.bar.baz': None,
+                                        'mymod.c': None},
                 'Release': '?'})
 
     @chdir_to_tmp


More information about the pypy-commit mailing list