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
8 changes: 5 additions & 3 deletions Cargo.lock

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

19 changes: 15 additions & 4 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -71,15 +71,15 @@ diff-struct = "0.5.3"
directories = "6.0.0"
elf = { version = "0.8.0", default-features = false }
materialized_artifact = { path = "crates/materialized_artifact" }
materialized_artifact_build = { path = "crates/materialized_artifact_build" }
materialized_artifact_macros = { path = "crates/materialized_artifact_macros" }
flate2 = "1.0.35"
fspy = { path = "crates/fspy" }
fspy_benchmark_launcher = { path = "crates/fspy_benchmark_launcher", artifact = "bin" }
fspy_benchmark_target = { path = "crates/fspy_benchmark_target", artifact = "bin" }
fspy_client_unix = { path = "crates/fspy_client_unix" }
fspy_detours_sys = { path = "crates/fspy_detours_sys" }
fspy_preload_unix = { path = "crates/fspy_preload_unix", artifact = "cdylib", target = "target" }
fspy_preload_windows = { path = "crates/fspy_preload_windows", artifact = "cdylib", target = "target" }
fspy_preload_unix = { path = "crates/fspy_preload_unix", artifact = "cdylib" }
fspy_preload_windows = { path = "crates/fspy_preload_windows", artifact = "cdylib" }
fspy_seccomp_unotify = { path = "crates/fspy_seccomp_unotify" }
fspy_shm = { path = "crates/fspy_shm" }
fspy_shared = { path = "crates/fspy_shared" }
Expand Down Expand Up @@ -113,9 +113,11 @@ petgraph = "0.8.2"
phf = { version = "0.13.0", features = ["macros"] }
portable-pty = "0.9.0"
pretty_assertions = "1.4.1"
proc-macro2 = "1"
pty_terminal = { path = "crates/pty_terminal" }
pty_terminal_test = { path = "crates/pty_terminal_test" }
pty_terminal_test_client = { path = "crates/pty_terminal_test_client" }
quote = "1"
ratatui = "0.30.0"
rayon = "1.10.0"
ref-cast = "1.0.24"
Expand All @@ -139,6 +141,7 @@ socket_ipc = { path = "crates/socket_ipc" }
stackalloc = "1.2.1"
subprocess_test = { path = "crates/subprocess_test" }
supports-color = "3.0.1"
syn = "2"
syscalls = { version = "0.8.0", default-features = false }
tar = "0.4.45"
tempfile = "3.14.0"
Expand All @@ -165,7 +168,7 @@ vt_str = { path = "crates/vt_str" }
vt = { path = "crates/vt" }
vt_bin = { path = "crates/vt_bin" }
vt_client = { path = "crates/vt_client" }
vt_client_napi = { path = "crates/vt_client_napi", artifact = "cdylib", target = "target" }
vt_client_napi = { path = "crates/vt_client_napi", artifact = "cdylib" }
vt_graph = { path = "crates/vt_graph" }
vt_ipc_shared = { path = "crates/vt_ipc_shared" }
vt_plan = { path = "crates/vt_plan" }
Expand Down Expand Up @@ -208,3 +211,11 @@ codegen-units = 1
strip = "symbols" # set to `false` for debug information
debug = false # set to `true` for debug information
panic = "abort" # Let it crash and force ourselves to write safe Rust.

# The workspace's artifact dependencies (the preload libraries and
# `vt_client_napi`) are declared under `[dependencies]`, so they get the
# normal profile — including this release profile — with no per-package
# overrides. Keeping them out of `[build-dependencies]` matters: there Cargo
# would compile them with the build-override profile (opt-level 0) even in
# release builds, in this workspace and in every downstream workspace. See
# https://github.com/rust-lang/cargo/issues/16719.
23 changes: 15 additions & 8 deletions crates/fspy/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -31,8 +31,23 @@ tokio = { workspace = true, features = ["bytes"] }
fspy_shared_unix = { workspace = true }
nix = { workspace = true, features = ["fs", "process", "socket", "feature"] }

