pmbootstrap init: Wizard for new port device- and linux-packages (#821)

* pmbootstrap init: Generate new port device- and linux-package
* adds `pmbootstrap aportgen device-*` and
  `pmbootstrap aportgen linux-*`
* ask for confirmation when selecting a non-existing device
* generate the packages directly from init
* refactor aportgen code
* fixed some easy things in the linux- APKBUILD (more to come in
  follow-up PRs!)

Testing:
* Test all questions to the user from pmb.config.init and pmb.aportgen.device
  (except for the timezone question, because we would need to monkeypatch the
  os.path.exists() function, which messes up pytest, so we'd need to refactor
  the timezone function to be more testsuite friendly first)
* Run the device wizard in a testcase a few times and check the output, that
  pmbootstrap.aportgen.device and pmbootstrap.aportgen.linux create by parsing
  the resulting APKBUILDs and deviceinfo and checking its contents.
* Build the generated device package once in the same testcase

Thanks a lot to @drebrez for all the help with this one:
<https://github.com/postmarketOS/pmbootstrap/pull/821>

See also the updated porting guide:
<https://wiki.postmarketos.org/wiki/Porting_to_a_new_device>
This commit is contained in:
Oliver Smith 2017-10-30 19:56:38 +00:00 committed by GitHub
parent 883bda01db
commit 6627599cf0
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
15 changed files with 695 additions and 36 deletions

View File

@ -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

View File

@ -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"

View File

@ -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"

View File

@ -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

View File

@ -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.")

View File

@ -19,33 +19,47 @@ along with pmbootstrap. If not, see <http://www.gnu.org/licenses/>.
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(

View File

@ -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 <http://www.gnu.org/licenses/>.
"""
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

196
pmb/aportgen/device.py Normal file
View File

@ -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 <http://www.gnu.org/licenses/>.
"""
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("<https://wiki.postmarketos.org/wiki/Deviceinfo_flash_methods#Isorec_or_bootimg.3F>")
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: <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_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)

View File

@ -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 <http://www.gnu.org/licenses/>.
"""
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

129
pmb/aportgen/linux.py Normal file
View File

@ -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 <http://www.gnu.org/licenses/>.
"""
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"])

View File

@ -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,
}
}

View File

@ -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:

View File

@ -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)

View File

@ -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 <http://www.gnu.org/licenses/>.
"""
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"

133
test/test_questions.py Normal file
View File

@ -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 <http://www.gnu.org/licenses/>.
"""
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