I have an existing C++/Qt project built with CMake. I'd like to start adding Rust code which I can invoke from inside the main C++ codebase.
What's the right way to structure the project?
Current project structure:
./CMakeLists.txt ./subproject-foo/CMakeLists.txt ./subproject-foo/src/... ./subproject-bar/CmakeLists.txt ./subproject-bar/src/... ./common/CMakeLists.txt ./common/src/...
I'd like to add a common-rust/...
directory with similar structure.
How can I incorporate this into the project?
Corrosion, formerly known as cmake-cargo, is a tool for integrating Rust into an existing CMake project. Corrosion is capable of importing executables, static libraries, and dynamic libraries from a crate.
1. Basic CMake project CMake is a meta build system that uses scripts called CMakeLists to generate build files for a specific environment (for example, makefiles on Unix machines). When you create a new CMake project in CLion, a CMakeLists. txt file is automatically generated under the project root.
You can use the ExternalProject
module for this. It's designed to allow building of external dependencies - even ones which don't use CMake. Here's a useful article on using it.
So say you have your "common-rust" subdirectory and its Cargo.toml contains:
[package] name = "rust_example" version = "0.1.0" [lib] name = "rust_example" crate-type = ["staticlib"]
and it exposes a function add
via its lib.rs:
#[no_mangle] pub extern fn add(lhs: u32, rhs: u32) -> u32 { lhs + rhs }
Then your top-level CMakeLists.txt could look something like this:
add_executable(Example cpp/main.cpp) # Enable ExternalProject CMake module include(ExternalProject) # Set default ExternalProject root directory set_directory_properties(PROPERTIES EP_PREFIX ${CMAKE_BINARY_DIR}/Rust) # Add rust_example as a CMake target ExternalProject_Add( rust_example DOWNLOAD_COMMAND "" CONFIGURE_COMMAND "" BUILD_COMMAND cargo build COMMAND cargo build --release BINARY_DIR "${CMAKE_SOURCE_DIR}/common-rust" INSTALL_COMMAND "" LOG_BUILD ON) # Create dependency of Example on rust_example add_dependencies(Example rust_example) # Specify Example's link libraries target_link_libraries(Example debug "${CMAKE_SOURCE_DIR}/common-rust/target/debug/librust_example.a" optimized "${CMAKE_SOURCE_DIR}/common-rust/target/release/librust_example.a" ws2_32 userenv advapi32) set_target_properties(Example PROPERTIES CXX_STANDARD 11 CXX_STANDARD_REQUIRED ON)
When you build the Rust target as a staticlib
it outputs which other libraries should be linked. I've only tried this on Windows, hence ws2_32
, userenv
, and advapi32
are linked. This won't be cross-platform obviously, but you can handle that easily enough (e.g. set a variable to the list of dependencies appropriate to each platform inside an if..else
block and append that in the target_link_libraries
call).
Also note that this depends on Cargo being available in the path.
You should be good to go now. The file "cpp/main.cpp" could contain something like:
#include <cstdint> #include <iostream> extern "C" { uint32_t add(uint32_t lhs, uint32_t rhs); } int main() { std::cout << "1300 + 14 == " << add(1300, 14) << '\n'; return 0; }
Here's a link to a working example project.
There is now a project that can be used to build: Corrosion https://github.com/corrosion-rs/corrosion
So your CMakeLists.txt would just have this:
# See the Corrosion README to find more ways to get Corrosion find_package(Corrosion REQUIRED) corrosion_import_crate(MANIFEST_PATH ${CMAKE_SOURCE_DIR}/common-rust)
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With