Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How can I reliably detect the version of clang at preprocessing time?

Apparently, the clang bundled with Xcode doesn't respect the upstream __clang_major__ and __clang_minor__ values, and instead reports an Xcode user-facing version of some sort.

Here are, for reference, the values for the various MacPorts installs of clang. They seem to respect the upstream release identifiers. I get similar values when testing on Linux.

➜  prohibit-clang-3.2  /opt/local/bin/clang++-mp-3.2 -dM -E -x c /dev/null |
grep __clang_m
#define __clang_major__ 3
#define __clang_minor__ 2

➜  prohibit-clang-3.2  /opt/local/bin/clang++-mp-3.3 -dM -E -x c /dev/null |
grep __clang_m
#define __clang_major__ 3
#define __clang_minor__ 3

➜  prohibit-clang-3.2  /opt/local/bin/clang++-mp-3.4 -dM -E -x c /dev/null |
grep __clang_m
#define __clang_major__ 3
#define __clang_minor__ 4

However, for some reason, the Apple provided clang has __clang_major__ and __clang_minor__ versions that track the Xcode version, not the base clang revision:

➜  prohibit-clang-3.2 
/Applications/Xcode-4.6.3.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/bin/clang++
-dM -E -x c /dev/null | grep __clang_m
#define __clang_major__ 4
#define __clang_minor__ 2

➜  prohibit-clang-3.2 
/Applications/Xcode-4.6.3.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/bin/clang++
--version
Apple LLVM version 4.2 (clang-425.0.28) (based on LLVM 3.2svn)
Target: x86_64-apple-darwin12.5.0
Thread model: posix

➜  prohibit-clang-3.2 
/Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/bin/clang++
-dM -E -x c /dev/null | grep __clang_m
#define __clang_major__ 5
#define __clang_minor__ 0

➜  prohibit-clang-3.2 
/Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/bin/clang++
--version
Apple LLVM version 5.0 (clang-500.2.76) (based on LLVM 3.3svn)
Target: x86_64-apple-darwin12.5.0
Thread model: posix

➜  prohibit-clang-3.2  /usr/bin/clang++ -dM -E -x c /dev/null | grep __clang_m
#define __clang_major__ 5
#define __clang_minor__ 0

This seems pretty awful, because it means you can't write conditional compilation lines like the following that will work accurately across both the Apple vendored clang, and clang built normally from the clang sources or from distro packages:

#if !defined(__clang__) || (__clang_major__ > 3) || ((__clang_major__ == 3) && (__clang_minor__ > 2))
// Thing that doesn't work for clang-3.2 due to an optimization bug.
#endif

I'd hate to need to extend that already pretty terrible preprocessor check to account for two different clang versioning schemes, one normal and one Apple. I'm not even sure how I could reliably detect that this is 'Xcode clang' rather than normal clang.

Does anyone have any suggestions on how to work around this? Is there a flag to pass to Apple's clang that will instruct it to report its 'true' version? Or has Apple doomed us to never be able to reliably use __clang_major__ and __clang_minor__? Are these not the macros I should be using here?

like image 580
acm Avatar asked Oct 15 '13 17:10

acm


1 Answers

If the feature checking macros don't cover what you need to check for then you do need to treat vendor releases separately as they may have very different content from the open source LLVM releases. You can use __apple_build_version__ to check for a specific Apple LLVM version.

like image 199
Matt Stevens Avatar answered Sep 26 '22 07:09

Matt Stevens