1 #===----------------------------------------------------------------------===## 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 10 import os 11 import lit.util 12 import libcxx.util 13 14 15 class CXXCompiler(object): 16 def __init__(self, path, flags=None, compile_flags=None, link_flags=None, 17 use_ccache=False): 18 self.path = path 19 self.flags = list(flags or []) 20 self.compile_flags = list(compile_flags or []) 21 self.link_flags = list(link_flags or []) 22 self.use_ccache = use_ccache 23 self.type = None 24 self.version = None 25 self._initTypeAndVersion() 26 27 def _initTypeAndVersion(self): 28 # Get compiler type and version 29 macros = self.dumpMacros() 30 if macros is None: 31 return 32 compiler_type = None 33 major_ver = minor_ver = patchlevel = None 34 if '__clang__' in macros.keys(): 35 compiler_type = 'clang' 36 # Treat apple's llvm fork differently. 37 if '__apple_build_version__' in macros.keys(): 38 compiler_type = 'apple-clang' 39 major_ver = macros['__clang_major__'] 40 minor_ver = macros['__clang_minor__'] 41 patchlevel = macros['__clang_patchlevel__'] 42 elif '__GNUC__' in macros.keys(): 43 compiler_type = 'gcc' 44 major_ver = macros['__GNUC__'] 45 minor_ver = macros['__GNUC_MINOR__'] 46 patchlevel = macros['__GNUC_PATCHLEVEL__'] 47 self.type = compiler_type 48 self.version = (major_ver, minor_ver, patchlevel) 49 50 def _basicCmd(self, source_files, out, is_link=False, input_is_cxx=False): 51 cmd = [] 52 if self.use_ccache and not is_link: 53 cmd += ['ccache'] 54 cmd += [self.path] 55 if out is not None: 56 cmd += ['-o', out] 57 if input_is_cxx: 58 cmd += ['-x', 'c++'] 59 if isinstance(source_files, list): 60 cmd += source_files 61 elif isinstance(source_files, str): 62 cmd += [source_files] 63 else: 64 raise TypeError('source_files must be a string or list') 65 return cmd 66 67 def preprocessCmd(self, source_files, out=None, flags=[]): 68 cmd = self._basicCmd(source_files, out, input_is_cxx=True) + ['-E'] 69 cmd += self.flags + self.compile_flags + flags 70 return cmd 71 72 def compileCmd(self, source_files, out=None, flags=[]): 73 cmd = self._basicCmd(source_files, out, input_is_cxx=True) + ['-c'] 74 cmd += self.flags + self.compile_flags + flags 75 return cmd 76 77 def linkCmd(self, source_files, out=None, flags=[]): 78 cmd = self._basicCmd(source_files, out, is_link=True) 79 cmd += self.flags + self.link_flags + flags 80 return cmd 81 82 def compileLinkCmd(self, source_files, out=None, flags=[]): 83 cmd = self._basicCmd(source_files, out, is_link=True) 84 cmd += self.flags + self.compile_flags + self.link_flags + flags 85 return cmd 86 87 def preprocess(self, source_files, out=None, flags=[], env=None, cwd=None): 88 cmd = self.preprocessCmd(source_files, out, flags) 89 out, err, rc = lit.util.executeCommand(cmd, env=env, cwd=cwd) 90 return cmd, out, err, rc 91 92 def compile(self, source_files, out=None, flags=[], env=None, cwd=None): 93 cmd = self.compileCmd(source_files, out, flags) 94 out, err, rc = lit.util.executeCommand(cmd, env=env, cwd=cwd) 95 return cmd, out, err, rc 96 97 def link(self, source_files, out=None, flags=[], env=None, cwd=None): 98 cmd = self.linkCmd(source_files, out, flags) 99 out, err, rc = lit.util.executeCommand(cmd, env=env, cwd=cwd) 100 return cmd, out, err, rc 101 102 def compileLink(self, source_files, out=None, flags=[], env=None, 103 cwd=None): 104 cmd = self.compileLinkCmd(source_files, out, flags) 105 out, err, rc = lit.util.executeCommand(cmd, env=env, cwd=cwd) 106 return cmd, out, err, rc 107 108 def compileLinkTwoSteps(self, source_file, out=None, object_file=None, 109 flags=[], env=None, cwd=None): 110 if not isinstance(source_file, str): 111 raise TypeError('This function only accepts a single input file') 112 if object_file is None: 113 # Create, use and delete a temporary object file if none is given. 114 with_fn = lambda: libcxx.util.guardedTempFilename(suffix='.o') 115 else: 116 # Otherwise wrap the filename in a context manager function. 117 with_fn = lambda: libcxx.util.nullContext(object_file) 118 with with_fn() as object_file: 119 cc_cmd, cc_stdout, cc_stderr, rc = self.compile( 120 source_file, object_file, flags=flags, env=env, cwd=cwd) 121 if rc != 0: 122 return cc_cmd, cc_stdout, cc_stderr, rc 123 124 link_cmd, link_stdout, link_stderr, rc = self.link( 125 object_file, out=out, flags=flags, env=env, cwd=cwd) 126 return (cc_cmd + ['&&'] + link_cmd, cc_stdout + link_stdout, 127 cc_stderr + link_stderr, rc) 128 129 def dumpMacros(self, source_files=None, flags=[], env=None, cwd=None): 130 if source_files is None: 131 source_files = os.devnull 132 flags = ['-dM'] + flags 133 cmd, out, err, rc = self.preprocess(source_files, flags=flags, env=env, 134 cwd=cwd) 135 if rc != 0: 136 return None 137 parsed_macros = {} 138 lines = [l.strip() for l in out.split('\n') if l.strip()] 139 for l in lines: 140 assert l.startswith('#define ') 141 l = l[len('#define '):] 142 macro, _, value = l.partition(' ') 143 parsed_macros[macro] = value 144 return parsed_macros 145 146 def getTriple(self): 147 cmd = [self.path] + self.flags + ['-dumpmachine'] 148 return lit.util.capture(cmd).strip() 149 150 def hasCompileFlag(self, flag): 151 if isinstance(flag, list): 152 flags = list(flag) 153 else: 154 flags = [flag] 155 # Add -Werror to ensure that an unrecognized flag causes a non-zero 156 # exit code. -Werror is supported on all known compiler types. 157 if self.type is not None: 158 flags += ['-Werror'] 159 cmd, out, err, rc = self.compile(os.devnull, out=os.devnull, 160 flags=flags) 161 return rc == 0 162 163 def addCompileFlagIfSupported(self, flag): 164 if isinstance(flag, list): 165 flags = list(flag) 166 else: 167 flags = [flag] 168 if self.hasCompileFlag(flags): 169 self.compile_flags += flags 170 return True 171 else: 172 return False 173 174 def addWarningFlagIfSupported(self, flag): 175 """ 176 addWarningFlagIfSupported - Add a warning flag if the compiler 177 supports it. Unlike addCompileFlagIfSupported, this function detects 178 when "-Wno-<warning>" flags are unsupported. If flag is a 179 "-Wno-<warning>" GCC will not emit an unknown option diagnostic unless 180 another error is triggered during compilation. 181 """ 182 assert isinstance(flag, str) 183 if not flag.startswith('-Wno-'): 184 return self.addCompileFlagIfSupported(flag) 185 flags = ['-Werror', flag] 186 cmd = self.compileCmd('-', os.devnull, flags) 187 # Remove '-v' because it will cause the command line invocation 188 # to be printed as part of the error output. 189 # TODO(EricWF): Are there other flags we need to worry about? 190 if '-v' in cmd: 191 cmd.remove('-v') 192 out, err, rc = lit.util.executeCommand(cmd, input='#error\n') 193 assert rc != 0 194 if flag in err: 195 return False 196 self.compile_flags += [flag] 197 return True 198