3ef78f9d102bb5f97aa5785cf99943c90179daf1
[platform/framework/web/crosswalk.git] / src / native_client / pnacl / driver / driver_env.py
1 #!/usr/bin/python
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.
5 #
6 # IMPORTANT NOTE: If you make local mods to this file, you must run:
7 #   %  pnacl/build.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 # Global environment and expression parsing for the PNaCl driver
12
13
14 # This dictionary initializes a shell-like environment.
15 # Shell escaping and ${} substitution are provided.
16 # See "class Environment" defined later for the implementation.
17
18 from driver_log import Log, DriverExit
19 from shelltools import shell
20 import types
21
22 INITIAL_ENV = {
23   # Set by DriverMain
24   'DRIVER_PATH'     : '', # Absolute path to this driver invocation
25   'DRIVER_BIN'      : '', # PNaCl driver bin/ directory
26   'DRIVER_REV_FILE' : '${DRIVER_BIN}/REV',
27
28   'BASE_NACL'       : '${@FindBaseNaCl}',      # Absolute path of native_client/
29   'BASE_TOOLCHAIN'  : '${@FindBaseToolchain}', # Absolute path to toolchain/
30   'BASE'            : '${@FindBasePNaCl}',     # Absolute path to PNaCl
31   'BUILD_OS'        : '${@GetBuildOS}',        # "linux", "darwin" or "windows"
32   'BUILD_ARCH'      : '${@GetBuildArch}',      # "x86_64" or "i686" or "i386"
33
34   # Directories
35   'BPREFIXES'       : '', # Prefixes specified using the -B flag.
36   'BASE_LLVM'       : '${@FindBaseHost:clang}',
37   'BASE_BINUTILS'   : '${@FindBaseHost:le32-nacl-ar}',
38
39   'BASE_LIB_NATIVE' : '${BASE}/lib-',
40
41   'BASE_USR'        : '${BASE}/usr',
42   'BASE_SDK'        : '${BASE}/sdk',
43   'BASE_LIB'        : '${BASE}/lib',
44   'BASE_USR_ARCH'   : '${BASE_USR_%BCLIB_ARCH%}',
45   'BASE_USR_X8664'   : '${BASE}/usr-bc-x86-64',
46   'BASE_LIB_ARCH'   : '${BASE_LIB_%BCLIB_ARCH%}',
47   'BASE_LIB_X8664'   : '${BASE}/lib-bc-x86-64',
48
49   'BASE_LLVM_BIN'   : '${BASE_LLVM}/bin',
50   'TRANSLATOR_BIN'  :
51     '${BASE_TOOLCHAIN}/pnacl_translator/${STANDARD_ARCH}/bin',
52
53   # TODO(pdox): Unify this with ARCH.
54   'STANDARD_ARCH'       : '${STANDARD_ARCH_%ARCH%}',
55   'STANDARD_ARCH_X8632' : 'i686',
56   'STANDARD_ARCH_X8664' : 'x86_64',
57   'STANDARD_ARCH_ARM'   : 'armv7',
58   'STANDARD_ARCH_MIPS32': 'mips32',
59
60   'SCONS_OUT'       : '${BASE_NACL}/scons-out',
61
62   # Driver settings
63   'ARCH'        : '',     # Target architecture
64   'BIAS'        : 'NONE', # This can be 'NONE', 'ARM', 'MIPS32', 'X8632' or
65                           # 'X8664'.
66                           # When not set to none, this causes the front-end to
67                           # act like a target-specific compiler. This bias is
68                           # currently needed while compiling newlib,
69                           # and some scons tests.
70   'DRY_RUN'     : '0',
71   'SAVE_TEMPS'  : '0',    # Do not clean up temporary files
72   'SANDBOXED'   : '0',    # Use sandboxed toolchain for this arch. (main switch)
73   'HAS_FRONTEND': '',     # Set by ReadConfig().  '1' if the driver install
74                           # has support for front-end bitcode tools, or '0'
75                           # if it only has the backend translator.
76
77   'USE_EMULATOR'        : '0',
78   'USE_BOOTSTRAP'       : '${BUILD_OS==linux ? 1 : 0}',
79   # Args passed from one driver invocation to another
80   'INHERITED_DRIVER_ARGS' : '',
81
82   'BCLIB_ARCH'          : '',
83   # Logging settings
84   'LOG_VERBOSE'        : '0', # Log to stdout (--pnacl-driver-verbose)
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   'EXEC_EXT'        : '${EXEC_EXT_%BUILD_OS%}',
99   'EXEC_EXT_darwin' : '',
100   'EXEC_EXT_linux'  : '',
101   'EXEC_EXT_windows': '.exe',
102
103   'SCONS_OS'            : '${SCONS_OS_%BUILD_OS%}',
104   'SCONS_OS_linux'      : 'linux',
105   'SCONS_OS_darwin'     : 'mac',
106   'SCONS_OS_windows'    : 'win',
107
108   # llvm goldplugin
109   'GOLD_PLUGIN_SO'  : '${BASE_LLVM}/${SO_DIR}/LLVMgold${SO_EXT}',
110
111   'SCONS_STAGING'       : '${SCONS_STAGING_%ARCH%}',
112   'SCONS_STAGING_X8632' : '${SCONS_OUT}/opt-${SCONS_OS}-x86-32/staging',
113   'SCONS_STAGING_X8664' : '${SCONS_OUT}/opt-${SCONS_OS}-x86-64/staging',
114   'SCONS_STAGING_ARM'   : '${SCONS_OUT}/opt-${SCONS_OS}-arm/staging',
115   'SCONS_STAGING_MIPS32': '${SCONS_OUT}/opt-${SCONS_OS}-mips32/staging',
116
117   'SEL_UNIVERSAL_PREFIX': '${USE_EMULATOR ? ${EMULATOR}}',
118   'SEL_UNIVERSAL'       : '${SCONS_STAGING}/sel_universal${EXEC_EXT}',
119   # NOTE: -Q skips sel_ldr qualification tests, -c -c skips validation
120   # NOTE: We are not using -B to load the IRT, since the translators do not
121   # use the IRT.
122   'SEL_UNIVERSAL_FLAGS' : '--abort_on_error ' +
123                           '--uses_reverse_service ' +
124                           '${USE_EMULATOR ? -Q -c -c --command_prefix ${EMULATOR}}',
125
126   'EMULATOR'            : '${EMULATOR_%ARCH%}',
127   'EMULATOR_X8632'      : '',
128   'EMULATOR_X8664'      : '',
129   # NOTE: this is currently the only dependency on the arm trusted TC
130   'EMULATOR_ARM'        :
131       '${BASE_NACL}/toolchain/linux_arm-trusted/run_under_qemu_arm',
132   'EMULATOR_MIPS32'     :
133       '${BASE_NACL}/toolchain/linux_mips-trusted/run_under_qemu_mips32',
134
135   'SEL_LDR'       : '${SCONS_STAGING}/sel_ldr${EXEC_EXT}',
136   'BOOTSTRAP_LDR' : '${SCONS_STAGING}/nacl_helper_bootstrap${EXEC_EXT}',
137
138   # sandboxed llvm backend
139   'LLC_SB'      : '${TRANSLATOR_BIN}/pnacl-llc.nexe',
140   # sandboxed linker (gold based)
141   'LD_SB'       : '${TRANSLATOR_BIN}/ld.nexe',
142
143   # Bitcode LLVM tools
144   'CLANG'         : '${BASE_LLVM_BIN}/clang${EXEC_EXT}',
145   # 'clang++' doesn't work on Windows (outside of Cygwin),
146   # because it is a symlink. '-ccc-cxx' enables C++ mode.
147   'CLANGXX'       : '${BASE_LLVM_BIN}/clang${EXEC_EXT} -ccc-cxx',
148   'LLVM_OPT'      : '${BASE_LLVM_BIN}/opt${EXEC_EXT}',
149   'LLVM_DIS'      : '${BASE_LLVM_BIN}/llvm-dis${EXEC_EXT}',
150   'LLVM_NM'       : '${BASE_LLVM_BIN}/llvm-nm${EXEC_EXT}',
151   # llvm-as compiles llvm assembly (.ll) to bitcode (.bc/.po)
152   'LLVM_AS'       : '${BASE_LLVM_BIN}/llvm-as${EXEC_EXT}',
153   'PNACL_ABICHECK': '${BASE_LLVM_BIN}/pnacl-abicheck${EXEC_EXT}',
154
155   # Native LLVM tools
156   'LLVM_PNACL_LLC': '${BASE_LLVM_BIN}/pnacl-llc${EXEC_EXT}',
157   # llvm-mc is llvm's native assembler
158   'LLVM_MC'       : '${BASE_LLVM_BIN}/llvm-mc${EXEC_EXT}',
159
160   # Binutils
161   'BINUTILS_BASE'  : '${BASE_BINUTILS}/bin/le32-nacl-',
162   'OBJDUMP'        : '${BINUTILS_BASE}objdump${EXEC_EXT}',
163   'NM'             : '${BINUTILS_BASE}nm${EXEC_EXT}',
164   'AR'             : '${BINUTILS_BASE}ar${EXEC_EXT}',
165   'RANLIB'         : '${BINUTILS_BASE}ranlib${EXEC_EXT}',
166   'READELF'        : '${BINUTILS_BASE}readelf${EXEC_EXT}',
167   'STRIP'          : '${BINUTILS_BASE}strip${EXEC_EXT}',
168   # linker (used for both bitcode and ELF linking)
169   'LD'        : '${BINUTILS_BASE}ld.gold${EXEC_EXT}',
170
171   # Use the default command line arguments to the sandboxed translator.
172   'USE_DEFAULT_CMD_LINE': '1',
173 }
174
175 ######################################################################
176 #
177 # Environment
178 #
179 ######################################################################
180
181 def ParseError(s, leftpos, rightpos, msg):
182   Log.Error("Parse Error: %s", msg)
183   Log.Error('  ' + s)
184   Log.Error('  ' + (' '*leftpos) + ('^'*(rightpos - leftpos + 1)))
185   DriverExit(1)
186
187 # Find the leftmost position in "s" which begins a substring
188 # in "strset", starting at "pos".
189 # For example:
190 #   FindFirst('hello world', 0, ['h','o']) = ('h', 0)
191 #   FindFirst('hello world', 1, ['h','o']) = ('o', 4)
192 #   FindFirst('hello world', 0, ['x']) = (None,11)
193 def FindFirst(s, pos, strset):
194   m = {}
195   for ss in strset:
196     m[s.find(ss, pos)] = ss
197   if -1 in m:
198     del m[-1]
199   if len(m) == 0:
200     return (None, len(s))
201   pos = min(m)
202   return (m[pos], pos)
203
204 class Environment(object):
205   functions = {}
206
207   @classmethod
208   def register(cls, func):
209     """ Register a function for use in the evaluator """
210     cls.functions[func.__name__] = func
211     return func
212
213   def __init__(self):
214     self.stack = []
215     self.reset()
216
217   def reset(self):
218     self.data = dict(INITIAL_ENV)
219
220   def update(self, extra):
221     self.data.update(extra)
222
223   def dump(self):
224     for (k,v) in self.data.iteritems():
225       print '%s == %s' % (k,v)
226
227   def push(self):
228     self.stack.append(self.data)
229     self.data = dict(self.data) # Make a copy
230
231   def pop(self):
232     self.data = self.stack.pop()
233
234   def has(self, varname):
235     return varname in self.data
236
237   def getraw(self, varname):
238     return self.eval(self.data[varname])
239
240   # Evaluate a variable from the environment.
241   # Returns a list of terms.
242   def get(self, varname):
243     return shell.split(self.getraw(varname))
244
245   # Retrieve a variable from the environment which
246   # is a single term. Returns a string.
247   def getone(self, varname):
248     return shell.unescape(self.getraw(varname))
249
250   def getbool(self, varname):
251     return bool(int(self.getone(varname)))
252
253   def setbool(self, varname, val):
254     if val:
255       self.set(varname, '1')
256     else:
257       self.set(varname, '0')
258
259   # Set a variable in the environment without shell-escape
260   def setraw(self, varname, val):
261     self.data[varname] = val
262
263   # Set one or more variables using named arguments
264   def setmany(self, **kwargs):
265     for k,v in kwargs.iteritems():
266       if isinstance(v, types.StringTypes):
267         self.set(k, v)
268       elif isinstance(v, types.ListType):
269         self.set(k, *v)
270       else:
271         Log.Fatal('env.setmany given a non-string and non-list value')
272
273   def clear(self, varname):
274     self.data[varname] = ''
275
276   # Set a variable to one or more terms, applying shell-escape.
277   def set(self, varname, *vals):
278     self.clear(varname)
279     self.append(varname, *vals)
280
281   # Append one or more terms to a variable in the
282   # environment, applying shell-escape.
283   def append(self, varname, *vals):
284     escaped = [ shell.escape(v) for v in vals ]
285     if len(self.data[varname]) > 0:
286       self.data[varname] += ' '
287     self.data[varname] += ' '.join(escaped)
288
289   # Evaluate an expression s
290   def eval(self, s):
291     (result, i) = self.eval_expr(s, 0, [])
292     assert(i == len(s))
293     return result
294
295   ######################################################################
296   # EXPRESSION EVALUATION CODE
297   # Context Free Grammar:
298   #
299   # str = empty | string literal
300   # expr = str | expr '$' '{' bracket_expr '}' expr
301   # bracket_expr = varname | boolexpr ? expr | boolexpr ? expr : expr | @call
302   # boolexpr = boolval | boolval '&&' boolexpr | boolval '||' boolexpr
303   # boolval = varname | !varname | #varname | !#varname | varname '==' str
304   # varname = str | varname '%' bracket_expr '%' varname
305   # call = func | func ':' arglist
306   # func = str
307   # arglist = empty | arg ':' arglist
308   #
309   # Do not call these functions outside of this class.
310   # The env.eval method is the external interface to the evaluator.
311   ######################################################################
312
313   # Evaluate a string literal
314   def eval_str(self, s, pos, terminators):
315     (_,i) = FindFirst(s, pos, terminators)
316     return (s[pos:i], i)
317
318   # Evaluate %var% substitutions inside a variable name.
319   # Returns (the_actual_variable_name, endpos)
320   # Terminated by } character
321   def eval_varname(self, s, pos, terminators):
322     (_,i) = FindFirst(s, pos, ['%'] + terminators)
323     leftpart = s[pos:i].strip(' ')
324     if i == len(s) or s[i] in terminators:
325       return (leftpart, i)
326
327     (middlepart, j) = self.eval_bracket_expr(s, i+1, ['%'])
328     if j == len(s) or s[j] != '%':
329       ParseError(s, i, j, "Unterminated %")
330
331     (rightpart, k) = self.eval_varname(s, j+1, terminators)
332
333     fullname = leftpart + middlepart + rightpart
334     fullname = fullname.strip()
335     return (fullname, k)
336
337   # Absorb whitespace
338   def eval_whitespace(self, s, pos):
339     i = pos
340     while i < len(s) and s[i] == ' ':
341       i += 1
342     return (None, i)
343
344   def eval_bool_val(self, s, pos, terminators):
345     (_,i) = self.eval_whitespace(s, pos)
346
347     if s[i] == '!':
348       negated = True
349       i += 1
350     else:
351       negated = False
352
353     (_,i) = self.eval_whitespace(s, i)
354
355     if s[i] == '#':
356       uselen = True
357       i += 1
358     else:
359       uselen = False
360
361     (varname, j) = self.eval_varname(s, i, ['=']+terminators)
362     if j == len(s):
363       # This is an error condition one level up. Don't evaluate anything.
364       return (False, j)
365
366     if varname not in self.data:
367       ParseError(s, i, j, "Undefined variable '%s'" % varname)
368     vardata = self.data[varname]
369
370     contents = self.eval(vardata)
371
372     if s[j] == '=':
373       # String equality test
374       if j+1 == len(s) or s[j+1] != '=':
375         ParseError(s, j, j, "Unexpected token")
376       if uselen:
377         ParseError(s, j, j, "Cannot combine == and #")
378       (_,j) = self.eval_whitespace(s, j+2)
379       (literal_str,j) = self.eval_str(s, j, [' ']+terminators)
380       (_,j) = self.eval_whitespace(s, j)
381       if j == len(s):
382         return (False, j) # Error one level up
383     else:
384       literal_str = None
385
386     if uselen:
387       val = (len(contents) != 0)
388     elif literal_str is not None:
389       val = (contents == literal_str)
390     else:
391       if contents not in ('0','1'):
392         ParseError(s, j, j,
393           "%s evaluated to %s, which is not a boolean!" % (varname, contents))
394       val = bool(int(contents))
395     return (negated ^ val, j)
396
397   # Evaluate a boolexpr
398   def eval_bool_expr(self, s, pos, terminators):
399     (boolval1, i) = self.eval_bool_val(s, pos, ['&','|']+terminators)
400     if i == len(s):
401       # This is an error condition one level up. Don't evaluate anything.
402       return (False, i)
403
404     if s[i] in ('&','|'):
405       # and/or expression
406       if i+1 == len(s) or s[i+1] != s[i]:
407         ParseError(s, i, i, "Unexpected token")
408       is_and = (s[i] == '&')
409
410       (boolval2, j) = self.eval_bool_expr(s, i+2, terminators)
411       if j == len(s):
412         # This is an error condition one level up.
413         return (False, j)
414
415       if is_and:
416         return (boolval1 and boolval2, j)
417       else:
418         return (boolval1 or boolval2, j)
419
420     return (boolval1, i)
421
422   # Evaluate the inside of a ${} or %%.
423   # Returns the (the_evaluated_string, endpos)
424   def eval_bracket_expr(self, s, pos, terminators):
425     (_,pos) = self.eval_whitespace(s, pos)
426
427     if s[pos] == '@':
428       # Function call: ${@func}
429       # or possibly  : ${@func:arg1:arg2...}
430       (_,i) = FindFirst(s, pos, [':']+terminators)
431       if i == len(s):
432         return ('', i) # Error one level up
433       funcname = s[pos+1:i]
434
435       if s[i] != ':':
436         j = i
437         args = []
438       else:
439         (_,j) = FindFirst(s, i+1, terminators)
440         if j == len(s):
441           return ('', j) # Error one level up
442         args = s[i+1:j].split(':')
443
444       val = self.functions[funcname](*args)
445       contents = self.eval(val)
446       return (contents, j)
447
448     (m,_) = FindFirst(s, pos, ['?']+terminators)
449     if m != '?':
450       # Regular variable substitution
451       (varname,i) = self.eval_varname(s, pos, terminators)
452       if len(s) == i:
453         return ('', i)  # Error one level up
454       if varname not in self.data:
455         ParseError(s, pos, i, "Undefined variable '%s'" % varname)
456       vardata = self.data[varname]
457       contents = self.eval(vardata)
458       return (contents, i)
459     else:
460       # Ternary Mode
461       (is_cond_true,i) = self.eval_bool_expr(s, pos, ['?']+terminators)
462       assert(i < len(s) and s[i] == '?')
463
464       (if_true_expr, j) = self.eval_expr(s, i+1, [' : ']+terminators)
465       if j == len(s):
466         return ('', j) # Error one level up
467
468       if s[j:j+3] == ' : ':
469         (if_false_expr,j) = self.eval_expr(s, j+3, terminators)
470         if j == len(s):
471           # This is an error condition one level up.
472           return ('', j)
473       else:
474         if_false_expr = ''
475
476       if is_cond_true:
477         contents = if_true_expr.strip()
478       else:
479         contents = if_false_expr.strip()
480
481       return (contents, j)
482
483   # Evaluate an expression with ${} in string s, starting at pos.
484   # Returns (the_evaluated_expression, endpos)
485   def eval_expr(self, s, pos, terminators):
486     (m,i) = FindFirst(s, pos, ['${'] + terminators)
487     leftpart = s[pos:i]
488     if i == len(s) or m in terminators:
489       return (leftpart, i)
490
491     (middlepart, j) = self.eval_bracket_expr(s, i+2, ['}'])
492     if j == len(s) or s[j] != '}':
493       ParseError(s, i, j, 'Unterminated ${')
494
495     (rightpart, k) = self.eval_expr(s, j+1, terminators)
496     return (leftpart + middlepart + rightpart, k)
497
498
499 env = Environment()
500
501 def override_env(meth_name, func):
502   """Override a method in the global |env|, given the method name
503   and the new function.
504   """
505   global env
506   setattr(env, meth_name, types.MethodType(func, env, Environment))