[r2r] More advanced binding generation
- Add "save-bindgen" feature to store generated bindings in source directory. - Add "doc-only" to disable bindgen and linking, and use saved bindings in source directory instead. - Bindings are cached to speed on bindgen process.
This commit is contained in:
parent
8c42f906c7
commit
81896423bb
|
|
@ -34,3 +34,10 @@ rand = "0.8.5"
|
|||
[build-dependencies]
|
||||
r2r_common = { path = "../r2r_common", version = "0.3.2" }
|
||||
r2r_msg_gen = { path = "../r2r_msg_gen", version = "0.3.3" }
|
||||
|
||||
[features]
|
||||
save-bindgen = ["r2r_rcl/save-bindgen", "r2r_msg_gen/save-bindgen", "r2r_actions/save-bindgen"]
|
||||
doc-only = ["r2r_rcl/doc-only", "r2r_msg_gen/doc-only", "r2r_actions/doc-only"]
|
||||
|
||||
[package.metadata.docs.rs]
|
||||
features = ["doc-only"]
|
||||
|
|
|
|||
277
r2r/build.rs
277
r2r/build.rs
|
|
@ -1,121 +1,216 @@
|
|||
use std::env;
|
||||
use std::fs::File;
|
||||
use std::fs::{File, OpenOptions};
|
||||
use std::io::Write;
|
||||
use std::path::PathBuf;
|
||||
use std::path::{Path, PathBuf};
|
||||
use std::{env, fs};
|
||||
|
||||
const LIST_FILENAME: &str = "files.txt";
|
||||
const MSGS_FILENAME: &str = "_r2r_generated_msgs.rs";
|
||||
const UNTYPED_FILENAME: &str = "_r2r_generated_untyped_helper.rs";
|
||||
const UNTYPED_SERVICE_FILENAME: &str = "_r2r_generated_service_helper.rs";
|
||||
const UNTYPED_ACTION_FILENAME: &str = "_r2r_generated_action_helper.rs";
|
||||
const GENERATED_FILES: &[&str] = &[
|
||||
MSGS_FILENAME,
|
||||
UNTYPED_FILENAME,
|
||||
UNTYPED_SERVICE_FILENAME,
|
||||
UNTYPED_ACTION_FILENAME,
|
||||
];
|
||||
|
||||
fn main() {
|
||||
r2r_common::print_cargo_watches();
|
||||
|
||||
let msg_list = r2r_common::get_wanted_messages();
|
||||
let env_hash = r2r_common::get_env_hash();
|
||||
let out_dir = PathBuf::from(env::var_os("OUT_DIR").unwrap());
|
||||
let manifest_dir = Path::new(env!("CARGO_MANIFEST_DIR"));
|
||||
let bindgen_dir = out_dir.join(env_hash);
|
||||
let save_dir = manifest_dir.join("bindings");
|
||||
let mark_file = bindgen_dir.join("done");
|
||||
|
||||
if cfg!(feature = "doc-only") {
|
||||
// If "doc-only" feature is present, copy from $crate/bindings/* to OUT_DIR
|
||||
copy_files(&save_dir, &out_dir);
|
||||
} else {
|
||||
// If bindgen was done before, use cached files.
|
||||
if !mark_file.exists() {
|
||||
eprintln!("Generate bindings in '{}'", bindgen_dir.display());
|
||||
generate_bindings(&bindgen_dir);
|
||||
touch(&mark_file);
|
||||
} else {
|
||||
eprintln!("Used cached files in '{}'", bindgen_dir.display());
|
||||
}
|
||||
|
||||
copy_files(&bindgen_dir, &out_dir);
|
||||
|
||||
#[cfg(feature = "save-bindgen")]
|
||||
{
|
||||
fs::create_dir_all(&save_dir).unwrap();
|
||||
copy_files(&bindgen_dir, &save_dir);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn generate_bindings(bindgen_dir: &Path) {
|
||||
fs::create_dir_all(&bindgen_dir).unwrap();
|
||||
|
||||
let msg_list = r2r_common::get_wanted_messages();
|
||||
let msgs = r2r_common::as_map(&msg_list);
|
||||
|
||||
let mut modules = String::new();
|
||||
let mod_files: Vec<_> = msgs
|
||||
.iter()
|
||||
.map(|(module, prefixes)| {
|
||||
let mod_text = format!(
|
||||
r#"pub mod {module}{{include!(concat!(env!("OUT_DIR"), "/{module}.rs"));}}{lf}"#,
|
||||
module = module,
|
||||
lf = "\n"
|
||||
);
|
||||
modules.push_str(&mod_text);
|
||||
|
||||
for (module, prefixes) in &msgs {
|
||||
modules.push_str(&format!(
|
||||
r#"pub mod {module}{{include!(concat!(env!("OUT_DIR"), "/{module}.rs"));}}{lf}"#,
|
||||
module = module,
|
||||
lf = "\n"
|
||||
));
|
||||
let mut codegen = String::new();
|
||||
|
||||
let mut codegen = String::new();
|
||||
for (prefix, msgs) in prefixes {
|
||||
codegen.push_str(&format!(" pub mod {} {{\n", prefix));
|
||||
prefixes.into_iter().for_each(|(prefix, msgs)| {
|
||||
codegen.push_str(&format!(" pub mod {} {{\n", prefix));
|
||||
|
||||
if prefix == &"action" {
|
||||
for msg in msgs {
|
||||
codegen.push_str("#[allow(non_snake_case)]\n");
|
||||
codegen.push_str(&format!(" pub mod {} {{\n", msg));
|
||||
codegen.push_str(" use super::super::super::*;\n");
|
||||
|
||||
codegen.push_str(&r2r_msg_gen::generate_rust_action(module, prefix, msg));
|
||||
|
||||
for s in &["Goal", "Result", "Feedback"] {
|
||||
let msgname = format!("{}_{}", msg, s);
|
||||
codegen.push_str(&r2r_msg_gen::generate_rust_msg(module, prefix, &msgname));
|
||||
println!("cargo:rustc-cfg=r2r__{}__{}__{}", module, prefix, msg);
|
||||
}
|
||||
|
||||
// "internal" services that implements the action type
|
||||
for srv in &["SendGoal", "GetResult"] {
|
||||
if prefix == &"action" {
|
||||
for msg in msgs {
|
||||
codegen.push_str("#[allow(non_snake_case)]\n");
|
||||
codegen.push_str(&format!(" pub mod {} {{\n", srv));
|
||||
codegen.push_str(" use super::super::super::super::*;\n");
|
||||
codegen.push_str(&format!(" pub mod {} {{\n", msg));
|
||||
codegen.push_str(" use super::super::super::*;\n");
|
||||
|
||||
let srvname = format!("{}_{}", msg, srv);
|
||||
codegen.push_str(&r2r_msg_gen::generate_rust_service(
|
||||
module, prefix, &srvname,
|
||||
));
|
||||
codegen.push_str(&r2r_msg_gen::generate_rust_action(module, prefix, msg));
|
||||
|
||||
for s in &["Request", "Response"] {
|
||||
let msgname = format!("{}_{}_{}", msg, srv, s);
|
||||
for s in &["Goal", "Result", "Feedback"] {
|
||||
let msgname = format!("{}_{}", msg, s);
|
||||
codegen.push_str(&r2r_msg_gen::generate_rust_msg(
|
||||
module, prefix, &msgname,
|
||||
));
|
||||
println!("cargo:rustc-cfg=r2r__{}__{}__{}", module, prefix, msg);
|
||||
}
|
||||
|
||||
// "internal" services that implements the action type
|
||||
for srv in &["SendGoal", "GetResult"] {
|
||||
codegen.push_str("#[allow(non_snake_case)]\n");
|
||||
codegen.push_str(&format!(" pub mod {} {{\n", srv));
|
||||
codegen.push_str(" use super::super::super::super::*;\n");
|
||||
|
||||
let srvname = format!("{}_{}", msg, srv);
|
||||
codegen.push_str(&r2r_msg_gen::generate_rust_service(
|
||||
module, prefix, &srvname,
|
||||
));
|
||||
|
||||
for s in &["Request", "Response"] {
|
||||
let msgname = format!("{}_{}_{}", msg, srv, s);
|
||||
codegen.push_str(&r2r_msg_gen::generate_rust_msg(
|
||||
module, prefix, &msgname,
|
||||
));
|
||||
}
|
||||
codegen.push_str(" }\n");
|
||||
}
|
||||
|
||||
// also "internal" feedback message type that wraps the feedback type with a uuid
|
||||
let feedback_msgname = format!("{}_FeedbackMessage", msg);
|
||||
codegen.push_str(&r2r_msg_gen::generate_rust_msg(
|
||||
module,
|
||||
prefix,
|
||||
&feedback_msgname,
|
||||
));
|
||||
|
||||
codegen.push_str(" }\n");
|
||||
}
|
||||
} else if prefix == &"srv" {
|
||||
for msg in msgs {
|
||||
codegen.push_str("#[allow(non_snake_case)]\n");
|
||||
codegen.push_str(&format!(" pub mod {} {{\n", msg));
|
||||
codegen.push_str(" use super::super::super::*;\n");
|
||||
|
||||
codegen.push_str(&r2r_msg_gen::generate_rust_service(module, prefix, msg));
|
||||
|
||||
for s in &["Request", "Response"] {
|
||||
let msgname = format!("{}_{}", msg, s);
|
||||
codegen.push_str(&r2r_msg_gen::generate_rust_msg(
|
||||
module, prefix, &msgname,
|
||||
));
|
||||
println!("cargo:rustc-cfg=r2r__{}__{}__{}", module, prefix, msg);
|
||||
}
|
||||
codegen.push_str(" }\n");
|
||||
}
|
||||
|
||||
// also "internal" feedback message type that wraps the feedback type with a uuid
|
||||
let feedback_msgname = format!("{}_FeedbackMessage", msg);
|
||||
codegen.push_str(&r2r_msg_gen::generate_rust_msg(
|
||||
module,
|
||||
prefix,
|
||||
&feedback_msgname,
|
||||
));
|
||||
|
||||
codegen.push_str(" }\n");
|
||||
}
|
||||
} else if prefix == &"srv" {
|
||||
for msg in msgs {
|
||||
codegen.push_str("#[allow(non_snake_case)]\n");
|
||||
codegen.push_str(&format!(" pub mod {} {{\n", msg));
|
||||
codegen.push_str(" use super::super::super::*;\n");
|
||||
|
||||
codegen.push_str(&r2r_msg_gen::generate_rust_service(module, prefix, msg));
|
||||
|
||||
for s in &["Request", "Response"] {
|
||||
let msgname = format!("{}_{}", msg, s);
|
||||
codegen.push_str(&r2r_msg_gen::generate_rust_msg(module, prefix, &msgname));
|
||||
} else if prefix == &"msg" {
|
||||
codegen.push_str(" use super::super::*;\n");
|
||||
for msg in msgs {
|
||||
codegen.push_str(&r2r_msg_gen::generate_rust_msg(module, prefix, msg));
|
||||
println!("cargo:rustc-cfg=r2r__{}__{}__{}", module, prefix, msg);
|
||||
}
|
||||
codegen.push_str(" }\n");
|
||||
} else {
|
||||
panic!("unknown prefix type: {}", prefix);
|
||||
}
|
||||
} else if prefix == &"msg" {
|
||||
codegen.push_str(" use super::super::*;\n");
|
||||
for msg in msgs {
|
||||
codegen.push_str(&r2r_msg_gen::generate_rust_msg(module, prefix, msg));
|
||||
println!("cargo:rustc-cfg=r2r__{}__{}__{}", module, prefix, msg);
|
||||
}
|
||||
} else {
|
||||
panic!("unknown prefix type: {}", prefix);
|
||||
}
|
||||
|
||||
codegen.push_str(" }\n");
|
||||
}
|
||||
let out_path = PathBuf::from(env::var("OUT_DIR").unwrap());
|
||||
let mod_fn = out_path.join(&format!("{}.rs", module));
|
||||
let mut f = File::create(mod_fn).unwrap();
|
||||
write!(f, "{}", codegen).unwrap();
|
||||
codegen.push_str(" }\n");
|
||||
});
|
||||
|
||||
let file_name = format!("{}.rs", module);
|
||||
let mod_file = bindgen_dir.join(&file_name);
|
||||
fs::write(&mod_file, codegen).unwrap();
|
||||
|
||||
file_name
|
||||
})
|
||||
.collect();
|
||||
|
||||
// Write helper files
|
||||
{
|
||||
let untyped_helper = r2r_msg_gen::generate_untyped_helper(&msg_list);
|
||||
let untyped_service_helper = r2r_msg_gen::generate_untyped_service_helper(&msg_list);
|
||||
let untyped_action_helper = r2r_msg_gen::generate_untyped_action_helper(&msg_list);
|
||||
|
||||
let msgs_file = bindgen_dir.join(MSGS_FILENAME);
|
||||
let untyped_file = bindgen_dir.join(UNTYPED_FILENAME);
|
||||
let untyped_service_file = bindgen_dir.join(UNTYPED_SERVICE_FILENAME);
|
||||
let untyped_action_file = bindgen_dir.join(UNTYPED_ACTION_FILENAME);
|
||||
|
||||
fs::write(&msgs_file, &modules).unwrap();
|
||||
fs::write(&untyped_file, &untyped_helper).unwrap();
|
||||
fs::write(&untyped_service_file, &untyped_service_helper).unwrap();
|
||||
fs::write(&untyped_action_file, &untyped_action_helper).unwrap();
|
||||
}
|
||||
|
||||
let untyped_helper = r2r_msg_gen::generate_untyped_helper(&msg_list);
|
||||
let untyped_service_helper = r2r_msg_gen::generate_untyped_service_helper(&msg_list);
|
||||
let untyped_action_helper = r2r_msg_gen::generate_untyped_action_helper(&msg_list);
|
||||
// Save file list
|
||||
{
|
||||
let list_file = bindgen_dir.join(LIST_FILENAME);
|
||||
let mut writer = File::create(list_file).unwrap();
|
||||
|
||||
let out_path = PathBuf::from(env::var("OUT_DIR").unwrap());
|
||||
let msgs_fn = out_path.join("_r2r_generated_msgs.rs");
|
||||
let untyped_fn = out_path.join("_r2r_generated_untyped_helper.rs");
|
||||
let untyped_service_fn = out_path.join("_r2r_generated_service_helper.rs");
|
||||
let untyped_action_fn = out_path.join("_r2r_generated_action_helper.rs");
|
||||
for file_name in mod_files {
|
||||
writeln!(writer, "{}", file_name).unwrap();
|
||||
}
|
||||
|
||||
let mut f = File::create(msgs_fn).unwrap();
|
||||
write!(f, "{}", modules).unwrap();
|
||||
let mut f = File::create(untyped_fn).unwrap();
|
||||
write!(f, "{}", untyped_helper).unwrap();
|
||||
let mut f = File::create(untyped_service_fn).unwrap();
|
||||
write!(f, "{}", untyped_service_helper).unwrap();
|
||||
let mut f = File::create(untyped_action_fn).unwrap();
|
||||
write!(f, "{}", untyped_action_helper).unwrap();
|
||||
for file_name in GENERATED_FILES {
|
||||
writeln!(writer, "{}", file_name).unwrap();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn copy_files(src_dir: &Path, tgt_dir: &Path) {
|
||||
eprintln!(
|
||||
"Copy files from '{}' to '{}'",
|
||||
src_dir.display(),
|
||||
tgt_dir.display()
|
||||
);
|
||||
|
||||
let src_list_file = src_dir.join(LIST_FILENAME);
|
||||
let tgt_list_file = tgt_dir.join(LIST_FILENAME);
|
||||
fs::read_to_string(&src_list_file)
|
||||
.unwrap()
|
||||
.lines()
|
||||
.for_each(|file_name| {
|
||||
let src_file = src_dir.join(file_name);
|
||||
let tgt_file = tgt_dir.join(file_name);
|
||||
fs::copy(&src_file, &tgt_file).unwrap();
|
||||
});
|
||||
|
||||
fs::copy(&src_list_file, &tgt_list_file).unwrap();
|
||||
}
|
||||
|
||||
fn touch(path: &Path) {
|
||||
OpenOptions::new()
|
||||
.create(true)
|
||||
.write(true)
|
||||
.open(path)
|
||||
.unwrap_or_else(|_| panic!("Unable to create file '{}'", path.display()));
|
||||
}
|
||||
|
|
|
|||
Loading…
Reference in New Issue