CBMC
gcc_version.cpp
Go to the documentation of this file.
1 /*******************************************************************\
2 
3 Module: GCC Mode
4 
5 Author: Daniel Kroening, 2018
6 
7 \*******************************************************************/
8 
9 #include "gcc_version.h"
10 
11 #include <util/run.h>
12 #include <util/string2int.h>
13 #include <util/string_utils.h>
14 #include <util/tempfile.h>
15 
16 #include <fstream>
17 
18 void gcc_versiont::get(const std::string &executable)
19 {
20  temporary_filet tmp_file_in("goto-gcc.", ".in");
21  temporary_filet tmp_file_out("goto-gcc.", ".out");
22  temporary_filet tmp_file_err("goto-gcc.", ".err");
23 
24  {
25  std::ofstream out(tmp_file_in());
26 
27  out << "#if defined(__clang_major__)\n"
28  "clang __clang_major__ __clang_minor__ __clang_patchlevel__\n"
29  "#elif defined(__BCC__)\n"
30  "bcc 0 0 0\n"
31  "#else\n"
32  "gcc __GNUC__ __GNUC_MINOR__ __GNUC_PATCHLEVEL__\n"
33  "#endif\n"
34  "default_c_standard __STDC_VERSION__\n";
35  }
36 
37  // some variants output stuff on stderr, say Apple LLVM,
38  // which we silence.
39  int result = run(
40  executable,
41  {executable, "-E", "-", "-o", "-"},
42  tmp_file_in(),
43  tmp_file_out(),
44  tmp_file_err());
45 
48 
49  if(result >= 0)
50  {
51  std::ifstream in(tmp_file_out());
52  std::string line;
53 
54  while(!in.fail() && std::getline(in, line))
55  {
56  if(line.empty() || line[0] == '#')
57  continue;
58 
59  auto split = split_string(line, ' ');
60 
61  if(split.size() >= 4)
62  {
63  if(split[0] == "gcc")
65  else if(split[0] == "bcc")
67  else if(split[0] == "clang")
69 
70  v_major = unsafe_string2unsigned(split[1]);
71  v_minor = unsafe_string2unsigned(split[2]);
73  }
74  else if(split.size() == 2 && split[0] == "default_c_standard")
75  {
76  if(split[1] == "199901L")
78  else if(split[1] == "201112L")
80  }
81  }
82 
84  {
85  // Grab the default C++ standard. Unfortunately this requires another
86  // run, as the compiler can't preprocess two files in one go.
87 
88  temporary_filet cpp_in("goto-gcc.", ".cpp");
89  temporary_filet cpp_out("goto-gcc.", ".out");
90  temporary_filet cpp_err("goto-gcc.", ".err");
91 
92  {
93  std::ofstream out(cpp_in());
94  out << "default_cxx_standard __cplusplus\n";
95  }
96 
97  result = run(
98  executable,
99  {executable, "-E", "-x", "c++", "-", "-o", "-"},
100  cpp_in(),
101  cpp_out(),
102  cpp_err());
103 
104  if(result >= 0)
105  {
106  std::ifstream in2(cpp_out());
107 
108  while(!in2.fail() && std::getline(in2, line))
109  {
110  if(line.empty() || line[0] == '#')
111  continue;
112 
113  auto split = split_string(line, ' ');
114 
115  if(split.size() == 2 && split[0] == "default_cxx_standard")
116  {
117  if(split[1] == "199711L")
119  else if(split[1] == "201103L")
121  else if(split[1] == "201402L")
123  else if(split[1] == "201703L")
125  }
126  }
127  }
128  }
129  }
130 }
131 
133  unsigned _major,
134  unsigned _minor,
135  unsigned _patchlevel) const
136 {
137  return v_major > _major || (v_major == _major && v_minor > _minor) ||
138  (v_major == _major && v_minor == _minor &&
139  v_patchlevel >= _patchlevel);
140 }
141 
142 std::ostream &operator<<(std::ostream &out, const gcc_versiont &v)
143 {
144  return out << v.v_major << '.' << v.v_minor << '.' << v.v_patchlevel;
145 }
146 
147 void configure_gcc(const gcc_versiont &gcc_version)
148 {
149  // ISO/IEC TS 18661-3:2015 support was introduced with gcc 7.0
150  if(
151  gcc_version.flavor == gcc_versiont::flavort::GCC &&
152  gcc_version.is_at_least(7u))
153  {
155  }
156 
157  const auto gcc_float128_minor_version =
158  config.ansi_c.arch == "x86_64" ? 3u : 5u;
159 
160  // __float128 exists (as a typedef) since gcc 4.5 everywhere,
161  // and since 4.3 on x86_64
163  gcc_version.flavor == gcc_versiont::flavort::GCC &&
164  gcc_version.is_at_least(4u, gcc_float128_minor_version);
166  gcc_version.flavor == gcc_versiont::flavort::CLANG ||
167  (gcc_version.flavor == gcc_versiont::flavort::GCC &&
168  config.ansi_c.arch == "arm64" &&
171 
173  (gcc_version.flavor == gcc_versiont::flavort::GCC &&
174  gcc_version.is_at_least(12u)) ||
175  (gcc_version.flavor == gcc_versiont::flavort::CLANG &&
176  gcc_version.is_at_least(15u));
177 
179  (gcc_version.flavor == gcc_versiont::flavort::GCC &&
180  gcc_version.is_at_least(13u)) ||
181  (gcc_version.flavor == gcc_versiont::flavort::CLANG &&
182  gcc_version.is_at_least(15u));
183 
185  (gcc_version.flavor == gcc_versiont::flavort::GCC &&
186  gcc_version.is_at_least(4u, 5u) &&
187  (config.ansi_c.arch == "arm" || config.ansi_c.arch == "arm64")) ||
188  (gcc_version.flavor == gcc_versiont::flavort::CLANG &&
189  gcc_version.is_at_least(6u));
190 }
configt config
Definition: config.cpp:25
struct configt::ansi_ct ansi_c
bool is_at_least(unsigned v_major, unsigned v_minor=0, unsigned v_patchlevel=0) const
void get(const std::string &executable)
Definition: gcc_version.cpp:18
configt::cppt::cpp_standardt default_cxx_standard
Definition: gcc_version.h:40
configt::ansi_ct::c_standardt default_c_standard
Definition: gcc_version.h:39
unsigned v_patchlevel
Definition: gcc_version.h:22
unsigned v_minor
Definition: gcc_version.h:22
unsigned v_major
Definition: gcc_version.h:22
enum gcc_versiont::flavort flavor
std::ostream & operator<<(std::ostream &out, const gcc_versiont &v)
void configure_gcc(const gcc_versiont &gcc_version)
int run(const std::string &what, const std::vector< std::string > &argv)
Definition: run.cpp:48
unsigned unsafe_string2unsigned(const std::string &str, int base)
Definition: string2int.cpp:35
void split_string(const std::string &s, char delim, std::vector< std::string > &result, bool strip, bool remove_empty)
bool float16_type
Definition: config.h:155
bool bf16_type
Definition: config.h:156
bool ts_18661_3_Floatn_types
Definition: config.h:152
bool gcc__float128_type
Definition: config.h:153
irep_idt arch
Definition: config.h:223
bool __float128_is_keyword
Definition: config.h:154
bool fp16_type
Definition: config.h:157