ການປ່ຽນແປງ ແລະ ການທົດສອບພາຍໃນເຄື່ອງ (Locally)

On this page you'll find information on how to build Ubuntu Touch system software for your device. Most of the software preinstalled on your Ubuntu Touch device is shipped in the device image in the form of a Debian package. This format is used by several Linux distributions, such as Debian, Ubuntu, and Linux Mint. Plenty of documentation on deb packages is available, so we won't be covering it here. Besides, in most cases you'll find yourself in need of modifying existing software rather than developing new packages from scratch. For this reason, this guide is mostly about recompiling an existing Ubuntu Touch package.

ໂດຍພື້ນຖານແລ້ວ ມີ 3 ວິທີໃນການພັດທະນາຊອບແວລະບົບ Ubuntu Touch ພາຍໃນເຄື່ອງ:

ຂໍ້ດີ ແລະ ຂໍ້ເສຍຂອງ sbuild ແລະ crossbuilder

sbuild uses a more minimal chroot-based build environment, while crossbuilder uses an LXD container with more pre-installed packages. When building with crossbuilder pre-installed build dependencies might be missed which would be caught by sbuild. crossbuilder can be quicker for subsequent builds since the LXD container persists. sbuild installs all build dependencies on each run. sbuild automatically creates a log file and runs lintian on the built packages in order to detect any problems. The use of LXD by crossbuilder also allows for easier inspection, debugging and manual modification of the build environment. crossbuilder can also automatically deploy build packages on a connected device via ADB.

We'll examine the use of crossbuilder and builds on the device using address-book-app (the Contacts application) as an example.

ພວກເຮົາແນະນຳໃຫ້ພັດທະນາແພັກເກັດໂດຍໃຊ້ອຸປະກອນທີ່ຕິດຕັ້ງ Ubuntu Touch ຈາກ devel channel.

ການກະກຽມແພັກເກັດສຳລັບການສ້າງ (Build)

sbuild or crossbuilder need to be invoked from a debianized package source tree (i.e. the package sources with a debian subdirectory), UBports packages mostly consists of git repositories containing native or non-native packages. Native packages can be built directly and the following script can be used in order to prepare a non-native package for a build:

#!/bin/sh

PATH=/bin:/usr/bin

die() {
    if [ $# -gt 0 ]; then
        printf "%s\n" "$1" >&2
    fi
    exit 1
}

if [ ! -d "./debian" ]; then
    die "not in a debianized package directory"
fi

if [ -f "./debian/ubports.source_location" ]; then

    {
        read -r src_url && \
        read -r src_filename
    } < "./debian/ubports.source_location" || \
        die "failed to parse ubports.source_location"
    case ${src_url} in
    http://*|https://*|ftp://*)
        ;;
    *)
        die "invalid url: \"${src_url}\""
        ;;
    esac
    src_filename="$(basename "${src_filename}")"

    wget -O "../${src_filename}" "${src_url}" || \
        die "failed to download source archive"
fi

ການສ້າງແພັກເກັດໃນ chroot ໂດຍໃຊ້ sbuild

sbuild is a tool for building Debian packages from source in an isolated environment using a chroot created by schroot. It closely resembles the package build process on the UBports CI system by using an isolated build environment with a minimal set of pre-installed packages. This detects any missing build dependencies and sbuild will also detect problems with the resulting packages by running lintian.

ມີ 3 ວິທີໃນການສ້າງໂດຍໃຊ້ sbuild:

ເງື່ອນໄຂເບື້ອງຕົ້ນ

A host system running either Debian 11 (Bullseye) or later or Ubuntu 20.04 (Focal Fossa) or later is required. An easy and performant way to set up Debian or Ubuntu on other distributions is to run it in a container using LXD.

A LXD container requires the following configuration setting in order to allow debootstrap to use the mknod system call for creating pseudo devices such as /dev/null inside a chroot:

lxc config set <container> security.syscalls.intercept.mknod true

