Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
47 changes: 44 additions & 3 deletions prost-build/src/config.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1267,10 +1267,34 @@ pub fn error_message_protoc_not_found() -> String {
}

/// Returns the path to the `protoc` binary.
///
/// When Cargo runs a build script it sets the `HOST` environment variable to the host target
/// triple. If a host specific `<HOST>_PROTOC` variable is set (for example
/// `X86_64_UNKNOWN_LINUX_GNU_PROTOC`), it takes precedence over the plain `PROTOC` variable.
/// This makes it possible to point at a different `protoc` per build host from a single `[env]`
/// table in `.cargo/config.toml`. If neither variable is set, `protoc` is looked up on the `PATH`.
pub fn protoc_from_env() -> PathBuf {
env::var_os("PROTOC")
.map(PathBuf::from)
.unwrap_or(PathBuf::from("protoc"))
host_protoc_from_env()
.or_else(|| env::var_os("PROTOC").map(PathBuf::from))
.unwrap_or_else(|| PathBuf::from("protoc"))
}

/// Returns the host specific `protoc` path, e.g. from `X86_64_UNKNOWN_LINUX_GNU_PROTOC`.
///
/// Returns `None` when the `HOST` triple is not available (so outside of a build script), or when
/// the host specific variable is not set.
fn host_protoc_from_env() -> Option<PathBuf> {
let host = env::var("HOST").ok()?;
env::var_os(host_protoc_var(&host)).map(PathBuf::from)
}

/// Builds the host specific `protoc` environment variable name from a target triple.
///
/// The triple is uppercased and any `-` or `.` is replaced with `_`, so the name is a valid
/// environment variable. This follows the same convention used by other build time crates such as
/// `openssl-sys`.
fn host_protoc_var(host: &str) -> String {
format!("{}_PROTOC", host.replace(['-', '.'], "_").to_uppercase())
}

/// Returns the path to the Protobuf include directory.
Expand Down Expand Up @@ -1312,6 +1336,23 @@ mod tests {
};
}

#[test]
fn test_host_protoc_var() {
assert_eq!(
host_protoc_var("x86_64-unknown-linux-gnu"),
"X86_64_UNKNOWN_LINUX_GNU_PROTOC"
);
assert_eq!(
host_protoc_var("aarch64-pc-windows-msvc"),
"AARCH64_PC_WINDOWS_MSVC_PROTOC"
);
// Triples containing a `.` are normalized to a valid environment variable name.
assert_eq!(
host_protoc_var("thumbv8m.main-none-eabi"),
"THUMBV8M_MAIN_NONE_EABI_PROTOC"
);
}

#[test]
fn test_error_protoc_not_found() {
let mut config = Config::new();
Expand Down
11 changes: 11 additions & 0 deletions prost-build/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -114,6 +114,17 @@
//! PROTOC=/usr/local/bin/protoc
//! ```
//!
//! When building on more than one host, a host specific `<HOST>_PROTOC` variable takes precedence
//! over `PROTOC`. The prefix is the host target triple, uppercased with `-` and `.` replaced by
//! `_`, so on an `x86_64-unknown-linux-gnu` host the variable is `X86_64_UNKNOWN_LINUX_GNU_PROTOC`.
//! This lets a single `[env]` table in `.cargo/config.toml` point at a different `protoc` per host:
//!
//! ```toml
//! [env]
//! X86_64_UNKNOWN_LINUX_GNU_PROTOC = { value = "tools/x86_64-linux/protoc", relative = true }
//! AARCH64_APPLE_DARWIN_PROTOC = { value = "tools/aarch64-macos/protoc", relative = true }
//! ```
//!
//! Alternatively, the path to `protoc` executable can be explicitly set
//! via [`Config::protoc_executable()`].
//!
Expand Down