Gentoo Websites Logo
Go to: Gentoo Home Documentation Forums Lists Bugs Planet Store Wiki Get Gentoo!
Bug 619490 - toolchain-funcs.eclass: clang-*version functions return literal string '__clang_major__.__clang_minor__' instead of actual values if CC is not clang (improve doc and change precondition)
Summary: toolchain-funcs.eclass: clang-*version functions return literal string '__cla...
Status: CONFIRMED
Alias: None
Product: Gentoo Linux
Classification: Unclassified
Component: Eclasses (show other bugs)
Hardware: All Linux
: Normal normal (vote)
Assignee: Gentoo Toolchain Maintainers
URL:
Whiteboard:
Keywords: PATCH
Depends on:
Blocks:
 
Reported: 2017-05-23 23:52 UTC by Adam Feldman
Modified: 2020-05-12 06:50 UTC (History)
1 user (show)

See Also:
Package list:
Runtime testing required: ---


Attachments
fix gcc/clang reporting (0001-toolchain-funcs.eclass-fix-clang-version-reporting-w.patch,1.55 KB, patch)
2017-08-14 18:42 UTC, Austin English (RETIRED)
Details | Diff

Note You need to log in before you can comment on or make changes to this bug.
Description Adam Feldman gentoo-dev 2017-05-23 23:52:44 UTC
If I create a new ebuild, inherit toolchain-funcs and eerror $(clang-version), I get the string "__clang_major__.__clang_minor__" instead of the intended value "3.9"
Comment 1 Austin English (RETIRED) gentoo-dev 2017-08-14 18:42:13 UTC
Created attachment 489038 [details, diff]
fix gcc/clang reporting

So the issue is that:
set -- $($(tc-getCPP "$@") -E -P - <<<"__clang_major__ __clang_minor__ __clang_patchlevel__")

always calls the configured C++ compiler. If you've set clang to be the compiler for that package, e.g., via https://wiki.gentoo.org/wiki/Clang#Environment, it works fine.

I'm not sure of the proper fix here. I'll attach a patch as a starting point for discussion.

Without patch and without setting clang as CC:
 * clang-fullversion: __clang_major__.__clang_minor__.__clang_patchlevel__ gcc-fullversion: 5.4.0
 * clang-version: __clang_major__.__clang_minor__ gcc-version: 5.4
 * clang-major-version: __clang_major__ gcc-major-version: 5
 * clang-minor-version: __clang_minor__ gcc-minor-version: 4
 * clang-micro-version: __clang_patchlevel__ gcc-micro-version: 0

Without patch, clang as CC:
 * clang-fullversion: 3.9.1 gcc-fullversion: 4.2.1
 * clang-version: 3.9 gcc-version: 4.2
 * clang-major-version: 3 gcc-major-version: 4
 * clang-minor-version: 9 gcc-minor-version: 2
 * clang-micro-version: 1 gcc-micro-version: 1

With patch, using clang's reported gcc version for gcc when querying gcc:
 * clang-fullversion: 3.9.1 gcc-fullversion: 4.2.1
 * clang-version: 3.9 gcc-version: 4.2
 * clang-major-version: 3 gcc-major-version: 4
 * clang-minor-version: 9 gcc-minor-version: 2
 * clang-micro-version: 1 gcc-micro-version: 1

With clang patch, and also always calling gcc explicitly:
 * clang-fullversion: 3.9.1 gcc-fullversion: 5.4.0
 * clang-version: 3.9 gcc-version: 5.4
 * clang-major-version: 3 gcc-major-version: 5
 * clang-minor-version: 9 gcc-minor-version: 4
 * clang-micro-version: 1 gcc-micro-version: 0

