1#!/usr/bin/env python 2 3import glob 4import os 5import shutil 6import sys 7import platform 8 9from distutils import log 10from setuptools import setup 11from distutils.util import get_platform 12from distutils.command.build import build 13from distutils.command.sdist import sdist 14from setuptools.command.bdist_egg import bdist_egg 15 16SYSTEM = sys.platform 17 18# adapted from commit e504b81 of Nguyen Tan Cong 19# Reference: https://docs.python.org/2/library/platform.html#cross-platform 20IS_64BITS = sys.maxsize > 2**32 21 22# are we building from the repository or from a source distribution? 23ROOT_DIR = os.path.dirname(os.path.realpath(__file__)) 24LIBS_DIR = os.path.join(ROOT_DIR, 'capstone', 'lib') 25HEADERS_DIR = os.path.join(ROOT_DIR, 'capstone', 'include') 26SRC_DIR = os.path.join(ROOT_DIR, 'src') 27BUILD_DIR = SRC_DIR if os.path.exists(SRC_DIR) else os.path.join(ROOT_DIR, '../..') 28 29# Parse version from pkgconfig.mk 30VERSION_DATA = {} 31with open(os.path.join(BUILD_DIR, 'pkgconfig.mk')) as fp: 32 lines = fp.readlines() 33 for line in lines: 34 line = line.strip() 35 if len(line) == 0: 36 continue 37 if line.startswith('#'): 38 continue 39 if '=' not in line: 40 continue 41 42 k, v = line.split('=', 1) 43 k = k.strip() 44 v = v.strip() 45 if len(k) == 0 or len(v) == 0: 46 continue 47 VERSION_DATA[k] = v 48 49if 'PKG_MAJOR' not in VERSION_DATA or \ 50 'PKG_MINOR' not in VERSION_DATA or \ 51 'PKG_EXTRA' not in VERSION_DATA: 52 raise Exception("Malformed pkgconfig.mk") 53 54if 'PKG_TAG' in VERSION_DATA: 55 VERSION = '{PKG_MAJOR}.{PKG_MINOR}.{PKG_EXTRA}.{PKG_TAG}'.format(**VERSION_DATA) 56else: 57 VERSION = '{PKG_MAJOR}.{PKG_MINOR}.{PKG_EXTRA}'.format(**VERSION_DATA) 58 59if SYSTEM == 'darwin': 60 LIBRARY_FILE = "libcapstone.dylib" 61 STATIC_LIBRARY_FILE = 'libcapstone.a' 62elif SYSTEM in ('win32', 'cygwin'): 63 LIBRARY_FILE = "capstone.dll" 64 STATIC_LIBRARY_FILE = None 65else: 66 LIBRARY_FILE = "libcapstone.so" 67 STATIC_LIBRARY_FILE = 'libcapstone.a' 68 69def clean_bins(): 70 shutil.rmtree(LIBS_DIR, ignore_errors=True) 71 shutil.rmtree(HEADERS_DIR, ignore_errors=True) 72 73def copy_sources(): 74 """Copy the C sources into the source directory. 75 This rearranges the source files under the python distribution 76 directory. 77 """ 78 src = [] 79 80 try: 81 shutil.rmtree("src/") 82 except (IOError, OSError): 83 pass 84 85 shutil.copytree(os.path.join(BUILD_DIR, "arch"), os.path.join(SRC_DIR, "arch")) 86 shutil.copytree(os.path.join(BUILD_DIR, "include"), os.path.join(SRC_DIR, "include")) 87 88 src.extend(glob.glob(os.path.join(BUILD_DIR, "*.[ch]"))) 89 src.extend(glob.glob(os.path.join(BUILD_DIR, "*.mk"))) 90 91 src.extend(glob.glob(os.path.join(BUILD_DIR, "Makefile"))) 92 src.extend(glob.glob(os.path.join(BUILD_DIR, "LICENSE*"))) 93 src.extend(glob.glob(os.path.join(BUILD_DIR, "README"))) 94 src.extend(glob.glob(os.path.join(BUILD_DIR, "*.TXT"))) 95 src.extend(glob.glob(os.path.join(BUILD_DIR, "RELEASE_NOTES"))) 96 src.extend(glob.glob(os.path.join(BUILD_DIR, "make.sh"))) 97 src.extend(glob.glob(os.path.join(BUILD_DIR, "CMakeLists.txt"))) 98 src.extend(glob.glob(os.path.join(BUILD_DIR, "pkgconfig.mk"))) 99 100 for filename in src: 101 outpath = os.path.join(SRC_DIR, os.path.basename(filename)) 102 log.info("%s -> %s" % (filename, outpath)) 103 shutil.copy(filename, outpath) 104 105def build_libraries(): 106 """ 107 Prepare the capstone directory for a binary distribution or installation. 108 Builds shared libraries and copies header files. 109 110 Will use a src/ dir if one exists in the current directory, otherwise assumes it's in the repo 111 """ 112 cwd = os.getcwd() 113 clean_bins() 114 os.mkdir(HEADERS_DIR) 115 os.mkdir(LIBS_DIR) 116 117 # copy public headers 118 shutil.copytree(os.path.join(BUILD_DIR, 'include'), os.path.join(HEADERS_DIR, 'capstone')) 119 120 # if prebuilt libraries are available, use those and cancel build 121 if os.path.exists(os.path.join(ROOT_DIR, 'prebuilt', LIBRARY_FILE)) and \ 122 (not STATIC_LIBRARY_FILE or os.path.exists(os.path.join(ROOT_DIR, 'prebuilt', STATIC_LIBRARY_FILE))): 123 shutil.copy(os.path.join(ROOT_DIR, 'prebuilt', LIBRARY_FILE), LIBS_DIR) 124 if STATIC_LIBRARY_FILE is not None: 125 shutil.copy(os.path.join(ROOT_DIR, 'prebuilt', STATIC_LIBRARY_FILE), LIBS_DIR) 126 return 127 128 os.chdir(BUILD_DIR) 129 130 # platform description refers at https://docs.python.org/2/library/sys.html#sys.platform 131 if SYSTEM == "win32": 132 # Windows build: this process requires few things: 133 # - CMake + MSVC installed 134 # - Run this command in an environment setup for MSVC 135 if not os.path.exists("build"): os.mkdir("build") 136 os.chdir("build") 137 # Do not build tests & static library 138 os.system('cmake -DCMAKE_BUILD_TYPE=RELEASE -DCAPSTONE_BUILD_TESTS=0 -DCAPSTONE_BUILD_STATIC=0 -G "NMake Makefiles" ..') 139 os.system("nmake") 140 else: # Unix incl. cygwin 141 os.system("CAPSTONE_BUILD_CORE_ONLY=yes bash ./make.sh") 142 143 shutil.copy(LIBRARY_FILE, LIBS_DIR) 144 if STATIC_LIBRARY_FILE: shutil.copy(STATIC_LIBRARY_FILE, LIBS_DIR) 145 os.chdir(cwd) 146 147 148class custom_sdist(sdist): 149 def run(self): 150 clean_bins() 151 copy_sources() 152 return sdist.run(self) 153 154 155class custom_build(build): 156 def run(self): 157 log.info('Building C extensions') 158 build_libraries() 159 return build.run(self) 160 161 162class custom_bdist_egg(bdist_egg): 163 def run(self): 164 self.run_command('build') 165 return bdist_egg.run(self) 166 167def dummy_src(): 168 return [] 169 170cmdclass = {} 171cmdclass['build'] = custom_build 172cmdclass['sdist'] = custom_sdist 173cmdclass['bdist_egg'] = custom_bdist_egg 174 175try: 176 from setuptools.command.develop import develop 177 class custom_develop(develop): 178 def run(self): 179 log.info("Building C extensions") 180 build_libraries() 181 return develop.run(self) 182 183 cmdclass['develop'] = custom_develop 184except ImportError: 185 print("Proper 'develop' support unavailable.") 186 187if 'bdist_wheel' in sys.argv and '--plat-name' not in sys.argv: 188 idx = sys.argv.index('bdist_wheel') + 1 189 sys.argv.insert(idx, '--plat-name') 190 name = get_platform() 191 if 'linux' in name: 192 # linux_* platform tags are disallowed because the python ecosystem is fubar 193 # linux builds should be built in the centos 5 vm for maximum compatibility 194 # see https://github.com/pypa/manylinux 195 # see also https://github.com/angr/angr-dev/blob/master/bdist.sh 196 sys.argv.insert(idx + 1, 'manylinux1_' + platform.machine()) 197 else: 198 # https://www.python.org/dev/peps/pep-0425/ 199 sys.argv.insert(idx + 1, name.replace('.', '_').replace('-', '_')) 200 201setup( 202 provides=['capstone'], 203 packages=['capstone'], 204 name='capstone', 205 version=VERSION, 206 author='Nguyen Anh Quynh', 207 author_email='aquynh@gmail.com', 208 description='Capstone disassembly engine', 209 url='http://www.capstone-engine.org', 210 classifiers=[ 211 'License :: OSI Approved :: BSD License', 212 'Programming Language :: Python :: 2', 213 'Programming Language :: Python :: 3', 214 ], 215 requires=['ctypes'], 216 cmdclass=cmdclass, 217 zip_safe=True, 218 include_package_data=True, 219 package_data={ 220 "capstone": ["lib/*", "include/capstone/*"], 221 } 222) 223