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
use std::{env, path::PathBuf};
fn main() {
let dst = cmake::Config::new("open62541")
// Use explicit paths here to avoid generating files where we do not expect them below.
.define("CMAKE_INSTALL_INCLUDEDIR", "include")
// Some systems (Fedora) default to `lib64/` instead of `lib/` for 64-bit libraries.
.define("CMAKE_INSTALL_LIBDIR", "lib")
// Explicitly set C99 standard to force Windows variants of `vsnprintf()` to conform to this
// standard. This also matches the expected (or supported) C standard of `open62541` itself.
.define("C_STANDARD", "99")
// Python defaults to creating bytecode in `__pycache__` directories. During build, this may
// happen when the tool `nodeset_compiler` is called. When we package a crate, builds should
// never modify files outside of `OUT_DIR`, so we disable the cache to prevent this.
.env("PYTHONDONTWRITEBYTECODE", "1")
.build();
println!("cargo:rustc-link-search={}", dst.join("lib").display());
println!("cargo:rustc-link-lib=open62541");
let input = env::current_dir().unwrap().join("wrapper.h");
println!("cargo:rerun-if-changed={}", input.display());
let bindings = bindgen::Builder::default()
// Include our wrapper functions.
.allowlist_function("(__)?RS_.*")
.allowlist_function("(__)?UA_.*")
// Include our wrapper types.
.allowlist_type("(__)?RS_.*")
.allowlist_type("(__)?UA_.*")
// Include our wrapper vars.
.allowlist_var("(__)?RS_.*")
.allowlist_var("(__)?UA_.*")
// Explicitly set C99 standard to force Windows variants of `vsnprintf()` to conform to this
// standard. This also matches the expected (or supported) C standard of `open62541` itself.
.clang_arg("-std=c99")
.clang_arg(format!("-I{}", dst.join("include").display()))
.default_enum_style(bindgen::EnumVariation::NewType {
is_bitfield: false,
is_global: false,
})
// Use explicit Rust target version that matches the entry in `Cargo.toml`.
.rust_target(bindgen::RustTarget::Stable_1_71)
// Do not derive `Copy` because most of the data types are not copy-safe (they own memory by
// pointers and need to be cloned manually to duplicate that memory).
.derive_copy(false)
// We want to initialize some types statically. This is used in `open62541`, we require that
// as well to mirror some of the functionality.
.derive_default(true)
// The auto-derived comments are not particularly useful because they often do not match the
// declaration they belong to.
.generate_comments(false)
.header(input.to_string_lossy())
// Activate parse callbacks. This causes cargo to invalidate the generated bindings when any
// of the included files change. It also enables us to rename items in the final bindings.
.parse_callbacks(Box::new(CustomCallbacks))
// We may use `core` instead of `std`. This might be useful for `no_std` environments.
.use_core()
// Wrap static functions. These are used in several places for inline helpers and we want to
// preserve those in the generated bindings. This outputs `extern.c` which we compile below.
.wrap_static_fns(true)
.generate()
.expect("should generate `Bindings` instance");
let out_path = PathBuf::from(env::var("OUT_DIR").unwrap());
bindings
.write_to_file(out_path.join("bindings.rs"))
.expect("should write `bindings.rs`");
let ext_name = "open62541-extern";
let statc_path = env::current_dir().unwrap().join("wrapper.c");
let extc_path = env::temp_dir().join("bindgen").join("extern.c");
cc::Build::new()
.file(extc_path)
.file(statc_path)
.include(dst.join("include"))
.include(input.parent().unwrap())
// Disable warnings for `open62541`. Not much we can do anyway.
.warnings(false)
// Explicitly disable deprecation warnings (seem to be enabled even when other warnings have
// been disabled above).
.flag_if_supported("-Wno-deprecated-declarations")
.flag_if_supported("-Wno-deprecated")
.compile(ext_name);
}
#[derive(Debug)]
struct CustomCallbacks;
// Include `cargo:rerun-if` instructions just like `bindgen::CargoCallbacks` does. In addition, make
// necessary adjustments to the names of items for the final bindings.
impl bindgen::callbacks::ParseCallbacks for CustomCallbacks {
fn header_file(&self, filename: &str) {
// Make sure to rerun build when header file changes.
println!("cargo:rerun-if-changed={}", filename);
}
fn include_file(&self, filename: &str) {
// Make sure to rerun build when include file changes.
println!("cargo:rerun-if-changed={}", filename);
}
fn read_env_var(&self, key: &str) {
// Make sure to rerun build when environment variable changes.
println!("cargo:rerun-if-env-changed={}", key);
}
fn item_name(&self, original_item_name: &str) -> Option<String> {
// Rename pointer constant back to its original name. See `wrapper.c` for details.
if original_item_name == "RS_EMPTY_ARRAY_SENTINEL" {
return Some("UA_EMPTY_ARRAY_SENTINEL".to_owned());
}
// Rename wrapper function back to its original name. See `wrapper.c` for details.
if original_item_name == "RS_vsnprintf" {
return Some("vsnprintf".to_owned());
}
if original_item_name == "RS_va_end" {
return Some("va_end".to_owned());
}
None
}
}