blob: 6a1354c44ad46b3b4cc7b75d041390f5fe2f5ac3 [file] [log] [blame]
David Tolnayb4dba232020-05-11 00:55:29 -07001// Functionality that is shared between the cxx_build::bridge entry point and
2// the cxxbridge CLI command.
David Tolnay7db73692019-10-20 14:51:12 -04003
4mod error;
David Tolnayfcd8f462020-08-29 12:13:09 -07005mod file;
David Tolnay7db73692019-10-20 14:51:12 -04006pub(super) mod include;
7pub(super) mod out;
8mod write;
9
David Tolnay0d47a532020-07-30 19:39:04 -070010#[cfg(test)]
11mod tests;
12
David Tolnaye9c533e2020-08-29 23:03:51 -070013pub(super) use self::error::Error;
David Tolnay6f7f6862020-08-29 22:55:03 -070014use self::error::{format_err, Result};
David Tolnayfcd8f462020-08-29 12:13:09 -070015use self::file::File;
David Tolnay0dd85ff2020-05-03 23:43:33 -070016use crate::syntax::report::Errors;
David Tolnayb6cf3142020-04-19 20:56:09 -070017use crate::syntax::{self, check, Types};
David Tolnay7db73692019-10-20 14:51:12 -040018use std::fs;
David Tolnay7db73692019-10-20 14:51:12 -040019use std::path::Path;
David Tolnay7db73692019-10-20 14:51:12 -040020
Adrian Taylor0926f642020-08-25 13:08:06 -070021/// Options for C++ code generation.
David Tolnayec088152020-08-29 23:29:26 -070022///
23/// We expect options to be added over time, so this is a non-exhaustive struct.
24/// To instantiate one you need to crate a default value and mutate those fields
25/// that you want to modify.
26///
27/// ```
28/// # use cxx_gen::Opt;
29/// #
30/// let impl_annotations = r#"__attribute__((visibility("default")))"#.to_owned();
31///
32/// let mut opt = Opt::default();
33/// opt.cxx_impl_annotations = Some(impl_annotations);
34/// ```
David Tolnaya5cca312020-08-29 23:40:04 -070035#[derive(Default)]
David Tolnayec088152020-08-29 23:29:26 -070036#[non_exhaustive]
Adrian Taylor0926f642020-08-25 13:08:06 -070037pub struct Opt {
David Tolnaydf2f78d2020-08-29 23:31:53 -070038 /// Any additional headers to #include. The cxxbridge tool does not parse or
39 /// even require the given paths to exist; they simply go into the generated
40 /// C++ code as #include lines.
David Tolnay33d30292020-03-18 18:02:02 -070041 pub include: Vec<String>,
David Tolnaydf2f78d2020-08-29 23:31:53 -070042 /// Optional annotation for implementations of C++ function wrappers that
43 /// may be exposed to Rust. You may for example need to provide
44 /// `__declspec(dllexport)` or `__attribute__((visibility("default")))` if
45 /// Rust code from one shared object or executable depends on these C++
46 /// functions in another.
Adrian Taylor21f0ff02020-07-21 16:21:48 -070047 pub cxx_impl_annotations: Option<String>,
David Tolnay33d30292020-03-18 18:02:02 -070048}
49
David Tolnay19cb7852020-08-30 00:04:59 -070050/// Results of code generation.
51pub struct GeneratedCode {
52 /// The bytes of a C++ header file.
53 pub header: Vec<u8>,
54 /// The bytes of a C++ implementation file (e.g. .cc, cpp etc.)
David Tolnayd3659d82020-08-30 00:12:25 -070055 pub implementation: Vec<u8>,
David Tolnay19cb7852020-08-30 00:04:59 -070056}
57
David Tolnaya5cca312020-08-29 23:40:04 -070058pub(super) fn do_generate_bridge(path: &Path, opt: &Opt) -> Vec<u8> {
David Tolnay7db73692019-10-20 14:51:12 -040059 let header = false;
Adrian Taylor8205e622020-07-21 21:53:59 -070060 generate_from_path(path, opt, header)
David Tolnay7db73692019-10-20 14:51:12 -040061}
62
David Tolnaya5cca312020-08-29 23:40:04 -070063pub(super) fn do_generate_header(path: &Path, opt: &Opt) -> Vec<u8> {
David Tolnay7db73692019-10-20 14:51:12 -040064 let header = true;
Adrian Taylor8205e622020-07-21 21:53:59 -070065 generate_from_path(path, opt, header)
David Tolnay7db73692019-10-20 14:51:12 -040066}
67
David Tolnaya5cca312020-08-29 23:40:04 -070068fn generate_from_path(path: &Path, opt: &Opt, header: bool) -> Vec<u8> {
David Tolnay7db73692019-10-20 14:51:12 -040069 let source = match fs::read_to_string(path) {
70 Ok(source) => source,
71 Err(err) => format_err(path, "", Error::Io(err)),
72 };
Adrian Taylor593eddb2020-08-21 23:46:08 -070073 match generate_from_string(&source, opt, header) {
Adrian Taylor8205e622020-07-21 21:53:59 -070074 Ok(out) => out,
David Tolnay7db73692019-10-20 14:51:12 -040075 Err(err) => format_err(path, &source, err),
Adrian Taylor593eddb2020-08-21 23:46:08 -070076 }
77}
78
David Tolnaya5cca312020-08-29 23:40:04 -070079fn generate_from_string(source: &str, opt: &Opt, header: bool) -> Result<Vec<u8>> {
David Tolnay5fc28552020-08-29 22:05:53 -070080 let mut source = source;
David Tolnay17c32302020-08-29 12:21:16 -070081 if source.starts_with("#!") && !source.starts_with("#![") {
82 let shebang_end = source.find('\n').unwrap_or(source.len());
83 source = &source[shebang_end..];
84 }
David Tolnay5fc28552020-08-29 22:05:53 -070085 let syntax: File = syn::parse_str(source)?;
David Tolnay19cb7852020-08-30 00:04:59 -070086 let generated = generate(syntax, opt, header, !header)?;
David Tolnayd3659d82020-08-30 00:12:25 -070087 Ok(if header {
88 generated.header
89 } else {
90 generated.implementation
91 })
David Tolnay7db73692019-10-20 14:51:12 -040092}
Adrian Taylor8205e622020-07-21 21:53:59 -070093
David Tolnay366c41a2020-08-29 22:28:21 -070094pub(super) fn generate(
Adrian Taylor9fc08462020-08-14 10:51:00 -070095 syntax: File,
David Tolnaya5cca312020-08-29 23:40:04 -070096 opt: &Opt,
Adrian Taylor9fc08462020-08-14 10:51:00 -070097 gen_header: bool,
David Tolnayd3659d82020-08-30 00:12:25 -070098 gen_implementation: bool,
David Tolnay19cb7852020-08-30 00:04:59 -070099) -> Result<GeneratedCode> {
Adrian Taylor8205e622020-07-21 21:53:59 -0700100 proc_macro2::fallback::force();
101 let ref mut errors = Errors::new();
David Tolnay3c64a4e2020-08-29 14:07:38 -0700102 let bridge = syntax
103 .modules
104 .into_iter()
105 .next()
106 .ok_or(Error::NoBridgeMod)?;
Adrian Taylor8205e622020-07-21 21:53:59 -0700107 let ref namespace = bridge.namespace;
David Tolnay805dca32020-08-29 19:09:55 -0700108 let trusted = bridge.unsafety.is_some();
109 let ref apis = syntax::parse_items(errors, bridge.content, trusted);
Adrian Taylor8205e622020-07-21 21:53:59 -0700110 let ref types = Types::collect(errors, apis);
111 errors.propagate()?;
112 check::typecheck(errors, namespace, apis, types);
113 errors.propagate()?;
Adrian Taylor9fc08462020-08-14 10:51:00 -0700114 // Some callers may wish to generate both header and C++
115 // from the same token stream to avoid parsing twice. But others
116 // only need to generate one or the other.
David Tolnay19cb7852020-08-30 00:04:59 -0700117 Ok(GeneratedCode {
118 header: if gen_header {
119 write::gen(namespace, apis, types, opt, true).content()
120 } else {
121 Vec::new()
122 },
David Tolnayd3659d82020-08-30 00:12:25 -0700123 implementation: if gen_implementation {
David Tolnay19cb7852020-08-30 00:04:59 -0700124 write::gen(namespace, apis, types, opt, false).content()
125 } else {
126 Vec::new()
127 },
128 })
Adrian Taylor8205e622020-07-21 21:53:59 -0700129}