use std::borrow::Borrow; use std::env; use std::ffi::OsStr; #[cfg(feature = "cmake-build")] use std::fs; use std::path::{Path, PathBuf}; use std::process::{self, Command}; fn run_command_or_fail(dir: &str, cmd: P, args: &[S]) where P: AsRef, S: Borrow + AsRef, { let cmd = cmd.as_ref(); let cmd = if cmd.components().count() > 1 && cmd.is_relative() { // If `cmd` is a relative path (and not a bare command that should be // looked up in PATH), absolutize it relative to `dir`, as otherwise the // behavior of std::process::Command is undefined. // https://github.com/rust-lang/rust/issues/37868 PathBuf::from(dir) .join(cmd) .canonicalize() .expect("canonicalization failed") } else { PathBuf::from(cmd) }; eprintln!( "Running command: \"{} {}\" in dir: {}", cmd.display(), args.join(" "), dir ); let ret = Command::new(cmd).current_dir(dir).args(args).status(); match ret.map(|status| (status.success(), status.code())) { Ok((true, _)) => (), Ok((false, Some(c))) => panic!("Command failed with error code {}", c), Ok((false, None)) => panic!("Command got killed"), Err(e) => panic!("Command failed with error: {}", e), } } fn main() { if env::var("CARGO_FEATURE_DYNAMIC_LINKING").is_ok() { eprintln!("librdkafka will be linked dynamically"); let librdkafka_version = match env!("CARGO_PKG_VERSION") .split('+') .collect::>() .as_slice() { [_rdsys_version, librdkafka_version] => *librdkafka_version, _ => panic!("Version format is not valid"), }; let pkg_probe = pkg_config::Config::new() .cargo_metadata(true) .atleast_version(librdkafka_version) .probe("rdkafka"); match pkg_probe { Ok(library) => { eprintln!("librdkafka found on the system:"); eprintln!(" Name: {:?}", library.libs); eprintln!(" Path: {:?}", library.link_paths); eprintln!(" Version: {}", library.version); } Err(err) => { eprintln!( "librdkafka {} cannot be found on the system: {}", librdkafka_version, err ); eprintln!("Dynamic linking failed. Exiting."); process::exit(1); } } } else { // Ensure that we are in the right directory let rdkafkasys_root = Path::new("rdkafka-sys"); if rdkafkasys_root.exists() { assert!(env::set_current_dir(&rdkafkasys_root).is_ok()); } if !Path::new("librdkafka/LICENSE").exists() { eprintln!("Setting up submodules"); run_command_or_fail("../", "git", &["submodule", "update", "--init"]); } eprintln!("Building and linking librdkafka statically"); build_librdkafka(); } } #[cfg(not(feature = "cmake-build"))] fn build_librdkafka() { let mut configure_flags: Vec = Vec::new(); let mut cflags = Vec::new(); if let Ok(var) = env::var("CFLAGS") { cflags.push(var); } let mut ldflags = Vec::new(); if let Ok(var) = env::var("LDFLAGS") { ldflags.push(var); } if env::var("CARGO_FEATURE_SSL").is_ok() { configure_flags.push("--enable-ssl".into()); if let Ok(openssl_root) = env::var("DEP_OPENSSL_ROOT") { cflags.push(format!("-I{}/include", openssl_root)); ldflags.push(format!("-L{}/lib", openssl_root)); } } else { configure_flags.push("--disable-ssl".into()); } if env::var("CARGO_FEATURE_GSSAPI").is_ok() { configure_flags.push("--enable-gssapi".into()); if let Ok(sasl2_root) = env::var("DEP_SASL2_ROOT") { cflags.push(format!("-I{}/include", sasl2_root)); ldflags.push(format!("-L{}/build", sasl2_root)); } } else { configure_flags.push("--disable-gssapi".into()); } if env::var("CARGO_FEATURE_LIBZ").is_ok() { // There is no --enable-zlib option, but it is enabled by default. if let Ok(z_root) = env::var("DEP_Z_ROOT") { cflags.push(format!("-I{}/include", z_root)); ldflags.push(format!("-L{}/build", z_root)); } } else { configure_flags.push("--disable-zlib".into()); } if env::var("CARGO_FEATURE_CURL").is_ok() { // There is no --enable-curl option, but it is enabled by default. if let Ok(curl_root) = env::var("DEP_CURL_ROOT") { cflags.push("-DCURLSTATIC_LIB".to_string()); cflags.push(format!("-I{}/include", curl_root)); } } else { configure_flags.push("--disable-curl".into()); } if env::var("CARGO_FEATURE_ZSTD").is_ok() { configure_flags.push("--enable-zstd".into()); if let Ok(zstd_root) = env::var("DEP_ZSTD_ROOT") { cflags.push(format!("-I{}/include", zstd_root)); ldflags.push(format!("-L{}", zstd_root)); } } else { configure_flags.push("--disable-zstd".into()); } if env::var("CARGO_FEATURE_EXTERNAL_LZ4").is_ok() { configure_flags.push("--enable-lz4-ext".into()); if let Ok(lz4_root) = env::var("DEP_LZ4_ROOT") { cflags.push(format!("-I{}/include", lz4_root)); ldflags.push(format!("-L{}", lz4_root)); } } else { configure_flags.push("--disable-lz4-ext".into()); } env::set_var("CFLAGS", cflags.join(" ")); env::set_var("LDFLAGS", ldflags.join(" ")); let out_dir = env::var("OUT_DIR").expect("OUT_DIR missing"); if !Path::new(&out_dir).join("LICENSE").exists() { // We're not allowed to build in-tree directly, as ~/.cargo/registry is // globally shared. mklove doesn't support out-of-tree builds [0], so we // work around the issue by creating a clone of librdkafka inside of // OUT_DIR, and build inside of *that* tree. // // https://github.com/edenhill/mklove/issues/17 println!("Cloning librdkafka"); run_command_or_fail(".", "cp", &["-a", "librdkafka/.", &out_dir]); } println!("Configuring librdkafka"); run_command_or_fail(&out_dir, "./configure", configure_flags.as_slice()); println!("Compiling librdkafka"); if let Some(makeflags) = env::var_os("CARGO_MAKEFLAGS") { env::set_var("MAKEFLAGS", makeflags); } run_command_or_fail( &out_dir, if cfg!(target_os = "freebsd") { "gmake" } else { "make" }, &["libs"], ); println!("cargo:rustc-link-search=native={}/src", out_dir); println!("cargo:rustc-link-lib=static=rdkafka"); println!("cargo:root={}", out_dir); } #[cfg(feature = "cmake-build")] fn build_librdkafka() { let mut config = cmake::Config::new("librdkafka"); let mut cmake_library_paths = vec![]; config .define("RDKAFKA_BUILD_STATIC", "1") .define("RDKAFKA_BUILD_TESTS", "0") .define("RDKAFKA_BUILD_EXAMPLES", "0") // CMAKE_INSTALL_LIBDIR is inferred as "lib64" on some platforms, but we // want a stable location that we can add to the linker search path. // Since we're not actually installing to /usr or /usr/local, there's no // harm to always using "lib" here. .define("CMAKE_INSTALL_LIBDIR", "lib"); if env::var("CARGO_FEATURE_LIBZ").is_ok() { config.define("WITH_ZLIB", "1"); config.register_dep("z"); if let Ok(z_root) = env::var("DEP_Z_ROOT") { cmake_library_paths.push(format!("{}/build", z_root)); } } else { config.define("WITH_ZLIB", "0"); } if env::var("CARGO_FEATURE_CURL").is_ok() { config.define("WITH_CURL", "1"); config.register_dep("curl"); if let Ok(curl_root) = env::var("DEP_CURL_ROOT") { config.define("CURL_STATICLIB", "1"); cmake_library_paths.push(format!("{}/lib", curl_root)); config.cflag("-DCURL_STATICLIB"); config.cxxflag("-DCURL_STATICLIB"); config.cflag(format!("-I{}/include", curl_root)); config.cxxflag(format!("-I{}/include", curl_root)); config.cflag(format!("-L{}/lib", curl_root)); config.cxxflag(format!("-L{}/lib", curl_root)); //FIXME: Upstream should be copying this in their build.rs fs::copy( format!("{}/build/libcurl.a", curl_root), format!("{}/lib/libcurl.a", curl_root), ) .unwrap(); } } else { config.define("WITH_CURL", "0"); } if env::var("CARGO_FEATURE_SSL").is_ok() { config.define("WITH_SSL", "1"); config.define("WITH_SASL_SCRAM", "1"); config.define("WITH_SASL_OAUTHBEARER", "1"); config.register_dep("openssl"); } else { config.define("WITH_SSL", "0"); } if env::var("CARGO_FEATURE_GSSAPI").is_ok() { config.define("WITH_SASL", "1"); config.register_dep("sasl2"); if let Ok(sasl2_root) = env::var("DEP_SASL2_ROOT") { config.cflag(format!("-I{}/include", sasl2_root)); config.cxxflag(format!("-I{}/include", sasl2_root)); } } else { config.define("WITH_SASL", "0"); } if env::var("CARGO_FEATURE_ZSTD").is_ok() { config.define("WITH_ZSTD", "1"); config.register_dep("zstd"); } else { config.define("WITH_ZSTD", "0"); } if env::var("CARGO_FEATURE_EXTERNAL_LZ4").is_ok() { config.define("ENABLE_LZ4_EXT", "1"); config.register_dep("lz4"); } else { config.define("ENABLE_LZ4_EXT", "0"); } if let Ok(system_name) = env::var("CMAKE_SYSTEM_NAME") { config.define("CMAKE_SYSTEM_NAME", system_name); } if let Ok(make_program) = env::var("CMAKE_MAKE_PROGRAM") { config.define("CMAKE_MAKE_PROGRAM", make_program); } if !cmake_library_paths.is_empty() { env::set_var("CMAKE_LIBRARY_PATH", cmake_library_paths.join(";")); } println!("Configuring and compiling librdkafka"); let dst = config.build(); println!("cargo:rustc-link-search=native={}/lib", dst.display()); println!("cargo:rustc-link-lib=static=rdkafka"); }