public inbox for isar-users@googlegroups.com
 help / color / mirror / Atom feed
From: "'Felix Moessbauer' via isar-users" <isar-users@googlegroups.com>
To: isar-users@googlegroups.com
Cc: cedric.hombourger@siemens.com, jan.kiszka@siemens.com,
	quirin.gylstorff@siemens.com,
	Felix Moessbauer <felix.moessbauer@siemens.com>
Subject: [PATCH 11/13] rootfs: use host apt to install packages into rootfs
Date: Fri,  2 Oct 2026 16:49:34 +0200	[thread overview]
Message-ID: <20261002144936.246628-12-felix.moessbauer@siemens.com> (raw)
In-Reply-To: <20261002144936.246628-1-felix.moessbauer@siemens.com>

We currently use the apt inside the rootfs to operate on the rootfs.
This has two major disadvantages:

1. on non-native, the apt invocations are emulated (including the
   extraction of the packages and downloading)
2. apt must be installed in the rootfs (which makes the rootfs larger
   and pulls in a lot of static-build-using dependencies related to the
   rust parts, which is relevant for license clearing)

We change that similar to how mmdebstrap is doing it: Use the host apt
to operate on the chroot, whereby dpkg itself runs inside the chroot to
have proper support for the maintainer scripts. By that, apt is not
emulated and the generated chroots can be much smaller.

Signed-off-by: Felix Moessbauer <felix.moessbauer@siemens.com>
---
 meta/classes-recipe/deb-dl-dir.bbclass |  34 ++++++
 meta/classes-recipe/rootfs.bbclass     | 145 ++++++++++---------------
 2 files changed, 89 insertions(+), 90 deletions(-)

diff --git a/meta/classes-recipe/deb-dl-dir.bbclass b/meta/classes-recipe/deb-dl-dir.bbclass
index bafe3a63..3f41f9e1 100644
--- a/meta/classes-recipe/deb-dl-dir.bbclass
+++ b/meta/classes-recipe/deb-dl-dir.bbclass
@@ -178,3 +178,37 @@ deb_dl_dir_export() {
         done
 EOF
 }
+
+# Point isar-apt at its real build-path location so host apt can access it.
+# The reproducible 'file:///isar-apt' encodes to the apt list prefix '_isar-apt',
+# the real repo path encodes to the same path with '/' replaced by '_'.
+deb_dl_dir_isar_apt_to_host() {
+    export rootfs=$1
+    export host_prefix="$(printf '%s' '${REPO_ISAR_DIR}/${DISTRO}' | tr '/' '_')"
+    run_privileged_heredoc << '    EOSUDO'
+        set -e
+        sed -i 's|file:///isar-apt|file://${REPO_ISAR_DIR}/${DISTRO}|g' \
+            "${rootfs}/etc/apt/sources.list.d/isar-apt.list"
+        for f in "${rootfs}/var/lib/apt/lists/"_isar-apt*; do
+            [ -e "$f" ] || continue
+            suffix="$(basename "$f" | sed 's|^_isar-apt||')"
+            mv "$f" "${rootfs}/var/lib/apt/lists/${host_prefix}${suffix}"
+        done
+    EOSUDO
+}
+
+# Revert the deb_dl_dir_isar_apt_to_host changes.
+deb_dl_dir_isar_apt_to_target() {
+    export rootfs=$1
+    export host_prefix="$(printf '%s' '${REPO_ISAR_DIR}/${DISTRO}' | tr '/' '_')"
+    run_privileged_heredoc << '    EOSUDO'
+        set -e
+        sed -i 's|file://${REPO_ISAR_DIR}/${DISTRO}|file:///isar-apt|g' \
+            "${rootfs}/etc/apt/sources.list.d/isar-apt.list"
+        for f in "${rootfs}/var/lib/apt/lists/${host_prefix}"*; do
+            [ -e "$f" ] || continue
+            suffix="$(basename "$f" | sed "s|^${host_prefix}||")"
+            mv "$f" "${rootfs}/var/lib/apt/lists/_isar-apt${suffix}"
+        done
+    EOSUDO
+}
diff --git a/meta/classes-recipe/rootfs.bbclass b/meta/classes-recipe/rootfs.bbclass
index 4fd627c2..994e8662 100644
--- a/meta/classes-recipe/rootfs.bbclass
+++ b/meta/classes-recipe/rootfs.bbclass
@@ -65,6 +65,16 @@ ROOTFS_FEATURES:remove:focal = "generate-sbom"
 # Capture all information needed for sbom generation
 ROOTFS_APT_STATE = "apt-state.tar.zst"
 ROOTFS_APT_ARGS="install --yes -o Debug::pkgProblemResolver=yes"
