1 #[cfg(test)]
2 #[path = "test.rs"]
3 mod test;
4
5 use super::{Opt, Output};
6 use crate::gen::include::Include;
7 use crate::syntax::IncludeKind;
8 use clap::AppSettings;
9 use std::ffi::{OsStr, OsString};
10 use std::path::PathBuf;
11
12 type App = clap::App<'static, 'static>;
13 type Arg = clap::Arg<'static, 'static>;
14
15 const USAGE: &str = "\
16 cxxbridge <input>.rs Emit .cc file for bridge to stdout
17 cxxbridge <input>.rs --header Emit .h file for bridge to stdout
18 cxxbridge --header Emit \"rust/cxx.h\" header to stdout\
19 ";
20
21 const TEMPLATE: &str = "\
22 {bin} {version}
23 David Tolnay <dtolnay@gmail.com>
24 https://github.com/dtolnay/cxx
25
26 USAGE:
27 {usage}
28
29 ARGS:
30 {positionals}
31 OPTIONS:
32 {unified}\
33 ";
34
app() -> App35 fn app() -> App {
36 let mut app = App::new("cxxbridge")
37 .usage(USAGE)
38 .template(TEMPLATE)
39 .setting(AppSettings::NextLineHelp)
40 .arg(arg_input())
41 .arg(arg_cxx_impl_annotations())
42 .arg(arg_header())
43 .arg(arg_include())
44 .arg(arg_output())
45 .help_message("Print help information.")
46 .version_message("Print version information.");
47 if let Some(version) = option_env!("CARGO_PKG_VERSION") {
48 app = app.version(version);
49 }
50 app
51 }
52
53 const INPUT: &str = "input";
54 const CXX_IMPL_ANNOTATIONS: &str = "cxx-impl-annotations";
55 const HEADER: &str = "header";
56 const INCLUDE: &str = "include";
57 const OUTPUT: &str = "output";
58
from_args() -> Opt59 pub(super) fn from_args() -> Opt {
60 let matches = app().get_matches();
61
62 let input = matches.value_of_os(INPUT).map(PathBuf::from);
63 let cxx_impl_annotations = matches.value_of(CXX_IMPL_ANNOTATIONS).map(str::to_owned);
64 let header = matches.is_present(HEADER);
65 let include = matches
66 .values_of(INCLUDE)
67 .unwrap_or_default()
68 .map(|include| {
69 if include.starts_with('<') && include.ends_with('>') {
70 Include {
71 path: include[1..include.len() - 1].to_owned(),
72 kind: IncludeKind::Bracketed,
73 }
74 } else {
75 Include {
76 path: include.to_owned(),
77 kind: IncludeKind::Quoted,
78 }
79 }
80 })
81 .collect();
82
83 let mut outputs = Vec::new();
84 for path in matches.values_of_os(OUTPUT).unwrap_or_default() {
85 outputs.push(if path == "-" {
86 Output::Stdout
87 } else {
88 Output::File(PathBuf::from(path))
89 });
90 }
91 if outputs.is_empty() {
92 outputs.push(Output::Stdout);
93 }
94
95 Opt {
96 input,
97 header,
98 cxx_impl_annotations,
99 include,
100 outputs,
101 }
102 }
103
validate_utf8(arg: &OsStr) -> Result<(), OsString>104 fn validate_utf8(arg: &OsStr) -> Result<(), OsString> {
105 if arg.to_str().is_some() {
106 Ok(())
107 } else {
108 Err(OsString::from("invalid utf-8 sequence"))
109 }
110 }
111
arg_input() -> Arg112 fn arg_input() -> Arg {
113 Arg::with_name(INPUT)
114 .help("Input Rust source file containing #[cxx::bridge].")
115 .required_unless(HEADER)
116 }
117
arg_cxx_impl_annotations() -> Arg118 fn arg_cxx_impl_annotations() -> Arg {
119 const HELP: &str = "\
120 Optional annotation for implementations of C++ function wrappers
121 that may be exposed to Rust. You may for example need to provide
122 __declspec(dllexport) or __attribute__((visibility(\"default\")))
123 if Rust code from one shared object or executable depends on
124 these C++ functions in another.
125 ";
126 Arg::with_name(CXX_IMPL_ANNOTATIONS)
127 .long(CXX_IMPL_ANNOTATIONS)
128 .takes_value(true)
129 .value_name("annotation")
130 .validator_os(validate_utf8)
131 .help(HELP)
132 }
133
arg_header() -> Arg134 fn arg_header() -> Arg {
135 const HELP: &str = "\
136 Emit header with declarations only. Optional if using `-o` with
137 a path ending in `.h`.
138 ";
139 Arg::with_name(HEADER).long(HEADER).help(HELP)
140 }
141
arg_include() -> Arg142 fn arg_include() -> Arg {
143 const HELP: &str = "\
144 Any additional headers to #include. The cxxbridge tool does not
145 parse or even require the given paths to exist; they simply go
146 into the generated C++ code as #include lines.
147 ";
148 Arg::with_name(INCLUDE)
149 .long(INCLUDE)
150 .short("i")
151 .takes_value(true)
152 .multiple(true)
153 .number_of_values(1)
154 .validator_os(validate_utf8)
155 .help(HELP)
156 }
157
arg_output() -> Arg158 fn arg_output() -> Arg {
159 const HELP: &str = "\
160 Path of file to write as output. Output goes to stdout if -o is
161 not specified.
162 ";
163 Arg::with_name(OUTPUT)
164 .long(OUTPUT)
165 .short("o")
166 .takes_value(true)
167 .multiple(true)
168 .number_of_values(1)
169 .validator_os(validate_utf8)
170 .help(HELP)
171 }
172