ສົມມຸດວ່າຜູ້ໃຊ້ມີສິດທິໃນການໃຊ້ຄຳສັ່ງ sudo.

sbuild uses schroot in order to manage chroots which in turn uses debootstrap for creating the chroot. The required packages are installed with:

sudo apt install sbuild schroot devscripts debhelper dh-migrations ccache

ທ່ານສາມາດຕິດຕັ້ງໃນ Virtual Machine ໄດ້, ແຕ່ຈະມີການໃຊ້ຊັບພະຍາກອນທີ່ເພີ່ມຂຶ້ນ (performance overhead).

ການຕັ້ງຄ່າ sbuild

An unprivileged user needs to be added to the sbuild group in order to gain the necessary privilege to build packages:

sudo sbuild-adduser <username>

The build user can configure sbuild by creating a file ~/.sbuildrc as follows:

cat >~/.sbuildrc <<'EOF'
# directory containing the build logs
$log_dir = "$HOME/logs";
1;
EOF

ສຳລັບການປັບແຕ່ງເພີ່ມເຕີມ ໃຫ້ເບິ່ງໄຟລ໌ຕົວຢ່າງທີ່ /usr/share/doc/sbuild/examples/example.sbuildrc.

Create the directory ~/logs if it does not exist, yet:

mkdir ~/logs

ແພັກເກັດ UBports

ການສ້າງບາງໂມດູນຂອງ Ubuntu Touch ຕ້ອງການແພັກເກັດຈາກຄັງເກັບ UBports ຕິດຕັ້ງໃນເຄື່ອງ Host.

In that case UBports package repository needs to be added on the host machine. First initialize the chroot_repo and chroot_distro variables as show in the Native build section and then add the repository by using:

wget 'http://repo.ubports.com/keyring.gpg' -O - | sudo tee "/usr/share/keyrings/ubports-keyring.gpg" >/dev/null
printf 'deb [signed-by=/usr/share/keyrings/ubports-keyring.gpg] %s %s main\n' "${chroot_repo}" "${chroot_distro}" | sudo tee "/etc/apt/sources.list.d/ubports.list" >/dev/null

Install the needed packages using:

sudo apt update
sudo apt install click-dev gobject-introspection

ການສ້າງແບບເນທີຟ (Native build)

In order to create a chroot based on Ubuntu 20.04 (Focal Fossa) with the amd64 architecture under the directory /srv/chroot/ubports-${chroot_distro}-amd64 (chroot_base can be changed if needed) the following variables can be defined for later use by the actual commands:

chroot_distro=focal
chroot_base=/srv/chroot/ubports-${chroot_distro}-amd64
chroot_repo=http://repo2.ubports.com/

For creating a chroot based on Ubuntu 16.04 (Xenial Xerus) with the amd64 architecture define the following variables instead:

chroot_distro=xenial
chroot_base=/srv/chroot/ubports-${chroot_distro}-amd64
chroot_repo=http://repo.ubports.com/

In both cases the chroot will be created by running the following command:

sudo sbuild-createchroot --components=main,restricted,universe --extra-repository="deb http://archive.ubuntu.com/ubuntu/ ${chroot_distro}-updates main restricted universe" --include=ccache "${chroot_distro}" "${chroot_base}" http://archive.ubuntu.com/ubuntu/

The UBports package repository needs to be added using:

wget 'http://repo.ubports.com/keyring.gpg' -O - | sudo tee "${chroot_base}/usr/share/keyrings/ubports-keyring.gpg" >/dev/null
printf 'deb [signed-by=/usr/share/keyrings/ubports-keyring.gpg] %s %s main\n' "${chroot_repo}" "${chroot_distro}" | sudo tee "${chroot_base}/etc/apt/sources.list.d/ubports.list" >/dev/null

Synchronizing package index files and subsequent package upgrades can be performed using:

sudo sbuild-update -u -d ${chroot_distro}

A build can be started from inside the debianized package source directory using:

sbuild -d ${chroot_distro}

