Fixes to make python distribution for windows work

This commit is contained in:
Andrew Dutcher 2016-11-04 19:56:31 -07:00
parent f8e82485da
commit 876570c8d7
3 changed files with 89 additions and 32 deletions

View file

@ -0,0 +1,2 @@
[bdist_wheel]
universal=1

View file

@ -4,30 +4,32 @@
from __future__ import print_function from __future__ import print_function
import glob import glob
import os import os
import subprocess
import shutil import shutil
import sys import sys
import platform
from distutils import log from distutils import log
from distutils.core import setup from distutils.core import setup
from distutils.util import get_platform
from distutils.command.build import build from distutils.command.build import build
from distutils.command.sdist import sdist from distutils.command.sdist import sdist
from setuptools.command.bdist_egg import bdist_egg 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 SYSTEM = sys.platform
VERSION = '1.0' VERSION = '1.0'
# adapted from commit e504b81 of Nguyen Tan Cong # sys.maxint is 2**31 - 1 on both 32 and 64 bit mingw
# Reference: https://docs.python.org/2/library/platform.html#cross-platform IS_64BITS = platform.architecture()[0] == '64bit'
IS_64BITS = sys.maxsize > 2**32
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? # are we building from the repository or from a source distribution?
ROOT_DIR = os.path.dirname(os.path.realpath(__file__)) ROOT_DIR = os.path.dirname(os.path.realpath(__file__))
@ -99,26 +101,41 @@ def build_libraries():
# copy public headers # copy public headers
shutil.copytree(os.path.join(BUILD_DIR, 'include', 'unicorn'), os.path.join(HEADERS_DIR, 'unicorn')) shutil.copytree(os.path.join(BUILD_DIR, 'include', 'unicorn'), os.path.join(HEADERS_DIR, 'unicorn'))
# if Windows prebuilt library is available, then include it # copy special library dependencies
if SYSTEM in ("win32", "cygwin"): if SYSTEM == 'win32':
if IS_64BITS and os.path.exists(PATH_LIB64): got_all = True
shutil.copy(PATH_LIB64, LIBS_DIR) for dll in ALL_WINDOWS_DLLS:
return dllpath = os.path.join(sys.prefix, 'bin', dll)
elif os.path.exists(PATH_LIB32): if os.path.exists(dllpath):
shutil.copy(PATH_LIB32, LIBS_DIR) shutil.copy(dllpath, LIBS_DIR)
return 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!! # otherwise, build!!
os.chdir(BUILD_DIR) os.chdir(BUILD_DIR)
# platform description refs at https://docs.python.org/2/library/sys.html#sys.platform # 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 SYSTEM == "cygwin":
if IS_64BITS: if IS_64BITS:
os.system("UNICORN_BUILD_CORE_ONLY=yes ./make.sh cygwin-mingw64") cmd.append('cygwin-mingw64')
else: else:
os.system("UNICORN_BUILD_CORE_ONLY=yes ./make.sh cygwin-mingw32") cmd.append('cygwin-mingw32')
else: # Unix elif SYSTEM == "win32":
os.system("UNICORN_BUILD_CORE_ONLY=yes ./make.sh") if IS_64BITS:
cmd.append('cross-win64')
else:
cmd.append('cross-win32')
subprocess.call(cmd, env=new_env)
shutil.copy(LIBRARY_FILE, LIBS_DIR) shutil.copy(LIBRARY_FILE, LIBS_DIR)
if STATIC_LIBRARY_FILE: shutil.copy(STATIC_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): class custom_sdist(sdist):
def run(self): def run(self):
clean_bins() clean_bins()
copy_sources()
# 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()
return sdist.run(self) return sdist.run(self)
class custom_build(build): class custom_build(build):
@ -153,6 +167,25 @@ cmdclass['build'] = custom_build
cmdclass['sdist'] = custom_sdist cmdclass['sdist'] = custom_sdist
cmdclass['bdist_egg'] = custom_bdist_egg 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: try:
from setuptools.command.develop import develop from setuptools.command.develop import develop
class custom_develop(develop): class custom_develop(develop):
@ -171,7 +204,7 @@ def join_all(src, files):
setup( setup(
provides=['unicorn'], provides=['unicorn'],
packages=['unicorn'], packages=['unicorn'],
name=PKG_NAME, name='unicorn',
version=VERSION, version=VERSION,
author='Nguyen Anh Quynh', author='Nguyen Anh Quynh',
author_email='aquynh@gmail.com', author_email='aquynh@gmail.com',
@ -186,6 +219,7 @@ setup(
cmdclass=cmdclass, cmdclass=cmdclass,
zip_safe=True, zip_safe=True,
include_package_data=True, include_package_data=True,
is_pure=True,
package_data={ package_data={
'unicorn': ['lib/*', 'include/unicorn/*'] 'unicorn': ['lib/*', 'include/unicorn/*']
} }

View file

@ -30,15 +30,32 @@ _all_windows_dlls = (
"libgcc_s_seh-1.dll", "libgcc_s_seh-1.dll",
"libgcc_s_dw2-1.dll", "libgcc_s_dw2-1.dll",
"libiconv-2.dll", "libiconv-2.dll",
"libpcre-1.dll",
"libintl-8.dll", "libintl-8.dll",
"libglib-2.0-0.dll", "libglib-2.0-0.dll",
) )
_loaded_windows_dlls = set()
def _load_win_support(path): def _load_win_support(path):
for dll in _all_windows_dlls: for dll in _all_windows_dlls:
if dll in _loaded_windows_dlls:
continue
lib_file = os.path.join(path, dll) lib_file = os.path.join(path, dll)
if os.path.exists(lib_file): if ('/' not in path and '\\' not in path) or os.path.exists(lib_file):
ctypes.cdll.LoadLibrary(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): def _load_lib(path):
try: try:
@ -46,8 +63,12 @@ def _load_lib(path):
_load_win_support(path) _load_win_support(path)
lib_file = os.path.join(path, _lib) 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: except OSError:
#print('FAILURE')
return None return None
_uc = None _uc = None