mirror of https://github.com/aya-rs/aya
Extract aya-build for building eBPF crates
We'll use this in the template and book to avoid duplicating all the code.reviewable/pr805/r5
parent
e0c4948e36
commit
2b2af44915
@ -0,0 +1,12 @@
|
|||||||
|
[package]
|
||||||
|
name = "aya-build"
|
||||||
|
version = "0.1.0"
|
||||||
|
authors.workspace = true
|
||||||
|
license.workspace = true
|
||||||
|
repository.workspace = true
|
||||||
|
homepage.workspace = true
|
||||||
|
edition.workspace = true
|
||||||
|
|
||||||
|
[dependencies]
|
||||||
|
anyhow = { workspace = true }
|
||||||
|
cargo_metadata = { workspace = true }
|
@ -0,0 +1,148 @@
|
|||||||
|
use std::{
|
||||||
|
env, fs,
|
||||||
|
io::{BufRead as _, BufReader},
|
||||||
|
path::PathBuf,
|
||||||
|
process::{Child, Command, Stdio},
|
||||||
|
};
|
||||||
|
|
||||||
|
use anyhow::{anyhow, Context as _, Result};
|
||||||
|
// Re-export `cargo_metadata` to having to encode the version downstream and risk mismatches.
|
||||||
|
pub use cargo_metadata;
|
||||||
|
use cargo_metadata::{Artifact, CompilerMessage, Message, Package, Target};
|
||||||
|
|
||||||
|
/// Build binary artifacts produced by `packages`.
|
||||||
|
///
|
||||||
|
/// This would be better expressed as one or more [artifact-dependencies][bindeps] but issues such
|
||||||
|
/// as:
|
||||||
|
///
|
||||||
|
/// * <https://github.com/rust-lang/cargo/issues/12374>
|
||||||
|
/// * <https://github.com/rust-lang/cargo/issues/12375>
|
||||||
|
/// * <https://github.com/rust-lang/cargo/issues/12385>
|
||||||
|
///
|
||||||
|
/// prevent their use for the time being.
|
||||||
|
///
|
||||||
|
/// [bindeps]: https://doc.rust-lang.org/nightly/cargo/reference/unstable.html?highlight=feature#artifact-dependencies
|
||||||
|
pub fn build_ebpf(packages: impl IntoIterator<Item = Package>) -> Result<()> {
|
||||||
|
let out_dir = env::var_os("OUT_DIR").ok_or(anyhow!("OUT_DIR not set"))?;
|
||||||
|
let out_dir = PathBuf::from(out_dir);
|
||||||
|
|
||||||
|
let endian =
|
||||||
|
env::var_os("CARGO_CFG_TARGET_ENDIAN").ok_or(anyhow!("CARGO_CFG_TARGET_ENDIAN not set"))?;
|
||||||
|
let target = if endian == "big" {
|
||||||
|
"bpfeb"
|
||||||
|
} else if endian == "little" {
|
||||||
|
"bpfel"
|
||||||
|
} else {
|
||||||
|
return Err(anyhow!("unsupported endian={endian:?}"));
|
||||||
|
};
|
||||||
|
|
||||||
|
let arch =
|
||||||
|
env::var_os("CARGO_CFG_TARGET_ARCH").ok_or(anyhow!("CARGO_CFG_TARGET_ARCH not set"))?;
|
||||||
|
|
||||||
|
let target = format!("{target}-unknown-none");
|
||||||
|
|
||||||
|
for Package {
|
||||||
|
name,
|
||||||
|
manifest_path,
|
||||||
|
..
|
||||||
|
} in packages
|
||||||
|
{
|
||||||
|
let dir = manifest_path
|
||||||
|
.parent()
|
||||||
|
.ok_or(anyhow!("no parent for {manifest_path}"))?;
|
||||||
|
|
||||||
|
// We have a build-dependency on `name`, so cargo will automatically rebuild us if `name`'s
|
||||||
|
// *library* target or any of its dependencies change. Since we depend on `name`'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={dir}");
|
||||||
|
|
||||||
|
let mut cmd = Command::new("cargo");
|
||||||
|
cmd.args([
|
||||||
|
"+nightly",
|
||||||
|
"build",
|
||||||
|
"--package",
|
||||||
|
&name,
|
||||||
|
"-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 correct toolchain is used.
|
||||||
|
for key in ["RUSTC", "RUSTC_WORKSPACE_WRAPPER"] {
|
||||||
|
cmd.env_remove(key);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Workaround for https://github.com/rust-lang/cargo/issues/6412 where cargo flocks itself.
|
||||||
|
let target_dir = out_dir.join(name);
|
||||||
|
cmd.arg("--target-dir").arg(&target_dir);
|
||||||
|
|
||||||
|
let mut child = cmd
|
||||||
|
.stdout(Stdio::piped())
|
||||||
|
.stderr(Stdio::piped())
|
||||||
|
.spawn()
|
||||||
|
.with_context(|| format!("failed to spawn {cmd:?}"))?;
|
||||||
|
let Child { stdout, stderr, .. } = &mut child;
|
||||||
|
|
||||||
|
// Trampoline stdout to cargo warnings.
|
||||||
|
let stderr = stderr.take().expect("stderr");
|
||||||
|
let stderr = BufReader::new(stderr);
|
||||||
|
let stderr = std::thread::spawn(move || {
|
||||||
|
for line in stderr.lines() {
|
||||||
|
let line = line.expect("read line");
|
||||||
|
println!("cargo:warning={line}");
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
let stdout = stdout.take().expect("stdout");
|
||||||
|
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}");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Message::TextLine(line) => {
|
||||||
|
println!("cargo:warning={line}");
|
||||||
|
}
|
||||||
|
_ => {}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
let status = child
|
||||||
|
.wait()
|
||||||
|
.with_context(|| format!("failed to wait for {cmd:?}"))?;
|
||||||
|
if !status.success() {
|
||||||
|
return Err(anyhow!("{cmd:?} failed: {status:?}"));
|
||||||
|
}
|
||||||
|
|
||||||
|
match stderr.join().map_err(std::panic::resume_unwind) {
|
||||||
|
Ok(()) => {}
|
||||||
|
}
|
||||||
|
|
||||||
|
for (name, binary) in executables {
|
||||||
|
let dst = out_dir.join(name);
|
||||||
|
let _: u64 = fs::copy(&binary, &dst)
|
||||||
|
.with_context(|| format!("failed to copy {binary:?} to {dst:?}"))?;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Ok(())
|
||||||
|
}
|
@ -0,0 +1,3 @@
|
|||||||
|
pub mod aya_build
|
||||||
|
pub use aya_build::cargo_metadata
|
||||||
|
pub fn aya_build::build_ebpf(packages: impl core::iter::traits::collect::IntoIterator<Item = cargo_metadata::Package>) -> anyhow::Result<()>
|
Loading…
Reference in New Issue