Home | History | Annotate | Download | only in nacl_test_injection
      1 #!/usr/bin/python
      2 # Copyright (c) 2012 The Chromium 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 """Do all the steps required to build and test against nacl."""
      7 
      8 
      9 import optparse
     10 import os.path
     11 import re
     12 import shutil
     13 import subprocess
     14 import sys
     15 
     16 import find_chrome
     17 
     18 
     19 # Copied from buildbot/buildbot_lib.py
     20 def TryToCleanContents(path, file_name_filter=lambda fn: True):
     21   """
     22   Remove the contents of a directory without touching the directory itself.
     23   Ignores all failures.
     24   """
     25   if os.path.exists(path):
     26     for fn in os.listdir(path):
     27       TryToCleanPath(os.path.join(path, fn), file_name_filter)
     28 
     29 
     30 # Copied from buildbot/buildbot_lib.py
     31 def TryToCleanPath(path, file_name_filter=lambda fn: True):
     32   """
     33   Removes a file or directory.
     34   Ignores all failures.
     35   """
     36   if os.path.exists(path):
     37     if file_name_filter(path):
     38       print 'Trying to remove %s' % path
     39       if os.path.isdir(path):
     40         shutil.rmtree(path, ignore_errors=True)
     41       else:
     42         try:
     43           os.remove(path)
     44         except Exception:
     45           pass
     46     else:
     47       print 'Skipping %s' % path
     48 
     49 
     50 # TODO(ncbray): this is somewhat unsafe.  We should fix the underlying problem.
     51 def CleanTempDir():
     52   # Only delete files and directories like:
     53   # a) C:\temp\83C4.tmp
     54   # b) /tmp/.org.chromium.Chromium.EQrEzl
     55   file_name_re = re.compile(
     56       r'[\\/]([0-9a-fA-F]+\.tmp|\.org\.chrom\w+\.Chrom\w+\..+)$')
     57   file_name_filter = lambda fn: file_name_re.search(fn) is not None
     58 
     59   path = os.environ.get('TMP', os.environ.get('TEMP', '/tmp'))
     60   if len(path) >= 4 and os.path.isdir(path):
     61     print
     62     print "Cleaning out the temp directory."
     63     print
     64     TryToCleanContents(path, file_name_filter)
     65   else:
     66     print
     67     print "Cannot find temp directory, not cleaning it."
     68     print
     69 
     70 
     71 def RunCommand(cmd, cwd, env):
     72   sys.stdout.write('\nRunning %s\n\n' % ' '.join(cmd))
     73   sys.stdout.flush()
     74   retcode = subprocess.call(cmd, cwd=cwd, env=env)
     75   if retcode != 0:
     76     sys.stdout.write('\nFailed: %s\n\n' % ' '.join(cmd))
     77     sys.exit(retcode)
     78 
     79 
     80 def RunTests(name, cmd, nacl_dir, env):
     81   sys.stdout.write('\n\nBuilding files needed for %s testing...\n\n' % name)
     82   RunCommand(cmd + ['do_not_run_tests=1', '-j8'], nacl_dir, env)
     83   sys.stdout.write('\n\nRunning %s tests...\n\n' % name)
     84   RunCommand(cmd, nacl_dir, env)
     85 
     86 
     87 def BuildAndTest(options):
     88   # Refuse to run under cygwin.
     89   if sys.platform == 'cygwin':
     90     raise Exception('I do not work under cygwin, sorry.')
     91 
     92   # By default, use the version of Python is being used to run this script.
     93   python = sys.executable
     94   if sys.platform == 'darwin':
     95     # Mac 10.5 bots tend to use a particularlly old version of Python, look for
     96     # a newer version.
     97     macpython27 = '/Library/Frameworks/Python.framework/Versions/2.7/bin/python'
     98     if os.path.exists(macpython27):
     99       python = macpython27
    100 
    101   script_dir = os.path.dirname(os.path.abspath(__file__))
    102   src_dir = os.path.dirname(os.path.dirname(os.path.dirname(script_dir)))
    103   nacl_dir = os.path.join(src_dir, 'native_client')
    104 
    105   # Decide platform specifics.
    106   env = dict(os.environ)
    107   if sys.platform in ['win32', 'cygwin']:
    108     if options.bits == 64:
    109       bits = 64
    110     elif options.bits == 32:
    111       bits = 32
    112     elif '64' in os.environ.get('PROCESSOR_ARCHITECTURE', '') or \
    113          '64' in os.environ.get('PROCESSOR_ARCHITEW6432', ''):
    114       bits = 64
    115     else:
    116       bits = 32
    117     msvs_path = ';'.join([
    118         r'c:\Program Files\Microsoft Visual Studio 9.0\VC',
    119         r'c:\Program Files (x86)\Microsoft Visual Studio 9.0\VC',
    120         r'c:\Program Files\Microsoft Visual Studio 9.0\Common7\Tools',
    121         r'c:\Program Files (x86)\Microsoft Visual Studio 9.0\Common7\Tools',
    122         r'c:\Program Files\Microsoft Visual Studio 8\VC',
    123         r'c:\Program Files (x86)\Microsoft Visual Studio 8\VC',
    124         r'c:\Program Files\Microsoft Visual Studio 8\Common7\Tools',
    125         r'c:\Program Files (x86)\Microsoft Visual Studio 8\Common7\Tools',
    126     ])
    127     env['PATH'] += ';' + msvs_path
    128     scons = [python, 'scons.py']
    129   elif sys.platform == 'darwin':
    130     bits = 32
    131     scons = [python, 'scons.py']
    132   else:
    133     p = subprocess.Popen(
    134         'uname -m | '
    135         'sed -e "s/i.86/ia32/;s/x86_64/x64/;s/amd64/x64/;s/arm.*/arm/"',
    136         shell=True, stdout=subprocess.PIPE)
    137     (p_stdout, _) = p.communicate()
    138     assert p.returncode == 0
    139     if options.bits == 64:
    140       bits = 64
    141     elif options.bits == 32:
    142       bits = 32
    143     elif p_stdout.find('64') >= 0:
    144       bits = 64
    145     else:
    146       bits = 32
    147     # xvfb-run has a 2-second overhead per invocation, so it is cheaper to wrap
    148     # the entire build step rather than each test (browser_headless=1).
    149     scons = ['xvfb-run', '--auto-servernum', python, 'scons.py']
    150 
    151   if options.browser_path:
    152     chrome_filename = options.browser_path
    153   else:
    154     chrome_filename = find_chrome.FindChrome(src_dir, [options.mode])
    155     if chrome_filename is None:
    156       raise Exception('Cannot find a chome binary - specify one with '
    157                       '--browser_path?')
    158 
    159   if options.jobs > 1:
    160     scons.append('-j%d' % options.jobs)
    161 
    162   scons.append('disable_tests=%s' % options.disable_tests)
    163 
    164   if options.buildbot is not None:
    165     scons.append('buildbot=%s' % (options.buildbot,))
    166 
    167   # Clean the output of the previous build.
    168   # Incremental builds can get wedged in weird ways, so we're trading speed
    169   # for reliability.
    170   shutil.rmtree(os.path.join(nacl_dir, 'scons-out'), True)
    171 
    172   # check that the HOST (not target) is 64bit
    173   # this is emulating what msvs_env.bat is doing
    174   if '64' in os.environ.get('PROCESSOR_ARCHITECTURE', '') or \
    175      '64' in os.environ.get('PROCESSOR_ARCHITEW6432', ''):
    176     # 64bit HOST
    177     env['VS90COMNTOOLS'] = ('c:\\Program Files (x86)\\'
    178                             'Microsoft Visual Studio 9.0\\Common7\\Tools\\')
    179     env['VS80COMNTOOLS'] = ('c:\\Program Files (x86)\\'
    180                             'Microsoft Visual Studio 8.0\\Common7\\Tools\\')
    181   else:
    182     # 32bit HOST
    183     env['VS90COMNTOOLS'] = ('c:\\Program Files\\Microsoft Visual Studio 9.0\\'
    184                             'Common7\\Tools\\')
    185     env['VS80COMNTOOLS'] = ('c:\\Program Files\\Microsoft Visual Studio 8.0\\'
    186                             'Common7\\Tools\\')
    187 
    188   # Run nacl/chrome integration tests.
    189   # Note that we have to add nacl_irt_test to --mode in order to get
    190   # inbrowser_test_runner to run.
    191   # TODO(mseaborn): Change it so that inbrowser_test_runner is not a
    192   # special case.
    193   cmd = scons + ['--verbose', '-k', 'platform=x86-%d' % bits,
    194       '--mode=opt-host,nacl,nacl_irt_test',
    195       'chrome_browser_path=%s' % chrome_filename,
    196   ]
    197   if not options.integration_bot and not options.morenacl_bot:
    198     cmd.append('disable_flaky_tests=1')
    199   cmd.append('chrome_browser_tests')
    200 
    201   # Download the toolchain(s).
    202   RunCommand([python,
    203               os.path.join(nacl_dir, 'build', 'download_toolchains.py'),
    204               '--no-arm-trusted', '--no-pnacl', 'TOOL_REVISIONS'],
    205              nacl_dir, os.environ)
    206 
    207   CleanTempDir()
    208 
    209   if options.enable_newlib:
    210     RunTests('nacl-newlib', cmd, nacl_dir, env)
    211 
    212   if options.enable_glibc:
    213     RunTests('nacl-glibc', cmd + ['--nacl_glibc'], nacl_dir, env)
    214 
    215 
    216 def MakeCommandLineParser():
    217   parser = optparse.OptionParser()
    218   parser.add_option('-m', '--mode', dest='mode', default='Debug',
    219                     help='Debug/Release mode')
    220   parser.add_option('-j', dest='jobs', default=1, type='int',
    221                     help='Number of parallel jobs')
    222 
    223   parser.add_option('--enable_newlib', dest='enable_newlib', default=-1,
    224                     type='int', help='Run newlib tests?')
    225   parser.add_option('--enable_glibc', dest='enable_glibc', default=-1,
    226                     type='int', help='Run glibc tests?')
    227 
    228   # Deprecated, but passed to us by a script in the Chrome repo.
    229   # Replaced by --enable_glibc=0
    230   parser.add_option('--disable_glibc', dest='disable_glibc',
    231                     action='store_true', default=False,
    232                     help='Do not test using glibc.')
    233 
    234   parser.add_option('--disable_tests', dest='disable_tests',
    235                     type='string', default='',
    236                     help='Comma-separated list of tests to omit')
    237   builder_name = os.environ.get('BUILDBOT_BUILDERNAME', '')
    238   is_integration_bot = 'nacl-chrome' in builder_name
    239   parser.add_option('--integration_bot', dest='integration_bot',
    240                     type='int', default=int(is_integration_bot),
    241                     help='Is this an integration bot?')
    242   is_morenacl_bot = (
    243       'More NaCl' in builder_name or
    244       'naclmore' in builder_name)
    245   parser.add_option('--morenacl_bot', dest='morenacl_bot',
    246                     type='int', default=int(is_morenacl_bot),
    247                     help='Is this a morenacl bot?')
    248 
    249   # Not used on the bots, but handy for running the script manually.
    250   parser.add_option('--bits', dest='bits', action='store',
    251                     type='int', default=None,
    252                     help='32/64')
    253   parser.add_option('--browser_path', dest='browser_path', action='store',
    254                     type='string', default=None,
    255                     help='Path to the chrome browser.')
    256   parser.add_option('--buildbot', dest='buildbot', action='store',
    257                     type='string', default=None,
    258                     help='Value passed to scons as buildbot= option.')
    259   return parser
    260 
    261 
    262 def Main():
    263   parser = MakeCommandLineParser()
    264   options, args = parser.parse_args()
    265   if options.integration_bot and options.morenacl_bot:
    266     parser.error('ERROR: cannot be both an integration bot and a morenacl bot')
    267 
    268   # Set defaults for enabling newlib.
    269   if options.enable_newlib == -1:
    270     options.enable_newlib = 1
    271 
    272   # Set defaults for enabling glibc.
    273   if options.enable_glibc == -1:
    274     if options.integration_bot or options.morenacl_bot:
    275       options.enable_glibc = 1
    276     else:
    277       options.enable_glibc = 0
    278 
    279   if args:
    280     parser.error('ERROR: invalid argument')
    281   BuildAndTest(options)
    282 
    283 
    284 if __name__ == '__main__':
    285   Main()
    286