I'm not sure which is better for gcc versions to report when clang the is the CC. IMO we should trust clang to set the value of what version of gcc is considers supported. The problem I see is packagers querying the gcc version to see if they need to enable a cflag or something for gcc proper, and getting the wrong behavior on clang. Arguably, they should wrap such logic behind an tc-is-gcc() check, of course.
Comment 2 Andreas K. Hüttel archtester gentoo-dev 2017-10-03 20:30:38 UTC
I'm a bit wary of just running "gcc" or "clang" as compilers to get the version.
(However, I also haven't read myself into the logic of gcc-config and CC / CPP / ... yet.)
Comment 3 Sergei Trofimovich (RETIRED) gentoo-dev 2019-10-23 18:57:49 UTC
You should always predicate clang-*() function with tc-is-clang and gcc-*() with tc-is-gcc(). I don't think we can specify a reasonable default otherwise.
Comment 4 Cecil Curry 2020-05-12 06:22:11 UTC
I just hit this while resolving a PySide2 issue at my overlay:
    https://github.com/leycec/raiagent/issues/85

PySide2 has a non-standard (but otherwise functional) build system that requires calling the clang-fullversion() function from the "toolchain-funcs" eclass in the src_prepare phase. I expect the function to return a sane value. Instead, it returns insanity.

In the case of PySide2, that's the only "toolchain-funcs" function that needs to be called. PySide2's non-standard build system magically handles the rest. Mandating that ebuild maintainers explicitly call tc-is-clang() beforehand does no good in this case, as PySide2 doesn't particularly care whether tc-is-clang() returns true or not; it unconditionally uses Clang in either case, regardless of what the end user has configured their C[++] compiler to be, because PySide2 unconditionally requires Clang. Period.

In short, it'd be nice if the clang-*() family of functions behaved sanely regardless of whether tc-is-clang() returns true or not; if it doesn't, these functions should simply report metadata for the newest installed version of Clang on the local machine.

Obviously, I can force the desired behaviour by explicitly exporting "CPP=clang" before calling a clang-*() function and then restoring ${CPP} to its prior value immediately afterwards. In fact, that's exactly what we're currently doing, because that's what the "toolchain-funcs" API insanely requires. But that's the sort of dangerous low-level minutiae that these functions should already implicitly implement on our behalf.

At the very least, the documentation for these functions should explicitly state that the returned string is nonsense unless the current C++ compiler is Clang -- either by end user configuration or by temporarily setting and then restoring ${CPP} to "clang".

Of course, I see no reason why these functions can't behave sanely by default. If I can force them to behave sanely by manually setting a single variable, there's no justifiable reason they can't internally do the same. *shrug*
Comment 5 Sergei Trofimovich (RETIRED) gentoo-dev 2020-05-12 06:50:53 UTC
(In reply to Cecil Curry from comment #4)
> I just hit this while resolving a PySide2 issue at my overlay:
>     https://github.com/leycec/raiagent/issues/85
> 
> PySide2 has a non-standard (but otherwise functional) build system that
> requires calling the clang-fullversion() function from the "toolchain-funcs"
> eclass in the src_prepare phase. I expect the function to return a sane
> value. Instead, it returns insanity.

If you expect toolchain-funcs.eclass functions to do a reasonable thing you need to make sure CC/CXX and friends point to a compiler it expects. It holds for all ebuilds. Otherwise you are testing against the wrong compiler.

> In the case of PySide2, that's the only "toolchain-funcs" function that
> needs to be called. PySide2's non-standard build system magically handles
> the rest. Mandating that ebuild maintainers explicitly call tc-is-clang()
> beforehand does no good in this case, as PySide2 doesn't particularly care
> whether tc-is-clang() returns true or not; it unconditionally uses Clang in
> either case, regardless of what the end user has configured their C[++]
> compiler to be, because PySide2 unconditionally requires Clang. Period.

If Gentoo's understanding of CC/CXX does not match build system's use of CC/CXX you can't use toolchain-funcs.eclass in a reliable way. You have many options with various drawbacks:
1. mimic what build system does to extract actually used compiler and set CC/CXX accordingly
2. force CC=clang
3. have a blend of [1.] and [2.] based on 'tc-is-clang', like 'tc-is-clang || export CC=clang'.

[3.] looks most reasonable as user could have multiple clang versions installed and could use CC=clang-${version} to pick a very specific version and has best chance to succeed.

> In short, it'd be nice if the clang-*() family of functions behaved sanely
> regardless of whether tc-is-clang() returns true or not; if it doesn't,
> these functions should simply report metadata for the newest installed
> version of Clang on the local machine.

It will keep masking mismatched assumption between rest of toolchain-funcs.eclass functions and return invalid data. We can try to turn 'clang-*' into an error if 'tc-is-clang' precondition fails.

> Obviously, I can force the desired behaviour by explicitly exporting
> "CPP=clang" before calling a clang-*() function and then restoring ${CPP} to
> its prior value immediately afterwards. In fact, that's exactly what we're
> currently doing, because that's what the "toolchain-funcs" API insanely
> requires. But that's the sort of dangerous low-level minutiae that these
> functions should already implicitly implement on our behalf.

If you want to override user's default CC/CXX compiler that is a right thing to do.

> At the very least, the documentation for these functions should explicitly
> state that the returned string is nonsense unless the current C++ compiler
> is Clang -- either by end user configuration or by temporarily setting and
> then restoring ${CPP} to "clang".

We can improve the docs.

> Of course, I see no reason why these functions can't behave sanely by
> default. If I can force them to behave sanely by manually setting a single
> variable, there's no justifiable reason they can't internally do the same.
> *shrug*

If your build system uses 'clang', sure. CC=clang is correct. If it uses CC=clang-7.0 then it's not correct.