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