blob: 3d12c71d21190d1db0b5d61778d00a7753d13d0c [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
David Tolnay0c033e32020-11-01 15:15:48 -08004mod block;
David Tolnay3be0e1f2020-10-31 20:53:00 -07005mod builtin;
David Tolnay75c23852020-10-27 21:07:53 -07006mod check;
David Tolnaybb3ba502020-08-30 19:59:41 -07007pub(super) mod error;
David Tolnayfcd8f462020-08-29 12:13:09 -07008mod file;
David Tolnay0d85ccd2020-08-30 20:18:13 -07009pub(super) mod fs;
David Tolnay3e278d72020-10-31 22:14:35 -070010mod ifndef;
David Tolnay7db73692019-10-20 14:51:12 -040011pub(super) mod include;
David Tolnay90b133b2020-11-01 22:53:37 -080012mod namespace;
David Tolnayf7b81fb2020-11-01 22:39:04 -080013mod nested;
David Tolnay7db73692019-10-20 14:51:12 -040014pub(super) mod out;
15mod write;
16
David Tolnaye9c533e2020-08-29 23:03:51 -070017pub(super) use self::error::Error;
David Tolnay6f7f6862020-08-29 22:55:03 -070018use self::error::{format_err, Result};
David Tolnayfcd8f462020-08-29 12:13:09 -070019use self::file::File;
David Tolnay700cd0c2020-10-28 12:40:27 -070020use self::include::Include;
David Tolnay0dd85ff2020-05-03 23:43:33 -070021use crate::syntax::report::Errors;
David Tolnay75c23852020-10-27 21:07:53 -070022use crate::syntax::{self, Types};
David Tolnay7db73692019-10-20 14:51:12 -040023use std::path::Path;
David Tolnay7db73692019-10-20 14:51:12 -040024
Adrian Taylor0926f642020-08-25 13:08:06 -070025/// Options for C++ code generation.
David Tolnayec088152020-08-29 23:29:26 -070026///
27/// We expect options to be added over time, so this is a non-exhaustive struct.
28/// To instantiate one you need to crate a default value and mutate those fields
29/// that you want to modify.
30///
31/// ```
32/// # use cxx_gen::Opt;
33/// #
34/// let impl_annotations = r#"__attribute__((visibility("default")))"#.to_owned();
35///
36/// let mut opt = Opt::default();
37/// opt.cxx_impl_annotations = Some(impl_annotations);
38/// ```
David Tolnayec088152020-08-29 23:29:26 -070039#[non_exhaustive]
Adrian Taylor0926f642020-08-25 13:08:06 -070040pub struct Opt {
David Tolnaydf2f78d2020-08-29 23:31:53 -070041 /// Any additional headers to #include. The cxxbridge tool does not parse or
42 /// even require the given paths to exist; they simply go into the generated
43 /// C++ code as #include lines.
David Tolnay700cd0c2020-10-28 12:40:27 -070044 pub include: Vec<Include>,
David Tolnaydf2f78d2020-08-29 23:31:53 -070045 /// Optional annotation for implementations of C++ function wrappers that
46 /// may be exposed to Rust. You may for example need to provide
47 /// `__declspec(dllexport)` or `__attribute__((visibility("default")))` if
48 /// Rust code from one shared object or executable depends on these C++
49 /// functions in another.
Adrian Taylor21f0ff02020-07-21 16:21:48 -070050 pub cxx_impl_annotations: Option<String>,
David Tolnay8238d4a2020-08-30 00:34:17 -070051
52 pub(super) gen_header: bool,
53 pub(super) gen_implementation: bool,
David Tolnay75c23852020-10-27 21:07:53 -070054 pub(super) allow_dot_includes: bool,
David Tolnay33d30292020-03-18 18:02:02 -070055}
56
David Tolnay19cb7852020-08-30 00:04:59 -070057/// Results of code generation.
David Tolnayf0277562020-09-24 10:46:14 -040058#[derive(Default)]
David Tolnay19cb7852020-08-30 00:04:59 -070059pub struct GeneratedCode {
60 /// The bytes of a C++ header file.
61 pub header: Vec<u8>,
62 /// The bytes of a C++ implementation file (e.g. .cc, cpp etc.)
David Tolnayd3659d82020-08-30 00:12:25 -070063 pub implementation: Vec<u8>,
David Tolnay19cb7852020-08-30 00:04:59 -070064}
65
David Tolnay318c3532020-08-30 00:50:05 -070066impl Default for Opt {
67 fn default() -> Self {
68 Opt {
69 include: Vec::new(),
70 cxx_impl_annotations: None,
David Tolnay8238d4a2020-08-30 00:34:17 -070071 gen_header: true,
72 gen_implementation: true,
David Tolnay75c23852020-10-27 21:07:53 -070073 allow_dot_includes: true,
David Tolnay318c3532020-08-30 00:50:05 -070074 }
75 }
76}
77
David Tolnay8238d4a2020-08-30 00:34:17 -070078pub(super) fn generate_from_path(path: &Path, opt: &Opt) -> GeneratedCode {
David Tolnaydbff3c42020-08-31 00:41:53 -070079 let source = match read_to_string(path) {
David Tolnay7db73692019-10-20 14:51:12 -040080 Ok(source) => source,
David Tolnaydbff3c42020-08-31 00:41:53 -070081 Err(err) => format_err(path, "", err),
David Tolnay7db73692019-10-20 14:51:12 -040082 };
David Tolnay8238d4a2020-08-30 00:34:17 -070083 match generate_from_string(&source, opt) {
Adrian Taylor8205e622020-07-21 21:53:59 -070084 Ok(out) => out,
David Tolnay7db73692019-10-20 14:51:12 -040085 Err(err) => format_err(path, &source, err),
Adrian Taylor593eddb2020-08-21 23:46:08 -070086 }
87}
88
David Tolnaydbff3c42020-08-31 00:41:53 -070089fn read_to_string(path: &Path) -> Result<String> {
David Tolnayc0e07dc2020-09-02 15:39:28 -070090 let bytes = if path == Path::new("-") {
91 fs::read_stdin()
92 } else {
93 fs::read(path)
94 }?;
David Tolnaydbff3c42020-08-31 00:41:53 -070095 match String::from_utf8(bytes) {
96 Ok(string) => Ok(string),
97 Err(err) => Err(Error::Utf8(path.to_owned(), err.utf8_error())),
98 }
99}
100
David Tolnay8238d4a2020-08-30 00:34:17 -0700101fn generate_from_string(source: &str, opt: &Opt) -> Result<GeneratedCode> {
David Tolnay5fc28552020-08-29 22:05:53 -0700102 let mut source = source;
David Tolnay17c32302020-08-29 12:21:16 -0700103 if source.starts_with("#!") && !source.starts_with("#![") {
104 let shebang_end = source.find('\n').unwrap_or(source.len());
105 source = &source[shebang_end..];
106 }
David Tolnay11926532020-11-01 00:06:10 -0700107 proc_macro2::fallback::force();
David Tolnay5fc28552020-08-29 22:05:53 -0700108 let syntax: File = syn::parse_str(source)?;
David Tolnay8238d4a2020-08-30 00:34:17 -0700109 generate(syntax, opt)
David Tolnay7db73692019-10-20 14:51:12 -0400110}
Adrian Taylor8205e622020-07-21 21:53:59 -0700111
David Tolnay8238d4a2020-08-30 00:34:17 -0700112pub(super) fn generate(syntax: File, opt: &Opt) -> Result<GeneratedCode> {
David Tolnay42e0d6f2020-11-01 00:08:46 -0700113 if syntax.modules.is_empty() {
114 return Err(Error::NoBridgeMod);
115 }
116
117 let ref mut apis = Vec::new();
Adrian Taylor8205e622020-07-21 21:53:59 -0700118 let ref mut errors = Errors::new();
David Tolnay42e0d6f2020-11-01 00:08:46 -0700119 for bridge in syntax.modules {
120 let ref namespace = bridge.namespace;
121 let trusted = bridge.unsafety.is_some();
David Tolnay159e7122020-11-01 12:35:44 -0800122 apis.extend(syntax::parse_items(
123 errors,
124 bridge.content,
125 trusted,
126 namespace,
127 ));
David Tolnay42e0d6f2020-11-01 00:08:46 -0700128 }
129
Adrian Taylor8205e622020-07-21 21:53:59 -0700130 let ref types = Types::collect(errors, apis);
David Tolnay75c23852020-10-27 21:07:53 -0700131 check::precheck(errors, apis, opt);
Adrian Taylor8205e622020-07-21 21:53:59 -0700132 errors.propagate()?;
Adrian Taylorc8713432020-10-21 18:20:55 -0700133 check::typecheck(errors, apis, types);
Adrian Taylor8205e622020-07-21 21:53:59 -0700134 errors.propagate()?;
David Tolnay42e0d6f2020-11-01 00:08:46 -0700135
David Tolnay9ca2ff22020-11-01 00:10:47 -0700136 // Some callers may wish to generate both header and implementation from the
137 // same token stream to avoid parsing twice. Others only need to generate
138 // one or the other.
David Tolnay92b7b6d2020-11-01 17:02:07 -0800139 let (mut header, mut implementation) = Default::default();
140 if opt.gen_header {
141 header = write::gen(apis, types, opt, true);
142 }
143 if opt.gen_implementation {
144 implementation = write::gen(apis, types, opt, false);
145 }
David Tolnay19cb7852020-08-30 00:04:59 -0700146 Ok(GeneratedCode {
David Tolnay92b7b6d2020-11-01 17:02:07 -0800147 header,
148 implementation,
David Tolnay19cb7852020-08-30 00:04:59 -0700149 })
Adrian Taylor8205e622020-07-21 21:53:59 -0700150}