ການສ້າງແບບຂ້າມສະຖາປັດຕະຍະກຳ (Cross compile build)

The only difference with the native build comes in the command for starting the build which is the following:

sbuild --host=arm64 --build=amd64 -d "${chroot_distro}"

ການສ້າງຂ້າມສະຖາປັດຕະຍະກຳດ້ວຍ Qemu

To simplify the chroot setup, we use mk-sbuild from the ubuntu-dev-tools package. The qemu-user-static package allows execution of non-native target executables just like native ones.

Install the packages with the following commands:

sudo apt install ubuntu-dev-tools qemu-user-static

In order to create a chroot based on Ubuntu 20.04 (Focal Fossa) with the arm64 architecture under the directory /var/lib/schroot/chroots/ubports-${chroot_distro}-arm64 (chroot_base can be changed if needed) the following variables can be defined for later use by the actual commands:

chroot_arch=arm64
chroot_distro=focal
chroot_base=/var/lib/schroot/chroots/ubports-${chroot_distro}-${chroot_arch}
chroot_repo=http://repo2.ubports.com/

For creating a chroot based on Ubuntu 16.04 (Xenial Xerus) with the arm64 architecture define the following variables instead:

chroot_arch=arm64
chroot_distro=focal
chroot_base=/var/lib/schroot/chroots/ubports-${chroot_distro}-${chroot_arch}
chroot_repo=http://repo.ubports.com/

In both cases the chroot will be created by running the following command:

mk-sbuild --arch=${chroot_arch} ${chroot_distro} --name ubports-${chroot_distro}

The UBports package repository needs to be added using:

wget 'http://repo.ubports.com/keyring.gpg' -O - | sudo tee "${chroot_base}/usr/share/keyrings/ubports-keyring.gpg" >/dev/null
printf 'deb [signed-by=/usr/share/keyrings/ubports-keyring.gpg] %s %s main\n' "${chroot_repo}" "${chroot_distro}" | sudo tee "${chroot_base}/etc/apt/sources.list.d/ubports.list" >/dev/null

Synchronizing package index files and subsequent package upgrades can be performed using:

sudo sbuild-update --arch=${chroot_arch} -u -d ubports-${chroot_distro}

A build can be started from inside the debianized package source directory using:

sbuild --build=${chroot_arch} --host=${chroot_arch} -d ubports-${chroot_distro}

ການສ້າງສຳເລັດ

ຖ້າການສ້າງສຳເລັດ, ໄຟລ໌ແພັກເກັດ (binary packages) ຈະຖືກວາງໄວ້ໃນ directory ຊັ້ນເທິງ. log ຂອງການສ້າງຈະຢູ່ໃນ ~/logs.

sudo sbuild-shell ${chroot_distro}

ການປັບແຕ່ງເພື່ອປະສິດທິພາບ (Optimizations)

ການເຮັດ Cache ສຳລັບການດາວໂຫລດແພັກເກັດ

In order to save bandwidth and time it is highly advisable to cache downloaded packages by using apt-cacher-ng. It can be installed with:

apt install apt-cacher-ng

Chroots then need to be configured so that apt inside the chroot uses apt-cacher-ng on the host as a proxy server:

printf 'Acquire::http { Proxy "http://localhost:3142"; }\n' | sudo tee "${chroot_base}/etc/apt/apt.conf.d/proxy" >/dev/null

ການເຮັດ Cache ສຳລັບຜົນການ Compile

ccache is a compiler cache which speeds up repeated compilation of the same source code by caching the resulting object files. The actual cache is stored on the host system and bind-mounted into sbuild chroots with a schroot hook:

ccache_dir=/var/cache/ccache-sbuild
sudo install --group=sbuild --mode=2775 -d "${ccache_dir}"
sudo env CCACHE_DIR="${ccache_dir}" ccache --max-size 4G
printf '%s %s none rw,bind 0 0\n' "${ccache_dir}" "${ccache_dir}" | sudo tee -a /etc/schroot/sbuild/fstab >/dev/null

