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     if not os.path.exists(json_data_file):
     34       Update()
     35     with open(json_data_file, 'r') as tempf:
     36       toolchain_data = json.load(tempf)
     37 
     38     toolchain = toolchain_data['path']
     39     version = toolchain_data['version']
     40     version_is_pro = version[-1] != 'e'
     41     win8sdk = toolchain_data['win8sdk']
     42     wdk = toolchain_data['wdk']
     43     # TODO(scottmg): The order unfortunately matters in these. They should be
     44     # split into separate keys for x86 and x64. (See CopyVsRuntimeDlls call
     45     # below). http://crbug.com/345992
     46     vs2013_runtime_dll_dirs = toolchain_data['runtime_dirs']
     47 
     48     os.environ['GYP_MSVS_OVERRIDE_PATH'] = toolchain
     49     os.environ['GYP_MSVS_VERSION'] = version
     50     # We need to make sure windows_sdk_path is set to the automated
     51     # toolchain values in GYP_DEFINES, but don't want to override any
     52     # otheroptions.express
     53     # values there.
     54     gyp_defines_dict = gyp.NameValueListToDict(gyp.ShlexEnv('GYP_DEFINES'))
     55     gyp_defines_dict['windows_sdk_path'] = win8sdk
     56     os.environ['GYP_DEFINES'] = ' '.join('%s=%s' % (k, pipes.quote(str(v)))
     57         for k, v in gyp_defines_dict.iteritems())
     58     os.environ['WINDOWSSDKDIR'] = win8sdk
     59     os.environ['WDK_DIR'] = wdk
     60     # Include the VS runtime in the PATH in case it's not machine-installed.
     61     runtime_path = ';'.join(vs2013_runtime_dll_dirs)
     62     os.environ['PATH'] = runtime_path + ';' + os.environ['PATH']
     63   return vs2013_runtime_dll_dirs
     64 
     65 
     66 def CopyVsRuntimeDlls(output_dir, runtime_dirs):
     67   """Copies the VS runtime DLLs from the given |runtime_dirs| to the output
     68   directory so that even if not system-installed, built binaries are likely to
     69   be able to run.
     70 
     71   This needs to be run after gyp has been run so that the expected target
     72   output directories are already created.
     73   """
     74   assert sys.platform.startswith(('win32', 'cygwin'))
     75 
     76   def copy_runtime_impl(target, source):
     77     """Copy |source| to |target| if it doesn't already exist or if it need to be
     78     updated.
     79     """
     80     if (os.path.isdir(os.path.dirname(target)) and
     81         (not os.path.isfile(target) or
     82           os.stat(target).st_mtime != os.stat(source).st_mtime)):
     83       print 'Copying %s to %s...' % (source, target)
     84       if os.path.exists(target):
     85         os.unlink(target)
     86       shutil.copy2(source, target)
     87 
     88   def copy_runtime(target_dir, source_dir, dll_pattern):
     89     """Copy both the msvcr and msvcp runtime DLLs, only if the target doesn't
     90     exist, but the target directory does exist."""
     91     for which in ('p', 'r'):
     92       dll = dll_pattern % which
     93       target = os.path.join(target_dir, dll)
     94       source = os.path.join(source_dir, dll)
     95       copy_runtime_impl(target, source)
     96 
     97   x86, x64 = runtime_dirs
     98   out_debug = os.path.join(output_dir, 'Debug')
     99   out_debug_nacl64 = os.path.join(output_dir, 'Debug', 'x64')
    100   out_release = os.path.join(output_dir, 'Release')
    101   out_release_nacl64 = os.path.join(output_dir, 'Release', 'x64')
    102   out_debug_x64 = os.path.join(output_dir, 'Debug_x64')
    103   out_release_x64 = os.path.join(output_dir, 'Release_x64')
    104 
    105   if os.path.exists(out_debug) and not os.path.exists(out_debug_nacl64):
    106     os.makedirs(out_debug_nacl64)
    107   if os.path.exists(out_release) and not os.path.exists(out_release_nacl64):
    108     os.makedirs(out_release_nacl64)
    109   copy_runtime(out_debug,          x86, 'msvc%s120d.dll')
    110   copy_runtime(out_release,        x86, 'msvc%s120.dll')
    111   copy_runtime(out_debug_x64,      x64, 'msvc%s120d.dll')
    112   copy_runtime(out_release_x64,    x64, 'msvc%s120.dll')
    113   copy_runtime(out_debug_nacl64,   x64, 'msvc%s120d.dll')
    114   copy_runtime(out_release_nacl64, x64, 'msvc%s120.dll')
    115 
    116   # Copy the PGO runtime library to the release directories.
    117   if os.environ.get('GYP_MSVS_OVERRIDE_PATH'):
    118     pgo_x86_runtime_dir = os.path.join(os.environ.get('GYP_MSVS_OVERRIDE_PATH'),
    119                                        'VC', 'bin')
    120     pgo_x64_runtime_dir = os.path.join(pgo_x86_runtime_dir, 'amd64')
    121     pgo_runtime_dll = 'pgort120.dll'
    122     source_x86 = os.path.join(pgo_x86_runtime_dir, pgo_runtime_dll)
    123     if os.path.exists(source_x86):
    124       copy_runtime_impl(os.path.join(out_release, pgo_runtime_dll), source_x86)
    125     source_x64 = os.path.join(pgo_x64_runtime_dir, pgo_runtime_dll)
    126     if os.path.exists(source_x64):
    127       copy_runtime_impl(os.path.join(out_release_x64, pgo_runtime_dll),
    128                         source_x64)
    129 
    130 
    131 def _GetDesiredVsToolchainHashes():
    132   """Load a list of SHA1s corresponding to the toolchains that we want installed
    133   to build with."""
    134   sha1path = os.path.join(script_dir,
    135                           '..', 'buildtools', 'toolchain_vs2013.hash')
    136   with open(sha1path, 'rb') as f:
    137     return f.read().strip().splitlines()
    138 
    139 
    140 def Update():
    141   """Requests an update of the toolchain to the specific hashes we have at
    142   this revision. The update outputs a .json of the various configuration
    143   information required to pass to gyp which we use in |GetToolchainDir()|.
    144   """
    145   depot_tools_win_toolchain = \
    146       bool(int(os.environ.get('DEPOT_TOOLS_WIN_TOOLCHAIN', '1')))
    147   if sys.platform in ('win32', 'cygwin') and depot_tools_win_toolchain:
    148     import find_depot_tools
    149     depot_tools_path = find_depot_tools.add_depot_tools_to_path()
    150     json_data_file = os.path.join(script_dir, 'win_toolchain.json')
    151     get_toolchain_args = [
    152         sys.executable,
    153         os.path.join(depot_tools_path,
    154                     'win_toolchain',
    155                     'get_toolchain_if_necessary.py'),
    156         '--output-json', json_data_file,
    157       ] + _GetDesiredVsToolchainHashes()
    158     subprocess.check_call(get_toolchain_args)
    159 
    160   return 0
    161 
    162 
    163 def GetToolchainDir():
    164   """Gets location information about the current toolchain (must have been
    165   previously updated by 'update'). This is used for the GN build."""
    166   SetEnvironmentAndGetRuntimeDllDirs()
    167   print '''vs_path = "%s"
    168 sdk_path = "%s"
    169 vs_version = "%s"
    170 wdk_dir = "%s"
    171 ''' % (
    172       os.environ['GYP_MSVS_OVERRIDE_PATH'],
    173       os.environ['WINDOWSSDKDIR'],
    174       os.environ['GYP_MSVS_VERSION'],
    175       os.environ.get('WDK_DIR', ''))
    176 
    177 
    178 def main():
    179   if not sys.platform.startswith(('win32', 'cygwin')):
    180     return 0
    181   commands = {
    182       'update': Update,
    183       'get_toolchain_dir': GetToolchainDir,
    184       # TODO(scottmg): Add copy_dlls for GN builds (gyp_chromium calls
    185       # CopyVsRuntimeDlls via import, currently).
    186   }
    187   if len(sys.argv) < 2 or sys.argv[1] not in commands:
    188     print >>sys.stderr, 'Expected one of: %s' % ', '.join(commands)
    189     return 1
    190   return commands[sys.argv[1]]()
    191 
    192 
    193 if __name__ == '__main__':
    194   sys.exit(main())
    195