Triton/CMakeLists.txt
2026-05-10 22:01:21 -03:00

259 lines
10 KiB
CMake

##
## Copyright (C) - Triton
##
## This program is under the terms of the BSD License.
##
##################################################################################### CMake libtriton
cmake_minimum_required(VERSION 3.20)
set(TRITON_ROOT "${CMAKE_CURRENT_LIST_DIR}")
# Read the build version
file(READ ${TRITON_ROOT}/.build_number BUILD_NUMBER)
set(VERSION_MAJOR 1)
set(VERSION_MINOR 0)
set(VERSION_BUILD ${BUILD_NUMBER})
# Used for write_basic_package_version_file()
project(triton VERSION ${VERSION_MAJOR}.${VERSION_MINOR})
# Define cmake options
include(CMakeDependentOption)
option(ASAN "Enable the ASAN linking" OFF)
option(BITWUZLA_INTERFACE "Use Bitwuzla as SMT solver" OFF)
option(BUILD_SHARED_LIBS "Build a shared library" ON)
option(GCOV "Enable code coverage" OFF)
option(LLVM_INTERFACE "Use LLVM for lifting" OFF)
option(MSVC_STATIC "Use statically-linked runtime library" OFF)
option(Z3_INTERFACE "Use Z3 as SMT solver" ON)
option(BOOST_INTERFACE "Use Boost as multiprecision library" OFF)
option(PYTHON_BINDINGS_AUTOCOMPLETE "Generate an autocomplete stub file" OFF)
# Define cmake dependent options
cmake_dependent_option(BUILD_EXAMPLES "Build the examples" ON BUILD_SHARED_LIBS OFF)
cmake_dependent_option(ENABLE_TEST "Do testing" ON BUILD_SHARED_LIBS OFF)
cmake_dependent_option(PYTHON_BINDINGS "Enable Python bindings" ON BUILD_SHARED_LIBS OFF)
#Enable ctest
include(CTest)
add_custom_target(check
COMMAND ${CMAKE_COMMAND} -E env PYTHONPATH="${CMAKE_CURRENT_BINARY_DIR}/src/libtriton" ctest --output-on-failure
DEPENDS triton
)
if(PYTHON_BINDINGS)
message(STATUS "Compiling with Python bindings")
if(NOT PYTHON_VERSION AND NOT PYTHON_EXECUTABLE)
# On Windows, python3 points to the Windows Store version of Python.
# Installations from the Python website do not contain python3.exe
if(WIN32)
set(PYTHON_NAMES python python3)
else()
set(PYTHON_NAMES python3 python)
endif()
# Find the python version the user has in the PATH
# This prevents an issue where an unexpected python version is used
# (eg the system/homebrew python in a virtual environment)
find_program(PYTHON_EXECUTABLE
NAMES ${PYTHON_NAMES}
NO_PACKAGE_ROOT_PATH
NO_CMAKE_PATH
NO_CMAKE_ENVIRONMENT_PATH
NO_CMAKE_SYSTEM_PATH
NO_CMAKE_INSTALL_PREFIX
REQUIRED
)
endif()
if(PYTHON_EXECUTABLE)
# Make sure the specified python is not fully broken
execute_process(COMMAND ${PYTHON_EXECUTABLE} --version
RESULT_VARIABLE PYTHON_RESULT
OUTPUT_QUIET
)
if(NOT PYTHON_RESULT EQUAL 0)
message(FATAL_ERROR "Broken python: ${PYTHON_EXECUTABLE}")
endif()
endif()
# Set the hints for the Python3 package
# Reference: https://cmake.org/cmake/help/latest/module/FindPython3.html#hints
# The PYTHON_XXX variables are not used by FindPython3, but they are translated
# for backwards compatibility with previous Triton versions
set(Python3_EXECUTABLE "${PYTHON_EXECUTABLE}")
if(PYTHON_LIBRARY)
set(Python3_LIBRARY "${PYTHON_LIBRARY}")
elseif(PYTHON_LIBRARIES)
set(Python3_LIBRARY "${PYTHON_LIBRARIES}")
endif()
if(PYTHON_INCLUDE_DIRS)
set(Python3_INCLUDE_DIR "${PYTHON_INCLUDE_DIRS}")
endif()
set(Python3_FIND_VIRTUALENV "FIRST")
find_package(Python3 ${PYTHON_VERSION} COMPONENTS Interpreter Development REQUIRED)
message(STATUS "Python3 includes: ${Python3_INCLUDE_DIRS}")
message(STATUS "Python3 libraries: ${Python3_LIBRARIES}")
# For backwards compatibility with existing CMake
set(PYTHON_EXECUTABLE "${Python3_EXECUTABLE}")
set(PYTHONPATH_CMD ${CMAKE_COMMAND} -E env PYTHONPATH=$<TARGET_FILE_DIR:triton>/)
add_custom_target(test-python
COMMAND ${PYTHONPATH_CMD} ${PYTHON_EXECUTABLE} -m unittest discover ${TRITON_ROOT}/src/testers/unittests
DEPENDS python-triton
)
add_test(NAME test-python
COMMAND ${PYTHON_EXECUTABLE} -m unittest discover ${triton_SOURCE_DIR}/src/testers/unittests
)
set_property(TEST test-python
PROPERTY ENVIRONMENT "PYTHONPATH=$<TARGET_FILE_DIR:triton>/"
)
else()
add_custom_target(test-python
COMMAND echo "No python test as python support is disabled"
)
endif()
if(${CMAKE_SYSTEM_NAME} MATCHES "Linux" AND GCOV)
message(STATUS "Compiled with GCOV")
add_custom_target(gcov-test
COMMAND lcov --zerocounters --directory $<TARGET_FILE_DIR:triton>
COMMAND lcov --capture --initial --directory $<TARGET_FILE_DIR:triton> --output-file app
COMMAND ${CMAKE_COMMAND} --build ${CMAKE_BINARY_DIR} --target test-python
COMMAND ctest --output-on-failure
COMMAND lcov --no-checksum --directory $<TARGET_FILE_DIR:triton> --capture --output-file coverage.info
COMMAND lcov --ignore-errors unused --remove coverage.info '/usr*' --remove coverage.info 'pintools*' --remove coverage.info 'examples*' -o coverage.info
COMMAND genhtml coverage.info -o coverage
COMMAND ${CMAKE_COMMAND} -E echo "-- Report generated in ${CMAKE_CURRENT_BINARY_DIR}/coverage/index.html"
)
add_dependencies(gcov-test triton)
add_dependencies(check gcov-test)
add_compile_options(--coverage)
add_link_options(--coverage)
endif()
# Specific OSX POLICY
if(${CMAKE_SYSTEM_NAME} MATCHES "Darwin")
if(POLICY CMP0025)
cmake_policy(SET CMP0025 NEW) # report Apple's Clang as just Clang
endif()
if(POLICY CMP0042)
cmake_policy(SET CMP0042 NEW) # MACOSX_RPATH
endif()
endif()
# Custom cmake search
list(APPEND CMAKE_MODULE_PATH "${TRITON_ROOT}/CMakeModules/")
# Find Z3
if(Z3_INTERFACE)
message(STATUS "Compiling with Z3 SMT solver")
find_package(Z3 REQUIRED)
message(STATUS "Z3 version: ${Z3_VERSION}")
if(TARGET z3::libz3)
link_libraries(z3::libz3)
elseif(DEFINED Z3_INCLUDE_DIRS)
include_directories(${Z3_INCLUDE_DIRS})
else()
message(FATAL_ERROR "Unexpected Z3 package search outcome: neither target z3::libz3 not variable Z3_INCLUDE_DIRS exists.")
endif()
set(TRITON_Z3_INTERFACE ON)
endif()
# Find bitwuzla
if(BITWUZLA_INTERFACE)
message(STATUS "Compiling with Bitwuzla SMT solver")
find_package(BITWUZLA REQUIRED)
if(TARGET Bitwuzla::bitwuzla)
link_libraries(Bitwuzla::bitwuzla)
elseif(DEFINED BITWUZLA_INCLUDE_DIRS)
include_directories(${BITWUZLA_INCLUDE_DIRS})
else()
message(FATAL_ERROR "Unexpected Bitwuzla package search outcome: neither target Bitwuzla::bitwuzla not variable BITWUZLA_INCLUDE_DIRS exists.")
endif()
set(TRITON_BITWUZLA_INTERFACE ON)
endif()
# Find LLVM
if(LLVM_INTERFACE)
message(STATUS "Compiling with LLVM")
if(NOT DEFINED LLVM_INCLUDE_DIRS)
find_package(LLVM REQUIRED CONFIG)
message(STATUS "Found LLVM ${LLVM_PACKAGE_VERSION}")
message(STATUS "Using LLVMConfig.cmake in: ${LLVM_DIR}")
if(LLVM_LINK_LLVM_DYLIB)
set(LLVM_LIBRARIES LLVM)
else()
set(LLVM_LIBRARIES ${LLVM_AVAILABLE_LIBS})
endif()
# LLVM 18+ Linux prebuilts are compiled without exception handling and
# propagate -fno-exceptions via INTERFACE_COMPILE_OPTIONS. Since those
# options are applied after Triton's own compile flags in the compiler
# command (last-flag-wins), they would override Triton's exceptions. Strip them.
if(NOT MSVC AND LLVM_VERSION_MAJOR GREATER_EQUAL 18)
foreach(_lib IN ITEMS LLVM ${LLVM_AVAILABLE_LIBS})
if(TARGET ${_lib})
get_target_property(_opts ${_lib} INTERFACE_COMPILE_OPTIONS)
if(_opts)
list(REMOVE_ITEM _opts -fno-exceptions -fno-rtti)
set_target_properties(${_lib} PROPERTIES INTERFACE_COMPILE_OPTIONS "${_opts}")
endif()
endif()
endforeach()
endif()
# LLVM 19+ Linux prebuilts ship static archives compiled with thin LTO.
# They contain LLVM bitcode rather than ELF objects, which GNU ld cannot
# link. lld handles thin-LTO archives transparently; force it here.
# Note: only static-archive builds (not dylib) on LLVM 19+; older
# distro-packaged LLVMs use plain ELF archives and may not have lld.
if(CMAKE_SYSTEM_NAME MATCHES "Linux"
AND NOT "$CACHE{LLVM_LINK_LLVM_DYLIB}"
AND LLVM_VERSION_MAJOR GREATER_EQUAL 19)
add_link_options(-B${LLVM_TOOLS_BINARY_DIR} -fuse-ld=lld)
message(STATUS "Using lld for thin-LTO static archives")
endif()
endif()
include_directories(${LLVM_INCLUDE_DIRS})
set(TRITON_LLVM_INTERFACE ON)
endif()
# Find Capstone
message(STATUS "Compiling with Capstone")
find_package(CAPSTONE 5 REQUIRED)
message(STATUS "CAPSTONE version: ${CAPSTONE_VERSION}")
if(TARGET capstone::capstone)
link_libraries(capstone::capstone)
elseif(DEFINED CAPSTONE_INCLUDE_DIRS)
include_directories(${CAPSTONE_INCLUDE_DIRS})
else()
message(FATAL_ERROR "Unexpected capstone package search outcome: neither target capstone::capstone not variable CAPSTONE_INCLUDE_DIRS exists.")
endif()
# Find boost
if(BOOST_INTERFACE)
message(STATUS "Compiling with Boost headers")
find_package(Boost 1.55.0)
if (Boost_FOUND)
include_directories("${Boost_INCLUDE_DIRS}")
set(TRITON_BOOST_INTERFACE ON)
else()
message(STATUS "Boost headers not found, compiling with wide-integer headers")
set(TRITON_BOOST_INTERFACE OFF)
endif()
else()
message(STATUS "Compiling with wide-integer headers")
set(TRITON_BOOST_INTERFACE OFF)
endif()
if(PYTHON_BINDINGS_AUTOCOMPLETE)
message(STATUS "Compiling with Python autocomplete stub")
endif()
add_subdirectory(src)
add_subdirectory(doc)