From 451159afd281c71f0fafa91b7eb2d33424bfec9b Mon Sep 17 00:00:00 2001 From: Tamir Duberstein Date: Wed, 30 Oct 2024 06:20:11 -0400 Subject: [PATCH] Always build eBPF; remove xtask --- .cargo/config.toml | 2 - Cargo.toml | 4 +- README.md | 11 +- test.sh | 4 +- xtask/Cargo.toml | 8 -- xtask/src/lib.rs | 1 - xtask/src/main.rs | 23 ---- xtask/src/run.rs | 47 ------- {{project-name}}-ebpf/Cargo.toml | 1 - {{project-name}}-ebpf/build.rs | 15 +-- {{project-name}}/Cargo.toml | 1 - {{project-name}}/build.rs | 204 +++++++++++++------------------ 12 files changed, 97 insertions(+), 224 deletions(-) delete mode 100644 .cargo/config.toml delete mode 100644 xtask/Cargo.toml delete mode 100644 xtask/src/lib.rs delete mode 100644 xtask/src/main.rs delete mode 100644 xtask/src/run.rs diff --git a/.cargo/config.toml b/.cargo/config.toml deleted file mode 100644 index 35049cb..0000000 --- a/.cargo/config.toml +++ /dev/null @@ -1,2 +0,0 @@ -[alias] -xtask = "run --package xtask --" diff --git a/Cargo.toml b/Cargo.toml index a833d12..b8ee240 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,7 +1,7 @@ [workspace] resolver = "2" -members = ["xtask", "{{project-name}}", "{{project-name}}-common", "{{project-name}}-ebpf"] -default-members = ["xtask", "{{project-name}}", "{{project-name}}-common"] +members = ["{{project-name}}", "{{project-name}}-common", "{{project-name}}-ebpf"] +default-members = ["{{project-name}}", "{{project-name}}-common"] [workspace.dependencies] aya = { version = "0.13.0", default-features = false } diff --git a/README.md b/README.md index 04946b9..d2bdda1 100644 --- a/README.md +++ b/README.md @@ -11,18 +11,21 @@ ## Build & Run -Use `cargo build`, `cargo check`, etc. as normal. Run your program with `xtask run`. +Use `cargo build`, `cargo check`, etc. as normal. Run your program with: + +```shell +cargo run --release --config 'target."cfg(all())".runner="sudo -E"' +``` Cargo build scripts are used to automatically build the eBPF correctly and include it in the -program. When not using `xtask run`, eBPF code generation is skipped for a faster developer -experience; this compromise necessitates the use of `xtask` to actually build the eBPF. +program. ## Cross-compiling on macOS Cross compilation should work on both Intel and Apple Silicon Macs. ```shell -AYA_BUILD_EBPF=true CC=${ARCH}-linux-musl-gcc cargo build --package {{project-name}} --release \ +CC=${ARCH}-linux-musl-gcc cargo build --package {{project-name}} --release \ --target=${ARCH}-unknown-linux-musl \ --config=target.${ARCH}-unknown-linux-musl.linker=\"${ARCH}-linux-musl-gcc\" ``` diff --git a/test.sh b/test.sh index e1015a6..1ef00e6 100755 --- a/test.sh +++ b/test.sh @@ -68,7 +68,7 @@ case $OS in if [[ "$ARCH" == "arm64" ]]; then ARCH="aarch64" fi - AYA_BUILD_EBPF=true CC=${ARCH}-linux-musl-gcc cargo build --package "${CRATE_NAME}" --release \ + CC=${ARCH}-linux-musl-gcc cargo build --package "${CRATE_NAME}" --release \ --target="${ARCH}"-unknown-linux-musl \ --config=target."${ARCH}"-unknown-linux-musl.linker=\""${ARCH}"-linux-musl-gcc\" ;; @@ -85,7 +85,7 @@ case $OS in expect < Result<()> { - let Options { command } = Parser::parse(); - - match command { - Command::Run(opts) => run::run(opts), - } -} diff --git a/xtask/src/run.rs b/xtask/src/run.rs deleted file mode 100644 index 16c80a8..0000000 --- a/xtask/src/run.rs +++ /dev/null @@ -1,47 +0,0 @@ -use std::{ffi::OsString, process::Command}; - -use anyhow::{bail, Context as _, Result}; -use clap::Parser; -use xtask::AYA_BUILD_EBPF; - -#[derive(Debug, Parser)] -pub struct Options { - /// Build and run the release target. - #[clap(long)] - release: bool, - /// The command used to wrap your application. - #[clap(short, long, default_value = "sudo -E")] - runner: String, - /// Arguments to pass to your application. - #[clap(global = true, last = true)] - run_args: Vec, -} - -/// Build and run the project. -pub fn run(opts: Options) -> Result<()> { - let Options { - release, - runner, - run_args, - } = opts; - - let mut cmd = Command::new("cargo"); - cmd.env(AYA_BUILD_EBPF, "true"); - cmd.args(["run", "--package", "{{project-name}}", "--config"]); - if release { - cmd.arg(format!("target.\"cfg(all())\".runner=\"{}\"", runner)); - cmd.arg("--release"); - } else { - cmd.arg(format!("target.\"cfg(all())\".runner=\"{}\"", runner)); - } - if !run_args.is_empty() { - cmd.arg("--").args(run_args); - } - let status = cmd - .status() - .with_context(|| format!("failed to run {cmd:?}"))?; - if status.code() != Some(0) { - bail!("{cmd:?} failed: {status:?}") - } - Ok(()) -} diff --git a/{{project-name}}-ebpf/Cargo.toml b/{{project-name}}-ebpf/Cargo.toml index 578c50e..8c4a0f3 100644 --- a/{{project-name}}-ebpf/Cargo.toml +++ b/{{project-name}}-ebpf/Cargo.toml @@ -11,7 +11,6 @@ aya-log-ebpf = { workspace = true } [build-dependencies] which = { workspace = true } -xtask = { path = "../xtask" } [[bin]] name = "{{ project-name }}" diff --git a/{{project-name}}-ebpf/build.rs b/{{project-name}}-ebpf/build.rs index 101ade2..0416db0 100644 --- a/{{project-name}}-ebpf/build.rs +++ b/{{project-name}}-ebpf/build.rs @@ -1,7 +1,6 @@ use std::env; use which::which; -use xtask::AYA_BUILD_EBPF; /// Building this crate has an undeclared dependency on the `bpf-linker` binary. This would be /// better expressed by [artifact-dependencies][bindeps] but issues such as @@ -15,16 +14,6 @@ use xtask::AYA_BUILD_EBPF; /// /// [bindeps]: https://doc.rust-lang.org/nightly/cargo/reference/unstable.html?highlight=feature#artifact-dependencies fn main() { - println!("cargo:rerun-if-env-changed={}", AYA_BUILD_EBPF); - - let build_ebpf = env::var(AYA_BUILD_EBPF) - .as_deref() - .map(str::parse) - .map(Result::unwrap) - .unwrap_or_default(); - - if build_ebpf { - let bpf_linker = which("bpf-linker").unwrap(); - println!("cargo:rerun-if-changed={}", bpf_linker.to_str().unwrap()); - } + let bpf_linker = which("bpf-linker").unwrap(); + println!("cargo:rerun-if-changed={}", bpf_linker.to_str().unwrap()); } diff --git a/{{project-name}}/Cargo.toml b/{{project-name}}/Cargo.toml index 0567166..8fe51d7 100644 --- a/{{project-name}}/Cargo.toml +++ b/{{project-name}}/Cargo.toml @@ -33,7 +33,6 @@ cargo_metadata = { workspace = true } # workflows with stable cargo; stable cargo outright refuses to load manifests that use unstable # features. {{project-name}}-ebpf = { path = "../{{project-name}}-ebpf" } -xtask = { path = "../xtask" } [[bin]] name = "{{project-name}}" diff --git a/{{project-name}}/build.rs b/{{project-name}}/build.rs index 61caab3..8464f60 100644 --- a/{{project-name}}/build.rs +++ b/{{project-name}}/build.rs @@ -8,7 +8,6 @@ use std::{ use cargo_metadata::{ Artifact, CompilerMessage, Message, Metadata, MetadataCommand, Package, Target, }; -use xtask::AYA_BUILD_EBPF; /// This crate has a runtime dependency on artifacts produced by the `{{project-name}}-ebpf` crate. /// This would be better expressed as one or more [artifact-dependencies][bindeps] but issues such @@ -20,32 +19,8 @@ use xtask::AYA_BUILD_EBPF; /// /// prevent their use for the time being. /// -/// This file, along with the xtask crate, allows analysis tools such as `cargo check`, `cargo -/// clippy`, and even `cargo build` to work as users expect. Prior to this file's existence, this -/// crate's undeclared dependency on artifacts from `{{project-name}}-ebpf` would cause build (and -/// `cargo check`, and `cargo clippy`) failures until the user ran certain other commands in the -/// workspace. Conversely, those same tools (e.g. cargo test --no-run) would produce stale results -/// if run naively because they'd make use of artifacts from a previous build of -/// `{{project-name}}-ebpf`. -/// -/// Note that this solution is imperfect: in particular it has to balance correctness with -/// performance; an environment variable is used to replace true builds of `{{project-name}}-ebpf` -/// with stubs to preserve the property that code generation and linking (in -/// `{{project-name}}-ebpf`) do not occur on metadata-only actions such as `cargo check` or `cargo -/// clippy` of this crate. This means that naively attempting to `cargo test --no-run` this crate -/// will produce binaries that fail at runtime because the stubs are inadequate for actually running -/// the tests. -/// /// [bindeps]: https://doc.rust-lang.org/nightly/cargo/reference/unstable.html?highlight=feature#artifact-dependencies fn main() { - println!("cargo:rerun-if-env-changed={}", AYA_BUILD_EBPF); - - let build_ebpf = env::var(AYA_BUILD_EBPF) - .as_deref() - .map(str::parse) - .map(Result::unwrap) - .unwrap_or_default(); - let Metadata { packages, .. } = MetadataCommand::new().no_deps().exec().unwrap(); let ebpf_package = packages .into_iter() @@ -64,109 +39,98 @@ fn main() { panic!("unsupported endian={:?}", endian) }; - if build_ebpf { - let arch = env::var_os("CARGO_CFG_TARGET_ARCH").unwrap(); - - let target = format!("{target}-unknown-none"); - - let Package { manifest_path, .. } = ebpf_package; - let ebpf_dir = manifest_path.parent().unwrap(); - - // We have a build-dependency on `{{project-name}}-ebpf`, so cargo will automatically rebuild us - // if `{{project-name}}-ebpf`'s *library* target or any of its dependencies change. Since we - // depend on `{{project-name}}-ebpf`'s *binary* targets, that only gets us half of the way. This - // stanza ensures cargo will rebuild us on changes to the binaries too, which gets us the - // rest of the way. - println!("cargo:rerun-if-changed={}", ebpf_dir.as_str()); - - let mut cmd = Command::new("cargo"); - cmd.args([ - "build", - "-Z", - "build-std=core", - "--bins", - "--message-format=json", - "--release", - "--target", - &target, - ]); - - cmd.env("CARGO_CFG_BPF_TARGET_ARCH", arch); - - // Workaround to make sure that the rust-toolchain.toml is respected. - for key in ["RUSTUP_TOOLCHAIN", "RUSTC"] { - cmd.env_remove(key); + let arch = env::var_os("CARGO_CFG_TARGET_ARCH").unwrap(); + + let target = format!("{target}-unknown-none"); + + let Package { manifest_path, .. } = ebpf_package; + let ebpf_dir = manifest_path.parent().unwrap(); + + // We have a build-dependency on `{{project-name}}-ebpf`, so cargo will automatically rebuild us + // if `{{project-name}}-ebpf`'s *library* target or any of its dependencies change. Since we + // depend on `{{project-name}}-ebpf`'s *binary* targets, that only gets us half of the way. This + // stanza ensures cargo will rebuild us on changes to the binaries too, which gets us the + // rest of the way. + println!("cargo:rerun-if-changed={}", ebpf_dir.as_str()); + + let mut cmd = Command::new("cargo"); + cmd.args([ + "build", + "-Z", + "build-std=core", + "--bins", + "--message-format=json", + "--release", + "--target", + &target, + ]); + + cmd.env("CARGO_CFG_BPF_TARGET_ARCH", arch); + + // Workaround to make sure that the rust-toolchain.toml is respected. + for key in ["RUSTUP_TOOLCHAIN", "RUSTC"] { + cmd.env_remove(key); + } + cmd.current_dir(ebpf_dir); + + // Workaround for https://github.com/rust-lang/cargo/issues/6412 where cargo flocks itself. + let ebpf_target_dir = out_dir.join("{{project-name}}-ebpf"); + cmd.arg("--target-dir").arg(&ebpf_target_dir); + + let mut child = cmd + .stdout(Stdio::piped()) + .stderr(Stdio::piped()) + .spawn() + .unwrap_or_else(|err| panic!("failed to spawn {cmd:?}: {err}")); + let Child { stdout, stderr, .. } = &mut child; + + // Trampoline stdout to cargo warnings. + let stderr = stderr.take().unwrap(); + let stderr = BufReader::new(stderr); + let stderr = std::thread::spawn(move || { + for line in stderr.lines() { + let line = line.unwrap(); + println!("cargo:warning={line}"); } - cmd.current_dir(ebpf_dir); - - // Workaround for https://github.com/rust-lang/cargo/issues/6412 where cargo flocks itself. - let ebpf_target_dir = out_dir.join("{{project-name}}-ebpf"); - cmd.arg("--target-dir").arg(&ebpf_target_dir); - - let mut child = cmd - .stdout(Stdio::piped()) - .stderr(Stdio::piped()) - .spawn() - .unwrap_or_else(|err| panic!("failed to spawn {cmd:?}: {err}")); - let Child { stdout, stderr, .. } = &mut child; - - // Trampoline stdout to cargo warnings. - let stderr = stderr.take().unwrap(); - let stderr = BufReader::new(stderr); - let stderr = std::thread::spawn(move || { - for line in stderr.lines() { - let line = line.unwrap(); - println!("cargo:warning={line}"); - } - }); - - let stdout = stdout.take().unwrap(); - let stdout = BufReader::new(stdout); - let mut executables = Vec::new(); - for message in Message::parse_stream(stdout) { - #[allow(clippy::collapsible_match)] - match message.expect("valid JSON") { - Message::CompilerArtifact(Artifact { - executable, - target: Target { name, .. }, - .. - }) => { - if let Some(executable) = executable { - executables.push((name, executable.into_std_path_buf())); - } - } - Message::CompilerMessage(CompilerMessage { message, .. }) => { - for line in message.rendered.unwrap_or_default().split('\n') { - println!("cargo:warning={line}"); - } + }); + + let stdout = stdout.take().unwrap(); + let stdout = BufReader::new(stdout); + let mut executables = Vec::new(); + for message in Message::parse_stream(stdout) { + #[allow(clippy::collapsible_match)] + match message.expect("valid JSON") { + Message::CompilerArtifact(Artifact { + executable, + target: Target { name, .. }, + .. + }) => { + if let Some(executable) = executable { + executables.push((name, executable.into_std_path_buf())); } - Message::TextLine(line) => { + } + Message::CompilerMessage(CompilerMessage { message, .. }) => { + for line in message.rendered.unwrap_or_default().split('\n') { println!("cargo:warning={line}"); } - _ => {} } + Message::TextLine(line) => { + println!("cargo:warning={line}"); + } + _ => {} } + } - let status = child - .wait() - .unwrap_or_else(|err| panic!("failed to wait for {cmd:?}: {err}")); - assert_eq!(status.code(), Some(0), "{cmd:?} failed: {status:?}"); + let status = child + .wait() + .unwrap_or_else(|err| panic!("failed to wait for {cmd:?}: {err}")); + assert_eq!(status.code(), Some(0), "{cmd:?} failed: {status:?}"); - stderr.join().map_err(std::panic::resume_unwind).unwrap(); + stderr.join().map_err(std::panic::resume_unwind).unwrap(); - for (name, binary) in executables { - let dst = out_dir.join(name); - let _: u64 = fs::copy(&binary, &dst) - .unwrap_or_else(|err| panic!("failed to copy {binary:?} to {dst:?}: {err}")); - } - } else { - let Package { targets, .. } = ebpf_package; - for Target { name, kind, .. } in targets { - if *kind != ["bin"] { - continue; - } - let dst = out_dir.join(name); - fs::write(&dst, []).unwrap_or_else(|err| panic!("failed to create {dst:?}: {err}")); - } + for (name, binary) in executables { + let dst = out_dir.join(name); + let _: u64 = fs::copy(&binary, &dst) + .unwrap_or_else(|err| panic!("failed to copy {binary:?} to {dst:?}: {err}")); } }