forked from Mirror/pmbootstrap
* 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>
204 lines
7.7 KiB
Python
204 lines
7.7 KiB
Python
"""
|
|
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 glob
|
|
import os
|
|
|
|
import pmb.config
|
|
import pmb.helpers.cli
|
|
import pmb.helpers.devices
|
|
import pmb.helpers.run
|
|
import pmb.helpers.ui
|
|
import pmb.chroot.zap
|
|
import pmb.parse.deviceinfo
|
|
|
|
|
|
def ask_for_work_path(args):
|
|
"""
|
|
Ask for the work path, until we can create it (when it does not exist) and
|
|
write into it.
|
|
:returns: the work path
|
|
"""
|
|
logging.info("Location of the 'work' path. Multiple chroots"
|
|
" (native, device arch, device rootfs) will be created"
|
|
" in there.")
|
|
while True:
|
|
try:
|
|
ret = os.path.expanduser(pmb.helpers.cli.ask(
|
|
args, "Work path", None, args.work, False))
|
|
|
|
# Create the folder with a version file
|
|
if not os.path.exists(ret):
|
|
os.makedirs(ret, 0o700, True)
|
|
with open(ret + "/version", "w") as handle:
|
|
handle.write(pmb.config.work_version + "\n")
|
|
|
|
# Make sure, that we can write into it
|
|
os.makedirs(ret + "/cache_http", 0o700, True)
|
|
return ret
|
|
except OSError:
|
|
logging.fatal("ERROR: Could not create this folder, or write"
|
|
" inside it! Please try again.")
|
|
|
|
|
|
def ask_for_ui(args):
|
|
ui_list = pmb.helpers.ui.list(args)
|
|
logging.info("Available user interfaces (" +
|
|
str(len(ui_list) - 1) + "): ")
|
|
for ui, description in ui_list.items():
|
|
logging.info("* " + ui + ": " + description)
|
|
while True:
|
|
ret = pmb.helpers.cli.ask(args, "User interface", None, args.ui, True)
|
|
if ret in ui_list:
|
|
return ret
|
|
logging.fatal("ERROR: Invalid user interface specified, please type in"
|
|
" one from the list above.")
|
|
|
|
|
|
def ask_for_keymaps(args, device):
|
|
info = pmb.parse.deviceinfo(args, device=device)
|
|
if "keymaps" not in info or info["keymaps"].strip() == "":
|
|
return ""
|
|
options = info["keymaps"].split(' ')
|
|
logging.info("Available keymaps for device (" + str(len(options)) +
|
|
"): " + ", ".join(options))
|
|
if args.keymap is "":
|
|
args.keymap = options[0]
|
|
|
|
while True:
|
|
ret = pmb.helpers.cli.ask(args, "Keymap", None, args.keymap, True)
|
|
if ret in options:
|
|
return ret
|
|
logging.fatal("ERROR: Invalid keymap specified, please type in"
|
|
" one from the list above.")
|
|
|
|
|
|
def ask_for_timezone(args):
|
|
localtimes = ["/etc/zoneinfo/localtime", "/etc/localtime"]
|
|
zoneinfo_path = "/usr/share/zoneinfo/"
|
|
for localtime in localtimes:
|
|
if not os.path.exists(localtime):
|
|
continue
|
|
tz = ""
|
|
if os.path.exists(localtime):
|
|
tzpath = os.path.realpath(localtime)
|
|
tzpath = tzpath.rstrip()
|
|
if os.path.exists(tzpath):
|
|
try:
|
|
_, tz = tzpath.split(zoneinfo_path)
|
|
except:
|
|
pass
|
|
if tz:
|
|
logging.info("Your host timezone: " + tz)
|
|
if pmb.helpers.cli.confirm(args, "Use this timezone instead of GMT?",
|
|
default="y"):
|
|
return tz
|
|
logging.info("WARNING: Unable to determine timezone configuration on host, using GMT.")
|
|
return "GMT"
|
|
|
|
|
|
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))
|
|
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
|
|
|
|
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:
|
|
cfg["pmbootstrap"]["keymap"] = ask_for_keymaps(args, device=cfg["pmbootstrap"]["device"])
|
|
|
|
# Username
|
|
cfg["pmbootstrap"]["user"] = pmb.helpers.cli.ask(args, "Username", None,
|
|
args.user, False,
|
|
"[a-z_][a-z0-9_-]*")
|
|
# UI and work folder
|
|
cfg["pmbootstrap"]["ui"] = ask_for_ui(args)
|
|
cfg["pmbootstrap"]["work"] = ask_for_work_path(args)
|
|
|
|
# Parallel job count
|
|
logging.info("How many jobs should run parallel on this machine, when"
|
|
" compiling?")
|
|
cfg["pmbootstrap"]["jobs"] = pmb.helpers.cli.ask(args, "Jobs",
|
|
None, args.jobs, validation_regex="[1-9][0-9]*")
|
|
|
|
# Timestamp based rebuilds
|
|
logging.info("Rebuild packages, when the last modified timestamp changed,"
|
|
" even if the version did not change? This makes pmbootstrap"
|
|
" behave more like 'make'.")
|
|
answer = pmb.helpers.cli.confirm(args, "Timestamp based rebuilds",
|
|
default=args.timestamp_based_rebuild)
|
|
cfg["pmbootstrap"]["timestamp_based_rebuild"] = str(answer)
|
|
|
|
# Extra packages to be installed to rootfs
|
|
logging.info("Additional packages that will be installed to rootfs."
|
|
" Specify them in a comma separated list (e.g.: vim,file)"
|
|
" or \"none\"")
|
|
cfg["pmbootstrap"]["extra_packages"] = pmb.helpers.cli.ask(args, "Extra packages",
|
|
None, args.extra_packages,
|
|
validation_regex="^(|[-.+\w\s]+(?:,[-.+\w\s]*)*)$")
|
|
|
|
# Configure timezone info
|
|
cfg["pmbootstrap"]["timezone"] = ask_for_timezone(args)
|
|
|
|
# Do not save aports location to config file
|
|
del cfg["pmbootstrap"]["aports"]
|
|
|
|
# Save config
|
|
pmb.config.save(args, cfg)
|
|
|
|
# Zap existing chroots
|
|
setattr(args, "work", cfg["pmbootstrap"]["work"])
|
|
if (device_exists and
|
|
len(glob.glob(args.work + "/chroot_*")) and
|
|
pmb.helpers.cli.confirm(args, "Zap existing chroots to apply configuration?", default=True)):
|
|
setattr(args, "deviceinfo", pmb.parse.deviceinfo(args, device=cfg["pmbootstrap"]["device"]))
|
|
# Do not zap any existing packages or cache_http directories
|
|
pmb.chroot.zap(args, confirm=False)
|
|
|
|
logging.info(
|
|
"WARNING: The applications in the chroots do not get updated automatically.")
|
|
logging.info("Run 'pmbootstrap zap' to delete all chroots once a day before"
|
|
" working with pmbootstrap!")
|
|
logging.info("It only takes a few seconds, and all packages are cached.")
|
|
|
|
logging.info("Done!")
|