commit 723ed5ce4c80ad8c656dfa8686e1e738bd3054cc Author: Alexander Stefanov Date: Wed Nov 13 11:48:35 2024 +0000 init new os-image-builder tool 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 0000000..cc89fd1 Binary files /dev/null and b/utils/__pycache__/bootstrap_setup.cpython-311.pyc differ 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"])