public inbox for gentoo-dev@lists.gentoo.org
 help / color / mirror / Atom feed
* [gentoo-dev] multilib: CBUILD, CHOST, and toolchain vars for non-default ABIs
@ 2025-09-07 18:43 Matt Whitlock
  2025-09-07 22:26 ` James Le Cuirot
  0 siblings, 1 reply; 3+ messages in thread
From: Matt Whitlock @ 2025-09-07 18:43 UTC (permalink / raw
  To: gentoo-dev

There has been a substantive discussion unfolding at https://github.com/gentoo/gentoo/pull/43693#pullrequestreview-3194144355 that has evolved into a more general discussion that Eli suggested should be moved to this mailing list. Allow me to summarize the issues and findings thus far...

The current state of affairs is as follows:

* multilib_toolchain_setup() in multilib.eclass, when switching to a non-default ABI, sets the CHOST toolchain variables to the values returned by the corresponding tc-get_*() functions in toolchain-funcs.eclass, except that it appends $(get_abi_*FLAGS) where appropriate to switch various tools to the non-default ABI. Then it overrides CHOST to the value of CHOST_${ABI}. For example, on an amd64 system, when switching to the x86_32 ABI, CHOST becomes "i686-pc-linux-gnu". Additionally, in the non-cross-compiling case, multilib_toolchain_setup() overrides CBUILD to that same value (so that ${CHOST} == ${CBUILD} is still true).

* setup_meson_src_configure() in meson.eclass generates a Meson "native file" that specifies toolchain tools and flags for the CBUILD machine. In the case of cross-compiling (i.e., ${CHOST} != ${CBUILD}), it also generates a Meson "cross file" that specifies toolchain tools and flags for the CHOST machine.

Although the above may seem reasonable at first glance, there are some issues:

* In the case that a build-time helper executable links with a library that is listed in BDEPEND, the link will fail in a non-default ABI phase if the library has not been installed for the non-default ABI. There is no way to require libraries in BDEPEND to be installed for non-default ABIs, as using ${MULTILIB_USEDEP} in BDEPEND would break cross-compiles since the ABI flags listed in MULTILIB_USEDEP are for the CHOST machine, not the CBUILD machine. The reasonable answer is that build-time helper executables should only ever be built for the default ABI.

--- a/eclass/multilib.eclass
+++ b/eclass/multilib.eclass
@@ -526,8 +526,12 @@ multilib_toolchain_setup() {
 		export _DEFAULT_ABI_SAVED="true"

 		# Set CBUILD only if not cross-compiling.
+		#
+		# It must use the default ABI since build-time helper
+		# executables might link with libraries that are installed
+		# only for the default ABI.
 		if [[ ${CBUILD} == "${CHOST}" ]]; then
-			export CBUILD=$(get_abi_CHOST $1)
+			export CBUILD=$(get_abi_CHOST ${DEFAULT_ABI})
 		fi

* The above won't work out of the box, as now tc-is-cross-compiler() will return true in non-default ABI phases (since ${CHOST} will no longer match ${CBUILD}), and thus the tc-getBUILD_*() functions will no longer fall back on the CHOST toolchain variables in the case that the CBUILD toolchain variables are unset. We wouldn't want that anyway, as the CHOST toolchain is being altered with a non-default ABI flag, but we need the CBUILD toolchain to use the default ABI. So, let's have multilib_toolchain_setup() set up the CBUILD toolchain variables as well:

--- a/eclass/multilib.eclass
+++ b/eclass/multilib.eclass
@@ -487,6 +487,17 @@ multilib_toolchain_setup() {
 	local save_restore_variables=(
 		CBUILD
 		CHOST
+		BUILD_AR
+		BUILD_CC
+		BUILD_CXX
+		BUILD_LD
+		BUILD_NM
+		BUILD_OBJCOPY
+		BUILD_PKG_CONFIG
+		BUILD_RANLIB
+		BUILD_READELF
+		BUILD_STRINGS
+		BUILD_STRIP
 		AR
 		CC
 		CXX
@@ -536,6 +551,20 @@ multilib_toolchain_setup() {
 		# Make sure ${save_restore_variables[@]} list matches below.
 		export CHOST=$(get_abi_CHOST ${DEFAULT_ABI})

+		# Derive the build-machine toolchain variables before we
+		# override the host-machine toolchain variables.
+		export BUILD_AR="$(tc-getBUILD_AR)" # Avoid 'ar', use "${CBUILD}-ar"
+		export BUILD_CC="$(tc-getBUILD_CC)" # Default ABI
+		export BUILD_CXX="$(tc-getBUILD_CXX)" # Default ABI
+		export BUILD_LD="$(tc-getBUILD_LD)" # Default ABI
+		export BUILD_NM="$(tc-getBUILD_NM)" # Avoid 'nm', use "${CBUILD}-nm"
+		export BUILD_OBJCOPY="$(tc-getBUILD_OBJCOPY)" # Avoid 'objcopy', use "${CBUILD}-objcopy"
+		export BUILD_PKG_CONFIG="$(tc-getBUILD_PKG_CONFIG)"
+		export BUILD_RANLIB="$(tc-getBUILD_RANLIB)" # Avoid 'ranlib', use "${CBUILD}-ranlib"
+		export BUILD_READELF="$(tc-getBUILD_READELF)" # Avoid 'readelf', use "${CBUILD}-readelf"
+		export BUILD_STRINGS="$(tc-getBUILD_STRINGS)" # Avoid 'strings', use "${CBUILD}-strings"
+		export BUILD_STRIP="$(tc-getBUILD_STRIP)" # Avoid 'strip', use "${CBUILD}-strip"
+
 		export AR="$(tc-getAR)" # Avoid 'ar', use '${CHOST}-ar'
 		export CC="$(tc-getCC) $(get_abi_CFLAGS)"
 		export CXX="$(tc-getCXX) $(get_abi_CFLAGS)"

* This works great for Autotools and CMake build systems, but it may subtly break packages using meson.eclass because now all non-default-ABI builds are configured in Meson's cross mode with "needs_exe_wrapper = true" specified in the cross file. Normally this is reasonable, as in the true cross-compiling case we cannot expect host-machine executables to run on the build machine without some help, but it is overly pessimistic in the non-cross, non-default-ABI case. We would prefer not to disable build-time feature tests that rely on being able to execute just-built binaries on the build machine. One solution would be to set "needs_exe_wrapper = false" in the case that ${CBUILD} == ${CHOST_${DEFAULT_ABI}}.

--- a/eclass/meson.eclass
+++ b/eclass/meson.eclass
@@ -147,6 +147,7 @@
 _meson_create_cross_file() {
 	local system cpu_family cpu
 	_meson_get_machine_info "${CHOST}"
+	local -n CHOST_default="CHOST_${DEFAULT_ABI}"

 	local fn=${T}/meson.${CHOST}.${ABI}.ini

@@ -181,7 +182,7 @@ _meson_create_cross_file() {
 	objcpp_link_args = $(_meson_env_array "${OBJCXXFLAGS} ${LDFLAGS}")

 	[properties]
-	needs_exe_wrapper = true
+	needs_exe_wrapper = $([[ "${CBUILD:-${CHOST}}" != "${CHOST_default}" ]] && echo true || echo false)
 	sys_root = '${SYSROOT}'
 	pkg_config_libdir = '${PKG_CONFIG_LIBDIR:-${EPREFIX}/usr/$(get_libdir)/pkgconfig}'

* There is one other loose end to tie up in meson.eclass: the native file is named "meson.${CBUILD}.${ABI}.ini", but this actually makes no sense, as ${ABI} belongs to the CHOST machine, not the CBUILD machine. It would make the most sense simply to omit the ABI from the native file name since there will only ever be one native ABI.

@@ -205,7 +206,7 @@ _meson_create_native_file() {
 	local system cpu_family cpu
 	_meson_get_machine_info "${CBUILD}"

-	local fn=${T}/meson.${CBUILD}.${ABI}.ini
+	local fn=${T}/meson.${CBUILD}.ini

 	cat > "${fn}" <<-EOF
 	[binaries]

A Pull Request containing all of these proposed changes is at: https://github.com/gentoo/gentoo/pull/43693


^ permalink raw reply	[flat|nested] 3+ messages in thread

* Re: [gentoo-dev] multilib: CBUILD, CHOST, and toolchain vars for non-default ABIs
  2025-09-07 18:43 [gentoo-dev] multilib: CBUILD, CHOST, and toolchain vars for non-default ABIs Matt Whitlock
@ 2025-09-07 22:26 ` James Le Cuirot
  2025-09-08  0:10   ` Matt Whitlock
  0 siblings, 1 reply; 3+ messages in thread
From: James Le Cuirot @ 2025-09-07 22:26 UTC (permalink / raw
  To: gentoo-dev

[-- Attachment #1: Type: text/plain, Size: 7426 bytes --]

On Sun, 2025-09-07 at 14:43 -0400, Matt Whitlock wrote:
> There has been a substantive discussion unfolding at https://github.com/gentoo/gentoo/pull/43693#pullrequestreview-3194144355 that has evolved into a more general discussion that Eli suggested should be moved to this mailing list. Allow me to summarize the issues and findings thus far...
> 
> The current state of affairs is as follows:
> 
> * multilib_toolchain_setup() in multilib.eclass, when switching to a non-default ABI, sets the CHOST toolchain variables to the values returned by the corresponding tc-get_*() functions in toolchain-funcs.eclass, except that it appends $(get_abi_*FLAGS) where appropriate to switch various tools to the non-default ABI. Then it overrides CHOST to the value of CHOST_${ABI}. For example, on an amd64 system, when switching to the x86_32 ABI, CHOST becomes "i686-pc-linux-gnu". Additionally, in the non-cross-compiling case, multilib_toolchain_setup() overrides CBUILD to that same value (so that ${CHOST} == ${CBUILD} is still true).

Agreed.

> * setup_meson_src_configure() in meson.eclass generates a Meson "native file" that specifies toolchain tools and flags for the CBUILD machine. In the case of cross-compiling (i.e., ${CHOST} != ${CBUILD}), it also generates a Meson "cross file" that specifies toolchain tools and flags for the CHOST machine.

Agreed.

> Although the above may seem reasonable at first glance, there are some issues:
> 
> * In the case that a build-time helper executable links with a library that is listed in BDEPEND, the link will fail in a non-default ABI phase if the library has not been installed for the non-default ABI. There is no way to require libraries in BDEPEND to be installed for non-default ABIs, as using ${MULTILIB_USEDEP} in BDEPEND would break cross-compiles since the ABI flags listed in MULTILIB_USEDEP are for the CHOST machine, not the CBUILD machine. The reasonable answer is that build-time helper executables should only ever be built for the default ABI.

I didn't see what you meant earlier, but I do now. I must admit that this is
not something I had considered before. I agree that is the right answer.

> --- a/eclass/multilib.eclass
> +++ b/eclass/multilib.eclass
> @@ -526,8 +526,12 @@ multilib_toolchain_setup() {
>  		export _DEFAULT_ABI_SAVED="true"
> 
>  		# Set CBUILD only if not cross-compiling.
> +		#
> +		# It must use the default ABI since build-time helper
> +		# executables might link with libraries that are installed
> +		# only for the default ABI.
>  		if [[ ${CBUILD} == "${CHOST}" ]]; then
> -			export CBUILD=$(get_abi_CHOST $1)
> +			export CBUILD=$(get_abi_CHOST ${DEFAULT_ABI})
>  		fi
> 
> * The above won't work out of the box, as now tc-is-cross-compiler() will return true in non-default ABI phases (since ${CHOST} will no longer match ${CBUILD}), and thus the tc-getBUILD_*() functions will no longer fall back on the CHOST toolchain variables in the case that the CBUILD toolchain variables are unset. We wouldn't want that anyway, as the CHOST toolchain is being altered with a non-default ABI flag, but we need the CBUILD toolchain to use the default ABI. So, let's have multilib_toolchain_setup() set up the CBUILD toolchain variables as well:
> 
> --- a/eclass/multilib.eclass
> +++ b/eclass/multilib.eclass
> @@ -487,6 +487,17 @@ multilib_toolchain_setup() {
>  	local save_restore_variables=(
>  		CBUILD
>  		CHOST
> +		BUILD_AR
> +		BUILD_CC
> +		BUILD_CXX
> +		BUILD_LD
> +		BUILD_NM
> +		BUILD_OBJCOPY
> +		BUILD_PKG_CONFIG
> +		BUILD_RANLIB
> +		BUILD_READELF
> +		BUILD_STRINGS
> +		BUILD_STRIP
>  		AR
>  		CC
>  		CXX
> @@ -536,6 +551,20 @@ multilib_toolchain_setup() {
>  		# Make sure ${save_restore_variables[@]} list matches below.
>  		export CHOST=$(get_abi_CHOST ${DEFAULT_ABI})
> 
> +		# Derive the build-machine toolchain variables before we
> +		# override the host-machine toolchain variables.
> +		export BUILD_AR="$(tc-getBUILD_AR)" # Avoid 'ar', use "${CBUILD}-ar"
> +		export BUILD_CC="$(tc-getBUILD_CC)" # Default ABI
> +		export BUILD_CXX="$(tc-getBUILD_CXX)" # Default ABI
> +		export BUILD_LD="$(tc-getBUILD_LD)" # Default ABI
> +		export BUILD_NM="$(tc-getBUILD_NM)" # Avoid 'nm', use "${CBUILD}-nm"
> +		export BUILD_OBJCOPY="$(tc-getBUILD_OBJCOPY)" # Avoid 'objcopy', use "${CBUILD}-objcopy"
> +		export BUILD_PKG_CONFIG="$(tc-getBUILD_PKG_CONFIG)"
> +		export BUILD_RANLIB="$(tc-getBUILD_RANLIB)" # Avoid 'ranlib', use "${CBUILD}-ranlib"
> +		export BUILD_READELF="$(tc-getBUILD_READELF)" # Avoid 'readelf', use "${CBUILD}-readelf"
> +		export BUILD_STRINGS="$(tc-getBUILD_STRINGS)" # Avoid 'strings', use "${CBUILD}-strings"
> +		export BUILD_STRIP="$(tc-getBUILD_STRIP)" # Avoid 'strip', use "${CBUILD}-strip"
> +
>  		export AR="$(tc-getAR)" # Avoid 'ar', use '${CHOST}-ar'
>  		export CC="$(tc-getCC) $(get_abi_CFLAGS)"
>  		export CXX="$(tc-getCXX) $(get_abi_CFLAGS)"
> 
> * This works great for Autotools and CMake build systems, but it may subtly break packages using meson.eclass because now all non-default-ABI builds are configured in Meson's cross mode with "needs_exe_wrapper = true" specified in the cross file. Normally this is reasonable, as in the true cross-compiling case we cannot expect host-machine executables to run on the build machine without some help, but it is overly pessimistic in the non-cross, non-default-ABI case. We would prefer not to disable build-time feature tests that rely on being able to execute just-built binaries on the build machine. One solution would be to set "needs_exe_wrapper = false" in the case that ${CBUILD} == ${CHOST_${DEFAULT_ABI}}.
> 
> --- a/eclass/meson.eclass
> +++ b/eclass/meson.eclass
> @@ -147,6 +147,7 @@
>  _meson_create_cross_file() {
>  	local system cpu_family cpu
>  	_meson_get_machine_info "${CHOST}"
> +	local -n CHOST_default="CHOST_${DEFAULT_ABI}"
> 
>  	local fn=${T}/meson.${CHOST}.${ABI}.ini
> 
> @@ -181,7 +182,7 @@ _meson_create_cross_file() {
>  	objcpp_link_args = $(_meson_env_array "${OBJCXXFLAGS} ${LDFLAGS}")
> 
>  	[properties]
> -	needs_exe_wrapper = true
> +	needs_exe_wrapper = $([[ "${CBUILD:-${CHOST}}" != "${CHOST_default}" ]] && echo true || echo false)
>  	sys_root = '${SYSROOT}'
>  	pkg_config_libdir = '${PKG_CONFIG_LIBDIR:-${EPREFIX}/usr/$(get_libdir)/pkgconfig}'
> 
> * There is one other loose end to tie up in meson.eclass: the native file is named "meson.${CBUILD}.${ABI}.ini", but this actually makes no sense, as ${ABI} belongs to the CHOST machine, not the CBUILD machine. It would make the most sense simply to omit the ABI from the native file name since there will only ever be one native ABI.
> 
> @@ -205,7 +206,7 @@ _meson_create_native_file() {
>  	local system cpu_family cpu
>  	_meson_get_machine_info "${CBUILD}"
> 
> -	local fn=${T}/meson.${CBUILD}.${ABI}.ini
> +	local fn=${T}/meson.${CBUILD}.ini
> 
>  	cat > "${fn}" <<-EOF
>  	[binaries]
> 
> A Pull Request containing all of these proposed changes is at: https://github.com/gentoo/gentoo/pull/43693

My head's a little fuzzy right now, but I think this makes sense. My only
concern is the precise CHOST can vary in the field, so the profile default
might not match. A better way would be for multilib.eclass to preserve the
original CHOST value in another variable before changing it. BUILD_CHOST? XD

[-- Attachment #2: This is a digitally signed message part --]
[-- Type: application/pgp-signature, Size: 858 bytes --]

^ permalink raw reply	[flat|nested] 3+ messages in thread

* Re: [gentoo-dev] multilib: CBUILD, CHOST, and toolchain vars for non-default ABIs
  2025-09-07 22:26 ` James Le Cuirot
