Files
2026-05-10 22:01:21 -03:00

250 lines
9.1 KiB
Python

##
## Copyright (C) - Triton
##
## This program is under the terms of the Apache License 2.0.
##
import os
import platform
import re
import subprocess
import sys
from distutils.file_util import copy_file
from distutils.version import LooseVersion
from setuptools import Extension
from setuptools import setup
from setuptools.command.build_ext import build_ext
VERSION_MAJOR = 1
VERSION_MINOR = 0
VERSION_PATCH = 0
ON = "ON"
OFF = "OFF"
clang = "clang"
clang_cl = "clang_cl"
vs2022 = "vs2022"
Release = "Release"
Debug = "Debug"
RELEASE_CANDIDATE = 4
Z3_INTERFACE = ON
LLVM_INTERFACE = OFF
BITWUZLA_INTERFACE = OFF
BOOST_INTERFACE = OFF
BUILDTOOLS = vs2022 # clang or clang_cl or vs2022
CMAKE_BUILD_TYPE = Release # Release or Debug
VERSION = f'{VERSION_MAJOR}.{VERSION_MINOR}.{VERSION_PATCH}' + \
f'rc{RELEASE_CANDIDATE}' if RELEASE_CANDIDATE else ''
def is_cmake_true(value):
"""Check if CMake would parse the value as True or False. Might not be completely accurate.
Based on https://cmake.org/cmake/help/latest/command/if.html#basic-expressions"""
if(value in ['ON', 'YES', 'TRUE', 'Y']):
return True
try:
float(value)
if(int(value) == 0):
return False
return True
except:
return False
class CMakeExtension(Extension):
def __init__(self, name, sourcedir=''):
Extension.__init__(self, name, sources=[])
self.sourcedir = os.path.abspath(sourcedir)
class CMakeBuild(build_ext):
def run(self):
ext = self.extensions[0]
self.build_extension(ext)
self.copy_extension_to_source(ext)
self.copy_autocomplete()
def build_extension(self, ext):
# Set platform-agnostric arguments.
cmake_args = [
# General arguments.
'-DPYTHON_EXECUTABLE=' + sys.executable,
'-DCMAKE_BUILD_TYPE='+ CMAKE_BUILD_TYPE,
]
# Interfaces can be defined using environment variables.
# Interfaces by default:
#
# Z3_INTERFACE=On
# LLVM_INTERFACE=Off
# BITWUZLA_INTERFACE=Off
# BOOST_INTERFACE=Off
#
for arg, value in [('Z3_INTERFACE', Z3_INTERFACE), ('LLVM_INTERFACE', LLVM_INTERFACE), ('BITWUZLA_INTERFACE', BITWUZLA_INTERFACE), ('BOOST_INTERFACE', BOOST_INTERFACE)]:
if os.getenv(arg):
cmake_args += [f'-D{arg}=' + os.getenv(arg)]
else:
cmake_args += [f'-D{arg}={value}']
build_args = []
env = os.environ.copy()
env['CXXFLAGS'] = '{} -DVERSION_INFO=\\"{}\\"'.format(env.get('CXXFLAGS', ''), self.distribution.get_version())
# Set platform-specific arguments.
if platform.system() == "Linux":
build_args += ['--', '-j4']
elif platform.system() == "Darwin":
build_args += ['--', '-j4']
elif platform.system() == "Windows":
if BUILDTOOLS == vs2022:
cmake_args += []
build_args += ["--", "/m:4"]
else:
assert os.getenv('COMPILER_DIR'), "COMPILER_DIR(clang or clang_cl) env not found"
if BUILDTOOLS == clang:
cmake_args += [
"-DCMAKE_C_COMPILER=" + os.getenv('COMPILER_DIR') + "/clang.exe",
"-DCMAKE_CXX_COMPILER=" + os.getenv('COMPILER_DIR') + "/clang++.exe",
"-DCMAKE_RC_COMPILER=" + os.getenv('COMPILER_DIR') + "/llvm-rc.exe",
"-G Ninja",
]
if BUILDTOOLS == clang_cl:
cmake_args += [
"-DCMAKE_C_COMPILER= " + os.getenv('COMPILER_DIR') + "/clang-cl.exe",
"-DCMAKE_CXX_COMPILER=" + os.getenv('COMPILER_DIR') + "/clang-cl.exe",
"-DCMAKE_RC_COMPILER=" + os.getenv('COMPILER_DIR') + "/llvm-rc.exe",
"-G Ninja",
]
else:
raise Exception(f'Platform not supported: {platform.system()}')
# Custom Python paths.
if os.getenv('PYTHON_LIBRARIES'):
cmake_args += ['-DPYTHON_LIBRARIES=' + os.getenv('PYTHON_LIBRARIES')]
if os.getenv('PYTHON_INCLUDE_DIRS'):
cmake_args += ['-DPYTHON_INCLUDE_DIRS=' + os.getenv('PYTHON_INCLUDE_DIRS')]
if os.getenv('PYTHON_LIBRARY'):
cmake_args += ['-DPYTHON_LIBRARY=' + os.getenv('PYTHON_LIBRARY')]
if os.getenv('PYTHON_VERSION'):
cmake_args += ['-DPYTHON_VERSION=' + os.getenv('PYTHON_VERSION')]
# Custom Z3 paths.
if os.getenv('Z3_LIBRARIES'):
cmake_args += ['-DZ3_LIBRARIES=' + os.getenv('Z3_LIBRARIES')]
if os.getenv('Z3_INCLUDE_DIRS'):
cmake_args += ['-DZ3_INCLUDE_DIRS=' + os.getenv('Z3_INCLUDE_DIRS')]
# Custom Bitwuzla paths.
if os.getenv('BITWUZLA_LIBRARIES'):
cmake_args += ['-DBITWUZLA_LIBRARIES=' + os.getenv('BITWUZLA_LIBRARIES')]
if os.getenv('BITWUZLA_INCLUDE_DIRS'):
cmake_args += ['-DBITWUZLA_INCLUDE_DIRS=' + os.getenv('BITWUZLA_INCLUDE_DIRS')]
# Custom Capstone paths.
if os.getenv('CAPSTONE_LIBRARIES'):
cmake_args += ['-DCAPSTONE_LIBRARIES=' + os.getenv('CAPSTONE_LIBRARIES')]
if os.getenv('CAPSTONE_INCLUDE_DIRS'):
cmake_args += ['-DCAPSTONE_INCLUDE_DIRS=' + os.getenv('CAPSTONE_INCLUDE_DIRS')]
# Custom LLVM paths.
if os.getenv('LLVM_LIBRARIES'):
cmake_args += ['-DLLVM_LIBRARIES=' + os.getenv('LLVM_LIBRARIES')]
if os.getenv('LLVM_INCLUDE_DIRS'):
cmake_args += ['-DLLVM_INCLUDE_DIRS=' + os.getenv('LLVM_INCLUDE_DIRS')]
# Custom CMake prefix path.
if os.getenv('CMAKE_PREFIX_PATH'):
cmake_args += ['-DCMAKE_PREFIX_PATH=' + os.getenv('CMAKE_PREFIX_PATH')]
# Autocomplete stub generation. Enabled by default.
python_autocomplete_value = os.getenv('PYTHON_BINDINGS_AUTOCOMPLETE', default='ON').upper()
if python_autocomplete_value:
cmake_args += ['-DPYTHON_BINDINGS_AUTOCOMPLETE=' + python_autocomplete_value]
# Create temp and lib folders.
if not os.path.exists(self.build_temp):
os.makedirs(self.build_temp)
if not os.path.exists(self.build_lib):
os.makedirs(self.build_lib)
subprocess.check_call(['cmake', ext.sourcedir] + cmake_args, cwd=self.build_temp, env=env)
subprocess.check_call(['cmake', '--build', '.', '--config', CMAKE_BUILD_TYPE, '--target', 'python-triton'] + build_args, cwd=self.build_temp)
# The autocomplete file has to be built separately.
if (is_cmake_true(python_autocomplete_value)):
subprocess.check_call(['cmake', '--build', '.', '--config', CMAKE_BUILD_TYPE, '--target', 'python_autocomplete'], cwd=self.build_temp)
def copy_extension_to_source(self, ext):
fullname = self.get_ext_fullname(ext.name)
filename = self.get_ext_filename(fullname)
if platform.system() == "Linux":
src_filename = os.path.join(self.build_temp + '/src/libtriton', 'triton.so')
dst_filename = os.path.join(self.build_lib, os.path.basename(filename))
elif platform.system() == "Darwin":
src_filename = os.path.join(self.build_temp + '/src/libtriton', 'libtriton.dylib')
dst_filename = os.path.join(self.build_lib, os.path.basename(filename))
elif platform.system() == "Windows":
if BUILDTOOLS == vs2022:
src_filename = os.path.join(self.build_temp + '/src/libtriton/' + CMAKE_BUILD_TYPE, 'triton.pyd')
else:
src_filename = os.path.join(self.build_temp + '/src/libtriton', 'triton.pyd')
dst_filename = os.path.join(self.build_lib, os.path.basename(filename))
else:
raise Exception(f'Platform not supported: {platform.system()}')
copy_file(src_filename, dst_filename, verbose=self.verbose)
def copy_autocomplete(self):
src_filename = os.path.join(self.build_temp + '/doc/triton_autocomplete', 'triton.pyi')
if(os.path.exists(src_filename)):
copy_file(src_filename, self.build_lib, verbose=self.verbose)
with open("README.md", "r") as f:
long_description = f.read()
setup(
name="triton-library",
version=VERSION,
author="The Triton's community",
author_email="tritonlibrary@gmail.com",
description="Triton is a dynamic binary analysis library",
long_description=long_description,
long_description_content_type="text/markdown",
license = "Apache License Version 2.0",
license_files = ('LICENSE.txt',),
classifiers=[
"Programming Language :: C++",
"Programming Language :: Python :: 3",
"Topic :: Security",
"Topic :: Software Development :: Libraries",
],
python_requires='>=3.6',
project_urls={
'Homepage': 'https://triton-library.github.io/',
'Source': 'https://github.com/jonathansalwan/Triton',
},
ext_modules=[
CMakeExtension('triton', sourcedir='.')
],
cmdclass=dict(build_ext=CMakeBuild),
zip_safe=False,
install_requires=[]
)