mirror of
https://gitlab.com/kupfer/kupferbootstrap.git
synced 2025-02-23 05:35:44 -05:00
164 lines
6.8 KiB
Python
164 lines
6.8 KiB
Python
import click
|
|
import logging
|
|
import os
|
|
|
|
from typing import Optional
|
|
|
|
from config import config
|
|
from constants import Arch, ARCHES
|
|
from config.scheme import DataClass, munchclass
|
|
from distro.distro import get_kupfer_local
|
|
from distro.package import LocalPackage
|
|
from utils import read_files_from_tar
|
|
|
|
from .build import check_package_version_built
|
|
from .pkgbuild import Pkgbuild, _pkgbuilds_cache, discover_pkgbuilds, get_pkgbuild_by_path, init_pkgbuilds
|
|
from .deviceinfo import DeviceInfo, parse_deviceinfo
|
|
|
|
DEVICE_DEPRECATIONS = {
|
|
"oneplus-enchilada": "sdm845-oneplus-enchilada",
|
|
"oneplus-fajita": "sdm845-oneplus-fajita",
|
|
"xiaomi-beryllium-ebbg": "sdm845-sdm845-xiaomi-beryllium-ebbg",
|
|
"xiaomi-beryllium-tianma": "sdm845-sdm845-xiaomi-tianma",
|
|
"bq-paella": "msm8916-bq-paella",
|
|
}
|
|
|
|
|
|
@munchclass()
|
|
class Device(DataClass):
|
|
name: str
|
|
arch: Arch
|
|
package: Pkgbuild
|
|
deviceinfo: Optional[DeviceInfo]
|
|
|
|
def __repr__(self):
|
|
return (f'Device "{self.name}": "{self.package.description if self.package else ""}", '
|
|
f'Architecture: {self.arch}, package: {self.package.name if self.package else "??? PROBABLY A BUG!"}')
|
|
|
|
def parse_deviceinfo(self, try_download: bool = True, lazy: bool = True):
|
|
if not lazy or 'deviceinfo' not in self or self.deviceinfo is None:
|
|
is_built = check_package_version_built(self.package, self.arch, try_download=try_download)
|
|
if not is_built:
|
|
raise Exception(f"device package {self.package.name} for device {self.name} couldn't be acquired!")
|
|
pkgs: dict[str, LocalPackage] = get_kupfer_local(arch=self.arch, in_chroot=False, scan=True).get_packages()
|
|
if self.package.name not in pkgs:
|
|
raise Exception(f"device package {self.package.name} somehow not in repos, this is a kupferbootstrap bug")
|
|
pkg = pkgs[self.package.name]
|
|
file_path = pkg.acquire()
|
|
assert file_path
|
|
assert os.path.exists(file_path)
|
|
deviceinfo_path = 'etc/kupfer/deviceinfo'
|
|
for path, f in read_files_from_tar(file_path, [deviceinfo_path]):
|
|
if path != deviceinfo_path:
|
|
raise Exception(f'Somehow, we got a wrong file: expected: "{deviceinfo_path}", got: "{path}"')
|
|
with f as fd:
|
|
lines = fd.readlines()
|
|
assert lines
|
|
if lines and isinstance(lines[0], bytes):
|
|
lines = [line.decode() for line in lines]
|
|
info = parse_deviceinfo(lines, self.name)
|
|
assert info.arch
|
|
assert info.arch == self.arch
|
|
self['deviceinfo'] = info
|
|
return self.deviceinfo
|
|
|
|
|
|
def check_devicepkg_name(name: str, log_level: Optional[int] = None):
|
|
valid = True
|
|
if not name.startswith('device-'):
|
|
valid = False
|
|
if log_level is not None:
|
|
logging.log(log_level, f'invalid device package name "{name}": doesn\'t start with "device-"')
|
|
if name.endswith('-common'):
|
|
valid = False
|
|
if log_level is not None:
|
|
logging.log(log_level, f'invalid device package name "{name}": ends with "-common"')
|
|
return valid
|
|
|
|
|
|
def parse_device_pkg(pkgbuild: Pkgbuild) -> Device:
|
|
if len(pkgbuild.arches) != 1:
|
|
raise Exception(f"{pkgbuild.name}: Device package must have exactly one arch, but has {pkgbuild.arches}")
|
|
arch = pkgbuild.arches[0]
|
|
if arch == 'any' or arch not in ARCHES:
|
|
raise Exception(f'unknown arch for device package: {arch}')
|
|
if pkgbuild.repo != 'device':
|
|
logging.warning(f'device package {pkgbuild.name} is in unexpected repo "{pkgbuild.repo}", expected "device"')
|
|
name = pkgbuild.name
|
|
prefix = 'device-'
|
|
if name.startswith(prefix):
|
|
name = name[len(prefix):]
|
|
return Device(name=name, arch=arch, package=pkgbuild, deviceinfo=None)
|
|
|
|
|
|
_device_cache: dict[str, Device] = {}
|
|
_device_cache_populated: bool = False
|
|
|
|
|
|
def get_devices(pkgbuilds: Optional[dict[str, Pkgbuild]] = None, lazy: bool = True) -> dict[str, Device]:
|
|
global _device_cache, _device_cache_populated
|
|
use_cache = _device_cache_populated and lazy
|
|
if not use_cache:
|
|
logging.info("Searching PKGBUILDs for device packages")
|
|
if not pkgbuilds:
|
|
pkgbuilds = discover_pkgbuilds(lazy=lazy, repositories=['device'])
|
|
_device_cache.clear()
|
|
for pkgbuild in pkgbuilds.values():
|
|
if not (pkgbuild.repo == 'device' and check_devicepkg_name(pkgbuild.name, log_level=None)):
|
|
continue
|
|
dev = parse_device_pkg(pkgbuild)
|
|
_device_cache[dev.name] = dev
|
|
_device_cache_populated = True
|
|
return _device_cache.copy()
|
|
|
|
|
|
def get_device(name: str, pkgbuilds: Optional[dict[str, Pkgbuild]] = None, lazy: bool = True, scan_all=False) -> Device:
|
|
global _device_cache, _device_cache_populated
|
|
assert lazy or pkgbuilds
|
|
if name in DEVICE_DEPRECATIONS:
|
|
warning = f"Deprecated device {name}"
|
|
replacement = DEVICE_DEPRECATIONS[name]
|
|
if replacement:
|
|
warning += (f': Device has been renamed to {replacement}! Please adjust your profile config!\n'
|
|
'This will become an error in a future version!')
|
|
name = replacement
|
|
logging.warning(warning)
|
|
if lazy and name in _device_cache:
|
|
return _device_cache[name]
|
|
if scan_all:
|
|
devices = get_devices(pkgbuilds=pkgbuilds, lazy=lazy)
|
|
if name not in devices:
|
|
raise Exception(f'Unknown device {name}!')
|
|
return devices[name]
|
|
else:
|
|
pkgname = f'device-{name}'
|
|
if pkgbuilds:
|
|
if pkgname not in pkgbuilds:
|
|
raise Exception(f'Unknown device {name}!')
|
|
pkgbuild = pkgbuilds[pkgname]
|
|
else:
|
|
if lazy and pkgname in _pkgbuilds_cache:
|
|
pkgbuild = _pkgbuilds_cache[pkgname]
|
|
else:
|
|
init_pkgbuilds()
|
|
relative_path = os.path.join('device', pkgname)
|
|
if not os.path.exists(os.path.join(config.get_path('pkgbuilds'), relative_path)):
|
|
raise Exception(f'unknown device "{name}": pkgbuilds/{relative_path} doesn\'t exist.')
|
|
pkgbuild = [p for p in get_pkgbuild_by_path(relative_path, lazy=lazy, _config=config) if p.name == pkgname][0]
|
|
device = parse_device_pkg(pkgbuild)
|
|
if lazy:
|
|
_device_cache[name] = device
|
|
return device
|
|
|
|
|
|
def get_profile_device(profile_name: Optional[str] = None, hint_or_set_arch: bool = False):
|
|
profile = config.enforce_profile_device_set(profile_name=profile_name, hint_or_set_arch=hint_or_set_arch)
|
|
return get_device(profile.device)
|
|
|
|
|
|
@click.command(name='list')
|
|
def cmd_devices_list():
|
|
'list the available flavours and descriptions'
|
|
devices = get_devices()
|
|
for d in sorted(devices.keys()):
|
|
print(devices[d])
|