In order to make use of ccache inside a sbuild chroot a wrapper script needs to be created:

cat >"${ccache_dir}/sbuild-ccache.sh" <<EOF
#!/bin/sh
export CCACHE_DIR=$ccache_dir
export CCACHE_UMASK=002
export CCACHE_COMPRESS=1
unset CCACHE_HARDLINK
export PATH=/usr/lib/ccache:\$PATH
exec "\$@"
EOF
chmod +x "${ccache_dir}/sbuild-ccache.sh"

In order to use this wrapper script the following line must be added to the configuration of a schroot chroot in /etc/schroot/chroot.d/:

command-prefix=/var/cache/ccache-sbuild/sbuild-ccache.sh

ຂໍ້ມູນເພີ່ມເຕີມ

Technical details are available from the sbuild(1) and sbuild-createchroot(8) manual pages and the Debian wiki.

ການສ້າງແພັກເກັດດ້ວຍ crossbuilder

Crossbuilder ແມ່ນສະຄຣິບທີ່ຊ່ວຍຈັດການສະພາບແວດລ້ອມການສ້າງແພັກເກັດແບບອັດຕະໂນມັດ.

Note

Crossbuilder requires a Linux distribution with lxd installed and the unprivileged commandset available. In other words, you must be able to run the lxc command. If you are running Ubuntu on your host, Crossbuilder will set up lxd for you.

Start by installing Crossbuilder on your host:

cd ~
git clone https://github.com/ubports/crossbuilder.git

Crossbuilder is a shell script, so you don't need to build it. Instead, you will need to add its directory to your PATH environment variable, so that you can execute it from any directory:

echo 'export PATH="$HOME/crossbuilder:$PATH"' >> ~/.bashrc
# and add it to your current session:
source ~/.bashrc

Now that Crossbuilder is installed, we can use it to set up LXD:

crossbuilder setup-lxd

ຖ້ານີ້ແມ່ນຄັ້ງທຳອິດທີ່ທ່ານໃຊ້ LXD, ທ່ານອາດຈະຕ້ອງ reboot ເຄື່ອງ Host ກ່ອນ.

After LXD has been set up, a build for UBports based on Ubuntu 20.04 (Focal Fossa) using the arm64 architecture can be started from inside the debianized package source directory using:

distro=20.04
arch=arm64
crossbuilder --lxd-image="ubuntu:${distro}" --architecture="${arch}" build

ທ່ານສາມາດປ່ຽນ distro ແລະ arch ໄດ້ຕາມຕ້ອງການ.

Crossbuilder ຈະສ້າງ LXD container, ດາວໂຫລດອິເມຈ ແລະ ຕິດຕັ້ງສິ່ງທີ່ຈຳເປັນທັງໝົດໃຫ້ໂດຍອັດຕະໂນມັດ.

In order to deploy the newly built package on a local device (see ການເຂົ້າເຖິງ Shell ຜ່ານ ADB to learn more about connecting your device), you can use:

crossbuilder --lxd-image="ubuntu:${distro}" --architecture="${arch}" --password="password-or-0000-if-not-set" deploy

Now, whenever you change the source code in your git repository, the same changes will be available inside the container. The next time you type the above crossbuilder command, only the changed files will be rebuilt.

If the build dependencies have changed the following command can be used to update the container accordingly (distro and arch should be set as above):

crossbuilder --lxd-image="ubuntu:${distro}" --architecture="${arch}" dependencies

While crossbuilder does not create log files for the build process, the script utility may be used for that purpose:

script -c "crossbuilder --lxd-image=\"ubuntu:${distro}\" --architecture=\"${arch}\" build" build.log

When a build container is no longer needed it maybe removed using:

crossbuilder --lxd-image="ubuntu:${distro}" --architecture="${arch}" delete

ການທົດສອບຫົວໜ່ວຍ (Unit tests)

