ClamAV is an open source (GPLv2) anti-virus toolkit.
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 
 
 
clamav/cmake/FindRust.cmake

350 lines
13 KiB

# Find the Rust toolchain and add the `add_rust_library()` API to build Rust
# libraries.
#
# Copyright (C) 2021 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 an `add_rust_library()` function which 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`
#
# 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)
#
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_TARGET_DIR=${CMAKE_CURRENT_BINARY_DIR}" ${cargo_EXECUTABLE} ARGS ${MY_CARGO_ARGS} --target=x86_64-apple-darwin
COMMAND ${CMAKE_COMMAND} -E env "CARGO_TARGET_DIR=${CMAKE_CURRENT_BINARY_DIR}" ${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_TARGET_DIR=${CMAKE_CURRENT_BINARY_DIR}" ${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}"
)
# 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_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()
if(NOT CMAKE_BUILD_TYPE)
set(LIB_BUILD_TYPE "debug")
elseif(${CMAKE_BUILD_TYPE} STREQUAL "Release")
set(LIB_BUILD_TYPE "release")
list(APPEND CARGO_ARGS "--release")
else()
set(LIB_BUILD_TYPE "debug")
endif()
find_package_handle_standard_args( Rust
REQUIRED_VARS cargo_EXECUTABLE
VERSION_VAR cargo_VERSION
)