Jump to content

Pmbootstrap development guide

From postmarketOS Wiki
Revision as of 13:43, 23 April 2023 by Maintenance script (talk | contribs) (rollbackEdits.php mass rollback)

Installation code (pmb.install.*)

Whenever you change the installation process, also adjust the recovery zip installer (which performs a good part of the installation procedure directly on the Android device with a shell script).

What is this args variable used everywhere?

See the big comment at the top of pmb/helpers/args.py.

Executing shell commands

Use one of the following functions instead of Python's built in subprocess:

These functions call pmb.helpers.run_core.core() internally to write to the log file (that you can read with pmbootstrap log) and timeout when there is no output. A lot of function parameters are passed through to core() as well, see its docstring for a detailed description of what these parameters do.

Writing to files

Note See also: #1316 Properly escape commands in pmb.chroot.user()

The passed commands gets interpreted as single command, and not executed in a shell. This means something like the following will not work:

pmb.chroot.root(args, ["echo", "test", ">", "/tmp/test"])

Instead, if you need shell functionality in the call, you can wrap the call with sh -c. Here's one example with the parameters correctly escaped (you don't need to do that for the strings in this example, but if you pass variables then they should be escaped properly):

shell_cmd = "echo " + shutil.quote("test") + " > " + shutil.quote("/tmp/test")
pmb.chroot.root(args, ["sh", "-c", shell_cmd])

Another technique (especially if you need to run many commands in the shell at once) is writing all commands down into a temporary file and executing it with the shell, see for example pmb/chroot/initfs.py.

Writing files to the chroot

The users in the chroots (root and pmos) have different user IDs than the user of the host system. Therefore we can't just write a file to anywhere in the chroot. But we can write to /tmp and move the file afterwards:

with open("tmp/somefile", "w") as handle:
    handle.write("It works")
pmb.chroot.root(args, ["mv", "/tmp/somefile", "/etc/somefile"])

Testing the APKBUILD parser after changes

Besides the python tests, it's a good idea to let the APKBUILD parsing code run over all APKBUILDs that we have in pmaports.git, before and after making changes. This makes it easy to spot regressions.

$ pmbootstrap apkbuild_parse > /tmp/new
$ git checkout master
$ pmbootstrap apkbuild_parse > /tmp/old
$ colordiff /tmp/old /tmp/new | less -R

Debugging tab completion (argparse)

When tab completion breaks, commands line pmbootstrap build <TAB> will simply not return the expected list of packages anymore. Exceptions are not printed. To change this behavior and get the exceptions, adjust the eval "$(register-python-argcomplete pmbootstrap)" in your shell's rc file.

$ register-python-argcomplete3 pmbootstrap

_python_argcomplete() {
    local IFS=$'\013'
    local SUPPRESS_SPACE=0
    if compopt +o nospace 2> /dev/null; then
        SUPPRESS_SPACE=1
    fi
    COMPREPLY=( $(IFS="$IFS" \
                  COMP_LINE="$COMP_LINE" \
                  COMP_POINT="$COMP_POINT" \
                  COMP_TYPE="$COMP_TYPE" \
                  _ARGCOMPLETE_COMP_WORDBREAKS="$COMP_WORDBREAKS" \
                  _ARGCOMPLETE=1 \
                  _ARGCOMPLETE_SUPPRESS_SPACE=$SUPPRESS_SPACE \
                  "$1" 8>&1 9>&2 1>/dev/null 2>/dev/null) )
    if [[ $? != 0 ]]; then
        unset COMPREPLY
    elif [[ $SUPPRESS_SPACE == 1 ]] && [[ "$COMPREPLY" =~ [=/:]$ ]]; then
        compopt -o nospace
    fi
}
complete -o nospace -o default -F _python_argcomplete "pmbootstrap"

Copy the whole output of the command to your shell's rc file instead of the eval line, but remove 1>/dev/null 2>/dev/null. Then it will print exceptions to the shell.

See also

  • README.md of pmbootstrap.git for instructions on running the testsuite