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 cfg;
7 mod check;
8 pub(super) mod error;
9 mod file;
10 pub(super) mod fs;
11 mod ifndef;
12 pub(super) mod include;
13 mod names;
14 mod namespace;
15 mod nested;
16 pub(super) mod out;
17 mod write;
18 
19 use self::cfg::UnsupportedCfgEvaluator;
20 use self::error::{format_err, Result};
21 use self::file::File;
22 use self::include::Include;
23 use crate::syntax::cfg::CfgExpr;
24 use crate::syntax::report::Errors;
25 use crate::syntax::{self, attrs, Types};
26 use std::collections::BTreeSet as Set;
27 use std::path::Path;
28 
29 pub(super) use self::error::Error;
30 
31 /// Options for C++ code generation.
32 ///
33 /// We expect options to be added over time, so this is a non-exhaustive struct.
34 /// To instantiate one you need to crate a default value and mutate those fields
35 /// that you want to modify.
36 ///
37 /// ```
38 /// # use cxx_gen::Opt;
39 /// #
40 /// let impl_annotations = r#"__attribute__((visibility("default")))"#.to_owned();
41 ///
42 /// let mut opt = Opt::default();
43 /// opt.cxx_impl_annotations = Some(impl_annotations);
44 /// ```
45 #[non_exhaustive]
46 pub struct Opt {
47     /// Any additional headers to #include. The cxxbridge tool does not parse or
48     /// even require the given paths to exist; they simply go into the generated
49     /// C++ code as #include lines.
50     pub include: Vec<Include>,
51     /// Optional annotation for implementations of C++ function wrappers that
52     /// may be exposed to Rust. You may for example need to provide
53     /// `__declspec(dllexport)` or `__attribute__((visibility("default")))` if
54     /// Rust code from one shared object or executable depends on these C++
55     /// functions in another.
56     pub cxx_impl_annotations: Option<String>,
57     /// Impl for handling conditional compilation attributes.
58     pub cfg_evaluator: Box<dyn CfgEvaluator>,
59 
60     pub(super) gen_header: bool,
61     pub(super) gen_implementation: bool,
62     pub(super) allow_dot_includes: bool,
63     pub(super) doxygen: bool,
64 }
65 
66 /// Logic to decide whether a conditional compilation attribute is enabled or
67 /// disabled.
68 pub trait CfgEvaluator {
69     /// A name-only attribute such as `cfg(ident)` is passed with a `value` of
70     /// None, while `cfg(key = "value")` is passed with the "value" in `value`.
eval(&self, name: &str, value: Option<&str>) -> CfgResult71     fn eval(&self, name: &str, value: Option<&str>) -> CfgResult;
72 }
73 
74 /// Result of a [`CfgEvaluator`] evaluation.
75 pub enum CfgResult {
76     /// Cfg option is enabled.
77     True,
78     /// Cfg option is disabled.
79     False,
80     /// Cfg option is neither enabled nor disabled.
81     Undetermined {
82         /// Message explaining why the cfg option is undetermined.
83         msg: String,
84     },
85 }
86 
87 /// Results of code generation.
88 #[derive(Default)]
89 pub struct GeneratedCode {
90     /// The bytes of a C++ header file.
91     pub header: Vec<u8>,
92     /// The bytes of a C++ implementation file (e.g. .cc, cpp etc.)
93     pub implementation: Vec<u8>,
94 }
95 
96 impl Default for Opt {
default() -> Self97     fn default() -> Self {
98         Opt {
99             include: Vec::new(),
100             cxx_impl_annotations: None,
101             gen_header: true,
102             gen_implementation: true,
103             allow_dot_includes: true,
104             cfg_evaluator: Box::new(UnsupportedCfgEvaluator),
105             doxygen: false,
106         }
107     }
108 }
109 
generate_from_path(path: &Path, opt: &Opt) -> GeneratedCode110 pub(super) fn generate_from_path(path: &Path, opt: &Opt) -> GeneratedCode {
111     let source = match read_to_string(path) {
112         Ok(source) => source,
113         Err(err) => format_err(path, "", err),
114     };
115     match generate_from_string(&source, opt) {
116         Ok(out) => out,
117         Err(err) => format_err(path, &source, err),
118     }
119 }
120 
read_to_string(path: &Path) -> Result<String>121 fn read_to_string(path: &Path) -> Result<String> {
122     let bytes = if path == Path::new("-") {
123         fs::read_stdin()
124     } else {
125         fs::read(path)
126     }?;
127     match String::from_utf8(bytes) {
128         Ok(string) => Ok(string),
129         Err(err) => Err(Error::Utf8(path.to_owned(), err.utf8_error())),
130     }
131 }
132 
generate_from_string(source: &str, opt: &Opt) -> Result<GeneratedCode>133 fn generate_from_string(source: &str, opt: &Opt) -> Result<GeneratedCode> {
134     let mut source = source;
135     if source.starts_with("#!") && !source.starts_with("#![") {
136         let shebang_end = source.find('\n').unwrap_or(source.len());
137         source = &source[shebang_end..];
138     }
139     let syntax: File = syn::parse_str(source)?;
140     generate(syntax, opt)
141 }
142 
generate(syntax: File, opt: &Opt) -> Result<GeneratedCode>143 pub(super) fn generate(syntax: File, opt: &Opt) -> Result<GeneratedCode> {
144     if syntax.modules.is_empty() {
145         return Err(Error::NoBridgeMod);
146     }
147 
148     let ref mut apis = Vec::new();
149     let ref mut errors = Errors::new();
150     let ref mut cfg_errors = Set::new();
151     for bridge in syntax.modules {
152         let mut cfg = CfgExpr::Unconditional;
153         attrs::parse(
154             errors,
155             bridge.attrs,
156             attrs::Parser {
157                 cfg: Some(&mut cfg),
158                 ignore_unrecognized: true,
159                 ..Default::default()
160             },
161         );
162         if cfg::eval(errors, cfg_errors, opt.cfg_evaluator.as_ref(), &cfg) {
163             let ref namespace = bridge.namespace;
164             let trusted = bridge.unsafety.is_some();
165             apis.extend(syntax::parse_items(
166                 errors,
167                 bridge.content,
168                 trusted,
169                 namespace,
170             ));
171         }
172     }
173 
174     cfg::strip(errors, cfg_errors, opt.cfg_evaluator.as_ref(), apis);
175     errors.propagate()?;
176 
177     let ref types = Types::collect(errors, apis);
178     check::precheck(errors, apis, opt);
179     errors.propagate()?;
180 
181     let generator = check::Generator::Build;
182     check::typecheck(errors, apis, types, generator);
183     errors.propagate()?;
184 
185     // Some callers may wish to generate both header and implementation from the
186     // same token stream to avoid parsing twice. Others only need to generate
187     // one or the other.
188     let (mut header, mut implementation) = Default::default();
189     if opt.gen_header {
190         header = write::gen(apis, types, opt, true);
191     }
192     if opt.gen_implementation {
193         implementation = write::gen(apis, types, opt, false);
194     }
195     Ok(GeneratedCode {
196         header,
197         implementation,
198     })
199 }
200