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