mirror of
https://github.com/logos-messaging/logos-delivery-rust-bindings.git
synced 2026-08-04 01:13:14 +00:00
Exposes the reliable-channel surface: channel_create / channel_send / channel_close on WakuNodeHandle, backed by the logosdelivery_channel_* FFI calls. Channel state is persisted, so re-creating a closed channel resumes it rather than starting fresh, and send payloads travel base64-encoded. Adds the three channel lifecycle events (received / sent / error) to WakuEvent, plus the messaging events (sent, error, propagated, received) and connection status change, so callers can observe delivery rather than only firing and hoping. build.rs resolves the nimble package dirs and librln by scanning rather than hardcoding, since their names carry versions and hashes that move whenever nimble.lock does. It also checks make's exit status: a failed build previously passed silently and the crate linked a stale library from an earlier run. Bumps the vendor submodule to master. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
198 lines
7.2 KiB
Rust
198 lines
7.2 KiB
Rust
use std::env;
|
|
use std::env::set_current_dir;
|
|
use std::path::{Path, PathBuf};
|
|
use std::process::Command;
|
|
|
|
extern crate cc;
|
|
|
|
fn submodules_init(project_dir: &Path) {
|
|
let mark_file_path = ".submodules-initialized";
|
|
|
|
// Check if the mark file exists
|
|
if !Path::new(mark_file_path).exists() {
|
|
// If mark file doesn't exist, initialize submodule
|
|
if Command::new("git")
|
|
.args(["submodule", "init"])
|
|
.status()
|
|
.expect("Failed to execute 'git submodule init'")
|
|
.success()
|
|
&& Command::new("git")
|
|
.args(["submodule", "update", "--recursive"])
|
|
.status()
|
|
.expect("Failed to execute 'git submodule update --recursive'")
|
|
.success()
|
|
{
|
|
// Now, inside nwaku folder, run 'make update' to get nwaku's vendors
|
|
let nwaku_path = project_dir.join("vendor");
|
|
set_current_dir(nwaku_path).expect("Moving to vendor dir");
|
|
|
|
if Command::new("make")
|
|
.args(["update"])
|
|
.status()
|
|
.expect("Failed to execute 'make update'")
|
|
.success()
|
|
{
|
|
std::fs::File::create(mark_file_path).expect("Failed to create mark file");
|
|
} else {
|
|
panic!("Failed to run 'make update' within nwaku folder.");
|
|
}
|
|
|
|
set_current_dir(project_dir).expect("Going back to project dir");
|
|
|
|
println!("Git submodules initialized and updated successfully.");
|
|
} else {
|
|
panic!("Failed to initialize or update git submodules.");
|
|
}
|
|
} else {
|
|
println!("Mark file '{mark_file_path}' exists. Skipping git submodule initialization.");
|
|
}
|
|
}
|
|
|
|
fn build_nwaku_lib(project_dir: &Path) {
|
|
let nwaku_path = project_dir.join("vendor");
|
|
set_current_dir(nwaku_path).expect("Moving to vendor dir");
|
|
|
|
let mut cmd = Command::new("make");
|
|
cmd.arg("liblogosdelivery").arg("STATIC=1");
|
|
let status = cmd
|
|
.status()
|
|
.unwrap_or_else(|e| panic!("Failed to run 'make liblogosdelivery': {e}"));
|
|
|
|
// The exit status has to be checked, not just the spawn: a failed make would
|
|
// otherwise pass silently and the crate would link a stale library from a
|
|
// previous build.
|
|
if !status.success() {
|
|
panic!("'make liblogosdelivery STATIC=1' failed with {status}");
|
|
}
|
|
|
|
set_current_dir(project_dir).expect("Going back to project dir");
|
|
}
|
|
|
|
/// Resolves a package directory under the vendor's `nimbledeps/pkgs2`, whose
|
|
/// names carry a version and hash (e.g. `nat_traversal-0.0.1-1a376d3e...`) that
|
|
/// change whenever `nimble.lock` moves.
|
|
fn nimble_pkg_dir(nwaku_path: &Path, pkg_name: &str) -> PathBuf {
|
|
let pkgs_dir = nwaku_path.join("nimbledeps/pkgs2");
|
|
let prefix = format!("{pkg_name}-");
|
|
std::fs::read_dir(&pkgs_dir)
|
|
.unwrap_or_else(|e| panic!("Cannot read {}: {e}", pkgs_dir.display()))
|
|
.filter_map(|entry| entry.ok())
|
|
.map(|entry| entry.path())
|
|
.find(|path| {
|
|
path.file_name()
|
|
.and_then(|name| name.to_str())
|
|
.is_some_and(|name| name.starts_with(&prefix))
|
|
})
|
|
.unwrap_or_else(|| {
|
|
panic!(
|
|
"No '{pkg_name}' package under {}. Was 'make liblogosdelivery' run?",
|
|
pkgs_dir.display()
|
|
)
|
|
})
|
|
}
|
|
|
|
/// Finds the vendor's `librln_<version>.a` and returns the name to link it by
|
|
/// (the file stem without the `lib` prefix).
|
|
fn find_librln(nwaku_path: &Path) -> String {
|
|
std::fs::read_dir(nwaku_path)
|
|
.unwrap_or_else(|e| panic!("Cannot read {}: {e}", nwaku_path.display()))
|
|
.filter_map(|entry| entry.ok())
|
|
.filter_map(|entry| entry.file_name().into_string().ok())
|
|
.find_map(|name| {
|
|
name.strip_prefix("lib")
|
|
.and_then(|name| name.strip_suffix(".a"))
|
|
.filter(|name| name.starts_with("rln"))
|
|
.map(str::to_owned)
|
|
})
|
|
.unwrap_or_else(|| {
|
|
panic!(
|
|
"No 'librln_*.a' in {}. Was 'make librln' run?",
|
|
nwaku_path.display()
|
|
)
|
|
})
|
|
}
|
|
|
|
fn generate_bindgen_code(project_dir: &Path) {
|
|
let nwaku_path = project_dir.join("vendor");
|
|
// The kernel header includes the stable one, so this single entry point
|
|
// yields both the low-level waku_* tier and the logosdelivery_* messaging
|
|
// and reliable-channel surface.
|
|
let header_path = nwaku_path.join("library/liblogosdelivery_kernel.h");
|
|
|
|
println!("cargo:rerun-if-changed={}", header_path.display());
|
|
println!(
|
|
"cargo:rustc-link-search={}",
|
|
nwaku_path.join("build").display()
|
|
);
|
|
println!("cargo:rustc-link-lib=static=logosdelivery");
|
|
|
|
// nat_traversal moved from a git submodule under vendor/ to a nimble
|
|
// dependency, and builds its NAT archives inside its own package dir.
|
|
let nat_traversal = nimble_pkg_dir(&nwaku_path, "nat_traversal");
|
|
|
|
println!(
|
|
"cargo:rustc-link-search={}",
|
|
nat_traversal
|
|
.join("vendor/miniupnp/miniupnpc/build")
|
|
.display()
|
|
);
|
|
println!("cargo:rustc-link-lib=static=miniupnpc");
|
|
|
|
println!(
|
|
"cargo:rustc-link-search={}",
|
|
nat_traversal.join("vendor/libnatpmp-upstream").display()
|
|
);
|
|
println!("cargo:rustc-link-lib=static=natpmp");
|
|
|
|
println!("cargo:rustc-link-lib=dl");
|
|
println!("cargo:rustc-link-lib=m");
|
|
|
|
// boringssl (pulled in by libp2p) is C++, so its runtime has to be linked.
|
|
if cfg!(target_os = "macos") {
|
|
println!("cargo:rustc-link-lib=c++");
|
|
} else {
|
|
println!("cargo:rustc-link-lib=stdc++");
|
|
}
|
|
|
|
// The vendor's Makefile fetches librln into its root, naming it after the
|
|
// RLN version it pins, so the archive is discovered rather than hardcoded.
|
|
let librln = find_librln(&nwaku_path);
|
|
println!("cargo:rustc-link-search={}", nwaku_path.display());
|
|
println!("cargo:rustc-link-lib=static={librln}");
|
|
|
|
// libbacktrace is not linked: the vendor builds with -d:disable_libbacktrace.
|
|
|
|
cc::Build::new()
|
|
.file("src/cmd.c") // Compile the C file
|
|
.compile("cmditems"); // Compile it as a library
|
|
println!("cargo:rustc-link-lib=static=cmditems");
|
|
|
|
// Generate waku bindings with bindgen
|
|
let bindings = bindgen::Builder::default()
|
|
// The input header we would like to generate
|
|
// bindings for.
|
|
.header(format!("{}", header_path.display()))
|
|
// Tell cargo to invalidate the built crate whenever any of the
|
|
// included header files changed.
|
|
.parse_callbacks(Box::new(bindgen::CargoCallbacks))
|
|
// Finish the builder and generate the bindings.
|
|
.generate()
|
|
// Unwrap the Result and panic on failure.
|
|
.expect("Unable to generate bindings");
|
|
|
|
// Write the bindings to the $OUT_DIR/bindings.rs file.
|
|
let out_path = PathBuf::from(env::var("OUT_DIR").unwrap());
|
|
bindings
|
|
.write_to_file(out_path.join("bindings.rs"))
|
|
.expect("Couldn't write bindings!");
|
|
}
|
|
|
|
#[cfg(not(doc))]
|
|
fn main() {
|
|
let project_dir = PathBuf::from(env::var("CARGO_MANIFEST_DIR").unwrap());
|
|
|
|
submodules_init(&project_dir);
|
|
build_nwaku_lib(&project_dir);
|
|
generate_bindgen_code(&project_dir);
|
|
}
|