Home | History | Annotate | Download | only in build
      1 #!/usr/bin/env python
      2 # Copyright 2015 the V8 project authors. All rights reserved.
      3 # Copyright 2014 The Chromium Authors. All rights reserved.
      4 # Use of this source code is governed by a BSD-style license that can be
      5 # found in the LICENSE file.
      6 
      7 import json
      8 import os
      9 import pipes
     10 import shutil
     11 import subprocess
     12 import sys
     13 import vs_toolchain
     14 
     15 
     16 script_dir = os.path.dirname(os.path.realpath(__file__))
     17 chrome_src = os.path.abspath(os.path.join(script_dir, os.pardir))
     18 SRC_DIR = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
     19 sys.path.insert(1, os.path.join(chrome_src, 'tools'))
     20 sys.path.insert(0, os.path.join(chrome_src, 'build', 'gyp', 'pylib'))
     21 json_data_file = os.path.join(script_dir, 'win_toolchain.json')
     22 
     23 
     24 import gyp
     25 
     26 
     27 def SetEnvironmentAndGetRuntimeDllDirs():
     28   """Sets up os.environ to use the depot_tools VS toolchain with gyp, and
     29   returns the location of the VS runtime DLLs so they can be copied into
     30   the output directory after gyp generation.
     31   """
     32   vs2013_runtime_dll_dirs = None
     33   depot_tools_win_toolchain = \
     34       bool(int(os.environ.get('DEPOT_TOOLS_WIN_TOOLCHAIN', '1')))
     35   # When running on a non-Windows host, only do this if the SDK has explicitly
     36   # been downloaded before (in which case json_data_file will exist).
     37   if ((sys.platform in ('win32', 'cygwin') or os.path.exists(json_data_file))
     38       and depot_tools_win_toolchain):
     39     if not os.path.exists(json_data_file):
     40       Update()
     41     with open(json_data_file, 'r') as tempf:
     42       toolchain_data = json.load(tempf)
     43 
     44     toolchain = toolchain_data['path']
     45     version = toolchain_data['version']
     46     win_sdk = toolchain_data.get('win_sdk')
     47     if not win_sdk:
     48       win_sdk = toolchain_data['win8sdk']
     49     wdk = toolchain_data['wdk']
     50     # TODO(scottmg): The order unfortunately matters in these. They should be
     51     # split into separate keys for x86 and x64. (See CopyVsRuntimeDlls call
     52     # below). http://crbug.com/345992
     53     vs2013_runtime_dll_dirs = toolchain_data['runtime_dirs']
     54 
     55     os.environ['GYP_MSVS_OVERRIDE_PATH'] = toolchain
     56     os.environ['GYP_MSVS_VERSION'] = version
     57     # We need to make sure windows_sdk_path is set to the automated
     58     # toolchain values in GYP_DEFINES, but don't want to override any
     59     # otheroptions.express
     60     # values there.
     61     gyp_defines_dict = gyp.NameValueListToDict(gyp.ShlexEnv('GYP_DEFINES'))
     62     gyp_defines_dict['windows_sdk_path'] = win_sdk
     63     os.environ['GYP_DEFINES'] = ' '.join('%s=%s' % (k, pipes.quote(str(v)))
     64         for k, v in gyp_defines_dict.iteritems())
     65     os.environ['WINDOWSSDKDIR'] = win_sdk
     66     os.environ['WDK_DIR'] = wdk
     67     # Include the VS runtime in the PATH in case it's not machine-installed.
     68     runtime_path = ';'.join(vs2013_runtime_dll_dirs)
     69     os.environ['PATH'] = runtime_path + ';' + os.environ['PATH']
     70   return vs2013_runtime_dll_dirs
     71 
     72 
     73 def _VersionNumber():
     74   """Gets the standard version number ('120', '140', etc.) based on
     75   GYP_MSVS_VERSION."""
     76   if os.environ['GYP_MSVS_VERSION'] == '2013':
     77     return '120'
     78   elif os.environ['GYP_MSVS_VERSION'] == '2015':
     79     return '140'
     80   else:
     81     raise ValueError('Unexpected GYP_MSVS_VERSION')
     82 
     83 
     84 def _CopyRuntimeImpl(target, source):
     85   """Copy |source| to |target| if it doesn't already exist or if it
     86   needs to be updated.
     87   """
     88   if (os.path.isdir(os.path.dirname(target)) and
     89       (not os.path.isfile(target) or
     90       os.stat(target).st_mtime != os.stat(source).st_mtime)):
     91     print 'Copying %s to %s...' % (source, target)
     92     if os.path.exists(target):
     93       os.unlink(target)
     94     shutil.copy2(source, target)
     95 
     96 
     97 def _CopyRuntime2013(target_dir, source_dir, dll_pattern):
     98   """Copy both the msvcr and msvcp runtime DLLs, only if the target doesn't
     99   exist, but the target directory does exist."""
    100   for file_part in ('p', 'r'):
    101     dll = dll_pattern % file_part
    102     target = os.path.join(target_dir, dll)
    103     source = os.path.join(source_dir, dll)
    104     _CopyRuntimeImpl(target, source)
    105 
    106 
    107 def _CopyRuntime2015(target_dir, source_dir, dll_pattern):
    108   """Copy both the msvcp and vccorlib runtime DLLs, only if the target doesn't
    109   exist, but the target directory does exist."""
    110   for file_part in ('msvcp', 'vccorlib'):
    111     dll = dll_pattern % file_part
    112     target = os.path.join(target_dir, dll)
    113     source = os.path.join(source_dir, dll)
    114     _CopyRuntimeImpl(target, source)
    115 
    116 
    117 def CopyVsRuntimeDlls(output_dir, runtime_dirs):
    118   """Copies the VS runtime DLLs from the given |runtime_dirs| to the output
    119   directory so that even if not system-installed, built binaries are likely to
    120   be able to run.
    121 
    122   This needs to be run after gyp has been run so that the expected target
    123   output directories are already created.
    124   """
    125   x86, x64 = runtime_dirs
    126   out_debug = os.path.join(output_dir, 'Debug')
    127   out_debug_nacl64 = os.path.join(output_dir, 'Debug', 'x64')
    128   out_release = os.path.join(output_dir, 'Release')
    129   out_release_nacl64 = os.path.join(output_dir, 'Release', 'x64')
    130   out_debug_x64 = os.path.join(output_dir, 'Debug_x64')
    131   out_release_x64 = os.path.join(output_dir, 'Release_x64')
    132 
    133   if os.path.exists(out_debug) and not os.path.exists(out_debug_nacl64):
    134     os.makedirs(out_debug_nacl64)
    135   if os.path.exists(out_release) and not os.path.exists(out_release_nacl64):
    136     os.makedirs(out_release_nacl64)
    137   if os.environ.get('GYP_MSVS_VERSION') == '2015':
    138     _CopyRuntime2015(out_debug,          x86, '%s140d.dll')
    139     _CopyRuntime2015(out_release,        x86, '%s140.dll')
    140     _CopyRuntime2015(out_debug_x64,      x64, '%s140d.dll')
    141     _CopyRuntime2015(out_release_x64,    x64, '%s140.dll')
    142     _CopyRuntime2015(out_debug_nacl64,   x64, '%s140d.dll')
    143     _CopyRuntime2015(out_release_nacl64, x64, '%s140.dll')
    144   else:
    145     # VS2013 is the default.
    146     _CopyRuntime2013(out_debug,          x86, 'msvc%s120d.dll')
    147     _CopyRuntime2013(out_release,        x86, 'msvc%s120.dll')
    148     _CopyRuntime2013(out_debug_x64,      x64, 'msvc%s120d.dll')
    149     _CopyRuntime2013(out_release_x64,    x64, 'msvc%s120.dll')
    150     _CopyRuntime2013(out_debug_nacl64,   x64, 'msvc%s120d.dll')
    151     _CopyRuntime2013(out_release_nacl64, x64, 'msvc%s120.dll')
    152 
    153   # Copy the PGO runtime library to the release directories.
    154   if os.environ.get('GYP_MSVS_OVERRIDE_PATH'):
    155     pgo_x86_runtime_dir = os.path.join(os.environ.get('GYP_MSVS_OVERRIDE_PATH'),
    156                                        'VC', 'bin')
    157     pgo_x64_runtime_dir = os.path.join(pgo_x86_runtime_dir, 'amd64')
    158     pgo_runtime_dll = 'pgort' + _VersionNumber() + '.dll'
    159     source_x86 = os.path.join(pgo_x86_runtime_dir, pgo_runtime_dll)
    160     if os.path.exists(source_x86):
    161       _CopyRuntimeImpl(os.path.join(out_release, pgo_runtime_dll), source_x86)
    162     source_x64 = os.path.join(pgo_x64_runtime_dir, pgo_runtime_dll)
    163     if os.path.exists(source_x64):
    164       _CopyRuntimeImpl(os.path.join(out_release_x64, pgo_runtime_dll),
    165                        source_x64)
    166 
    167 
    168 def CopyDlls(target_dir, configuration, target_cpu):
    169   """Copy the VS runtime DLLs into the requested directory as needed.
    170 
    171   configuration is one of 'Debug' or 'Release'.
    172   target_cpu is one of 'x86' or 'x64'.
    173 
    174   The debug configuration gets both the debug and release DLLs; the
    175   release config only the latter.
    176   """
    177   vs2013_runtime_dll_dirs = SetEnvironmentAndGetRuntimeDllDirs()
    178   if not vs2013_runtime_dll_dirs:
    179     return
    180 
    181   x64_runtime, x86_runtime = vs2013_runtime_dll_dirs
    182   runtime_dir = x64_runtime if target_cpu == 'x64' else x86_runtime
    183   _CopyRuntime2013(
    184       target_dir, runtime_dir, 'msvc%s' + _VersionNumber() + '.dll')
    185   if configuration == 'Debug':
    186     _CopyRuntime2013(
    187         target_dir, runtime_dir, 'msvc%s' + _VersionNumber() + 'd.dll')
    188 
    189 
    190 def _GetDesiredVsToolchainHashes():
    191   """Load a list of SHA1s corresponding to the toolchains that we want installed
    192   to build with."""
    193   if os.environ.get('GYP_MSVS_VERSION') == '2015':
    194     return ['49ae4b60d898182fc3f521c2fcda82c453915011']
    195   else:
    196     # Default to VS2013.
    197     return ['ee7d718ec60c2dc5d255bbe325909c2021a7efef']
    198 
    199 
    200 def Update(force=False):
    201   """Requests an update of the toolchain to the specific hashes we have at
    202   this revision. The update outputs a .json of the various configuration
    203   information required to pass to gyp which we use in |GetToolchainDir()|.
    204   """
    205   if force != False and force != '--force':
    206     print >>sys.stderr, 'Unknown parameter "%s"' % force
    207     return 1
    208   if force == '--force' or os.path.exists(json_data_file):
    209     force = True
    210 
    211   depot_tools_win_toolchain = \
    212       bool(int(os.environ.get('DEPOT_TOOLS_WIN_TOOLCHAIN', '1')))
    213   if ((sys.platform in ('win32', 'cygwin') or force) and
    214         depot_tools_win_toolchain):
    215     import find_depot_tools
    216     depot_tools_path = find_depot_tools.add_depot_tools_to_path()
    217     get_toolchain_args = [
    218         sys.executable,
    219         os.path.join(depot_tools_path,
    220                     'win_toolchain',
    221                     'get_toolchain_if_necessary.py'),
    222         '--output-json', json_data_file,
    223       ] + _GetDesiredVsToolchainHashes()
    224     if force:
    225       get_toolchain_args.append('--force')
    226     subprocess.check_call(get_toolchain_args)
    227 
    228   return 0
    229 
    230 
    231 def GetToolchainDir():
    232   """Gets location information about the current toolchain (must have been
    233   previously updated by 'update'). This is used for the GN build."""
    234   runtime_dll_dirs = SetEnvironmentAndGetRuntimeDllDirs()
    235 
    236   # If WINDOWSSDKDIR is not set, search the default SDK path and set it.
    237   if not 'WINDOWSSDKDIR' in os.environ:
    238     default_sdk_path = 'C:\\Program Files (x86)\\Windows Kits\\8.1'
    239     if os.path.isdir(default_sdk_path):
    240       os.environ['WINDOWSSDKDIR'] = default_sdk_path
    241 
    242   print '''vs_path = "%s"
    243 sdk_path = "%s"
    244 vs_version = "%s"
    245 wdk_dir = "%s"
    246 runtime_dirs = "%s"
    247 ''' % (
    248       os.environ['GYP_MSVS_OVERRIDE_PATH'],
    249       os.environ['WINDOWSSDKDIR'],
    250       os.environ['GYP_MSVS_VERSION'],
    251       os.environ.get('WDK_DIR', ''),
    252       ';'.join(runtime_dll_dirs or ['None']))
    253 
    254 
    255 def main():
    256   commands = {
    257       'update': Update,
    258       'get_toolchain_dir': GetToolchainDir,
    259       'copy_dlls': CopyDlls,
    260   }
    261   if len(sys.argv) < 2 or sys.argv[1] not in commands:
    262     print >>sys.stderr, 'Expected one of: %s' % ', '.join(commands)
    263     return 1
    264   return commands[sys.argv[1]](*sys.argv[2:])
    265 
    266 
    267 if __name__ == '__main__':
    268   sys.exit(main())
    269