Repository: geekbrother/cxx-corrosion-cmake Branch: main Commit: 2c16706a6340 Files: 11 Total size: 12.0 KB Directory structure: gitextract_66rc6919/ ├── .github/ │ └── workflows/ │ └── docker-image.yml ├── .gitignore ├── LICENSE ├── README.MD ├── cmake/ │ └── corrosion_cxx.cmake └── examples/ ├── Dockerfile └── rust-from-cpp/ ├── CMakeLists.txt └── src/ ├── cxxbridge_code/ │ ├── Cargo.toml │ ├── build.rs │ └── src/ │ └── lib.rs └── main.cpp ================================================ FILE CONTENTS ================================================ ================================================ FILE: .github/workflows/docker-image.yml ================================================ name: Docker Image CI on: push: branches: [ "main" ] pull_request: branches: [ "main" ] jobs: build: runs-on: ubuntu-latest steps: - uses: actions/checkout@v3 - name: Build the Docker image on examples working-directory: . run: docker build . --tag my-image-name:$(date +%s) -f examples/Dockerfile ================================================ FILE: .gitignore ================================================ # Calling Rust from C++ examples/rust-from-cpp/src/cxxbridge_code/target examples/rust-from-cpp/build # Calling C++ from Rust ================================================ FILE: LICENSE ================================================ This is free and unencumbered software released into the public domain. Anyone is free to copy, modify, publish, use, compile, sell, or distribute this software, either in source code form or as a compiled binary, for any purpose, commercial or non-commercial, and by any means. In jurisdictions that recognize copyright laws, the author or authors of this software dedicate any and all copyright interest in the software to the public domain. We make this dedication for the benefit of the public at large and to the detriment of our heirs and successors. We intend this dedication to be an overt act of relinquishment in perpetuity of all present and future rights to this software under copyright law. 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 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. For more information, please refer to ================================================ FILE: README.MD ================================================ # CMake file for using [CXX](https://cxx.rs/) and [Corrosion](https://github.com/corrosion-rs/corrosion) in CMake project to call Rust functions from C++ and C++ from Rust This is a fork of the [rusty_cmake](https://github.com/trondhe/rusty_cmake) repository to use CXX and Corrosion in a CMake project with the following modifications: ### `cxx_corrosion.cmake` file: - Function to get STEM is removed in a favor of one-line solution; - Simplify by using a simple one CMakeLists file for the project; - Windows-related code is removed (Linux and MacOS are supported); - Minor code changes to simplify and use a modern C++. # Why? This `cmake/corrosion_cxx.cmake` can be imported in any CMake project to simplify Rust code usage in C++. Current solution is a battle-tested and `cmake/corrosion_cxx.cmake` file is used in a [Comm](https://comm.app) application development. # Current examples Examples are located in `examples` folder. This repo examples can be used as a sandbox playground for testing simple ideas of the Rust library integration in the current C++ CMake project (`examples/rust-from-cpp`) and as examples of the usage and calling C++ code from Rust (`examples/cpp-from-rust`). # Calling Rust code from C++ ## Code examples to call Rust functions from C++: - [Example of using a different primitive types](https://github.com/geekbrother/cxx-corrosion-cmake/blob/fe14ac52173737163e3ba427557187b9e4ba5d33/examples/src/main.cpp#L8) for arguments and returns; - [Example](https://github.com/geekbrother/cxx-corrosion-cmake/blob/fe14ac52173737163e3ba427557187b9e4ba5d33/examples/src/main.cpp#L21) of using [Rust Result type](https://cxx.rs/binding/result.html#returning-result-from-rust-to-c) and [Anyhow](https://docs.rs/anyhow/latest/anyhow/) in return to C++; - [Example of using panics](https://github.com/geekbrother/cxx-corrosion-cmake/blob/fe14ac52173737163e3ba427557187b9e4ba5d33/examples/src/main.cpp#L33) in Rust when calling from C++. ## Dependencies - Linux or MacOS - CMake - Clang - [Corrosion](https://github.com/corrosion-rs/corrosion#installation) - Rustup - Nix (optional) or Docker (optional) ## Build ### Using CMake Run CMake and build commands in the `examples/rust-from-cpp` folder: `cmake -B build . && make -C build -j4`. Then you can run the example app by calling: `build/cxx_cmake`. ### In Nix If you are using [Nix](https://nixos.org/download.html) as a development environment you can use a nix shell with all dependencies: ``` nix-shell -p cmake -p clang -p rustup -p corrosion -p libiconv -p git --pure ``` And then run build commands from the CMake section above. ### In Docker There is a Docker file to build and run the example app in Docker container. To build it run the build command from the project root directory: ``` docker build . -t cxx-corrosion-cmake -f examples/Dockerfile ``` Then you can run the example app by calling: ``` docker run cxx-corrosion-cmake ``` # Todo Create a packages in package managers to help tracking updates in the project instead of manually track the changes. - Make it nix package - Make it vcpkg package # Sponsors ❤️ [**Comm.app**](https://comm.app) is a crypto-native chat (think "Web3 Discord"). ================================================ FILE: cmake/corrosion_cxx.cmake ================================================ # Creates a target including rust lib and cxxbridge which is # named as ${NAMESPACE}::${_LIB_PATH_STEM} # <_LIB_PATH_STEM> must match the crate name: # "path/to/myrustcrate" -> "libmyrustcrate.a" function(add_library_rust) set(value_keywords PATH NAMESPACE CXX_BRIDGE_SOURCE_FILE) cmake_parse_arguments( rust_lib "${OPTIONS}" "${value_keywords}" "${MULTI_value_KEYWORDS}" ${ARGN} ) if("${Rust_CARGO_TARGET}" STREQUAL "") message( FATAL_ERROR "Rust_CARGO_TARGET is not detected and empty") endif() if("${rust_lib_PATH}" STREQUAL "") message( FATAL_ERROR "add_library_rust called without a given path to root of a rust crate") endif() if("${rust_lib_NAMESPACE}" STREQUAL "") message( FATAL_ERROR "Must supply a namespace given by keyvalue NAMESPACE ") endif() if("${rust_lib_CXX_BRIDGE_SOURCE_FILE}" STREQUAL "") set(rust_lib_CXX_BRIDGE_SOURCE_FILE "src/lib.rs") endif() if(NOT EXISTS "${CMAKE_CURRENT_LIST_DIR}/${rust_lib_PATH}/Cargo.toml") message( FATAL_ERROR "${CMAKE_CURRENT_LIST_DIR}/${rust_lib_PATH} doesn't contain a Cargo.toml") endif() set(lib_path ${rust_lib_PATH}) set(namespace ${rust_lib_NAMESPACE}) set(cxx_bridge_source_file ${rust_lib_CXX_BRIDGE_SOURCE_FILE}) corrosion_import_crate(MANIFEST_PATH "${lib_path}/Cargo.toml") # Set cxxbridge values get_filename_component(_LIB_PATH_STEM ${lib_path} NAME) message(STATUS "Library stem path: ${_LIB_PATH_STEM}") set( cxx_bridge_binary_folder ${CMAKE_BINARY_DIR}/cargo/build/${Rust_CARGO_TARGET}/cxxbridge) set( common_header ${cxx_bridge_binary_folder}/rust/cxx.h) set( binding_header ${cxx_bridge_binary_folder}/${_LIB_PATH_STEM}/${cxx_bridge_source_file}.h) set( binding_source ${cxx_bridge_binary_folder}/${_LIB_PATH_STEM}/${cxx_bridge_source_file}.cc) set( cxx_binding_include_dir ${cxx_bridge_binary_folder}) # Create cxxbridge target add_custom_command( OUTPUT ${common_header} ${binding_header} ${binding_source} COMMAND DEPENDS ${_LIB_PATH_STEM}-static COMMENT "Fixing cmake to find source files" ) add_library(${_LIB_PATH_STEM}_cxxbridge ${common_header} ${binding_header} ${binding_source} ) target_include_directories(${_LIB_PATH_STEM}_cxxbridge PUBLIC ${cxx_binding_include_dir} ) # Create total target with alias with given namespace add_library(${_LIB_PATH_STEM}-total INTERFACE) target_link_libraries(${_LIB_PATH_STEM}-total INTERFACE ${_LIB_PATH_STEM}_cxxbridge ${_LIB_PATH_STEM} ) # for end-user to link into project add_library(${namespace}::${_LIB_PATH_STEM} ALIAS ${_LIB_PATH_STEM}-total) endfunction(add_library_rust) ================================================ FILE: examples/Dockerfile ================================================ FROM ubuntu:23.04 ENV DEBIAN_FRONTEND=noninteractive TZ=Etc/UTC ARG LOCAL_DIRECTORY=/var/cxx_corrosion_cmake ARG RUST_VERSION=1.69.0 # Install essential packages for building the c++ RUN apt update \ && apt install -y \ build-essential curl cmake git \ lsb-release software-properties-common # Install Rust RUN curl https://sh.rustup.rs -sSf | bash -s -- --default-toolchain ${RUST_VERSION} -y # Install Corrosion WORKDIR /var RUN git clone https://github.com/corrosion-rs/corrosion.git RUN cmake -Scorrosion -Bbuild -DCMAKE_BUILD_TYPE=Release RUN cmake --build build --config Release && cmake --install build --config Release # Copy files COPY examples ${LOCAL_DIRECTORY}/examples COPY cmake ${LOCAL_DIRECTORY}/cmake # Compile example app WORKDIR ${LOCAL_DIRECTORY}/examples/rust-from-cpp RUN rm -dfr build && cmake -B build . && make -C build -j4 # Run the example app CMD ["${LOCAL_DIRECTORY}/examples/rust-from-cpp/build/cxx_cmake"] ================================================ FILE: examples/rust-from-cpp/CMakeLists.txt ================================================ cmake_minimum_required(VERSION 3.16) set(CMAKE_EXPORT_COMPILE_COMMANDS true) project(cxx_cmake CXX) set(CMAKE_CXX_STANDARD 17) find_package(Corrosion REQUIRED) include(../../cmake/corrosion_cxx.cmake) add_executable(${PROJECT_NAME}) target_sources(${PROJECT_NAME} PRIVATE src/main.cpp ) add_library_rust(PATH src/cxxbridge_code NAMESPACE my) target_link_libraries(${PROJECT_NAME} PUBLIC my::cxxbridge_code ) ================================================ FILE: examples/rust-from-cpp/src/cxxbridge_code/Cargo.toml ================================================ [package] name = "cxxbridge_code" version = "0.1.0" authors = ["Trond H Emaus ", "Max Kalashnikoff "] edition = "2021" [dependencies] cxx = "1.0" anyhow = "1.0" [build-dependencies] cxx-build = "1.0" [lib] crate-type = ["staticlib"] [profile.release] debug = true panic = "abort" [profile.dev] panic = "abort" ================================================ FILE: examples/rust-from-cpp/src/cxxbridge_code/build.rs ================================================ fn main() { let _build = cxx_build::bridge("src/lib.rs"); println!("cargo:rerun-if-changed=src/lib.rs"); } ================================================ FILE: examples/rust-from-cpp/src/cxxbridge_code/src/lib.rs ================================================ use anyhow::Result; #[cxx::bridge] mod ffi { extern "Rust" { // Primitive types: fn lib_cxxbridge_bool(some: bool) -> bool; fn lib_cxxbridge_integer(some: i32) -> i32; fn lib_cxxbridge_string(some: &str) -> String; // Return Result: fn lib_cxxbridge_return_result_ok() -> Result; fn lib_cxxbridge_return_result_error() -> Result; // Panic in a function call: fn lib_cxxbridge_panicked_function() -> String; } } pub fn lib_cxxbridge_bool(some: bool) -> bool { if some { return false; } true } pub fn lib_cxxbridge_integer(some: i32) -> i32 { some + 10 } pub fn lib_cxxbridge_string(some: &str) -> String { String::from("Say hello to ".to_owned() + &some) } pub fn lib_cxxbridge_return_result_ok() -> Result { Ok(String::from("This is a string from result")) } pub fn lib_cxxbridge_return_result_error() -> Result { let some_string = std::fs::read_to_string("cluster.json")?; Ok(some_string) } pub fn lib_cxxbridge_panicked_function() -> String { panic!("Panicked_function in panic"); } ================================================ FILE: examples/rust-from-cpp/src/main.cpp ================================================ #include "cxxbridge_code/src/lib.rs.h" #include #include #include "rust/cxx.h" int main() { // Primitive types: std::cout << "[1] A value given via generateb cxxbridge for lib_cxxbridge_bool: " << lib_cxxbridge_bool(true) << std::endl; std::cout << "[2] A value given via generated cxxbridge for lib_cxxbridge_integer: " << lib_cxxbridge_integer(86) << std::endl; const rust::String testString = "Max"; std::cout << "[3] A value given via generated cxxbridge for lib_cxxbridge_string: " << lib_cxxbridge_string(testString) << std::endl; // Return Rust `Result` with Ok: std::cout << "[4] A Rust `Result Ok` value given via generated cxxbridge for lib_cxxbridge_return_result_ok:" << std::endl << lib_cxxbridge_return_result_ok() << std::endl; // Return Rust `Result` with Error: try { std::cout << "[5] A Rust `Result Error` value given via generated cxxbridge for lib_cxxbridge_return_result_error:" << std::endl << lib_cxxbridge_return_result_error() << std::endl; } catch (rust::Error e) { std::cerr << "Got an error from Rust function `Result`:" << std::endl; std::cerr << e.what() << std::endl; } // Panic in a function call: std::cout << "[6] Testing a case of panic by running lib_cxxbridge_panicked_function:" << std::endl; std::cout << lib_cxxbridge_panicked_function(); return 0; }