xtask: Standardize command logging

Don't run `cargo build --verbose`; it's too noisy.
pull/641/head
Tamir Duberstein 1 year ago
parent b611038d5b
commit b0a4ab5f20
No known key found for this signature in database

@ -1,4 +1,4 @@
use anyhow::{Context, Result}; use anyhow::{bail, Context as _, Result};
use std::{path::PathBuf, process::Command, thread::sleep, time::Duration}; use std::{path::PathBuf, process::Command, thread::sleep, time::Duration};
use tempfile::TempDir; use tempfile::TempDir;
@ -266,15 +266,20 @@ impl RelocationTest {
"# "#
)) ))
.context("Failed to compile BTF")?; .context("Failed to compile BTF")?;
Command::new("llvm-objcopy") let mut cmd = Command::new("llvm-objcopy");
.current_dir(tmp_dir.path()) cmd.current_dir(tmp_dir.path())
.args(["--dump-section", ".BTF=target.btf"]) .args(["--dump-section", ".BTF=target.btf"])
.arg(compiled_file) .arg(compiled_file);
let status = cmd
.status() .status()
.context("Failed to run llvm-objcopy")? .with_context(|| format!("Failed to run {cmd:?}"))?;
.success() match status.code() {
.then_some(()) Some(code) => match code {
.context("Failed to extract BTF")?; 0 => {}
code => bail!("{cmd:?} exited with code {code}"),
},
None => bail!("{cmd:?} terminated by signal"),
}
let btf = Btf::parse_file(tmp_dir.path().join("target.btf"), Endianness::default()) let btf = Btf::parse_file(tmp_dir.path().join("target.btf"), Endianness::default())
.context("Error parsing generated BTF")?; .context("Error parsing generated BTF")?;
Ok(btf) Ok(btf)
@ -287,15 +292,20 @@ fn compile(source_code: &str) -> Result<(TempDir, PathBuf)> {
let tmp_dir = tempfile::tempdir().context("Error making temp dir")?; let tmp_dir = tempfile::tempdir().context("Error making temp dir")?;
let source = tmp_dir.path().join("source.c"); let source = tmp_dir.path().join("source.c");
std::fs::write(&source, source_code).context("Writing bpf program failed")?; std::fs::write(&source, source_code).context("Writing bpf program failed")?;
Command::new("clang") let mut cmd = Command::new("clang");
.current_dir(&tmp_dir) cmd.current_dir(&tmp_dir)
.args(["-c", "-g", "-O2", "-target", "bpf"]) .args(["-c", "-g", "-O2", "-target", "bpf"])
.arg(&source) .arg(&source);
let status = cmd
.status() .status()
.context("Failed to run clang")? .with_context(|| format!("Failed to run {cmd:?}"))?;
.success() match status.code() {
.then_some(()) Some(code) => match code {
.context("Failed to compile eBPF source")?; 0 => {}
code => bail!("{cmd:?} exited with code {code}"),
},
None => bail!("{cmd:?} terminated by signal"),
}
Ok((tmp_dir, source.with_extension("o"))) Ok((tmp_dir, source.with_extension("o")))
} }

@ -12,5 +12,4 @@ clap = { version = "4", features = ["derive"] }
indoc = "2.0" indoc = "2.0"
proc-macro2 = "1" proc-macro2 = "1"
quote = "1" quote = "1"
serde_json = "1"
syn = "2" syn = "2"

