Skip to content
New issue

Have a question about this project? # for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “#”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? # to your account

sys-devel/sysroot-wrappers: Drop in favour of setting CPP/CC/CXX #2177

Closed
wants to merge 4 commits into from

Conversation

chewi
Copy link
Contributor

@chewi chewi commented Jul 30, 2024

Drop sysroot-wrappers in favour of setting CPP/CC/CXX

cross-boss has shown that setting CPP/CC/CXX is a highly effective alternative to using wrappers. This approach is not susceptible to the PATH or SYSROOT variables being lost. It works so well because Gentoo uses a similar approach for amd64 multilib and failure to respect these variables is treated as a serious QA violation.

On the downside, it can lead to the --sysroot argument leaking into certain files and subsequently being applied to builds started from the target environment. This can be worked around with a symlink, but it is tidier to strip such flags after the build. cross-boss does this with a pkg_preinst hook that carefully strips them from non-binary files. This hook has been adapted here, but only for the dev profile because we don't build the SDK with sysroot flags and we don't install build tools to the production image. It has been tested against binary packages.

There has been talk in Gentoo circles of achieving this using gcc specs instead, which would not suffer from the downside, but my initial investigation has found that you cannot simply add --sysroot this way, and it may not be viable at all.

This change allows us to use Gentoo's own dev-lang/rust package, which currently fails with the sysroot-wrappers. It requires some eclass changes for other Rust packages though. These have already been submitted upstream and I will apply them before merging this.

How to use

You can perform a full build and run the tests, but I have already done it.

Testing done

A two-phase Jenkins build was successful. This was before I realised the flag stripping is needed for the development image though, so I've added that and am now running it again.

  • Changelog entries added in the respective changelog/ directory (user-facing change, bug fix, security fix, update) -- N/A
  • Inspected CI output for image differences: /boot and /usr size, packages, list files for any missing binaries, kernel modules, config files, kernel modules, etc.

chewi added 4 commits July 30, 2024 14:11
Signed-off-by: James Le Cuirot <jlecuirot@microsoft.com>
Signed-off-by: James Le Cuirot <jlecuirot@microsoft.com>
Signed-off-by: James Le Cuirot <jlecuirot@microsoft.com>
cross-boss has shown that setting CPP/CC/CXX is a highly effective
alternative to using wrappers. This approach is not susceptible to the
PATH or SYSROOT variables being lost. It works so well because Gentoo
uses a similar approach for amd64 multilib and failure to respect these
variables is treated as a serious QA violation.

On the downside, it can lead to the --sysroot argument leaking into
certain files and subsequently being applied to builds started from the
target environment. This can be worked around with a symlink, but it is
tidier to strip such flags after the build. cross-boss does this with a
pkg_preinst hook that carefully strips them from non-binary files. This
hook has been adapted here, but only for the dev profile because we
don't build the SDK with sysroot flags and we don't install build tools
to the production image. It has been tested against binary packages.

There has been talk in Gentoo circles of achieving this using gcc specs
instead, which would not suffer from the downside, but my initial
investigation has found that you cannot simply add --sysroot this way,
and it may not be viable at all.

Signed-off-by: James Le Cuirot <jlecuirot@microsoft.com>
@chewi chewi requested a review from a team July 30, 2024 14:07
@chewi chewi self-assigned this Jul 30, 2024
@tormath1 tormath1 added the main label Jul 30, 2024
Copy link
Member

@krnowak krnowak left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Looks ok I guess. That hook for stripping sysroot flags is hacky, so we are trading one hack for another, no?

local FILES FILE

