Use Opt to control which outputs get generated

This commit is contained in:
David Tolnay 2020-08-30 00:34:17 -07:00
parent 318c3530b4
commit 8238d4a316
No known key found for this signature in database
GPG Key ID: F9BA143B95FF6D82
5 changed files with 31 additions and 40 deletions

View File

@ -103,16 +103,16 @@ pub fn bridges(rust_source_files: impl IntoIterator<Item = impl AsRef<Path>>) ->
fn try_generate_bridge(build: &mut cc::Build, rust_source_file: &Path) -> Result<()> {
let opt = Opt::default();
let header = gen::do_generate_header(rust_source_file, &opt);
let generated = gen::generate_from_path(rust_source_file, &opt);
let header_path = paths::out_with_extension(rust_source_file, ".h")?;
fs::create_dir_all(header_path.parent().unwrap())?;
fs::write(&header_path, header)?;
fs::write(&header_path, generated.header)?;
paths::symlink_header(&header_path, rust_source_file);
let bridge = gen::do_generate_bridge(rust_source_file, &opt);
let bridge_path = paths::out_with_extension(rust_source_file, ".cc")?;
fs::write(&bridge_path, bridge)?;
build.file(&bridge_path);
let implementation_path = paths::out_with_extension(rust_source_file, ".cc")?;
fs::write(&implementation_path, generated.implementation)?;
build.file(&implementation_path);
let ref cxx_h = paths::include_dir()?.join("rust").join("cxx.h");
let _ = fs::create_dir_all(cxx_h.parent().unwrap());

View File

@ -32,11 +32,13 @@ fn main() {
let gen = gen::Opt {
include: opt.include,
cxx_impl_annotations: opt.cxx_impl_annotations,
gen_header: opt.header,
gen_implementation: !opt.header,
};
match (opt.input, opt.header) {
(Some(input), true) => write(gen::do_generate_header(&input, &gen)),
(Some(input), false) => write(gen::do_generate_bridge(&input, &gen)),
(Some(input), true) => write(gen::generate_from_path(&input, &gen).header),
(Some(input), false) => write(gen::generate_from_path(&input, &gen).implementation),
(None, true) => write(include::HEADER),
(None, false) => unreachable!(), // enforced by required_unless
}

View File

@ -20,7 +20,7 @@ pub fn generate_header_and_cc(rust_source: TokenStream, opt: &Opt) -> Result<Gen
let syntax = syn::parse2(rust_source)
.map_err(crate::gen::Error::from)
.map_err(Error)?;
gen::generate(syntax, opt, true, true).map_err(Error)
gen::generate(syntax, opt).map_err(Error)
}
impl Display for Error {

View File

@ -44,6 +44,9 @@ pub struct Opt {
/// Rust code from one shared object or executable depends on these C++
/// functions in another.
pub cxx_impl_annotations: Option<String>,
pub(super) gen_header: bool,
pub(super) gen_implementation: bool,
}
/// Results of code generation.
@ -59,52 +62,34 @@ impl Default for Opt {
Opt {
include: Vec::new(),
cxx_impl_annotations: None,
gen_header: true,
gen_implementation: true,
}
}
}
pub(super) fn do_generate_bridge(path: &Path, opt: &Opt) -> Vec<u8> {
let header = false;
generate_from_path(path, opt, header)
}
pub(super) fn do_generate_header(path: &Path, opt: &Opt) -> Vec<u8> {
let header = true;
generate_from_path(path, opt, header)
}
fn generate_from_path(path: &Path, opt: &Opt, header: bool) -> Vec<u8> {
pub(super) fn generate_from_path(path: &Path, opt: &Opt) -> GeneratedCode {
let source = match fs::read_to_string(path) {
Ok(source) => source,
Err(err) => format_err(path, "", Error::Io(err)),
};
match generate_from_string(&source, opt, header) {
match generate_from_string(&source, opt) {
Ok(out) => out,
Err(err) => format_err(path, &source, err),
}
}
fn generate_from_string(source: &str, opt: &Opt, header: bool) -> Result<Vec<u8>> {
fn generate_from_string(source: &str, opt: &Opt) -> Result<GeneratedCode> {
let mut source = source;
if source.starts_with("#!") && !source.starts_with("#![") {
let shebang_end = source.find('\n').unwrap_or(source.len());
source = &source[shebang_end..];
}
let syntax: File = syn::parse_str(source)?;
let generated = generate(syntax, opt, header, !header)?;
Ok(if header {
generated.header
} else {
generated.implementation
})
generate(syntax, opt)
}
pub(super) fn generate(
syntax: File,
opt: &Opt,
gen_header: bool,
gen_implementation: bool,
) -> Result<GeneratedCode> {
pub(super) fn generate(syntax: File, opt: &Opt) -> Result<GeneratedCode> {
proc_macro2::fallback::force();
let ref mut errors = Errors::new();
let bridge = syntax
@ -123,12 +108,12 @@ pub(super) fn generate(
// from the same token stream to avoid parsing twice. But others
// only need to generate one or the other.
Ok(GeneratedCode {
header: if gen_header {
header: if opt.gen_header {
write::gen(namespace, apis, types, opt, true).content()
} else {
Vec::new()
},
implementation: if gen_implementation {
implementation: if opt.gen_implementation {
write::gen(namespace, apis, types, opt, false).content()
} else {
Vec::new()

View File

@ -14,9 +14,11 @@ fn test_cpp() {
let opts = Opt {
include: Vec::new(),
cxx_impl_annotations: None,
gen_header: false,
gen_implementation: true,
};
let output = generate_from_string(CPP_EXAMPLE, &opts, false).unwrap();
let output = std::str::from_utf8(&output).unwrap();
let output = generate_from_string(CPP_EXAMPLE, &opts).unwrap();
let output = std::str::from_utf8(&output.implementation).unwrap();
// To avoid continual breakage we won't test every byte.
// Let's look for the major features.
assert!(output.contains("void cxxbridge03$do_cpp_thing(::rust::Str::Repr foo)"));
@ -27,8 +29,10 @@ fn test_annotation() {
let opts = Opt {
include: Vec::new(),
cxx_impl_annotations: Some("ANNOTATION".to_string()),
gen_header: false,
gen_implementation: true,
};
let output = generate_from_string(CPP_EXAMPLE, &opts, false).unwrap();
let output = std::str::from_utf8(&output).unwrap();
let output = generate_from_string(CPP_EXAMPLE, &opts).unwrap();
let output = std::str::from_utf8(&output.implementation).unwrap();
assert!(output.contains("ANNOTATION void cxxbridge03$do_cpp_thing(::rust::Str::Repr foo)"));
}