Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 6 additions & 0 deletions .github/workflows/ext-test.yml
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,12 @@ jobs:
matrix:
include:
- { target: qemux86-64, distro-release: "2024", distro-channel: next }
# Keep a cross-arch leg. A same-arch target cannot catch a host/target
# toolchain mix-up in avocado-cli-compile.sh: on qemux86-64 the target
# gcc runs natively, so picking the wrong one still builds. On aarch64
# the wrong pick goes through qemu-user and fails every C probe.
# qemuarm64, not a board: CI legs stay on qemu targets.
- { target: qemuarm64, distro-release: "2024", distro-channel: next }
uses: avocado-linux/actions/.github/workflows/extension-test.yml@v1
with:
target: ${{ matrix.target }}
Expand Down
47 changes: 40 additions & 7 deletions avocado-cli-compile.sh
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,17 @@ for var in $(env | grep -o 'CARGO_TARGET_[A-Z0-9_]*_RUSTFLAGS'); do
unset "$var"
done

# Require the SDK vars before touching the tree or deriving any path. Letting
# them expand empty would collapse CROSS_BINDIR to "/usr/bin", where the -x check
# below finds the *host* gcc and quietly produces a native binary packaged as a
# target extension; an empty SDKTARGETSYSROOT would bake a bogus --sysroot into
# .cargo/config.toml. This script runs with `set -e` but not `set -u`, so nothing
# else catches it. Guarding here also means neither abort leaves a half-written
# .cargo/config.toml behind.
: "${OECORE_NATIVE_SYSROOT:?not set -- SDK environment-setup was not sourced}"
: "${SDKTARGETSYSROOT:?not set -- SDK environment-setup was not sourced}"
: "${CROSS_COMPILE:?not set -- SDK environment-setup was not sourced}"

# Remove only the generated cross-compile config, preserving any committed
# .cargo files used for development.
rm -f .cargo/config.toml
Expand All @@ -44,13 +55,35 @@ rustflags = ["--sysroot=$SDKTARGETSYSROOT/usr", "-C", "link-arg=--sysroot=$SDKTA
EOF

# The SDK exports $CC as the cross-compiler command (bare name + target flags +
# --sysroot), but in the `ext build` environment that compiler binary lives in
# the SDK target-sysroot bindir, which is not on PATH. Without this, the `cc`
# crate (pulled in by the remaining C dep aws-lc-sys) can't resolve the compiler
# from $CC and falls back to guessing "<triple>-gcc", failing with ToolNotFound.
# Put the SDK compiler bindir on PATH so the build uses exactly the $CC the SDK
# configured. Arch-agnostic: derived from $SDKTARGETSYSROOT, no hardcoded triple.
export PATH="$SDKTARGETSYSROOT/usr/bin:$PATH"
# --sysroot), so the `cc` crate (pulled in by the C dep aws-lc-sys) needs that
# binary on PATH or it fails with ToolNotFound.
#
# It lives in the cross-canadian bindir under the SDK *native* sysroot -- NOT in
# $SDKTARGETSYSROOT/usr/bin, which holds the target-*native* toolchain. Putting
# the target sysroot bindir on PATH resolves "<triple>-gcc" to a target ELF;
# binfmt_misc then hands it to qemu-user, which dies on the unresolvable target
# loader ("qemu-aarch64: Could not open '/usr/lib/ld-linux-aarch64.so.1'") and
# fails every compiler probe with exit 255. That is invisible on a same-arch
# target like qemux86-64, where the target gcc happens to run natively.
#
# Arch-agnostic: the triple comes from $CROSS_COMPILE, no hardcoded value.
CROSS_BINDIR="$OECORE_NATIVE_SYSROOT/usr/bin/${CROSS_COMPILE%-}"

# Name the compiler from $CROSS_COMPILE rather than parsing $CC. oe-core writes
# both from ${TARGET_PREFIX} in toolchain-scripts.bbclass, so $CC's first token
# is byte-identical to this in every SDK we ship against, and meta-clang only
# appends CLANGCC without overriding CC. Deriving it from $CC bought nothing and
# mis-read three real inputs: an empty first token (making -x test the bindir
# itself, so the guard passed), a ccache/distcc wrapper, and an absolute path.
CC_BIN="${CROSS_COMPILE}gcc"
if [ ! -x "$CROSS_BINDIR/$CC_BIN" ]; then

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Empty CC_BIN makes -x test the bindir dir, so the guard passes

When $CC's first token resolves to empty, [ ! -x "$CROSS_BINDIR/$CC_BIN" ] tests $CROSS_BINDIR itself, and -x is true for any searchable directory - so the guard silently passes on exactly the case it exists to catch.

Reproduced against this logic: with the toolchain genuinely absent and CC=" aarch64-avocado-linux-gcc -mcpu=cortex-a57" (leading space), ${CC:-...} does not fire because the value is non-empty, and ${CC_BIN%% *} yields the empty string. The control run prints GUARD FIRED; the leading-space run prints GUARD PASSED and proceeds to cargo build with CC_BIN=[]. The build then dies inside aws-lc-sys with the opaque ToolNotFound this block was added to pre-empt.

Worth noting cc-rs handles this input fine - cc-1.2.61 src/lib.rs:3224 trims and :3225 returns None on empty. Only the guard does not.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fixed in a9feb3a. This is the one that mattered most — a guard that reports success on the exact condition it was added to catch is worse than no guard.

Reproduced your leading-space case first (CC=" aarch64-avocado-linux-gcc -mcpu=cortex-a57", toolchain absent → PASSED), then confirmed it fires after the change. -x on a searchable directory being true is an easy thing to write twice, so it is worth having in the thread.

echo "Error: cross compiler '$CC_BIN' not found in $CROSS_BINDIR" >&2
echo "The SDK is missing the C cross-canadian toolchain. An SDK installed" >&2
echo "before it was added to avocado.yaml will not have it -- reinstall with:" >&2
echo " avocado sdk install --force" >&2
exit 1
fi
export PATH="$CROSS_BINDIR:$PATH"

# --locked: published builds run from staged package_files; fail loudly on a
# missing/stale Cargo.lock instead of silently re-resolving dependencies.
Expand Down
5 changes: 5 additions & 0 deletions avocado.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,11 @@ extensions:
nativesdk-rust: '*'
nativesdk-git: '*'
packagegroup-rust-cross-canadian-avocado-{{ avocado.target }}: '*'
# The rust- packagegroup brings rust-cross-canadian + nativesdk-gcc (a
# HOST compiler); it does NOT bring a C cross-compiler. aws-lc-sys needs
# one, so pull the C cross-canadian toolchain explicitly -- this is what
# provides $OECORE_NATIVE_SYSROOT/usr/bin/<triple>/<triple>-gcc.
packagegroup-cross-canadian-avocado-{{ avocado.target }}: '*'

sdk:
image: docker.io/avocadolinux/sdk:{{ env.AVOCADO_DISTRO_RELEASE }}
Expand Down
Loading