clamav/cmake/FindRust.cmake

486 lines
20 KiB
CMake
Raw Normal View History

2021-03-04 21:14:08 -08:00
# Find the Rust toolchain and add the `add_rust_library()` API to build Rust
# libraries.
#
2023-02-07 19:35:18 -08:00
# Copyright (C) 2021-2023 Cisco Systems, Inc. and/or its affiliates. All rights reserved.
2021-03-04 21:14:08 -08:00
#
# 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
2021-03-04 21:14:08 -08:00
#
# 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
2021-03-04 21:14:08 -08:00
#
# ... for the following Rust toolchain programs:
# - cargo
# - rustc
# - rustup
# - rust-gdb
# - rust-lldb
# - rustdoc
# - rustfmt
# - bindgen
2021-03-04 21:14:08 -08:00
#
# Callers can make any program mandatory by setting `<program>_REQUIRED` before
# the call to `find_package(Rust)`
#
# Eg:
# find_package(Rust REQUIRED)
2021-03-04 21:14:08 -08:00
#
# This module provides the following functions:
# =============================================
2021-03-04 21:14:08 -08:00
#
# `add_rust_library()`
# --------------------
#
# This allows a caller to create a Rust static library
# target which you can link to with `target_link_libraries()`.
2021-03-04 21:14:08 -08:00
#
# 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`
2021-03-04 21:14:08 -08:00
#
# 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.
2021-03-04 21:14:08 -08:00
#
# Example `add_rust_library()` usage:
#
# ```cmake
# add_rust_library(TARGET yourlib
# SOURCE_DIRECTORY "${CMAKE_CURRENT_SOURCE_DIR}")
# BINARY_DIRECTORY "${CMAKE_BINARY_DIR}")
# add_library(YourProject::yourlib ALIAS yourlib)
#
# add_executable(yourexe)
# target_link_libraries(yourexe YourProject::yourlib)
# ```
#
# If your library has unit tests AND your library does NOT depend on your C
# librar(ies), you can use `add_rust_library()` to build your library and unit
# tests at the same time. Just pass `PRECOMPILE_TESTS TRUE` to add_rust_library.
# This should make it so when you run the tests, they don't have to compile
# during the test run.
#
# If your library does have C dependencies, you can still precompile the tests
# by passing `PRECOMPILE_TESTS TRUE`, with `add_rust_test()` instead.
# It will be slower because it will have to compile the C stuff first,
# then compile the Rust stuff from scratch. See below.
#
# `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_test()` usage:
#
# ```cmake
# add_rust_test(NAME yourlib
# SOURCE_DIRECTORY "${CMAKE_SOURCE_DIR}/path/to/yourlib"
# BINARY_DIRECTORY "${CMAKE_BINARY_DIR}"
# )
# set_property(TEST yourlib PROPERTY ENVIRONMENT ${ENVIRONMENT})
# ```
2021-03-04 21:14:08 -08:00
#
# Experimental: Precompile the Tests Executable
# ~~~~~~~~~~~~
# This feature will cause install failures if you `sudo make install` because
# it will recompile the test executable with sudo and Cargo is likely to fail to
# run with sudo.
# This cannot be fixed unless we can predetermine the test exeecutable OUTPUT
# filepath. See: https://github.com/rust-lang/cargo/issues/1924
#
# If your library has unit tests AND your library DOES depend on your C
# libraries, you can precompile the unit tests application with some extra
# parameters to `add_rust_test()`:
# - `PRECOMPILE_TESTS TRUE`
# - `PRECOMPILE_DEPENDS <the CMake target name for your C library dependency>`
# - `PRECOMPILE_ENVIRONMENT <a linked list of environment vars to build the Rust lib>`
#
# The `PRECOMPILE_DEPENDS` option is required so CMake will build the C library first.
# The `PRECOMPILE_ENVIRONMENT` option is required for use in your `build.rs` file so you
# can tell rustc how to link to your C library.
#
# For example:
#
# ```cmake
# add_rust_test(NAME yourlib
# SOURCE_DIRECTORY "${CMAKE_SOURCE_DIR}/yourlib"
# BINARY_DIRECTORY "${CMAKE_BINARY_DIR}"
# PRECOMPILE_TESTS TRUE
# PRECOMPILE_DEPENDS ClamAV::libclamav
# PRECOMPILE_ENVIRONMENT "${ENVIRONMENT}"
# )
# set_property(TEST yourlib PROPERTY ENVIRONMENT ${ENVIRONMENT})
# ```
#
# `add_rust_executable()`
# -----------------------
#
# This allows a caller to create a Rust executable target.
#
# Example `add_rust_executable()` usage:
#
# ```cmake
# add_rust_executable(TARGET yourexe
# SOURCE_DIRECTORY "${CMAKE_CURRENT_SOURCE_DIR}"
# BINARY_DIRECTORY "${CMAKE_BINARY_DIR}"
# )
# add_executable(YourProject::yourexe ALIAS yourexe)
# ```
2021-03-04 21:14:08 -08:00
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
2021-03-04 21:14:08 -08:00
RESULT_VARIABLE ${RUST_PROGRAM}_VERSION_RESULT
)
2021-03-04 21:14:08 -08:00
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 SOURCE_DIRECTORY BINARY_DIRECTORY)
cmake_parse_arguments(ARGS "${options}" "${oneValueArgs}" "${multiValueArgs}" ${ARGN})
if(NOT EXISTS ${ARGS_SOURCE_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_SOURCE_DIRECTORY}/.cargo)
execute_process(
COMMAND ${CMAKE_COMMAND} -E env "CARGO_TARGET_DIR=${ARGS_BINARY_DIRECTORY}" ${cargo_EXECUTABLE} vendor ".cargo/vendor"
WORKING_DIRECTORY "${ARGS_SOURCE_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_SOURCE_DIRECTORY}/.cargo/config.toml "
[source.crates-io]
replace-with = \"vendored-sources\"
[source.vendored-sources]
directory = \".cargo/vendor\"
"
)
endif()
endfunction()
function(add_rust_executable)
set(options)
set(oneValueArgs TARGET SOURCE_DIRECTORY BINARY_DIRECTORY)
cmake_parse_arguments(ARGS "${options}" "${oneValueArgs}" "${multiValueArgs}" ${ARGN})
if(WIN32)
set(OUTPUT "${ARGS_BINARY_DIRECTORY}/${RUST_COMPILER_TARGET}/${CARGO_BUILD_TYPE}/${ARGS_TARGET}.exe")
else()
set(OUTPUT "${ARGS_BINARY_DIRECTORY}/${RUST_COMPILER_TARGET}/${CARGO_BUILD_TYPE}/${ARGS_TARGET}")
endif()
file(GLOB_RECURSE EXE_SOURCES "${ARGS_SOURCE_DIRECTORY}/*.rs")
set(MY_CARGO_ARGS ${CARGO_ARGS})
list(APPEND MY_CARGO_ARGS "--target-dir" ${ARGS_BINARY_DIRECTORY})
list(JOIN MY_CARGO_ARGS " " MY_CARGO_ARGS_STRING)
# Build the executable.
add_custom_command(
OUTPUT "${OUTPUT}"
COMMAND ${CMAKE_COMMAND} -E env "CARGO_TARGET_DIR=${ARGS_BINARY_DIRECTORY}" ${cargo_EXECUTABLE} ${MY_CARGO_ARGS}
WORKING_DIRECTORY "${ARGS_SOURCE_DIRECTORY}"
DEPENDS ${EXE_SOURCES}
COMMENT "Building ${ARGS_TARGET} in ${ARGS_BINARY_DIRECTORY} with:\n\t ${cargo_EXECUTABLE} ${MY_CARGO_ARGS_STRING}")
# Create a target from the build output
add_custom_target(${ARGS_TARGET}_target
DEPENDS ${OUTPUT})
# Create an executable target from custom target
add_custom_target(${ARGS_TARGET} ALL DEPENDS ${ARGS_TARGET}_target)
# Specify where the executable is
set_target_properties(${ARGS_TARGET}
PROPERTIES
IMPORTED_LOCATION "${OUTPUT}"
)
# Vendor the dependencies, if desired
if(VENDOR_DEPENDENCIES)
cargo_vendor(TARGET "${ARGS_TARGET}"
SOURCE_DIRECTORY "${ARGS_SOURCE_DIRECTORY}"
BINARY_DIRECTORY "${ARGS_BINARY_DIRECTORY}"
)
endif()
endfunction()
2021-03-04 21:14:08 -08:00
function(add_rust_library)
set(options)
set(oneValueArgs TARGET SOURCE_DIRECTORY BINARY_DIRECTORY PRECOMPILE_TESTS)
2021-03-04 21:14:08 -08:00
cmake_parse_arguments(ARGS "${options}" "${oneValueArgs}" "${multiValueArgs}" ${ARGN})
if(WIN32)
set(OUTPUT "${ARGS_BINARY_DIRECTORY}/${RUST_COMPILER_TARGET}/${CARGO_BUILD_TYPE}/${ARGS_TARGET}.lib")
2021-03-04 21:14:08 -08:00
else()
set(OUTPUT "${ARGS_BINARY_DIRECTORY}/${RUST_COMPILER_TARGET}/${CARGO_BUILD_TYPE}/lib${ARGS_TARGET}.a")
2021-03-04 21:14:08 -08:00
endif()
file(GLOB_RECURSE LIB_SOURCES "${ARGS_SOURCE_DIRECTORY}/*.rs")
2021-03-04 21:14:08 -08:00
set(MY_CARGO_ARGS ${CARGO_ARGS})
if(ARGS_PRECOMPILE_TESTS)
list(APPEND MY_CARGO_ARGS "--tests")
endif()
list(APPEND MY_CARGO_ARGS "--target-dir" ${ARGS_BINARY_DIRECTORY})
2021-03-04 21:14:08 -08:00
list(JOIN MY_CARGO_ARGS " " MY_CARGO_ARGS_STRING)
2021-11-29 17:24:39 -08:00
# Build the library and generate the c-binding
if("${CMAKE_OSX_ARCHITECTURES}" MATCHES "^(arm64;x86_64|x86_64;arm64)$")
2021-11-29 17:24:39 -08:00
add_custom_command(
OUTPUT "${OUTPUT}"
COMMAND ${CMAKE_COMMAND} -E env "CARGO_CMD=build" "CARGO_TARGET_DIR=${ARGS_BINARY_DIRECTORY}" "MAINTAINER_MODE=${MAINTAINER_MODE}" "RUSTFLAGS=${RUSTFLAGS}" ${cargo_EXECUTABLE} ${MY_CARGO_ARGS} --target=x86_64-apple-darwin
COMMAND ${CMAKE_COMMAND} -E env "CARGO_CMD=build" "CARGO_TARGET_DIR=${ARGS_BINARY_DIRECTORY}" "MAINTAINER_MODE=${MAINTAINER_MODE}" "RUSTFLAGS=${RUSTFLAGS}" ${cargo_EXECUTABLE} ${MY_CARGO_ARGS} --target=aarch64-apple-darwin
COMMAND ${CMAKE_COMMAND} -E make_directory "${ARGS_BINARY_DIRECTORY}/${RUST_COMPILER_TARGET}/${CARGO_BUILD_TYPE}"
COMMAND lipo -create ${ARGS_BINARY_DIRECTORY}/x86_64-apple-darwin/${CARGO_BUILD_TYPE}/lib${ARGS_TARGET}.a ${ARGS_BINARY_DIRECTORY}/aarch64-apple-darwin/${CARGO_BUILD_TYPE}/lib${ARGS_TARGET}.a -output "${OUTPUT}"
WORKING_DIRECTORY "${ARGS_SOURCE_DIRECTORY}"
2021-11-29 17:24:39 -08:00
DEPENDS ${LIB_SOURCES}
COMMENT "Building ${ARGS_TARGET} in ${ARGS_BINARY_DIRECTORY} with: ${cargo_EXECUTABLE} ${MY_CARGO_ARGS_STRING}")
elseif("${CMAKE_OSX_ARCHITECTURES}" MATCHES "^(arm64)$")
add_custom_command(
OUTPUT "${OUTPUT}"
COMMAND ${CMAKE_COMMAND} -E env "CARGO_CMD=build" "CARGO_TARGET_DIR=${ARGS_BINARY_DIRECTORY}" "MAINTAINER_MODE=${MAINTAINER_MODE}" "RUSTFLAGS=${RUSTFLAGS}" ${cargo_EXECUTABLE} ${MY_CARGO_ARGS} --target=aarch64-apple-darwin
WORKING_DIRECTORY "${ARGS_SOURCE_DIRECTORY}"
DEPENDS ${LIB_SOURCES}
COMMENT "Building ${ARGS_TARGET} in ${ARGS_BINARY_DIRECTORY} with: ${cargo_EXECUTABLE} ${MY_CARGO_ARGS_STRING}")
elseif("${CMAKE_OSX_ARCHITECTURES}" MATCHES "^(x86_64)$")
add_custom_command(
OUTPUT "${OUTPUT}"
COMMAND ${CMAKE_COMMAND} -E env "CARGO_CMD=build" "CARGO_TARGET_DIR=${ARGS_BINARY_DIRECTORY}" "MAINTAINER_MODE=${MAINTAINER_MODE}" "RUSTFLAGS=${RUSTFLAGS}" ${cargo_EXECUTABLE} ${MY_CARGO_ARGS} --target=x86_64-apple-darwin
COMMAND ${CMAKE_COMMAND} -E make_directory "${ARGS_BINARY_DIRECTORY}/${RUST_COMPILER_TARGET}/${CARGO_BUILD_TYPE}"
WORKING_DIRECTORY "${ARGS_SOURCE_DIRECTORY}"
DEPENDS ${LIB_SOURCES}
COMMENT "Building ${ARGS_TARGET} in ${ARGS_BINARY_DIRECTORY} with: ${cargo_EXECUTABLE} ${MY_CARGO_ARGS_STRING}")
2021-03-04 21:14:08 -08:00
else()
2021-11-29 17:24:39 -08:00
add_custom_command(
OUTPUT "${OUTPUT}"
COMMAND ${CMAKE_COMMAND} -E env "CARGO_CMD=build" "CARGO_TARGET_DIR=${ARGS_BINARY_DIRECTORY}" "MAINTAINER_MODE=${MAINTAINER_MODE}" "RUSTFLAGS=${RUSTFLAGS}" ${cargo_EXECUTABLE} ${MY_CARGO_ARGS}
WORKING_DIRECTORY "${ARGS_SOURCE_DIRECTORY}"
2021-11-29 17:24:39 -08:00
DEPENDS ${LIB_SOURCES}
COMMENT "Building ${ARGS_TARGET} in ${ARGS_BINARY_DIRECTORY} with: ${cargo_EXECUTABLE} ${MY_CARGO_ARGS_STRING}")
2021-03-04 21:14:08 -08:00
endif()
# Create a target from the build output
add_custom_target(${ARGS_TARGET}_target
DEPENDS ${OUTPUT})
# Create a static imported library target from custom target
2021-03-04 21:14:08 -08:00
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_SOURCE_DIRECTORY};${ARGS_BINARY_DIRECTORY}"
2021-03-04 21:14:08 -08:00
)
# Vendor the dependencies, if desired
if(VENDOR_DEPENDENCIES)
cargo_vendor(TARGET "${ARGS_TARGET}"
SOURCE_DIRECTORY "${ARGS_SOURCE_DIRECTORY}"
BINARY_DIRECTORY "${ARGS_BINARY_DIRECTORY}")
endif()
2021-03-04 21:14:08 -08:00
endfunction()
function(add_rust_test)
set(options)
set(oneValueArgs NAME SOURCE_DIRECTORY BINARY_DIRECTORY PRECOMPILE_TESTS PRECOMPILE_DEPENDS)
set(multiValueArgs PRECOMPILE_ENVIRONMENT)
2021-03-04 21:14:08 -08:00
cmake_parse_arguments(ARGS "${options}" "${oneValueArgs}" "${multiValueArgs}" ${ARGN})
Add Rust logging module unit test & integrate with CMake / CTest Add a basic unit test for the new libclamav_rust `logging.rs` module. This test simply initializes logging and then prints out a message with each of the `log` macros. Also set the Rust edition to 2018 because the default is the 2015 edition in which using external crates is very clunky. For the Rust test support in CMake this commit adds support for cross-compiling the Rust tests. Rust tests must be built for the same LLVM triple (target platform) as the rest of the project. In particular this is needed to build both x64 and x86 packages on a 64bit Windows host. For Alpine, we observed that the LLVM triple for the host platform tools may be either: - x86_64-unknown-linux-musl, or - x86_64-alpine-linux-musl To support it either way, we look up the host triple with `rustc -vV` and use that if the musl libc exists. This is a big hacky and unfortunately means that we probably can't cross-compile to other platforms when running on a musl libc host. There are probably improvements to be made to improve cross compiling support. The Rust test programs must link with libclamav, libclammspack, and possibly libclamunrar_iface and libclamunrar plus all of the library dependencies for those libraries. To do this, we pass the path of each library in environment variables when building the libclamav_rust unit test program. Within `libclamav_rust/build.rs`, we read those environment variables. If set, we parse each into library path and name components to use as directives for how to build the unit test program. See: https://doc.rust-lang.org/cargo/reference/build-scripts.html Our `build.rs` file ignores the library path environment variables if thye're not set, which is necessary when building the libclamav_rust library and when libclamunrar isn't static and for when not linking with a libiconv external to libc. Rust test programs are built and executed in subdirectory under: <target>/<llvm triple>/<config>/deps where "target" for libclamav_rust tests is set to <build>/unit_tests For example: clamav/build/unit_tests/x86_64-pc-windows-msvc/debug/deps/clamav_rust-7e1343f8a2bff1cc.exe Since this program isn't co-located with the rest of the libraries we also have to set environment variables so the test program can find and load the shared libraries: - Windows: PATH - macOS: DYLD_LIBRARY_PATH We already set LD_LIBRARY_PATH when not Windows for similar reasons. Note: In build.rs, we iterate references to LIB_ENV_LINK & Co because older Rust versions do implement Iterator for [&str].
2021-11-18 10:24:00 -08:00
set(MY_CARGO_ARGS "test")
if(NOT "${CMAKE_OSX_ARCHITECTURES}" MATCHES "^(arm64;x86_64|x86_64;arm64)$") # Don't specify the target for universal, we'll do that manually for each build.
list(APPEND MY_CARGO_ARGS "--target" ${RUST_COMPILER_TARGET})
Add Rust logging module unit test & integrate with CMake / CTest Add a basic unit test for the new libclamav_rust `logging.rs` module. This test simply initializes logging and then prints out a message with each of the `log` macros. Also set the Rust edition to 2018 because the default is the 2015 edition in which using external crates is very clunky. For the Rust test support in CMake this commit adds support for cross-compiling the Rust tests. Rust tests must be built for the same LLVM triple (target platform) as the rest of the project. In particular this is needed to build both x64 and x86 packages on a 64bit Windows host. For Alpine, we observed that the LLVM triple for the host platform tools may be either: - x86_64-unknown-linux-musl, or - x86_64-alpine-linux-musl To support it either way, we look up the host triple with `rustc -vV` and use that if the musl libc exists. This is a big hacky and unfortunately means that we probably can't cross-compile to other platforms when running on a musl libc host. There are probably improvements to be made to improve cross compiling support. The Rust test programs must link with libclamav, libclammspack, and possibly libclamunrar_iface and libclamunrar plus all of the library dependencies for those libraries. To do this, we pass the path of each library in environment variables when building the libclamav_rust unit test program. Within `libclamav_rust/build.rs`, we read those environment variables. If set, we parse each into library path and name components to use as directives for how to build the unit test program. See: https://doc.rust-lang.org/cargo/reference/build-scripts.html Our `build.rs` file ignores the library path environment variables if thye're not set, which is necessary when building the libclamav_rust library and when libclamunrar isn't static and for when not linking with a libiconv external to libc. Rust test programs are built and executed in subdirectory under: <target>/<llvm triple>/<config>/deps where "target" for libclamav_rust tests is set to <build>/unit_tests For example: clamav/build/unit_tests/x86_64-pc-windows-msvc/debug/deps/clamav_rust-7e1343f8a2bff1cc.exe Since this program isn't co-located with the rest of the libraries we also have to set environment variables so the test program can find and load the shared libraries: - Windows: PATH - macOS: DYLD_LIBRARY_PATH We already set LD_LIBRARY_PATH when not Windows for similar reasons. Note: In build.rs, we iterate references to LIB_ENV_LINK & Co because older Rust versions do implement Iterator for [&str].
2021-11-18 10:24:00 -08:00
endif()
if("${CMAKE_BUILD_TYPE}" STREQUAL "Release" OR "${CMAKE_BUILD_TYPE}" STREQUAL "RelWithDebInfo")
Add Rust logging module unit test & integrate with CMake / CTest Add a basic unit test for the new libclamav_rust `logging.rs` module. This test simply initializes logging and then prints out a message with each of the `log` macros. Also set the Rust edition to 2018 because the default is the 2015 edition in which using external crates is very clunky. For the Rust test support in CMake this commit adds support for cross-compiling the Rust tests. Rust tests must be built for the same LLVM triple (target platform) as the rest of the project. In particular this is needed to build both x64 and x86 packages on a 64bit Windows host. For Alpine, we observed that the LLVM triple for the host platform tools may be either: - x86_64-unknown-linux-musl, or - x86_64-alpine-linux-musl To support it either way, we look up the host triple with `rustc -vV` and use that if the musl libc exists. This is a big hacky and unfortunately means that we probably can't cross-compile to other platforms when running on a musl libc host. There are probably improvements to be made to improve cross compiling support. The Rust test programs must link with libclamav, libclammspack, and possibly libclamunrar_iface and libclamunrar plus all of the library dependencies for those libraries. To do this, we pass the path of each library in environment variables when building the libclamav_rust unit test program. Within `libclamav_rust/build.rs`, we read those environment variables. If set, we parse each into library path and name components to use as directives for how to build the unit test program. See: https://doc.rust-lang.org/cargo/reference/build-scripts.html Our `build.rs` file ignores the library path environment variables if thye're not set, which is necessary when building the libclamav_rust library and when libclamunrar isn't static and for when not linking with a libiconv external to libc. Rust test programs are built and executed in subdirectory under: <target>/<llvm triple>/<config>/deps where "target" for libclamav_rust tests is set to <build>/unit_tests For example: clamav/build/unit_tests/x86_64-pc-windows-msvc/debug/deps/clamav_rust-7e1343f8a2bff1cc.exe Since this program isn't co-located with the rest of the libraries we also have to set environment variables so the test program can find and load the shared libraries: - Windows: PATH - macOS: DYLD_LIBRARY_PATH We already set LD_LIBRARY_PATH when not Windows for similar reasons. Note: In build.rs, we iterate references to LIB_ENV_LINK & Co because older Rust versions do implement Iterator for [&str].
2021-11-18 10:24:00 -08:00
list(APPEND MY_CARGO_ARGS "--release")
endif()
list(APPEND MY_CARGO_ARGS "--target-dir" ${ARGS_BINARY_DIRECTORY})
Add Rust logging module unit test & integrate with CMake / CTest Add a basic unit test for the new libclamav_rust `logging.rs` module. This test simply initializes logging and then prints out a message with each of the `log` macros. Also set the Rust edition to 2018 because the default is the 2015 edition in which using external crates is very clunky. For the Rust test support in CMake this commit adds support for cross-compiling the Rust tests. Rust tests must be built for the same LLVM triple (target platform) as the rest of the project. In particular this is needed to build both x64 and x86 packages on a 64bit Windows host. For Alpine, we observed that the LLVM triple for the host platform tools may be either: - x86_64-unknown-linux-musl, or - x86_64-alpine-linux-musl To support it either way, we look up the host triple with `rustc -vV` and use that if the musl libc exists. This is a big hacky and unfortunately means that we probably can't cross-compile to other platforms when running on a musl libc host. There are probably improvements to be made to improve cross compiling support. The Rust test programs must link with libclamav, libclammspack, and possibly libclamunrar_iface and libclamunrar plus all of the library dependencies for those libraries. To do this, we pass the path of each library in environment variables when building the libclamav_rust unit test program. Within `libclamav_rust/build.rs`, we read those environment variables. If set, we parse each into library path and name components to use as directives for how to build the unit test program. See: https://doc.rust-lang.org/cargo/reference/build-scripts.html Our `build.rs` file ignores the library path environment variables if thye're not set, which is necessary when building the libclamav_rust library and when libclamunrar isn't static and for when not linking with a libiconv external to libc. Rust test programs are built and executed in subdirectory under: <target>/<llvm triple>/<config>/deps where "target" for libclamav_rust tests is set to <build>/unit_tests For example: clamav/build/unit_tests/x86_64-pc-windows-msvc/debug/deps/clamav_rust-7e1343f8a2bff1cc.exe Since this program isn't co-located with the rest of the libraries we also have to set environment variables so the test program can find and load the shared libraries: - Windows: PATH - macOS: DYLD_LIBRARY_PATH We already set LD_LIBRARY_PATH when not Windows for similar reasons. Note: In build.rs, we iterate references to LIB_ENV_LINK & Co because older Rust versions do implement Iterator for [&str].
2021-11-18 10:24:00 -08:00
list(JOIN MY_CARGO_ARGS " " MY_CARGO_ARGS_STRING)
if(ARGS_PRECOMPILE_TESTS)
list(APPEND ARGS_PRECOMPILE_ENVIRONMENT "CARGO_CMD=test" "CARGO_TARGET_DIR=${ARGS_BINARY_DIRECTORY}")
add_custom_target(${ARGS_NAME}_tests ALL
COMMAND ${CMAKE_COMMAND} -E env ${ARGS_PRECOMPILE_ENVIRONMENT} ${cargo_EXECUTABLE} ${MY_CARGO_ARGS} --color always --no-run
DEPENDS ${ARGS_PRECOMPILE_DEPENDS}
WORKING_DIRECTORY ${ARGS_SOURCE_DIRECTORY}
)
endif()
2021-03-04 21:14:08 -08:00
add_test(
Add Rust logging module unit test & integrate with CMake / CTest Add a basic unit test for the new libclamav_rust `logging.rs` module. This test simply initializes logging and then prints out a message with each of the `log` macros. Also set the Rust edition to 2018 because the default is the 2015 edition in which using external crates is very clunky. For the Rust test support in CMake this commit adds support for cross-compiling the Rust tests. Rust tests must be built for the same LLVM triple (target platform) as the rest of the project. In particular this is needed to build both x64 and x86 packages on a 64bit Windows host. For Alpine, we observed that the LLVM triple for the host platform tools may be either: - x86_64-unknown-linux-musl, or - x86_64-alpine-linux-musl To support it either way, we look up the host triple with `rustc -vV` and use that if the musl libc exists. This is a big hacky and unfortunately means that we probably can't cross-compile to other platforms when running on a musl libc host. There are probably improvements to be made to improve cross compiling support. The Rust test programs must link with libclamav, libclammspack, and possibly libclamunrar_iface and libclamunrar plus all of the library dependencies for those libraries. To do this, we pass the path of each library in environment variables when building the libclamav_rust unit test program. Within `libclamav_rust/build.rs`, we read those environment variables. If set, we parse each into library path and name components to use as directives for how to build the unit test program. See: https://doc.rust-lang.org/cargo/reference/build-scripts.html Our `build.rs` file ignores the library path environment variables if thye're not set, which is necessary when building the libclamav_rust library and when libclamunrar isn't static and for when not linking with a libiconv external to libc. Rust test programs are built and executed in subdirectory under: <target>/<llvm triple>/<config>/deps where "target" for libclamav_rust tests is set to <build>/unit_tests For example: clamav/build/unit_tests/x86_64-pc-windows-msvc/debug/deps/clamav_rust-7e1343f8a2bff1cc.exe Since this program isn't co-located with the rest of the libraries we also have to set environment variables so the test program can find and load the shared libraries: - Windows: PATH - macOS: DYLD_LIBRARY_PATH We already set LD_LIBRARY_PATH when not Windows for similar reasons. Note: In build.rs, we iterate references to LIB_ENV_LINK & Co because older Rust versions do implement Iterator for [&str].
2021-11-18 10:24:00 -08:00
NAME ${ARGS_NAME}
COMMAND ${CMAKE_COMMAND} -E env "CARGO_CMD=test" "CARGO_TARGET_DIR=${ARGS_BINARY_DIRECTORY}" "RUSTFLAGS=${RUSTFLAGS}" ${cargo_EXECUTABLE} ${MY_CARGO_ARGS} --color always
WORKING_DIRECTORY ${ARGS_SOURCE_DIRECTORY}
2021-03-04 21:14:08 -08:00
)
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)
if(RUSTC_MINIMUM_REQUIRED AND rustc_VERSION VERSION_LESS RUSTC_MINIMUM_REQUIRED)
message(FATAL_ERROR "Your Rust toolchain is to old to build this project:
${rustc_VERSION} < ${RUSTC_MINIMUM_REQUIRED}")
endif()
if(WIN32)
file(TOUCH ${CMAKE_BINARY_DIR}/empty-file)
set(EMPTY_FILE "${CMAKE_BINARY_DIR}/empty-file")
else()
set(EMPTY_FILE "/dev/null")
endif()
2021-03-04 21:14:08 -08:00
# 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 ${EMPTY_FILE}")
2021-03-04 21:14:08 -08:00
execute_process(
COMMAND ${CMAKE_COMMAND} -E env "CARGO_TARGET_DIR=${CMAKE_BINARY_DIR}" ${rustc_EXECUTABLE} --crate-type staticlib --print=native-static-libs ${EMPTY_FILE}
2021-03-04 21:14:08 -08:00
OUTPUT_VARIABLE RUST_NATIVE_STATIC_LIBS_OUTPUT
ERROR_VARIABLE RUST_NATIVE_STATIC_LIBS_ERROR
2021-03-04 21:14:08 -08:00
RESULT_VARIABLE RUST_NATIVE_STATIC_LIBS_RESULT
)
string(REGEX REPLACE "\r?\n" ";" LINE_LIST "${RUST_NATIVE_STATIC_LIBS_ERROR}")
2021-03-04 21:14:08 -08:00
foreach(LINE ${LINE_LIST})
# do the match on each line
string(REGEX MATCH "native-static-libs: .*" LINE "${LINE}")
2021-03-04 21:14:08 -08:00
if(NOT LINE)
continue()
endif()
2021-03-04 21:14:08 -08:00
string(REPLACE "native-static-libs: " "" LINE "${LINE}")
string(REGEX REPLACE " " "" LINE "${LINE}")
string(REGEX REPLACE " " ";" LINE "${LINE}")
2021-03-04 21:14:08 -08:00
if(LINE)
message(STATUS "Rust's native static libs: ${LINE}")
set(RUST_NATIVE_STATIC_LIBS "${LINE}")
break()
endif()
endforeach()
if(NOT RUST_COMPILER_TARGET)
# Automatically determine the Rust Target Triple.
# Note: Users may override automatic target detection by specifying their own. Most likely needed for cross-compiling.
# For reference determining target platform: https://doc.rust-lang.org/nightly/rustc/platform-support.html
if(WIN32)
# For windows x86/x64, it's easy enough to guess the target.
if(CMAKE_SIZEOF_VOID_P EQUAL 8)
set(RUST_COMPILER_TARGET "x86_64-pc-windows-msvc")
else()
set(RUST_COMPILER_TARGET "i686-pc-windows-msvc")
endif()
elseif(CMAKE_SYSTEM_NAME STREQUAL Darwin AND "${CMAKE_OSX_ARCHITECTURES}" MATCHES "^(arm64;x86_64|x86_64;arm64)$")
# Special case for Darwin because we may want to build universal binaries.
set(RUST_COMPILER_TARGET "universal-apple-darwin")
else()
# 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_RUST_COMPILER_TARGET1 "${RUSTC_VV_OUT}")
string(STRIP ${DEFAULT_RUST_COMPILER_TARGET1} DEFAULT_RUST_COMPILER_TARGET)
set(RUST_COMPILER_TARGET "${DEFAULT_RUST_COMPILER_TARGET}")
2021-03-04 21:14:08 -08:00
endif()
endif()
set(CARGO_ARGS "build")
if(NOT "${RUST_COMPILER_TARGET}" MATCHES "^universal-apple-darwin$")
# Don't specify the target for macOS universal builds, we'll do that manually for each build.
list(APPEND CARGO_ARGS "--target" ${RUST_COMPILER_TARGET})
2021-03-04 21:14:08 -08:00
endif()
if(NOT CMAKE_BUILD_TYPE)
set(CARGO_BUILD_TYPE "debug")
elseif(${CMAKE_BUILD_TYPE} STREQUAL "Release" OR ${CMAKE_BUILD_TYPE} STREQUAL "MinSizeRel")
set(CARGO_BUILD_TYPE "release")
2021-03-04 21:14:08 -08:00
list(APPEND CARGO_ARGS "--release")
elseif(${CMAKE_BUILD_TYPE} STREQUAL "RelWithDebInfo")
set(CARGO_BUILD_TYPE "release")
list(APPEND CARGO_ARGS "--release")
string(APPEND RUSTFLAGS " -g")
2021-03-04 21:14:08 -08:00
else()
set(CARGO_BUILD_TYPE "debug")
2021-03-04 21:14:08 -08:00
endif()
string(STRIP "${RUSTFLAGS}" RUSTFLAGS)
2021-03-04 21:14:08 -08:00
find_package_handle_standard_args(Rust
2021-03-04 21:14:08 -08:00
REQUIRED_VARS cargo_EXECUTABLE
VERSION_VAR cargo_VERSION
)