+ROOTFS_HOST_APT_OPTS = "-o Dir=${ROOTFSDIR} -o APT::Architecture=${ROOTFS_ARCH} -o DPkg::Chroot-Directory=${ROOTFSDIR}"
+
+# The host apt-get used to populate the rootfs must honor the rootfs' own apt
+# configuration fragments instead of the host's /etc/apt. A command-line '-o' is
+# applied too late (after apt has already read its config parts), so point apt at
+# the chroot via APT_CONFIG, which is parsed during apt initialization - before
+# the config parts directory is read. No host file is touched.
+ROOTFS_HOST_APT_CONFIG = "${WORKDIR}/isar-host-apt.conf"
+# Wrapper to run the host apt-get with that configuration.
+HOST_APT_CMD = "env APT_CONFIG=${ROOTFS_HOST_APT_CONFIG} /usr/bin/apt-get ${ROOTFS_HOST_APT_OPTS}"
 
 ROOTFS_CLEAN_FILES="/etc/hostname /etc/resolv.conf"
 
@@ -76,7 +86,7 @@ ROOTFS_INITRD_STUBS = "update-initramfs"
 ROOTFS_INITRD_STUBS += "${@ ' dracut' if bb.utils.to_boolean(d.getVar('ROOTFS_USE_DRACUT')) else '' }"
 
 # list of <outer>:<inner> or <outer> mount entries
-ROOTFS_MOUNTS ??= "${REPO_ISAR_DIR}/${DISTRO}:/isar-apt ${WORKDIR}:/isar-work"
+ROOTFS_MOUNTS ??= "${WORKDIR}:/isar-work"
 
 python () {
     mounts = d.getVar('ROOTFS_MOUNTS', False)
@@ -102,75 +112,6 @@ export LANG ??= "C"
 export LANGUAGE ??= "C"
 export LC_ALL ??= "C"
 
-# Execute a command against a rootfs and with isar-apt bind-mounted.
-# Additional mounts may be specified using --bind <source> <target> and a
-# custom directory for the command to be executed with --chdir <dir>. The
-# command is assumed to follow the special "--" argument. This would replace
-# "sudo chroot" calls especially when a native command may be used instead of
-# chroot'ed command and without elevated privileges (the command will likely
-# take the rootfs as argument; e.g. apt-get -o Dir=${ROOTFSDIR}). If the
-# optional rootfs argument is omitted, the host rootfs will be used (e.g. to
-# run native commands): this should be used with care.
-#
-# Usage: rootfs_cmd [options] [rootfs] -- command
-#
-rootfs_cmd() {
-    set -- "$@"
-    bwrap_args="--bind ${REPO_ISAR_DIR}/${DISTRO} /isar-apt"
-    bwrap_binds=""
-    bwrap_rootfs=""
-
-    while [ "${#}" -gt "0" ] && [ "$1" != "--" ]; do
-        case "$1" in
-            --bind)
-                if [ "${#}" -lt "3" ]; then
-                    bbfatal "--bind requires two arguments"
-                fi
-                bwrap_binds="${bwrap_binds} --bind $2 $3"
-                shift 3
-                ;;
-            --chdir)
-                if [ "${#}" -lt "2" ]; then
-                    bbfatal "$1 requires an argument"
-                fi
-                bwrap_args="${bwrap_args} $1 $2"
-                shift 2
-                ;;
-            -*)
-                bbfatal "$1 is not a supported option!"
-                ;;
-            *)
-                if [ -z "${bwrap_rootfs}" ]; then
-                    bwrap_rootfs="$1"
-                    shift
-                else
-                    bbfatal "unexpected argument '$1'"
-                fi
-                ;;
-        esac
-    done
-
-    if [ -n "${bwrap_rootfs}" ]; then
-        bwrap_args="${bwrap_args} --bind ${bwrap_rootfs} /"
-    fi
-
-    if [ "${#}" -le "1" ] || [ "$1" != "--" ]; then
-        bbfatal "no command specified (missing --)"
-    fi
-    shift  # remove "--", command and its arguments follows
-
-    # bin, lib and lib64 are only real directories on unmerged-usr rootfs
-    for ro_d in bin etc lib lib64 sys usr var; do
-        [ -d ${bwrap_rootfs}/${ro_d} ] && [ ! -L ${bwrap_rootfs}/${ro_d} ] || continue
-        bwrap_args="${bwrap_args} --ro-bind ${bwrap_rootfs}/${ro_d} /${ro_d}"
-    done
-
-    bwrap --unshare-user --unshare-pid ${bwrap_args} \
-        --dev-bind /dev /dev --proc /proc --tmpfs /tmp \
-        ${@'--bind "${REPO_ISAR_DIR}/${DISTRO}" /isar-apt' if d.getVar('ISAR_CHROOT_MODE') == 'unshare' else ''} \
-        ${bwrap_binds} -- "${@}"
-}
-
 rootfs_do_mounts[weight] = "3"
 python rootfs_do_mounts() {
     if d.getVar('ISAR_CHROOT_MODE') == 'schroot':
@@ -293,6 +234,20 @@ EOF
 EOSUDO
 }
 
