From 876570c8d771b231e734332194a59d56b543ab23 Mon Sep 17 00:00:00 2001 From: Andrew Dutcher Date: Fri, 4 Nov 2016 19:56:31 -0700 Subject: [PATCH] Fixes to make python distribution for windows work --- bindings/python/setup.cfg | 2 + bindings/python/setup.py | 92 ++++++++++++++++++++---------- bindings/python/unicorn/unicorn.py | 27 ++++++++- 3 files changed, 89 insertions(+), 32 deletions(-) create mode 100644 bindings/python/setup.cfg diff --git a/bindings/python/setup.cfg b/bindings/python/setup.cfg new file mode 100644 index 00000000..3c6e79cf --- /dev/null +++ b/bindings/python/setup.cfg @@ -0,0 +1,2 @@ +[bdist_wheel] +universal=1 diff --git a/bindings/python/setup.py b/bindings/python/setup.py index 155b524a..5b968cf9 100755 --- a/bindings/python/setup.py +++ b/bindings/python/setup.py @@ -4,30 +4,32 @@ from __future__ import print_function import glob import os +import subprocess import shutil import sys +import platform from distutils import log from distutils.core import setup +from distutils.util import get_platform from distutils.command.build import build from distutils.command.sdist import sdist from setuptools.command.bdist_egg import bdist_egg -# prebuilt libraries for Windows - for sdist -PATH_LIB64 = "prebuilt/win64/unicorn.dll" -PATH_LIB32 = "prebuilt/win32/unicorn.dll" - -# package name can be 'unicorn' or 'unicorn-windows' -PKG_NAME = 'unicorn' -if os.path.exists(PATH_LIB64) and os.path.exists(PATH_LIB32): - PKG_NAME = 'unicorn-windows' - SYSTEM = sys.platform VERSION = '1.0' -# adapted from commit e504b81 of Nguyen Tan Cong -# Reference: https://docs.python.org/2/library/platform.html#cross-platform -IS_64BITS = sys.maxsize > 2**32 +# sys.maxint is 2**31 - 1 on both 32 and 64 bit mingw +IS_64BITS = platform.architecture()[0] == '64bit' + +ALL_WINDOWS_DLLS = ( + "libwinpthread-1.dll", + "libgcc_s_seh-1.dll" if IS_64BITS else "libgcc_s_dw2-1.dll", + "libiconv-2.dll", + "libpcre-1.dll", + "libintl-8.dll", + "libglib-2.0-0.dll", +) # are we building from the repository or from a source distribution? ROOT_DIR = os.path.dirname(os.path.realpath(__file__)) @@ -99,26 +101,41 @@ def build_libraries(): # copy public headers shutil.copytree(os.path.join(BUILD_DIR, 'include', 'unicorn'), os.path.join(HEADERS_DIR, 'unicorn')) - # if Windows prebuilt library is available, then include it - if SYSTEM in ("win32", "cygwin"): - if IS_64BITS and os.path.exists(PATH_LIB64): - shutil.copy(PATH_LIB64, LIBS_DIR) - return - elif os.path.exists(PATH_LIB32): - shutil.copy(PATH_LIB32, LIBS_DIR) - return + # copy special library dependencies + if SYSTEM == 'win32': + got_all = True + for dll in ALL_WINDOWS_DLLS: + dllpath = os.path.join(sys.prefix, 'bin', dll) + if os.path.exists(dllpath): + shutil.copy(dllpath, LIBS_DIR) + else: + got_all = False + + if not got_all: + print('Warning: not all DLLs were found! This build is not appropriate for a binary distribution') + # enforce this + if 'upload' in sys.argv: + sys.exit(1) # otherwise, build!! os.chdir(BUILD_DIR) # platform description refs at https://docs.python.org/2/library/sys.html#sys.platform + new_env = dict(os.environ) + new_env['UNICORN_BUILD_CORE_ONLY'] = 'yes' + cmd = ['sh', './make.sh'] if SYSTEM == "cygwin": if IS_64BITS: - os.system("UNICORN_BUILD_CORE_ONLY=yes ./make.sh cygwin-mingw64") + cmd.append('cygwin-mingw64') else: - os.system("UNICORN_BUILD_CORE_ONLY=yes ./make.sh cygwin-mingw32") - else: # Unix - os.system("UNICORN_BUILD_CORE_ONLY=yes ./make.sh") + cmd.append('cygwin-mingw32') + elif SYSTEM == "win32": + if IS_64BITS: + cmd.append('cross-win64') + else: + cmd.append('cross-win32') + + subprocess.call(cmd, env=new_env) shutil.copy(LIBRARY_FILE, LIBS_DIR) if STATIC_LIBRARY_FILE: shutil.copy(STATIC_LIBRARY_FILE, LIBS_DIR) @@ -128,10 +145,7 @@ def build_libraries(): class custom_sdist(sdist): def run(self): clean_bins() - - # if prebuilt libraries are existent, then do not copy source - if not os.path.exists(PATH_LIB64) or not os.path.exists(PATH_LIB32): - copy_sources() + copy_sources() return sdist.run(self) class custom_build(build): @@ -153,6 +167,25 @@ cmdclass['build'] = custom_build cmdclass['sdist'] = custom_sdist cmdclass['bdist_egg'] = custom_bdist_egg +if 'bdist_wheel' in sys.argv and '--plat-name' not in sys.argv: + idx = sys.argv.index('bdist_wheel') + 1 + sys.argv.insert(idx, '--plat-name') + name = get_platform() + if 'linux' in name: + # linux_* platform tags are disallowed because the python ecosystem is fubar + # linux builds should be built in the centos 5 vm for maximum compatibility + # see https://github.com/pypa/manylinux + # see also https://github.com/angr/angr-dev/blob/master/bdist.sh + sys.argv.insert(idx + 1, 'manylinux1_' + platform.machine()) + elif 'mingw' in name: + if IS_64BITS: + sys.argv.insert(idx + 1, 'win_amd64') + else: + sys.argv.insert(idx + 1, 'win32') + else: + # https://www.python.org/dev/peps/pep-0425/ + sys.argv.insert(idx + 1, name.replace('.', '_').replace('-', '_')) + try: from setuptools.command.develop import develop class custom_develop(develop): @@ -171,7 +204,7 @@ def join_all(src, files): setup( provides=['unicorn'], packages=['unicorn'], - name=PKG_NAME, + name='unicorn', version=VERSION, author='Nguyen Anh Quynh', author_email='aquynh@gmail.com', @@ -186,6 +219,7 @@ setup( cmdclass=cmdclass, zip_safe=True, include_package_data=True, + is_pure=True, package_data={ 'unicorn': ['lib/*', 'include/unicorn/*'] } diff --git a/bindings/python/unicorn/unicorn.py b/bindings/python/unicorn/unicorn.py index 9c54edff..5ccd7b9f 100644 --- a/bindings/python/unicorn/unicorn.py +++ b/bindings/python/unicorn/unicorn.py @@ -30,15 +30,32 @@ _all_windows_dlls = ( "libgcc_s_seh-1.dll", "libgcc_s_dw2-1.dll", "libiconv-2.dll", + "libpcre-1.dll", "libintl-8.dll", "libglib-2.0-0.dll", ) +_loaded_windows_dlls = set() + def _load_win_support(path): for dll in _all_windows_dlls: + if dll in _loaded_windows_dlls: + continue + lib_file = os.path.join(path, dll) - if os.path.exists(lib_file): - ctypes.cdll.LoadLibrary(lib_file) + if ('/' not in path and '\\' not in path) or os.path.exists(lib_file): + try: + #print('Trying to load windows library', lib_file) + ctypes.cdll.LoadLibrary(lib_file) + #print('SUCCESS') + _loaded_windows_dlls.add(dll) + except OSError: + #print('FAILURE') + continue + +# Initial attempt: load all dlls globally +if sys.platform in ('win32', 'cygwin'): + _load_win_support('') def _load_lib(path): try: @@ -46,8 +63,12 @@ def _load_lib(path): _load_win_support(path) lib_file = os.path.join(path, _lib) - return ctypes.cdll.LoadLibrary(lib_file) + #print('Trying to load shared library', lib_file) + dll = ctypes.cdll.LoadLibrary(lib_file) + #print('SUCCESS') + return dll except OSError: + #print('FAILURE') return None _uc = None