scx_utils/
builder.rs

1// Copyright (c) Meta Platforms, Inc. and affiliates.
2//
3// This software may be used and distributed according to the terms of the
4// GNU General Public License version 2.
5
6use std::{
7    fs::File,
8    io::{Read, Seek},
9    path::PathBuf,
10};
11
12include!("clang_info.rs");
13
14const BPF_H: &str = "bpf_h";
15
16pub struct Builder;
17
18impl Builder {
19    pub fn new() -> Self {
20        Builder
21    }
22
23    fn gen_bpf_h(&self) {
24        let out_dir = env::var("OUT_DIR").unwrap();
25        let file = File::create(PathBuf::from(&out_dir).join(format!("{BPF_H}.tar"))).unwrap();
26        let mut ar = tar::Builder::new(file);
27
28        ar.follow_symlinks(false);
29        ar.append_dir_all(".", BPF_H).unwrap();
30
31        let vmlinux_dir = tempfile::tempdir().unwrap();
32        let mut vmlinux_tar_zst = File::open("vmlinux.tar.zst").unwrap();
33        let vmlinux_tar = ruzstd::decoding::StreamingDecoder::new(&mut vmlinux_tar_zst).unwrap();
34        tar::Archive::new(vmlinux_tar)
35            .unpack(vmlinux_dir.path())
36            .unwrap();
37        ar.append_dir_all(".", vmlinux_dir.path().join("vmlinux"))
38            .unwrap();
39
40        ar.finish().unwrap();
41
42        for ent in walkdir::WalkDir::new(BPF_H) {
43            let ent = ent.unwrap();
44            if !ent.file_type().is_dir() {
45                println!("cargo:rerun-if-changed={}", ent.path().to_string_lossy());
46            }
47        }
48    }
49
50    fn gen_bindings(&self) {
51        let out_dir = env::var("OUT_DIR").unwrap();
52        let clang = ClangInfo::new().unwrap();
53        let kernel_target = clang.kernel_target().unwrap();
54
55        let mut vmlinux_tar_zst = File::open("vmlinux.tar.zst").unwrap();
56
57        let mut vmlinux_h = String::new();
58
59        // vmlinux.h is a symlink. dereference it here.
60        let search: PathBuf = format!("vmlinux/arch/{kernel_target}/vmlinux.h").into();
61
62        let mut vmlinux_tar =
63            ruzstd::decoding::StreamingDecoder::new(&mut vmlinux_tar_zst).unwrap();
64        let mut archive = tar::Archive::new(&mut vmlinux_tar);
65        let vmlinux_link_entry = archive
66            .entries()
67            .unwrap()
68            .find(|x| x.as_ref().unwrap().path().unwrap() == search.as_path())
69            .unwrap()
70            .unwrap();
71
72        let vmlinux_path = PathBuf::from(vmlinux_link_entry.path().unwrap())
73            .parent()
74            .unwrap()
75            .join(vmlinux_link_entry.link_name().unwrap().unwrap());
76
77        vmlinux_tar_zst.rewind().unwrap();
78        let vmlinux_tar = ruzstd::decoding::StreamingDecoder::new(&mut vmlinux_tar_zst).unwrap();
79
80        tar::Archive::new(vmlinux_tar)
81            .entries()
82            .unwrap()
83            .find(|x| x.as_ref().unwrap().path().unwrap() == vmlinux_path.as_path())
84            .unwrap()
85            .unwrap()
86            .read_to_string(&mut vmlinux_h)
87            .unwrap();
88
89        let bindings = bindgen::Builder::default()
90            .header_contents(&search.to_string_lossy(), &vmlinux_h)
91            .allowlist_type("scx_exit_kind")
92            .allowlist_type("scx_consts")
93            .parse_callbacks(Box::new(bindgen::CargoCallbacks::new()))
94            .generate()
95            .expect("Unable to generate bindings");
96
97        bindings
98            .write_to_file(PathBuf::from(&out_dir).join("bindings.rs"))
99            .expect("Couldn't write bindings");
100    }
101
102    pub fn build(self) {
103        self.gen_bpf_h();
104        self.gen_bindings();
105    }
106}