Skip to content

Commit b1dced5

Browse files
authored
Merge pull request #231 from adetaylor/set-visibility
Allow DSO export for C Rust bindings.
2 parents 4caede6 + 8205e62 commit b1dced5

File tree

3 files changed

+85
-22
lines changed

3 files changed

+85
-22
lines changed

gen/cmd/src/main.rs

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,15 @@ struct Opt {
3535
#[structopt(long)]
3636
header: bool,
3737

38+
/// Optional annotation for implementations of C++ function
39+
/// wrappers that may be exposed to Rust. You may for example
40+
/// need to provide __declspec(dllexport) or
41+
/// __attribute__((visibility("default"))) if Rust code from
42+
/// one shared object or executable depends on these C++ functions
43+
/// in another.
44+
#[structopt(long)]
45+
cxx_impl_annotations: Option<String>,
46+
3847
/// Any additional headers to #include
3948
#[structopt(short, long)]
4049
include: Vec<String>,
@@ -49,6 +58,7 @@ fn main() {
4958

5059
let gen = gen::Opt {
5160
include: opt.include,
61+
cxx_impl_annotations: opt.cxx_impl_annotations,
5262
};
5363

5464
match (opt.input, opt.header) {

gen/src/mod.rs

Lines changed: 61 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -24,38 +24,81 @@ struct Input {
2424
pub(super) struct Opt {
2525
/// Any additional headers to #include
2626
pub include: Vec<String>,
27+
/// Whether to set __attribute__((visibility("default")))
28+
/// or similar annotations on function implementations.
29+
pub cxx_impl_annotations: Option<String>,
2730
}
2831

2932
pub(super) fn do_generate_bridge(path: &Path, opt: Opt) -> Vec<u8> {
3033
let header = false;
31-
generate(path, opt, header)
34+
generate_from_path(path, opt, header)
3235
}
3336

3437
pub(super) fn do_generate_header(path: &Path, opt: Opt) -> Vec<u8> {
3538
let header = true;
36-
generate(path, opt, header)
39+
generate_from_path(path, opt, header)
3740
}
3841

39-
fn generate(path: &Path, opt: Opt, header: bool) -> Vec<u8> {
42+
fn generate_from_path(path: &Path, opt: Opt, header: bool) -> Vec<u8> {
4043
let source = match fs::read_to_string(path) {
4144
Ok(source) => source,
4245
Err(err) => format_err(path, "", Error::Io(err)),
4346
};
44-
match (|| -> Result<_> {
45-
proc_macro2::fallback::force();
46-
let ref mut errors = Errors::new();
47-
let syntax = syn::parse_file(&source)?;
48-
let bridge = find::find_bridge_mod(syntax)?;
49-
let ref namespace = bridge.namespace;
50-
let ref apis = syntax::parse_items(errors, bridge.module);
51-
let ref types = Types::collect(errors, apis);
52-
errors.propagate()?;
53-
check::typecheck(errors, namespace, apis, types);
54-
errors.propagate()?;
55-
let out = write::gen(namespace, apis, types, opt, header);
56-
Ok(out)
57-
})() {
58-
Ok(out) => out.content(),
47+
match generate(&source, opt, header) {
48+
Ok(out) => out,
5949
Err(err) => format_err(path, &source, err),
6050
}
6151
}
52+
53+
fn generate(source: &str, opt: Opt, header: bool) -> Result<Vec<u8>> {
54+
proc_macro2::fallback::force();
55+
let ref mut errors = Errors::new();
56+
let syntax = syn::parse_file(&source)?;
57+
let bridge = find::find_bridge_mod(syntax)?;
58+
let ref namespace = bridge.namespace;
59+
let ref apis = syntax::parse_items(errors, bridge.module);
60+
let ref types = Types::collect(errors, apis);
61+
errors.propagate()?;
62+
check::typecheck(errors, namespace, apis, types);
63+
errors.propagate()?;
64+
let out = write::gen(namespace, apis, types, opt, header);
65+
Ok(out.content())
66+
}
67+
68+
#[cfg(test)]
69+
mod tests {
70+
use crate::gen::{generate, Opt};
71+
72+
const CPP_EXAMPLE: &'static str = r#"
73+
#[cxx::bridge]
74+
mod ffi {
75+
extern "C" {
76+
pub fn do_cpp_thing(foo: &str);
77+
}
78+
}
79+
"#;
80+
81+
#[test]
82+
fn test_cpp() {
83+
let opts = Opt {
84+
include: Vec::new(),
85+
cxx_impl_annotations: None,
86+
};
87+
let output = generate(CPP_EXAMPLE, opts, false).unwrap();
88+
let output = std::str::from_utf8(&output).unwrap();
89+
// To avoid continual breakage we won't test every byte.
90+
// Let's look for the major features.
91+
assert!(output.contains("void cxxbridge03$do_cpp_thing(::rust::Str::Repr foo)"));
92+
}
93+
94+
#[test]
95+
fn test_annotation() {
96+
let opts = Opt {
97+
include: Vec::new(),
98+
cxx_impl_annotations: Some("ANNOTATION".to_string()),
99+
};
100+
let output = generate(CPP_EXAMPLE, opts, false).unwrap();
101+
let output = std::str::from_utf8(&output).unwrap();
102+
assert!(output.contains("ANNOTATION void cxxbridge03$do_cpp_thing(::rust::Str::Repr foo)"));
103+
}
104+
}

gen/src/write.rs

Lines changed: 14 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -86,13 +86,13 @@ pub(super) fn gen(
8686
out.begin_block("extern \"C\"");
8787
write_exception_glue(out, apis);
8888
for api in apis {
89-
let (efn, write): (_, fn(_, _, _)) = match api {
89+
let (efn, write): (_, fn(_, _, _, _)) = match api {
9090
Api::CxxFunction(efn) => (efn, write_cxx_function_shim),
9191
Api::RustFunction(efn) => (efn, write_rust_function_decl),
9292
_ => continue,
9393
};
9494
out.next_section();
95-
write(out, efn, types);
95+
write(out, efn, types, &opt.cxx_impl_annotations);
9696
}
9797
out.end_block("extern \"C\"");
9898
}
@@ -399,7 +399,17 @@ fn write_exception_glue(out: &mut OutFile, apis: &[Api]) {
399399
}
400400
}
401401

402-
fn write_cxx_function_shim(out: &mut OutFile, efn: &ExternFn, types: &Types) {
402+
fn write_cxx_function_shim(
403+
out: &mut OutFile,
404+
efn: &ExternFn,
405+
types: &Types,
406+
impl_annotations: &Option<String>,
407+
) {
408+
if !out.header {
409+
if let Some(annotation) = impl_annotations {
410+
write!(out, "{} ", annotation);
411+
}
412+
}
403413
if efn.throws {
404414
write!(out, "::rust::Str::Repr ");
405415
} else {
@@ -560,7 +570,7 @@ fn write_function_pointer_trampoline(
560570
write_rust_function_shim_impl(out, &c_trampoline, f, types, &r_trampoline, indirect_call);
561571
}
562572

563-
fn write_rust_function_decl(out: &mut OutFile, efn: &ExternFn, types: &Types) {
573+
fn write_rust_function_decl(out: &mut OutFile, efn: &ExternFn, types: &Types, _: &Option<String>) {
564574
let link_name = mangle::extern_fn(&out.namespace, efn);
565575
let indirect_call = false;
566576
write_rust_function_decl_impl(out, &link_name, efn, types, indirect_call);

0 commit comments

Comments
 (0)