xref: /third_party/rust/crates/cxx/gen/src/mod.rs (revision 33d722a9)
1// Functionality that is shared between the cxx_build::bridge entry point and
2// the cxxbridge CLI command.
3
4mod block;
5mod builtin;
6mod cfg;
7mod check;
8pub(super) mod error;
9mod file;
10pub(super) mod fs;
11mod ifndef;
12pub(super) mod include;
13mod names;
14mod namespace;
15mod nested;
16pub(super) mod out;
17mod write;
18
19use self::cfg::UnsupportedCfgEvaluator;
20use self::error::{format_err, Result};
21use self::file::File;
22use self::include::Include;
23use crate::syntax::cfg::CfgExpr;
24use crate::syntax::report::Errors;
25use crate::syntax::{self, attrs, Types};
26use std::collections::BTreeSet as Set;
27use std::path::Path;
28
29pub(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]
46pub 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
65pub(super) trait CfgEvaluator {
66    fn eval(&self, name: &str, value: Option<&str>) -> CfgResult;
67}
68
69pub(super) enum CfgResult {
70    True,
71    False,
72    Undetermined { msg: String },
73}
74
75/// Results of code generation.
76#[derive(Default)]
77pub 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
84impl Default for Opt {
85    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
98pub(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
109fn 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
121fn 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
132pub(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