diff --git a/aports/device/device-nokia-rx51/APKBUILD b/aports/device/device-nokia-rx51/APKBUILD index 6b4b44d9..84faf287 100644 --- a/aports/device/device-nokia-rx51/APKBUILD +++ b/aports/device/device-nokia-rx51/APKBUILD @@ -1,6 +1,6 @@ pkgname=device-nokia-rx51 pkgver=1 -pkgrel=25 +pkgrel=26 pkgdesc="Nokia N900" url="https://github.com/postmarketOS" arch="noarch" @@ -69,7 +69,7 @@ weston() { "$subpkgdir"/etc/xdg/weston/weston.ini } -sha512sums="6309f2e0c6c08f8907fc2a0b94c4a0842fc6d927bd92acb35a068605acffecbaa7d4af31e9329e6686b592840ba7761c58856f7b8378c795771fbd84ddbfcf33 deviceinfo +sha512sums="8e010bebddf1bb09cde3966e402d2c146476eb21761c3110d2ac01010eced42519dab7b81915899894914c7e3820eaf6c48767a21c955412ad53da0bade0c38c deviceinfo 1b89309dd4fe7ee0ba37c6224a0152d6864bb1c7bc4e96918a57e01bebc4173559855ae9673887223de4a8baa3191c8ad88ec8594776a4110cdb19a7be790db4 uboot-script.cmd 3d55e34b95791636e44a5f41754f3d0de039dbba41f7a556d43a95c9e64afcfa930046b4b96b40020b6f196096ffba93514682927e32fa4488686fdd19c6da5a backlight-enable.sh d303734dd49fe75a299ca723f4da52bc0cda2775683c54aa736aabf397db4ae8deb6d912d4116800cf2ba17f3a2987ab3e839652879b8ab023b4a91a55849f08 90-touchscreen-dev.rules diff --git a/aports/device/device-nokia-rx51/deviceinfo b/aports/device/device-nokia-rx51/deviceinfo index ef0f00bc..574f9b2a 100644 --- a/aports/device/device-nokia-rx51/deviceinfo +++ b/aports/device/device-nokia-rx51/deviceinfo @@ -12,7 +12,7 @@ deviceinfo_dtb="omap3-n900" deviceinfo_modules_initfs="tsc2005 tsc200x-core omap_wdt twl4030_wdt omap-sham" deviceinfo_external_disk="true" deviceinfo_external_disk_install="true" -deviceinfo_flash_methods="0xFFFF" +deviceinfo_flash_methods="0xffff" deviceinfo_generate_legacy_uboot_initfs="true" deviceinfo_arch="armhf" deviceinfo_dev_touchscreen="/dev/input/event3" diff --git a/aports/main/postmarketos-update-kernel/APKBUILD b/aports/main/postmarketos-update-kernel/APKBUILD index 332240da..85cd0b57 100644 --- a/aports/main/postmarketos-update-kernel/APKBUILD +++ b/aports/main/postmarketos-update-kernel/APKBUILD @@ -1,5 +1,5 @@ pkgname=postmarketos-update-kernel -pkgver=0.0.1 +pkgver=0.0.2 pkgrel=0 pkgdesc="kernel updater script for postmarketOS" url="https://github.com/postmarketOS" @@ -12,4 +12,4 @@ package() { install -Dm755 "$srcdir/update-kernel.sh" \ "$pkgdir/sbin/pmos-update-kernel" } -sha512sums="7d2f3031b1a468accff5a3584bd51d3607141b01e1d2a93d611852476bdeecc3edd7b80f8e3b034012d9b5f09de907af1de02f48586d82d06ee4b5746d4fd286 update-kernel.sh" +sha512sums="17fa14327622fcdefa335fccfeac33623a8cf3cb93e6ad833631990f3c88757e81d6eb3b02f0a69177c518b8f45f249e8b9709fe3eb5126a7322da5f7700becb update-kernel.sh" diff --git a/aports/main/postmarketos-update-kernel/update-kernel.sh b/aports/main/postmarketos-update-kernel/update-kernel.sh index e45693c9..8ce79bc4 100755 --- a/aports/main/postmarketos-update-kernel/update-kernel.sh +++ b/aports/main/postmarketos-update-kernel/update-kernel.sh @@ -28,7 +28,7 @@ case $METHOD in echo "Flashing initramfs..." gunzip -c /boot/initramfs-"$FLAVOR" | lzop | dd of="$INITFS_PARTITION" bs=1M ;; - 0xFFFF) + 0xffff) echo -n "No need to use this utility, since uboot loads the kernel directly from" echo " the boot partition. Your kernel should be updated already." exit 1 diff --git a/pmb/__init__.py b/pmb/__init__.py index 5f47b1a8..3d4b32db 100644 --- a/pmb/__init__.py +++ b/pmb/__init__.py @@ -25,6 +25,7 @@ import traceback from . import config from . import parse +from .config import init as config_init from .helpers import frontend from .helpers import logging as pmb_logging from .helpers import other @@ -42,7 +43,7 @@ def main(): # Initialize or require config if args.action == "init": - return config.init(args) + return config_init.frontend(args) elif not os.path.exists(args.config): logging.critical("Please specify a config file, or run" " 'pmbootstrap init' to generate one.") diff --git a/pmb/aportgen/__init__.py b/pmb/aportgen/__init__.py index 452e6a3b..154084c4 100644 --- a/pmb/aportgen/__init__.py +++ b/pmb/aportgen/__init__.py @@ -19,33 +19,47 @@ along with pmbootstrap. If not, see . import os import logging import pmb.aportgen.binutils -import pmb.aportgen.musl -import pmb.aportgen.gcc import pmb.aportgen.busybox_static -import pmb.helpers.git +import pmb.aportgen.device +import pmb.aportgen.gcc +import pmb.aportgen.linux +import pmb.aportgen.musl +import pmb.config +import pmb.helpers.cli + + +def properties(pkgname): + """ + Get the `pmb.config.aportgen` properties for the aport generator, based on + the pkgname prefix. + + Example: "musl-armhf" => ("musl", "cross", {"confirm_overwrite": False}) + + :param pkgname: package name + :returns: (prefix, folder, options) + """ + for folder, options in pmb.config.aportgen.items(): + for prefix in options["prefixes"]: + if pkgname.startswith(prefix): + return (prefix, folder, options) + raise ValueError("No generator available for " + pkgname + "!") def generate(args, pkgname): - # Prepare git repo and temp folder - pmb.helpers.git.clone(args, "aports_upstream") - logging.info("(native) generate " + pkgname + " aport") + # Confirm overwrite + prefix, folder, options = properties(pkgname) + path_target = args.aports + "/" + folder + "/" + pkgname + if options["confirm_overwrite"] and os.path.exists(path_target): + logging.warning("WARNING: Target folder already exists: " + path_target) + if not pmb.helpers.cli.confirm(args, "Continue and overwrite?"): + raise RuntimeError("Aborted.") + + # Run pmb.aportgen.PREFIX.generate() if os.path.exists(args.work + "/aportgen"): pmb.helpers.run.user(args, ["rm", "-r", args.work + "/aportgen"]) - - # Choose generator based on the name - if pkgname.startswith("binutils-"): - pmb.aportgen.binutils.generate(args, pkgname) - elif pkgname.startswith("musl-"): - pmb.aportgen.musl.generate(args, pkgname) - elif pkgname.startswith("gcc-"): - pmb.aportgen.gcc.generate(args, pkgname) - elif pkgname.startswith("busybox-static-"): - pmb.aportgen.busybox_static.generate(args, pkgname) - else: - raise ValueError("No generator available for " + pkgname + "!") + getattr(pmb.aportgen, prefix.replace("-", "_")).generate(args, pkgname) # Move to the aports folder - path_target = args.aports + "/cross/" + pkgname if os.path.exists(path_target): pmb.helpers.run.user(args, ["rm", "-r", path_target]) pmb.helpers.run.user( diff --git a/pmb/aportgen/binutils.py b/pmb/aportgen/binutils.py index 64ca12ca..90fc8561 100644 --- a/pmb/aportgen/binutils.py +++ b/pmb/aportgen/binutils.py @@ -16,8 +16,9 @@ GNU General Public License for more details. You should have received a copy of the GNU General Public License along with pmbootstrap. If not, see . """ -import pmb.helpers.run import pmb.aportgen.core +import pmb.helpers.git +import pmb.helpers.run def generate(args, pkgname): @@ -25,6 +26,7 @@ def generate(args, pkgname): arch = pkgname.split("-")[1] path_original = "main/binutils" upstream = (args.work + "/cache_git/aports_upstream/" + path_original) + pmb.helpers.git.clone(args, "aports_upstream") pmb.helpers.run.user(args, ["cp", "-r", upstream, args.work + "/aportgen"]) # Rewrite APKBUILD diff --git a/pmb/aportgen/device.py b/pmb/aportgen/device.py new file mode 100644 index 00000000..80d060e3 --- /dev/null +++ b/pmb/aportgen/device.py @@ -0,0 +1,196 @@ +""" +Copyright 2017 Oliver Smith + +This file is part of pmbootstrap. + +pmbootstrap is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +pmbootstrap is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with pmbootstrap. If not, see . +""" +import logging +import pmb.helpers.run +import pmb.aportgen.core +import pmb.parse.apkindex + + +def ask_for_architecture(args): + architectures = pmb.config.build_device_architectures + while True: + ret = pmb.helpers.cli.ask(args, "Device architecture", architectures, + architectures[0]) + 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(args): + logging.info("Who produced the device (e.g. LG)?") + return pmb.helpers.cli.ask(args, "Manufacturer", None, None, False) + + +def ask_for_name(args): + logging.info("What is the official name (e.g. Google Nexus 5)?") + return pmb.helpers.cli.ask(args, "Name", None, None, False) + + +def ask_for_keyboard(args): + return pmb.helpers.cli.confirm(args, "Does the device have a hardware keyboard?") + + +def ask_for_external_storage(args): + return pmb.helpers.cli.confirm(args, "Does the device have a sdcard or other" + " external storage medium?") + + +def ask_for_flash_method(args): + flash_methods = ["fastboot", "heimdall", "0xffff"] + while True: + logging.info("Which flash method does the device support?") + method = pmb.helpers.cli.ask(args, "Flash method", flash_methods, + flash_methods[0]) + + if method in 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("") + heimdall_type = pmb.helpers.cli.ask(args, "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 generate_deviceinfo(args, pkgname, name, manufacturer, arch, has_keyboard, + has_external_storage, flash_method): + content = """\ + # Reference: + # Please use double quotes only. You can source this file in shell scripts. + + deviceinfo_format_version="0" + deviceinfo_name=\"""" + name + """\" + deviceinfo_manufacturer=\"""" + manufacturer + """\" + deviceinfo_date="" + deviceinfo_dtb="" + deviceinfo_modules_initfs="" + deviceinfo_external_disk_install="false" + deviceinfo_arch=\"""" + arch + """\" + + # Device related + deviceinfo_keyboard=\"""" + ("true" if has_keyboard else "false") + """\" + deviceinfo_external_disk=\"""" + ("true" if has_external_storage else "false") + """\" + deviceinfo_screen_width="800" + deviceinfo_screen_height="600" + deviceinfo_dev_touchscreen="" + deviceinfo_dev_keyboard="" + + # Bootloader related + deviceinfo_flash_methods=\"""" + flash_method + """\" + """ + + content_fastboot = """\ + deviceinfo_kernel_cmdline="" + deviceinfo_generate_bootimg="true" + deviceinfo_bootimg_qcdt="false" + deviceinfo_flash_offset_base="" + deviceinfo_flash_offset_kernel="" + deviceinfo_flash_offset_ramdisk="" + deviceinfo_flash_offset_second="" + deviceinfo_flash_offset_tags="" + deviceinfo_flash_pagesize="2048" + """ + + content_heimdall_bootimg = """\ + deviceinfo_flash_heimdall_partition_kernel="" + deviceinfo_flash_heimdall_partition_system="" + """ + + content_heimdall_isorec = """\ + deviceinfo_flash_heimdall_partition_kernel="" + deviceinfo_flash_heimdall_partition_initfs="" + deviceinfo_flash_heimdall_partition_system="" + """ + + content_0xffff = """\ + deviceinfo_generate_legacy_uboot_initfs="true" + """ + + if flash_method == "fastboot": + content += content_fastboot + elif flash_method == "heimdall-bootimg": + content += content_fastboot + content += content_heimdall_bootimg + elif flash_method == "heimdall-isorec": + content += content_heimdall_isorec + elif flash_method == "0xffff": + content += content_0xffff + + # Write to file + pmb.helpers.run.user(args, ["mkdir", "-p", args.work + "/aportgen"]) + with open(args.work + "/aportgen/deviceinfo", "w", encoding="utf-8") as handle: + for line in content.split("\n"): + handle.write(line.lstrip() + "\n") + + +def generate_apkbuild(args, pkgname, name, arch, flash_method): + depends = "linux-" + "-".join(pkgname.split("-")[1:]) + if flash_method in ["fastboot", "heimdall-bootimg"]: + depends += " mkbootimg" + if flash_method == "0xffff": + depends += " uboot-tools" + content = """\ + pkgname=\"""" + pkgname + """\" + pkgdesc=\"""" + name + """\" + pkgver=0.1 + pkgrel=0 + url="https://postmarketos.org" + license="MIT" + arch="noarch" + options="!check" + depends=\"""" + depends + """\" + source="deviceinfo" + + package() { + install -Dm644 "$srcdir"/deviceinfo \\ + "$pkgdir"/etc/deviceinfo + } + + sha512sums="(run 'pmbootstrap checksum """ + pkgname + """' to fill)" + """ + + # Write the file + pmb.helpers.run.user(args, ["mkdir", "-p", args.work + "/aportgen"]) + with open(args.work + "/aportgen/APKBUILD", "w", encoding="utf-8") as handle: + for line in content.split("\n"): + handle.write(line[8:].replace(" " * 4, "\t") + "\n") + + +def generate(args, pkgname): + arch = ask_for_architecture(args) + manufacturer = ask_for_manufacturer(args) + name = ask_for_name(args) + has_keyboard = ask_for_keyboard(args) + has_external_storage = ask_for_external_storage(args) + flash_method = ask_for_flash_method(args) + + generate_deviceinfo(args, pkgname, name, manufacturer, arch, has_keyboard, + has_external_storage, flash_method) + generate_apkbuild(args, pkgname, name, arch, flash_method) diff --git a/pmb/aportgen/gcc.py b/pmb/aportgen/gcc.py index 18abc88e..a1b0bb8b 100644 --- a/pmb/aportgen/gcc.py +++ b/pmb/aportgen/gcc.py @@ -16,8 +16,9 @@ GNU General Public License for more details. You should have received a copy of the GNU General Public License along with pmbootstrap. If not, see . """ -import pmb.helpers.run import pmb.aportgen.core +import pmb.helpers.git +import pmb.helpers.run def generate(args, pkgname): @@ -25,6 +26,7 @@ def generate(args, pkgname): arch = pkgname.split("-")[1] path_original = "main/gcc" upstream = (args.work + "/cache_git/aports_upstream/" + path_original) + pmb.helpers.git.clone(args, "aports_upstream") pmb.helpers.run.user(args, ["cp", "-r", upstream, args.work + "/aportgen"]) # Rewrite APKBUILD diff --git a/pmb/aportgen/linux.py b/pmb/aportgen/linux.py new file mode 100644 index 00000000..c20e66a6 --- /dev/null +++ b/pmb/aportgen/linux.py @@ -0,0 +1,129 @@ +""" +Copyright 2017 Oliver Smith + +This file is part of pmbootstrap. + +pmbootstrap is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +pmbootstrap is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with pmbootstrap. If not, see . +""" +import pmb.helpers.run +import pmb.aportgen.core +import pmb.parse.apkindex +import pmb.parse.arch + + +def generate_apkbuild(args, pkgname, name, arch): + device = "-".join(pkgname.split("-")[1:]) + carch = pmb.parse.arch.alpine_to_kernel(arch) + content = """\ + # Kernel config based on: arch/""" + carch + """/configs/(CHANGEME!) + + pkgname=\"""" + pkgname + """\" + pkgver=3.x.x + pkgrel=0 + pkgdesc=\"""" + name + """ kernel fork\" + arch=\"""" + arch + """\" + _carch=\"""" + carch + """\" + _flavor=\"""" + device + """\" + url="https://kernel.org" + license="GPL2" + options="!strip !check !tracedeps" + makedepends="perl sed installkernel bash gmp-dev bc linux-headers elfutils-dev" + HOSTCC="${CC:-gcc}" + HOSTCC="${HOSTCC#${CROSS_COMPILE}}" + + # Source + _repository="(CHANGEME!)" + _commit="ffffffffffffffffffffffffffffffffffffffff" + _config="config-${_flavor}.${arch}" + source=" + $pkgname-$_commit.tar.gz::https://github.com/LineageOS/${_repository}/archive/${_commit}.tar.gz + $_config + compiler-gcc6.h + 01_msm-fix-perf_trace_counters.patch + 02_gpu-msm-fix-gcc5-compile.patch + " + builddir="$srcdir/${_repository}-${_commit}" + + prepare() { + default_prepare + + # gcc6 support + cp -v "$srcdir/compiler-gcc6.h" "$builddir/include/linux/" + + # Remove -Werror from all makefiles + find . -type f -name Makefile -print0 | \\ + xargs -0 sed -i 's/-Werror-/-W/g' + find . -type f -name Makefile -print0 | \\ + xargs -0 sed -i 's/-Werror//g' + + # Prepare kernel config ('yes ""' for kernels lacking olddefconfig) + cp "$srcdir"/$_config "$builddir"/.config + yes "" | make ARCH="$_carch" HOSTCC="$HOSTCC" oldconfig + } + + menuconfig() { + cd "$builddir" + make ARCH="$_carch" menuconfig + cp .config "$startdir"/$_config + } + + build() { + unset LDFLAGS + make ARCH="$_carch" CC="${CC:-gcc}" \\ + KBUILD_BUILD_VERSION="$((pkgrel + 1 ))-postmarketOS" + } + + package() { + # kernel.release + install -D "$builddir/include/config/kernel.release" \\ + "$pkgdir/usr/share/kernel/$_flavor/kernel.release" + + # zImage (find the right one) + cd "$builddir/arch/$_carch/boot" + _target="$pkgdir/boot/vmlinuz-$_flavor" + for _zimg in zImage-dtb Image.gz-dtb *zImage Image; do + [ -e "$_zimg" ] || continue + msg "zImage found: $_zimg" + install -Dm644 "$_zimg" "$_target" + break + done + if ! [ -e "$_target" ]; then + error "Could not find zImage in $PWD!" + return 1 + fi + } + + sha512sums="(run 'pmbootstrap checksum """ + pkgname + """' to fill)" + """ + + # Write the file + with open(args.work + "/aportgen/APKBUILD", "w", encoding="utf-8") as handle: + for line in content.split("\n"): + handle.write(line[8:].replace(" " * 4, "\t") + "\n") + + +def generate(args, pkgname): + device = "-".join(pkgname.split("-")[1:]) + deviceinfo = pmb.parse.deviceinfo(args, device) + + # Copy gcc6 support header and the patches from lg-mako for now + # (automatically finding the right patches is planned in #688) + pmb.helpers.run.user(args, ["mkdir", "-p", args.work + "/aportgen"]) + for file in ["compiler-gcc6.h", "01_msm-fix-perf_trace_counters.patch", + "02_gpu-msm-fix-gcc5-compile.patch"]: + pmb.helpers.run.user(args, ["cp", args.aports + + "/device/linux-lg-mako/" + file, + args.work + "/aportgen/"]) + + generate_apkbuild(args, pkgname, deviceinfo["name"], deviceinfo["arch"]) diff --git a/pmb/config/__init__.py b/pmb/config/__init__.py index 6e79d6d1..fe216b8f 100644 --- a/pmb/config/__init__.py +++ b/pmb/config/__init__.py @@ -22,7 +22,6 @@ import os # # Exported functions # -from pmb.config.init import init from pmb.config.load import load from pmb.config.save import save @@ -344,3 +343,18 @@ git_repos = { "aports_upstream": "https://github.com/alpinelinux/aports", "apk-tools": "https://github.com/alpinelinux/apk-tools", } + + +# +# APORTGEN +# +aportgen = { + "cross": { + "prefixes": ["binutils", "busybox-static", "gcc", "musl"], + "confirm_overwrite": False, + }, + "device": { + "prefixes": ["device", "linux"], + "confirm_overwrite": True, + } +} diff --git a/pmb/config/init.py b/pmb/config/init.py index b0e5e254..ecdb6958 100644 --- a/pmb/config/init.py +++ b/pmb/config/init.py @@ -113,19 +113,35 @@ def ask_for_timezone(args): return "GMT" -def init(args): - cfg = pmb.config.load(args) - - # Device +def ask_for_device(args): devices = sorted(pmb.helpers.devices.list(args)) logging.info("Target device (either an existing one, or a new one for" " porting).") logging.info("Available (" + str(len(devices)) + "): " + ", ".join(devices)) - cfg["pmbootstrap"]["device"] = pmb.helpers.cli.ask(args, "Device", - None, args.device, False, "[a-z0-9]+-[a-z0-9]+") + while True: + device = pmb.helpers.cli.ask(args, "Device", None, args.device, False, + "[a-z0-9]+-[a-z0-9]+") + device_exists = os.path.exists(args.aports + "/device/device-" + + device + "/deviceinfo") + if not device_exists: + logging.info("You are about to do a new device port for '" + + device + "'.") + if not pmb.helpers.cli.confirm(args, default=True): + continue - device_exists = os.path.exists(args.aports + "/device/device-" + cfg["pmbootstrap"]["device"] + "/deviceinfo") + pmb.aportgen.generate(args, "device-" + device) + pmb.aportgen.generate(args, "linux-" + device) + break + + return (device, device_exists) + + +def frontend(args): + cfg = pmb.config.load(args) + + # Device + cfg["pmbootstrap"]["device"], device_exists = ask_for_device(args) # Device keymap if device_exists: diff --git a/pmb/helpers/frontend.py b/pmb/helpers/frontend.py index 3eac89f2..b090f25c 100644 --- a/pmb/helpers/frontend.py +++ b/pmb/helpers/frontend.py @@ -80,6 +80,7 @@ def _parse_suffix(args): def aportgen(args): for package in args.packages: + logging.info("Generate aport: " + package) pmb.aportgen.generate(args, package) diff --git a/test/test_aportgen_device_wizard.py b/test/test_aportgen_device_wizard.py new file mode 100644 index 00000000..bb50d86a --- /dev/null +++ b/test/test_aportgen_device_wizard.py @@ -0,0 +1,151 @@ +""" +Copyright 2017 Oliver Smith + +This file is part of pmbootstrap. + +pmbootstrap is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +pmbootstrap is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with pmbootstrap. If not, see . +""" +import logging +import os +import pytest +import sys + +# Import from parent directory +sys.path.append(os.path.realpath( + os.path.join(os.path.dirname(__file__) + "/.."))) +import pmb.aportgen +import pmb.config +import pmb.helpers.logging +import pmb.parse + + +@pytest.fixture +def args(tmpdir, request): + sys.argv = ["pmbootstrap.py", "chroot"] + args = pmb.parse.arguments() + args.log = args.work + "/log_testsuite.txt" + pmb.helpers.logging.init(args) + request.addfinalizer(args.logfd.close) + + # Fake aports folder): + tmpdir = str(tmpdir) + setattr(args, "_aports_real", args.aports) + args.aports = tmpdir + pmb.helpers.run.user(args, ["mkdir", "-p", tmpdir + "/device"]) + + # Copy the linux-lg-mako aport (we currently copy patches from there) + path_mako = args._aports_real + "/device/linux-lg-mako" + pmb.helpers.run.user(args, ["cp", "-r", path_mako, tmpdir + "/device"]) + return args + + +def generate(args, monkeypatch, answers): + """ + Generate the device-new-device and linux-new-device aports (with a patched pmb.helpers.cli()). + + :returns: (deviceinfo, apkbuild, apkbuild_linux) - the parsed dictionaries + of the created files, as returned by pmb.parse.apkbuild() and + pmb.parse.deviceinfo(). + """ + # Patched function + def fake_ask(args, question="Continue?", choices=["y", "n"], default="n", + lowercase_answer=True, validation_regex=None): + for substr, answer in answers.items(): + if substr in question: + logging.info(question + ": " + answer) + # raise RuntimeError("test>" + answer) + return answer + raise RuntimeError("This testcase didn't expect the question '" + + question + "', please add it to the mapping.") + + # Generate the aports + monkeypatch.setattr(pmb.helpers.cli, "ask", fake_ask) + pmb.aportgen.generate(args, "device-testsuite-testdevice") + pmb.aportgen.generate(args, "linux-testsuite-testdevice") + monkeypatch.undo() + + # Parse the deviceinfo and apkbuilds + args.cache["apkbuild"] = {} + apkbuild_path = (args.aports + "/device/device-testsuite-testdevice/" + "APKBUILD") + apkbuild_path_linux = (args.aports + "/device/" + "linux-testsuite-testdevice/APKBUILD") + apkbuild = pmb.parse.apkbuild(args, apkbuild_path) + apkbuild_linux = pmb.parse.apkbuild(args, apkbuild_path_linux) + deviceinfo = pmb.parse.deviceinfo(args, "testsuite-testdevice") + return (deviceinfo, apkbuild, apkbuild_linux) + + +def test_aportgen_device_wizard(args, monkeypatch): + """ + Generate a device-testsuite-testdevice and linux-testsuite-testdevice + package multiple times and check if the output is correct. Also build the + device package once. + """ + # Answers to interactive questions + answers = { + "Device architecture": "armhf", + "external storage": "y", + "hardware keyboard": "n", + "Flash method": "heimdall", + "Manufacturer": "Testsuite", + "Name": "Testsuite Testdevice", + "Type": "isorec", + } + + # First run + deviceinfo, apkbuild, apkbuild_linux = generate(args, monkeypatch, answers) + assert apkbuild["pkgname"] == "device-testsuite-testdevice" + assert apkbuild["pkgdesc"] == "Testsuite Testdevice" + assert apkbuild["depends"] == ["linux-testsuite-testdevice"] + + assert apkbuild_linux["pkgname"] == "linux-testsuite-testdevice" + assert apkbuild_linux["pkgdesc"] == "Testsuite Testdevice kernel fork" + assert apkbuild_linux["arch"] == ["armhf"] + assert apkbuild_linux["_flavor"] == "testsuite-testdevice" + + assert deviceinfo["name"] == "Testsuite Testdevice" + assert deviceinfo["manufacturer"] == answers["Manufacturer"] + assert deviceinfo["arch"] == "armhf" + assert deviceinfo["keyboard"] == "false" + assert deviceinfo["external_disk"] == "true" + assert deviceinfo["flash_methods"] == "heimdall-isorec" + assert deviceinfo["generate_bootimg"] == "" + assert deviceinfo["generate_legacy_uboot_initfs"] == "" + + # Build the device package + pkgname = "device-testsuite-testdevice" + pmb.build.checksum(args, pkgname) + pmb.build.package(args, pkgname, "x86_64", force=True) + + # Abort on overwrite confirmation + answers["overwrite"] = "n" + with pytest.raises(RuntimeError) as e: + deviceinfo, apkbuild, apkbuild_linux = generate(args, monkeypatch, + answers) + assert "Aborted." in str(e.value) + + # fastboot (mkbootimg) + answers["overwrite"] = "y" + answers["Flash method"] = "fastboot" + deviceinfo, apkbuild, apkbuild_linux = generate(args, monkeypatch, answers) + assert apkbuild["depends"] == ["linux-testsuite-testdevice", "mkbootimg"] + assert deviceinfo["flash_methods"] == answers["Flash method"] + assert deviceinfo["generate_bootimg"] == "true" + + # 0xffff (legacy uboot initfs) + answers["Flash method"] = "0xffff" + deviceinfo, apkbuild, apkbuild_linux = generate(args, monkeypatch, answers) + assert apkbuild["depends"] == ["linux-testsuite-testdevice", "uboot-tools"] + assert deviceinfo["generate_legacy_uboot_initfs"] == "true" diff --git a/test/test_questions.py b/test/test_questions.py new file mode 100644 index 00000000..a7c76485 --- /dev/null +++ b/test/test_questions.py @@ -0,0 +1,133 @@ +""" +Copyright 2017 Oliver Smith + +This file is part of pmbootstrap. + +pmbootstrap is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +pmbootstrap is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with pmbootstrap. If not, see . +""" +import logging +import os +import pytest +import sys + +# Import from parent directory +sys.path.append(os.path.realpath( + os.path.join(os.path.dirname(__file__) + "/.."))) +import pmb.aportgen.device +import pmb.config +import pmb.config.init +import pmb.helpers.logging + + +@pytest.fixture +def args(tmpdir, request): + import pmb.parse + sys.argv = ["pmbootstrap.py", "init"] + args = pmb.parse.arguments() + args.log = args.work + "/log_testsuite.txt" + pmb.helpers.logging.init(args) + request.addfinalizer(args.logfd.close) + return args + + +def test_questions(args, monkeypatch, tmpdir): + # + # PREPARATION + # + + # Use prepared answers + def fake_ask(args, question="Continue?", choices=["y", "n"], default="n", + lowercase_answer=True, validation_regex=None): + answer = answers.pop(0) + logging.info("pmb.helpers.cli.ask: fake answer: " + answer) + return answer + monkeypatch.setattr(pmb.helpers.cli, "ask", fake_ask) + + # Do not generate aports + def fake_generate(args, pkgname): + return + monkeypatch.setattr(pmb.aportgen, "generate", fake_generate) + + # Self-test + answers = ["first", "second"] + assert pmb.helpers.cli.ask(args) == "first" + assert pmb.helpers.cli.ask(args) == "second" + assert len(answers) == 0 + + # + # SIMPLE QUESTIONS + # + + # Booleans + functions = [pmb.aportgen.device.ask_for_keyboard, + pmb.aportgen.device.ask_for_external_storage] + for func in functions: + answers = ["y", "n"] + assert func(args) is True + assert func(args) is False + + # Strings + functions = [pmb.aportgen.device.ask_for_manufacturer, + pmb.aportgen.device.ask_for_name] + for func in functions: + answers = ["Simple string answer"] + assert func(args) == "Simple string answer" + + # + # QUESTIONS WITH ANSWER VERIFICATION + # + + # Architecture + answers = ["invalid_arch", "aarch64"] + assert pmb.aportgen.device.ask_for_architecture(args) == "aarch64" + + # Device + func = pmb.config.init.ask_for_device + answers = ["lg-mako"] + assert func(args) == ("lg-mako", True) + + answers = ["whoops-typo", "n", "lg-mako"] + assert func(args) == ("lg-mako", True) + + answers = ["new-device", "y"] + assert func(args) == ("new-device", False) + + # Flash methods + func = pmb.aportgen.device.ask_for_flash_method + answers = ["invalid_flash_method", "fastboot"] + assert func(args) == "fastboot" + + answers = ["0xffff"] + assert func(args) == "0xffff" + + answers = ["heimdall", "invalid_type", "isorec"] + assert func(args) == "heimdall-isorec" + + answers = ["heimdall", "bootimg"] + assert func(args) == "heimdall-bootimg" + + # Keymaps + func = pmb.config.init.ask_for_keymaps + answers = ["invalid_keymap", "us/rx51_us"] + assert func(args, "nokia-rx51") == "us/rx51_us" + assert func(args, "lg-mako") == "" + + # UI + answers = ["invalid_UI", "weston"] + assert pmb.config.init.ask_for_ui(args) == "weston" + + # Work path + tmpdir = str(tmpdir) + answers = ["/dev/null", tmpdir] + assert pmb.config.init.ask_for_work_path(args) == tmpdir