By default crossbuilder does not run unit tests; that's both for speed reasons, and because the container created by crossbuilder is not meant to run native (target) executables: the development tools (qmake/cmake, make, gcc, etc.) are all run in the host architecture, with no emulation (again, for speed reasons). However, qemu emulation is available inside the container, so it should be possible to run unit tests. You can do that by getting a shell inside the container:

crossbuilder --lxd-image="ubuntu:${distro}" --architecture="${arch}" shell

ຈາກນັ້ນ ໃຫ້ຊອກຫາ ແລະ ຣັນ unit tests. ເນື່ອງຈາກການຈຳລອງ (emulation) ອາດບໍ່ສົມບູນແບບ, ບາງເທື່ອ tests ອາດຫຼົ້ມເຫຼວໄດ້.

ການສ້າງ (Build) ໃນຕົວອຸປະກອນໂດຍກົງ

ວິທີນີ້ໄວ ແລະ ງ່າຍທີ່ສຸດສຳລັບການແກ້ໄຂເລັກໆນ້ອຍໆ, ແຕ່ຕ້ອງມີພື້ນທີ່ຫວ່າງ ແລະ RAM ພຽງພໍໃນອຸປະກອນ.

Warning

This method is limited. Many devices do not have enough free image space to install the packages required to build components of Ubuntu Touch. Installing packages has a risk of damaging the software on your device, rendering it unusable. If this happens, you can reinstall Ubuntu Touch.

ໃນຕົວຢ່າງນີ້ ພວກເຮົາຈະສ້າງ ແລະ ຕິດຕັ້ງ address-book-app ໂດຍຣັນຄຳສັ່ງຜ່ານ remote shell ໃນອຸປະກອນ.

You can gain a shell on the device using ການເຂົ້າເຖິງ Shell ຜ່ານ ADB or ການເຂົ້າເຖິງ Shell ຜ່ານ SSH. Remount the root filesystem read-write to begin:

sudo mount / -o remount,rw

Next, install all the packages needed to rebuild the component you want to modify (the Contacts app, in this example):

sudo apt update
sudo apt build-dep address-book-app
sudo apt install fakeroot

Additionally, you probably want to install git in order to get your app's source code on the device and later push your changes back into the repository:

sudo apt install git

Once you're finished, you can retrieve the source for an app (in our example, the address book) and move into its directory:

git clone https://gitlab.com/ubports/core/address-book-app.git
cd address-book-app

Now, you are ready to build the package:

DEB_BUILD_OPTIONS="parallel=2 debug" dpkg-buildpackage -rfakeroot -b

The dpkg-buildpackage command will print out the names of generated packages. Install those packages with dpkg:

sudo dpkg -i ../<package>.deb [../<package2>.deb ...]

ແນວໃດກໍຕາມ, ໃຫ້ສັງເກດວ່າ ທ່ານອາດຈະບໍ່ຈຳເປັນຕ້ອງຕິດຕັ້ງທຸກແພັກເກັດ: ໂດຍທົ່ວໄປ, ທ່ານສາມາດຂ້າມແພັກເກັດທັງໝົດທີ່ມີຊື່ລົງທ້າຍດ້ວຍ -doc ຫຼື dev, ເນື່ອງຈາກພວກມັນບໍ່ມີໂຄດ (code) ທີ່ອຸປະກອນນຳໃຊ້.

ຂັ້ນຕອນຕໍ່ໄປ

ຕອນນີ້ທ່ານໄດ້ປ່ຽນແປງ ແລະ ທົດສອບພວກມັນພາຍໃນເຄື່ອງສຳເລັດແລ້ວ, ທ່ານພ້ອມທີ່ຈະອັບໂຫຼດພວກມັນໄປຍັງ GitHub ແລ້ວ. ໄປທີ່ໜ້າຖັດໄປເພື່ອຮຽນຮູ້ກ່ຽວກັບການນຳໃຊ້ UBports CI ເພື່ອສ້າງ ແລະ ຈັດຫາແພັກເກັດສຳລັບການພັດທະນາ (development packages)!