6 class CXXCompiler(object):
7 def __init__(self, path, flags=None, compile_flags=None, link_flags=None,
10 self.flags = list(flags or [])
11 self.compile_flags = list(compile_flags or [])
12 self.link_flags = list(link_flags or [])
13 self.use_ccache = use_ccache
16 self._initTypeAndVersion()
18 def _initTypeAndVersion(self):
19 # Get compiler type and version
20 macros = self.dumpMacros()
24 major_ver = minor_ver = patchlevel = None
25 if '__clang__' in macros.keys():
26 compiler_type = 'clang'
27 # Treat apple's llvm fork differently.
28 if '__apple_build_version__' in macros.keys():
29 compiler_type = 'apple-clang'
30 major_ver = macros['__clang_major__']
31 minor_ver = macros['__clang_minor__']
32 patchlevel = macros['__clang_patchlevel__']
33 elif '__GNUC__' in macros.keys():
35 major_ver = macros['__GNUC__']
36 minor_ver = macros['__GNUC_MINOR__']
37 patchlevel = macros['__GNUC_PATCHLEVEL__']
38 self.type = compiler_type
39 self.version = (major_ver, minor_ver, patchlevel)
41 def _basicCmd(self, source_files, out, is_link=False, input_is_cxx=False):
43 if self.use_ccache and not is_link:
50 if isinstance(source_files, list):
52 elif isinstance(source_files, str):
55 raise TypeError('source_files must be a string or list')
58 def preprocessCmd(self, source_files, out=None, flags=[]):
59 cmd = self._basicCmd(source_files, out, input_is_cxx=True) + ['-E']
60 cmd += self.flags + self.compile_flags + flags
63 def compileCmd(self, source_files, out=None, flags=[]):
64 cmd = self._basicCmd(source_files, out, input_is_cxx=True) + ['-c']
65 cmd += self.flags + self.compile_flags + flags
68 def linkCmd(self, source_files, out=None, flags=[]):
69 cmd = self._basicCmd(source_files, out, is_link=True)
70 cmd += self.flags + self.link_flags + flags
73 def compileLinkCmd(self, source_files, out=None, flags=[]):
74 cmd = self._basicCmd(source_files, out, is_link=True)
75 cmd += self.flags + self.compile_flags + self.link_flags + flags
78 def preprocess(self, source_files, out=None, flags=[], env=None, cwd=None):
79 cmd = self.preprocessCmd(source_files, out, flags)
80 out, err, rc = lit.util.executeCommand(cmd, env=env, cwd=cwd)
81 return cmd, out, err, rc
83 def compile(self, source_files, out=None, flags=[], env=None, cwd=None):
84 cmd = self.compileCmd(source_files, out, flags)
85 out, err, rc = lit.util.executeCommand(cmd, env=env, cwd=cwd)
86 return cmd, out, err, rc
88 def link(self, source_files, out=None, flags=[], env=None, cwd=None):
89 cmd = self.linkCmd(source_files, out, flags)
90 out, err, rc = lit.util.executeCommand(cmd, env=env, cwd=cwd)
91 return cmd, out, err, rc
93 def compileLink(self, source_files, out=None, flags=[], env=None,
95 cmd = self.compileLinkCmd(source_files, out, flags)
96 out, err, rc = lit.util.executeCommand(cmd, env=env, cwd=cwd)
97 return cmd, out, err, rc
99 def compileLinkTwoSteps(self, source_file, out=None, object_file=None,
100 flags=[], env=None, cwd=None):
101 if not isinstance(source_file, str):
102 raise TypeError('This function only accepts a single input file')
103 if object_file is None:
104 # Create, use and delete a temporary object file if none is given.
105 with_fn = lambda: libcxx.util.guardedTempFilename(suffix='.o')
107 # Otherwise wrap the filename in a context manager function.
108 with_fn = lambda: libcxx.util.nullContext(object_file)
109 with with_fn() as object_file:
110 cc_cmd, cc_stdout, cc_stderr, rc = self.compile(
111 source_file, object_file, flags=flags, env=env, cwd=cwd)
113 return cc_cmd, cc_stdout, cc_stderr, rc
115 link_cmd, link_stdout, link_stderr, rc = self.link(
116 object_file, out=out, flags=flags, env=env, cwd=cwd)
117 return (cc_cmd + ['&&'] + link_cmd, cc_stdout + link_stdout,
118 cc_stderr + link_stderr, rc)
120 def dumpMacros(self, source_files=None, flags=[], env=None, cwd=None):
121 if source_files is None:
122 source_files = os.devnull
123 flags = ['-dM'] + flags
124 cmd, out, err, rc = self.preprocess(source_files, flags=flags, env=env,
129 lines = [l.strip() for l in out.split('\n') if l.strip()]
131 assert l.startswith('#define ')
132 l = l[len('#define '):]
133 macro, _, value = l.partition(' ')
134 parsed_macros[macro] = value
138 cmd = [self.path] + self.flags + ['-dumpmachine']
139 return lit.util.capture(cmd).strip()
141 def hasCompileFlag(self, flag):
142 if isinstance(flag, list):
146 # Add -Werror to ensure that an unrecognized flag causes a non-zero
147 # exit code. -Werror is supported on all known compiler types.
148 if self.type is not None:
150 cmd, out, err, rc = self.compile(os.devnull, out=os.devnull,