pub(super) mod cargo_bins;
pub(super) mod clean_overlay;
pub(super) mod direct_compiler;
use super::command_parsing::{cargo_build_only_test, cargo_custom_profile_output_dir};
use super::*;
pub(super) fn artifact_delivery_kind(
kind: Option<CompilationKind>,
command: Option<&str>,
) -> Option<CompilationKind> {
if command.is_some_and(|command| cargo_build_only_test(kind, command)) {
Some(CompilationKind::CargoBuild)
} else {
kind
}
}
pub(super) fn get_artifact_patterns(
kind: Option<CompilationKind>,
command: Option<&str>,
) -> Vec<String> {
let kind = artifact_delivery_kind(kind, command);
if let Some(patterns) = direct_compiler::patterns(kind, command) {
return patterns;
}
if kind == Some(CompilationKind::CargoBuild)
&& command.is_some_and(rch_common::patterns::is_cargo_package_verification)
{
return vec![
"target/package/*.crate".to_string(),
"target/package/tmp-registry/*.crate".to_string(),
"target/package/tmp-crate/*.crate".to_string(),
];
}
if let Some(patterns) = cargo_bins::patterns(kind, command) {
return patterns;
}
let mut patterns = match kind {
Some(CompilationKind::BunTest) | Some(CompilationKind::BunTypecheck) => {
default_bun_artifact_patterns()
}
Some(CompilationKind::CargoTest)
| Some(CompilationKind::CargoNextest)
| Some(CompilationKind::CargoBench)
| Some(CompilationKind::CargoCheck)
| Some(CompilationKind::CargoClippy) => default_rust_test_artifact_patterns(),
Some(CompilationKind::Rustc)
| Some(CompilationKind::CargoBuild)
| Some(CompilationKind::CargoDoc) => default_rust_artifact_patterns(),
Some(CompilationKind::CargoZigbuild) => default_zigbuild_artifact_patterns(),
Some(CompilationKind::Gcc)
| Some(CompilationKind::Gpp)
| Some(CompilationKind::Clang)
| Some(CompilationKind::Clangpp)
| Some(CompilationKind::Make)
| Some(CompilationKind::CmakeBuild)
| Some(CompilationKind::Ninja)
| Some(CompilationKind::Meson) => {
let mut patterns = default_c_cpp_artifact_patterns();
clean_overlay::exclude_source_archive(&mut patterns);
patterns
}
Some(CompilationKind::NixBuild) => Vec::new(),
Some(CompilationKind::GoBuild)
| Some(CompilationKind::GoTest)
| Some(CompilationKind::GoVet)
| Some(CompilationKind::Tsc) => Vec::new(),
Some(CompilationKind::Job) => Vec::new(),
_ => default_rust_artifact_patterns(),
};
if rust_kind_targets_cargo_output_tree(kind)
&& let Some(profile_dir) = command.and_then(cargo_custom_profile_output_dir)
{
patterns.push(format!("target/{profile_dir}/**"));
patterns.push(format!("target/*/{profile_dir}/**"));
}
patterns
}
fn rust_kind_targets_cargo_output_tree(kind: Option<CompilationKind>) -> bool {
matches!(
kind,
Some(
CompilationKind::CargoBuild
| CompilationKind::CargoDoc
| CompilationKind::CargoZigbuild
| CompilationKind::Rustc
) | None
)
}
pub(super) fn get_project_artifact_patterns(
kind: Option<CompilationKind>,
command: Option<&str>,
custom_target_sync: bool,
) -> Vec<String> {
if let Some(patterns) = direct_compiler::patterns(kind, command) {
return patterns;
}
let patterns = get_artifact_patterns(kind, command);
if custom_target_sync {
patterns
.into_iter()
.filter(|pattern| !pattern.starts_with("target/"))
.collect()
} else {
patterns
}
}
pub(super) fn expected_output_glob_list(patterns: &[String]) -> Vec<String> {
patterns
.iter()
.map(|p| {
p.strip_prefix("+ ")
.map_or_else(|| p.clone(), str::to_string)
})
.filter(|p| {
!p.starts_with("- ")
&& !matches!(
p.as_str(),
".rustc_info.json"
| "CACHEDIR.TAG"
| "target/.rustc_info.json"
| "target/CACHEDIR.TAG"
)
})
.collect()
}
const CARGO_TARGET_CACHE_EXCLUDES: &[&str] = &[
"- debug/incremental/",
"- debug/.fingerprint/",
"- debug/build/",
"- release/incremental/",
"- release/.fingerprint/",
"- release/build/",
"- */incremental/",
"- */.fingerprint/",
"- */build/",
"- *.d",
];
fn custom_profile_cache_excludes(profile_dir: &str) -> Vec<String> {
[
format!("- {profile_dir}/incremental/"),
format!("- {profile_dir}/.fingerprint/"),
format!("- {profile_dir}/build/"),
format!("- */{profile_dir}/incremental/"),
format!("- */{profile_dir}/.fingerprint/"),
format!("- */{profile_dir}/build/"),
]
.to_vec()
}
pub(super) fn get_custom_target_artifact_patterns(
kind: Option<CompilationKind>,
command: Option<&str>,
) -> Vec<String> {
let build_only_test = command.is_some_and(|command| cargo_build_only_test(kind, command));
let kind = artifact_delivery_kind(kind, command);
if direct_compiler::patterns(kind, command).is_some() {
return Vec::new();
}
match kind {
Some(CompilationKind::CargoTest)
| Some(CompilationKind::CargoCheck)
| Some(CompilationKind::CargoClippy)
| Some(CompilationKind::NixBuild)
| Some(CompilationKind::Job) => Vec::new(),
Some(CompilationKind::CargoNextest) | Some(CompilationKind::CargoBench) => {
get_artifact_patterns(kind, command)
.into_iter()
.map(|pattern| {
pattern
.strip_prefix("target/")
.unwrap_or(pattern.as_str())
.to_string()
})
.collect()
}
Some(CompilationKind::CargoZigbuild) => {
let mut patterns: Vec<String> = CARGO_TARGET_CACHE_EXCLUDES
.iter()
.map(|s| (*s).to_string())
.collect();
patterns.extend(
[
"- */release/incremental/",
"- */release/.fingerprint/",
"- */release/build/",
"- */debug/incremental/",
"- */debug/.fingerprint/",
"- */debug/build/",
]
.iter()
.map(|s| (*s).to_string()),
);
if let Some(profile_dir) = command.and_then(cargo_custom_profile_output_dir) {
patterns.extend(custom_profile_cache_excludes(&profile_dir));
}
patterns.extend(get_artifact_patterns(kind, command).into_iter().map(|pattern| {
pattern
.strip_prefix("target/")
.unwrap_or(pattern.as_str())
.to_string()
}));
patterns
}
_ => {
let mut patterns: Vec<String> = CARGO_TARGET_CACHE_EXCLUDES
.iter()
.map(|s| (*s).to_string())
.collect();
patterns.extend(
[
"- */release/incremental/",
"- */release/.fingerprint/",
"- */release/build/",
"- */debug/incremental/",
"- */debug/.fingerprint/",
"- */debug/build/",
]
.iter()
.map(|s| (*s).to_string()),
);
if let Some(profile_dir) = command.and_then(cargo_custom_profile_output_dir) {
patterns.extend(custom_profile_cache_excludes(&profile_dir));
}
patterns.extend(get_artifact_patterns(kind, command).into_iter().map(|pattern| {
pattern
.strip_prefix("target/")
.unwrap_or(pattern.as_str())
.to_string()
}));
if build_only_test {
patterns.extend(new_layout_executable_patterns(command));
}
patterns
}
}
}
fn new_layout_executable_patterns(command: Option<&str>) -> Vec<String> {
let mut profiles = vec!["debug".to_string(), "release".to_string()];
profiles.extend(command.and_then(cargo_custom_profile_output_dir));
let mut patterns = Vec::new();
for profile in &profiles {
for root in [profile.clone(), format!("*/{profile}")] {
patterns.push(format!("+ {root}/build/*/*/out/*-????????????????"));
patterns.push(format!("+ {root}/build/*/*/out/*-????????????????.exe"));
patterns.push(format!("- {root}/build/**"));
}
}
patterns
}
pub(super) fn kind_produces_transferable_artifacts(kind: Option<CompilationKind>) -> bool {
match kind {
Some(CompilationKind::CargoBuild)
| Some(CompilationKind::CargoDoc)
| Some(CompilationKind::Rustc)
| Some(CompilationKind::CargoZigbuild)
| Some(CompilationKind::GoBuild)
| Some(CompilationKind::Gcc)
| Some(CompilationKind::Gpp)
| Some(CompilationKind::Clang)
| Some(CompilationKind::Clangpp)
| Some(CompilationKind::Make)
| Some(CompilationKind::CmakeBuild)
| Some(CompilationKind::Ninja)
| Some(CompilationKind::Meson) => true,
Some(CompilationKind::CargoTest)
| Some(CompilationKind::CargoNextest)
| Some(CompilationKind::CargoBench)
| Some(CompilationKind::CargoCheck)
| Some(CompilationKind::CargoClippy)
| Some(CompilationKind::BunTest)
| Some(CompilationKind::BunTypecheck)
| Some(CompilationKind::NixBuild)
| Some(CompilationKind::Job)
| Some(CompilationKind::GoTest)
| Some(CompilationKind::GoVet)
| Some(CompilationKind::Tsc) => false,
None => false,
}
}
fn retrieved_path_is_build_output(path: &str, custom_target_sync: bool) -> bool {
let rel = if custom_target_sync {
path
} else {
path.strip_prefix("target/").unwrap_or(path)
};
let components: Vec<&str> = rel.split('/').filter(|c| !c.is_empty()).collect();
if components.len() < 2 {
return false;
}
if components
.iter()
.any(|c| matches!(*c, "incremental" | ".fingerprint" | "build"))
{
return false;
}
!rel.ends_with(".d")
}
pub(super) fn kind_has_enumerable_output_contract(kind: Option<CompilationKind>) -> bool {
matches!(
kind,
Some(
CompilationKind::CargoBuild
| CompilationKind::CargoDoc
| CompilationKind::CargoZigbuild
)
)
}
pub(super) fn sync_back_verified_zero_package_archives(
matched_regular_files: Option<u32>,
command: &str,
) -> bool {
matched_regular_files == Some(0) && rch_common::patterns::is_cargo_package_verification(command)
}
pub(super) fn sync_back_verified_zero_build_outputs(
manifest: &[String],
matched_regular_files: Option<u32>,
kind: Option<CompilationKind>,
custom_target_sync: bool,
) -> bool {
if !kind_has_enumerable_output_contract(kind) {
return false;
}
let Some(matched) = matched_regular_files else {
return false;
};
if matched == 0 {
return false;
}
if manifest.len() < matched as usize {
return false;
}
!manifest
.iter()
.any(|path| retrieved_path_is_build_output(path, custom_target_sync))
}
#[cfg(test)]
mod profile_artifact_tests {
use super::*;
#[test]
fn only_build_only_tests_open_the_new_layout_build_tree() {
let command = "cargo test --no-run --profile=fixture";
let patterns =
get_custom_target_artifact_patterns(Some(CompilationKind::CargoTest), Some(command));
for root in ["debug", "release", "fixture", "*/fixture"] {
assert!(
patterns.contains(&format!("+ {root}/build/*/*/out/*-????????????????")),
"{root}: {patterns:?}"
);
assert!(patterns.contains(&format!("- {root}/build/**")), "{root}");
}
for (kind, command) in [
(CompilationKind::CargoBuild, "cargo build"),
(CompilationKind::CargoBuild, "cargo build --profile=fixture"),
(CompilationKind::CargoTest, "cargo test"),
] {
let patterns = get_custom_target_artifact_patterns(Some(kind), Some(command));
assert!(
!patterns.iter().any(|pattern| pattern.starts_with("+ ")),
"{command} must keep the cache excludes closed: {patterns:?}"
);
}
}
#[test]
fn build_only_tests_use_the_build_contract_for_both_retrieval_bases_and_gates() {
for (kind, command) in [
(CompilationKind::CargoTest, "cargo test --no-run --lib"),
(
CompilationKind::CargoTest,
"env -- cargo test --no-run --profile release-perf",
),
(
CompilationKind::CargoBench,
"cargo bench --no-run --bench timing",
),
] {
let kind = Some(kind);
let command = Some(command);
let delivery_kind = artifact_delivery_kind(kind, command);
assert_eq!(delivery_kind, Some(CompilationKind::CargoBuild));
assert_eq!(
get_artifact_patterns(kind, command),
get_artifact_patterns(delivery_kind, command)
);
let mut expected = get_custom_target_artifact_patterns(delivery_kind, command);
expected.extend(new_layout_executable_patterns(command));
assert_eq!(get_custom_target_artifact_patterns(kind, command), expected);
assert!(!get_project_artifact_patterns(kind, command, false).is_empty());
assert!(get_project_artifact_patterns(kind, command, true).is_empty());
assert!(kind_produces_transferable_artifacts(delivery_kind));
assert!(kind_has_enumerable_output_contract(delivery_kind));
assert!(sync_back_verified_zero_build_outputs(
&["target/CACHEDIR.TAG".into()],
Some(1),
delivery_kind,
false,
));
assert!(!sync_back_verified_zero_build_outputs(
&["debug/deps/unit_test-123".into()],
Some(1),
delivery_kind,
true,
));
}
}
#[test]
fn runtime_tests_and_opaque_no_run_values_keep_the_stream_only_contract() {
for (kind, command) in [
(CompilationKind::CargoTest, "cargo test -- --no-run"),
(CompilationKind::CargoTest, "cargo test --config --no-run"),
(CompilationKind::CargoBench, "cargo bench --bench --no-run"),
(CompilationKind::CargoNextest, "cargo nextest run --no-run"),
] {
let kind = Some(kind);
assert_eq!(artifact_delivery_kind(kind, Some(command)), kind);
assert_eq!(
get_artifact_patterns(kind, Some(command)),
get_artifact_patterns(kind, None)
);
assert_eq!(
get_custom_target_artifact_patterns(kind, Some(command)),
get_custom_target_artifact_patterns(kind, None)
);
assert!(!kind_produces_transferable_artifacts(kind));
}
}
#[test]
fn cargo_profile_artifacts_use_wrapped_profile_for_includes_and_cache_excludes() {
for kind in [
CompilationKind::CargoBuild,
CompilationKind::CargoDoc,
CompilationKind::CargoZigbuild,
] {
for command in [
"env -- cargo build --profile release-perf",
"/usr/bin/time -f cargo -- cargo build --profile release-perf",
"rustup run nightly cargo build --config --profile=decoy --profile release-perf",
] {
let project = get_artifact_patterns(Some(kind), Some(command));
for include in ["target/release-perf/**", "target/*/release-perf/**"] {
assert!(
project.iter().any(|pattern| pattern == include),
"{command}: {project:?}"
);
}
let custom = get_custom_target_artifact_patterns(Some(kind), Some(command));
for include in ["release-perf/**", "*/release-perf/**"] {
assert!(
custom.iter().any(|pattern| pattern == include),
"{command}: {custom:?}"
);
}
let first_include = custom
.iter()
.position(|pattern| !pattern.starts_with("- "))
.unwrap();
for exclude in custom_profile_cache_excludes("release-perf") {
assert!(
custom
.iter()
.position(|pattern| pattern == &exclude)
.unwrap()
< first_include,
"profile cache exclusion must precede every output include"
);
}
assert!(custom.iter().all(|pattern| !pattern.contains("decoy")));
assert!(
get_project_artifact_patterns(Some(kind), Some(command), true)
.iter()
.all(|pattern| !pattern.starts_with("target/")),
"custom-target forwarding must not re-enable stale project-root retrieval"
);
}
}
}
#[test]
fn cargo_profile_artifacts_keep_passthrough_and_stream_only_policies_unchanged() {
let build = Some(CompilationKind::CargoBuild);
for command in [
"env -- cargo run -- --profile release-perf",
"cargo build --config --profile=decoy",
"env -- cargo build --profile=release",
] {
assert_eq!(
get_artifact_patterns(build, Some(command)),
get_artifact_patterns(build, None)
);
assert_eq!(
get_custom_target_artifact_patterns(build, Some(command)),
get_custom_target_artifact_patterns(build, None)
);
}
for kind in [
CompilationKind::CargoTest,
CompilationKind::CargoCheck,
CompilationKind::CargoClippy,
CompilationKind::CargoNextest,
CompilationKind::CargoBench,
] {
let command = Some("env -- cargo test --profile release-perf");
assert_eq!(
get_artifact_patterns(Some(kind), command),
get_artifact_patterns(Some(kind), None)
);
assert_eq!(
get_custom_target_artifact_patterns(Some(kind), command),
get_custom_target_artifact_patterns(Some(kind), None)
);
}
assert!(!sync_back_verified_zero_build_outputs(
&["debug/application".to_owned()],
Some(1),
build,
true,
));
}
#[cfg(unix)]
#[tokio::test]
async fn build_only_cargo_returns_real_test_and_bench_executables() {
use std::os::unix::fs::PermissionsExt;
use std::process::Stdio;
use std::time::Duration;
use tokio::process::Command;
for (kind, subcommand, selector, forwarded) in [
(CompilationKind::CargoTest, "test", "--lib", false),
(CompilationKind::CargoTest, "test", "--lib", true),
(
CompilationKind::CargoBench,
"bench",
"--bench=runproof",
false,
),
] {
let root = std::fs::canonicalize(tempfile::tempdir().unwrap().keep()).unwrap();
let source = root.join("source project");
let local_project = root.join("local project");
std::fs::create_dir_all(source.join("src")).unwrap();
std::fs::create_dir_all(source.join("benches")).unwrap();
std::fs::create_dir_all(local_project.join("src")).unwrap();
std::fs::write(source.join("Cargo.toml"), concat!(
"[package]\nname = \"rch_no_run_fixture\"\nversion = \"0.0.0\"\nedition = \"2024\"\n",
"[workspace]\n",
"[[bench]]\nname = \"runproof\"\nharness = false\n",
"[profile.no-run-fixture]\ninherits = \"dev\"\nopt-level = 0\n",
)).unwrap();
std::fs::write(
source.join("src/lib.rs"),
concat!(
"#[test]\nfn build_only_test_must_not_execute() {\n",
" panic!(\"Cargo ran a build-only test\");\n}\n",
),
)
.unwrap();
std::fs::write(
source.join("benches/runproof.rs"),
concat!(
"fn main() {\n",
" if std::env::args().any(|arg| arg == \"--list\") {\n",
" println!(\"build_only_bench_must_not_execute\");\n",
" } else { panic!(\"Cargo ran a build-only benchmark\"); }\n}\n",
),
)
.unwrap();
let sentinel = local_project.join("src/lib.rs");
std::fs::write(&sentinel, b"local source must not be overwritten\n").unwrap();
let remote_target = root.join("worker target");
let local_target = root.join("local target");
let mut args = vec![
subcommand.to_owned(),
"--no-run".into(),
selector.into(),
"--offline".into(),
"--jobs=1".into(),
"--message-format=json".into(),
];
if forwarded {
args.extend([
"--target-dir".into(),
remote_target.to_str().unwrap().to_owned(),
"--profile=no-run-fixture".into(),
]);
}
let command_text =
shell_words::join(std::iter::once("cargo").chain(args.iter().map(String::as_str)));
let cargo = std::env::var_os("CARGO").unwrap_or_else(|| "cargo".into());
let mut compile = Command::new(cargo);
compile
.current_dir(&source)
.args(&args)
.env("CARGO_HOME", root.join("cargo-home"))
.env_remove("RUSTC_WRAPPER")
.env_remove("RUSTC_WORKSPACE_WRAPPER")
.env_remove("RUSTFLAGS")
.env_remove("CARGO_ENCODED_RUSTFLAGS")
.env_remove("CARGO_BUILD_TARGET")
.env_remove("CARGO_TARGET_DIR")
.env_remove("CARGO_BUILD_TARGET_DIR")
.env_remove("CARGO_BUILD_BUILD_DIR")
.env_remove("CARGO_MAKEFLAGS")
.env_remove("MAKEFLAGS")
.stdin(Stdio::null())
.kill_on_drop(true);
let built = tokio::time::timeout(Duration::from_secs(90), compile.output())
.await
.expect("owned Cargo fixture timed out")
.expect("Cargo is required for the build-only artifact regression");
assert!(
built.status.success(),
"{command_text}: {}",
String::from_utf8_lossy(&built.stderr)
);
let executables: Vec<_> = String::from_utf8(built.stdout)
.unwrap()
.lines()
.filter_map(|line| serde_json::from_str::<serde_json::Value>(line).ok())
.filter(|message| message["reason"] == "compiler-artifact")
.filter_map(|message| message["executable"].as_str().map(std::path::PathBuf::from))
.collect();
assert_eq!(
executables.len(),
1,
"fixture must emit one runnable test/bench"
);
let (remote_basis, local_basis, patterns) = if forwarded {
assert!(
get_project_artifact_patterns(Some(kind), Some(&command_text), true).is_empty()
);
(
&remote_target,
&local_target,
get_custom_target_artifact_patterns(Some(kind), Some(&command_text)),
)
} else {
(
&source,
&local_project,
get_project_artifact_patterns(Some(kind), Some(&command_text), false),
)
};
std::fs::create_dir_all(local_basis).unwrap();
for executable in &executables {
let relative = executable.strip_prefix(remote_basis).unwrap();
let local = local_basis.join(relative);
std::fs::create_dir_all(local.parent().unwrap()).unwrap();
std::fs::write(&local, b"stale local test executable").unwrap();
}
let mut copy = Command::new("rsync");
copy.args([
"-a",
"--checksum",
"--no-owner",
"--no-group",
"--safe-links",
"--prune-empty-dirs",
]);
for rule in crate::transfer::priority_rsync_includes(&patterns) {
copy.arg(format!("--include={rule}"));
}
for pattern in &patterns {
if let Some(exclude) = pattern.strip_prefix("- ") {
copy.arg(format!("--exclude={exclude}"));
}
}
copy.arg("--include=*/");
for pattern in &patterns {
if !pattern.starts_with("- ") {
let pattern = pattern.strip_prefix("+ ").unwrap_or(pattern);
copy.arg(format!("--include=/{pattern}"));
}
}
copy.arg("--exclude=*")
.arg(format!("{}/", remote_basis.display()))
.arg(format!("{}/", local_basis.display()))
.stdin(Stdio::null())
.kill_on_drop(true);
let copied = tokio::time::timeout(Duration::from_secs(15), copy.output())
.await
.expect("owned rsync fixture timed out")
.expect("rsync is required for the build-only artifact regression");
assert!(copied.status.success(), "{copied:?}");
for executable in &executables {
let local = local_basis.join(executable.strip_prefix(remote_basis).unwrap());
assert!(
std::fs::read(&local).unwrap() == std::fs::read(executable).unwrap(),
"{command_text}: {} was not returned by patterns {patterns:?}",
executable.display()
);
assert_ne!(
std::fs::metadata(&local).unwrap().permissions().mode() & 0o111,
0
);
let mut list = Command::new(&local);
list.arg("--list")
.current_dir(&local_project)
.stdin(Stdio::null())
.kill_on_drop(true);
let listed = tokio::time::timeout(Duration::from_secs(10), list.output())
.await
.expect("returned executable timed out")
.unwrap();
assert!(
listed.status.success(),
"returned executable is not runnable: {listed:?}"
);
let expected = if kind == CompilationKind::CargoTest {
"build_only_test_must_not_execute"
} else {
"build_only_bench_must_not_execute"
};
assert!(String::from_utf8_lossy(&listed.stdout).contains(expected));
}
assert_eq!(
std::fs::read(&sentinel).unwrap(),
b"local source must not be overwritten\n"
);
if forwarded {
for cache in ["incremental", ".fingerprint"] {
assert!(!local_target.join("no-run-fixture").join(cache).exists());
}
let returned: Vec<_> = executables
.iter()
.map(|executable| {
local_basis.join(executable.strip_prefix(remote_basis).unwrap())
})
.collect();
let mut pending = vec![local_target.join("no-run-fixture").join("build")];
while let Some(directory) = pending.pop() {
let Ok(entries) = std::fs::read_dir(&directory) else {
continue;
};
for entry in entries {
let path = entry.unwrap().path();
if path.is_dir() {
pending.push(path);
} else {
assert!(
returned.contains(&path),
"build cache file came back: {}",
path.display()
);
}
}
}
}
}
}
#[cfg(unix)]
#[tokio::test]
async fn cargo_profile_artifacts_real_rsync_refreshes_custom_outputs_without_cache_or_source_overwrite()
{
use std::path::Path;
use std::process::Stdio;
use std::time::Duration;
use tokio::process::Command;
let root = tempfile::tempdir().unwrap().keep();
for (index, prefix) in ["release-perf", "aarch64-unknown-linux-gnu/release-perf"]
.iter()
.enumerate()
{
let source = root.join(format!("source-{index}"));
let destination = root.join(format!("destination-{index}"));
let relative = Path::new(prefix).join("application");
for base in [&source, &destination] {
std::fs::create_dir_all(base.join(prefix)).unwrap();
}
std::fs::create_dir_all(source.join("debug")).unwrap();
std::fs::write(
source.join("debug/application"),
b"older default-profile output\n",
)
.unwrap();
std::fs::write(
source.join(&relative),
b"fresh requested custom-profile output\n",
)
.unwrap();
std::fs::write(
destination.join(&relative),
b"stale local custom-profile output\n",
)
.unwrap();
std::fs::write(source.join("source.rs"), b"foreign source\n").unwrap();
std::fs::write(destination.join("source.rs"), b"local source sentinel\n").unwrap();
for cache in ["incremental", ".fingerprint", "build"] {
std::fs::create_dir_all(source.join(prefix).join(cache)).unwrap();
std::fs::write(
source.join(prefix).join(cache).join("cache"),
b"must stay remote\n",
)
.unwrap();
}
std::fs::write(
source.join(prefix).join("application.d"),
b"must stay remote\n",
)
.unwrap();
let patterns = get_custom_target_artifact_patterns(
Some(CompilationKind::CargoBuild),
Some("env -- /usr/bin/time -f cargo -- cargo build --profile release-perf"),
);
let mut command = Command::new("rsync");
command.args([
"-a",
"--checksum",
"--no-owner",
"--no-group",
"--safe-links",
"--prune-empty-dirs",
]);
for pattern in &patterns {
if let Some(exclude) = pattern.strip_prefix("- ") {
command.arg(format!("--exclude={exclude}"));
}
}
command.arg("--include=*/");
for pattern in &patterns {
if !pattern.starts_with("- ") {
command.arg(format!("--include=/{pattern}"));
}
}
command
.arg("--exclude=*")
.arg(format!("{}/", source.display()))
.arg(format!("{}/", destination.display()))
.stdin(Stdio::null())
.kill_on_drop(true);
let output = tokio::time::timeout(Duration::from_secs(10), command.output())
.await
.expect("owned local rsync fixture exceeded its deadline")
.expect("rsync must be installed to run the artifact-transfer regression");
assert!(output.status.success(), "{output:?}");
assert_eq!(
std::fs::read(destination.join(&relative)).unwrap(),
b"fresh requested custom-profile output\n",
"successful sync left the requested artifact stale for {prefix}"
);
assert_eq!(
std::fs::read(destination.join("source.rs")).unwrap(),
b"local source sentinel\n"
);
for cache in ["incremental", ".fingerprint", "build"] {
assert!(
!destination.join(prefix).join(cache).exists(),
"copied {prefix}/{cache}"
);
}
assert!(!destination.join(prefix).join("application.d").exists());
}
}
}