Bug#910664: Acknowledgement (ghc: ghc package can no longer be cross-compiled)

Helmut Grohne helmut at subdivi.de
Sat Dec 16 18:34:53 GMT 2023


Hi Ilias,

On Sat, Dec 16, 2023 at 04:45:00PM +0200, Ilias Tsitsimpis wrote:
> Sorry for the late reply. Comments inline.

Thank you for taking the time to write such a detailed response.

> Let me try to explain how GHC's build system works with the new Hadrian
> tool. Then let's see how we can move forward and make it so that the
> build fails at the right stage.
> 
> 1. Run boot and configure. Here is where we decide if we want to
>    build GHC for the same architecture or do cross compilation.

I fear this is misleadingly imprecise. If I understand what you write
later correctly, we always build GHC for the same architecture (i.e. we
ask configure for build=host), but we may opt for a cross compiler (i.e.
target!=host). When the ghc Debian package is asked to cross compile,
what we ask configure instead is natively building a cross compiler. Do
you confirm?

> 2. Run hadrian. The build artifacts will be under '_build/stageX'. With
>    the Hadrian build system, the naming convention for these directories
>    have changed. '_build/stage1' contains the binaries that were built
>    using the stage1 compiler.

This is still quite confusing to me. I suppose stage0 is /usr/bin/ghc.
Do you confirm? Then there should be a _build/stage0 containing the
stage1 compiler. That stage1 compiler probably is a simple native build
of the ghc sources at hand, which means that its binary may have a
different ABI from what the sources expect. That stage1 will be unable
to use packages, but it can be used to build ghc again. Is that also
correct? Then that stage1 is used to build a stage2 where the ABI of the
binary matches the behaviour and this can be used with packages.

>    If we are building a compiler for the same architecture,
>    '_build/stage1/' will contain the stage2 compiler for this
>    architecture (i.e., the binaries that we ship in our Debian package).

This would be consistent with the possible understanding given above.

>    If we are cross compiling, '_build/stage1/' will contain a *stage1
>    cross-compiler*. As discussed in this issue [1], upstream is working
>    towards making this a *stage2 cross-compiler*. Is is still a
>    cross-compiler though, and not a cross-compiled GHC as it used to be.

Do I understand correctly that (currently) stage1 is a simple native
build of the current ghc sources where the binary uses the ABI that
/usr/bin/ghc generated and doesn't necessarily match the sources? Do I
also understand that stage2 (stored in _build/stage1) the is a cross
compiler generating code for $DEB_HOST_ARCH and runnable on
$DEB_BUILD_ARCH using the ABI given by the current ghc sources?

>    The final output of this step is a binary distribution of GHC, i.e.,
>    a tarball that you can distribute to anyone that wants to install GHC.
> 
> 3. We run configure and 'make install' to convert the binary
>    distribution to a Debian binary package. This is why we run configure
>    twice.

At no point do we (currently) actually cross build using that stage2. Do
you also confirm?

