From 8917d8c534f5fdc13bfe4961a4ff8e511b2e8160 Mon Sep 17 00:00:00 2001 From: Atheria Date: Thu, 9 Oct 2025 19:41:04 +0700 Subject: [PATCH 1/4] Removal of certain functions --- cmake/variable.cmake | 2 +- fs/FAT/FAT1x.c | 4 ++-- include/Io.h | 28 ---------------------------- kernel/etc/Shell.c | 1 - 4 files changed, 3 insertions(+), 32 deletions(-) diff --git a/cmake/variable.cmake b/cmake/variable.cmake index e477be7..01d2c3c 100644 --- a/cmake/variable.cmake +++ b/cmake/variable.cmake @@ -13,5 +13,5 @@ add_compile_definitions( KERNEL_SPACE_END=0xFFFFFFFFFFFFFFFFULL PREFETCH_DISTANCE=256 NT_STORE_THRESHOLD=4*1024*1024 - MAX_SUPPORTED_MEMORY=128*1024*1024*1024 + MAX_SUPPORTED_MEMORY=128ULL*1024*1024*1024 ) \ No newline at end of file diff --git a/fs/FAT/FAT1x.c b/fs/FAT/FAT1x.c index 8779644..91264be 100644 --- a/fs/FAT/FAT1x.c +++ b/fs/FAT/FAT1x.c @@ -14,13 +14,13 @@ static uint8_t* sector_buffer = NULL; int Fat1xDetect(BlockDevice* device) { uint8_t boot_sector[512]; if (BlockDeviceRead(device->id, 0, 1, boot_sector) != 0) { - PrintKernelError("Failed to read boot sector\n"); + PrintKernel("Failed to read boot sector\n"); return 0; } Fat1xBootSector* bs = (Fat1xBootSector*)boot_sector; if (bs->jump[0] != 0xEB && bs->jump[0] != 0xE9) { - PrintKernelError("Invalid jump instruction\n"); + PrintKernel("Invalid jump instruction\n"); return 0; } diff --git a/include/Io.h b/include/Io.h index 2a569f3..19dfa8a 100644 --- a/include/Io.h +++ b/include/Io.h @@ -23,16 +23,6 @@ static inline uint16_t inw(uint16_t port) { return ret; } -static inline void outd(uint16_t port, uint32_t val) { - __asm__ volatile ("outl %0, %1" : : "a"(val), "Nd"(port)); -} - -static inline uint32_t ind(uint16_t port) { - uint32_t ret; - __asm__ volatile ("inl %1, %0" : "=a"(ret) : "Nd"(port)); - return ret; -} - static inline void outl(uint16_t port, uint32_t val) { __asm__ volatile ("outl %0, %1" : : "a"(val), "Nd"(port)); } @@ -43,16 +33,6 @@ static inline uint32_t inl(uint16_t port) { return ret; } -static inline void outq(uint16_t port, uint64_t val) { - __asm__ volatile ("outq %0, %1" : : "a"(val), "Nd"(port)); -} - -static inline uint64_t inq(uint16_t port) { - uint64_t ret; - __asm__ volatile ("inq %1, %0" : "=a"(ret) : "Nd"(port)); - return ret; -} - static inline void outsb(uint16_t port, void* buf, size_t len) { __asm__ volatile ("cld; rep outsb" : "+D"(buf), "+c"(len) : "d"(port)); } @@ -61,14 +41,6 @@ static inline void insb(uint16_t port, void* buf, size_t len) { __asm__ volatile ("cld; rep insb" : "+D"(buf), "+c"(len) : "d"(port)); } -static inline void outsd(uint16_t port, void* buf, size_t len) { - __asm__ volatile ("cld; rep outsd" : "+D"(buf), "+c"(len) : "d"(port)); -} - -static inline void insd(uint16_t port, void* buf, size_t len) { - __asm__ volatile ("cld; rep insd" : "+D"(buf), "+c"(len) : "d"(port)); -} - static inline void outsl(uint16_t port, void* buf, size_t len) { __asm__ volatile ("cld; rep outsl" : "+D"(buf), "+c"(len) : "d"(port)); } diff --git a/kernel/etc/Shell.c b/kernel/etc/Shell.c index ac4a8b5..a4204ff 100644 --- a/kernel/etc/Shell.c +++ b/kernel/etc/Shell.c @@ -596,7 +596,6 @@ static void CatHandler(const char * args) { if (bytes > 0) { PrintKernel((char*)file_buffer); } - PrintKernel("\n"); } else { PrintKernel("cat: file not found or read error\n"); } From 92b0d2de65625603940c1894b3d2808946781940 Mon Sep 17 00:00:00 2001 From: Atheria Date: Sat, 11 Oct 2025 13:08:26 +0700 Subject: [PATCH 2/4] Corrosion support --- CMakeLists.txt | 52 +++++++++++++++++++++---------- cmake/rust_utils.cmake | 9 ++++++ kernel/atomic/rust/CMakeLists.txt | 37 ---------------------- kernel/ipc/Ipc.c | 3 ++ mm/rust/Cargo.lock | 4 +-- mm/rust/Cargo.toml | 2 +- 6 files changed, 51 insertions(+), 56 deletions(-) create mode 100644 cmake/rust_utils.cmake delete mode 100644 kernel/atomic/rust/CMakeLists.txt diff --git a/CMakeLists.txt b/CMakeLists.txt index e4576c2..895d4cc 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -2,7 +2,12 @@ # VoidFrame CMake Build Script - v0.0.2-development3 # ============================================================================ cmake_minimum_required(VERSION 3.30) -project(VoidFrame VERSION 0.0.2 LANGUAGES C CXX ASM_NASM) +project(VoidFrame + VERSION 0.0.2 + LANGUAGES C CXX ASM_NASM + HOMEPAGE_URL "https://github.com/assembler-0/VoidFrame" + DESCRIPTION "A hobbyist operating system kernel written in C and Rust" +) enable_language(ASM_NASM) # ============================================================================ @@ -16,6 +21,7 @@ include(dependencies) include(flags) include(configuration) include(source) +include(rust_utils) # ============================================================================ # Platform Checks @@ -44,6 +50,7 @@ endif() if(CMAKE_SYSTEM_PROCESSOR MATCHES "x86_64|AMD64") message(STATUS "CMake: Target Architecture: ${CMAKE_SYSTEM_PROCESSOR}") + set(Rust_CARGO_TARGET "x86_64-unknown-none") else() message(FATAL_ERROR "Unsupported target architecture: ${CMAKE_SYSTEM_PROCESSOR}") endif() @@ -67,15 +74,28 @@ set(CMAKE_CXX_STANDARD 20) set(CMAKE_C_STANDARD_REQUIRED ON) set(CMAKE_CXX_STANDARD_REQUIRED ON) -# ============================================================================ -# Rust Memory Management -# ============================================================================ -add_subdirectory(mm/rust) -add_subdirectory(kernel/atomic/rust) +# ============================================================================ +# Manifest paths for Rust components (corrosion with CMake) +# ============================================================================ +set(RUST_HEAP_MANIFEST_PATH "${CMAKE_SOURCE_DIR}/mm/rust/Cargo.toml") +set(RUST_ATOMIC_MANIFEST_PATH "${CMAKE_SOURCE_DIR}/kernel/atomic/rust/Cargo.toml") + +# ============================================================================ +# Corrosion +# ============================================================================ +add_subdirectory(corrosion) +corrosion_import_crate( + MANIFEST_PATH ${RUST_HEAP_MANIFEST_PATH} + NO_STD +) +corrosion_import_crate( + MANIFEST_PATH ${RUST_ATOMIC_MANIFEST_PATH} + NO_STD +) -# ============================================================================ +# ============================================================================ # Build Include Directories -# ============================================================================ +# ============================================================================ include_directories( . include @@ -116,9 +136,9 @@ include_directories( arch/x86_64/syscall ) -# ============================================================================ +# ============================================================================ # Kernel Linking -# ============================================================================ +# ============================================================================ add_executable(voidframe.krnl ${C_SOURCES} ${CPP_SOURCES} @@ -131,8 +151,8 @@ if(NOT EXCLUDE_EXTRA_OBJECTS) endif() # Rust libraries -target_link_libraries(voidframe.krnl PRIVATE rust_heap) -target_link_libraries(voidframe.krnl PRIVATE rust_spinlock) +link_rust_library(voidframe.krnl voidframe-spinlock) +link_rust_library(voidframe.krnl voidframe-mm) # Configure the linker to use ld.lld with proper arguments set_target_properties(voidframe.krnl PROPERTIES @@ -140,7 +160,7 @@ set_target_properties(voidframe.krnl PROPERTIES ) # Set linker flags for this specific target -target_link_options(voidframe.krnl PRIVATE +target_link_options(voidframe.krnl PRIVATE -fuse-ld=lld -T ${CMAKE_CURRENT_SOURCE_DIR}/voidframe.ld -nostdlib @@ -148,9 +168,9 @@ target_link_options(voidframe.krnl PRIVATE -Wl,-melf_x86_64 ) -# ============================================================================ +# ============================================================================ # ISO Creation -# ============================================================================ +# ============================================================================ add_custom_command( OUTPUT VoidFrame.iso COMMAND ${CMAKE_COMMAND} -E make_directory ${CMAKE_CURRENT_BINARY_DIR}/isodir/boot/grub @@ -230,4 +250,4 @@ add_custom_target(dump COMMAND ${LLVM_OBJDUMP} -t $ > voidframe.sym DEPENDS voidframe.krnl COMMENT "Generating disassembly and symbols" -) \ No newline at end of file +) diff --git a/cmake/rust_utils.cmake b/cmake/rust_utils.cmake new file mode 100644 index 0000000..ad169c8 --- /dev/null +++ b/cmake/rust_utils.cmake @@ -0,0 +1,9 @@ +# ============================================================================ +# Rust Utilities for CMake +# ============================================================================ + +# Function to link Rust libraries with automatic hyphen-to-underscore conversion +function(link_rust_library target_name rust_lib_name) + string(REPLACE "-" "_" converted_name "${rust_lib_name}") + target_link_libraries(${target_name} PRIVATE ${converted_name}) +endfunction() \ No newline at end of file diff --git a/kernel/atomic/rust/CMakeLists.txt b/kernel/atomic/rust/CMakeLists.txt deleted file mode 100644 index 637e3e6..0000000 --- a/kernel/atomic/rust/CMakeLists.txt +++ /dev/null @@ -1,37 +0,0 @@ -# Set target triple based on architecture -if(CMAKE_SYSTEM_PROCESSOR MATCHES "x86_64|AMD64") - set(RUST_TARGET "x86_64-unknown-none") -else() - message(FATAL_ERROR "Unsupported architecture: ${CMAKE_SYSTEM_PROCESSOR}") -endif() - -# Rust library target -set(RUST_LIB_NAME "voidframe_spinlock") -set(RUST_LIB_PATH "${CMAKE_CURRENT_SOURCE_DIR}/target/${RUST_TARGET}/release/lib${RUST_LIB_NAME}.a") - -# Custom command to build Rust library -add_custom_command( - OUTPUT ${RUST_LIB_PATH} - COMMAND ${CARGO_EXECUTABLE} build --release --target ${RUST_TARGET} - WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR} - DEPENDS ${CMAKE_CURRENT_SOURCE_DIR}/Cargo.toml - ${CMAKE_CURRENT_SOURCE_DIR}/src/lib.rs - ${CMAKE_CURRENT_SOURCE_DIR}/src/spinlock.rs - ${CMAKE_CURRENT_SOURCE_DIR}/src/ffi.rs - ${CMAKE_CURRENT_SOURCE_DIR}/src/rwlock.rs - ${CMAKE_CURRENT_SOURCE_DIR}/src/mcs.rs - COMMENT "Building Rust spinlock library" - -) - -# Create imported library target -add_custom_target(rust_spinlock_build DEPENDS ${RUST_LIB_PATH}) - -# Create imported library target -add_library(rust_spinlock STATIC IMPORTED GLOBAL) -set_target_properties(rust_spinlock PROPERTIES - IMPORTED_LOCATION ${RUST_LIB_PATH} -) - -# Add dependency to ensure Rust library is built first -add_dependencies(rust_spinlock rust_spinlock_build) \ No newline at end of file diff --git a/kernel/ipc/Ipc.c b/kernel/ipc/Ipc.c index 12c11ce..bfe41b9 100644 --- a/kernel/ipc/Ipc.c +++ b/kernel/ipc/Ipc.c @@ -89,6 +89,7 @@ IpcResult IpcSendMessage(uint32_t target_pid, const IpcMessage* msg) { target->state = PROC_READY; } rust_spinlock_unlock(queue->lock); + rust_spinlock_free(queue->lock); return IPC_SUCCESS; } @@ -128,11 +129,13 @@ IpcResult IpcReceiveMessage(IpcMessage* msg_buffer) { } rust_spinlock_unlock(queue->lock); + rust_spinlock_free(queue->lock); return IPC_SUCCESS; } current->state = PROC_BLOCKED; rust_spinlock_unlock(queue->lock); + rust_spinlock_free(queue->lock); Yield(); } } diff --git a/mm/rust/Cargo.lock b/mm/rust/Cargo.lock index 5370749..7794842 100644 --- a/mm/rust/Cargo.lock +++ b/mm/rust/Cargo.lock @@ -4,9 +4,9 @@ version = 4 [[package]] name = "spin" -version = "0.9.8" +version = "0.10.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6980e8d7511241f8acf4aebddbb1ff938df5eebe98691418c4468d0b72a96a67" +checksum = "d5fe4ccb98d9c292d56fec89a5e07da7fc4cf0dc11e156b41793132775d3e591" [[package]] name = "voidframe-mm" diff --git a/mm/rust/Cargo.toml b/mm/rust/Cargo.toml index f827a59..7560f70 100644 --- a/mm/rust/Cargo.toml +++ b/mm/rust/Cargo.toml @@ -7,7 +7,7 @@ edition = "2021" crate-type = ["staticlib"] [dependencies] -spin = { version = "0.9", default-features = false, features = ["mutex", "spin_mutex"] } +spin = { version = "0.10.0", default-features = false, features = ["mutex", "spin_mutex"] } [profile.release] panic = "abort" From 876f513920dc90557a87059421fbb9048ba0d964 Mon Sep 17 00:00:00 2001 From: Atheria Date: Sat, 11 Oct 2025 13:09:44 +0700 Subject: [PATCH 3/4] Corrosion --- corrosion/.gitignore | 8 + corrosion/CMakeLists.txt | 86 + corrosion/CMakePresets.json | 290 +++ corrosion/LICENSE | 21 + corrosion/README.md | 50 + corrosion/RELEASES.md | 499 +++++ corrosion/cmake/Corrosion.cmake | 2291 ++++++++++++++++++++++ corrosion/cmake/CorrosionConfig.cmake.in | 9 + corrosion/cmake/CorrosionGenerator.cmake | 338 ++++ corrosion/cmake/FindRust.cmake | 904 +++++++++ 10 files changed, 4496 insertions(+) create mode 100644 corrosion/.gitignore create mode 100644 corrosion/CMakeLists.txt create mode 100644 corrosion/CMakePresets.json create mode 100644 corrosion/LICENSE create mode 100644 corrosion/README.md create mode 100644 corrosion/RELEASES.md create mode 100644 corrosion/cmake/Corrosion.cmake create mode 100644 corrosion/cmake/CorrosionConfig.cmake.in create mode 100644 corrosion/cmake/CorrosionGenerator.cmake create mode 100644 corrosion/cmake/FindRust.cmake diff --git a/corrosion/.gitignore b/corrosion/.gitignore new file mode 100644 index 0000000..3a618f3 --- /dev/null +++ b/corrosion/.gitignore @@ -0,0 +1,8 @@ + +**/target/ +**/*.rs.bk +build*/ +.vscode +.idea +cmake-build-* +test/test_header.cmake diff --git a/corrosion/CMakeLists.txt b/corrosion/CMakeLists.txt new file mode 100644 index 0000000..ac05ffe --- /dev/null +++ b/corrosion/CMakeLists.txt @@ -0,0 +1,86 @@ +cmake_minimum_required(VERSION 3.22) +project(Corrosion + # Official releases will be major.minor.patch. When the `tweak` field is + # set it indicates that we are on a commit, that is not a officially + # tagged release. Users don't need to care about this, it is mainly to + # clearly see in configure logs which version was used, without needing to + # rely on `git`, since Corrosion may be installed or otherwise packaged. + VERSION 0.99.99 # 1.0-pre-release + LANGUAGES NONE + HOMEPAGE_URL "https://corrosion-rs.github.io/corrosion/" +) + +# ==== Corrosion Configuration ==== + +option( + CORROSION_BUILD_TESTS + "Build Corrosion test project" + ${PROJECT_IS_TOP_LEVEL} +) + +if (PROJECT_IS_TOP_LEVEL) + # We need to enable a language for corrosions test to work. + # For projects using corrosion this is not needed + enable_language(C) +endif() + +# This little bit self-hosts the Corrosion toolchain to build the generator +# tool. +# +# It is strongly encouraged to install Corrosion separately and use +# `find_package(Corrosion REQUIRED)` instead if that works with your workflow. +option(CORROSION_INSTALL_ONLY "Only add rules for installing Corrosion itself." OFF) +if (NOT CORROSION_INSTALL_ONLY) + list(APPEND CMAKE_MODULE_PATH ${CMAKE_CURRENT_SOURCE_DIR}/cmake) + include(Corrosion) +endif() + +# Testing +if (CORROSION_BUILD_TESTS) + include(CTest) + add_subdirectory(test) +endif() + +# If Corrosion is a subdirectory, do not enable its install code +if (NOT PROJECT_IS_TOP_LEVEL) + return() +endif() + +# Installation + +include(GNUInstallDirs) + +# Generate the Config file +include(CMakePackageConfigHelpers) + +configure_package_config_file( + cmake/CorrosionConfig.cmake.in CorrosionConfig.cmake + INSTALL_DESTINATION + "${CMAKE_INSTALL_FULL_LIBDIR}/cmake/Corrosion" +) + +write_basic_package_version_file( + "${CMAKE_CURRENT_BINARY_DIR}/CorrosionConfigVersion.cmake" + VERSION ${PROJECT_VERSION} + COMPATIBILITY + SameMajorVersion + ARCH_INDEPENDENT +) + +install( + FILES + "${CMAKE_CURRENT_BINARY_DIR}/CorrosionConfig.cmake" + "${CMAKE_CURRENT_BINARY_DIR}/CorrosionConfigVersion.cmake" + DESTINATION + "${CMAKE_INSTALL_FULL_LIBDIR}/cmake/Corrosion" +) + +# These CMake scripts are needed both for the install and as a subdirectory +install( + FILES + cmake/Corrosion.cmake + cmake/CorrosionGenerator.cmake + cmake/FindRust.cmake + DESTINATION + "${CMAKE_INSTALL_FULL_DATADIR}/cmake" +) diff --git a/corrosion/CMakePresets.json b/corrosion/CMakePresets.json new file mode 100644 index 0000000..1be2029 --- /dev/null +++ b/corrosion/CMakePresets.json @@ -0,0 +1,290 @@ +{ + "version": 3, + "cmakeMinimumRequired": { + "major": 3, + "minor": 22, + "patch": 0 + }, + "configurePresets": [ + { + "name": "ninja", + "hidden": true, + "generator": "Ninja" + }, + { + "name": "ninja-mc", + "hidden": true, + "generator": "Ninja Multi-Config" + }, + { + "name": "make", + "hidden": true, + "generator": "Unix Makefiles" + }, + { + "name": "vs-2019", + "hidden": true, + "generator": "Visual Studio 16 2019" + }, + { + "name": "vs-2022", + "hidden": true, + "generator": "Visual Studio 17 2022" + }, + { + "name": "windows-only", + "hidden": true, + "condition": { + "type": "equals", + "lhs": "${hostSystemName}", + "rhs": "Windows" + } + }, + { + "name": "windows-10-cross", + "hidden": true, + "cacheVariables": { + "CMAKE_SYSTEM_NAME": "Windows", + "CMAKE_SYSTEM_VERSION": "10.0" + }, + "condition": { + "type": "equals", + "lhs": "${hostSystemName}", + "rhs": "Windows" + } + }, + { + "name": "x86_64-pc-windows-msvc", + "hidden": true, + "inherits": ["windows-only"], + "cacheVariables": { + "Rust_CARGO_TARGET": "x86_64-pc-windows-msvc" + } + }, + { + "name": "i686-pc-windows-msvc", + "hidden": true, + "cacheVariables": { + "Rust_CARGO_TARGET": "i686-pc-windows-msvc" + } + }, + { + "name": "aarch64-pc-windows-msvc", + "hidden": true, + "cacheVariables": { + "Rust_CARGO_TARGET": "aarch64-pc-windows-msvc" + } + }, + { + "name": "x86_64-unknown-linux-gnu", + "hidden": true, + "cacheVariables": { + "Rust_CARGO_TARGET": "x86_64-unknown-linux-gnu" + } + }, + { + "name": "i686-unknown-linux-gnu", + "hidden": true, + "cacheVariables": { + "Rust_CARGO_TARGET": "i686-unknown-linux-gnu" + } + }, + { + "name": "aarch64-unknown-linux-gnu", + "hidden": true, + "cacheVariables": { + "Rust_CARGO_TARGET": "aarch64-unknown-linux-gnu" + } + }, + { + "name": "x86_64-apple-darwin", + "hidden": true, + "cacheVariables": { + "Rust_CARGO_TARGET": "x86_64-apple-darwin" + } + }, + { + "name": "aarch64-apple-darwin", + "hidden": true, + "cacheVariables": { + "Rust_CARGO_TARGET": "aarch64-apple-darwin" + } + }, + { + "name": "vs-platform-arm64", + "hidden": true, + "inherits": ["aarch64-pc-windows-msvc","windows-10-cross"], + "architecture": { + "value": "ARM64" + } + }, + { + "name": "vs-platform-x64", + "hidden": true, + "inherits": ["x86_64-pc-windows-msvc"], + "architecture": { + "value": "x64" + } + }, + { + "name": "vs-platform-i686", + "hidden": true, + "inherits": ["i686-pc-windows-msvc", "windows-10-cross"], + "architecture": { + "value": "Win32" + } + }, + { + "name": "vs-2019-x86_64", + "inherits": ["vs-platform-x64", "vs-2019"] + }, + { + "name": "vs-2022-x86_64", + "inherits": ["vs-platform-x64", "vs-2022"] + }, + { + "name": "vs-2019-i686", + "inherits": ["vs-platform-i686", "vs-2019"] + }, + { + "name": "vs-2022-i686", + "inherits": ["vs-platform-i686", "vs-2022"] + }, + { + "name": "vs-2019-aarch64", + "inherits": ["vs-platform-arm64", "vs-2019"] + }, + { + "name": "vs-2022-aarch64", + "inherits": ["vs-platform-arm64", "vs-2022"] + }, + { + "name": "clang", + "hidden": true, + "cacheVariables": { + "CMAKE_C_COMPILER": "clang", + "CMAKE_CXX_COMPILER": "clang++" + } + }, + { + "name": "host-gcc", + "hidden": true, + "cacheVariables": { + "CMAKE_C_COMPILER": "gcc", + "CMAKE_CXX_COMPILER": "g++" + } + }, + { + "name": "clang-cl", + "hidden": true, + "inherits": ["windows-only"], + "cacheVariables": { + "CMAKE_C_COMPILER": "clang-cl", + "CMAKE_CXX_COMPILER": "clang-cl" + } + }, + { + "name": "cl", + "hidden": true, + "inherits": ["windows-only"], + "cacheVariables": { + "CMAKE_C_COMPILER": "cl", + "CMAKE_CXX_COMPILER": "cl" + } + }, + { + "name": "ninja-x86_64-pc-windows-msvc-cl", + "inherits": ["ninja", "x86_64-pc-windows-msvc", "cl"] + }, + { + "name": "ninja-x86_64-pc-windows-msvc-clang-cl", + "inherits": ["ninja", "x86_64-pc-windows-msvc", "clang-cl"] + }, + { + "name": "ninja-x86_64-pc-windows-msvc-clang", + "inherits": ["ninja", "x86_64-pc-windows-msvc", "clang"] + }, + { + "name": "ninja-i686-pc-windows-msvc-cl", + "inherits": ["ninja", "i686-pc-windows-msvc", "cl", "windows-10-cross"] + }, + { + "name": "ninja-i686-pc-windows-msvc-clang-cl", + "inherits": ["ninja", "i686-pc-windows-msvc", "clang-cl", "windows-10-cross"] + }, + { + "name": "ninja-i686-pc-windows-msvc-clang", + "inherits": ["ninja", "i686-pc-windows-msvc", "clang", "windows-10-cross"] + }, + { + "name": "ninja-aarch64-pc-windows-msvc-cl", + "inherits": ["ninja", "aarch64-pc-windows-msvc", "cl", "windows-10-cross"] + }, + { + "name": "ninja-aarch64-pc-windows-msvc-clang-cl", + "inherits": ["ninja", "aarch64-pc-windows-msvc", "clang-cl", "windows-10-cross"] + }, + { + "name": "ninja-aarch64-pc-windows-msvc-clang", + "inherits": ["ninja", "aarch64-pc-windows-msvc", "clang", "windows-10-cross"] + }, + { + "name": "ninja-x86_64-pc-windows-gnullvm", + "inherits": ["ninja", "windows-only", "clang"], + "toolchainFile": "${sourceDir}/.github/scripts/toolchains/x86_64-pc-windows-gnullvm.cmake" + }, + { + "name": "make-x86_64-pc-windows-gnullvm", + "inherits": ["make", "windows-only", "clang"], + "toolchainFile": "${sourceDir}/.github/scripts/toolchains/x86_64-pc-windows-gnullvm.cmake" + }, + { + "name": "ninja-x86_64-pc-windows-gnu-gcc", + "inherits": ["ninja", "host-gcc", "windows-only"] + }, + { + "name": "make-x86_64-pc-windows-gnu-gcc", + "inherits": ["make", "host-gcc", "windows-only"] + }, + { + "name": "x86_64-unknown-linux-gnu-clang", + "inherits": ["x86_64-unknown-linux-gnu"], + "toolchainFile": "${sourceDir}/.github/scripts/toolchains/${presetName}.cmake" + }, + { + "name": "x86_64-unknown-linux-gnu-gcc", + "inherits": ["x86_64-unknown-linux-gnu"], + "toolchainFile": "${sourceDir}/.github/scripts/toolchains/${presetName}.cmake" + }, + { + "name": "i686-unknown-linux-gnu-clang", + "inherits": ["i686-unknown-linux-gnu"], + "toolchainFile": "${sourceDir}/.github/scripts/toolchains/${presetName}.cmake" + }, + { + "name": "i686-unknown-linux-gnu-gcc", + "inherits": ["i686-unknown-linux-gnu"], + "toolchainFile": "${sourceDir}/.github/scripts/toolchains/${presetName}.cmake" + }, + { + "name": "aarch64-unknown-linux-gnu-clang", + "inherits": ["aarch64-unknown-linux-gnu"], + "toolchainFile": "${sourceDir}/.github/scripts/toolchains/${presetName}.cmake" + }, + { + "name": "aarch64-unknown-linux-gnu-gcc", + "inherits": ["aarch64-unknown-linux-gnu"], + "toolchainFile": "${sourceDir}/.github/scripts/toolchains/${presetName}.cmake" + }, + { + "name": "x86_64-apple-darwin-clang", + "inherits": ["x86_64-apple-darwin", "clang"], + "toolchainFile": "${sourceDir}/.github/scripts/toolchains/${presetName}.cmake" + }, + { + "name": "aarch64-apple-darwin-clang", + "inherits": ["aarch64-apple-darwin"], + "toolchainFile": "${sourceDir}/.github/scripts/toolchains/${presetName}.cmake" + } + ] +} diff --git a/corrosion/LICENSE b/corrosion/LICENSE new file mode 100644 index 0000000..5e30d77 --- /dev/null +++ b/corrosion/LICENSE @@ -0,0 +1,21 @@ +MIT License + +Copyright (c) 2018 Andrew Gaspar + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/corrosion/README.md b/corrosion/README.md new file mode 100644 index 0000000..dcefcfd --- /dev/null +++ b/corrosion/README.md @@ -0,0 +1,50 @@ +# Corrosion +[![Build Status](https://github.com/corrosion-rs/corrosion/actions/workflows/test.yaml/badge.svg)](https://github.com/corrosion-rs/corrosion/actions?query=branch%3Amaster) +[![Documentation](https://img.shields.io/badge/docs-latest-blue.svg)](https://corrosion-rs.github.io/corrosion/) +![License](https://img.shields.io/badge/license-MIT-blue) + +Corrosion, formerly known as cmake-cargo, is a tool for integrating Rust into an existing CMake +project. Corrosion can automatically import executables, static libraries, and dynamic libraries +from a workspace or package manifest (`Cargo.toml` file). + +## Features +- Automatic Import of Executable, Static, and Shared Libraries from Rust Crate +- Easy Installation of Rust Executables +- Trivially Link Rust Executables to C/C++ Libraries in Tree +- Multi-Config Generator Support +- Simple Cross-Compilation + +## Sample Usage with FetchContent + +Using the CMake `FetchContent` module allows you to easily integrate corrosion into your build. +Other methods including installing corrosion or adding it as a subdirectory are covered in the +[setup chapter](https://corrosion-rs.github.io/corrosion/setup_corrosion.html) of the +corrosion [documentation](https://corrosion-rs.github.io/corrosion/). + +```cmake +include(FetchContent) + +FetchContent_Declare( + Corrosion + GIT_REPOSITORY https://github.com/corrosion-rs/corrosion.git + GIT_TAG v0.5 # Optionally specify a commit hash, version tag or branch here +) +FetchContent_MakeAvailable(Corrosion) + +# Import targets defined in a package or workspace manifest `Cargo.toml` file +corrosion_import_crate(MANIFEST_PATH rust-lib/Cargo.toml) + +add_executable(your_cpp_bin main.cpp) +target_link_libraries(your_cpp_bin PUBLIC rust-lib) +``` + +## Requirements + +### Stable v0.5 Release + +- CMake 3.15 or newer. Some features may only be available on more recent CMake versions +- Rust 1.46 or newer. Some platforms / features may require more recent Rust versions + +### Development (master branch) + +- CMake 3.22 or newer diff --git a/corrosion/RELEASES.md b/corrosion/RELEASES.md new file mode 100644 index 0000000..8a6239f --- /dev/null +++ b/corrosion/RELEASES.md @@ -0,0 +1,499 @@ +# Unreleased + +### Breaking Changes + +- The master branch of corrosion now requires CMake 3.22. See also the + [v0.4.0 Release notes](#040-lts-2023-06-01) for more details. +- Removed native tooling and the corresponding option `CORROSION_NATIVE_TOOLING`. + Corrosion now always uses pure CMake. +- Fix Corrosion placing artifacts into the wrong directory when: + 1. using a Multi-Config Generator (e.g Visual Studio or XCode) AND + 2. `OUTPUT_DIRECTORY_` is not set AND + 3. `OUTPUT_DIRECTORY` is set AND + 4. `OUTPUT_DIRECTORY` does not contain a generator expression + + Corrosion now places artifacts into a `$` subdirectory of the + specified `OUTPUT_DIRECTORY`. This matches the [documented behavior][doc-cmake-rt-output-dir] + of CMake for regular CMake targets. ([#568]). + +### New features + +- Support using the `$` generator expression in `OUTPUT_DIRECTORY`. [#459] +- Add `OVERRIDE_CRATE_TYPE` option to corrosion_import_crate, allowing users to override + the crate-types of Rust libraries (e.g. force building as a staticlib instead of an rlib). +- Support *-windows-gnullvm targets. +- experimental support in corrosion_install for installing libraries and header files +- Add `CORROSION_TOOLS_RUST_TOOLCHAIN` cache variable which allows users to select a different + rust toolchain for compiling build-tools used by corrosion (currently cbindgen and cxxbridge). + This mainly allows using a newer toolchain for such build-tools then for the actual project. + +[doc-cmake-rt-output-dir]: https://cmake.org/cmake/help/latest/prop_tgt/RUNTIME_OUTPUT_DIRECTORY.html +[#459]: https://github.com/corrosion-rs/corrosion/pull/459 +[#568]: https://github.com/corrosion-rs/corrosion/pull/568 + +# v0.5.1 (2024-12-29) + +### Fixes + +- Update FindRust to support `rustup` v1.28.0. Support for older rustup versions is retained, + so updating corrosion quickly is recommended to all rustup users. + + +# v0.5.0 (2024-05-11) + +### Breaking Changes + +- Dashes (`-`) in names of imported CMake **library** targets are now replaced with underscores (`_`). + See [issue #501] for details. Users on older Corrosion versions will experience the same + change when using Rust 1.79 or newer. `bin` targets are not affected by this change. + +[issue #501]: https://github.com/corrosion-rs/corrosion/issues/501 + +# v0.4.10 (2024-05-11) + +### New features + +- `corrosion_experimental_cbindgen()` can now be called multiple times on the same Rust target, + as long as the output header name differs. This may be useful to generate separate C and C++ + bindings. [#507] +- If `corrosion_link_libraries()` is called on a Rust static library target, then + `target_link_libraries()` is called to propagate the dependencies to C/C++ consumers. + Previously a warning was emitted in this case and the arguments ignored. [#506] + +### Fixes + +- Combine `-framework` flags on macos to avoid linker deduplication errors [#455] +- `corrosion_experimental_cbindgen()` will now correctly use the package name, instead of assuming that + the package and crate name are identical. ([11e27c]) +- Set the `AR_` variable for `cc-rs` (except for msvc targets) [#456] +- Fix hostbuild when cross-compiling to windows [#477] +- Consider vworks executable suffix [#504] +- `corrosion_experimental_cbindgen()` now forwards the Rust target-triple (e.g. `aarch64-unknown-linux-gnu`) + to cbindgen via the `TARGET` environment variable. The `hostbuild` property is considered. [#507] +- Fix linking errors with Rust >= 1.79 and `-msvc` targets.` [#511] + + +[#455]: https://github.com/corrosion-rs/corrosion/pull/455 +[#456]: https://github.com/corrosion-rs/corrosion/pull/456 +[#477]: https://github.com/corrosion-rs/corrosion/pull/477 +[#504]: https://github.com/corrosion-rs/corrosion/pull/504 +[#506]: https://github.com/corrosion-rs/corrosion/pull/506 +[#507]: https://github.com/corrosion-rs/corrosion/pull/507 +[#511]: https://github.com/corrosion-rs/corrosion/pull/511 +[11e27c]: https://github.com/corrosion-rs/corrosion/pull/514/commits/11e27cde2cf32c7ed539c96eb03c2f10035de538 + +# v0.4.9 (2024-05-01) + +### New Features + +- Automatically detect Rust target for OpenHarmony ([#510]). + +### Fixes + +- Make find_package portable ([#509]). + +[#510]: https://github.com/corrosion-rs/corrosion/pull/510 +[#509]: https://github.com/corrosion-rs/corrosion/pull/509 + +# v0.4.8 (2024-04-03) + +### Fixes + +- Fix an internal error when passing both the `PROFILE` and `CRATES` option to + `corrosion_import_crate()` ([#496]). + +[#496]: https://github.com/corrosion-rs/corrosion/pull/496 + +# v0.4.7 (2024-01-19) + +### Fixes + +- The C/C++ compiler passed from corrosion to `cc-rs` can now be overridden by users setting + `CC_` (e.g. `CC_x86_64-unknown-linux-gnu=/path/to/my-compiler`) environment variables ([#475]). + +[#475]: https://github.com/corrosion-rs/corrosion/pull/475 + +# v0.4.6 (2024-01-17) + +### Fixes + +- Fix hostbuild executables when cross-compiling from non-windows to windows targets. + (Only with CMake >= 3.19). + +# v0.4.5 (2023-11-30) + +### Fixes + +- Fix hostbuild executables when cross-compiling on windows to non-windows targets + (Only with CMake >= 3.19). + +# v0.4.4 (2023-10-06) + +### Fixes + +- Add `chimera` ([#445]) and `unikraft` ([#446]) to the list of known vendors + +[#445]: https://github.com/corrosion-rs/corrosion/pull/445 +[#446]: https://github.com/corrosion-rs/corrosion/pull/446 + +# v0.4.3 (2023-09-09) + +### Fixes + +- Fix the PROFILE option with CMake < 3.19 [#427] +- Relax vendor parsing for espressif targets (removes warnings) +- Fix an issue detecting required link libraries with Rust >= 1.71 + when the cmake build directory is located in a Cargo workspace. + +# 0.4.2 (2023-07-16) + +### Fixes + +- Fix an issue when cross-compiling with clang +- Fix detecting required libraries with cargo 1.71 + +### New features + +- Users can now set `Rust_RESOLVE_RUSTUP_TOOLCHAINS` to `OFF`, which will result in Corrosion + not attempting to resolve rustc/cargo. + +# 0.4.1 (2023-06-03) + +This is a bugfix release. + +### Fixes + +- Fixes a regression on multi-config Generators + +# 0.4.0 LTS (2023-06-01) + +No changes compared to v0.4.0-beta2. + +## Announcements + +The `v0.4.x` LTS series will be the last release to support older CMake and Rust versions. +If necessary, fixes will be backported to the v0.4 branch. New features will not be +actively backported after the next major release, but community contributions are possible. +The `v0.4.x` series is currently planned to be maintained until the end of 2024. + +The following major release will increase the minimum required CMake version to 3.22. The +minimum supported Rust version will also be increased to make use of newly added flags, but +the exact version is not fixed yet. + + +## Changes compared to v0.3.5: + +### Breaking Changes + +- The Visual Studio Generators now require at least CMake 3.20. + This was previously announced in the 0.3.0 release notes and is the same + requirement as for the other Multi-Config Generators. +- The previously deprecated function `corrosion_set_linker_language()` + will now raise an error when called and may be removed without further + notice in future stable releases. Use `corrosion_set_linker()` instead. +- Improved the FindRust target triple detection, which may cause different behavior in some cases. + The detection does not require an enabled language anymore and will always fall back + to the default host target triple. A warning is issued if target triple detection failed. + +### Potentially Breaking Changes + +- Corrosion now sets the `IMPORTED_NO_SONAME` property for shared rust libraries, since by + default they won't have an `soname` field. + If you add a rustflag like `-Clink-arg=-Wl,-soname,libmycrate.so` in your project, + you should set this property to false on the shared rust library. +- Corrosion now uses a mechanism to determine which native libraries need to be linked with + Rust `staticlib` targets into C/C++ targets. The previous mechanism contained a hardcoded list. + The new mechanism asks `rustc` which libraries are needed at minimum for a given + target triple (with `std` support). This should not be a breaking change, but if you + do encounter a new linking issue when upgrading with `staticlib` targets, please open an + issue. + +### New features + +- `corrosion_import_crate()` has two new options `LOCKED` and `FROZEN` which pass the + `--locked` and `--frozen` flags to all invocations of cargo. +- `FindRust` now provides cache variables containing information on the default host + target triple: + - `Rust_CARGO_HOST_ARCH` + - `Rust_CARGO_HOST_VENDOR` + - `Rust_CARGO_HOST_OS` + - `Rust_CARGO_HOST_ENV` + +### Other changes + +- When installing Corrosion with CMake >= 3.19, the legacy Generator tool is + no longer built and installed by default. +- Corrosion now issues a warning when setting the linker or setting linker + options for a Rust static library. +- Corrosion no longer enables the `C` language when CMake is in crosscompiling mode and + no languages where previously enabled. This is not considered a breaking change. +- `corrosion_import_crate()` now warns about unexpected arguments. + +### Fixes + +- Fix building when the `dev` profile is explicitly set by the user. + +## Experimental features (may be changed or removed without a major version bump) + +- Experimental cxxbridge and cbindgen integration. +- Add a helper function to parse the package version from a Cargo.toml file +- Expose rustup toolchains discovered by `FindRust` in the following cache variables + which contain a list. + - `Rust_RUSTUP_TOOLCHAINS`: List of toolchains names + - `Rust_RUSTUP_TOOLCHAINS_VERSION`: List of `rustc` version of the toolchains + - `Rust_RUSTUP_TOOLCHAINS_RUSTC_PATH`: List of the path to `rustc` + - `Rust_RUSTUP_TOOLCHAINS_CARGO_PATH`: List of the path to `cargo`. Entries may be `NOTFOUND` if cargo + is not available for that toolchain. +- Add target properties `INTERFACE_CORROSION_RUSTC` and `INTERFACE_CORROSION_CARGO`, which may + be set to paths to `rustc` and `cargo` respectively to override the toolchain for a specific + target. + +# 0.3.5 (2023-03-19) + +- Fix building the Legacy Generator on Rust toolchains < 1.56 ([#365]) + +[#365]: https://github.com/corrosion-rs/corrosion/pull/365 + +# 0.3.4 (2023-03-02) + +## Fixes + +- Fix hostbuild (when CMake/Cargo is configured for cross-compiling) if clang is used ([#338]). + +## Other + +- Pass `--no-deps` to cargo metadata ([#334]). +- Bump the legacy generator dependencies + +[#334]: https://github.com/corrosion-rs/corrosion/pull/334 +[#338]: https://github.com/corrosion-rs/corrosion/pull/338 + + +# 0.3.3 (2023-02-17) + +## New features (Only available on CMake >= 3.19) + +- Add new `IMPORTED_CRATES` flag to `corrosion_import_crate()` to retrieve the list of imported crates in the current + scope ([#312](https://github.com/corrosion-rs/corrosion/pull/312)). + +## Fixes + +- Fix imported location target property when the rust target name contains dashes + and a custom OUTPUT_DIRECTORY was specified by the user ([#322](https://github.com/corrosion-rs/corrosion/pull/322)). +- Fix building for custom rust target-triples ([#316](https://github.com/corrosion-rs/corrosion/pull/316)) + +# 0.3.2 (2023-01-11) + +## New features (Only available on CMake >= 3.19) + +- Add new `CRATE_TYPES` flag to `corrosion_import_crate()` to restrict which + crate types should be imported ([#269](https://github.com/corrosion-rs/corrosion/pull/269)). +- Add `NO_LINKER_OVERRIDE` flag to let Rust choose the default linker for the target + instead of what Corrosion thinks is the appropriate linker driver ([#272](https://github.com/corrosion-rs/corrosion/pull/272)). + +## Fixes + +- Fix clean target when cross-compiling ([#291](https://github.com/corrosion-rs/corrosion/pull/291)). +- Don't set the linker for Rust static libraries ([#275](https://github.com/corrosion-rs/corrosion/pull/275)). +- Minor fixes in FindRust [#297](https://github.com/corrosion-rs/corrosion/pull/297): + - fix a logic error in the version detection + - fix a logic error in `QUIET` mode when rustup is not found. + +# 0.3.1 (2022-12-13) + +### Fixes + +- Fix a regression in detecting the MSVC abi ([#256]) +- Fix an issue on macOS 13 which affected rust crates compiling C++ code in build scripts ([#254]). +- Fix corrosion not respecting `CMAKE__OUTPUT_DIRECTORY` values ([#268]). +- Don't override rusts linker choice for the msvc abi (previously this was only skipped for msvc generators) ([#271]) + +[#254]: https://github.com/corrosion-rs/corrosion/pull/254 +[#256]: https://github.com/corrosion-rs/corrosion/pull/256 +[#268]: https://github.com/corrosion-rs/corrosion/pull/268 +[#271]: https://github.com/corrosion-rs/corrosion/pull/271 + +# 0.3.0 (2022-10-31) + +## Breaking + +- The minimum supported rust version (MSRV) was increased to 1.46, due to a cargo issue that recently + surfaced on CI when using crates.io. On MacOS 12 and Windows-2022 at least Rust 1.54 is required. +- MacOS 10 and 11 are no longer officially supported and untested in CI. +- The minimum required CMake version is now 3.15. +- Adding a `PRE_BUILD` custom command on a `cargo-build_` CMake target will no + longer work as expected. To support executing user defined commands before cargo build is + invoked users should use the newly added targets `cargo-prebuild` (before all cargo build invocations) + or `cargo-prebuild_` as a dependency target. + Example: `add_dependencies(cargo-prebuild code_generator_target)` + +### Breaking: Removed previously deprecated functionality +- Removed `add_crate()` function. Use `corrosio_import_crate()` instead. +- Removed `cargo_link_libraries()` function. Use `corrosion_link_libraries()` instead. +- Removed experimental CMake option `CORROSION_EXPERIMENTAL_PARSER`. + The corresponding stable option is `CORROSION_NATIVE_TOOLING` albeit with inverted semantics. +- Previously Corrosion would set the `HOST_CC` and `HOST_CXX` environment variables when invoking + cargo build, if the environment variables `CC` and `CXX` outside of CMake where set. + However this did not work as expected in all cases and sometimes the `HOST_CC` variable would be set + to a cross-compiler for unknown reasons. For this reason `HOST_CC` and `HOST_CXX` are not set by + corrosion anymore, but users can still set them manually if required via `corrosion_set_env_vars()`. +- The `CARGO_RUST_FLAGS` family of cache variables were removed. Corrosion does not internally use them + anymore. + +## Potentially breaking + +- The working directory when invoking `cargo build` was changed to the directory of the Manifest + file. This now allows cargo to pick up `.cargo/config.toml` files located in the source tree. + ([205](https://github.com/corrosion-rs/corrosion/pull/205)) +- Corrosion internally invokes `cargo build`. When passing arguments to `cargo build`, Corrosion + now uses the CMake `VERBATIM` option. In rare cases this may require you to change how you quote + parameters passed to corrosion (e.g. via `corrosion_add_target_rustflags()`). + For example setting a `cfg` option previously required double escaping the rustflag like this + `"--cfg=something=\\\"value\\\""`, but now it can be passed to corrosion without any escapes: + `--cfg=something="value"`. +- Corrosion now respects the CMake `OUTPUT_DIRECTORY` target properties. More details in the "New features" section. + +## New features + +- Support setting rustflags for only the main target and none of its dependencies ([215](https://github.com/corrosion-rs/corrosion/pull/215)). + A new function `corrosion_add_target_local_rustflags(target_name rustc_flag [more_flags ...])` + is added for this purpose. + This is useful in cases where you only need rustflags on the main-crate, but need to set different + flags for different targets. Without "local" Rustflags this would require rebuilds of the + dependencies when switching targets. +- Support explicitly selecting a linker ([208](https://github.com/corrosion-rs/corrosion/pull/208)). + The linker can be selected via `corrosion_set_linker(target_name linker)`. + Please note that this only has an effect for targets, where the final linker invocation is done + by cargo, i.e. targets where foreign code is linked into rust code and not the other way around. +- Corrosion now respects the CMake `OUTPUT_DIRECTORY` target properties and copies build artifacts to the expected + locations ([217](https://github.com/corrosion-rs/corrosion/pull/217)), if the properties are set. + This feature requires at least CMake 3.19 and is enabled by default if supported. Please note that the `OUTPUT_NAME` + target properties are currently not supported. + Specifically, the following target properties are now respected: + - [ARCHIVE_OUTPUT_DIRECTORY](https://cmake.org/cmake/help/latest/prop_tgt/ARCHIVE_OUTPUT_DIRECTORY.html) + - [LIBRARY_OUTPUT_DIRECTORY](https://cmake.org/cmake/help/latest/prop_tgt/LIBRARY_OUTPUT_DIRECTORY.html) + - [RUNTIME_OUTPUT_DIRECTORY](https://cmake.org/cmake/help/latest/prop_tgt/RUNTIME_OUTPUT_DIRECTORY.html) + - [PDB_OUTPUT_DIRECTORY](https://cmake.org/cmake/help/latest/prop_tgt/PDB_OUTPUT_DIRECTORY.html) +- Corrosion now supports packages with potentially multiple binaries (bins) and a library (lib) at the + same time. The only requirement is that the names of all `bin`s and `lib`s in the whole project must be unique. + Users can set the names in the `Cargo.toml` by adding `name = ` in the `[[bin]]` and `[lib]` tables. +- FindRust now has improved support for the `VERSION` option of `find_package` and will now attempt to find a matching + toolchain version. Previously it was only checked if the default toolchain matched to required version. +- For rustup managed toolchains a CMake error is issued with a helpful message if the required target for + the selected toolchain is not installed. + +## Fixes + +- Fix a CMake developer Warning when a Multi-Config Generator and Rust executable targets + ([#213](https://github.com/corrosion-rs/corrosion/pull/213)). +- FindRust now respects the `QUIET` option to `find_package()` in most cases. + +## Deprecation notice + +- Support for the MSVC Generators with CMake toolchains before 3.20 is deprecated and will be removed in the next + release (v0.4). All other Multi-config Generators already require CMake 3.20. + +## Internal Changes + +- The CMake Generator written in Rust and `CorrosionGenerator.cmake` which are responsible for parsing + `cargo metadata` output to create corresponding CMake targets for all Rust targets now share most code. + This greatly simplified the CMake generator written in Rust and makes it much easier maintaining and adding + new features regardless of how `cargo metadata` is parsed. + +# 0.2.2 (2022-09-01) + +## Fixes + +- Do not use C++17 in the tests (makes tests work with older C++ compilers) ([184](https://github.com/corrosion-rs/corrosion/pull/184)) +- Fix finding cargo on NixOS ([192](https://github.com/corrosion-rs/corrosion/pull/192)) +- Fix issue with Rustflags test when using a Build type other than Debug and Release ([203](https://github.com/corrosion-rs/corrosion/pull/203)). + +# 0.2.1 (2022-05-07) + +## Fixes + +- Fix missing variables provided by corrosion, when corrosion is used as a subdirectory ([181](https://github.com/corrosion-rs/corrosion/pull/181)): + Public [Variables](https://github.com/corrosion-rs/corrosion#information-provided-by-corrosion) set + by Corrosion were not visible when using Corrosion as a subdirectory, due to the wrong scope of + the variables. This was fixed by promoting the respective variables to Cache variables. + +# 0.2.0 (2022-05-05) + +## Breaking changes + +- Removed the integrator build script ([#156](https://github.com/corrosion-rs/corrosion/pull/156)). + The build script provided by corrosion (for rust code that links in foreign code) is no longer necessary, + so users can just remove the dependency. + +## Deprecations + +- Direct usage of the following target properties has been deprecated. The names of the custom properties are + no longer considered part of the public API and may change in the future. Instead, please use the functions + provided by corrosion. Internally different property names are used depending on the CMake version. + - `CORROSION_FEATURES`, `CORROSION_ALL_FEATURES`, `CORROSION_NO_DEFAULT_FEATURES`. Instead please use + `corrosion_set_features()`. See the updated Readme for details. + - `CORROSION_ENVIRONMENT_VARIABLES`. Please use `corrosion_set_env_vars()` instead. + - `CORROSION_USE_HOST_BUILD`. Please use `corrosion_set_hostbuild()` instead. +- The Minimum CMake version will likely be increased for the next major release. At the very least we want to drop + support for CMake 3.12, but requiring CMake 3.16 or even 3.18 is also on the table. If you are using a CMake version + that would be no longer supported by corrosion, please comment on issue + [#168](https://github.com/corrosion-rs/corrosion/issues/168), so that we can gauge the number of affected users. + +## New features + +- Add `NO_STD` option to `corrosion_import_crate` ([#154](https://github.com/corrosion-rs/corrosion/pull/154)). +- Remove the requirement of building the Rust based generator crate for CMake >= 3.19. This makes using corrosion as + a subdirectory as fast as the installed version (since everything is done in CMake). + ([#131](https://github.com/corrosion-rs/corrosion/pull/131), [#161](https://github.com/corrosion-rs/corrosion/pull/161)) + If you do choose to install Corrosion, then by default the old Generator is still compiled and installed, so you can + fall back to using it in case you use multiple cmake versions on the same machine for different projects. + +## Fixes + +- Fix Corrosion on MacOS 11 and 12 ([#167](https://github.com/corrosion-rs/corrosion/pull/167) and + [#164](https://github.com/corrosion-rs/corrosion/pull/164)). +- Improve robustness of parsing the LLVM version (exported in `Rust_LLVM_VERSION`). It now also works for + Rust versions, where the LLVM version is reported as `MAJOR.MINOR`. ([#148](https://github.com/corrosion-rs/corrosion/pull/148)) +- Fix a bug which occurred when Corrosion was added multiple times via `add_subdirectory()` + ([#143](https://github.com/corrosion-rs/corrosion/pull/143)). +- Set `CC_` and `CXX_` environment variables for the invocation of + `cargo build` to the compilers selected by CMake (if any) + ([#138](https://github.com/corrosion-rs/corrosion/pull/138) and [#161](https://github.com/corrosion-rs/corrosion/pull/161)). + This should ensure that C dependencies built in cargo buildscripts via [cc-rs](https://github.com/alexcrichton/cc-rs) + use the same compiler as CMake built dependencies. Users can override the compiler by specifying the higher + priority environment variable variants with dashes instead of underscores (See cc-rs documentation for details). +- Fix Ninja-Multiconfig Generator support for CMake versions >= 3.20. Previous CMake versions are missing a feature, + which prevents us from supporting the Ninja-Multiconfig generator. ([#137](https://github.com/corrosion-rs/corrosion/pull/137)) + + +# 0.1.0 (2022-02-01) + +This is the first release of corrosion after it was moved to the new corrosion-rs organization. +Since there are no previous releases, this is not a complete changelog but only lists changes since +September 2021. + +## New features +- [Add --profile support for rust >= 1.57](https://github.com/corrosion-rs/corrosion/pull/130): + Allows users to specify a custom cargo profile with + `corrosion_import_crate(... PROFILE )`. +- [Add support for specifying per-target Rustflags](https://github.com/corrosion-rs/corrosion/pull/127): + Rustflags can be added via `corrosion_add_target_rustflags( [rustflags1...])` +- [Add `Rust_IS_NIGHTLY` and `Rust_LLVM_VERSION` variables](https://github.com/corrosion-rs/corrosion/pull/123): + This may be useful if you want to conditionally enabled features when using a nightly toolchain + or a specific LLVM Version. +- [Let `FindRust` fail gracefully if rustc is not found](https://github.com/corrosion-rs/corrosion/pull/111): + This allows using `FindRust` in a more general setting (without corrosion). +- [Add support for cargo feature selection](https://github.com/corrosion-rs/corrosion/pull/108): + See the [README](https://github.com/corrosion-rs/corrosion#cargo-feature-selection) for details on + how to select features. + + +## Fixes +- [Fix the cargo-clean target](https://github.com/corrosion-rs/corrosion/pull/129) +- [Fix #84: CorrosionConfig.cmake looks in wrong place for Corrosion::Generator when CMAKE_INSTALL_LIBEXEC is an absolute path](https://github.com/corrosion-rs/corrosion/pull/122/commits/6f29af3ac53917ca2e0638378371e715a18a532d) +- [Fix #116: (Option CORROSION_INSTALL_EXECUTABLE not working)](https://github.com/corrosion-rs/corrosion/commit/97d44018fac1b1a2a7c095288c628f5bbd9b3184) +- [Fix building on Windows with rust >= 1.57](https://github.com/corrosion-rs/corrosion/pull/120) + +## Known issues: +- Corrosion is currently not working on macos-11 and newer. See issue [#104](https://github.com/corrosion-rs/corrosion/issues/104). + Contributions are welcome. diff --git a/corrosion/cmake/Corrosion.cmake b/corrosion/cmake/Corrosion.cmake new file mode 100644 index 0000000..ac4fcc4 --- /dev/null +++ b/corrosion/cmake/Corrosion.cmake @@ -0,0 +1,2291 @@ +cmake_minimum_required(VERSION 3.22) + +list(APPEND CMAKE_MESSAGE_CONTEXT "Corrosion") + +message(DEBUG "Using Corrosion ${Corrosion_VERSION} with CMake ${CMAKE_VERSION} " + "and the `${CMAKE_GENERATOR}` Generator" +) + +get_cmake_property(COR_IS_MULTI_CONFIG GENERATOR_IS_MULTI_CONFIG) +set(COR_IS_MULTI_CONFIG "${COR_IS_MULTI_CONFIG}" CACHE BOOL "Do not change this" FORCE) +mark_as_advanced(FORCE COR_IS_MULTI_CONFIG) + + +if(NOT COR_IS_MULTI_CONFIG AND DEFINED CMAKE_CONFIGURATION_TYPES) + message(WARNING "The Generator is ${CMAKE_GENERATOR}, which is not a multi-config " + "Generator, but CMAKE_CONFIGURATION_TYPES is set. Please don't set " + "CMAKE_CONFIGURATION_TYPES unless you are using a multi-config Generator." + ) +endif() + +option(CORROSION_VERBOSE_OUTPUT "Enables verbose output from Corrosion and Cargo" OFF) + +if(DEFINED CORROSION_RESPECT_OUTPUT_DIRECTORY AND NOT CORROSION_RESPECT_OUTPUT_DIRECTORY) + message(WARNING "The option CORROSION_RESPECT_OUTPUT_DIRECTORY was removed." + " Corrosion now always attempts to respect the output directory.") +endif() + +option( + CORROSION_NO_WARN_PARSE_TARGET_TRIPLE_FAILED + "Surpresses a warning if the parsing the target triple failed." + OFF +) + +find_package(Rust REQUIRED) + +if(CMAKE_GENERATOR MATCHES "Visual Studio" + AND (NOT CMAKE_VS_PLATFORM_NAME STREQUAL CMAKE_VS_PLATFORM_NAME_DEFAULT) + AND Rust_VERSION VERSION_LESS "1.54") + message(FATAL_ERROR "Due to a cargo issue, cross-compiling with a Visual Studio generator and rust versions" + " before 1.54 is not supported. Rust build scripts would be linked with the cross-compiler linker, which" + " causes the build to fail. Please upgrade your Rust version to 1.54 or newer.") +endif() + +# message(STATUS "Using Corrosion as a subdirectory") + +get_property( + RUSTC_EXECUTABLE + TARGET Rust::Rustc PROPERTY IMPORTED_LOCATION +) + +get_property( + CARGO_EXECUTABLE + TARGET Rust::Cargo PROPERTY IMPORTED_LOCATION +) + +if(Rust_TOOLCHAIN_IS_RUSTUP_MANAGED AND DEFINED Rust_RUSTUP_TOOLCHAINS) + set(corrosion_tools_rust_toolchain_docstring "Rust toolchain to use for building helper tools such as cbindgen or cxx-bridge") + if(DEFINED CORROSION_TOOLS_RUST_TOOLCHAIN) + set(cor_default_tools_toolchain "${CORROSION_TOOLS_RUST_TOOLCHAIN}") + else() + set(cor_default_tools_toolchain "${Rust_TOOLCHAIN}") + endif() + set(CORROSION_TOOLS_RUST_TOOLCHAIN "${cor_default_tools_toolchain}" CACHE STRING + "${corrosion_tools_rust_toolchain_docstring}" FORCE) + set_property(CACHE CORROSION_TOOLS_RUST_TOOLCHAIN PROPERTY STRINGS "${Rust_RUSTUP_TOOLCHAINS}") + if(NOT "$CACHE{CORROSION_TOOLS_RUST_TOOLCHAIN}" IN_LIST Rust_RUSTUP_TOOLCHAINS) + if("$CACHE{CORROSION_TOOLS_RUST_TOOLCHAIN}-${Rust_CARGO_HOST_TARGET}" IN_LIST Rust_RUSTUP_TOOLCHAINS) + set(CORROSION_TOOLS_RUST_TOOLCHAIN "$CACHE{CORROSION_TOOLS_RUST_TOOLCHAIN}-${Rust_CARGO_HOST_TARGET}" + CACHE PATH "${corrosion_tools_rust_toolchain_docstring}" FORCE) + else() + message(FATAL_ERROR "CORROSION_TOOLS_RUST_TOOLCHAIN must be set to a valid rustup managed toolchain path." + "Rust_RUSTUP_TOOLCHAINS contains a list of valid installed toolchains." + ) + endif() + endif() + foreach(toolchain tc_rustc tc_cargo IN ZIP_LISTS Rust_RUSTUP_TOOLCHAINS Rust_RUSTUP_TOOLCHAINS_RUSTC_PATH Rust_RUSTUP_TOOLCHAINS_CARGO_PATH) + if("${toolchain}" STREQUAL $CACHE{CORROSION_TOOLS_RUST_TOOLCHAIN}) + # Minimum CMake version 3.29 for `IS_EXECUTABLE`. + if(NOT (tc_cargo AND tc_rustc )) + message(FATAL_ERROR "Failed to find executable rustc or cargo for toolchain `$CACHE{CORROSION_TOOLS_RUST_TOOLCHAIN}`") + endif() + set(CORROSION_TOOLS_RUSTC "${tc_rustc}" CACHE INTERNAL "" FORCE) + set(CORROSION_TOOLS_CARGO "${tc_cargo}" CACHE INTERNAL "" FORCE) + break() + endif() + endforeach() + if(NOT DEFINED CACHE{CORROSION_TOOLS_CARGO}) + message(FATAL_ERROR "Internal error: Failed to find toolchain $CACHE{CORROSION_TOOLS_RUST_TOOLCHAIN} in " + "list of rustup managed toolchains: ${Rust_RUSTUP_TOOLCHAINS}" + ) + endif() +else() + # Fallback to the default project toolchain if rust is not rustup managed. + if(DEFINED CORROSION_TOOLS_RUST_TOOLCHAIN) + message(DEBUG "Ignoring `CORROSION_TOOLS_RUST_TOOLCHAIN=${CORROSION_TOOLS_RUST_TOOLCHAIN}` " + "since the toolchains are not rustup managed. Falling back to the default rust toolchain" + " for this project." + ) + endif() + set(CORROSION_TOOLS_RUSTC "${RUSTC_EXECUTABLE}" CACHE INTERNAL "" FORCE) + set(CORROSION_TOOLS_CARGO "${CARGO_EXECUTABLE}" CACHE INTERNAL "" FORCE) +endif() + +function(_corrosion_bin_target_suffix target_name out_var_suffix) + get_target_property(hostbuild "${target_name}" ${_CORR_PROP_HOST_BUILD}) + if((hostbuild AND CMAKE_HOST_WIN32) + OR ((NOT hostbuild) AND (Rust_CARGO_TARGET_OS STREQUAL "windows"))) + set(_suffix ".exe") + elseif(Rust_CARGO_TARGET_OS STREQUAL "vxworks") + set(_suffix ".vxe") + else() + set(_suffix "") + endif() + set(${out_var_suffix} "${_suffix}" PARENT_SCOPE) +endfunction() + +function(_handle_output_directory_genex input_path config_type output_path) + if("${config_type}" STREQUAL "") + # Prevent new path from being `dir//file`, since that causes issues with the + # file dependency. + string(REPLACE "/\$" "${config_type}" curr_out_dir "${input_path}") + string(REPLACE "\$" "${config_type}" curr_out_dir "${curr_out_dir}") + else() + string(REPLACE "\$" "${config_type}" curr_out_dir "${input_path}") + endif() + string(GENEX_STRIP "${curr_out_dir}" stripped_out_dir) + if("${stripped_out_dir}" STREQUAL "${curr_out_dir}") + set("${output_path}" "${curr_out_dir}" PARENT_SCOPE) + else() + unset("${output_path}" PARENT_SCOPE) + message(WARNING "Encountered output directory path with unsupported genex. " + "Output dir: `${curr_out_dir}`" + "Note: Corrosion only supports the `\$` generator expression for output directories.") + endif() +endfunction() + +# Do not call this function directly! +# +# This function should be called deferred to evaluate target properties late in the configure stage. +# IMPORTED_LOCATION does not support Generator expressions, so we must evaluate the output +# directory target property value at configure time. This function must be deferred to the end of +# the configure stage, so we can be sure that the output directory is not modified afterwards. +function(_corrosion_set_imported_location_deferred target_name base_property output_directory_property filename) + # The output directory property is expected to be set on the exposed target (without postfix), + # but we need to set the imported location on the actual library target with postfix. + if("${target_name}" MATCHES "^(.+)-(static|shared)$") + set(output_dir_prop_target_name "${CMAKE_MATCH_1}") + else() + set(output_dir_prop_target_name "${target_name}") + endif() + + # Append .exe suffix for executable by-products if the target is windows or if it's a host + # build and the host is Windows. + get_target_property(target_type ${target_name} TYPE) + if(${target_type} STREQUAL "EXECUTABLE" AND (NOT "${filename}" MATCHES "\.pdb$")) + _corrosion_bin_target_suffix(${target_name} "suffix") + string(APPEND filename "${suffix}") + endif() + + get_target_property(output_directory "${output_dir_prop_target_name}" "${output_directory_property}") + message(DEBUG "Output directory property (target ${output_dir_prop_target_name}): ${output_directory_property} dir: ${output_directory}") + + foreach(config_type ${CMAKE_CONFIGURATION_TYPES}) + string(TOUPPER "${config_type}" config_type_upper) + get_target_property(output_dir_curr_config ${output_dir_prop_target_name} + "${output_directory_property}_${config_type_upper}" + ) + if(output_dir_curr_config) + set(curr_out_dir "${output_dir_curr_config}") + elseif(output_directory) + string(GENEX_STRIP "${output_directory}" output_dir_no_genex) + # Only add config dir if there is no genex in here. See + # https://cmake.org/cmake/help/latest/prop_tgt/RUNTIME_OUTPUT_DIRECTORY.html + if(output_directory STREQUAL output_dir_no_genex) + set(curr_out_dir "${output_directory}/${config_type}") + else() + set(curr_out_dir "${output_directory}") + endif() + else() + set(curr_out_dir "${CMAKE_CURRENT_BINARY_DIR}") + endif() + _handle_output_directory_genex("${curr_out_dir}" "${config_type}" sanitized_out_dir) + if(NOT DEFINED sanitized_out_dir) + message(FATAL_ERROR "${output_directory_property} for target ${output_dir_prop_target_name} " + "contained an unexpected Generator expression. Output dir: `${curr_out_dir}`" + "Note: Corrosion only supports the `\$` generator expression for output directories.") + endif() + + # For Multiconfig we want to specify the correct location for each configuration + set_property( + TARGET ${target_name} + PROPERTY "${base_property}_${config_type_upper}" + "${sanitized_out_dir}/${filename}" + ) + set(base_output_directory "${sanitized_out_dir}") + endforeach() + + if(NOT COR_IS_MULTI_CONFIG) + if(output_directory) + set(base_output_directory "${output_directory}") + else() + set(base_output_directory "${CMAKE_CURRENT_BINARY_DIR}") + endif() + _handle_output_directory_genex("${base_output_directory}" "${CMAKE_BUILD_TYPE}" sanitized_output_directory) + if(NOT DEFINED sanitized_output_directory) + message(FATAL_ERROR "${output_dir_prop_target_name} for target ${output_dir_prop_target_name} " + "contained an unexpected Generator expression. Output dir: `${base_output_directory}`." + "Note: Corrosion only supports the `\$` generator expression for output directories.") + endif() + set(base_output_directory "${sanitized_output_directory}") + endif() + + message(DEBUG "Setting ${base_property} for target ${target_name}" + " to `${base_output_directory}/${filename}`.") + + # IMPORTED_LOCATION must be set regardless of possible overrides. In the multiconfig case, + # the last configuration "wins" (IMPORTED_LOCATION is not documented to have Genex support). + set_property( + TARGET ${target_name} + PROPERTY "${base_property}" "${base_output_directory}/${filename}" + ) +endfunction() + +# Set the imported location of a Rust target. +# +# Rust targets are built via custom targets / custom commands. The actual artifacts are exposed +# to CMake as imported libraries / executables that depend on the cargo_build command. For CMake +# to find the built artifact we need to set the IMPORTED location to the actual location on disk. +# Corrosion tries to copy the artifacts built by cargo to standard locations. The IMPORTED_LOCATION +# is set to point to the copy, and not the original from the cargo build directory. +# +# Parameters: +# - target_name: Name of the Rust target +# - base_property: Name of the base property - i.e. `IMPORTED_LOCATION` or `IMPORTED_IMPLIB`. +# - output_directory_property: Target property name that determines the standard location for the +# artifact. +# - filename of the artifact. +function(_corrosion_set_imported_location target_name base_property output_directory_property filename) + cmake_language(EVAL CODE " + cmake_language(DEFER + CALL + _corrosion_set_imported_location_deferred + [[${target_name}]] + [[${base_property}]] + [[${output_directory_property}]] + [[${filename}]] + ) + ") +endfunction() + +function(_corrosion_copy_byproduct_deferred target_name output_dir_prop_names cargo_build_dir file_names) + if(ARGN) + message(FATAL_ERROR "Unexpected additional arguments") + endif() + + foreach(output_dir_prop_name ${output_dir_prop_names}) + get_target_property(output_dir ${target_name} "${output_dir_prop_name}") + if(output_dir) + break() + endif() + endforeach() + + # A Genex expanding to the output directory depending on the configuration. + set(multiconfig_out_dir_genex "") + + foreach(config_type ${CMAKE_CONFIGURATION_TYPES}) + string(TOUPPER "${config_type}" config_type_upper) + foreach(output_dir_prop_name ${output_dir_prop_names}) + get_target_property(output_dir_curr_config ${target_name} "${output_dir_prop_name}_${config_type_upper}") + if(output_dir_curr_config) + break() + endif() + endforeach() + + if(output_dir_curr_config) + set(curr_out_dir "${output_dir_curr_config}") + elseif(output_dir) + string(GENEX_STRIP "${output_dir}" output_dir_no_genex) + # Only add config dir if there is no genex in here. See + # https://cmake.org/cmake/help/latest/prop_tgt/RUNTIME_OUTPUT_DIRECTORY.html + # Logic duplicated from _corrosion_set_imported_location_deferred + if(output_dir STREQUAL output_dir_no_genex) + set(curr_out_dir "${output_dir}/${config_type}") + else() + set(curr_out_dir "${output_dir}") + endif() + else() + # Fallback to the default directory. We do not append the configuration directory here + # and instead let CMake do this, since otherwise the resolving of dynamic library + # imported paths may fail. + set(curr_out_dir "${CMAKE_CURRENT_BINARY_DIR}") + endif() + set(multiconfig_out_dir_genex "${multiconfig_out_dir_genex}$<$:${curr_out_dir}>") + endforeach() + + if(COR_IS_MULTI_CONFIG) + set(output_dir "${multiconfig_out_dir_genex}") + else() + if(NOT output_dir) + # Fallback to default directory. + set(output_dir "${CMAKE_CURRENT_BINARY_DIR}") + endif() + endif() + + # Append .exe suffix for executable by-products if the target is windows or if it's a host + # build and the host is Windows. + get_target_property(target_type "${target_name}" TYPE) + if (target_type STREQUAL "EXECUTABLE") + list(LENGTH file_names list_len) + if(NOT list_len EQUAL "1") + message(FATAL_ERROR + "Internal error: Exactly one filename should be passed for executable types.") + endif() + _corrosion_bin_target_suffix(${target_name} "suffix") + if(suffix AND (NOT "${file_names}" MATCHES "\.pdb$")) + # For executable targets we know / checked that only one file will be passed. + string(APPEND file_names "${suffix}") + endif() + endif() + set(src_file_names "${file_names}") + if(Rust_CARGO_TARGET_ENV STREQUAL "gnullvm") + # Workaround for cargo not exposing implibs yet. + list(TRANSFORM src_file_names PREPEND "deps/" REGEX "\.dll\.a$") + endif() + list(TRANSFORM src_file_names PREPEND "${cargo_build_dir}/") + list(TRANSFORM file_names PREPEND "${output_dir}/" OUTPUT_VARIABLE dst_file_names) + message(DEBUG "Adding command to copy byproducts `${file_names}` to ${dst_file_names}") + add_custom_command(TARGET _cargo-build_${target_name} + POST_BUILD + # output_dir may contain a Generator expression. + COMMAND ${CMAKE_COMMAND} -E make_directory "${output_dir}" + COMMAND + ${CMAKE_COMMAND} -E copy_if_different + # tested to work with both multiple files and paths with spaces + ${src_file_names} + "${output_dir}" + BYPRODUCTS ${dst_file_names} + COMMENT "Copying byproducts `${file_names}` to ${output_dir}" + VERBATIM + COMMAND_EXPAND_LISTS + ) +endfunction() + +# Copy the artifacts generated by cargo to the appropriate destination. +# +# Parameters: +# - target_name: The name of the Rust target +# - output_dir_prop_names: The property name(s) controlling the destination (e.g. +# `LIBRARY_OUTPUT_DIRECTORY` or `PDB_OUTPUT_DIRECTORY;RUNTIME_OUTPUT_DIRECTORY`) +# - cargo_build_dir: the directory cargo build places it's output artifacts in. +# - filenames: the file names of any output artifacts as a list. +function(_corrosion_copy_byproducts target_name output_dir_prop_names cargo_build_dir file_names) + cmake_language(EVAL CODE " + cmake_language(DEFER + CALL + _corrosion_copy_byproduct_deferred + [[${target_name}]] + [[${output_dir_prop_names}]] + [[${cargo_build_dir}]] + [[${file_names}]] + ) + ") +endfunction() + + +# Add targets for the static and/or shared libraries of the rust target. +# The generated byproduct names are returned via the `OUT__BYPRODUCTS` arguments. +function(_corrosion_add_library_target) + set(OPTIONS "") + set(ONE_VALUE_KEYWORDS + WORKSPACE_MANIFEST_PATH + TARGET_NAME + OUT_ARCHIVE_OUTPUT_BYPRODUCTS + OUT_SHARED_LIB_BYPRODUCTS + OUT_PDB_BYPRODUCT + ) + set(MULTI_VALUE_KEYWORDS LIB_KINDS) + cmake_parse_arguments(PARSE_ARGV 0 CALT "${OPTIONS}" "${ONE_VALUE_KEYWORDS}" "${MULTI_VALUE_KEYWORDS}") + + if(DEFINED CALT_UNPARSED_ARGUMENTS) + message(FATAL_ERROR "Internal error - unexpected arguments: ${CALT_UNPARSED_ARGUMENTS}") + elseif(DEFINED CALT_KEYWORDS_MISSING_VALUES) + message(FATAL_ERROR "Internal error - the following keywords had no associated value(s):" + "${CALT_KEYWORDS_MISSING_VALUES}") + endif() + list(TRANSFORM ONE_VALUE_KEYWORDS PREPEND CALT_ OUTPUT_VARIABLE required_arguments) + foreach(required_argument ${required_arguments} ) + if(NOT DEFINED "${required_argument}") + message(FATAL_ERROR "Internal error: Missing required argument ${required_argument}." + "Complete argument list: ${ARGN}" + ) + endif() + endforeach() + if("staticlib" IN_LIST CALT_LIB_KINDS) + set(has_staticlib TRUE) + endif() + if("cdylib" IN_LIST CALT_LIB_KINDS) + set(has_cdylib TRUE) + endif() + + if(NOT (has_staticlib OR has_cdylib)) + message(FATAL_ERROR "Unknown library type(s): ${CALT_LIB_KINDS}") + endif() + set(workspace_manifest_path "${CALT_WORKSPACE_MANIFEST_PATH}") + set(target_name "${CALT_TARGET_NAME}") + + set(is_windows "") + set(is_windows_gnu "") + set(is_windows_msvc "") + set(is_macos "") + if(Rust_CARGO_TARGET_OS STREQUAL "windows") + set(is_windows TRUE) + if(Rust_CARGO_TARGET_ENV STREQUAL "msvc") + set(is_windows_msvc TRUE) + elseif(Rust_CARGO_TARGET_ENV STREQUAL "gnu" OR Rust_CARGO_TARGET_ENV STREQUAL "gnullvm") + set(is_windows_gnu TRUE) + endif() + elseif(Rust_CARGO_TARGET_OS STREQUAL "darwin") + set(is_macos TRUE) + endif() + + # target file names + string(REPLACE "-" "_" lib_name "${target_name}") + + if(is_windows_msvc) + set(static_lib_name "${lib_name}.lib") + else() + set(static_lib_name "lib${lib_name}.a") + endif() + + if(is_windows) + set(dynamic_lib_name "${lib_name}.dll") + elseif(is_macos) + set(dynamic_lib_name "lib${lib_name}.dylib") + else() + set(dynamic_lib_name "lib${lib_name}.so") + endif() + + if(is_windows_msvc) + set(implib_name "${lib_name}.dll.lib") + elseif(is_windows_gnu) + set(implib_name "lib${lib_name}.dll.a") + elseif(is_windows) + message(FATAL_ERROR "Unknown windows environment - Can't determine implib name") + endif() + + + set(pdb_name "${lib_name}.pdb") + + set(archive_output_byproducts "") + if(has_staticlib) + list(APPEND archive_output_byproducts ${static_lib_name}) + endif() + + if(has_cdylib) + set("${CALT_OUT_SHARED_LIB_BYPRODUCTS}" "${dynamic_lib_name}" PARENT_SCOPE) + if(is_windows) + list(APPEND archive_output_byproducts ${implib_name}) + endif() + if(is_windows_msvc) + set("${CALT_OUT_PDB_BYPRODUCT}" "${pdb_name}" PARENT_SCOPE) + endif() + endif() + set("${CALT_OUT_ARCHIVE_OUTPUT_BYPRODUCTS}" "${archive_output_byproducts}" PARENT_SCOPE) + + if(has_staticlib) + add_library(${target_name}-static STATIC IMPORTED GLOBAL) + add_dependencies(${target_name}-static cargo-build_${target_name}) + set_target_properties(${target_name}-static PROPERTIES COR_FILE_NAME ${static_lib_name}) + + _corrosion_set_imported_location("${target_name}-static" "IMPORTED_LOCATION" + "ARCHIVE_OUTPUT_DIRECTORY" + "${static_lib_name}") + + # Todo: NO_STD target property? + if(NOT COR_NO_STD) + set_property( + TARGET ${target_name}-static + PROPERTY INTERFACE_LINK_LIBRARIES ${Rust_CARGO_TARGET_LINK_NATIVE_LIBS} + ) + set_property( + TARGET ${target_name}-static + PROPERTY INTERFACE_LINK_OPTIONS ${Rust_CARGO_TARGET_LINK_OPTIONS} + ) + if(is_macos) + set_property(TARGET ${target_name}-static + PROPERTY INTERFACE_LINK_DIRECTORIES "/Library/Developer/CommandLineTools/SDKs/MacOSX.sdk/usr/lib" + ) + endif() + endif() + endif() + + if(has_cdylib) + add_library(${target_name}-shared SHARED IMPORTED GLOBAL) + add_dependencies(${target_name}-shared cargo-build_${target_name}) + set_target_properties(${target_name}-shared PROPERTIES COR_FILE_NAME ${dynamic_lib_name}) + + # Todo: (Not new issue): What about IMPORTED_SONAME and IMPORTED_NO_SYSTEM? + _corrosion_set_imported_location("${target_name}-shared" "IMPORTED_LOCATION" + "LIBRARY_OUTPUT_DIRECTORY" + "${dynamic_lib_name}" + ) + # In the future we would probably prefer to let Rust set the soname for packages >= 1.0. + # This is tracked in issue #333. + set_target_properties(${target_name}-shared PROPERTIES IMPORTED_NO_SONAME TRUE) + + if(is_windows) + _corrosion_set_imported_location("${target_name}-shared" "IMPORTED_IMPLIB" + "ARCHIVE_OUTPUT_DIRECTORY" + "${implib_name}" + ) + set_target_properties(${target_name}-shared PROPERTIES COR_IMPLIB_FILE_NAME ${implib_name}) + endif() + + if(is_macos) + set_property(TARGET ${target_name}-shared + PROPERTY INTERFACE_LINK_DIRECTORIES "/Library/Developer/CommandLineTools/SDKs/MacOSX.sdk/usr/lib" + ) + endif() + endif() + + if(has_cdylib AND has_staticlib) + if(BUILD_SHARED_LIBS) + target_link_libraries(${target_name} INTERFACE ${target_name}-shared) + else() + target_link_libraries(${target_name} INTERFACE ${target_name}-static) + endif() + elseif(has_cdylib) + target_link_libraries(${target_name} INTERFACE ${target_name}-shared) + else() + target_link_libraries(${target_name} INTERFACE ${target_name}-static) + endif() +endfunction() + +function(_corrosion_add_bin_target workspace_manifest_path bin_name out_bin_byproduct out_pdb_byproduct) + if(NOT bin_name) + message(FATAL_ERROR "No bin_name in _corrosion_add_bin_target for target ${target_name}") + endif() + + string(REPLACE "-" "_" bin_name_underscore "${bin_name}") + + set(pdb_name "${bin_name_underscore}.pdb") + + if(Rust_CARGO_TARGET_ENV STREQUAL "msvc") + set(${out_pdb_byproduct} "${pdb_name}" PARENT_SCOPE) + endif() + + # Potential .exe suffix will be added later, also depending on possible hostbuild + # target property + set(bin_filename "${bin_name}") + set(${out_bin_byproduct} "${bin_filename}" PARENT_SCOPE) + add_dependencies(${bin_name} cargo-build_${bin_name}) + + if(Rust_CARGO_TARGET_OS STREQUAL "darwin") + set_property(TARGET ${bin_name} + PROPERTY INTERFACE_LINK_DIRECTORIES "/Library/Developer/CommandLineTools/SDKs/MacOSX.sdk/usr/lib" + ) + endif() + + _corrosion_set_imported_location("${bin_name}" "IMPORTED_LOCATION" + "RUNTIME_OUTPUT_DIRECTORY" + "${bin_filename}" + ) + +endfunction() + + +include(CorrosionGenerator) + +# Note: `cmake_language(GET_MESSAGE_LOG_LEVEL )` requires CMake 3.25, +# so we offer our own option to control verbosity of downstream commands (e.g. cargo build) +if (CORROSION_VERBOSE_OUTPUT) + set(_CORROSION_VERBOSE_OUTPUT_FLAG --verbose CACHE INTERNAL "") +else() + # We want to silence some less important commands by default. + set(_CORROSION_QUIET_OUTPUT_FLAG --quiet CACHE INTERNAL "") +endif() + +set(_CORROSION_CARGO_VERSION ${Rust_CARGO_VERSION} CACHE INTERNAL "cargo version used by corrosion") +set(_CORROSION_RUST_CARGO_TARGET ${Rust_CARGO_TARGET} CACHE INTERNAL "target triple used by corrosion") +set(_CORROSION_RUST_CARGO_HOST_TARGET ${Rust_CARGO_HOST_TARGET} CACHE INTERNAL "host triple used by corrosion") +set(_CORROSION_RUSTC "${RUSTC_EXECUTABLE}" CACHE INTERNAL "Path to rustc used by corrosion") +set(_CORROSION_CARGO "${CARGO_EXECUTABLE}" CACHE INTERNAL "Path to cargo used by corrosion") + +string(REPLACE "-" "_" _CORROSION_RUST_CARGO_TARGET_UNDERSCORE "${Rust_CARGO_TARGET}") +set(_CORROSION_RUST_CARGO_TARGET_UNDERSCORE "${_CORROSION_RUST_CARGO_TARGET_UNDERSCORE}" CACHE INTERNAL "lowercase target triple with underscores") +string(TOUPPER "${_CORROSION_RUST_CARGO_TARGET_UNDERSCORE}" _CORROSION_TARGET_TRIPLE_UPPER) +set(_CORROSION_RUST_CARGO_TARGET_UPPER + "${_CORROSION_TARGET_TRIPLE_UPPER}" + CACHE INTERNAL + "target triple in uppercase with underscore" +) + +# We previously specified some Custom properties as part of our public API, however the chosen names prevented us from +# supporting CMake versions before 3.19. In order to both support older CMake versions and not break existing code +# immediately, we are using a different property name depending on the CMake version. However users avoid using +# any of the properties directly, as they are no longer part of the public API and are to be considered deprecated. +# Instead use the corrosion_set_... functions as documented in the Readme. +set(_CORR_PROP_FEATURES CORROSION_FEATURES CACHE INTERNAL "") +set(_CORR_PROP_ALL_FEATURES CORROSION_ALL_FEATURES CACHE INTERNAL "") +set(_CORR_PROP_NO_DEFAULT_FEATURES CORROSION_NO_DEFAULT_FEATURES CACHE INTERNAL "") +set(_CORR_PROP_ENV_VARS CORROSION_ENVIRONMENT_VARIABLES CACHE INTERNAL "") +set(_CORR_PROP_HOST_BUILD CORROSION_USE_HOST_BUILD CACHE INTERNAL "") + +# Add custom command to build one target in a package (crate) +# +# A target may be either a specific bin +function(_add_cargo_build out_cargo_build_out_dir) + set(options NO_LINKER_OVERRIDE) + set(one_value_args PACKAGE TARGET MANIFEST_PATH WORKSPACE_MANIFEST_PATH) + set(multi_value_args BYPRODUCTS TARGET_KINDS) + cmake_parse_arguments( + ACB + "${options}" + "${one_value_args}" + "${multi_value_args}" + ${ARGN} + ) + + if(DEFINED ACB_UNPARSED_ARGUMENTS) + message(FATAL_ERROR "Internal error - unexpected arguments: " + ${ACB_UNPARSED_ARGUMENTS}) + elseif(DEFINED ACB_KEYWORDS_MISSING_VALUES) + message(FATAL_ERROR "Internal error - missing values for the following arguments: " + ${ACB_KEYWORDS_MISSING_VALUES}) + endif() + + set(package_name "${ACB_PACKAGE}") + set(target_name "${ACB_TARGET}") + set(path_to_toml "${ACB_MANIFEST_PATH}") + set(target_kinds "${ACB_TARGET_KINDS}") + set(workspace_manifest_path "${ACB_WORKSPACE_MANIFEST_PATH}") + set(build_byproducts "${ACB_BYPRODUCTS}") + + unset(cargo_rustc_crate_types) + if(NOT target_kinds) + message(FATAL_ERROR "TARGET_KINDS not specified") + elseif("staticlib" IN_LIST target_kinds OR "cdylib" IN_LIST target_kinds) + set(cargo_rustc_filter "--lib") + if("${Rust_VERSION}" VERSION_GREATER_EQUAL "1.64") + # https://doc.rust-lang.org/1.64.0/cargo/commands/cargo-rustc.html + # `--crate-type` is documented since Rust 1.64 for `cargo rustc`. + # We just unconditionally set it when available, to support overriding the crate type. + # Due to https://github.com/rust-lang/cargo/issues/14498 we can't use one argument and pass a + # comma seperated list. Instead we use multiple arguments. + set(cargo_rustc_crate_types "${target_kinds}") + list(TRANSFORM cargo_rustc_crate_types PREPEND "--crate-type=") + endif() + elseif("bin" IN_LIST target_kinds) + set(cargo_rustc_filter "--bin=${target_name}") + else() + message(FATAL_ERROR "TARGET_KINDS contained unknown kind `${target_kind}`") + endif() + + if (NOT IS_ABSOLUTE "${path_to_toml}") + set(path_to_toml "${CMAKE_SOURCE_DIR}/${path_to_toml}") + endif() + get_filename_component(workspace_toml_dir ${path_to_toml} DIRECTORY ) + + if (CMAKE_VS_PLATFORM_NAME) + set(build_dir "${CMAKE_VS_PLATFORM_NAME}/$") + elseif(COR_IS_MULTI_CONFIG) + set(build_dir "$") + else() + unset(build_dir) + endif() + + # If a CMake sysroot is specified, forward it to the linker rustc invokes, too. CMAKE_SYSROOT is documented + # to be passed via --sysroot, so we assume that when it's set, the linker supports this option in that style. + if(CMAKE_CROSSCOMPILING AND CMAKE_SYSROOT) + set(corrosion_link_args "--sysroot=${CMAKE_SYSROOT}") + endif() + + if(COR_ALL_FEATURES) + set(all_features_arg --all-features) + endif() + if(COR_NO_DEFAULT_FEATURES) + set(no_default_features_arg --no-default-features) + endif() + if(COR_NO_USES_TERMINAL) + unset(cor_uses_terminal) + else() + set(cor_uses_terminal USES_TERMINAL) + endif() + + set(global_rustflags_target_property "$>") + set(local_rustflags_target_property "$>") + + # todo: this probably should be TARGET_GENEX_EVAL + set(features_target_property "$>") + set(features_genex "$<$:--features=$>>") + + # target property overrides corrosion_import_crate argument + set(all_features_target_property "$>") + set(all_features_arg "$<$:--all-features>") + + set(no_default_features_target_property "$>") + set(no_default_features_arg "$<$:--no-default-features>") + + set(build_env_variable_genex "$>") + set(hostbuild_override "$>") + set(if_not_host_build_condition "$") + + set(corrosion_link_args "$<${if_not_host_build_condition}:${corrosion_link_args}>") + # We always set `--target`, so that cargo always places artifacts into a directory with the + # target triple. + set(cargo_target_option "--target=$") + + # The target may be a filepath to custom target json file. For host targets we assume that they are built-in targets. + _corrosion_strip_target_triple("${_CORROSION_RUST_CARGO_TARGET}" stripped_target_triple) + _corrosion_strip_target_triple("${_CORROSION_RUST_CARGO_TARGET_UPPER}" stripped_target_triple_upper) + set(target_artifact_dir "$") + + set(flags_genex "$>") + + set(explicit_linker_property "$") + set(explicit_linker_defined "$") + + set(cargo_profile_target_property "$>") + + # Option to override the rustc/cargo binary to something other than the global default + set(rustc_override "$") + set(cargo_override "$") + set(rustc_bin "$,${rustc_override},${_CORROSION_RUSTC}>") + set(cargo_bin "$,${cargo_override},${_CORROSION_CARGO}>") + + + # Rust will add `-lSystem` as a flag for the linker on macOS. Adding the -L flag via RUSTFLAGS only fixes the + # problem partially - buildscripts still break, since they won't receive the RUSTFLAGS. This seems to only be a + # problem if we specify the linker ourselves (which we do, since this is necessary for e.g. linking C++ code). + # We can however set `LIBRARY_PATH`, which is propagated to the build-script-build properly. + if(NOT CMAKE_CROSSCOMPILING AND CMAKE_SYSTEM_NAME STREQUAL "Darwin") + # not needed anymore on macos 13 (and causes issues) + if(${CMAKE_SYSTEM_VERSION} VERSION_LESS 22) + set(cargo_library_path "LIBRARY_PATH=/Library/Developer/CommandLineTools/SDKs/MacOSX.sdk/usr/lib") + endif() + elseif(CMAKE_CROSSCOMPILING AND CMAKE_HOST_SYSTEM_NAME STREQUAL "Darwin") + if(${CMAKE_HOST_SYSTEM_VERSION} VERSION_LESS 22) + set(cargo_library_path "$<${hostbuild_override}:LIBRARY_PATH=/Library/Developer/CommandLineTools/SDKs/MacOSX.sdk/usr/lib>") + endif() + endif() + + set(cargo_profile_set "$") + # In the default case just specify --release or nothing to stay compatible with + # older rust versions. + set(default_profile_option "$<$,$>>:--release>") + # evaluates to either `--profile=`, `--release` or nothing (for debug). + set(cargo_profile "$") + + # If the profile name is `dev` change the dir name to `debug`. + set(is_dev_profile "$") + set(profile_dir_override "$<${is_dev_profile}:debug>") + set(profile_dir_is_overridden "$") + set(custom_profile_build_type_dir "$") + + set(default_build_type_dir "$,$>,debug,release>") + set(build_type_dir "$") + + # We set a target folder based on the manifest path so if you build multiple workspaces (or standalone projects + # without workspace) they won't collide if they use a common dependency. This would confuse cargo and trigger + # unnecessary rebuilds + cmake_path(GET workspace_manifest_path PARENT_PATH parent_path) + cmake_path(GET parent_path PARENT_PATH grandparent_path) + string(REPLACE "${grandparent_path}/" "" cargo_folder_name "${parent_path}") + string(SHA1 cargo_path_hash ${workspace_manifest_path}) + # Include a hash of the full path in case there are multiple projects with the same folder name + string(SUBSTRING "${cargo_path_hash}" 0 5 cargo_path_hash) + cmake_path(APPEND CMAKE_BINARY_DIR ${build_dir} cargo "${cargo_folder_name}_${cargo_path_hash}" + OUTPUT_VARIABLE cargo_target_dir) + set(cargo_build_dir "${cargo_target_dir}/${target_artifact_dir}/${build_type_dir}") + set("${out_cargo_build_out_dir}" "${cargo_build_dir}" PARENT_SCOPE) + + set(corrosion_cc_rs_flags) + + if(CMAKE_C_COMPILER) + # This variable is read by cc-rs (often used in build scripts) to determine the c-compiler. + # It can still be overridden if the user sets the non underscore variant via the environment variables + # on the target. + list(APPEND corrosion_cc_rs_flags "CC_${stripped_target_triple}=${CMAKE_C_COMPILER}") + endif() + if(CMAKE_CXX_COMPILER) + list(APPEND corrosion_cc_rs_flags "CXX_${stripped_target_triple}=${CMAKE_CXX_COMPILER}") + endif() + # cc-rs doesn't seem to support `llvm-ar` (commandline syntax), wo we might as well just use + # the default AR. + if(CMAKE_AR AND NOT (Rust_CARGO_TARGET_ENV STREQUAL "msvc")) + list(APPEND corrosion_cc_rs_flags "AR_${stripped_target_triple}=${CMAKE_AR}") + endif() + + # Since we instruct cc-rs to use the compiler found by CMake, it is likely one that requires also + # specifying the target sysroot to use. CMake's generator makes sure to pass --sysroot with + # CMAKE_OSX_SYSROOT. Fortunately the compilers Apple ships also respect the SDKROOT environment + # variable, which we can set for use when cc-rs invokes the compiler. + if(CMAKE_HOST_SYSTEM_NAME STREQUAL "Darwin" AND CMAKE_OSX_SYSROOT) + list(APPEND corrosion_cc_rs_flags "SDKROOT=${CMAKE_OSX_SYSROOT}") + endif() + + # Ensure that cc-rs targets same Apple platform version as the CMake build + if(CMAKE_SYSTEM_NAME STREQUAL "Darwin" AND CMAKE_OSX_DEPLOYMENT_TARGET) + list(APPEND corrosion_cc_rs_flags "MACOSX_DEPLOYMENT_TARGET=${CMAKE_OSX_DEPLOYMENT_TARGET}") + endif() + + corrosion_add_target_local_rustflags("${target_name}" "$<$:-Clink-args=${corrosion_link_args}>") + + # todo: this should probably also be guarded by if_not_host_build_condition. + if(COR_NO_STD) + corrosion_add_target_local_rustflags("${target_name}" "-Cdefault-linker-libraries=no") + else() + corrosion_add_target_local_rustflags("${target_name}" "-Cdefault-linker-libraries=yes") + endif() + + set(global_joined_rustflags "$") + set(global_rustflags_genex "$<$:RUSTFLAGS=${global_joined_rustflags}>") + set(local_rustflags_delimiter "$<$:-->") + set(local_rustflags_genex "$<$:${local_rustflags_target_property}>") + + set(deps_link_languages_prop "$") + set(deps_link_languages "$") + set(target_uses_cxx "$") + unset(default_linker) + # With the MSVC ABI rustc only supports directly invoking the linker - Invoking cl as the linker driver is not supported. + if(NOT (Rust_CARGO_TARGET_ENV STREQUAL "msvc" OR COR_NO_LINKER_OVERRIDE)) + set(default_linker "$,${CMAKE_CXX_COMPILER},${CMAKE_C_COMPILER}>") + endif() + # Used to set a linker for a specific target-triple. + set(cargo_target_linker_var "CARGO_TARGET_${stripped_target_triple_upper}_LINKER") + set(linker "$") + set(cargo_target_linker $<$:${cargo_target_linker_var}=${linker}>) + + if(Rust_CROSSCOMPILING AND (CMAKE_C_COMPILER_TARGET OR CMAKE_CXX_COMPILER_TARGET)) + set(linker_target_triple "$,${CMAKE_CXX_COMPILER_TARGET},${CMAKE_C_COMPILER_TARGET}>") + set(rustflag_linker_arg "-Clink-args=--target=${linker_target_triple}") + set(rustflag_linker_arg "$<${if_not_host_build_condition}:${rustflag_linker_arg}>") + # Skip adding the linker argument, if the linker is explicitly set, since the + # explicit_linker_property will not be set when this function runs. + # Passing this rustflag is necessary for clang. + corrosion_add_target_local_rustflags("${target_name}" "$<$:${rustflag_linker_arg}>") + endif() + + message(DEBUG "TARGET ${target_name} produces byproducts ${build_byproducts}") + message(DEBUG "corrosion_cc_rs_flags: ${corrosion_cc_rs_flags}") + + add_custom_target( + _cargo-build_${target_name} + # Build crate + COMMAND + ${CMAKE_COMMAND} -E env + "${build_env_variable_genex}" + "${global_rustflags_genex}" + "${cargo_target_linker}" + "${corrosion_cc_rs_flags}" + "${cargo_library_path}" + "CORROSION_BUILD_DIR=${CMAKE_CURRENT_BINARY_DIR}" + "CARGO_BUILD_RUSTC=${rustc_bin}" + "${cargo_bin}" + rustc + ${cargo_rustc_filter} + ${cargo_target_option} + ${_CORROSION_VERBOSE_OUTPUT_FLAG} + ${all_features_arg} + ${no_default_features_arg} + ${features_genex} + --package ${package_name} + ${cargo_rustc_crate_types} + --manifest-path "${path_to_toml}" + --target-dir "${cargo_target_dir}" + ${cargo_profile} + ${flags_genex} + # Any arguments to cargo must be placed before this line + ${local_rustflags_delimiter} + ${local_rustflags_genex} + + # Note: `BYPRODUCTS` may not contain **target specific** generator expressions. + # This means we cannot use `${cargo_build_dir}`, since it currently uses `$` + # to determine the correct target directory, depending on if the hostbuild target property is + # set or not. + # BYPRODUCTS "${cargo_build_dir}/${build_byproducts}" + # The build is conducted in the directory of the Manifest, so that configuration files such as + # `.cargo/config.toml` or `toolchain.toml` are applied as expected. + WORKING_DIRECTORY "${workspace_toml_dir}" + ${cor_uses_terminal} + COMMAND_EXPAND_LISTS + VERBATIM + ) + + # User exposed custom target, that depends on the internal target. + # Corrosion post build steps are added on the internal target, which + # ensures that they run before any user defined post build steps on this + # target. + add_custom_target( + cargo-build_${target_name} + ALL + ) + add_dependencies(cargo-build_${target_name} _cargo-build_${target_name}) + + # Add custom target before actual build that user defined custom commands (e.g. code generators) can + # use as a hook to do something before the build. This mainly exists to not expose the `_cargo-build` targets. + add_custom_target(cargo-prebuild_${target_name}) + add_dependencies(_cargo-build_${target_name} cargo-prebuild_${target_name}) + if(NOT TARGET cargo-prebuild) + add_custom_target(cargo-prebuild) + endif() + add_dependencies(cargo-prebuild cargo-prebuild_${target_name}) + + add_custom_target( + cargo-clean_${target_name} + COMMAND + "${cargo_bin}" clean ${cargo_target_option} + -p ${package_name} --manifest-path "${path_to_toml}" + WORKING_DIRECTORY "${CMAKE_BINARY_DIR}/${build_dir}" + ${cor_uses_terminal} + ) + + if (NOT TARGET cargo-clean) + add_custom_target(cargo-clean) + endif() + add_dependencies(cargo-clean cargo-clean_${target_name}) +endfunction() + +#[=======================================================================[.md: +ANCHOR: corrosion-import-crate +```cmake +corrosion_import_crate( + MANIFEST_PATH + [ALL_FEATURES] + [NO_DEFAULT_FEATURES] + [NO_STD] + [NO_LINKER_OVERRIDE] + [NO_USES_TERMINAL] + [LOCKED] + [FROZEN] + [PROFILE ] + [IMPORTED_CRATES ] + [CRATE_TYPES ... ] + [OVERRIDE_CRATE_TYPE = ...] + [CRATES ... ] + [FEATURES ... ] + [FLAGS ... ] +) +``` +* **MANIFEST_PATH**: Path to a [Cargo.toml Manifest] file. +* **ALL_FEATURES**: Equivalent to [--all-features] passed to cargo build +* **NO_DEFAULT_FEATURES**: Equivalent to [--no-default-features] passed to cargo build +* **NO_STD**: Disable linking of standard libraries (required for no_std crates). +* **NO_LINKER_OVERRIDE**: Will let Rust/Cargo determine which linker to use instead of corrosion (when linking is invoked by Rust) +* **NO_USES_TERMINAL**: Don't pass the `USES_TERMINAL` flag when creating the custom CMake targets. +* **LOCKED**: Pass [`--locked`] to cargo build and cargo metadata. +* **FROZEN**: Pass [`--frozen`] to cargo build and cargo metadata. +* **PROFILE**: Specify cargo build profile (`dev`/`release` or a [custom profile]; `bench` and `test` are not supported) +* **IMPORTED_CRATES**: Save the list of imported crates into the variable with the provided name in the current scope. +* **CRATE_TYPES**: Only import the specified crate types. Valid values: `staticlib`, `cdylib`, `bin`. +* **OVERRIDE_CRATE_TYPE**: Override the crate-types of a cargo crate with the given comma-separated values. + Internally uses the `rustc` flag [`--crate-type`] to override the crate-type. + Valid values for the crate types are the library types `staticlib` and `cdylib`. +* **CRATES**: Only import the specified crates from a workspace. Values: Crate names. +* **FEATURES**: Enable the specified features. Equivalent to [--features] passed to `cargo build`. +* **FLAGS**: Arbitrary flags to `cargo build`. + +[custom profile]: https://doc.rust-lang.org/cargo/reference/profiles.html#custom-profiles +[--all-features]: https://doc.rust-lang.org/cargo/reference/features.html#command-line-feature-options +[--no-default-features]: https://doc.rust-lang.org/cargo/reference/features.html#command-line-feature-options +[--features]: https://doc.rust-lang.org/cargo/reference/features.html#command-line-feature-options +[`--locked`]: https://doc.rust-lang.org/cargo/commands/cargo.html#manifest-options +[`--frozen`]: https://doc.rust-lang.org/cargo/commands/cargo.html#manifest-options +[`--crate-type`]: https://doc.rust-lang.org/rustc/command-line-arguments.html#--crate-type-a-list-of-types-of-crates-for-the-compiler-to-emit +[Cargo.toml Manifest]: https://doc.rust-lang.org/cargo/appendix/glossary.html#manifest + +ANCHOR_END: corrosion-import-crate +#]=======================================================================] +function(corrosion_import_crate) + set(OPTIONS + ALL_FEATURES + NO_DEFAULT_FEATURES + NO_STD + NO_LINKER_OVERRIDE + NO_USES_TERMINAL + LOCKED + FROZEN) + set(ONE_VALUE_KEYWORDS MANIFEST_PATH PROFILE IMPORTED_CRATES) + set(MULTI_VALUE_KEYWORDS CRATE_TYPES CRATES FEATURES FLAGS OVERRIDE_CRATE_TYPE) + cmake_parse_arguments(COR "${OPTIONS}" "${ONE_VALUE_KEYWORDS}" "${MULTI_VALUE_KEYWORDS}" ${ARGN}) + list(APPEND CMAKE_MESSAGE_CONTEXT "corrosion_import_crate") + + if(DEFINED COR_UNPARSED_ARGUMENTS) + message(AUTHOR_WARNING "Unexpected arguments: " ${COR_UNPARSED_ARGUMENTS} + "\nCorrosion will ignore these unexpected arguments." + ) + endif() + if(DEFINED COR_KEYWORDS_MISSING_VALUES) + message(DEBUG "Note: the following keywords passed to corrosion_import_crate had no associated value(s): " + ${COR_KEYWORDS_MISSING_VALUES} + ) + endif() + if (NOT DEFINED COR_MANIFEST_PATH) + message(FATAL_ERROR "MANIFEST_PATH is a required keyword to corrosion_add_crate") + endif() + _corrosion_option_passthrough_helper(NO_LINKER_OVERRIDE COR no_linker_override) + _corrosion_option_passthrough_helper(LOCKED COR locked) + _corrosion_option_passthrough_helper(FROZEN COR frozen) + _corrosion_arg_passthrough_helper(CRATES COR crate_allowlist) + _corrosion_arg_passthrough_helper(CRATE_TYPES COR crate_types) + + if(COR_PROFILE) + if(Rust_VERSION VERSION_LESS 1.57.0) + message(FATAL_ERROR "Selecting custom profiles via `PROFILE` requires at least rust 1.57.0, but you " + "have ${Rust_VERSION}." + ) + # The profile name could be part of a Generator expression, so this won't catch all occurences. + # Since it is hard to add an error message for genex, we don't do that here. + elseif("${COR_PROFILE}" STREQUAL "test" OR "${COR_PROFILE}" STREQUAL "bench") + message(FATAL_ERROR "Corrosion does not support building Rust crates with the cargo profiles" + " `test` or `bench`. These profiles add a hash to the output artifact name that we" + " cannot predict. Please consider using a custom cargo profile which inherits from the" + " built-in profile instead." + ) + endif() + endif() + + # intended to be used with foreach(... ZIP_LISTS ...), meaning + # that the crate_types at index i of `override_crate_type_types_list` are + # for the package_name at index i of `override_crate_type_package_name_list`. + # It would really be nice if CMake had structs or dicts. + unset(override_crate_type_package_name_list) + unset(override_crate_type_types_list) + unset(OVERRIDE_CRATE_TYPE_ARGS) + if(DEFINED COR_OVERRIDE_CRATE_TYPE) + string(JOIN " " usage_help + "Each argument to OVERRIDE_CRATE_TYPE must be of the form `=." + "The package_name must be a valid cargo package name and the crate_type must be " + "a comma-seperated list with valid values being `staticlib`, `cdylib` and `bin`" + ) + foreach(entry IN LISTS COR_OVERRIDE_CRATE_TYPE) + string(REPLACE "=" ";" key_val_list ${entry}) + list(LENGTH key_val_list key_val_list_len) + if(NOT key_val_list_len EQUAL "2") + message(FATAL_ERROR "Invalid argument: `${entry}` for parameter OVERRIDE_CRATE_TYPE!\n" + "${usage_help}" + ) + endif() + list(GET key_val_list "0" package_name) + list(GET key_val_list "1" crate_types) + list(APPEND override_crate_type_package_name_list "${package_name}") + list(APPEND override_crate_type_types_list "${crate_types}") + endforeach() + list(LENGTH override_crate_type_package_name_list num_override_packages) + list(LENGTH override_crate_type_types_list num_override_packages2) + if("${Rust_VERSION}" VERSION_LESS "1.64") + message(WARNING "OVERRIDE_CRATE_TYPE requires at Rust 1.64 or newer. Ignoring the option") + elseif(NOT num_override_packages EQUAL num_override_packages2) + message(WARNING "Internal error while parsing OVERRIDE_CRATE_TYPE arguments.\n" + "Corrosion will ignore this argument and continue." + ) + else() + # Pass by ref: we intentionally pass the list names here! + set(override_crate_types_arg "OVERRIDE_CRATE_TYPE_ARGS" "override_crate_type_package_name_list" "override_crate_type_types_list") + endif() + endif() + + if (NOT IS_ABSOLUTE "${COR_MANIFEST_PATH}") + set(COR_MANIFEST_PATH ${CMAKE_CURRENT_SOURCE_DIR}/${COR_MANIFEST_PATH}) + endif() + + set(additional_cargo_flags ${COR_FLAGS}) + + if(COR_LOCKED AND NOT "--locked" IN_LIST additional_cargo_flags) + list(APPEND additional_cargo_flags "--locked") + endif() + if(COR_FROZEN AND NOT "--frozen" IN_LIST additional_cargo_flags) + list(APPEND additional_cargo_flags "--frozen") + endif() + + set(imported_crates "") + + _generator_add_cargo_targets( + MANIFEST_PATH + "${COR_MANIFEST_PATH}" + IMPORTED_CRATES + imported_crates + ${crate_allowlist} + ${crate_types} + ${no_linker_override} + ${override_crate_types_arg} + ) + + # Not target props yet: + # NO_STD + # NO_LINKER_OVERRIDE # We could simply zero INTERFACE_CORROSION_LINKER if this is set. + # LOCKED / FROZEN get merged into FLAGS after cargo metadata. + + # Initialize the target properties with the arguments to corrosion_import_crate. + set_target_properties( + ${imported_crates} + PROPERTIES + "${_CORR_PROP_ALL_FEATURES}" "${COR_ALL_FEATURES}" + "${_CORR_PROP_NO_DEFAULT_FEATURES}" "${COR_NO_DEFAULT_FEATURES}" + "${_CORR_PROP_FEATURES}" "${COR_FEATURES}" + INTERFACE_CORROSION_CARGO_PROFILE "${COR_PROFILE}" + INTERFACE_CORROSION_CARGO_FLAGS "${additional_cargo_flags}" + ) + + # _CORR_PROP_ENV_VARS + if(DEFINED COR_IMPORTED_CRATES) + set(${COR_IMPORTED_CRATES} ${imported_crates} PARENT_SCOPE) + endif() +endfunction() + +function(corrosion_set_linker target_name linker) + if(NOT linker) + message(FATAL_ERROR "The linker passed to `corrosion_set_linker` may not be empty") + elseif(NOT TARGET "${target_name}") + message(FATAL_ERROR "The target `${target_name}` does not exist.") + endif() + if(MSVC) + message(WARNING "Explicitly setting the linker with the MSVC toolchain is currently not supported and ignored") + endif() + + if(TARGET "${target_name}-static" AND NOT TARGET "${target_name}-shared") + message(WARNING "The target ${target_name} builds a static library." + "The linker is never invoked for a static library so specifying a linker has no effect." + ) + endif() + + set_property( + TARGET ${target_name} + PROPERTY INTERFACE_CORROSION_LINKER "${linker}" + ) +endfunction() + +function(corrosion_set_hostbuild target_name) + # Configure the target to be compiled for the Host target and ignore any cross-compile configuration. + set_property( + TARGET ${target_name} + PROPERTY ${_CORR_PROP_HOST_BUILD} 1 + ) +endfunction() + +# Add flags for rustc (RUSTFLAGS) which affect the target and all of it's Rust dependencies +# +# Additional rustflags may be passed as optional parameters after rustflag. +# Please note, that if you import multiple targets from a package or workspace, but set different +# Rustflags via this function, the Rust dependencies will have to be rebuilt when changing targets. +# Consider `corrosion_add_target_local_rustflags()` as an alternative to avoid this. +function(corrosion_add_target_rustflags target_name rustflag) + # Additional rustflags may be passed as optional parameters after rustflag. + set_property( + TARGET ${target_name} + APPEND + PROPERTY INTERFACE_CORROSION_RUSTFLAGS ${rustflag} ${ARGN} + ) +endfunction() + +# Add flags for rustc (RUSTFLAGS) which only affect the target, but none of it's (Rust) dependencies +# +# Additional rustflags may be passed as optional parameters after rustc_flag. +function(corrosion_add_target_local_rustflags target_name rustc_flag) + # Set Rustflags via `cargo rustc` which only affect the current crate, but not dependencies. + set_property( + TARGET ${target_name} + APPEND + PROPERTY INTERFACE_CORROSION_LOCAL_RUSTFLAGS ${rustc_flag} ${ARGN} + ) +endfunction() + +function(corrosion_set_env_vars target_name env_var) + # Additional environment variables may be passed as optional parameters after env_var. + set_property( + TARGET ${target_name} + APPEND + PROPERTY ${_CORR_PROP_ENV_VARS} ${env_var} ${ARGN} + ) +endfunction() + +function(corrosion_set_cargo_flags target_name) + # corrosion_set_cargo_flags( [ ... ]) + + set_property( + TARGET ${target_name} + APPEND + PROPERTY INTERFACE_CORROSION_CARGO_FLAGS ${ARGN} + ) +endfunction() + +function(corrosion_set_features target_name) + # corrosion_set_features( [ALL_FEATURES=Bool] [NO_DEFAULT_FEATURES] [FEATURES ... ]) + set(options NO_DEFAULT_FEATURES) + set(one_value_args ALL_FEATURES) + set(multi_value_args FEATURES) + cmake_parse_arguments( + PARSE_ARGV 1 + SET + "${options}" + "${one_value_args}" + "${multi_value_args}" + ) + + if(DEFINED SET_ALL_FEATURES) + set_property( + TARGET ${target_name} + PROPERTY ${_CORR_PROP_ALL_FEATURES} ${SET_ALL_FEATURES} + ) + endif() + if(SET_NO_DEFAULT_FEATURES) + set_property( + TARGET ${target_name} + PROPERTY ${_CORR_PROP_NO_DEFAULT_FEATURES} 1 + ) + endif() + if(SET_FEATURES) + set_property( + TARGET ${target_name} + APPEND + PROPERTY ${_CORR_PROP_FEATURES} ${SET_FEATURES} + ) + endif() +endfunction() + +function(corrosion_link_libraries target_name) + if(TARGET "${target_name}-static") + message(DEBUG "The target ${target_name} builds a static Rust library." + "Calling `target_link_libraries()` instead." + ) + target_link_libraries("${target_name}-static" INTERFACE ${ARGN}) + if(NOT TARGET "${target_name}-shared") + # Early return, since Rust won't invoke the linker for static libraries + return() + endif() + endif() + foreach(library ${ARGN}) + set_property( + TARGET _cargo-build_${target_name} + APPEND + PROPERTY CARGO_DEPS_LINKER_LANGUAGES + $ + ) + + if (TARGET "${library}") + corrosion_add_target_local_rustflags(${target_name} + "-L$" + "-l$" + ) + add_dependencies(_cargo-build_${target_name} ${library}) + elseif(IS_ABSOLUTE "${library}") + # Linking via full path (See https://doc.rust-lang.org/rustc/command-line-arguments.html#linking-modifiers-verbatim) + corrosion_add_target_local_rustflags(${target_name} "-Clink-arg=${library}") + else() + # We have to assume ${library} is a non-CMake library name + corrosion_add_target_local_rustflags(${target_name} "-l${library}") + endif() + endforeach() +endfunction() + +#[=======================================================================[.md: +ANCHOR: corrosion-install +** EXPERIMENTAL **: This function is currently still considered experimental + and is not officially released yet. Feedback and Suggestions are welcome. + +```cmake +corrosion_install(TARGETS ... [EXPORT ] + [[ARCHIVE|LIBRARY|RUNTIME|PUBLIC_HEADER] + [DESTINATION ] + [PERMISSIONS ] + [CONFIGURATIONS [Debug|Release|]] + ] [...]) +``` +* **TARGETS**: Target or targets to install. +* **EXPORT**: Creates an export that can be installed with `install(EXPORT)`. must be globally unique. + Also creates a file at ${CMAKE_BINARY_DIR}/corrosion/Corrosion.cmake that must be included in the installed config file. +* **ARCHIVE**/**LIBRARY**/**RUNTIME**/PUBLIC_HEADER: Designates that the following settings only apply to that specific type of object. +* **DESTINATION**: The subdirectory within the CMAKE_INSTALL_PREFIX that a specific object should be placed. Defaults to values from GNUInstallDirs. +* **PERMISSIONS**: The permissions of files copied into the install prefix. + +Any `PUBLIC` or `INTERFACE` [file sets] will be installed. + +[file sets]: https://cmake.org/cmake/help/latest/command/target_sources.html#file-sets + +ANCHOR_END: corrosion-install +#]=======================================================================] +function(corrosion_install) + # Default install dirs + include(GNUInstallDirs) + + # Parse arguments to corrosion_install + list(GET ARGN 0 INSTALL_TYPE) + list(REMOVE_AT ARGN 0) + + # The different install types that are supported. Some targets may have more than one of these + # types. For example, on Windows, a shared library will have both an ARCHIVE component and a + # RUNTIME component. + set(INSTALL_TARGET_TYPES ARCHIVE LIBRARY RUNTIME PRIVATE_HEADER PUBLIC_HEADER) + + # Arguments to each install target type + set(OPTIONS) + set(ONE_VALUE_ARGS DESTINATION) + set(MULTI_VALUE_ARGS PERMISSIONS CONFIGURATIONS) + set(TARGET_ARGS ${OPTIONS} ${ONE_VALUE_ARGS} ${MULTI_VALUE_ARGS}) + + if (INSTALL_TYPE STREQUAL "TARGETS") + # Extract targets + set(INSTALL_TARGETS) + list(LENGTH ARGN ARGN_LENGTH) + set(DELIMITERS EXPORT ${INSTALL_TARGET_TYPES} ${TARGET_ARGS}) + while(ARGN_LENGTH) + # If we hit another keyword, stop - we've found all the targets + list(GET ARGN 0 FRONT) + if (FRONT IN_LIST DELIMITERS) + break() + endif() + + list(APPEND INSTALL_TARGETS ${FRONT}) + list(REMOVE_AT ARGN 0) + + # Update ARGN_LENGTH + list(LENGTH ARGN ARGN_LENGTH) + endwhile() + + # Check if there are any args left before proceeding + list(LENGTH ARGN ARGN_LENGTH) + if (ARGN_LENGTH) + list(GET ARGN 0 FRONT) + if (FRONT STREQUAL "EXPORT") + list(REMOVE_AT ARGN 0) # Pop "EXPORT" + + list(GET ARGN 0 EXPORT_NAME) + list(REMOVE_AT ARGN 0) # Pop + set(EXTRA_TARGETS_EXPORT_NAME ${EXPORT_NAME}Corrosion.cmake) + set(EXPORT_NAME EXPORT ${EXPORT_NAME}) + set(EXPORT_FILE_PATH "${CMAKE_BINARY_DIR}/corrosion/${EXTRA_TARGETS_EXPORT_NAME}") + # Remove first, since otherwise we will append to the file on every reconfigure. + # Assumes that the corrosion_install will only be called once for a given EXPORT_NAME. + file(REMOVE "${EXPORT_FILE_PATH}") + endif() + else() + # Prevent variable set in user code from interfering + set(EXPORT_NAME) + endif() + + # Loop over all arguments and get options for each install target type + list(LENGTH ARGN ARGN_LENGTH) + while(ARGN_LENGTH) + # Check if we're dealing with arguments for a specific install target type, or with + # default options for all target types. + list(GET ARGN 0 FRONT) + if (FRONT IN_LIST INSTALL_TARGET_TYPES) + set(INSTALL_TARGET_TYPE ${FRONT}) + list(REMOVE_AT ARGN 0) + else() + set(INSTALL_TARGET_TYPE DEFAULT) + endif() + + # Gather the arguments to this install type + set(ARGS) + list(LENGTH ARGN ARGN_LENGTH) + while(ARGN_LENGTH) + # If the next keyword is an install target type, then break - arguments have been + # gathered. + list(GET ARGN 0 FRONT) + if (FRONT IN_LIST INSTALL_TARGET_TYPES) + break() + endif() + + list(APPEND ARGS ${FRONT}) + list(REMOVE_AT ARGN 0) + + list(LENGTH ARGN ARGN_LENGTH) + endwhile() + + # Parse the arguments and register the file install + cmake_parse_arguments( + COR "${OPTIONS}" "${ONE_VALUE_ARGS}" "${MULTI_VALUE_ARGS}" ${ARGS}) + + if (COR_DESTINATION) + set(COR_INSTALL_${INSTALL_TARGET_TYPE}_DESTINATION ${COR_DESTINATION}) + endif() + + if (COR_PERMISSIONS) + set(COR_INSTALL_${INSTALL_TARGET_TYPE}_PERMISSIONS ${COR_PERMISSIONS}) + endif() + + if (COR_CONFIGURATIONS) + set(COR_INSTALL_${INSTALL_TARGET_TYPE}_CONFIGURATIONS ${COR_CONFIGURATIONS}) + endif() + + # Update ARG_LENGTH + list(LENGTH ARGN ARGN_LENGTH) + endwhile() + + # Default permissions for all files + set(DEFAULT_PERMISSIONS OWNER_WRITE OWNER_READ GROUP_READ WORLD_READ) + + # Loop through each install target and register file installations + foreach(INSTALL_TARGET ${INSTALL_TARGETS}) + if(NOT TARGET ${INSTALL_TARGET}) + message(FATAL_ERROR "Install target ${INSTALL_TARGET} is not a valid target") + endif() + # Don't both implementing target type differentiation using generator expressions since + # TYPE cannot change after target creation + get_property( + TARGET_TYPE + TARGET ${INSTALL_TARGET} PROPERTY TYPE + ) + + # Install executable files first + if (TARGET_TYPE STREQUAL "EXECUTABLE") + if (DEFINED COR_INSTALL_RUNTIME_DESTINATION) + set(DESTINATION ${COR_INSTALL_RUNTIME_DESTINATION}) + elseif (DEFINED COR_INSTALL_DEFAULT_DESTINATION) + set(DESTINATION ${COR_INSTALL_DEFAULT_DESTINATION}) + else() + set(DESTINATION ${CMAKE_INSTALL_BINDIR}) + endif() + + if (DEFINED COR_INSTALL_RUNTIME_PERMISSIONS) + set(PERMISSIONS ${COR_INSTALL_RUNTIME_PERMISSIONS}) + elseif (DEFINED COR_INSTALL_DEFAULT_PERMISSIONS) + set(PERMISSIONS ${COR_INSTALL_DEFAULT_PERMISSIONS}) + else() + set( + PERMISSIONS + ${DEFAULT_PERMISSIONS} OWNER_EXECUTE GROUP_EXECUTE WORLD_EXECUTE) + endif() + + if (DEFINED COR_INSTALL_RUNTIME_CONFIGURATIONS) + set(CONFIGURATIONS CONFIGURATIONS ${COR_INSTALL_RUNTIME_CONFIGURATIONS}) + elseif (DEFINED COR_INSTALL_DEFAULT_CONFIGURATIONS) + set(CONFIGURATIONS CONFIGURATIONS ${COR_INSTALL_DEFAULT_CONFIGURATIONS}) + else() + set(CONFIGURATIONS) + endif() + + install( + FILES $ + DESTINATION ${DESTINATION} + PERMISSIONS ${PERMISSIONS} + ${CONFIGURATIONS} + ) + elseif(TARGET_TYPE STREQUAL "INTERFACE_LIBRARY") + if(TARGET ${INSTALL_TARGET}-static) + if (DEFINED COR_INSTALL_ARCHIVE_DESTINATION) + set(DESTINATION ${COR_INSTALL_ARCHIVE_DESTINATION}) + elseif (DEFINED COR_INSTALL_DEFAULT_DESTINATION) + set(DESTINATION ${COR_INSTALL_DEFAULT_DESTINATION}) + else() + set(DESTINATION ${CMAKE_INSTALL_LIBDIR}) + endif() + + if (DEFINED COR_INSTALL_ARCHIVE_PERMISSIONS) + set(PERMISSIONS ${COR_INSTALL_ARCHIVE_PERMISSIONS}) + elseif (DEFINED COR_INSTALL_DEFAULT_PERMISSIONS) + set(PERMISSIONS ${COR_INSTALL_DEFAULT_PERMISSIONS}) + else() + set(PERMISSIONS ${DEFAULT_PERMISSIONS}) + endif() + + if (DEFINED COR_INSTALL_ARCHIVE_CONFIGURATIONS) + set(CONFIGURATIONS CONFIGURATIONS ${COR_INSTALL_ARCHIVE_CONFIGURATIONS}) + elseif (DEFINED COR_INSTALL_DEFAULT_CONFIGURATIONS) + set(CONFIGURATIONS CONFIGURATIONS ${COR_INSTALL_DEFAULT_CONFIGURATIONS}) + else() + set(CONFIGURATIONS) + endif() + + install( + FILES $ + DESTINATION ${DESTINATION} + PERMISSIONS ${PERMISSIONS} + ${CONFIGURATIONS} + ) + + if(EXPORT_NAME) + get_target_property(COR_FILE_NAME ${INSTALL_TARGET}-static COR_FILE_NAME) + file(APPEND "${EXPORT_FILE_PATH}" +" +add_library(${INSTALL_TARGET}-static STATIC IMPORTED) +set_target_properties(${INSTALL_TARGET}-static + PROPERTIES + IMPORTED_LOCATION \"\${PACKAGE_PREFIX_DIR}/${DESTINATION}/${COR_FILE_NAME}\" +) +" + ) + endif() + endif() + + if(TARGET ${INSTALL_TARGET}-shared) + if (DEFINED COR_INSTALL_LIBRARY_DESTINATION) + set(DESTINATION ${COR_INSTALL_LIBRARY_DESTINATION}) + elseif (DEFINED COR_INSTALL_DEFAULT_DESTINATION) + set(DESTINATION ${COR_INSTALL_DEFAULT_DESTINATION}) + else() + set(DESTINATION ${CMAKE_INSTALL_LIBDIR}) + endif() + + if (DEFINED COR_INSTALL_LIBRARY_PERMISSIONS) + set(PERMISSIONS ${COR_INSTALL_LIBRARY_PERMISSIONS}) + elseif (DEFINED COR_INSTALL_DEFAULT_PERMISSIONS) + set(PERMISSIONS ${COR_INSTALL_DEFAULT_PERMISSIONS}) + else() + set( + PERMISSIONS + ${DEFAULT_PERMISSIONS} OWNER_EXECUTE GROUP_EXECUTE WORLD_EXECUTE + ) + endif() + + if (DEFINED COR_INSTALL_LIBRARY_CONFIGURATIONS) + set(CONFIGURATIONS CONFIGURATIONS ${COR_INSTALL_LIBRARY_CONFIGURATIONS}) + elseif (DEFINED COR_INSTALL_DEFAULT_CONFIGURATIONS) + set(CONFIGURATIONS CONFIGURATIONS ${COR_INSTALL_DEFAULT_CONFIGURATIONS}) + else() + set(CONFIGURATIONS) + endif() + + install( + IMPORTED_RUNTIME_ARTIFACTS ${INSTALL_TARGET}-shared + PERMISSIONS ${PERMISSIONS} + DESTINATION ${DESTINATION} + ${CONFIGURATIONS} + ) + + if(EXPORT_NAME) + get_target_property(COR_FILE_NAME ${INSTALL_TARGET}-shared COR_FILE_NAME) + file(APPEND "${EXPORT_FILE_PATH}" +" +add_library(${INSTALL_TARGET}-shared SHARED IMPORTED) +set_target_properties(${INSTALL_TARGET}-shared + PROPERTIES + IMPORTED_LOCATION \"\${PACKAGE_PREFIX_DIR}/${DESTINATION}/${COR_FILE_NAME}\" +) +" + ) + + get_target_property(COR_IMPLIB_FILE_NAME ${INSTALL_TARGET}-shared COR_IMPLIB_FILE_NAME) + if (NOT COR_IMPLIB_FILE_NAME MATCHES .*-NOTFOUND) + file(APPEND "${EXPORT_FILE_PATH}" +" +set_target_properties(${INSTALL_TARGET}-shared + PROPERTIES + IMPORTED_IMPLIB \"\${PACKAGE_PREFIX_DIR}/${DESTINATION}/${COR_IMPLIB_FILE_NAME}\" +)" + ) + endif() + endif() + endif() + else() + message(FATAL_ERROR "Unknown target type ${TARGET_TYPE} for install target ${INSTALL_TARGET}") + endif() + + # Executables can also have export tables, so they _might_ also need header files + if (DEFINED COR_INSTALL_PUBLIC_HEADER_DESTINATION) + set(DESTINATION ${COR_INSTALL_PUBLIC_HEADER_DESTINATION}) + elseif (DEFINED COR_INSTALL_DEFAULT_DESTINATION) + set(DESTINATION ${COR_INSTALL_DEFAULT_DESTINATION}) + else() + set(DESTINATION ${CMAKE_INSTALL_INCLUDEDIR}) + endif() + + if (DEFINED COR_INSTALL_PUBLIC_HEADER_PERMISSIONS) + set(PERMISSIONS ${COR_INSTALL_PUBLIC_HEADER_PERMISSIONS}) + elseif (DEFINED COR_INSTALL_DEFAULT_PERMISSIONS) + set(PERMISSIONS ${COR_INSTALL_DEFAULT_PERMISSIONS}) + else() + # Directories need OWNER_EXECUTE in order to be deletable by owner + set(PERMISSIONS ${DEFAULT_PERMISSIONS} OWNER_EXECUTE) + endif() + + if (DEFINED COR_INSTALL_PUBLIC_HEADER_CONFIGURATIONS) + set(CONFIGURATIONS CONFIGURATIONS ${COR_INSTALL_PUBLIC_HEADER_CONFIGURATIONS}) + elseif (DEFINED COR_INSTALL_DEFAULT_CONFIGURATIONS) + set(CONFIGURATIONS CONFIGURATIONS ${COR_INSTALL_DEFAULT_CONFIGURATIONS}) + else() + set(CONFIGURATIONS) + endif() + + get_target_property(FILE_SET ${INSTALL_TARGET} INTERFACE_HEADER_SETS) + if(NOT FILE_SET OR FILE_SET MATCHES .*-NOTFOUND) + set(TARGET_HAS_FILE_SET FALSE) + else() + set(TARGET_HAS_FILE_SET TRUE) + endif() + + if(NOT TARGET_HAS_FILE_SET) + if(EXPORT_NAME) + # We still need to generate a EXPORT but we can't do that with install(DIRECTORY) + install(TARGETS ${INSTALL_TARGET} ${EXPORT_NAME}) + endif() + + set(PUBLIC_HEADER_PROPERTIES INCLUDE_DIRECTORIES PUBLIC_INCLUDE_DIRECTORIES INTERFACE_INCLUDE_DIRECTORIES) + foreach(PUBLIC_HEADER_PROPERTY ${PUBLIC_HEADER_PROPERTIES}) + get_target_property(PUBLIC_HEADER ${INSTALL_TARGET} ${PUBLIC_HEADER_PROPERTY}) + + if(NOT PUBLIC_HEADER MATCHES .*-NOTFOUND) + foreach(INCLUDE_DIRECTORY ${PUBLIC_HEADER}) + install( + DIRECTORY ${INCLUDE_DIRECTORY} + DESTINATION . + FILE_PERMISSIONS ${PERMISSIONS} + DIRECTORY_PERMISSIONS ${PERMISSIONS} + ${CONFIGURATIONS} + ) + endforeach() + endif() + endforeach() + else() + install( + TARGETS ${INSTALL_TARGET} + ${EXPORT_NAME} + FILE_SET HEADERS + DESTINATION ${DESTINATION} + PERMISSIONS ${PERMISSIONS} + ${CONFIGURATIONS} + ) + endif() + endforeach() + + elseif(INSTALL_TYPE STREQUAL "EXPORT") + message(FATAL_ERROR "install(EXPORT ...) not yet implemented") + else() + message(FATAL_ERROR "Unknown arg: ${INSTALL_TYPE}") + endif() +endfunction() + +function(_corrosion_check_cxx_version_helper manifest_dir cxx_name out_required_version) + execute_process(COMMAND ${CMAKE_COMMAND} -E env + "CARGO_BUILD_RUSTC=${_CORROSION_RUSTC}" + ${_CORROSION_CARGO} tree -i "${cxx_name}" + # Usage of `cxx` could be gated behind a feature. Features can use Generator expressions, + # so we can't really know what features we will enable when building at this point. + # Features should be additive though, so simply enabling all-features should work for + # dependency resolution. + --all-features + --target all + --depth=0 + WORKING_DIRECTORY "${manifest_dir}" + RESULT_VARIABLE cxx_version_result + OUTPUT_VARIABLE cxx_version_output + ERROR_VARIABLE cxx_version_error + ) + if(NOT "${cxx_version_result}" EQUAL "0") + message(DEBUG "`cargo tree -i ${cxx_name}` returned an error: ${cxx_version_error}") + set("${out_required_version}" "${cxx_name}-NOTFOUND" PARENT_SCOPE) + return() + endif() + if(cxx_version_output MATCHES "${cxx_name} v([0-9]+.[0-9]+.[0-9]+)") + set("${out_required_version}" "${CMAKE_MATCH_1}" PARENT_SCOPE) + else() + message(DEBUG "Failed to parse `cargo tree -i ${cxx_name}` output: ${cxx_version_output}") + set("${out_required_version}" "${cxx_name}-NOTFOUND" PARENT_SCOPE) + endif() +endfunction() + +function(_corrosion_check_cxx_version manifest_dir out_required_version) + # cxxbridge-cmd is known to be available in lockfiles since cxx 1.0.131. + # We include `cxx` as a fallback to support older versions too. `cxxbridge` should always + # be exactly the same version as `cxx`, so falling back to `cxx` version should not cause issues. + foreach(cxxbridge_name cxxbridge-cmd cxx) + unset(cxx_required_version) + _corrosion_check_cxx_version_helper("${manifest_dir}" + "${cxxbridge_name}" + cxx_required_version) + if(cxx_required_version) + set("${out_required_version}" "${cxx_required_version}" PARENT_SCOPE) + break() + else() + set("${out_required_version}" "cxx-NOTFOUND" PARENT_SCOPE) + endif() + endforeach() + +endfunction() + + + +#[=======================================================================[.md: +** EXPERIMENTAL **: This function is currently still considered experimental + and is not officially released yet. Feedback and Suggestions are welcome. + +ANCHOR: corrosion_add_cxxbridge + +```cmake +corrosion_add_cxxbridge(cxx_target + CRATE + REGEN_TARGET + [FILES ] +) +``` + +Adds build-rules to create C++ bindings using the [cxx] crate. + +### Arguments: +* `cxxtarget`: Name of the C++ library target for the bindings, which corrosion will create. +* **FILES**: Input Rust source file containing #[cxx::bridge]. +* **CRATE**: Name of an imported Rust target. Note: Parameter may be renamed before release +* **REGEN_TARGET**: Name of a custom target that will regenerate the cxx bindings **without** recompiling. Note: Parameter may be renamed before release + +#### Currently missing arguments + +The following arguments to cxxbridge **currently** have no way to be passed by the user: +- `--cfg` +- `--cxx-impl-annotations` +- `--include` + +The created rules approximately do the following: +- Check which version of `cxx` the Rust crate specified by the `CRATE` argument depends on. +- Check if the exact same version of `cxxbridge-cmd` is installed (available in `PATH`) +- If not, create a rule to build the exact same version of `cxxbridge-cmd`. +- Create rules to run `cxxbridge` and generate + - The `rust/cxx.h` header + - A header and source file for each of the files specified in `FILES` +- The generated sources (and header include directories) are added to the `cxxtarget` CMake + library target. + +### Limitations + +We currently require the `CRATE` argument to be a target imported by Corrosion, however, +Corrosion does not import `rlib` only libraries. As a workaround users can add +`staticlib` to their list of crate kinds. In the future this may be solved more properly, +by either adding an option to also import Rlib targets (without build rules) or by +adding a `MANIFEST_PATH` argument to this function, specifying where the crate is. + +### Contributing + +Specifically some more realistic test / demo projects and feedback about limitations would be +welcome. + +[cxx]: https://github.com/dtolnay/cxx + +ANCHOR_END: corrosion_add_cxxbridge +#]=======================================================================] +function(corrosion_add_cxxbridge cxx_target) + set(OPTIONS) + set(ONE_VALUE_KEYWORDS CRATE REGEN_TARGET) + set(MULTI_VALUE_KEYWORDS FILES) + cmake_parse_arguments(PARSE_ARGV 1 _arg "${OPTIONS}" "${ONE_VALUE_KEYWORDS}" "${MULTI_VALUE_KEYWORDS}") + + set(required_keywords CRATE FILES) + foreach(keyword ${required_keywords}) + if(NOT DEFINED "_arg_${keyword}") + message(FATAL_ERROR "Missing required parameter `${keyword}`.") + elseif("${_arg_${keyword}}" STREQUAL "") + message(FATAL_ERROR "Required parameter `${keyword}` may not be set to an empty string.") + endif() + endforeach() + + if(DEFINED _arg_UNPARSED_ARGUMENTS) + message(AUTHOR_WARNING "corrosion_add_cxxbridge was called with the following unknown arguments: " + "`${_arg_UNPARSED_ARGUMENTS}`\n" + "Unknown arguments will be ignored." + ) + endif() + + get_target_property(manifest_path "${_arg_CRATE}" INTERFACE_COR_PACKAGE_MANIFEST_PATH) + + if(NOT EXISTS "${manifest_path}") + message(FATAL_ERROR "Internal error: No package manifest found at ${manifest_path}") + endif() + + get_filename_component(manifest_dir ${manifest_path} DIRECTORY) + + _corrosion_check_cxx_version("${manifest_dir}" cxx_required_version) + + if(NOT cxx_required_version) + message(FATAL_ERROR + "Failed to find a dependency on `cxxbridge-cmd` / `cxx` for crate ${_arg_CRATE}" + ) + endif() + + # First check if a suitable version of cxxbridge is installed + find_program(INSTALLED_CXXBRIDGE cxxbridge PATHS "$ENV{HOME}/.cargo/bin/") + mark_as_advanced(INSTALLED_CXXBRIDGE) + if(INSTALLED_CXXBRIDGE) + execute_process(COMMAND ${INSTALLED_CXXBRIDGE} --version OUTPUT_VARIABLE cxxbridge_version_output) + if(cxxbridge_version_output MATCHES "cxxbridge ([0-9]+.[0-9]+.[0-9]+)") + set(cxxbridge_version "${CMAKE_MATCH_1}") + else() + set(cxxbridge_version "") + endif() + endif() + + set(cxxbridge "") + if(cxxbridge_version) + if(cxxbridge_version VERSION_EQUAL cxx_required_version) + set(cxxbridge "${INSTALLED_CXXBRIDGE}") + if(NOT TARGET "cxxbridge_v${cxx_required_version}") + # Add an empty target. + add_custom_target("cxxbridge_v${cxx_required_version}" + ) + endif() + endif() + endif() + + # No suitable version of cxxbridge was installed, so use custom target to build correct version. + if(NOT cxxbridge) + if(NOT TARGET "cxxbridge_v${cxx_required_version}") + unset(executable_postfix) + if(Rust_CARGO_HOST_OS STREQUAL "windows") + set(executable_postfix ".exe") + endif() + + add_custom_command(OUTPUT "${CMAKE_BINARY_DIR}/corrosion/cxxbridge_v${cxx_required_version}/bin/cxxbridge${executable_postfix}" + COMMAND + ${CMAKE_COMMAND} -E make_directory "${CMAKE_BINARY_DIR}/corrosion/cxxbridge_v${cxx_required_version}" + COMMAND + ${CMAKE_COMMAND} -E env + "CARGO_BUILD_RUSTC=$CACHE{CORROSION_TOOLS_RUSTC}" + $CACHE{CORROSION_TOOLS_CARGO} install + cxxbridge-cmd + --version "${cxx_required_version}" + --locked + --root "${CMAKE_BINARY_DIR}/corrosion/cxxbridge_v${cxx_required_version}" + --quiet + # todo: use --target-dir to potentially reuse artifacts + COMMENT "Building cxxbridge (version ${cxx_required_version}) with Rust toolchain $CACHE{CORROSION_TOOLS_RUST_TOOLCHAIN}" + ) + add_custom_target("cxxbridge_v${cxx_required_version}" + DEPENDS "${CMAKE_BINARY_DIR}/corrosion/cxxbridge_v${cxx_required_version}/bin/cxxbridge${executable_postfix}" + ) + endif() + set(cxxbridge "${CMAKE_BINARY_DIR}/corrosion/cxxbridge_v${cxx_required_version}/bin/cxxbridge${executable_postfix}") + endif() + + + # The generated folder structure will be of the following form + # + # CMAKE_CURRENT_BINARY_DIR + # corrosion_generated + # cxxbridge + # + # include + # + # + # rust + # cxx.h + # src + # + # cbindgen + # ... + # other + # ... + + set(corrosion_generated_dir "${CMAKE_CURRENT_BINARY_DIR}/corrosion_generated") + set(generated_dir "${corrosion_generated_dir}/cxxbridge/${cxx_target}") + set(header_placement_dir "${generated_dir}/include/${cxx_target}") + set(source_placement_dir "${generated_dir}/src") + + add_library(${cxx_target} STATIC) + target_include_directories(${cxx_target} + PUBLIC + $ + $ + ) + + # cxx generated code is using c++11 features in headers, so propagate c++11 as minimal requirement + target_compile_features(${cxx_target} PUBLIC cxx_std_11) + + if (TARGET "${_arg_CRATE}-static") + target_link_libraries(${cxx_target} PRIVATE "${_arg_CRATE}-static") + endif() + if (TARGET "${_arg_CRATE}-shared") + target_link_libraries(${cxx_target} PRIVATE "${_arg_CRATE}-shared") + endif() + + file(MAKE_DIRECTORY "${generated_dir}/include/rust") + add_custom_command( + OUTPUT "${generated_dir}/include/rust/cxx.h" + COMMAND + ${cxxbridge} --header --output "${generated_dir}/include/rust/cxx.h" + DEPENDS "cxxbridge_v${cxx_required_version}" + COMMENT "Generating rust/cxx.h header" + ) + + set(GENERATED_SOURCES "") + set(GENERATED_HEADERS "${generated_dir}/include/rust/cxx.h") + + foreach(filepath ${_arg_FILES}) + get_filename_component(filename ${filepath} NAME_WE) + get_filename_component(directory ${filepath} DIRECTORY) + set(directory_component "") + if(directory) + set(directory_component "${directory}/") + endif() + # todo: convert potentially absolute paths to relative paths.. + set(cxx_header ${directory_component}${filename}.h) + set(cxx_source ${directory_component}${filename}.cpp) + + # todo: not all projects may use the `src` directory. + set(rust_source_path "${manifest_dir}/src/${filepath}") + + file(MAKE_DIRECTORY "${header_placement_dir}/${directory}" "${source_placement_dir}/${directory}") + + add_custom_command( + OUTPUT + "${header_placement_dir}/${cxx_header}" + "${source_placement_dir}/${cxx_source}" + COMMAND + ${cxxbridge} ${rust_source_path} --header --output "${header_placement_dir}/${cxx_header}" + COMMAND + ${cxxbridge} ${rust_source_path} + --output "${source_placement_dir}/${cxx_source}" + --include "${cxx_target}/${cxx_header}" + DEPENDS "cxxbridge_v${cxx_required_version}" "${rust_source_path}" + COMMENT "Generating cxx bindings for crate ${_arg_CRATE} and file src/${filepath}" + ) + + list(APPEND GENERATED_SOURCES "${source_placement_dir}/${cxx_source}") + list(APPEND GENERATED_HEADERS "${header_placement_dir}/${cxx_header}") + endforeach() + target_sources(${cxx_target} PRIVATE ${GENERATED_SOURCES}) + # Make sure to export the headers with PUBLIC. + # This ensures that any target that depends on cxx_target also has these files as a dependency + # CMake will then make sure to generate the files before building either target, which is important + # in the presence of circular dependencies + target_sources(${cxx_target} PUBLIC ${GENERATED_HEADERS}) + + if(DEFINED _arg_REGEN_TARGET) + # Add only the headers to the regen target, as the sources are actually not needed + # For the IDE to pick everything up + add_custom_target(${_arg_REGEN_TARGET} + DEPENDS ${GENERATED_HEADERS} + COMMENT "Generated cxx bindings for crate ${_arg_CRATE}") + endif() + +endfunction() + +#[=======================================================================[.md: +ANCHOR: corrosion_cbindgen + +A helper function which uses [cbindgen] to generate C/C++ bindings for a Rust crate. +If `cbindgen` is not in `PATH` the helper function will automatically try to download +`cbindgen` and place the built binary into `CMAKE_BINARY_DIR`. The binary is shared +between multiple invocations of this function. + +The function comes with two different signatures. It's recommended to use the `TARGET` based signature when possible. + +### Auto mode (With a Rust target imported by corrosion) +```cmake +corrosion_experimental_cbindgen( + TARGET + HEADER_NAME + [CBINDGEN_VERSION ] + [FLAGS ... ] +) +``` + +### Auto-mode specific Arguments + + +* **TARGET**: The name of an imported Rust library target, for which bindings should be generated. + If the target is not imported by Corrosion, because the crate only produces an + `rlib`, you can instead use the second signature and manually pass `MANIFEST_DIRECTORY`, + `CARGO_PACKAGE` and `BINDINGS_TARGET` + +### Manual mode (Without a Rust target imported by corrosion) +```cmake +corrosion_experimental_cbindgen( + MANIFEST_DIRECTORY + CARGO_PACKAGE + BINDINGS_TARGET + [TARGET_TRIPLE ] + HEADER_NAME + [CBINDGEN_VERSION ] + [FLAGS ... ] +) +``` + +### Manual-mode specific Arguments + +* **MANIFEST_DIRECTORY**: Manual mode only. + Directory of the package defining the library crate bindings should be generated for. + If you want to avoid specifying `MANIFEST_DIRECTORY` you could add a `staticlib` target to your package + manifest as a workaround to make corrosion import the crate. + +* **CARGO_PACKAGE**: Manual mode only. + The name of the cargo package that bindings should be generated for. + Note: This corresponds to the `cbindgen` `--crate` option, which actually wants a package name. + +* **BINDINGS_TARGET**: Manual mode only. + Name of an `INTERFACE` CMake target that the generated bindings should be attached to. + In auto mode, the generated headers will be attached to the imported rust CMake crate, + and corrosion will take care of adding the necessary build dependencies. + In manual mode, this target likely doesn't exist, so the user needs to specify an INTERFACE CMake + target, which the header files should be attached to. The user must create this target themselves and + ensure to add any necessary dependencies (e.g. via `add_dependencies()`) to ensure that consumers of the + `INTERFACE` library are not linked before the Rust library has been built. + +* **TARGET_TRIPLE**: Manual mode only. + Rust target triple (e.g. `x86_64-unknown-linux-gnu`) that cbindgen should use when generating the bindings. + Defaults to target triple that corrosion was confiured to compile for. + +### Common Arguments + +* **HEADER_NAME**: The name of the generated header file. This will be the name which you include in your C/C++ code + (e.g. `#include "myproject/myheader.h" if you specify `HEADER_NAME "myproject/myheader.h"`. +* **CBINDGEN_VERSION**: Version requirement for cbindgen. Exact semantics to be specified. Currently not implemented. +* **FLAGS**: Arbitrary other flags for `cbindgen`. Run `cbindgen --help` to see the possible flags. + +[cbindgen]: https://github.com/mozilla/cbindgen + +### Current limitations + +- Cbindgens (optional) macro expansion feature internally actually builds the crate / runs the build script. + For this to work as expected in all cases, we probably need to set all the same environment variables + as when corrosion builds the crate. However the crate is a **library**, so we would need to figure out which + target builds it - and if there are multiple, potentially generate bindings per-target? + Alternatively we could add support of setting some environment variables on rlibs, and pulling that + information in when building the actual corrosion targets + Alternatively we could restrict corrosions support of this feature to actual imported staticlib/cdylib targets. +ANCHOR_END: corrosion_cbindgen +#]=======================================================================] +function(corrosion_experimental_cbindgen) + set(OPTIONS "") + set(ONE_VALUE_KEYWORDS + TARGET + MANIFEST_DIRECTORY + CARGO_PACKAGE + BINDINGS_TARGET + TARGET_TRIPLE + HEADER_NAME + CBINDGEN_VERSION + ) + set(MULTI_VALUE_KEYWORDS "FLAGS") + cmake_parse_arguments(PARSE_ARGV 0 CCN "${OPTIONS}" "${ONE_VALUE_KEYWORDS}" "${MULTI_VALUE_KEYWORDS}") + + set(required_keywords HEADER_NAME) + foreach(keyword ${required_keywords}) + if(NOT DEFINED "CCN_${keyword}") + message(FATAL_ERROR "Missing required parameter `${keyword}`.") + elseif("${CCN_${keyword}}" STREQUAL "") + message(FATAL_ERROR "Required parameter `${keyword}` may not be set to an empty string.") + endif() + endforeach() + if(NOT (DEFINED CCN_TARGET + OR (DEFINED CCN_MANIFEST_DIRECTORY AND DEFINED CCN_BINDINGS_TARGET + AND DEFINED CCN_BINDINGS_TARGET) + ) + ) + message(FATAL_ERROR "Unknown signature for corrosion_experimental_cbindgen.\n" + "Either the `TARGET` or the `MANIFEST_DIRECTORY` based signature must be chosen.\n" + "Please view the documentation for details on the function signature.\n" + "Passed arguments where: `${ARGV}`" + ) + endif() + + if(DEFINED CCN_UNPARSED_ARGUMENTS) + message(AUTHOR_WARNING "corrosion_experimental_cbindgen was called with the following unknown arguments: " + "`${CCN_UNPARSED_ARGUMENTS}`\n" + "Unknown arguments will be ignored." + ) + endif() + unset(package_manifest_dir) + + + if(TARGET "${CCN_TARGET}") + set(cbindgen_bindings_target "${CCN_TARGET}") + set(hostbuild_override "$>") + set(cbindgen_target_triple "$") + + get_target_property(package_manifest_path "${CCN_TARGET}" INTERFACE_COR_PACKAGE_MANIFEST_PATH) + if(NOT EXISTS "${package_manifest_path}") + message(FATAL_ERROR "Internal error: No package manifest found at ${package_manifest_path}") + endif() + get_filename_component(package_manifest_dir "${package_manifest_path}" DIRECTORY) + get_target_property(rust_cargo_package "${CCN_TARGET}" COR_CARGO_PACKAGE_NAME ) + if(NOT rust_cargo_package) + message(FATAL_ERROR "Internal Error: Could not determine cargo package name for cbindgen. ") + endif() + # todo: as an optimization we could cache the cargo metadata output (but --no-deps makes that slightly more complicated) + else() + if(NOT DEFINED CCN_MANIFEST_DIRECTORY) + message(FATAL_ERROR + "Internal error: There should have been a fatal error already if neither TARGET or " + "MANIFEST_DIRECTORY are specfied.") + endif() + cmake_path(ABSOLUTE_PATH CCN_MANIFEST_DIRECTORY NORMALIZE OUTPUT_VARIABLE package_manifest_dir) + if(DEFINED CCN_TARGET_TRIPLE) + set(cbindgen_target_triple "${CCN_TARGET_TRIPLE}") + else() + set(cbindgen_target_triple "${Rust_CARGO_TARGET}") + endif() + set(rust_cargo_package "${CCN_CARGO_PACKAGE}") + set(cbindgen_bindings_target "${CCN_BINDINGS_TARGET}") + get_target_property(type "${cbindgen_bindings_target}" TYPE) + if(NOT ${type} STREQUAL "INTERFACE_LIBRARY") + message(AUTHOR_WARNING "The CMake target for the cbindgen generated files is expected to be" + " an `INTERFACE` library, but was `${type}` instead." + ) + endif() + endif() + + message(STATUS "Using package `${rust_cargo_package}` as crate for cbindgen") + + set(output_header_name "${CCN_HEADER_NAME}") + + find_program(installed_cbindgen cbindgen) + + # Install the newest cbindgen version into our build tree. + if(installed_cbindgen) + set(cbindgen "${installed_cbindgen}") + else() + set(local_cbindgen_install_dir "${CMAKE_BINARY_DIR}/corrosion/cbindgen") + unset(executable_postfix) + if(Rust_CARGO_HOST_OS STREQUAL "windows") + set(executable_postfix ".exe") + endif() + set(cbindgen "${local_cbindgen_install_dir}/bin/cbindgen${executable_postfix}") + + if(NOT TARGET "_corrosion_cbindgen") + file(MAKE_DIRECTORY "${local_cbindgen_install_dir}") + + add_custom_command(OUTPUT "${cbindgen}" + COMMAND ${CMAKE_COMMAND} + -E env + "CARGO_BUILD_RUSTC=$CACHE{CORROSION_TOOLS_RUSTC}" + $CACHE{CORROSION_TOOLS_CARGO} install + cbindgen + --locked + --root "${local_cbindgen_install_dir}" + ${_CORROSION_QUIET_OUTPUT_FLAG} + COMMENT "Building cbindgen with Rust toolchain $CACHE{CORROSION_TOOLS_RUST_TOOLCHAIN}" + VERBATIM + ) + add_custom_target("_corrosion_cbindgen" + DEPENDS "${cbindgen}" + ) + endif() + endif() + + set(corrosion_generated_dir "${CMAKE_CURRENT_BINARY_DIR}/corrosion_generated") + set(generated_dir "${corrosion_generated_dir}/cbindgen/${cbindgen_bindings_target}") + set(header_placement_dir "${generated_dir}/include") + set(depfile_placement_dir "${generated_dir}/depfile") + set(generated_depfile "${depfile_placement_dir}/${output_header_name}.d") + set(generated_header "${header_placement_dir}/${output_header_name}") + + if(CMAKE_VERSION VERSION_GREATER_EQUAL "3.23") + target_sources(${cbindgen_bindings_target} + INTERFACE + FILE_SET HEADERS + BASE_DIRS "${header_placement_dir}" + FILES "${header_placement_dir}/${output_header_name}" + ) + else() + # Note: not clear to me how install would best work before CMake 3.23 + target_include_directories(${cbindgen_bindings_target} + INTERFACE + $ + $ + ) + endif() + + # This may be different from $header_placement_dir since the user specified HEADER_NAME may contain + # relative directories. + get_filename_component(generated_header_dir "${generated_header}" DIRECTORY) + file(MAKE_DIRECTORY "${generated_header_dir}") + + unset(depfile_cbindgen_arg) + get_filename_component(generated_depfile_dir "${generated_depfile}" DIRECTORY) + file(MAKE_DIRECTORY "${generated_depfile_dir}") + set(depfile_cbindgen_arg "--depfile=${generated_depfile}") + + add_custom_command( + OUTPUT + "${generated_header}" + COMMAND + "${CMAKE_COMMAND}" -E env + TARGET="${cbindgen_target_triple}" + # cbindgen invokes cargo-metadata and checks the CARGO environment variable + CARGO="${_CORROSION_CARGO}" + RUSTC="${_CORROSION_RUSTC}" + "${cbindgen}" + --output "${generated_header}" + --crate "${rust_cargo_package}" + ${depfile_cbindgen_arg} + ${CCN_FLAGS} + COMMENT "Generate cbindgen bindings for package ${rust_cargo_package} and output header ${generated_header}" + DEPFILE "${generated_depfile}" + COMMAND_EXPAND_LISTS + WORKING_DIRECTORY "${package_manifest_dir}" + ) + + if(NOT installed_cbindgen) + add_custom_command( + OUTPUT "${generated_header}" + APPEND + DEPENDS _corrosion_cbindgen + ) + endif() + + if(NOT TARGET "_corrosion_cbindgen_${cbindgen_bindings_target}_bindings") + add_custom_target(_corrosion_cbindgen_${cbindgen_bindings_target}_bindings + COMMENT "Generate cbindgen bindings for package ${rust_cargo_package}" + ) + endif() + # Users might want to call cbindgen multiple times, e.g. to generate separate C++ and C header files. + string(MAKE_C_IDENTIFIER "${output_header_name}" header_identifier ) + add_custom_target("_corrosion_cbindgen_${cbindgen_bindings_target}_bindings_${header_identifier}" + DEPENDS "${generated_header}" + COMMENT "Generate ${generated_header} for ${cbindgen_bindings_target}" + ) + add_dependencies("_corrosion_cbindgen_${cbindgen_bindings_target}_bindings" "_corrosion_cbindgen_${cbindgen_bindings_target}_bindings_${header_identifier}") + add_dependencies(${cbindgen_bindings_target} "_corrosion_cbindgen_${cbindgen_bindings_target}_bindings") + if(TARGET "${CCN_TARGET}") + add_dependencies(cargo-build_${CCN_TARGET} "_corrosion_cbindgen_${cbindgen_bindings_target}_bindings") + endif() +endfunction() + +# Parse the version of a Rust package from it's package manifest (Cargo.toml) +function(corrosion_parse_package_version package_manifest_path out_package_version) + if(NOT EXISTS "${package_manifest_path}") + message(FATAL_ERROR "Package manifest `${package_manifest_path}` does not exist.") + endif() + + file(READ "${package_manifest_path}" package_manifest) + + # Find the package table. It may contain arrays, so match until \n\[, which should mark the next + # table. Note: backslashes must be doubled to escape the backslash for the bracket. LF is single + # backslash however. On windows the line also ends in \n, so matching against \n\[ is sufficient + # to detect an opening bracket on a new line. + set(package_table_regex "\\[package\\](.*)\n\\[") + + string(REGEX MATCH "${package_table_regex}" _package_table "${package_manifest}") + + if(CMAKE_MATCH_COUNT EQUAL "1") + set(package_table "${CMAKE_MATCH_1}") + else() + message(DEBUG + "Failed to find `[package]` table in package manifest `${package_manifest_path}`.\n" + "Matches: ${CMAKE_MATCH_COUNT}\n" + ) + set(${out_package_version} + "NOTFOUND" + PARENT_SCOPE + ) + endif() + # Match `version = "0.3.2"`, `"version" = "0.3.2" Contains one matching group for the version + set(version_regex "[\r]?\n[\"']?version[\"']?[ \t]*=[ \t]*[\"']([0-9\.]+)[\"']") + + string(REGEX MATCH "${version_regex}" _version "${package_table}") + + if("${package_table}" MATCHES "${version_regex}") + set(${out_package_version} + "${CMAKE_MATCH_1}" + PARENT_SCOPE + ) + else() + message(DEBUG "Failed to extract package version from manifest `${package_manifest_path}`.") + set(${out_package_version} + "NOTFOUND" + PARENT_SCOPE + ) + endif() +endfunction() + +function(_corrosion_initialize_properties target_name) + # Initialize the `_OUTPUT_DIRECTORY` properties based on `CMAKE__OUTPUT_DIRECTORY`. + foreach(output_var RUNTIME_OUTPUT_DIRECTORY ARCHIVE_OUTPUT_DIRECTORY LIBRARY_OUTPUT_DIRECTORY PDB_OUTPUT_DIRECTORY) + if (DEFINED "CMAKE_${output_var}") + set_property(TARGET ${target_name} PROPERTY "${output_var}" "${CMAKE_${output_var}}") + endif() + + foreach(config_type ${CMAKE_CONFIGURATION_TYPES}) + string(TOUPPER "${config_type}" config_type_upper) + if (DEFINED "CMAKE_${output_var}_${config_type_upper}") + set_property(TARGET ${target_name} PROPERTY "${output_var}_${config_type_upper}" "${CMAKE_${output_var}_${config_type_upper}}") + endif() + endforeach() + endforeach() +endfunction() + +# Helper macro to pass through an optional `OPTION` argument parsed via `cmake_parse_arguments` +# to another function that takes the same OPTION. +# If the option was set, then the variable will be set to the same option name again, +# otherwise will be unset. +macro(_corrosion_option_passthrough_helper option_name prefix var_name) + if(${${prefix}_${option_name}}) + set("${var_name}" "${option_name}") + else() + unset("${var_name}") + endif() +endmacro() + +# Helper macro to pass through an optional argument with value(s), parsed via `cmake_parse_arguments`, +# to another function that takes the same keyword + associated values. +# If the argument was given, then the variable will be a list of the argument name and the values, +# which will be expanded, when calling the function (assuming no quotes). +macro(_corrosion_arg_passthrough_helper arg_name prefix var_name) + if(DEFINED "${prefix}_${arg_name}") + set("${var_name}" "${arg_name}" "${${prefix}_${arg_name}}") + else() + unset("${var_name}") + endif() +endmacro() + +list(POP_BACK CMAKE_MESSAGE_CONTEXT) + diff --git a/corrosion/cmake/CorrosionConfig.cmake.in b/corrosion/cmake/CorrosionConfig.cmake.in new file mode 100644 index 0000000..f8f4de3 --- /dev/null +++ b/corrosion/cmake/CorrosionConfig.cmake.in @@ -0,0 +1,9 @@ +@PACKAGE_INIT@ + +if (Corrosion_FOUND) + return() +endif() + +list(APPEND CMAKE_MODULE_PATH "${PACKAGE_PREFIX_DIR}/@CMAKE_INSTALL_DATADIR@/cmake") + +include(Corrosion) diff --git a/corrosion/cmake/CorrosionGenerator.cmake b/corrosion/cmake/CorrosionGenerator.cmake new file mode 100644 index 0000000..b1a1d84 --- /dev/null +++ b/corrosion/cmake/CorrosionGenerator.cmake @@ -0,0 +1,338 @@ +function(_cargo_metadata out manifest) + set(OPTIONS LOCKED FROZEN) + set(ONE_VALUE_KEYWORDS "") + set(MULTI_VALUE_KEYWORDS "") + cmake_parse_arguments(PARSE_ARGV 2 CM "${OPTIONS}" "${ONE_VALUE_KEYWORDS}" "${MULTI_VALUE_KEYWORDS}") + + list(APPEND CMAKE_MESSAGE_CONTEXT "_cargo_metadata") + + if(DEFINED CM_UNPARSED_ARGUMENTS) + message(FATAL_ERROR "Internal error - unexpected arguments: ${CM_UNPARSED_ARGUMENTS}") + elseif(DEFINED CM_KEYWORDS_MISSING_VALUES) + message(FATAL_ERROR "Internal error - the following keywords had no associated value(s):" + "${CM_KEYWORDS_MISSING_VALUES}") + endif() + + set(cargo_locked "") + set(cargo_frozen "") + if(LOCKED) + set(cargo_locked "--locked") + endif() + if(FROZEN) + set(cargo_frozen "--frozen") + endif() + execute_process( + COMMAND + ${CMAKE_COMMAND} -E env + "CARGO_BUILD_RUSTC=${_CORROSION_RUSTC}" + "${_CORROSION_CARGO}" + metadata + --manifest-path "${manifest}" + --format-version 1 + # We don't care about non-workspace dependencies + --no-deps + ${cargo_locked} + ${cargo_frozen} + + OUTPUT_VARIABLE json + COMMAND_ERROR_IS_FATAL ANY + ) + + set(${out} "${json}" PARENT_SCOPE) +endfunction() + +# Add targets (crates) of one package +function(_generator_add_package_targets) + set(OPTIONS NO_LINKER_OVERRIDE) + set(ONE_VALUE_KEYWORDS + WORKSPACE_MANIFEST_PATH + PACKAGE_MANIFEST_PATH + PACKAGE_NAME + PACKAGE_VERSION + TARGETS_JSON + OUT_CREATED_TARGETS) + set(MULTI_VALUE_KEYWORDS CRATE_TYPES OVERRIDE_CRATE_TYPE_ARGS) + cmake_parse_arguments(PARSE_ARGV 0 GAPT "${OPTIONS}" "${ONE_VALUE_KEYWORDS}" "${MULTI_VALUE_KEYWORDS}") + + if(DEFINED GAPT_UNPARSED_ARGUMENTS) + message(FATAL_ERROR "Internal error - unexpected arguments: ${GAPT_UNPARSED_ARGUMENTS}") + elseif(DEFINED GAPT_KEYWORDS_MISSING_VALUES) + message(FATAL_ERROR "Internal error - the following keywords had no associated value(s):" + "${GAPT_KEYWORDS_MISSING_VALUES}") + endif() + + _corrosion_option_passthrough_helper(NO_LINKER_OVERRIDE GAPT no_linker_override) + + set(workspace_manifest_path "${GAPT_WORKSPACE_MANIFEST_PATH}") + set(package_manifest_path "${GAPT_PACKAGE_MANIFEST_PATH}") + set(package_name "${GAPT_PACKAGE_NAME}") + set(package_version "${GAPT_PACKAGE_VERSION}") + set(targets "${GAPT_TARGETS_JSON}") + set(out_created_targets "${GAPT_OUT_CREATED_TARGETS}") + set(crate_types "${GAPT_CRATE_TYPES}") + if(DEFINED GAPT_OVERRIDE_CRATE_TYPE_ARGS) + list(GET GAPT_OVERRIDE_CRATE_TYPE_ARGS 0 override_crate_name_list_ref) + list(GET GAPT_OVERRIDE_CRATE_TYPE_ARGS 1 override_crate_types_list_ref) + endif() + + set(corrosion_targets "") + + file(TO_CMAKE_PATH "${package_manifest_path}" manifest_path) + + string(JSON targets_len LENGTH "${targets}") + math(EXPR targets_len-1 "${targets_len} - 1") + + message(DEBUG "Found ${targets_len} targets in package ${package_name}") + + foreach(ix RANGE ${targets_len-1}) + string(JSON target GET "${targets}" ${ix}) + string(JSON target_name GET "${target}" "name") + string(JSON target_kind GET "${target}" "kind") + string(JSON target_kind_len LENGTH "${target_kind}") + + math(EXPR target_kind_len-1 "${target_kind_len} - 1") + set(kinds) + unset(override_package_crate_type) + # OVERRIDE_CRATE_TYPE is more specific than the CRATE_TYPES argument to corrosion_import_crate, and thus takes + # priority. + if(DEFINED GAPT_OVERRIDE_CRATE_TYPE_ARGS) + foreach(override_crate_name override_crate_types IN ZIP_LISTS ${override_crate_name_list_ref} ${override_crate_types_list_ref}) + if("${override_crate_name}" STREQUAL "${target_name}") + message(DEBUG "Forcing crate ${target_name} to crate-type(s): ${override_crate_types}.") + # Convert to CMake list + string(REPLACE "," ";" kinds "${override_crate_types}") + break() + endif() + endforeach() + else() + foreach(ix RANGE ${target_kind_len-1}) + string(JSON kind GET "${target_kind}" ${ix}) + if(NOT crate_types OR ${kind} IN_LIST crate_types) + list(APPEND kinds ${kind}) + endif() + endforeach() + endif() + if(TARGET "${target_name}" + AND ("staticlib" IN_LIST kinds OR "cdylib" IN_LIST kinds OR "bin" IN_LIST kinds) + ) + message(WARNING "Failed to import Rust crate ${target_name} (kind: `${target_kind}`) because a target " + "with the same name already exists. Skipping this target.\n" + "Help: If you are importing a package which exposes both a `lib` and " + "a `bin` target, please consider explicitly naming the targets in your `Cargo.toml` manifest.\n" + "Note: If you have multiple different packages which have targets with the same name, please note that " + "this is currently not supported by Corrosion. Feel free to open an issue on Github to request " + "supporting this scenario." + ) + # Skip this target to prevent a hard error. + continue() + endif() + + if("staticlib" IN_LIST kinds OR "cdylib" IN_LIST kinds) + # Explicitly set library names have always been forbidden from using dashes (by cargo). + # Starting with Rust 1.79, names inherited from the package name will have dashes replaced + # by underscores too. Corrosion will thus replace dashes with underscores, to make the target + # name consistent independent of the Rust version. `bin` target names are not affected. + # See https://github.com/corrosion-rs/corrosion/issues/501 for more details. + string(REPLACE "\-" "_" target_name "${target_name}") + + set(archive_byproducts "") + set(shared_lib_byproduct "") + set(pdb_byproduct "") + + add_library(${target_name} INTERFACE) + _corrosion_initialize_properties(${target_name}) + _corrosion_add_library_target( + WORKSPACE_MANIFEST_PATH "${workspace_manifest_path}" + TARGET_NAME "${target_name}" + LIB_KINDS ${kinds} + OUT_ARCHIVE_OUTPUT_BYPRODUCTS archive_byproducts + OUT_SHARED_LIB_BYPRODUCTS shared_lib_byproduct + OUT_PDB_BYPRODUCT pdb_byproduct + ) + + set(byproducts "") + list(APPEND byproducts "${archive_byproducts}" "${shared_lib_byproduct}" "${pdb_byproduct}") + + set(cargo_build_out_dir "") + _add_cargo_build( + cargo_build_out_dir + PACKAGE ${package_name} + TARGET ${target_name} + MANIFEST_PATH "${manifest_path}" + WORKSPACE_MANIFEST_PATH "${workspace_manifest_path}" + TARGET_KINDS "${kinds}" + BYPRODUCTS "${byproducts}" + # Optional + ${no_linker_override} + ) + if(archive_byproducts) + _corrosion_copy_byproducts( + ${target_name} ARCHIVE_OUTPUT_DIRECTORY "${cargo_build_out_dir}" "${archive_byproducts}" + ) + endif() + if(shared_lib_byproduct) + _corrosion_copy_byproducts( + ${target_name} LIBRARY_OUTPUT_DIRECTORY "${cargo_build_out_dir}" "${shared_lib_byproduct}" + ) + endif() + if(pdb_byproduct) + _corrosion_copy_byproducts( + ${target_name} "PDB_OUTPUT_DIRECTORY;LIBRARY_OUTPUT_DIRECTORY" "${cargo_build_out_dir}" "${pdb_byproduct}" + ) + endif() + list(APPEND corrosion_targets ${target_name}) + set_property(TARGET "${target_name}" PROPERTY COR_CARGO_PACKAGE_NAME "${package_name}" ) + # Note: "bin" is mutually exclusive with "staticlib/cdylib", since `bin`s are seperate crates from libraries. + elseif("bin" IN_LIST kinds) + set(bin_byproduct "") + set(pdb_byproduct "") + add_executable(${target_name} IMPORTED GLOBAL) + _corrosion_initialize_properties(${target_name}) + _corrosion_add_bin_target("${workspace_manifest_path}" "${target_name}" + "bin_byproduct" "pdb_byproduct" + ) + + set(byproducts "") + list(APPEND byproducts "${bin_byproduct}" "${pdb_byproduct}") + + set(cargo_build_out_dir "") + _add_cargo_build( + cargo_build_out_dir + PACKAGE "${package_name}" + TARGET "${target_name}" + MANIFEST_PATH "${manifest_path}" + WORKSPACE_MANIFEST_PATH "${workspace_manifest_path}" + TARGET_KINDS "bin" + BYPRODUCTS "${byproducts}" + # Optional + ${no_linker_override} + ) + _corrosion_copy_byproducts( + ${target_name} RUNTIME_OUTPUT_DIRECTORY "${cargo_build_out_dir}" "${bin_byproduct}" + ) + if(pdb_byproduct) + _corrosion_copy_byproducts( + ${target_name} "PDB_OUTPUT_DIRECTORY;RUNTIME_OUTPUT_DIRECTORY" "${cargo_build_out_dir}" "${pdb_byproduct}" + ) + endif() + list(APPEND corrosion_targets ${target_name}) + set_property(TARGET "${target_name}" PROPERTY COR_CARGO_PACKAGE_NAME "${package_name}" ) + else() + # ignore other kinds (like examples, tests, build scripts, ...) + endif() + endforeach() + + if(NOT corrosion_targets) + message(DEBUG "No relevant targets found in package ${package_name} - Ignoring") + else() + set_target_properties(${corrosion_targets} PROPERTIES INTERFACE_COR_PACKAGE_MANIFEST_PATH "${package_manifest_path}") + endif() + set(${out_created_targets} "${corrosion_targets}" PARENT_SCOPE) + +endfunction() + +# Add all cargo targets defined in the packages defined in the Cargo.toml manifest at +# `MANIFEST_PATH`. +function(_generator_add_cargo_targets) + set(options NO_LINKER_OVERRIDE) + set(one_value_args MANIFEST_PATH IMPORTED_CRATES) + set(multi_value_args CRATES CRATE_TYPES OVERRIDE_CRATE_TYPE_ARGS) + cmake_parse_arguments( + GGC + "${options}" + "${one_value_args}" + "${multi_value_args}" + ${ARGN} + ) + list(APPEND CMAKE_MESSAGE_CONTEXT "_add_cargo_targets") + + _corrosion_option_passthrough_helper(NO_LINKER_OVERRIDE GGC no_linker_override) + _corrosion_arg_passthrough_helper(CRATE_TYPES GGC crate_types) + _corrosion_arg_passthrough_helper(OVERRIDE_CRATE_TYPE_ARGS GGC override_crate_types) + + _cargo_metadata(json "${GGC_MANIFEST_PATH}") + string(JSON packages GET "${json}" "packages") + string(JSON workspace_members GET "${json}" "workspace_members") + + string(JSON pkgs_len LENGTH "${packages}") + math(EXPR pkgs_len-1 "${pkgs_len} - 1") + + string(JSON ws_mems_len LENGTH ${workspace_members}) + math(EXPR ws_mems_len-1 "${ws_mems_len} - 1") + + set(created_targets "") + set(available_package_names "") + foreach(ix RANGE ${pkgs_len-1}) + string(JSON pkg GET "${packages}" ${ix}) + string(JSON pkg_id GET "${pkg}" "id") + string(JSON pkg_name GET "${pkg}" "name") + string(JSON pkg_manifest_path GET "${pkg}" "manifest_path") + string(JSON pkg_version GET "${pkg}" "version") + list(APPEND available_package_names "${pkg_name}") + + if(DEFINED GGC_CRATES) + if(NOT pkg_name IN_LIST GGC_CRATES) + continue() + endif() + endif() + + # probably this loop is not necessary at all, since when using --no-deps, the + # contents of packages should already be only workspace members! + unset(pkg_is_ws_member) + foreach(ix RANGE ${ws_mems_len-1}) + string(JSON ws_mem GET "${workspace_members}" ${ix}) + if(ws_mem STREQUAL pkg_id) + set(pkg_is_ws_member YES) + break() + endif() + endforeach() + + if(NOT DEFINED pkg_is_ws_member) + # Since we pass `--no-deps` to cargo metadata now, I think this situation can't happen, but lets check for + # it anyway, just to discover any potential issues. + # If nobody complains for a while, it should be safe to remove this check and the previous loop, which + # should speed up the configuration process. + message(WARNING "The package `${pkg_name}` unexpectedly is not part of the workspace." + "Please open an issue at corrosion with some background information on the package" + ) + endif() + + string(JSON targets GET "${pkg}" "targets") + + _generator_add_package_targets( + WORKSPACE_MANIFEST_PATH "${GGC_MANIFEST_PATH}" + PACKAGE_MANIFEST_PATH "${pkg_manifest_path}" + PACKAGE_NAME "${pkg_name}" + PACKAGE_VERSION "${pkg_version}" + TARGETS_JSON "${targets}" + OUT_CREATED_TARGETS curr_created_targets + ${no_linker_override} + ${crate_types} + ${override_crate_types} + ) + list(APPEND created_targets "${curr_created_targets}") + endforeach() + + if(NOT created_targets) + set(crates_error_message "") + if(DEFINED GGC_CRATES) + set(crates_error_message "\n`corrosion_import_crate()` was called with the `CRATES` " + "parameter set to `${GGC_CRATES}`. Corrosion will only attempt to import packages matching " + "names from this list." + ) + endif() + message(FATAL_ERROR + "Found no targets in ${pkgs_len} packages." + ${crates_error_message}. + "\nPlease keep in mind that corrosion will only import Rust `bin` targets or" + "`staticlib` or `cdylib` library targets." + "The following packages were found in the Manifest: ${available_package_names}" + ) + else() + message(DEBUG "Corrosion created the following CMake targets: ${created_targets}") + endif() + + if(GGC_IMPORTED_CRATES) + set(${GGC_IMPORTED_CRATES} "${created_targets}" PARENT_SCOPE) + endif() +endfunction() diff --git a/corrosion/cmake/FindRust.cmake b/corrosion/cmake/FindRust.cmake new file mode 100644 index 0000000..413645a --- /dev/null +++ b/corrosion/cmake/FindRust.cmake @@ -0,0 +1,904 @@ +#[=======================================================================[.rst: +FindRust +-------- + +Find Rust + +This module finds an installed rustc compiler and the cargo build tool. If Rust +is managed by rustup it determines the available toolchains and returns a +concrete Rust version, not a rustup proxy. + +#]=======================================================================] + +cmake_minimum_required(VERSION 3.12) + +option( + Rust_RUSTUP_INSTALL_MISSING_TARGET + "Use Rustup to automatically install missing targets instead of giving up" + OFF +) + +# search for Cargo here and set up a bunch of cool flags and stuff +include(FindPackageHandleStandardArgs) + +list(APPEND CMAKE_MESSAGE_CONTEXT "FindRust") + +# Print error message and return. Should not be used from inside functions +macro(_findrust_failed) + if("${Rust_FIND_REQUIRED}") + message(FATAL_ERROR ${ARGN}) + elseif(NOT "${Rust_FIND_QUIETLY}") + message(WARNING ${ARGN}) + endif() + set(Rust_FOUND "") + return() +endmacro() + +# Checks if the actual version of a Rust toolchain matches the VERSION requirements specified in find_package. +function(_findrust_version_ok ACTUAL_VERSION OUT_IS_OK) + if(DEFINED Rust_FIND_VERSION_RANGE) + if(Rust_FIND_VERSION_RANGE_MAX STREQUAL "INCLUDE") + set(COMPARSION_OPERATOR "VERSION_LESS_EQUAL") + elseif(Rust_FIND_VERSION_RANGE_MAX STREQUAL "EXCLUDE") + set(COMPARSION_OPERATOR "VERSION_LESS") + else() + message(FATAL_ERROR "Unexpected value in `_FIND_VERSION_RANGE_MAX`: " + "`${Rust_FIND_VERSION_RANGE_MAX}`.") + endif() + if(("${ACTUAL_VERSION}" VERSION_GREATER_EQUAL "${Rust_FIND_VERSION_RANGE_MIN}") + AND + ( "${ACTUAL_VERSION}" ${COMPARSION_OPERATOR} "${Rust_FIND_VERSION_RANGE_MAX}" ) + ) + set("${OUT_IS_OK}" TRUE PARENT_SCOPE) + else() + set("${OUT_IS_OK}" FALSE PARENT_SCOPE) + endif() + elseif(DEFINED Rust_FIND_VERSION) + if(Rust_VERSION_EXACT) + set(COMPARISON_OPERATOR VERSION_EQUAL) + else() + set(COMPARISON_OPERATOR VERSION_GREATER_EQUAL) + endif() + if(_TOOLCHAIN_${_TOOLCHAIN_SELECTED}_VERSION "${COMPARISON_OPERATOR}" Rust_FIND_VERSION) + set("${OUT_IS_OK}" TRUE PARENT_SCOPE) + else() + set("${OUT_IS_OK}" FALSE PARENT_SCOPE) + endif() + else() + # if no VERSION requirement was specified, the version is always okay. + set("${OUT_IS_OK}" TRUE PARENT_SCOPE) + endif() +endfunction() + +function(_corrosion_strip_target_triple input_triple_or_path output_triple) + # If the target_triple is a path to a custom target specification file, then strip everything + # except the filename from `target_triple`. + get_filename_component(target_triple_ext "${input_triple_or_path}" EXT) + set(target_triple "${input_triple_or_path}") + if(target_triple_ext) + if(target_triple_ext STREQUAL ".json") + get_filename_component(target_triple "${input_triple_or_path}" NAME_WE) + endif() + endif() + set(${output_triple} "${target_triple}" PARENT_SCOPE) +endfunction() + +function(_corrosion_parse_target_triple target_triple out_arch out_vendor out_os out_env) + _corrosion_strip_target_triple(${target_triple} target_triple) + + # The vendor part may be left out from the target triple, and since `env` is also optional, + # we determine if vendor is present by matching against a list of known vendors. + set(known_vendors + "apple" + "esp[a-z0-9]*" # espressif, e.g. riscv32imc-esp-espidf or xtensa-esp32s3-none-elf + "fortanix" + "kmc" + "pc" + "nintendo" + "nvidia" + "openwrt" + "alpine" + "chimera" + "unikraft" + "unknown" + "uwp" # aarch64-uwp-windows-msvc + "wrs" # e.g. aarch64-wrs-vxworks + "sony" + "sun" + ) + # todo: allow users to add additional vendors to the list via a cmake variable. + list(JOIN known_vendors "|" known_vendors_joined) + # vendor is optional - We detect if vendor is present by matching against a known list of + # vendors. The next field is the OS, which we assume to always be present, while the last field + # is again optional and contains the environment. + string(REGEX MATCH + "^([a-z0-9_\.]+)-((${known_vendors_joined})-)?([a-z0-9_]+)(-([a-z0-9_]+))?$" + whole_match + "${target_triple}" + ) + if((NOT whole_match) AND (NOT CORROSION_NO_WARN_PARSE_TARGET_TRIPLE_FAILED)) + message(WARNING "Failed to parse target-triple `${target_triple}`." + "Corrosion determines some information about the output artifacts based on OS " + "specified in the Rust target-triple.\n" + "Currently this is relevant for windows and darwin (mac) targets, since file " + "extensions differ.\n" + "Note: If you are targeting a different OS you can suppress this warning by" + " setting the CMake cache variable " + "`CORROSION_NO_WARN_PARSE_TARGET_TRIPLE_FAILED`." + "Please consider opening an issue on github if you you need to add a new vendor to the list." + ) + endif() + + message(DEBUG "Parsed Target triple: arch: ${CMAKE_MATCH_1}, vendor: ${CMAKE_MATCH_3}, " + "OS: ${CMAKE_MATCH_4}, env: ${CMAKE_MATCH_6}") + + set("${out_arch}" "${CMAKE_MATCH_1}" PARENT_SCOPE) + set("${out_vendor}" "${CMAKE_MATCH_3}" PARENT_SCOPE) + set("${out_os}" "${CMAKE_MATCH_4}" PARENT_SCOPE) + set("${out_env}" "${CMAKE_MATCH_6}" PARENT_SCOPE) +endfunction() + +function(_corrosion_determine_libs_new target_triple out_libs out_flags) + set(package_dir "${CMAKE_BINARY_DIR}/corrosion/required_libs") + # Cleanup on reconfigure to get a cleans state (in case we change something in the future) + file(REMOVE_RECURSE "${package_dir}") + file(MAKE_DIRECTORY "${package_dir}") + set(manifest "[package]\nname = \"required_libs\"\nedition = \"2018\"\nversion = \"0.1.0\"\n") + string(APPEND manifest "\n[lib]\ncrate-type=[\"staticlib\"]\npath = \"lib.rs\"\n") + string(APPEND manifest "\n[workspace]\n") + file(WRITE "${package_dir}/Cargo.toml" "${manifest}") + file(WRITE "${package_dir}/lib.rs" "pub fn add(left: usize, right: usize) -> usize {left + right}\n") + + execute_process( + COMMAND ${CMAKE_COMMAND} -E env + "CARGO_BUILD_RUSTC=${Rust_COMPILER_CACHED}" + ${Rust_CARGO_CACHED} rustc --verbose --color never --target=${target_triple} -- --print=native-static-libs + WORKING_DIRECTORY "${CMAKE_BINARY_DIR}/corrosion/required_libs" + RESULT_VARIABLE cargo_build_result + ERROR_VARIABLE cargo_build_error_message + ) + if(cargo_build_result) + message(DEBUG "Determining required native libraries - failed: ${cargo_build_result}.") + message(TRACE "The cargo build error was: ${cargo_build_error_message}") + message(DEBUG "Note: This is expected for Rust targets without std support") + return() + else() + # The pattern starts with `native-static-libs:` and goes to the end of the line. + if(cargo_build_error_message MATCHES "native-static-libs: ([^\r\n]+)\r?\n") + string(REPLACE " " ";" "libs_list" "${CMAKE_MATCH_1}") + set(stripped_lib_list "") + set(flag_list "") + + set(was_last_framework OFF) + foreach(lib ${libs_list}) + # merge -framework;lib -> "-framework lib" as CMake does de-duplication of link libraries, and -framework prefix is required + if (lib STREQUAL "-framework") + set(was_last_framework ON) + continue() + endif() + if (was_last_framework) + list(APPEND stripped_lib_list "-framework ${lib}") + set(was_last_framework OFF) + continue() + endif() + + # Flags start with / for MSVC + if (lib MATCHES "^/" AND ${target_triple} MATCHES "msvc$") + # Windows GNU uses the compiler to invoke the linker, so -Wl, prefix is needed + # https://gitlab.kitware.com/cmake/cmake/-/blob/9bed4f4d817f139f0c2e050d7420e1e247949fe4/Modules/Platform/Windows-GNU.cmake#L156 + if (CMAKE_CXX_COMPILER_FRONTEND_VARIANT STREQUAL "GNU") + list(APPEND flag_list "-Wl,${lib}") + else() + list(APPEND flag_list "${lib}") + endif() + else() + # Strip leading `-l` (unix) and potential .lib suffix (windows) + string(REGEX REPLACE "^-l" "" "stripped_lib" "${lib}") + string(REGEX REPLACE "\.lib$" "" "stripped_lib" "${stripped_lib}") + list(APPEND stripped_lib_list "${stripped_lib}") + endif() + endforeach() + set(libs_list "${stripped_lib_list}") + # We leave it up to the C/C++ executable that links in the Rust static-library + # to determine which version of the msvc runtime library it should select. + list(FILTER libs_list EXCLUDE REGEX "^msvcrtd?") + list(FILTER flag_list EXCLUDE REGEX "^(-Wl,)?/defaultlib:msvcrtd?") + else() + message(DEBUG "Determining required native libraries - failed: Regex match failure.") + message(DEBUG "`native-static-libs` not found in: `${cargo_build_error_message}`") + return() + endif() + endif() + set("${out_libs}" "${libs_list}" PARENT_SCOPE) + set("${out_flags}" "${flag_list}" PARENT_SCOPE) +endfunction() + +if (NOT "${Rust_TOOLCHAIN}" STREQUAL "$CACHE{Rust_TOOLCHAIN}") + # Promote Rust_TOOLCHAIN to a cache variable if it is not already a cache variable + set(Rust_TOOLCHAIN ${Rust_TOOLCHAIN} CACHE STRING "Requested rustup toolchain" FORCE) +endif() + +set(_RESOLVE_RUSTUP_TOOLCHAINS_DESC "Indicates whether to descend into the toolchain pointed to by rustup") +set(Rust_RESOLVE_RUSTUP_TOOLCHAINS ON CACHE BOOL ${_RESOLVE_RUSTUP_TOOLCHAINS_DESC}) + +# This block checks to see if we're prioritizing a rustup-managed toolchain. +if (DEFINED Rust_TOOLCHAIN) + # If the user specifies `Rust_TOOLCHAIN`, then look for `rustup` first, rather than `rustc`. + find_program(Rust_RUSTUP rustup PATHS "$ENV{HOME}/.cargo/bin") + if(NOT Rust_RUSTUP) + if(NOT "${Rust_FIND_QUIETLY}") + message( + WARNING "CMake variable `Rust_TOOLCHAIN` specified, but `rustup` was not found. " + "Ignoring toolchain and looking for a Rust toolchain not managed by rustup.") + endif() + endif() +else() + # If we aren't definitely using a rustup toolchain, look for rustc first - the user may have + # a toolchain installed via a method other than rustup higher in the PATH, which should be + # preferred. However, if the first-found rustc is a rustup proxy, then we'll revert to + # finding the preferred toolchain via rustup. + + # Uses `Rust_COMPILER` to let user-specified `rustc` win. But we will still "override" the + # user's setting if it is pointing to `rustup`. Default rustup install path is provided as a + # backup if a toolchain cannot be found in the user's PATH. + + if (DEFINED Rust_COMPILER) + set(_Rust_COMPILER_TEST "${Rust_COMPILER}") + set(_USER_SPECIFIED_RUSTC ON) + if(NOT (EXISTS "${_Rust_COMPILER_TEST}" AND NOT IS_DIRECTORY "${_Rust_COMPILER_TEST}")) + set(_ERROR_MESSAGE "Rust_COMPILER was set to `${Rust_COMPILER}`, but this file does " + "not exist." + ) + _findrust_failed(${_ERROR_MESSAGE}) + return() + endif() + else() + find_program(_Rust_COMPILER_TEST rustc PATHS "$ENV{HOME}/.cargo/bin") + if(NOT EXISTS "${_Rust_COMPILER_TEST}") + cmake_path(CONVERT "$ENV{HOME}/.cargo/bin" TO_CMAKE_PATH_LIST _cargo_bin_dir) + set(_ERROR_MESSAGE "`rustc` not found in PATH or `${_cargo_bin_dir}`.\n" + "Hint: Check if `rustc` is in PATH or manually specify the location " + "by setting `Rust_COMPILER` to the path to `rustc`.") + _findrust_failed(${_ERROR_MESSAGE}) + endif() + endif() + + # Check if the discovered rustc is actually a "rustup" proxy. + execute_process( + COMMAND + ${CMAKE_COMMAND} -E env + RUSTUP_FORCE_ARG0=rustup + "${_Rust_COMPILER_TEST}" --version + OUTPUT_VARIABLE _RUSTC_VERSION_RAW + ERROR_VARIABLE _RUSTC_VERSION_STDERR + RESULT_VARIABLE _RUSTC_VERSION_RESULT + ) + + if(NOT (_RUSTC_VERSION_RESULT EQUAL "0")) + _findrust_failed("`${_Rust_COMPILER_TEST} --version` failed with ${_RUSTC_VERSION_RESULT}\n" + "rustc stderr:\n${_RUSTC_VERSION_STDERR}" + ) + endif() + + if (_RUSTC_VERSION_RAW MATCHES "rustup [0-9\\.]+") + if (_USER_SPECIFIED_RUSTC) + message( + WARNING "User-specified Rust_COMPILER pointed to rustup's rustc proxy. Corrosion's " + "FindRust will always try to evaluate to an actual Rust toolchain, and so the " + "user-specified Rust_COMPILER will be discarded in favor of the default " + "rustup-managed toolchain." + ) + + unset(Rust_COMPILER) + unset(Rust_COMPILER CACHE) + endif() + + # Get `rustup` next to the `rustc` proxy + get_filename_component(_RUST_PROXIES_PATH "${_Rust_COMPILER_TEST}" DIRECTORY) + find_program(Rust_RUSTUP rustup HINTS "${_RUST_PROXIES_PATH}" NO_DEFAULT_PATH) + endif() + + unset(_Rust_COMPILER_TEST CACHE) +endif() + +# At this point, the only thing we should have evaluated is a path to `rustup` _if that's what the +# best source for a Rust toolchain was determined to be_. +if (NOT Rust_RUSTUP) + set(Rust_RESOLVE_RUSTUP_TOOLCHAINS OFF CACHE BOOL ${_RESOLVE_RUSTUP_TOOLCHAINS_DESC} FORCE) +endif() + +# List of user variables that will override any toolchain-provided setting +set(_Rust_USER_VARS Rust_COMPILER Rust_CARGO Rust_CARGO_TARGET Rust_CARGO_HOST_TARGET) +foreach(_VAR ${_Rust_USER_VARS}) + if (DEFINED "${_VAR}") + set(${_VAR}_CACHED "${${_VAR}}" CACHE INTERNAL "Internal cache of ${_VAR}") + else() + unset(${_VAR}_CACHED CACHE) + endif() +endforeach() + +# Discover what toolchains are installed by rustup, if the discovered `rustc` is a proxy from +# `rustup` and the user hasn't explicitly requested to override this behavior, then select either +# the default toolchain, or the requested toolchain Rust_TOOLCHAIN +if (Rust_RESOLVE_RUSTUP_TOOLCHAINS) + execute_process( + COMMAND + "${Rust_RUSTUP}" toolchain list --verbose + OUTPUT_VARIABLE _TOOLCHAINS_RAW + ) + + string(REPLACE "\n" ";" _TOOLCHAINS_RAW "${_TOOLCHAINS_RAW}") + set(_DISCOVERED_TOOLCHAINS "") + set(_DISCOVERED_TOOLCHAINS_RUSTC_PATH "") + set(_DISCOVERED_TOOLCHAINS_CARGO_PATH "") + set(_DISCOVERED_TOOLCHAINS_VERSION "") + + foreach(_TOOLCHAIN_RAW ${_TOOLCHAINS_RAW}) + if (_TOOLCHAIN_RAW MATCHES "([a-zA-Z0-9\\._\\-]+)[ \t\r\n]?(\\(active\\)|\\(active, default\\)|\\(default\\) \\(override\\)|\\(default\\)|\\(override\\))?[ \t\r\n]+(.+)") + set(_TOOLCHAIN "${CMAKE_MATCH_1}") + set(_TOOLCHAIN_TYPE "${CMAKE_MATCH_2}") + + set(_TOOLCHAIN_PATH "${CMAKE_MATCH_3}") + set(_TOOLCHAIN_${_TOOLCHAIN}_PATH "${CMAKE_MATCH_3}") + + if (_TOOLCHAIN_TYPE MATCHES ".*\\((active, )?default\\).*") + set(_TOOLCHAIN_DEFAULT "${_TOOLCHAIN}") + endif() + + if (_TOOLCHAIN_TYPE MATCHES ".*\\((active|override)\\).*") + set(_TOOLCHAIN_OVERRIDE "${_TOOLCHAIN}") + endif() + + execute_process( + COMMAND + "${_TOOLCHAIN_PATH}/bin/rustc" --version + OUTPUT_VARIABLE _TOOLCHAIN_RAW_VERSION + ) + if (_TOOLCHAIN_RAW_VERSION MATCHES "rustc ([0-9]+)\\.([0-9]+)\\.([0-9]+)(-nightly)?") + list(APPEND _DISCOVERED_TOOLCHAINS "${_TOOLCHAIN}") + list(APPEND _DISCOVERED_TOOLCHAINS_RUSTC_PATH "${_TOOLCHAIN_PATH}/bin/rustc") + list(APPEND _DISCOVERED_TOOLCHAINS_VERSION "${CMAKE_MATCH_1}.${CMAKE_MATCH_2}.${CMAKE_MATCH_3}") + + # We need this variable to determine the default toolchain, since `foreach(... IN ZIP_LISTS ...)` + # requires CMake 3.17. As a workaround we define this variable to lookup the version when iterating + # through the `_DISCOVERED_TOOLCHAINS` lists. + set(_TOOLCHAIN_${_TOOLCHAIN}_VERSION "${CMAKE_MATCH_1}.${CMAKE_MATCH_2}.${CMAKE_MATCH_3}") + if(CMAKE_MATCH_4) + set(_TOOLCHAIN_${_TOOLCHAIN}_IS_NIGHTLY "TRUE") + else() + set(_TOOLCHAIN_${_TOOLCHAIN}_IS_NIGHTLY "FALSE") + endif() + set(_suffix "") + if(CMAKE_HOST_WIN32) + set(_suffix ".exe") + endif() + if(EXISTS "${_TOOLCHAIN_PATH}/bin/cargo${_suffix}") + list(APPEND _DISCOVERED_TOOLCHAINS_CARGO_PATH "${_TOOLCHAIN_PATH}/bin/cargo") + else() + list(APPEND _DISCOVERED_TOOLCHAINS_CARGO_PATH "NOTFOUND") + endif() + else() + message(AUTHOR_WARNING "Unexpected output from `rustc --version` for Toolchain `${_TOOLCHAIN}`: " + "`${_TOOLCHAIN_RAW_VERSION}`.\n" + "Ignoring this toolchain (Path: `${_TOOLCHAIN_PATH}`)." + ) + endif() + else() + message(AUTHOR_WARNING "Didn't recognize toolchain: ${_TOOLCHAIN_RAW}. Ignoring this toolchain.\n" + "Rustup toolchain list output( `${Rust_RUSTUP} toolchain list --verbose`):\n" + "${_TOOLCHAINS_RAW}" + ) + endif() + endforeach() + + # Expose a list of available rustup toolchains. + list(LENGTH _DISCOVERED_TOOLCHAINS _toolchain_len) + list(LENGTH _DISCOVERED_TOOLCHAINS_RUSTC_PATH _toolchain_rustc_len) + list(LENGTH _DISCOVERED_TOOLCHAINS_CARGO_PATH _toolchain_cargo_len) + list(LENGTH _DISCOVERED_TOOLCHAINS_VERSION _toolchain_version_len) + if(NOT + (_toolchain_len EQUAL _toolchain_rustc_len + AND _toolchain_cargo_len EQUAL _toolchain_version_len + AND _toolchain_len EQUAL _toolchain_cargo_len) + ) + message(FATAL_ERROR "Internal error - list length mismatch." + "List lengths: ${_toolchain_len} toolchains, ${_toolchain_rustc_len} rustc, ${_toolchain_cargo_len} cargo," + " ${_toolchain_version_len} version. The lengths should be the same." + ) + endif() + + set(Rust_RUSTUP_TOOLCHAINS "${_DISCOVERED_TOOLCHAINS}" CACHE INTERNAL "List of available Rustup toolchains") + set(Rust_RUSTUP_TOOLCHAINS_RUSTC_PATH "${_DISCOVERED_TOOLCHAINS_RUSTC_PATH}" + CACHE INTERNAL + "List of the rustc paths corresponding to the toolchain at the same index in `Rust_RUSTUP_TOOLCHAINS`." + ) + set(Rust_RUSTUP_TOOLCHAINS_CARGO_PATH "${_DISCOVERED_TOOLCHAINS_CARGO_PATH}" + CACHE INTERNAL + "List of the cargo paths corresponding to the toolchain at the same index in `Rust_RUSTUP_TOOLCHAINS`. \ + May also be `NOTFOUND` if the toolchain does not have a cargo executable." + ) + set(Rust_RUSTUP_TOOLCHAINS_VERSION "${_DISCOVERED_TOOLCHAINS_VERSION}" + CACHE INTERNAL + "List of the rust toolchain version corresponding to the toolchain at the same index in \ + `Rust_RUSTUP_TOOLCHAINS`." + ) + + # Rust_TOOLCHAIN is preferred over a requested version if it is set. + if (NOT DEFINED Rust_TOOLCHAIN) + if (NOT DEFINED _TOOLCHAIN_OVERRIDE) + set(_TOOLCHAIN_SELECTED "${_TOOLCHAIN_DEFAULT}") + else() + set(_TOOLCHAIN_SELECTED "${_TOOLCHAIN_OVERRIDE}") + endif() + # Check default toolchain first. + _findrust_version_ok("_TOOLCHAIN_${_TOOLCHAIN_SELECTED}_VERSION" _VERSION_OK) + if(NOT "${_VERSION_OK}") + foreach(_TOOLCHAIN "${_DISCOVERED_TOOLCHAINS}") + _findrust_version_ok("_TOOLCHAIN_${_TOOLCHAIN}_VERSION" _VERSION_OK) + if("${_VERSION_OK}") + set(_TOOLCHAIN_SELECTED "${_TOOLCHAIN}") + break() + endif() + endforeach() + # Check if we found a suitable version in the for loop. + if(NOT "${_VERSION_OK}") + string(REPLACE ";" "\n" _DISCOVERED_TOOLCHAINS "${_DISCOVERED_TOOLCHAINS}") + _findrust_failed("Failed to find a Rust toolchain matching the version requirements of " + "${Rust_FIND_VERSION}. Available toolchains: ${_DISCOVERED_TOOLCHAINS}") + endif() + endif() + endif() + + set(Rust_TOOLCHAIN "${_TOOLCHAIN_SELECTED}" CACHE STRING "The rustup toolchain to use") + set_property(CACHE Rust_TOOLCHAIN PROPERTY STRINGS "${_DISCOVERED_TOOLCHAINS}") + + if(NOT Rust_FIND_QUIETLY) + message(STATUS "Rust Toolchain: ${Rust_TOOLCHAIN}") + endif() + + if (NOT Rust_TOOLCHAIN IN_LIST _DISCOVERED_TOOLCHAINS) + # If the precise toolchain wasn't found, try appending the default host + execute_process( + COMMAND + "${Rust_RUSTUP}" show + RESULT_VARIABLE _SHOW_RESULT + OUTPUT_VARIABLE _SHOW_RAW + ) + if(NOT "${_SHOW_RESULT}" EQUAL "0") + _findrust_failed("Command `${Rust_RUSTUP} show` failed") + endif() + + if (_SHOW_RAW MATCHES "Default host: ([a-zA-Z0-9_\\-]*)\n") + set(_DEFAULT_HOST "${CMAKE_MATCH_1}") + else() + _findrust_failed("Failed to parse \"Default host\" from `${Rust_RUSTUP} show`. Got: ${_SHOW_RAW}") + endif() + + if (NOT "${Rust_TOOLCHAIN}-${_DEFAULT_HOST}" IN_LIST _DISCOVERED_TOOLCHAINS) + set(_NOT_FOUND_MESSAGE "Could not find toolchain '${Rust_TOOLCHAIN}'\n" + "Available toolchains:\n" + ) + foreach(_TOOLCHAIN ${_DISCOVERED_TOOLCHAINS}) + list(APPEND _NOT_FOUND_MESSAGE " `${_TOOLCHAIN}`\n") + endforeach() + _findrust_failed(${_NOT_FOUND_MESSAGE}) + endif() + + set(_RUSTUP_TOOLCHAIN_FULL "${Rust_TOOLCHAIN}-${_DEFAULT_HOST}") + else() + set(_RUSTUP_TOOLCHAIN_FULL "${Rust_TOOLCHAIN}") + endif() + + set(_RUST_TOOLCHAIN_PATH "${_TOOLCHAIN_${_RUSTUP_TOOLCHAIN_FULL}_PATH}") + if(NOT "${Rust_FIND_QUIETLY}") + message(VERBOSE "Rust toolchain ${_RUSTUP_TOOLCHAIN_FULL}") + message(VERBOSE "Rust toolchain path ${_RUST_TOOLCHAIN_PATH}") + endif() + + # Is overridden if the user specifies `Rust_COMPILER` explicitly. + find_program( + Rust_COMPILER_CACHED + rustc + HINTS "${_RUST_TOOLCHAIN_PATH}/bin" + NO_DEFAULT_PATH) +else() + message(DEBUG "Rust_RESOLVE_RUSTUP_TOOLCHAINS=OFF and Rust_RUSTUP=${Rust_RUSTUP}") + if(Rust_RUSTUP) + get_filename_component(_RUSTUP_DIR "${Rust_RUSTUP}" DIRECTORY) + find_program(Rust_COMPILER_CACHED rustc HINTS "${_RUSTUP_DIR}") + else() + find_program(Rust_COMPILER_CACHED rustc) + endif() + message(DEBUG "find_program rustc: ${Rust_COMPILER_CACHED}") + if (EXISTS "${Rust_COMPILER_CACHED}") + # rustc is expected to be at `/bin/rustc`. + get_filename_component(_RUST_TOOLCHAIN_PATH "${Rust_COMPILER_CACHED}" DIRECTORY) + get_filename_component(_RUST_TOOLCHAIN_PATH "${_RUST_TOOLCHAIN_PATH}" DIRECTORY) + endif() +endif() + +if (NOT EXISTS "${Rust_COMPILER_CACHED}") + set(_NOT_FOUND_MESSAGE "The rustc executable was not found. " + "Rust not installed or ~/.cargo/bin not added to path?\n" + "Hint: Consider setting `Rust_COMPILER` to the absolute path of `rustc`." + ) + _findrust_failed(${_NOT_FOUND_MESSAGE}) +endif() + +if (Rust_RESOLVE_RUSTUP_TOOLCHAINS) + set(_NOT_FOUND_MESSAGE "Rust was detected to be managed by rustup, but failed to find `cargo` " + "next to `rustc` in `${_RUST_TOOLCHAIN_PATH}/bin`. This can happen for custom toolchains, " + "if cargo was not built. " + "Please manually specify the path to a compatible `cargo` by setting `Rust_CARGO`." + ) + find_program( + Rust_CARGO_CACHED + cargo + HINTS "${_RUST_TOOLCHAIN_PATH}/bin" + NO_DEFAULT_PATH + ) + # note: maybe can use find_package_handle_standard_args here, if we remove the _CACHED postfix. + # not sure why that is here... + if(NOT EXISTS "${Rust_CARGO_CACHED}") + _findrust_failed(${_NOT_FOUND_MESSAGE}) + endif() + set(Rust_TOOLCHAIN_IS_RUSTUP_MANAGED TRUE CACHE INTERNAL "" FORCE) +else() + set(_NOT_FOUND_MESSAGE "Failed to find `cargo` in PATH and `${_RUST_TOOLCHAIN_PATH}/bin`.\n" + "Please ensure cargo is in PATH or manually specify the path to a compatible `cargo` by " + "setting `Rust_CARGO`." + ) + # On some systems (e.g. NixOS) cargo is not managed by rustup and also not next to rustc. + find_program( + Rust_CARGO_CACHED + cargo + HINTS "${_RUST_TOOLCHAIN_PATH}/bin" + ) + # note: maybe can use find_package_handle_standard_args here, if we remove the _CACHED postfix. + # not sure why that is here... + if(NOT EXISTS "${Rust_CARGO_CACHED}") + _findrust_failed(${_NOT_FOUND_MESSAGE}) + endif() +endif() + +execute_process( + COMMAND "${Rust_CARGO_CACHED}" --version --verbose + OUTPUT_VARIABLE _CARGO_VERSION_RAW + RESULT_VARIABLE _CARGO_VERSION_RESULT +) +# todo: check if cargo is a required component! +if(NOT ( "${_CARGO_VERSION_RESULT}" EQUAL "0" )) + _findrust_failed("Failed to get cargo version.\n" + "`${Rust_CARGO_CACHED} --version` failed with error: `${_CARGO_VERSION_RESULT}" +) +endif() + +# todo: don't set cache variables here, but let find_package_handle_standard_args do the promotion +# later. +if (_CARGO_VERSION_RAW MATCHES "cargo ([0-9]+)\\.([0-9]+)\\.([0-9]+)") + set(Rust_CARGO_VERSION_MAJOR "${CMAKE_MATCH_1}" CACHE INTERNAL "" FORCE) + set(Rust_CARGO_VERSION_MINOR "${CMAKE_MATCH_2}" CACHE INTERNAL "" FORCE) + set(Rust_CARGO_VERSION_PATCH "${CMAKE_MATCH_3}" CACHE INTERNAL "" FORCE) + set(Rust_CARGO_VERSION "${Rust_CARGO_VERSION_MAJOR}.${Rust_CARGO_VERSION_MINOR}.${Rust_CARGO_VERSION_PATCH}" CACHE INTERNAL "" FORCE) +# Workaround for the version strings where the `cargo ` prefix is missing. +elseif(_CARGO_VERSION_RAW MATCHES "([0-9]+)\\.([0-9]+)\\.([0-9]+)") + set(Rust_CARGO_VERSION_MAJOR "${CMAKE_MATCH_1}" CACHE INTERNAL "" FORCE) + set(Rust_CARGO_VERSION_MINOR "${CMAKE_MATCH_2}" CACHE INTERNAL "" FORCE) + set(Rust_CARGO_VERSION_PATCH "${CMAKE_MATCH_3}" CACHE INTERNAL "" FORCE) + set(Rust_CARGO_VERSION "${Rust_CARGO_VERSION_MAJOR}.${Rust_CARGO_VERSION_MINOR}.${Rust_CARGO_VERSION_PATCH}" CACHE INTERNAL "" FORCE) +else() + _findrust_failed( + "Failed to parse cargo version. `cargo --version` evaluated to (${_CARGO_VERSION_RAW}). " + "Expected a .. version triple." + ) +endif() + +execute_process( + COMMAND "${Rust_COMPILER_CACHED}" --version --verbose + OUTPUT_VARIABLE _RUSTC_VERSION_RAW + RESULT_VARIABLE _RUSTC_VERSION_RESULT +) + +if(NOT ( "${_RUSTC_VERSION_RESULT}" EQUAL "0" )) + _findrust_failed("Failed to get rustc version.\n" + "${Rust_COMPILER_CACHED} --version failed with error: `${_RUSTC_VERSION_RESULT}`") +endif() + +if (_RUSTC_VERSION_RAW MATCHES "rustc ([0-9]+)\\.([0-9]+)\\.([0-9]+)(-nightly)?") + set(Rust_VERSION_MAJOR "${CMAKE_MATCH_1}" CACHE INTERNAL "" FORCE) + set(Rust_VERSION_MINOR "${CMAKE_MATCH_2}" CACHE INTERNAL "" FORCE) + set(Rust_VERSION_PATCH "${CMAKE_MATCH_3}" CACHE INTERNAL "" FORCE) + set(Rust_VERSION "${Rust_VERSION_MAJOR}.${Rust_VERSION_MINOR}.${Rust_VERSION_PATCH}" CACHE INTERNAL "" FORCE) + if(CMAKE_MATCH_4) + set(Rust_IS_NIGHTLY 1 CACHE INTERNAL "" FORCE) + else() + set(Rust_IS_NIGHTLY 0 CACHE INTERNAL "" FORCE) + endif() +else() + _findrust_failed("Failed to parse rustc version. `${Rust_COMPILER_CACHED} --version --verbose` " + "evaluated to:\n`${_RUSTC_VERSION_RAW}`" + ) +endif() + +if (_RUSTC_VERSION_RAW MATCHES "host: ([a-zA-Z0-9_\\-]*)\n") + set(Rust_DEFAULT_HOST_TARGET "${CMAKE_MATCH_1}") + set(Rust_CARGO_HOST_TARGET_CACHED "${Rust_DEFAULT_HOST_TARGET}" CACHE STRING "Host triple") +else() + _findrust_failed( + "Failed to parse rustc host target. `rustc --version --verbose` evaluated to:\n${_RUSTC_VERSION_RAW}" + ) +endif() + +if (_RUSTC_VERSION_RAW MATCHES "LLVM version: ([0-9]+)\\.([0-9]+)(\\.([0-9]+))?") + set(Rust_LLVM_VERSION_MAJOR "${CMAKE_MATCH_1}" CACHE INTERNAL "" FORCE) + set(Rust_LLVM_VERSION_MINOR "${CMAKE_MATCH_2}" CACHE INTERNAL "" FORCE) + # With the Rust toolchain 1.44.1 the reported LLVM version is 9.0, i.e. without a patch version. + # Since cmake regex does not support non-capturing groups, just ignore Match 3. + set(Rust_LLVM_VERSION_PATCH "${CMAKE_MATCH_4}" CACHE INTERNAL "" FORCE) + set(Rust_LLVM_VERSION "${Rust_LLVM_VERSION_MAJOR}.${Rust_LLVM_VERSION_MINOR}.${Rust_LLVM_VERSION_PATCH}" CACHE INTERNAL "" FORCE) +elseif(NOT Rust_FIND_QUIETLY) + message( + WARNING + "Failed to parse rustc LLVM version. `rustc --version --verbose` evaluated to:\n${_RUSTC_VERSION_RAW}" + ) +endif() + +if (NOT Rust_CARGO_TARGET_CACHED) + unset(_CARGO_ARCH) + unset(_CARGO_ABI) + if (WIN32) + if (CMAKE_VS_PLATFORM_NAME) + string(TOLOWER "${CMAKE_VS_PLATFORM_NAME}" LOWER_VS_PLATFORM_NAME) + if ("${LOWER_VS_PLATFORM_NAME}" STREQUAL "win32") + set(_CARGO_ARCH i686) + elseif("${LOWER_VS_PLATFORM_NAME}" STREQUAL "x64") + set(_CARGO_ARCH x86_64) + elseif("${LOWER_VS_PLATFORM_NAME}" STREQUAL "arm64") + set(_CARGO_ARCH aarch64) + else() + message(WARNING "VS Platform '${CMAKE_VS_PLATFORM_NAME}' not recognized") + endif() + endif() + # Fallback path + if(NOT DEFINED _CARGO_ARCH) + # Possible values for windows when not cross-compiling taken from here: + # https://learn.microsoft.com/en-us/windows/win32/winprog64/wow64-implementation-details + # When cross-compiling the user is expected to supply the value, so we match more variants. + if(CMAKE_SYSTEM_PROCESSOR MATCHES "^(AMD64|amd64|x86_64)$") + set(_CARGO_ARCH x86_64) + elseif(CMAKE_SYSTEM_PROCESSOR MATCHES "^(ARM64|arm64|aarch64)$") + set(_CARGO_ARCH aarch64) + elseif(CMAKE_SYSTEM_PROCESSOR MATCHES "^(X86|x86|i686)$") + set(_CARGO_ARCH i686) + elseif(CMAKE_SYSTEM_PROCESSOR STREQUAL "i586") + set(_CARGO_ARCH i586) + elseif(CMAKE_SYSTEM_PROCESSOR STREQUAL "IA64") + message(FATAL_ERROR "No rust target for Intel Itanium.") + elseif(NOT "${CMAKE_SYSTEM_PROCESSOR}") + message(WARNING "Failed to detect target architecture. Please set `CMAKE_SYSTEM_PROCESSOR`" + " to your target architecture or set `Rust_CARGO_TARGET` to your cargo target triple." + ) + else() + message(WARNING "Failed to detect target architecture. Please set " + "`Rust_CARGO_TARGET` to your cargo target triple." + ) + endif() + endif() + + set(_CARGO_VENDOR "pc-windows") + + # The MSVC Generators will always target the msvc ABI. + # For other generators we check the compiler ID and compiler target (if present) + # If no compiler is set and we are not cross-compiling then we just choose the + # default rust host target. + if(DEFINED MSVC + OR "${CMAKE_CXX_COMPILER_ID}" STREQUAL "MSVC" + OR "${CMAKE_C_COMPILER_ID}" STREQUAL "MSVC" + OR "${CMAKE_CXX_COMPILER_TARGET}" MATCHES "-msvc$" + OR "${CMAKE_C_COMPILER_TARGET}" MATCHES "-msvc$" + ) + set(_CARGO_ABI msvc) + elseif("${CMAKE_CXX_COMPILER_ID}" STREQUAL "GNU" + OR "${CMAKE_C_COMPILER_ID}" STREQUAL "GNU" + OR (NOT CMAKE_CROSSCOMPILING + AND NOT DEFINED CMAKE_CXX_COMPILER_ID + AND NOT DEFINED CMAKE_C_COMPILER_ID + AND "${Rust_DEFAULT_HOST_TARGET}" MATCHES "-gnu$" + ) + ) + set(_CARGO_ABI gnu) + elseif(("${CMAKE_C_COMPILER_ID}" MATCHES "Clang$" OR "${CMAKE_CXX_COMPILER_ID}" MATCHES "Clang$") + AND ("${CMAKE_CXX_COMPILER_TARGET}" MATCHES "-gnu(llvm)?$" + OR "${CMAKE_C_COMPILER_TARGET}" MATCHES "-gnu(llvm)?$") + ) + if("${Rust_VERSION}" VERSION_GREATER_EQUAL "1.79") + set(_CARGO_ABI gnullvm) + else() + message(WARNING "Your selected C/C++ compilers suggest you want to use the -gnullvm" + " rust targets, however your Rust compiler version is ${Rust_VERSION}, which is" + " before the promotion of the gnullvm target to tier2." + " Please either use a more recent rust compiler or manually choose a target " + " triple by specifying `Rust_CARGO_TARGET` manually." + ) + endif() + elseif(NOT "${CMAKE_CROSSCOMPILING}" AND "${Rust_DEFAULT_HOST_TARGET}" MATCHES "-msvc$") + # We first check if the gnu branches match to ensure this fallback is only used + # if no compiler is enabled. + set(_CARGO_ABI msvc) + else() + message(WARNING "Could not determine the target ABI. Please specify `Rust_CARGO_TARGET` manually.") + endif() + + if(DEFINED _CARGO_ARCH AND DEFINED _CARGO_VENDOR AND DEFINED _CARGO_ABI) + set(Rust_CARGO_TARGET_CACHED "${_CARGO_ARCH}-${_CARGO_VENDOR}-${_CARGO_ABI}" + CACHE STRING "Target triple") + endif() + elseif (ANDROID) + if (CMAKE_ANDROID_ARCH_ABI STREQUAL armeabi-v7a) + if (CMAKE_ANDROID_ARM_MODE) + set(_Rust_ANDROID_TARGET armv7-linux-androideabi) + else () + set(_Rust_ANDROID_TARGET thumbv7neon-linux-androideabi) + endif() + elseif (CMAKE_ANDROID_ARCH_ABI STREQUAL arm64-v8a) + set(_Rust_ANDROID_TARGET aarch64-linux-android) + elseif (CMAKE_ANDROID_ARCH_ABI STREQUAL x86) + set(_Rust_ANDROID_TARGET i686-linux-android) + elseif (CMAKE_ANDROID_ARCH_ABI STREQUAL x86_64) + set(_Rust_ANDROID_TARGET x86_64-linux-android) + endif() + + if (_Rust_ANDROID_TARGET) + set(Rust_CARGO_TARGET_CACHED "${_Rust_ANDROID_TARGET}" CACHE STRING "Target triple") + endif() + elseif("${CMAKE_SYSTEM_NAME}" STREQUAL "OHOS") + if(CMAKE_OHOS_ARCH_ABI STREQUAL arm64-v8a) + set(_RUST_OHOS_TARGET aarch64-unknown-linux-ohos) + elseif(CMAKE_OHOS_ARCH_ABI STREQUAL armeabi-v7a) + set(_RUST_OHOS_TARGET armv7-unknown-linux-ohos) + elseif(CMAKE_OHOS_ARCH_ABI STREQUAL x86_64) + set(_RUST_OHOS_TARGET x86_64-unknown-linux-ohos) + else() + message(WARNING "unrecognized OHOS architecture: ${OHOS_ARCH}") + endif() + if(_RUST_OHOS_TARGET) + set(Rust_CARGO_TARGET_CACHED "${_RUST_OHOS_TARGET}" CACHE STRING "Target triple") + endif() + endif() + # Fallback to the default host target + if(NOT Rust_CARGO_TARGET_CACHED) + if(CMAKE_CROSSCOMPILING) + message(WARNING "CMake is in cross-compiling mode, but the cargo target-triple could not be inferred." + "Falling back to the default host target. Please consider manually setting `Rust_CARGO_TARGET`." + ) + endif() + set(Rust_CARGO_TARGET_CACHED "${Rust_DEFAULT_HOST_TARGET}" CACHE STRING "Target triple") + endif() + + message(STATUS "Rust Target: ${Rust_CARGO_TARGET_CACHED}") +endif() + + +if(Rust_TOOLCHAIN_IS_RUSTUP_MANAGED) + execute_process(COMMAND rustup target list --toolchain "${Rust_TOOLCHAIN}" + OUTPUT_VARIABLE AVAILABLE_TARGETS_RAW + ) + string(REPLACE "\n" ";" AVAILABLE_TARGETS_RAW "${AVAILABLE_TARGETS_RAW}") + string(REPLACE " (installed)" "" "AVAILABLE_TARGETS" "${AVAILABLE_TARGETS_RAW}") + set(INSTALLED_TARGETS_RAW "${AVAILABLE_TARGETS_RAW}") + list(FILTER INSTALLED_TARGETS_RAW INCLUDE REGEX " \\(installed\\)") + string(REPLACE " (installed)" "" "INSTALLED_TARGETS" "${INSTALLED_TARGETS_RAW}") + list(TRANSFORM INSTALLED_TARGETS STRIP) + if("${Rust_CARGO_TARGET_CACHED}" IN_LIST AVAILABLE_TARGETS) + message(DEBUG "Cargo target ${Rust_CARGO_TARGET} is an official target-triple") + message(DEBUG "Installed targets: ${INSTALLED_TARGETS}") + if(NOT ("${Rust_CARGO_TARGET_CACHED}" IN_LIST INSTALLED_TARGETS)) + if(Rust_RUSTUP_INSTALL_MISSING_TARGET) + message(STATUS "Cargo target ${Rust_CARGO_TARGET_CACHED} is not installed. Installing via rustup.") + execute_process(COMMAND "${Rust_RUSTUP}" target add + --toolchain ${Rust_TOOLCHAIN} + ${Rust_CARGO_TARGET_CACHED} + RESULT_VARIABLE target_add_result + ) + if(NOT "${target_add_result}" EQUAL "0") + message(FATAL_ERROR "Target ${Rust_CARGO_TARGET_CACHED} is not installed for toolchain " + "${Rust_TOOLCHAIN} and automatically installing failed with ${target_add_result}.\n" + "You can try to manually install by running\n" + "`rustup target add --toolchain ${Rust_TOOLCHAIN} ${Rust_CARGO_TARGET}`." + ) + endif() + message(STATUS "Installed target ${Rust_CARGO_TARGET_CACHED} successfully.") + else() + message(FATAL_ERROR "Target ${Rust_CARGO_TARGET_CACHED} is not installed for toolchain ${Rust_TOOLCHAIN}.\n" + "Help: Run `rustup target add --toolchain ${Rust_TOOLCHAIN} ${Rust_CARGO_TARGET_CACHED}` to install " + "the missing target or configure corrosion with `Rust_RUSTUP_INSTALL_MISSING_TARGET=ON`." + ) + endif() + endif() + endif() +endif() + +if(Rust_CARGO_TARGET_CACHED STREQUAL Rust_DEFAULT_HOST_TARGET) + set(Rust_CROSSCOMPILING FALSE CACHE INTERNAL "Rust is configured for cross-compiling") +else() + set(Rust_CROSSCOMPILING TRUE CACHE INTERNAL "Rust is configured for cross-compiling") +endif() + +_corrosion_parse_target_triple("${Rust_CARGO_TARGET_CACHED}" rust_arch rust_vendor rust_os rust_env) +_corrosion_parse_target_triple("${Rust_CARGO_HOST_TARGET_CACHED}" rust_host_arch rust_host_vendor rust_host_os rust_host_env) + +set(Rust_CARGO_TARGET_ARCH "${rust_arch}" CACHE INTERNAL "Target architecture") +set(Rust_CARGO_TARGET_VENDOR "${rust_vendor}" CACHE INTERNAL "Target vendor") +set(Rust_CARGO_TARGET_OS "${rust_os}" CACHE INTERNAL "Target Operating System") +set(Rust_CARGO_TARGET_ENV "${rust_env}" CACHE INTERNAL "Target environment") + +set(Rust_CARGO_HOST_ARCH "${rust_host_arch}" CACHE INTERNAL "Host architecture") +set(Rust_CARGO_HOST_VENDOR "${rust_host_vendor}" CACHE INTERNAL "Host vendor") +set(Rust_CARGO_HOST_OS "${rust_host_os}" CACHE INTERNAL "Host Operating System") +set(Rust_CARGO_HOST_ENV "${rust_host_env}" CACHE INTERNAL "Host environment") + +if(NOT DEFINED CACHE{Rust_CARGO_TARGET_LINK_NATIVE_LIBS}) + message(STATUS "Determining required link libraries for target ${Rust_CARGO_TARGET_CACHED}") + unset(required_native_libs) + _corrosion_determine_libs_new("${Rust_CARGO_TARGET_CACHED}" required_native_libs required_link_flags) + if(DEFINED required_native_libs) + message(STATUS "Required static libs for target ${Rust_CARGO_TARGET_CACHED}: ${required_native_libs}" ) + endif() + if(DEFINED required_link_flags) + message(STATUS "Required link flags for target ${Rust_CARGO_TARGET_CACHED}: ${required_link_flags}" ) + endif() + # In very recent corrosion versions it is possible to override the rust compiler version + # per target, so to be totally correct we would need to determine the libraries for + # every installed Rust version, that the user could choose from. + # In practice there aren't likely going to be any major differences, so we just do it once + # for the target and once for the host target (if cross-compiling). + set(Rust_CARGO_TARGET_LINK_NATIVE_LIBS "${required_native_libs}" CACHE INTERNAL + "Required native libraries when linking Rust static libraries") + set(Rust_CARGO_TARGET_LINK_OPTIONS "${required_link_flags}" CACHE INTERNAL + "Required link flags when linking Rust static libraries") +endif() + +if(Rust_CROSSCOMPILING AND NOT DEFINED CACHE{Rust_CARGO_HOST_TARGET_LINK_NATIVE_LIBS}) + message(STATUS "Determining required link libraries for target ${Rust_CARGO_HOST_TARGET_CACHED}") + unset(host_libs) + _corrosion_determine_libs_new("${Rust_CARGO_HOST_TARGET_CACHED}" host_libs host_flags) + if(DEFINED host_libs) + message(STATUS "Required static libs for host target ${Rust_CARGO_HOST_TARGET_CACHED}: ${host_libs}" ) + endif() + set(Rust_CARGO_HOST_TARGET_LINK_NATIVE_LIBS "${host_libs}" CACHE INTERNAL + "Required native libraries when linking Rust static libraries for the host target") + set(Rust_CARGO_HOST_TARGET_LINK_OPTIONS "${host_flags}" CACHE INTERNAL + "Required linker flags when linking Rust static libraries for the host target") +endif() + +# Set the input variables as non-cache variables so that the variables are available after +# `find_package`, even if the values were evaluated to defaults. +foreach(_VAR ${_Rust_USER_VARS}) + set(${_VAR} "${${_VAR}_CACHED}") + # Ensure cached variables have type INTERNAL + set(${_VAR}_CACHED "${${_VAR}_CACHED}" CACHE INTERNAL "Internal cache of ${_VAR}") +endforeach() + +find_package_handle_standard_args( + Rust + REQUIRED_VARS Rust_COMPILER Rust_VERSION Rust_CARGO Rust_CARGO_VERSION Rust_CARGO_TARGET Rust_CARGO_HOST_TARGET + VERSION_VAR Rust_VERSION +) + + +if(NOT TARGET Rust::Rustc) + add_executable(Rust::Rustc IMPORTED GLOBAL) + set_property( + TARGET Rust::Rustc + PROPERTY IMPORTED_LOCATION "${Rust_COMPILER_CACHED}" + ) + + add_executable(Rust::Cargo IMPORTED GLOBAL) + set_property( + TARGET Rust::Cargo + PROPERTY IMPORTED_LOCATION "${Rust_CARGO_CACHED}" + ) + set(Rust_FOUND true) +endif() + +list(POP_BACK CMAKE_MESSAGE_CONTEXT) From 33c58fadc0f456d41e40a5003c8b934867e2c243 Mon Sep 17 00:00:00 2001 From: Atheria Date: Sat, 11 Oct 2025 13:13:33 +0700 Subject: [PATCH 4/4] Corrosion support --- kernel/ipc/Ipc.c | 3 --- 1 file changed, 3 deletions(-) diff --git a/kernel/ipc/Ipc.c b/kernel/ipc/Ipc.c index bfe41b9..12c11ce 100644 --- a/kernel/ipc/Ipc.c +++ b/kernel/ipc/Ipc.c @@ -89,7 +89,6 @@ IpcResult IpcSendMessage(uint32_t target_pid, const IpcMessage* msg) { target->state = PROC_READY; } rust_spinlock_unlock(queue->lock); - rust_spinlock_free(queue->lock); return IPC_SUCCESS; } @@ -129,13 +128,11 @@ IpcResult IpcReceiveMessage(IpcMessage* msg_buffer) { } rust_spinlock_unlock(queue->lock); - rust_spinlock_free(queue->lock); return IPC_SUCCESS; } current->state = PROC_BLOCKED; rust_spinlock_unlock(queue->lock); - rust_spinlock_free(queue->lock); Yield(); } }