Place ISO artifacts directly into the ISO tree

For MAKE_ISO=yes builds, squashfs, kernel, initrd, manifests, and sizes
are now placed directly into config/iso-dir/iso-root/casper/ during the
build rather than creating livecd.* intermediates that get linked as
for-iso.* files and then copied into casper/ by isobuild.

This stops publishing the intermediate livecd.* artifacts so that only
livecd.*.iso and livecd.*.netboot.tar.gz are published for ISO builds.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
michael.hudson@canonical.com 2026-03-24 20:26:37 +13:00
parent 1975bbd52b
commit 51624c1b44
No known key found for this signature in database
GPG Key ID: 80E627A0AB757E23
8 changed files with 180 additions and 257 deletions

View File

@ -424,6 +424,10 @@ case $LB_INITRAMFS in
;; ;;
esac esac
# For MAKE_ISO=yes builds, artifacts (squashfs, kernel, initrd, manifests) are
# placed directly into the ISO tree by lb_binary_layered and binary hooks.
# Only create livecd.* intermediate artifacts for non-ISO builds.
if [ "${MAKE_ISO}" != "yes" ]; then
for OUTPUT in ext2 ext3 ext4 manifest manifest-remove size squashfs; do for OUTPUT in ext2 ext3 ext4 manifest manifest-remove size squashfs; do
[ -e "binary/$INITFS/filesystem.$OUTPUT" ] || continue [ -e "binary/$INITFS/filesystem.$OUTPUT" ] || continue
ln "binary/$INITFS/filesystem.$OUTPUT" "$PREFIX.$OUTPUT" ln "binary/$INITFS/filesystem.$OUTPUT" "$PREFIX.$OUTPUT"
@ -562,6 +566,7 @@ case $SUBARCH in
cp $PREFIX.kernel $UBOOT_BOOT/vmlinuz || true cp $PREFIX.kernel $UBOOT_BOOT/vmlinuz || true
;; ;;
esac esac
fi
case $PROJECT in case $PROJECT in
ubuntu-cpc) ubuntu-cpc)
@ -569,25 +574,14 @@ case $PROJECT in
esac esac
if [ "${MAKE_ISO}" = "yes" ]; then if [ "${MAKE_ISO}" = "yes" ]; then
# Link build artifacts with "for-iso." prefix for isobuild to consume. # For non-layered builds, create squashfs with cdrom.sources directly
# Layered builds create squashfs via lb_binary_layered (which already # in casper/. Layered builds already handle this in lb_binary_layered.
# creates for-iso.*.squashfs files). Single-pass builds only have
# ${PREFIX}.squashfs, which does not contain cdrom.sources, so we
# create a for-iso.filesystem.squashfs that does.
if [ -z "$PASSES" ]; then if [ -z "$PASSES" ]; then
isobuild generate-sources --mountpoint=/cdrom > chroot/etc/apt/sources.list.d/cdrom.sources isobuild generate-sources --mountpoint=/cdrom > chroot/etc/apt/sources.list.d/cdrom.sources
create_squashfs chroot ${PWD}/for-iso.filesystem.squashfs create_squashfs chroot ${CASPER_DIR}/filesystem.squashfs
rm chroot/etc/apt/sources.list.d/cdrom.sources
fi fi
# Link kernel and initrd files. The ${thing#${PREFIX}} expansion strips isobuild extract-casper-uuids
# the PREFIX, so "livecd.ubuntu-server.kernel-generic" becomes
# "for-iso.kernel-generic".
for thing in ${PREFIX}.kernel-* ${PREFIX}.initrd-*; do
for_iso_path=for-iso${thing#${PREFIX}}
if [ ! -f $for_iso_path ]; then
ln -v $thing $for_iso_path
fi
done
isobuild add-live-filesystem --artifact-prefix for-iso.
isobuild make-bootable --project "${PROJECT}" --capproject "$(cat config/iso-ids/capproject)" \ isobuild make-bootable --project "${PROJECT}" --capproject "$(cat config/iso-ids/capproject)" \
${SUBARCH:+--subarch "${SUBARCH}"} ${SUBARCH:+--subarch "${SUBARCH}"}
isobuild make-iso --volid "$(cat config/iso-ids/vol-id)" --dest ${PREFIX}.iso isobuild make-iso --volid "$(cat config/iso-ids/vol-id)" --dest ${PREFIX}.iso

View File

@ -1456,3 +1456,17 @@ gpt_root_partition_uuid() {
isobuild () { isobuild () {
PYTHONPATH=${LIVECD_ROOTFS_ROOT}/live-build/ ${LIVECD_ROOTFS_ROOT}/live-build/isobuild --workdir config/iso-dir "$@" PYTHONPATH=${LIVECD_ROOTFS_ROOT}/live-build/ ${LIVECD_ROOTFS_ROOT}/live-build/isobuild --workdir config/iso-dir "$@"
} }
CASPER_DIR=config/iso-dir/iso-root/casper
# Install kernel+initrd into the ISO casper directory.
# Usage: iso_install_kernel <flavor> <kernel-path> <initrd-path>
iso_install_kernel() {
local flavor=$1 kernel=$2 initrd=$3
local kernel_name=vmlinuz
case $ARCH in ppc64el) kernel_name=vmlinux ;; esac
local prefix=""
case $flavor in *-hwe) prefix="hwe-" ;; esac
cp "$kernel" "$CASPER_DIR/${prefix}${kernel_name}"
cp "$initrd" "$CASPER_DIR/${prefix}initrd"
}

