blob: 1a3febd31b261ce07cafe1acd84c19257cb71490 [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 Tolnaybb3ba502020-08-30 19:59:41 -07004pub(super) mod error;
David Tolnayfcd8f462020-08-29 12:13:09 -07005mod file;
David Tolnay0d85ccd2020-08-30 20:18:13 -07006pub(super) mod fs;
David Tolnay7db73692019-10-20 14:51:12 -04007pub(super) mod include;
8pub(super) mod out;
9mod write;
10
David Tolnaye9c533e2020-08-29 23:03:51 -070011pub(super) use self::error::Error;
David Tolnay6f7f6862020-08-29 22:55:03 -070012use self::error::{format_err, Result};
David Tolnayfcd8f462020-08-29 12:13:09 -070013use self::file::File;
David Tolnay700cd0c2020-10-28 12:40:27 -070014use self::include::Include;
David Tolnay0dd85ff2020-05-03 23:43:33 -070015use crate::syntax::report::Errors;
David Tolnayb6cf3142020-04-19 20:56:09 -070016use crate::syntax::{self, check, Types};
David Tolnay7db73692019-10-20 14:51:12 -040017use std::path::Path;
David Tolnay7db73692019-10-20 14:51:12 -040018
Adrian Taylor0926f642020-08-25 13:08:06 -070019/// Options for C++ code generation.
David Tolnayec088152020-08-29 23:29:26 -070020///
21/// We expect options to be added over time, so this is a non-exhaustive struct.
22/// To instantiate one you need to crate a default value and mutate those fields
23/// that you want to modify.
24///
25/// ```
26/// # use cxx_gen::Opt;
27/// #
28/// let impl_annotations = r#"__attribute__((visibility("default")))"#.to_owned();
29///
30/// let mut opt = Opt::default();
31/// opt.cxx_impl_annotations = Some(impl_annotations);
32/// ```
David Tolnayec088152020-08-29 23:29:26 -070033#[non_exhaustive]
Adrian Taylor0926f642020-08-25 13:08:06 -070034pub struct Opt {
David Tolnaydf2f78d2020-08-29 23:31:53 -070035 /// Any additional headers to #include. The cxxbridge tool does not parse or
36 /// even require the given paths to exist; they simply go into the generated
37 /// C++ code as #include lines.
David Tolnay700cd0c2020-10-28 12:40:27 -070038 pub include: Vec<Include>,
David Tolnaydf2f78d2020-08-29 23:31:53 -070039 /// Optional annotation for implementations of C++ function wrappers that
40 /// may be exposed to Rust. You may for example need to provide
41 /// `__declspec(dllexport)` or `__attribute__((visibility("default")))` if
42 /// Rust code from one shared object or executable depends on these C++
43 /// functions in another.
Adrian Taylor21f0ff02020-07-21 16:21:48 -070044 pub cxx_impl_annotations: Option<String>,
David Tolnay8238d4a2020-08-30 00:34:17 -070045
46 pub(super) gen_header: bool,
47 pub(super) gen_implementation: bool,
David Tolnay33d30292020-03-18 18:02:02 -070048}
49
David Tolnay19cb7852020-08-30 00:04:59 -070050/// Results of code generation.
David Tolnayf0277562020-09-24 10:46:14 -040051#[derive(Default)]
David Tolnay19cb7852020-08-30 00:04:59 -070052pub struct GeneratedCode {
53 /// The bytes of a C++ header file.
54 pub header: Vec<u8>,
55 /// The bytes of a C++ implementation file (e.g. .cc, cpp etc.)
David Tolnayd3659d82020-08-30 00:12:25 -070056 pub implementation: Vec<u8>,
David Tolnay19cb7852020-08-30 00:04:59 -070057}
58
David Tolnay318c3532020-08-30 00:50:05 -070059impl Default for Opt {
60 fn default() -> Self {
61 Opt {
62 include: Vec::new(),
63 cxx_impl_annotations: None,
David Tolnay8238d4a2020-08-30 00:34:17 -070064 gen_header: true,
65 gen_implementation: true,
David Tolnay318c3532020-08-30 00:50:05 -070066 }
67 }
68}
69
David Tolnay8238d4a2020-08-30 00:34:17 -070070pub(super) fn generate_from_path(path: &Path, opt: &Opt) -> GeneratedCode {
David Tolnaydbff3c42020-08-31 00:41:53 -070071 let source = match read_to_string(path) {
David Tolnay7db73692019-10-20 14:51:12 -040072 Ok(source) => source,
David Tolnaydbff3c42020-08-31 00:41:53 -070073 Err(err) => format_err(path, "", err),
David Tolnay7db73692019-10-20 14:51:12 -040074 };
David Tolnay8238d4a2020-08-30 00:34:17 -070075 match generate_from_string(&source, opt) {
Adrian Taylor8205e622020-07-21 21:53:59 -070076 Ok(out) => out,
David Tolnay7db73692019-10-20 14:51:12 -040077 Err(err) => format_err(path, &source, err),
Adrian Taylor593eddb2020-08-21 23:46:08 -070078 }
79}
80
David Tolnaydbff3c42020-08-31 00:41:53 -070081fn read_to_string(path: &Path) -> Result<String> {
David Tolnayc0e07dc2020-09-02 15:39:28 -070082 let bytes = if path == Path::new("-") {
83 fs::read_stdin()
84 } else {
85 fs::read(path)
86 }?;
David Tolnaydbff3c42020-08-31 00:41:53 -070087 match String::from_utf8(bytes) {
88 Ok(string) => Ok(string),
89 Err(err) => Err(Error::Utf8(path.to_owned(), err.utf8_error())),
90 }
91}
92
David Tolnay8238d4a2020-08-30 00:34:17 -070093fn generate_from_string(source: &str, opt: &Opt) -> Result<GeneratedCode> {
David Tolnay5fc28552020-08-29 22:05:53 -070094 let mut source = source;
David Tolnay17c32302020-08-29 12:21:16 -070095 if source.starts_with("#!") && !source.starts_with("#![") {
96 let shebang_end = source.find('\n').unwrap_or(source.len());
97 source = &source[shebang_end..];
98 }
David Tolnay5fc28552020-08-29 22:05:53 -070099 let syntax: File = syn::parse_str(source)?;
David Tolnay8238d4a2020-08-30 00:34:17 -0700100 generate(syntax, opt)
David Tolnay7db73692019-10-20 14:51:12 -0400101}
Adrian Taylor8205e622020-07-21 21:53:59 -0700102
David Tolnay8238d4a2020-08-30 00:34:17 -0700103pub(super) fn generate(syntax: File, opt: &Opt) -> Result<GeneratedCode> {
Adrian Taylor8205e622020-07-21 21:53:59 -0700104 proc_macro2::fallback::force();
105 let ref mut errors = Errors::new();
David Tolnay3c64a4e2020-08-29 14:07:38 -0700106 let bridge = syntax
107 .modules
108 .into_iter()
109 .next()
110 .ok_or(Error::NoBridgeMod)?;
Adrian Taylor8205e622020-07-21 21:53:59 -0700111 let ref namespace = bridge.namespace;
David Tolnay805dca32020-08-29 19:09:55 -0700112 let trusted = bridge.unsafety.is_some();
113 let ref apis = syntax::parse_items(errors, bridge.content, trusted);
Adrian Taylor8205e622020-07-21 21:53:59 -0700114 let ref types = Types::collect(errors, apis);
115 errors.propagate()?;
116 check::typecheck(errors, namespace, apis, types);
117 errors.propagate()?;
Adrian Taylor9fc08462020-08-14 10:51:00 -0700118 // Some callers may wish to generate both header and C++
119 // from the same token stream to avoid parsing twice. But others
120 // only need to generate one or the other.
David Tolnay19cb7852020-08-30 00:04:59 -0700121 Ok(GeneratedCode {
David Tolnay8238d4a2020-08-30 00:34:17 -0700122 header: if opt.gen_header {
David Tolnay19cb7852020-08-30 00:04:59 -0700123 write::gen(namespace, apis, types, opt, true).content()
124 } else {
125 Vec::new()
126 },
David Tolnay8238d4a2020-08-30 00:34:17 -0700127 implementation: if opt.gen_implementation {
David Tolnay19cb7852020-08-30 00:04:59 -0700128 write::gen(namespace, apis, types, opt, false).content()
129 } else {
130 Vec::new()
131 },
132 })
Adrian Taylor8205e622020-07-21 21:53:59 -0700133}