Hi fellow cross/toolchain people,
you know I'm looking into making packages cross build and today I
happened to look into fftw3. What sounds like any other package turns
out to be a very interesting one, because fftw3 uses Fortran. It
happens
to declare Build-Depends: mpi-default-dev, which happens to declare
Depends: libopenmpi-dev, which happens to declare Depends: gfortran-14
|
gfortran-mod-15. Since the entire chain is architecture dependent
packages with no Multi-Arch: foreign nor :any annotations, what we end
up requesting here is the host architecture gfortran-14 native
compiler
and that of course is bad for cross compilation.
Earlier this year, Matthias merged and fixed (thanks a lot!) my
gcc-for-host patches. What we'd want here is some fortran compiler for
the architecture of the libopenmpi-dev package. Now that we have those
fancy -for-host packages it's just a matter of writing
gfortran-14-for-host, right? Unfortunately, no.
I learned the hard way that the semantics provided by the -for-host
packages do not match the practical requirements. If one installs just
the -for-host package, the provided interface really is just the
triplet-prefixed compiler and you don't get the bare one. However,
when
performing a native build, what really is getting called is the bare
one
and that goes missing. So what we really really want here is different
semantics. If installing a -for-host package, the expectation from
most
build systems really is that we can call the triplet-prefixed tool and
in addition to that, but also if we are doing a native build we want
the
bare names to work. However, the bare names are not provided for the
native case by the -for-host package.
This sounds easy enough. A -for-host package simply Depends on some
-<triplet> package and when generating that package, we might just add
a
Depends: ...-for-build whenever the compiler is native (i.e. the
triplet
matches the package architecture). I think this would technically work
in the first instance, but it breaks another use case.
More and more we're running into the problem that 32bit builds run out
of address space and the only final solution to this problem is using
a
64bit compiler executable. For example, we'd like to do an i386 build
using gcc-i686-linux-gnu:amd64. From an autotools (and other build
systems) point of view, this is a native build. Hence, the expectation
is that cc and gcc exist and build for i386, but
gcc-i686-linux-gnu:amd64 would not depend on gcc-for-build in that
scheme and gcc-for-build. In fact, gcc-for-build would depend on gcc,
which would depend on gcc-i686-linux-gnu with a (= ...) version
constraint that is never matched by gcc-i686-linux-gnu:amd64.
How do we want to deal with this?
I see the following options:
1. Remove the assumption that you can run bare toolchains from
build-essential. Expect that all toolchain invocations correctly
spell out their architecture. For most build systems, this amounts
to
exporting a suitable CC variable or equivalent:
* dpkg's buildtools.mk would always emit prefixed toolchains.
* The debhelper makefile buildsystem would pass a
triplet-prefixed
CC for all builds, not just cross builds.
* The debhelper autotools buildsystem would export a
triplet-prefixed CC for the configure invocation.
* meson env2mfile would always emit a triplet-prefixed compiler.
* The debhelper cmake buildsystem would always pass a
triplet-prefixed CMAKE_C_COMPILER (and friends).
With these changes, I expect that we could make about 80% (wild
guess)
of packages just work, the remaining ones are many and often hard
to
fix (e.g. we completely break any use of mpicc).
2. Ignore the large address space for 32bit use case and just add the
bare toolchain names to the API for -for-host whenever it happens
to
be native.
3. Dynamically manage the bare toolchain names using maintainer
scripts.
This is a bit tricky. We currently install the gcc-14 ->
<triplet>-gcc-14 symbolic link in gcc-14. This package is
important
for a few rare use cases where the compiler architectures must
match
the host architecture (e.g. for using plugins). We'd stop
installing
this by default and move the gcc-14 link into gcc-14-<triplet>
package. There it would not be part of the data.tar and instead be
managed using a maintainer script that creates the link if the
native
architecture happens to be the triplet of the package.
As a result, dpkg -S /usr/bin/gcc-14 would no longer report an
owning
package. Since toolchains are diverted in a number of places, the
script would have to use dpkg-divert --truename for creating the
link.
Once the bare links are created in this way, the -for-build
packages
would be updated to no longer depend on the bare packages and
instead
depend on the matching -for-host packages. For instance,
gcc-14-for-build would change its dependency on gcc-14 to
gcc-14-for-host. Since gcc-14-for-build is an Arch:all package, it
would require e.g. gcc-14-for-host:i386 when installed on i386. If
the dependencies of it are satisfied using
gcc-14-i686-linux-gnu:amd64, the maintainer script would create
the
bare tool links.
To make matters worse, the actual gfortran dependency of
libopenmpi-dev
is emitted from dh-fortran-mod. If we were to change dh-fortran-mod to
change fortran:Depends to emit gfortran-for-host right now, a lot of
packages would FTBFS as they'd expect gfortran and only find
<triplet>-gfortran.
So this little adventure of fftw3 turned out to yield quite
fundamental
insights (which really is why I am doing this). I now ask readers of
this mail to verify my analysis. Do you concur with the implications
here? Do you see any other options to meet the requirements? Do you
have
a preference for moving this forward?
Helmut