Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(404)

Side by Side Diff: tools/llvm/driver/driver_tools.py

Issue 8395028: Move tools/llvm/driver to pnacl/driver. (Closed) Base URL: svn://svn.chromium.org/native_client/trunk/src/native_client/
Patch Set: Created 9 years, 1 month ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch | Annotate | Revision Log
« no previous file with comments | « tools/llvm/driver/artools.py ('k') | tools/llvm/driver/ldtools.py » ('j') | no next file with comments »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
OLDNEW
(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
OLDNEW
« no previous file with comments | « tools/llvm/driver/artools.py ('k') | tools/llvm/driver/ldtools.py » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698