Skip to content
Merged
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
28 changes: 14 additions & 14 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@ exclude = ["dylints/ban_raw_subprocess"]
libraries = [{ path = "dylints/*" }]

[workspace.package]
version = "2.3.2"
version = "2.3.3"
edition = "2021"
rust-version = "1.94.1"
license = "MIT OR Apache-2.0"
Expand Down
15 changes: 14 additions & 1 deletion crates/fbuild-build/src/compiler.rs
Original file line number Diff line number Diff line change
Expand Up @@ -652,7 +652,20 @@ pub fn compile_source(
tracing::info!("compile: {}", args.join(" "));
}

let result = run_command(&args_ref, compile_cwd.as_deref(), None, None)?;
let mut result = run_command(&args_ref, compile_cwd.as_deref(), None, None)?;

if !result.success() {
if let Some(zccache) = compiler_cache {
if crate::zccache::output_has_stale_daemon_error(&result.stdout, &result.stderr) {
tracing::warn!(
"zccache protocol mismatch detected; stopping daemon and retrying compile"
);
crate::zccache::stop(zccache)?;
crate::zccache::ensure_running(zccache)?;
result = run_command(&args_ref, compile_cwd.as_deref(), None, None)?;
}
}
}

if result.success() {
std::fs::write(command_hash_path(output), rebuild_signature)?;
Expand Down
4 changes: 2 additions & 2 deletions crates/fbuild-build/src/managed_zccache.rs
Original file line number Diff line number Diff line change
Expand Up @@ -24,12 +24,12 @@ use fbuild_core::{FbuildError, Result};

/// The zccache release fbuild pins. Bump in lockstep with the floor that
/// the rest of the toolchain expects.
pub const MANAGED_ZCCACHE_VERSION: &str = "1.12.9";
pub const MANAGED_ZCCACHE_VERSION: &str = "1.12.10";

/// GitHub release tag for [`MANAGED_ZCCACHE_VERSION`]. The zccache release
/// workflow tags without a `v` prefix, while the per-asset filenames carry
/// `v<version>` — keep both spellings in sync when bumping.
const RELEASE_TAG: &str = "1.12.9";
const RELEASE_TAG: &str = "1.12.10";

/// Source repository for managed downloads.
const REPO: &str = "zackees/zccache";
Expand Down
107 changes: 107 additions & 0 deletions crates/fbuild-build/src/zccache.rs
Original file line number Diff line number Diff line change
Expand Up @@ -213,6 +213,47 @@ pub fn ensure_running(zccache: &Path) -> Result<()> {
if output.status.success() {
tracing::info!("zccache daemon running");
Ok(())
} else if output_has_stale_daemon_error(
&String::from_utf8_lossy(&output.stdout),
&String::from_utf8_lossy(&output.stderr),
) {
tracing::warn!("zccache daemon appears stale; stopping and retrying start");
let _ = stop(zccache);
std::thread::sleep(std::time::Duration::from_millis(250));

let mut retry_cmd = std::process::Command::new(zccache);
retry_cmd
.arg("start")
.stdout(Stdio::piped())
.stderr(Stdio::piped());

#[cfg(windows)]
{
use std::os::windows::process::CommandExt;
const CREATE_NO_WINDOW: u32 = 0x08000000;
retry_cmd.creation_flags(CREATE_NO_WINDOW);
}

let retry = retry_cmd.output().map_err(|e| {
FbuildError::BuildFailed(format!(
"failed to spawn zccache daemon via `{}` start: {}",
zccache.display(),
e
))
})?;
if retry.status.success() {
tracing::info!("zccache daemon running after stale-daemon recovery");
Ok(())
} else {
let message = format_zccache_start_failure(
zccache,
retry.status.to_string(),
&retry.stdout,
&retry.stderr,
);
tracing::warn!("{message}");
Err(FbuildError::BuildFailed(message))
}
} else {
let message = format_zccache_start_failure(
zccache,
Expand All @@ -225,6 +266,55 @@ pub fn ensure_running(zccache: &Path) -> Result<()> {
}
}

/// Stop the zccache daemon for this user, if one is running.
pub fn stop(zccache: &Path) -> Result<()> {
let mut cmd = std::process::Command::new(zccache);
cmd.arg("stop")
.stdout(Stdio::piped())
.stderr(Stdio::piped());

#[cfg(windows)]
{
use std::os::windows::process::CommandExt;
const CREATE_NO_WINDOW: u32 = 0x08000000;
cmd.creation_flags(CREATE_NO_WINDOW);
}

let output = cmd.output().map_err(|e| {
FbuildError::BuildFailed(format!(
"failed to spawn zccache daemon via `{}` stop: {}",
zccache.display(),
e
))
})?;

if output.status.success() {
Ok(())
} else {
Err(FbuildError::BuildFailed(format_zccache_start_failure(
zccache,
output.status,
&output.stdout,
&output.stderr,
)))
}
}

/// zccache can leave a previous-version daemon running across fbuild upgrades.
/// The new client then fails wrapped compiles with a protocol mismatch until
/// the old daemon is stopped.
pub fn output_has_protocol_mismatch(stdout: &str, stderr: &str) -> bool {
stdout.contains("protocol version mismatch") || stderr.contains("protocol version mismatch")
}

pub fn output_has_stale_daemon_error(stdout: &str, stderr: &str) -> bool {
output_has_protocol_mismatch(stdout, stderr)
|| stdout.contains("lost connection to daemon")
|| stderr.contains("lost connection to daemon")
|| stdout.contains("not accepting connections")
|| stderr.contains("not accepting connections")
}

fn format_zccache_start_failure(
zccache: &Path,
status: impl std::fmt::Display,
Expand Down Expand Up @@ -597,6 +687,23 @@ mod tests {
assert!(message.contains("fbuild daemon logs"));
}

#[test]
fn detects_zccache_protocol_mismatch_from_stderr_or_stdout() {
let mismatch =
"zccache[err][R]: broken connection to daemon: protocol error: protocol version mismatch: expected v16, received v15";

assert!(output_has_protocol_mismatch("", mismatch));
assert!(output_has_protocol_mismatch(mismatch, ""));
assert!(output_has_stale_daemon_error(
"",
"zccache[err][R]: lost connection to daemon (no response)"
));
assert!(!output_has_protocol_mismatch(
"ordinary stdout",
"ordinary stderr"
));
}

#[test]
fn path_arg_for_compile_cwd_returns_workspace_relative_path() {
let tmp = tempfile::TempDir::new().unwrap();
Expand Down
2 changes: 1 addition & 1 deletion pyproject.toml
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
[project]
name = "fbuild"
version = "2.3.2"
version = "2.3.3"
description = "PlatformIO-compatible embedded build tool (Rust implementation)"
readme = "README.md"
requires-python = ">=3.10"
Expand Down
Loading