blob: 37934e5357dc9b6bb2c29cfe376df70e3196642a [file] [log] [blame]
David Tolnay39fa3662020-07-30 20:23:18 -07001use super::Opt;
2use clap::AppSettings;
3use std::ffi::{OsStr, OsString};
4use std::path::PathBuf;
5
6type App = clap::App<'static, 'static>;
7type Arg = clap::Arg<'static, 'static>;
8
9const USAGE: &str = "\
10 cxxbridge <input>.rs Emit .cc file for bridge to stdout
11 cxxbridge <input>.rs --header Emit .h file for bridge to stdout
12 cxxbridge --header Emit rust/cxx.h header to stdout\
13";
14
15const TEMPLATE: &str = "\
16{bin} {version}
17David Tolnay <dtolnay@gmail.com>
18https://github.com/dtolnay/cxx
19
20USAGE:
21 {usage}
22
23ARGS:
24{positionals}
25OPTIONS:
26{unified}\
27";
28
29fn app() -> App {
30 let mut app = App::new("cxxbridge")
31 .usage(USAGE)
32 .template(TEMPLATE)
33 .setting(AppSettings::NextLineHelp)
34 .arg(arg_input())
35 .arg(arg_cxx_impl_annotations())
36 .arg(arg_header())
37 .arg(arg_include())
38 .help_message("Print help information.")
39 .version_message("Print version information.");
40 if let Some(version) = option_env!("CARGO_PKG_VERSION") {
41 app = app.version(version);
42 }
43 app
44}
45
46const INPUT: &str = "input";
47const CXX_IMPL_ANNOTATIONS: &str = "cxx-impl-annotations";
48const HEADER: &str = "header";
49const INCLUDE: &str = "include";
50
51pub(super) fn from_args() -> Opt {
52 let matches = app().get_matches();
53 Opt {
54 input: matches.value_of_os(INPUT).map(PathBuf::from),
55 cxx_impl_annotations: matches.value_of(CXX_IMPL_ANNOTATIONS).map(str::to_owned),
56 header: matches.is_present(HEADER),
57 include: matches
58 .values_of(INCLUDE)
59 .map_or_else(Vec::new, |v| v.map(str::to_owned).collect()),
60 }
61}
62
63fn validate_utf8(arg: &OsStr) -> Result<(), OsString> {
64 if arg.to_str().is_some() {
65 Ok(())
66 } else {
67 Err(OsString::from("invalid utf-8 sequence"))
68 }
69}
70
71fn arg_input() -> Arg {
72 Arg::with_name(INPUT)
73 .help("Input Rust source file containing #[cxx::bridge].")
74 .required_unless(HEADER)
75}
76
77fn arg_cxx_impl_annotations() -> Arg {
78 const HELP: &str = "\
79Optional annotation for implementations of C++ function wrappers
80that may be exposed to Rust. You may for example need to provide
81__declspec(dllexport) or __attribute__((visibility(\"default\")))
82if Rust code from one shared object or executable depends on
83these C++ functions in another.
84 ";
85 Arg::with_name(CXX_IMPL_ANNOTATIONS)
86 .long(CXX_IMPL_ANNOTATIONS)
87 .takes_value(true)
88 .value_name("annotation")
89 .validator_os(validate_utf8)
90 .help(HELP)
91}
92
93fn arg_header() -> Arg {
94 Arg::with_name(HEADER)
95 .long(HEADER)
96 .help("Emit header with declarations only.")
97}
98
99fn arg_include() -> Arg {
100 const HELP: &str = "\
101Any additional headers to #include. The cxxbridge tool does not
102parse or even require the given paths to exist; they simply go
103into the generated C++ code as #include lines.
104 ";
105 Arg::with_name(INCLUDE)
106 .long(INCLUDE)
107 .short("i")
108 .takes_value(true)
109 .multiple(true)
110 .validator_os(validate_utf8)
111 .help(HELP)
112}