diff --git a/cargo/build/protobuf.rs b/cargo/build/protobuf.rs index 6d279fd85..e3fd67169 100644 --- a/cargo/build/protobuf.rs +++ b/cargo/build/protobuf.rs @@ -12,12 +12,49 @@ pub fn collect_protobuf_includes() -> Vec { paths::get_env_paths("PROTOBUF_INCLUDE_PATH") } +fn is_protobuf_lib(name: &str) -> bool { + // Protobuf's CMake build prefixes these archives with `lib` on every platform, + // including Windows, where `collect_static_libs` keeps the file stem as-is. + matches!(name.strip_prefix("lib").unwrap_or(name), "protobuf" | "utf8_validity") +} + /// Returns the paths to the protobuf libraries (to be used as a search path) and a /// list of libraries to be linked. +/// +/// This is an allowlist rather than "everything in the directory", because a protobuf +/// install tree holds several archives that must not be linked together: +/// +/// * `protobuf` is the library Crubit uses, and it has undefined references into +/// `utf8_validity`, so the two belong together. +/// * `utf8_range` is compiled from the same `utf8_range.c` as `utf8_validity` and +/// defines the same symbols, so linking both is a duplicate symbol error. +/// * `protobuf-lite` is a subset of `protobuf`, and would collide with it likewise. +/// * `protoc` and `upb` are not used by Crubit. pub fn collect_protobuf_libs() -> (Vec, Vec) { - paths::collect_static_libs("PROTOBUF_LIB_STATIC_PATH", |name| { - name.strip_prefix("lib").unwrap_or(name) == "protobuf" - }) + paths::collect_static_libs("PROTOBUF_LIB_STATIC_PATH", is_protobuf_lib) +} + +/// Unit tests for protobuf library allowlist matching. +/// +/// Note: These tests use standard `#[test]` rather than `googletest` because +/// `crubit_build` is a build helper crate built via Cargo. +#[cfg(test)] +mod tests { + use super::*; + + #[test] // allow_core_test (see mod tests doc comment) + fn test_is_protobuf_lib() { + assert!(is_protobuf_lib("protobuf")); + assert!(is_protobuf_lib("libprotobuf")); + assert!(is_protobuf_lib("utf8_validity")); + assert!(is_protobuf_lib("libutf8_validity")); + assert!(!is_protobuf_lib("utf8_range")); + assert!(!is_protobuf_lib("libutf8_range")); + assert!(!is_protobuf_lib("protobuf-lite")); + assert!(!is_protobuf_lib("libprotobuf-lite")); + assert!(!is_protobuf_lib("protoc")); + assert!(!is_protobuf_lib("upb")); + } } /// Locates pre-generated .pb.cc C++ source files corresponding to `proto_sources` diff --git a/docs/cpp/building.md b/docs/cpp/building.md index 2bf843da2..c88572030 100644 --- a/docs/cpp/building.md +++ b/docs/cpp/building.md @@ -94,7 +94,9 @@ export ABSL_INCLUDE_PATH=/path/to/absl/include/dir export ABSL_LIB_STATIC_PATH=/path/to/absl/libs ## This path contains google/protobuf/ dir with all the includes. export PROTOBUF_INCLUDE_PATH=/path/to/protobuf/include/dir -## This path contains libprotobuf.a +## This path contains libprotobuf.a and libutf8_validity.a. Other archives that a +## protobuf install tree holds (libutf8_range.a, libprotobuf-lite.a, libprotoc.a, +## libupb.a) are ignored; the first two would collide with the ones above. export PROTOBUF_LIB_STATIC_PATH=/path/to/protobuf/libs cargo build --bin rs_bindings_from_cc