• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 
58     pub(super) gen_header: bool,
59     pub(super) gen_implementation: bool,
60     pub(super) allow_dot_includes: bool,
61     pub(super) cfg_evaluator: Box<dyn CfgEvaluator>,
62     pub(super) doxygen: bool,
63 }
64 
65 pub(super) trait CfgEvaluator {
eval(&self, name: &str, value: Option<&str>) -> CfgResult66     fn eval(&self, name: &str, value: Option<&str>) -> CfgResult;
67 }
68 
69 pub(super) enum CfgResult {
70     True,
71     False,
72     Undetermined { msg: String },
73 }
74 
75 /// Results of code generation.
76 #[derive(Default)]
77 pub struct GeneratedCode {
78     /// The bytes of a C++ header file.
79     pub header: Vec<u8>,
80     /// The bytes of a C++ implementation file (e.g. .cc, cpp etc.)
81     pub implementation: Vec<u8>,
82 }
83 
84 impl Default for Opt {
default() -> Self85     fn default() -> Self {
86         Opt {
87             include: Vec::new(),
88             cxx_impl_annotations: None,
89             gen_header: true,
90             gen_implementation: true,
91             allow_dot_includes: true,
92             cfg_evaluator: Box::new(UnsupportedCfgEvaluator),
93             doxygen: false,
94         }
95     }
96 }
97 
generate_from_path(path: &Path, opt: &Opt) -> GeneratedCode98 pub(super) fn generate_from_path(path: &Path, opt: &Opt) -> GeneratedCode {
99     let source = match read_to_string(path) {
100         Ok(source) => source,
101         Err(err) => format_err(path, "", err),
102     };
103     match generate_from_string(&source, opt) {
104         Ok(out) => out,
105         Err(err) => format_err(path, &source, err),
106     }
107 }
108 
read_to_string(path: &Path) -> Result<String>109 fn read_to_string(path: &Path) -> Result<String> {
110     let bytes = if path == Path::new("-") {
111         fs::read_stdin()
112     } else {
113         fs::read(path)
114     }?;
115     match String::from_utf8(bytes) {
116         Ok(string) => Ok(string),
117         Err(err) => Err(Error::Utf8(path.to_owned(), err.utf8_error())),
118     }
119 }
120 
generate_from_string(source: &str, opt: &Opt) -> Result<GeneratedCode>121 fn generate_from_string(source: &str, opt: &Opt) -> Result<GeneratedCode> {
122     let mut source = source;
123     if source.starts_with("#!") && !source.starts_with("#![") {
124         let shebang_end = source.find('\n').unwrap_or(source.len());
125         source = &source[shebang_end..];
126     }
127     proc_macro2::fallback::force();
128     let syntax: File = syn::parse_str(source)?;
129     generate(syntax, opt)
130 }
131 
generate(syntax: File, opt: &Opt) -> Result<GeneratedCode>132 pub(super) fn generate(syntax: File, opt: &Opt) -> Result<GeneratedCode> {
133     if syntax.modules.is_empty() {
134         return Err(Error::NoBridgeMod);
135     }
136 
137     let ref mut apis = Vec::new();
138     let ref mut errors = Errors::new();
139     let ref mut cfg_errors = Set::new();
140     for bridge in syntax.modules {
141         let mut cfg = CfgExpr::Unconditional;
142         attrs::parse(
143             errors,
144             bridge.attrs,
145             attrs::Parser {
146                 cfg: Some(&mut cfg),
147                 ignore_unrecognized: true,
148                 ..Default::default()
149             },
150         );
151         if cfg::eval(errors, cfg_errors, opt.cfg_evaluator.as_ref(), &cfg) {
152             let ref namespace = bridge.namespace;
153             let trusted = bridge.unsafety.is_some();
154             apis.extend(syntax::parse_items(
155                 errors,
156                 bridge.content,
157                 trusted,
158                 namespace,
159             ));
160         }
161     }
162 
163     cfg::strip(errors, cfg_errors, opt.cfg_evaluator.as_ref(), apis);
164     errors.propagate()?;
165 
166     let ref types = Types::collect(errors, apis);
167     check::precheck(errors, apis, opt);
168     errors.propagate()?;
169 
170     let generator = check::Generator::Build;
171     check::typecheck(errors, apis, types, generator);
172     errors.propagate()?;
173 
174     // Some callers may wish to generate both header and implementation from the
175     // same token stream to avoid parsing twice. Others only need to generate
176     // one or the other.
177     let (mut header, mut implementation) = Default::default();
178     if opt.gen_header {
179         header = write::gen(apis, types, opt, true);
180     }
181     if opt.gen_implementation {
182         implementation = write::gen(apis, types, opt, false);
183     }
184     Ok(GeneratedCode {
185         header,
186         implementation,
187     })
188 }
189