Subject: bug#53406: union-build incorrectly handles grafts

From: Ludovic Courtès <[email-masked]>
Date: Tue, 25 Jan 2022 14:47:41 +0100

Hi John,

John Kehayias <[email-masked]> skribis:

> Yes, that makes sense, thank you for clarifying. So this is the currently expected behavior. Ideally grafting would be smarter to maybe avoid this (missing changes in e.g. version number)? But I would guess this is not something that would be expected to cause a problem for the vast majority of cases, as you explain, and adds complexity to the process.

I don’t think it can be smarter; grafting is just text substitution, it
knows nothing about the files it’s fiddling with (with the exception of
debugging sections in ELF files).

> Perhaps I was too hasty in noting this "problem" which like I said was not the error I originally encountered. I was using a package that constructs both the 64- and 32-bit libraries to put in a container (say, a /lib32 and /lib64 or something similar to an FHS environment). A collision was happening between a file and directory, one being a good symlink and the other broken, rather than a "real" mismatch in file vs directory. Anyway, going back to that what I see is that one link is broken for the above reasons, but the good one is good because it is to the *ungrafted* library store path. I don't know now if these 2 things are connected other than one led me to the other, but I turn now to what demonstrates my original problem.
>
> I don't know why this happens or if it is something in this building process that is not correct, but I did come up with a minimal example (attached). The code is a bit odd in its stripped down form, though hopefully is clear in what way this would be used to do something useful (again, like an FHS environment or other container). Apologies for the old style and lack of gexps which I'm finally getting used to. The example package just tries to make a dummy package that has, for illustration, a "/lib64" and "/lib32" which link to the respective union-build inputs (of a single library for simplicity). I don't think the actual package being made matters so much, or how it is constructed, but that two inputs are union-builds of the same library (x86_64 and i686) which should have a graft of expat. Just my guess though.
>
> Doing:
>
> ls -la $(guix build -f graft-test.scm)/lib64/lib/libexpat*
> lrwxrwxrwx 1 root root 71 Dec 31  1969 /gnu/store/qbh16hfdv8pnfw01k6izbs3jkji6i978-test-pkg-0.0/lib64/lib/libexpat.la -> /gnu/store/2q8wwhd3prib0swky68rbx9hl0xxs6hf-expat-2.4.3/lib/libexpat.la*
> lrwxrwxrwx 1 root root 71 Dec 31  1969 /gnu/store/qbh16hfdv8pnfw01k6izbs3jkji6i978-test-pkg-0.0/lib64/lib/libexpat.so -> /gnu/store/2q8wwhd3prib0swky68rbx9hl0xxs6hf-expat-2.4.3/lib/libexpat.so*
> lrwxrwxrwx 1 root root 73 Dec 31  1969 /gnu/store/qbh16hfdv8pnfw01k6izbs3jkji6i978-test-pkg-0.0/lib64/lib/libexpat.so.1 -> /gnu/store/2q8wwhd3prib0swky68rbx9hl0xxs6hf-expat-2.4.3/lib/libexpat.so.1*
> lrwxrwxrwx 1 root root 77 Dec 31  1969 /gnu/store/qbh16hfdv8pnfw01k6izbs3jkji6i978-test-pkg-0.0/lib64/lib/libexpat.so.1.8.1 -> /gnu/store/2q8wwhd3prib0swky68rbx9hl0xxs6hf-expat-2.4.3/lib/libexpat.so.1.8.1
>
> is what we saw already: libexpat is the newer (replacement, 2.4.3) version, with the full version symlink broken since the version number is wrong. Likewise in other pieces that have the version number, like share/doc. Okay, that's expected. But now, in the i686-linux union-build input:
>
> ls -la $(guix build -f graft-test.scm)/lib32/lib/libexpat*
> lrwxrwxrwx 1 root root 71 Dec 31  1969 /gnu/store/qbh16hfdv8pnfw01k6izbs3jkji6i978-test-pkg-0.0/lib32/lib/libexpat.la -> /gnu/store/b0jns3vzhhpna7lim8bc3dr0payzx5yy-expat-2.4.1/lib/libexpat.la*
> lrwxrwxrwx 1 root root 71 Dec 31  1969 /gnu/store/qbh16hfdv8pnfw01k6izbs3jkji6i978-test-pkg-0.0/lib32/lib/libexpat.so -> /gnu/store/b0jns3vzhhpna7lim8bc3dr0payzx5yy-expat-2.4.1/lib/libexpat.so*
> lrwxrwxrwx 1 root root 73 Dec 31  1969 /gnu/store/qbh16hfdv8pnfw01k6izbs3jkji6i978-test-pkg-0.0/lib32/lib/libexpat.so.1 -> /gnu/store/b0jns3vzhhpna7lim8bc3dr0payzx5yy-expat-2.4.1/lib/libexpat.so.1*
> lrwxrwxrwx 1 root root 77 Dec 31  1969 /gnu/store/qbh16hfdv8pnfw01k6izbs3jkji6i978-test-pkg-0.0/lib32/lib/libexpat.so.1.8.1 -> /gnu/store/b0jns3vzhhpna7lim8bc3dr0payzx5yy-expat-2.4.1/lib/libexpat.so.1.8.1*
>
> all the links are good and to the original (version 2.4.1) expat. In other words, the constructed union64 and union32 inputs (in the sample code) do not both get grafted, even though doing just the fhs-union command on it's own (not building both for another package) does graft for either architecture. At least that seems like the most obvious difference between the earlier example and this new one.
>
> Why? Does the grafting just happen "once" somehow and misses the "same" input again (but built for different system)? Is this expected or just a weird/wrong way to do this kind of build which is causing this? I'm not sure if this is just with union-build or if it would happen just with inputs of the same library but different architectures. I didn't know how to do that quickly off hand, so I haven't tried it yet.

Woow, it’s a sophisticated example.  :-)

I don’t actually have the Expat replacement we’re talking about so I
can’t easily test.

At first sight I’d say it should work (both lib32 and lib64 should refer
to the Expat replacement), but it could be that something somewhere
assumes all the packages are built for the system.  That would need more
investigation work in (guix packages) in (guix grafts).

Thanks,
Ludo’.