5-Minute Read

Cross Compile to PinePhone Part One

Click Here For Part two

Click Here for Part three

PinePhone has been out for over one year now. In the last year, KDE plasma mobile saw rapid progress. We’ve developed almost all utility apps you’d have on iOS and Android. And actions like flashlight, screen capture also get added to top panel. What’s more, the launch screen and app drawer only get better.

When I started develop apps for PinePhone, I often find myself spent majority of time on PC. Only occassionaly I’d download the Android artifacts from CI and test it on Android(I don’t have access to PinePhone then). Pretty soon pine64 sent me two PinePhones(thank you, pine64!). But even then, I didn’t spent many time on them. Because I’m trying to first get my app running, what it will look on phone is not on the top of my list.

With the priority shifting from new features to stability and usability, bug fixes and UI/UX improvements are the most important things now. While I certainly can run my apps on PC, it doesn’t help with finding UI/UX bugs. Manjaro Plasma Mobile develop image updates everyday, but if you’re debugging an app, there is no other way than compiling your own.

The problem with PinePhone is that we have either XCode or Android Studio. An IDE can build and install for you, on the cost of being platform specific. Well, we don’t have these fancy things. The simplest solution is to compile on PinePhone, it’s Linux after all. However I don’t think PinePhone’s tiny 4 core CPU is suitable for this. We do have projects with over 10k lines of code, apps like Elisa can take ages to build on PinePhone. IDEs like XCode and Android studio use technic called cross compiling. Cross compiling enable you to build executables for another platform. My final goal is to have our own cross compiling toolchain, but that’s not in today’s scope. Today we’ll using emulator, it’s easier to setup on your own.

Chroot into image

We’ll using the Manjaro Plasma Mobile dev image, it already has the majority of our dependencies. The first step of course is to download the image. You can grab the newest image from here: https://github.com/manjaro-pinephone/plasma-mobile-dev/releases/ . After extracting the image with xz -d Manjaro-ARM-plasma-mobile-pinephone-20210919.img, we need to mount it. You can’t mount img file as normal flash driver. Image files are image of a whole disk. It contains partitions, bootloader and partition table. We only want the system partition. To find where the system partition starts, we’ll using fdisk.

$ fdisk -l Manjaro-ARM-plasma-mobile-pinephone-20210919.img
Disk Manjaro-ARM-plasma-mobile-pinephone-20210919.img: 5.87 GiB, 6307184640 bytes, 12318720 sectors
Units: sectors of 1 * 512 = 512 bytes
Sector size (logical/physical): 512 bytes / 512 bytes
I/O size (minimum/optimal): 512 bytes / 512 bytes
Disklabel type: dos
Disk identifier: 0x132613bb

Device                                            Boot  Start      End  Sectors   Size Id Type
Manjaro-ARM-plasma-mobile-pinephone-20210919.img1       62500   500000   437501 213.6M  c W95 FAT32 (LBA)
Manjaro-ARM-plasma-mobile-pinephone-20210919.img2      500001 12318719 11818719   5.6G 83 Linux

Here we find that it contains two partitions, the first partition is obviously UEFI boot partition. The second partition is the system root. Now we know the block-size is 512 bytes and the second partition starts at block 500001, we mount it with offset 512 * 500001 = 256000512:

$ cd
$ mkdir pp-mnt
$ sudo mount -o loop,offset=256000512 Manjaro-ARM-plasma-mobile-pinephone-20210919.img pp-mnt

Now you maybe want to chroot to pp-mnt, but pp-mnt is just part of image, it doesn’t have enough free space to install tools we need. So we’ll copy pp-mnt to another place.

$ cp -r pp-mnt pinephone-manjaro-rootfs
$ sudo umount pp-mnt

You can use arch-chroot to chroot into pinephone-manjaro-rootfs, but doing so pacman will complain something about “can’t find mount point” and refuse to install packages. To satisfy pacman, we have to bind mount it first:

$ sudo mount --bind pinephone-manjaro-rootfs pinephone-manjaro-rootfs

Before we chroot, install emulator first. Which is qemu-user-static-bin from aur. This package allows us to run binary for other architecture transparently, the very package is also used by docker. We’ll also need to restart systemd-binfmt service:

$ sudo systemctl restart systemd-binfmt.service

I won’t elaborate on emulator here, those interested can consult wikipedia

Now let’s chroot into our root file system! Today we’ll build kalk for PinePhone.

$ sudo arch-chroot pinephone-manjaro-rootfs

Once in, first init pacman keyring. Then install build tools that weren’t shipped by default.

# pacman-key --init
# pacman-key --populate
# pacman -Syu base-devel cmake ninja git

ArchLinux uses PKGBUILD to generate packages, this requires a separate directory. We create one and make it public, because later a normal user will be the one generate package.

# mkdir pinephone-kalk-build
# chmod 777 pinephone-kalk-build
# cd pinephone-kalk-build

Everything is ready, we only lack the PKGBUILD file and the source. Source part is easy, compress the source directory and place it rootfs build directory. Do it on your host system, of course. Let’s assume it’s named kalk.tar.xz. For the PKGBUILD, I’ll just hacking the one from aur

# git clone https://aur.archlinux.org/kalk.git
# mv kalk/PKGBUILD .
# vim PKGBUILD

The origin file looks like this:

# Maintainer: Gustavo Castro < gustawho [ at ] gmail [ dot ] com >

pkgname=kalk
pkgver=21.08
pkgrel=1
pkgdesc="Kalk is a powerful cross-platfrom calculator application built with the Kirigami framework"
arch=(x86_64 i686 arm armv6h armv7h aarch64)
url="https://invent.kde.org/plasma-mobile/kalk"
license=('GPL3')
depends=('ki18n' 'kconfig' 'kcoreaddons' 'knotifications' 'kirigami2' 'kunitconversion'
         'kdbusaddons' 'plasma-framework' 'gmp' 'mpfr' 'qt5-feedback')
makedepends=('qt5-tools' 'qt5-svg' 'extra-cmake-modules')
source=("http://download.kde.org/stable/plasma-mobile/${pkgver}/${pkgname}-${pkgver}.tar.xz")
sha256sums=('43f8d6a5cf80bcd66113df7349df22b972d9b7aaff56a7a992ca1ff22f54f977')

build() {
  cmake -DCMAKE_INSTALL_PREFIX=/usr -DCMAKE_BUILD_TYPE=Release -B build -S "${pkgname}-${pkgver}"
  cmake --build build --config Release
}

package() {
  DESTDIR="${pkgdir}" cmake --install build --config Release
}

The part we want to hack is package version, source, checksum and build commands. The result is:

pkgver=69.69

...

source=("kalk.tar.xz")
sha256sums=(SKIP)

build() {
  cmake -DCMAKE_INSTALL_PREFIX=/usr -DCMAKE_BUILD_TYPE=Release -B build -S "kalk"
  cmake --build build --config Release
}

One more step! ArchLinux doesn’t allow us to generate package as root, fearing “permantely corrupt system”. Though in our case it’s perfectly safe since it’s not our system :P

# useradd packager
# su packager
# makepkg

Now you should have kalk-69.69-1-aarch64.pkg.tar.zst inside pinephone-manjaro-rootfs/pinephone-kalk-build. Scp it to your pinephone and run sudo pacman -U kalk-69.69-1-aarch64.pkg.tar.zst to install kalk version 69.69!

Strictly speaking, this isn’t cross compiling. It’s just compiling with emulator, it’s faster than compiling on pinephone only because our PC cpu is way more powerful than pinephone’s. In reality, emulators are much slower than native instructions. For the actual cross compiling, waiting for Part Two!

Recent Posts

Categories

About

A young developer who loves Linux.