xref: /aosp_15_r20/external/capstone/bindings/python/setup.py (revision 9a0e4156d50a75a99ec4f1653a0e9602a5d45c18)
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