Randa sprint wrap-up and considerations on packaging complex applications with AppImage

On Sunday, this year’s KDE sprint in Randa, Switzerland came to an end and I am now back home. It was a productive and fun week, as always when meeting the other KDE folks.

Screenshot_20160620_234752

Navigation widget in KDevelop with proper spacing and margins

I spent a lot of time on polishing things and fixing small issues mostly in KDevelop, such as:

  • reduce flickering of the completion widget in kdevelop
  • fix various cases where KDevelop’s navigation widget (see above) would have weirdly large spacing in between or at the end
  • fix kdev-python’s completion widget from being absurdly wide in full mode for long type names
  • fix some scoping issues in kdev-python
  • fix colors in kdevelop’s Find in Files widget
  • use a nicely formatted file name in the window title when no project is open
  • fix a few crashes

Open project dialog

One mentionable major task I did was to make the “Open project” dialog work properly on non-linux platforms; that required a bit of thinking, because before, you could open either a project manager config file (e.g. CMakeLists.txt) or a directory from the dialog. However, when using the native file dialog, you have to select in advance whether you want to select a file or a directory (can’t have both). The solution is to always select only the directory, and offer a choice of possible project managers (and the associated config files) later in the process.

Remove assistant overlay in favour of navigation widget

Another major thing Kevin Funk and I worked on was rethinking KDevelop’s assistant popup; especially in the current 5.0 betas, it tends to be a bit annoying and gets in the way a lot. We are thus trying to remove the assistant overlay in favour of offering executions of assistants from the navigation widget:

Screenshot_20160620_235618

“Adjust signature” assistant invoked from the navigation widget.

It’s not working perfectly yet, but it is certainly much less agressive than the previous solution, while behaving very similar in terms of usage (Alt+1 still executes the first fix, etc.). It’s currently only in the assistantpopup-ng branch, but we hope to be able to merge that soon.

Packaging KDevelop with AppImage: advanced thoughs

For users stuck on old systems and for testing purposes, we’d like to be able to ship single-file executables containing KDevelop to users (click here to get one). One solution for creating such executables is AppImage. An AppImage basically contains a file system hierarchy containing all the things which cannot be reasonably assumed to be on the user’s system (it is not a container or chroot though, just a set of libraries and assets). In our case, that is most notably a recent version of Qt, some Qt plugins, various KDE frameworks, llvm, KDevelop itself, and libpython plus the kdevelop-python plugin.

In order to start up KDevelop in this environment, various environment variables have to be set in order to make it access the things from the AppImage. Most notably, that is $PATH, $LD_LIBRARY_PATH, $XDG_DATA_DIRS and $QT_PLUGIN_PATH. Sounds simple enough, right? But now consider this: the user wants to run cmake from inside KDevelop. cmake is not shipped with the AppImage, but inherits this environment — and crashes, because it picks up a wrong version of some library from the bundle. The solution seems simple enough: undo the changes to LD_LIBRARY_PATH before launching cmake.
However, in practice, how do you do that? We cannot undo the changes “globally”, because executables shipped with the AppImage, such as  kioexec (used to open [remote] files) still need it even after KDevelop is running. One solution is to go over the code base and change it back everywhere manually where it’s required. But even that is not sufficient; library calls (like KRun::run) might start processes as well, and the calling application cannot influence those processes’ environment directly.

Screenshot_20160620_233936

KDevelop with kdev-python running from an AppImage. The image was built on CentOS6 from 2011 (and runs there), and runs on a bleeding-edge Arch system. It should run on most things in between as well.

The solution I finally managed to come up with, and which seems to work okay, is by using LD_PRELOAD. By setting this environment variable, you tell the dynamic linker to load your own versions of library functions instead of the ones it would otherwise load. What use is that to us? Well, when a program wants to start another program, it has to ask the operating system. That usually happens through calling a function from the glibc library. The most common function to launch a process is execve(). We can now provide a library in LD_PRELOAD which overrides the system’s execve() and does this:

  • Look at the path of the process which is launched.
    • If the path is inside the AppImage, do nothing.
    • Otherwise, modify the environment such that the changes we did for start-up of the application are rolled back (remove added components from $PATH, etc.).
  • Call the original system’s execve() to execute the program in the modified environment.

It is quite a hack, but it does seem to work well, and the beauty is that you don’t have to scatter “change the environment back” code all over your application code.

AppImage: Next steps

I will clean up my exec-wrapper and publish it. I will also clean up my KDevelop build script, and try to create a docker image I can run it in, so that people can do that easily (not requiring them to set up a CentOS6 VM themselves).

If you like what we’re doing in those sprints, consider supporting us so we can do another one next year 🙂

Categories: Everything

Tags: , , ,

1 reply

Trackbacks

  1. Links 23/6/2016: Red Hat Results, Randa Stories | Techrights

Leave a Reply

Your email address will not be published. Required fields are marked *