2 # Copyright (c) 2012 The Native Client Authors. All rights reserved.
3 # Use of this source code is governed by a BSD-style license that can be
4 # found in the LICENSE file.
6 """NaCl SDK tool SCons."""
19 # Mapping from env['PLATFORM'] (scons) to a single host platform from the point
21 NACL_CANONICAL_PLATFORM_MAP = {
33 'tooldir': 'arm-nacl',
41 'tooldir': 'i686-nacl',
42 'other_libdir': 'lib32',
45 'ld_flag': ' -melf_i386_nacl',
48 'tooldir': 'x86_64-nacl',
49 'other_libdir': 'lib64',
52 'ld_flag': ' -melf_x86_64_nacl',
57 def _StubOutEnvToolsForBuiltElsewhere(env):
58 """Stub out all tools so that they point to 'true'.
60 Some machines have their code built by another machine, they'll therefore
61 run 'true' instead of running the usual build tools.
64 env: The SCons environment in question.
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',
71 def _PlatformSubdirs(env):
72 if env.Bit('bitcode'):
73 os = NACL_CANONICAL_PLATFORM_MAP[env['PLATFORM']]
74 name = 'pnacl_%s_x86' % os
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'
85 def _GetNaClSdkRoot(env, sdk_mode, psdk_mode):
86 """Return the path to the sdk.
89 env: The SCons environment in question.
90 sdk_mode: A string indicating which location to select the tools from.
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:'):])
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.
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', '')
111 raise NotImplementedError(
112 'Not able to decide where /usr/local/nacl-sdk is on this platform,'
113 'use naclsdk_mode=custom:...')
116 return '/usr/local/nacl-sdk'
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:'):])
124 elif sdk_mode == 'manual':
128 raise Exception('Unknown sdk mode: %r' % sdk_mode)
131 def _SetEnvForNativeSdk(env, sdk_path):
132 """Initialize environment according to target architecture."""
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)
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')
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'))
168 if tool_prefix is None:
169 raise Exception("Cannot find a toolchain for %s in %s" %
170 (env['TARGET_FULLARCH'], sdk_path))
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'],
204 '-Wno-variadic-macros',
207 '-fno-stack-protector',
208 '-fdiagnostics-show-option',
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',
222 SHLIBPREFIX='$LIBPREFIX',
224 LIBPREFIXES=['$LIBPREFIX'],
225 LIBSUFFIXES=['$LIBSUFFIX', '$SHLIBSUFFIX'],
227 # Force -fPIC when compiling for shared libraries.
228 env.AppendUnique(SHCCFLAGS=['-fPIC'],
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)
238 arch = env['TARGET_FULLARCH']
239 assert arch in ['arm', 'arm-thumb2', 'mips32', 'x86-32', 'x86-64']
241 if env.Bit('pnacl_unsandboxed'):
242 arch = 'linux-%s' % arch
243 arch_flag = ' -arch %s' % arch
244 if env.Bit('pnacl_generate_pexe'):
247 ld_arch_flag = arch_flag
249 if env.Bit('nacl_glibc'):
250 subroot = root + '/glibc'
254 translator_root = os.path.join(os.path.dirname(root), 'pnacl_translator')
256 binprefix = os.path.join(subroot, 'bin', 'pnacl-')
258 if env.Bit('host_windows'):
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')
268 pnacl_lib = os.path.join(subroot, 'lib')
271 #TODO(robertm): remove NACL_SDK_INCLUDE ASAP
272 if env.Bit('nacl_glibc'):
273 pnacl_include = os.path.join(root, 'glibc', 'usr', 'include')
275 pnacl_include = os.path.join(root, 'usr', 'include')
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)
286 pnacl_translate = binprefix + 'translate' + binext
288 pnacl_cc = binprefix + 'clang' + binext
289 pnacl_cxx = binprefix + 'clang++' + binext
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
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.
302 pnacl_cc_flags = ' -std=gnu99'
303 pnacl_ld_flags = ' ' + ' '.join(env['PNACL_BCLDFLAGS'])
304 pnacl_translate_flags = ''
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'
314 if env.Bit('use_sandboxed_translator'):
315 sb_flags = ' --pnacl-sb'
316 pnacl_ld_flags += sb_flags
317 pnacl_translate_flags += sb_flags
319 if env.Bit('x86_64_zero_based_sandbox'):
320 pnacl_translate_flags += ' -sfi-zero-based-sandbox'
323 env.Prepend(LIBPATH=pnacl_extra_lib)
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)
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,
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,
349 NATIVELD=pnacl_nativeld,
351 AS=pnacl_as + ld_arch_flag,
354 OBJDUMP=pnacl_disass,
356 TRANSLATE=pnacl_translate + arch_flag + pnacl_translate_flags,
357 PNACLFINALIZE=pnacl_finalize,
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')
370 def _SetEnvForSdkManually(env):
371 def GetEnvOrDummy(v):
372 return os.getenv('NACL_SDK_' + v, 'MISSING_SDK_' + v)
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'),
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')
393 env.Replace(OBJSUFFIX='.o',
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)
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'))
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)
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')
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'])
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')
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'])
457 raise Exception("Unknown architecture!")
460 def ValidateSdk(env):
461 checkables = ['${NACL_SDK_INCLUDE}/stdio.h']
463 if os.path.exists(env.subst(c)):
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/([^/]*)$',
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',
476 message = env.subst('''
477 ERROR: NativeClient toolchain does not seem present!,
481 NACL_SDK_INCLUDE=${NACL_SDK_INCLUDE}
482 NACL_SDK_LIB=${NACL_SDK_LIB}
491 Run: gclient runhooks --force or build the SDK yourself.
493 sys.stderr.write(message + "\n\n")
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.
503 A typical such linker script looks like:
506 INPUT ( foo.a libbar.a libbaz.a )
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 ) )
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.
522 comment_pattern = re.compile(r'/\*.*?\*/', re.DOTALL | re.MULTILINE)
523 def remove_comments(text):
524 return re.sub(comment_pattern, '', text)
526 tokens = remove_comments(contents).split()
530 if token.startswith('OUTPUT_FORMAT('):
532 elif token == 'OUTPUT_FORMAT':
533 # Swallow the next three tokens: '(', 'xyz', ')'
535 elif token in ['(', ')', 'INPUT', 'GROUP', 'AS_NEEDED']:
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]
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
555 env["TEMPFILE"] = TempFileMunge
556 env["LINKCOM"] = "${TEMPFILE('$LINK $TARGET $SOURCES')}"
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'.
562 env["TEMPFILEPREFIX"] = '-@' # diab compiler
563 env["TEMPFILEPREFIX"] = '-via' # arm tool chain
565 def __init__(self, cmd):
568 def __call__(self, target, source, env, 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.
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
581 cmd = env.subst_list(self.cmd, SCons.Subst.SUBST_CMD, target, source)[0]
583 maxline = int(env.subst('$MAXLINELENGTH'))
590 if length <= maxline:
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
598 # We use the .lnk suffix for the benefit of the Phar Lap
599 # linkloc linker, which likes to append an .lnk suffix if
601 (fd, tmp) = tempfile.mkstemp('.lnk', text=True)
602 native_tmp = SCons.Util.get_native_path(os.path.normpath(tmp))
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'
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.
617 prefix = env.subst('$TEMPFILEPREFIX')
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):
639 if ' ' in s or '\t' in s:
640 return '"' + re.sub('([ \t"])', r'\\\1', s) + '"'
641 return s.replace('\\', '\\\\')
643 args = list(map(quote_for_at_file, cmd[1:]))
644 os.write(fd, " ".join(args) + "\n")
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 ]
667 """SCons entry point for this tool.
670 env: The SCons environment in question.
672 NOTE: SCons requires the use of this name, which fails lint.
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)
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
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:'):
689 'pnaclsdk_mode only supports "default" or "custom:path", not %s' %
692 # Invoke the various unix tools that the NativeClient SDK resembles.
699 if env.Bit('pnacl_generate_pexe'):
700 suffix = '.nonfinal.pexe'
705 COMPONENT_LINKFLAGS=[''],
706 COMPONENT_LIBRARY_LINK_SUFFIXES=['.pso', '.so', '.a'],
708 COMPONENT_LIBRARY_DEBUG_SUFFIXES=[],
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
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',
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',
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',
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',
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}',
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]
760 # Get root of the SDK.
761 root = _GetNaClSdkRoot(env, sdk_mode, psdk_mode)
763 # Determine where to get the SDK from.
764 if sdk_mode == 'manual':
765 _SetEnvForSdkManually(env)
767 # if bitcode=1 use pnacl toolchain
768 if env.Bit('bitcode'):
769 _SetEnvForPnacl(env, root)
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)
785 _SetEnvForNativeSdk(env, root)
787 env.Prepend(LIBPATH='${NACL_SDK_LIB}')
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'),
800 env.Append(SCANNERS=ldscript_scanner)
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'])
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())
814 env.Replace(TOOLCHAIN_FEATURE_VERSION=version)