Warning, very long post ahead.
TL&DR: this is to run and debug locally the system settings on a daily driver phone (no need to turn the rootfs read/write)
A secondary goal could be to build proposed changes as a click for normal users
so they could test the new features.
It's better IMO than posting screenshots, actual testing on the device
is more involved but much more powerful.
LIMITATIONS
this is not a complete lomiri-system-settings build.
This app has a layered structure with plugins. Many plugins are integrated in the lomiri-system-settings repository,
some are not. I have not yet found an effective way to build the full lomiri-system-settings experience by
building the lomiri-system-settings repo with the other plugins found in other repos.
At the moment I don't have a pressing need for it since debugging standard plugins is quite enough for my available time,
but I'm interested in running (and debugging) the cellular settings so I intend to take a look at it.
I don't know how to do it to be candid, I'm having an idea of using git submodules for it but I have no
idea if it will turn out to be doable or if yes the most effective way.
The changes to the normal settings are in the root CMakeFile.txt file.
To be candid, I hate cmake and every second of fighting this beast has been a pain.
These changes are not pretty. They are not intended for inclusion into Ubuntu Touch.
When selecting the 'click' branch, it's not tested and intended to build the app
in the normal way of debugging it (that is, by making the rootfs read/write)
Their use is limited as well, it's only targetted at the 24.04-1.x branch because
there is no 26.04 possible use with click at the moment and there is currently a problem with
targetting 24.04-2 branch (IIRC UT devs have made a change in the repo to be compatible with
26.04 and there is no 24.04-2 branch in the UT lomiri-system-settings)
This is not limiting me too much because my daily driver is 24.04-2 and the generated
click runs under it.
STEP 1: BUILDING THE CLICK
(py311) gerard@j5005:/opt/ut/dev$ git clone https://gitlab.com/gpatel-fr/lomiri-system-settings
Clonage dans 'lomiri-system-settings'...
warning: redirection vers https://gitlab.com/gpatel-fr/lomiri-system-settings.git/
remote: Enumerating objects: 60914, done.
remote: Counting objects: 100% (2422/2422), done.
remote: Compressing objects: 100% (218/218), done.
remote: Total 60914 (delta 2219), reused 2387 (delta 2200), pack-reused 58492 (from 1)
Réception d'objets: 100% (60914/60914), 66.62 Mio | 1.72 Mio/s, fait.
Résolution des deltas: 100% (47536/47536), fait.
(py311) gerard@j5005:/opt/ut/dev$ cd lomiri-system-settings
(py311) gerard@j5005:/opt/ut/dev/lomiri-system-settings$ git checkout click
la branche 'click' est paramétrée pour suivre 'origin/click'.
Basculement sur la nouvelle branche 'click'
(py311) gerard@j5005:/opt/ut/dev/lomiri-system-settings$ clic build --debug --arch arm64
(..many lines of output skipped...)
Successfully built package in './lomiri-settings_1.0.0_arm64.click'.
Running review on /opt/ut/dev/lomiri-system-settings/build/aarch64-linux-gnu/app/lomiri-settings_1.0.0_arm64.click
Errors
------
- security:template_valid:lomiri-settings.apparmor
(NEEDS REVIEW) 'unconfined' not allowed
http://askubuntu.com/q/671403
Warnings
--------
- desktop:X-Lomiri-Gettext-Domain:lomiri-system-setting
'lomiri-system-settings' != 'lomiri-settings' (ok if app uses i18n.domain('lomiri-system-settings') or uses organizationName
/opt/ut/dev/lomiri-system-settings/build/aarch64-linux-gnu/app/lomiri-settings_1.0.0_arm64.click: FAIL
-> never mind the messages on the final testing, it's mostly about publishing the click on the store.
Obviously the goal of building lomiri-system-settings as a click is NOT to publish on the store, it's to run and test locally.
The only message that matters here is the 'Successfully built package' result.
(py311) gerard@j5005:/opt/ut/dev/lomiri-system-settings$ clic install --ssh fp5
(..skipped..)
Installing the app.
()
-> I don't use adb for current use, I connect to the phone using wifi and 'fp5' is the local name for the IP address of my phone.
It avoids me the drudgery of having a cable between my computer and the phone.
(py311) gerard@j5005:/opt/ut/dev/lomiri-system-settings$ ,transfer_fp5
(..skipped..)
sent 148.572 bytes received 162.092 bytes 88.761,14 bytes/sec
total size is 17.508.621 speedup is 56,36
-> ,transfer_fp5 is a script to avoid me the mental strain of having to remember this:
rsync -av -e ssh --exclude '.git' --exclude 'build' --exclude 'debian'
```--exclude 'tests' /opt/ut/dev/lomiri-system-settings/ fp5:/opt/ut/dev/lomiri-system-settings/
-> this particular step is only to debug the resulting clic.
this sends the necessary code source on the phone so that I can debug with gdb.
For all of this to work, I have created a pseudo work directory on the phone that has the same structure that the one on my computer.
phablet@ubuntu-phablet:~$ sudo mkdir -p /opt/ut/dev/lomiri-system-settings
phablet@ubuntu-phablet:~$ sudo chown -R phablet /opt/ut
phablet@ubuntu-phablet:~$ sudo chgrp -R phablet /opt/ut
-> an alternative way would be to connect from the phone to the computer to have a direct sharing of the code. I did not do this because I'm not confortable with the idea of connecting to my computer from the phone (security is as good on the phone IMO)
STEP2: RUNNING THE CLICK ON THE PHONE
the previous step will have created an app on the phone called 'lomiri-setting' installed under /opt/click.ubuntu.com/lomiri-settings
It will duplicate the icon for normal settings in the app drawer. The first one is the system one, the second is the test app.
The launcher will still start the normal system app.
To see what is the test app and the system app is easy, because the test app only includes the integrated plugins as explained at the beginning of this post. So there are less icons, and some icons using non integrated plugins will display but not work at all (notably, the update function)
It's also possible to start the test app from the command line, I have a script for that:
phablet@ubuntu-phablet:~$ cat st
source ~/stsrc
#strace -f -ff -o /tmp/aa ./lomiri-system-settings
./lomiri-system-settings
#aa-exec-click -p lomiri-settings_lomiri-system-setting_1.0.0 -- ./lomiri-system-settings %u
the 'stsrc' script contains the necessary abracadabras to emulate (mostly) what lomiri-app-launch is doing to setup click applications:
cd /opt/click.ubuntu.com/lomiri-settings/current
export QT_DEBUG_PLUGINS=1
export QML_IMPORT_TRACE=1
# -> QQmlImportDatabase
export XDG_DATA_DIRS=/opt/click.ubuntu.com/.click/users/@all/lomiri-settings:/usr/share/ubuntu-touch:/usr/local/share:/usr/share:/custom/usr/share/:/var/lib/snapd/desktop
export QML2_IMPORT_PATH=/opt/click.ubuntu.com/.click/users/@all/lomiri-settings/lib/aarch64-linux-gnu
export LD_LIBRARY_PATH=/opt/click.ubuntu.com/lomiri-settings/current/lib/aarch64-linux-gnu
with that, and the necessary permissions on the scripts, I can run
./st
from the root of the terminal (either the true UT terminal or a ssh session) and the fake lomiri-system-settings will appear on the phone.
WARNING, this is a test app but the effects are REAL, it's running on the phone just like the system app, this is NOT a simulated environment !
If you do an undesired change, you can always revert it using either the test app or the real system app (both can run in parallel)
(unless you reset the phone... this can't be cancelled)
STEP3: DEBUGGING THE CLICK
This involves first a debugger: gdb and a gdb python extension to display QT objects.
This should be amended to 'partly' display variables. Not all QT objects display. But enough display correctly for the result to be useful.
I downloaded an arm64 binary from https://github.com/guyush1/gdb-static/releases/tag/v17.2-static
Use the arm64 full build (gdb-static-full-aarch64.tar.gz.
This so-called 'full' build includes a python interpreter.
The python extension is based on the following repo:
https://github.com/Lekensteyn/qt5printers
and work from KDE developers.
phablet@ubuntu-phablet:~/.config/gdb$ cat gdbinit
python
import sys, os
print(f".gdbinit Python: current working directory is {os.getcwd()}")
print(f".gdbinit Python: adding custom pretty-printers directory to the GDB path: {os.getcwd() + '/.gdb/qt5printers'}")
sys.path.insert(0, "/home/phablet/.config/gdb/qt5printers")
print(sys.path)
print(sys.version)
from qt import register_qt_printers
register_qt_printers (None)
I downloaded kdevelop from https://github.com/KDE/kdevelop and placed under
~/.config/gdb/qt5printers
the copy of the plugins/gdb/printers/ subdirectory of kdevelop.
RESULT
here is a gdb debugging session example:
phablet@ubuntu-phablet:~$ source ./stsrc
phablet@ubuntu-phablet:/opt/click.ubuntu.com/lomiri-settings/current$ ~/gdb lomiri-system-settings
GNU gdb (GDB) 17.2
Copyright (C) 2025 Free Software Foundation, Inc.
License GPLv3+: GNU GPL version 3 or later <http://gnu.org/licenses/gpl.html>
This is free software: you are free to change and redistribute it.
There is NO WARRANTY, to the extent permitted by law.
Type "show copying" and "show warranty" for details.
This GDB was configured as "aarch64-linux-musl".
Type "show configuration" for configuration details.
For bug reporting instructions, please see:
<https://www.gnu.org/software/gdb/bugs/>.
Find the GDB manual and other documentation resources online at:
<http://www.gnu.org/software/gdb/documentation/>.
For help, type "help".
Type "apropos word" to search for commands related to "word"...
.gdbinit Python: current working directory is /opt/click.ubuntu.com/lomiri-settings/1.0.0
.gdbinit Python: adding custom pretty-printers directory to the GDB path: /opt/click.ubuntu.com/lomiri-settings/1.0.0/.gdb/qt5printers
['/home/phablet/.config/gdb/qt5printers', '/usr/share/gdb/python', '/usr/lib/python314.zip', '/usr/lib/python3.14', '/app/gdb/src/submodule_packages/cpython-static/build-aarch64/lib/python3.14/lib-dynload']
3.14.5 (3c47a7c-dirty:3c47a7c, Jun 10 2026, 10:00:44) [GCC 14.2.0]
Reading symbols from lomiri-system-settings...
(gdb) l
35 static QQmlDebuggingEnabler debuggingEnabler(false);
36
37 using namespace LomiriSystemSettings;
38
39 int main(int argc, char **argv)
40 {
41 QApplication app(argc, argv);
42 QByteArray mountPoint = qEnvironmentVariableIsSet("SNAP") ? qgetenv("SNAP") : "";
43 bool isSnap = !mountPoint.isEmpty();
44
(gdb) b /opt/ut/dev/lomiri-system-settings/plugins/about/storageabout.cpp:616
❌ No source file named /opt/ut/dev/lomiri-system-settings/plugins/about/storageabout.cpp.
Make breakpoint pending on future shared library load? (y or [n]) y
Breakpoint 1 (/opt/ut/dev/lomiri-system-settings/plugins/about/storageabout.cpp:616) pending.
(gdb) info b
Num Type Disp Enb Address What
1 breakpoint keep y <PENDING> /opt/ut/dev/lomiri-system-settings/plugins/about/storageabout.cpp:616
(gdb) r
(many lines skipped, on the phone the app displays..)
Thread 1 "lomiri-system-s" hit Breakpoint 1, StorageAbout::getFreeSpace (this=0x5555de61e0, mount_point="/home") at /opt/ut/dev/lomiri-system-settings/plugins/about/storageabout.cpp:616
616 if (si.isValid())
(gdb) p mount_point
$4 = "/home"
(gdb) n
617 return si.bytesFree();
(gdb) p si.bytesFree()
$3 = 183778746368
(gdb) c
Continuing.
(many lines skipped on the terminal, on the phone the system information
screen appears)
(closing the app on the phone the normal way)
[Inferior 1 (process 8211) exited normally]
(gdb)
<hit ctrl D to exit>
(gdb) quit
phablet@ubuntu-phablet:/opt/click.ubuntu.com/lomiri-settings/current$