Get Apple clang version and corresponding upstream LLVM version
I want to understand which version of clang Apple installed in my macbook, to see with c++11 and/or c++14 features are available. I typed this command:
clang --version
//----response
Apple LLVM version 7.0.0 (clang-700.1.76)
Target: x86_64-apple-darwin15.0.0
Thread model: posix
But I am not able to understand what (clang-700.1.76)
mean.
How can I convert this code to a clang version?
This is the site where you could check c++ features available in clang version http://clang.llvm.org/cxx_status.html
Wikipedia's Xcode page has a map of Apple to LLVM versions. The LLVM column has the open-source LLVM/Clang version. From this you can look up a language feature in cppreference's chart of compiler support for language features.
Here is the best listing I've found that correlates Apple's clang versions with the LLVM versions:
https://trac.macports.org/wiki/XcodeVersionInfo
Previous versions used to say what LLVM version they corresponded to, but starting with 7.0, Apple decided to no longer do that. They even define the __clang_version__
and related preprocessor macros to indicate the Apple version number, not the LLVM version. So they're useless for this as well.
Unfortunately, it looks like the only way to see if you have a feature is to try it and check if it works. e.g. 7.0.2 still doesn't have OpenMP enabled by default (although it's enable-able), so I guess it's still 3.6, not 3.7 yet.
As hinted by pkolbus
, you can look at the /src/CMakeLists.txt
to guess the corresponding Clang version. For example, Apple Clang 800.0.38 and 800.0.42.1 both seem to based on Clang 3.9.0 according to
if(NOT DEFINED LLVM_VERSION_MAJOR)
set(LLVM_VERSION_MAJOR 3)
endif()
if(NOT DEFINED LLVM_VERSION_MINOR)
set(LLVM_VERSION_MINOR 9)
endif()
if(NOT DEFINED LLVM_VERSION_PATCH)
set(LLVM_VERSION_PATCH 0)
endif()
if(NOT DEFINED LLVM_VERSION_SUFFIX)
set(LLVM_VERSION_SUFFIX svn)
endif()
Take a look at https://en.wikipedia.org/wiki/Xcode#Toolchain_versions
------------------------------------------------------------------------------------
Xcode cctools[93] ld64[94] LLVM[85] Clang version string[95]
8.3.3 898 278.4 3.9.0svn[85] 8.1.0 (clang-802.0.42)[80]
9.0 900 302.3 4.0.0?[86] 9.0.0 (clang-900.0.37)[80]
9.1 900 302.3.1 4.0.0?[87] 9.0.0 (clang-900.0.38)[80]
9.2 900 305 4.0.0?[88] 9.0.0 (clang-900.0.39.2)[80]
9.3 906 351.8 5.0.2?[89] 9.1.0 (clang-902.0.39.1)[80]
9.3.1 906 351.8 5.0.2?[89] 9.1.0 (clang-902.0.39.1)[80]
9.4 906 351.8 5.0.2?[90] 9.1.0 (clang-902.0.39.2)[80]
9.4.1 906 351.8 5.0.2?[90] 9.1.0 (clang-902.0.39.2)[80]
10.0 921.0.1 409.12 6.0.1?[91] 10.0.0 (clang-1000.11.45.2)[80]
10.1 921.0.1 409.12 6.0.1?[92] 10.0.0 (clang-1000.11.45.5)[80]
For example, Apple CLang 10.x is LLVM 6.0.1 based.
First, I want to say that Daniel Frey's answer is absolutely correct; you really should be using __has_feature
, __has_extension
, etc. when possible. The Clang Language Extensions page documents the different things you can check for, and that should be your go-to solution.
That said, sometimes you really do need to check the version. For example, sometimes it is necessary to work around compiler bugs which have been fixed in newer versions, or which only appear in newer versions. Sometimes new functionality is added; for example, prior to clang 9 __builtin_constant_p
didn't work correctly with the diagnose_if
attribute. Sometimes a feature is added but there is no corresponding check.
I really wish clang would just expose the upstream version numbers as preprocessor macros so we could reliably handle cases like that, but they don't. You could manually create a map of Apple version numbers to upstream, which is what several other answers have proposed, but that has some pretty obvious drawbacks. For me, the fatal flaw is that it doesn't really work for compilers other than Apple clang; there are a lot of compilers based on clang these days (IBM XL C/C++, some newer PGI/NVIDIA compilers, next-gen Intel C/C++, etc.).
My work-around is to use feature detection macros to estimate a version number. For example, -Wimplicit-const-int-float-conversion
was added in clang 11, so if __has_warning("-Wimplicit-const-int-float-conversion")
is true we can assume the upstream clang version is >= 11. Similarly, clang 10 added -Wmisleading-indentation
, clang 9 started definining the __FILE_NAME__
preprocessor macro, etc.
I've created a small header which contains the necessary logic. It's public domain (CC0), and even though it is part of one of my projects (SIMDe) it doesn't depend on anything else from any other files, so you're free to steal it for your own projects without copying all of SIMDe.
Obviously the file needs a new test for each version of clang, so it does require occasional updates if you need to be able to check for newer compilers, so I'd suggest grabbing the latest version from the SIMDe git repository (I'm not likely to keep this answer up to date), but here is what the checks look like right now:
#if defined(__clang__) && !defined(SIMDE_DETECT_CLANG_VERSION)
# if __has_warning("-Wformat-insufficient-args")
# define SIMDE_DETECT_CLANG_VERSION 120000
# elif __has_warning("-Wimplicit-const-int-float-conversion")
# define SIMDE_DETECT_CLANG_VERSION 110000
# elif __has_warning("-Wmisleading-indentation")
# define SIMDE_DETECT_CLANG_VERSION 100000
# elif defined(__FILE_NAME__)
# define SIMDE_DETECT_CLANG_VERSION 90000
# elif __has_warning("-Wextra-semi-stmt") || __has_builtin(__builtin_rotateleft32)
# define SIMDE_DETECT_CLANG_VERSION 80000
# elif __has_warning("-Wc++98-compat-extra-semi")
# define SIMDE_DETECT_CLANG_VERSION 70000
# elif __has_warning("-Wpragma-pack")
# define SIMDE_DETECT_CLANG_VERSION 60000
# elif __has_warning("-Wbitfield-enum-conversion")
# define SIMDE_DETECT_CLANG_VERSION 50000
# elif __has_attribute(diagnose_if)
# define SIMDE_DETECT_CLANG_VERSION 40000
# elif __has_warning("-Wcomma")
# define SIMDE_DETECT_CLANG_VERSION 39000
# elif __has_warning("-Wdouble-promotion")
# define SIMDE_DETECT_CLANG_VERSION 38000
# elif __has_warning("-Wshift-negative-value")
# define SIMDE_DETECT_CLANG_VERSION 37000
# elif __has_warning("-Wambiguous-ellipsis")
# define SIMDE_DETECT_CLANG_VERSION 36000
# else
# define SIMDE_DETECT_CLANG_VERSION 1
# endif
#endif /* defined(__clang__) && !defined(SIMDE_DETECT_CLANG_VERSION) */
I think the biggest problem with this method is actually shared with all other attempts to detect the upstream clang version that I'm aware of: there isn't necessarily a clang release that corresponds to the code in question. As far as I can tell, most compilers based on clang aren't actually based on releases, but rather some random commit (probably whatever is the latest commit for the branch they want to base their work on). That means that, for example, if an issue is was fixed late in the clang $N development cycle, Apple's fork may generally be the same as clang $N but not contain the bug fix. Conversely, maybe Apple will back-port a fix from clang $N+1 and a bug present in clang $N will be fixed in Apple's version.