From 28694dcec72b11faca6e9d9371fb0f65c590de8b Mon Sep 17 00:00:00 2001 From: "Yann E. MORIN" Date: Mon, 1 Sep 2025 11:01:26 +0200 Subject: [PATCH] toolchain/external: support merged-bin When an external toolchain does not have a merged-bin, we can end up with a situation where the toolchain installs an staging/usr/sbin/ directory, overriding the sbin symlink with an actual directory. When not using PPD, this does not cause any harm. However, with PPD, the build fails when preparing the staging (the host/) for packages when the skeleton is eventually rsynced, e.g.: $ cat defconfig BR2_aarch64=y BR2_TOOLCHAIN_EXTERNAL=y BR2_TOOLCHAIN_EXTERNAL_BOOTLIN=y BR2_PER_PACKAGE_DIRECTORIES=y BR2_INIT_NONE=y BR2_ROOTFS_MERGED_USR=y BR2_ROOTFS_MERGED_SBIN=y # BR2_PACKAGE_BUSYBOX is not set BR2_PACKAGE_ZLIB=y # BR2_TARGET_ROOTFS_TAR is not set $ make zlib [...] >>> zlib Configuring mkdir -p [...]/per-package/zlib/host rsync -a --hard-links --link-dest=[...]/per-package/host-skeleton/host/ [...]/per-package/host-skeleton/host/ [...]/per-package/zlib/host rsync -a --hard-links --link-dest=[...]/per-package/libzlib/host/ [...]/per-package/libzlib/host/ [...]/per-package/zlib/host rsync -a --hard-links --link-dest=[...]/per-package/skeleton/host/ [...]/per-package/skeleton/host/ [...]/per-package/zlib/host could not make way for new symlink: aarch64-buildroot-linux-gnu/sysroot/usr/sbin cannot delete non-empty directory: aarch64-buildroot-linux-gnu/sysroot/usr/sbin rsync error: some files/attrs were not transferred (see previous errors) (code 23) at main.c(1338) [sender=3.3.0] make[1]: *** [package/pkg-generic.mk:256: [...]/build/zlib/.stamp_configured] Error 23 make: *** [Makefile:23: _all] Error 2 The root cause is that, in the skeleton, we end up with [staging]/usr/sbin as a synlink to bin, but when the external toolchain gets installed, the symlinbk is replaced by a directory. Later, when we aggregate the PPD before configuring a package, it often happens that a dependant package be rsynced before the toolchain and the skeleton, as seen above, in which case the sbin directory from the toolchain, by way of the dependency to a package, is already present when rsync wants to create a symlink as rsynced from the skeleton. In the example above, this plays in this order: 1. skeleton gets installed, provides a symlink 2. toolchain-external-bootlin rsyncs from skeleton, gets a symlink 3. toolchain-external-bootlin gets installed, replaces symlink with a directory 4. libzlib rsyncs from skeleton, gets a symlink, then rsyncs from toolchain-e-b, gets a directory 5. zlib rsyncs from libzlib first (because alphabetical ordering), gets a directory, then rsyncs from skeleton, which rsyncs from a symlink, but the destination is a non-empty directory, so rsync fails. It is perfectly legit that an external toolchain does not use a merged-bin setup, so we must accept that as input. We do so by treating the /usr/sbin entry specially, like we already do for a few others, of which /sbin itself for example. Note that the merged-usr setup has no issue, because we already handle the lib*/ directories specially too. Signed-off-by: Yann E. MORIN Cc: Giulio Benetti Cc: Romain Naour Cc: Thomas Petazzoni Signed-off-by: Romain Naour --- toolchain/helpers.mk | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/toolchain/helpers.mk b/toolchain/helpers.mk index f3fdaaec07..21c710d0fb 100644 --- a/toolchain/helpers.mk +++ b/toolchain/helpers.mk @@ -96,6 +96,10 @@ copy_toolchain_lib_root = \ # Note that the 'locale' directories are not copied. They are huge # (400+MB) in CodeSourcery toolchains, and they are not really useful. # +# usr/sbin may be a directory in the source toolchain, but a symlink +# in the destination sysroot, when we are using a merged-bin. Account +# for that. +# # $1: main sysroot directory of the toolchain # $2: arch specific sysroot directory of the toolchain # $3: arch specific subdirectory in the sysroot @@ -109,13 +113,13 @@ copy_toolchain_sysroot = \ ARCH_SUBDIR="$(strip $3)"; \ ARCH_LIB_DIR="$(strip $4)" ; \ SUPPORT_LIB_DIR="$(strip $5)" ; \ - for i in etc $${ARCH_LIB_DIR} sbin usr usr/$${ARCH_LIB_DIR}; do \ + for i in etc $${ARCH_LIB_DIR} sbin usr usr/$${ARCH_LIB_DIR} usr/sbin; do \ if [ ! -d $${ARCH_SYSROOT_DIR}/$$i ] ; then \ continue ; \ fi ; \ if [ "$$i" = "usr" ]; then \ rsync -au --chmod=u=rwX,go=rX --exclude 'locale/' \ - --include '/libexec*/' --exclude '/lib*/' \ + --include '/libexec*/' --exclude '/lib*/' --exclude '/sbin' \ $${ARCH_SYSROOT_DIR}/$$i/ $(STAGING_DIR)/$$i/ ; \ else \ rsync -au --chmod=u=rwX,go=rX --exclude 'locale/' \