182 lines
6.8 KiB
Rust
182 lines
6.8 KiB
Rust
//! Builds the microReticulum carrier shim when the `rns` feature is on.
|
|
//!
|
|
//! The library source is not vendored into this repository: the environment
|
|
//! must point at a checkout via MICRORETICULUM_SOURCE_DIR (the Nix flake and
|
|
//! the dev shell both set it). The source tree is copied into OUT_DIR and
|
|
//! patched there — a one-line `<cstdint>` include the upstream master needs
|
|
//! under current libstdc++/libc++ — because the input may be read-only and
|
|
//! both build paths should compile identical sources.
|
|
|
|
#[cfg(feature = "rns")]
|
|
fn main() {
|
|
println!("cargo:rerun-if-changed=shim/smolmail_rns.cpp");
|
|
println!("cargo:rerun-if-changed=shim/udp_interface.cpp");
|
|
println!("cargo:rerun-if-env-changed=MICRORETICULUM_SOURCE_DIR");
|
|
for dep in DEP_VARS {
|
|
println!("cargo:rerun-if-env-changed={dep}");
|
|
}
|
|
|
|
let out_dir = std::path::PathBuf::from(std::env::var("OUT_DIR").unwrap());
|
|
let source = std::env::var("MICRORETICULUM_SOURCE_DIR")
|
|
.expect("the rns feature requires MICRORETICULUM_SOURCE_DIR (a microReticulum checkout)");
|
|
|
|
// Copy the parts cmake needs: the root CMakeLists.txt and src/.
|
|
let copy = out_dir.join("microReticulum");
|
|
copy_tree(
|
|
std::path::Path::new(&source),
|
|
©,
|
|
&["CMakeLists.txt", "src", "LICENSE"],
|
|
);
|
|
|
|
// Upstream master (2026-07-20) relies on transitive <cstdint> includes
|
|
// that libstdc++ 15 dropped; add the include itself.
|
|
let memory_h = copy.join("src/microReticulum/Utilities/Memory.h");
|
|
let text = std::fs::read_to_string(&memory_h).expect("Memory.h readable");
|
|
if !text.contains("#include <cstdint>") {
|
|
let patched = text.replacen(
|
|
"#include <memory>",
|
|
"#include <memory>\n#include <cstdint>",
|
|
1,
|
|
);
|
|
std::fs::write(&memory_h, patched).expect("Memory.h writable");
|
|
}
|
|
|
|
// Configure + build the library. Dependency overrides (RNS_*_SOURCE_DIR)
|
|
// are passed straight through so a sandboxed build never fetches.
|
|
let build = out_dir.join("rns-build");
|
|
let mut configure = std::process::Command::new("cmake");
|
|
configure
|
|
.arg("-S")
|
|
.arg(©)
|
|
.arg("-B")
|
|
.arg(&build)
|
|
.arg("-DCMAKE_BUILD_TYPE=Release")
|
|
// The NixOS gcc wrapper injects -Werror=format-security, which
|
|
// errors once the project's -Wno-format disables -Wformat.
|
|
.arg("-DCMAKE_C_FLAGS=-Wno-error=format-security")
|
|
.arg("-DCMAKE_CXX_FLAGS=-Wno-error=format-security")
|
|
.arg("-DRNS_BUILD_TESTS=OFF")
|
|
.arg("-DRNS_BUILD_EXAMPLES=OFF")
|
|
.arg("-DRNS_BUILD_INTEROP=OFF")
|
|
.arg("-DRNS_USE_FS=ON")
|
|
.arg("-DRNS_PERSIST_PATHS=OFF")
|
|
.arg("-DRNS_PERSIST_KNOWN_DESTINATIONS=OFF")
|
|
.arg("-DRNS_PERSIST_HASHLIST=OFF");
|
|
for dep in DEP_VARS {
|
|
if let Ok(path) = std::env::var(dep) {
|
|
configure.arg(format!("-D{dep}={path}"));
|
|
}
|
|
}
|
|
let status = configure.status().expect("cmake configure");
|
|
assert!(status.success(), "cmake configure failed");
|
|
let status = std::process::Command::new("cmake")
|
|
.arg("--build")
|
|
.arg(&build)
|
|
.arg("--target")
|
|
.arg("microReticulum")
|
|
.status()
|
|
.expect("cmake build");
|
|
assert!(status.success(), "cmake build failed");
|
|
|
|
// The same include roots and defines the library's CMake target exports
|
|
// publicly, mirrored so the shim TU sees identical macros (the upstream
|
|
// CMakeLists warns about ODR divergence otherwise). A vendored dep (env
|
|
// override) lives at its checkout root; a fetched one under _deps.
|
|
let deps = build.join("_deps");
|
|
let mut includes: Vec<std::path::PathBuf> = vec![copy.join("src")];
|
|
for (env_var, fetch_name, sub) in DEP_INCLUDES {
|
|
includes.push(match std::env::var(env_var) {
|
|
Ok(root) => std::path::Path::new(&root).join(sub),
|
|
Err(_) => deps.join(format!("{fetch_name}-src")).join(sub),
|
|
});
|
|
}
|
|
|
|
let mut build_cc = cc::Build::new();
|
|
build_cc
|
|
.cpp(true)
|
|
.std("c++17")
|
|
.define("NATIVE", None)
|
|
.define("USTORE_USE_UNIVERSALFS", None)
|
|
.define("RNS_USE_FS", None)
|
|
.file("shim/smolmail_rns.cpp")
|
|
.file("shim/udp_interface.cpp");
|
|
for include in &includes {
|
|
build_cc.include(include);
|
|
}
|
|
build_cc.compile("smolmail_rns_shim");
|
|
|
|
println!("cargo:rustc-link-search=native={}", build.display());
|
|
println!("cargo:rustc-link-lib=static=microReticulum");
|
|
println!("cargo:rustc-link-lib=static=CryptoLib");
|
|
}
|
|
|
|
// microReticulum's own local-checkout override variables.
|
|
#[cfg(feature = "rns")]
|
|
const DEP_VARS: &[&str] = &[
|
|
"RNS_ARDUINOJSON_SOURCE_DIR",
|
|
"RNS_MSGPACK_SOURCE_DIR",
|
|
"RNS_CRYPTO_SOURCE_DIR",
|
|
"RNS_MICROSTORE_SOURCE_DIR",
|
|
"RNS_ARXCONTAINER_SOURCE_DIR",
|
|
"RNS_ARXTYPETRAITS_SOURCE_DIR",
|
|
"RNS_DEBUGLOG_SOURCE_DIR",
|
|
];
|
|
|
|
// (override env var, FetchContent name, include subdir within the dep root).
|
|
#[cfg(feature = "rns")]
|
|
const DEP_INCLUDES: &[(&str, &str, &str)] = &[
|
|
("RNS_ARDUINOJSON_SOURCE_DIR", "arduinojson", "src"),
|
|
("RNS_MSGPACK_SOURCE_DIR", "msgpack", ""),
|
|
("RNS_ARXCONTAINER_SOURCE_DIR", "arxcontainer", ""),
|
|
("RNS_ARXTYPETRAITS_SOURCE_DIR", "arxtypetraits", ""),
|
|
("RNS_DEBUGLOG_SOURCE_DIR", "debuglog", ""),
|
|
("RNS_CRYPTO_SOURCE_DIR", "crypto", ""),
|
|
("RNS_MICROSTORE_SOURCE_DIR", "microstore", "include"),
|
|
];
|
|
|
|
#[cfg(feature = "rns")]
|
|
fn copy_tree(src: &std::path::Path, dst: &std::path::Path, entries: &[&str]) {
|
|
std::fs::create_dir_all(dst).expect("OUT_DIR writable");
|
|
for entry in entries {
|
|
let from = src.join(entry);
|
|
if !from.exists() {
|
|
continue;
|
|
}
|
|
let to = dst.join(entry);
|
|
if from.is_dir() {
|
|
copy_dir(&from, &to);
|
|
} else {
|
|
copy_file(&from, &to);
|
|
}
|
|
}
|
|
}
|
|
|
|
#[cfg(feature = "rns")]
|
|
fn copy_dir(src: &std::path::Path, dst: &std::path::Path) {
|
|
std::fs::create_dir_all(dst).expect("OUT_DIR writable");
|
|
for entry in std::fs::read_dir(src).expect("readable source dir") {
|
|
let entry = entry.expect("readable source dir").path();
|
|
let to = dst.join(entry.file_name().unwrap());
|
|
if entry.is_dir() {
|
|
copy_dir(&entry, &to);
|
|
} else {
|
|
copy_file(&entry, &to);
|
|
}
|
|
}
|
|
}
|
|
|
|
/// fs::copy preserves the source's mode, and a Nix store checkout is
|
|
/// read-only, so the copy must be made writable before the patch step.
|
|
#[cfg(feature = "rns")]
|
|
fn copy_file(from: &std::path::Path, to: &std::path::Path) {
|
|
std::fs::copy(from, to).expect("copy file");
|
|
#[cfg(unix)]
|
|
{
|
|
use std::os::unix::fs::PermissionsExt;
|
|
std::fs::set_permissions(to, std::fs::Permissions::from_mode(0o644))
|
|
.expect("copied file writable");
|
|
}
|
|
}
|
|
|
|
#[cfg(not(feature = "rns"))]
|
|
fn main() {}
|