2 # Copyright (c) 2012 The Native Client Authors. All rights reserved.
3 # Use of this source code is governed by a BSD-style license that can be
4 # found in the LICENSE file.
6 # Global environment and expression parsing for the PNaCl driver
9 # This dictionary initializes a shell-like environment.
10 # Shell escaping and ${} substitution are provided.
11 # See "class Environment" defined later for the implementation.
13 from driver_log import Log, DriverExit
14 from shelltools import shell
19 'DRIVER_PATH' : '', # Absolute path to this driver invocation
20 'DRIVER_BIN' : '', # PNaCl driver bin/ directory
21 'DRIVER_REV_FILE' : '${BASE}/REV',
23 'BASE_NACL' : '${@FindBaseNaCl}', # Absolute path of native_client/
24 'BASE_TOOLCHAIN' : '${@FindBaseToolchain}', # Absolute path to toolchain/OS_ARCH/
25 'BASE' : '${@FindBasePNaCl}', # Absolute path to PNaCl
26 'BUILD_OS' : '${@GetBuildOS}', # "linux", "darwin" or "windows"
27 'BUILD_ARCH' : '${@GetBuildArch}', # "x86_64" or "i686" or "i386"
30 'CLANG_VER' : '3.4', # Included in path to compiler-owned libs/headers.
31 'BPREFIXES' : '', # Prefixes specified using the -B flag.
32 'BASE_LLVM' : '${@FindBaseHost:clang}',
33 'BASE_BINUTILS' : '${@FindBaseHost:le32-nacl-ar}',
35 'BASE_LIB_NATIVE' : '${BASE}/translator/',
37 'BASE_USR' : '${BASE}/le32-nacl',
38 'BASE_SDK' : '${BASE}/sdk',
39 'BASE_LIB' : '${BASE}/lib/clang/${CLANG_VER}/lib/le32-nacl',
40 'BASE_USR_ARCH' : '${BASE_USR_%BCLIB_ARCH%}',
41 'BASE_USR_X8632' : '${BASE}/x86-32_bc-nacl',
42 'BASE_USR_X8664' : '${BASE}/x86-64_bc-nacl',
43 'BASE_USR_ARM' : '${BASE}/arm_bc-nacl',
44 'BASE_LIB_ARCH' : '${BASE_LIB_%BCLIB_ARCH%}',
45 'BASE_LIB_X8632' : '${BASE}/lib/clang/${CLANG_VER}/lib/x86-32_bc-nacl',
46 'BASE_LIB_X8664' : '${BASE}/lib/clang/${CLANG_VER}/lib/x86-64_bc-nacl',
47 'BASE_LIB_ARM' : '${BASE}/lib/clang/${CLANG_VER}/lib/arm_bc-nacl',
49 'LIBS_NATIVE_ARCH' : '${LIBS_NATIVE_%ARCH%}',
50 'LIBS_NATIVE_ARM' : '${BASE_LIB_NATIVE}arm/lib',
51 'LIBS_NATIVE_ARM_NONSFI' : '${BASE_LIB_NATIVE}arm-nonsfi/lib',
52 'LIBS_NATIVE_X8632' : '${BASE_LIB_NATIVE}x86-32/lib',
53 'LIBS_NATIVE_X8632_NONSFI' : '${BASE_LIB_NATIVE}x86-32-nonsfi/lib',
54 'LIBS_NATIVE_X8664' : '${BASE_LIB_NATIVE}x86-64/lib',
55 'LIBS_NATIVE_MIPS32' : '${BASE_LIB_NATIVE}mips32/lib',
57 'BASE_LLVM_BIN' : '${BASE_LLVM}/bin',
59 '${BASE_TOOLCHAIN}/pnacl_translator/translator/${TRANSLATOR_ARCH}/bin',
61 # TODO(dschuff): Switch these directories to be triple-style arches,
62 # to match the main toolchain?
63 'TRANSLATOR_ARCH' : '${TRANSLATOR_ARCH_%ARCH%}',
64 'TRANSLATOR_ARCH_X8632' : 'x86-32',
65 'TRANSLATOR_ARCH_X8664' : 'x86-64',
66 'TRANSLATOR_ARCH_ARM' : 'arm',
67 'TRANSLATOR_ARCH_MIPS32': 'mips32',
69 'SCONS_OUT' : '${BASE_NACL}/scons-out',
72 'ARCH' : '', # Target architecture, including optional
73 # suffixes such as '_NONSFI' or '_LINUX'.
74 'BASE_ARCH' : '', # Target architecture without any '_NONSFI' suffix.
75 # Derived from ARCH field.
76 'NONSFI_NACL' : '0', # Whether targeting Non-SFI Mode. Derived from
78 'BIAS' : 'NONE', # This can be 'NONE', 'ARM', 'MIPS32', 'X8632' or
80 # When not set to none, this causes the front-end to
81 # act like a target-specific compiler. This bias is
82 # currently needed while compiling newlib,
83 # and some scons tests.
85 'SAVE_TEMPS' : '0', # Do not clean up temporary files
86 'SANDBOXED' : '0', # Use sandboxed toolchain for this arch. (main switch)
87 'HAS_FRONTEND': '', # Set by ReadConfig(). '1' if the driver install
88 # has support for front-end bitcode tools, or '0'
89 # if it only has the backend translator.
92 # Args passed from one driver invocation to another
93 'INHERITED_DRIVER_ARGS' : '',
97 'LOG_VERBOSE' : '0', # Log to stdout (--pnacl-driver-verbose)
100 'SO_EXT' : '${SO_EXT_%BUILD_OS%}',
101 'SO_EXT_darwin' : '.dylib',
102 'SO_EXT_linux' : '.so',
103 'SO_EXT_windows' : '.dll',
105 'SO_DIR' : '${SO_DIR_%BUILD_OS%}',
106 'SO_DIR_darwin' : 'lib',
107 'SO_DIR_linux' : 'lib',
108 'SO_DIR_windows' : 'bin', # On Windows, DLLs are placed in bin/
109 # because the dynamic loader searches %PATH%
111 'EXEC_EXT' : '${EXEC_EXT_%BUILD_OS%}',
112 'EXEC_EXT_darwin' : '',
113 'EXEC_EXT_linux' : '',
114 'EXEC_EXT_windows': '.exe',
116 'SCONS_OS' : '${SCONS_OS_%BUILD_OS%}',
117 'SCONS_OS_linux' : 'linux',
118 'SCONS_OS_darwin' : 'mac',
119 'SCONS_OS_windows' : 'win',
122 'GOLD_PLUGIN_SO' : '${BASE_LLVM}/${SO_DIR}/LLVMgold${SO_EXT}',
124 'SCONS_STAGING' : '${SCONS_STAGING_%ARCH%}',
125 'SCONS_STAGING_X8632' : '${SCONS_OUT}/opt-${SCONS_OS}-x86-32/staging',
126 'SCONS_STAGING_X8664' : '${SCONS_OUT}/opt-${SCONS_OS}-x86-64/staging',
127 'SCONS_STAGING_ARM' : '${SCONS_OUT}/opt-${SCONS_OS}-arm/staging',
128 'SCONS_STAGING_MIPS32': '${SCONS_OUT}/opt-${SCONS_OS}-mips32/staging',
130 'SEL_UNIVERSAL_PREFIX': '${USE_EMULATOR ? ${EMULATOR}}',
131 'SEL_UNIVERSAL' : '${SCONS_STAGING}/sel_universal${EXEC_EXT}',
132 # NOTE: -Q skips sel_ldr qualification tests, -c -c skips validation
133 # NOTE: We are not using -B to load the IRT, since the translators do not
135 'SEL_UNIVERSAL_FLAGS' : '--abort_on_error ' +
136 '--uses_reverse_service ' +
137 '${USE_EMULATOR ? -Q -c -c --command_prefix ${EMULATOR}}',
139 'EMULATOR' : '${EMULATOR_%ARCH%}',
140 'EMULATOR_X8632' : '',
141 'EMULATOR_X8664' : '',
142 # NOTE: this is currently the only dependency on the arm trusted TC
144 '${BASE_NACL}/toolchain/linux_x86/arm_trusted/run_under_qemu_arm',
146 '${BASE_NACL}/toolchain/linux_x86/mips_trusted/run_under_qemu_mips32',
148 'SEL_LDR' : '${SCONS_STAGING}/sel_ldr${EXEC_EXT}',
149 'BOOTSTRAP_LDR' : '${SCONS_STAGING}/nacl_helper_bootstrap${EXEC_EXT}',
151 # sandboxed llvm backend
152 'LLC_SB' : '${TRANSLATOR_BIN}/pnacl-llc.nexe',
153 # sandboxed linker (gold based)
154 'LD_SB' : '${TRANSLATOR_BIN}/ld.nexe',
157 'CLANG' : '${BASE_LLVM_BIN}/clang${EXEC_EXT}',
158 # 'clang++' doesn't work on Windows (outside of Cygwin),
159 # because it is a symlink.
160 'CLANGXX' : '${BASE_LLVM_BIN}/clang${EXEC_EXT} --driver-mode=g++',
161 'LLVM_OPT' : '${BASE_LLVM_BIN}/opt${EXEC_EXT}',
162 'LLVM_DIS' : '${BASE_LLVM_BIN}/llvm-dis${EXEC_EXT}',
163 'LLVM_NM' : '${BASE_LLVM_BIN}/llvm-nm${EXEC_EXT}',
164 # llvm-as compiles llvm assembly (.ll) to bitcode (.bc/.po)
165 'LLVM_AS' : '${BASE_LLVM_BIN}/llvm-as${EXEC_EXT}',
166 'PNACL_ABICHECK': '${BASE_LLVM_BIN}/pnacl-abicheck${EXEC_EXT}',
167 'PNACL_COMPRESS': '${BASE_LLVM_BIN}/pnacl-bccompress${EXEC_EXT}',
170 'LLVM_PNACL_LLC': '${BASE_LLVM_BIN}/pnacl-llc${EXEC_EXT}',
171 # llvm-mc is llvm's native assembler
172 'LLVM_MC' : '${BASE_LLVM_BIN}/llvm-mc${EXEC_EXT}',
175 'BINUTILS_BASE' : '${BASE_BINUTILS}/bin/le32-nacl-',
176 'OBJDUMP' : '${BINUTILS_BASE}objdump${EXEC_EXT}',
177 'NM' : '${BINUTILS_BASE}nm${EXEC_EXT}',
178 'AR' : '${BINUTILS_BASE}ar${EXEC_EXT}',
179 'RANLIB' : '${BINUTILS_BASE}ranlib${EXEC_EXT}',
180 'READELF' : '${BINUTILS_BASE}readelf${EXEC_EXT}',
181 'STRIP' : '${BINUTILS_BASE}strip${EXEC_EXT}',
182 # linker (used for both bitcode and ELF linking)
183 'LD' : '${BINUTILS_BASE}ld.gold${EXEC_EXT}',
186 ######################################################################
190 ######################################################################
192 def ParseError(s, leftpos, rightpos, msg):
193 Log.Error("Parse Error: %s", msg)
195 Log.Error(' ' + (' '*leftpos) + ('^'*(rightpos - leftpos + 1)))
198 # Find the leftmost position in "s" which begins a substring
199 # in "strset", starting at "pos".
201 # FindFirst('hello world', 0, ['h','o']) = ('h', 0)
202 # FindFirst('hello world', 1, ['h','o']) = ('o', 4)
203 # FindFirst('hello world', 0, ['x']) = (None,11)
204 def FindFirst(s, pos, strset):
207 m[s.find(ss, pos)] = ss
211 return (None, len(s))
215 class Environment(object):
219 def register(cls, func):
220 """ Register a function for use in the evaluator """
221 cls.functions[func.__name__] = func
229 self.data = dict(INITIAL_ENV)
231 def update(self, extra):
232 self.data.update(extra)
235 for (k,v) in self.data.iteritems():
236 print '%s == %s' % (k,v)
239 self.stack.append(self.data)
240 self.data = dict(self.data) # Make a copy
243 self.data = self.stack.pop()
245 def has(self, varname):
246 return varname in self.data
248 def getraw(self, varname):
249 return self.eval(self.data[varname])
251 # Evaluate a variable from the environment.
252 # Returns a list of terms.
253 def get(self, varname):
254 return shell.split(self.getraw(varname))
256 # Retrieve a variable from the environment which
257 # is a single term. Returns a string.
258 def getone(self, varname):
259 return shell.unescape(self.getraw(varname))
261 def getbool(self, varname):
262 return bool(int(self.getone(varname)))
264 def setbool(self, varname, val):
266 self.set(varname, '1')
268 self.set(varname, '0')
270 # Set a variable in the environment without shell-escape
271 def setraw(self, varname, val):
272 self.data[varname] = val
274 # Set one or more variables using named arguments
275 def setmany(self, **kwargs):
276 for k,v in kwargs.iteritems():
277 if isinstance(v, types.StringTypes):
279 elif isinstance(v, types.ListType):
282 Log.Fatal('env.setmany given a non-string and non-list value')
284 def clear(self, varname):
285 self.data[varname] = ''
287 # Set a variable to one or more terms, applying shell-escape.
288 def set(self, varname, *vals):
290 self.append(varname, *vals)
292 # Append one or more terms to a variable in the
293 # environment, applying shell-escape.
294 def append(self, varname, *vals):
295 escaped = [ shell.escape(v) for v in vals ]
296 if len(self.data[varname]) > 0:
297 self.data[varname] += ' '
298 self.data[varname] += ' '.join(escaped)
300 # Evaluate an expression s
302 (result, i) = self.eval_expr(s, 0, [])
306 ######################################################################
307 # EXPRESSION EVALUATION CODE
308 # Context Free Grammar:
310 # str = empty | string literal
311 # expr = str | expr '$' '{' bracket_expr '}' expr
312 # bracket_expr = varname | boolexpr ? expr | boolexpr ? expr : expr | @call
313 # boolexpr = boolval | boolval '&&' boolexpr | boolval '||' boolexpr
314 # boolval = varname | !varname | #varname | !#varname | varname '==' str
315 # varname = str | varname '%' bracket_expr '%' varname
316 # call = func | func ':' arglist
318 # arglist = empty | arg ':' arglist
320 # Do not call these functions outside of this class.
321 # The env.eval method is the external interface to the evaluator.
322 ######################################################################
324 # Evaluate a string literal
325 def eval_str(self, s, pos, terminators):
326 (_,i) = FindFirst(s, pos, terminators)
329 # Evaluate %var% substitutions inside a variable name.
330 # Returns (the_actual_variable_name, endpos)
331 # Terminated by } character
332 def eval_varname(self, s, pos, terminators):
333 (_,i) = FindFirst(s, pos, ['%'] + terminators)
334 leftpart = s[pos:i].strip(' ')
335 if i == len(s) or s[i] in terminators:
338 (middlepart, j) = self.eval_bracket_expr(s, i+1, ['%'])
339 if j == len(s) or s[j] != '%':
340 ParseError(s, i, j, "Unterminated %")
342 (rightpart, k) = self.eval_varname(s, j+1, terminators)
344 fullname = leftpart + middlepart + rightpart
345 fullname = fullname.strip()
349 def eval_whitespace(self, s, pos):
351 while i < len(s) and s[i] == ' ':
355 def eval_bool_val(self, s, pos, terminators):
356 (_,i) = self.eval_whitespace(s, pos)
364 (_,i) = self.eval_whitespace(s, i)
372 (varname, j) = self.eval_varname(s, i, ['=']+terminators)
374 # This is an error condition one level up. Don't evaluate anything.
377 if varname not in self.data:
378 ParseError(s, i, j, "Undefined variable '%s'" % varname)
379 vardata = self.data[varname]
381 contents = self.eval(vardata)
384 # String equality test
385 if j+1 == len(s) or s[j+1] != '=':
386 ParseError(s, j, j, "Unexpected token")
388 ParseError(s, j, j, "Cannot combine == and #")
389 (_,j) = self.eval_whitespace(s, j+2)
390 (literal_str,j) = self.eval_str(s, j, [' ']+terminators)
391 (_,j) = self.eval_whitespace(s, j)
393 return (False, j) # Error one level up
398 val = (len(contents) != 0)
399 elif literal_str is not None:
400 val = (contents == literal_str)
402 if contents not in ('0','1'):
404 "%s evaluated to %s, which is not a boolean!" % (varname, contents))
405 val = bool(int(contents))
406 return (negated ^ val, j)
408 # Evaluate a boolexpr
409 def eval_bool_expr(self, s, pos, terminators):
410 (boolval1, i) = self.eval_bool_val(s, pos, ['&','|']+terminators)
412 # This is an error condition one level up. Don't evaluate anything.
415 if s[i] in ('&','|'):
417 if i+1 == len(s) or s[i+1] != s[i]:
418 ParseError(s, i, i, "Unexpected token")
419 is_and = (s[i] == '&')
421 (boolval2, j) = self.eval_bool_expr(s, i+2, terminators)
423 # This is an error condition one level up.
427 return (boolval1 and boolval2, j)
429 return (boolval1 or boolval2, j)
433 # Evaluate the inside of a ${} or %%.
434 # Returns the (the_evaluated_string, endpos)
435 def eval_bracket_expr(self, s, pos, terminators):
436 (_,pos) = self.eval_whitespace(s, pos)
439 # Function call: ${@func}
440 # or possibly : ${@func:arg1:arg2...}
441 (_,i) = FindFirst(s, pos, [':']+terminators)
443 return ('', i) # Error one level up
444 funcname = s[pos+1:i]
450 (_,j) = FindFirst(s, i+1, terminators)
452 return ('', j) # Error one level up
453 args = s[i+1:j].split(':')
455 val = self.functions[funcname](*args)
456 contents = self.eval(val)
459 (m,_) = FindFirst(s, pos, ['?']+terminators)
461 # Regular variable substitution
462 (varname,i) = self.eval_varname(s, pos, terminators)
464 return ('', i) # Error one level up
465 if varname not in self.data:
466 ParseError(s, pos, i, "Undefined variable '%s'" % varname)
467 vardata = self.data[varname]
468 contents = self.eval(vardata)
472 (is_cond_true,i) = self.eval_bool_expr(s, pos, ['?']+terminators)
473 assert(i < len(s) and s[i] == '?')
475 (if_true_expr, j) = self.eval_expr(s, i+1, [' : ']+terminators)
477 return ('', j) # Error one level up
479 if s[j:j+3] == ' : ':
480 (if_false_expr,j) = self.eval_expr(s, j+3, terminators)
482 # This is an error condition one level up.
488 contents = if_true_expr.strip()
490 contents = if_false_expr.strip()
494 # Evaluate an expression with ${} in string s, starting at pos.
495 # Returns (the_evaluated_expression, endpos)
496 def eval_expr(self, s, pos, terminators):
497 (m,i) = FindFirst(s, pos, ['${'] + terminators)
499 if i == len(s) or m in terminators:
502 (middlepart, j) = self.eval_bracket_expr(s, i+2, ['}'])
503 if j == len(s) or s[j] != '}':
504 ParseError(s, i, j, 'Unterminated ${')
506 (rightpart, k) = self.eval_expr(s, j+1, terminators)
507 return (leftpart + middlepart + rightpart, k)
512 def override_env(meth_name, func):
513 """Override a method in the global |env|, given the method name
514 and the new function.
517 setattr(env, meth_name, types.MethodType(func, env, Environment))