aboutsummaryrefslogtreecommitdiffhomepage
path: root/content
diff options
context:
space:
mode:
authorJohannes Stoelp <johannes.stoelp@gmail.com>2022-05-30 20:44:53 +0200
committerJohannes Stoelp <johannes.stoelp@gmail.com>2022-05-30 20:44:53 +0200
commita93e90cf3c50344a2582acb0e60187dbef90ee28 (patch)
tree2fd049370f4bf5df5792258d3471f582d1bf7449 /content
parent3389d0a128d874d930e22f256b2646dd86b3b402 (diff)
downloadblog-a93e90cf3c50344a2582acb0e60187dbef90ee28.tar.gz
blog-a93e90cf3c50344a2582acb0e60187dbef90ee28.zip
cmake: add example how to integrate cargo
Diffstat (limited to 'content')
-rw-r--r--content/2022-05-30-cmake-cargo-example.md68
-rw-r--r--content/2022-05-30-cmake-cargo-example/.gitignore1
-rw-r--r--content/2022-05-30-cmake-cargo-example/CMakeLists.txt11
-rw-r--r--content/2022-05-30-cmake-cargo-example/Makefile14
-rw-r--r--content/2022-05-30-cmake-cargo-example/calc.c19
-rw-r--r--content/2022-05-30-cmake-cargo-example/libcalc/.gitignore3
-rw-r--r--content/2022-05-30-cmake-cargo-example/libcalc/CMakeLists.txt48
-rw-r--r--content/2022-05-30-cmake-cargo-example/libcalc/Cargo.toml10
-rw-r--r--content/2022-05-30-cmake-cargo-example/libcalc/build.rs24
-rw-r--r--content/2022-05-30-cmake-cargo-example/libcalc/src/lib.rs9
10 files changed, 207 insertions, 0 deletions
diff --git a/content/2022-05-30-cmake-cargo-example.md b/content/2022-05-30-cmake-cargo-example.md
new file mode 100644
index 0000000..56810e6
--- /dev/null
+++ b/content/2022-05-30-cmake-cargo-example.md
@@ -0,0 +1,68 @@
++++
+title = "Example: integrate cargo into cmake"
+
+[taxonomies]
+tags = ["rust", "c++", "cmake"]
++++
+
+This post outlines an example on how to integrate a library written in `rust`
+into a cmake based `C/C++` project using the [ExternalProject][cmake-ext_prj]
+module.
+I created that post mainly as reference for myself but hopefully it is helpful
+to someone else who stumbles across it :^)
+
+The rust library is compiled as static library and then linked into the
+executable written in C. The top-level cmake file to build the executable just
+includes the rust crate via [add_subdirectory][cmake-add_sub] and then adds a
+link dependency.
+
+```cmake
+{{ include(path="2022-05-30-cmake-cargo-example/CMakeLists.txt") }}
+```
+
+The more interesting bits are included in the rust crate.
+
+First we have to tell cargo to output a static library. This can be done with
+the following configuration in the [Cargo.toml][cargo-toml] file.
+```toml
+[lib]
+crate-type = ["staticlib"]
+```
+> A header file with the C definitions is automatically generated using the
+> [cbindgen][cbindgen] crate invoked from the [build.rs][build-rs] script.
+
+Finally we can write a `CMakeLists.txt` file which exposes a pseudo target
+`libcalc` to cmake which consumers can depend on. The pseudo target depends on
+the `ext_libcalc` ([ExternalProject][cmake-ext_prj]) target.
+
+The `BUILD_COMMAND` contains an generator expression to build the rust crate in
+release mode in case `CMAKE_BUILD_TYPE=Release` is configured.
+
+Additionally, the properties of the `libcalc` pseudo target are setup to
+automatically add the correct static library and add the include path
+accordingly for consumers.
+
+With this setup, the rust crate is nicely build out of source. This is achieved
+by the following two configurations:
+- Pass `--target-dir` to cargo to specify the build directory.
+- Setup `LIBCALC_BUILD_DIR` before invoking cargo to tell [build.rs][build-rs]
+ where to generate the C header.
+
+```cmake
+{{ include(path="2022-05-30-cmake-cargo-example/libcalc/CMakeLists.txt") }}
+```
+
+The sources referenced in this post are available [here][post-src]. This
+repository includes the following Makefile to build and run the calc
+executable.
+```make
+{{ include(path="2022-05-30-cmake-cargo-example/Makefile") }}
+```
+
+[post-src]: https://git.memzero.de/johannst/blog/src/branch/main/content/2022-05-30-cmake-cargo-example
+[cargo-toml]: https://git.memzero.de/johannst/blog/src/branch/main/content/2022-05-30-cmake-cargo-example/libcalc/Cargo.toml
+[build-rs]: https://git.memzero.de/johannst/blog/src/branch/main/content/2022-05-30-cmake-cargo-example/libcalc/build.rs
+[make]: https://git.memzero.de/johannst/blog/src/branch/main/content/2022-05-30-cmake-cargo-example/Makefile
+[cbindgen]:https://lib.rs/crates/cbindgen
+[cmake-add_sub]: https://cmake.org/cmake/help/latest/command/add_subdirectory.html?highlight=add_subdirectory
+[cmake-ext_prj]: https://cmake.org/cmake/help/latest/module/ExternalProject.html
diff --git a/content/2022-05-30-cmake-cargo-example/.gitignore b/content/2022-05-30-cmake-cargo-example/.gitignore
new file mode 100644
index 0000000..f2ce476
--- /dev/null
+++ b/content/2022-05-30-cmake-cargo-example/.gitignore
@@ -0,0 +1 @@
+/BUILD
diff --git a/content/2022-05-30-cmake-cargo-example/CMakeLists.txt b/content/2022-05-30-cmake-cargo-example/CMakeLists.txt
new file mode 100644
index 0000000..6444ed0
--- /dev/null
+++ b/content/2022-05-30-cmake-cargo-example/CMakeLists.txt
@@ -0,0 +1,11 @@
+cmake_minimum_required(VERSION 3.14)
+project(calc)
+
+# Add libcalc (rust).
+add_subdirectory(libcalc)
+
+# Build calc add_executable.
+add_executable(calc calc.c)
+target_compile_options(calc PRIVATE -Wall -Wextra)
+# Link against rust lib.
+target_link_libraries(calc libcalc)
diff --git a/content/2022-05-30-cmake-cargo-example/Makefile b/content/2022-05-30-cmake-cargo-example/Makefile
new file mode 100644
index 0000000..e269cb2
--- /dev/null
+++ b/content/2022-05-30-cmake-cargo-example/Makefile
@@ -0,0 +1,14 @@
+TYPE ?= Debug
+BDIR := BUILD/$(TYPE)
+
+calc: config
+ cmake --build $(BDIR) --target calc
+
+config: CMakeLists.txt
+ cmake -B $(BDIR) -DCMAKE_BUILD_TYPE=$(TYPE) .
+
+run: calc
+ $(BDIR)/calc
+
+clean:
+ $(RM) -r BUILD
diff --git a/content/2022-05-30-cmake-cargo-example/calc.c b/content/2022-05-30-cmake-cargo-example/calc.c
new file mode 100644
index 0000000..27ca0a9
--- /dev/null
+++ b/content/2022-05-30-cmake-cargo-example/calc.c
@@ -0,0 +1,19 @@
+#include <stdio.h>
+#include <limits.h>
+
+#include <libcalc.h>
+
+#define EVAL(expr) \
+ printf("%s = %d\n", #expr, expr);
+
+int main() {
+ EVAL(wrap_add(1,2));
+ EVAL(wrap_add(INT_MAX,0));
+ EVAL(wrap_add(INT_MAX,1));
+
+ EVAL(sat_add(1,2));
+ EVAL(sat_add(INT_MAX,0));
+ EVAL(sat_add(INT_MAX,1));
+
+ return 0;
+}
diff --git a/content/2022-05-30-cmake-cargo-example/libcalc/.gitignore b/content/2022-05-30-cmake-cargo-example/libcalc/.gitignore
new file mode 100644
index 0000000..008b9b4
--- /dev/null
+++ b/content/2022-05-30-cmake-cargo-example/libcalc/.gitignore
@@ -0,0 +1,3 @@
+/target
+Cargo.lock
+libcalc.h
diff --git a/content/2022-05-30-cmake-cargo-example/libcalc/CMakeLists.txt b/content/2022-05-30-cmake-cargo-example/libcalc/CMakeLists.txt
new file mode 100644
index 0000000..2abab17
--- /dev/null
+++ b/content/2022-05-30-cmake-cargo-example/libcalc/CMakeLists.txt
@@ -0,0 +1,48 @@
+cmake_minimum_required(VERSION 3.14)
+
+set(BDIR ${CMAKE_BINARY_DIR}/libcalc)
+
+# Define external project to build rust lib with cargo.
+include(ExternalProject)
+ExternalProject_Add(
+ ext_libcalc
+ # Always trigger build, let cargo decide if we want to rebuild.
+ BUILD_ALWAYS ON
+ CONFIGURE_COMMAND ""
+ # Can not pass arguments to build.rs and was not able to set env variables
+ # for the external build command, therefore use 'env' to setup env var.
+ BUILD_COMMAND env LIBCALC_BUILD_DIR=${BDIR}
+ cargo build --target-dir ${BDIR} "$<IF:$<CONFIG:Release>,--release,>"
+ BUILD_BYPRODUCTS ${BDIR}/$<IF:$<CONFIG:Release>,release,debug>/libcalc.a
+ ${BDIR}/libcalc.h
+ INSTALL_COMMAND ""
+ # Location of sources (since we don't download).
+ SOURCE_DIR "${CMAKE_CURRENT_SOURCE_DIR}"
+ # Build dir location used as CWD for build commands.
+ BINARY_DIR "${CMAKE_CURRENT_SOURCE_DIR}"
+ # Root directory for external project in cmake build dir.
+ PREFIX "libcalc"
+ # Log directory (relative to PREFIX).
+ LOG_DIR "log"
+ # Log build step.
+ LOG_BUILD ON
+ # In case of error output log on terminal.
+ LOG_OUTPUT_ON_FAILURE ON
+)
+
+# Define pseudo target (import lib) for usage in cmake and let it depend on
+# the cargo build.
+add_library(libcalc STATIC IMPORTED GLOBAL)
+add_dependencies(libcalc ext_libcalc)
+
+# Configure the import locations (libs) for the import lib.
+
+set_target_properties(libcalc PROPERTIES
+ IMPORTED_CONFIGURATIONS "Debug;Release"
+ IMPORTED_LOCATION "${BDIR}/release/libcalc.a"
+ IMPORTED_LOCATION_DEBUG "${BDIR}/debug/libcalc.a"
+)
+
+# Configure the additional interface for they pseudo target.
+
+target_include_directories(libcalc INTERFACE "${BDIR}")
diff --git a/content/2022-05-30-cmake-cargo-example/libcalc/Cargo.toml b/content/2022-05-30-cmake-cargo-example/libcalc/Cargo.toml
new file mode 100644
index 0000000..23ea182
--- /dev/null
+++ b/content/2022-05-30-cmake-cargo-example/libcalc/Cargo.toml
@@ -0,0 +1,10 @@
+[package]
+name = "calc"
+version = "0.1.0"
+edition = "2021"
+
+[lib]
+crate-type = ["staticlib"]
+
+[build-dependencies]
+cbindgen = "0.*"
diff --git a/content/2022-05-30-cmake-cargo-example/libcalc/build.rs b/content/2022-05-30-cmake-cargo-example/libcalc/build.rs
new file mode 100644
index 0000000..acfa6a7
--- /dev/null
+++ b/content/2022-05-30-cmake-cargo-example/libcalc/build.rs
@@ -0,0 +1,24 @@
+fn main() {
+ let crate_dir = std::env::var("CARGO_MANIFEST_DIR").unwrap();
+
+ let out_file = format!("{}/libcalc.h", std::env::var("LIBCALC_BUILD_DIR").unwrap_or(String::from(".")));
+
+ let cfg = cbindgen::Config {
+ cpp_compat: true,
+ ..Default::default()
+ };
+
+ if std::path::Path::new(&out_file).exists() {
+ std::fs::remove_file(&out_file).unwrap();
+ }
+
+ let ok = cbindgen::Builder::new()
+ .with_config(cfg)
+ .with_crate(crate_dir)
+ .with_language(cbindgen::Language::C)
+ .with_include_guard("LIBCALC_H")
+ .generate()
+ .expect("Unable to generate bindings")
+ .write_to_file(&out_file);
+ assert!(ok);
+}
diff --git a/content/2022-05-30-cmake-cargo-example/libcalc/src/lib.rs b/content/2022-05-30-cmake-cargo-example/libcalc/src/lib.rs
new file mode 100644
index 0000000..a36aace
--- /dev/null
+++ b/content/2022-05-30-cmake-cargo-example/libcalc/src/lib.rs
@@ -0,0 +1,9 @@
+#[no_mangle]
+pub extern "C" fn wrap_add(a: i32, b: i32) -> i32 {
+ a.wrapping_add(b)
+}
+
+#[no_mangle]
+pub extern "C" fn sat_add(a: i32, b: i32) -> i32 {
+ a.saturating_add(b)
+}