mkosi tools for bootstrapping distro and images

This commit is contained in:
alexander stefanov 2025-01-27 17:13:07 +03:00
parent c7fe20c8b3
commit 5c716dd277
34 changed files with 186 additions and 10549 deletions

View file

@ -1,27 +0,0 @@
# TODO for `image-builder` Project
1. **Add a handler for `boot.cmd`**
- Implement processing and integration of `boot.cmd` during image creation.
- Ensure compatibility with common boot scenarios.
2. **Kernel Build from Custom Configurations**
- Develop a mechanism to compile the Linux kernel using custom `.config` files.
- Provide clear documentation for users to supply and use their own configurations.
3. **Custom Kernel Installation (Non-Repository)**
- Design a solution for installing custom-built kernels that are not available in standard repositories.
- Consider integrating a kernel packaging or direct installation process.
4. **Enhance U-Boot Writing Functionality**
- Finalize the function for writing U-Boot to `/dev/loopX`.
- Add error handling, validation, and logging to ensure robustness.
5. **Finalize Loop Device Unmount Function**
- Complete the function to properly unmount `/dev/loop` devices after use.
- Implement safeguards to prevent accidental unmounting of active devices.
6. **Root Password Setup**
- Resolve issues with `systemd-firstboot` for setting the root password.
- Implement a workaround to delete the default `root` user before initializing a new password.
- Alternatively, explore other methods for securely setting the root password during image creation.

View file

View file

@ -1,7 +0,0 @@
RELEASE="rosa13"
ABF_DOWNLOADS="https://abf-downloads.rosa.ru"
PKGS="basesystem systemd openssh-server dnf rosa-repos"
WEAK_DEPS="false"
DEFAULT_USER="rosa"
DEFAULT_USER_PASSWORD="rosa"
PASSWD_ROOT="root"

View file

@ -1,91 +0,0 @@
#!/usr/bin/env python
import os
import sys
import subprocess
import multiprocessing
from utils.bootstrap_setup import setup_bootstrap
from utils.common import load_config, clone_repo
from utils.make_disk import create_disk_image, setup_loop_device
from utils.make_disk import create_partitions, mount_partitions
from utils.generate_spec import generate_spec_file
from utils.kernel import clone_kernel, make_kernel_tar
from utils.uboot import build_uboot, flash_uboot
from utils.patch import apply_uboot_patches, apply_kernel_patches
from utils.rpmbuild import run_rpmbuild
BASE_DIR = os.getcwd()
TMP_DIR = os.path.join(BASE_DIR, "tmp")
NUM_CORES = str(multiprocessing.cpu_count())
def main():
if len(sys.argv) < 3 or "--distro" not in sys.argv:
print("example: python build.py --distro <distro_name> <vendor/device> [--skip-kernel] [--skip-uboot] [--skip-rootfs]")
print("""Usage: optional features:
--skip-kernel [do not build kernel]
--skip-uboot [do not build u-boot]
--skip-rootfs [do not create distro rootfs]""")
sys.exit(1)
distro_idx = sys.argv.index("--distro") + 1
distro = sys.argv[distro_idx]
vendor_device = sys.argv[distro_idx + 1]
vendor, device = vendor_device.split("/")
config_path = os.path.join("device", vendor, device, "config")
skip_kernel = "--skip-kernel" in sys.argv
skip_uboot = "--skip-uboot" in sys.argv
skip_rootfs = "--skip-rootfs" in sys.argv
if not os.path.exists(config_path):
print(f"Configuration file for {vendor}/{device} not found.")
sys.exit(1)
config = load_config(config_path)
arch = config["ARCH"]
print(f"Building for {vendor}/{device} with distro {distro}...")
if not skip_kernel:
kernel_dir = os.path.join(TMP_DIR, vendor, device, "kernel")
clone_kernel(TMP_DIR, BASE_DIR, config, vendor, device, kernel_dir)
generate_spec_file(TMP_DIR, config, vendor, device)
kernel_rpm_dir = os.path.join(TMP_DIR, vendor, device, "kernel-build")
make_kernel_tar(kernel_dir, kernel_rpm_dir)
# Call rpmbuild to build the kernel RPM
try:
run_rpmbuild(kernel_rpm_dir, arch)
except Exception as e:
print(f"Failed to build RPM: {e}")
sys.exit(1)
else:
print("Skipping kernel build.")
if not skip_uboot:
build_uboot(TMP_DIR, BASE_DIR, config, vendor, device)
else:
print("Skipping U-Boot build.")
if not skip_rootfs:
# dd here
disk_image_path = create_disk_image(TMP_DIR, config, vendor, device)
if disk_image_path:
loop_device = setup_loop_device(disk_image_path)
print(f"Loop device setup at {loop_device}")
# fdisk, mkfs here
create_partitions(loop_device, config)
if not skip_uboot:
flash_uboot(loop_device, TMP_DIR, config, vendor, device)
mount_partitions(config, loop_device, TMP_DIR, vendor, device)
# dnf install rootfs here
setup_bootstrap("bootstrap", TMP_DIR, vendor, device, distro, arch)
else:
print("Skipping rootfs bootstrap")
print(f"Build completed for {vendor}/{device} with distro {distro}")
if __name__ == "__main__":
main()

View file

