# Copyright (c) 2012 The Native Client Authors. All rights reserved.
# Use of this source code is governed by a BSD-style license that can be
# found in the LICENSE file.
-#
-# IMPORTANT NOTE: If you make local mods to this file, you must run:
-# % pnacl/build.sh driver
-# in order for them to take effect in the scons build. This command
-# updates the copy in the toolchain/ tree.
-#
import platform
import os
+import random
import re
import shlex
import signal
import subprocess
-import struct
import sys
import tempfile
-import artools
import elftools
-import ldtools
# filetype needs to be imported here because pnacl-driver injects calls to
# filetype.ForceFileType into argument parse actions.
# TODO(dschuff): That's ugly. Find a better way.
from driver_env import env
# TODO: import driver_log and change these references from 'foo' to
# 'driver_log.foo', or split driver_log further
-from driver_log import Log, DriverOpen, DriverClose, StringifyCommand, TempFiles, DriverExit, FixArch
+from driver_log import Log, DriverOpen, DriverClose, StringifyCommand, DriverExit, FixArch
+from driver_temps import TempFiles
from shelltools import shell
def ParseError(s, leftpos, rightpos, msg):
Log.Fatal('machine/os ' + '-'.join(tokens[1:]) + ' not supported.')
-def RunDriver(invocation, args, suppress_inherited_arch_args=False):
+def GetOSName():
+ if sys.platform == 'darwin':
+ os_name = 'mac'
+ elif sys.platform.startswith('linux'):
+ os_name = 'linux'
+ elif sys.platform in ('cygwin', 'win32'):
+ os_name = 'win'
+ else:
+ Log.Fatal('Machine: %s not supported.' % sys.platform)
+
+ return os_name
+
+def GetArchNameShort():
+ machine = platform.machine().lower()
+ if machine.startswith('arm'):
+ return 'arm'
+ elif machine.startswith('mips'):
+ return 'mips'
+ elif (machine.startswith('x86')
+ or machine in ('amd32', 'i386', 'i686', 'ia32', '32', 'amd64', '64')):
+ return 'x86'
+
+ Log.Fatal('Architecture: %s not supported.' % machine)
+ return 'unknown'
+
+def RunDriver(module_name, args, suppress_inherited_arch_args=False):
"""
RunDriver() is used to invoke "driver" tools, e.g.
those prefixed with "pnacl-"
if isinstance(args, str):
args = shell.split(env.eval(args))
- module_name = 'pnacl-%s' % invocation
script = env.eval('${DRIVER_BIN}/%s' % module_name)
script = shell.unescape(script)
@env.register
@memoize
def FindBaseToolchain():
- """ Find toolchain/ directory """
- dir = FindBaseDir(lambda cur: pathtools.basename(cur) == 'toolchain')
- if dir is None:
+ """ Find toolchain/OS_ARCH directory """
+ base_dir = FindBaseDir(lambda cur: pathtools.basename(cur) == 'toolchain')
+ if base_dir is None:
Log.Fatal("Unable to find 'toolchain' directory")
- return shell.escape(dir)
+ toolchain_dir = os.path.join(
+ base_dir,
+ '%s_%s' % (GetOSName(), GetArchNameShort())
+ )
+ return shell.escape(toolchain_dir)
@env.register
@memoize
( '--pnacl-i686-bias', "env.set('BIAS', 'X8632')"),
( '--pnacl-x86_64-bias', "env.set('BIAS', 'X8664')"),
( '--pnacl-bias=(.+)', "env.set('BIAS', FixArch($0))"),
- ( '--pnacl-default-command-line', "env.set('USE_DEFAULT_CMD_LINE', '1')"),
( '-save-temps', "env.set('SAVE_TEMPS', '1')"),
( '-no-save-temps', "env.set('SAVE_TEMPS', '0')"),
( ('-B', '(.*)'), AddHostBinarySearchPath),
paths.reverse()
return paths
+
+def CheckPathLength(filename, exit_on_failure=True):
+ '''Check that the length of the path is short enough for Windows.
+
+ On Windows, MAX_PATH is ~260 and applies to absolute paths, and to relative
+ paths and the absolute paths they expand to (except for specific uses of
+ some APIs; see link below). Most applications don't bother to support long
+ paths properly (including LLVM, GNU binutils, and ninja). If a path is too
+ long, ERROR_PATH_NOT_FOUND is returned, which isn't very useful or clear for
+ users. In addition the Chrome build has deep directory hierarchies with long
+ names.
+ This function checks that the path is valid, so we can throw meaningful
+ errors.
+
+ http://msdn.microsoft.com/en-us/library/windows/desktop/aa365247(v=vs.85).aspx
+ '''
+ if not IsWindowsPython() and not env.has('PNACL_RUNNING_UNITTESTS'):
+ return True
+
+ # First check the name as-is (it's usually a relative path)
+ if len(filename) > 255:
+ if exit_on_failure:
+ Log.Fatal('Path name %s is too long (%d characters)' %
+ (filename, len(filename)))
+ return False
+ if os.path.isabs(filename):
+ return True
+
+ # Don't assume that the underlying tools or windows APIs will normalize
+ # the path before using it. Conservatively count the length of CWD + filename
+ appended_name = os.path.join(os.getcwd(), filename)
+ if len(appended_name) > 255:
+ if exit_on_failure:
+ Log.Fatal('Path name %s (expanded from %s) is too long (%d characters)' %
+ (appended_name, filename, len(appended_name)))
+ return False
+ return True
+
# Generate a unique identifier for each input file.
# Start with the basename, and if that is not unique enough,
# add parent directories. Rinse, repeat.
output = pathtools.abspath(output)
self.TempBase = output + '---linked'
+ self.OutputDir = pathtools.dirname(output)
# TODO(pdox): Figure out if there's a less confusing way
# to simplify the intermediate filename in this case.
self.TempMap = NewMap
return
+ def ValidatePathLength(self, temp, imtype):
+ # If the temp name is too long, just pick a random one instead.
+ if not CheckPathLength(temp, exit_on_failure=False):
+ # imtype is sometimes just an extension, and sometimes a compound
+ # extension (e.g. pre_opt.pexe). To keep name length shorter,
+ # only take the last extension
+ if '.' in imtype:
+ imtype = imtype[imtype.rfind('.') + 1:]
+ temp = pathtools.join(
+ self.OutputDir,
+ str(random.randrange(100000, 1000000)) + '.' + imtype)
+ CheckPathLength(temp)
+ return temp
+
def TempNameForOutput(self, imtype):
- temp = self.TempBase + '.' + imtype
- if not env.getbool('SAVE_TEMPS'):
- TempFiles.add(temp)
+ temp = self.ValidatePathLength(self.TempBase + '.' + imtype, imtype)
+ TempFiles.add(temp)
return temp
def TempNameForInput(self, input, imtype):
# Source file
temp = self.TempMap[fullpath] + '.' + imtype
- if not env.getbool('SAVE_TEMPS'):
- TempFiles.add(temp)
+ temp = self.ValidatePathLength(temp, imtype)
+ TempFiles.add(temp)
return temp
# (Invoked from loader.py)
def SetArch(arch):
- env.set('ARCH', FixArch(arch))
+ arch = FixArch(arch)
+ env.set('ARCH', arch)
+
+ nonsfi_nacl = False
+ if arch.endswith('_NONSFI'):
+ arch = arch[:-len('_NONSFI')]
+ nonsfi_nacl = True
+ env.set('BASE_ARCH', arch)
+ env.setbool('NONSFI_NACL', nonsfi_nacl)
+
def GetArch(required = False):
arch = env.getone('ARCH')
# If we're compiling for a single file, then we use
# TempNameForInput. If there are multiple files
# (e.g. linking), then we use TempNameForOutput.
- self.use_names_for_input = isinstance(input, str)
+ if isinstance(input, str):
+ self.use_names_for_input = True
+ CheckPathLength(input)
+ else:
+ self.use_names_for_input = False
+ for path in input:
+ CheckPathLength(path)
+ CheckPathLength(output)
def add(self, callback, output_type, **extra):
step = (callback, output_type, extra)