1 // Copyright 2019 TiKV Project Authors. Licensed under Apache-2.0.
2
3 use std::env;
4 use std::env::VarError;
5 use std::fs;
6 use std::io::Read;
7 use std::path::{Path, PathBuf};
8 use walkdir::WalkDir;
9
10 // Platform-Specific Instructions: Regenerating gRPC Bindings for Windows
11
12 // If you need to manually update the 'android/grpc-bindings.rs' file on Windows,
13 // follow these steps:
14
15 // 1. LLVM Setup
16 // - Download and install the pre-built LLVM binaries from:
17 // https://releases.llvm.org/download.html
18 // - Update the `LIBCLANG_PATH` environment variable to point to the 'bin'
19 // directory within your LLVM installation. (e.g., `C:\Program Files\LLVM\bin`)
20
21 // 2. Environment Configuration
22 // - Set the `GRPCIO_SYS_GRPC_INCLUDE_PATH` environment variable to the
23 // location of your gRPC 'include' directory.
24
25 // 3. Building the Bindings
26 // - Run the following Cargo command to regenerate the bindings:
27 // `cargo build --features _gen-bindings`
28
29 include!("../link-deps.rs");
30
get_env(name: &str) -> Option<String>31 fn get_env(name: &str) -> Option<String> {
32 println!("cargo:rerun-if-env-changed={name}");
33 match env::var(name) {
34 Ok(s) => Some(s),
35 Err(VarError::NotPresent) => None,
36 Err(VarError::NotUnicode(s)) => {
37 panic!("unrecognize env var of {name}: {:?}", s.to_string_lossy());
38 }
39 }
40 }
41
42 // Generate the bindings to grpc C-core.
43 // Try to disable the generation of platform-related bindings.
44 #[cfg(any(
45 feature = "_gen-bindings",
46 not(all(
47 any(target_os = "linux", target_os = "macos"),
48 any(target_arch = "x86_64", target_arch = "aarch64")
49 ))
50 ))]
bindgen_grpc(file_path: &Path)51 fn bindgen_grpc(file_path: &Path) {
52 // create a config to generate binding file
53 let mut config = bindgen::Builder::default();
54 if cfg!(feature = "_secure") {
55 config = config.clang_arg("-DGRPC_SYS_SECURE");
56 }
57
58 if get_env("CARGO_CFG_TARGET_OS").map_or(false, |s| s == "windows") {
59 config = config.clang_arg("-D _WIN32_WINNT=0x600");
60 }
61
62 // Search header files with API interface
63 let mut headers = Vec::new();
64 for result in WalkDir::new(Path::new("./grpc/include")) {
65 let dent = result.expect("Error happened when search headers");
66 if !dent.file_type().is_file() {
67 continue;
68 }
69 let mut file = fs::File::open(dent.path()).expect("couldn't open headers");
70 let mut buf = String::new();
71 file.read_to_string(&mut buf)
72 .expect("Coundn't read header content");
73 if buf.contains("GRPCAPI") || buf.contains("GPRAPI") {
74 headers.push(String::from(dent.path().to_str().unwrap()));
75 }
76 }
77
78 // To control the order of bindings
79 headers.sort();
80 for path in headers {
81 config = config.header(path);
82 }
83
84 println!("cargo:rerun-if-env-changed=TEST_BIND");
85 let gen_tests = env::var("TEST_BIND").map_or(false, |s| s == "1");
86
87 let cfg = config
88 .header("grpc_wrap.cc")
89 .clang_arg("-xc++")
90 .clang_arg("-I./grpc/include")
91 .clang_arg("-std=c++11")
92 .rustfmt_bindings(true)
93 .impl_debug(true)
94 .size_t_is_usize(true)
95 .disable_header_comment()
96 .allowlist_function(r"\bgrpc_.*")
97 .allowlist_function(r"\bgpr_.*")
98 .allowlist_function(r"\bgrpcwrap_.*")
99 .allowlist_var(r"\bGRPC_.*")
100 .allowlist_type(r"\bgrpc_.*")
101 .allowlist_type(r"\bgpr_.*")
102 .allowlist_type(r"\bgrpcwrap_.*")
103 .allowlist_type(r"\bcensus_context.*")
104 .allowlist_type(r"\bverify_peer_options.*")
105 // Block all system headers.
106 .blocklist_file(r"^/.*")
107 .blocklist_function(r"\bgpr_mu_.*")
108 .blocklist_function(r"\bgpr_cv_.*")
109 .blocklist_function(r"\bgpr_once_.*")
110 .blocklist_type(r"gpr_mu")
111 .blocklist_type(r"gpr_cv")
112 .blocklist_type(r"gpr_once")
113 .constified_enum_module(r"grpc_status_code")
114 .layout_tests(gen_tests)
115 .default_enum_style(bindgen::EnumVariation::Rust {
116 non_exhaustive: false,
117 });
118 println!("running {}", cfg.command_line_flags().join(" "));
119 cfg.generate()
120 .expect("Unable to generate grpc bindings")
121 .write_to_file(file_path)
122 .expect("Couldn't write bindings!");
123 }
124
125 // Determine if need to update bindings. Supported platforms do not
126 // need to be updated by default unless the _gen-bindings feature is specified.
config_binding_path()127 fn config_binding_path() {
128 let target = env::var("TARGET").unwrap();
129 let file_path: PathBuf = match target.as_str() {
130 "x86_64-unknown-linux-gnu"
131 | "x86_64-unknown-linux-musl"
132 | "aarch64-unknown-linux-musl"
133 | "aarch64-unknown-linux-gnu"
134 | "x86_64-apple-darwin"
135 | "aarch64-apple-darwin" => {
136 // Cargo treats nonexistent files changed, so we only emit the rerun-if-changed
137 // directive when we expect the target-specific pre-generated binding file to be
138 // present.
139 println!("cargo:rerun-if-changed=bindings/bindings.rs");
140
141 PathBuf::from(env::var("CARGO_MANIFEST_DIR").unwrap())
142 .join("bindings")
143 .join("bindings.rs")
144 }
145 _ => PathBuf::from(env::var("CARGO_MANIFEST_DIR").unwrap())
146 .join("android")
147 .join("grpc-bindings.rs"),
148 };
149 #[cfg(feature = "_gen-bindings")]
150 {
151 // On some system (like Windows), stack size of main thread may
152 // be too small.
153 let f = file_path.clone();
154 std::thread::Builder::new()
155 .stack_size(8 * 1024 * 1024)
156 .name("bindgen_grpc".to_string())
157 .spawn(move || bindgen_grpc(&f))
158 .unwrap()
159 .join()
160 .unwrap();
161 }
162
163 println!(
164 "cargo:rustc-env=BINDING_PATH={}",
165 file_path.to_str().unwrap()
166 );
167 }
168
main()169 fn main() {
170 println!("cargo:rerun-if-changed=grpc_wrap.cc");
171 println!("cargo:rerun-if-changed=grpc");
172
173 // create a builder to compile grpc_wrap.cc
174 let mut cc = cc::Build::new();
175
176 if get_env("CARGO_CFG_TARGET_OS").map_or(false, |s| s == "windows") {
177 // At lease vista
178 cc.define("_WIN32_WINNT", Some("0x600"));
179 }
180
181 let mut include_paths: Vec<PathBuf> = Vec::new();
182 let include_path = get_env("GRPCIO_SYS_GRPC_INCLUDE_PATH");
183 if include_path.is_none() {
184 panic!("$GRPCIO_SYS_GRPC_INCLUDE_PATH is not set");
185 }
186 include_paths.push(include_path.unwrap().into());
187 for inc_path in include_paths {
188 cc.include(inc_path);
189 }
190
191 cc.cpp(true);
192 if !cfg!(target_env = "msvc") {
193 cc.flag("-std=c++11");
194 }
195 cc.file("grpc_wrap.cc");
196 cc.warnings_into_errors(true);
197 cc.compile("libgrpc_wrap.a");
198
199 config_binding_path();
200 }
201