Tooling and wrappers to make it easier to run + debug SWT in multiple Linux environments. See this blog post for some more screenshots of this in action.
The Eclipse SWT project, on Linux especially, has a huge number of configurations it needs to support. SWT needs to support multiple versions of Java, two major versions of GTK, many minor releases of GTK, display types (x11, wayland) and compositors (kwin, mutter). Add into that the customizations different Linux distros apply, different window managers, clipboard helpers, and the complications of dbus. Testing or reproducing a change may require customizing very many things in the stack.
Traditionally, doing these tests or trying to reproduce an issue involves dual-booting machines, installing VMs, or modifying your main dev machine.
This project aims to reduce some of that complication by making it easy to develop on a main machine, but debug and run SWT on the myriad of different environments.
Some of this repo falls into tips and tricks, some is tooling to make it easier to setup and run those different environments using docker, and some of it is documenting how I have my development setup in the hope it helps others.
While the focus is on SWT, this repo should make running and debugging Java easier, regardless of the use of SWT or not, especially if GUIs are involved.
This repo contains some scripts and Dockerfiles to allow building and debugging from with a single Eclipse instance, but running Java tests, snippets and example in a variety of environments.
The create-javas.sh script will, using docker and your existing java installation, create many additional Java installations that can be configured in Eclipse (or any IDE or command line) that will redirect the execution of that Java command into its corresponding docker container.
For example, after installation, in addition to your main java in $JAVA_HOME/bin/java, you will have java installation such as $PWD/javas/jdk-21.0.9+10-swt-dev-ubuntu-24.04/bin/java that will transparently run Java 21 in the swt-dev-ubuntu-24.04 container.
These are the tools required:
- java downloaded and installed with
JAVA_HOMEpointing at the installation- the additional java installs are created using overlay file systems with this initial install as a base
- docker installed
- docker configured to run without sudo
If you don't have all your java + eclipse tools in /scratch as I do then you need to modify run-in-docker.sh to update the mounts.
The idea is to have the paths in Docker exactly match those on the host operating system to allow everything to run without having to be concerned about path mappings.
This means adding/changing -v command line to make all your tools, sources, jars, etc available to the docker container.
Avoid mapping /usr, you want to use the distro's base tools and libraries.
It may work to share your java installation in /usr/lib/jvm, but I have not tried it yet.
If it works for you, consider a PR to update this paragraph.
Run the create-javas.sh script which will build all the docker containers located in images and then create a java installation for them.
On successful installation, the basename of JAVA_HOME + the image name of the docker image will be created as a new JDK with some tools replaced and enhanced.
Test out the installation by running jshell transparently on docker, for example:
$ ./javas/$(basename $JAVA_HOME)-swt-dev-ubuntu-24.04/bin/jshell -q
jshell> System.getenv("HOME")
$1 ==> "/home/ubuntu"
$ ./javas/$(basename $JAVA_HOME)-swt-dev-ubuntu-24.04/bin/java Example.java
Hello from Java.
HOME=/home/ubuntu
Press enter to continue.
To use the new JVMs in Eclipse, first add them to Installed JREs (in Preferences):
And then in the launch configuration, select the new JRE to run as the Alternate JRE
The main tool and purpose is to replace java with a transparently redirected java to the docker container.
Running the newly created java executable will launch java with docker run by redirecting to the run-in-docker.sh script for configuration of the run.
Running the newly created jshell executable will launch jshell with docker run by redirecting to the run-in-docker.sh script for configuration of the run.
jps in $JAVA_HOME will show all java processes by their PID as known to the host machine.
To see the PID of the java processes in all the docker containers for the image of its JDK, run jps.all.
Similarly, jstack.all can be used to run jstack on all the running containers for the image of its JDK.
jstack.all is convenient if there is only one running container, if there are multiple containers it will try to get stack traces from unexpected processes.
VisualVM should work as normal allowing inspection JVM state even though it is running in docker.
Note that for best behaviour VisualVM also wants paths in the docker container to match host machine. See this comment on VisualVM bug tracker for more info.
This section contains guidance on common errors that occur when running SWT + GTK under docker containers.
GTK4's WebKit runs by default in a sandbox for extra protection. This is problematic in the docker container and leads to errors such as:
bwrap: No permissions to creating new namespace, likely because the kernel does not allow non-privileged user namespaces. On e.g. debian this can be enabled with 'sysctl kernel.unprivileged_userns_clone=1'.
** (SWT:1): ERROR **: 19:45:44.678: readPIDFromPeer: Unexpected short read from PID socket. (This usually means the auxiliary process crashed immediately. Investigate that instead!)To enable webkit for GTK4 turn on the WEBKIT_DISABLE_SANDBOX_THIS_IS_DANGEROUS environment variable in the wrapper (run-in-docker.sh).
Adding some combination of these flags to the command line of docker:
--security-opt seccomp=unconfined
--security-opt apparmor=unconfinedAnd/or this to the Dockerfile:
RUN sysctl kernel.unprivileged_userns_clone=1
RUN sysctl kernel.apparmor_restrict_unprivileged_userns=0
May provide the correct workaround, however I could not get that to work. PRs are welcome!
By default docker does not allocate much shared memory to containers, currently 64MB.
This may not be enough for some large GUI applications, therefore if you can increase shared memory by adding --shm-size 1g, or a suitable size to run-in-docker.sh.
Shared memory errors seem to only be a problem if hardware acceleration is enabled.
Hardware acceleration is off, or mostly off when using these docker wrappers. This leads to errors/warnings like:
MESA: error: Failed to query drm device.
libEGL warning: egl: failed to create dri2 screen
** (SWT:1): WARNING **: 19:53:07.751: Disabled hardware acceleration because GTK failed to initialize GL: No GL implementation is available.
libEGL warning: wayland-egl: could not open /dev/dri/renderD129 (No such file or directory)
libEGL warning: wayland-egl: could not open /dev/dri/renderD129 (No such file or directory)
These can be safely ignored, at the expense of some potentially slower rendering.
There are a number of command line options you can add to docker (in run-in-docker.sh) that may enable hardware acceleration, depending on your machine setup:
--device=/dev/dri/card1
--device=/dev/dri/card2
--device=/dev/dri/renderD128
--device=/dev/dri/renderD129
--group-add video
--group-add renderThe docker user (e.g. ubuntu) may need to have permission to access these items by being part of the above mentioned groups:
RUN groupadd -g 991 render # check ID
RUN usermod -a -G render ubuntu
RUN usermod -a -G video ubuntu
The programs run under the wrapper will not have dbus support, this leads to errors/warnings such as:
SWT SessionManagerDBus: Failed to connect to org.gnome.SessionManager: Failed to execute child process “dbus-launch” (No such file or directory)
SWT SessionManagerDBus: Failed to connect to org.xfce.SessionManager: Failed to execute child process “dbus-launch” (No such file or directory)These can be ignored assuming you are not testing something related to having dbus working properly. Most SWT issues do not require dbus to work, however testing issues within the wider IDE, such as opening files from the command line or launching file managers may require dbus to work.

