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 VERSIONED_LIBRARY_FILE = "libcapstone.{PKG_MAJOR}.dylib".format(**VERSION_DATA) 61 LIBRARY_FILE = "libcapstone.dylib" 62 STATIC_LIBRARY_FILE = 'libcapstone.a' 63elif SYSTEM in ('win32', 'cygwin'): 64 VERSIONED_LIBRARY_FILE = "capstone.dll" 65 LIBRARY_FILE = "capstone.dll" 66 STATIC_LIBRARY_FILE = None 67else: 68 VERSIONED_LIBRARY_FILE = "libcapstone.so.{PKG_MAJOR}".format(**VERSION_DATA) 69 LIBRARY_FILE = "libcapstone.so" 70 STATIC_LIBRARY_FILE = 'libcapstone.a' 71 72def clean_bins(): 73 shutil.rmtree(LIBS_DIR, ignore_errors=True) 74 shutil.rmtree(HEADERS_DIR, ignore_errors=True) 75 76def copy_sources(): 77 """Copy the C sources into the source directory. 78 This rearranges the source files under the python distribution 79 directory. 80 """ 81 src = [] 82 83 try: 84 shutil.rmtree("src/") 85 except (IOError, OSError): 86 pass 87 88 shutil.copytree(os.path.join(BUILD_DIR, "arch"), os.path.join(SRC_DIR, "arch")) 89 shutil.copytree(os.path.join(BUILD_DIR, "include"), os.path.join(SRC_DIR, "include")) 90 91 src.extend(glob.glob(os.path.join(BUILD_DIR, "*.[ch]"))) 92 src.extend(glob.glob(os.path.join(BUILD_DIR, "*.mk"))) 93 94 src.extend(glob.glob(os.path.join(BUILD_DIR, "Makefile"))) 95 src.extend(glob.glob(os.path.join(BUILD_DIR, "LICENSE*"))) 96 src.extend(glob.glob(os.path.join(BUILD_DIR, "README"))) 97 src.extend(glob.glob(os.path.join(BUILD_DIR, "*.TXT"))) 98 src.extend(glob.glob(os.path.join(BUILD_DIR, "RELEASE_NOTES"))) 99 src.extend(glob.glob(os.path.join(BUILD_DIR, "make.sh"))) 100 src.extend(glob.glob(os.path.join(BUILD_DIR, "CMakeLists.txt"))) 101 src.extend(glob.glob(os.path.join(BUILD_DIR, "pkgconfig.mk"))) 102 103 for filename in src: 104 outpath = os.path.join(SRC_DIR, os.path.basename(filename)) 105 log.info("%s -> %s" % (filename, outpath)) 106 shutil.copy(filename, outpath) 107 108def build_libraries(): 109 """ 110 Prepare the capstone directory for a binary distribution or installation. 111 Builds shared libraries and copies header files. 112 113 Will use a src/ dir if one exists in the current directory, otherwise assumes it's in the repo 114 """ 115 cwd = os.getcwd() 116 clean_bins() 117 os.mkdir(HEADERS_DIR) 118 os.mkdir(LIBS_DIR) 119 120 # copy public headers 121 shutil.copytree(os.path.join(BUILD_DIR, 'include', 'capstone'), os.path.join(HEADERS_DIR, 'capstone')) 122 123 # if prebuilt libraries are available, use those and cancel build 124 if os.path.exists(os.path.join(ROOT_DIR, 'prebuilt', LIBRARY_FILE)) and \ 125 (not STATIC_LIBRARY_FILE or os.path.exists(os.path.join(ROOT_DIR, 'prebuilt', STATIC_LIBRARY_FILE))): 126 shutil.copy(os.path.join(ROOT_DIR, 'prebuilt', LIBRARY_FILE), LIBS_DIR) 127 if STATIC_LIBRARY_FILE is not None: 128 shutil.copy(os.path.join(ROOT_DIR, 'prebuilt', STATIC_LIBRARY_FILE), LIBS_DIR) 129 return 130 131 os.chdir(BUILD_DIR) 132 133 # platform description refers at https://docs.python.org/2/library/sys.html#sys.platform 134 if SYSTEM == "win32": 135 # Windows build: this process requires few things: 136 # - CMake + MSVC installed 137 # - Run this command in an environment setup for MSVC 138 if not os.path.exists("build"): os.mkdir("build") 139 os.chdir("build") 140 # Do not build tests & static library 141 os.system('cmake -DCMAKE_BUILD_TYPE=RELEASE -DCAPSTONE_BUILD_TESTS=0 -DCAPSTONE_BUILD_STATIC=0 -G "NMake Makefiles" ..') 142 os.system("nmake") 143 elif "bsd" in SYSTEM: 144 # *BSD distinguishes make (BSD) vs gmake (GNU). Use cmake + bsd make :-) 145 if not os.path.exists("build"): os.mkdir("build") 146 os.chdir("build") 147 # Do not build tests & static library 148 os.system('cmake -DCMAKE_BUILD_TYPE=RELEASE -DCAPSTONE_BUILD_TESTS=0 -DCAPSTONE_BUILD_STATIC=0 ..') 149 os.system("make") 150 else: # Unix incl. cygwin 151 os.system("CAPSTONE_BUILD_CORE_ONLY=yes bash ./make.sh") 152 153 shutil.copy(VERSIONED_LIBRARY_FILE, os.path.join(LIBS_DIR, LIBRARY_FILE)) 154 155 # only copy static library if it exists (it's a build option) 156 if STATIC_LIBRARY_FILE and os.path.exists(STATIC_LIBRARY_FILE): 157 shutil.copy(STATIC_LIBRARY_FILE, LIBS_DIR) 158 os.chdir(cwd) 159 160 161class custom_sdist(sdist): 162 def run(self): 163 clean_bins() 164 copy_sources() 165 return sdist.run(self) 166 167 168class custom_build(build): 169 def run(self): 170 if 'LIBCAPSTONE_PATH' in os.environ: 171 log.info('Skipping building C extensions since LIBCAPSTONE_PATH is set') 172 else: 173 log.info('Building C extensions') 174 build_libraries() 175 return build.run(self) 176 177 178class custom_bdist_egg(bdist_egg): 179 def run(self): 180 self.run_command('build') 181 return bdist_egg.run(self) 182 183def dummy_src(): 184 return [] 185 186cmdclass = {} 187cmdclass['build'] = custom_build 188cmdclass['sdist'] = custom_sdist 189cmdclass['bdist_egg'] = custom_bdist_egg 190 191try: 192 from setuptools.command.develop import develop 193 class custom_develop(develop): 194 def run(self): 195 log.info("Building C extensions") 196 build_libraries() 197 return develop.run(self) 198 199 cmdclass['develop'] = custom_develop 200except ImportError: 201 print("Proper 'develop' support unavailable.") 202 203if 'bdist_wheel' in sys.argv and '--plat-name' not in sys.argv: 204 idx = sys.argv.index('bdist_wheel') + 1 205 sys.argv.insert(idx, '--plat-name') 206 name = get_platform() 207 if 'linux' in name: 208 # linux_* platform tags are disallowed because the python ecosystem is fubar 209 # linux builds should be built in the centos 6 vm for maximum compatibility 210 # see https://github.com/pypa/manylinux 211 # see also https://github.com/angr/angr-dev/blob/master/bdist.sh and 212 # https://www.python.org/dev/peps/pep-0599/ 213 sys.argv.insert(idx + 1, 'manylinux2014_' + platform.machine()) 214 else: 215 # https://www.python.org/dev/peps/pep-0425/ 216 sys.argv.insert(idx + 1, name.replace('.', '_').replace('-', '_')) 217 218long_desc = ''' 219Capstone is a disassembly framework with the target of becoming the ultimate 220disasm engine for binary analysis and reversing in the security community. 221 222Created by Nguyen Anh Quynh, then developed and maintained by a small community, 223Capstone offers some unparalleled features: 224 225- Support multiple hardware architectures: ARM, ARM64 (ARMv8), Mips, PPC, Sparc, 226 SystemZ, XCore and X86 (including X86_64). 227 228- Having clean/simple/lightweight/intuitive architecture-neutral API. 229 230- Provide details on disassembled instruction (called "decomposer" by others). 231 232- Provide semantics of the disassembled instruction, such as list of implicit 233 registers read & written. 234 235- Implemented in pure C language, with lightweight wrappers for C++, C#, Go, 236 Java, NodeJS, Ocaml, Python, Ruby & Vala ready (available in main code, 237 or provided externally by the community). 238 239- Native support for all popular platforms: Windows, Mac OSX, iOS, Android, 240 Linux, *BSD, Solaris, etc. 241 242- Thread-safe by design. 243 244- Special support for embedding into firmware or OS kernel. 245 246- High performance & suitable for malware analysis (capable of handling various 247 X86 malware tricks). 248 249- Distributed under the open source BSD license. 250 251Further information is available at http://www.capstone-engine.org 252 253 254[License] 255 256This project is released under the BSD license. If you redistribute the binary 257or source code of Capstone, please attach file LICENSE.TXT with your products. 258''' 259 260setup( 261 provides=['capstone'], 262 packages=['capstone'], 263 name='capstone', 264 version=VERSION, 265 author='Nguyen Anh Quynh', 266 author_email='[email protected]', 267 description='Capstone disassembly engine', 268 long_description=long_desc, 269 long_description_content_type="text/markdown", 270 url='https://www.capstone-engine.org', 271 python_requires='>=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*', 272 classifiers=[ 273 'Development Status :: 5 - Production/Stable', 274 'Intended Audience :: Developers', 275 'Topic :: Software Development :: Build Tools', 276 'License :: OSI Approved :: BSD License', 277 'Programming Language :: Python :: 2', 278 'Programming Language :: Python :: 2.7', 279 'Programming Language :: Python :: 3', 280 ], 281 requires=['ctypes'], 282 cmdclass=cmdclass, 283 zip_safe=True, 284 include_package_data=True, 285 is_pure=False, 286 package_data={ 287 "capstone": ["lib/*", "include/capstone/*"], 288 } 289) 290