Add cross-compilation to CI

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.
reviewable/pr131/r1
Tamir Duberstein 7 months ago
parent 20ce988ecf
commit dc7c2cbd0d
No known key found for this signature in database

@ -21,12 +21,14 @@ concurrency:
jobs: jobs:
build: build:
runs-on: ubuntu-22.04
strategy: strategy:
fail-fast: false fail-fast: false
matrix: matrix:
runner:
- macos-13 # x86
- macos-latest # arm64
- ubuntu-latest # x86
rust: rust:
- stable
- 1.80.1 - 1.80.1
program: program:
- kprobe - kprobe
@ -48,6 +50,7 @@ jobs:
- raw_tracepoint - raw_tracepoint
- tp_btf - tp_btf
- tracepoint - tracepoint
runs-on: ${{ matrix.runner }}
steps: steps:
- uses: actions/checkout@v4 - uses: actions/checkout@v4
@ -67,6 +70,4 @@ jobs:
with: with:
tool: bpf-linker,cargo-generate tool: bpf-linker,cargo-generate
- run: sudo apt update - run: ./test.py ${{ github.workspace }} ${{ matrix.program }}
- run: sudo apt install expect
- run: ./test.sh ${{ github.workspace }} ${{ matrix.program }}

@ -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. Cross compilation should work on both Intel and Apple Silicon Macs.
```bash ```shell
AYA_BUILD_EBPF=true \ AYA_BUILD_EBPF=true \
CC=${ARCH}-linux-musl-gcc \ CC=${ARCH}-linux-musl-gcc \
RUSTFLAGS="-C linker=${ARCH}-linux-musl-gcc" \ cargo build --package {{project-name}} --release \
cargo build --package {{project-name}} --release --target=${ARCH}-unknown-linux-musl --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 The cross-compiled program `target/${ARCH}-unknown-linux-musl/release/{{project-name}}` can be
copied to a Linux server or VM and run there. copied to a Linux server or VM and run there.

@ -1,6 +1,6 @@
[template] [template]
cargo_generate_version = ">=0.10.0" cargo_generate_version = ">=0.10.0"
ignore = [".github", "test.sh"] ignore = [".github", "test.py"]
[placeholders.program_type] [placeholders.program_type]
type = "string" type = "string"

@ -0,0 +1,174 @@
#!/usr/bin/env python3
import argparse
import os
import platform
import signal
import subprocess
import sys
import tempfile
from typing import TypedDict
if platform.system() == "Linux":
import asyncio
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",
"--exclude",
f"{CRATE_NAME}-ebpf",
"--all-targets",
"--workspace",
"--",
"--deny",
"warnings",
],
[
"cargo",
"--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:
print(f"Running command: {' '.join(cmd)} with kwargs: {kwargs}")
subprocess.check_call(cmd, **kwargs)
if platform.system() == "Linux":
async def run():
async with asyncio.create_subprocess_exec(
"cargo",
"xtask",
"run",
cwd=project_dir,
stdin=subprocess.DEVNULL,
stdout=asyncio.subprocess.PIPE,
text=True,
) as process:
async with asyncio.timeout(30):
for line in process.stdout:
sys.stdout.write(line)
if "Waiting for Ctrl-C" in line:
process.send_signal(signal.SIGINT)
retcode = await process.wait()
if retcode != 0:
raise subprocess.CalledProcessError(retcode, process.args)
asyncio.run(run())
if __name__ == "__main__":
main()

@ -1,88 +0,0 @@
#!/usr/bin/env bash
set -eux
TEMPLATE_DIR=$1
if [ -z "${TEMPLATE_DIR}" ]; then echo "template dir required"; exit 1; fi
PROG_TYPE=$2
if [ -z "${PROG_TYPE}" ]; then echo "program type required"; exit 1; fi
CRATE_NAME=aya-test-crate
case "${PROG_TYPE}" in
"cgroup_sockopt")
ADDITIONAL_ARGS=(-d sockopt_target=getsockopt)
;;
"classifier"|"cgroup_skb")
ADDITIONAL_ARGS=(-d direction=Ingress)
;;
"fentry"|"fexit")
ADDITIONAL_ARGS=(-d fn_name=try_to_wake_up)
;;
"kprobe"|"kretprobe")
ADDITIONAL_ARGS=(-d kprobe=do_unlinkat)
;;
"lsm")
ADDITIONAL_ARGS=(-d lsm_hook=file_open)
;;
"raw_tracepoint")
ADDITIONAL_ARGS=(-d tracepoint_name=sys_enter)
;;
"sk_msg")
ADDITIONAL_ARGS=(-d sock_map=SOCK_MAP)
;;
"tp_btf")
ADDITIONAL_ARGS=(-d tracepoint_name=net_dev_queue)
;;
"tracepoint")
ADDITIONAL_ARGS=(-d tracepoint_category=net -d tracepoint_name=net_dev_queue)
;;
"uprobe"|"uretprobe")
ADDITIONAL_ARGS=(-d uprobe_target=/proc/self/exe -d uprobe_fn_name=main)
;;
*)
ADDITIONAL_ARGS=()
esac
TMP_DIR=$(mktemp -d)
clean_up() {
# shellcheck disable=SC2317
rm -rf "${TMP_DIR}"
}
trap clean_up EXIT
pushd "${TMP_DIR}"
cargo generate --path "${TEMPLATE_DIR}" -n "${CRATE_NAME}" -d program_type="${PROG_TYPE}" "${ADDITIONAL_ARGS[@]}"
pushd "${CRATE_NAME}"
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 "${CRATE_NAME}-ebpf" --all-targets --workspace -- --deny warnings
cargo clippy --package "${CRATE_NAME}-ebpf" --all-targets -- --deny warnings
expect << EOF
set timeout 30 ;# Increase timeout if necessary
spawn cargo xtask run
expect {
-re "Waiting for Ctrl-C.*" {
send -- \003 ;# Send Ctrl-C
}
timeout {
puts "Error: Timed out waiting for 'Waiting for Ctrl-C...'"
exit 1
}
eof {
puts "Error: Process exited prematurely"
exit 1
}
}
expect {
-re "Exiting.*" { }
eof { }
}
EOF
Loading…
Cancel
Save