• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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