@ -4,13 +4,13 @@ use std::{
ffi::{OsStr, OsString}, ffi::{OsStr, OsString},
fs, fs,
path::{Path, PathBuf}, path::{Path, PathBuf},
process::{Command, Output}, process::Command,
}; };
use anyhow::{bail, Context}; use anyhow::Result;
use clap::Parser; use clap::Parser;
use crate::utils::workspace_root; use crate::utils::{exec, workspace_root};
#[derive(Debug, Copy, Clone)] #[derive(Debug, Copy, Clone)]
pub enum Architecture { pub enum Architecture {
@ -49,61 +49,56 @@ pub struct BuildEbpfOptions {
pub libbpf_dir: PathBuf, pub libbpf_dir: PathBuf,
} }
pub fn build_ebpf(opts: BuildEbpfOptions) -> anyhow::Result<()> { pub fn build_ebpf(opts: BuildEbpfOptions) -> Result<()> {
build_rust_ebpf(&opts)?; build_rust_ebpf(&opts)?;
build_c_ebpf(&opts) build_c_ebpf(&opts)
} }
fn build_rust_ebpf(opts: &BuildEbpfOptions) -> anyhow::Result<()> { fn build_rust_ebpf(opts: &BuildEbpfOptions) -> Result<()> {
let BuildEbpfOptions {
target,
libbpf_dir: _,
} = opts;
let mut dir = PathBuf::from(workspace_root()); let mut dir = PathBuf::from(workspace_root());
dir.push("test/integration-ebpf"); dir.push("test/integration-ebpf");
let target = format!("--target={}", opts.target); exec(
let args = vec![ Command::new("cargo")
"+nightly", .current_dir(&dir)
"build", .args(["+nightly", "build", "--release", "--target"])
"--release", .arg(target.to_string())
"--verbose", .args(["-Z", "build-std=core"])
target.as_str(), .current_dir(&dir),
"-Z", )
"build-std=core",
];
let status = Command::new("cargo")
.current_dir(&dir)
.args(&args)
.status()
.expect("failed to build bpf program");
assert!(status.success());
Ok(())
} }
fn get_libbpf_headers<P: AsRef<Path>>(libbpf_dir: P, include_path: P) -> anyhow::Result<()> { fn get_libbpf_headers(libbpf_dir: &Path, include_path: &Path) -> Result<()> {
let dir = include_path.as_ref(); fs::create_dir_all(include_path)?;
fs::create_dir_all(dir)?;
let mut includedir = OsString::new(); let mut includedir = OsString::new();
includedir.push("INCLUDEDIR="); includedir.push("INCLUDEDIR=");
includedir.push(dir.as_os_str()); includedir.push(include_path);
let status = Command::new("make") exec(
.current_dir(libbpf_dir.as_ref().join("src")) Command::new("make")
.arg(includedir) .current_dir(libbpf_dir.join("src"))
.arg("install_headers") .arg(includedir)
.status() .arg("install_headers"),
.expect("failed to build get libbpf headers"); )
assert!(status.success());
Ok(())
} }
fn build_c_ebpf(opts: &BuildEbpfOptions) -> anyhow::Result<()> { fn build_c_ebpf(opts: &BuildEbpfOptions) -> Result<()> {
let BuildEbpfOptions { target, libbpf_dir } = opts;
let mut src = PathBuf::from(workspace_root()); let mut src = PathBuf::from(workspace_root());
src.push("test/integration-ebpf/src/bpf"); src.push("test/integration-ebpf/src/bpf");
let mut out_path = PathBuf::from(workspace_root()); let mut out_path = PathBuf::from(workspace_root());
out_path.push("target"); out_path.push("target");
out_path.push(opts.target.to_string()); out_path.push(target.to_string());
out_path.push("release"); out_path.push("release");
let include_path = out_path.join("include"); let include_path = out_path.join("include");
get_libbpf_headers(&opts.libbpf_dir, &include_path)?; get_libbpf_headers(libbpf_dir, &include_path)?;
let files = fs::read_dir(&src).unwrap(); let files = fs::read_dir(&src).unwrap();
for file in files { for file in files {
let p = file.unwrap().path(); let p = file.unwrap().path();
@ -120,11 +115,7 @@ fn build_c_ebpf(opts: &BuildEbpfOptions) -> anyhow::Result<()> {
} }
/// Build eBPF programs with clang and libbpf headers. /// Build eBPF programs with clang and libbpf headers.
fn compile_with_clang<P: Clone + AsRef<Path>>( fn compile_with_clang(src: &Path, out: &Path, include_path: &Path) -> Result<()> {
src: P,
out: P,
include_path: P,
) -> anyhow::Result<()> {
let clang: Cow<'_, _> = match env::var_os("CLANG") { let clang: Cow<'_, _> = match env::var_os("CLANG") {
Some(val) => val.into(), Some(val) => val.into(),
None => OsStr::new("/usr/bin/clang").into(), None => OsStr::new("/usr/bin/clang").into(),
@ -134,36 +125,14 @@ fn compile_with_clang<P: Clone + AsRef<Path>>(
"aarch64" => "arm64", "aarch64" => "arm64",
arch => arch, arch => arch,
}; };
let mut cmd = Command::new(clang); exec(
cmd.arg("-v") Command::new(clang)
.arg("-I") .arg("-I")
.arg(include_path.as_ref()) .arg(include_path)
.arg("-g") .args(["-g", "-O2", "-target", "bpf", "-c"])
.arg("-O2") .arg(format!("-D__TARGET_ARCH_{arch}"))
.arg("-target") .arg(src)
.arg("bpf") .arg("-o")
.arg("-c") .arg(out),
.arg(format!("-D__TARGET_ARCH_{arch}")) )
.arg(src.as_ref().as_os_str())
.arg("-o")
.arg(out.as_ref().as_os_str());
let Output {
status,
stdout,
stderr,
} = cmd.output().context("Failed to execute clang")?;
if !status.success() {
bail!(
"Failed to compile eBPF programs\n \
stdout=\n \
{}\n \
stderr=\n \
{}\n",
String::from_utf8(stdout).unwrap(),
String::from_utf8(stderr).unwrap()
);
}
Ok(())
} }

@ -1,7 +1,8 @@
use anyhow::Result;
use clap::Parser; use clap::Parser;
use std::process::Command; use std::process::Command;
use crate::build_ebpf; use crate::{build_ebpf, utils::exec};
#[derive(Parser)] #[derive(Parser)]
pub struct Options { pub struct Options {
@ -13,20 +14,19 @@ pub struct Options {
pub ebpf_options: build_ebpf::BuildEbpfOptions, pub ebpf_options: build_ebpf::BuildEbpfOptions,
} }
pub fn build_test(opts: Options) -> anyhow::Result<()> { pub fn build_test(opts: Options) -> Result<()> {
build_ebpf::build_ebpf(opts.ebpf_options)?; let Options {
musl_target,
ebpf_options,
} = opts;
let mut args = ["build", "-p", "integration-test", "--verbose"] build_ebpf::build_ebpf(ebpf_options)?;
.iter()
.map(|s| s.to_string()) let mut cmd = Command::new("cargo");
.collect::<Vec<_>>(); cmd.args(["build", "-p", "integration-test"]);
if let Some(target) = opts.musl_target {
args.push(format!("--target={target}")); if let Some(target) = musl_target {
cmd.args(["--target", &target]);
} }
let status = Command::new("cargo") exec(&mut cmd)
.args(&args)
.status()
.expect("failed to build bpf program");
assert!(status.success());
Ok(())
} }

@ -1,3 +1,5 @@
use crate::utils::exec;
use anyhow::{Context as _, Result};
use std::{ use std::{
path::{Path, PathBuf}, path::{Path, PathBuf},
process::Command, process::Command,
@ -7,7 +9,7 @@ use std::{fs, io, io::Write};
use indoc::indoc; use indoc::indoc;
pub fn docs() -> Result<(), anyhow::Error> { pub fn docs() -> Result<()> {
let current_dir = PathBuf::from("."); let current_dir = PathBuf::from(".");
let header_path = current_dir.join("header.html"); let header_path = current_dir.join("header.html");
let mut header = fs::File::create(&header_path).expect("can't create header.html"); let mut header = fs::File::create(&header_path).expect("can't create header.html");
@ -19,8 +21,11 @@ pub fn docs() -> Result<(), anyhow::Error> {
build_docs(&current_dir.join("aya"), &abs_header_path)?; build_docs(&current_dir.join("aya"), &abs_header_path)?;
build_docs(&current_dir.join("bpf/aya-bpf"), &abs_header_path)?; build_docs(&current_dir.join("bpf/aya-bpf"), &abs_header_path)?;
copy_dir_all("./target/doc", "./site/user")?; copy_dir_all("./target/doc".as_ref(), "./site/user".as_ref())?;
copy_dir_all("./target/bpfel-unknown-none/doc", "./site/bpf")?; copy_dir_all(
"./target/bpfel-unknown-none/doc".as_ref(),
"./site/bpf".as_ref(),
)?;
let mut robots = fs::File::create("site/robots.txt").expect("can't create robots.txt"); let mut robots = fs::File::create("site/robots.txt").expect("can't create robots.txt");
robots robots
@ -53,49 +58,46 @@ pub fn docs() -> Result<(), anyhow::Error> {
Ok(()) Ok(())
} }
fn build_docs(working_dir: &PathBuf, abs_header_path: &Path) -> Result<(), anyhow::Error> { fn build_docs(working_dir: &Path, abs_header_path: &Path) -> Result<()> {
let replace = Command::new("sed") exec(Command::new("sed").current_dir(working_dir).args([
.current_dir(working_dir) "-i.bak",
.args(vec!["-i.bak", "s/crabby.svg/crabby_dev.svg/", "src/lib.rs"]) "s/crabby.svg/crabby_dev.svg/",
.status() "src/lib.rs",
.expect("failed to replace logo"); ]))?;
assert!(replace.success());
let args = vec!["+nightly", "doc", "--no-deps", "--all-features"]; exec(
Command::new("cargo")
.current_dir(working_dir)
.env(
"RUSTDOCFLAGS",
format!(
"--cfg docsrs --html-in-header {} -D warnings",
abs_header_path.to_str().unwrap()
),
)
.args(["+nightly", "doc", "--no-deps", "--all-features"]),
)?;
let status = Command::new("cargo")
.current_dir(working_dir)
.env(
"RUSTDOCFLAGS",
format!(
"--cfg docsrs --html-in-header {} -D warnings",
abs_header_path.to_str().unwrap()
),
)
.args(args)
.status()
.expect("failed to build aya docs");
assert!(status.success());
fs::rename( fs::rename(
working_dir.join("src/lib.rs.bak"), working_dir.join("src/lib.rs.bak"),
working_dir.join("src/lib.rs"), working_dir.join("src/lib.rs"),
) )
.unwrap(); .context("Failed to rename lib.rs.bak to lib.rs")
Ok(())
} }
fn copy_dir_all<P1: AsRef<Path>, P2: AsRef<Path>>(src: P1, dst: P2) -> io::Result<()> { fn copy_dir_all(src: &Path, dst: &Path) -> io::Result<()> {
fs::create_dir_all(&dst)?; fs::create_dir_all(dst)?;
for entry in fs::read_dir(src)? { for entry in fs::read_dir(src)? {
let entry = entry?; let entry = entry?;
let ty = entry.file_type()?; let ty = entry.file_type()?;
let src = entry.path();
let src = src.as_path();
let dst = dst.join(entry.file_name());
let dst = dst.as_path();
if ty.is_dir() { if ty.is_dir() {
copy_dir_all(entry.path(), dst.as_ref().join(entry.file_name()))?; copy_dir_all(src, dst)?;
} else { } else if !dst.exists() {
let new_path = dst.as_ref().join(entry.file_name()); fs::copy(src, dst)?;
if !new_path.exists() {
fs::copy(entry.path(), dst.as_ref().join(entry.file_name()))?;
}
} }
} }
Ok(()) Ok(())

@ -5,7 +5,7 @@ use std::{
process::{Command, Stdio}, process::{Command, Stdio},
}; };
use anyhow::Context as _; use anyhow::{Context as _, Result};
use cargo_metadata::{Artifact, CompilerMessage, Message, Target}; use cargo_metadata::{Artifact, CompilerMessage, Message, Target};
use clap::Parser; use clap::Parser;
@ -31,12 +31,14 @@ pub struct Options {
} }
/// Build the project /// Build the project
fn build(release: bool) -> Result<Vec<(PathBuf, PathBuf)>, anyhow::Error> { fn build(release: bool) -> Result<Vec<(PathBuf, PathBuf)>> {
let mut cmd = Command::new("cargo"); let mut cmd = Command::new("cargo");
cmd.arg("build") cmd.args([
.arg("--tests") "build",
.arg("--message-format=json") "--tests",
.arg("--package=integration-test"); "--message-format=json",
"--package=integration-test",
]);
if release { if release {
cmd.arg("--release"); cmd.arg("--release");
} }
@ -83,7 +85,7 @@ fn build(release: bool) -> Result<Vec<(PathBuf, PathBuf)>, anyhow::Error> {
} }
/// Build and run the project /// Build and run the project
pub fn run(opts: Options) -> Result<(), anyhow::Error> { pub fn run(opts: Options) -> Result<()> {
let Options { let Options {
bpf_target, bpf_target,
release, release,
@ -116,10 +118,8 @@ pub fn run(opts: Options) -> Result<(), anyhow::Error> {
println!("{} running {cmd:?}", src_path.display()); println!("{} running {cmd:?}", src_path.display());
let status = cmd let status = cmd
.stdout(Stdio::inherit())
.stderr(Stdio::inherit())
.status() .status()
.context("failed to run {cmd:?}")?; .with_context(|| format!("failed to run {cmd:?}"))?;
match status.code() { match status.code() {
Some(code) => match code { Some(code) => match code {
0 => {} 0 => {}

@ -1,15 +1,24 @@
use serde_json::Value;
use std::{cell::OnceCell, process::Command}; use std::{cell::OnceCell, process::Command};
use anyhow::{bail, Context as _, Result};
pub fn workspace_root() -> &'static str { pub fn workspace_root() -> &'static str {
static mut WORKSPACE_ROOT: OnceCell<String> = OnceCell::new(); static mut WORKSPACE_ROOT: OnceCell<String> = OnceCell::new();
unsafe { &mut WORKSPACE_ROOT }.get_or_init(|| { unsafe { &mut WORKSPACE_ROOT }.get_or_init(|| {
let output = Command::new("cargo").arg("metadata").output().unwrap(); let cmd = cargo_metadata::MetadataCommand::new();
if !output.status.success() { cmd.exec().unwrap().workspace_root.to_string()
panic!("unable to run cargo metadata")
}
let stdout = String::from_utf8(output.stdout).unwrap();
let v: Value = serde_json::from_str(&stdout).unwrap();
v["workspace_root"].as_str().unwrap().to_string()
}) })
} }
pub fn exec(cmd: &mut Command) -> Result<()> {
let status = cmd
.status()
.with_context(|| format!("failed to run {cmd:?}"))?;
match status.code() {
Some(code) => match code {
0 => Ok(()),
code => bail!("{cmd:?} exited with code {code}"),
},
None => bail!("{cmd:?} terminated by signal"),
}
}

Loading…
Cancel
Save