# Gather all the non-binary files with the --sysroot or --with-sysroot flag.
mapfile -t -d '' < <(find "${D}" -type f -exec grep -lZEIe "--sysroot=${ROOT}\b|--with-sysroot=${EROOT}\b" {} +) FILES
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm uneasy seeing some variables passed to grep as a regexp. I hope we won't see some weird paths in ${ROOT} or ${EROOT}. Maybe we could escape those paths somehow? Ideally, we could pass -F instead of -E to grep, but then \b won't work…

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm also curious if there are files that quote (either with single quotes or double quotes) the value of the flag (--sysroot="/quoted/path").

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'll see whether I can use fixed strings, although these values are only ever going to be things like /build/arm64-usr.

We control the --sysroot flag, so it will never have quotes. Similarly, the --with-sysroot flag comes from Portage itself.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think you could use fixed strings. There is a "risk" that this would pick up more files (with --sysroot=/build/amd64-usr/foo out of the blue), but your sed invocation should be careful enough to ignore the non-matching lines.

@chewi
Copy link
Contributor Author

chewi commented Jul 31, 2024

Looks ok I guess. That hook for stripping sysroot flags is hacky, so we are trading one hack for another, no?

That's fair. I'd forgotten about this stripping when I initially proposed the idea. Then I figured it didn't apply to Flatcar. Then I realised that it did, but only to the dev image, which isn't so bad. I looked further into the specs solution at that point, but no luck there. For what it's worth, I've never seen any fall out from this. Very few text files have these flags baked in. The Python "sysconfigdata" file is one example. If you'd prefer to have a /build/amd64-usr -> / symlink in the dev image, then I can certainly do that instead.

Copy link

github-actions bot commented Jul 31, 2024

# Carefully strip the sysroot flags.
local sedargs=( -i -r )
local flag
for flag in --{,with-}sysroot="${EROOT}"; do
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should this be the following instead? To match the flags you check with grep?

Suggested change
for flag in --{,with-}sysroot="${EROOT}"; do
for flag in "--sysroot=${ROOT}" "--with-sysroot=${EROOT}"; do

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think ROOT and EROOT are basically the same thing in our case, because we don't use eprefix, but still.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Well spotted. This line is different in cross-boss, so it isn't an issue there. It's not really an issue for Flatcar either because we don't use prefix here, but still!

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I thought about this some more and realised both should be ESYSROOT to match what we set the flag too. I guess I just forgot to update this in cross-boss way back. It's academic most of the time, but it does make a difference in this case.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It's funny how Flatcar is forcing me to rethink things I've been doing fine for years. I'd forgotten that I didn't use ESYSROOT here because it's not defined in the pkg phases. I'm not sure whether the dev image is prepared in the same ROOT location as the binary packages it's built from, but I'd rather assume it's not. That means I need to change this hook from pre_pkg_preinst to post_src_install, and that means it needs to apply to the generic profile, not just the dev profile. While I'm happy to make this change for cross-boss, I'm now leaning towards just making a symlink for Flatcar. What do you think?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes, the dev container image. I know they are both built in a ROOT, but I'm not sure whether that location is exactly the same for both images. Probably not. If not, then ESYSROOT will differ from EROOT, and the stripping therefore won't work if only applied when installing the binary packages.

The symlink wouldn't be in the binary packages, so it wouldn't appear in the production image. It would just be added to the dev container image before finalising it.

I think setting these variables and adding a single symlink only to the dev container image is probably still cleaner on balance, but I admit that I have been wavering here. I would like it to be cleaner, not just for Flatcar, but for cross-boss as well, so this has inspired me to look further into alternatives.

The specs-based solution isn't viable without changes to gcc, which might be hard sell to upstream, so I came up with a much simpler gcc patch (about 5 new lines in one place) that doesn't use specs at all. With that, things literally just work with no special flags or other adjustments needed. It's not like Gentoo doesn't patch gcc already. I'm running it past the Gentoo toolchain folks, and I'm currently testing out a full @system build with cross-boss.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes, the dev container image. I know they are both built in a ROOT, but I'm not sure whether that location is exactly the same for both images. Probably not.

It happens to be the same ROOT for both, and they are built sequentially but we shouldn't depend on that.

