Using Bindgen

The bindgen tool can auto-generate bindings from a C header file.

First create a small C library:

interoperability/bindgen/libbirthday.h:

  1. typedef struct card {
  2. const char* name;
  3. int years;
  4. } card;
  5. void print_card(const card* card);

interoperability/bindgen/libbirthday.c:

  1. #include <stdio.h>
  2. #include "libbirthday.h"
  3. void print_card(const card* card) {
  4. printf("+--------------\n");
  5. printf("| Happy Birthday %s!\n", card->name);
  6. printf("| Congratulations with the %i years!\n", card->years);
  7. printf("+--------------\n");
  8. }

Add this to your Android.bp file:

interoperability/bindgen/Android.bp:

  1. cc_library {
  2. name: "libbirthday",
  3. srcs: ["libbirthday.c"],
  4. }

Create a wrapper header file for the library (not strictly needed in this example):

interoperability/bindgen/libbirthday_wrapper.h:

  1. #include "libbirthday.h"

You can now auto-generate the bindings:

interoperability/bindgen/Android.bp:

  1. rust_bindgen {
  2. name: "libbirthday_bindgen",
  3. crate_name: "birthday_bindgen",
  4. wrapper_src: "libbirthday_wrapper.h",
  5. source_stem: "bindings",
  6. static_libs: ["libbirthday"],
  7. }

Finally, we can use the bindings in our Rust program:

interoperability/bindgen/Android.bp:

  1. rust_binary {
  2. name: "print_birthday_card",
  3. srcs: ["main.rs"],
  4. rustlibs: ["libbirthday_bindgen"],
  5. }

interoperability/bindgen/main.rs:

  1. //! Bindgen demo.
  2. use birthday_bindgen::{card, print_card};
  3. fn main() {
  4.     let name = std::ffi::CString::new("Peter").unwrap();
  5.     let card = card { name: name.as_ptr(), years: 42 };
  6.     // SAFETY: The pointer we pass is valid because it came from a Rust
  7.     // reference, and the `name` it contains refers to `name` above which also
  8.     // remains valid. `print_card` doesn't store either pointer to use later
  9.     // after it returns.
  10.     unsafe {
  11.         print_card(&card as *const card);
  12.     }
  13. }

Build, push, and run the binary on your device:

  1. m print_birthday_card
  2. adb push "$ANDROID_PRODUCT_OUT/system/bin/print_birthday_card" /data/local/tmp
  3. adb shell /data/local/tmp/print_birthday_card

Finally, we can run auto-generated tests to ensure the bindings work:

interoperability/bindgen/Android.bp:

  1. rust_test {
  2. name: "libbirthday_bindgen_test",
  3. srcs: [":libbirthday_bindgen"],
  4. crate_name: "libbirthday_bindgen_test",
  5. test_suites: ["general-tests"],
  6. auto_gen_config: true,
  7. clippy_lints: "none", // Generated file, skip linting
  8. lints: "none",
  9. }
  1. atest libbirthday_bindgen_test