blob: 70022d7a43a0a0d6e7c76ef9ba87a70c62fa7143 [file] [log] [blame]
Eric Fiselierfa1e5db2016-01-19 21:52:04 +00001#===----------------------------------------------------------------------===##
2#
3# The LLVM Compiler Infrastructure
4#
5# This file is dual licensed under the MIT and the University of Illinois Open
6# Source Licenses. See LICENSE.TXT for details.
7#
8#===----------------------------------------------------------------------===##
9
Eric Fiselierbca6de02016-12-05 23:16:07 +000010import platform
Eric Fiselierdd7a4832015-01-22 18:05:58 +000011import os
Eric Fiselierdd7a4832015-01-22 18:05:58 +000012import libcxx.util
Eric Fiselier28a058b2015-01-16 21:35:08 +000013
14
15class CXXCompiler(object):
Eric Fiselier2edb3262016-07-20 23:37:28 +000016 CM_Default = 0
17 CM_PreProcess = 1
18 CM_Compile = 2
19 CM_Link = 3
20
Eric Fiselierdd7a4832015-01-22 18:05:58 +000021 def __init__(self, path, flags=None, compile_flags=None, link_flags=None,
Eric Fiselierd95f62e2016-12-06 01:02:15 +000022 warning_flags=None, verify_supported=None,
23 verify_flags=None, use_verify=False,
24 modules_flags=None, use_modules=False,
Eric Fiselierbca6de02016-12-05 23:16:07 +000025 use_ccache=False, use_warnings=False, compile_env=None,
26 cxx_type=None, cxx_version=None):
Erik Pilkington64182a52017-05-19 23:02:49 +000027 self.source_lang = 'c++'
Eric Fiselier28a058b2015-01-16 21:35:08 +000028 self.path = path
Eric Fiselierdd7a4832015-01-22 18:05:58 +000029 self.flags = list(flags or [])
30 self.compile_flags = list(compile_flags or [])
31 self.link_flags = list(link_flags or [])
Eric Fiselierd95f62e2016-12-06 01:02:15 +000032 self.warning_flags = list(warning_flags or [])
33 self.verify_supported = verify_supported
34 self.use_verify = use_verify
35 self.verify_flags = list(verify_flags or [])
36 assert not use_verify or verify_supported
37 assert not use_verify or verify_flags is not None
Eric Fiselierbca6de02016-12-05 23:16:07 +000038 self.modules_flags = list(modules_flags or [])
39 self.use_modules = use_modules
40 assert not use_modules or modules_flags is not None
Eric Fiselier28a058b2015-01-16 21:35:08 +000041 self.use_ccache = use_ccache
Eric Fiselierbca6de02016-12-05 23:16:07 +000042 self.use_warnings = use_warnings
43 if compile_env is not None:
44 self.compile_env = dict(compile_env)
45 else:
46 self.compile_env = None
47 self.type = cxx_type
48 self.version = cxx_version
49 if self.type is None or self.version is None:
50 self._initTypeAndVersion()
51
Eric Fiselierd95f62e2016-12-06 01:02:15 +000052 def isVerifySupported(self):
53 if self.verify_supported is None:
54 self.verify_supported = self.hasCompileFlag(['-Xclang',
55 '-verify-ignore-unexpected'])
56 if self.verify_supported:
57 self.verify_flags = [
58 '-Xclang', '-verify',
59 '-Xclang', '-verify-ignore-unexpected=note',
60 '-ferror-limit=1024'
61 ]
62 return self.verify_supported
63
64 def useVerify(self, value=True):
65 self.use_verify = value
66 assert not self.use_verify or self.verify_flags is not None
67
Eric Fiselierbca6de02016-12-05 23:16:07 +000068 def useModules(self, value=True):
69 self.use_modules = value
70 assert not self.use_modules or self.modules_flags is not None
71
72 def useCCache(self, value=True):
73 self.use_ccache = value
74
75 def useWarnings(self, value=True):
76 self.use_warnings = value
Eric Fiselier28a058b2015-01-16 21:35:08 +000077
78 def _initTypeAndVersion(self):
79 # Get compiler type and version
80 macros = self.dumpMacros()
81 if macros is None:
82 return
83 compiler_type = None
84 major_ver = minor_ver = patchlevel = None
85 if '__clang__' in macros.keys():
86 compiler_type = 'clang'
87 # Treat apple's llvm fork differently.
88 if '__apple_build_version__' in macros.keys():
89 compiler_type = 'apple-clang'
90 major_ver = macros['__clang_major__']
91 minor_ver = macros['__clang_minor__']
92 patchlevel = macros['__clang_patchlevel__']
93 elif '__GNUC__' in macros.keys():
94 compiler_type = 'gcc'
95 major_ver = macros['__GNUC__']
96 minor_ver = macros['__GNUC_MINOR__']
97 patchlevel = macros['__GNUC_PATCHLEVEL__']
98 self.type = compiler_type
99 self.version = (major_ver, minor_ver, patchlevel)
100
Eric Fiselier2edb3262016-07-20 23:37:28 +0000101 def _basicCmd(self, source_files, out, mode=CM_Default, flags=[],
Eric Fiselierbca6de02016-12-05 23:16:07 +0000102 input_is_cxx=False):
Eric Fiselier28a058b2015-01-16 21:35:08 +0000103 cmd = []
Eric Fiselierbca6de02016-12-05 23:16:07 +0000104 if self.use_ccache \
Eric Fiselier2edb3262016-07-20 23:37:28 +0000105 and not mode == self.CM_Link \
106 and not mode == self.CM_PreProcess:
Eric Fiselier28a058b2015-01-16 21:35:08 +0000107 cmd += ['ccache']
108 cmd += [self.path]
109 if out is not None:
110 cmd += ['-o', out]
Eric Fiseliera769d7f2015-01-28 00:05:48 +0000111 if input_is_cxx:
Erik Pilkington64182a52017-05-19 23:02:49 +0000112 cmd += ['-x', self.source_lang]
Eric Fiselierdd7a4832015-01-22 18:05:58 +0000113 if isinstance(source_files, list):
114 cmd += source_files
115 elif isinstance(source_files, str):
116 cmd += [source_files]
Eric Fiselier28a058b2015-01-16 21:35:08 +0000117 else:
Eric Fiselierdd7a4832015-01-22 18:05:58 +0000118 raise TypeError('source_files must be a string or list')
Eric Fiselier2edb3262016-07-20 23:37:28 +0000119 if mode == self.CM_PreProcess:
120 cmd += ['-E']
121 elif mode == self.CM_Compile:
122 cmd += ['-c']
123 cmd += self.flags
Eric Fiselierd95f62e2016-12-06 01:02:15 +0000124 if self.use_verify:
125 cmd += self.verify_flags
126 assert mode in [self.CM_Default, self.CM_Compile]
Eric Fiselierbca6de02016-12-05 23:16:07 +0000127 if self.use_modules:
128 cmd += self.modules_flags
Eric Fiselier2edb3262016-07-20 23:37:28 +0000129 if mode != self.CM_Link:
130 cmd += self.compile_flags
Eric Fiselierbca6de02016-12-05 23:16:07 +0000131 if self.use_warnings:
Eric Fiselier2edb3262016-07-20 23:37:28 +0000132 cmd += self.warning_flags
133 if mode != self.CM_PreProcess and mode != self.CM_Compile:
134 cmd += self.link_flags
135 cmd += flags
Eric Fiselier28a058b2015-01-16 21:35:08 +0000136 return cmd
137
Eric Fiselierbca6de02016-12-05 23:16:07 +0000138 def preprocessCmd(self, source_files, out=None, flags=[]):
Eric Fiselier2edb3262016-07-20 23:37:28 +0000139 return self._basicCmd(source_files, out, flags=flags,
140 mode=self.CM_PreProcess,
Eric Fiselier2edb3262016-07-20 23:37:28 +0000141 input_is_cxx=True)
Eric Fiselier28a058b2015-01-16 21:35:08 +0000142
Eric Fiselierbca6de02016-12-05 23:16:07 +0000143 def compileCmd(self, source_files, out=None, flags=[]):
Eric Fiselier2edb3262016-07-20 23:37:28 +0000144 return self._basicCmd(source_files, out, flags=flags,
145 mode=self.CM_Compile,
Eric Fiselierbca6de02016-12-05 23:16:07 +0000146 input_is_cxx=True) + ['-c']
Eric Fiselier28a058b2015-01-16 21:35:08 +0000147
Eric Fiselierdd7a4832015-01-22 18:05:58 +0000148 def linkCmd(self, source_files, out=None, flags=[]):
Eric Fiselier2edb3262016-07-20 23:37:28 +0000149 return self._basicCmd(source_files, out, flags=flags,
Eric Fiselierbca6de02016-12-05 23:16:07 +0000150 mode=self.CM_Link)
Eric Fiselier28a058b2015-01-16 21:35:08 +0000151
Eric Fiselierbca6de02016-12-05 23:16:07 +0000152 def compileLinkCmd(self, source_files, out=None, flags=[]):
153 return self._basicCmd(source_files, out, flags=flags)
154
155 def preprocess(self, source_files, out=None, flags=[], cwd=None):
Eric Fiselierdd7a4832015-01-22 18:05:58 +0000156 cmd = self.preprocessCmd(source_files, out, flags)
Eric Fiseliered803862017-02-09 23:18:11 +0000157 out, err, rc = libcxx.util.executeCommand(cmd, env=self.compile_env,
158 cwd=cwd)
Eric Fiselier28a058b2015-01-16 21:35:08 +0000159 return cmd, out, err, rc
160
Eric Fiselierbca6de02016-12-05 23:16:07 +0000161 def compile(self, source_files, out=None, flags=[], cwd=None):
162 cmd = self.compileCmd(source_files, out, flags)
Eric Fiseliered803862017-02-09 23:18:11 +0000163 out, err, rc = libcxx.util.executeCommand(cmd, env=self.compile_env,
164 cwd=cwd)
Eric Fiselier28a058b2015-01-16 21:35:08 +0000165 return cmd, out, err, rc
166
Eric Fiselierbca6de02016-12-05 23:16:07 +0000167 def link(self, source_files, out=None, flags=[], cwd=None):
Eric Fiselierdd7a4832015-01-22 18:05:58 +0000168 cmd = self.linkCmd(source_files, out, flags)
Eric Fiseliered803862017-02-09 23:18:11 +0000169 out, err, rc = libcxx.util.executeCommand(cmd, env=self.compile_env,
170 cwd=cwd)
Eric Fiselier28a058b2015-01-16 21:35:08 +0000171 return cmd, out, err, rc
172
Eric Fiselierbca6de02016-12-05 23:16:07 +0000173 def compileLink(self, source_files, out=None, flags=[],
Eric Fiselierdd7a4832015-01-22 18:05:58 +0000174 cwd=None):
175 cmd = self.compileLinkCmd(source_files, out, flags)
Eric Fiseliered803862017-02-09 23:18:11 +0000176 out, err, rc = libcxx.util.executeCommand(cmd, env=self.compile_env,
177 cwd=cwd)
Eric Fiselier28a058b2015-01-16 21:35:08 +0000178 return cmd, out, err, rc
179
Eric Fiselierdd7a4832015-01-22 18:05:58 +0000180 def compileLinkTwoSteps(self, source_file, out=None, object_file=None,
Eric Fiselierbca6de02016-12-05 23:16:07 +0000181 flags=[], cwd=None):
Eric Fiselierdd7a4832015-01-22 18:05:58 +0000182 if not isinstance(source_file, str):
183 raise TypeError('This function only accepts a single input file')
184 if object_file is None:
185 # Create, use and delete a temporary object file if none is given.
186 with_fn = lambda: libcxx.util.guardedTempFilename(suffix='.o')
187 else:
188 # Otherwise wrap the filename in a context manager function.
189 with_fn = lambda: libcxx.util.nullContext(object_file)
190 with with_fn() as object_file:
Logan Chien21f5b242015-05-17 00:24:11 +0000191 cc_cmd, cc_stdout, cc_stderr, rc = self.compile(
Eric Fiselierbca6de02016-12-05 23:16:07 +0000192 source_file, object_file, flags=flags, cwd=cwd)
Eric Fiselierdd7a4832015-01-22 18:05:58 +0000193 if rc != 0:
Logan Chien21f5b242015-05-17 00:24:11 +0000194 return cc_cmd, cc_stdout, cc_stderr, rc
195
196 link_cmd, link_stdout, link_stderr, rc = self.link(
Eric Fiselierbca6de02016-12-05 23:16:07 +0000197 object_file, out=out, flags=flags, cwd=cwd)
Logan Chien21f5b242015-05-17 00:24:11 +0000198 return (cc_cmd + ['&&'] + link_cmd, cc_stdout + link_stdout,
199 cc_stderr + link_stderr, rc)
Eric Fiselierdd7a4832015-01-22 18:05:58 +0000200
Eric Fiselierbca6de02016-12-05 23:16:07 +0000201 def dumpMacros(self, source_files=None, flags=[], cwd=None):
Eric Fiselierdd7a4832015-01-22 18:05:58 +0000202 if source_files is None:
203 source_files = os.devnull
Eric Fiselier28a058b2015-01-16 21:35:08 +0000204 flags = ['-dM'] + flags
Eric Fiselierbca6de02016-12-05 23:16:07 +0000205 cmd, out, err, rc = self.preprocess(source_files, flags=flags, cwd=cwd)
Eric Fiselier28a058b2015-01-16 21:35:08 +0000206 if rc != 0:
Eric Fiselier5fe87972017-10-02 22:52:51 +0000207 return cmd, out, err, rc
Eric Fiselier9ef5d452015-01-20 16:14:18 +0000208 parsed_macros = {}
Eric Fiselier28a058b2015-01-16 21:35:08 +0000209 lines = [l.strip() for l in out.split('\n') if l.strip()]
210 for l in lines:
211 assert l.startswith('#define ')
212 l = l[len('#define '):]
213 macro, _, value = l.partition(' ')
214 parsed_macros[macro] = value
215 return parsed_macros
216
217 def getTriple(self):
218 cmd = [self.path] + self.flags + ['-dumpmachine']
Eric Fiseliered803862017-02-09 23:18:11 +0000219 return libcxx.util.capture(cmd).strip()
Eric Fiseliera985b8c2015-05-19 15:15:53 +0000220
221 def hasCompileFlag(self, flag):
Eric Fiselierfaaf5ee2015-07-06 19:56:45 +0000222 if isinstance(flag, list):
223 flags = list(flag)
224 else:
225 flags = [flag]
Eric Fiseliera985b8c2015-05-19 15:15:53 +0000226 # Add -Werror to ensure that an unrecognized flag causes a non-zero
227 # exit code. -Werror is supported on all known compiler types.
228 if self.type is not None:
Eric Fiselier78c020b2016-10-14 10:30:33 +0000229 flags += ['-Werror', '-fsyntax-only']
Eric Fiseliera985b8c2015-05-19 15:15:53 +0000230 cmd, out, err, rc = self.compile(os.devnull, out=os.devnull,
231 flags=flags)
232 return rc == 0
Eric Fiselier7b86ce52015-07-18 21:17:16 +0000233
Eric Fiselier6e467a82016-11-13 22:27:00 +0000234 def addFlagIfSupported(self, flag):
235 if isinstance(flag, list):
236 flags = list(flag)
237 else:
238 flags = [flag]
239 if self.hasCompileFlag(flags):
240 self.flags += flags
241 return True
242 else:
243 return False
244
Eric Fiselier7b86ce52015-07-18 21:17:16 +0000245 def addCompileFlagIfSupported(self, flag):
246 if isinstance(flag, list):
247 flags = list(flag)
248 else:
249 flags = [flag]
250 if self.hasCompileFlag(flags):
251 self.compile_flags += flags
252 return True
253 else:
254 return False
Eric Fiseliere94b4842015-08-26 20:17:33 +0000255
Eric Fiselierbca6de02016-12-05 23:16:07 +0000256 def hasWarningFlag(self, flag):
Eric Fiseliere94b4842015-08-26 20:17:33 +0000257 """
Eric Fiselierbca6de02016-12-05 23:16:07 +0000258 hasWarningFlag - Test if the compiler supports a given warning flag.
259 Unlike addCompileFlagIfSupported, this function detects when
260 "-Wno-<warning>" flags are unsupported. If flag is a
Eric Fiseliere94b4842015-08-26 20:17:33 +0000261 "-Wno-<warning>" GCC will not emit an unknown option diagnostic unless
262 another error is triggered during compilation.
263 """
264 assert isinstance(flag, str)
Eric Fiselierab9ab942016-12-24 04:34:33 +0000265 assert flag.startswith('-W')
Eric Fiseliere94b4842015-08-26 20:17:33 +0000266 if not flag.startswith('-Wno-'):
Eric Fiselierab9ab942016-12-24 04:34:33 +0000267 return self.hasCompileFlag(flag)
Eric Fiseliere94b4842015-08-26 20:17:33 +0000268 flags = ['-Werror', flag]
Eric Fiselierbca6de02016-12-05 23:16:07 +0000269 old_use_warnings = self.use_warnings
270 self.useWarnings(False)
271 cmd = self.compileCmd('-', os.devnull, flags)
Eric Fiselierab9ab942016-12-24 04:34:33 +0000272 self.useWarnings(old_use_warnings)
Eric Fiseliere94b4842015-08-26 20:17:33 +0000273 # Remove '-v' because it will cause the command line invocation
274 # to be printed as part of the error output.
275 # TODO(EricWF): Are there other flags we need to worry about?
276 if '-v' in cmd:
277 cmd.remove('-v')
Eric Fiseliered803862017-02-09 23:18:11 +0000278 out, err, rc = libcxx.util.executeCommand(
279 cmd, input=libcxx.util.to_bytes('#error\n'))
Eric Fiselierab9ab942016-12-24 04:34:33 +0000280
Eric Fiseliere94b4842015-08-26 20:17:33 +0000281 assert rc != 0
282 if flag in err:
283 return False
Eric Fiseliere94b4842015-08-26 20:17:33 +0000284 return True
Eric Fiselierbca6de02016-12-05 23:16:07 +0000285
286 def addWarningFlagIfSupported(self, flag):
287 if self.hasWarningFlag(flag):
Eric Fiselierebaf7da2017-01-13 22:02:08 +0000288 if flag not in self.warning_flags:
289 self.warning_flags += [flag]
Eric Fiselierbca6de02016-12-05 23:16:07 +0000290 return True
291 return False