From patchwork Sun Aug 30 21:48:37 2026 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: AdrianF X-Patchwork-Id: 96860 Return-Path: X-Spam-Checker-Version: SpamAssassin 3.4.0 (2014-02-07) on aws-us-west-2-korg-lkml-1.web.codeaurora.org Received: from aws-us-west-2-korg-lkml-1.web.codeaurora.org (localhost.localdomain [127.0.0.1]) by smtp.lore.kernel.org (Postfix) with ESMTP id C28A8C624D5 for ; Sun, 30 Aug 2026 21:49:27 +0000 (UTC) Received: from mta-64-225.siemens.flowmailer.net (mta-64-225.siemens.flowmailer.net [185.136.64.225]) by mx.groups.io with SMTP id smtpd.msgproc01-g2.17150.1788126560454563338 for ; Sun, 30 Aug 2026 14:49:21 -0700 Authentication-Results: mx.groups.io; dkim=pass header.i=adrian.freihofer@siemens.com header.s=fm2 header.b=iXqvgnbF; spf=pass (domain: rts-flowmailer.siemens.com, ip: 185.136.64.225, mailfrom: fm-1329275-20260830214918bbc413a5950002076f-__lnip@rts-flowmailer.siemens.com) Received: by mta-64-225.siemens.flowmailer.net with ESMTPSA id 20260830214918bbc413a5950002076f for ; Sun, 30 Aug 2026 23:49:18 +0200 DKIM-Signature: v=1; a=rsa-sha256; q=dns/txt; c=relaxed/relaxed; s=fm2; d=siemens.com; i=adrian.freihofer@siemens.com; h=Date:From:Subject:To:Message-ID:MIME-Version:Content-Type:Content-Transfer-Encoding:Cc:References:In-Reply-To; bh=zapn1ATaEPuLk0BjjMDJRIZsVJtdMIz1iekdlLb+m+U=; b=iXqvgnbFy1B2ln+W0yNm8X+BjW6qS1MANY/K2NP9yL9TjvcMtH/XqJkBHcDm1GULE3zult aeyi28trF520RJPiw+E0rkuqcoyUcKrTpolVQFJsWXzFTMWtQPO2I/ECZSaS5b6/LigUptGA iLpY+gLQXzrT5Lv2kxhF0yRNZcua+2oG2vVsbGAdwx5fpjwzsuLEts3y97pPqigtsOjd6Dj6 kSgWq/gIgE3+QDY6hodvEXEu+6cltpgSeOXbl8eAog+8K2OkfrT9IEvYYmXSIdR+bYOx41iS KqvoUHyKs1LeRtIkuJd9Lp2VQDlF+TL3cyjPftLvqzldNLIGrYRQT6qQ==; From: AdrianF To: openembedded-core@lists.openembedded.org Cc: Adrian Freihofer Subject: [PATCH v2 15/25] runqemu-extract-sdk: refactor in Python Date: Sun, 30 Aug 2026 23:48:37 +0200 Message-ID: <20260830214912.1346063-16-adrian.freihofer@siemens.com> In-Reply-To: <20260830214912.1346063-1-adrian.freihofer@siemens.com> References: <20260830214912.1346063-1-adrian.freihofer@siemens.com> MIME-Version: 1.0 X-Flowmailer-Platform: Siemens Feedback-ID: 519:519-1329275:519-21489:flowmailer List-Id: X-Webhook-Received: from 45-33-107-173.ip.linodeusercontent.com [45.33.107.173] by aws-us-west-2-korg-lkml-1.web.codeaurora.org with HTTPS for ; Sun, 30 Aug 2026 21:49:27 -0000 X-Groupsio-URL: https://lists.openembedded.org/g/openembedded-core/message/244701 From: Adrian Freihofer Move the pseudo-backed rootfs extraction logic into a reusable Python module while preserving the existing command interface. Install the module with qemu-helper so the wrapper also works from an SDK. AI-Generated: Uses GitHub Copilot Signed-off-by: Adrian Freihofer --- .../qemu/nativesdk-qemu-helper_1.0.bb | 3 + scripts/lib/runqemu_utils.py | 124 ++++++++++++++++++ scripts/runqemu-extract-sdk | 103 +-------------- 3 files changed, 133 insertions(+), 97 deletions(-) create mode 100644 scripts/lib/runqemu_utils.py diff --git a/meta/recipes-devtools/qemu/nativesdk-qemu-helper_1.0.bb b/meta/recipes-devtools/qemu/nativesdk-qemu-helper_1.0.bb index e1f343989a..16fc07fadf 100644 --- a/meta/recipes-devtools/qemu/nativesdk-qemu-helper_1.0.bb +++ b/meta/recipes-devtools/qemu/nativesdk-qemu-helper_1.0.bb @@ -9,6 +9,7 @@ RDEPENDS:${PN} = "nativesdk-qemu nativesdk-unfs3 nativesdk-pseudo \ LIC_FILES_CHKSUM = "file://${COREBASE}/scripts/runqemu;beginline=5;endline=10;md5=ac2b489a58739c7628a2604698db5e7f" SRC_URI = "file://${COREBASE}/scripts/runqemu \ + file://${COREBASE}/scripts/lib/runqemu_utils.py \ file://${COREBASE}/scripts/runqemu-addptable2image \ file://${COREBASE}/scripts/runqemu-gen-tapdevs \ file://${COREBASE}/scripts/runqemu-ifup \ @@ -30,4 +31,6 @@ do_install() { install -d ${D}${bindir} install -m 0755 ${S}${COREBASE}/scripts/oe-* ${D}${bindir}/ install -m 0755 ${S}${COREBASE}/scripts/runqemu* ${D}${bindir}/ + # The runqemu-* wrappers import this module from their own directory. + install -m 0644 ${S}${COREBASE}/scripts/lib/runqemu_utils.py ${D}${bindir}/ } diff --git a/scripts/lib/runqemu_utils.py b/scripts/lib/runqemu_utils.py new file mode 100644 index 0000000000..dff0ca8d3a --- /dev/null +++ b/scripts/lib/runqemu_utils.py @@ -0,0 +1,124 @@ +#!/usr/bin/env python3 +# +# Helpers shared by runqemu and NFS-rootfs preparation tools. +# +# SPDX-License-Identifier: GPL-2.0-only + +"""Extract and export rootfs tarballs for NFS booting.""" + +import os +import subprocess +import sys + + +class RunQemuRootfsError(Exception): + """Raised when an NFS rootfs cannot be prepared or exported.""" + + +def native_environment(): + """Return the qemu-helper-native environment set by oe-find-native-sysroot.""" + command = ''' + helper=$(command -v oe-find-native-sysroot) || exit 127 + . "$helper" qemu-helper-native >/dev/null + env -0 + printf 'PSEUDO=%s\\0OECORE_NATIVE_SYSROOT=%s\\0' "$PSEUDO" "$OECORE_NATIVE_SYSROOT" + ''' + result = subprocess.run(['bash', '-c', command], capture_output=True) + if result.returncode: + if result.returncode == 127: + raise RunQemuRootfsError( + 'Unable to find the oe-find-native-sysroot script.\n' + 'Did you forget to source your build system environment setup script?') + raise RunQemuRootfsError( + result.stderr.decode(errors='replace').strip() or + 'Unable to set up the qemu-helper-native sysroot') + + environment = {} + for entry in result.stdout.split(b'\0'): + if b'=' in entry: + key, value = entry.split(b'=', 1) + environment[key.decode()] = value.decode() + return environment + + +def _tar_options(rootfs_tarball): + if rootfs_tarball.endswith('.tar.xz'): + return ['--numeric-owner', '-xJf'] + if rootfs_tarball.endswith('.tar.bz2'): + return ['--numeric-owner', '-xjf'] + if rootfs_tarball.endswith('.tar.gz'): + return ['--numeric-owner', '-xzf'] + if rootfs_tarball.endswith('.tar.zst'): + return ['--numeric-owner', '--zstd', '-xf'] + if rootfs_tarball.endswith('.tar'): + return ['--numeric-owner', '-xf'] + raise RunQemuRootfsError( + 'Unable to determine sdk tarball format\n' + 'Accepted types: .tar / .tar.gz / .tar.bz2 / .tar.xz / .tar.zst') + + +def pseudo_state_dir(rootfs_dir): + """Return the pseudo database location associated with an extracted rootfs.""" + rootfs_dir = os.path.realpath(rootfs_dir) + return os.path.join(os.path.dirname(rootfs_dir), + os.path.basename(rootfs_dir) + '.pseudo_state') + + +def extract_sdk_rootfs(rootfs_tarball, rootfs_dir): + """Extract a rootfs tarball under pseudo and return its absolute directory.""" + if not os.path.exists(rootfs_tarball): + raise RunQemuRootfsError("sdk tarball '%s' does not exist" % rootfs_tarball) + + rootfs_tarball = os.path.realpath(rootfs_tarball) + rootfs_dir = os.path.realpath(rootfs_dir) + tar_options = _tar_options(rootfs_tarball) + state_dir = pseudo_state_dir(rootfs_dir) + debug_image = '-dbg' in os.path.basename(rootfs_tarball) + + if os.path.exists(state_dir) and not debug_image: + raise RunQemuRootfsError( + '%s already exists!\n' + 'Please delete the rootfs tree and pseudo directory manually\n' + 'if this is really what you want.' % state_dir) + + os.makedirs(rootfs_dir, exist_ok=True) + os.makedirs(state_dir, exist_ok=True) + open(os.path.join(state_dir, 'pseudo.pid'), 'a').close() + + environment = native_environment() + environment['PSEUDO_LOCALSTATEDIR'] = state_dir + environment['PSEUDO_INCLUDE_PATHS'] = rootfs_dir + pseudo = environment.get('PSEUDO') + native_sysroot = environment.get('OECORE_NATIVE_SYSROOT') + if not pseudo or not native_sysroot: + raise RunQemuRootfsError('qemu-helper-native did not provide pseudo') + + command = [pseudo, '-P', os.path.join(native_sysroot, 'usr'), 'tar', '-C', rootfs_dir] + command.extend(tar_options) + command.append(rootfs_tarball) + print('Extracting rootfs tarball using pseudo...') + print(' '.join(command)) + try: + subprocess.run(command, env=environment, check=True) + except subprocess.CalledProcessError as exc: + raise RunQemuRootfsError('Failed to extract rootfs tarball') from exc + + if len(os.listdir(rootfs_dir)) < 4: + print("Warning: I don't see many files in %s" % rootfs_dir) + print('Please double-check the extraction worked as intended') + else: + print('SDK image successfully extracted to %s' % rootfs_dir) + return rootfs_dir + + +def extract_sdk_main(argv=None): + argv = sys.argv[1:] if argv is None else argv + if len(argv) != 2: + print('Usage: %s ' % sys.argv[0]) + return 1 + try: + extract_sdk_rootfs(*argv) + except RunQemuRootfsError as exc: + print('Error: %s' % exc) + return 1 + return 0 diff --git a/scripts/runqemu-extract-sdk b/scripts/runqemu-extract-sdk index db9813e1dc..6cad2fdf90 100755 --- a/scripts/runqemu-extract-sdk +++ b/scripts/runqemu-extract-sdk @@ -1,104 +1,13 @@ -#!/bin/bash -# -# This utility extracts an SDK image tarball using pseudo, and stores -# the pseudo database in var/pseudo within the rootfs. If you want to -# boot QEMU using an nfsroot, you *must* use this script to create the -# rootfs to ensure it is done correctly with pseudo. -# -# Copyright (c) 2010 Intel Corp. +#!/usr/bin/env python3 # # SPDX-License-Identifier: GPL-2.0-only -# -function usage() { - echo "Usage: $0 " -} +import os +import sys -if [ $# -ne 2 ]; then - usage - exit 1 -fi +sys.path.insert(0, os.path.join(os.path.dirname(os.path.realpath(__file__)), 'lib')) -SYSROOT_SETUP_SCRIPT=`which oe-find-native-sysroot 2> /dev/null` -if [ -z "$SYSROOT_SETUP_SCRIPT" ]; then - echo "Error: Unable to find the oe-find-native-sysroot script" - echo "Did you forget to source your build system environment setup script?" - exit 1 -fi -. $SYSROOT_SETUP_SCRIPT qemu-helper-native -PSEUDO_OPTS="-P $OECORE_NATIVE_SYSROOT/usr" +from runqemu_utils import extract_sdk_main -ROOTFS_TARBALL=$1 -SDK_ROOTFS_DIR=$2 -if [ ! -e "$ROOTFS_TARBALL" ]; then - echo "Error: sdk tarball '$ROOTFS_TARBALL' does not exist" - usage - exit 1 -fi - -# Convert SDK_ROOTFS_DIR to a full pathname -if [[ ${SDK_ROOTFS_DIR:0:1} != "/" ]]; then - SDK_ROOTFS_DIR=$(readlink -f $(pwd)/$SDK_ROOTFS_DIR) -fi - -TAR_OPTS="" -if [[ "$ROOTFS_TARBALL" =~ tar\.xz$ ]]; then - TAR_OPTS="--numeric-owner -xJf" -fi -if [[ "$ROOTFS_TARBALL" =~ tar\.bz2$ ]]; then - TAR_OPTS="--numeric-owner -xjf" -fi -if [[ "$ROOTFS_TARBALL" =~ tar\.gz$ ]]; then - TAR_OPTS="--numeric-owner -xzf" -fi -if [[ "$ROOTFS_TARBALL" =~ tar\.zst$ ]]; then - TAR_OPTS="--numeric-owner --zstd -xf" -fi -if [[ "$ROOTFS_TARBALL" =~ \.tar$ ]]; then - TAR_OPTS="--numeric-owner -xf" -fi -if [ -z "$TAR_OPTS" ]; then - echo "Error: Unable to determine sdk tarball format" - echo "Accepted types: .tar / .tar.gz / .tar.bz2 / .tar.xz / tar.zst" - exit 1 -fi - -if [ ! -d "$SDK_ROOTFS_DIR" ]; then - echo "Creating directory $SDK_ROOTFS_DIR" - mkdir -p "$SDK_ROOTFS_DIR" -fi - -pseudo_state_dir="$SDK_ROOTFS_DIR/../$(basename "$SDK_ROOTFS_DIR").pseudo_state" -pseudo_state_dir="$(readlink -f $pseudo_state_dir)" - -debug_image="`echo $ROOTFS_TARBALL | grep '\-dbg\.rootfs\.tar'`" - -if [ -e "$pseudo_state_dir" -a -z "$debug_image" ]; then - echo "Error: $pseudo_state_dir already exists!" - echo "Please delete the rootfs tree and pseudo directory manually" - echo "if this is really what you want." - exit 1 -fi - -mkdir -p "$pseudo_state_dir" -touch "$pseudo_state_dir/pseudo.pid" -PSEUDO_LOCALSTATEDIR="$pseudo_state_dir" -export PSEUDO_LOCALSTATEDIR -PSEUDO_INCLUDE_PATHS="$SDK_ROOTFS_DIR" -export PSEUDO_INCLUDE_PATHS - -echo "Extracting rootfs tarball using pseudo..." -echo "$PSEUDO $PSEUDO_OPTS tar -C \"$SDK_ROOTFS_DIR\" $TAR_OPTS \"$ROOTFS_TARBALL\"" -$PSEUDO $PSEUDO_OPTS tar -C "$SDK_ROOTFS_DIR" $TAR_OPTS "$ROOTFS_TARBALL" - -DIRCHECK=`ls -l "$SDK_ROOTFS_DIR" | wc -l` -if [ "$DIRCHECK" -lt 5 ]; then - echo "Warning: I don't see many files in $SDK_ROOTFS_DIR" - echo "Please double-check the extraction worked as intended" - exit 0 -fi - -echo "SDK image successfully extracted to $SDK_ROOTFS_DIR" - -exit 0 +sys.exit(extract_sdk_main())