Skip to content

Commit

Permalink
Add cross-compilation to CI
Browse files Browse the repository at this point in the history
Updates the README to use cargo target config instead of RUSTFLAGS to
avoid setting the linker for ebpf in cargo-in-cargo.

Rewrite test.sh in python.
  • Loading branch information
tamird committed Oct 17, 2024
1 parent 20ce988 commit 3d4fe16
Show file tree
Hide file tree
Showing 5 changed files with 231 additions and 98 deletions.
44 changes: 38 additions & 6 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -21,12 +21,12 @@ concurrency:

jobs:
build:
runs-on: ubuntu-22.04
strategy:
fail-fast: false
matrix:
runner:
- ubuntu-latest # x86
rust:
- stable
- 1.80.1
program:
- kprobe
Expand All @@ -48,6 +48,15 @@ jobs:
- raw_tracepoint
- tp_btf
- tracepoint
include:
- runner: macos-13 # x86
rust: 1.80.1
program: kprobe
- runner: macos-14 # arm64
rust: 1.80.1
program: kprobe

runs-on: ${{ matrix.runner }}

steps:
- uses: actions/checkout@v4
Expand All @@ -57,6 +66,21 @@ jobs:
components: rust-src,rustfmt

- uses: dtolnay/rust-toolchain@master
if: runner.os == 'macOS' && runner.arch == 'X64'
with:
toolchain: ${{ matrix.rust }}
targets: x86_64-unknown-linux-musl
components: clippy

- uses: dtolnay/rust-toolchain@master
if: runner.os == 'macOS' && runner.arch == 'ARM64'
with:
toolchain: ${{ matrix.rust }}
targets: aarch64-unknown-linux-musl
components: clippy

- uses: dtolnay/rust-toolchain@master
if: runner.os != 'macOS'
with:
toolchain: ${{ matrix.rust }}
components: clippy
Expand All @@ -65,8 +89,16 @@ jobs:

- uses: taiki-e/install-action@v2
with:
tool: bpf-linker,cargo-generate
tool: cargo-generate

- run: brew install filosottile/musl-cross/musl-cross llvm
if: runner.os == 'macos'

- run: cargo install bpf-linker --git https://github.com/aya-rs/bpf-linker.git --no-default-features
if: runner.os == 'macos'

- run: cargo install bpf-linker --git https://github.com/aya-rs/bpf-linker.git
if: runner.os != 'macos'


- run: sudo apt update
- run: sudo apt install expect
- run: ./test.sh ${{ github.workspace }} ${{ matrix.program }}
- run: ./test.py ${{ github.workspace }} ${{ matrix.program }}
7 changes: 4 additions & 3 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -21,11 +21,12 @@ experience; this compromise necessitates the use of `xtask` to actually build th

Cross compilation should work on both Intel and Apple Silicon Macs.

```bash
```shell
AYA_BUILD_EBPF=true \
CC=${ARCH}-linux-musl-gcc \
RUSTFLAGS="-C linker=${ARCH}-linux-musl-gcc" \
cargo build --package {{project-name}} --release --target=${ARCH}-unknown-linux-musl
cargo build --package {{project-name}} --release \
--target=${ARCH}-unknown-linux-musl \
--config=target.${ARCH}-unknown-linux-musl.linker=\"${ARCH}-linux-musl-gcc\"
```
The cross-compiled program `target/${ARCH}-unknown-linux-musl/release/{{project-name}}` can be
copied to a Linux server or VM and run there.
2 changes: 1 addition & 1 deletion cargo-generate.toml
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
[template]
cargo_generate_version = ">=0.10.0"
ignore = [".github", "test.sh"]
ignore = [".github", "test.py"]

[placeholders.program_type]
type = "string"
Expand Down
188 changes: 188 additions & 0 deletions test.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,188 @@
#!/usr/bin/env python3

import argparse
import datetime
import io
import os
import platform
import signal
import subprocess
import sys
import tempfile
import time
from typing import TypedDict


class SubprocessArgs(TypedDict, total=False):
cwd: str
env: dict[str, str]


def main() -> None:
parser = argparse.ArgumentParser(
description="Generate and build a Rust project using cargo."
)
parser.add_argument("template_dir", help="Template directory")
parser.add_argument("program_type", help="Program type")
args = parser.parse_args()

match args.program_type:
case "cgroup_sockopt":
additional_args = ["-d", "sockopt_target=getsockopt"]
case "classifier" | "cgroup_skb":
additional_args = ["-d", "direction=Ingress"]
case "fentry" | "fexit":
additional_args = ["-d", "fn_name=try_to_wake_up"]
case "kprobe" | "kretprobe":
additional_args = ["-d", "kprobe=do_unlinkat"]
case "lsm":
additional_args = ["-d", "lsm_hook=file_open"]
case "raw_tracepoint":
additional_args = ["-d", "tracepoint_name=sys_enter"]
case "sk_msg":
additional_args = ["-d", "sock_map=SOCK_MAP"]
case "tp_btf":
additional_args = ["-d", "tracepoint_name=net_dev_queue"]
case "tracepoint":
additional_args = [
"-d",
"tracepoint_category=net",
"-d",
"tracepoint_name=net_dev_queue",
]
case "uprobe" | "uretprobe":
additional_args = [
"-d",
"uprobe_target=/proc/self/exe",
"-d",
"uprobe_fn_name=main",
]
case _:
additional_args = []

CRATE_NAME = "aya-test-crate"
with tempfile.TemporaryDirectory() as tmp_dir:
cmds: list[tuple[list[str], SubprocessArgs]] = [
(
[
"cargo",
"generate",
"--path",
args.template_dir,
"-n",
CRATE_NAME,
"-d",
f"program_type={args.program_type}",
]
+ additional_args,
{"cwd": tmp_dir},
),
]
project_dir = os.path.join(tmp_dir, CRATE_NAME)
match platform.system():
case "Linux":
cmds.extend(
(cmd, {"cwd": project_dir})
for cmd in (
["cargo", "+nightly", "fmt", "--all", "--", "--check"],
["cargo", "build", "--package", CRATE_NAME],
["cargo", "build", "--package", CRATE_NAME, "--release"],
# We cannot run clippy over the whole workspace at once due to feature unification.
# Since both ${CRATE_NAME} and ${CRATE_NAME}-ebpf depend on ${CRATE_NAME}-common and
# ${CRATE_NAME} activates ${CRATE_NAME}-common's aya dependency, we end up trying to
# compile the panic handler twice: once from the bpf program, and again from std via
# aya.
[
"cargo",
"clippy",
"--exclude",
f"{CRATE_NAME}-ebpf",
"--all-targets",
"--workspace",
"--",
"--deny",
"warnings",
],
[
"cargo",
"clippy",
"--package",
f"{CRATE_NAME}-ebpf",
"--all-targets",
"--",
"--deny",
"warnings",
],
)
)
case "Darwin":
arch = platform.machine()
if arch == "arm64":
arch = "aarch64"
target = f"{arch}-unknown-linux-musl"
cmds.append(
(
[
"cargo",
"build",
"--package",
CRATE_NAME,
"--release",
"--target",
target,
"--config",
f'target.{target}.linker = "rust-lld"',
],
{
"cwd": project_dir,
"env": os.environ
| {
"AYA_BUILD_EBPF": "true",
"CC": f"{arch}-linux-musl-gcc",
},
},
)
)

for cmd, kwargs in cmds:
sys.stdout.write(f"{cmd=} {kwargs=}\n")
sys.stdout.flush()
subprocess.check_call(cmd, **kwargs)

if platform.system() == "Linux":
with subprocess.Popen(
["cargo", "xtask", "run"],
cwd=project_dir,
stdin=subprocess.PIPE,
stdout=subprocess.PIPE,
stderr=sys.stderr,
) as process:
assert process.stdin is not None
assert process.stdout is not None
start = time.monotonic_ns()
for line in io.TextIOWrapper(process.stdout, encoding="utf-8"):
elapsed = datetime.timedelta(
milliseconds=(time.monotonic_ns() - start) / 1_000_000,
)
sys.stdout.write(f"{elapsed=}{line=}\n")
sys.stdout.flush()
if "Waiting for Ctrl-C" in line:
# Send SIGINT signal to simulate Ctrl-C
process.send_signal(signal.SIGINT)

try:
# Wait up to 5 seconds for graceful termination
process.wait(timeout=5)
except subprocess.TimeoutExpired:
# If the process doesn't exit, forcefully kill it
sys.stdout.write("Process did not exit after SIGINT, sending SIGKILL\n")
sys.stdout.flush()
process.kill() # Force kill
process.wait() # Wait until fully terminated
retcode = process.returncode
if retcode != 0:
raise subprocess.CalledProcessError(retcode, process.args)


if __name__ == "__main__":
main()
88 changes: 0 additions & 88 deletions test.sh

This file was deleted.

0 comments on commit 3d4fe16

Please sign in to comment.