From 723ed5ce4c80ad8c656dfa8686e1e738bd3054cc Mon Sep 17 00:00:00 2001 From: Alexander Stefanov Date: Wed, 13 Nov 2024 11:48:35 +0000 Subject: [PATCH] init new os-image-builder tool --- bootstrap/README.md | 0 bootstrap/rosa13 | 7 ++ build.py | 87 ++++++++++++++++ device/raspberry/pi4b/config | 7 ++ device/raspberry/pi4b/config.txt | 25 +++++ .../bootstrap_setup.cpython-311.pyc | Bin 0 -> 4785 bytes utils/bootstrap_setup.py | 95 ++++++++++++++++++ 7 files changed, 221 insertions(+) create mode 100644 bootstrap/README.md create mode 100644 bootstrap/rosa13 create mode 100755 build.py create mode 100644 device/raspberry/pi4b/config create mode 100644 device/raspberry/pi4b/config.txt create mode 100644 utils/__pycache__/bootstrap_setup.cpython-311.pyc create mode 100644 utils/bootstrap_setup.py diff --git a/bootstrap/README.md b/bootstrap/README.md new file mode 100644 index 0000000..e69de29 diff --git a/bootstrap/rosa13 b/bootstrap/rosa13 new file mode 100644 index 0000000..380d9b6 --- /dev/null +++ b/bootstrap/rosa13 @@ -0,0 +1,7 @@ +RELEASE="rosa13" +ABF_DOWNLOADS="https://abf-downloads.rosalinux.ru" +PKGS="basesystem-minimal locales-en dbus coreutils findutils util-linux xz e2fsprogs passwd openssh-server" +WEAK_DEPS="false" +DEFAULT_USER="rosa" +DEFAULT_USER_PASSWORD="rosa" +PASSWD_ROOT="root" diff --git a/build.py b/build.py new file mode 100755 index 0000000..c2f1d06 --- /dev/null +++ b/build.py @@ -0,0 +1,87 @@ +#!/usr/bin/env python + +import os +import sys +import subprocess +import multiprocessing +from utils.bootstrap_setup import setup_bootstrap, load_config + +BASE_DIR = os.getcwd() +TMP_DIR = os.path.join(BASE_DIR, "tmp") +NUM_CORES = str(multiprocessing.cpu_count()) + + +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 build_kernel(config, vendor, device): + kernel_dir = os.path.join(TMP_DIR, vendor, device, "kernel") + clone_repo(config["KERNEL"].split("#")[0], config["KERNEL"].split("#")[1], kernel_dir, "Kernel") + + os.chdir(kernel_dir) + subprocess.run(["make", config["KERNEL_CONFIG"]], check=True) + subprocess.run(["make", config.get("KERNELTARGET", "Image"), "-j" + NUM_CORES], check=True) + os.chdir(BASE_DIR) + + +def build_uboot(config, vendor, device): + if "UBOOT" not in config or "UBOOT_VERSION" not in config: + print("U-Boot configuration not found. Skipping U-Boot build.") + return + + uboot_dir = os.path.join(TMP_DIR, vendor, device, "u-boot") + clone_repo(config["UBOOT"], config["UBOOT_VERSION"], uboot_dir, "U-Boot") + + os.chdir(uboot_dir) + subprocess.run(["make", config["UBOOT_CONFIG"]], check=True) + subprocess.run(["make", "-j" + NUM_CORES], check=True) + os.chdir(BASE_DIR) + + +def main(): + if len(sys.argv) < 3 or "--distro" not in sys.argv: + print("Usage: python build.py --distro [--skip-kernel] [--skip-uboot]") + 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 + + if not os.path.exists(config_path): + print(f"Configuration file for {vendor}/{device} not found.") + sys.exit(1) + + config = load_config(config_path) + if config["ARCH"] != "aarch64": + print("Unsupported architecture.") + sys.exit(1) + + print(f"Building for {vendor}/{device} with distro {distro}...") + + if not skip_kernel: + build_kernel(config, vendor, device) + else: + print("Skipping kernel build.") + + if not skip_uboot: + build_uboot(config, vendor, device) + else: + print("Skipping U-Boot build.") + + setup_bootstrap("bootstrap", TMP_DIR, vendor, device, distro) + + print(f"Build completed for {vendor}/{device} with distro {distro}") + + +if __name__ == "__main__": + main() + diff --git a/device/raspberry/pi4b/config b/device/raspberry/pi4b/config new file mode 100644 index 0000000..b2379d8 --- /dev/null +++ b/device/raspberry/pi4b/config @@ -0,0 +1,7 @@ +ARCH="aarch64" +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 diff --git a/device/raspberry/pi4b/config.txt b/device/raspberry/pi4b/config.txt new file mode 100644 index 0000000..543df3e --- /dev/null +++ b/device/raspberry/pi4b/config.txt @@ -0,0 +1,25 @@ +# 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 diff --git a/utils/__pycache__/bootstrap_setup.cpython-311.pyc b/utils/__pycache__/bootstrap_setup.cpython-311.pyc new file mode 100644 index 0000000000000000000000000000000000000000..cc89fd1f6825f59ac789efbf15a13ca03740713e GIT binary patch literal 4785 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 R1!NPShw3e$jl^2ozX1{IV0Hii literal 0 HcmV?d00001 diff --git a/utils/bootstrap_setup.py b/utils/bootstrap_setup.py new file mode 100644 index 0000000..e976783 --- /dev/null +++ b/utils/bootstrap_setup.py @@ -0,0 +1,95 @@ +import os +import sys +import subprocess + + +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 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): + """Run dnf command to install packages based on the bootstrap configuration.""" + pkgs = config["PKGS"] + weak_deps = config["WEAK_DEPS"].lower() + + dnf_command = [ + "sudo", + "dnf", + "--setopt=install_weak_deps=" + str(weak_deps), + "--config", dnf_conf_path, + "--installroot", rootfs_dir, + "install" + ] + pkgs.split() + + subprocess.run(dnf_command, check=True) + + +def setup_bootstrap(bootstrap_dir, tmp_dir, vendor, device, distro): + # 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.") + sys.exit(1) + + config = load_config(distro_config_path) + + 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) + + #setup_user(rootfs_dir, config["DEFAULT_USER"], config["DEFAULT_USER_PASSWORD"], config["PASSWD_ROOT"])