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