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