• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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