1 // Functionality that is shared between the cxx_build::bridge entry point and
2 // the cxxbridge CLI command.
3 
4 mod block;
5 mod builtin;
6 mod check;
7 pub(super) mod error;
8 mod file;
9 pub(super) mod fs;
10 mod ifndef;
11 pub(super) mod include;
12 mod namespace;
13 mod nested;
14 pub(super) mod out;
15 mod write;
16 
17 pub(super) use self::error::Error;
18 use self::error::{format_err, Result};
19 use self::file::File;
20 use self::include::Include;
21 use crate::syntax::report::Errors;
22 use crate::syntax::{self, Types};
23 use std::path::Path;
24 
25 /// Options for C++ code generation.
26 ///
27 /// We expect options to be added over time, so this is a non-exhaustive struct.
28 /// To instantiate one you need to crate a default value and mutate those fields
29 /// that you want to modify.
30 ///
31 /// ```
32 /// # use cxx_gen::Opt;
33 /// #
34 /// let impl_annotations = r#"__attribute__((visibility("default")))"#.to_owned();
35 ///
36 /// let mut opt = Opt::default();
37 /// opt.cxx_impl_annotations = Some(impl_annotations);
38 /// ```
39 #[non_exhaustive]
40 pub struct Opt {
41     /// Any additional headers to #include. The cxxbridge tool does not parse or
42     /// even require the given paths to exist; they simply go into the generated
43     /// C++ code as #include lines.
44     pub include: Vec<Include>,
45     /// Optional annotation for implementations of C++ function wrappers that
46     /// may be exposed to Rust. You may for example need to provide
47     /// `__declspec(dllexport)` or `__attribute__((visibility("default")))` if
48     /// Rust code from one shared object or executable depends on these C++
49     /// functions in another.
50     pub cxx_impl_annotations: Option<String>,
51 
52     pub(super) gen_header: bool,
53     pub(super) gen_implementation: bool,
54     pub(super) allow_dot_includes: bool,
55 }
56 
57 /// Results of code generation.
58 #[derive(Default)]
59 pub struct GeneratedCode {
60     /// The bytes of a C++ header file.
61     pub header: Vec<u8>,
62     /// The bytes of a C++ implementation file (e.g. .cc, cpp etc.)
63     pub implementation: Vec<u8>,
64 }
65 
66 impl Default for Opt {
default() -> Self67     fn default() -> Self {
68         Opt {
69             include: Vec::new(),
70             cxx_impl_annotations: None,
71             gen_header: true,
72             gen_implementation: true,
73             allow_dot_includes: true,
74         }
75     }
76 }
77 
generate_from_path(path: &Path, opt: &Opt) -> GeneratedCode78 pub(super) fn generate_from_path(path: &Path, opt: &Opt) -> GeneratedCode {
79     let source = match read_to_string(path) {
80         Ok(source) => source,
81         Err(err) => format_err(path, "", err),
82     };
83     match generate_from_string(&source, opt) {
84         Ok(out) => out,
85         Err(err) => format_err(path, &source, err),
86     }
87 }
88 
read_to_string(path: &Path) -> Result<String>89 fn read_to_string(path: &Path) -> Result<String> {
90     let bytes = if path == Path::new("-") {
91         fs::read_stdin()
92     } else {
93         fs::read(path)
94     }?;
95     match String::from_utf8(bytes) {
96         Ok(string) => Ok(string),
97         Err(err) => Err(Error::Utf8(path.to_owned(), err.utf8_error())),
98     }
99 }
100 
generate_from_string(source: &str, opt: &Opt) -> Result<GeneratedCode>101 fn generate_from_string(source: &str, opt: &Opt) -> Result<GeneratedCode> {
102     let mut source = source;
103     if source.starts_with("#!") && !source.starts_with("#![") {
104         let shebang_end = source.find('\n').unwrap_or(source.len());
105         source = &source[shebang_end..];
106     }
107     proc_macro2::fallback::force();
108     let syntax: File = syn::parse_str(source)?;
109     generate(syntax, opt)
110 }
111 
generate(syntax: File, opt: &Opt) -> Result<GeneratedCode>112 pub(super) fn generate(syntax: File, opt: &Opt) -> Result<GeneratedCode> {
113     if syntax.modules.is_empty() {
114         return Err(Error::NoBridgeMod);
115     }
116 
117     let ref mut apis = Vec::new();
118     let ref mut errors = Errors::new();
119     for bridge in syntax.modules {
120         let ref namespace = bridge.namespace;
121         let trusted = bridge.unsafety.is_some();
122         apis.extend(syntax::parse_items(
123             errors,
124             bridge.content,
125             trusted,
126             namespace,
127         ));
128     }
129 
130     let ref types = Types::collect(errors, apis);
131     check::precheck(errors, apis, opt);
132     errors.propagate()?;
133     check::typecheck(errors, apis, types);
134     errors.propagate()?;
135 
136     // Some callers may wish to generate both header and implementation from the
137     // same token stream to avoid parsing twice. Others only need to generate
138     // one or the other.
139     let (mut header, mut implementation) = Default::default();
140     if opt.gen_header {
141         header = write::gen(apis, types, opt, true);
142     }
143     if opt.gen_implementation {
144         implementation = write::gen(apis, types, opt, false);
145     }
146     Ok(GeneratedCode {
147         header,
148         implementation,
149     })
150 }
151