mirror of
https://github.com/rosalinux/image-builder.git
synced 2025-02-23 10:22:50 +00:00
init new os-image-builder tool
This commit is contained in:
commit
723ed5ce4c
7 changed files with 221 additions and 0 deletions
0
bootstrap/README.md
Normal file
0
bootstrap/README.md
Normal file
7
bootstrap/rosa13
Normal file
7
bootstrap/rosa13
Normal file
|
@ -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"
|
87
build.py
Executable file
87
build.py
Executable file
|
@ -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 <distro_name> <vendor/device> [--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()
|
||||||
|
|
7
device/raspberry/pi4b/config
Normal file
7
device/raspberry/pi4b/config
Normal file
|
@ -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
|
25
device/raspberry/pi4b/config.txt
Normal file
25
device/raspberry/pi4b/config.txt
Normal file
|
@ -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
|
BIN
utils/__pycache__/bootstrap_setup.cpython-311.pyc
Normal file
BIN
utils/__pycache__/bootstrap_setup.cpython-311.pyc
Normal file
Binary file not shown.
95
utils/bootstrap_setup.py
Normal file
95
utils/bootstrap_setup.py
Normal file
|
@ -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"])
|
Loading…
Add table
Reference in a new issue