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