| // Functionality that is shared between the cxx_build::bridge entry point and |
| // the cxxbridge CLI command. |
| |
| mod alphasort; |
| mod block; |
| mod builtin; |
| mod check; |
| pub(super) mod error; |
| mod file; |
| pub(super) mod fs; |
| mod ifndef; |
| pub(super) mod include; |
| pub(super) mod out; |
| mod write; |
| |
| pub(super) use self::error::Error; |
| use self::error::{format_err, Result}; |
| use self::file::File; |
| use self::include::Include; |
| use crate::syntax::report::Errors; |
| use crate::syntax::{self, Types}; |
| use std::path::Path; |
| |
| /// Options for C++ code generation. |
| /// |
| /// We expect options to be added over time, so this is a non-exhaustive struct. |
| /// To instantiate one you need to crate a default value and mutate those fields |
| /// that you want to modify. |
| /// |
| /// ``` |
| /// # use cxx_gen::Opt; |
| /// # |
| /// let impl_annotations = r#"__attribute__((visibility("default")))"#.to_owned(); |
| /// |
| /// let mut opt = Opt::default(); |
| /// opt.cxx_impl_annotations = Some(impl_annotations); |
| /// ``` |
| #[non_exhaustive] |
| pub struct Opt { |
| /// 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. |
| pub include: Vec<Include>, |
| /// 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. |
| pub cxx_impl_annotations: Option<String>, |
| |
| pub(super) gen_header: bool, |
| pub(super) gen_implementation: bool, |
| pub(super) allow_dot_includes: bool, |
| } |
| |
| /// Results of code generation. |
| #[derive(Default)] |
| pub struct GeneratedCode { |
| /// The bytes of a C++ header file. |
| pub header: Vec<u8>, |
| /// The bytes of a C++ implementation file (e.g. .cc, cpp etc.) |
| pub implementation: Vec<u8>, |
| } |
| |
| impl Default for Opt { |
| fn default() -> Self { |
| Opt { |
| include: Vec::new(), |
| cxx_impl_annotations: None, |
| gen_header: true, |
| gen_implementation: true, |
| allow_dot_includes: true, |
| } |
| } |
| } |
| |
| pub(super) fn generate_from_path(path: &Path, opt: &Opt) -> GeneratedCode { |
| let source = match read_to_string(path) { |
| Ok(source) => source, |
| Err(err) => format_err(path, "", err), |
| }; |
| match generate_from_string(&source, opt) { |
| Ok(out) => out, |
| Err(err) => format_err(path, &source, err), |
| } |
| } |
| |
| fn read_to_string(path: &Path) -> Result<String> { |
| let bytes = if path == Path::new("-") { |
| fs::read_stdin() |
| } else { |
| fs::read(path) |
| }?; |
| match String::from_utf8(bytes) { |
| Ok(string) => Ok(string), |
| Err(err) => Err(Error::Utf8(path.to_owned(), err.utf8_error())), |
| } |
| } |
| |
| 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..]; |
| } |
| proc_macro2::fallback::force(); |
| let syntax: File = syn::parse_str(source)?; |
| generate(syntax, opt) |
| } |
| |
| pub(super) fn generate(syntax: File, opt: &Opt) -> Result<GeneratedCode> { |
| if syntax.modules.is_empty() { |
| return Err(Error::NoBridgeMod); |
| } |
| |
| let ref mut apis = Vec::new(); |
| let ref mut errors = Errors::new(); |
| for bridge in syntax.modules { |
| let ref namespace = bridge.namespace; |
| let trusted = bridge.unsafety.is_some(); |
| apis.extend(syntax::parse_items( |
| errors, |
| bridge.content, |
| trusted, |
| namespace, |
| )); |
| } |
| |
| let ref types = Types::collect(errors, apis); |
| check::precheck(errors, apis, opt); |
| errors.propagate()?; |
| check::typecheck(errors, apis, types); |
| errors.propagate()?; |
| |
| // Some callers may wish to generate both header and implementation from the |
| // same token stream to avoid parsing twice. Others only need to generate |
| // one or the other. |
| let (mut header, mut implementation) = Default::default(); |
| if opt.gen_header { |
| header = write::gen(apis, types, opt, true); |
| } |
| if opt.gen_implementation { |
| implementation = write::gen(apis, types, opt, false); |
| } |
| Ok(GeneratedCode { |
| header, |
| implementation, |
| }) |
| } |