2 # Copyright (c) 2013 The Native Client Authors. All rights reserved.
3 # Use of this source code is governed by a BSD-style license that can be
4 # found in the LICENSE file.
6 """Utilities for determining (and overriding) the types of files
15 LLVM_BITCODE_MAGIC = 'BC\xc0\xde'
16 LLVM_WRAPPER_MAGIC = '\xde\xc0\x17\x0b'
17 PNACL_BITCODE_MAGIC = 'PEXE'
19 class SimpleCache(object):
20 """ Cache results of a function using a dictionary. """
25 def ClearAllCaches(cls):
26 """ Clear cached results from all functions. """
27 for d in cls.__all_caches.itervalues():
30 def __init__(self, f):
31 SimpleCache.__all_caches[self] = dict()
32 self.cache = SimpleCache.__all_caches[self]
35 def __call__(self, *args):
36 if args in self.cache:
37 return self.cache[args]
39 result = self.func(*args)
40 self.cache[args] = result
44 return self.func.__doc__
46 def OverrideValue(self, value, *args):
47 """ Force a function call with |args| to return |value|. """
48 self.cache[args] = value
51 """ Clear cached results for one instance (function). """
56 def IsNative(filename):
57 return (IsNativeObject(filename) or
58 IsNativeDSO(filename) or
59 IsNativeArchive(filename))
62 def IsNativeObject(filename):
63 return FileType(filename) == 'o'
66 def IsNativeDSO(filename):
67 return FileType(filename) == 'so'
70 def GetBitcodeMagic(filename):
71 fp = driver_log.DriverOpen(filename, 'rb')
73 driver_log.DriverClose(fp)
76 def IsLLVMBitcodeWrapperHeader(data):
77 return data[:4] == LLVM_WRAPPER_MAGIC
80 def IsLLVMWrappedBitcode(filename):
81 return IsLLVMBitcodeWrapperHeader(GetBitcodeMagic(filename))
83 def IsPNaClBitcodeHeader(data):
84 return data[:4] == PNACL_BITCODE_MAGIC
87 def IsPNaClBitcode(filename):
88 return IsPNaClBitcodeHeader(GetBitcodeMagic(filename))
90 def IsLLVMRawBitcodeHeader(data):
91 return data[:4] == LLVM_BITCODE_MAGIC
94 def IsLLVMBitcode(filename):
95 header = GetBitcodeMagic(filename)
96 return IsLLVMRawBitcodeHeader(header) or IsLLVMBitcodeWrapperHeader(header)
99 def IsArchive(filename):
100 return artools.IsArchive(filename)
103 def IsBitcodeArchive(filename):
104 filetype = FileType(filename)
105 return filetype == 'archive-bc'
108 def IsNativeArchive(filename):
109 return IsArchive(filename) and not IsBitcodeArchive(filename)
114 return elftools.IsELF(filename)
117 def GetELFType(filename):
118 """ ELF type as determined by ELF metadata """
119 assert(elftools.IsELF(filename))
120 elfheader = elftools.GetELFHeader(filename)
126 return elf_type_map[elfheader.type]
129 # Parses a linker script to determine additional ld arguments specified.
130 # Returns a list of linker arguments.
132 # For example, if the linker script contains
134 # GROUP ( libc.so.6 libc_nonshared.a AS_NEEDED ( ld-linux.so.2 ) )
136 # Then this function will return:
138 # ['--start-group', '-l:libc.so.6', '-l:libc_nonshared.a',
139 # '--as-needed', '-l:ld-linux.so.2', '--no-as-needed', '--end-group']
141 # Returns None on any parse error.
142 def ParseLinkerScript(filename):
143 fp = driver_log.DriverOpen(filename, 'r')
147 expect = '' # Expected next token
149 token = GetNextToken(fp)
169 elif token == 'GROUP':
171 ret.append('--start-group')
173 elif token == 'OUTPUT_FORMAT':
176 elif token == 'EXTERN':
185 section = stack.pop()
186 if section == 'AS_NEEDED':
187 ret.append('--no-as-needed')
188 elif section == 'GROUP':
189 ret.append('--end-group')
190 elif token == 'AS_NEEDED':
192 ret.append('--as-needed')
193 stack.append('AS_NEEDED')
194 elif stack[-1] == 'OUTPUT_FORMAT':
195 # Ignore stuff inside OUTPUT_FORMAT
197 elif stack[-1] == 'EXTERN':
198 ret.append('--undefined=' + token)
200 ret.append('-l:' + token)
206 # Get the next token from the linker script
207 # Returns: '' for EOF.
209 def GetNextToken(fp):
217 # Whitespace terminates a token
218 # (but ignore whitespace before the token)
219 if ch in (' ', '\t', '\n'):
225 # ( and ) are tokens themselves (or terminate existing tokens)
228 fp.seek(-1, os.SEEK_CUR)
235 if token.endswith('/*'):
236 if not ReadPastComment(fp, '*/'):
242 def ReadPastComment(fp, terminator):
249 if s.endswith(terminator):
254 def IsLinkerScript(filename):
255 _, ext = os.path.splitext(filename)
256 return (len(ext) > 0 and ext[1:] in ('o', 'so', 'a', 'po', 'pa', 'x')
257 and not IsELF(filename)
258 and not IsArchive(filename)
259 and not IsLLVMBitcode(filename)
260 and ParseLinkerScript(filename) is not None)
263 # If FORCED_FILE_TYPE is set, FileType() will return FORCED_FILE_TYPE for all
264 # future input files. This is useful for the "as" incarnation, which
265 # needs to accept files of any extension and treat them as ".s" (or ".ll")
266 # files. Also useful for gcc's "-x", which causes all files between the
267 # current -x and the next -x to be treated in a certain way.
268 FORCED_FILE_TYPE = None
269 def SetForcedFileType(t):
270 global FORCED_FILE_TYPE
273 def GetForcedFileType():
274 return FORCED_FILE_TYPE
276 def ForceFileType(filename, newtype = None):
278 if FORCED_FILE_TYPE is None:
280 newtype = FORCED_FILE_TYPE
281 FileType.OverrideValue(newtype, filename)
283 def ClearFileTypeCaches():
284 """ Clear caches for all filetype functions (externally they must all be
285 cleared together because they can call each other)
287 SimpleCache.ClearAllCaches()
289 # File Extension -> Type string
290 # TODO(pdox): Add types for sources which should not be preprocessed.
293 'i' : 'c', # C, but should not be preprocessed.
302 'ii' : 'c++', # C++, but should not be preprocessed.
305 'hpp' : 'c++-header',
307 'm' : 'objc', # .m = "Objective-C source file"
311 'po' : 'po', # .po = "Portable object file"
312 'pexe': 'pexe', # .pexe = "Portable executable"
323 def IsSourceType(filetype):
324 return filetype in ('c','c++','objc')
326 def IsHeaderType(filetype):
327 return filetype in ('c-header', 'c++-header')
329 # The SimpleCache decorator is required for correctness, due to the
330 # ForceFileType mechanism.
332 def FileType(filename):
333 # Auto-detect bitcode files, since we can't rely on extensions
334 ext = filename.split('.')[-1]
336 # TODO(pdox): We open and read the the first few bytes of each file
337 # up to 4 times, when we only need to do it once. The
338 # OS cache prevents us from hitting the disk, but this
339 # is still slower than it needs to be.
340 if IsArchive(filename):
341 return artools.GetArchiveType(filename)
343 if elftools.IsELF(filename):
344 return GetELFType(filename)
346 # If this is LLVM bitcode, we don't have a good way of determining if it
347 # is an object file or a non-finalized program, so just say 'po' for now.
348 if IsLLVMBitcode(filename):
351 if IsPNaClBitcode(filename):
354 if IsLinkerScript(filename):
357 # Use the file extension if it is recognized
358 if ext in ExtensionMap:
359 return ExtensionMap[ext]
361 driver_log.Log.Fatal('%s: Unrecognized file type', filename)
363 # Map from GCC's -x file types and this driver's file types.
368 'assembler-with-cpp': 'S',
369 'c-header' : 'c-header',
370 'c++-header' : 'c++-header',
372 FILE_TYPE_MAP_REVERSE = dict([reversed(_tmp) for _tmp in FILE_TYPE_MAP.items()])
374 def FileTypeToGCCType(filetype):
375 return FILE_TYPE_MAP_REVERSE[filetype]
377 def GCCTypeToFileType(gcctype):
378 if gcctype not in FILE_TYPE_MAP:
379 driver_log.Log.Fatal('language "%s" not recognized' % gcctype)
380 return FILE_TYPE_MAP[gcctype]