@ -1,20 +0,0 @@
ARCH="aarch64"
EXTRA_PKGS="kernel-raspberry"
#KERNEL="https://github.com/raspberrypi/linux.git#rpi-6.6.y"
#KERNEL_CONFIG="bcm2711_defconfig"
#KERNEL_EXTRACONFIG="--module NTFS3_FS --enable NTFS3_LZX_XPRESS --enable NTFS3_FS_POSIX_ACL --disable NTFS3_64BIT_CLUSTER"
DTB="broadcom/bcm2711-rpi-4-b"
CMDLINE="dwc_otg.lpm_enable=0 console=ttyS0,115200 console=tty1 root=/dev/mmcblk0p2 rootfstype=ext4 elevator=deadline fsck.repair=yes rootwait"
NEED_INITRD=no
# disk section
# /boot is vfat partition
BOOT_FSTYPE="vfat"
BOOT_SIZE="256MB"
# / is ext4 partition
ROOT_FSTYPE="ext4"
ROOT_SIZE="1024MB"
# PRESET_CONFIG="local_config"

View file

@ -1,25 +0,0 @@
# See
# https://www.raspberrypi.org/documentation/configuration/config-txt
arm_64bit=1
kernel=kernel8.img
dtoverlay=vc4-kms-v3d-pi4
# Force the monitor to HDMI mode so that sound will be sent over HDMI cable
hdmi_drive=2
# Force monitor mode to DMT
hdmi_group=2
# Force monitor resolution (e.g. 16 = 1920x1080)
#hdmi_mode=16
# Some displays might have issues with text/pixels spilling off the screen.
# If this affects you, uncomment the overscan_* settings and adjust the values
#overscan_left=20
#overscan_right=12
#overscan_top=10
#overscan_bottom=10
# For i2c & spi
dtparam=i2c_arm=on
dtparam=spi=on
# Memory reserved for the GPU (in MB)
mem_gpu=256
# Enable audio (loads snd_bcm2835)
dtparam=audio=on

View file

@ -1,50 +0,0 @@
ARCH="aarch64"
BOOT_SOC="rk3568"
# kernel section
KERNEL="https://github.com/armbian/linux-rockchip.git#rk-6.1-rkr4.1"
PRESET_CONFIG="kernel_ok3568_defconfig"
DTB="dtbs/rockchip/rk3568-ok3568c"
# u-boot section
UBOOT="https://github.com/radxa/u-boot.git"
UBOOT_VERSION="next-dev-v2024.03"
UBOOT_CONFIG="rk3568-ok3568c_defconfig"
# cmdline
# root=/dev/mmcblk1p2 rootwait rootfstype=ext4 splash=verbose console=ttyS2,1500000 console=tty1 consoleblank=0 loglevel=1 earlycon=uart8250,mmio32,0xfe660000 console=ttyFIQ0 cma=256M androidboot.fwver=bl31-v1.44,uboot-rmbian-201-11/11/2024
CMDLINE="earlycon=uart8250,mmio32,0xfeb50000 console=ttyFIQ0 console=tty1 consoleblank=0"
EXTRA_PKGS="uboot-tools dracut"
# disk section
# /boot is vfat partition
BOOT_FSTYPE="vfat"
BOOT_SIZE="256MB"
# / is ext4 partition
ROOT_SIZE="1500MB"
ROOT_FSTYPE="ext4"
# platform section
# # put it in blobs dir
RK_DDR="rk3568_ddr_1560MHz_v1.23.bin"
BL31="rk3568_bl31_v1.44.elf"
UBOOT_BUILD="make KCFLAGS='-Wno-error' BL31={BL31} spl/u-boot-spl.bin u-boot.dtb u-boot.itb CROSS_COMPILE='{ARCH}-linux-gnu-'"
MKIMAGE_CMD="tools/mkimage -n {BOOT_SOC} -T rksd -d {RK_DDR}:spl/u-boot-spl.bin idbloader.img"
# url to download rockchip bootloader blobs
BLOBS_URL="https://github.com/rockchip-linux/rkbin/raw/refs/heads/master/bin/rk35/"
BOOT_IDB="idbloader.img"
BOOT_ITB="u-boot.itb"
UINITRD="yes"
# make BL31=rk3568_bl31_v1.44.elf spl/u-boot-spl.bin u-boot.dtb u-boot.itb CROSS_COMPILE="aarch64-linux-gnu-"
#
# dd if=idbloader.img of=/dev/loop1 seek=64 conv=notrunc status=none
# dd if=u-boot.itb of=/dev/loop1 seek=16384 conv=notrunc status=none
#
# sgdisk --zap-all /dev/loop0
# sgdisk --new=1:15M:+256M --typecode=1:8300 /dev/loop0
# sgdisk --new=2:0:0 --typecode=2:8300 /dev/loop0
#
# dracut --regenerate-all --force
# mkimage -A arm64 -O linux -T ramdisk -C gzip -n uInitrd -d /boot/initramfs-6.1.75.img /boot/uInitrd
# mkimage -C none -A arm -T script -d /boot/boot.cmd /boot/boot.scr

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,33 @@
[Distribution]
Distribution=rosa
Release=rosa13
Architecture=arm64
[Output]
Format=disk
CompressOutput=
SectorSize=512
[Content]
Packages=
kernel-6.1-rockchip
coreutils
dnf
neovim
dracut
iproute2
locales-en
openssh-server
rosa-repos
systemd
uboot-tools
Bootable=no
Autologin=yes
RootPassword=root
RootShell=/usr/bin/bash
Locale=en_US.UTF-8
Timezone=Europe/Moscow
Hostname=elara
WithDocs=no
CleanPackageMetadata=false

