summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorBruce Ashfield <bruce.ashfield@gmail.com>2026-01-05 03:28:17 +0000
committerBruce Ashfield <bruce.ashfield@gmail.com>2026-02-09 03:32:52 +0000
commitc03fa452f381d54af66c6bc0d0394622c3d3d61f (patch)
treef341cba988b577493ec9228ac1c8cb063a10e33f
parentf9f8e294e5d870f28dd65f13ddb43c224be958fc (diff)
downloadmeta-virtualization-c03fa452f381d54af66c6bc0d0394622c3d3d61f.tar.gz
vcontainer: add SDK-based standalone tarball
Add vcontainer-tarball.bb recipe that creates a relocatable standalone distribution of vdkr and vpdmn container tools using Yocto SDK infrastructure. Features: - Auto-detects available architectures from built blobs - Custom SDK installer with vcontainer-specific messages - nativesdk-qemu-vcontainer.bb: minimal QEMU without OpenGL deps Recipe changes: - DELETE vdkr-native_1.0.bb (functionality moved to SDK) - DELETE vpdmn-native_1.0.bb (functionality moved to SDK) - ADD vcontainer-tarball.bb (SDK tarball recipe) - ADD toolchain-shar-extract.sh (SDK installer template) - ADD nativesdk-qemu-vcontainer.bb (minimal QEMU for SDK) Usage: MACHINE=qemux86-64 bitbake vcontainer-tarball ./tmp/deploy/sdk/vcontainer-standalone.sh -d /opt/vcontainer -y source /opt/vcontainer/init-env.sh vdkr images Signed-off-by: Bruce Ashfield <bruce.ashfield@gmail.com>
-rw-r--r--recipes-containers/vcontainer/README.md84
-rw-r--r--recipes-containers/vcontainer/files/toolchain-shar-extract.sh326
-rwxr-xr-xrecipes-containers/vcontainer/files/vrunner.sh10
-rw-r--r--recipes-containers/vcontainer/vcontainer-tarball.bb448
-rw-r--r--recipes-containers/vcontainer/vdkr-native_1.0.bb191
-rw-r--r--recipes-containers/vcontainer/vpdmn-native_1.0.bb177
-rw-r--r--recipes-devtools/qemu/nativesdk-qemu-vcontainer.bb65
7 files changed, 899 insertions, 402 deletions
diff --git a/recipes-containers/vcontainer/README.md b/recipes-containers/vcontainer/README.md
index 4b7b03ed..76d54d18 100644
--- a/recipes-containers/vcontainer/README.md
+++ b/recipes-containers/vcontainer/README.md
@@ -10,8 +10,10 @@ Execute Docker or Podman commands inside a QEMU-emulated target environment.
10## Quick Start 10## Quick Start
11 11
12```bash 12```bash
13# Build vdkr 13# Build and install SDK (see "Standalone SDK" section for full instructions)
14bitbake vdkr-native 14MACHINE=qemux86-64 bitbake vcontainer-tarball
15./tmp/deploy/sdk/vcontainer-standalone.sh -d /tmp/vcontainer -y
16source /tmp/vcontainer/init-env.sh
15 17
16# List images (uses host architecture by default) 18# List images (uses host architecture by default)
17vdkr images 19vdkr images
@@ -190,34 +192,52 @@ vdkr --stateless images # Empty
190vdkr clean 192vdkr clean
191``` 193```
192 194
193## Standalone Distribution 195## Standalone SDK
194 196
195Create a self-contained redistributable tarball that works without Yocto: 197Create a self-contained redistributable SDK that works without Yocto:
196 198
197```bash 199```bash
198# Build the standalone tarball 200# Ensure multiconfig is enabled in local.conf:
199MACHINE=qemux86-64 bitbake vdkr-native -c create_tarball 201# BBMULTICONFIG = "vruntime-aarch64 vruntime-x86-64"
200 202
201# Output: tmp/deploy/vdkr/vdkr-standalone-x86_64.tar.gz 203# Step 1: Build blobs for desired architectures (sequentially to avoid deadlocks)
204bitbake mc:vruntime-x86-64:vdkr-initramfs-create mc:vruntime-x86-64:vpdmn-initramfs-create
205bitbake mc:vruntime-aarch64:vdkr-initramfs-create mc:vruntime-aarch64:vpdmn-initramfs-create
206
207# Step 2: Build SDK (auto-detects available architectures)
208MACHINE=qemux86-64 bitbake vcontainer-tarball
209
210# Output: tmp/deploy/sdk/vcontainer-standalone.sh
211```
212
213To limit architectures, set in local.conf:
214```bash
215VCONTAINER_ARCHITECTURES = "x86_64" # x86_64 only
216VCONTAINER_ARCHITECTURES = "aarch64" # aarch64 only
217VCONTAINER_ARCHITECTURES = "x86_64 aarch64" # both (default if both built)
202``` 218```
203 219
204The tarball includes: 220The SDK includes:
205- `vdkr` - Main CLI script 221- `vdkr`, `vpdmn` - Main CLI scripts
206- `vdkr-aarch64`, `vdkr-x86_64` - Symlinks (only for available architectures) 222- `vdkr-<arch>`, `vpdmn-<arch>` - Symlinks for each included architecture
207- `vrunner.sh` - QEMU runner 223- `vrunner.sh` - Shared QEMU runner
208- `vdkr-blobs/` - Kernel and initramfs per architecture 224- `vdkr-blobs/`, `vpdmn-blobs/` - Kernel and initramfs per architecture
209- `qemu/` - QEMU system emulators with wrapper scripts 225- `sysroots/` - SDK binaries (QEMU, socat, libraries)
210- `lib/` - Shared libraries for QEMU
211- `share/qemu/` - QEMU firmware files
212- `socat` - Socket communication for memres mode
213- `init-env.sh` - Environment setup script 226- `init-env.sh` - Environment setup script
214 227
215Usage: 228Usage:
216```bash 229```bash
217tar -xzf vdkr-standalone-x86_64.tar.gz 230# Install (self-extracting)
218cd vdkr-standalone 231./vcontainer-standalone.sh -d /tmp/vcontainer -y
232
233# Or extract tarball directly
234tar -xf vcontainer-standalone.tar.xz -C /tmp/vcontainer
235
236# Use
237cd /tmp/vcontainer
219source init-env.sh 238source init-env.sh
220vdkr images 239vdkr-x86_64 images
240vdkr-aarch64 images
221``` 241```
222 242
223## Interactive Mode 243## Interactive Mode
@@ -297,13 +317,13 @@ vdkr rm -f debug
297See `tests/README.md` for the pytest-based test suite: 317See `tests/README.md` for the pytest-based test suite:
298 318
299```bash 319```bash
300# Build standalone tarball 320# Build and install SDK
301MACHINE=qemux86-64 bitbake vdkr-native -c create_tarball 321MACHINE=qemux86-64 bitbake vcontainer-tarball
322./tmp/deploy/sdk/vcontainer-standalone.sh -d /tmp/vcontainer -y
302 323
303# Extract and run tests 324# Run tests
304cd /tmp && tar -xzf .../vdkr-standalone-x86_64.tar.gz
305cd /opt/bruce/poky/meta-virtualization 325cd /opt/bruce/poky/meta-virtualization
306pytest tests/test_vdkr.py -v --vdkr-dir /tmp/vdkr-standalone 326pytest tests/test_vdkr.py -v --vdkr-dir /tmp/vcontainer
307``` 327```
308 328
309## vpdmn (Podman) 329## vpdmn (Podman)
@@ -331,9 +351,7 @@ Key differences from vdkr:
331 351
332| Recipe | Purpose | 352| Recipe | Purpose |
333|--------|---------| 353|--------|---------|
334| `vdkr-native_1.0.bb` | Main vdkr (Docker) CLI and blobs | 354| `vcontainer-tarball.bb` | Standalone SDK with vdkr and vpdmn |
335| `vpdmn-native_1.0.bb` | Main vpdmn (Podman) CLI and blobs |
336| `vcontainer-native_1.0.bb` | Unified tarball with both tools |
337| `vdkr-initramfs-create_1.0.bb` | Build vdkr initramfs blobs | 355| `vdkr-initramfs-create_1.0.bb` | Build vdkr initramfs blobs |
338| `vpdmn-initramfs-create_1.0.bb` | Build vpdmn initramfs blobs | 356| `vpdmn-initramfs-create_1.0.bb` | Build vpdmn initramfs blobs |
339 357
@@ -347,18 +365,16 @@ Key differences from vdkr:
347| `vdkr-init.sh` | Docker init script (baked into initramfs) | 365| `vdkr-init.sh` | Docker init script (baked into initramfs) |
348| `vpdmn-init.sh` | Podman init script (daemonless) | 366| `vpdmn-init.sh` | Podman init script (daemonless) |
349 367
350## Testing 368## Testing Both Tools
351 369
352```bash 370```bash
353# Build unified standalone tarball 371# Build and install SDK (includes both vdkr and vpdmn)
354bitbake vcontainer-native -c create_tarball 372MACHINE=qemux86-64 bitbake vcontainer-tarball
355 373./tmp/deploy/sdk/vcontainer-standalone.sh -d /tmp/vcontainer -y
356# Extract
357cd /tmp && tar -xzf .../vcontainer-standalone-*.tar.gz
358 374
359# Run tests for both tools 375# Run tests for both tools
360cd /opt/bruce/poky/meta-virtualization 376cd /opt/bruce/poky/meta-virtualization
361pytest tests/test_vdkr.py tests/test_vpdmn.py -v --vdkr-dir /tmp/vcontainer-standalone 377pytest tests/test_vdkr.py tests/test_vpdmn.py -v --vdkr-dir /tmp/vcontainer
362``` 378```
363 379
364## See Also 380## See Also
diff --git a/recipes-containers/vcontainer/files/toolchain-shar-extract.sh b/recipes-containers/vcontainer/files/toolchain-shar-extract.sh
new file mode 100644
index 00000000..5e72f14d
--- /dev/null
+++ b/recipes-containers/vcontainer/files/toolchain-shar-extract.sh
@@ -0,0 +1,326 @@
1#!/bin/sh
2# SPDX-FileCopyrightText: Copyright (C) 2025 Bruce Ashfield
3#
4# SPDX-License-Identifier: GPL-2.0-only
5
6export LC_ALL=en_US.UTF-8
7
8# The pipefail option is now part of POSIX (POSIX.1-2024) and available in more
9# and more shells. Enable it if available to make the SDK installer more robust.
10(set -o pipefail 2> /dev/null) && set -o pipefail
11
12#Make sure at least one python is installed
13INIT_PYTHON=$(command -v python3 2>/dev/null )
14[ -z "$INIT_PYTHON" ] && INIT_PYTHON=$(command -v python2 2>/dev/null)
15[ -z "$INIT_PYTHON" ] && echo "Error: The SDK needs a python installed" && exit 1
16
17# Remove invalid PATH elements first (maybe from a previously setup toolchain now deleted
18PATH=`$INIT_PYTHON -c 'import os; print(":".join(e for e in os.environ["PATH"].split(":") if os.path.exists(e)))'`
19
20tweakpath () {
21 case ":${PATH}:" in
22 *:"$1":*)
23 ;;
24 *)
25 PATH=$PATH:$1
26 esac
27}
28
29# Some systems don't have /usr/sbin or /sbin in the cleaned environment PATH but we make need it
30# for the system's host tooling checks
31tweakpath /usr/sbin
32tweakpath /sbin
33
34INST_ARCH=$(uname -m | sed -e "s/i[3-6]86/ix86/" -e "s/x86[-_]64/x86_64/")
35SDK_ARCH=$(echo @SDK_ARCH@ | sed -e "s/i[3-6]86/ix86/" -e "s/x86[-_]64/x86_64/")
36
37verlte () {
38 [ "$1" = "`printf "$1\n$2" | sort -V | head -n1`" ]
39}
40
41verlt() {
42 [ "$1" = "$2" ] && return 1 || verlte $1 $2
43}
44
45verlt `uname -r` @OLDEST_KERNEL@
46if [ $? = 0 ]; then
47 echo "Error: The SDK needs a kernel > @OLDEST_KERNEL@"
48 exit 1
49fi
50
51if [ "$INST_ARCH" != "$SDK_ARCH" ]; then
52 # Allow for installation of ix86 SDK on x86_64 host
53 if [ "$INST_ARCH" != x86_64 -o "$SDK_ARCH" != ix86 ]; then
54 echo "Error: Incompatible SDK installer! Your host is $INST_ARCH and this SDK was built for $SDK_ARCH hosts."
55 exit 1
56 fi
57fi
58
59if ! xz -V > /dev/null 2>&1; then
60 echo "Error: xz is required for installation of this SDK, please install it first"
61 exit 1
62fi
63
64SDK_BUILD_PATH="@SDKPATH@"
65DEFAULT_INSTALL_DIR="@SDKPATHINSTALL@"
66SUDO_EXEC=""
67EXTRA_TAR_OPTIONS=""
68target_sdk_dir=""
69answer=""
70relocate=1
71savescripts=0
72verbose=0
73publish=0
74listcontents=0
75while getopts ":yd:npDRSl" OPT; do
76 case $OPT in
77 y)
78 answer="Y"
79 ;;
80 d)
81 target_sdk_dir=$OPTARG
82 ;;
83 n)
84 prepare_buildsystem="no"
85 ;;
86 p)
87 prepare_buildsystem="no"
88 publish=1
89 ;;
90 D)
91 verbose=1
92 ;;
93 R)
94 relocate=0
95 savescripts=1
96 ;;
97 S)
98 savescripts=1
99 ;;
100 l)
101 listcontents=1
102 ;;
103 *)
104 echo "Usage: $(basename "$0") [-y] [-d <dir>]"
105 echo " -y Automatic yes to all prompts"
106 echo " -d <dir> Install the SDK to <dir>"
107 echo "======== Extensible SDK only options ============"
108 echo " -n Do not prepare the build system"
109 echo " -p Publish mode (implies -n)"
110 echo "======== Advanced DEBUGGING ONLY OPTIONS ========"
111 echo " -S Save relocation scripts"
112 echo " -R Do not relocate executables"
113 echo " -D use set -x to see what is going on"
114 echo " -l list files that will be extracted"
115 exit 1
116 ;;
117 esac
118done
119
120payload_offset=$(($(grep -na -m1 "^MARKER:$" "$0"|cut -d':' -f1) + 1))
121if [ "$listcontents" = "1" ] ; then
122 if [ @SDK_ARCHIVE_TYPE@ = "zip" ]; then
123 tail -n +$payload_offset "$0" > sdk.zip
124 if unzip -l sdk.zip;then
125 rm sdk.zip
126 else
127 rm sdk.zip && exit 1
128 fi
129 else
130 tail -n +$payload_offset "$0"| tar tvJ || exit 1
131 fi
132 exit
133fi
134
135titlestr="@SDK_TITLE@ installer version @SDK_VERSION@"
136printf "%s\n" "$titlestr"
137printf "%${#titlestr}s\n" | tr " " "="
138
139if [ $verbose = 1 ] ; then
140 set -x
141fi
142
143@SDK_PRE_INSTALL_COMMAND@
144
145# SDK_EXTENSIBLE is exposed from the SDK_PRE_INSTALL_COMMAND above
146if [ "$SDK_EXTENSIBLE" = "1" ]; then
147 DEFAULT_INSTALL_DIR="@SDKEXTPATH@"
148fi
149
150if [ "$target_sdk_dir" = "" ]; then
151 if [ "$answer" = "Y" ]; then
152 target_sdk_dir="$DEFAULT_INSTALL_DIR"
153 else
154 read -p "Enter target directory for SDK (default: $DEFAULT_INSTALL_DIR): " target_sdk_dir
155 [ "$target_sdk_dir" = "" ] && target_sdk_dir=$DEFAULT_INSTALL_DIR
156 fi
157fi
158
159eval target_sdk_dir=$(echo "$target_sdk_dir"|sed 's/ /\\ /g')
160if [ -d "$target_sdk_dir" ]; then
161 target_sdk_dir=$(cd "$target_sdk_dir"; pwd)
162else
163 target_sdk_dir=$(readlink -m "$target_sdk_dir")
164fi
165
166# limit the length for target_sdk_dir, ensure the relocation behaviour in relocate_sdk.py has right result.
167# This is due to ELF interpreter being set to 'a'*1024 in
168# meta/recipes-core/meta/uninative-tarball.bb
169if [ ${#target_sdk_dir} -gt 1024 ]; then
170 echo "Error: The target directory path is too long!!!"
171 exit 1
172fi
173
174if [ "$SDK_EXTENSIBLE" = "1" ]; then
175 # We're going to be running the build system, additional restrictions apply
176 if echo "$target_sdk_dir" | grep -q '[+\ @$]'; then
177 echo "The target directory path ($target_sdk_dir) contains illegal" \
178 "characters such as spaces, @, \$ or +. Abort!"
179 exit 1
180 fi
181 # The build system doesn't work well with /tmp on NFS
182 fs_dev_path="$target_sdk_dir"
183 while [ ! -d "$fs_dev_path" ] ; do
184 fs_dev_path=`dirname $fs_dev_path`
185 done
186 fs_dev_type=`stat -f -c '%t' "$fs_dev_path"`
187 if [ "$fsdevtype" = "6969" ] ; then
188 echo "The target directory path $target_sdk_dir is on NFS, this is not possible. Abort!"
189 exit 1
190 fi
191else
192 if [ -n "$(echo $target_sdk_dir|grep ' ')" ]; then
193 echo "The target directory path ($target_sdk_dir) contains spaces. Abort!"
194 exit 1
195 fi
196fi
197
198if [ -e "$target_sdk_dir/environment-setup-@REAL_MULTIMACH_TARGET_SYS@" ]; then
199 echo "The directory \"$target_sdk_dir\" already contains vcontainer tools."
200 printf "If you continue, existing files will be overwritten! Proceed [y/N]? "
201
202 default_answer="n"
203else
204 printf "You are about to install vcontainer tools (vdkr/vpdmn) to \"$target_sdk_dir\". Proceed [Y/n]? "
205
206 default_answer="y"
207fi
208
209if [ "$answer" = "" ]; then
210 read answer
211 [ "$answer" = "" ] && answer="$default_answer"
212else
213 echo $answer
214fi
215
216if [ "$answer" != "Y" -a "$answer" != "y" ]; then
217 echo "Installation aborted!"
218 exit 1
219fi
220
221# Try to create the directory (this will not succeed if user doesn't have rights)
222mkdir -p $target_sdk_dir >/dev/null 2>&1
223
224# if don't have the right to access dir, gain by sudo
225if [ ! -x $target_sdk_dir -o ! -w $target_sdk_dir -o ! -r $target_sdk_dir ]; then
226 if [ "$SDK_EXTENSIBLE" = "1" ]; then
227 echo "Unable to access \"$target_sdk_dir\", will not attempt to use" \
228 "sudo as as extensible SDK cannot be used as root."
229 exit 1
230 fi
231
232 SUDO_EXEC=$(command -v "sudo")
233 if [ -z $SUDO_EXEC ]; then
234 echo "No command 'sudo' found, please install sudo first. Abort!"
235 exit 1
236 fi
237
238 # test sudo could gain root right
239 $SUDO_EXEC pwd >/dev/null 2>&1
240 [ $? -ne 0 ] && echo "Sorry, you are not allowed to execute as root." && exit 1
241
242 # now that we have sudo rights, create the directory
243 $SUDO_EXEC mkdir -p $target_sdk_dir >/dev/null 2>&1
244fi
245
246printf "Extracting vcontainer tools..."
247if [ @SDK_ARCHIVE_TYPE@ = "zip" ]; then
248 if [ -z "$(command -v unzip)" ]; then
249 echo "Aborted, unzip is required to extract the SDK archive, please make sure it's installed on your system!"
250 exit 1
251 fi
252 tail -n +$payload_offset "$0" > sdk.zip
253 if $SUDO_EXEC unzip $EXTRA_TAR_OPTIONS sdk.zip -d $target_sdk_dir;then
254 rm sdk.zip
255 else
256 rm sdk.zip && exit 1
257 fi
258elif [ @SDK_ARCHIVE_TYPE@ = "tar.zst" ]; then
259 if [ -z "$(command -v zstd)" ]; then
260 echo "Aborted, zstd is required to extract the SDK archive, please make sure it's installed on your system!"
261 exit 1
262 fi
263 tail -n +$payload_offset "$0"| zstd -T0 -dc | $SUDO_EXEC tar mx -C $target_sdk_dir --checkpoint=.2500 $EXTRA_TAR_OPTIONS || exit 1
264else
265 if [ -z "$(command -v xz)" ]; then
266 echo "Aborted, xz is required to extract the SDK archive, please make sure it's installed on your system!"
267 exit 1
268 fi
269 tail -n +$payload_offset "$0"| $SUDO_EXEC tar mxJ -C $target_sdk_dir --checkpoint=.2500 $EXTRA_TAR_OPTIONS || exit 1
270fi
271echo "done"
272
273printf "Configuring paths..."
274# fix environment paths
275real_env_setup_script=""
276for env_setup_script in `ls $target_sdk_dir/environment-setup-*`; do
277 if grep -q 'OECORE_NATIVE_SYSROOT=' $env_setup_script; then
278 # Handle custom env setup scripts that are only named
279 # environment-setup-* so that they have relocation
280 # applied - what we want beyond here is the main one
281 # rather than the one that simply sorts last
282 real_env_setup_script="$env_setup_script"
283 fi
284 $SUDO_EXEC sed -e "s:@SDKPATH@:$target_sdk_dir:g" -i $env_setup_script
285done
286if [ -n "$real_env_setup_script" ] ; then
287 env_setup_script="$real_env_setup_script"
288fi
289echo "done"
290
291@SDK_POST_INSTALL_COMMAND@
292
293# delete the relocating script, so that user is forced to re-run the installer
294# if he/she wants another location for the sdk
295if [ $savescripts = 0 ] ; then
296 $SUDO_EXEC rm -f ${env_setup_script%/*}/relocate_sdk.py ${env_setup_script%/*}/relocate_sdk.sh
297fi
298
299# Execute post-relocation script
300post_relocate="$target_sdk_dir/post-relocate-setup.sh"
301if [ -e "$post_relocate" ]; then
302 $SUDO_EXEC sed -e "s:@SDKPATH@:$target_sdk_dir:g" -i $post_relocate
303 $SUDO_EXEC /bin/sh $post_relocate "$target_sdk_dir" "@SDKPATH@"
304 if [ $? -ne 0 ]; then
305 echo "Executing $post_relocate failed"
306 exit 1
307 fi
308 $SUDO_EXEC rm -f $post_relocate
309fi
310
311echo ""
312echo "vcontainer tools installed successfully!"
313echo ""
314echo "To use, source the environment script:"
315echo " \$ . $target_sdk_dir/init-env.sh"
316echo ""
317echo "Architectures included: @VCONTAINER_ARCHITECTURES@"
318echo ""
319echo "Then run:"
320echo " \$ vdkr-<arch> images # Docker"
321echo " \$ vpdmn-<arch> images # Podman"
322echo ""
323
324exit 0
325
326MARKER:
diff --git a/recipes-containers/vcontainer/files/vrunner.sh b/recipes-containers/vcontainer/files/vrunner.sh
index 588261ff..2be33765 100755
--- a/recipes-containers/vcontainer/files/vrunner.sh
+++ b/recipes-containers/vcontainer/files/vrunner.sh
@@ -872,6 +872,16 @@ if [ -n "$STATE_DIR" ]; then
872 mkdir -p "$STATE_DIR" 872 mkdir -p "$STATE_DIR"
873 STATE_IMG="$STATE_DIR/$STATE_FILE" 873 STATE_IMG="$STATE_DIR/$STATE_FILE"
874 874
875 # Migration: vpdmn used to use docker-state.img, now uses podman-state.img
876 # If old file exists but new file doesn't, rename it automatically
877 if [ "$STATE_FILE" = "podman-state.img" ]; then
878 OLD_STATE_IMG="$STATE_DIR/docker-state.img"
879 if [ -f "$OLD_STATE_IMG" ] && [ ! -f "$STATE_IMG" ]; then
880 log "INFO" "Migrating old vpdmn state file: docker-state.img -> podman-state.img"
881 mv "$OLD_STATE_IMG" "$STATE_IMG"
882 fi
883 fi
884
875 if [ ! -f "$STATE_IMG" ]; then 885 if [ ! -f "$STATE_IMG" ]; then
876 log "INFO" "Creating new state disk at $STATE_IMG..." 886 log "INFO" "Creating new state disk at $STATE_IMG..."
877 # Create 2GB state disk for Docker storage 887 # Create 2GB state disk for Docker storage
diff --git a/recipes-containers/vcontainer/vcontainer-tarball.bb b/recipes-containers/vcontainer/vcontainer-tarball.bb
new file mode 100644
index 00000000..edd788db
--- /dev/null
+++ b/recipes-containers/vcontainer/vcontainer-tarball.bb
@@ -0,0 +1,448 @@
1# SPDX-FileCopyrightText: Copyright (C) 2025 Bruce Ashfield
2#
3# SPDX-License-Identifier: MIT
4#
5# vcontainer-tarball.bb
6# ===========================================================================
7# Standalone SDK-style tarball for vdkr/vpdmn container tools
8# ===========================================================================
9#
10# This recipe uses Yocto's populate_sdk infrastructure to create a
11# relocatable standalone distribution of vdkr (Docker) and vpdmn (Podman).
12#
13# USAGE:
14# MACHINE=qemux86-64 bitbake vcontainer-tarball
15# MACHINE=qemuarm64 bitbake vcontainer-tarball
16#
17# OUTPUT:
18# tmp/deploy/sdk/vcontainer-standalone-<arch>.tar.xz
19# tmp/deploy/sdk/vcontainer-standalone-<arch>.sh (self-extracting installer)
20#
21# ===========================================================================
22
23SUMMARY = "Standalone SDK tarball for vdkr and vpdmn container tools"
24DESCRIPTION = "A relocatable standalone distribution of vdkr (Docker) and \
25 vpdmn (Podman) CLI tools using Yocto SDK infrastructure."
26HOMEPAGE = "https://git.yoctoproject.org/meta-virtualization/"
27LICENSE = "MIT"
28LIC_FILES_CHKSUM = "file://${COMMON_LICENSE_DIR}/MIT;md5=0835ade698e0bcf8506ecda2f7b4f302"
29
30# Use our custom SDK installer template with vcontainer-specific messages
31FILESEXTRAPATHS:prepend := "${THISDIR}/files:"
32TOOLCHAIN_SHAR_EXT_TMPL = "${THISDIR}/files/toolchain-shar-extract.sh"
33
34# No target sysroot - host tools only (like buildtools-tarball)
35TOOLCHAIN_TARGET_TASK = ""
36TARGET_ARCH = "none"
37TARGET_OS = "none"
38
39# Host tools to include via SDK
40# Note: nativesdk-qemu-vcontainer is a minimal QEMU without OpenGL/virgl
41# to avoid mesa -> llvm -> clang build dependency chain
42TOOLCHAIN_HOST_TASK = "\
43 nativesdk-sdk-provides-dummy \
44 nativesdk-qemu-vcontainer \
45 nativesdk-socat \
46 "
47
48# SDK naming and metadata
49TOOLCHAIN_OUTPUTNAME = "vcontainer-standalone"
50SDK_TITLE = "vcontainer tools (vdkr/vpdmn)"
51
52# SDK configuration (same pattern as buildtools-tarball)
53MULTIMACH_TARGET_SYS = "${SDK_ARCH}-nativesdk${SDK_VENDOR}-${SDK_OS}"
54PACKAGE_ARCH = "${SDK_ARCH}_${SDK_OS}"
55PACKAGE_ARCHS = ""
56SDK_PACKAGE_ARCHS += "vcontainer-dummy-${SDKPKGSUFFIX}"
57
58RDEPENDS = "${TOOLCHAIN_HOST_TASK}"
59EXCLUDE_FROM_WORLD = "1"
60
61inherit populate_sdk
62inherit toolchain-scripts-base
63inherit nopackages
64
65# Must be set AFTER inherit populate_sdk (class sets it to target arch)
66REAL_MULTIMACH_TARGET_SYS = "none"
67
68# Disable tasks we don't need
69deltask install
70deltask populate_sysroot
71
72# No config site needed
73TOOLCHAIN_NEED_CONFIGSITE_CACHE = ""
74INHIBIT_DEFAULT_DEPS = "1"
75
76do_populate_sdk[stamp-extra-info] = "${PACKAGE_ARCH}"
77
78# ===========================================================================
79# Architecture mapping
80# ===========================================================================
81VCONTAINER_TARGET_ARCH = "${@d.getVar('MACHINE').replace('qemuarm64', 'aarch64').replace('qemux86-64', 'x86_64')}"
82VCONTAINER_KERNEL_NAME = "${@'Image' if d.getVar('MACHINE') == 'qemuarm64' else 'bzImage'}"
83VCONTAINER_MC = "${@'vruntime-aarch64' if d.getVar('MACHINE') == 'qemuarm64' else 'vruntime-x86-64'}"
84
85# ===========================================================================
86# Multiconfig dependencies for blobs
87# ===========================================================================
88# Auto-detect which architectures have blobs available.
89# To limit to specific architectures, set in local.conf:
90# VCONTAINER_ARCHITECTURES = "x86_64" # x86_64 only
91# VCONTAINER_ARCHITECTURES = "aarch64" # aarch64 only
92# VCONTAINER_ARCHITECTURES = "x86_64 aarch64" # both (default if both built)
93def get_available_architectures(d):
94 import os
95 topdir = d.getVar('TOPDIR')
96 available = []
97 arch_info = {
98 'x86_64': ('vruntime-x86-64', 'qemux86-64'),
99 'aarch64': ('vruntime-aarch64', 'qemuarm64'),
100 }
101 for arch, (mc, machine) in arch_info.items():
102 deploy_dir = os.path.join(topdir, 'tmp-%s' % mc, 'deploy', 'images', machine, 'vdkr', arch)
103 if os.path.isdir(deploy_dir):
104 available.append(arch)
105 # If nothing available yet, default to current MACHINE's arch (will be built)
106 if not available:
107 available.append(d.getVar('VCONTAINER_TARGET_ARCH'))
108 return " ".join(sorted(available))
109
110VCONTAINER_ARCHITECTURES ?= "${@get_available_architectures(d)}"
111
112# Trigger multiconfig blob builds automatically via mcdepends
113# VCONTAINER_MC is set based on MACHINE (qemuarm64 -> vruntime-aarch64, qemux86-64 -> vruntime-x86-64)
114do_populate_sdk[mcdepends] = "mc::${VCONTAINER_MC}:vdkr-initramfs-create:do_deploy mc::${VCONTAINER_MC}:vpdmn-initramfs-create:do_deploy"
115
116# ===========================================================================
117# Custom SDK files - environment script AND blobs/scripts
118# ===========================================================================
119# IMPORTANT: All custom files must be added in create_sdk_files:append()
120# because SDK_POSTPROCESS_COMMAND runs archive_sdk BEFORE any appended commands.
121# The order is: create_sdk_files -> check_sdk_sysroots -> archive_sdk -> ...
122# ===========================================================================
123create_sdk_files:append () {
124 # Variables - these are Yocto variables expanded at parse time
125 TOPDIR="${TOPDIR}"
126 THISDIR="${THISDIR}"
127 ARCHITECTURES="${VCONTAINER_ARCHITECTURES}"
128
129 # SDK output directory
130 SDK_OUT="${SDK_OUTPUT}/${SDKPATH}"
131
132 bbnote "TOPDIR=${TOPDIR}"
133 bbnote "THISDIR=${THISDIR}"
134 bbnote "ARCHITECTURES=${ARCHITECTURES}"
135 bbnote "SDK_OUT=${SDK_OUT}"
136
137 # -----------------------------------------------------------------------
138 # Step 1: Copy blobs and scripts (MUST happen before archive_sdk)
139 # -----------------------------------------------------------------------
140 VDKR_INCLUDED=0
141 VPDMN_INCLUDED=0
142
143 # Copy blobs for each architecture
144 for ARCH in ${ARCHITECTURES}; do
145 bbnote "Adding vcontainer blobs for ${ARCH}"
146
147 # Determine multiconfig and kernel name for this arch
148 case "${ARCH}" in
149 aarch64)
150 MC="vruntime-aarch64"
151 MC_MACHINE="qemuarm64"
152 KERNEL="Image"
153 ;;
154 x86_64)
155 MC="vruntime-x86-64"
156 MC_MACHINE="qemux86-64"
157 KERNEL="bzImage"
158 ;;
159 *)
160 bbwarn "Unknown architecture: ${ARCH}"
161 continue
162 ;;
163 esac
164
165 MC_DEPLOY="${TOPDIR}/tmp-${MC}/deploy/images/${MC_MACHINE}"
166 bbnote "MC_DEPLOY=${MC_DEPLOY} for ${ARCH}"
167
168 # Create blob directories
169 mkdir -p "${SDK_OUT}/vdkr-blobs/${ARCH}"
170 mkdir -p "${SDK_OUT}/vpdmn-blobs/${ARCH}"
171
172 # Copy vdkr blobs
173 VDKR_SRC="${MC_DEPLOY}/vdkr/${ARCH}"
174 if [ -d "${VDKR_SRC}" ]; then
175 for blob in ${KERNEL} initramfs.cpio.gz rootfs.img; do
176 if [ -f "${VDKR_SRC}/${blob}" ]; then
177 cp "${VDKR_SRC}/${blob}" "${SDK_OUT}/vdkr-blobs/${ARCH}/"
178 bbnote "Copied vdkr blob: ${ARCH}/${blob}"
179 else
180 bbfatal "vdkr blob not found: ${VDKR_SRC}/${blob}"
181 fi
182 done
183 VDKR_INCLUDED=1
184 else
185 bbfatal "vdkr blobs not found for ${ARCH}. Build them first with:
186 bitbake mc:${MC}:vdkr-initramfs-create"
187 fi
188
189 # Copy vpdmn blobs
190 VPDMN_SRC="${MC_DEPLOY}/vpdmn/${ARCH}"
191 if [ -d "${VPDMN_SRC}" ]; then
192 for blob in ${KERNEL} initramfs.cpio.gz rootfs.img; do
193 if [ -f "${VPDMN_SRC}/${blob}" ]; then
194 cp "${VPDMN_SRC}/${blob}" "${SDK_OUT}/vpdmn-blobs/${ARCH}/"
195 bbnote "Copied vpdmn blob: ${ARCH}/${blob}"
196 else
197 bbfatal "vpdmn blob not found: ${VPDMN_SRC}/${blob}"
198 fi
199 done
200 VPDMN_INCLUDED=1
201 else
202 bbfatal "vpdmn blobs not found for ${ARCH}. Build them first with:
203 bitbake mc:${MC}:vpdmn-initramfs-create"
204 fi
205 done
206
207 # Copy scripts from layer
208 FILES_DIR="${THISDIR}/files"
209
210 # Copy shared scripts
211 for script in vrunner.sh vcontainer-common.sh; do
212 if [ -f "${FILES_DIR}/${script}" ]; then
213 cp "${FILES_DIR}/${script}" "${SDK_OUT}/"
214 chmod 755 "${SDK_OUT}/${script}"
215 bbnote "Copied ${script}"
216 else
217 bbfatal "${script} not found in ${FILES_DIR}"
218 fi
219 done
220
221 # Copy and set up vdkr
222 if [ "${VDKR_INCLUDED}" = "1" ] && [ -f "${FILES_DIR}/vdkr.sh" ]; then
223 cp "${FILES_DIR}/vdkr.sh" "${SDK_OUT}/vdkr"
224 chmod 755 "${SDK_OUT}/vdkr"
225 # Create symlinks for each included architecture
226 for ARCH in ${ARCHITECTURES}; do
227 if [ -d "${SDK_OUT}/vdkr-blobs/${ARCH}" ]; then
228 ln -sf vdkr "${SDK_OUT}/vdkr-${ARCH}"
229 bbnote "Created symlink vdkr-${ARCH}"
230 fi
231 done
232 bbnote "Installed vdkr"
233 fi
234
235 # Copy and set up vpdmn
236 if [ "${VPDMN_INCLUDED}" = "1" ] && [ -f "${FILES_DIR}/vpdmn.sh" ]; then
237 cp "${FILES_DIR}/vpdmn.sh" "${SDK_OUT}/vpdmn"
238 chmod 755 "${SDK_OUT}/vpdmn"
239 # Create symlinks for each included architecture
240 for ARCH in ${ARCHITECTURES}; do
241 if [ -d "${SDK_OUT}/vpdmn-blobs/${ARCH}" ]; then
242 ln -sf vpdmn "${SDK_OUT}/vpdmn-${ARCH}"
243 bbnote "Created symlink vpdmn-${ARCH}"
244 fi
245 done
246 bbnote "Installed vpdmn"
247 fi
248
249 # Create README
250 cat > "${SDK_OUT}/README.txt" <<EOF
251vcontainer Standalone SDK
252=========================
253
254This is a self-contained, relocatable distribution of vdkr (Docker) and
255vpdmn (Podman) CLI tools for cross-architecture container operations.
256
257Quick Start:
258 source init-env.sh
259 vdkr-x86_64 images # Docker for x86_64
260 vpdmn-aarch64 images # Podman for aarch64
261
262Architectures included: ${ARCHITECTURES}
263
264Contents:
265 init-env.sh - Environment setup script
266 vdkr, vdkr-<arch> - Docker CLI wrapper
267 vpdmn, vpdmn-<arch> - Podman CLI wrapper
268 vrunner.sh - Shared QEMU runner
269 vcontainer-common.sh - Shared CLI code
270 vdkr-blobs/ - Docker QEMU blobs (per-arch subdirectories)
271 vpdmn-blobs/ - Podman QEMU blobs (per-arch subdirectories)
272 sysroots/ - SDK binaries (QEMU, socat, libraries)
273
274Requirements:
275 - Linux x86_64 host
276 - KVM support recommended (for performance)
277
278For more information:
279 https://git.yoctoproject.org/meta-virtualization/
280EOF
281
282 bbnote "vcontainer blobs and scripts added to SDK"
283
284 # -----------------------------------------------------------------------
285 # Step 2: Create environment script (after blobs so we can check for them)
286 # -----------------------------------------------------------------------
287
288 # Remove ALL default SDK files - we create our own
289 rm -f ${SDK_OUTPUT}/${SDKPATH}/site-config-*
290 rm -f ${SDK_OUTPUT}/${SDKPATH}/version-*
291 rm -f ${SDK_OUTPUT}/${SDKPATH}/environment-setup-*
292
293 # Create vcontainer-specific environment script
294 # Keep the environment-setup-* naming so SDK installer works
295 script=${SDK_OUTPUT}/${SDKPATH}/environment-setup-${REAL_MULTIMACH_TARGET_SYS}
296
297 # Create environment script
298 # Set OECORE_NATIVE_SYSROOT temporarily for SDK relocation, then unset it
299 # (like buildtools-tarball does to avoid confusing other Yocto tools)
300 cat > $script <<'HEADER'
301#!/bin/bash
302# vcontainer environment setup script
303# Source this file: source environment-setup-none
304# Or use the symlink: source init-env.sh
305
306VCONTAINER_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
307HEADER
308 # Yocto variables (${SDK_SYS}, ${SDKPATHNATIVE}) expand at parse time
309 # Shell variables use $VAR to avoid Yocto expansion
310 echo 'export OECORE_NATIVE_SYSROOT="'"${SDKPATHNATIVE}"'"' >> $script
311 echo 'export PATH="$VCONTAINER_DIR:$OECORE_NATIVE_SYSROOT/usr/bin:/usr/bin:/bin:$PATH"' >> $script
312 cat >> $script <<'FOOTER'
313
314echo "vcontainer environment configured."
315echo ""
316FOOTER
317
318 # Add usage info based on what's included (check files we just copied)
319 if [ -f "${SDK_OUT}/vdkr" ]; then
320 cat >> $script <<'ENVEOF'
321echo "vdkr (Docker) commands:"
322echo " vdkr images # List docker images"
323echo " vdkr vimport ./oci/ app # Import OCI directory"
324ENVEOF
325 fi
326
327 if [ -f "${SDK_OUT}/vpdmn" ]; then
328 cat >> $script <<'ENVEOF'
329echo "vpdmn (Podman) commands:"
330echo " vpdmn images # List podman images"
331echo " vpdmn vimport ./oci/ app # Import OCI directory"
332ENVEOF
333 fi
334
335 cat >> $script <<ENVEOF
336echo ""
337echo "Architectures: ${VCONTAINER_ARCHITECTURES}"
338ENVEOF
339
340 # Unset OECORE_NATIVE_SYSROOT to avoid confusing other Yocto tools
341 # (same pattern as buildtools-tarball)
342 echo '' >> $script
343 echo '# Clean up - unset to avoid confusing other Yocto tools' >> $script
344 echo 'unset OECORE_NATIVE_SYSROOT' >> $script
345
346 # Replace placeholder with actual SDK_SYS
347 sed -i -e "s:@SDK_SYS@:${SDK_SYS}:g" $script
348 chmod 755 $script
349
350 # Create init-env.sh symlink for convenience
351 ln -sf environment-setup-${REAL_MULTIMACH_TARGET_SYS} ${SDK_OUTPUT}/${SDKPATH}/init-env.sh
352
353 # Create version file
354 echo "vcontainer SDK version: ${PV}" > ${SDK_OUTPUT}/${SDKPATH}/version.txt
355 echo "Built: $(date)" >> ${SDK_OUTPUT}/${SDKPATH}/version.txt
356 echo "Architectures: ${VCONTAINER_ARCHITECTURES}" >> ${SDK_OUTPUT}/${SDKPATH}/version.txt
357
358 # -----------------------------------------------------------------------
359 # Step 3: Remove unnecessary files to reduce SDK size (~100MB savings)
360 # -----------------------------------------------------------------------
361 bbnote "Removing unnecessary files from SDK..."
362
363 # Remove locale data (~97MB) - not needed for vcontainer tools
364 rm -rf ${SDK_OUTPUT}/${SDKPATHNATIVE}/usr/lib/locale
365 bbnote "Removed locale data"
366
367 # Remove QEMU user-mode emulators (we only need system emulators)
368 # Keep: qemu-system-aarch64, qemu-system-x86_64
369 for qemu_bin in ${SDK_OUTPUT}/${SDKPATHNATIVE}/usr/bin/qemu-*; do
370 case "$(basename $qemu_bin)" in
371 qemu-system-aarch64|qemu-system-x86_64|qemu-img|qemu-nbd)
372 # Keep these
373 ;;
374 qemu-system-*)
375 # Remove other system emulators (handled by QEMU_TARGETS now, but just in case)
376 rm -f "$qemu_bin"
377 ;;
378 *)
379 # Remove user-mode emulators (qemu-aarch64, qemu-arm, etc.)
380 rm -f "$qemu_bin"
381 bbnote "Removed $(basename $qemu_bin)"
382 ;;
383 esac
384 done
385
386 bbnote "SDK size optimization complete"
387}
388
389create_sdk_files[vardeps] += "VCONTAINER_TARGET_ARCH VCONTAINER_KERNEL_NAME VCONTAINER_MC"
390
391# ===========================================================================
392# Substitute custom placeholders in installer script
393# ===========================================================================
394# This runs AFTER archive_sdk to replace placeholders in our custom template
395SDK_POSTPROCESS_COMMAND += "substitute_vcontainer_vars;"
396
397substitute_vcontainer_vars () {
398 # Replace placeholders in the installer script
399 # Use SDKDEPLOYDIR (work directory) not SDK_DEPLOY (final deploy location)
400 sed -i -e "s|@VCONTAINER_ARCHITECTURES@|${VCONTAINER_ARCHITECTURES}|g" ${SDKDEPLOYDIR}/${TOOLCHAIN_OUTPUTNAME}.sh
401 bbnote "Substituted VCONTAINER_ARCHITECTURES=${VCONTAINER_ARCHITECTURES} in installer"
402}
403
404substitute_vcontainer_vars[vardeps] += "VCONTAINER_ARCHITECTURES"
405
406# ===========================================================================
407# Print usage information after SDK is built
408# ===========================================================================
409python do_populate_sdk:append() {
410 import os
411
412 deploy_dir = d.getVar('SDK_DEPLOY')
413 toolchain_outputname = d.getVar('TOOLCHAIN_OUTPUTNAME')
414 architectures = d.getVar('VCONTAINER_ARCHITECTURES').split()
415
416 # Find the installer script
417 installer = os.path.join(deploy_dir, toolchain_outputname + '.sh')
418 installer_size = 0
419 if os.path.exists(installer):
420 installer_size = os.path.getsize(installer) // (1024 * 1024)
421
422 # Check what was included
423 sdk_output = d.getVar('SDK_OUTPUT')
424 sdkpath = d.getVar('SDKPATH')
425 sdk_out = os.path.join(sdk_output, sdkpath.lstrip('/'))
426 vdkr_included = os.path.exists(os.path.join(sdk_out, 'vdkr'))
427 vpdmn_included = os.path.exists(os.path.join(sdk_out, 'vpdmn'))
428
429 bb.plain("")
430 bb.plain("=" * 70)
431 bb.plain("vcontainer SDK tarball created:")
432 bb.plain(" %s" % installer)
433 bb.plain(" Size: %d MB" % installer_size)
434 bb.plain(" Architectures: %s" % " ".join(architectures))
435 bb.plain(" vdkr (Docker): %s" % ("included" if vdkr_included else "NOT included"))
436 bb.plain(" vpdmn (Podman): %s" % ("included" if vpdmn_included else "NOT included"))
437 bb.plain("")
438 bb.plain("To extract and use:")
439 bb.plain(" %s -d /tmp/vcontainer -y" % installer)
440 bb.plain(" cd /tmp/vcontainer")
441 bb.plain(" source init-env.sh")
442 for arch in architectures:
443 if vdkr_included:
444 bb.plain(" vdkr-%s images # Docker for %s" % (arch, arch))
445 if vpdmn_included:
446 bb.plain(" vpdmn-%s images # Podman for %s" % (arch, arch))
447 bb.plain("=" * 70)
448}
diff --git a/recipes-containers/vcontainer/vdkr-native_1.0.bb b/recipes-containers/vcontainer/vdkr-native_1.0.bb
deleted file mode 100644
index a6a56ecb..00000000
--- a/recipes-containers/vcontainer/vdkr-native_1.0.bb
+++ /dev/null
@@ -1,191 +0,0 @@
1# SPDX-FileCopyrightText: Copyright (C) 2025 Bruce Ashfield
2#
3# SPDX-License-Identifier: MIT
4#
5# vdkr-native_1.0.bb
6# ===========================================================================
7# Emulated Docker for cross-architecture container operations
8# ===========================================================================
9#
10# vdkr provides a Docker-like CLI that executes arbitrary docker commands
11# inside a QEMU-emulated environment with the target architecture's Docker
12# daemon. Commands like "docker load", "docker export", "docker images" etc
13# are passed through to Docker running inside QEMU and results streamed back.
14#
15# vdkr uses its own initramfs (built by vdkr-initramfs-create) which
16# has vdkr-init.sh baked in. This is separate from container-cross-install.
17#
18# USAGE:
19# vdkr images # Uses detected default arch
20# vdkr -a aarch64 images # Explicit arch
21# vdkr-aarch64 images # Symlink (backwards compatible)
22# vdkr-x86_64 load -i myimage.tar
23#
24# Architecture detection (in priority order):
25# 1. --arch / -a flag
26# 2. Executable name (vdkr-aarch64, vdkr-x86_64)
27# 3. VDKR_ARCH environment variable
28# 4. Config file: ~/.config/vdkr/arch
29# 5. Host architecture (uname -m)
30#
31# DEPENDENCIES:
32# - Kernel/initramfs blobs from vdkr-initramfs-create
33# - QEMU system emulator (qemu-system-native)
34#
35# ===========================================================================
36
37SUMMARY = "Emulated Docker for cross-architecture container operations"
38DESCRIPTION = "Provides vdkr CLI that executes docker commands inside \
39 QEMU-emulated environment. Useful for building/manipulating \
40 containers for target architectures on a different host."
41HOMEPAGE = "https://github.com/anthropics/meta-virtualization"
42LICENSE = "MIT"
43LIC_FILES_CHKSUM = "file://${COMMON_LICENSE_DIR}/MIT;md5=0835ade698e0bcf8506ecda2f7b4f302"
44
45inherit native
46
47# Dependencies
48DEPENDS = "qemu-system-native coreutils-native socat-native"
49
50# vdkr-init.sh is now baked into the initramfs, not installed separately
51SRC_URI = "\
52 file://vdkr.sh \
53 file://vrunner.sh \
54"
55
56# Pre-built blobs are optional - they're checked into the layer after being
57# built by vdkr-initramfs-build. If not present, vdkr will still build
58# but will require --blob-dir at runtime.
59#
60# To build blobs:
61# MACHINE=qemuarm64 bitbake vdkr-initramfs-build
62# MACHINE=qemux86-64 bitbake vdkr-initramfs-build
63# Then copy from tmp/deploy/images/<machine>/vdkr-initramfs/ to files/blobs/vdkr/<arch>/
64#
65# For development, set VDKR_USE_DEPLOY = "1" in local.conf to use blobs
66# directly from DEPLOY_DIR instead of copying to layer.
67
68FILESEXTRAPATHS:prepend := "${THISDIR}/files:"
69
70# Layer directory containing optional blobs
71VDKR_LAYER_BLOBS = "${THISDIR}/files/blobs/vdkr"
72
73# Deploy directories (used when VDKR_USE_DEPLOY = "1")
74VDKR_DEPLOY_AARCH64 = "${DEPLOY_DIR}/images/qemuarm64/vdkr-initramfs"
75VDKR_DEPLOY_X86_64 = "${DEPLOY_DIR}/images/qemux86-64/vdkr-initramfs"
76
77# Set to "1" in local.conf to prefer DEPLOY_DIR blobs over layer
78VDKR_USE_DEPLOY ?= "0"
79
80S = "${UNPACKDIR}"
81
82do_install() {
83 # Install vdkr main script and architecture symlinks
84 install -d ${D}${bindir}
85 install -d ${D}${bindir}/vdkr-blobs/aarch64
86 install -d ${D}${bindir}/vdkr-blobs/x86_64
87
88 # Install main vdkr script (arch detected at runtime)
89 install -m 0755 ${S}/vdkr.sh ${D}${bindir}/vdkr
90
91 # Create backwards-compatible symlinks
92 ln -sf vdkr ${D}${bindir}/vdkr-aarch64
93 ln -sf vdkr ${D}${bindir}/vdkr-x86_64
94
95 # Install runner script
96 install -m 0755 ${S}/vrunner.sh ${D}${bindir}/
97
98 # Determine blob source directories based on VDKR_USE_DEPLOY
99 if [ "${VDKR_USE_DEPLOY}" = "1" ]; then
100 AARCH64_SRC="${VDKR_DEPLOY_AARCH64}"
101 X86_64_SRC="${VDKR_DEPLOY_X86_64}"
102 bbwarn "============================================================"
103 bbwarn "VDKR_USE_DEPLOY=1: Using blobs from DEPLOY_DIR"
104 bbwarn "This is for development only. For permanent use, copy blobs:"
105 bbwarn ""
106 bbwarn " # For aarch64:"
107 bbwarn " cp ${VDKR_DEPLOY_AARCH64}/Image \\"
108 bbwarn " ${VDKR_LAYER_BLOBS}/aarch64/"
109 bbwarn " cp ${VDKR_DEPLOY_AARCH64}/initramfs.cpio.gz \\"
110 bbwarn " ${VDKR_LAYER_BLOBS}/aarch64/"
111 bbwarn ""
112 bbwarn " # For x86_64:"
113 bbwarn " cp ${VDKR_DEPLOY_X86_64}/bzImage \\"
114 bbwarn " ${VDKR_LAYER_BLOBS}/x86_64/"
115 bbwarn " cp ${VDKR_DEPLOY_X86_64}/initramfs.cpio.gz \\"
116 bbwarn " ${VDKR_LAYER_BLOBS}/x86_64/"
117 bbwarn ""
118 bbwarn "Then remove VDKR_USE_DEPLOY from local.conf"
119 bbwarn "============================================================"
120 else
121 AARCH64_SRC="${VDKR_LAYER_BLOBS}/aarch64"
122 X86_64_SRC="${VDKR_LAYER_BLOBS}/x86_64"
123 fi
124
125 # Install aarch64 blobs (if available)
126 # Requires: Image, initramfs.cpio.gz, rootfs.img
127 if [ -f "$AARCH64_SRC/Image" ] && [ -f "$AARCH64_SRC/rootfs.img" ]; then
128 install -m 0644 "$AARCH64_SRC/Image" ${D}${bindir}/vdkr-blobs/aarch64/
129 install -m 0644 "$AARCH64_SRC/initramfs.cpio.gz" ${D}${bindir}/vdkr-blobs/aarch64/
130 install -m 0644 "$AARCH64_SRC/rootfs.img" ${D}${bindir}/vdkr-blobs/aarch64/
131 bbnote "Installed aarch64 blobs from $AARCH64_SRC"
132 else
133 bbnote "No aarch64 blobs found at $AARCH64_SRC"
134 bbnote "Required: Image, initramfs.cpio.gz, rootfs.img"
135 fi
136
137 # Install x86_64 blobs (if available)
138 # Requires: bzImage, initramfs.cpio.gz, rootfs.img
139 if [ -f "$X86_64_SRC/bzImage" ] && [ -f "$X86_64_SRC/rootfs.img" ]; then
140 install -m 0644 "$X86_64_SRC/bzImage" ${D}${bindir}/vdkr-blobs/x86_64/
141 install -m 0644 "$X86_64_SRC/initramfs.cpio.gz" ${D}${bindir}/vdkr-blobs/x86_64/
142 install -m 0644 "$X86_64_SRC/rootfs.img" ${D}${bindir}/vdkr-blobs/x86_64/
143 bbnote "Installed x86_64 blobs from $X86_64_SRC"
144 else
145 bbnote "No x86_64 blobs found at $X86_64_SRC"
146 bbnote "Required: bzImage, initramfs.cpio.gz, rootfs.img"
147 fi
148}
149
150# Make available in native sysroot
151SYSROOT_DIRS += "${bindir}"
152
153# Task to print usage instructions for using vdkr from current location
154# Run with: bitbake vdkr-native -c print_usage
155python do_print_usage() {
156 import os
157 bindir = d.getVar('D') + d.getVar('bindir')
158
159 # Find the actual install location
160 image_dir = d.getVar('D')
161 native_sysroot = d.getVar('STAGING_DIR_NATIVE')
162
163 bb.plain("")
164 bb.plain("=" * 70)
165 bb.plain("vdkr Usage Instructions")
166 bb.plain("=" * 70)
167 bb.plain("")
168 bb.plain("Option 1: Add to PATH (recommended)")
169 bb.plain("-" * 40)
170 bb.plain("export PATH=\"%s:$PATH\"" % (native_sysroot + d.getVar('bindir')))
171 bb.plain("")
172 bb.plain("Then use:")
173 bb.plain(" vdkr images # Uses default arch (host or config)")
174 bb.plain(" vdkr -a aarch64 images # Explicit arch")
175 bb.plain(" vdkr-x86_64 images # Symlink (backwards compatible)")
176 bb.plain("")
177 bb.plain("Option 2: Direct invocation")
178 bb.plain("-" * 40)
179 bb.plain("%s/vdkr images" % (native_sysroot + d.getVar('bindir')))
180 bb.plain("")
181 bb.plain("Option 3: Set default architecture")
182 bb.plain("-" * 40)
183 bb.plain("mkdir -p ~/.config/vdkr && echo 'aarch64' > ~/.config/vdkr/arch")
184 bb.plain("")
185 bb.plain("Note: QEMU must be in PATH. If not found, also add:")
186 bb.plain("export PATH=\"%s:$PATH\"" % (d.getVar('STAGING_BINDIR_NATIVE')))
187 bb.plain("")
188 bb.plain("=" * 70)
189}
190addtask print_usage
191do_print_usage[nostamp] = "1"
diff --git a/recipes-containers/vcontainer/vpdmn-native_1.0.bb b/recipes-containers/vcontainer/vpdmn-native_1.0.bb
deleted file mode 100644
index 7aaa8b28..00000000
--- a/recipes-containers/vcontainer/vpdmn-native_1.0.bb
+++ /dev/null
@@ -1,177 +0,0 @@
1# SPDX-FileCopyrightText: Copyright (C) 2025 Bruce Ashfield
2#
3# SPDX-License-Identifier: MIT
4#
5# vpdmn-native_1.0.bb
6# ===========================================================================
7# Emulated Podman for cross-architecture container operations
8# ===========================================================================
9#
10# vpdmn provides a Podman-like CLI that executes arbitrary podman commands
11# inside a QEMU-emulated environment with the target architecture's Podman.
12# Commands like "podman load", "podman images", etc. are passed through to
13# Podman running inside QEMU and results streamed back.
14#
15# vpdmn shares the vrunner.sh runner with vdkr (it's runtime-agnostic).
16#
17# USAGE:
18# vpdmn images # Uses detected default arch
19# vpdmn -a aarch64 images # Explicit arch
20# vpdmn-aarch64 images # Symlink (backwards compatible)
21# vpdmn-x86_64 load -i myimage.tar
22#
23# Architecture detection (in priority order):
24# 1. --arch / -a flag
25# 2. Executable name (vpdmn-aarch64, vpdmn-x86_64)
26# 3. VPDMN_ARCH environment variable
27# 4. Config file: ~/.config/vpdmn/arch
28# 5. Host architecture (uname -m)
29#
30# DEPENDENCIES:
31# - Kernel/initramfs blobs from vpdmn-initramfs-create
32# - QEMU system emulator (qemu-system-native)
33# - vrunner.sh (shared runner from vdkr-native)
34#
35# ===========================================================================
36
37SUMMARY = "Emulated Podman for cross-architecture container operations"
38DESCRIPTION = "Provides vpdmn CLI that executes podman commands inside \
39 QEMU-emulated environment. Useful for building/manipulating \
40 containers for target architectures on a different host."
41HOMEPAGE = "https://github.com/anthropics/meta-virtualization"
42LICENSE = "MIT"
43LIC_FILES_CHKSUM = "file://${COMMON_LICENSE_DIR}/MIT;md5=0835ade698e0bcf8506ecda2f7b4f302"
44
45inherit native
46
47# Dependencies - we use vdkr's runner script
48DEPENDS = "qemu-system-native coreutils-native socat-native vdkr-native"
49
50SRC_URI = "\
51 file://vpdmn.sh \
52"
53
54# Pre-built blobs are optional - they're checked into the layer after being
55# built by vpdmn-initramfs-create. If not present, vpdmn will still build
56# but will require --blob-dir at runtime.
57#
58# To build blobs:
59# MACHINE=qemuarm64 bitbake vpdmn-initramfs-create
60# MACHINE=qemux86-64 bitbake vpdmn-initramfs-create
61# Then copy from tmp/deploy/images/<machine>/vpdmn-initramfs/ to files/blobs/vpdmn/<arch>/
62
63FILESEXTRAPATHS:prepend := "${THISDIR}/files:"
64
65# Layer directory containing optional blobs
66VPDMN_LAYER_BLOBS = "${THISDIR}/files/blobs/vpdmn"
67
68# Deploy directories (used when VPDMN_USE_DEPLOY = "1")
69VPDMN_DEPLOY_AARCH64 = "${DEPLOY_DIR}/images/qemuarm64/vpdmn-initramfs"
70VPDMN_DEPLOY_X86_64 = "${DEPLOY_DIR}/images/qemux86-64/vpdmn-initramfs"
71
72# Set to "1" in local.conf to prefer DEPLOY_DIR blobs over layer
73VPDMN_USE_DEPLOY ?= "0"
74
75S = "${UNPACKDIR}"
76
77do_install() {
78 # Install vpdmn main script and architecture symlinks
79 install -d ${D}${bindir}
80 install -d ${D}${bindir}/vpdmn-blobs/aarch64
81 install -d ${D}${bindir}/vpdmn-blobs/x86_64
82
83 # Install main vpdmn script (arch detected at runtime)
84 install -m 0755 ${S}/vpdmn.sh ${D}${bindir}/vpdmn
85
86 # Create backwards-compatible symlinks
87 ln -sf vpdmn ${D}${bindir}/vpdmn-aarch64
88 ln -sf vpdmn ${D}${bindir}/vpdmn-x86_64
89
90 # Note: vpdmn uses vrunner.sh from vdkr-native (it's runtime-agnostic)
91
92 # Determine blob source directories based on VPDMN_USE_DEPLOY
93 if [ "${VPDMN_USE_DEPLOY}" = "1" ]; then
94 AARCH64_SRC="${VPDMN_DEPLOY_AARCH64}"
95 X86_64_SRC="${VPDMN_DEPLOY_X86_64}"
96 bbwarn "============================================================"
97 bbwarn "VPDMN_USE_DEPLOY=1: Using blobs from DEPLOY_DIR"
98 bbwarn "This is for development only. For permanent use, copy blobs:"
99 bbwarn ""
100 bbwarn " # For aarch64:"
101 bbwarn " cp ${VPDMN_DEPLOY_AARCH64}/Image \\"
102 bbwarn " ${VPDMN_LAYER_BLOBS}/aarch64/"
103 bbwarn " cp ${VPDMN_DEPLOY_AARCH64}/initramfs.cpio.gz \\"
104 bbwarn " ${VPDMN_LAYER_BLOBS}/aarch64/"
105 bbwarn ""
106 bbwarn " # For x86_64:"
107 bbwarn " cp ${VPDMN_DEPLOY_X86_64}/bzImage \\"
108 bbwarn " ${VPDMN_LAYER_BLOBS}/x86_64/"
109 bbwarn " cp ${VPDMN_DEPLOY_X86_64}/initramfs.cpio.gz \\"
110 bbwarn " ${VPDMN_LAYER_BLOBS}/x86_64/"
111 bbwarn ""
112 bbwarn "Then remove VPDMN_USE_DEPLOY from local.conf"
113 bbwarn "============================================================"
114 else
115 AARCH64_SRC="${VPDMN_LAYER_BLOBS}/aarch64"
116 X86_64_SRC="${VPDMN_LAYER_BLOBS}/x86_64"
117 fi
118
119 # Install aarch64 blobs (if available)
120 # Requires: Image, initramfs.cpio.gz, rootfs.img
121 if [ -f "$AARCH64_SRC/Image" ] && [ -f "$AARCH64_SRC/rootfs.img" ]; then
122 install -m 0644 "$AARCH64_SRC/Image" ${D}${bindir}/vpdmn-blobs/aarch64/
123 install -m 0644 "$AARCH64_SRC/initramfs.cpio.gz" ${D}${bindir}/vpdmn-blobs/aarch64/
124 install -m 0644 "$AARCH64_SRC/rootfs.img" ${D}${bindir}/vpdmn-blobs/aarch64/
125 bbnote "Installed aarch64 blobs from $AARCH64_SRC"
126 else
127 bbnote "No aarch64 blobs found at $AARCH64_SRC"
128 bbnote "Required: Image, initramfs.cpio.gz, rootfs.img"
129 fi
130
131 # Install x86_64 blobs (if available)
132 # Requires: bzImage, initramfs.cpio.gz, rootfs.img
133 if [ -f "$X86_64_SRC/bzImage" ] && [ -f "$X86_64_SRC/rootfs.img" ]; then
134 install -m 0644 "$X86_64_SRC/bzImage" ${D}${bindir}/vpdmn-blobs/x86_64/
135 install -m 0644 "$X86_64_SRC/initramfs.cpio.gz" ${D}${bindir}/vpdmn-blobs/x86_64/
136 install -m 0644 "$X86_64_SRC/rootfs.img" ${D}${bindir}/vpdmn-blobs/x86_64/
137 bbnote "Installed x86_64 blobs from $X86_64_SRC"
138 else
139 bbnote "No x86_64 blobs found at $X86_64_SRC"
140 bbnote "Required: bzImage, initramfs.cpio.gz, rootfs.img"
141 fi
142}
143
144# Make available in native sysroot
145SYSROOT_DIRS += "${bindir}"
146
147# Task to print usage instructions
148# Run with: bitbake vpdmn-native -c print_usage
149python do_print_usage() {
150 import os
151 native_sysroot = d.getVar('STAGING_DIR_NATIVE')
152
153 bb.plain("")
154 bb.plain("=" * 70)
155 bb.plain("vpdmn Usage Instructions")
156 bb.plain("=" * 70)
157 bb.plain("")
158 bb.plain("Option 1: Add to PATH (recommended)")
159 bb.plain("-" * 40)
160 bb.plain("export PATH=\"%s:$PATH\"" % (native_sysroot + d.getVar('bindir')))
161 bb.plain("")
162 bb.plain("Then use:")
163 bb.plain(" vpdmn images # Uses default arch (host or config)")
164 bb.plain(" vpdmn -a aarch64 images # Explicit arch")
165 bb.plain(" vpdmn-x86_64 images # Symlink (backwards compatible)")
166 bb.plain("")
167 bb.plain("Option 2: Set default architecture")
168 bb.plain("-" * 40)
169 bb.plain("mkdir -p ~/.config/vpdmn && echo 'aarch64' > ~/.config/vpdmn/arch")
170 bb.plain("")
171 bb.plain("Note: QEMU must be in PATH. If not found, also add:")
172 bb.plain("export PATH=\"%s:$PATH\"" % (d.getVar('STAGING_BINDIR_NATIVE')))
173 bb.plain("")
174 bb.plain("=" * 70)
175}
176addtask print_usage
177do_print_usage[nostamp] = "1"
diff --git a/recipes-devtools/qemu/nativesdk-qemu-vcontainer.bb b/recipes-devtools/qemu/nativesdk-qemu-vcontainer.bb
new file mode 100644
index 00000000..8fc68679
--- /dev/null
+++ b/recipes-devtools/qemu/nativesdk-qemu-vcontainer.bb
@@ -0,0 +1,65 @@
1# SPDX-FileCopyrightText: Copyright (C) 2025 Bruce Ashfield
2#
3# SPDX-License-Identifier: MIT
4#
5# Minimal QEMU variant for vcontainer tools (vdkr/vpdmn)
6#
7# This recipe provides a stripped-down nativesdk QEMU without OpenGL/SDL
8# dependencies, avoiding the mesa -> llvm -> clang build chain.
9#
10# This is separate from the main nativesdk-qemu to avoid affecting other
11# users who may need OpenGL support in their SDK QEMU.
12#
13# VERSION TRACKING: This recipe automatically discovers the QEMU version
14# from oe-core, so no updates are needed when oe-core bumps QEMU.
15
16SUMMARY = "Minimal QEMU for vcontainer tools"
17DESCRIPTION = "QEMU built without OpenGL/virglrenderer for use in vcontainer \
18 standalone tarballs. Avoids pulling in mesa/llvm/clang."
19
20# Dynamically discover QEMU version from oe-core (same pattern as busybox-initrd.bb)
21def get_qemu_pv(d):
22 import os
23 import re
24 corebase = d.getVar('COREBASE')
25 qemu_dir = os.path.join(corebase, 'meta', 'recipes-devtools', 'qemu')
26 if os.path.isdir(qemu_dir):
27 re_bb_name = re.compile(r"^qemu_([0-9.]+)\.bb$")
28 for bb_file in os.listdir(qemu_dir):
29 result = re_bb_name.match(bb_file)
30 if result:
31 return result.group(1)
32 bb.fatal("Cannot find qemu recipe in %s" % qemu_dir)
33
34PV := "${@get_qemu_pv(d)}"
35
36# Point to oe-core qemu files directory for patches and support files
37FILESEXTRAPATHS:prepend := "${COREBASE}/meta/recipes-devtools/qemu/qemu:"
38
39# Use the same base as oe-core qemu
40require recipes-devtools/qemu/qemu.inc
41
42# Pull in the main recipe's dependencies and settings
43DEPENDS += "glib-2.0 zlib pixman"
44DEPENDS:append:libc-musl = " libucontext"
45
46# Inherit nativesdk explicitly (not via BBCLASSEXTEND)
47inherit nativesdk
48
49# Target list for nativesdk (cross-prefix is already set by qemu.inc for class-nativesdk)
50EXTRA_OECONF:append = " --target-list=${@get_qemu_target_list(d)}"
51
52# Minimal PACKAGECONFIG - no opengl, no sdl, no virglrenderer
53# This avoids mesa -> llvm -> clang dependency chain
54# virtfs is needed for volume mounts (-v host:container)
55PACKAGECONFIG = "fdt kvm pie slirp virtfs"
56
57# Only build the QEMU targets we actually need for vcontainer
58# This saves ~150MB compared to building all architectures
59QEMU_TARGETS = "aarch64 x86_64"
60
61# QEMU's configure doesn't support --disable-static, so disable it
62DISABLE_STATIC = ""
63
64# Ensure proper naming
65BPN = "qemu"