View File

@ -39,10 +39,9 @@
# Generate an apt deb822 source for the pool, assuming it is mounted at the # Generate an apt deb822 source for the pool, assuming it is mounted at the
# passed mountpoint, and output it on stdout. # passed mountpoint, and output it on stdout.
# #
# $ isobuild --work-dir "" add-live-filesystem --artifact-prefix "" # $ isobuild --work-dir "" extract-casper-uuids
# #
# Copy the relevant artifacts to the casper directory (and extract the uuids # Extract casper UUID files from the initrds in the casper directory.
# from the initrds)
# #
# $ isobuild --work-dir "" make-bootable --project "" --capitalized-project "" # $ isobuild --work-dir "" make-bootable --project "" --capitalized-project ""
# --subarch "" # --subarch ""
@ -169,14 +168,9 @@ def generate_sources(builder, mountpoint: str):
builder.generate_sources(mountpoint) builder.generate_sources(mountpoint)
@click.option(
"--artifact-prefix",
type=click.Path(dir_okay=False, resolve_path=True, path_type=pathlib.Path),
required=True,
)
@subcommand @subcommand
def add_live_filesystem(builder, artifact_prefix: pathlib.Path): def extract_casper_uuids(builder):
builder.add_live_filesystem(artifact_prefix) builder.extract_casper_uuids()
@click.option( @click.option(

View File

@ -218,7 +218,7 @@ class ISOBuilder:
) )
) )
def _extract_casper_uuids(self): def extract_casper_uuids(self):
# Extract UUID files from initrd images for casper (the live boot system). # Extract UUID files from initrd images for casper (the live boot system).
# Each initrd contains a conf/uuid.conf with a unique identifier that # Each initrd contains a conf/uuid.conf with a unique identifier that
# casper uses at boot time to locate the correct root filesystem. These # casper uses at boot time to locate the correct root filesystem. These
@ -255,43 +255,6 @@ class ISOBuilder:
uuid_conf.rename(dot_disk.joinpath(f"casper-uuid-{suffix}")) uuid_conf.rename(dot_disk.joinpath(f"casper-uuid-{suffix}"))
shutil.rmtree(initrddir) shutil.rmtree(initrddir)
def add_live_filesystem(self, artifact_prefix: pathlib.Path):
casper_dir = self.iso_root.joinpath("casper")
artifact_dir = artifact_prefix.parent
filename_prefix = artifact_prefix.name
def link(src: pathlib.Path, target_name: str):
target = casper_dir.joinpath(target_name)
self.logger.log(
f"creating link from $ISOROOT/casper/{target_name} to $src/{src.name}"
)
target.hardlink_to(src)
kernel_name = "vmlinuz"
if self.arch in ("ppc64el", "riscv64"):
kernel_name = "vmlinux"
with self.logger.logged(
f"linking artifacts from {casper_dir} to {artifact_dir}"
):
for ext in "squashfs", "squashfs.gpg", "size", "manifest", "yaml":
for path in artifact_dir.glob(f"{filename_prefix}*.{ext}"):
newname = path.name[len(filename_prefix) :]
link(path, newname)
for kernel_path in artifact_dir.glob(f"{filename_prefix}kernel*"):
suffix = kernel_path.name[len(filename_prefix) + len("kernel") :]
prefix = "hwe-" if suffix.endswith("-hwe") else ""
link(
artifact_dir.joinpath(f"{filename_prefix}kernel{suffix}"),
f"{prefix}{kernel_name}",
)
link(
artifact_dir.joinpath(f"{filename_prefix}initrd{suffix}"),
f"{prefix}initrd",
)
self._extract_casper_uuids()
def make_bootable(self, project: str, capproject: str, subarch: str): def make_bootable(self, project: str, capproject: str, subarch: str):
configurator = make_boot_configurator_for_arch( configurator = make_boot_configurator_for_arch(
self.arch, self.arch,

View File

@ -61,7 +61,7 @@ build_layered_squashfs () {
# Building squashfs filesystem & manifest # Building squashfs filesystem & manifest
local overlay_dir="overlay.${pass}" local overlay_dir="overlay.${pass}"
base="${PWD}/livecd.${PROJECT_FULL}.${pass}" base="${PWD}/${CASPER_DIR}/${pass}"
squashfs_f="${base}.squashfs" squashfs_f="${base}.squashfs"
# We have already treated that pass # We have already treated that pass
@ -116,32 +116,13 @@ build_layered_squashfs () {
create_manifest "chroot" "${squashfs_f_manifest}.full" create_manifest "chroot" "${squashfs_f_manifest}.full"
# Delta manifest # Delta manifest
diff -NU0 ${PWD}/livecd.${PROJECT_FULL}.$(get_parent_pass $pass).manifest.full ${squashfs_f_manifest}.full|grep -v ^@ > $squashfs_f_manifest || true diff -NU0 ${PWD}/${CASPER_DIR}/$(get_parent_pass $pass).manifest.full ${squashfs_f_manifest}.full|grep -v ^@ > $squashfs_f_manifest || true
echo "Delta manifest:" echo "Delta manifest:"
cat $squashfs_f_manifest cat $squashfs_f_manifest
squashfs_f_size="${base}.size" squashfs_f_size="${base}.size"
du -B 1 -s "overlay.${pass}/" | cut -f1 > "${squashfs_f_size}" du -B 1 -s "overlay.${pass}/" | cut -f1 > "${squashfs_f_size}"
# We take first live pass for "global" ISO properties (used by installers and checkers):
# Prepare initrd + kernel
# Main manifest and size files
prefix="livecd.$PROJECT_FULL"
if [ ! -e "${prefix}.manifest" ] && $(is_live_layer "$pass"); then
totalsize=$(cat ${squashfs_f_size})
curpass="$pass"
while :; do
curpass=$(get_parent_pass $curpass)
# We climbed up the tree to the root layer, we are done
[ -z "$curpass" ] && break
totalsize=$(expr $totalsize + $(cat "${PWD}/livecd.${PROJECT_FULL}.${curpass}.size"))
done
echo ${totalsize} > "${prefix}.size"
cp "${squashfs_f_manifest}.full" "${prefix}.manifest"
fi
if [ -n "$lowerdirs" ]; then if [ -n "$lowerdirs" ]; then
# Although the current chroot was created as an overlay over # Although the current chroot was created as an overlay over
# the previous layer, many operations can result in redundant # the previous layer, many operations can result in redundant
@ -183,23 +164,17 @@ build_layered_squashfs () {
${LIVECD_ROOTFS_ROOT}/sync-mtime chroot "$overlay_dir" ${LIVECD_ROOTFS_ROOT}/sync-mtime chroot "$overlay_dir"
fi fi
create_squashfs "${overlay_dir}" ${squashfs_f} # For the root layer when building with a pool, include
# Create a "for-iso" variant of the squashfs for ISO builds. For # cdrom.sources so casper can access the ISO's package repository.
# the root layer (the base system) when building with a pool, we
# need to include cdrom.sources so casper can access the ISO's
# package repository. This requires regenerating the squashfs with
# that file included, then removing it (so it doesn't pollute the
# regular squashfs). Non-root layers (desktop environment, etc.)
# and builds without pools can just hardlink to the regular squashfs.
if [ -n "${POOL_SEED_NAME}" ] && $(is_root_layer $pass); then if [ -n "${POOL_SEED_NAME}" ] && $(is_root_layer $pass); then
isobuild generate-sources --mountpoint=/cdrom > ${overlay_dir}/etc/apt/sources.list.d/cdrom.sources isobuild generate-sources --mountpoint=/cdrom > ${overlay_dir}/etc/apt/sources.list.d/cdrom.sources
create_squashfs "${overlay_dir}" ${PWD}/for-iso.${pass}.squashfs
rm ${overlay_dir}/etc/apt/sources.list.d/cdrom.sources
fi fi
create_squashfs "${overlay_dir}" ${squashfs_f}
rm -f ${overlay_dir}/etc/apt/sources.list.d/cdrom.sources
if [ -f config/$pass.catalog-in.yaml ]; then if [ -f config/$pass.catalog-in.yaml ]; then
echo "Expanding catalog entry template for $pass" echo "Expanding catalog entry template for $pass"
usc_opts="--output livecd.${PROJECT_FULL}.install-sources.yaml \ usc_opts="--output ${CASPER_DIR}/install-sources.yaml \
--template config/$pass.catalog-in.yaml \ --template config/$pass.catalog-in.yaml \
--size $(du -B 1 -s chroot/ | cut -f1) --squashfs ${pass}.squashfs \ --size $(du -B 1 -s chroot/ | cut -f1) --squashfs ${pass}.squashfs \
--translations config/catalog-translations" --translations config/catalog-translations"
@ -225,25 +200,11 @@ do
build_layered_squashfs "${_PASS}" ${*} build_layered_squashfs "${_PASS}" ${*}
done done
if [ -n "$DEFAULT_KERNEL" -a -f livecd.${PROJECT_FULL}.install-sources.yaml ]; then if [ -n "$DEFAULT_KERNEL" -a -f ${CASPER_DIR}/install-sources.yaml ]; then
write_kernel_yaml "$DEFAULT_KERNEL" "$BRIDGE_KERNEL_REASONS" write_kernel_yaml "$DEFAULT_KERNEL" "$BRIDGE_KERNEL_REASONS"
${LIVECD_ROOTFS_ROOT}/update-source-catalog merge \ ${LIVECD_ROOTFS_ROOT}/update-source-catalog merge \
--output livecd.${PROJECT_FULL}.install-sources.yaml \ --output ${CASPER_DIR}/install-sources.yaml \
--template config/kernel.yaml --template config/kernel.yaml
fi fi
# Ubiquity-compatible removal manifest for ISO not using a layered-aware installer chmod 644 ${CASPER_DIR}/*.squashfs ${CASPER_DIR}/*.manifest* ${CASPER_DIR}/*.size
if [ -n "$(ls livecd.${PROJECT_FULL}.*install.live.manifest.full 2>/dev/null)" ] && \
[ -n "$(ls livecd.${PROJECT_FULL}.*install.manifest.full 2>/dev/null)" ]; then
echo "$(diff livecd.${PROJECT_FULL}.*install.live.manifest.full livecd.${PROJECT_FULL}.*install.manifest.full | awk '/^< / { print $2 }')" > livecd.${PROJECT_FULL}-manifest-remove
fi
chmod 644 *.squashfs *.manifest* *.size
prefix=livecd.${PROJECT_FULL}
for artifact in ${prefix}.*; do
for_iso_path=for-iso${artifact#${prefix}}
if [ ! -f $for_iso_path ]; then
ln -v $artifact $for_iso_path
fi
done

View File

@ -1,8 +1,6 @@
#!/bin/sh #!/bin/sh
# Create kernel/initrd artifacts for isobuilder to consume. # Install kernel/initrd directly into the ISO casper directory.
# The standard MAKE_ISO flow in auto/build expects files named
# ${PREFIX}.kernel-${flavour} and ${PREFIX}.initrd-${flavour}.
set -eu set -eu
@ -14,7 +12,4 @@ case $ARCH in
;; ;;
esac esac
PREFIX="livecd.${PROJECT}" iso_install_kernel generic chroot/boot/vmlinuz chroot/boot/initrd.img
cp chroot/boot/vmlinuz "${PREFIX}.kernel-generic"
cp chroot/boot/initrd.img "${PREFIX}.initrd-generic"

View File

@ -13,9 +13,5 @@ case $PASS in
;; ;;
esac esac
PROJECT=$PROJECT${SUBARCH:+-$SUBARCH} # Install kernel and initrd directly into the ISO casper directory
iso_install_kernel "$flavor" chroot/boot/vmlinu?-* chroot/boot/initrd.img-*
# Fish out generated kernel image and initrd
mv chroot/boot/initrd.img-* ${PWD}/livecd.${PROJECT}.initrd-$flavor
mv chroot/boot/vmlinu?-* ${PWD}/livecd.${PROJECT}.kernel-$flavor
chmod a+r ${PWD}/livecd.${PROJECT}.initrd-$flavor ${PWD}/livecd.${PROJECT}.kernel-$flavor

View File

@ -29,8 +29,14 @@ flavor=${flavor##*.}
PROJECT=$PROJECT${SUBARCH:+-$SUBARCH} PROJECT=$PROJECT${SUBARCH:+-$SUBARCH}
KERNEL=${PWD}/livecd.${PROJECT}.kernel-$flavor # Read kernel/initrd from the ISO casper directory where iso_install_kernel
INITRD=${PWD}/livecd.${PROJECT}.initrd-$flavor # placed them.
kernel_name=vmlinuz
case $ARCH in ppc64el) kernel_name=vmlinux ;; esac
casper_prefix=""
case $flavor in *-hwe) casper_prefix="hwe-" ;; esac
KERNEL=${CASPER_DIR}/${casper_prefix}${kernel_name}
INITRD=${CASPER_DIR}/${casper_prefix}initrd
mkdir -p tarball/$ARCH mkdir -p tarball/$ARCH