View file

@ -1,13 +1,13 @@
setenv fdtfile "dtbs/rockchip/rk3568-ok3568c.dtb" setenv fdtfile "dtbs/rockchip/rk3568-ok3568c.dtb"
setenv bootargs "root=/dev/mmcblk1p2 rootwait rootfstype=ext4 splash=verbose console=ttyS2,1500000 console=tty1 consoleblank=0 loglevel=1 earlycon=uart8250,mmio32,0xfe660000 console=ttyFIQ0 cma=256M" setenv bootargs "root=/dev/mmcblk1p3 rootwait rootfstype=ext4 splash=verbose console=ttyS2,1500000 console=tty1 consoleblank=0 loglevel=1 earlycon=uart8250,mmio32,0xfe660000 console=ttyFIQ0 cma=256M"
test -n "${distro_bootpart}" || distro_bootpart=1 test -n "${distro_bootpart}" || distro_bootpart=1
echo "Boot script loaded from ${devtype} ${devnum}:${distro_bootpart}" echo "Boot script loaded from ${devtype} ${devnum}:${distro_bootpart}"
load ${devtype} ${devnum}:${distro_bootpart} ${ramdisk_addr_r} ${prefix}uInitrd load ${devtype} ${devnum}:${distro_bootpart} ${ramdisk_addr_r} ${prefix}INITRD_REPLACE
load ${devtype} ${devnum}:${distro_bootpart} ${kernel_addr_r} ${prefix}Image load ${devtype} ${devnum}:${distro_bootpart} ${kernel_addr_r} ${prefix}IMAGE_REPLACE
load ${devtype} ${devnum}:${distro_bootpart} ${fdt_addr_r} ${prefix}${fdtfile} load ${devtype} ${devnum}:${distro_bootpart} ${fdt_addr_r} ${prefix}${fdtfile}
fdt addr ${fdt_addr_r} fdt addr ${fdt_addr_r}

View file

@ -0,0 +1,4 @@
# grow up rootfs on first boot
[Partition]
Type=root
GrowFileSystem=yes

View file

@ -0,0 +1,5 @@
[Unit]
ConditionFirstBoot=true
[Install]
WantedBy=multi-user.target

View file

@ -0,0 +1,6 @@
[Match]
Name=en*
[Network]
DHCP=yes
IPv6PrivacyExtensions=yes

View file

@ -0,0 +1,7 @@
enable sshd.service
enable systemd-networkd.service
enable systemd-growfs-root.service
# We install dnf in some images but it's only going to be used rarely,
# so let's not have dnf create its cache.
disable dnf-makecache.*

View file

@ -0,0 +1,4 @@
#!/bin/sh
# SPDX-License-Identifier: GPL-2.0-only OR GPL-3.0-only OR LicenseRef-KDE-Accepted-GPL
set -x
env

View file

@ -0,0 +1,33 @@
#!/bin/sh
# SPDX-License-Identifier: GPL-2.0-only OR GPL-3.0-only OR LicenseRef-KDE-Accepted-GPL
set -Eeuo pipefail
env
# install or remove files here
# systemctl not working here
# systemctl enable sshd
# systemctl enable bpftune
# systemctl disable dnf-makecache.timer
version=$(rpm -q --queryformat '%{version}\n' kernel-6.1-rockchip)
printf "Generate /boot/uInitrd-${version}...\n"
# mkimage -A arm64 -O linux -T ramdisk -C gzip -n uInitrd -d /boot/initrd-%{version}.img /boot/uInitrd
mkimage -A arm64 -O linux -T ramdisk -C gzip -d /boot/initrd-${version}.img /boot/uInitrd-${version}
printf "Remove dracut-generated initrd...\n"
rm -fv /boot/initrd-${version}.img
rm -fv /boot/uInitrd
# generate boot.scr
# see mkosi.extra/boot/boot.cmd
printf "Adjust /boot/boot.cmd for kernel ${version}...\n"
sed -i "s!INITRD_REPLACE!uInitrd-${version}!g" /boot/boot.cmd
sed -i "s!IMAGE_REPLACE!vmlinuz-${version}!g" /boot/boot.cmd
printf "Current /boot/boot.cmd...\n"
printf "#########################\n"
cat /boot/boot.cmd
printf "#########################\n"
printf "Generate /boot/boot.scr...\n"
/usr/bin/mkimage -C none -A arm -T script -d /boot/boot.cmd /boot/boot.scr

View file

@ -0,0 +1,6 @@
#!/bin/sh
# SPDX-License-Identifier: GPL-2.0-only OR GPL-3.0-only OR LicenseRef-KDE-Accepted-GPL
set -x
env
ls -la ${SRCDIR}
ls -la ${OUTPUTDIR}

View file

@ -0,0 +1,5 @@
[Partition]
Type=21686148-6449-6e6f-744e-656564454649
Format=
SizeMinBytes=15M
SizeMaxBytes=15M

View file

@ -0,0 +1,8 @@
[Partition]
Type=esp
Format=vfat
#MountPoint=/boot
Label=bootfs
SizeMinBytes=256M
SizeMaxBytes=256M
CopyFiles=/boot:/

View file

@ -0,0 +1,6 @@
[Partition]
Type=root
Label=rootfs
Format=ext4
CopyFiles=/
Minimize=guess

View file

