mavlink-bindgen 0.16.1

Library used by rust-mavlink.
Documentation

mavlink-bindgen

Build status Crate info Documentation

Library and CLI for generating code for the Rust implementation of the MAVLink UAV messaging protocol.

mavlink-bindgen can be used to create MAVLink bindings for Rust. This is used from build.rs in the mavlink crate to create bindings from the standard MAVLink dialects in https://github.com/mavlink/mavlink.

Usage

mavlink-bindgen can be used as a code generator from build.rs as done is the mavlink crate for a custom MAVLink dialect or as a CLI tool to generate rust binding from XML dialect definitions. The generated code will depend on the mavlink-core crate in both use cases. Each dialect generated will be locked behind a feature flag of the same name, that must be enabled when using the generated code.

Furthermore the following feature gates will be present in the generated code:

  • serde: enable support for the serde crate
  • arbitrary: enable support for the arbitrary crate

CLI

Build the binary using cargo with cli feature enabled:

cd mavlink-bindgen
cargo build --features cli

Alternatively you can build and install mavlink-bindgen to your locally installed crates:

cargo install mavlink-bindgen --features cli

To generate code using the resulting binary:

mavlink-bindgen --format-generated-code message_definitions mavlink_dialects

The full command line options are shown below.

Usage: mavlink-bindgen [OPTIONS] <DEFINITIONS_DIR> <DESTINATION_DIR>

Arguments:
  <DEFINITIONS_DIR>  Path to the directory containing the MAVLink dialect definitions
  <DESTINATION_DIR>  Path to the directory where the code is generated into, must already exist

Options:
      --format-generated-code      format code generated code, requires rustfmt to be installed
      --emit-cargo-build-messages  prints cargo build message indicating when the code has to be rebuild
  -h, --help                       Print help

The output dir will contain a mod.rs file with each dialect in its own file locked behind a feature flag.

Library as build dependency

Add to your Cargo.toml:

mavlink-bindgen = "0.13.1"

Add a build/main.rs or build.rs to your project if it does not already exist. Then add the following to the main function to generate the code:

let out_dir = env::var("OUT_DIR").unwrap();
let result = match mavlink_bindgen::generate(XmlDefinitions::Directory(definitions_dir), out_dir) {
    Ok(r) => r,
    Err(e) => {
        eprintln!("{e}");
        return ExitCode::FAILURE;
    }
};

If the generated code should be formatted use

    mavlink_bindgen::format_generated_code(&result);

To tell cargo when to regenerate code from the definitions use:

    mavlink_bindgen::emit_cargo_build_messages(&result);

Finally include the generated code into the lib.rs or main.rs :

#![cfg_attr(not(feature = "std"), no_std)]
// include generate definitions
include!(concat!(env!("OUT_DIR"), "/mod.rs"));

pub use mavlink_core::*;

Since each dialect is locked behind a feature flag these need to be enabled for the dialects to become available when using the generated code.

This approach is used by the mavlink crate see its build script for an example.

Contributing

Snapshot tests

This crate uses snapshot tests to guard against subtle changes to the generated code. The tests generate the formatted MAVLink Rust code from small MAVLink definition files under tests/definitions/ and assert that the output hasn't changed. If the output changed the tests will fail. Snapshots can easily be updated with the new changes if they are valid.

Performing snapshot testing has two main purposes, the first is to avoid having changes to the generated code go unnoticed. Any change to the generated code will be very obvious with the snapshot tests. The second purpose is that by checking in the snapshots we have examples of generated code and changes to generated code are included in the review diff, making reviews easier.

Layout

  • tests/definitions/: Definition files including MAVLink messages. Keeping the amount of messages per definition file to a minimum will make it easier to review the output in the snapshots.
  • tests/e2e_snapshots.rs: This is where the tests live. It invokes the generator for each XML definition file and snapshots all emitted .rs files.
  • tests/snapshots/: The committed snapshot files created by insta are located here. Only the .snap files should be commited, .snap.new files are created automatically when the tests detect diverging output. They have to be reviewed and either the generator has to be fixed or the snapshots have to be updated.

Running tests

Run all tests (including snapshots):

cargo test

On the first run or after codegen changes, new snapshots will be created with a .snap.new suffix. Review and accept them if the changes are intended.

Reviewing and accepting snapshots

For a better workflow, install cargo-insta and use it to collect and review updates interactively. See the Insta quickstart for details: Insta quickstart.

# Install cargo-insta from crates.io
cargo install cargo-insta

After running cargo test and snapshot tests have failed:

# interactively review and accept/reject changes
cargo insta review

Alternatively, if you don't want to install cargo-insta you can control updates via the INSTA_UPDATE environment variable:

# do not update snapshots (useful for CI-like checks)
INSTA_UPDATE=no cargo test -p mavlink-bindgen

# overwrite all existing snapshots with new results
INSTA_UPDATE=always cargo test -p mavlink-bindgen

# write only new snapshots (existing ones stay unchanged)
INSTA_UPDATE=new cargo test -p mavlink-bindgen

Adding a new snapshot test

  1. Create a minimal XML in tests/definitions/, e.g. foo.xml. Prefer one message or a tiny set of enums/messages to keep snapshots small and stable.
  2. Run the tests: cargo test -p mavlink-bindgen (or cargo insta test).
  3. Review and accept the new snapshots: cargo insta review.

The test harness automatically discovers generated .rs files for each XML and creates one snapshot per file (e.g. e2e_snapshots__foo.xml@foo.rs.snap).

Determinism and formatting

The generator strives to emit items in a deterministic order. If you change code generation intentionally, expect corresponding snapshot updates. Review the diffs carefully for unintended regressions.