Skip to content

Commit

Permalink
Use aya-build
Browse files Browse the repository at this point in the history
  • Loading branch information
tamird authored and vadorovsky committed Dec 2, 2024
1 parent cb9ab79 commit 6580d77
Show file tree
Hide file tree
Showing 3 changed files with 14 additions and 150 deletions.
2 changes: 1 addition & 1 deletion Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -5,12 +5,12 @@ default-members = ["{{project-name}}", "{{project-name}}-common"]

[workspace.dependencies]
aya = { version = "0.13.1", default-features = false }
aya-build = { version = "0.1.2", default-features = false }
aya-ebpf = { version = "0.1.1", default-features = false }
aya-log = { version = "0.2.1", default-features = false }
aya-log-ebpf = { version = "0.1.1", default-features = false }

anyhow = { version = "1", default-features = false }
cargo_metadata = { version = "0.18.0", default-features = false }
# `std` feature is currently required to build `clap`.
#
# See https://github.com/clap-rs/clap/blob/61f5ee5/clap_builder/src/lib.rs#L15.
Expand Down
3 changes: 2 additions & 1 deletion {{project-name}}/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,8 @@ clap = { workspace = true, features = ["derive"] }
{% endif -%}

[build-dependencies]
cargo_metadata = { workspace = true }
anyhow = { workspace = true }
aya-build = { workspace = true }
# TODO(https://github.com/rust-lang/cargo/issues/12375): this should be an artifact dependency, but
# it's not possible to tell cargo to use `-Z build-std` to build it. We cargo-in-cargo in the build
# script to build this, but we want to teach cargo about the dependecy so that cache invalidation
Expand Down
159 changes: 11 additions & 148 deletions {{project-name}}/build.rs
Original file line number Diff line number Diff line change
@@ -1,151 +1,14 @@
use std::{
env, fs,
io::{BufRead as _, BufReader},
path::PathBuf,
process::{Child, Command, Stdio},
};

use cargo_metadata::{
Artifact, CompilerMessage, Message, Metadata, MetadataCommand, Package, Target,
};

/// 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
/// 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
fn main() {
let Metadata { packages, .. } = MetadataCommand::new().no_deps().exec().unwrap();
use anyhow::{anyhow, Context as _};
use aya_build::cargo_metadata;

fn main() -> anyhow::Result<()> {
let cargo_metadata::Metadata { packages, .. } = cargo_metadata::MetadataCommand::new()
.no_deps()
.exec()
.context("MetadataCommand::exec")?;
let ebpf_package = packages
.into_iter()
.find(|Package { name, .. }| name == "{{project-name}}-ebpf")
.unwrap();

let out_dir = env::var_os("OUT_DIR").unwrap();
let out_dir = PathBuf::from(out_dir);

let endian = env::var_os("CARGO_CFG_TARGET_ENDIAN").unwrap();
let target = if endian == "big" {
"bpfeb"
} else if endian == "little" {
"bpfel"
} else {
panic!("unsupported endian={:?}", endian)
};

// TODO(https://github.com/rust-lang/cargo/issues/4001): Make this `false` if we can determine
// we're in a check build.
let build_ebpf = true;
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([
"+nightly",
"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 correct toolchain is used.
for key in ["RUSTC", "RUSTC_WORKSPACE_WRAPPER"] {
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}");
}
});

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}");
}
}
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:?}");

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}"));
}
}
.find(|cargo_metadata::Package { name, .. }| name == "{{project-name}}-ebpf")
.ok_or_else(|| anyhow!("{{project-name}}-ebpf package not found"))?;
aya_build::build_ebpf([ebpf_package])
}

0 comments on commit 6580d77

Please sign in to comment.