[Debian GNUstep maintainers] Forky goal: Dual GCC/GNUstep runtime support
Yavor Doganov
yavor at gnu.org
Wed Jan 1 18:12:12 GMT 2025
Some time ago, Gregory contacted me privately with a request to
package the GNUstep Objective-C runtime (libobjc2) for Debian as some
companies providing commercial support for GNUstep software need it in
the official archive. I finally found some time to fiddle with it and
as it was supposed to be a leaf library with no reverse-dependencies
(considered "A Bad Thing" in Debian), I've been thinking about adding
support for the GNUstep runtime in all GNUstep packages. I hope I
found a way of doing that, although it won't be easy and will require
sourceful changes in every package. More details below.
libobjc2
========
libobjc2 doesn't clash with the GNU/GCC runtime because the former
installs the .so symlink in /usr/lib/$(DEB_HOST_MULTIARCH) while the
latter is at /usr/lib/gcc/$(DEB_HOST_MULTIARCH)/$(GCC_MAJOR_VER).
Also, as you all know very well, the GCC runtime's headers are
installed there as well and not in the public location /usr/include.
However, Debian's Clang has GCC's internal directories before the
public ones in the library search path. And the clang-19 package
depends on libobjc-14-dev. To demonstrate this:
$ clang -v
Debian clang version 19.1.6 (1)
Target: x86_64-pc-linux-gnu
Thread model: posix
InstalledDir: /usr/lib/llvm-19/bin
Found candidate GCC installation: /usr/lib/gcc/x86_64-linux-gnu/11
Found candidate GCC installation: /usr/lib/gcc/x86_64-linux-gnu/12
Found candidate GCC installation: /usr/lib/gcc/x86_64-linux-gnu/13
Found candidate GCC installation: /usr/lib/gcc/x86_64-linux-gnu/14
Selected GCC installation: /usr/lib/gcc/x86_64-linux-gnu/14
Candidate multilib: .;@m64
Candidate multilib: 32;@m32
Candidate multilib: x32;@mx32
Selected multilib: .;@m64
On FreeBSD 14:
$ clang -v
clang version 13.0.0
Target: aarch64-unknown-freebsd14.0
Thread model: posix
InstalledDir: /usr/local64/llvm-morello/bin
This means that when building an Objective-C program with Clang on
Debian, it will always link the GNU runtime even if libobjc2 is
installed. This is because it will first find GCC's libobjc.so in
GCC's private dir which is a symlink to the GCC library at
/usr/lib/$(DEB_HOST_MULTIARCH). It is possible to link explicitly
with the GNUstep runtime with -l:libobjc.so.4.6 which is a nuisance.
So, to avoid any possibility for further clashes (for example, if some
future library version of the GCC runtime happens to be 4.6) and any
undesirable linking done by mistake, I decided that it's best to name
the library libobjc2 and link with it with -lobjc2.
So, in conclusion:
$ dh_listpackages
libobjc2-dev
libobjc2-4.6
Both Objective-C runtimes installed:
$ LC_ALL=C ls -l /usr/lib/x86_64-linux-gnu/libobjc*
lrwxrwxrwx 1 root root 16 Dec 30 12:54 /usr/lib/x86_64-linux-gnu/libobjc.so.4 -> libobjc.so.4.0.0
-rw-r--r-- 1 root root 92040 Dec 30 12:54 /usr/lib/x86_64-linux-gnu/libobjc.so.4.0.0
-rw-r--r-- 1 root root 329374 Dec 29 16:50 /usr/lib/x86_64-linux-gnu/libobjc2.a
lrwxrwxrwx 1 root root 15 Dec 29 16:50 /usr/lib/x86_64-linux-gnu/libobjc2.so -> libobjc2.so.4.6
-rw-r--r-- 1 root root 202536 Dec 29 16:50 /usr/lib/x86_64-linux-gnu/libobjc2.so.4.6
lrwxrwxrwx 1 root root 19 Dec 30 12:54 /usr/lib/x86_64-linux-gnu/libobjc_gc.so.4 -> libobjc_gc.so.4.0.0
-rw-r--r-- 1 root root 96104 Dec 30 12:54 /usr/lib/x86_64-linux-gnu/libobjc_gc.so.4.0.0
GNUstep core
============
The gnustep-make source package will build-depend on clang and
libobjc2-dev (only on supported architectures) and will be
configured/built twice in separate directories: first with the
arguments that we use now plus --with-library-combo=gnu-gnu-gnu, and
the second time with --with-library-combo=ng-gnu-gnu
--with-objc-lib-flag=-lobjc2. The files which differ as a result of
the different configuration will be controlled via the alternatives
mechanism. These are:
/usr/bin/gnustep-config
/usr/share/GNUstep/Makefiles/config.make
/usr/share/GNUstep/Makefiles/config-noarch.make
/usr/share/GNUstep/Makefiles/executable.template
/usr/share/GNUstep/Makefiles/GNUstep.csh
/usr/share/GNUstep/Makefiles/GNUstep.sh
The gnustep-common package will ship a new configuration file
/etc/GNUstep/runtime.conf which will allow the sysadmin to set the
system default to the GNUstep runtime. It will also ship a new shell
script (let's say gnustep-runtime) which will allow to switch the
runtime temporary, without editing runtime.conf (this will be
necessary for building two variants of all GNUstep packages). This
script will run update-alternatives with the right arguments and all
relevant alternatives will be prefixed with a common string (like
gnustep-runtime-$package-file...). It can probably find processes
linked with gnustep-base and can kill them by default when a switch
is being made.
/usr/share/GNUstep/debian/config.mk will include a new variable, let's
say DEB_GS_LIBOBJC2_SUPPORTED, containing a list of architectures
where libobjc2 is available. Thus, we'll conditionally do an
additional build only on architectures where the GNUstep runtime is
supported. These will exclude architectures where clang is not
available at all, and also those where src:libobjc2 FTBFS or its
testsuite doesn't pass.
The gnustep-common package will provide the virtual package
gnustep-runtime-is-ng [supported_architectures], which will allow
packages supporting only the GNUstep runtime to declare this package
as a build-dependency. That way, we can package stuff like the
NEXTSPACE desktop.
In addition to gobjc, the gnustep-make package will depend on clang
and libobjc2-dev (only on supported architectures). This will avoid
adding these as build-dependencies to every GNUstep package.
The gnustep-base package will be built twice for both runtimes and the
library + all tools will be controlled with the alternatives
mechanism. The same can be said for GUI, Back, etc. For Back it'll
be a bit more complex since we already use the alternatives mechanism
to select the default backend.
Rest of GNUstep
===============
Basically every arch:any package will need modifications to
debian/rules, perhaps *.install files and the introduction of
maintainer scripts to manage the alternatives. Here's an example
pseudo-code snippet of a simple .app package with no library and
bundles (completely untested as everything is still only in my head):
override_dh_auto_build:
ifeq ($(filter $(DEB_GS_LIBOBJC2_SUPPORTED)),$(DEB_HOST_GNU_ARCH))
mkdir build-gnu
# The GNU runtime is the default but a user with a modified
# /etc/GNUstep/runtime.conf might be building the package with
# dpkg-buildpackage or debuild (not in a chroot).
gnustep-runtime --gnu
dh_auto_build --builddirectory=build-gnu -- \
$(verbose) $(optim) \
$(shell dpkg-buildflags --export=cmdline)
mv build-gnu$(GNUSTEP_SYSTEM_APPS)/AClock \
build-gnu$(GNUSTEP_SYSTEM_APPS)/AClock.gnu
mkdir build-ng
gnustep-runtime --ng
dh_auto_build --builddirectory=build-ng -- \
$(verbose) $(optim) \
$(shell dpkg-buildflags --export=cmdline)
mv build-ng$(GNUSTEP_SYSTEM_APPS)/AClock \
build-ng$(GNUSTEP_SYSTEM_APPS)/AClock.ng
# Restore default as per /etc/GNUstep/runtime.conf.
gnustep-runtime --default
else
dh_auto_build -- $(verbose) $(optim) \
$(shell dpkg-buildflags --export=cmdline)
endif
Of course the dh_auto_install target must be overridden as well.
We'll see how we can automate this, perhaps via dh_gnustep or a new
"gnustep" debhelper build system, if I manage to write that in Perl.
GNUstep packages supporting only one runtime
============================================
In theory, there should be no packages that only support the GCC/GNU
runtime, because libobjc2 is advertised as a fully compatible drop-in
replacement.
For packages supporting only libobjc2, we can install wrappers in
/usr/libexec, and such package can symlink their executable(s) to the
corresponding wrapper. The gnustep-common package can provide a
simple shell script for tools which will exit with an error and a
helpful message that this tool is not supported with the GCC/GNU
runtime. Likewise, for apps we can provide a simple app spawning an
informative NSAlertPanel in the gnustep-gui-runtime package.
Conclusion
==========
Summing up the pros and cons:
Pros:
~~~~~
* We are perhaps rightfully being accused for being stubborn,
ultra-conservative and impeding "progress". While the GCC/GNU
runtime will still be the default, it will be very easy to switch
to the GNUstep runtime by one-time editing
/etc/GNUstep/runtime.conf and running "gnustep-runtime --default".
All maintainer scripts will read that file and will respect the
sysadmin's setting.
* We'll get Clang's warnings for every package and will probably find
some new bugs.
* It will be very easy to test the "gnu" and the "ng" variant of a
package. I guess it will also help us narrow down bugs and
discover the real cause (compiler/runtime/programmer's error/etc).
* Because build-time tests (like gnustep-base's extensive testsuite)
and autopkgtests will be run for both variants of the package,
we'll discover even more bugs.
* This change will allow us to package libobjc2-only software and
probably much more free software from the muckOS world.
* We don't need FTP masters' or Release Team's approval to carry out
this plan. We probably need RT's assistance to set up a tracker
when the time comes, but that's all. Although I spent a lot of
time looking, I couldn't find any violation of Debian's policies.
Cons:
~~~~~
* It's significant work. While we're all lazy by nature, I don't
mind extra work if it's not in vain.
* The build times of all packages will grow about twice. This is
a minor concern since most GNUstep packages, including GNUstep core,
build relevantly fast even on the slowest architectures.
* Right now, I don't see a way to include the default runtime in bug
reports. This is a much more important detail than the GUI backend.
* The alternatives mechanism is ... fragile. One broken symlink can
make the whole system useless for GNUstep users.
* This was never done in any GNU distro or a BSD variant so
(unpleasant) surprises are likely.
* Some users dislike gnustep-make (like libobcj2's principal author)
and don't use it so they'll have to find alternative ways to cater
for our incompatible -lobjc2 if they wish to build on Debian with
libobjc2.
I'll wait a week or two for comments and suggestions. Then I plan to
start working on this at "ng" Git branches, starting with the core
packages first. These can be merged at any point, but I need the
packages installed to perform any kind of runtime tests. Hopefully,
we'll start uploading these changes to experimental as soon as the
forky development cycle begins (right now I want to keep experimental
clear if there are new GNUstep releases prior to the trixie freeze).
When we're ready with all packages, we can start uploading to
unstable.
In any case, this is clearly forky material, given the amount and the
complexity of the changes.
More information about the pkg-GNUstep-maintainers
mailing list