Handle multiple outputs from the same cxxbridge invocation

This commit is contained in:
David Tolnay 2020-09-24 10:46:14 -04:00
parent 13213b7a85
commit f027756ba7
No known key found for this signature in database
GPG Key ID: F9BA143B95FF6D82
4 changed files with 53 additions and 25 deletions

View File

@ -65,18 +65,25 @@ pub(super) fn from_args() -> Opt {
.unwrap_or_default()
.map(str::to_owned)
.collect();
let output = match matches.value_of_os(OUTPUT) {
None => Output::Stdout,
Some(path) if path == "-" => Output::Stdout,
Some(path) => Output::File(PathBuf::from(path)),
};
let mut outputs = Vec::new();
for path in matches.values_of_os(OUTPUT).unwrap_or_default() {
outputs.push(if path == "-" {
Output::Stdout
} else {
Output::File(PathBuf::from(path))
});
}
if outputs.is_empty() {
outputs.push(Output::Stdout);
}
Opt {
input,
cxx_impl_annotations,
header,
include,
output,
outputs,
}
}
@ -142,6 +149,7 @@ not specified.
.long(OUTPUT)
.short("o")
.takes_value(true)
.multiple(true)
.validator_os(validate_utf8)
.help(HELP)
}

View File

@ -25,7 +25,7 @@ struct Opt {
header: bool,
cxx_impl_annotations: Option<String>,
include: Vec<String>,
output: Output,
outputs: Vec<Output>,
}
fn main() {
@ -35,35 +35,54 @@ fn main() {
}
}
enum Kind {
GeneratedHeader,
GeneratedImplementation,
Header,
}
fn try_main() -> Result<()> {
let opt = app::from_args();
let gen_header = opt.header || opt.output.ends_with(".h");
let mut outputs = Vec::new();
let mut gen_header = false;
let mut gen_implementation = false;
for output in opt.outputs {
let kind = if opt.input.is_none() {
Kind::Header
} else if opt.header || output.ends_with(".h") {
gen_header = true;
Kind::GeneratedHeader
} else {
gen_implementation = true;
Kind::GeneratedImplementation
};
outputs.push((output, kind));
}
let gen = gen::Opt {
include: opt.include,
cxx_impl_annotations: opt.cxx_impl_annotations,
gen_header,
gen_implementation: !gen_header,
gen_implementation,
};
let content;
let content = match (opt.input, gen_header) {
(Some(input), true) => {
content = gen::generate_from_path(&input, &gen).header;
content.as_slice()
}
(Some(input), false) => {
content = gen::generate_from_path(&input, &gen).implementation;
content.as_slice()
}
(None, true) => include::HEADER.as_bytes(),
(None, false) => unreachable!(), // enforced by required_unless
let generated_code = if let Some(input) = opt.input {
gen::generate_from_path(&input, &gen)
} else {
Default::default()
};
match opt.output {
Output::Stdout => drop(io::stdout().write_all(content)),
Output::File(path) => fs::write(path, content)?,
for (output, kind) in outputs {
let content = match kind {
Kind::GeneratedHeader => &generated_code.header,
Kind::GeneratedImplementation => &generated_code.implementation,
Kind::Header => include::HEADER.as_bytes(),
};
match output {
Output::Stdout => drop(io::stdout().write_all(content)),
Output::File(path) => fs::write(path, content)?,
}
}
Ok(())

View File

@ -32,7 +32,7 @@ OPTIONS:
parse or even require the given paths to exist; they simply go
into the generated C++ code as #include lines.
\x20
-o, --output <output>
-o, --output <output>...
Path of file to write as output. Output goes to stdout if -o is
not specified.
\x20

View File

@ -50,6 +50,7 @@ pub struct Opt {
}
/// Results of code generation.
#[derive(Default)]
pub struct GeneratedCode {
/// The bytes of a C++ header file.
pub header: Vec<u8>,