From 6530cdeaa78d2b09e7e29d693fc0566e02db4a40 Mon Sep 17 00:00:00 2001 From: Willi Ballenthin Date: Wed, 30 Oct 2024 09:38:52 +0000 Subject: [PATCH] core: find protobuf-src protoc binary dynamically --- core/build.rs | 72 ++++++++++++++++++++++++++++++++++++++++++++------- 1 file changed, 62 insertions(+), 10 deletions(-) diff --git a/core/build.rs b/core/build.rs index e60da8b..b850b89 100644 --- a/core/build.rs +++ b/core/build.rs @@ -1,15 +1,67 @@ -use std::io::Result; +use std::{io::Result, ops::Not, path::PathBuf}; + +/// Figure out where the protoc executable from protobuf-src is, if available. +fn find_protoc() -> Option { + // protobuf-src doesn't support Windows, + // so the external build environment must provide protoc.exe. + // https://github.com/MaterializeInc/rust-protobuf-native/issues/4 + // + // Our setup approximates the changes here: + // https://github.com/CerebusOSS/ella/pull/14/files + // + // But there are some problems... + // + // We might want to use `if cfg!(not(target_os = "windows"))` + // to include protobuf-src on supported platforms, + // but this doesn't actually work correctly in a build.rs script, + // because the target is for the build.rs script, not the final program, + // which is important during cross compilation. + // + // So then we might want to use + // `if std::env::var_os("CARGO_CFG_WINDOWS").is_none()` and we can, + // but we can't conditionally compile code within the block, + // such as conditionally reference protobuf-src. + // + // So instead, we look around the build directory for the protoc file + // :evilgrin: + // + // This requires that protobuf-src is listed in the build-dependencies + // and that it gets built before this script, which seems to be true. + + // like: .../lancelot/target/debug/build/lancelot-f70fef48d1b50e7f/out + let out_dir = std::env::var("OUT_DIR").expect("OUT_DIR not present"); + let out_dir = PathBuf::from(out_dir); + + // like: .../lancelot/target/debug/build + let build_dir = &out_dir.parent().unwrap().parent().unwrap(); + + for entry in std::fs::read_dir(build_dir).ok()? { + if let Ok(entry) = entry { + if entry.file_name().to_string_lossy().starts_with("protobuf-src-").not() { + continue; + } + + let mut path = entry.path(); + path.push("out"); + path.push("bin"); + path.push("protoc"); + + if matches!(std::fs::exists(&path), Ok(true)) { + return Some(path); + } + } + } + + None +} fn main() -> Result<()> { - if std::env::var_os("CARGO_CFG_WINDOWS").is_none() { - // protobuf-src doesn't support Windows, - // so the external build environment must provide protoc.exe. - // https://github.com/MaterializeInc/rust-protobuf-native/issues/4 - // - // Our setup approximates the changes here: - // https://github.com/CerebusOSS/ella/pull/14/files - std::env::set_var("PROTOC", protobuf_src::protoc()); - std::env::set_var("PROTOC_INCLUDE", protobuf_src::include()); + if let Some(protoc) = find_protoc() { + std::env::set_var("PROTOC", &*protoc.to_string_lossy()); + } else if std::env::var_os("CARGO_CFG_WINDOWS").is_none() { + panic!("expected to find protoc for non-Windows target") + } else { + // on Windows, external environment is expected to provide protoc. } // this may fail on windows if protoc.exe is not present.