@ -0,0 +1,39 @@
#!/bin/sh
set -x
echo "--- mkosi.build: args=$@"
echo "--- mkosi.build: container=$container"
if [ "$container" != "mkosi" ]; then
exec mkosi-chroot "$CHROOT_SCRIPT" "$@"
fi
echo "--- mkosi.build: user=$USER"
echo "--- mkosi.build: home=$HOME"
echo "--- mkosi.build: pwd=$PWD"
echo "--- mkosi.build: srcdir=$SRCDIR"
echo "--- mkosi.build: builddir=$BUILDDIR"
cd $SRCDIR
echo "--- mkosi.build: clone u-boot"
dnf in -y python2 --release 13
git clone https://github.com/radxa/u-boot.git --depth 1 -b next-dev-v2024.03
cd u-boot
git apply ${SRCDIR}/configuration/*.patch
BL31="rk3568_bl31_v1.44.elf"
RKDDR="rk3568_ddr_1560MHz_v1.23.bin"
BOOT_SOC="rk3568"
ARCH="aarch64"
wget https://github.com/rockchip-linux/rkbin/raw/refs/heads/master/bin/rk35/${RKDDR}
wget https://github.com/rockchip-linux/rkbin/raw/refs/heads/master/bin/rk35/${BL31}
make rk3568-ok3568c_defconfig
make -s -j$(nproc) KCFLAGS="-Wno-error" \
BL31=${BL31} \
spl/u-boot-spl.bin \
u-boot.dtb u-boot.itb CROSS_COMPILE="${ARCH}-linux-gnu-"
tools/mkimage -n ${BOOT_SOC} -T rksd -d ${RKDDR}:spl/u-boot-spl.bin idbloader.img
cp -fv idbloader.img u-boot.itb ${SRCDIR}/mkosi/

View file

@ -0,0 +1,27 @@
[Distribution]
Distribution=rosa
Release=rosa13
Architecture=x86-64
[Output]
Format=directory
[Content]
Bootable=no
Packages=basesystem-build
curl
dnf
git
gcc-aarch64-linux-gnu
binutils-aarch64-linux-gnu
wget
dtc
bc
rosa-repos-contrib
rosa-repos
[Build]
WithNetwork=yes
BuildSources=./:mkosi
./configuration:configuration

View file

@ -1,109 +0,0 @@
import os
import sys
import subprocess
from utils.common import load_config
def load_bootstrap_config(bootstrap_path):
"""Load the bootstrap configuration from the specified file."""
config = {}
with open(bootstrap_path, "r") as f:
for line in f:
if "=" in line and not line.strip().startswith("#"):
key, value = line.strip().split("=", 1)
config[key] = value.strip('"')
return config
def generate_dnf_conf(dnf_conf_path, abf_downloads, release):
"""Generate dnf.conf based on the bootstrap configuration."""
dnf_conf_content = f"""
[main]
keepcache=1
debuglevel=2
reposdir=/dev/null
retries=20
obsoletes=1
gpgcheck=0
assumeyes=1
syslog_ident=mock
syslog_device=
install_weak_deps=0
metadata_expire=60s
best=1
[{release}_main_release]
name={release}_main_release
baseurl={abf_downloads}/{release}/repository/aarch64/main/release
gpgcheck=0
enabled=1
[{release}_main_updates]
name={release}_main_updates
baseurl={abf_downloads}/{release}/repository/aarch64/main/updates
gpgcheck=0
enabled=1
"""
os.makedirs(os.path.dirname(dnf_conf_path), exist_ok=True)
with open(dnf_conf_path, "w") as f:
f.write(dnf_conf_content)
def run_dnf_install(config, dnf_conf_path, rootfs_dir, arch, extra_pkgs=""):
"""Run dnf command to install packages based on the bootstrap configuration."""
pkgs = config["PKGS"]
weak_deps = config["WEAK_DEPS"].lower()
if extra_pkgs:
pkgs += f" {extra_pkgs}"
print(f"Bootstrapping '{arch}' rootfs...")
dnf_command = [
"sudo",
"dnf",
"--setopt=install_weak_deps=" + str(weak_deps),
"--config", dnf_conf_path,
"--nodocs",
"--forcearch", arch,
"--installroot", rootfs_dir,
"install"
] + pkgs.split()
subprocess.run(dnf_command, check=True, stdout=subprocess.DEVNULL)
def set_root_password_with_systemd(rootfs_dir, password_root):
"""Set root password using systemd-firstboot in chroot environment."""
subprocess.run(
["sudo", "systemd-firstboot", "--root-password=", password_root, "--root=", rootfs_dir],
check=True
)
print(f"root password set to '{password_root}' in chroot at {rootfs_dir}")
def setup_bootstrap(bootstrap_dir, tmp_dir, vendor, device, distro, arch):
# load distro config
# bootstrap/DISTRO_NAME
distro_config_path = os.path.join(bootstrap_dir, distro)
if not os.path.exists(distro_config_path):
print(f"Bootstrap configuration for distro '{distro}' not found.")
current_directory = os.getcwd()
print(f"Текущая рабочая директория: {current_directory}")
sys.exit(1)
config = load_config(distro_config_path)
device_config_path = os.path.join("device", vendor, device, "config")
device_config = load_config(device_config_path) if os.path.exists(device_config_path) else {}
extra_pkgs = device_config.get("EXTRA_PKGS", "")
dnf_conf_path = os.path.join(tmp_dir, vendor, device, "dnf.conf")
rootfs_dir = os.path.join(tmp_dir, vendor, device, "rootfs")
generate_dnf_conf(dnf_conf_path, config["ABF_DOWNLOADS"], config["RELEASE"])
run_dnf_install(config, dnf_conf_path, rootfs_dir, arch, extra_pkgs)
set_root_password_with_systemd(rootfs_dir, config["PASSWD_ROOT"])
#setup_user(rootfs_dir, config["DEFAULT_USER"], config["DEFAULT_USER_PASSWORD"], config["PASSWD_ROOT"])

View file

@ -1,34 +0,0 @@
#!/usr/bin/env python
import os
import subprocess
from urllib.request import urlretrieve
def load_config(config_path):
config = {}
with open(config_path, "r") as f:
for line in f:
if "=" in line and not line.strip().startswith("#"):
key, value = line.strip().split("=", 1)
config[key] = value.strip('"')
return config
def clone_repo(repo_url, branch, dest_dir, name):
if os.path.exists(dest_dir):
print(f"Warning: {name} directory '{dest_dir}' already exists. Skipping clone.")
else:
os.makedirs(dest_dir, exist_ok=True)
subprocess.run(["git", "clone", "--depth", "1", repo_url, "-b", branch, dest_dir], check=True)
def download_blob(blob_url, destination):
"""Download a file from the given URL and save it to the destination."""
try:
print(f"Downloading {blob_url} to {destination}...")
os.makedirs(os.path.dirname(destination), exist_ok=True)
urlretrieve(blob_url, destination)
print(f"Downloaded: {destination}")
return True
except Exception as e:
print(f"Warning: Unable to download {blob_url}. {e}")
return False

View file

@ -1,127 +0,0 @@
#!/usr/bin/env python
# -*- coding: utf-8 -*-
import os
import sys
import subprocess
import shutil
#from common import load_config
def get_kernel_version(kernel_dir):
"""Extract kernel version by running 'make kernelversion' in the kernel directory."""
try:
result = subprocess.run(
["make", "kernelversion"],
cwd=kernel_dir,
stdout=subprocess.PIPE,
stderr=subprocess.PIPE,
text=True,
check=True
)
return result.stdout.strip()
except subprocess.CalledProcessError as e:
print(f"Error: Failed to get kernel version from {kernel_dir}")
print(e.stderr)
sys.exit(1)
def generate_spec_file(TMP_DIR, config, vendor, device):
"""Generate RPM spec file from template and config"""
# Load device config
config_path = os.path.join("device", vendor, device, "config")
if not os.path.exists(config_path):
print(f"Error: Config file not found at {config_path}")
sys.exit(1)
# Load template
template_path = os.path.join("utils", "kernel.template")
if not os.path.exists(template_path):
print(f"Error: Template file not found at {template_path}")
sys.exit(1)
with open(template_path, 'r') as f:
template = f.read()
# Extract kernel version from kernel URL and branch
kernel_url, kernel_branch = config["KERNEL"].split("#")
# Extract kernel version from Makefile
kernel_dir = os.path.join(TMP_DIR, vendor, device, "kernel")
if not os.path.exists(kernel_dir):
print(f"Error: Kernel directory not found at {kernel_dir}")
sys.exit(1)
kernel_version = get_kernel_version(kernel_dir)
# Determine KERNEL_ARCH and CROSS_COMPILE based on ARCH
arch = config.get("ARCH", "unknown")
kernel_config = config.get("KERNEL_CONFIG", "unknown")
if arch == "aarch64":
kernel_arch = "arm64"
cross_compile = "aarch64-linux-gnu"
else:
kernel_arch = arch
cross_compile = f"{arch}-linux-gnu" # Default cross compile format
# Prepare replacements
device_name = f"{vendor}-{device}"
replacements = {
"{BOARD_NAME}": device_name,
"{KERNEL_VERSION}": kernel_version,
"{DEVICE_NAME}": device_name,
"{KERNEL_ARCH}": kernel_arch,
"{ARCH}": arch, # Add ARCH replacement
"{KERNEL_CONFIG}": kernel_config,
"{CROSS_COMPILE}": cross_compile # Add CROSS_COMPILE replacement
}
# Handle Source1 based on PRESET_CONFIG
preset_config = config.get("PRESET_CONFIG", "").strip()
if preset_config:
replacements["{Source1}"] = f"Source1: {preset_config}" # Format Source1 correctly
replacements["{SOURCE1_COMMAND}"] = "cp %{S:1} .config"
else:
replacements["{Source1}"] = "" # Leave Source1 empty if PRESET_CONFIG is not set
replacements["{SOURCE1_COMMAND}"] = ""
kernel_defconfig = config.get("KERNEL_CONFIG", "").strip()
if kernel_defconfig:
replacements["{MAKE_DEFCONFIG}"] = f"%make_build {kernel_defconfig} ARCH={kernel_arch}" # Format Source1 correctly
else:
replacements["{MAKE_DEFCONFIG}"] = ""
# Apply replacements
spec_content = template
for key, value in replacements.items():
spec_content = spec_content.replace(key, value)
# Define output directory and path
output_dir = os.path.join(TMP_DIR, vendor, device, "kernel-build")
os.makedirs(output_dir, exist_ok=True)
# Copy PRESET_CONFIG file if it exists
if preset_config:
preset_config_path = os.path.join("device", vendor, device, preset_config)
if os.path.exists(preset_config_path):
shutil.copy(preset_config_path, output_dir)
print(f"Copied {preset_config} to {output_dir}")
else:
print(f"Warning: PRESET_CONFIG file {preset_config_path} not found.")
# Write spec file
spec_file_path = os.path.join(output_dir, f"kernel-{device_name}.spec")
with open(spec_file_path, 'w') as f:
f.write(spec_content)
print(f"Generated spec file: {spec_file_path}")
return spec_file_path
if __name__ == "__main__":
if len(sys.argv) != 3:
print("Usage: generate_spec.py <vendor> <device>")
sys.exit(1)
vendor = sys.argv[1]
device = sys.argv[2]
generate_spec_file(vendor, device)

View file

@ -1,23 +0,0 @@
#!/usr/bin/env python
# -*- coding: utf-8 -*-
import os
import sys
import subprocess
from utils.common import clone_repo
from utils.patch import apply_kernel_patches
def clone_kernel(TMP_DIR, BASE_DIR, config, vendor, device, kernel_dir):
kernel_git = config.get("KERNEL").split("#")[0]
kernel_branch = config.get("KERNEL").split("#")[1]
clone_repo(kernel_git, kernel_branch, kernel_dir, "kernel")
apply_kernel_patches(BASE_DIR, vendor, device, kernel_dir)
def make_kernel_tar(kernel_dir, kernel_rpm_dir):
base_dir = os.getcwd()
os.chdir(kernel_dir)
subprocess.run(["git", "archive",
"--format=tar", "--prefix=kernel/",
f"--output={kernel_rpm_dir}/kernel.tar", "HEAD"])
os.chdir(base_dir)

View file

@ -1,46 +0,0 @@
%global debug_package %{nil}
%global _build_pkgcheck_set %nil
%global _build_pkgcheck_srpm %nil
%global _modulesdir /usr/lib/modules
Name: kernel-{BOARD_NAME}
Version: {KERNEL_VERSION}
Release: 1
Summary: kernel for {BOARD_NAME} devices
Group: System/Kernel and hardware
Source0: kernel.tar
# This line will be conditionally included or removed
{Source1}
License: GPLv2
Provides: kernel = %{EVRD}
%description
kernel for {BOARD_NAME}
%prep
%autosetup -p1 -n kernel
# If Source1 is defined, include this line
{SOURCE1_COMMAND}
grep -Irl /lib/modules | xargs sed -i -e 's,/lib/modules,%{_modulesdir},g' -e 's,/usr/usr/lib/modules,/usr/lib/modules,g'
%build
{MAKE_DEFCONFIG}
%make_build -s CROSS_COMPILE=/usr/bin/{ARCH}-linux-gnu- ARCH={KERNEL_ARCH}
%install
# Install kernel modules, if any
make V=1 modules_install ARCH={KERNEL_ARCH} \
INSTALL_MOD_PATH=%{buildroot} \
INSTALL_DTBS_PATH=%{buildroot}/boot/dtbs
make dtbs_install ARCH={KERNEL_ARCH} \
INSTALL_MOD_PATH=%{buildroot} \
INSTALL_DTBS_PATH=%{buildroot}/boot/dtbs
cp arch/arm64/boot/Image %{buildroot}/boot/vmlinuz-%{version}
%files
/boot/*
%{_modulesdir}/*

View file

@ -1,116 +0,0 @@
#!/usr/bin/env python
import os
import subprocess
def create_disk_image(tmp_dir, config, vendor, device):
boot_size = config.get("BOOT_SIZE", "").rstrip("MB")
root_size = config.get("ROOT_SIZE", "0").rstrip("MB")
if not root_size:
print("Error: ROOT_SIZE is not defined in the configuration.")
return
if not boot_size:
boot_size = "0"
disk_image_path = os.path.join(tmp_dir, vendor, device, "disk.img")
os.makedirs(os.path.dirname(disk_image_path), exist_ok=True)
cmd = [
"dd",
"if=/dev/zero",
f"of={disk_image_path}",
"bs=1M",
f"count={int(boot_size) + int(root_size)}"
]
print(f"Creating disk image: {disk_image_path} size {root_size} MB")
subprocess.run(cmd, check=True)
print(f"Disk image created at {disk_image_path}")
return disk_image_path
def cleanup_loop_devices():
result = subprocess.run(["losetup", "-l", "-O", "NAME"], capture_output=True, text=True)
active_loops = result.stdout.splitlines()[1:]
for loop_device in active_loops:
loop_device = loop_device.strip()
if loop_device:
print(f"Detaching {loop_device}")
subprocess.run(["losetup", "-d", loop_device], check=False)
def setup_loop_device(disk_image_path):
cleanup_loop_devices()
cmd = ["losetup", "-fP", "--show", disk_image_path]
result = subprocess.run(cmd, check=True, capture_output=True, text=True)
loop_device = result.stdout.strip()
print(f"Disk image mounted to loop device {loop_device}")
return loop_device
def create_partitions(loop_device, config):
root_size = config.get("ROOT_SIZE", "1024MB").rstrip("MB")
root_fstype = config.get("ROOT_FSTYPE", "ext4")
boot_size = config.get("BOOT_SIZE", "0").rstrip("MB") # Default to 0 if not specified
boot_fstype = config.get("BOOT_FSTYPE", "ext4") # Default to ext4 if not specified
print(f"Creating partitions on {loop_device} using sgdisk...")
try:
# Clear existing partitions
subprocess.run(["sgdisk", "--zap-all", loop_device], check=True)
if int(boot_size) > 0:
# Create boot partition
# 16M here s empty space for u-boot
subprocess.run(["sgdisk",
"--new=1:16M:+{}M".format(boot_size),
"--typecode=1:8300", loop_device], check=True)
# Create root partition
subprocess.run(["sgdisk",
"--new=2:0:0",
"--typecode=2:8300", loop_device], check=True)
# Format partitions
if boot_fstype == "vfat":
subprocess.run(["mkfs.vfat", "-F", "32", f"{loop_device}p1"], check=True)
else:
subprocess.run(["mkfs.ext4", f"{loop_device}p1"], check=True)
subprocess.run(["mkfs.ext4", f"{loop_device}p2"], check=True)
print(f" - Boot partition ({boot_size}MB) created and formatted as {boot_fstype}")
print(f" - Root partition created and formatted as {root_fstype}")
else:
# Create single root partition
subprocess.run(["sgdisk", "--new=1:0:0", "--typecode=1:8300", loop_device], check=True)
subprocess.run(["mkfs.ext4", f"{loop_device}p1"], check=True)
print(f" - Single root partition created and formatted as {root_fstype}")
except subprocess.CalledProcessError as e:
print(f"Error creating partitions with sgdisk: {e}")
return False
print("Partitioning and formatting complete.")
return True
def mount_partitions(config, loop_device, tmp_dir, vendor, device):
rootfs_dir = os.path.join(tmp_dir, vendor, device, "rootfs")
os.makedirs(rootfs_dir, exist_ok=True)
boot_partition = f"{loop_device}p1"
root_partition = f"{loop_device}p2" if "BOOT_SIZE" in config else f"{loop_device}p1"
print(f"Mounting root (/) partition at {rootfs_dir}")
subprocess.run(["mount", root_partition, rootfs_dir], check=True)
if "BOOT_SIZE" in config:
boot_dir = os.path.join(rootfs_dir, "boot")
os.makedirs(boot_dir, exist_ok=True)
print(f"Mounting /boot partition at {boot_dir}")
subprocess.run(["mount", boot_partition, boot_dir], check=True)
print("Mounting complete.")

View file

@ -1,60 +0,0 @@
#!/usr/bin/env python
# -*- coding: utf-8 -*-
import os
import subprocess
def apply_patches(patch_dir, target_dir):
"""
Applies patches from the specified patch directory to the target directory using `git apply`.
"""
if not os.path.exists(patch_dir):
print(f"No patches directory found at {patch_dir}. Skipping patch application.")
return
patches = sorted(os.listdir(patch_dir))
if not patches:
print(f"No patches found in {patch_dir}. Skipping patch application.")
return
print(f"Applying patches from {patch_dir} to {target_dir}...")
for patch in patches:
patch_path = os.path.join(patch_dir, patch)
if os.path.isfile(patch_path):
print(f"Applying patch: {patch}")
try:
check_result = subprocess.run(
["git", "apply", "--check", patch_path],
cwd=target_dir,
stdout=subprocess.PIPE,
stderr=subprocess.PIPE,
)
if check_result.returncode != 0:
print(f"Warning: Patch {patch} has already been applied or conflicts exist. Skipping.")
continue
subprocess.run(["git", "apply", patch_path], cwd=target_dir, check=True)
except subprocess.CalledProcessError as e:
print(f"Failed to apply patch {patch}: {e}")
else:
print(f"Skipping non-file item: {patch_path}")
def apply_kernel_patches(base_dir, vendor, device, kernel_dir):
"""
Apply kernel patches for the specified vendor and device.
"""
patch_dir = os.path.join(base_dir, "device", vendor, device, "patches", "kernel")
apply_patches(patch_dir, kernel_dir)
def apply_uboot_patches(base_dir, vendor, device, uboot_dir):
"""
Apply U-Boot patches for the specified vendor and device.
"""
patch_dir = os.path.join(base_dir, "device", vendor, device, "patches", "u-boot")
apply_patches(patch_dir, uboot_dir)

View file

@ -1,73 +0,0 @@
#!/usr/bin/env python
# -*- coding: utf-8 -*-
import os
import sys
import subprocess
def run_rpmbuild(kernel_build_dir, target_arch):
"""Run rpmbuild for the given kernel build directory and target architecture."""
# Ensure the kernel-build directory exists
if not os.path.isdir(kernel_build_dir):
print(f"Error: Kernel build directory '{kernel_build_dir}' does not exist.")
sys.exit(1)
base_dir = os.getcwd()
# Change to the kernel-build directory
os.chdir(kernel_build_dir)
# Check if a spec file exists
spec_files = [f for f in os.listdir(kernel_build_dir) if f.endswith(".spec")]
if not spec_files:
print(f"Error: No spec files found in '{kernel_build_dir}'.")
sys.exit(1)
elif len(spec_files) > 1:
print(f"Warning: Multiple spec files found in '{kernel_build_dir}'. Using '{spec_files[0]}'.")
# Use the first spec file found
spec_file = spec_files[0]
# Define paths for RPM build directories
rpmdir = os.path.join(kernel_build_dir, "RPMS")
builddir = os.path.join(kernel_build_dir, "BUILD")
os.makedirs(rpmdir, exist_ok=True)
os.makedirs(builddir, exist_ok=True)
# Construct the rpmbuild command
rpmbuild_command = [
"rpmbuild", "-ba",
f"--target={target_arch}",
f"--define", f"_sourcedir {kernel_build_dir}",
f"--define", f"_rpmdir {rpmdir}",
f"--define", f"_builddir {builddir}",
spec_file
]
# Run the rpmbuild command
try:
print(f"Running rpmbuild for spec file: {spec_file} with target architecture: {target_arch}")
#subprocess.run(rpmbuild_command, check=True)
subprocess.run(
rpmbuild_command,
stdin=subprocess.DEVNULL, # Отключение ввода
stdout=sys.stdout, # Вывод в стандартный поток
stderr=sys.stderr, # Вывод ошибок
check=True
)
print(f"RPM build completed successfully. RPMs are located in: {rpmdir}")
except subprocess.CalledProcessError as e:
print(f"Error: rpmbuild failed with error code {e.returncode}")
sys.exit(1)
# go back to script dir
os.chdir(base_dir)
if __name__ == "__main__":
if len(sys.argv) != 3:
print("Usage: rpmbuild.py <kernel-build-dir> <target-arch>")
sys.exit(1)
kernel_build_dir = sys.argv[1]
target_arch = sys.argv[2]
run_rpmbuild(kernel_build_dir, target_arch)

View file

@ -1,106 +0,0 @@
#!/usr/bin/env python
# -*- coding: utf-8 -*-
import os
import sys
import subprocess
from utils.common import clone_repo
from utils.common import download_blob
from utils.patch import apply_uboot_patches
def build_uboot(TMP_DIR, BASE_DIR, config, vendor, device):
uboot_git = config.get("UBOOT")
uboot_branch = config.get("UBOOT_VERSION")
uboot_config = config.get("UBOOT_CONFIG")
uboot_build_cmd = config.get("UBOOT_BUILD")
mkimage_cmd = config.get("MKIMAGE_CMD")
blobs_url = config.get("BLOBS_URL", "")
uboot_dir = os.path.join(TMP_DIR, vendor, device, "u-boot")
if "UBOOT" not in config or "UBOOT_VERSION" not in config:
print("U-Boot configuration not found. Skipping U-Boot build.")
return
# Clone U-Boot repository
clone_repo(uboot_git, uboot_branch, uboot_dir, "u-boot")
apply_uboot_patches(BASE_DIR, vendor, device, uboot_dir)
# Download RK_DDR blob to uboot_dir
rk_ddr = None
if "RK_DDR" in config:
rk_ddr = os.path.join(uboot_dir, os.path.basename(config.get("RK_DDR")))
rk_ddr_url = os.path.join(blobs_url, os.path.basename(rk_ddr))
if not os.path.isfile(rk_ddr):
if not download_blob(rk_ddr_url, rk_ddr):
print(f"Warning: RK_DDR blob {rk_ddr_url} could not be downloaded.")
# Download BL31 blob to uboot_dir
bl31 = None
if "BL31" in config:
bl31 = os.path.join(uboot_dir, os.path.basename(config.get("BL31")))
bl31_url = os.path.join(blobs_url, os.path.basename(bl31))
if not os.path.isfile(bl31):
if not download_blob(bl31_url, bl31):
print(f"Warning: BL31 blob {bl31_url} could not be downloaded.")
# Build U-Boot
os.chdir(uboot_dir)
try:
print(f"Building U-Boot for {vendor}/{device} {uboot_config}...")
subprocess.run(["make", uboot_config], check=True)
# Format U-Boot build command
build_command = uboot_build_cmd.format(
BL31=bl31 or "",
ARCH=config.get("ARCH", "aarch64")
)
subprocess.run(build_command, shell=True, check=True)
# Format mkimage command
mkimage_command = mkimage_cmd.format(
BOOT_SOC=config.get("BOOT_SOC", ""),
RK_DDR=rk_ddr or ""
)
print(mkimage_command)
subprocess.run(mkimage_command, shell=True, check=True)
print("U-Boot build completed successfully.")
except subprocess.CalledProcessError as e:
print(f"Error during U-Boot build: {e}")
os.chdir(BASE_DIR)
def flash_uboot(loop_device, TMP_DIR, config, vendor, device):
"""
Flash U-Boot components to the disk image.
Parameters:
loop_device (str): The loop device path (e.g., /dev/loop0).
uboot_dir (str): Directory where U-Boot artifacts are located.
config (dict): Configuration dictionary.
"""
uboot_dir = os.path.join(TMP_DIR, vendor, device, "u-boot")
idbloader_path = os.path.join(uboot_dir, config.get("BOOT_IDB", "idbloader.img"))
uboot_itb_path = os.path.join(uboot_dir, config.get("BOOT_ITB", "u-boot.itb"))
if not os.path.isfile(idbloader_path) or not os.path.isfile(uboot_itb_path):
print(f"Error: Required U-Boot files not found: {idbloader_path}, {uboot_itb_path}")
return False
try:
print(f"Flashing {idbloader_path} to {loop_device}...")
subprocess.run([
"dd", f"if={idbloader_path}", f"of={loop_device}", "seek=64",
"conv=notrunc", "status=none"
], check=True)
print(f"Flashing {uboot_itb_path} to {loop_device}...")
subprocess.run([
"dd", f"if={uboot_itb_path}", f"of={loop_device}", "seek=16384",
"conv=notrunc", "status=none"
], check=True)
print("U-Boot flashing completed successfully.")
except subprocess.CalledProcessError as e:
print(f"Error flashing U-Boot files: {e}")
return False
return True