Home | History | Annotate | Download | only in build
      1 # Copyright 2014 The Chromium Authors. All rights reserved.
      2 # Use of this source code is governed by a BSD-style license that can be
      3 # found in the LICENSE file.
      4 
      5 import json
      6 import os
      7 import pipes
      8 import shutil
      9 import subprocess
     10 import sys
     11 
     12 
     13 script_dir = os.path.dirname(os.path.realpath(__file__))
     14 chrome_src = os.path.abspath(os.path.join(script_dir, os.pardir))
     15 SRC_DIR = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
     16 sys.path.insert(1, os.path.join(chrome_src, 'tools'))
     17 sys.path.insert(0, os.path.join(chrome_src, 'tools', 'gyp', 'pylib'))
     18 json_data_file = os.path.join(script_dir, 'win_toolchain.json')
     19 
     20 
     21 import gyp
     22 
     23 
     24 def SetEnvironmentAndGetRuntimeDllDirs():
     25   """Sets up os.environ to use the depot_tools VS toolchain with gyp, and
     26   returns the location of the VS runtime DLLs so they can be copied into
     27   the output directory after gyp generation.
     28   """
     29   vs2013_runtime_dll_dirs = None
     30   depot_tools_win_toolchain = \
     31       bool(int(os.environ.get('DEPOT_TOOLS_WIN_TOOLCHAIN', '1')))
     32   if sys.platform in ('win32', 'cygwin') and depot_tools_win_toolchain:
     33     with open(json_data_file, 'r') as tempf:
     34       toolchain_data = json.load(tempf)
     35 
     36     toolchain = toolchain_data['path']
     37     version = toolchain_data['version']
     38     version_is_pro = version[-1] != 'e'
     39     win8sdk = toolchain_data['win8sdk']
     40     wdk = toolchain_data['wdk']
     41     # TODO(scottmg): The order unfortunately matters in these. They should be
     42     # split into separate keys for x86 and x64. (See CopyVsRuntimeDlls call
     43     # below). http://crbug.com/345992
     44     vs2013_runtime_dll_dirs = toolchain_data['runtime_dirs']
     45 
     46     os.environ['GYP_MSVS_OVERRIDE_PATH'] = toolchain
     47     os.environ['GYP_MSVS_VERSION'] = version
     48     # We need to make sure windows_sdk_path is set to the automated
     49     # toolchain values in GYP_DEFINES, but don't want to override any
     50     # otheroptions.express
     51     # values there.
     52     gyp_defines_dict = gyp.NameValueListToDict(gyp.ShlexEnv('GYP_DEFINES'))
     53     gyp_defines_dict['windows_sdk_path'] = win8sdk
     54     os.environ['GYP_DEFINES'] = ' '.join('%s=%s' % (k, pipes.quote(str(v)))
     55         for k, v in gyp_defines_dict.iteritems())
     56     os.environ['WINDOWSSDKDIR'] = win8sdk
     57     os.environ['WDK_DIR'] = wdk
     58     # Include the VS runtime in the PATH in case it's not machine-installed.
     59     runtime_path = ';'.join(vs2013_runtime_dll_dirs)
     60     os.environ['PATH'] = runtime_path + ';' + os.environ['PATH']
     61   return vs2013_runtime_dll_dirs
     62 
     63 
     64 def CopyVsRuntimeDlls(output_dir, runtime_dirs):
     65   """Copies the VS runtime DLLs from the given |runtime_dirs| to the output
     66   directory so that even if not system-installed, built binaries are likely to
     67   be able to run.
     68 
     69   This needs to be run after gyp has been run so that the expected target
     70   output directories are already created.
     71   """
     72   assert sys.platform.startswith(('win32', 'cygwin'))
     73 
     74   def copy_runtime(target_dir, source_dir, dll_pattern):
     75     """Copy both the msvcr and msvcp runtime DLLs, only if the target doesn't
     76     exist, but the target directory does exist."""
     77     for which in ('p', 'r'):
     78       dll = dll_pattern % which
     79       target = os.path.join(target_dir, dll)
     80       source = os.path.join(source_dir, dll)
     81       # If gyp generated to that output dir, and the runtime isn't already
     82       # there, then copy it over.
     83       if (os.path.isdir(target_dir) and
     84           (not os.path.isfile(target) or
     85             os.stat(target).st_mtime != os.stat(source).st_mtime)):
     86         print 'Copying %s to %s...' % (source, target)
     87         if os.path.exists(target):
     88           os.unlink(target)
     89         shutil.copy2(source, target)
     90 
     91   x86, x64 = runtime_dirs
     92   out_debug = os.path.join(output_dir, 'Debug')
     93   out_debug_nacl64 = os.path.join(output_dir, 'Debug', 'x64')
     94   out_release = os.path.join(output_dir, 'Release')
     95   out_release_nacl64 = os.path.join(output_dir, 'Release', 'x64')
     96   out_debug_x64 = os.path.join(output_dir, 'Debug_x64')
     97   out_release_x64 = os.path.join(output_dir, 'Release_x64')
     98 
     99   if os.path.exists(out_debug) and not os.path.exists(out_debug_nacl64):
    100     os.makedirs(out_debug_nacl64)
    101   if os.path.exists(out_release) and not os.path.exists(out_release_nacl64):
    102     os.makedirs(out_release_nacl64)
    103   copy_runtime(out_debug,          x86, 'msvc%s120d.dll')
    104   copy_runtime(out_release,        x86, 'msvc%s120.dll')
    105   copy_runtime(out_debug_x64,      x64, 'msvc%s120d.dll')
    106   copy_runtime(out_release_x64,    x64, 'msvc%s120.dll')
    107   copy_runtime(out_debug_nacl64,   x64, 'msvc%s120d.dll')
    108   copy_runtime(out_release_nacl64, x64, 'msvc%s120.dll')
    109 
    110 
    111 def _GetDesiredVsToolchainHashes():
    112   """Load a list of SHA1s corresponding to the toolchains that we want installed
    113   to build with."""
    114   sha1path = os.path.join(script_dir, 'toolchain_vs2013.hash')
    115   with open(sha1path, 'rb') as f:
    116     return f.read().strip().splitlines()
    117 
    118 
    119 def Update():
    120   """Requests an update of the toolchain to the specific hashes we have at
    121   this revision. The update outputs a .json of the various configuration
    122   information required to pass to gyp which we use in |GetToolchainDir()|.
    123   """
    124   depot_tools_win_toolchain = \
    125       bool(int(os.environ.get('DEPOT_TOOLS_WIN_TOOLCHAIN', '1')))
    126   if sys.platform in ('win32', 'cygwin') and depot_tools_win_toolchain:
    127     import find_depot_tools
    128     depot_tools_path = find_depot_tools.add_depot_tools_to_path()
    129     json_data_file = os.path.join(script_dir, 'win_toolchain.json')
    130     get_toolchain_args = [
    131         sys.executable,
    132         os.path.join(depot_tools_path,
    133                     'win_toolchain',
    134                     'get_toolchain_if_necessary.py'),
    135         '--output-json', json_data_file,
    136       ] + _GetDesiredVsToolchainHashes()
    137     subprocess.check_call(get_toolchain_args)
    138 
    139   return 0
    140 
    141 
    142 def GetToolchainDir():
    143   """Gets location information about the current toolchain (must have been
    144   previously updated by 'update'). This is used for the GN build."""
    145   SetEnvironmentAndGetRuntimeDllDirs()
    146   print '''vs_path = "%s"
    147 sdk_path = "%s"
    148 vs_version = "%s"
    149 wdk_dir = "%s"
    150 ''' % (
    151       os.environ['GYP_MSVS_OVERRIDE_PATH'],
    152       os.environ['WINDOWSSDKDIR'],
    153       os.environ['GYP_MSVS_VERSION'],
    154       os.environ['WDK_DIR'])
    155 
    156 
    157 def main():
    158   if not sys.platform.startswith(('win32', 'cygwin')):
    159     return 0
    160   commands = {
    161       'update': Update,
    162       'get_toolchain_dir': GetToolchainDir,
    163       # TODO(scottmg): Add copy_dlls for GN builds (gyp_chromium calls
    164       # CopyVsRuntimeDlls via import, currently).
    165   }
    166   if len(sys.argv) < 2 or sys.argv[1] not in commands:
    167     print >>sys.stderr, 'Expected one of: %s' % ', '.join(commands)
    168     return 1
    169   return commands[sys.argv[1]]()
    170 
    171 
    172 if __name__ == '__main__':
    173   sys.exit(main())
    174