setup: detect Python DLL filename from loaded DLL stable
authorGregory Szorc <gregory.szorc@gmail.com>
Thu, 28 Apr 2016 08:52:13 -0700
branchstable
changeset 29020 ee2e4a2c3690
parent 29019 210bb28ca4fb
child 29021 92d37fb3f1aa
setup: detect Python DLL filename from loaded DLL Attempting to build Mercurial from source using MinGW from msys2 on Windows produces a hg.exe that attempts to load e.g. python27.dll. MinGW prefixes its library name with "lib" and adds a period between the major and minor versions. e.g. "libpython2.7.dll." Before this patch, hg.exe files in a MinGW environment would either fail to find a Python DLL or would attempt to load a non-MinGW DLL, which would summarily explode. Either way, hg.exe wouldn't work. This patch improves the code that determines the Python DLL filename to actually use the loaded Python DLL instead of inferring it. Basically we take the handle of the loaded DLL from sys.dllhandle and call a Windows API to try to resolve that handle to a filename.
setup.py
--- a/setup.py	Wed Apr 27 09:23:39 2016 -0700
+++ b/setup.py	Thu Apr 28 08:52:13 2016 -0700
@@ -57,6 +57,7 @@
 
 ispypy = "PyPy" in sys.version
 
+import ctypes
 import os, stat, subprocess, time
 import re
 import shutil
@@ -369,8 +370,34 @@
         if isinstance(self.compiler, HackedMingw32CCompiler):
             self.compiler.compiler_so = self.compiler.compiler # no -mdll
             self.compiler.dll_libraries = [] # no -lmsrvc90
-        hv = sys.hexversion
-        pythonlib = 'python%d%d' % (hv >> 24, (hv >> 16) & 0xff)
+
+        # Different Python installs can have different Python library
+        # names. e.g. the official CPython distribution uses pythonXY.dll
+        # and MinGW uses libpythonX.Y.dll.
+        _kernel32 = ctypes.windll.kernel32
+        _kernel32.GetModuleFileNameA.argtypes = [ctypes.c_void_p,
+                                                 ctypes.c_void_p,
+                                                 ctypes.c_ulong]
+        _kernel32.GetModuleFileNameA.restype = ctypes.c_ulong
+        size = 1000
+        buf = ctypes.create_string_buffer(size + 1)
+        filelen = _kernel32.GetModuleFileNameA(sys.dllhandle, ctypes.byref(buf),
+                                               size)
+
+        if filelen > 0 and filelen != size:
+            dllbasename = os.path.basename(buf.value)
+            if not dllbasename.lower().endswith('.dll'):
+                raise SystemExit('Python DLL does not end with .dll: %s' %
+                                 dllbasename)
+            pythonlib = dllbasename[:-4]
+        else:
+            log.warn('could not determine Python DLL filename; '
+                     'assuming pythonXY')
+
+            hv = sys.hexversion
+            pythonlib = 'python%d%d' % (hv >> 24, (hv >> 16) & 0xff)
+
+        log.info('using %s as Python library name' % pythonlib)
         with open('mercurial/hgpythonlib.h', 'wb') as f:
             f.write('/* this file is autogenerated by setup.py */\n')
             f.write('#define HGPYTHONLIB "%s"\n' % pythonlib)