If not, then ESYSROOT will differ from EROOT, and the stripping therefore won't work if only applied when installing the binary packages.

I don't follow this conclusion. At a given time ESYSROOT should be equal to EROOT, from man emerge:

SYSROOT = [path]
    (...) The value must either be / or equal to ROOT. When cross-compiling, only the latter is valid.

Unless you mean that the value of ROOT will be different between binpkg generation and installation.

The symlink wouldn't be in the binary packages, so it wouldn't appear in the production image. It would just be added to the dev container image before finalising it.

Oh, sorry I didn't clarify. To me the dev container image is a production artifact that is shipped to users.

The specs-based solution isn't viable without changes to gcc, which might be hard sell to upstream, so I came up with a much simpler gcc patch (about 5 new lines in one place) that doesn't use specs at all.

What does the gcc patch look like?

Copy link
Contributor Author

@chewi chewi Aug 2, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't follow this conclusion. At a given time ESYSROOT should be equal to EROOT, from man emerge:

I wrote that line. 😀 It actually doesn't work for three reasons. As you say, ROOT can change between building and installation. Also, Portage doesn't set ESYSROOT during the pkg phases, and it doesn't preserve its value in binary packages.

Here's the gcc patch. I'll probably add something to the man page too.

diff -Naur a/gcc/gcc.cc b/gcc/gcc.cc
--- a/gcc/gcc.cc	2024-08-01 23:34:33.525082176 +0100
+++ b/gcc/gcc.cc	2024-08-01 23:43:31.557156041 +0100
@@ -5527,6 +5527,16 @@
 	      "BINUTILS", PREFIX_PRIORITY_LAST, 0, 1);
   free (tooldir_prefix);

+  if (*cross_compile == '1' && !target_system_root_changed)
+    {
+      const char *esysroot = env.get("ESYSROOT");
+      if (esysroot && esysroot[0] != '\0' && strcmp(esysroot, "/") != 0 && (!target_system_root || strcmp(esysroot, target_system_root) != 0))
+	{
+	  target_system_root = esysroot;
+	  target_system_root_changed = 1;
+	}
+    }
+
 #if defined(TARGET_SYSTEM_ROOT_RELOCATABLE) && !defined(VMS)
   /* If the normal TARGET_SYSTEM_ROOT is inside of $exec_prefix,
      then consider it to relocate with the rest of the GCC installation

My @system build successfully completed. There were no traces of the sysroot flag.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

i see, that's basically the sysroot-wrappers approach but baked into gcc

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It's got the initial thumbs up from Sam at Gentoo. I'm now doing a two-phase Jenkins build with it to really put it through its paces. If that works, I'll press ahead and open a new PR to supersede this one.

@krnowak
Copy link
Member

krnowak commented Jul 31, 2024

Looks ok I guess. That hook for stripping sysroot flags is hacky, so we are trading one hack for another, no?

That's fair. I'd forgotten about this stripping when I initially proposed the idea. Then I figured it didn't apply to Flatcar. Then I realised that it did, but only to the dev image, which isn't so bad. I looked further into the specs solution at that point, but no luck there. For what it's worth, I've never seen any fall out from this. Very few text files have these flags baked in. The Python "sysconfigdata" file is one example. If you'd prefer to have a /build/amd64-usr -> / symlink in the dev image, then I can certainly do that instead.

Nah, I'm fine with what you did here. I have no clue about Python sysconfigdata file, so no idea if the baked-in sysroot there is harmless or not.

@chewi
Copy link
Contributor Author

chewi commented Aug 8, 2024

Closing in favour of #2207.

@chewi chewi closed this Aug 8, 2024
@chewi chewi deleted the chewi/no-sysroot-wrappers branch August 8, 2024 10:23
# for free to join this conversation on GitHub. Already have an account? # to comment
Labels
Projects
None yet
Development

Successfully merging this pull request may close these issues.

4 participants