> [1] https://gitlab.haskell.org/ghc/ghc/-/issues/19174
> 
> I hope the above explains why we cannot cross compile GHC (the build
> system doesn't give us this option).

I fear it does not. It explains why we currently do not cross compile
GHC, but I still fail to understand why we cannot. (Or maybe this is
splitting hairs and we mean the same thing with different words.)

> Given the above, I believe your patch needs a few adjustments.

Probably.

> > diff --minimal -Nru ghc-9.4.7/debian/rules ghc-9.4.7/debian/rules
> > --- ghc-9.4.7/debian/rules	2023-10-18 21:49:38.000000000 +0200
> > +++ ghc-9.4.7/debian/rules	2023-12-10 20:03:52.000000000 +0100
> > @@ -47,8 +47,10 @@
> >    EXTRA_HADRIAN_FLAGS += --flavour=quickest
> >    # Do not build docs *at all* (avoid dependency on Sphinx)
> >    EXTRA_HADRIAN_FLAGS += --docs=none
> > +  STAGE1_TOOL = _build/stage1/bin/$(DEB_HOST_GNU_TYPE)-
> >    BUILD_CROSS = YES
> >  else
> > +  STAGE1_TOOL = _build/stage1/bin/
> >    BUILD_CROSS = NO
> >  endif
> 
> As explained, this is not the stage1 tool, this is the tool built by the
> stage1 compiler, and this directory contains vastly different tools
> depending on whether we cross-compile or not.

As far as I can see, the name "STAGE1_TOOL" is misleading and it should
be called "STAGE2_TOOL" instead. Do you concur? Then a STAGE2_TOOL
always is something that runs on the build machine and operates on
$DEB_HOST_ARCH, which seems just about right, no? STAGE2_TOOL not
necessarily is something we'd want to install into a .deb though. Do you
agree?

> > @@ -162,7 +164,7 @@
> >  	# correct), but we use ghc-pkg from stage2 when we generate our control file.
> >  	# Maybe we should consider using ghc-pkg from binary-dist-dir instead.
> >  	# As a work-around for now, regenerate the stage2 package cache.
> > -	_build/stage1/bin/ghc-pkg recache
> > +	$(STAGE1_TOOL)ghc-pkg recache
> 
> In case we are cross-compiling, this is the cross-compiler ghc-pkg.

This is how I expected it, yes.

> Calling 'ghc-pkg recache' here is wrong. I suppose we can skip this step
> if we are cross-compiling (so we can reach the next failure).

Can you elaborate on why we do not want to reset the package cache here?

> >  # --------------------------------------------------------------------
> > @@ -335,23 +337,23 @@
> >  	rm -rf debian/testghc
> >  	mkdir debian/testghc
> >  	echo 'main = putStrLn "Foo"' > debian/testghc/foo.hs
> > -	_build/stage1/bin/ghc debian/testghc/foo.hs -o debian/testghc/foo
> > +	$(STAGE1_TOOL)ghc debian/testghc/foo.hs -o debian/testghc/foo
> >  	[ "$$(debian/testghc/foo)" = "Foo" ]
> >  	rm debian/testghc/*
> >  	echo 'main = putStrLn "Foo"' > debian/testghc/foo.hs
> > -	_build/stage1/bin/ghc debian/testghc/foo.hs -o debian/testghc/foo -O2
> > +	$(STAGE1_TOOL)ghc debian/testghc/foo.hs -o debian/testghc/foo -O2
> >  	[ "$$(debian/testghc/foo)" = "Foo" ]
> >  	rm debian/testghc/*
> >  	# Test runghc
> >  	echo 'main = putStrLn "Foo"' > debian/testghc/foo.hs
> > -	[ "$$(_build/stage1/bin/runghc debian/testghc/foo.hs)" = "Foo" ]
> > +	[ "$$($(STAGE1_TOOL)runghc debian/testghc/foo.hs)" = "Foo" ]
> >  	rm debian/testghc/*
> >  	# Output information about GHC
> >  	@printf "====BEGIN GHC INFO OUTPUT====\n"
> > -	_build/stage1/bin/ghc --info
> > +	$(STAGE1_TOOL)ghc --info
> >  	@printf "====END GHC INFO OUTPUT====\n"
> >  	@printf "====BEGIN GHC-PKG OUTPUT====\n"
> > -	_build/stage1/bin/ghc-pkg list
> > +	$(STAGE1_TOOL)ghc-pkg list
> >  	@printf "====END GHC-PKG OUTPUT====\n"
> >  endif
> 
> Running the tests when cross-compiling will not work. Either we are
> calling the cross-compiler (which will produce binaries we cannot test)
> or we are calling a cross-compiled GHC which will not work. I propose we
> skip the tests as well, and let the build fail elsewhere.

Most of the time, this is true. However, we can also cross build from
amd64 to i386 or arm64 to armhf (which gets us 64bit address space
during build) and we can build with a qemu-user-static installed. In
both cases, we can actually run tests. Therefore the decision whether to
run tests is left to the builder. Both sbuild and pbuilder default to
not running tests by automatically adding nocheck to DEB_BUILD_OPTIONS
when you ask for a cross build. This whole block is conditional to
DEB_BUILD_OPTIONS not containing nocheck, so the only way this is
relevant is when a builder overrides this default and thus explicitly
requests running tests despite performing a cross build. And in that
case, the proposed patch should make sense, no? Your proposed skipping
already is implemented via nocheck.

> I don't object in removing the error if this will help us move forward.

Thank you.

> I can also apply your patch with the above changes (i.e., skip some
> steps, instead of calling the cross-compiler). But I really believe we
> should focus on having end-to-end instructions on how to cross-compile
> GHC and then try to fix Debian's build rules. Fixing errors as we see
> them doesn't always move us towards the right direction, if we don't
> understand how the end-to-end process works.

Thank you for considering "my way". You have made a good case for
understanding the end-to-end process and you definitely convinced me
that this is necessary. Often times, the incremental process just works
and here it likely is not ideal, but the discussion still seems to
advance us and I would be more than happy to continue and improve our
understanding to reach that state where we make that end-to-end process
work practically. Hope you can bear with me.

Please take your time to respond even if that happens to be next year.
This is not something we have to fix right now. I prefer a good and
maintainable solution over a quick solution.

Helmut



More information about the Pkg-haskell-maintainers mailing list