@ 2025-09-08  0:10   ` Matt Whitlock
  0 siblings, 0 replies; 3+ messages in thread
From: Matt Whitlock @ 2025-09-08  0:10 UTC (permalink / raw
  To: gentoo-dev

On Sunday, 7 September 2025 18:26:19 EDT, James Le Cuirot wrote:
> On Sun, 2025-09-07 at 14:43 -0400, Matt Whitlock wrote:
>> --- a/eclass/meson.eclass
>> +++ b/eclass/meson.eclass
>> @@ -147,6 +147,7 @@
>>  _meson_create_cross_file() {
>>  	local system cpu_family cpu
>>  	_meson_get_machine_info "${CHOST}"
>> +	local -n CHOST_default="CHOST_${DEFAULT_ABI}"
>> 
>>  	local fn=${T}/meson.${CHOST}.${ABI}.ini
>> 
>> @@ -181,7 +182,7 @@ _meson_create_cross_file() {
>>  	objcpp_link_args = $(_meson_env_array "${OBJCXXFLAGS} ${LDFLAGS}")
>> 
>>  	[properties]
>> -	needs_exe_wrapper = true
>> +	needs_exe_wrapper = $([[ "${CBUILD:-${CHOST}}" != 
>> "${CHOST_default}" ]] && echo true || echo false)
>>  	sys_root = '${SYSROOT}'
>>  	pkg_config_libdir = 
>> '${PKG_CONFIG_LIBDIR:-${EPREFIX}/usr/$(get_libdir)/pkgconfig}'
>
> My only
> concern is the precise CHOST can vary in the field, so the profile default
> might not match. A better way would be for multilib.eclass to preserve the
> original CHOST value in another variable before changing it.

multilib.eclass *does* already stash the original CHOST value in 
_abi_saved_CHOST. However, we can't lean on that in meson.eclass, as 
meson.eclass still has to work even if it's not being run under a multilib 
multi-build.

Is there actually a problem, though? This only comes into play in the case 
of a non-cross-compiling, non-default-ABI build. As a reminder, we have 
three cases to consider:

* The non-cross-compiling, default-ABI case: Meson runs in native mode, so 
we don't have a cross file at all.

* The non-cross-compiling, non-default-ABI case: multilib.eclass will have 
set CBUILD *exactly* to the value of CHOST_${DEFAULT_ABI}, so the 
conditional in meson.eclass is solid, and we will always have 
"needs_exe_wrapper = false" in this case. It doesn't matter what CHOST has 
been set to.

* The cross-compiling case: CBUILD will be set to something unlike CHOST or 
CHOST_${DEFAULT_ABI}, so we will always have "needs_exe_wrapper = true" in 
this case. As Eli pointed out, this isn't strictly true, but it makes us no 
worse off than we are at present.

Or are you suggesting that an ebuild may alter the value of CBUILD after 
multilib.eclass sets it but before meson.eclass checks it? I would argue 
that's a trigger of Undefined Behavior.


^ permalink raw reply	[flat|nested] 3+ messages in thread

end of thread, other threads:[~2025-09-08  0:12 UTC | newest]

Thread overview: 3+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2025-09-07 18:43 [gentoo-dev] multilib: CBUILD, CHOST, and toolchain vars for non-default ABIs Matt Whitlock
2025-09-07 22:26 ` James Le Cuirot
2025-09-08  0:10   ` Matt Whitlock

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox