blob: 7f045083ff33854f07b0a221c5e2621772693803 [file] [log] [blame]
Jeff Vander Stoep3adfea82020-10-14 15:35:59 +02001// Copyright 2019 TiKV Project Authors. Licensed under Apache-2.0.
2
3use std::env::VarError;
4use std::io::prelude::*;
5use std::io::BufReader;
6use std::path::{Path, PathBuf};
7use std::{env, fs, io};
8
9use cmake::Config as CmakeConfig;
10use pkg_config::{Config as PkgConfig, Library};
11use walkdir::WalkDir;
12
Joel Galenson2c224052021-06-21 17:11:55 -070013const GRPC_VERSION: &str = "1.38.0";
14
15/// Following two arrays are generated by running pkg-config manually. We can
16/// also choose to run pkg-config at build time, but it will requires pkg-config
17/// in path, which is unfriendly for platforms like Windows.
18// grpc_unsecure.pc is not accurate, see also grpc/grpc#24512. Should also include "address_sorting", "upb", "cares", "z".
19#[rustfmt::skip]
20const COMMON_DEPS: &[&str] = &[
21 "absl_bad_optional_access", "absl_bad_variant_access", "absl_base", "absl_city", "absl_civil_time",
22 "absl_cord", "absl_debugging_internal", "absl_demangle_internal", "absl_exponential_biased",
23 "absl_graphcycles_internal", "absl_hash", "absl_hashtablez_sampler", "absl_int128", "absl_log_severity",
24 "absl_malloc_internal", "absl_raw_hash_set", "absl_raw_logging_internal", "absl_spinlock_wait",
25 "absl_stacktrace", "absl_status", "absl_statusor", "absl_str_format_internal", "absl_strings",
26 "absl_strings_internal", "absl_symbolize", "absl_synchronization", "absl_throw_delegate", "absl_time",
27 "absl_time_zone", "absl_wyhash", "address_sorting", "cares", "gpr", "upb", "z",
28];
29const GRPC_DEPS: &[&str] = &["grpc", "re2"];
30const GRPC_UNSECURE_DEPS: &[&str] = &["grpc_unsecure"];
Jeff Vander Stoep3adfea82020-10-14 15:35:59 +020031
32fn probe_library(library: &str, cargo_metadata: bool) -> Library {
33 match PkgConfig::new()
34 .atleast_version(GRPC_VERSION)
35 .cargo_metadata(cargo_metadata)
36 .probe(library)
37 {
38 Ok(lib) => lib,
39 Err(e) => panic!("can't find library {} via pkg-config: {:?}", library, e),
40 }
41}
42
43fn prepare_grpc() {
Jeff Vander Stoep08902cf2020-11-19 19:03:52 +010044 let modules = vec![
Jeff Vander Stoep3adfea82020-10-14 15:35:59 +020045 "grpc",
46 "grpc/third_party/cares/cares",
47 "grpc/third_party/address_sorting",
48 "grpc/third_party/abseil-cpp",
Jeff Vander Stoep08902cf2020-11-19 19:03:52 +010049 "grpc/third_party/re2",
Jeff Vander Stoep3adfea82020-10-14 15:35:59 +020050 ];
51
Jeff Vander Stoep3adfea82020-10-14 15:35:59 +020052 for module in modules {
53 if is_directory_empty(module).unwrap_or(true) {
54 panic!(
55 "Can't find module {}. You need to run `git submodule \
56 update --init --recursive` first to build the project.",
57 module
58 );
59 }
60 }
61}
62
63fn is_directory_empty<P: AsRef<Path>>(p: P) -> Result<bool, io::Error> {
64 let mut entries = fs::read_dir(p)?;
65 Ok(entries.next().is_none())
66}
67
68fn trim_start<'a>(s: &'a str, prefix: &str) -> Option<&'a str> {
69 if s.starts_with(prefix) {
70 Some(s.trim_start_matches(prefix))
71 } else {
72 None
73 }
74}
75
Joel Galenson23c9e5e2021-04-02 14:59:08 -070076/// If cache is stale, remove it to avoid compilation failure.
77fn clean_up_stale_cache(cxx_compiler: String) {
78 // We don't know the cmake output path before it's configured.
79 let build_dir = format!("{}/build", env::var("OUT_DIR").unwrap());
80 let path = format!("{}/CMakeCache.txt", build_dir);
81 let f = match std::fs::File::open(&path) {
82 Ok(f) => BufReader::new(f),
83 // It may be an empty directory.
84 Err(_) => return,
85 };
86 let cache_stale = f.lines().any(|l| {
87 let l = l.unwrap();
88 trim_start(&l, "CMAKE_CXX_COMPILER:").map_or(false, |s| {
89 let mut splits = s.splitn(2, '=');
90 splits.next();
91 splits.next().map_or(false, |p| p != cxx_compiler)
92 })
93 });
94 // CMake can't handle compiler change well, it will invalidate cache without respecting command
95 // line settings and result in configuration failure.
96 // See https://gitlab.kitware.com/cmake/cmake/-/issues/18959.
97 if cache_stale {
98 let _ = fs::remove_dir_all(&build_dir);
99 }
100}
101
Jeff Vander Stoep3adfea82020-10-14 15:35:59 +0200102fn build_grpc(cc: &mut cc::Build, library: &str) {
103 prepare_grpc();
104
Jeff Vander Stoep08902cf2020-11-19 19:03:52 +0100105 let target = env::var("TARGET").unwrap();
Jeff Vander Stoep3adfea82020-10-14 15:35:59 +0200106 let dst = {
107 let mut config = CmakeConfig::new("grpc");
108
109 if get_env("CARGO_CFG_TARGET_OS").map_or(false, |s| s == "macos") {
110 config.cxxflag("-stdlib=libc++");
111 }
112
113 // Ensure CoreFoundation be found in macos or ios
114 if get_env("CARGO_CFG_TARGET_OS").map_or(false, |s| s == "macos")
115 || get_env("CARGO_CFG_TARGET_OS").map_or(false, |s| s == "ios")
116 {
117 println!("cargo:rustc-link-lib=framework=CoreFoundation");
118 }
119
Joel Galenson23c9e5e2021-04-02 14:59:08 -0700120 let cxx_compiler = if let Some(val) = get_env("CXX") {
121 config.define("CMAKE_CXX_COMPILER", val.clone());
122 val
Haibo Huangae543892020-12-18 02:22:45 -0800123 } else if env::var("CARGO_CFG_TARGET_ENV").unwrap() == "musl" {
Jeff Vander Stoep3adfea82020-10-14 15:35:59 +0200124 config.define("CMAKE_CXX_COMPILER", "g++");
Joel Galenson23c9e5e2021-04-02 14:59:08 -0700125 "g++".to_owned()
126 } else {
127 format!("{}", cc.get_compiler().path().display())
128 };
129 clean_up_stale_cache(cxx_compiler);
Jeff Vander Stoep3adfea82020-10-14 15:35:59 +0200130
131 // Cross-compile support for iOS
Jeff Vander Stoep08902cf2020-11-19 19:03:52 +0100132 match target.as_str() {
Jeff Vander Stoep3adfea82020-10-14 15:35:59 +0200133 "aarch64-apple-ios" => {
134 config
135 .define("CMAKE_OSX_SYSROOT", "iphoneos")
136 .define("CMAKE_OSX_ARCHITECTURES", "arm64");
137 }
138 "armv7-apple-ios" => {
139 config
140 .define("CMAKE_OSX_SYSROOT", "iphoneos")
141 .define("CMAKE_OSX_ARCHITECTURES", "armv7");
142 }
143 "armv7s-apple-ios" => {
144 config
145 .define("CMAKE_OSX_SYSROOT", "iphoneos")
146 .define("CMAKE_OSX_ARCHITECTURES", "armv7s");
147 }
148 "i386-apple-ios" => {
149 config
150 .define("CMAKE_OSX_SYSROOT", "iphonesimulator")
151 .define("CMAKE_OSX_ARCHITECTURES", "i386");
152 }
153 "x86_64-apple-ios" => {
154 config
155 .define("CMAKE_OSX_SYSROOT", "iphonesimulator")
156 .define("CMAKE_OSX_ARCHITECTURES", "x86_64");
157 }
158 _ => {}
159 };
160
161 // Allow overriding of the target passed to cmake
162 // (needed for Android crosscompile)
163 if let Ok(val) = env::var("CMAKE_TARGET_OVERRIDE") {
164 config.target(&val);
165 }
166
167 // We don't need to generate install targets.
168 config.define("gRPC_INSTALL", "false");
169 // We don't need to build csharp target.
170 config.define("gRPC_BUILD_CSHARP_EXT", "false");
171 // We don't need to build codegen target.
172 config.define("gRPC_BUILD_CODEGEN", "false");
173 // We don't need to build benchmarks.
174 config.define("gRPC_BENCHMARK_PROVIDER", "none");
Joel Galensondacbb992021-09-23 10:55:24 -0700175
176 // `package` should only be set for secure feature, otherwise cmake will always search for
177 // ssl library.
178 if cfg!(feature = "secure") {
179 config.define("gRPC_SSL_PROVIDER", "package");
180 }
181 #[cfg(feature = "secure")]
Jeff Vander Stoep3adfea82020-10-14 15:35:59 +0200182 if cfg!(feature = "openssl") {
Jeff Vander Stoep3adfea82020-10-14 15:35:59 +0200183 if cfg!(feature = "openssl-vendored") {
184 config.register_dep("openssl");
185 }
Jeff Vander Stoep08902cf2020-11-19 19:03:52 +0100186 } else {
187 build_boringssl(&mut config);
Jeff Vander Stoep3adfea82020-10-14 15:35:59 +0200188 }
189 if cfg!(feature = "no-omit-frame-pointer") {
190 config
191 .cflag("-fno-omit-frame-pointer")
192 .cxxflag("-fno-omit-frame-pointer");
193 }
194 // Uses zlib from libz-sys.
195 setup_libz(&mut config);
196 config.build_target(library).uses_cxx11().build()
197 };
198
Jeff Vander Stoep08902cf2020-11-19 19:03:52 +0100199 let lib_suffix = if target.contains("msvc") {
200 ".lib"
Jeff Vander Stoep3adfea82020-10-14 15:35:59 +0200201 } else {
Jeff Vander Stoep08902cf2020-11-19 19:03:52 +0100202 ".a"
203 };
204 let build_dir = format!("{}/build", dst.display());
205 for e in WalkDir::new(&build_dir) {
206 let e = e.unwrap();
207 if e.file_name().to_string_lossy().ends_with(lib_suffix) {
Jeff Vander Stoep3adfea82020-10-14 15:35:59 +0200208 println!(
Jeff Vander Stoep08902cf2020-11-19 19:03:52 +0100209 "cargo:rustc-link-search=native={}",
210 e.path().parent().unwrap().display()
Jeff Vander Stoep3adfea82020-10-14 15:35:59 +0200211 );
212 }
213 }
214
Joel Galenson2c224052021-06-21 17:11:55 -0700215 let libs = if library.contains("unsecure") {
216 GRPC_UNSECURE_DEPS
217 } else {
218 GRPC_DEPS
Jeff Vander Stoep08902cf2020-11-19 19:03:52 +0100219 };
Joel Galenson2c224052021-06-21 17:11:55 -0700220 for l in COMMON_DEPS.iter().chain(libs) {
Jeff Vander Stoep08902cf2020-11-19 19:03:52 +0100221 println!("cargo:rustc-link-lib=static={}", l);
222 }
Jeff Vander Stoep3adfea82020-10-14 15:35:59 +0200223
224 if cfg!(feature = "secure") {
225 if cfg!(feature = "openssl") && !cfg!(feature = "openssl-vendored") {
226 figure_ssl_path(&build_dir);
227 } else {
228 println!("cargo:rustc-link-lib=static=ssl");
229 println!("cargo:rustc-link-lib=static=crypto");
230 }
231 }
232
233 cc.include("grpc/include");
234}
235
236fn figure_ssl_path(build_dir: &str) {
237 let path = format!("{}/CMakeCache.txt", build_dir);
238 let f = BufReader::new(std::fs::File::open(&path).unwrap());
239 let mut cnt = 0;
240 for l in f.lines() {
241 let l = l.unwrap();
242 let t = trim_start(&l, "OPENSSL_CRYPTO_LIBRARY:FILEPATH=")
243 .or_else(|| trim_start(&l, "OPENSSL_SSL_LIBRARY:FILEPATH="));
244 if let Some(s) = t {
245 let path = Path::new(s);
246 println!(
247 "cargo:rustc-link-search=native={}",
248 path.parent().unwrap().display()
249 );
250 cnt += 1;
251 }
252 }
253 if cnt != 2 {
254 panic!(
255 "CMake cache invalid, file {} contains {} ssl keys!",
256 path, cnt
257 );
258 }
259 println!("cargo:rustc-link-lib=ssl");
260 println!("cargo:rustc-link-lib=crypto");
261}
262
Joel Galensondacbb992021-09-23 10:55:24 -0700263#[cfg(feature = "secure")]
Jeff Vander Stoep08902cf2020-11-19 19:03:52 +0100264fn build_boringssl(config: &mut CmakeConfig) {
265 let boringssl_artifact = boringssl_src::Build::new().build();
266 config.define(
267 "OPENSSL_ROOT_DIR",
268 format!("{}", boringssl_artifact.root_dir().display()),
269 );
270 // To avoid linking system library, set lib path explicitly.
271 println!(
272 "cargo:rustc-link-search=native={}",
273 boringssl_artifact.lib_dir().display()
274 );
275}
276
Jeff Vander Stoep3adfea82020-10-14 15:35:59 +0200277fn setup_libz(config: &mut CmakeConfig) {
278 config.define("gRPC_ZLIB_PROVIDER", "package");
279 config.register_dep("Z");
280 // cmake script expect libz.a being under ${DEP_Z_ROOT}/lib, but libz-sys crate put it
281 // under ${DEP_Z_ROOT}/build. Append the path to CMAKE_PREFIX_PATH to get around it.
282 let zlib_root = env::var("DEP_Z_ROOT").unwrap();
283 let prefix_path = if let Ok(prefix_path) = env::var("CMAKE_PREFIX_PATH") {
284 format!("{};{}/build", prefix_path, zlib_root)
285 } else {
286 format!("{}/build", zlib_root)
287 };
288 // To avoid linking system library, set lib path explicitly.
289 println!("cargo:rustc-link-search=native={}/build", zlib_root);
290 println!("cargo:rustc-link-search=native={}/lib", zlib_root);
291 env::set_var("CMAKE_PREFIX_PATH", prefix_path);
292}
293
294fn get_env(name: &str) -> Option<String> {
295 println!("cargo:rerun-if-env-changed={}", name);
296 match env::var(name) {
297 Ok(s) => Some(s),
298 Err(VarError::NotPresent) => None,
299 Err(VarError::NotUnicode(s)) => {
300 panic!("unrecognize env var of {}: {:?}", name, s.to_string_lossy());
301 }
302 }
303}
304
305// Generate the bindings to grpc C-core.
306// Try to disable the generation of platform-related bindings.
Joel Galenson23c9e5e2021-04-02 14:59:08 -0700307#[cfg(feature = "use-bindgen")]
Joel Galenson2c224052021-06-21 17:11:55 -0700308fn bindgen_grpc(file_path: &Path) {
Joel Galenson23c9e5e2021-04-02 14:59:08 -0700309 // create a config to generate binding file
310 let mut config = bindgen::Builder::default();
311 if cfg!(feature = "secure") {
312 config = config.clang_arg("-DGRPC_SYS_SECURE");
313 }
314
315 if get_env("CARGO_CFG_TARGET_OS").map_or(false, |s| s == "windows") {
316 config = config.clang_arg("-D _WIN32_WINNT=0x600");
317 }
318
Jeff Vander Stoep3adfea82020-10-14 15:35:59 +0200319 // Search header files with API interface
320 let mut headers = Vec::new();
321 for result in WalkDir::new(Path::new("./grpc/include")) {
322 let dent = result.expect("Error happened when search headers");
323 if !dent.file_type().is_file() {
324 continue;
325 }
326 let mut file = fs::File::open(dent.path()).expect("couldn't open headers");
327 let mut buf = String::new();
328 file.read_to_string(&mut buf)
329 .expect("Coundn't read header content");
330 if buf.contains("GRPCAPI") || buf.contains("GPRAPI") {
331 headers.push(String::from(dent.path().to_str().unwrap()));
332 }
333 }
334
335 // To control the order of bindings
336 headers.sort();
337 for path in headers {
338 config = config.header(path);
339 }
340
Jeff Vander Stoep08902cf2020-11-19 19:03:52 +0100341 println!("cargo:rerun-if-env-changed=TEST_BIND");
342 let gen_tests = env::var("TEST_BIND").map_or(false, |s| s == "1");
343
Jeff Vander Stoep3adfea82020-10-14 15:35:59 +0200344 let cfg = config
345 .header("grpc_wrap.cc")
346 .clang_arg("-xc++")
347 .clang_arg("-I./grpc/include")
348 .clang_arg("-std=c++11")
349 .rustfmt_bindings(true)
350 .impl_debug(true)
Joel Galenson23c9e5e2021-04-02 14:59:08 -0700351 .size_t_is_usize(true)
352 .disable_header_comment()
Jeff Vander Stoep3adfea82020-10-14 15:35:59 +0200353 .whitelist_function(r"\bgrpc_.*")
354 .whitelist_function(r"\bgpr_.*")
355 .whitelist_function(r"\bgrpcwrap_.*")
356 .whitelist_var(r"\bGRPC_.*")
357 .whitelist_type(r"\bgrpc_.*")
358 .whitelist_type(r"\bgpr_.*")
359 .whitelist_type(r"\bgrpcwrap_.*")
360 .whitelist_type(r"\bcensus_context.*")
361 .whitelist_type(r"\bverify_peer_options.*")
362 .blacklist_type(r"(__)?pthread.*")
363 .blacklist_function(r"\bgpr_mu_.*")
364 .blacklist_function(r"\bgpr_cv_.*")
365 .blacklist_function(r"\bgpr_once_.*")
366 .blacklist_type(r"gpr_mu")
367 .blacklist_type(r"gpr_cv")
368 .blacklist_type(r"gpr_once")
369 .constified_enum_module(r"grpc_status_code")
Jeff Vander Stoep08902cf2020-11-19 19:03:52 +0100370 .layout_tests(gen_tests)
Jeff Vander Stoep3adfea82020-10-14 15:35:59 +0200371 .default_enum_style(bindgen::EnumVariation::Rust {
372 non_exhaustive: false,
373 });
374 println!("running {}", cfg.command_line_flags().join(" "));
375 cfg.generate()
376 .expect("Unable to generate grpc bindings")
377 .write_to_file(file_path)
378 .expect("Couldn't write bindings!");
379}
380
381// Determine if need to update bindings. Supported platforms do not
382// need to be updated by default unless the UPDATE_BIND is specified.
383// Other platforms use bindgen to generate the bindings every time.
Joel Galenson23c9e5e2021-04-02 14:59:08 -0700384fn config_binding_path() {
Jeff Vander Stoep3adfea82020-10-14 15:35:59 +0200385 let target = env::var("TARGET").unwrap();
Joel Galenson23c9e5e2021-04-02 14:59:08 -0700386 let file_path: PathBuf = match target.as_str() {
Jeff Vander Stoep3adfea82020-10-14 15:35:59 +0200387 "x86_64-unknown-linux-gnu" | "aarch64-unknown-linux-gnu" => {
388 // Cargo treats nonexistent files changed, so we only emit the rerun-if-changed
389 // directive when we expect the target-specific pre-generated binding file to be
390 // present.
391 println!("cargo:rerun-if-changed=bindings/{}-bindings.rs", &target);
392
Joel Galenson23c9e5e2021-04-02 14:59:08 -0700393 let file_path = PathBuf::from(env::var("CARGO_MANIFEST_DIR").unwrap())
Jeff Vander Stoep3adfea82020-10-14 15:35:59 +0200394 .join("bindings")
395 .join(format!("{}-bindings.rs", &target));
Joel Galenson23c9e5e2021-04-02 14:59:08 -0700396
397 #[cfg(feature = "use-bindgen")]
398 if env::var("UPDATE_BIND").is_ok() {
399 bindgen_grpc(&file_path);
Jeff Vander Stoep3adfea82020-10-14 15:35:59 +0200400 }
Joel Galenson23c9e5e2021-04-02 14:59:08 -0700401
402 file_path
Jeff Vander Stoep3adfea82020-10-14 15:35:59 +0200403 }
404 _ => {
Joel Galenson23c9e5e2021-04-02 14:59:08 -0700405 let file_path = PathBuf::from(env::var("OUT_DIR").unwrap()).join("grpc-bindings.rs");
406
407 #[cfg(feature = "use-bindgen")]
408 bindgen_grpc(&file_path);
409
410 file_path
Jeff Vander Stoep3adfea82020-10-14 15:35:59 +0200411 }
412 };
Joel Galenson23c9e5e2021-04-02 14:59:08 -0700413
Jeff Vander Stoep3adfea82020-10-14 15:35:59 +0200414 println!(
415 "cargo:rustc-env=BINDING_PATH={}",
416 file_path.to_str().unwrap()
417 );
418}
419
420fn main() {
421 println!("cargo:rerun-if-changed=grpc_wrap.cc");
422 println!("cargo:rerun-if-changed=grpc");
423 println!("cargo:rerun-if-env-changed=UPDATE_BIND");
424
425 // create a builder to compile grpc_wrap.cc
426 let mut cc = cc::Build::new();
Jeff Vander Stoep3adfea82020-10-14 15:35:59 +0200427
428 let library = if cfg!(feature = "secure") {
429 cc.define("GRPC_SYS_SECURE", None);
Jeff Vander Stoep3adfea82020-10-14 15:35:59 +0200430 "grpc"
431 } else {
432 "grpc_unsecure"
433 };
434
435 if get_env("CARGO_CFG_TARGET_OS").map_or(false, |s| s == "windows") {
436 // At lease vista
437 cc.define("_WIN32_WINNT", Some("0x600"));
Jeff Vander Stoep3adfea82020-10-14 15:35:59 +0200438 }
439
440 if get_env("GRPCIO_SYS_USE_PKG_CONFIG").map_or(false, |s| s == "1") {
441 // Print cargo metadata.
442 let lib_core = probe_library(library, true);
443 for inc_path in lib_core.include_paths {
444 cc.include(inc_path);
445 }
446 } else {
447 build_grpc(&mut cc, library);
448 }
449
450 cc.cpp(true);
451 if !cfg!(target_env = "msvc") {
452 cc.flag("-std=c++11");
453 }
454 cc.file("grpc_wrap.cc");
455 cc.warnings_into_errors(true);
456 cc.compile("libgrpc_wrap.a");
457
Joel Galenson23c9e5e2021-04-02 14:59:08 -0700458 config_binding_path();
Jeff Vander Stoep3adfea82020-10-14 15:35:59 +0200459}