-
Notifications
You must be signed in to change notification settings - Fork 2
/
build.rs
148 lines (131 loc) · 4.54 KB
/
build.rs
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
extern crate bindgen;
extern crate clang_sys;
extern crate libc;
use std::env;
use std::ffi::{CStr, CString};
use std::fs::{remove_file, File};
use std::io::Write;
use std::path::PathBuf;
use clang_sys::*;
use libc::c_void;
fn main() {
println!("cargo:rerun-if-changed=wrapper.h");
// Generate rust definitions for the jmp_buf and sigjmp_buf
// types. Do NOT generate bindings for the function declarations;
// that's done below.
let bindings = bindgen::Builder::default()
.header("wrapper.h")
// we only want these two type definitions
.whitelist_type("jmp_buf")
.whitelist_type("sigjmp_buf")
.parse_callbacks(Box::new(bindgen::CargoCallbacks))
.generate()
.expect("Unable to generate bindings");
// Write the bindings to the $OUT_DIR/jmpbuf.rs file.
let out_path = PathBuf::from(env::var("OUT_DIR").unwrap());
bindings
.write_to_file(out_path.join("jmpbuf.rs"))
.expect("Couldn't write bindings!");
// We link against the platform's libc, but to do so we need the
// proper symbol names for setjmp, sigsetjmp, etc. Sometimes,
// these names are actually a macro that's rewritten to the real
// symbol name, e.g. __sigsetjmp.
//
// To find the real symbol names, we create temporary C file that
// refers to the names, then use libclang to parse it to get the
// actual libc symbols after macro expansion.
let c_file_path = out_path.join("find_symbols.c");
let mut c_file = File::create(&c_file_path).unwrap();
c_file.write_all(&c_contents()).unwrap();
let symbols = find_symbols(&c_file_path);
remove_file(&c_file_path).unwrap();
// write out a rust file containing declarations including link
// names that point to the correct libc symbols found above
let mut file = File::create(out_path.join("decls.rs")).unwrap();
file.write_all(&decls_contents(symbols)).unwrap();
}
// Parse the given C file with libclang, extracting the symbols from
// the four call sites.
fn find_symbols(filename: &PathBuf) -> Vec<String> {
let mut vec: Vec<String> = Vec::new();
unsafe {
let filename_cstr = CString::new(filename.to_str().unwrap()).unwrap();
let index = clang_createIndex(0, 0);
let tu = clang_parseTranslationUnit(
index,
filename_cstr.as_ptr(),
std::ptr::null_mut(),
0,
std::ptr::null_mut(),
0,
CXTranslationUnit_None,
);
let cursor = clang_getTranslationUnitCursor(tu);
clang_visitChildren(cursor, visitor, &mut vec as *mut Vec<String> as *mut c_void);
}
assert!(vec.len() == 4);
vec
}
// Walk the AST, extracting the CallExprs and pushing them into the
// given vector.
extern "C" fn visitor(
cursor: CXCursor,
_parent: CXCursor,
client_data: CXClientData,
) -> CXChildVisitResult {
unsafe {
let symbols: &mut Vec<String> = &mut *(client_data as *mut Vec<String>);
let cursor_name = clang_getCursorSpelling(cursor);
let cursor_cstr = CStr::from_ptr(clang_getCString(cursor_name));
let cursor_str = cursor_cstr.to_str().unwrap();
let cursorkind: CXCursorKind = clang_getCursorKind(cursor);
if cursorkind == CXCursor_CallExpr {
symbols.push(cursor_str.to_string());
}
}
CXChildVisit_Recurse
}
// The contents of the temporary C file that we need to create. This
// will be parsed with libclang to find the real symbol names for
// setjmp (etc.) after macro expansion.
//
// The order of calls in this function must match the order of
// declarations in decls_contents().
fn c_contents() -> Vec<u8> {
return r###"
#include <setjmp.h>
int find_symbols()
{
jmp_buf jmpbuf;
sigjmp_buf sigjmpbuf;
setjmp(jmpbuf);
sigsetjmp(sigjmpbuf, 0);
longjmp(jmpbuf, 1);
siglongjmp(sigjmpbuf, 1);
}
"###
.as_bytes()
.to_vec();
}
// The contents of the function declarations for setjmp, etc. The
// order of declarations must match the order of calls in
// c_contents().
fn decls_contents(symbols: Vec<String>) -> Vec<u8> {
return format!(
r###"
extern "C" {{
#[link_name="{}"]
pub fn setjmp(env: *mut jmp_buf) -> c_int;
#[link_name="{}"]
pub fn sigsetjmp(env: *mut sigjmp_buf, savesigs: c_int) -> c_int;
#[link_name="{}"]
pub fn longjmp(env: *mut jmp_buf, val: c_int) -> !;
#[link_name="{}"]
pub fn siglongjmp(env: *mut sigjmp_buf, val: c_int) -> !;
}}
"###,
symbols[0], symbols[1], symbols[2], symbols[3]
)
.as_bytes()
.to_vec();
}