forked from Mirror/pmbootstrap
Move pmb/parse/arch.py over to core and refactor it as an Arch type, similar to how Chroot was done. Fix all the uses (that I can find) of arch in the codebase that need adjusting. The new Arch type is an Enum, making it clear what architectures can be represented and making it much easier to reason about. Since we support ~5 (kinda) different representations of an Architecture (Alpine, Kernel, target triple, platform, and QEMU), we now formalise that the Alpine format is what we represent internally, with methods to convert to any of the others as-needed. Signed-off-by: Caleb Connolly <caleb@postmarketos.org>
339 lines
12 KiB
Python
339 lines
12 KiB
Python
# Copyright 2023 Oliver Smith
|
|
# SPDX-License-Identifier: GPL-3.0-or-later
|
|
from pmb.core import get_context
|
|
from pmb.helpers import logging
|
|
import os
|
|
from pmb.types import PmbArgs
|
|
import pmb.helpers.cli
|
|
import pmb.helpers.run
|
|
import pmb.aportgen.core
|
|
import pmb.parse.apkindex
|
|
import pmb.parse
|
|
|
|
|
|
def ask_for_architecture():
|
|
architectures = Arch.supported()
|
|
# Don't show armhf, new ports shouldn't use this architecture
|
|
if "armhf" in architectures:
|
|
architectures.remove("armhf")
|
|
while True:
|
|
ret = pmb.helpers.cli.ask("Device architecture", architectures,
|
|
"aarch64", complete=architectures)
|
|
if ret in architectures:
|
|
return ret
|
|
logging.fatal("ERROR: Invalid architecture specified. If you want to"
|
|
" add a new architecture, edit"
|
|
" build_device_architectures in"
|
|
" pmb/config/__init__.py.")
|
|
|
|
|
|
def ask_for_manufacturer():
|
|
logging.info("Who produced the device (e.g. LG)?")
|
|
return pmb.helpers.cli.ask("Manufacturer", None, None, False)
|
|
|
|
|
|
def ask_for_name(manufacturer):
|
|
logging.info("What is the official name (e.g. Google Nexus 5)?")
|
|
ret = pmb.helpers.cli.ask("Name", None, None, False)
|
|
|
|
# Always add the manufacturer
|
|
if not ret.startswith(manufacturer) and \
|
|
not ret.startswith("Google"):
|
|
ret = manufacturer + " " + ret
|
|
return ret
|
|
|
|
|
|
def ask_for_year():
|
|
# Regex from https://stackoverflow.com/a/12240826
|
|
logging.info("In what year was the device released (e.g. 2012)?")
|
|
return pmb.helpers.cli.ask("Year", None, None, False,
|
|
validation_regex=r'^[1-9]\d{3,}$')
|
|
|
|
|
|
def ask_for_chassis():
|
|
types = pmb.config.deviceinfo_chassis_types
|
|
|
|
logging.info("What type of device is it?")
|
|
logging.info("Valid types are: " + ", ".join(types))
|
|
return pmb.helpers.cli.ask("Chassis", None, None, True,
|
|
validation_regex='|'.join(types),
|
|
complete=types)
|
|
|
|
|
|
def ask_for_keyboard():
|
|
return pmb.helpers.cli.confirm("Does the device have a hardware"
|
|
" keyboard?")
|
|
|
|
|
|
def ask_for_external_storage():
|
|
return pmb.helpers.cli.confirm("Does the device have a sdcard or"
|
|
" other external storage medium?")
|
|
|
|
|
|
def ask_for_flash_method():
|
|
while True:
|
|
logging.info("Which flash method does the device support?")
|
|
method = pmb.helpers.cli.ask("Flash method",
|
|
pmb.config.flash_methods,
|
|
"none",
|
|
complete=pmb.config.flash_methods)
|
|
|
|
if method in pmb.config.flash_methods:
|
|
if method == "heimdall":
|
|
heimdall_types = ["isorec", "bootimg"]
|
|
while True:
|
|
logging.info("Does the device use the \"isolated"
|
|
" recovery\" or boot.img?")
|
|
logging.info("<https://wiki.postmarketos.org/wiki"
|
|
"/Deviceinfo_flash_methods#Isorec_or_bootimg"
|
|
".3F>")
|
|
heimdall_type = pmb.helpers.cli.ask("Type",
|
|
heimdall_types,
|
|
heimdall_types[0])
|
|
if heimdall_type in heimdall_types:
|
|
method += "-" + heimdall_type
|
|
break
|
|
logging.fatal("ERROR: Invalid type specified.")
|
|
return method
|
|
|
|
logging.fatal("ERROR: Invalid flash method specified. If you want to"
|
|
" add a new flash method, edit flash_methods in"
|
|
" pmb/config/__init__.py.")
|
|
|
|
|
|
def ask_for_bootimg():
|
|
logging.info("You can analyze a known working boot.img file to"
|
|
" automatically fill out the flasher information for your"
|
|
" deviceinfo file. Either specify the path to an image or"
|
|
" press return to skip this step (you can do it later with"
|
|
" 'pmbootstrap bootimg_analyze').")
|
|
|
|
while True:
|
|
response = pmb.helpers.cli.ask("Path", None, "", False)
|
|
path = os.path.expanduser(response)
|
|
if not path:
|
|
return None
|
|
try:
|
|
return pmb.parse.bootimg(path)
|
|
except Exception as e:
|
|
logging.fatal("ERROR: " + str(e) + ". Please try again.")
|
|
|
|
|
|
def generate_deviceinfo_fastboot_content(bootimg=None):
|
|
if bootimg is None:
|
|
bootimg = {"cmdline": "",
|
|
"qcdt": "false",
|
|
"dtb_second": "false",
|
|
"base": "",
|
|
"kernel_offset": "",
|
|
"ramdisk_offset": "",
|
|
"second_offset": "",
|
|
"tags_offset": "",
|
|
"pagesize": "2048",
|
|
"mtk_label_kernel": "",
|
|
"mtk_label_ramdisk": ""}
|
|
|
|
content = f"""\
|
|
deviceinfo_kernel_cmdline="{bootimg["cmdline"]}"
|
|
deviceinfo_generate_bootimg="true"
|
|
deviceinfo_bootimg_qcdt="{bootimg["qcdt"]}"
|
|
deviceinfo_bootimg_dtb_second="{bootimg["dtb_second"]}"
|
|
deviceinfo_flash_pagesize="{bootimg["pagesize"]}"
|
|
"""
|
|
|
|
if "qcdt_type" in bootimg.keys():
|
|
content += f"""\
|
|
deviceinfo_bootimg_qcdt_type="{bootimg["qcdt_type"]}"
|
|
"""
|
|
|
|
if "mtk_label_kernel" in bootimg.keys():
|
|
content += f"""\
|
|
deviceinfo_mtk_label_kernel="{bootimg["mtk_label_kernel"]}"
|
|
"""
|
|
if "mtk_label_ramdisk" in bootimg.keys():
|
|
content += f"""\
|
|
deviceinfo_mtk_label_ramdisk="{bootimg["mtk_label_ramdisk"]}"
|
|
"""
|
|
|
|
if "header_version" in bootimg.keys():
|
|
content += f"""\
|
|
deviceinfo_header_version="{bootimg["header_version"]}"
|
|
"""
|
|
|
|
if bootimg["header_version"] == "2":
|
|
content += f"""\
|
|
deviceinfo_append_dtb="false"
|
|
deviceinfo_flash_offset_dtb="{bootimg["dtb_offset"]}"
|
|
"""
|
|
|
|
if "base" in bootimg.keys():
|
|
content += f"""\
|
|
deviceinfo_flash_offset_base="{bootimg["base"]}"
|
|
deviceinfo_flash_offset_kernel="{bootimg["kernel_offset"]}"
|
|
deviceinfo_flash_offset_ramdisk="{bootimg["ramdisk_offset"]}"
|
|
deviceinfo_flash_offset_second="{bootimg["second_offset"]}"
|
|
deviceinfo_flash_offset_tags="{bootimg["tags_offset"]}"
|
|
"""
|
|
|
|
return content
|
|
|
|
|
|
def generate_deviceinfo(pkgname, name, manufacturer, year, arch,
|
|
chassis, has_keyboard, has_external_storage,
|
|
flash_method, bootimg=None):
|
|
codename = "-".join(pkgname.split("-")[1:])
|
|
external_storage = "true" if has_external_storage else "false"
|
|
# Note: New variables must be added to pmb/config/__init__.py as well
|
|
content = f"""\
|
|
# Reference: <https://postmarketos.org/deviceinfo>
|
|
# Please use double quotes only. You can source this file in shell
|
|
# scripts.
|
|
|
|
deviceinfo_format_version="0"
|
|
deviceinfo_name="{name}"
|
|
deviceinfo_manufacturer="{manufacturer}"
|
|
deviceinfo_codename="{codename}"
|
|
deviceinfo_year="{year}"
|
|
deviceinfo_dtb=""
|
|
deviceinfo_arch="{arch}"
|
|
|
|
# Device related
|
|
deviceinfo_chassis="{chassis}"
|
|
deviceinfo_keyboard="{"true" if has_keyboard else "false"}"
|
|
deviceinfo_external_storage="{external_storage}"
|
|
|
|
# Bootloader related
|
|
deviceinfo_flash_method="{flash_method}"
|
|
"""
|
|
|
|
content_heimdall_bootimg = """\
|
|
deviceinfo_flash_heimdall_partition_kernel=""
|
|
deviceinfo_flash_heimdall_partition_rootfs=""
|
|
"""
|
|
|
|
content_heimdall_isorec = """\
|
|
deviceinfo_flash_heimdall_partition_kernel=""
|
|
deviceinfo_flash_heimdall_partition_initfs=""
|
|
deviceinfo_flash_heimdall_partition_rootfs=""
|
|
"""
|
|
|
|
content_0xffff = """\
|
|
deviceinfo_generate_legacy_uboot_initfs="true"
|
|
"""
|
|
|
|
content_uuu = """\
|
|
deviceinfo_generate_legacy_uboot_initfs="true"
|
|
"""
|
|
|
|
if flash_method == "fastboot":
|
|
content += generate_deviceinfo_fastboot_content(bootimg)
|
|
elif flash_method == "heimdall-bootimg":
|
|
content += generate_deviceinfo_fastboot_content(bootimg)
|
|
content += content_heimdall_bootimg
|
|
elif flash_method == "heimdall-isorec":
|
|
content += content_heimdall_isorec
|
|
elif flash_method == "0xffff":
|
|
content += content_0xffff
|
|
elif flash_method == "uuu":
|
|
content += content_uuu
|
|
|
|
# Write to file
|
|
work = get_context().config.work
|
|
pmb.helpers.run.user(["mkdir", "-p", work / "aportgen"])
|
|
path = work / "aportgen/deviceinfo"
|
|
with open(path, "w", encoding="utf-8") as handle:
|
|
for line in content.rstrip().split("\n"):
|
|
handle.write(line.lstrip() + "\n")
|
|
|
|
|
|
def generate_modules_initfs():
|
|
content = """\
|
|
# Remove this file if unnecessary (CHANGEME!)
|
|
# This file shall contain a list of modules to be included in the initramfs,
|
|
# so that they are available in early boot stages. In general, it should
|
|
# include modules to support unlocking FDE (touchscreen, panel, etc),
|
|
# USB networking, and telnet in the debug-shell.
|
|
# The format is one module name per line. Lines starting with the character
|
|
# '#', and empty lines are ignored. If there are multiple kernel variants
|
|
# with different initramfs module requirements, one modules-initfs.$variant
|
|
# file should be created for each of them.
|
|
"""
|
|
|
|
# Write to file
|
|
work = get_context().config.work
|
|
pmb.helpers.run.user(["mkdir", "-p", work / "aportgen"])
|
|
path = work / "aportgen/modules-initfs"
|
|
with open(path, "w", encoding="utf-8") as handle:
|
|
for line in content.rstrip().split("\n"):
|
|
handle.write(line.lstrip() + "\n")
|
|
|
|
|
|
def generate_apkbuild(pkgname, name, arch, flash_method):
|
|
# Dependencies
|
|
depends = ["postmarketos-base",
|
|
"linux-" + "-".join(pkgname.split("-")[1:])]
|
|
if flash_method in ["fastboot", "heimdall-bootimg"]:
|
|
depends.append("mkbootimg")
|
|
if flash_method == "0xffff":
|
|
depends.append("uboot-tools")
|
|
|
|
# Whole APKBUILD
|
|
depends.sort()
|
|
depends_fmt = ("\n" + " " * 12).join(depends)
|
|
content = f"""\
|
|
# Reference: <https://postmarketos.org/devicepkg>
|
|
pkgname={pkgname}
|
|
pkgdesc="{name}"
|
|
pkgver=1
|
|
pkgrel=0
|
|
url="https://postmarketos.org"
|
|
license="MIT"
|
|
arch="{arch}"
|
|
options="!check !archcheck"
|
|
depends="
|
|
{depends_fmt}
|
|
"
|
|
makedepends="devicepkg-dev"
|
|
source="
|
|
deviceinfo
|
|
modules-initfs
|
|
"
|
|
|
|
build() {{
|
|
devicepkg_build $startdir $pkgname
|
|
}}
|
|
|
|
package() {{
|
|
devicepkg_package $startdir $pkgname
|
|
}}
|
|
|
|
sha512sums="(run 'pmbootstrap checksum {pkgname}' to fill)"
|
|
"""
|
|
|
|
# Write the file
|
|
work = get_context().config.work
|
|
pmb.helpers.run.user(["mkdir", "-p", work / "aportgen"])
|
|
path = work / "aportgen/APKBUILD"
|
|
with open(path, "w", encoding="utf-8") as handle:
|
|
for line in content.rstrip().split("\n"):
|
|
handle.write(line[8:].replace(" " * 4, "\t") + "\n")
|
|
|
|
|
|
def generate(pkgname):
|
|
arch = ask_for_architecture()
|
|
manufacturer = ask_for_manufacturer()
|
|
name = ask_for_name(manufacturer)
|
|
year = ask_for_year()
|
|
chassis = ask_for_chassis()
|
|
has_keyboard = ask_for_keyboard()
|
|
has_external_storage = ask_for_external_storage()
|
|
flash_method = ask_for_flash_method()
|
|
bootimg = None
|
|
if flash_method in ["fastboot", "heimdall-bootimg"]:
|
|
bootimg = ask_for_bootimg()
|
|
|
|
generate_deviceinfo(pkgname, name, manufacturer, year, arch,
|
|
chassis, has_keyboard, has_external_storage,
|
|
flash_method, bootimg)
|
|
generate_modules_initfs()
|
|
generate_apkbuild(pkgname, name, arch, flash_method)
|