2f41b8f661f8bee5ce70002ceff89abdef2d80b1
[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   'PNACL_COMPRESS': '${BASE_LLVM_BIN}/pnacl-bccompress${EXEC_EXT}',
155
156   # Native LLVM tools
157   'LLVM_PNACL_LLC': '${BASE_LLVM_BIN}/pnacl-llc${EXEC_EXT}',
158   # llvm-mc is llvm's native assembler
159   'LLVM_MC'       : '${BASE_LLVM_BIN}/llvm-mc${EXEC_EXT}',
160
161   # Binutils
162   'BINUTILS_BASE'  : '${BASE_BINUTILS}/bin/le32-nacl-',
163   'OBJDUMP'        : '${BINUTILS_BASE}objdump${EXEC_EXT}',
164   'NM'             : '${BINUTILS_BASE}nm${EXEC_EXT}',
165   'AR'             : '${BINUTILS_BASE}ar${EXEC_EXT}',
166   'RANLIB'         : '${BINUTILS_BASE}ranlib${EXEC_EXT}',
167   'READELF'        : '${BINUTILS_BASE}readelf${EXEC_EXT}',
168   'STRIP'          : '${BINUTILS_BASE}strip${EXEC_EXT}',
169   # linker (used for both bitcode and ELF linking)
170   'LD'        : '${BINUTILS_BASE}ld.gold${EXEC_EXT}',
171
172   # Use the default command line arguments to the sandboxed translator.
173   'USE_DEFAULT_CMD_LINE': '1',
174 }
175
176 ######################################################################
177 #
178 # Environment
179 #
180 ######################################################################
181
182 def ParseError(s, leftpos, rightpos, msg):
183   Log.Error("Parse Error: %s", msg)
184   Log.Error('  ' + s)
185   Log.Error('  ' + (' '*leftpos) + ('^'*(rightpos - leftpos + 1)))
186   DriverExit(1)
187
188 # Find the leftmost position in "s" which begins a substring
189 # in "strset", starting at "pos".
190 # For example:
191 #   FindFirst('hello world', 0, ['h','o']) = ('h', 0)
192 #   FindFirst('hello world', 1, ['h','o']) = ('o', 4)
193 #   FindFirst('hello world', 0, ['x']) = (None,11)
194 def FindFirst(s, pos, strset):
195   m = {}
196   for ss in strset:
197     m[s.find(ss, pos)] = ss
198   if -1 in m:
199     del m[-1]
200   if len(m) == 0:
201     return (None, len(s))
202   pos = min(m)
203   return (m[pos], pos)
204
205 class Environment(object):
206   functions = {}
207
208   @classmethod
209   def register(cls, func):
210     """ Register a function for use in the evaluator """
211     cls.functions[func.__name__] = func
212     return func
213
214   def __init__(self):
215     self.stack = []
216     self.reset()
217
218   def reset(self):
219     self.data = dict(INITIAL_ENV)
220
221   def update(self, extra):
222     self.data.update(extra)
223
224   def dump(self):
225     for (k,v) in self.data.iteritems():
226       print '%s == %s' % (k,v)
227
228   def push(self):
229     self.stack.append(self.data)
230     self.data = dict(self.data) # Make a copy
231
232   def pop(self):
233     self.data = self.stack.pop()
234
235   def has(self, varname):
236     return varname in self.data
237
238   def getraw(self, varname):
239     return self.eval(self.data[varname])
240
241   # Evaluate a variable from the environment.
242   # Returns a list of terms.
243   def get(self, varname):
244     return shell.split(self.getraw(varname))
245
246   # Retrieve a variable from the environment which
247   # is a single term. Returns a string.
248   def getone(self, varname):
249     return shell.unescape(self.getraw(varname))
250
251   def getbool(self, varname):
252     return bool(int(self.getone(varname)))
253
254   def setbool(self, varname, val):
255     if val:
256       self.set(varname, '1')
257     else:
258       self.set(varname, '0')
259
260   # Set a variable in the environment without shell-escape
261   def setraw(self, varname, val):
262     self.data[varname] = val
263
264   # Set one or more variables using named arguments
265   def setmany(self, **kwargs):
266     for k,v in kwargs.iteritems():
267       if isinstance(v, types.StringTypes):
268         self.set(k, v)
269       elif isinstance(v, types.ListType):
270         self.set(k, *v)
271       else:
272         Log.Fatal('env.setmany given a non-string and non-list value')
273
274   def clear(self, varname):
275     self.data[varname] = ''
276
277   # Set a variable to one or more terms, applying shell-escape.
278   def set(self, varname, *vals):
279     self.clear(varname)
280     self.append(varname, *vals)
281
282   # Append one or more terms to a variable in the
283   # environment, applying shell-escape.
284   def append(self, varname, *vals):
285     escaped = [ shell.escape(v) for v in vals ]
286     if len(self.data[varname]) > 0:
287       self.data[varname] += ' '
288     self.data[varname] += ' '.join(escaped)
289
290   # Evaluate an expression s
291   def eval(self, s):
292     (result, i) = self.eval_expr(s, 0, [])
293     assert(i == len(s))
294     return result
295
296   ######################################################################
297   # EXPRESSION EVALUATION CODE
298   # Context Free Grammar:
299   #
300   # str = empty | string literal
301   # expr = str | expr '$' '{' bracket_expr '}' expr
302   # bracket_expr = varname | boolexpr ? expr | boolexpr ? expr : expr | @call
303   # boolexpr = boolval | boolval '&&' boolexpr | boolval '||' boolexpr
304   # boolval = varname | !varname | #varname | !#varname | varname '==' str
305   # varname = str | varname '%' bracket_expr '%' varname
306   # call = func | func ':' arglist
307   # func = str
308   # arglist = empty | arg ':' arglist
309   #
310   # Do not call these functions outside of this class.
311   # The env.eval method is the external interface to the evaluator.
312   ######################################################################
313
314   # Evaluate a string literal
315   def eval_str(self, s, pos, terminators):
316     (_,i) = FindFirst(s, pos, terminators)
317     return (s[pos:i], i)
318
319   # Evaluate %var% substitutions inside a variable name.
320   # Returns (the_actual_variable_name, endpos)
321   # Terminated by } character
322   def eval_varname(self, s, pos, terminators):
323     (_,i) = FindFirst(s, pos, ['%'] + terminators)
324     leftpart = s[pos:i].strip(' ')
325     if i == len(s) or s[i] in terminators:
326       return (leftpart, i)
327
328     (middlepart, j) = self.eval_bracket_expr(s, i+1, ['%'])
329     if j == len(s) or s[j] != '%':
330       ParseError(s, i, j, "Unterminated %")
331
332     (rightpart, k) = self.eval_varname(s, j+1, terminators)
333
334     fullname = leftpart + middlepart + rightpart
335     fullname = fullname.strip()
336     return (fullname, k)
337
338   # Absorb whitespace
339   def eval_whitespace(self, s, pos):
340     i = pos
341     while i < len(s) and s[i] == ' ':
342       i += 1
343     return (None, i)
344
345   def eval_bool_val(self, s, pos, terminators):
346     (_,i) = self.eval_whitespace(s, pos)
347
348     if s[i] == '!':
349       negated = True
350       i += 1
351     else:
352       negated = False
353
354     (_,i) = self.eval_whitespace(s, i)
355
356     if s[i] == '#':
357       uselen = True
358       i += 1
359     else:
360       uselen = False
361
362     (varname, j) = self.eval_varname(s, i, ['=']+terminators)
363     if j == len(s):
364       # This is an error condition one level up. Don't evaluate anything.
365       return (False, j)
366
367     if varname not in self.data:
368       ParseError(s, i, j, "Undefined variable '%s'" % varname)
369     vardata = self.data[varname]
370
371     contents = self.eval(vardata)
372
373     if s[j] == '=':
374       # String equality test
375       if j+1 == len(s) or s[j+1] != '=':
376         ParseError(s, j, j, "Unexpected token")
377       if uselen:
378         ParseError(s, j, j, "Cannot combine == and #")
379       (_,j) = self.eval_whitespace(s, j+2)
380       (literal_str,j) = self.eval_str(s, j, [' ']+terminators)
381       (_,j) = self.eval_whitespace(s, j)
382       if j == len(s):
383         return (False, j) # Error one level up
384     else:
385       literal_str = None
386
387     if uselen:
388       val = (len(contents) != 0)
389     elif literal_str is not None:
390       val = (contents == literal_str)
391     else:
392       if contents not in ('0','1'):
393         ParseError(s, j, j,
394           "%s evaluated to %s, which is not a boolean!" % (varname, contents))
395       val = bool(int(contents))
396     return (negated ^ val, j)
397
398   # Evaluate a boolexpr
399   def eval_bool_expr(self, s, pos, terminators):
400     (boolval1, i) = self.eval_bool_val(s, pos, ['&','|']+terminators)
401     if i == len(s):
402       # This is an error condition one level up. Don't evaluate anything.
403       return (False, i)
404
405     if s[i] in ('&','|'):
406       # and/or expression
407       if i+1 == len(s) or s[i+1] != s[i]:
408         ParseError(s, i, i, "Unexpected token")
409       is_and = (s[i] == '&')
410
411       (boolval2, j) = self.eval_bool_expr(s, i+2, terminators)
412       if j == len(s):
413         # This is an error condition one level up.
414         return (False, j)
415
416       if is_and:
417         return (boolval1 and boolval2, j)
418       else:
419         return (boolval1 or boolval2, j)
420
421     return (boolval1, i)
422
423   # Evaluate the inside of a ${} or %%.
424   # Returns the (the_evaluated_string, endpos)
425   def eval_bracket_expr(self, s, pos, terminators):
426     (_,pos) = self.eval_whitespace(s, pos)
427
428     if s[pos] == '@':
429       # Function call: ${@func}
430       # or possibly  : ${@func:arg1:arg2...}
431       (_,i) = FindFirst(s, pos, [':']+terminators)
432       if i == len(s):
433         return ('', i) # Error one level up
434       funcname = s[pos+1:i]
435
436       if s[i] != ':':
437         j = i
438         args = []
439       else:
440         (_,j) = FindFirst(s, i+1, terminators)
441         if j == len(s):
442           return ('', j) # Error one level up
443         args = s[i+1:j].split(':')
444
445       val = self.functions[funcname](*args)
446       contents = self.eval(val)
447       return (contents, j)
448
449     (m,_) = FindFirst(s, pos, ['?']+terminators)
450     if m != '?':
451       # Regular variable substitution
452       (varname,i) = self.eval_varname(s, pos, terminators)
453       if len(s) == i:
454         return ('', i)  # Error one level up
455       if varname not in self.data:
456         ParseError(s, pos, i, "Undefined variable '%s'" % varname)
457       vardata = self.data[varname]
458       contents = self.eval(vardata)
459       return (contents, i)
460     else:
461       # Ternary Mode
462       (is_cond_true,i) = self.eval_bool_expr(s, pos, ['?']+terminators)
463       assert(i < len(s) and s[i] == '?')
464
465       (if_true_expr, j) = self.eval_expr(s, i+1, [' : ']+terminators)
466       if j == len(s):
467         return ('', j) # Error one level up
468
469       if s[j:j+3] == ' : ':
470         (if_false_expr,j) = self.eval_expr(s, j+3, terminators)
471         if j == len(s):
472           # This is an error condition one level up.
473           return ('', j)
474       else:
475         if_false_expr = ''
476
477       if is_cond_true:
478         contents = if_true_expr.strip()
479       else:
480         contents = if_false_expr.strip()
481
482       return (contents, j)
483
484   # Evaluate an expression with ${} in string s, starting at pos.
485   # Returns (the_evaluated_expression, endpos)
486   def eval_expr(self, s, pos, terminators):
487     (m,i) = FindFirst(s, pos, ['${'] + terminators)
488     leftpart = s[pos:i]
489     if i == len(s) or m in terminators:
490       return (leftpart, i)
491
492     (middlepart, j) = self.eval_bracket_expr(s, i+2, ['}'])
493     if j == len(s) or s[j] != '}':
494       ParseError(s, i, j, 'Unterminated ${')
495
496     (rightpart, k) = self.eval_expr(s, j+1, terminators)
497     return (leftpart + middlepart + rightpart, k)
498
499
500 env = Environment()
501
502 def override_env(meth_name, func):
503   """Override a method in the global |env|, given the method name
504   and the new function.
505   """
506   global env
507   setattr(env, meth_name, types.MethodType(func, env, Environment))