mirror of
https://github.com/Cisco-Talos/clamav.git
synced 2025-10-19 10:23:17 +00:00

RelWithDebInfo is our preferred build type. It has optimizations and should run faster, but includes debugging symbols for better profiling, stack traces, etc. The Rust MinSizeRel support is just Release mode for now. There are optimizations we can do to shrink it further, but for now at least it won't actually be Debug (aka slow).
370 lines
14 KiB
CMake
370 lines
14 KiB
CMake
# Find the Rust toolchain and add the `add_rust_library()` API to build Rust
|
|
# libraries.
|
|
#
|
|
# Copyright (C) 2021-2022 Cisco Systems, Inc. and/or its affiliates. All rights reserved.
|
|
#
|
|
# Author: Micah Snyder
|
|
# To see this in a sample project, visit: https://github.com/micahsnyder/cmake-rust-demo
|
|
#
|
|
# Code to set the Cargo arguments was lifted from:
|
|
# https://github.com/Devolutions/CMakeRust
|
|
#
|
|
# This Module defines the following variables:
|
|
# - <program>_FOUND - True if the program was found
|
|
# - <program>_EXECUTABLE - path of the program
|
|
# - <program>_VERSION - version number of the program
|
|
#
|
|
# ... for the following Rust toolchain programs:
|
|
# - cargo
|
|
# - rustc
|
|
# - rustup
|
|
# - rust-gdb
|
|
# - rust-lldb
|
|
# - rustdoc
|
|
# - rustfmt
|
|
# - bindgen
|
|
#
|
|
# Callers can make any program mandatory by setting `<program>_REQUIRED` before
|
|
# the call to `find_package(Rust)`
|
|
#
|
|
# Eg:
|
|
#
|
|
# if(MAINTAINER_MODE)
|
|
# set(bindgen_REQUIRED 1)
|
|
# endif()
|
|
# find_package(Rust REQUIRED)
|
|
#
|
|
# This module also provides:
|
|
#
|
|
# - `add_rust_library()` - This allows a caller to create a Rust static library
|
|
# target which you can link to with `target_link_libraries()`.
|
|
#
|
|
# Your Rust static library target will itself depend on the native static libs
|
|
# you get from `rustc --crate-type staticlib --print=native-static-libs /dev/null`
|
|
#
|
|
# The CARGO_CMD environment variable will be set to "BUILD" so you can tell
|
|
# it's not building the unit tests inside your (optional) `build.rs` file.
|
|
#
|
|
# Example `add_rust_library()` usage:
|
|
#
|
|
# add_rust_library(TARGET yourlib WORKING_DIRECTORY "${CMAKE_CURRENT_SOURCE_DIR}")
|
|
# add_library(YourProject::yourlib ALIAS yourlib)
|
|
#
|
|
# add_executable(yourexe)
|
|
# target_link_libraries(yourexe YourProject::yourlib)
|
|
#
|
|
# - `add_rust_test()` - This allows a caller to run `cargo test` for a specific
|
|
# Rust target as a CTest test.
|
|
#
|
|
# The CARGO_CMD environment variable will be set to "TEST" so you can tell
|
|
# it's not building the unit tests inside your (optional) `build.rs` file.
|
|
#
|
|
# Example `add_rust_library()` usage:
|
|
#
|
|
# add_rust_test(NAME yourlib WORKING_DIRECTORY "${CMAKE_SOURCE_DIR}/yourlib")
|
|
# set_property(TEST yourlib PROPERTY ENVIRONMENT ${ENVIRONMENT})
|
|
#
|
|
|
|
if(NOT DEFINED CARGO_HOME)
|
|
if(WIN32)
|
|
set(CARGO_HOME "$ENV{USERPROFILE}/.cargo")
|
|
else()
|
|
set(CARGO_HOME "$ENV{HOME}/.cargo")
|
|
endif()
|
|
endif()
|
|
|
|
include(FindPackageHandleStandardArgs)
|
|
|
|
function(find_rust_program RUST_PROGRAM)
|
|
find_program(${RUST_PROGRAM}_EXECUTABLE ${RUST_PROGRAM}
|
|
HINTS "${CARGO_HOME}"
|
|
PATH_SUFFIXES "bin"
|
|
)
|
|
|
|
if(${RUST_PROGRAM}_EXECUTABLE)
|
|
execute_process(COMMAND "${${RUST_PROGRAM}_EXECUTABLE}" --version
|
|
OUTPUT_VARIABLE ${RUST_PROGRAM}_VERSION_OUTPUT
|
|
ERROR_VARIABLE ${RUST_PROGRAM}_VERSION_ERROR
|
|
RESULT_VARIABLE ${RUST_PROGRAM}_VERSION_RESULT
|
|
)
|
|
if(NOT ${${RUST_PROGRAM}_VERSION_RESULT} EQUAL 0)
|
|
message(STATUS "Rust tool `${RUST_PROGRAM}` not found: Failed to determine version.")
|
|
unset(${RUST_PROGRAM}_EXECUTABLE)
|
|
else()
|
|
string(REGEX
|
|
MATCH "[0-9]+\\.[0-9]+(\\.[0-9]+)?(-nightly)?"
|
|
${RUST_PROGRAM}_VERSION "${${RUST_PROGRAM}_VERSION_OUTPUT}"
|
|
)
|
|
set(${RUST_PROGRAM}_VERSION "${${RUST_PROGRAM}_VERSION}" PARENT_SCOPE)
|
|
message(STATUS "Rust tool `${RUST_PROGRAM}` found: ${${RUST_PROGRAM}_EXECUTABLE}, ${${RUST_PROGRAM}_VERSION}")
|
|
endif()
|
|
|
|
mark_as_advanced(${RUST_PROGRAM}_EXECUTABLE ${RUST_PROGRAM}_VERSION)
|
|
else()
|
|
if(${${RUST_PROGRAM}_REQUIRED})
|
|
message(FATAL_ERROR "Rust tool `${RUST_PROGRAM}` not found.")
|
|
else()
|
|
message(STATUS "Rust tool `${RUST_PROGRAM}` not found.")
|
|
endif()
|
|
endif()
|
|
endfunction()
|
|
|
|
function(cargo_vendor)
|
|
set(options)
|
|
set(oneValueArgs TARGET WORKING_DIRECTORY)
|
|
cmake_parse_arguments(ARGS "${options}" "${oneValueArgs}" "${multiValueArgs}" ${ARGN})
|
|
|
|
if(NOT EXISTS ${ARGS_WORKING_DIRECTORY}}/.cargo/config.toml)
|
|
# Vendor the dependencies and create .cargo/config.toml
|
|
# Vendored dependencies will be used during the build.
|
|
# This will allow us to package vendored dependencies in source tarballs
|
|
# for online builds when we run `cpack --config CPackSourceConfig.cmake`
|
|
message(STATUS "Running `cargo vendor` to collect dependencies for ${ARGS_TARGET}. This may take a while if the local crates.io index needs to be updated ...")
|
|
make_directory(${ARGS_WORKING_DIRECTORY}/.cargo)
|
|
execute_process(
|
|
COMMAND ${CMAKE_COMMAND} -E env "CARGO_TARGET_DIR=${CMAKE_CURRENT_BINARY_DIR}" ${cargo_EXECUTABLE} vendor ".cargo/vendor"
|
|
WORKING_DIRECTORY "${ARGS_WORKING_DIRECTORY}"
|
|
OUTPUT_VARIABLE CARGO_VENDOR_OUTPUT
|
|
ERROR_VARIABLE CARGO_VENDOR_ERROR
|
|
RESULT_VARIABLE CARGO_VENDOR_RESULT
|
|
)
|
|
if(NOT ${CARGO_VENDOR_RESULT} EQUAL 0)
|
|
message(FATAL_ERROR "Failed!\n${CARGO_VENDOR_ERROR}")
|
|
else()
|
|
message("Success!")
|
|
endif()
|
|
write_file(${ARGS_WORKING_DIRECTORY}/.cargo/config.toml "
|
|
[source.crates-io]
|
|
replace-with = \"vendored-sources\"
|
|
|
|
[source.vendored-sources]
|
|
directory = \".cargo/vendor\"
|
|
"
|
|
)
|
|
endif()
|
|
endfunction()
|
|
|
|
function(add_rust_library)
|
|
set(options)
|
|
set(oneValueArgs TARGET WORKING_DIRECTORY)
|
|
cmake_parse_arguments(ARGS "${options}" "${oneValueArgs}" "${multiValueArgs}" ${ARGN})
|
|
|
|
if(WIN32)
|
|
set(OUTPUT "${CMAKE_CURRENT_BINARY_DIR}/${LIB_TARGET}/${LIB_BUILD_TYPE}/${ARGS_TARGET}.lib")
|
|
else()
|
|
set(OUTPUT "${CMAKE_CURRENT_BINARY_DIR}/${LIB_TARGET}/${LIB_BUILD_TYPE}/lib${ARGS_TARGET}.a")
|
|
endif()
|
|
|
|
file(GLOB_RECURSE LIB_SOURCES "${ARGS_WORKING_DIRECTORY}/*.rs")
|
|
|
|
set(MY_CARGO_ARGS ${CARGO_ARGS})
|
|
list(APPEND MY_CARGO_ARGS "--target-dir" ${CMAKE_CURRENT_BINARY_DIR})
|
|
list(JOIN MY_CARGO_ARGS " " MY_CARGO_ARGS_STRING)
|
|
|
|
# Build the library and generate the c-binding
|
|
if ("${CMAKE_OSX_ARCHITECTURES}" MATCHES "^arm64;x86_64$")
|
|
add_custom_command(
|
|
OUTPUT "${OUTPUT}"
|
|
COMMAND ${CMAKE_COMMAND} -E env "CARGO_CMD=build" "CARGO_TARGET_DIR=${CMAKE_CURRENT_BINARY_DIR}" "MAINTAINER_MODE=${MAINTAINER_MODE}" "RUSTFLAGS=\"${RUSTFLAGS}\"" ${cargo_EXECUTABLE} ARGS ${MY_CARGO_ARGS} --target=x86_64-apple-darwin
|
|
COMMAND ${CMAKE_COMMAND} -E env "CARGO_CMD=build" "CARGO_TARGET_DIR=${CMAKE_CURRENT_BINARY_DIR}" "MAINTAINER_MODE=${MAINTAINER_MODE}" "RUSTFLAGS=\"${RUSTFLAGS}\"" ${cargo_EXECUTABLE} ARGS ${MY_CARGO_ARGS} --target=aarch64-apple-darwin
|
|
COMMAND ${CMAKE_COMMAND} -E make_directory "${CMAKE_CURRENT_BINARY_DIR}/${LIB_TARGET}/${LIB_BUILD_TYPE}"
|
|
COMMAND lipo ARGS -create ${CMAKE_CURRENT_BINARY_DIR}/x86_64-apple-darwin/${LIB_BUILD_TYPE}/lib${ARGS_TARGET}.a ${CMAKE_CURRENT_BINARY_DIR}/aarch64-apple-darwin/${LIB_BUILD_TYPE}/lib${ARGS_TARGET}.a -output "${OUTPUT}"
|
|
WORKING_DIRECTORY "${ARGS_WORKING_DIRECTORY}"
|
|
DEPENDS ${LIB_SOURCES}
|
|
COMMENT "Building ${ARGS_TARGET} in ${ARGS_WORKING_DIRECTORY} with: ${cargo_EXECUTABLE} ${MY_CARGO_ARGS_STRING}")
|
|
else()
|
|
add_custom_command(
|
|
OUTPUT "${OUTPUT}"
|
|
COMMAND ${CMAKE_COMMAND} -E env "CARGO_CMD=build" "CARGO_TARGET_DIR=${CMAKE_CURRENT_BINARY_DIR}" "MAINTAINER_MODE=${MAINTAINER_MODE}" "RUSTFLAGS=\"${RUSTFLAGS}\"" ${cargo_EXECUTABLE} ARGS ${MY_CARGO_ARGS}
|
|
WORKING_DIRECTORY "${ARGS_WORKING_DIRECTORY}"
|
|
DEPENDS ${LIB_SOURCES}
|
|
COMMENT "Building ${ARGS_TARGET} in ${ARGS_WORKING_DIRECTORY} with: ${cargo_EXECUTABLE} ${MY_CARGO_ARGS_STRING}")
|
|
endif()
|
|
|
|
# Create a target from the build output
|
|
add_custom_target(${ARGS_TARGET}_target
|
|
DEPENDS ${OUTPUT})
|
|
|
|
# Create a static imported library target from library target
|
|
add_library(${ARGS_TARGET} STATIC IMPORTED GLOBAL)
|
|
add_dependencies(${ARGS_TARGET} ${ARGS_TARGET}_target)
|
|
target_link_libraries(${ARGS_TARGET} INTERFACE ${RUST_NATIVE_STATIC_LIBS})
|
|
|
|
# Specify where the library is and where to find the headers
|
|
set_target_properties(${ARGS_TARGET}
|
|
PROPERTIES
|
|
IMPORTED_LOCATION "${OUTPUT}"
|
|
INTERFACE_INCLUDE_DIRECTORIES "${ARGS_WORKING_DIRECTORY};${CMAKE_CURRENT_BINARY_DIR}"
|
|
)
|
|
|
|
# Vendor the dependencies, if desired
|
|
if(VENDOR_DEPENDENCIES)
|
|
cargo_vendor(TARGET "${ARGS_TARGET}" WORKING_DIRECTORY "${ARGS_WORKING_DIRECTORY}")
|
|
endif()
|
|
endfunction()
|
|
|
|
function(add_rust_test)
|
|
set(options)
|
|
set(oneValueArgs NAME WORKING_DIRECTORY)
|
|
cmake_parse_arguments(ARGS "${options}" "${oneValueArgs}" "${multiValueArgs}" ${ARGN})
|
|
|
|
set(MY_CARGO_ARGS "test")
|
|
if (NOT "${CMAKE_OSX_ARCHITECTURES}" MATCHES "^arm64;x86_64$") # Don't specify the target for universal, we'll do that manually for each build.
|
|
list(APPEND MY_CARGO_ARGS "--target" ${LIB_TARGET})
|
|
endif()
|
|
|
|
if("${CMAKE_BUILD_TYPE}" STREQUAL "Release")
|
|
list(APPEND MY_CARGO_ARGS "--release")
|
|
endif()
|
|
|
|
list(APPEND MY_CARGO_ARGS "--target-dir" ${CMAKE_CURRENT_BINARY_DIR})
|
|
list(JOIN MY_CARGO_ARGS " " MY_CARGO_ARGS_STRING)
|
|
|
|
add_test(
|
|
NAME ${ARGS_NAME}
|
|
COMMAND ${CMAKE_COMMAND} -E env "CARGO_CMD=test" "CARGO_TARGET_DIR=${CMAKE_CURRENT_BINARY_DIR}" ${cargo_EXECUTABLE} ${MY_CARGO_ARGS} -vv --color always
|
|
WORKING_DIRECTORY ${ARGS_WORKING_DIRECTORY}
|
|
)
|
|
endfunction()
|
|
|
|
#
|
|
# Cargo is the primary tool for using the Rust Toolchain to to build static
|
|
# libs that can include other crate dependencies.
|
|
#
|
|
find_rust_program(cargo)
|
|
|
|
# These other programs may also be useful...
|
|
find_rust_program(rustc)
|
|
find_rust_program(rustup)
|
|
find_rust_program(rust-gdb)
|
|
find_rust_program(rust-lldb)
|
|
find_rust_program(rustdoc)
|
|
find_rust_program(rustfmt)
|
|
find_rust_program(bindgen)
|
|
|
|
# Determine the native libs required to link w/ rust static libs
|
|
# message(STATUS "Detecting native static libs for rust: ${rustc_EXECUTABLE} --crate-type staticlib --print=native-static-libs /dev/null")
|
|
execute_process(
|
|
COMMAND ${CMAKE_COMMAND} -E env "CARGO_TARGET_DIR=${CMAKE_CURRENT_BINARY_DIR}" ${rustc_EXECUTABLE} --crate-type staticlib --print=native-static-libs /dev/null
|
|
OUTPUT_VARIABLE RUST_NATIVE_STATIC_LIBS_OUTPUT
|
|
ERROR_VARIABLE RUST_NATIVE_STATIC_LIBS_ERROR
|
|
RESULT_VARIABLE RUST_NATIVE_STATIC_LIBS_RESULT
|
|
)
|
|
string(REGEX REPLACE "\r?\n" ";" LINE_LIST "${RUST_NATIVE_STATIC_LIBS_ERROR}")
|
|
foreach(LINE ${LINE_LIST})
|
|
# do the match on each line
|
|
string(REGEX MATCH "native-static-libs: .*" LINE "${LINE}")
|
|
if(NOT LINE)
|
|
continue()
|
|
endif()
|
|
string(REPLACE "native-static-libs: " "" LINE "${LINE}")
|
|
string(REGEX REPLACE " " "" LINE "${LINE}")
|
|
string(REGEX REPLACE " " ";" LINE "${LINE}")
|
|
if(LINE)
|
|
message(STATUS "Rust's native static libs: ${LINE}")
|
|
set(RUST_NATIVE_STATIC_LIBS "${LINE}")
|
|
break()
|
|
endif()
|
|
endforeach()
|
|
|
|
# Determine default LLVM target triple
|
|
execute_process(COMMAND ${rustc_EXECUTABLE} -vV
|
|
OUTPUT_VARIABLE RUSTC_VV_OUT ERROR_QUIET)
|
|
string(REGEX REPLACE "^.*host: ([a-zA-Z0-9_\\-]+).*" "\\1" DEFAULT_LIB_TARGET1 "${RUSTC_VV_OUT}")
|
|
string(STRIP ${DEFAULT_LIB_TARGET1} DEFAULT_LIB_TARGET)
|
|
|
|
if(WIN32)
|
|
if(CMAKE_SIZEOF_VOID_P EQUAL 8)
|
|
set(LIB_TARGET "x86_64-pc-windows-msvc")
|
|
else()
|
|
set(LIB_TARGET "i686-pc-windows-msvc")
|
|
endif()
|
|
|
|
elseif(ANDROID)
|
|
if(ANDROID_SYSROOT_ABI STREQUAL "x86")
|
|
set(LIB_TARGET "i686-linux-android")
|
|
elseif(ANDROID_SYSROOT_ABI STREQUAL "x86_64")
|
|
set(LIB_TARGET "x86_64-linux-android")
|
|
elseif(ANDROID_SYSROOT_ABI STREQUAL "arm")
|
|
set(LIB_TARGET "arm-linux-androideabi")
|
|
elseif(ANDROID_SYSROOT_ABI STREQUAL "arm64")
|
|
set(LIB_TARGET "aarch64-linux-android")
|
|
endif()
|
|
|
|
elseif(IOS)
|
|
set(LIB_TARGET "universal")
|
|
|
|
# For reference determining target platform:
|
|
# CMake Systems: https://github.com/Kitware/CMake/blob/master/Modules/CMakeDetermineSystem.cmake
|
|
# Rust Targets: https://doc.rust-lang.org/nightly/rustc/platform-support.html
|
|
elseif(CMAKE_SYSTEM_NAME STREQUAL Darwin)
|
|
if ("${CMAKE_OSX_ARCHITECTURES}" MATCHES "^arm64;x86_64$")
|
|
set(LIB_TARGET "universal-apple-darwin")
|
|
|
|
else()
|
|
if(CMAKE_SYSTEM_PROCESSOR STREQUAL arm64)
|
|
set(LIB_TARGET "aarch64-apple-darwin")
|
|
else()
|
|
set(LIB_TARGET "x86_64-apple-darwin")
|
|
endif()
|
|
endif()
|
|
|
|
elseif(CMAKE_SYSTEM_NAME STREQUAL FreeBSD)
|
|
if(CMAKE_SIZEOF_VOID_P EQUAL 8)
|
|
set(LIB_TARGET "x86_64-unknown-freebsd")
|
|
else()
|
|
set(LIB_TARGET "i686-unknown-freebsd")
|
|
endif()
|
|
|
|
elseif(CMAKE_SYSTEM_NAME STREQUAL OpenBSD)
|
|
if(CMAKE_SIZEOF_VOID_P EQUAL 8)
|
|
set(LIB_TARGET "x86_64-unknown-openbsd")
|
|
else()
|
|
set(LIB_TARGET "i686-unknown-openbsd")
|
|
endif()
|
|
|
|
else() # Probably Linux
|
|
if(EXISTS "/lib/libc.musl-x86_64.so.1")
|
|
# Just use the default target, no cross-compiling on libc.musl today :(
|
|
set(LIB_TARGET "${DEFAULT_LIB_TARGET}")
|
|
|
|
else()
|
|
if(CMAKE_SYSTEM_PROCESSOR STREQUAL aarch64)
|
|
set(LIB_TARGET "aarch64-unknown-linux-gnu")
|
|
|
|
elseif(CMAKE_SIZEOF_VOID_P EQUAL 8)
|
|
set(LIB_TARGET "x86_64-unknown-linux-gnu")
|
|
|
|
else()
|
|
set(LIB_TARGET "i686-unknown-linux-gnu")
|
|
endif()
|
|
endif()
|
|
endif()
|
|
|
|
if(IOS)
|
|
set(CARGO_ARGS "lipo")
|
|
else()
|
|
set(CARGO_ARGS "build")
|
|
if (NOT "${CMAKE_OSX_ARCHITECTURES}" MATCHES "^arm64;x86_64$") # Don't specify the target for universal, we'll do that manually for each build.
|
|
list(APPEND CARGO_ARGS "--target" ${LIB_TARGET})
|
|
endif()
|
|
endif()
|
|
|
|
set(RUSTFLAGS "")
|
|
if(NOT CMAKE_BUILD_TYPE)
|
|
set(LIB_BUILD_TYPE "debug")
|
|
elseif(${CMAKE_BUILD_TYPE} STREQUAL "Release" OR ${CMAKE_BUILD_TYPE} STREQUAL "MinSizeRel")
|
|
set(LIB_BUILD_TYPE "release")
|
|
list(APPEND CARGO_ARGS "--release")
|
|
elseif(${CMAKE_BUILD_TYPE} STREQUAL "RelWithDebInfo")
|
|
set(LIB_BUILD_TYPE "release")
|
|
list(APPEND CARGO_ARGS "--release")
|
|
set(RUSTFLAGS "-g")
|
|
else()
|
|
set(LIB_BUILD_TYPE "debug")
|
|
endif()
|
|
|
|
find_package_handle_standard_args( Rust
|
|
REQUIRED_VARS cargo_EXECUTABLE
|
|
VERSION_VAR cargo_VERSION
|
|
)
|