Reputation: 3149
In a Python script, I am trying to determine the highest C++ standard supported by the installed Clang.
One problem is that I cannot rely on the output of clang --version
to always be the same - the best example is AppleClang on OSX.
Trying to compile a hello world .cpp
file with test-flags like -std=c++11
, -std=c++14
, ... does not seem the most robust approach and would require the creation of temporary files.
Is there any command one could run to test if a certain dialect is available without actually compiling anything?
Upvotes: 6
Views: 3836
Reputation: 61515
Is there any command one could run to test if a certain dialect is available without actually compiling anything?
Yes. You can ask the compiler just to preprocess an empty file. It will do that without complaint:
$ clang++ --version
clang version 4.0.1-6 (tags/RELEASE_401/final)
Target: x86_64-pc-linux-gnu
Thread model: posix
InstalledDir: /usr/bin
$ echo '' | clang++ -x c++ -E -
# 1 "<stdin>"
# 1 "<built-in>" 1
# 1 "<built-in>" 3
# 329 "<built-in>" 3
# 1 "<command line>" 1
# 1 "<built-in>" 2
# 1 "<stdin>" 2
$ echo $?
0
You can then incidentally add a -std
option. If the compiler supports it:
$ echo '' | clang++ -std=c++98 -x c++ -E -
# 1 "<stdin>"
# 1 "<built-in>" 1
# 1 "<built-in>" 3
# 326 "<built-in>" 3
# 1 "<command line>" 1
# 1 "<built-in>" 2
# 1 "<stdin>" 2
$ echo $?
0
still no complaint. But if not:
$ echo '' | clang++ -std=c++17 -x c++ -E -
error: invalid value 'c++17' in '-std=c++17'
$ echo $?
1
In a python script, you can conveniently supply an empty input file in the form of an empty string to an invocation of subprocess.run
that executes the compiler probing command,
and at the same time swallows the unwanted stdout. You'd iterate such
invocations over a chronologically sorted list of -std
-values to find the
latest supported. It would be prudent not simply to test the return code but
also to capture the stderr and, in case of failure, parse it for the right
sort of diagnostic, in case the command has failed for some surprise reason.
Here's a shot that serves for GCC as well as clang:
$ cat std_max.py
#!/usr/bin/python3
import subprocess
standards = ['98','03','11','14','17']
gpp_barf_pattern = "error: unrecognized command line option ‘-std=c++{0}’"
clangpp_barf_pattern = "error: invalid value 'c++{0}'"
def has_standard(compiler, std_year, barf_pattern):
std_opt = '-std=c++' + std_year
try:
subprocess.run([compiler,std_opt,'-x','c++','-E','-'],\
check=True,input=b'',stdout=subprocess.PIPE,stderr=subprocess.PIPE)
except subprocess.CalledProcessError as e:
barf = barf_pattern.format(std_year)
strerr = e.stderr.decode('utf8','strict')
if barf in strerr:
return False
raise
return True
def get_std_max(compiler,standards,barf_pattern):
max_std = standards[0] if len(standards) else ''
for std_year in standards:
if not has_standard(compiler,std_year,barf_pattern):
break
max_std = 'c++' + std_year
return max_std
which will tell me, correctly:
$ python3
Python 3.6.3 (default, Oct 3 2017, 21:45:48)
[GCC 7.2.0] on linux
Type "help", "copyright", "credits" or "license" for more information.
>>> from std_max import *
>>> get_std_max('clang++',standards,clangpp_barf_pattern)
'c++14'
>>> get_std_max('g++',standards,gpp_barf_pattern)
'c++17'
>>>
No C++20 yet:
>>> has_standard('g++','20',gpp_barf_pattern)
False
>>>
Upvotes: 4