cxx_gen/gen/
mod.rs

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    /// 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.
68pub 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`.
71    fn eval(&self, name: &str, value: Option<&str>) -> CfgResult;
72}
73
74/// Result of a [`CfgEvaluator`] evaluation.
75pub 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)]
89pub 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
96impl Default for Opt {
97    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
110pub(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
121fn 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
133fn 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
143pub(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}