On Thu, 11 Jan 2024 at 14:15:50 +0100, Helmut Grohne wrote:
> On Thu, Jan 11, 2024 at 12:08:53PM +0000, Simon McVittie wrote:
> > The ${GNU_TYPE}-g-ir-compiler wrapper script (which happens to be written
> > in Python, the same as the upstream g-ir-compiler) explicitly tells
> > g-ir-compiler to run the "dumper" binary under qemu-user if it detects
> > that the Python architecture is not one that can run the host architecture.
> 
> Do I understand correctly that cross building to i386 on amd64 would
> cause this wrapper to run the i386 binary in qemu?

Until today: no, but only because there was a hard-coded special case
for the common i386-on-amd64. Cross-building to armhf on arm64, or to
powerpc on ppc64, *would* have used qemu automatically, even if not
actually necessary.

In the version I hope to upload today: no, because I've added auto-detection
of whether we can execute host binaries as you suggested.

> I agree with the approach taken, but I think g-ir-compiler could be more
> clever. Rather than assume that the host architecture is not runnable
> when it differs from the build architecture, could it detect that? A
> simple way would be invoking arch-test ${DEB_HOST_ARCH}, but it can as
> well compile and run trivial program (as autoconf does all the time).

I'm testing an implementation of that. arch-test needs specific
porting for each new architecture because of how it's written,
so I'm not intending to use that directly, but it's easy to add a
precompiled arch-test-like binary of the host architecture to the
gobject-introspection binary package, and have the wrapper script try
to invoke it and see what happens.

> > I would tend to think that qemu dependencies in Build-Depends are
> > appropriate if and only if it's the source package that is making the
> > choice to invoke qemu.
> 
> The argument is reasonable. Your way of looking at it also lowers
> maintenance cost as we don't have to modify tons of B-D.

Right - if we decide that qemu is not so good for some pair of (real,
emulated) architectures, and actually we'd prefer to use some other
user-space emulator like FEX or box86 for a particular pair, I don't
want to have to make new sourceful changes in all 238 source packages[1]
that produce public GIR/typelibs, plus however many packages produce
private GIR/typelibs. It seems like it would be better to only change
src:gobject-introspection and O(1) other packages.

We will want to make sourceful changes in all of those 238 packages
eventually, to replace libgirepository1.0-dev (which cannot be M-A:same
without breaking some dependent packages) with a cross-friendly
alternative, but I only want to do that once per source package in
most cases.

For some of those packages (the ones where GIR is optional, like
src:flatpak, but not the ones where GIR is required functionality, like
src:gnome-shell) we will eventually also want to add support for <!nogir>,
and maybe split out gir1.2-NAMESPACE-VERSION-dev into its own binary
package so that the nogir build profile can be a reproducible/"safe"
one - but, again, that should be something we can do once per source
package, not something that we have to repeat every time an implementation
detail changes.

[1] grep-dctrl -FPackage-List -sPackage -e --pattern='gir1\.2-' \
    /var/lib/apt/lists/deb.debian.org_debian_dists_sid_main_source_Sources \
    | sort -u | wc -l

> I am wondering
> about a middle-ground of having a package can-run-arch being M-A:same
> and having a maintainer script that validates the property. Then you
> could Depends: qemu-user | can-run-arch  (expressing the preference for
> qemu-user) while any builder could still --add-depends=can-run-arch to
> opt out of qemu.

If the cross-toolchain team implements such a thing, it would be fine to
add it as an alternative dependency in a later version. I'd prefer not to
do that while it's still hypothetical, because until there's a concrete
implementation we'd have no way to test it.

Another option (which could perhaps be combined with this) would be for
the cross-toolchain team to define an interface to "the preferred way to
run executables from architecture A if they can't be run directly", and
then gobject-introspection could try that in preference to qemu. Meson
calls this an "EXE wrapper", which seems like as good a name as any other.

Here's a straw-man design, assuming for the sake of concrete examples that
the build architecture is amd64 and the host architecture is riscv64:

- gobject-introspection:riscv64 Depends: cross-exe-wrapper | can-run-arch

- cross-exe-wrapper:riscv64 is M-A:same and Depends on
  cross-exe-wrapper-riscv64-linux-gnu

- cross-exe-wrapper-TUPLE is M-A:foreign (or perhaps a virtual
  package provided by cross-exe-wrapper-bin, which is M-A:foreign)

- For the trivial case, cross-exe-wrapper-TUPLE:ARCH, where TUPLE and
  ARCH match, contains a /usr/bin/TUPLE-exe-wrapper which just runs its
  arguments as-is
  (like a trivial shell script that just does an 'exec "$@"')

- There could be simple special cases for some pairs, like
  cross-exe-wrapper-i686-linux-gnu:amd64 might contain a
  /usr/bin/i686-linux-gnu-exe-wrapper which invokes linux32

- in general cross-exe-wrapper-TUPLE:ARCH, where ARCH is supported by qemu
  and TUPLE and ARCH do not match, might contain a symlink
  /usr/bin/TUPLE-exe-wrapper -> qemu-ARCH and depend on qemu-user
  (or it might be a script that auto-detects whether a wrapper is needed
  or does something more complicated, that's an implementation detail)

- Document that the first argument to /usr/bin/TUPLE-exe-wrapper should
  be a TUPLE binary, normally by its absolute path; if it starts with
  "-" then the result is undefined; and it needs to pass all subsequent
  arguments to the binary
  (for some emulators this might mean it needs to invoke
  'exec /usr/bin/emulator -- "$@"')

For the gobject-introspection use-case it would be possible to skip the
cross-exe-wrapper package name and make g-i depend directly on
cross-exe-wrapper-${local:DEB-HOST-GNU-TYPE}, but I think we would need
the intermediate package name anyway if you want to be able to add
Build-Depends: cross-exe-wrapper <cross>, for use-cases where the upstream
build system will invoke TUPLE-exe-wrapper itself.

> I wonder though whether we should make such "do not depend" rules
> explicitly checkable somehow. There is more of that in the archive.

A Lintian check could make sense, perhaps? It could have a table of
package names that should not be directly (build-)depended on, each with
its allowed exceptions if any, for example:

gobject-introspection-%-endian src:gobject-introspection
gobject-introspection-bin src:gobject-introspection
libmutter% src:budgie-desktop src:gnome-remote-desktop src:gnome-shell 
src:mutter
liburweb0 src:urweb
lighttpd-modules-% src:lighttpd
perl-modules-% src:perl
python-dev-is-% src:what-is-python
python-is-% src:what-is-python
python3-minimal python3
python3.%-minimal python3.%

(mostly based on an aptitude search for; ~d"(do|should|must) not depend")

    smcv

Reply via email to