+ROOTFS_CONFIGURE_COMMAND += "rootfs_redirect_isar_apt_to_host"
+rootfs_redirect_isar_apt_to_host() {
+    if [ -f "${ROOTFSDIR}/etc/apt/sources.list.d/isar-apt.list" ]; then
+        deb_dl_dir_isar_apt_to_host "${ROOTFSDIR}"
+    fi
+}
+
+# restore by latest before capturing the apt state and before sstate caching
+rootfs_redirect_isar_apt_to_target() {
+    if [ -f "${ROOTFSDIR}/etc/apt/sources.list.d/isar-apt.list" ]; then
+        deb_dl_dir_isar_apt_to_target "${ROOTFSDIR}"
+    fi
+}
+
 ROOTFS_CONFIGURE_COMMAND += "rootfs_configure_apt"
 rootfs_configure_apt[weight] = "2"
 rootfs_configure_apt() {
@@ -315,6 +270,14 @@ rootfs_configure_apt() {
 EOSUDO
 }
 
+# Point the host apt-get at the rootfs' apt.conf.d (see ROOTFS_HOST_APT_CONFIG).
+ROOTFS_CONFIGURE_COMMAND =+ "rootfs_configure_host_apt_config"
+rootfs_configure_host_apt_config[weight] = "1"
+rootfs_configure_host_apt_config() {
+    echo 'Dir::Etc::parts "${ROOTFSDIR}/etc/apt/apt.conf.d";' \
+        > '${ROOTFS_HOST_APT_CONFIG}'
+}
+
 rootfs_exclude_docs_drop() {
     if [ -d '${ROOTFSDIR}/usr/share/man' ]; then
         find '${ROOTFSDIR}/usr/share/man/' -mindepth 1 ! -type d -delete
@@ -371,7 +334,7 @@ rootfs_install_pkgs_update() {
     run_privileged_heredoc <<'EOF'
         set -e
         ${@insert_isar_mounts(d, d.getVar('ROOTFSDIR'), d.getVar('ROOTFS_MOUNTS')) if d.getVar('ISAR_CHROOT_MODE') == 'unshare' else ''}
-        chroot '${ROOTFSDIR}' /usr/bin/apt-get update \
+        ${HOST_APT_CMD} update \
             -o Dir::Etc::SourceList="sources.list.d/isar-apt.list" \
             -o Dir::Etc::SourceParts="-" \
             -o APT::Get::List-Cleanup="0"
@@ -402,10 +365,14 @@ rootfs_install_pkgs_download[progress] = "custom:rootfs_progress.PkgsDownloadPro
 rootfs_install_pkgs_download[isar-apt-lock] = "release-after"
 rootfs_install_pkgs_download[network] = "${TASK_USE_NETWORK}"
 rootfs_install_pkgs_download() {
-    # download packages using apt in a non-privileged namespace
-    rootfs_cmd --bind "${ROOTFSDIR}/var/cache/apt/archives" /var/cache/apt/archives \
-               ${ROOTFSDIR} \
-               -- /usr/bin/apt-get ${ROOTFS_APT_ARGS} -o Debug::NoLocking=1 --download-only ${ROOTFS_PACKAGES}
+    run_privileged_heredoc <<'EOF'
+        set -e
+        ${@insert_isar_mounts(d, d.getVar('ROOTFSDIR'), d.getVar('ROOTFS_MOUNTS')) if d.getVar('ISAR_CHROOT_MODE') == 'unshare' else ''}
+        ${HOST_APT_CMD} \
+            ${ROOTFS_APT_ARGS} \
+            -o Debug::NoLocking=1 \
+            --download-only ${ROOTFS_PACKAGES}
+EOF
 }
 
 ROOTFS_INSTALL_COMMAND_BEFORE_EXPORT ??= ""
@@ -421,16 +388,15 @@ ROOTFS_INSTALL_COMMAND += "rootfs_install_pkgs_isar_download"
 rootfs_install_pkgs_isar_download[weight] = "50"
 rootfs_install_pkgs_isar_download[isar-apt-lock] = "acquire-before release-after"
 rootfs_install_pkgs_isar_download() {
-    # Command apt-get install do not cache packages from local repos
-    # We can obtain non cached package URIs by recalling install command here
-    # No need to export those files to dl_dir, so we can run it right after
-    rootfs_cmd --bind "${ROOTFSDIR}/var/cache/apt/archives" /var/cache/apt/archives \
-               --chdir "/var/cache/apt/archives" \
-               ${ROOTFSDIR} \
-               -- /usr/bin/sh -c 'apt-get ${ROOTFS_APT_ARGS} --print-uris ${ROOTFS_PACKAGES} | \
-                                  sed -n "s|^.file:\(/[^'\'']*\.deb\). \([^ ]*\.deb\).*|\1 \2|p" | \
-                                  sed ":a; s|^\([^ ]*\)%|\1\\\\x|; ta" | \
-                                  while read -r path name; do cp -n "$(/usr/bin/printf "%b" "$path")" "$name" ; done'
+    run_privileged_heredoc <<'EOF'
+        set -e
+        ${@insert_isar_mounts(d, d.getVar('ROOTFSDIR'), d.getVar('ROOTFS_MOUNTS')) if d.getVar('ISAR_CHROOT_MODE') == 'unshare' else ''}
+        ${HOST_APT_CMD} \
+            ${ROOTFS_APT_ARGS} --print-uris ${ROOTFS_PACKAGES} | \
+                sed -n "s|^.file:\(/[^'\'']*\.deb\). \([^ ]*\.deb\).*|\1 \2|p" | \
+                sed ":a; s|^\([^ ]*\)%|\1\\\\x|; ta" | \
+                while read -r path name; do cp -n "$(/usr/bin/printf "%b" "$path")" "${ROOTFSDIR}/var/cache/apt/archives/$name" ; done
+EOF
 }
 
 ROOTFS_INSTALL_COMMAND += "${@ 'rootfs_install_clean_files' if (d.getVar('ROOTFS_CLEAN_FILES') or '').strip() else ''}"
@@ -451,8 +417,7 @@ rootfs_install_pkgs_install() {
     run_privileged_heredoc <<'EOF'
     set -e
     ${@insert_isar_mounts(d, d.getVar('ROOTFSDIR'), d.getVar('ROOTFS_MOUNTS')) if d.getVar('ISAR_CHROOT_MODE') == 'unshare' else ''}
-    chroot "${ROOTFSDIR}" \
-        /usr/bin/apt-get ${ROOTFS_APT_ARGS} --no-download ${ROOTFS_PACKAGES}
+    ${HOST_APT_CMD} ${ROOTFS_APT_ARGS} --no-download ${ROOTFS_PACKAGES}
 EOF
 }
 
@@ -472,7 +437,7 @@ rootfs_clear_initrd_symlinks() {
     run_privileged rm -f ${ROOTFSDIR}/initrd.img.old
 }
 
-ROOTFS_INSTALL_COMMAND += "${@bb.utils.contains('ROOTFS_FEATURES', 'generate-sbom', 'rootfs_capture_apt_state', '', d)}"
+ROOTFS_POSTPROCESS_COMMAND:prepend = "${@bb.utils.contains('ROOTFS_FEATURES', 'generate-sbom', 'rootfs_capture_apt_state', '', d)} "
 rootfs_capture_apt_state() {
     ( cd ${ROOTFSDIR} && find usr/share/doc -name copyright -print0 ) | \
     tar -cf ${WORKDIR}/${ROOTFS_APT_STATE} --zstd --sort=name \
@@ -485,6 +450,7 @@ rootfs_capture_apt_state() {
         var/lib/apt/extended_states \
         var/lib/dpkg/status
 }
+ROOTFS_POSTPROCESS_COMMAND:prepend = "rootfs_redirect_isar_apt_to_target "
 
 ROOTFS_INSTALL_DEPENDS ?= ""
 
@@ -583,8 +549,7 @@ cache_dbg_pkgs() {
 
 ROOTFS_POSTPROCESS_COMMAND += "${@bb.utils.contains('ROOTFS_FEATURES', 'clean-package-cache', 'rootfs_postprocess_clean_package_cache', '', d)}"
 rootfs_postprocess_clean_package_cache() {
-    run_in_chroot '${ROOTFSDIR}' \
-        /usr/bin/apt-get clean
+    run_privileged ${HOST_APT_CMD} clean
     # remove apt-cache folder itself (required in case rootfs is provided by sstate cache)
     run_privileged find "${ROOTFSDIR}/var/cache/apt" -type f \
         \( -name '*.deb' -o -name '*.bin' \) -delete
-- 
2.55.0

-- 
You received this message because you are subscribed to the Google Groups "isar-users" group.
To unsubscribe from this group and stop receiving emails from it, send an email to isar-users+unsubscribe@googlegroups.com.
To view this discussion visit https://groups.google.com/d/msgid/isar-users/20261002144936.246628-12-felix.moessbauer%40siemens.com.

  parent reply	other threads:[~2026-10-02 14:50 UTC|newest]

Thread overview: 14+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2026-10-02 14:49 [PATCH 00/13] More build time reductions 'Felix Moessbauer' via isar-users
2026-10-02 14:49 ` [PATCH 01/13] debianize: services are named after BPN not PN 'Felix Moessbauer' via isar-users
2026-10-02 14:49 ` [PATCH 02/13] dpkg-raw: do not leak package variants into debian package 'Felix Moessbauer' via isar-users
2026-10-02 14:49 ` [PATCH 03/13] debian templates: name package after BPN instead of PN 'Felix Moessbauer' via isar-users
2026-10-02 14:49 ` [PATCH 04/13] linux-distro: provide fake packages for native variant of packages 'Felix Moessbauer' via isar-users
2026-10-02 14:49 ` [PATCH 05/13] dpkg-source: let the native package variant own the source package 'Felix Moessbauer' via isar-users
2026-10-02 14:49 ` [PATCH 06/13] do not assert on legitimate re-execution of tasks 'Felix Moessbauer' via isar-users
2026-10-02 14:49 ` [PATCH 07/13] bitbake: backport cooker: use BB_HASHSERVE_DB_DIR for hash server database location 'Felix Moessbauer' via isar-users
2026-10-02 14:49 ` [PATCH 08/13] set default sstate signature handler to OEEquivHash 'Felix Moessbauer' via isar-users
2026-10-02 14:49 ` [PATCH 09/13] testsuite: add support to run with bitbake hashserver 'Felix Moessbauer' via isar-users
2026-10-02 14:49 ` [PATCH 10/13] isar-sstate: add support to clean hashes from hashequiv server 'Felix Moessbauer' via isar-users
2026-10-02 14:49 ` 'Felix Moessbauer' via isar-users [this message]
2026-10-02 14:49 ` [PATCH 12/13] bootstrap: do not include apt 'Felix Moessbauer' via isar-users
2026-10-02 14:49 ` [PATCH 13/13] bootstrap: do not add ca-certificates 'Felix Moessbauer' via isar-users

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=20261002144936.246628-12-felix.moessbauer@siemens.com \
    --to=isar-users@googlegroups.com \
    --cc=cedric.hombourger@siemens.com \
    --cc=felix.moessbauer@siemens.com \
    --cc=jan.kiszka@siemens.com \
    --cc=quirin.gylstorff@siemens.com \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox