Ryochan7 / sc-controller

User-mode driver and GTK3 based GUI for Steam Controller
GNU General Public License v2.0
176 stars 23 forks source link

Update AppImage build and add CI #98

Closed git-developer closed 10 months ago

git-developer commented 11 months ago

Background

I'm maintaining batocera-extra, a project that integrates multiple cemuhook servers into the retro gaming distribution batocera.

kozec/sc-controller was integrated recently. After that was done, I found this fork which seems to be maintained actively, and thus I'd like to use it instead.

I used the latest AppImage release and had some trouble getting it to work. I'd like to contribute what I was able to fix.

Summary

Everything below here is not really important anymore. It is kept to allow tracking the history of this PR.

History of this PR ### Bugs #### Bug: python-evdev is outdated - fixed The log reports a warning: ```W evdev Failed to enable Evdev driver: 'python-evdev' package is missing.``` Debugging was painful, but in the end it turned out that the bundled `evdev-python` version 0.7.0 causes this problem. An update to the latest version `1.6.1` fixes it. #### Bug: missing dependency to cairo - fixed The log reports an error: ```g_module_open() failed for /tmp/.mount_sc-conHsUk9d/usr/lib/gdk-pixbuf-2.0/2.10.0/loaders/libpixbufloader-svg.so: libcairo-gobject.so.2: cannot open shared object file: No such file or directory``` The reason is a missing dependency in the AppImage. Adding `cairo` fixes it. #### Bug: AppImage is broken when using python 3.11 - fixed When inspecting the `appimage-build.sh` script, I found that some libraries are downloaded from Arch Linux repositories. Thus I decided to use Arch Linux as base OS to build the AppImage. Unfortunately, the resulting AppImage is broken because Arch Linux currently bundles Python 3.11, and some version numbers in the scripts are hardcoded to 3.10. One effect was that some files and resources (e.g. `input-event-codes.h`) could not be found. Changing the hardcoded version numbers fixes this problem. #### Filename and project name differ - fixed A tiny one, but breaks automation: the project is called `sc-controller` (with a dash) but the AppImage file begins with `sc_controller` (with a underscore). #### Bug: Bluetooth - broken, but workaround possible The log contains a warning about bluetooth: ```W DevMon Failed to load libbluetooth.so, bluetooth support will be incomplete``` The AppImage does not contain a bluetooth library, and I suppose this is by design. On my test system, the host contains a `libbluetooth.so`, but the AppImage cannot find it. The problem is caused by the way how it is located. First, `gcc` is called (`gcc -Wl,-t -o /tmp/tmpyxtuxg9k -lbluetooth` and `gcc -Wl,-t -o /tmp/tmpgk90thlu -lc`). These calls are expected to print the path of the libraries `libbluetooth` and `libc`. After that, the tools `ld` and `objdump` are called. Neither of these tools is part of my target OS (the batocera distribution). I found a nasty workaround: 1. Install `binutils` `libelf` so that `ld` and `objdump` are available. 2. `gcc` can't be installed because it is too big for my target partition. So instead provide a script called `gcc` that does what the AppImage expects, and append it to the `$PATH`: ```sh if [ "${1-}" = '-Wl,-t' ]; then arg_outfile="${3}" query="${4#-l*}" find /lib /usr/lib -name "lib${query}.so.?" | tee "${arg_outfile}" fi ``` I'd **really** appreciate if we could do something here. Maybe it is reasonable to bundle `bluez-libs` in the AppImage? Or maybe the algorithm to locate the library can be simplifed? #### Bug: Errors on daemon termination - open The log contains errors about USB devices when the daemon is stopped:
Log (spoiler) ``` D SCCDaemon Starting SCCDaemon... D SCCDaemon Initializing drivers... W SCCDaemon Skipping disabled driver 'fake' W SCCDaemon Skipping disabled driver 'remotepad' D USB Registered USB driver for 054c:09cc D USB Registered USB driver for 054c:05c4 D USB Registered USB driver for 054c:0ce6 D USB Registered USB driver for 28de:1102 D USB Registered USB driver for 28de:1142 D USB Registered USB driver for 28de:1205 D Mapper Creating virtual devices D Mapper Keyboard: D Mapper Mouse: D Mapper Gamepad: D SCCDaemon Created control socket /userdata/system/.config/scc/daemon.socket W SCCDaemon DISPLAY env variable not set. Some functionality will be unavailable D asyncio Using selector: EpollSelector D USB USB device added: 28de:1142 D USB Releasing devices... Exception ignored in: Traceback (most recent call last): File "/tmp/.mount_sc-conyJZ86J/usr/lib64/python3.10/site-packages/scc/lib/usb1.py", line 1077, in __del__ self.close() File "/tmp/.mount_sc-conyJZ86J/usr/lib64/python3.10/site-packages/scc/lib/usb1.py", line 1112, in close transfer.cancel() File "/tmp/.mount_sc-conyJZ86J/usr/lib64/python3.10/site-packages/scc/lib/usb1.py", line 736, in cancel raise self.__USBError(result) scc.lib.libusb1.USBError: LIBUSB_ERROR_NOT_FOUND [-5] Exception ignored in: Traceback (most recent call last): File "/tmp/.mount_sc-conyJZ86J/usr/lib64/python3.10/site-packages/scc/lib/usb1.py", line 306, in __del__ self.cancel() File "/tmp/.mount_sc-conyJZ86J/usr/lib64/python3.10/site-packages/scc/lib/usb1.py", line 736, in cancel raise self.__USBError(result) scc.lib.libusb1.USBError: LIBUSB_ERROR_NOT_FOUND [-5] Exception ignored in: Traceback (most recent call last): File "/tmp/.mount_sc-conyJZ86J/usr/lib64/python3.10/site-packages/scc/lib/usb1.py", line 306, in __del__ self.cancel() File "/tmp/.mount_sc-conyJZ86J/usr/lib64/python3.10/site-packages/scc/lib/usb1.py", line 736, in cancel raise self.__USBError(result) scc.lib.libusb1.USBError: LIBUSB_ERROR_NOT_FOUND [-5] Exception ignored in: Traceback (most recent call last): File "/tmp/.mount_sc-conyJZ86J/usr/lib64/python3.10/site-packages/scc/lib/usb1.py", line 306, in __del__ self.cancel() File "/tmp/.mount_sc-conyJZ86J/usr/lib64/python3.10/site-packages/scc/lib/usb1.py", line 736, in cancel raise self.__USBError(result) scc.lib.libusb1.USBError: LIBUSB_ERROR_NOT_FOUND [-5] Exception ignored in: Traceback (most recent call last): File "/tmp/.mount_sc-conyJZ86J/usr/lib64/python3.10/site-packages/scc/lib/usb1.py", line 306, in __del__ self.cancel() File "/tmp/.mount_sc-conyJZ86J/usr/lib64/python3.10/site-packages/scc/lib/usb1.py", line 736, in cancel raise self.__USBError(result) scc.lib.libusb1.USBError: LIBUSB_ERROR_NOT_FOUND [-5] ```
I don't know the cause for that, maybe the daemon tries to close connections that it didn't open? ### Features #### Feature: CI Build I added a `Dockerfile` that runs a build for the AppImage based on an Arch Linux image, and a GitHub Workflow that is triggered when a GitHub release is published. The AppImage has a `zsync` file for updates, too. Example how it could look like: * [Release page](https://github.com/git-developer/sc-controller/releases/tag/v0.4.8.11-1) * [Run Log](https://github.com/git-developer/sc-controller/actions/runs/6225212809) The workflow is prepared for a multi-platform build, but since Arch Linux is `amd64` only, this is the only enabled platform for now. It could be extended to ARM later. ### Notes Please tell if you think that some of the problems should be reported as separated issues. I'm looking forward to your comments.
git-developer commented 11 months ago

Everything below here is not really important anymore. It is kept to allow tracking the history of this PR.

History of this PR I found and addressed two further points in additional commits. #### Site packages path The `site-packages` path was derived from the OS where the AppImage is built. This can be different from the runtime system and lead to conflicts at runtime, e.g. when the python versions differ. In addition, Debian-derived distros use `dist-packages` instead of `site-packages` which would lead to a mess within the AppImage and a broken `PYTHONPATH`. The build was changed to use a single directory that is independent of the OS of the building system. As a proof-of-concept, the build OS was changed to Ubuntu. The script is still compatible to Arch. #### Bluetooth support The AppImage now contains the required libraries for bluetooth support.
git-developer commented 11 months ago

Everything below here is not really important anymore. It is kept to allow tracking the history of this PR.

History of this PR I added another commit which introduces [appimage-builder](https://appimage-builder.readthedocs.io/) in favor of the hand-crafted shell script. It allows a stable build environment, up-to-date library versions and a multi-platform build. An AppImage example is available [here](https://github.com/git-developer/sc-controller/releases/tag/v0.4.8.11-3). It contains all required libs (incl. bluetooth) and was tested on amd64, for both daemon and GUI, using a Steam Controller via USB dongle and a PS4 controller via bluetooth/evdev. The arm64 binary is untested. There's a single issue left, the bug _Errors on daemon termination_.
Ryochan7 commented 10 months ago

Neat. I will take a look at this later. Sorry about not getting around to looking at this sooner.

git-developer commented 10 months ago

Thanks. I just added another commit to increase host compatibility.

The (ugly) details

When I began to learn what an AppImage is, I thought it is a "build once, run anywhere" solution. Unfortunately it's not.

I successfully tested the AppImage based on Ubuntu Jammy on a host running batocera-37 (which is based on buildroot 2023.02.1). The same AppImage does not work on batocera-38 (which is based on buildroot 2023.05.1): it fails with unresolved symbol g_string_free_and_steal. Debugging revealed that libgdk-3.so.0 (part of gtk3, on the host) requires some of the bundled libraries (libglib-2.0.so.0 & libgio-2.0.so.0, part of glib2) in newer versions (gtk3: 3.24.36 vs. 3.24.38, glib2: 2.72.3 vs. 2.76.1). Welcome in dependency hell.

So, what can we do about it?

  1. Do not bundle glib2 in the AppImage, but make it a requirement on the host. I don't prefer this option because the point of using an AppImage is being free of requirements.
  2. Bundle gtk in the AppImage. This would make the AppImage really big, it would include a complete theme whose look and feel would differ from the host.
  3. Offer several AppImages.

I decided for option 3. The build now creates an AppImage based on Ubuntu Jammy / 22.04 (compatible with batocera-37) and another AppImage based on Ubuntu Lunar / 23.04 (compatible with batocera-38). This allows users to choose an AppImage compatible for their host environment. The build creates another AppImage based on Ubuntu Mantic / 23.10 which crashes with a segfault on batocera-38 (maybe the kernel is too old). An AppImage based on the Ubuntu Focal / 20.04 is not possible because the required library python3-vdf is not available there.

I saw in the git history of sc-controller that it wasn't easy to create an AppImage that works on a Steam Deck. I don't have one, and I'm curious if one of the AppImages is compatible.

Ryochan7 commented 10 months ago

I do not own a Steam Deck either so I have no idea if the older AppImage builds are compatible. The distro targets I have been using are Ubuntu 22.04, Fedora 35, and the latest Manjaro. I still primarily use Fedora as my daily driver. Not sure how far behind the current SteamOS 3.0 distro is compared to vanilla Arch Linux. I would hope that testing against Manjaro / Arch Linux would help improve compatibility with SteamOS 3.0 somewhat.

Flatpak is probably a bit better off when it comes to being more distro agnostic than AppImage. I have run into many problems trying to keep the existing AppImage build system compatible with Ubuntu 22.04 more than other distros. Even subsequent versions with very small changes have required some extra debugging to keep the AppImage compatible.

Option 3 was likely the best choice. It is unfortunate that even that option has its own share of problems. Anyway, thank you for the workflows and the Docker files.

git-developer commented 10 months ago

Thanks a lot for merging!

Currently, the GitHub workflow is configured to create artifacts for each commit on branch main, or when a GitHub Release is created. Do you already have plans for a next release? Without, the AppImage will not be available. Or do you prefer to create an AppImage for every commit in branch python3, too? I could open another PR, just have to know the build trigger.