# The preload artifact deps live under `[dependencies]` — not
# `[build-dependencies]`, where Cargo would compile them with the
# build-override profile (opt-level 0 in release; see
# https://github.com/rust-lang/cargo/issues/16719) — so they get the normal
# profile, in this workspace and downstream ones, with no per-package profile
# overrides. Cargo provides `CARGO_CDYLIB_FILE_*` while compiling this crate
# for `artifact!` to embed. Each dep is scoped to the targets whose code
# embeds it, so other targets (e.g. musl, which never dynamically links a
# preload) don't build a useless empty cdylib. Scoping artifact deps under
# `[target.cfg…]` is only safe for normal deps: the same shape under
# `[target.cfg….build-dependencies]` panics cargo's resolver on cross-compile.
[target.'cfg(all(unix, not(target_env = "musl")))'.dependencies]
fspy_preload_unix = { workspace = true }

[target.'cfg(target_os = "windows")'.dependencies]
fspy_detours_sys = { workspace = true }
fspy_preload_windows = { workspace = true }
winapi = { workspace = true, features = ["winbase", "securitybaseapi", "handleapi"] }
winsafe = { workspace = true }

Expand All @@ -54,17 +69,9 @@ fspy_test_bin = { path = "../fspy_test_bin", artifact = "bin", target = "aarch64
[target.'cfg(all(target_os = "linux", target_arch = "x86_64"))'.dev-dependencies]
fspy_test_bin = { path = "../fspy_test_bin", artifact = "bin", target = "x86_64-unknown-linux-musl" }

# Artifact build-deps must be unconditional: cargo's resolver panics when
# `artifact = "cdylib"` deps live under a `[target.cfg.build-dependencies]`
# block on cross-compile. Each preload crate's source is cfg-gated to compile
# as an empty cdylib on non-applicable targets, so the unused cross-target
# builds are cheap.
[build-dependencies]
anyhow = { workspace = true }
materialized_artifact_build = { workspace = true }
flate2 = { workspace = true }
fspy_preload_unix = { workspace = true }
fspy_preload_windows = { workspace = true }
sha2 = { workspace = true }
tar = { workspace = true }

Expand Down
33 changes: 14 additions & 19 deletions crates/fspy/build.rs
Original file line number Diff line number Diff line change
Expand Up @@ -53,9 +53,11 @@ fn sha256_hex(bytes: &[u8]) -> String {
}

struct BinaryDownload {
/// Identifier used both as the on-disk filename in `OUT_DIR` and as the
/// env-var prefix consumed by `artifact!($name)` at runtime.
/// On-disk filename in `OUT_DIR`.
name: &'static str,
/// Env var published via `cargo:rustc-env`, through which
/// `materialized_artifact::artifact!` finds and embeds the binary.
env_var: &'static str,
/// GitHub release asset URL.
url: &'static str,
/// Path of the binary within the tarball.
Expand All @@ -73,13 +75,15 @@ const MACOS_BINARY_DOWNLOADS: &[(&str, &[BinaryDownload])] = &[
// https://github.com/wan9chi/oils-for-unix-build/releases/tag/oils-for-unix-0.37.0
BinaryDownload {
name: "oils_for_unix",
env_var: "FSPY_MACOS_ARTIFACT_OILS_FOR_UNIX",
url: "https://github.com/wan9chi/oils-for-unix-build/releases/download/oils-for-unix-0.37.0/oils-for-unix-0.37.0-darwin-arm64.tar.gz",
path_in_targz: "oils-for-unix",
expected_sha256: "ce4bb80b15f0a0371af08b19b65bfa5ea17d30429ebb911f487de3d2bcc7a07d",
},
// https://github.com/uutils/coreutils/releases/tag/0.4.0
BinaryDownload {
name: "coreutils",
env_var: "FSPY_MACOS_ARTIFACT_COREUTILS",
url: "https://github.com/uutils/coreutils/releases/download/0.4.0/coreutils-0.4.0-aarch64-apple-darwin.tar.gz",
path_in_targz: "coreutils-0.4.0-aarch64-apple-darwin/coreutils",
expected_sha256: "8e8f38d9323135a19a73d617336fce85380f3c46fcb83d3ae3e031d1c0372f21",
Expand All @@ -92,13 +96,15 @@ const MACOS_BINARY_DOWNLOADS: &[(&str, &[BinaryDownload])] = &[
// https://github.com/wan9chi/oils-for-unix-build/releases/tag/oils-for-unix-0.37.0
BinaryDownload {
name: "oils_for_unix",
env_var: "FSPY_MACOS_ARTIFACT_OILS_FOR_UNIX",
url: "https://github.com/wan9chi/oils-for-unix-build/releases/download/oils-for-unix-0.37.0/oils-for-unix-0.37.0-darwin-x86_64.tar.gz",
path_in_targz: "oils-for-unix",
expected_sha256: "cf1a95993127770e2a5fff277cd256a2bb28cf97d7f83ae42fdccc172cdb540d",
},
// https://github.com/uutils/coreutils/releases/tag/0.4.0
BinaryDownload {
name: "coreutils",
env_var: "FSPY_MACOS_ARTIFACT_COREUTILS",
url: "https://github.com/uutils/coreutils/releases/download/0.4.0/coreutils-0.4.0-x86_64-apple-darwin.tar.gz",
path_in_targz: "coreutils-0.4.0-x86_64-apple-darwin/coreutils",
expected_sha256: "6be8bee6e8b91fc44a465203b9cc30538af00084b6657dc136d9e55837753eb1",
Expand All @@ -119,8 +125,12 @@ fn fetch_macos_binaries(out_dir: &Path) -> anyhow::Result<()> {
.context(format!("Unsupported macOS arch: {target_arch}"))?
.1;

for BinaryDownload { name, url, path_in_targz, expected_sha256 } in downloads {
for BinaryDownload { name, env_var, url, path_in_targz, expected_sha256 } in downloads {
let dest = out_dir.join(name);
let dest_str = dest.to_str().expect("OUT_DIR path must be valid UTF-8");
// Emit rerun-if-changed before fetching so cargo still sees it even
// if the download or write below fails.
println!("cargo:rerun-if-changed={dest_str}");
// Cache hit: an already-extracted binary whose contents hash to
// `expected_sha256` is known-good and reused without redownloading.
let cached = matches!(
Expand All @@ -138,29 +148,14 @@ fn fetch_macos_binaries(out_dir: &Path) -> anyhow::Result<()> {
);
fs::write(&dest, &data).with_context(|| format!("writing {}", dest.display()))?;
}
materialized_artifact_build::register(name, &dest);
println!("cargo:rustc-env={env_var}={dest_str}");
}
Ok(())
}

fn register_preload_cdylib() -> anyhow::Result<()> {
let env_name = match env::var("CARGO_CFG_TARGET_OS").unwrap().as_str() {
"windows" => "CARGO_CDYLIB_FILE_FSPY_PRELOAD_WINDOWS",
_ if env::var("CARGO_CFG_TARGET_ENV").unwrap() == "musl" => return Ok(()),
_ => "CARGO_CDYLIB_FILE_FSPY_PRELOAD_UNIX",
};
// The cdylib path is content-addressed by cargo; when its content changes
// the path changes. Track it so we re-publish the hash on update.
println!("cargo:rerun-if-env-changed={env_name}");
let dylib_path = env::var_os(env_name).with_context(|| format!("{env_name} not set"))?;
materialized_artifact_build::register("fspy_preload", Path::new(&dylib_path));
Ok(())
}

fn main() -> anyhow::Result<()> {
println!("cargo:rerun-if-changed=build.rs");
let out_dir = PathBuf::from(env::var_os("OUT_DIR").unwrap());
fetch_macos_binaries(&out_dir).context("Failed to fetch macOS binaries")?;
register_preload_cdylib().context("Failed to register preload cdylib")?;
Ok(())
}
4 changes: 2 additions & 2 deletions crates/fspy/src/unix/macos_artifacts.rs
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
use materialized_artifact::{Artifact, artifact};

pub const COREUTILS_BINARY: Artifact = artifact!("coreutils");
pub const OILS_BINARY: Artifact = artifact!("oils_for_unix");
pub const COREUTILS_BINARY: Artifact = artifact!("coreutils", "FSPY_MACOS_ARTIFACT_COREUTILS");
pub const OILS_BINARY: Artifact = artifact!("oils_for_unix", "FSPY_MACOS_ARTIFACT_OILS_FOR_UNIX");

#[cfg(test)]
mod tests {
Expand Down
3 changes: 2 additions & 1 deletion crates/fspy/src/unix/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -47,7 +47,8 @@ impl SpyImpl {
let preload_path = {
use materialized_artifact::{Artifact, artifact};

const PRELOAD_CDYLIB: Artifact = artifact!("fspy_preload");
const PRELOAD_CDYLIB: Artifact =
artifact!("fspy_preload", "CARGO_CDYLIB_FILE_FSPY_PRELOAD_UNIX");

let preload_cdylib_path = PRELOAD_CDYLIB.materialize().suffix(".dylib").at(dir)?;
preload_cdylib_path.as_path().into()
Expand Down
3 changes: 2 additions & 1 deletion crates/fspy/src/windows/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,8 @@ use crate::{
ipc::{OwnedReceiverLockGuard, SHM_CAPACITY},
};

const INTERPOSE_CDYLIB: Artifact = artifact!("fspy_preload");
const INTERPOSE_CDYLIB: Artifact =
artifact!("fspy_preload", "CARGO_CDYLIB_FILE_FSPY_PRELOAD_WINDOWS");

pub struct PathAccessIterable {
ipc_receiver_lock_guard: OwnedReceiverLockGuard,
Expand Down
1 change: 1 addition & 0 deletions crates/materialized_artifact/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ publish = false
rust-version.workspace = true

[dependencies]
materialized_artifact_macros = { workspace = true }
tempfile = { workspace = true }

[lints]
Expand Down
24 changes: 5 additions & 19 deletions crates/materialized_artifact/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -9,9 +9,8 @@
//! the value-add over a bare `include_bytes!`.
//!
//! Materialized files are named `{name}_{hash}{suffix}` in the caller-chosen
//! directory. The hash (computed at build time by
//! `materialized_artifact_build::register`) gives three properties without
//! any coordination between processes:
//! directory. The hash (computed at macro-expansion time by [`artifact!`])
//! gives three properties without any coordination between processes:
//!
//! - **No repeated writes.** [`Materialize::at`] returns the existing path if
//! the file is already there; repeated calls and re-runs skip I/O.
Expand Down Expand Up @@ -42,22 +41,9 @@ pub struct Artifact {
hash: &'static str,
}

/// Construct an [`Artifact`] from the env vars published by a build script
/// via `materialized_artifact_build::register`.
#[macro_export]
macro_rules! artifact {
($name:literal) => {
$crate::Artifact::__new(
$name,
::core::include_bytes!(::core::env!(::core::concat!(
"MATERIALIZED_ARTIFACT_",
$name,
"_PATH"
))),
::core::env!(::core::concat!("MATERIALIZED_ARTIFACT_", $name, "_HASH")),
)
};
}
/// Construct an [`Artifact`] from an env var holding a file path at compile
/// time — see the macro's own docs for usage and design notes.
pub use materialized_artifact_macros::artifact;

impl Artifact {
#[doc(hidden)]
Expand Down
4 changes: 0 additions & 4 deletions crates/materialized_artifact_build/README.md

This file was deleted.

37 changes: 0 additions & 37 deletions crates/materialized_artifact_build/src/lib.rs

This file was deleted.

Original file line number Diff line number Diff line change
@@ -1,17 +1,21 @@
[package]
name = "materialized_artifact_build"
name = "materialized_artifact_macros"
version = "0.0.0"
edition.workspace = true
license.workspace = true
publish = false
rust-version.workspace = true

[lib]
proc-macro = true
doctest = false
test = false

[dependencies]
proc-macro2 = { workspace = true }
quote = { workspace = true }
syn = { workspace = true }
xxhash-rust = { workspace = true, features = ["xxh3"] }

[lints]
workspace = true

[lib]
doctest = false
test = false
Loading
Loading