Upstream version 10.39.225.0
[platform/framework/web/crosswalk.git] / src / native_client / site_scons / site_tools / naclsdk.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 """NaCl SDK tool SCons."""
7
8 import __builtin__
9 import re
10 import os
11 import shutil
12 import sys
13 import SCons.Scanner
14 import SCons.Script
15 import subprocess
16 import tempfile
17
18
19 NACL_TOOL_MAP = {
20     'arm': {
21         '32': {
22             'tooldir': 'arm-nacl',
23             'as_flag': '',
24             'cc_flag': '',
25             'ld_flag': '',
26             },
27         },
28     'x86': {
29         '32': {
30             'tooldir': 'i686-nacl',
31             'other_libdir': 'lib32',
32             'as_flag': '--32',
33             'cc_flag': '-m32',
34             'ld_flag': ' -melf_i386_nacl',
35             },
36         '64': {
37             'tooldir': 'x86_64-nacl',
38             'other_libdir': 'lib64',
39             'as_flag': '--64',
40             'cc_flag': '-m64',
41             'ld_flag': ' -melf_x86_64_nacl',
42             },
43         },
44     }
45
46 def _StubOutEnvToolsForBuiltElsewhere(env):
47   """Stub out all tools so that they point to 'true'.
48
49   Some machines have their code built by another machine, they'll therefore
50   run 'true' instead of running the usual build tools.
51
52   Args:
53     env: The SCons environment in question.
54   """
55   assert(env.Bit('built_elsewhere'))
56   env.Replace(CC='true', CXX='true', LINK='true', AR='true',
57               RANLIB='true', AS='true', ASPP='true', LD='true',
58               STRIP='true', PNACLOPT='true', PNACLFINALIZE='true')
59
60
61 def _SetEnvForNativeSdk(env, sdk_path):
62   """Initialize environment according to target architecture."""
63
64   bin_path = os.path.join(sdk_path, 'bin')
65   # NOTE: attempts to eliminate this PATH setting and use
66   #       absolute path have been futile
67   env.PrependENVPath('PATH', bin_path)
68
69   tool_prefix = None
70   tool_map = NACL_TOOL_MAP[env['TARGET_ARCHITECTURE']]
71   subarch_spec = tool_map[env['TARGET_SUBARCH']]
72   tooldir = subarch_spec['tooldir']
73   # We need to pass it extra options for the subarch we are building.
74   as_mode_flag = subarch_spec['as_flag']
75   cc_mode_flag = subarch_spec['cc_flag']
76   ld_mode_flag = subarch_spec['ld_flag']
77   if os.path.exists(os.path.join(sdk_path, tooldir)):
78     # The tooldir for the build target exists.
79     # The tools there do the right thing without special options.
80     tool_prefix = tooldir
81     libdir = os.path.join(tooldir, 'lib')
82   else:
83     # We're building for a target for which there is no matching tooldir.
84     # For example, for x86-32 when only <sdk_path>/x86_64-nacl/ exists.
85     # Find a tooldir for a different subarch that does exist.
86     others_map = tool_map.copy()
87     del others_map[env['TARGET_SUBARCH']]
88     for subarch, tool_spec in others_map.iteritems():
89       tooldir = tool_spec['tooldir']
90       if os.path.exists(os.path.join(sdk_path, tooldir)):
91         # OK, this is the other subarch to use as tooldir.
92         tool_prefix = tooldir
93         # The lib directory may have an alternate name, i.e.
94         # 'lib32' in the x86_64-nacl tooldir.
95         libdir = os.path.join(tooldir, subarch_spec.get('other_libdir', 'lib'))
96         break
97
98   if tool_prefix is None:
99     raise Exception("Cannot find a toolchain for %s in %s" %
100                     (env['TARGET_FULLARCH'], sdk_path))
101
102   env.Replace(# Replace header and lib paths.
103               # where to put nacl extra sdk headers
104               # TODO(robertm): switch to using the mechanism that
105               #                passes arguments to scons
106               NACL_SDK_INCLUDE='%s/%s/include' % (sdk_path, tool_prefix),
107               # where to find/put nacl generic extra sdk libraries
108               NACL_SDK_LIB='%s/%s' % (sdk_path, libdir),
109               # Replace the normal unix tools with the NaCl ones.
110               CC=os.path.join(bin_path, '%s-gcc' % tool_prefix),
111               CXX=os.path.join(bin_path, '%s-g++' % tool_prefix),
112               AR=os.path.join(bin_path, '%s-ar' % tool_prefix),
113               AS=os.path.join(bin_path, '%s-as' % tool_prefix),
114               ASPP=os.path.join(bin_path, '%s-gcc' % tool_prefix),
115               GDB=os.path.join(bin_path, '%s-gdb' % tool_prefix),
116               # NOTE: use g++ for linking so we can handle C AND C++.
117               LINK=os.path.join(bin_path, '%s-g++' % tool_prefix),
118               # Grrr... and sometimes we really need ld.
119               LD=os.path.join(bin_path, '%s-ld' % tool_prefix) + ld_mode_flag,
120               RANLIB=os.path.join(bin_path, '%s-ranlib' % tool_prefix),
121               NM=os.path.join(bin_path, '%s-nm' % tool_prefix),
122               OBJDUMP=os.path.join(bin_path, '%s-objdump' % tool_prefix),
123               STRIP=os.path.join(bin_path, '%s-strip' % tool_prefix),
124               ADDR2LINE=os.path.join(bin_path, '%s-addr2line' % tool_prefix),
125               BASE_LINKFLAGS=[cc_mode_flag],
126               BASE_CFLAGS=[cc_mode_flag],
127               BASE_CXXFLAGS=[cc_mode_flag],
128               BASE_ASFLAGS=[as_mode_flag],
129               BASE_ASPPFLAGS=[cc_mode_flag],
130               CFLAGS=['-std=gnu99'],
131               CCFLAGS=['-O3',
132                        '-Werror',
133                        '-Wall',
134                        '-Wno-variadic-macros',
135                        '-Wswitch-enum',
136                        '-g',
137                        '-fno-stack-protector',
138                        '-fdiagnostics-show-option',
139                        '-pedantic',
140                        '-D__linux__',
141                        ],
142               ASFLAGS=[],
143               )
144
145   # NaClSdk environment seems to be inherited from the host environment.
146   # On Linux host, this probably makes sense. On Windows and Mac, this
147   # introduces nothing except problems.
148   # For now, simply override the environment settings as in
149   # <scons>/engine/SCons/Platform/posix.py
150   env.Replace(LIBPREFIX='lib',
151               LIBSUFFIX='.a',
152               SHLIBPREFIX='$LIBPREFIX',
153               SHLIBSUFFIX='.so',
154               LIBPREFIXES=['$LIBPREFIX'],
155               LIBSUFFIXES=['$LIBSUFFIX', '$SHLIBSUFFIX'],
156               )
157   # Force -fPIC when compiling for shared libraries.
158   env.AppendUnique(SHCCFLAGS=['-fPIC'],
159                    )
160
161 def _SetEnvForPnacl(env, root):
162   # All the PNaCl tools require Python to be in the PATH.
163   arch = env['TARGET_FULLARCH']
164   assert arch in ['arm', 'mips32', 'x86-32', 'x86-64']
165
166   if env.Bit('pnacl_unsandboxed'):
167     if env.Bit('host_linux'):
168       arch = '%s-linux' % arch
169     elif env.Bit('host_mac'):
170       arch = '%s-mac' % arch
171   if env.Bit('nonsfi_nacl'):
172     arch += '-nonsfi'
173   arch_flag = ' -arch %s' % arch
174   if env.Bit('pnacl_generate_pexe'):
175     ld_arch_flag = ''
176   else:
177     ld_arch_flag = arch_flag
178
179   llc_mtriple_flag = ''
180   if env.Bit('minsfi'):
181     llc_cpu = ''
182     if env.Bit('build_x86_32'):
183       llc_cpu = 'i686'
184     elif env.Bit('build_x86_64'):
185       llc_cpu = 'x86_64'
186
187     if env.Bit('host_linux'):
188       llc_mtriple_flag = ' -mtriple=%s-linux-gnu' % llc_cpu
189     elif env.Bit('host_mac'):
190       llc_mtriple_flag = ' -mtriple=%s-apple-darwin' % llc_cpu
191
192   translator_root = os.path.join(os.path.dirname(root), 'pnacl_translator')
193
194   binprefix = os.path.join(root, 'bin', 'pnacl-')
195   binext = ''
196   if env.Bit('host_windows'):
197     binext = '.bat'
198
199   pnacl_ar = binprefix + 'ar' + binext
200   pnacl_as = binprefix + 'as' + binext
201   pnacl_nm = binprefix + 'nm' + binext
202   pnacl_ranlib = binprefix + 'ranlib' + binext
203   # Use the standalone sandboxed translator in sbtc mode
204   if env.Bit('use_sandboxed_translator'):
205     pnacl_translate = os.path.join(translator_root, 'bin',
206                                    'pnacl-translate' + binext)
207   else:
208     pnacl_translate = binprefix + 'translate' + binext
209
210   pnacl_cc = binprefix + 'clang' + binext
211   pnacl_cxx = binprefix + 'clang++' + binext
212
213   pnacl_ld = binprefix + 'ld' + binext
214   pnacl_disass = binprefix + 'dis' + binext
215   pnacl_finalize = binprefix + 'finalize' + binext
216   pnacl_opt = binprefix + 'opt' + binext
217   pnacl_strip = binprefix + 'strip' + binext
218   pnacl_llc = binprefix + 'llc' + binext
219
220   # NOTE: XXX_flags start with space for easy concatenation
221   # The flags generated here get baked into the commands (CC, CXX, LINK)
222   # instead of CFLAGS etc to keep them from getting blown away by some
223   # tests. Don't add flags here unless they always need to be preserved.
224   pnacl_cxx_flags = ''
225   pnacl_cc_flags = ' -std=gnu99'
226   pnacl_ld_flags = ' ' + ' '.join(env['PNACL_BCLDFLAGS'])
227   pnacl_translate_flags = ''
228   pnacl_llc_flags = ''
229
230   if env.Bit('nacl_pic'):
231     pnacl_cc_flags += ' -fPIC'
232     pnacl_cxx_flags += ' -fPIC'
233     # NOTE: this is a special hack for the pnacl backend which
234     #       does more than linking
235     pnacl_ld_flags += ' -fPIC'
236     pnacl_translate_flags += ' -fPIC'
237
238   if env.Bit('minsfi'):
239     pnacl_llc_flags += ' -relocation-model=pic -filetype=obj'
240     pnacl_ld_flags += ' -nostdlib -Wl,-r -L' + os.path.join(root, 'usr', 'lib')
241
242   if env.Bit('use_sandboxed_translator'):
243     sb_flags = ' --pnacl-sb'
244     pnacl_ld_flags += sb_flags
245     pnacl_translate_flags += sb_flags
246
247   if env.Bit('x86_64_zero_based_sandbox'):
248     pnacl_translate_flags += ' -sfi-zero-based-sandbox'
249
250   env.Replace(# Replace header and lib paths.
251               NACL_SDK_INCLUDE=os.path.join(root, 'le32-nacl', 'include'),
252               NACL_SDK_LIB=os.path.join(root, 'le32-nacl', 'lib'),
253               # Remove arch-specific flags (if any)
254               BASE_LINKFLAGS='',
255               BASE_CFLAGS='',
256               BASE_CXXFLAGS='',
257               BASE_ASFLAGS='',
258               BASE_ASPPFLAGS='',
259               # Replace the normal unix tools with the PNaCl ones.
260               CC=pnacl_cc + pnacl_cc_flags,
261               CXX=pnacl_cxx + pnacl_cxx_flags,
262               ASPP=pnacl_cc + pnacl_cc_flags,
263               LIBPREFIX="lib",
264               SHLIBPREFIX="lib",
265               SHLIBSUFFIX=".so",
266               OBJSUFFIX=".bc",
267               LINK=pnacl_cxx + ld_arch_flag + pnacl_ld_flags,
268               # Although we are currently forced to produce native output
269               # for LINK, we are free to produce bitcode for SHLINK
270               # (SharedLibrary linking) because scons doesn't do anything
271               # with shared libraries except use them with the toolchain.
272               SHLINK=pnacl_cxx + ld_arch_flag + pnacl_ld_flags,
273               LD=pnacl_ld,
274               AR=pnacl_ar,
275               AS=pnacl_as + ld_arch_flag,
276               RANLIB=pnacl_ranlib,
277               DISASS=pnacl_disass,
278               OBJDUMP=pnacl_disass,
279               STRIP=pnacl_strip,
280               TRANSLATE=pnacl_translate + arch_flag + pnacl_translate_flags,
281               PNACLFINALIZE=pnacl_finalize,
282               PNACLOPT=pnacl_opt,
283               LLC=pnacl_llc + llc_mtriple_flag + pnacl_llc_flags,
284               )
285
286   if env.Bit('built_elsewhere'):
287     def FakeInstall(dest, source, env):
288       print 'Not installing', dest
289     _StubOutEnvToolsForBuiltElsewhere(env)
290     env.Replace(INSTALL=FakeInstall)
291     if env.Bit('translate_in_build_step'):
292       env.Replace(TRANSLATE='true')
293     env.Replace(PNACLFINALIZE='true')
294
295
296 def PNaClForceNative(env):
297   assert(env.Bit('bitcode'))
298   if env.Bit('pnacl_generate_pexe'):
299     env.Replace(CC='NO-NATIVE-CC-INVOCATION-ALLOWED',
300                 CXX='NO-NATIVE-CXX-INVOCATION-ALLOWED')
301     return
302
303   env.Replace(OBJSUFFIX='.o',
304               SHLIBSUFFIX='.so')
305   arch_flag = ' -arch ${TARGET_FULLARCH}'
306   cc_flags = ' --pnacl-allow-native --pnacl-allow-translate'
307   env.Append(CC=arch_flag + cc_flags,
308              CXX=arch_flag + cc_flags,
309              ASPP=arch_flag + cc_flags,
310              LINK=cc_flags) # Already has -arch
311   env['LD'] = 'NO-NATIVE-LD-INVOCATION-ALLOWED'
312   env['SHLINK'] = '${LINK}'
313   if env.Bit('built_elsewhere'):
314     _StubOutEnvToolsForBuiltElsewhere(env)
315
316 # Get an environment for nacl-gcc when in PNaCl mode.
317 def PNaClGetNNaClEnv(env):
318   assert(env.Bit('bitcode'))
319   assert(not env.Bit('target_mips32'))
320
321   # This is kind of a hack. We clone the environment,
322   # clear the bitcode bit, and then reload naclsdk.py
323   native_env = env.Clone()
324   native_env.ClearBits('bitcode')
325   if env.Bit('built_elsewhere'):
326     _StubOutEnvToolsForBuiltElsewhere(env)
327   else:
328     native_env = native_env.Clone(tools=['naclsdk'])
329     if native_env.Bit('pnacl_generate_pexe'):
330       native_env.Replace(CC='NO-NATIVE-CC-INVOCATION-ALLOWED',
331                          CXX='NO-NATIVE-CXX-INVOCATION-ALLOWED')
332     else:
333       # These are unfortunately clobbered by running Tool.
334       native_env.Replace(EXTRA_CFLAGS=env['EXTRA_CFLAGS'],
335                          EXTRA_CXXFLAGS=env['EXTRA_CXXFLAGS'],
336                          CCFLAGS=env['CCFLAGS'],
337                          CFLAGS=env['CFLAGS'],
338                          CXXFLAGS=env['CXXFLAGS'])
339   return native_env
340
341
342 # This adds architecture specific defines for the target architecture.
343 # These are normally omitted by PNaCl.
344 # For example: __i686__, __arm__, __mips__, __x86_64__
345 def AddBiasForPNaCl(env, temporarily_allow=True):
346   assert(env.Bit('bitcode'))
347   # re: the temporarily_allow flag -- that is for:
348   # BUG= http://code.google.com/p/nativeclient/issues/detail?id=1248
349   if env.Bit('pnacl_generate_pexe') and not temporarily_allow:
350     env.Replace(CC='NO-NATIVE-CC-INVOCATION-ALLOWED',
351                 CXX='NO-NATIVE-CXX-INVOCATION-ALLOWED')
352     return
353
354   if env.Bit('target_arm'):
355     env.AppendUnique(CCFLAGS=['--pnacl-arm-bias'],
356                      ASPPFLAGS=['--pnacl-arm-bias'])
357   elif env.Bit('target_x86_32'):
358     env.AppendUnique(CCFLAGS=['--pnacl-i686-bias'],
359                      ASPPFLAGS=['--pnacl-i686-bias'])
360   elif env.Bit('target_x86_64'):
361     env.AppendUnique(CCFLAGS=['--pnacl-x86_64-bias'],
362                      ASPPFLAGS=['--pnacl-x86_64-bias'])
363   elif env.Bit('target_mips32'):
364     env.AppendUnique(CCFLAGS=['--pnacl-mips-bias'],
365                      ASPPFLAGS=['--pnacl-mips-bias'])
366   else:
367     raise Exception("Unknown architecture!")
368
369
370 def ValidateSdk(env):
371   checkables = ['${NACL_SDK_INCLUDE}/stdio.h']
372   for c in checkables:
373     if os.path.exists(env.subst(c)):
374       continue
375     # Windows build does not use cygwin and so can not see nacl subdirectory
376     # if it's cygwin's symlink - check for /include instead...
377     if os.path.exists(re.sub(r'(nacl64|nacl)/include/([^/]*)$',
378                              r'include/\2',
379                              env.subst(c))):
380       continue
381     # TODO(pasko): remove the legacy header presence test below.
382     if os.path.exists(re.sub(r'nacl/include/([^/]*)$',
383                              r'nacl64/include/\1',
384                              env.subst(c))):
385       continue
386     message = env.subst('''
387 ERROR: NativeClient toolchain does not seem present!,
388        Missing: %s
389
390 Configuration is:
391   NACL_SDK_INCLUDE=${NACL_SDK_INCLUDE}
392   NACL_SDK_LIB=${NACL_SDK_LIB}
393   CC=${CC}
394   CXX=${CXX}
395   AR=${AR}
396   AS=${AS}
397   ASPP=${ASPP}
398   LINK=${LINK}
399   RANLIB=${RANLIB}
400
401 Run: gclient runhooks --force or build the SDK yourself.
402 ''' % c)
403     sys.stderr.write(message + "\n\n")
404     sys.exit(-1)
405
406
407 def ScanLinkerScript(node, env, libpath):
408   """SCons scanner for linker script files.
409 This handles trivial linker scripts like those used for libc.so and libppapi.a.
410 These scripts just indicate more input files to be linked in, so we want
411 to produce dependencies on them.
412
413 A typical such linker script looks like:
414
415         /* Some comments.  */
416         INPUT ( foo.a libbar.a libbaz.a )
417
418 or:
419
420         /* GNU ld script
421            Use the shared library, but some functions are only in
422            the static library, so try that secondarily.  */
423         OUTPUT_FORMAT(elf64-x86-64)
424         GROUP ( /lib/libc.so.6 /usr/lib/libc_nonshared.a
425                 AS_NEEDED ( /lib/ld-linux-x86-64.so.2 ) )
426 """
427   contents = node.get_text_contents()
428   if contents.startswith('!<arch>\n') or contents.startswith('\177ELF'):
429     # An archive or ELF file is not a linker script.
430     return []
431
432   comment_pattern = re.compile(r'/\*.*?\*/', re.DOTALL | re.MULTILINE)
433   def remove_comments(text):
434     return re.sub(comment_pattern, '', text)
435
436   tokens = remove_comments(contents).split()
437   libs = []
438   while tokens:
439     token = tokens.pop()
440     if token.startswith('OUTPUT_FORMAT('):
441       pass
442     elif token == 'OUTPUT_FORMAT':
443       # Swallow the next three tokens: '(', 'xyz', ')'
444       del tokens[0:2]
445     elif token in ['(', ')', 'INPUT', 'GROUP', 'AS_NEEDED']:
446       pass
447     else:
448       libs.append(token)
449
450   # Find those items in the library path, ignoring ones we fail to find.
451   found = [SCons.Node.FS.find_file(lib, libpath) for lib in libs]
452   return [lib for lib in found if lib is not None]
453
454 # This is a modified copy of the class TempFileMunge in
455 # third_party/scons-2.0.1/engine/SCons/Platform/__init__.py.
456 # It differs in using quote_for_at_file (below) in place of
457 # SCons.Subst.quote_spaces.
458 class NaClTempFileMunge(object):
459   """A callable class.  You can set an Environment variable to this,
460   then call it with a string argument, then it will perform temporary
461   file substitution on it.  This is used to circumvent the long command
462   line limitation.
463
464   Example usage:
465   env["TEMPFILE"] = TempFileMunge
466   env["LINKCOM"] = "${TEMPFILE('$LINK $TARGET $SOURCES')}"
467
468   By default, the name of the temporary file used begins with a
469   prefix of '@'.  This may be configred for other tool chains by
470   setting '$TEMPFILEPREFIX'.
471
472   env["TEMPFILEPREFIX"] = '-@'        # diab compiler
473   env["TEMPFILEPREFIX"] = '-via'      # arm tool chain
474   """
475   def __init__(self, cmd):
476     self.cmd = cmd
477
478   def __call__(self, target, source, env, for_signature):
479     if for_signature:
480       # If we're being called for signature calculation, it's
481       # because we're being called by the string expansion in
482       # Subst.py, which has the logic to strip any $( $) that
483       # may be in the command line we squirreled away.  So we
484       # just return the raw command line and let the upper
485       # string substitution layers do their thing.
486       return self.cmd
487
488     # Now we're actually being called because someone is actually
489     # going to try to execute the command, so we have to do our
490     # own expansion.
491     cmd = env.subst_list(self.cmd, SCons.Subst.SUBST_CMD, target, source)[0]
492     try:
493       maxline = int(env.subst('$MAXLINELENGTH'))
494     except ValueError:
495       maxline = 2048
496
497     length = 0
498     for c in cmd:
499       length += len(c)
500     if length <= maxline:
501       return self.cmd
502
503     # We do a normpath because mktemp() has what appears to be
504     # a bug in Windows that will use a forward slash as a path
505     # delimiter.  Windows's link mistakes that for a command line
506     # switch and barfs.
507     #
508     # We use the .lnk suffix for the benefit of the Phar Lap
509     # linkloc linker, which likes to append an .lnk suffix if
510     # none is given.
511     (fd, tmp) = tempfile.mkstemp('.lnk', text=True)
512     native_tmp = SCons.Util.get_native_path(os.path.normpath(tmp))
513
514     if env['SHELL'] and env['SHELL'] == 'sh':
515       # The sh shell will try to escape the backslashes in the
516       # path, so unescape them.
517       native_tmp = native_tmp.replace('\\', r'\\\\')
518       # In Cygwin, we want to use rm to delete the temporary
519       # file, because del does not exist in the sh shell.
520       rm = env.Detect('rm') or 'del'
521     else:
522       # Don't use 'rm' if the shell is not sh, because rm won't
523       # work with the Windows shells (cmd.exe or command.com) or
524       # Windows path names.
525       rm = 'del'
526
527     prefix = env.subst('$TEMPFILEPREFIX')
528     if not prefix:
529       prefix = '@'
530
531     # The @file is sometimes handled by a GNU tool itself, using
532     # the libiberty/argv.c code, and sometimes handled implicitly
533     # by Cygwin before the tool's own main even sees it.  These
534     # two treat the contents differently, so there is no single
535     # perfect way to quote.  The libiberty @file code uses a very
536     # regular scheme: a \ in any context is always swallowed and
537     # quotes the next character, whatever it is; '...' or "..."
538     # quote whitespace in ... and the outer quotes are swallowed.
539     # The Cygwin @file code uses a vaguely similar scheme, but its
540     # treatment of \ is much less consistent: a \ outside a quoted
541     # string is never stripped, and a \ inside a quoted string is
542     # only stripped when it quoted something (Cygwin's definition
543     # of "something" here is nontrivial).  In our uses the only
544     # appearances of \ we expect are in Windows-style file names.
545     # Fortunately, an extra doubling of \\ that doesn't get
546     # stripped is harmless in the middle of a file name.
547     def quote_for_at_file(s):
548       s = str(s)
549       if ' ' in s or '\t' in s:
550         return '"' + re.sub('([ \t"])', r'\\\1', s) + '"'
551       return s.replace('\\', '\\\\')
552
553     args = list(map(quote_for_at_file, cmd[1:]))
554     os.write(fd, " ".join(args) + "\n")
555     os.close(fd)
556     # XXX Using the SCons.Action.print_actions value directly
557     # like this is bogus, but expedient.  This class should
558     # really be rewritten as an Action that defines the
559     # __call__() and strfunction() methods and lets the
560     # normal action-execution logic handle whether or not to
561     # print/execute the action.  The problem, though, is all
562     # of that is decided before we execute this method as
563     # part of expanding the $TEMPFILE construction variable.
564     # Consequently, refactoring this will have to wait until
565     # we get more flexible with allowing Actions to exist
566     # independently and get strung together arbitrarily like
567     # Ant tasks.  In the meantime, it's going to be more
568     # user-friendly to not let obsession with architectural
569     # purity get in the way of just being helpful, so we'll
570     # reach into SCons.Action directly.
571     if SCons.Action.print_actions:
572       print("Using tempfile "+native_tmp+" for command line:\n"+
573             str(cmd[0]) + " " + " ".join(args))
574     return [ cmd[0], prefix + native_tmp + '\n' + rm, native_tmp ]
575
576 def generate(env):
577   """SCons entry point for this tool.
578
579   Args:
580     env: The SCons environment in question.
581
582   NOTE: SCons requires the use of this name, which fails lint.
583   """
584
585   # make these methods to the top level scons file
586   env.AddMethod(ValidateSdk)
587   env.AddMethod(AddBiasForPNaCl)
588   env.AddMethod(PNaClForceNative)
589   env.AddMethod(PNaClGetNNaClEnv)
590
591   # Invoke the various unix tools that the NativeClient SDK resembles.
592   env.Tool('g++')
593   env.Tool('gcc')
594   env.Tool('gnulink')
595   env.Tool('ar')
596   env.Tool('as')
597
598   if env.Bit('pnacl_generate_pexe'):
599     suffix = '.nonfinal.pexe'
600   else:
601     suffix = '.nexe'
602
603   env.Replace(
604       COMPONENT_LINKFLAGS=[''],
605       COMPONENT_LIBRARY_LINK_SUFFIXES=['.pso', '.so', '.a'],
606       _RPATH='',
607       COMPONENT_LIBRARY_DEBUG_SUFFIXES=[],
608       PROGSUFFIX=suffix,
609       # adding BASE_ AND EXTRA_ flags to common command lines
610       # The suggested usage pattern is:
611       # BASE_XXXFLAGS can only be set in this file
612       # EXTRA_XXXFLAGS can only be set in a ComponentXXX call
613       # NOTE: we also have EXTRA_LIBS which is handles separately in
614       # site_scons/site_tools/component_builders.py
615       # NOTE: the command lines were gleaned from:
616       # * ../third_party/scons-2.0.1/engine/SCons/Tool/cc.py
617       # * ../third_party/scons-2.0.1/engine/SCons/Tool/c++.py
618       # * etc.
619       CCCOM='$CC $BASE_CFLAGS $CFLAGS $EXTRA_CFLAGS ' +
620             '$CCFLAGS $_CCCOMCOM -c -o $TARGET $SOURCES',
621       SHCCCOM='$SHCC $BASE_CFLAGS $SHCFLAGS $EXTRA_CFLAGS ' +
622               '$SHCCFLAGS $_CCCOMCOM -c -o $TARGET $SOURCES',
623
624       CXXCOM='$CXX $BASE_CXXFLAGS $CXXFLAGS $EXTRA_CXXFLAGS ' +
625              '$CCFLAGS $_CCCOMCOM -c -o $TARGET $SOURCES',
626       SHCXXCOM='$SHCXX $BASE_CXXFLAGS $SHCXXFLAGS $EXTRA_CXXFLAGS ' +
627                '$SHCCFLAGS $_CCCOMCOM -c -o $TARGET $SOURCES',
628
629       LINKCOM='$LINK $BASE_LINKFLAGS $LINKFLAGS $EXTRA_LINKFLAGS ' +
630               '$SOURCES $_LIBDIRFLAGS $_LIBFLAGS -o $TARGET',
631       SHLINKCOM='$SHLINK $BASE_LINKFLAGS $SHLINKFLAGS $EXTRA_LINKFLAGS ' +
632                 '$SOURCES $_LIBDIRFLAGS $_LIBFLAGS -o $TARGET',
633
634       ASCOM='$AS $BASE_ASFLAGS $ASFLAGS $EXTRA_ASFLAGS -o $TARGET $SOURCES',
635       ASPPCOM='$ASPP $BASE_ASPPFLAGS $ASPPFLAGS $EXTRA_ASPPFLAGS ' +
636               '$CPPFLAGS $_CPPDEFFLAGS $_CPPINCFLAGS -c -o $TARGET $SOURCES',
637
638       # Strip doesn't seem to be a first-class citizen in SCons country,
639       # so we have to add these *COM, *COMSTR manually.
640       # Note: it appears we cannot add this in component_setup.py
641       STRIPFLAGS=['--strip-all'],
642       STRIPCOM='${STRIP} ${STRIPFLAGS}',
643       TRANSLATECOM='${TRANSLATE} ${TRANSLATEFLAGS} ${SOURCES} -o ${TARGET}',
644       PNACLFINALIZEFLAGS=[],
645       PNACLFINALIZECOM='${PNACLFINALIZE} ${PNACLFINALIZEFLAGS} ' +
646                        '${SOURCES} -o ${TARGET}',
647   )
648
649   # Windows has a small limit on the command line size.  The linking and AR
650   # commands can get quite large.  So bring in the SCons machinery to put
651   # most of a command line into a temporary file and pass it with
652   # @filename, which works with gcc.
653   if env['PLATFORM'] in ['win32', 'cygwin']:
654     env['TEMPFILE'] = NaClTempFileMunge
655     for com in ['LINKCOM', 'SHLINKCOM', 'ARCOM']:
656       env[com] = "${TEMPFILE('%s')}" % env[com]
657
658   # Get root of the SDK.
659   root = env.GetToolchainDir()
660
661   # if bitcode=1 use pnacl toolchain
662   if env.Bit('bitcode'):
663     _SetEnvForPnacl(env, root)
664
665     # Get GDB from the nacl-gcc toolchain even when using PNaCl.
666     # TODO(mseaborn): We really want the nacl-gdb binary to be in a
667     # separate tarball from the nacl-gcc toolchain, then this step
668     # will not be necessary.
669     # See http://code.google.com/p/nativeclient/issues/detail?id=2773
670     if env.Bit('target_x86'):
671       temp_env = env.Clone()
672       temp_env.ClearBits('bitcode')
673       temp_root = temp_env.GetToolchainDir()
674       _SetEnvForNativeSdk(temp_env, temp_root)
675       env.Replace(GDB=temp_env['GDB'])
676   elif env.Bit('built_elsewhere'):
677     _StubOutEnvToolsForBuiltElsewhere(env)
678   else:
679     _SetEnvForNativeSdk(env, root)
680
681   env.Prepend(LIBPATH='${NACL_SDK_LIB}')
682
683   # Install our scanner for (potential) linker scripts.
684   # It applies to "source" files ending in .a or .so.
685   # Dependency files it produces are to be found in ${LIBPATH}.
686   # It is applied recursively to those dependencies in case
687   # some of them are linker scripts too.
688   ldscript_scanner = SCons.Scanner.Base(
689       function=ScanLinkerScript,
690       skeys=['.a', '.so', '.pso'],
691       path_function=SCons.Scanner.FindPathDirs('LIBPATH'),
692       recursive=True
693       )
694   env.Append(SCANNERS=ldscript_scanner)
695
696   # Scons tests can check this version number to decide whether to
697   # enable tests for toolchain bug fixes or new features.  See
698   # description in pnacl/build.sh.
699   if 'toolchain_feature_version' in SCons.Script.ARGUMENTS:
700     version = int(SCons.Script.ARGUMENTS['toolchain_feature_version'])
701   else:
702     version_file = os.path.join(root, 'FEATURE_VERSION')
703     # There is no pnacl_newlib toolchain on ARM, only a pnacl_translator, so
704     # use that if necessary. Otherwise use it if we are doing sandboxed
705     # translation.
706     if not os.path.exists(version_file) or env.Bit('use_sandboxed_translator'):
707       version_file = os.path.join(os.path.dirname(root), 'pnacl_translator',
708                                   'FEATURE_VERSION')
709     if os.path.exists(version_file):
710       with open(version_file, 'r') as fh:
711         version = int(fh.read())
712     else:
713       version = 0
714   env.Replace(TOOLCHAIN_FEATURE_VERSION=version)