| OLD | NEW |
| (Empty) |
| 1 #!/usr/bin/python | |
| 2 # Copyright (c) 2011 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. | |
| 5 # | |
| 6 # IMPORTANT NOTE: If you make local mods to this file, you must run: | |
| 7 # % tools/llvm/utman.sh driver | |
| 8 # in order for them to take effect in the scons build. This command | |
| 9 # updates the copy in the toolchain/ tree. | |
| 10 # | |
| 11 | |
| 12 import os | |
| 13 import re | |
| 14 import subprocess | |
| 15 import sys | |
| 16 import signal | |
| 17 import platform | |
| 18 import threading | |
| 19 import Queue | |
| 20 import artools | |
| 21 import ldtools | |
| 22 import pathtools | |
| 23 | |
| 24 ###################################################################### | |
| 25 # | |
| 26 # Environment Settings | |
| 27 # | |
| 28 ###################################################################### | |
| 29 | |
| 30 # This dictionary initializes a shell-like environment. | |
| 31 # Shell escaping and ${} substitution are provided. | |
| 32 # See "class env" defined later for the implementation. | |
| 33 | |
| 34 INITIAL_ENV = { | |
| 35 'BASE_NACL' : '${@FindBaseNaCl}', # Absolute path of native_client/ | |
| 36 'BASE' : '${@FindBasePNaCl}', # Absolute path to PNaCl | |
| 37 'BASE_DRIVER' : '${@FindBaseDriver}', # Location of PNaCl drivers | |
| 38 'BUILD_OS' : '${@GetBuildOS}', # "linux", "darwin" or "windows" | |
| 39 'BUILD_ARCH' : '${@GetBuildArch}', # "x86_64" or "i686" or "i386" | |
| 40 'DRIVER_EXT' : '${@GetDriverExt}', # '.py' or '' | |
| 41 | |
| 42 # Directories | |
| 43 'BASE_PKG' : '${BASE}/pkg', | |
| 44 'BASE_SDK' : '${BASE}/sdk', | |
| 45 'BASE_LLVM' : '${BASE_PKG}/llvm', | |
| 46 'BASE_LLVM_GCC' : '${BASE_PKG}/llvm-gcc', | |
| 47 'BASE_NEWLIB' : '${BASE_PKG}/newlib', | |
| 48 'BASE_GLIBC' : '${BASE_PKG}/glibc', | |
| 49 'BASE_BINUTILS' : '${BASE_PKG}/binutils', | |
| 50 'BASE_LIBSTDCPP' : '${BASE_PKG}/libstdcpp', | |
| 51 'BASE_SYSROOT' : '${BASE}/sysroot', | |
| 52 'BASE_GCC' : '${BASE_PKG}/gcc', | |
| 53 | |
| 54 'BASE_INCLUDE' : '${BASE_SYSROOT}/include', | |
| 55 'BASE_LLVM_BIN' : '${BASE_LLVM}/bin', | |
| 56 'BASE_BIN' : '${BASE}/bin', | |
| 57 'BASE_SB' : '${BASE_SB_%ARCH%}', | |
| 58 'BASE_SB_X8632' : '${BASE}/tools-sb/x8632', | |
| 59 'BASE_SB_X8664' : '${BASE}/tools-sb/x8664', | |
| 60 'BASE_SB_ARM' : '${BASE}/tools-sb/arm', | |
| 61 'SCONS_OUT' : '${BASE_NACL}/scons-out', | |
| 62 | |
| 63 # Driver settings | |
| 64 'ARCH' : '', # Target architecture | |
| 65 | |
| 66 'DRY_RUN' : '0', | |
| 67 'DEBUG' : '0', # Print out internal actions | |
| 68 'RECURSE' : '0', # In a recursive driver call | |
| 69 'SAVE_TEMPS' : '1', # Do not clean up temporary files | |
| 70 # TODO(pdox): Disable for SDK version | |
| 71 'SANDBOXED' : '0', # Use sandboxed toolchain for this arch. (main switch) | |
| 72 'SRPC' : '1', # Use SRPC sandboxed toolchain | |
| 73 'MC_DIRECT' : '1', | |
| 74 'USE_EMULATOR' : '0', | |
| 75 'DRIVER_FLAGS' : '', # Flags passed to the driver | |
| 76 'LIBMODE' : '${@DetectLibMode}', # glibc or newlib | |
| 77 'LIBMODE_GLIBC' : '${LIBMODE==glibc ? 1 : 0}', | |
| 78 'LIBMODE_NEWLIB' : '${LIBMODE==newlib ? 1 : 0}', | |
| 79 | |
| 80 # Logging settings | |
| 81 'LOG_TO_FILE' : '1', | |
| 82 'LOG_FILENAME' : '${BASE}/driver.log', | |
| 83 'LOG_FILE_SIZE_LIMIT' : str(20 * 1024 * 1024), | |
| 84 'LOG_PRETTY_PRINT' : '1', | |
| 85 | |
| 86 # Conventions | |
| 87 'SO_EXT' : '${SO_EXT_%BUILD_OS%}', | |
| 88 'SO_EXT_darwin' : '.dylib', | |
| 89 'SO_EXT_linux' : '.so', | |
| 90 'SO_EXT_windows' : '.dll', | |
| 91 | |
| 92 'SO_DIR' : '${SO_DIR_%BUILD_OS%}', | |
| 93 'SO_DIR_darwin' : 'lib', | |
| 94 'SO_DIR_linux' : 'lib', | |
| 95 'SO_DIR_windows' : 'bin', # On Windows, DLLs are placed in bin/ | |
| 96 # because the dynamic loader searches %PATH% | |
| 97 | |
| 98 'SO_PREFIX' : '${SO_PREFIX_%BUILD_OS%}', | |
| 99 'SO_PREFIX_darwin' : 'lib', | |
| 100 'SO_PREFIX_linux' : 'lib', | |
| 101 'SO_PREFIX_windows': 'cyg', | |
| 102 | |
| 103 'EXEC_EXT' : '${EXEC_EXT_%BUILD_OS%}', | |
| 104 'EXEC_EXT_darwin' : '', | |
| 105 'EXEC_EXT_linux' : '', | |
| 106 'EXEC_EXT_windows': '.exe', | |
| 107 | |
| 108 'SCONS_OS' : '${SCONS_OS_%BUILD_OS%}', | |
| 109 'SCONS_OS_linux' : 'linux', | |
| 110 'SCONS_OS_darwin' : 'mac', | |
| 111 'SCONS_OS_windows' : 'win', | |
| 112 | |
| 113 # Tool Pathnames | |
| 114 'GOLD_PLUGIN_SO' : '${BASE_LLVM}/${SO_DIR}/${SO_PREFIX}LLVMgold${SO_EXT}', | |
| 115 'DRAGONEGG_PLUGIN': '${BASE_GCC}/lib/dragonegg${SO_EXT}', | |
| 116 | |
| 117 'SCONS_STAGING' : '${SCONS_STAGING_%ARCH%}', | |
| 118 'SCONS_STAGING_X8632' : '${SCONS_OUT}/opt-${SCONS_OS}-x86-32/staging', | |
| 119 'SCONS_STAGING_X8664' : '${SCONS_OUT}/opt-${SCONS_OS}-x86-64/staging', | |
| 120 'SCONS_STAGING_ARM' : '${SCONS_OUT}/opt-${SCONS_OS}-arm/staging', | |
| 121 | |
| 122 'SEL_UNIVERSAL_PREFIX': '${USE_EMULATOR ? ${EMULATOR}}', | |
| 123 'SEL_UNIVERSAL' : '${SCONS_STAGING}/sel_universal${EXEC_EXT}', | |
| 124 'SEL_UNIVERSAL_FLAGS' : '--abort_on_error -B ${IRT_BLOB} ' + | |
| 125 '${USE_EMULATOR ? -Q --command_prefix ${EMULATOR}}', | |
| 126 | |
| 127 'IRT_STAGING' : '${IRT_STAGING_%ARCH%}', | |
| 128 'IRT_STAGING_X8632' : '${SCONS_OUT}/nacl_irt-x86-32/staging', | |
| 129 'IRT_STAGING_X8664' : '${SCONS_OUT}/nacl_irt-x86-64/staging', | |
| 130 'IRT_STAGING_ARM' : '${SCONS_OUT}/nacl_irt-arm/staging', | |
| 131 # The irt_core.nexe should suffice for the sandboxed translators, since | |
| 132 # they do not use PPAPI. | |
| 133 'IRT_BLOB' : '${IRT_STAGING}/irt_core.nexe', | |
| 134 | |
| 135 'EMULATOR' : '${EMULATOR_%ARCH%}', | |
| 136 'EMULATOR_X8632' : '', | |
| 137 'EMULATOR_X8664' : '', | |
| 138 # NOTE: this is currently the only dependency on the arm trusted TC | |
| 139 'EMULATOR_ARM' : | |
| 140 '${BASE_NACL}/toolchain/linux_arm-trusted/run_under_qemu_arm', | |
| 141 | |
| 142 'SEL_LDR' : '${SCONS_STAGING}/sel_ldr${EXEC_EXT}', | |
| 143 | |
| 144 # c.f. http://code.google.com/p/nativeclient/issues/detail?id=1968 | |
| 145 # LLVM_AS is used to compile a .ll file to a bitcode file (".po") | |
| 146 'LLVM_AS' : '${BASE_LLVM_BIN}/llvm-as${EXEC_EXT}', | |
| 147 # LLVM_MC is llvm's replacement for bintutil's as | |
| 148 'LLVM_MC' : '${BASE_LLVM_BIN}/llvm-mc${EXEC_EXT}', | |
| 149 | |
| 150 # c.f. http://code.google.com/p/nativeclient/issues/detail?id=1968 | |
| 151 'AS_ARM' : '${BINUTILS_BASE}as', | |
| 152 'AS_X8632' : '${LLVM_MC}', | |
| 153 'AS_X8664' : '${LLVM_MC}', | |
| 154 | |
| 155 'LD_SB' : '${SEL_LDR} -a -B ${IRT_BLOB} -- ${BASE_SB}/nonsrpc/bin/ld', | |
| 156 'LLC_SB' : '${SEL_LDR} -a -B ${IRT_BLOB} -- ${RUNNABLE_LD} ' + | |
| 157 '${BASE_SB}/nonsrpc/bin/llc', | |
| 158 'SB_DYNAMIC' : '0', | |
| 159 'NNACL_LIBDIR' : '${BASE_NACL}/toolchain/${SCONS_OS}_x86/' + | |
| 160 'x86_64-nacl/${ARCH == X8632 ? lib32 : lib}', | |
| 161 'RUNNABLE_LD' : '${SB_DYNAMIC ? ${NNACL_LIBDIR}/runnable-ld.so ' + | |
| 162 '--library-path ${NNACL_LIBDIR}}', | |
| 163 | |
| 164 | |
| 165 'LLC_SRPC' : '${BASE_SB}/srpc/bin/llc', | |
| 166 'LD_SRPC' : '${BASE_SB}/srpc/bin/ld', | |
| 167 | |
| 168 'LLVM_GCC_PREFIX': '${BASE_LLVM_GCC}/bin/arm-none-linux-gnueabi-', | |
| 169 'LLVM_GCC' : '${LLVM_GCC_PREFIX}gcc${EXEC_EXT}', | |
| 170 'LLVM_GXX' : '${LLVM_GCC_PREFIX}g++${EXEC_EXT}', | |
| 171 | |
| 172 'CLANG' : '${BASE_LLVM_BIN}/clang${EXEC_EXT}', | |
| 173 'CLANGXX' : '${BASE_LLVM_BIN}/clang++${EXEC_EXT}', | |
| 174 | |
| 175 'DRAGONEGG_GCC' : '${BASE_GCC}/bin/i686-unknown-linux-gnu-gcc', | |
| 176 'DRAGONEGG_GXX' : '${BASE_GCC}/bin/i686-unknown-linux-gnu-g++', | |
| 177 | |
| 178 'LLVM_OPT' : '${BASE_LLVM_BIN}/opt${EXEC_EXT}', | |
| 179 'LLVM_LLC' : '${BASE_LLVM_BIN}/llc${EXEC_EXT}', | |
| 180 'LLVM_LD' : '${BASE_LLVM_BIN}/llvm-ld${EXEC_EXT}', | |
| 181 'LLVM_DIS' : '${BASE_LLVM_BIN}/llvm-dis${EXEC_EXT}', | |
| 182 'LLVM_LINK' : '${BASE_LLVM_BIN}/llvm-link${EXEC_EXT}', | |
| 183 | |
| 184 'BINUTILS_BASE' : '${BASE_BINUTILS}/bin/arm-pc-nacl-', | |
| 185 'OBJDUMP' : '${BINUTILS_BASE}objdump${EXEC_EXT}', | |
| 186 'NM' : '${BINUTILS_BASE}nm${EXEC_EXT}', | |
| 187 'AR' : '${BINUTILS_BASE}ar${EXEC_EXT}', | |
| 188 'RANLIB' : '${BINUTILS_BASE}ranlib${EXEC_EXT}', | |
| 189 'STRIP' : '${BINUTILS_BASE}strip${EXEC_EXT}', | |
| 190 | |
| 191 'LD_BFD' : '${BINUTILS_BASE}ld.bfd${EXEC_EXT}', | |
| 192 'LD_GOLD' : '${BINUTILS_BASE}ld.gold${EXEC_EXT}', | |
| 193 } | |
| 194 | |
| 195 | |
| 196 | |
| 197 | |
| 198 DriverPatterns = [ | |
| 199 ( '--pnacl-driver-verbose', "Log.LOG_OUT.append(sys.stderr)"), | |
| 200 ( '--pnacl-driver-debug', "env.set('DEBUG', '1')"), | |
| 201 ( '--pnacl-driver-recurse', "env.set('RECURSE', '1')"), | |
| 202 ( '--pnacl-driver-set-([^=]+)=(.*)', "env.set($0, $1)"), | |
| 203 ( '--pnacl-driver-append-([^=]+)=(.*)', "env.append($0, $1)"), | |
| 204 ( ('-arch', '(.+)'), "SetArch($0)"), | |
| 205 ( '--pnacl-sb', "env.set('SANDBOXED', '1')"), | |
| 206 ( '--pnacl-sb-dynamic', "env.set('SB_DYNAMIC', '1')"), | |
| 207 ( '--pnacl-use-emulator', "env.set('USE_EMULATOR', '1')"), | |
| 208 ( '--dry-run', "env.set('DRY_RUN', '1')"), | |
| 209 ( '--pnacl-arm-bias', "env.set('BIAS', 'ARM')"), | |
| 210 ( '--pnacl-i686-bias', "env.set('BIAS', 'X8632')"), | |
| 211 ( '--pnacl-x86_64-bias', "env.set('BIAS', 'X8664')"), | |
| 212 ( '--pnacl-bias=(.+)', "env.set('BIAS', FixArch($0))"), | |
| 213 ( '-save-temps', "env.set('SAVE_TEMPS', '1')"), | |
| 214 ( '-no-save-temps', "env.set('SAVE_TEMPS', '0')"), | |
| 215 ] | |
| 216 | |
| 217 | |
| 218 ###################################################################### | |
| 219 # | |
| 220 # Environment | |
| 221 # | |
| 222 ###################################################################### | |
| 223 | |
| 224 # TODO(pdox): Move the environment & parser into a separate .py file | |
| 225 class env(object): | |
| 226 data = {} | |
| 227 stack = [] | |
| 228 functions = {} | |
| 229 | |
| 230 @classmethod | |
| 231 def register(cls, func): | |
| 232 """ Register a function for use in the evaluator """ | |
| 233 cls.functions[func.__name__] = func | |
| 234 return func | |
| 235 | |
| 236 @classmethod | |
| 237 def update(cls, extra): | |
| 238 INITIAL_ENV.update(extra) | |
| 239 assert(cls) # This makes the pychecker "unused variable" warning go away | |
| 240 | |
| 241 @classmethod | |
| 242 def reset(cls): | |
| 243 cls.data = dict(INITIAL_ENV) | |
| 244 | |
| 245 @classmethod | |
| 246 def dump(cls): | |
| 247 for (k,v) in cls.data.iteritems(): | |
| 248 print '%s == %s' % (k,v) | |
| 249 | |
| 250 @classmethod | |
| 251 def push(cls): | |
| 252 cls.stack.append(cls.data) | |
| 253 cls.data = dict(cls.data) # Make a copy | |
| 254 | |
| 255 @classmethod | |
| 256 def pop(cls): | |
| 257 cls.data = cls.stack.pop() | |
| 258 | |
| 259 @classmethod | |
| 260 def has(cls, varname): | |
| 261 return varname in cls.data | |
| 262 | |
| 263 @classmethod | |
| 264 def getraw(cls, varname): | |
| 265 return cls.eval(cls.data[varname]) | |
| 266 | |
| 267 # Evaluate a variable from the environment. | |
| 268 # Returns a list of terms. | |
| 269 @classmethod | |
| 270 def get(cls, varname): | |
| 271 return shell.split(cls.getraw(varname)) | |
| 272 | |
| 273 # Retrieve a variable from the environment which | |
| 274 # is a single term. Returns a string. | |
| 275 @classmethod | |
| 276 def getone(cls, varname): | |
| 277 return shell.unescape(cls.getraw(varname)) | |
| 278 | |
| 279 @classmethod | |
| 280 def getbool(cls, varname): | |
| 281 return bool(int(cls.getone(varname))) | |
| 282 | |
| 283 # Set a variable in the environment without shell-escape | |
| 284 @classmethod | |
| 285 def setraw(cls, varname, val): | |
| 286 cls.data[varname] = val | |
| 287 | |
| 288 # Set one or more variables using named arguments | |
| 289 @classmethod | |
| 290 def setmany(cls, **kwargs): | |
| 291 for k,v in kwargs.iteritems(): | |
| 292 if isinstance(v, str): | |
| 293 cls.set(k, v) | |
| 294 else: | |
| 295 cls.set(k, *v) | |
| 296 | |
| 297 @classmethod | |
| 298 def clear(cls, varname): | |
| 299 cls.data[varname] = '' | |
| 300 | |
| 301 # Set a variable to one or more terms, applying shell-escape. | |
| 302 @classmethod | |
| 303 def set(cls, varname, *vals): | |
| 304 cls.clear(varname) | |
| 305 cls.append(varname, *vals) | |
| 306 | |
| 307 # Append one or more terms to a variable in the | |
| 308 # environment, applying shell-escape. | |
| 309 @classmethod | |
| 310 def append(cls, varname, *vals): | |
| 311 escaped = [ shell.escape(v) for v in vals ] | |
| 312 if len(cls.data[varname]) > 0: | |
| 313 cls.data[varname] += ' ' | |
| 314 cls.data[varname] += ' '.join(escaped) | |
| 315 | |
| 316 # Evaluate an expression s | |
| 317 @classmethod | |
| 318 def eval(cls, s): | |
| 319 (result, i) = cls.eval_expr(s, 0, []) | |
| 320 assert(i == len(s)) | |
| 321 return result | |
| 322 | |
| 323 ###################################################################### | |
| 324 # EXPRESSION EVALUATION CODE | |
| 325 # Context Free Grammar: | |
| 326 # | |
| 327 # str = empty | string literal | |
| 328 # expr = str | expr '$' '{' bracket_expr '}' expr | |
| 329 # bracket_expr = varname | boolexpr ? expr | boolexpr ? expr : expr | @call | |
| 330 # boolexpr = boolval | boolval '&&' boolexpr | boolval '||' boolexpr | |
| 331 # boolval = varname | !varname | #varname | !#varname | varname '==' str | |
| 332 # varname = str | varname '%' bracket_expr '%' varname | |
| 333 # call = func | func ':' arglist | |
| 334 # func = str | |
| 335 # arglist = empty | arg ':' arglist | |
| 336 # | |
| 337 # Do not call these functions outside of class env. | |
| 338 # The env.eval method is the external interface to the evaluator. | |
| 339 ###################################################################### | |
| 340 | |
| 341 # Evaluate a string literal | |
| 342 @classmethod | |
| 343 def eval_str(cls, s, pos, terminators): | |
| 344 (_,i) = FindFirst(s, pos, terminators) | |
| 345 return (s[pos:i], i) | |
| 346 | |
| 347 # Evaluate %var% substitutions inside a variable name. | |
| 348 # Returns (the_actual_variable_name, endpos) | |
| 349 # Terminated by } character | |
| 350 @classmethod | |
| 351 def eval_varname(cls, s, pos, terminators): | |
| 352 (_,i) = FindFirst(s, pos, ['%'] + terminators) | |
| 353 leftpart = s[pos:i].strip(' ') | |
| 354 if i == len(s) or s[i] in terminators: | |
| 355 return (leftpart, i) | |
| 356 | |
| 357 (middlepart, j) = cls.eval_bracket_expr(s, i+1, ['%']) | |
| 358 if j == len(s) or s[j] != '%': | |
| 359 ParseError(s, i, j, "Unterminated %") | |
| 360 | |
| 361 (rightpart, k) = cls.eval_varname(s, j+1, terminators) | |
| 362 | |
| 363 fullname = leftpart + middlepart + rightpart | |
| 364 fullname = fullname.strip() | |
| 365 return (fullname, k) | |
| 366 | |
| 367 # Absorb whitespace | |
| 368 @classmethod | |
| 369 def eval_whitespace(cls, s, pos): | |
| 370 i = pos | |
| 371 while i < len(s) and s[i] == ' ': | |
| 372 i += 1 | |
| 373 return (None, i) | |
| 374 | |
| 375 @classmethod | |
| 376 def eval_bool_val(cls, s, pos, terminators): | |
| 377 (_,i) = cls.eval_whitespace(s, pos) | |
| 378 | |
| 379 if s[i] == '!': | |
| 380 negated = True | |
| 381 i += 1 | |
| 382 else: | |
| 383 negated = False | |
| 384 | |
| 385 (_,i) = cls.eval_whitespace(s, i) | |
| 386 | |
| 387 if s[i] == '#': | |
| 388 uselen = True | |
| 389 i += 1 | |
| 390 else: | |
| 391 uselen = False | |
| 392 | |
| 393 (varname, j) = cls.eval_varname(s, i, ['=']+terminators) | |
| 394 if j == len(s): | |
| 395 # This is an error condition one level up. Don't evaluate anything. | |
| 396 return (False, j) | |
| 397 | |
| 398 if varname not in cls.data: | |
| 399 ParseError(s, i, j, "Undefined variable '%s'" % varname) | |
| 400 vardata = cls.data[varname] | |
| 401 | |
| 402 contents = cls.eval(vardata) | |
| 403 | |
| 404 if s[j] == '=': | |
| 405 # String equality test | |
| 406 if j+1 == len(s) or s[j+1] != '=': | |
| 407 ParseError(s, j, j, "Unexpected token") | |
| 408 if uselen: | |
| 409 ParseError(s, j, j, "Cannot combine == and #") | |
| 410 (_,j) = cls.eval_whitespace(s, j+2) | |
| 411 (literal_str,j) = cls.eval_str(s, j, [' ']+terminators) | |
| 412 (_,j) = cls.eval_whitespace(s, j) | |
| 413 if j == len(s): | |
| 414 return (False, j) # Error one level up | |
| 415 else: | |
| 416 literal_str = None | |
| 417 | |
| 418 if uselen: | |
| 419 val = (len(contents) != 0) | |
| 420 elif literal_str is not None: | |
| 421 val = (contents == literal_str) | |
| 422 else: | |
| 423 if contents not in ('0','1'): | |
| 424 ParseError(s, j, j, | |
| 425 "%s evaluated to %s, which is not a boolean!" % (varname, contents)) | |
| 426 val = bool(int(contents)) | |
| 427 return (negated ^ val, j) | |
| 428 | |
| 429 # Evaluate a boolexpr | |
| 430 @classmethod | |
| 431 def eval_bool_expr(cls, s, pos, terminators): | |
| 432 (boolval1, i) = cls.eval_bool_val(s, pos, ['&','|']+terminators) | |
| 433 if i == len(s): | |
| 434 # This is an error condition one level up. Don't evaluate anything. | |
| 435 return (False, i) | |
| 436 | |
| 437 if s[i] in ('&','|'): | |
| 438 # and/or expression | |
| 439 if i+1 == len(s) or s[i+1] != s[i]: | |
| 440 ParseError(s, i, i, "Unexpected token") | |
| 441 is_and = (s[i] == '&') | |
| 442 | |
| 443 (boolval2, j) = cls.eval_bool_expr(s, i+2, terminators) | |
| 444 if j == len(s): | |
| 445 # This is an error condition one level up. | |
| 446 return (False, j) | |
| 447 | |
| 448 if is_and: | |
| 449 return (boolval1 and boolval2, j) | |
| 450 else: | |
| 451 return (boolval1 or boolval2, j) | |
| 452 | |
| 453 return (boolval1, i) | |
| 454 | |
| 455 # Evaluate the inside of a ${} or %%. | |
| 456 # Returns the (the_evaluated_string, endpos) | |
| 457 @classmethod | |
| 458 def eval_bracket_expr(cls, s, pos, terminators): | |
| 459 (_,pos) = cls.eval_whitespace(s, pos) | |
| 460 | |
| 461 if s[pos] == '@': | |
| 462 # Function call: ${@func} | |
| 463 # or possibly : ${@func:arg1:arg2...} | |
| 464 (_,i) = FindFirst(s, pos, [':']+terminators) | |
| 465 if i == len(s): | |
| 466 return ('', i) # Error one level up | |
| 467 funcname = s[pos+1:i] | |
| 468 | |
| 469 if s[i] != ':': | |
| 470 j = i | |
| 471 args = [] | |
| 472 else: | |
| 473 (_,j) = FindFirst(s, i+1, terminators) | |
| 474 if j == len(s): | |
| 475 return ('', j) # Error one level up | |
| 476 args = s[i+1:j].split(':') | |
| 477 | |
| 478 val = cls.functions[funcname](*args) | |
| 479 contents = cls.eval(val) | |
| 480 return (contents, j) | |
| 481 | |
| 482 (m,_) = FindFirst(s, pos, ['?']+terminators) | |
| 483 if m != '?': | |
| 484 # Regular variable substitution | |
| 485 (varname,i) = cls.eval_varname(s, pos, terminators) | |
| 486 if len(s) == i: | |
| 487 return ('', i) # Error one level up | |
| 488 if varname not in cls.data: | |
| 489 ParseError(s, pos, i, "Undefined variable '%s'" % varname) | |
| 490 vardata = cls.data[varname] | |
| 491 contents = cls.eval(vardata) | |
| 492 return (contents, i) | |
| 493 else: | |
| 494 # Ternary Mode | |
| 495 (is_cond_true,i) = cls.eval_bool_expr(s, pos, ['?']+terminators) | |
| 496 assert(i < len(s) and s[i] == '?') | |
| 497 | |
| 498 (if_true_expr, j) = cls.eval_expr(s, i+1, [':']+terminators) | |
| 499 if j == len(s): | |
| 500 return ('', j) # Error one level up | |
| 501 | |
| 502 if s[j] == ':': | |
| 503 (if_false_expr,j) = cls.eval_expr(s, j+1, terminators) | |
| 504 if j == len(s): | |
| 505 # This is an error condition one level up. | |
| 506 return ('', j) | |
| 507 else: | |
| 508 if_false_expr = '' | |
| 509 | |
| 510 if is_cond_true: | |
| 511 contents = if_true_expr.strip() | |
| 512 else: | |
| 513 contents = if_false_expr.strip() | |
| 514 | |
| 515 return (contents, j) | |
| 516 | |
| 517 # Evaluate an expression with ${} in string s, starting at pos. | |
| 518 # Returns (the_evaluated_expression, endpos) | |
| 519 @classmethod | |
| 520 def eval_expr(cls, s, pos, terminators): | |
| 521 (m,i) = FindFirst(s, pos, ['${'] + terminators) | |
| 522 leftpart = s[pos:i] | |
| 523 if i == len(s) or m in terminators: | |
| 524 return (leftpart, i) | |
| 525 | |
| 526 (middlepart, j) = cls.eval_bracket_expr(s, i+2, ['}']) | |
| 527 if j == len(s) or s[j] != '}': | |
| 528 ParseError(s, i, j, 'Unterminated ${') | |
| 529 | |
| 530 (rightpart, k) = cls.eval_expr(s, j+1, terminators) | |
| 531 return (leftpart + middlepart + rightpart, k) | |
| 532 | |
| 533 ###################################################################### | |
| 534 # END EXPRESSION EVALUATION CODE | |
| 535 ###################################################################### | |
| 536 | |
| 537 def ParseError(s, leftpos, rightpos, msg): | |
| 538 Log.Error("Parse Error: %s", msg); | |
| 539 Log.Error(' ' + s); | |
| 540 Log.Error(' ' + (' '*leftpos) + ('^'*(rightpos - leftpos + 1))) | |
| 541 DriverExit(1) | |
| 542 | |
| 543 # Run a command with extra environment settings | |
| 544 def RunWithEnv(cmd, **kwargs): | |
| 545 RunWithLogArgs = { } | |
| 546 if 'RunWithLogArgs' in kwargs: | |
| 547 RunWithLogArgs = kwargs['RunWithLogArgs'] | |
| 548 del kwargs['RunWithLogArgs'] | |
| 549 | |
| 550 env.push() | |
| 551 env.setmany(**kwargs) | |
| 552 RunWithLog(cmd, **RunWithLogArgs) | |
| 553 env.pop() | |
| 554 | |
| 555 def RunDriver(invocation, args, suppress_arch = False): | |
| 556 if isinstance(args, str): | |
| 557 args = shell.split(env.eval(args)) | |
| 558 | |
| 559 script = env.eval('${BASE_DRIVER}/%s${DRIVER_EXT}' % invocation) | |
| 560 script = shell.unescape(script) | |
| 561 | |
| 562 driver_args = env.get('DRIVER_FLAGS') | |
| 563 | |
| 564 if '--pnacl-driver-recurse' not in driver_args: | |
| 565 driver_args.append('--pnacl-driver-recurse') | |
| 566 | |
| 567 # Get rid of -arch <arch> in the driver flags. | |
| 568 if suppress_arch: | |
| 569 while '-arch' in driver_args: | |
| 570 i = driver_args.index('-arch') | |
| 571 driver_args = driver_args[:i] + driver_args[i+2:] | |
| 572 | |
| 573 python_interpreter = pathtools.normalize(sys.executable) | |
| 574 script = pathtools.tosys(script) | |
| 575 cmd = [python_interpreter, script] + driver_args + args | |
| 576 | |
| 577 # The invoked driver will do it's own logging, so | |
| 578 # don't use RunWithLog() for the recursive driver call. | |
| 579 # Use Run() so that the subprocess's stdout/stderr | |
| 580 # will go directly to the real stdout/stderr. | |
| 581 Log.Debug('-' * 60) | |
| 582 Log.Debug('\n' + StringifyCommand(cmd)) | |
| 583 Run(cmd) | |
| 584 | |
| 585 # Find the leftmost position in "s" which begins a substring | |
| 586 # in "strset", starting at "pos". | |
| 587 # For example: | |
| 588 # FindFirst('hello world', 0, ['h','o']) = ('h', 0) | |
| 589 # FindFirst('hello world', 1, ['h','o']) = ('o', 4) | |
| 590 # FindFirst('hello world', 0, ['x']) = (None,11) | |
| 591 def FindFirst(s, pos, strset): | |
| 592 m = {} | |
| 593 for ss in strset: | |
| 594 m[s.find(ss, pos)] = ss | |
| 595 if -1 in m: | |
| 596 del m[-1] | |
| 597 if len(m) == 0: | |
| 598 return (None, len(s)) | |
| 599 pos = min(m) | |
| 600 return (m[pos], pos) | |
| 601 | |
| 602 def memoize(f): | |
| 603 """ Memoize a function with no arguments """ | |
| 604 saved = {} | |
| 605 def newf(): | |
| 606 if len(saved) == 0: | |
| 607 saved[None] = f() | |
| 608 return saved[None] | |
| 609 newf.__name__ = f.__name__ | |
| 610 return newf | |
| 611 | |
| 612 | |
| 613 @env.register | |
| 614 @memoize | |
| 615 def GetBuildOS(): | |
| 616 name = platform.system().lower() | |
| 617 if name.startswith('cygwin_nt') or 'windows' in name: | |
| 618 name = 'windows' | |
| 619 if name not in ('linux', 'darwin', 'windows'): | |
| 620 Log.Fatal("Unsupported platform '%s'", name) | |
| 621 return name | |
| 622 | |
| 623 @env.register | |
| 624 @memoize | |
| 625 def GetBuildArch(): | |
| 626 m = platform.machine() | |
| 627 | |
| 628 # Windows is special | |
| 629 if m == 'x86': | |
| 630 m = 'i686' | |
| 631 | |
| 632 if m not in ('i386', 'i686', 'x86_64'): | |
| 633 Log.Fatal("Unsupported architecture '%s'", m) | |
| 634 return m | |
| 635 | |
| 636 # Crawl backwards, starting from the directory containing this script, | |
| 637 # until we find a directory satisfying a filter function. | |
| 638 def FindBaseDir(function): | |
| 639 Depth = 0 | |
| 640 cur = DriverPath() | |
| 641 while not function(cur) and Depth < 16: | |
| 642 cur = pathtools.dirname(cur) | |
| 643 Depth += 1 | |
| 644 if function(cur): | |
| 645 return cur | |
| 646 return None | |
| 647 | |
| 648 def DriverPath(): | |
| 649 return pathtools.abspath(pathtools.normalize(sys.argv[0])) | |
| 650 | |
| 651 @env.register | |
| 652 @memoize | |
| 653 def FindBaseNaCl(): | |
| 654 """ Find native_client/ directory """ | |
| 655 dir = FindBaseDir(lambda cur: pathtools.basename(cur) == 'native_client') | |
| 656 if dir is None: | |
| 657 Log.Fatal("Unable to find 'native_client' directory") | |
| 658 return shell.escape(dir) | |
| 659 | |
| 660 @env.register | |
| 661 @memoize | |
| 662 def FindBasePNaCl(): | |
| 663 """ Find the base directory of the PNaCl toolchain """ | |
| 664 | |
| 665 # Normally, the driver is in pnacl_*_*_*/bin/. | |
| 666 # But we can also be invoked from tools/llvm/driver. | |
| 667 # For now, default to using newlib when invoked from tools/llvm/driver. | |
| 668 bindir = pathtools.dirname(DriverPath()) | |
| 669 if pathtools.basename(bindir) == 'bin': | |
| 670 dir = pathtools.dirname(bindir) | |
| 671 return shell.escape(dir) | |
| 672 else: | |
| 673 # If we've invoked from tools/llvm/driver | |
| 674 return '${BASE_NACL}/toolchain/pnacl_${BUILD_OS}_${BUILD_ARCH}_${LIBMODE}' | |
| 675 | |
| 676 | |
| 677 @env.register | |
| 678 @memoize | |
| 679 def FindBaseDriver(): | |
| 680 """Find the directory with the driver""" | |
| 681 return shell.escape(pathtools.dirname(DriverPath())) | |
| 682 | |
| 683 @env.register | |
| 684 @memoize | |
| 685 def GetDriverExt(): | |
| 686 return pathtools.splitext(DriverPath())[1] | |
| 687 | |
| 688 @env.register | |
| 689 @memoize | |
| 690 def DetectLibMode(): | |
| 691 """ Determine if this is glibc or newlib """ | |
| 692 dir = pathtools.dirname(DriverPath()) | |
| 693 | |
| 694 is_newlib = pathtools.exists(pathtools.join(dir, 'newlib.cfg')) | |
| 695 is_glibc = pathtools.exists(pathtools.join(dir, 'glibc.cfg')) | |
| 696 | |
| 697 assert(not (is_newlib and is_glibc)) | |
| 698 | |
| 699 if is_newlib: | |
| 700 return "newlib" | |
| 701 | |
| 702 if is_glibc: | |
| 703 return "glibc" | |
| 704 | |
| 705 Log.Fatal("Cannot determine library mode!") | |
| 706 | |
| 707 @env.register | |
| 708 def AddPrefix(prefix, varname): | |
| 709 values = env.get(varname) | |
| 710 return ' '.join([prefix + shell.escape(v) for v in values ]) | |
| 711 | |
| 712 ###################################################################### | |
| 713 # | |
| 714 # Argument Parser | |
| 715 # | |
| 716 ###################################################################### | |
| 717 | |
| 718 def ParseArgs(argv, patternlist, must_match = True): | |
| 719 """ Parse argv using the patterns in patternlist | |
| 720 Returns: (matched, unmatched) | |
| 721 """ | |
| 722 matched = [] | |
| 723 unmatched = [] | |
| 724 i = 0 | |
| 725 while i < len(argv): | |
| 726 num_matched, action, groups = MatchOne(argv, i, patternlist) | |
| 727 if num_matched == 0: | |
| 728 if must_match: | |
| 729 Log.Fatal('Unrecognized argument: ' + argv[i]) | |
| 730 else: | |
| 731 unmatched.append(argv[i]) | |
| 732 i += 1 | |
| 733 continue | |
| 734 matched += argv[i:i+num_matched] | |
| 735 if isinstance(action, str): | |
| 736 # Perform $N substitution | |
| 737 for g in xrange(0, len(groups)): | |
| 738 action = action.replace('$%d' % g, 'groups[%d]' % g) | |
| 739 try: | |
| 740 if isinstance(action, str): | |
| 741 # NOTE: this is essentially an eval for python expressions | |
| 742 # which does rely on the current environment for unbound vars | |
| 743 # Log.Info('about to exec [%s]', str(action)) | |
| 744 exec(action) | |
| 745 else: | |
| 746 action(*groups) | |
| 747 except Exception, err: | |
| 748 Log.Fatal('ParseArgs action [%s] failed with: %s', action, err) | |
| 749 i += num_matched | |
| 750 return (matched, unmatched) | |
| 751 | |
| 752 def MatchOne(argv, i, patternlist): | |
| 753 """Find a pattern which matches argv starting at position i""" | |
| 754 for (regex, action) in patternlist: | |
| 755 if isinstance(regex, str): | |
| 756 regex = [regex] | |
| 757 j = 0 | |
| 758 matches = [] | |
| 759 for r in regex: | |
| 760 if i+j < len(argv): | |
| 761 match = re.compile('^'+r+'$').match(argv[i+j]) | |
| 762 else: | |
| 763 match = None | |
| 764 matches.append(match) | |
| 765 j += 1 | |
| 766 if None in matches: | |
| 767 continue | |
| 768 groups = [ list(m.groups()) for m in matches ] | |
| 769 groups = reduce(lambda x,y: x+y, groups, []) | |
| 770 return (len(regex), action, groups) | |
| 771 return (0, '', []) | |
| 772 | |
| 773 def UnrecognizedOption(*args): | |
| 774 Log.Fatal("Unrecognized option: " + ' '.join(args)) | |
| 775 | |
| 776 ###################################################################### | |
| 777 # File Type Tools | |
| 778 ###################################################################### | |
| 779 | |
| 780 def SimpleCache(f): | |
| 781 """ Cache results of a one-argument function using a dictionary """ | |
| 782 cache = dict() | |
| 783 def wrapper(arg): | |
| 784 if arg in cache: | |
| 785 return cache[arg] | |
| 786 else: | |
| 787 result = f(arg) | |
| 788 cache[arg] = result | |
| 789 return result | |
| 790 wrapper.__name__ = f.__name__ | |
| 791 wrapper.__cache = cache | |
| 792 return wrapper | |
| 793 | |
| 794 @SimpleCache | |
| 795 def IsNative(filename): | |
| 796 return FileType(filename) in ['o','so'] | |
| 797 | |
| 798 @SimpleCache | |
| 799 def IsBitcode(filename): | |
| 800 fp = DriverOpen(filename, 'rb') | |
| 801 header = fp.read(2) | |
| 802 DriverClose(fp) | |
| 803 if header == 'BC': | |
| 804 return True | |
| 805 return False | |
| 806 | |
| 807 @SimpleCache | |
| 808 def IsArchive(filename): | |
| 809 return artools.IsArchive(filename) | |
| 810 | |
| 811 @SimpleCache | |
| 812 def IsBitcodeArchive(filename): | |
| 813 filetype = FileType(filename) | |
| 814 return filetype == 'archive-bc' | |
| 815 | |
| 816 @SimpleCache | |
| 817 def IsNativeArchive(filename): | |
| 818 return IsArchive(filename) and not IsBitcodeArchive(filename) | |
| 819 | |
| 820 class ELFHeader(object): | |
| 821 ELF_MAGIC = '\x7fELF' | |
| 822 ELF_TYPES = { 1: 'REL', # .o | |
| 823 2: 'EXEC', # .exe | |
| 824 3: 'DYN' } # .so | |
| 825 ELF_MACHINES = { 3: '386', | |
| 826 40: 'ARM', | |
| 827 62: 'X86_64' } | |
| 828 ELF_OSABI = { 0: 'UNIX', | |
| 829 3: 'LINUX', | |
| 830 123: 'NACL' } | |
| 831 ELF_ABI_VER = { 0: 'NONE', | |
| 832 7: 'NACL' } | |
| 833 | |
| 834 def __init__(self, e_type, e_machine, e_osabi, e_abiver): | |
| 835 self.type = self.ELF_TYPES[e_type] | |
| 836 self.machine = self.ELF_MACHINES[e_machine] | |
| 837 self.osabi = self.ELF_OSABI[e_osabi] | |
| 838 self.abiver = self.ELF_ABI_VER[e_abiver] | |
| 839 self.arch = FixArch(self.machine) # For convenience | |
| 840 | |
| 841 # If the file is not ELF, returns None. | |
| 842 # Otherwise, returns an ELFHeader object. | |
| 843 @SimpleCache | |
| 844 def GetELFHeader(filename): | |
| 845 fp = DriverOpen(filename, 'rb') | |
| 846 header = fp.read(16 + 2 + 2) | |
| 847 DriverClose(fp) | |
| 848 return DecodeELFHeader(header, filename) | |
| 849 | |
| 850 def DecodeELFHeader(header, filename): | |
| 851 # Pull e_ident, e_type, e_machine | |
| 852 if header[0:4] != ELFHeader.ELF_MAGIC: | |
| 853 return None | |
| 854 | |
| 855 e_osabi = DecodeLE(header[7]) | |
| 856 e_abiver = DecodeLE(header[8]) | |
| 857 e_type = DecodeLE(header[16:18]) | |
| 858 e_machine = DecodeLE(header[18:20]) | |
| 859 | |
| 860 if e_osabi not in ELFHeader.ELF_OSABI: | |
| 861 Log.Fatal('%s: ELF file has unknown OS ABI (%d)', filename, e_osabi) | |
| 862 if e_abiver not in ELFHeader.ELF_ABI_VER: | |
| 863 Log.Fatal('%s: ELF file has unknown ABI version (%d)', filename, e_abiver) | |
| 864 if e_type not in ELFHeader.ELF_TYPES: | |
| 865 Log.Fatal('%s: ELF file has unknown type (%d)', filename, e_type) | |
| 866 if e_machine not in ELFHeader.ELF_MACHINES: | |
| 867 Log.Fatal('%s: ELF file has unknown machine type (%d)', filename, e_machine) | |
| 868 | |
| 869 eh = ELFHeader(e_type, e_machine, e_osabi, e_abiver) | |
| 870 return eh | |
| 871 | |
| 872 def IsELF(filename): | |
| 873 return GetELFHeader(filename) is not None | |
| 874 | |
| 875 # Decode Little Endian bytes into an unsigned value | |
| 876 def DecodeLE(bytes): | |
| 877 value = 0 | |
| 878 for b in reversed(bytes): | |
| 879 value *= 2 | |
| 880 value += ord(b) | |
| 881 return value | |
| 882 | |
| 883 @SimpleCache | |
| 884 def GetBitcodeMetadata(filename): | |
| 885 assert(IsBitcode(filename)) | |
| 886 | |
| 887 # TODO(pdox): Make this work with sandboxed translator | |
| 888 llc = env.getone('LLVM_LLC') | |
| 889 args = [ llc, '-only-dump-metadata', '-dump-metadata=-', filename ] | |
| 890 _, stdout_contents, _ = Run(args, echo_stdout = False, return_stdout = True) | |
| 891 | |
| 892 metadata = { 'OutputFormat': '', | |
| 893 'SOName' : '', | |
| 894 'NeedsLibrary': [] } | |
| 895 for line in stdout_contents.split('\n'): | |
| 896 if not line.strip(): | |
| 897 continue | |
| 898 k, v = line.split(':') | |
| 899 k = k.strip() | |
| 900 v = v.strip() | |
| 901 assert(k in metadata) | |
| 902 if isinstance(metadata[k], list): | |
| 903 metadata[k].append(v) | |
| 904 else: | |
| 905 metadata[k] = v | |
| 906 | |
| 907 return metadata | |
| 908 | |
| 909 # If FORCED_FILE_TYPE is set, FileType() will return FORCED_FILE_TYPE for all | |
| 910 # future input files. This is useful for the "as" incarnation, which | |
| 911 # needs to accept files of any extension and treat them as ".s" (or ".ll") | |
| 912 # files. Also useful for gcc's "-x", which causes all files to be treated | |
| 913 # in a certain way. | |
| 914 FORCED_FILE_TYPE = None | |
| 915 def SetForcedFileType(t): | |
| 916 global FORCED_FILE_TYPE | |
| 917 FORCED_FILE_TYPE = t | |
| 918 | |
| 919 def ForceFileType(filename, newtype = None): | |
| 920 if newtype is None: | |
| 921 if FORCED_FILE_TYPE is None: | |
| 922 return | |
| 923 newtype = FORCED_FILE_TYPE | |
| 924 FileType.__cache[filename] = newtype | |
| 925 | |
| 926 # File Extension -> Type string | |
| 927 ExtensionMap = { | |
| 928 'c' : 'src', | |
| 929 'cc' : 'src', | |
| 930 'cpp' : 'src', | |
| 931 'C' : 'src', | |
| 932 'm' : 'src', # .m = "Objective-C source file" | |
| 933 'll' : 'll', | |
| 934 'bc' : 'po', | |
| 935 'po' : 'po', # .po = "Portable object file" | |
| 936 'pexe': 'pexe', # .pexe = "Portable executable" | |
| 937 'pso' : 'pso', # .pso = "Portable Shared Object" | |
| 938 'asm' : 'S', | |
| 939 'S' : 'S', | |
| 940 's' : 's', | |
| 941 'o' : 'o', | |
| 942 'os' : 'o', | |
| 943 'so' : 'so', | |
| 944 'nexe': 'nexe', | |
| 945 } | |
| 946 | |
| 947 # The SimpleCache decorator is required for correctness, due to the | |
| 948 # ForceFileType mechanism. | |
| 949 @SimpleCache | |
| 950 def FileType(filename): | |
| 951 # Auto-detect bitcode files, since we can't rely on extensions | |
| 952 ext = filename.split('.')[-1] | |
| 953 | |
| 954 # TODO(pdox): We open and read the the first few bytes of each file | |
| 955 # up to 4 times, when we only need to do it once. The | |
| 956 # OS cache prevents us from hitting the disk, but this | |
| 957 # is still slower than it needs to be. | |
| 958 if IsArchive(filename): | |
| 959 return artools.GetArchiveType(filename) | |
| 960 | |
| 961 if IsELF(filename): | |
| 962 return GetELFType(filename) | |
| 963 | |
| 964 if IsBitcode(filename): | |
| 965 return GetBitcodeType(filename) | |
| 966 | |
| 967 if ext in ('o','so','a','po','pso','pa') and ldtools.IsLinkerScript(filename): | |
| 968 return 'ldscript' | |
| 969 | |
| 970 # Use the file extension if it is recognized | |
| 971 if ext in ExtensionMap: | |
| 972 return ExtensionMap[ext] | |
| 973 | |
| 974 Log.Fatal('%s: Unrecognized file type', filename) | |
| 975 | |
| 976 | |
| 977 @SimpleCache | |
| 978 def GetELFType(filename): | |
| 979 """ ELF type as determined by ELF metadata """ | |
| 980 assert(IsELF(filename)) | |
| 981 elfheader = GetELFHeader(filename) | |
| 982 elf_type_map = { | |
| 983 'EXEC': 'nexe', | |
| 984 'REL' : 'o', | |
| 985 'DYN' : 'so' | |
| 986 } | |
| 987 return elf_type_map[elfheader.type] | |
| 988 | |
| 989 @SimpleCache | |
| 990 def GetBitcodeType(filename): | |
| 991 """ Bitcode type as determined by bitcode metadata """ | |
| 992 assert(IsBitcode(filename)) | |
| 993 metadata = GetBitcodeMetadata(filename) | |
| 994 format_map = { | |
| 995 'object': 'po', | |
| 996 'shared': 'pso', | |
| 997 'executable': 'pexe' | |
| 998 } | |
| 999 return format_map[metadata['OutputFormat']] | |
| 1000 | |
| 1001 ###################################################################### | |
| 1002 # | |
| 1003 # File Naming System (Temp files & Output files) | |
| 1004 # | |
| 1005 ###################################################################### | |
| 1006 | |
| 1007 def DefaultOutputName(filename, outtype): | |
| 1008 base = pathtools.basename(filename) | |
| 1009 base = RemoveExtension(base) | |
| 1010 | |
| 1011 if outtype in ('pp','dis'): return '-'; # stdout | |
| 1012 if outtype in ('po'): return base + '.o' | |
| 1013 | |
| 1014 assert(outtype in ExtensionMap.values()) | |
| 1015 assert(outtype != 'src') | |
| 1016 | |
| 1017 return base + '.' + outtype | |
| 1018 | |
| 1019 def RemoveExtension(filename): | |
| 1020 if filename.endswith('.opt.bc'): | |
| 1021 return filename[0:-len('.opt.bc')] | |
| 1022 | |
| 1023 name, ext = pathtools.splitext(filename) | |
| 1024 if ext == '': | |
| 1025 Log.Fatal('File has no extension: ' + filename) | |
| 1026 return name | |
| 1027 | |
| 1028 def PathSplit(f): | |
| 1029 paths = [] | |
| 1030 cur = f | |
| 1031 while True: | |
| 1032 cur, piece = pathtools.split(cur) | |
| 1033 if piece == '': | |
| 1034 break | |
| 1035 paths.append(piece) | |
| 1036 paths.reverse() | |
| 1037 return paths | |
| 1038 | |
| 1039 # Generate a unique identifier for each input file. | |
| 1040 # Start with the basename, and if that is not unique enough, | |
| 1041 # add parent directories. Rinse, repeat. | |
| 1042 class TempNameGen(object): | |
| 1043 def __init__(self, inputs, output): | |
| 1044 inputs = [ pathtools.abspath(i) for i in inputs ] | |
| 1045 output = pathtools.abspath(output) | |
| 1046 | |
| 1047 self.TempBase = output + '---linked' | |
| 1048 | |
| 1049 # TODO(pdox): Figure out if there's a less confusing way | |
| 1050 # to simplify the intermediate filename in this case. | |
| 1051 #if len(inputs) == 1: | |
| 1052 # # There's only one input file, don't bother adding the source name. | |
| 1053 # TempMap[inputs[0]] = output + '---' | |
| 1054 # return | |
| 1055 | |
| 1056 # Build the initial mapping | |
| 1057 self.TempMap = dict() | |
| 1058 for f in inputs: | |
| 1059 if f.startswith('-'): | |
| 1060 continue | |
| 1061 path = PathSplit(f) | |
| 1062 self.TempMap[f] = [1, path] | |
| 1063 | |
| 1064 while True: | |
| 1065 # Find conflicts | |
| 1066 ConflictMap = dict() | |
| 1067 Conflicts = set() | |
| 1068 for (f, [n, path]) in self.TempMap.iteritems(): | |
| 1069 candidate = output + '---' + '_'.join(path[-n:]) + '---' | |
| 1070 if candidate in ConflictMap: | |
| 1071 Conflicts.add(ConflictMap[candidate]) | |
| 1072 Conflicts.add(f) | |
| 1073 else: | |
| 1074 ConflictMap[candidate] = f | |
| 1075 | |
| 1076 if len(Conflicts) == 0: | |
| 1077 break | |
| 1078 | |
| 1079 # Resolve conflicts | |
| 1080 for f in Conflicts: | |
| 1081 n = self.TempMap[f][0] | |
| 1082 if n+1 > len(self.TempMap[f][1]): | |
| 1083 Log.Fatal('Unable to resolve naming conflicts') | |
| 1084 self.TempMap[f][0] = n+1 | |
| 1085 | |
| 1086 # Clean up the map | |
| 1087 NewMap = dict() | |
| 1088 for (f, [n, path]) in self.TempMap.iteritems(): | |
| 1089 candidate = output + '---' + '_'.join(path[-n:]) + '---' | |
| 1090 NewMap[f] = candidate | |
| 1091 self.TempMap = NewMap | |
| 1092 return | |
| 1093 | |
| 1094 def TempNameForOutput(self, imtype): | |
| 1095 temp = self.TempBase + '.' + imtype | |
| 1096 if not env.getbool('SAVE_TEMPS'): | |
| 1097 TempFiles.add(temp) | |
| 1098 return temp | |
| 1099 | |
| 1100 def TempNameForInput(self, input, imtype): | |
| 1101 fullpath = pathtools.abspath(input) | |
| 1102 # If input is already a temporary name, just change the extension | |
| 1103 if fullpath.startswith(self.TempBase): | |
| 1104 temp = self.TempBase + '.' + imtype | |
| 1105 else: | |
| 1106 # Source file | |
| 1107 temp = self.TempMap[fullpath] + '.' + imtype | |
| 1108 | |
| 1109 if not env.getbool('SAVE_TEMPS'): | |
| 1110 TempFiles.add(temp) | |
| 1111 return temp | |
| 1112 | |
| 1113 ###################################################################### | |
| 1114 # | |
| 1115 # Shell Utilities | |
| 1116 # | |
| 1117 ###################################################################### | |
| 1118 | |
| 1119 class shell(object): | |
| 1120 | |
| 1121 @staticmethod | |
| 1122 def unescape(s): | |
| 1123 w = shell.split(s) | |
| 1124 if len(w) == 0: | |
| 1125 return '' | |
| 1126 if len(w) == 1: | |
| 1127 return w[0] | |
| 1128 # String was not properly escaped in the first place? | |
| 1129 assert(False) | |
| 1130 | |
| 1131 # TODO(pdox): Simplify this function by moving more of it into unescape | |
| 1132 @staticmethod | |
| 1133 def split(s): | |
| 1134 """Split a shell-style string up into a list of distinct arguments. | |
| 1135 For example: split('cmd -arg1 -arg2="a b c"') | |
| 1136 Returns ['cmd', '-arg1', '-arg2=a b c'] | |
| 1137 """ | |
| 1138 assert(isinstance(s, str)) | |
| 1139 out = [] | |
| 1140 inspace = True | |
| 1141 inquote = False | |
| 1142 buf = '' | |
| 1143 | |
| 1144 i = 0 | |
| 1145 while i < len(s): | |
| 1146 if s[i] == '"': | |
| 1147 inspace = False | |
| 1148 inquote = not inquote | |
| 1149 elif s[i] == ' ' and not inquote: | |
| 1150 if not inspace: | |
| 1151 out.append(buf) | |
| 1152 buf = '' | |
| 1153 inspace = True | |
| 1154 elif s[i] == '\\': | |
| 1155 if not i+1 < len(s): | |
| 1156 Log.Fatal('Unterminated \\ escape sequence') | |
| 1157 inspace = False | |
| 1158 i += 1 | |
| 1159 buf += s[i] | |
| 1160 else: | |
| 1161 inspace = False | |
| 1162 buf += s[i] | |
| 1163 i += 1 | |
| 1164 if inquote: | |
| 1165 Log.Fatal('Unterminated quote') | |
| 1166 if not inspace: | |
| 1167 out.append(buf) | |
| 1168 return out | |
| 1169 | |
| 1170 @staticmethod | |
| 1171 def join(args): | |
| 1172 """Turn a list into a shell-style string For example: | |
| 1173 shell.join([ 'a', 'b', 'c d e' ]) = 'a b "c d e"' | |
| 1174 """ | |
| 1175 return ' '.join([ shell.escape(a) for a in args ]) | |
| 1176 | |
| 1177 @staticmethod | |
| 1178 def escape(s): | |
| 1179 """Shell-escape special characters in a string | |
| 1180 Surround with quotes if necessary | |
| 1181 """ | |
| 1182 s = s.replace('\\', '\\\\') | |
| 1183 s = s.replace('"', '\\"') | |
| 1184 if ' ' in s: | |
| 1185 s = '"' + s + '"' | |
| 1186 return s | |
| 1187 | |
| 1188 ###################################################################### | |
| 1189 # | |
| 1190 # Logging | |
| 1191 # | |
| 1192 ###################################################################### | |
| 1193 | |
| 1194 class Log(object): | |
| 1195 # Lists of streams | |
| 1196 prefix = '' | |
| 1197 LOG_OUT = [] | |
| 1198 ERROR_OUT = [sys.stderr] | |
| 1199 | |
| 1200 @classmethod | |
| 1201 def reset(cls): | |
| 1202 if env.getbool('LOG_TO_FILE'): | |
| 1203 filename = env.getone('LOG_FILENAME') | |
| 1204 sizelimit = int(env.getone('LOG_FILE_SIZE_LIMIT')) | |
| 1205 cls.AddFile(filename, sizelimit) | |
| 1206 | |
| 1207 @classmethod | |
| 1208 def AddFile(cls, filename, sizelimit): | |
| 1209 file_too_big = pathtools.isfile(filename) and \ | |
| 1210 pathtools.getsize(filename) > sizelimit | |
| 1211 mode = 'a' | |
| 1212 if file_too_big: | |
| 1213 mode = 'w' | |
| 1214 fp = DriverOpen(filename, mode, fail_ok = True) | |
| 1215 if fp: | |
| 1216 cls.LOG_OUT.append(fp) | |
| 1217 | |
| 1218 @classmethod | |
| 1219 def Banner(cls, argv): | |
| 1220 if not env.getbool('RECURSE'): | |
| 1221 cls.Info('-' * 60) | |
| 1222 cls.Info('PNaCl Driver Invoked With:\n' + StringifyCommand(argv)) | |
| 1223 | |
| 1224 @classmethod | |
| 1225 def Info(cls, m, *args): | |
| 1226 cls.LogPrint(m, *args) | |
| 1227 | |
| 1228 @classmethod | |
| 1229 def Error(cls, m, *args): | |
| 1230 cls.ErrorPrint(m, *args) | |
| 1231 | |
| 1232 @classmethod | |
| 1233 def FatalWithResult(cls, ret, m, *args): | |
| 1234 m = 'FATAL: ' + m | |
| 1235 cls.LogPrint(m, *args) | |
| 1236 cls.ErrorPrint(m, *args) | |
| 1237 DriverExit(ret) | |
| 1238 | |
| 1239 @classmethod | |
| 1240 def Warning(cls, m, *args): | |
| 1241 m = 'Warning: ' + m | |
| 1242 cls.ErrorPrint(m, *args) | |
| 1243 | |
| 1244 @classmethod | |
| 1245 def Debug(cls, m, *args): | |
| 1246 if args: | |
| 1247 m = m % args | |
| 1248 if env.getbool('DEBUG'): | |
| 1249 print m | |
| 1250 assert(cls) # This makes the pychecker "unused variable" warning go away | |
| 1251 | |
| 1252 @classmethod | |
| 1253 def Fatal(cls, m, *args): | |
| 1254 # Note, using keyword args and arg lists while trying to keep | |
| 1255 # the m and *args parameters next to each other does not work | |
| 1256 cls.FatalWithResult(-1, m, *args) | |
| 1257 | |
| 1258 @classmethod | |
| 1259 def LogPrint(cls, m, *args): | |
| 1260 # NOTE: m may contain '%' if no args are given | |
| 1261 if args: | |
| 1262 m = m % args | |
| 1263 for o in cls.LOG_OUT: | |
| 1264 print >> o, m | |
| 1265 | |
| 1266 @classmethod | |
| 1267 def ErrorPrint(cls, m, *args): | |
| 1268 # NOTE: m may contain '%' if no args are given | |
| 1269 if args: | |
| 1270 m = m % args | |
| 1271 for o in cls.ERROR_OUT: | |
| 1272 print >> o, m | |
| 1273 | |
| 1274 def EscapeEcho(s): | |
| 1275 """ Quick and dirty way of escaping characters that may otherwise be | |
| 1276 interpreted by bash / the echo command (rather than preserved). """ | |
| 1277 return s.replace("\\", r"\\").replace("$", r"\$").replace('"', r"\"") | |
| 1278 | |
| 1279 def StringifyCommand(cmd, stdin_contents=None): | |
| 1280 """ Return a string for reproducing the command "cmd", which will be | |
| 1281 fed stdin_contents through stdin. """ | |
| 1282 stdin_str = "" | |
| 1283 if stdin_contents: | |
| 1284 stdin_str = "echo \"\"\"" + EscapeEcho(stdin_contents) + "\"\"\" | " | |
| 1285 if env.getbool('LOG_PRETTY_PRINT'): | |
| 1286 return stdin_str + PrettyStringify(cmd) | |
| 1287 else: | |
| 1288 return stdin_str + SimpleStringify(cmd) | |
| 1289 | |
| 1290 def SimpleStringify(args): | |
| 1291 return " ".join(args) | |
| 1292 | |
| 1293 def PrettyStringify(args): | |
| 1294 ret = '' | |
| 1295 grouping = 0 | |
| 1296 for a in args: | |
| 1297 if grouping == 0 and len(ret) > 0: | |
| 1298 ret += " \\\n " | |
| 1299 elif grouping > 0: | |
| 1300 ret += " " | |
| 1301 if grouping == 0: | |
| 1302 grouping = 1 | |
| 1303 if a.startswith('-') and len(a) == 2: | |
| 1304 grouping = 2 | |
| 1305 ret += a | |
| 1306 grouping -= 1 | |
| 1307 return ret | |
| 1308 | |
| 1309 # If the driver is waiting on a background process in RunWithLog() | |
| 1310 # and the user Ctrl-C's or kill's the driver, it may leave | |
| 1311 # the child process (such as llc) running. To prevent this, | |
| 1312 # the code below sets up a signal handler which issues a kill to | |
| 1313 # the currently running child processes. | |
| 1314 CleanupProcesses = [] | |
| 1315 def SetupSignalHandlers(): | |
| 1316 global CleanupProcesses | |
| 1317 def signal_handler(unused_signum, unused_frame): | |
| 1318 for p in CleanupProcesses: | |
| 1319 try: | |
| 1320 p.kill() | |
| 1321 except BaseException: | |
| 1322 pass | |
| 1323 os.kill(os.getpid(), signal.SIGKILL) | |
| 1324 return 0 | |
| 1325 if os.name == "posix": | |
| 1326 signal.signal(signal.SIGINT, signal_handler) | |
| 1327 signal.signal(signal.SIGHUP, signal_handler) | |
| 1328 signal.signal(signal.SIGTERM, signal_handler) | |
| 1329 | |
| 1330 def PipeRecord(sem, f, q): | |
| 1331 """ Read the output of a subprocess from the file object f one line at a | |
| 1332 time. Put each line on Queue q and release semaphore sem to wake the | |
| 1333 parent thread. | |
| 1334 """ | |
| 1335 while True: | |
| 1336 line = f.readline() | |
| 1337 if line: | |
| 1338 q.put(line) | |
| 1339 sem.release() | |
| 1340 else: | |
| 1341 f.close() | |
| 1342 break | |
| 1343 return 0 | |
| 1344 | |
| 1345 def ProcessWait(sem, p): | |
| 1346 """ Wait for the subprocess.Popen object p to finish, and release | |
| 1347 the semaphore sem to wake the parent thread. | |
| 1348 """ | |
| 1349 try: | |
| 1350 p.wait() | |
| 1351 except BaseException: | |
| 1352 pass | |
| 1353 sem.release() | |
| 1354 return 0 | |
| 1355 | |
| 1356 def QueueGetNext(q): | |
| 1357 """ Return the next line from Queue q, or None if empty. | |
| 1358 """ | |
| 1359 try: | |
| 1360 nextline = q.get_nowait() | |
| 1361 except Queue.Empty: | |
| 1362 return None | |
| 1363 except KeyboardInterrupt as e: | |
| 1364 raise e | |
| 1365 else: | |
| 1366 return nextline | |
| 1367 | |
| 1368 def RunWithLog(args, **kwargs): | |
| 1369 kwargs.setdefault('log_command', True) | |
| 1370 kwargs.setdefault('log_stdout', True) | |
| 1371 kwargs.setdefault('log_stderr', True) | |
| 1372 Run(args, **kwargs) | |
| 1373 | |
| 1374 # | |
| 1375 # RunDirect: Run a command. | |
| 1376 # Returns: Exit code | |
| 1377 # If return_stdout or return_stderr is true, | |
| 1378 # returns: (exit code, stdout_contents, stderr_contents) | |
| 1379 # | |
| 1380 def Run(args, # Command and arguments | |
| 1381 stdin = None, # Contents for child's stdin (string) | |
| 1382 echo_stdout = True, # Echo the child's stdout to stdout | |
| 1383 echo_stderr = True, # Echo the child's stderr to stderr | |
| 1384 log_command = False, # Log the command being run | |
| 1385 log_stdout = False, # Log the child's stdout | |
| 1386 log_stderr = False, # Log the child's stderr | |
| 1387 errexit = True, # Exit on failure (errcode != 0) | |
| 1388 return_stdout = False, # Return the contents of stdout | |
| 1389 return_stderr = False, # Return the contents of stderr | |
| 1390 redirect_stdout = None, # Send stdout to a file object | |
| 1391 redirect_stderr = None): # Send stderr to a file object | |
| 1392 | |
| 1393 if isinstance(args, str): | |
| 1394 args = shell.split(env.eval(args)) | |
| 1395 | |
| 1396 args = [pathtools.tosys(args[0])] + args[1:] | |
| 1397 | |
| 1398 if log_command: | |
| 1399 Log.Info('-' * 60) | |
| 1400 Log.Info('\n' + StringifyCommand(args, stdin)) | |
| 1401 | |
| 1402 if env.getbool('DRY_RUN'): | |
| 1403 if return_stdout or return_stderr: | |
| 1404 # TODO(pdox): Prevent this from happening, so that | |
| 1405 # dry-run is more useful. | |
| 1406 Log.Fatal("Unhandled dry-run case.") | |
| 1407 return 0 | |
| 1408 | |
| 1409 # If we only want to echo or redirect the output, we directly pass | |
| 1410 # a descriptor to the child (process_stdout = False), which is is much | |
| 1411 # faster than doing all the processing here. For any other combination | |
| 1412 # (e.g. to log, return, or tee), we process the output by firing off | |
| 1413 # a separate thread below | |
| 1414 record_stdout = log_stdout or return_stdout | |
| 1415 record_stderr = log_stderr or return_stderr | |
| 1416 process_stdout = record_stdout or (redirect_stdout and echo_stdout) | |
| 1417 process_stderr = record_stderr or (redirect_stderr and echo_stderr) | |
| 1418 | |
| 1419 stdin_pipe = None | |
| 1420 if stdin is not None: | |
| 1421 stdin_pipe = subprocess.PIPE | |
| 1422 | |
| 1423 stdout_pipe = None # By default, inherit the parent's stdout | |
| 1424 if process_stdout: | |
| 1425 stdout_pipe = subprocess.PIPE | |
| 1426 elif redirect_stdout: | |
| 1427 stdout_pipe = redirect_stdout | |
| 1428 elif not echo_stdout: | |
| 1429 stdout_pipe = open(os.devnull) | |
| 1430 | |
| 1431 stderr_pipe = None # By default, inherit the parent's stderr | |
| 1432 if process_stderr: | |
| 1433 stderr_pipe = subprocess.PIPE | |
| 1434 elif redirect_stderr: | |
| 1435 stderr_pipe = redirect_stderr | |
| 1436 elif not echo_stderr: | |
| 1437 stderr_pipe = open(os.devnull) | |
| 1438 | |
| 1439 try: | |
| 1440 p = subprocess.Popen(args, stdin=stdin_pipe, | |
| 1441 stdout=stdout_pipe, | |
| 1442 stderr=stderr_pipe ) | |
| 1443 except Exception, e: | |
| 1444 msg = 'failed (%s) to run: %s' % (str(e), StringifyCommand(args, stdin)) | |
| 1445 if log_command: | |
| 1446 Log.Fatal(msg) | |
| 1447 else: | |
| 1448 print msg | |
| 1449 DriverExit(1) | |
| 1450 | |
| 1451 CleanupProcesses.append(p) | |
| 1452 | |
| 1453 stdoutq = Queue.Queue() | |
| 1454 stderrq = Queue.Queue() | |
| 1455 IOReady = threading.Semaphore() | |
| 1456 threads = [] | |
| 1457 | |
| 1458 t = threading.Thread(target=ProcessWait, args=(IOReady,p)) | |
| 1459 threads.append(t) | |
| 1460 | |
| 1461 if process_stdout: | |
| 1462 t = threading.Thread(target=PipeRecord, args=(IOReady, p.stdout, stdoutq)) | |
| 1463 threads.append(t) | |
| 1464 if process_stderr: | |
| 1465 t = threading.Thread(target=PipeRecord, args=(IOReady, p.stderr, stderrq)) | |
| 1466 threads.append(t) | |
| 1467 | |
| 1468 for t in threads: | |
| 1469 t.start() | |
| 1470 | |
| 1471 if stdin is not None: | |
| 1472 # This blocks while writing stdin. | |
| 1473 # TODO(pdox): Ideally, stdin would be fed in synchronously. | |
| 1474 p.stdin.write(stdin) | |
| 1475 p.stdin.close() | |
| 1476 | |
| 1477 stdout_contents = '' | |
| 1478 stderr_contents = '' | |
| 1479 # Loop while handling I/O on stdout/stderr until the child finishes. | |
| 1480 # If process_stderr/stdout are both false, then we just wait for the | |
| 1481 # ProcessWait thread | |
| 1482 lastio = False | |
| 1483 while True: | |
| 1484 IOReady.acquire() | |
| 1485 if p.poll() is not None: | |
| 1486 # Wait for the threads to finish so that the pipes are flushed. | |
| 1487 for t in threads: | |
| 1488 t.join() | |
| 1489 # The threads are now closed, but there might still | |
| 1490 # be data on the queue. | |
| 1491 lastio = True | |
| 1492 | |
| 1493 # For fair queueing, record the size here. | |
| 1494 stdout_qsize = stdoutq.qsize() | |
| 1495 stderr_qsize = stderrq.qsize() | |
| 1496 | |
| 1497 # Flush stdout queue | |
| 1498 while stdout_qsize > 0: | |
| 1499 line = QueueGetNext(stdoutq) | |
| 1500 if line: | |
| 1501 if echo_stdout: | |
| 1502 sys.stdout.write(line) | |
| 1503 if record_stdout: | |
| 1504 stdout_contents += line | |
| 1505 if redirect_stdout: | |
| 1506 redirect_stdout.write(line) | |
| 1507 stdout_qsize -= 1 | |
| 1508 | |
| 1509 # Flush stderr queue | |
| 1510 while stderr_qsize > 0: | |
| 1511 line = QueueGetNext(stderrq) | |
| 1512 if line: | |
| 1513 if echo_stderr: | |
| 1514 sys.stderr.write(line) | |
| 1515 if record_stderr: | |
| 1516 stderr_contents += line | |
| 1517 if redirect_stderr: | |
| 1518 redirect_stderr.write(line) | |
| 1519 stderr_qsize -= 1 | |
| 1520 | |
| 1521 if lastio: | |
| 1522 break | |
| 1523 | |
| 1524 CleanupProcesses.pop() | |
| 1525 | |
| 1526 if errexit and p.returncode != 0: | |
| 1527 if log_command: | |
| 1528 Log.FatalWithResult(p.returncode, | |
| 1529 'failed command: %s\n' | |
| 1530 'stdout : %s\n' | |
| 1531 'stderr : %s\n', | |
| 1532 StringifyCommand(args, stdin), | |
| 1533 stdout_contents, stderr_contents) | |
| 1534 else: | |
| 1535 DriverExit(p.returncode) | |
| 1536 else: | |
| 1537 if log_command: | |
| 1538 Log.Info('Return Code: ' + str(p.returncode)) | |
| 1539 | |
| 1540 if return_stdout or return_stderr: | |
| 1541 return (p.returncode, stdout_contents, stderr_contents) | |
| 1542 return p.returncode | |
| 1543 | |
| 1544 def FixArch(arch): | |
| 1545 arch = arch.lower() | |
| 1546 archfix = { 'x86-32': 'X8632', | |
| 1547 'x86_32': 'X8632', | |
| 1548 'x8632' : 'X8632', | |
| 1549 'i686' : 'X8632', | |
| 1550 'ia32' : 'X8632', | |
| 1551 '386' : 'X8632', | |
| 1552 '686' : 'X8632', | |
| 1553 | |
| 1554 'amd64' : 'X8664', | |
| 1555 'x86_64': 'X8664', | |
| 1556 'x86-64': 'X8664', | |
| 1557 'x8664' : 'X8664', | |
| 1558 | |
| 1559 'arm' : 'ARM', | |
| 1560 'armv7' : 'ARM', | |
| 1561 'arm-thumb2' : 'ARM' } | |
| 1562 if arch not in archfix: | |
| 1563 Log.Fatal('Unrecognized arch "%s"!', arch) | |
| 1564 return archfix[arch] | |
| 1565 | |
| 1566 def IsWindowsPython(): | |
| 1567 return 'windows' in platform.system().lower() | |
| 1568 | |
| 1569 def SetupCygwinLibs(): | |
| 1570 bindir = os.path.dirname(os.path.abspath(sys.argv[0])) | |
| 1571 os.environ['PATH'] += os.pathsep + bindir | |
| 1572 | |
| 1573 def DriverMain(main): | |
| 1574 SetupSignalHandlers() | |
| 1575 env.reset() | |
| 1576 | |
| 1577 if IsWindowsPython(): | |
| 1578 SetupCygwinLibs() | |
| 1579 | |
| 1580 # Parse driver arguments | |
| 1581 (driver_flags, main_args) = ParseArgs(sys.argv[1:], | |
| 1582 DriverPatterns, | |
| 1583 must_match = False) | |
| 1584 env.append('DRIVER_FLAGS', *driver_flags) | |
| 1585 | |
| 1586 # Start the Log | |
| 1587 Log.reset() | |
| 1588 Log.Banner(sys.argv) | |
| 1589 | |
| 1590 # Pull the arch from the filename | |
| 1591 # Examples: pnacl-i686-as (as incarnation, i686 arch) | |
| 1592 tokens = pathtools.basename(DriverPath()).split('-') | |
| 1593 if len(tokens) > 2: | |
| 1594 arch = FixArch(tokens[-2]) | |
| 1595 SetArch(arch) | |
| 1596 | |
| 1597 ret = main(main_args) | |
| 1598 DriverExit(ret) | |
| 1599 | |
| 1600 def SetArch(arch): | |
| 1601 env.set('ARCH', FixArch(arch)) | |
| 1602 | |
| 1603 def GetArch(required = False): | |
| 1604 arch = env.getone('ARCH') | |
| 1605 if arch == '': | |
| 1606 arch = None | |
| 1607 | |
| 1608 if required and not arch: | |
| 1609 Log.Fatal('Missing -arch!') | |
| 1610 | |
| 1611 return arch | |
| 1612 | |
| 1613 # Read an ELF file to determine the machine type. If ARCH is already set, | |
| 1614 # make sure the file has the same architecture. If ARCH is not set, | |
| 1615 # set the ARCH to the file's architecture. | |
| 1616 # | |
| 1617 # Returns True if the file matches ARCH. | |
| 1618 # | |
| 1619 # Returns False if the file doesn't match ARCH. This only happens when | |
| 1620 # must_match is False. If must_match is True, then a fatal error is generated | |
| 1621 # instead. | |
| 1622 def ArchMerge(filename, must_match): | |
| 1623 filetype = FileType(filename) | |
| 1624 if filetype in ('o','so'): | |
| 1625 elfheader = GetELFHeader(filename) | |
| 1626 if not elfheader: | |
| 1627 Log.Fatal("%s: Cannot read ELF header", filename) | |
| 1628 new_arch = elfheader.arch | |
| 1629 elif IsNativeArchive(filename): | |
| 1630 new_arch = filetype[len('archive-'):] | |
| 1631 else: | |
| 1632 Log.Fatal('%s: Unexpected file type in ArchMerge', filename) | |
| 1633 | |
| 1634 existing_arch = GetArch() | |
| 1635 | |
| 1636 if not existing_arch: | |
| 1637 SetArch(new_arch) | |
| 1638 return True | |
| 1639 elif new_arch != existing_arch: | |
| 1640 if must_match: | |
| 1641 msg = "%s: Incompatible object file (%s != %s)" | |
| 1642 logfunc = Log.Fatal | |
| 1643 else: | |
| 1644 msg = "%s: Skipping incompatible object file (%s != %s)" | |
| 1645 logfunc = Log.Warning | |
| 1646 logfunc(msg, filename, new_arch, existing_arch) | |
| 1647 return False | |
| 1648 else: # existing_arch and new_arch == existing_arch | |
| 1649 return True | |
| 1650 | |
| 1651 | |
| 1652 class TempFileHandler(object): | |
| 1653 def __init__(self): | |
| 1654 self.files = [] | |
| 1655 | |
| 1656 def add(self, path): | |
| 1657 path = pathtools.abspath(path) | |
| 1658 self.files.append(path) | |
| 1659 | |
| 1660 def wipe(self): | |
| 1661 for path in self.files: | |
| 1662 try: | |
| 1663 os.remove(path) | |
| 1664 except OSError: | |
| 1665 # If we're exiting early, the temp file | |
| 1666 # may have never been created. | |
| 1667 pass | |
| 1668 self.files = [] | |
| 1669 | |
| 1670 TempFiles = TempFileHandler() | |
| 1671 | |
| 1672 def DriverExit(code): | |
| 1673 TempFiles.wipe() | |
| 1674 sys.exit(code) | |
| 1675 | |
| 1676 def CheckTranslatorPrerequisites(): | |
| 1677 """ Assert that the scons artifacts for running the sandboxed translator | |
| 1678 exist: sel_universal, sel_ldr and the irt blob. """ | |
| 1679 sel_universal = env.getone('SEL_UNIVERSAL') | |
| 1680 if not pathtools.exists(sel_universal): | |
| 1681 Log.Fatal('Could not find sel_universal [%s]', sel_universal) | |
| 1682 sel_ldr = env.getone('SEL_LDR') | |
| 1683 if not pathtools.exists(sel_ldr): | |
| 1684 Log.Fatal('Could not find sel_ldr [%s]', sel_ldr) | |
| 1685 irt_blob = env.getone('IRT_BLOB') | |
| 1686 if not pathtools.exists(irt_blob): | |
| 1687 Log.Fatal('Could not find irt_blob [%s]', irt_blob) | |
| 1688 | |
| 1689 | |
| 1690 def DriverOpen(filename, mode, fail_ok = False): | |
| 1691 try: | |
| 1692 fp = open(pathtools.tosys(filename), mode) | |
| 1693 except Exception: | |
| 1694 if not fail_ok: | |
| 1695 Log.Fatal("%s: Unable to open file", pathtools.touser(filename)) | |
| 1696 DriverExit(1) | |
| 1697 else: | |
| 1698 return None | |
| 1699 return fp | |
| 1700 | |
| 1701 def DriverClose(fp): | |
| 1702 fp.close() | |
| 1703 | |
| 1704 class DriverChain(object): | |
| 1705 """ The DriverChain class takes one or more input files, | |
| 1706 an output file, and a sequence of steps. It executes | |
| 1707 those steps, using intermediate files in between, | |
| 1708 to generate the final outpu. | |
| 1709 """ | |
| 1710 | |
| 1711 def __init__(self, input, output, namegen): | |
| 1712 self.input = input | |
| 1713 self.output = output | |
| 1714 self.steps = [] | |
| 1715 self.namegen = namegen | |
| 1716 | |
| 1717 # "input" can be a list of files or a single file. | |
| 1718 # If we're compiling for a single file, then we use | |
| 1719 # TempNameForInput. If there are multiple files | |
| 1720 # (e.g. linking), then we use TempNameForOutput. | |
| 1721 self.use_names_for_input = isinstance(input, str) | |
| 1722 | |
| 1723 def add(self, callback, output_type, **extra): | |
| 1724 step = (callback, output_type, extra) | |
| 1725 self.steps.append(step) | |
| 1726 | |
| 1727 def run(self): | |
| 1728 step_input = self.input | |
| 1729 for (i, (callback, output_type, extra)) in enumerate(self.steps): | |
| 1730 if i == len(self.steps)-1: | |
| 1731 # Last step | |
| 1732 step_output = self.output | |
| 1733 else: | |
| 1734 # Intermediate step | |
| 1735 if self.use_names_for_input: | |
| 1736 step_output = self.namegen.TempNameForInput(self.input, output_type) | |
| 1737 else: | |
| 1738 step_output = self.namegen.TempNameForOutput(output_type) | |
| 1739 callback(step_input, step_output, **extra) | |
| 1740 step_input = step_output | |
| OLD | NEW |