blob: 37934e5357dc9b6bb2c29cfe376df70e3196642a [file] [log] [blame]
use super::Opt;
use clap::AppSettings;
use std::ffi::{OsStr, OsString};
use std::path::PathBuf;
type App = clap::App<'static, 'static>;
type Arg = clap::Arg<'static, 'static>;
const USAGE: &str = "\
cxxbridge <input>.rs Emit .cc file for bridge to stdout
cxxbridge <input>.rs --header Emit .h file for bridge to stdout
cxxbridge --header Emit rust/cxx.h header to stdout\
";
const TEMPLATE: &str = "\
{bin} {version}
David Tolnay <dtolnay@gmail.com>
https://github.com/dtolnay/cxx
USAGE:
{usage}
ARGS:
{positionals}
OPTIONS:
{unified}\
";
fn app() -> App {
let mut app = App::new("cxxbridge")
.usage(USAGE)
.template(TEMPLATE)
.setting(AppSettings::NextLineHelp)
.arg(arg_input())
.arg(arg_cxx_impl_annotations())
.arg(arg_header())
.arg(arg_include())
.help_message("Print help information.")
.version_message("Print version information.");
if let Some(version) = option_env!("CARGO_PKG_VERSION") {
app = app.version(version);
}
app
}
const INPUT: &str = "input";
const CXX_IMPL_ANNOTATIONS: &str = "cxx-impl-annotations";
const HEADER: &str = "header";
const INCLUDE: &str = "include";
pub(super) fn from_args() -> Opt {
let matches = app().get_matches();
Opt {
input: matches.value_of_os(INPUT).map(PathBuf::from),
cxx_impl_annotations: matches.value_of(CXX_IMPL_ANNOTATIONS).map(str::to_owned),
header: matches.is_present(HEADER),
include: matches
.values_of(INCLUDE)
.map_or_else(Vec::new, |v| v.map(str::to_owned).collect()),
}
}
fn validate_utf8(arg: &OsStr) -> Result<(), OsString> {
if arg.to_str().is_some() {
Ok(())
} else {
Err(OsString::from("invalid utf-8 sequence"))
}
}
fn arg_input() -> Arg {
Arg::with_name(INPUT)
.help("Input Rust source file containing #[cxx::bridge].")
.required_unless(HEADER)
}
fn arg_cxx_impl_annotations() -> Arg {
const HELP: &str = "\
Optional annotation for implementations of C++ function wrappers
that may be exposed to Rust. You may for example need to provide
__declspec(dllexport) or __attribute__((visibility(\"default\")))
if Rust code from one shared object or executable depends on
these C++ functions in another.
";
Arg::with_name(CXX_IMPL_ANNOTATIONS)
.long(CXX_IMPL_ANNOTATIONS)
.takes_value(true)
.value_name("annotation")
.validator_os(validate_utf8)
.help(HELP)
}
fn arg_header() -> Arg {
Arg::with_name(HEADER)
.long(HEADER)
.help("Emit header with declarations only.")
}
fn arg_include() -> Arg {
const HELP: &str = "\
Any additional headers to #include. The cxxbridge tool does not
parse or even require the given paths to exist; they simply go
into the generated C++ code as #include lines.
";
Arg::with_name(INCLUDE)
.long(INCLUDE)
.short("i")
.takes_value(true)
.multiple(true)
.validator_os(validate_utf8)
.help(HELP)
}