133d722a9Sopenharmony_ci#[cfg(test)] 233d722a9Sopenharmony_ci#[path = "test.rs"] 333d722a9Sopenharmony_cimod test; 433d722a9Sopenharmony_ci 533d722a9Sopenharmony_ciuse super::{Opt, Output}; 633d722a9Sopenharmony_ciuse crate::cfg::{self, CfgValue}; 733d722a9Sopenharmony_ciuse crate::gen::include::Include; 833d722a9Sopenharmony_ciuse crate::syntax::IncludeKind; 933d722a9Sopenharmony_ciuse clap::builder::{ArgAction, ValueParser}; 1033d722a9Sopenharmony_ciuse clap::{Arg, Command}; 1133d722a9Sopenharmony_ciuse std::collections::{BTreeMap as Map, BTreeSet as Set}; 1233d722a9Sopenharmony_ciuse std::path::PathBuf; 1333d722a9Sopenharmony_ciuse std::process; 1433d722a9Sopenharmony_ciuse std::sync::{Arc, Mutex, PoisonError}; 1533d722a9Sopenharmony_ciuse syn::parse::Parser; 1633d722a9Sopenharmony_ci 1733d722a9Sopenharmony_ciconst USAGE: &str = "\ 1833d722a9Sopenharmony_ci cxxbridge <input>.rs Emit .cc file for bridge to stdout 1933d722a9Sopenharmony_ci cxxbridge <input>.rs --header Emit .h file for bridge to stdout 2033d722a9Sopenharmony_ci cxxbridge --header Emit \"rust/cxx.h\" header to stdout\ 2133d722a9Sopenharmony_ci"; 2233d722a9Sopenharmony_ci 2333d722a9Sopenharmony_ciconst TEMPLATE: &str = "\ 2433d722a9Sopenharmony_ci{bin} {version} 2533d722a9Sopenharmony_ciDavid Tolnay <dtolnay@gmail.com> 2633d722a9Sopenharmony_cihttps://github.com/dtolnay/cxx 2733d722a9Sopenharmony_ci 2833d722a9Sopenharmony_ci{usage-heading} 2933d722a9Sopenharmony_ci {usage} 3033d722a9Sopenharmony_ci 3133d722a9Sopenharmony_ci{all-args}\ 3233d722a9Sopenharmony_ci"; 3333d722a9Sopenharmony_ci 3433d722a9Sopenharmony_cifn app() -> Command { 3533d722a9Sopenharmony_ci let mut app = Command::new("cxxbridge") 3633d722a9Sopenharmony_ci .override_usage(USAGE) 3733d722a9Sopenharmony_ci .help_template(TEMPLATE) 3833d722a9Sopenharmony_ci .next_line_help(true) 3933d722a9Sopenharmony_ci .disable_help_flag(true) 4033d722a9Sopenharmony_ci .disable_version_flag(true) 4133d722a9Sopenharmony_ci .arg(arg_input()) 4233d722a9Sopenharmony_ci .arg(arg_cfg()) 4333d722a9Sopenharmony_ci .arg(arg_cxx_impl_annotations()) 4433d722a9Sopenharmony_ci .arg(arg_header()) 4533d722a9Sopenharmony_ci .arg(arg_help()) 4633d722a9Sopenharmony_ci .arg(arg_include()) 4733d722a9Sopenharmony_ci .arg(arg_output()); 4833d722a9Sopenharmony_ci if let Some(version) = option_env!("CARGO_PKG_VERSION") { 4933d722a9Sopenharmony_ci app = app.arg(arg_version()).version(version); 5033d722a9Sopenharmony_ci } 5133d722a9Sopenharmony_ci app 5233d722a9Sopenharmony_ci} 5333d722a9Sopenharmony_ci 5433d722a9Sopenharmony_ciconst INPUT: &str = "input"; 5533d722a9Sopenharmony_ciconst CFG: &str = "cfg"; 5633d722a9Sopenharmony_ciconst CXX_IMPL_ANNOTATIONS: &str = "cxx-impl-annotations"; 5733d722a9Sopenharmony_ciconst HELP: &str = "help"; 5833d722a9Sopenharmony_ciconst HEADER: &str = "header"; 5933d722a9Sopenharmony_ciconst INCLUDE: &str = "include"; 6033d722a9Sopenharmony_ciconst OUTPUT: &str = "output"; 6133d722a9Sopenharmony_ciconst VERSION: &str = "version"; 6233d722a9Sopenharmony_ci 6333d722a9Sopenharmony_cipub(super) fn from_args() -> Opt { 6433d722a9Sopenharmony_ci let matches = app().get_matches(); 6533d722a9Sopenharmony_ci 6633d722a9Sopenharmony_ci if matches.get_flag(HELP) { 6733d722a9Sopenharmony_ci let _ = app().print_long_help(); 6833d722a9Sopenharmony_ci process::exit(0); 6933d722a9Sopenharmony_ci } 7033d722a9Sopenharmony_ci 7133d722a9Sopenharmony_ci let input = matches.get_one::<PathBuf>(INPUT).cloned(); 7233d722a9Sopenharmony_ci let cxx_impl_annotations = matches 7333d722a9Sopenharmony_ci .get_one::<String>(CXX_IMPL_ANNOTATIONS) 7433d722a9Sopenharmony_ci .map(String::clone); 7533d722a9Sopenharmony_ci let header = matches.get_flag(HEADER); 7633d722a9Sopenharmony_ci let include = matches 7733d722a9Sopenharmony_ci .get_many::<String>(INCLUDE) 7833d722a9Sopenharmony_ci .unwrap_or_default() 7933d722a9Sopenharmony_ci .map(|include| { 8033d722a9Sopenharmony_ci if include.starts_with('<') && include.ends_with('>') { 8133d722a9Sopenharmony_ci Include { 8233d722a9Sopenharmony_ci path: include[1..include.len() - 1].to_owned(), 8333d722a9Sopenharmony_ci kind: IncludeKind::Bracketed, 8433d722a9Sopenharmony_ci } 8533d722a9Sopenharmony_ci } else { 8633d722a9Sopenharmony_ci Include { 8733d722a9Sopenharmony_ci path: include.to_owned(), 8833d722a9Sopenharmony_ci kind: IncludeKind::Quoted, 8933d722a9Sopenharmony_ci } 9033d722a9Sopenharmony_ci } 9133d722a9Sopenharmony_ci }) 9233d722a9Sopenharmony_ci .collect(); 9333d722a9Sopenharmony_ci 9433d722a9Sopenharmony_ci let mut outputs = Vec::new(); 9533d722a9Sopenharmony_ci for path in matches.get_many::<PathBuf>(OUTPUT).unwrap_or_default() { 9633d722a9Sopenharmony_ci outputs.push(if path.as_os_str() == "-" { 9733d722a9Sopenharmony_ci Output::Stdout 9833d722a9Sopenharmony_ci } else { 9933d722a9Sopenharmony_ci Output::File(path.clone()) 10033d722a9Sopenharmony_ci }); 10133d722a9Sopenharmony_ci } 10233d722a9Sopenharmony_ci if outputs.is_empty() { 10333d722a9Sopenharmony_ci outputs.push(Output::Stdout); 10433d722a9Sopenharmony_ci } 10533d722a9Sopenharmony_ci 10633d722a9Sopenharmony_ci let mut cfg = Map::new(); 10733d722a9Sopenharmony_ci for arg in matches.get_many::<String>(CFG).unwrap_or_default() { 10833d722a9Sopenharmony_ci let (name, value) = cfg::parse.parse_str(arg).unwrap(); 10933d722a9Sopenharmony_ci cfg.entry(name).or_insert_with(Set::new).insert(value); 11033d722a9Sopenharmony_ci } 11133d722a9Sopenharmony_ci 11233d722a9Sopenharmony_ci Opt { 11333d722a9Sopenharmony_ci input, 11433d722a9Sopenharmony_ci header, 11533d722a9Sopenharmony_ci cxx_impl_annotations, 11633d722a9Sopenharmony_ci include, 11733d722a9Sopenharmony_ci outputs, 11833d722a9Sopenharmony_ci cfg, 11933d722a9Sopenharmony_ci } 12033d722a9Sopenharmony_ci} 12133d722a9Sopenharmony_ci 12233d722a9Sopenharmony_cifn arg_input() -> Arg { 12333d722a9Sopenharmony_ci Arg::new(INPUT) 12433d722a9Sopenharmony_ci .help("Input Rust source file containing #[cxx::bridge].") 12533d722a9Sopenharmony_ci .required_unless_present_any(&[HEADER, HELP]) 12633d722a9Sopenharmony_ci .value_parser(ValueParser::path_buf()) 12733d722a9Sopenharmony_ci} 12833d722a9Sopenharmony_ci 12933d722a9Sopenharmony_cifn arg_cfg() -> Arg { 13033d722a9Sopenharmony_ci const HELP: &str = "\ 13133d722a9Sopenharmony_ciCompilation configuration matching what will be used to build 13233d722a9Sopenharmony_cithe Rust side of the bridge."; 13333d722a9Sopenharmony_ci let bool_cfgs = Arc::new(Mutex::new(Map::<String, bool>::new())); 13433d722a9Sopenharmony_ci Arg::new(CFG) 13533d722a9Sopenharmony_ci .long(CFG) 13633d722a9Sopenharmony_ci .num_args(1) 13733d722a9Sopenharmony_ci .value_name("name=\"value\" | name[=true] | name=false") 13833d722a9Sopenharmony_ci .action(ArgAction::Append) 13933d722a9Sopenharmony_ci .value_parser(move |arg: &str| match cfg::parse.parse_str(arg) { 14033d722a9Sopenharmony_ci Ok((_, CfgValue::Str(_))) => Ok(arg.to_owned()), 14133d722a9Sopenharmony_ci Ok((name, CfgValue::Bool(value))) => { 14233d722a9Sopenharmony_ci let mut bool_cfgs = bool_cfgs.lock().unwrap_or_else(PoisonError::into_inner); 14333d722a9Sopenharmony_ci if let Some(&prev) = bool_cfgs.get(&name) { 14433d722a9Sopenharmony_ci if prev != value { 14533d722a9Sopenharmony_ci return Err(format!("cannot have both {0}=false and {0}=true", name)); 14633d722a9Sopenharmony_ci } 14733d722a9Sopenharmony_ci } 14833d722a9Sopenharmony_ci bool_cfgs.insert(name, value); 14933d722a9Sopenharmony_ci Ok(arg.to_owned()) 15033d722a9Sopenharmony_ci } 15133d722a9Sopenharmony_ci Err(_) => Err("expected name=\"value\", name=true, or name=false".to_owned()), 15233d722a9Sopenharmony_ci }) 15333d722a9Sopenharmony_ci .help(HELP) 15433d722a9Sopenharmony_ci} 15533d722a9Sopenharmony_ci 15633d722a9Sopenharmony_cifn arg_cxx_impl_annotations() -> Arg { 15733d722a9Sopenharmony_ci const HELP: &str = "\ 15833d722a9Sopenharmony_ciOptional annotation for implementations of C++ function wrappers 15933d722a9Sopenharmony_cithat may be exposed to Rust. You may for example need to provide 16033d722a9Sopenharmony_ci__declspec(dllexport) or __attribute__((visibility(\"default\"))) 16133d722a9Sopenharmony_ciif Rust code from one shared object or executable depends on 16233d722a9Sopenharmony_cithese C++ functions in another."; 16333d722a9Sopenharmony_ci Arg::new(CXX_IMPL_ANNOTATIONS) 16433d722a9Sopenharmony_ci .long(CXX_IMPL_ANNOTATIONS) 16533d722a9Sopenharmony_ci .num_args(1) 16633d722a9Sopenharmony_ci .value_name("annotation") 16733d722a9Sopenharmony_ci .value_parser(ValueParser::string()) 16833d722a9Sopenharmony_ci .help(HELP) 16933d722a9Sopenharmony_ci} 17033d722a9Sopenharmony_ci 17133d722a9Sopenharmony_cifn arg_header() -> Arg { 17233d722a9Sopenharmony_ci const HELP: &str = "\ 17333d722a9Sopenharmony_ciEmit header with declarations only. Optional if using `-o` with 17433d722a9Sopenharmony_cia path ending in `.h`."; 17533d722a9Sopenharmony_ci Arg::new(HEADER).long(HEADER).num_args(0).help(HELP) 17633d722a9Sopenharmony_ci} 17733d722a9Sopenharmony_ci 17833d722a9Sopenharmony_cifn arg_help() -> Arg { 17933d722a9Sopenharmony_ci Arg::new(HELP) 18033d722a9Sopenharmony_ci .long(HELP) 18133d722a9Sopenharmony_ci .help("Print help information.") 18233d722a9Sopenharmony_ci .num_args(0) 18333d722a9Sopenharmony_ci} 18433d722a9Sopenharmony_ci 18533d722a9Sopenharmony_cifn arg_include() -> Arg { 18633d722a9Sopenharmony_ci const HELP: &str = "\ 18733d722a9Sopenharmony_ciAny additional headers to #include. The cxxbridge tool does not 18833d722a9Sopenharmony_ciparse or even require the given paths to exist; they simply go 18933d722a9Sopenharmony_ciinto the generated C++ code as #include lines."; 19033d722a9Sopenharmony_ci Arg::new(INCLUDE) 19133d722a9Sopenharmony_ci .long(INCLUDE) 19233d722a9Sopenharmony_ci .short('i') 19333d722a9Sopenharmony_ci .num_args(1) 19433d722a9Sopenharmony_ci .action(ArgAction::Append) 19533d722a9Sopenharmony_ci .value_parser(ValueParser::string()) 19633d722a9Sopenharmony_ci .help(HELP) 19733d722a9Sopenharmony_ci} 19833d722a9Sopenharmony_ci 19933d722a9Sopenharmony_cifn arg_output() -> Arg { 20033d722a9Sopenharmony_ci const HELP: &str = "\ 20133d722a9Sopenharmony_ciPath of file to write as output. Output goes to stdout if -o is 20233d722a9Sopenharmony_cinot specified."; 20333d722a9Sopenharmony_ci Arg::new(OUTPUT) 20433d722a9Sopenharmony_ci .long(OUTPUT) 20533d722a9Sopenharmony_ci .short('o') 20633d722a9Sopenharmony_ci .num_args(1) 20733d722a9Sopenharmony_ci .action(ArgAction::Append) 20833d722a9Sopenharmony_ci .value_parser(ValueParser::path_buf()) 20933d722a9Sopenharmony_ci .help(HELP) 21033d722a9Sopenharmony_ci} 21133d722a9Sopenharmony_ci 21233d722a9Sopenharmony_cifn arg_version() -> Arg { 21333d722a9Sopenharmony_ci Arg::new(VERSION) 21433d722a9Sopenharmony_ci .long(VERSION) 21533d722a9Sopenharmony_ci .help("Print version information.") 21633d722a9Sopenharmony_ci .action(ArgAction::Version) 21733d722a9Sopenharmony_ci} 218