#!/bin/bash set -e -u # edition-specific strings iso_edition="SystemD/CLI" # default init/wm pair iso_title="Parabola GNU/Linux-libre Live" # $iso_edition and $iso_version appended base_group='base' # default base package group # CLI option defaults archs='i686 x86_64' iso_arch='dual' iso_version=$(date +%Y.%m.%d) iso_label="PARA_$(date +%Y%m)" iso_dirname='parabola' enable_offline_install='false' work_dir=./work out_dir=./out target='' verbose='' efi_img_kbytes=40000 pacman_conf=${work_dir}/pacman.conf chroot_customization_script=/root/customize_root_image.sh # environment data_dir=/usr/share/parabolaiso/data releng_dir=$(readlink -f ${0%/*}) initcpio_dir=${releng_dir}/../../parabolaiso/initcpio packages_dir=${releng_dir}/packages [ "`which mkparabolaiso 2> /dev/null`" ] || export PATH=$PATH:"${releng_dir}/../../parabolaiso" _usage () { echo "usage ${0} [options]" echo echo " General options:" echo " -E Release edition (init/WM pair e.g. 'SystemD/CLI')" echo " Default: ${iso_edition}" echo " -T Target architecture (one of 'i686', 'x86_64', or 'dual')" echo " Default: ${iso_arch}" echo " -V Set the iso version in the filename" echo " Default: ${iso_version}" echo " -L Set the iso volume label" echo " Default: ${iso_label}" echo " -D Set the name of the directory inside the ISO" echo " Default: ${iso_dirname}" echo " -O Enable offline install." echo " Default: '${enable_offline_install}'" echo " -C Specify the pacman.conf file used for chroot install." echo " Default: '${pacman_conf}'" echo " -w Set the working directory" echo " Default: ${work_dir}" echo " -o Set the output directory" echo " Default: ${out_dir}" echo " -v Enable verbose output" echo " -h This help message" exit ${1} } # Helper function to run make_*() only one time per architecture. run_once() { if [[ ! -e ${work_dir}/build.${1}_${arch} ]]; then $1 touch ${work_dir}/build.${1}_${arch} fi } # Setup custom pacman.conf with current cache directories. make_pacman_conf() { sed -r "s|^#?\\s*CacheDir.+|CacheDir = ${work_dir}/${arch}/root-image/isorepo/|g" ${releng_dir}/pacman.conf > ${pacman_conf} } # Base installation, plus needed packages (root-image) make_basefs() { setarch ${arch} mkparabolaiso ${verbose} -w ${work_dir}/${arch} -C ${pacman_conf} -D ${iso_dirname} -B ${base_group} init setarch ${arch} mkparabolaiso ${verbose} -w ${work_dir}/${arch} -C ${pacman_conf} -D ${iso_dirname} -p "memtest86+ mkinitcpio-nfs-utils nbd" install } # Additional packages (root-image) make_packages() { packages=$(grep -h -v ^# ${packages_dir}/packages-{${iso_init},${iso_gui},${iso_wm}}.{both,${arch}} 2> /dev/null || true) setarch ${arch} mkparabolaiso ${verbose} -w ${work_dir}/${arch} -C ${pacman_conf} -D ${iso_dirname} -p "${packages}" install # create live environment local repo if [[ "$enable_offline_install" == 'true' ]]; then local iso_repo_dir=${work_dir}/${arch}/root-image/isorepo setarch ${arch} repo-add ${iso_repo_dir}/isorepo.db.tar.gz ${iso_repo_dir}/* fi } # Copy mkinitcpio parabolaiso hooks and build initramfs (root-image) make_setup_mkinitcpio() { local _hook for _hook in parabolaiso parabolaiso_shutdown parabolaiso_pxe_common parabolaiso_pxe_nbd parabolaiso_pxe_http parabolaiso_pxe_nfs parabolaiso_loop_mnt; do cp ${initcpio_dir}/hooks/${_hook} ${work_dir}/${arch}/root-image/usr/lib/initcpio/hooks cp ${initcpio_dir}/install/${_hook} ${work_dir}/${arch}/root-image/usr/lib/initcpio/install done cp ${initcpio_dir}/install/parabolaiso_kms ${work_dir}/${arch}/root-image/usr/lib/initcpio/install cp ${initcpio_dir}/hooks/parabolaiso_shutdown ${work_dir}/${arch}/root-image/usr/lib/initcpio cp ${releng_dir}/mkinitcpio.conf ${work_dir}/${arch}/root-image/etc/mkinitcpio-parabolaiso.conf setarch ${arch} mkparabolaiso ${verbose} -w ${work_dir}/${arch} -C ${pacman_conf} -D ${iso_dirname} -r 'mkinitcpio -c /etc/mkinitcpio-parabolaiso.conf -k /boot/vmlinuz-linux-libre -g /boot/parabolaiso.img' run } # Customize installation (root-image) make_customize_root_image() { [[ "$enable_offline_install" == 'true' ]] && offline='-O' || offline='' cp -af ${releng_dir}/root-image ${work_dir}/${arch} echo "Customizing root image" iso_title="${iso_title}" \ iso_init="${iso_init}" \ iso_wm="${iso_wm}" \ enable_offline_install="${enable_offline_install}" \ setarch ${arch} mkparabolaiso ${verbose} -w ${work_dir}/${arch} \ -C ${pacman_conf} \ -D ${iso_dirname} \ ${offline} \ -r ${chroot_customization_script} run rm ${work_dir}/${arch}/root-image${chroot_customization_script} } # Prepare kernel/initramfs ${iso_dirname}/boot/ make_boot() { mkdir -p ${work_dir}/iso/${iso_dirname}/boot/${arch} cp ${work_dir}/${arch}/root-image/boot/parabolaiso.img ${work_dir}/iso/${iso_dirname}/boot/${arch}/parabolaiso.img cp ${work_dir}/${arch}/root-image/boot/vmlinuz-linux-libre ${work_dir}/iso/${iso_dirname}/boot/${arch}/vmlinuz } # Add other aditional/extra files to ${iso_dirname}/boot/ make_boot_extra() { cp ${work_dir}/${arch}/root-image/boot/memtest86+/memtest.bin ${work_dir}/iso/${iso_dirname}/boot/memtest cp ${work_dir}/${arch}/root-image/usr/share/licenses/common/GPL2/license.txt ${work_dir}/iso/${iso_dirname}/boot/memtest.COPYING } # Prepare /${iso_dirname}/boot/syslinux make_syslinux() { mkdir -p ${work_dir}/iso/${iso_dirname}/boot/syslinux for _cfg in ${releng_dir}/syslinux/*.cfg; do sed "s|%PARABOLAISO_LABEL%|${iso_label}|g; s|%INSTALL_DIR%|${iso_dirname}|g" ${_cfg} > ${work_dir}/iso/${iso_dirname}/boot/syslinux/${_cfg##*/} done cp ${releng_dir}/syslinux/splash.png ${work_dir}/iso/${iso_dirname}/boot/syslinux cp ${work_dir}/${arch}/root-image/usr/lib/syslinux/bios/*.c32 ${work_dir}/iso/${iso_dirname}/boot/syslinux cp ${work_dir}/${arch}/root-image/usr/lib/syslinux/bios/lpxelinux.0 ${work_dir}/iso/${iso_dirname}/boot/syslinux cp ${work_dir}/${arch}/root-image/usr/lib/syslinux/bios/memdisk ${work_dir}/iso/${iso_dirname}/boot/syslinux mkdir -p ${work_dir}/iso/${iso_dirname}/boot/syslinux/hdt gzip -c -9 ${work_dir}/${arch}/root-image/usr/share/hwdata/pci.ids > ${work_dir}/iso/${iso_dirname}/boot/syslinux/hdt/pciids.gz gzip -c -9 ${work_dir}/${arch}/root-image/usr/lib/modules/*-gnu-*/modules.alias > ${work_dir}/iso/${iso_dirname}/boot/syslinux/hdt/modalias.gz # inject edition title sed -i "s|_EDITION_TITLE_|${iso_title}|" ${work_dir}/iso/${iso_dirname}/boot/syslinux/parabolaiso_head.cfg # prune orphan boot entries for single-architecture ISO if [[ "${archs}" == 'i686' ]]; then rm ${work_dir}/iso/${iso_dirname}/boot/syslinux/parabolaiso_*64*.cfg elif [[ "${archs}" == 'x86_64' ]]; then rm ${work_dir}/iso/${iso_dirname}/boot/syslinux/parabolaiso_*32*.cfg fi } # Prepare /isolinux make_isolinux() { mkdir -p ${work_dir}/iso/isolinux sed "s|%INSTALL_DIR%|${iso_dirname}|g" ${releng_dir}/isolinux/isolinux.cfg > ${work_dir}/iso/isolinux/isolinux.cfg cp ${work_dir}/${arch}/root-image/usr/lib/syslinux/bios/isolinux.bin ${work_dir}/iso/isolinux/ cp ${work_dir}/${arch}/root-image/usr/lib/syslinux/bios/isohdpfx.bin ${work_dir}/iso/isolinux/ cp ${work_dir}/${arch}/root-image/usr/lib/syslinux/bios/ldlinux.c32 ${work_dir}/iso/isolinux/ } # Prepare /EFI make_efi() { mkdir -p ${work_dir}/iso/EFI/boot cp ${work_dir}/x86_64/root-image/usr/lib/systemd/boot/efi/systemd-bootx64.efi ${work_dir}/iso/EFI/boot/bootx64.efi mkdir -p ${work_dir}/iso/loader/entries cp ${releng_dir}/efiboot/loader/loader.conf ${work_dir}/iso/loader/ cp ${releng_dir}/efiboot/loader/entries/uefi-shell-v2-x86_64.conf ${work_dir}/iso/loader/entries/ cp ${releng_dir}/efiboot/loader/entries/uefi-shell-v1-x86_64.conf ${work_dir}/iso/loader/entries/ sed "s|%PARABOLAISO_LABEL%|${iso_label}|g; s|%INSTALL_DIR%|${iso_dirname}|g" \ ${releng_dir}/efiboot/loader/entries/parabolaiso-x86_64-usb.conf > ${work_dir}/iso/loader/entries/parabolaiso-x86_64.conf # EFI Shell 2.0 for UEFI 2.3+ ( http://sourceforge.net/apps/mediawiki/tianocore/index.php?title=UEFI_Shell ) cp ${data_dir}/Shell.efi ${work_dir}/iso/EFI/shellx64_v2.efi # EFI Shell 1.0 for non UEFI 2.3+ ( http://sourceforge.net/apps/mediawiki/tianocore/index.php?title=Efi-shell ) cp ${data_dir}/Shell_Full.efi ${work_dir}/iso/EFI/shellx64_v1.efi } # Prepare efiboot.img::/EFI for "El Torito" EFI boot mode make_efiboot() { # cleanup from previous runs while mount | grep ${work_dir}/efiboot; do umount ${work_dir}/efiboot; sleep 1; done; rm ${work_dir}/iso/EFI/parabolaiso/efiboot.img 2> /dev/null || true # create EFI image mkdir -p ${work_dir}/iso/EFI/parabolaiso truncate -s ${efi_img_kbytes}K ${work_dir}/iso/EFI/parabolaiso/efiboot.img mkfs.vfat -n PARABOLAISO_EFI ${work_dir}/iso/EFI/parabolaiso/efiboot.img mkdir -p ${work_dir}/efiboot mkdir -p ${work_dir}/efiboot-staging mount ${work_dir}/iso/EFI/parabolaiso/efiboot.img ${work_dir}/efiboot mkdir -p ${work_dir}/efiboot-staging/EFI/parabolaiso cp ${work_dir}/iso/${iso_dirname}/boot/x86_64/vmlinuz ${work_dir}/efiboot-staging/EFI/parabolaiso/vmlinuz.efi cp ${work_dir}/iso/${iso_dirname}/boot/x86_64/parabolaiso.img ${work_dir}/efiboot-staging/EFI/parabolaiso/parabolaiso.img mkdir -p ${work_dir}/efiboot-staging/EFI/boot cp ${work_dir}/x86_64/root-image/usr/lib/systemd/boot/efi/systemd-bootx64.efi ${work_dir}/efiboot-staging/EFI/boot/bootx64.efi mkdir -p ${work_dir}/efiboot-staging/loader/entries cp ${releng_dir}/efiboot/loader/loader.conf ${work_dir}/efiboot-staging/loader/ cp ${releng_dir}/efiboot/loader/entries/uefi-shell-v2-x86_64.conf ${work_dir}/efiboot-staging/loader/entries/ cp ${releng_dir}/efiboot/loader/entries/uefi-shell-v1-x86_64.conf ${work_dir}/efiboot-staging/loader/entries/ sed "s|%PARABOLAISO_LABEL%|${iso_label}|g; s|%INSTALL_DIR%|${iso_dirname}|g" \ ${releng_dir}/efiboot/loader/entries/parabolaiso-x86_64-cd.conf > ${work_dir}/efiboot-staging/loader/entries/parabolaiso-x86_64.conf cp ${work_dir}/iso/EFI/shellx64_v2.efi ${work_dir}/efiboot-staging/EFI/ cp ${work_dir}/iso/EFI/shellx64_v1.efi ${work_dir}/efiboot-staging/EFI/ efi_kbytes=$(($(du -ks ${work_dir}/efiboot-staging | cut -f 1) + 32)) if [[ ${efi_kbytes} -lt $((${efi_img_kbytes})) ]]; then echo "Populating EFI image (${efi_kbytes}KB)" cp -rT ${work_dir}/efiboot-staging ${work_dir}/efiboot rm -rf ${work_dir}/efiboot-staging umount ${work_dir}/efiboot else echo "Error: Not enough space on EFI image (${efi_img_kbytes}KB - need ${efi_kbytes}KB)" umount ${work_dir}/efiboot exit 1 fi } # Copy aitab make_aitab() { mkdir -p ${work_dir}/iso/${iso_dirname} cp ${releng_dir}/aitab ${work_dir}/iso/${iso_dirname}/aitab } # Build all filesystem images specified in aitab (.fs.sfs .sfs) make_prepare() { [[ "$enable_offline_install" == 'true' ]] && offline='-O' || offline='' cp -a -l -f ${work_dir}/${arch}/root-image ${work_dir} setarch ${arch} mkparabolaiso ${verbose} -w ${work_dir} -D ${iso_dirname} pkglist setarch ${arch} mkparabolaiso ${verbose} -w ${work_dir} -D ${iso_dirname} ${offline} prepare rm -rf ${work_dir}/root-image # rm -rf ${work_dir}/${arch}/root-image (if low space, this helps) } # Build ISO make_iso() { mkparabolaiso ${verbose} -w ${work_dir} -D ${iso_dirname} checksum mkparabolaiso ${verbose} -w ${work_dir} -D ${iso_dirname} -L ${iso_label} -o ${out_dir} iso ${iso_filename} } ## prepare state ## # set CLI options while getopts 'E:T:V:L:D:OC:w:o:vh' arg; do case "${arg}" in E) iso_edition="${OPTARG}" ;; T) target="${OPTARG}" ;; V) iso_version="${OPTARG}" ;; L) iso_label="${OPTARG}" ;; D) iso_dirname="${OPTARG}" ;; O) enable_offline_install='true' ;; C) pacman_conf="${OPTARG}" ;; w) work_dir="${OPTARG}" ;; o) out_dir="${OPTARG}" ;; v) verbose="-v" ;; h) _usage 0 ;; *) echo "Invalid argument '${arg}'" _usage 1 ;; esac done # set target arch, GRUB title, and ISO filename case "${target}" in 'i686'|'x86_64') archs=${target} iso_arch=${target} launch_msg="Building single-architecture ${iso_edition} ISO for ${archs}" ;; *) launch_msg="Building multi-architecture ${iso_edition} ISO for ${archs}" ;; esac if [[ "$enable_offline_install" == 'true' ]]; then iso_arch="${iso_arch}-complete" launch_msg="${launch_msg} (net-install or offline install)" else iso_arch="${iso_arch}-netinstall" launch_msg="${launch_msg} (net-install only)" fi iso_title="${iso_title}${iso_edition} Edition ${iso_version}" iso_filename="parabola-${iso_edition}-${iso_arch}-${iso_version}.iso" iso_filename=$(echo ${iso_filename} | tr '[:upper:]/' '[:lower:]-') iso_init=$(echo ${iso_edition} | cut -d '/' -f 1 | tr '[:upper:]' '[:lower:]') iso_wm=$( echo ${iso_edition} | cut -d '/' -f 2 | tr '[:upper:]' '[:lower:]') [[ "${iso_wm}" == 'cli' ]] && iso_gui='' || iso_gui='gui' [[ "${archs}" == 'i686' ]] && arch='i686' || arch='x86_64' ## sanity checks ## # sanitize paths iso_label=${iso_label// /} iso_dirname=${iso_dirname// /} iso_filename=${iso_filename// /} iso_init=${iso_init// /} iso_wm=${iso_wm// /} work_dir=${work_dir// /} out_dir=${out_dir// /} pacman_conf=${pacman_conf// /} # validate build environment if [[ "$(uname -m)" != 'x86_64' ]] || ! grep 'ID_LIKE=.*archlinux' /usr/lib/os-release > /dev/null; then echo "This script needs to be run on an x86_64 ArchLinux derrivative." exit 1 elif [[ ${EUID} -ne 0 ]]; then echo "This script needs to be run with root privileges." exit 1 elif ! pacman --version | grep libalpm > /dev/null; then echo "This script needs the 'pacman' package manager to be installed." exit 1 elif ! pacman -Qi parabolaiso-data > /dev/null; then echo "This script needs the 'parabolaiso-data' package to be installed." exit 1 elif ! ls ${packages_dir}/packages-{${iso_init},${iso_wm}}.both > /dev/null; then echo "Invalid init/wm combination: '${iso_edition}'." exit 1 fi # detect previously completed build prefix=${work_dir}/build.make_ is_done=1 [ ! -f ${prefix}pacman_conf_${arch} -o ! -f ${prefix}boot_extra_${arch} -o \ ! -f ${prefix}syslinux_${arch} -o ! -f ${prefix}isolinux_${arch} -o \ ! -f ${prefix}aitab_${arch} -o ! -f ${prefix}iso_${arch} ] && \ is_done=0 for arch in ${archs}; do [ ! -f ${prefix}basefs_${arch} -o ! -f ${prefix}packages_${arch} -o \ ! -f ${prefix}setup_mkinitcpio_${arch} -o ! -f ${prefix}customize_root_image_${arch} -o \ ! -f ${prefix}boot_${arch} -o ! -f ${prefix}prepare_${arch} ] && \ is_done=0 done if ! (($is_done)); then echo ${launch_msg}; else echo "Nothing to do"; exit; fi; ## build ISO ## # prepare environment chown -R 0:0 "${releng_dir}/root-image/" mkdir -p ${work_dir} # prepare target filesystems for arch in ${archs}; do run_once make_pacman_conf run_once make_basefs run_once make_packages run_once make_setup_mkinitcpio run_once make_customize_root_image done # prepare kernels and initrds for arch in ${archs}; do run_once make_boot done # prepare ISO run_once make_boot_extra run_once make_syslinux run_once make_isolinux # prepare EFI if [[ "`echo ${archs} | grep x86_64`" ]]; then run_once make_efi run_once make_efiboot fi # prepare SFSs run_once make_aitab for arch in ${archs}; do run_once make_prepare done # build ISO run_once make_iso