cxx_build/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 guard;
12mod ifndef;
13pub(super) mod include;
14mod names;
15mod namespace;
16mod nested;
17pub(super) mod out;
18mod pragma;
19mod write;
20
21use self::cfg::UnsupportedCfgEvaluator;
22use self::error::{format_err, Result};
23use self::file::File;
24use self::include::Include;
25use crate::syntax::cfg::CfgExpr;
26use crate::syntax::report::Errors;
27use crate::syntax::{self, attrs, Types};
28use std::collections::BTreeSet as Set;
29use std::path::Path;
30
31pub(super) use self::error::Error;
32
33/// Options for C++ code generation.
34///
35/// We expect options to be added over time, so this is a non-exhaustive struct.
36/// To instantiate one you need to crate a default value and mutate those fields
37/// that you want to modify.
38///
39/// ```
40/// # use cxx_gen::Opt;
41/// #
42/// let impl_annotations = r#"__attribute__((visibility("default")))"#.to_owned();
43///
44/// let mut opt = Opt::default();
45/// opt.cxx_impl_annotations = Some(impl_annotations);
46/// ```
47#[non_exhaustive]
48pub struct Opt {
49    /// Any additional headers to #include. The cxxbridge tool does not parse or
50    /// even require the given paths to exist; they simply go into the generated
51    /// C++ code as #include lines.
52    pub include: Vec<Include>,
53    /// Optional annotation for implementations of C++ function wrappers that
54    /// may be exposed to Rust. You may for example need to provide
55    /// `__declspec(dllexport)` or `__attribute__((visibility("default")))` if
56    /// Rust code from one shared object or executable depends on these C++
57    /// functions in another.
58    pub cxx_impl_annotations: Option<String>,
59    /// Impl for handling conditional compilation attributes.
60    pub cfg_evaluator: Box<dyn CfgEvaluator>,
61
62    pub(super) gen_header: bool,
63    pub(super) gen_implementation: bool,
64    pub(super) allow_dot_includes: bool,
65    pub(super) doxygen: bool,
66}
67
68/// Logic to decide whether a conditional compilation attribute is enabled or
69/// disabled.
70pub trait CfgEvaluator {
71    /// A name-only attribute such as `cfg(ident)` is passed with a `value` of
72    /// None, while `cfg(key = "value")` is passed with the "value" in `value`.
73    fn eval(&self, name: &str, value: Option<&str>) -> CfgResult;
74}
75
76/// Result of a [`CfgEvaluator`] evaluation.
77pub enum CfgResult {
78    /// Cfg option is enabled.
79    True,
80    /// Cfg option is disabled.
81    False,
82    /// Cfg option is neither enabled nor disabled.
83    Undetermined {
84        /// Message explaining why the cfg option is undetermined.
85        msg: String,
86    },
87}
88
89/// Results of code generation.
90#[derive(#[automatically_derived]
impl ::core::default::Default for GeneratedCode {
    #[inline]
    fn default() -> GeneratedCode {
        GeneratedCode {
            header: ::core::default::Default::default(),
            implementation: ::core::default::Default::default(),
        }
    }
}Default)]
91pub struct GeneratedCode {
92    /// The bytes of a C++ header file.
93    pub header: Vec<u8>,
94    /// The bytes of a C++ implementation file (e.g. .cc, cpp etc.)
95    pub implementation: Vec<u8>,
96}
97
98impl Default for Opt {
99    fn default() -> Self {
100        Opt {
101            include: Vec::new(),
102            cxx_impl_annotations: None,
103            gen_header: true,
104            gen_implementation: true,
105            allow_dot_includes: true,
106            cfg_evaluator: Box::new(UnsupportedCfgEvaluator),
107            doxygen: false,
108        }
109    }
110}
111
112pub(super) fn generate_from_path(path: &Path, opt: &Opt) -> GeneratedCode {
113    let source = match read_to_string(path) {
114        Ok(source) => source,
115        Err(err) => format_err(path, "", err),
116    };
117    match generate_from_string(&source, opt) {
118        Ok(out) => out,
119        Err(err) => format_err(path, &source, err),
120    }
121}
122
123fn read_to_string(path: &Path) -> Result<String> {
124    let bytes = if path == Path::new("-") {
125        fs::read_stdin()
126    } else {
127        fs::read(path)
128    }?;
129    match String::from_utf8(bytes) {
130        Ok(string) => Ok(string),
131        Err(err) => Err(Error::Utf8(path.to_owned(), err.utf8_error())),
132    }
133}
134
135fn generate_from_string(source: &str, opt: &Opt) -> Result<GeneratedCode> {
136    let mut source = source;
137    if source.starts_with("#!") && !source.starts_with("#![") {
138        let shebang_end = source.find('\n').unwrap_or(source.len());
139        source = &source[shebang_end..];
140    }
141    let syntax: File = syn::parse_str(source)?;
142    generate(syntax, opt)
143}
144
145pub(super) fn generate(syntax: File, opt: &Opt) -> Result<GeneratedCode> {
146    if syntax.modules.is_empty() {
147        return Err(Error::NoBridgeMod);
148    }
149
150    let ref mut apis = Vec::new();
151    let ref mut errors = Errors::new();
152    let ref mut cfg_errors = Set::new();
153    for bridge in syntax.modules {
154        let mut cfg = CfgExpr::Unconditional;
155        let _ = attrs::parse(
156            errors,
157            bridge.attrs,
158            attrs::Parser {
159                cfg: Some(&mut cfg),
160                ignore_unrecognized: true,
161                ..Default::default()
162            },
163        );
164        if cfg::eval(errors, cfg_errors, opt.cfg_evaluator.as_ref(), &cfg) {
165            let ref namespace = bridge.namespace;
166            let trusted = bridge.unsafety.is_some();
167            apis.extend(syntax::parse_items(
168                errors,
169                bridge.content,
170                trusted,
171                namespace,
172            ));
173        }
174    }
175
176    cfg::strip(errors, cfg_errors, opt.cfg_evaluator.as_ref(), apis);
177    errors.propagate()?;
178
179    let ref types = Types::collect(errors, apis);
180    check::precheck(errors, apis, opt);
181    errors.propagate()?;
182
183    let generator = check::Generator::Build;
184    check::typecheck(errors, apis, types, generator);
185    errors.propagate()?;
186
187    // Some callers may wish to generate both header and implementation from the
188    // same token stream to avoid parsing twice. Others only need to generate
189    // one or the other.
190    let (mut header, mut implementation) = Default::default();
191    if opt.gen_header {
192        header = write::gen(apis, types, opt, true);
193    }
194    if opt.gen_implementation {
195        implementation = write::gen(apis, types, opt, false);
196    }
197    Ok(GeneratedCode {
198        header,
199        implementation,
200    })
201}