Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 0 additions & 5 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -29,11 +29,6 @@ UDP, TCP, file connection protocols with a rich set of features.
| [`mavlink-core`](https://crates.io/crates/mavlink-core) | Core protocol types, parser/serializer, and connection traits |
| [`mavlink-bindgen`](https://crates.io/crates/mavlink-bindgen) | XML-to-Rust code generator used by `mavlink` |

## Build requirements

Building the `mavlink` crate runs a build script that initializes
the bundled MAVLink definition submodule, so `git` must be available.

## Quick start

Add the crate:
Expand Down
96 changes: 61 additions & 35 deletions mavlink/build/main.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
#![recursion_limit = "256"]

use std::collections::{BTreeMap, BTreeSet};
use std::env;
use std::fs::read_dir;
use std::path::Path;
Expand All @@ -9,29 +10,29 @@ use mavlink_bindgen::XmlDefinitions;

fn main() -> ExitCode {
let src_dir = Path::new(env!("CARGO_MANIFEST_DIR"));
let mavlink_dir = src_dir.join("mavlink");

// Check if git is installed
if Command::new("git").arg("--version").status().is_err() {
eprintln!("error: Git is not installed or could not be found.");
return ExitCode::FAILURE;
}

// Update and init submodule
if let Err(error) = Command::new("git")
.arg("submodule")
.arg("update")
.arg("--init")
.current_dir(src_dir)
.status()
{
eprintln!("Failed to update MAVLink definitions submodule: {error}");
return ExitCode::FAILURE;
// It is a submodule if it contains `.git` or if it's completely empty (uninitialized)
let is_mavlink_empty = read_dir(&mavlink_dir)
.map(|mut d| d.next().is_none())
.unwrap_or(true);
let is_submodule = mavlink_dir.join(".git").exists() || is_mavlink_empty;

if is_submodule {
if let Err(error) = Command::new("git")
.arg("submodule")
.arg("update")
.arg("--init")
.current_dir(src_dir)
.status()
{
eprintln!("Failed to update MAVLink definitions submodule: {error}");
return ExitCode::FAILURE;
}
}

// find & apply patches to XML definitions to avoid crashes
let patch_dir = src_dir.join("build/patches");
let mavlink_dir = src_dir.join("mavlink");

if let Ok(dir) = read_dir(patch_dir) {
for entry in dir.flatten() {
if let Err(error) = Command::new("git")
Expand All @@ -46,40 +47,65 @@ fn main() -> ExitCode {
}
}

let out_dir = env::var("OUT_DIR").unwrap();
let source_definitions_dir = mavlink_dir.join("message_definitions/v1.0");

let source_definitions_dir = src_dir.join("mavlink/message_definitions/v1.0");
// Check if the source definitions directory exists
if !source_definitions_dir.is_dir() {
eprintln!(
"MAVLink message definitions directory not found at: {}\n\
Ensure submodules are included.",
source_definitions_dir.display(),
);
return ExitCode::FAILURE;
}

let enabled_dialects: Vec<String> = env::vars()
let enabled_dialects: BTreeSet<String> = env::vars()
.filter_map(|(key, _)| {
key.strip_prefix("CARGO_FEATURE_DIALECT_")
.map(str::to_lowercase)
})
.collect();

let mut definitions_to_bind = vec![];
let mut definitions_to_bind = BTreeSet::new();

if !enabled_dialects.is_empty() {
// Handle case-insensitive Cargo features against case-sensitive files e.g., `csAirLink.xml`.
let mut available_dialects = BTreeMap::new();
for entry in read_dir(&source_definitions_dir)
.into_iter()
.flatten()
.flatten()
{
let path = entry.path();
let Some(stem) = path.file_stem() else {
continue;
};

available_dialects.insert(stem.to_string_lossy().to_lowercase(), path);
}

if let Ok(dir) = read_dir(&source_definitions_dir) {
for entry in dir.flatten() {
let filename = entry
.path()
.file_stem()
.unwrap()
.to_string_lossy()
.to_lowercase();

if enabled_dialects.contains(&filename) {
definitions_to_bind.push(entry.path());
}
// Check if the expected dialects requested by Cargo features are missing
for dialect in &enabled_dialects {
let Some(actual_path) = available_dialects.get(dialect) else {
eprintln!(
"Dialect definition for '{}' not found in {}",
dialect,
source_definitions_dir.display(),
);
return ExitCode::FAILURE;
};

definitions_to_bind.insert(actual_path.clone());
}
}

let xml_definitions = if definitions_to_bind.is_empty() {
XmlDefinitions::Directory(source_definitions_dir)
} else {
XmlDefinitions::Files(definitions_to_bind)
XmlDefinitions::Files(definitions_to_bind.into_iter().collect())
};

let out_dir = env::var("OUT_DIR").unwrap();
let result = match mavlink_bindgen::generate(xml_definitions, out_dir) {
Ok(r) => r,
Err(e) => {
Expand Down
Loading