[email protected] | 7cc7a1c | 2014-01-15 22:05:05 | [diff] [blame] | 1 | #!/usr/bin/env python |
| 2 | # Copyright 2013 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 | """Extracts a Windows VS2013 toolchain from various downloadable pieces.""" |
| 7 | |
| 8 | |
| 9 | import ctypes |
[email protected] | 5d17b92 | 2014-02-22 01:47:53 | [diff] [blame] | 10 | import json |
[email protected] | 7cc7a1c | 2014-01-15 22:05:05 | [diff] [blame] | 11 | import optparse |
| 12 | import os |
| 13 | import shutil |
| 14 | import subprocess |
| 15 | import sys |
| 16 | import tempfile |
| 17 | import urllib2 |
| 18 | |
| 19 | |
| 20 | BASEDIR = os.path.dirname(os.path.abspath(__file__)) |
[email protected] | 26cb836 | 2014-02-04 00:07:02 | [diff] [blame] | 21 | WDK_ISO_URL = ( |
| 22 | 'http://download.microsoft.com/download/' |
| 23 | '4/A/2/4A25C7D5-EFBE-4182-B6A9-AE6850409A78/GRMWDK_EN_7600_1.ISO') |
[email protected] | 7cc7a1c | 2014-01-15 22:05:05 | [diff] [blame] | 24 | g_temp_dirs = [] |
| 25 | |
| 26 | |
[email protected] | 7923129 | 2014-01-28 03:17:54 | [diff] [blame] | 27 | sys.path.append(os.path.join(BASEDIR, '..')) |
| 28 | import download_from_google_storage |
| 29 | |
| 30 | |
[email protected] | 7cc7a1c | 2014-01-15 22:05:05 | [diff] [blame] | 31 | def GetLongPathName(path): |
| 32 | """Converts any 8dot3 names in the path to the full name.""" |
| 33 | buf = ctypes.create_unicode_buffer(260) |
| 34 | size = ctypes.windll.kernel32.GetLongPathNameW(unicode(path), buf, 260) |
| 35 | if (size > 260): |
| 36 | sys.exit('Long form of path longer than 260 chars: %s' % path) |
| 37 | return buf.value |
| 38 | |
| 39 | |
| 40 | def RunOrDie(command): |
| 41 | subprocess.check_call(command, shell=True) |
| 42 | |
| 43 | |
[email protected] | 40167ed | 2014-02-13 18:24:52 | [diff] [blame] | 44 | class ScopedSubstTempDir(object): |
| 45 | """Creates a |TempDir()| and subst's a drive to the path. |
| 46 | |
| 47 | This is done to avoid exceedingly long names in some .msi packages which |
| 48 | fail to extract because they exceed _MAX_PATH. Only the "subst" part of this |
| 49 | is scoped, not the temp dir, which is left for use and cleanup by the |
| 50 | caller. |
| 51 | """ |
| 52 | DefineDosDevice = ctypes.windll.kernel32.DefineDosDeviceW |
| 53 | DefineDosDevice.argtypes = [ctypes.c_int, ctypes.c_wchar_p, ctypes.c_wchar_p] |
| 54 | DDD_NO_BROADCAST_SYSTEM = 0x08 |
| 55 | DDD_REMOVE_DEFINITION = 0x02 |
| 56 | |
| 57 | def __init__(self): |
| 58 | self.real_path = TempDir() |
| 59 | self.subst_drive = None |
| 60 | |
| 61 | def __enter__(self): |
| 62 | """Tries to find a subst that we can use for the temporary directory, and |
| 63 | aborts on failure.""" |
| 64 | for drive in range(ord('Z'), ord('A') - 1, -1): |
| 65 | candidate = '%c:' % drive |
| 66 | if self.DefineDosDevice( |
| 67 | self.DDD_NO_BROADCAST_SYSTEM, candidate, self.real_path) != 0: |
| 68 | self.subst_drive = candidate |
| 69 | return self |
| 70 | raise RuntimeError('Unable to find a subst path') |
| 71 | |
| 72 | def __exit__(self, typ, value, traceback): |
| 73 | if self.subst_drive: |
| 74 | if self.DefineDosDevice(int(self.DDD_REMOVE_DEFINITION), |
| 75 | self.subst_drive, |
| 76 | self.real_path) == 0: |
| 77 | raise RuntimeError('Unable to remove subst') |
| 78 | |
| 79 | def ShortenedPath(self): |
| 80 | return self.subst_drive + '\\' |
| 81 | |
| 82 | def RealPath(self): |
| 83 | return self.real_path |
| 84 | |
| 85 | |
[email protected] | 7cc7a1c | 2014-01-15 22:05:05 | [diff] [blame] | 86 | def TempDir(): |
| 87 | """Generates a temporary directory (for downloading or extracting to) and keep |
| 88 | track of the directory that's created for cleaning up later. |
| 89 | """ |
[email protected] | 7cc7a1c | 2014-01-15 22:05:05 | [diff] [blame] | 90 | temp = tempfile.mkdtemp() |
| 91 | g_temp_dirs.append(temp) |
| 92 | return temp |
| 93 | |
| 94 | |
| 95 | def DeleteAllTempDirs(): |
| 96 | """Removes all temporary directories created by |TempDir()|.""" |
| 97 | global g_temp_dirs |
| 98 | if g_temp_dirs: |
| 99 | sys.stdout.write('Cleaning up temporaries...\n') |
| 100 | for temp in g_temp_dirs: |
| 101 | # shutil.rmtree errors out on read only attributes. |
| 102 | RunOrDie('rmdir /s/q "%s"' % temp) |
| 103 | g_temp_dirs = [] |
| 104 | |
| 105 | |
[email protected] | 26cb836 | 2014-02-04 00:07:02 | [diff] [blame] | 106 | def GetMainIsoUrl(pro): |
| 107 | """Gets the main .iso URL. |
[email protected] | 7cc7a1c | 2014-01-15 22:05:05 | [diff] [blame] | 108 | |
[email protected] | 0d23d77 | 2014-03-07 20:17:49 | [diff] [blame] | 109 | If |pro| is False, downloads the Express edition. |
[email protected] | 7cc7a1c | 2014-01-15 22:05:05 | [diff] [blame] | 110 | """ |
| 111 | prefix = 'http://download.microsoft.com/download/' |
| 112 | if pro: |
| 113 | return (prefix + |
| 114 | 'A/F/1/AF128362-A6A8-4DB3-A39A-C348086472CC/VS2013_RTM_PRO_ENU.iso') |
| 115 | else: |
| 116 | return (prefix + |
| 117 | '7/2/E/72E0F986-D247-4289-B9DC-C4FB07374894/VS2013_RTM_DskExp_ENU.iso') |
| 118 | |
| 119 | |
| 120 | def Download(url, local_path): |
| 121 | """Downloads a large-ish binary file and print some status information while |
| 122 | doing so. |
| 123 | """ |
| 124 | sys.stdout.write('Downloading %s...\n' % url) |
| 125 | req = urllib2.urlopen(url) |
| 126 | content_length = int(req.headers.get('Content-Length', 0)) |
| 127 | bytes_read = 0L |
| 128 | terminator = '\r' if sys.stdout.isatty() else '\n' |
[email protected] | 9eabb22 | 2014-01-16 02:42:43 | [diff] [blame] | 129 | with open(local_path, 'wb') as file_handle: |
[email protected] | 7cc7a1c | 2014-01-15 22:05:05 | [diff] [blame] | 130 | while True: |
| 131 | chunk = req.read(1024 * 1024) |
| 132 | if not chunk: |
| 133 | break |
| 134 | bytes_read += len(chunk) |
[email protected] | 9eabb22 | 2014-01-16 02:42:43 | [diff] [blame] | 135 | file_handle.write(chunk) |
[email protected] | 7cc7a1c | 2014-01-15 22:05:05 | [diff] [blame] | 136 | sys.stdout.write('... %d/%d%s' % (bytes_read, content_length, terminator)) |
| 137 | sys.stdout.flush() |
| 138 | sys.stdout.write('\n') |
| 139 | if content_length and content_length != bytes_read: |
[email protected] | 22b2ec7 | 2014-01-28 18:10:06 | [diff] [blame] | 140 | sys.exit('Got incorrect number of bytes downloading %s' % url) |
[email protected] | 7cc7a1c | 2014-01-15 22:05:05 | [diff] [blame] | 141 | |
| 142 | |
| 143 | def ExtractIso(iso_path): |
| 144 | """Uses 7zip to extract the contents of the given .iso (or self-extracting |
| 145 | .exe). |
| 146 | """ |
| 147 | target_path = TempDir() |
| 148 | sys.stdout.write('Extracting %s...\n' % iso_path) |
| 149 | sys.stdout.flush() |
| 150 | # TODO(scottmg): Do this (and exe) manually with python code. |
| 151 | # Note that at the beginning of main() we set the working directory to 7z's |
| 152 | # location so that 7z can find its codec dll. |
| 153 | RunOrDie('7z x "%s" -y "-o%s" >nul' % (iso_path, target_path)) |
| 154 | return target_path |
| 155 | |
| 156 | |
| 157 | def ExtractMsi(msi_path): |
| 158 | """Uses msiexec to extract the contents of the given .msi file.""" |
| 159 | sys.stdout.write('Extracting %s...\n' % msi_path) |
[email protected] | 40167ed | 2014-02-13 18:24:52 | [diff] [blame] | 160 | with ScopedSubstTempDir() as temp_dir: |
| 161 | RunOrDie('msiexec /a "%s" /qn TARGETDIR="%s"' % ( |
| 162 | msi_path, temp_dir.ShortenedPath())) |
| 163 | return temp_dir.RealPath() |
[email protected] | 7cc7a1c | 2014-01-15 22:05:05 | [diff] [blame] | 164 | |
| 165 | |
| 166 | def DownloadMainIso(url): |
| 167 | temp_dir = TempDir() |
| 168 | target_path = os.path.join(temp_dir, os.path.basename(url)) |
| 169 | Download(url, target_path) |
| 170 | return target_path |
| 171 | |
| 172 | |
[email protected] | dfaf79d | 2014-01-16 20:50:19 | [diff] [blame] | 173 | def DownloadSDK8(): |
| 174 | """Downloads the Win8 SDK. |
| 175 | |
| 176 | This one is slightly different than the simpler direct downloads. There is |
| 177 | no .ISO distribution for the Windows 8 SDK. Rather, a tool is provided that |
| 178 | is a download manager. This is used to download the various .msi files to a |
| 179 | target location. Unfortunately, this tool requires elevation for no obvious |
| 180 | reason even when only downloading, so this function will trigger a UAC |
| 181 | elevation if the script is not run from an elevated prompt. This is mostly |
| 182 | grabbed for windbg and cdb (See http://crbug.com/321187) as most of the SDK |
| 183 | is in VS2013, however we need a couple D3D related things from the SDK. |
| 184 | """ |
| 185 | # Use the long path name here because because 8dot3 names don't seem to work. |
| 186 | sdk_temp_dir = GetLongPathName(TempDir()) |
| 187 | target_path = os.path.join(sdk_temp_dir, 'sdksetup.exe') |
| 188 | standalone_path = os.path.join(sdk_temp_dir, 'Standalone') |
| 189 | Download( |
| 190 | ('http://download.microsoft.com/download/' |
| 191 | 'F/1/3/F1300C9C-A120-4341-90DF-8A52509B23AC/standalonesdk/sdksetup.exe'), |
| 192 | target_path) |
| 193 | sys.stdout.write( |
| 194 | 'Running sdksetup.exe to download Win8 SDK (may request elevation)...\n') |
| 195 | count = 0 |
| 196 | while count < 5: |
[email protected] | 5b9f90e | 2014-03-05 03:28:24 | [diff] [blame] | 197 | rc = subprocess.call([target_path, |
| 198 | '/quiet', |
| 199 | '/features', 'OptionId.WindowsDesktopDebuggers', |
| 200 | 'OptionId.WindowsDesktopSoftwareDevelopmentKit', |
| 201 | '/layout', standalone_path]) |
[email protected] | dfaf79d | 2014-01-16 20:50:19 | [diff] [blame] | 202 | if rc == 0: |
| 203 | return standalone_path |
| 204 | count += 1 |
| 205 | sys.stdout.write('Windows 8 SDK failed to download, retrying.\n') |
[email protected] | 22b2ec7 | 2014-01-28 18:10:06 | [diff] [blame] | 206 | sys.exit('After multiple retries, couldn\'t download Win8 SDK') |
[email protected] | dfaf79d | 2014-01-16 20:50:19 | [diff] [blame] | 207 | |
| 208 | |
[email protected] | 26cb836 | 2014-02-04 00:07:02 | [diff] [blame] | 209 | def DownloadWDKIso(): |
| 210 | wdk_temp_dir = TempDir() |
| 211 | target_path = os.path.join(wdk_temp_dir, 'GRMWDK_EN_7600_1.ISO') |
| 212 | Download(WDK_ISO_URL, target_path) |
| 213 | return target_path |
| 214 | |
| 215 | |
[email protected] | 7923129 | 2014-01-28 03:17:54 | [diff] [blame] | 216 | def DownloadUsingGsutil(filename): |
| 217 | """Downloads the given file from Google Storage chrome-wintoolchain bucket.""" |
| 218 | temp_dir = TempDir() |
| 219 | assert os.path.basename(filename) == filename |
| 220 | target_path = os.path.join(temp_dir, filename) |
| 221 | gsutil = download_from_google_storage.Gsutil( |
| 222 | download_from_google_storage.GSUTIL_DEFAULT_PATH, boto_path=None) |
| 223 | code = gsutil.call('cp', 'gs://chrome-wintoolchain/' + filename, target_path) |
| 224 | if code != 0: |
[email protected] | 22b2ec7 | 2014-01-28 18:10:06 | [diff] [blame] | 225 | sys.exit('gsutil failed') |
[email protected] | 7923129 | 2014-01-28 03:17:54 | [diff] [blame] | 226 | return target_path |
| 227 | |
| 228 | |
| 229 | def GetVSInternal(): |
| 230 | """Uses gsutil to pull the toolchain from internal Google Storage bucket.""" |
| 231 | return DownloadUsingGsutil('VS2013_RTM_PRO_ENU.iso') |
| 232 | |
| 233 | |
| 234 | def GetSDKInternal(): |
| 235 | """Downloads a zipped copy of the SDK from internal Google Storage bucket, |
| 236 | and extracts it.""" |
| 237 | zip_file = DownloadUsingGsutil('Standalone.zip') |
| 238 | return ExtractIso(zip_file) |
| 239 | |
| 240 | |
[email protected] | dfaf79d | 2014-01-16 20:50:19 | [diff] [blame] | 241 | class SourceImages(object): |
[email protected] | 26cb836 | 2014-02-04 00:07:02 | [diff] [blame] | 242 | """Local paths for components. |wdk_path| may be None if it's unnecessary for |
| 243 | the given configuration.""" |
| 244 | def __init__(self, vs_path, sdk8_path, wdk_path): |
[email protected] | dfaf79d | 2014-01-16 20:50:19 | [diff] [blame] | 245 | self.vs_path = vs_path |
| 246 | self.sdk8_path = sdk8_path |
[email protected] | 26cb836 | 2014-02-04 00:07:02 | [diff] [blame] | 247 | self.wdk_path = wdk_path |
[email protected] | dfaf79d | 2014-01-16 20:50:19 | [diff] [blame] | 248 | |
| 249 | |
[email protected] | 7175632 | 2014-03-02 22:59:57 | [diff] [blame] | 250 | def GetSourceImages(local_dir, pro): |
[email protected] | 26cb836 | 2014-02-04 00:07:02 | [diff] [blame] | 251 | """Downloads the various sources that we need. |
| 252 | |
| 253 | Of note: Because Express does not include ATL, there's an additional download |
| 254 | of the 7.1 WDK which is the latest publically accessible source for ATL. When |
| 255 | |pro| this is not necessary (and CHROME_HEADLESS always implies Pro). |
| 256 | """ |
[email protected] | 0d23d77 | 2014-03-07 20:17:49 | [diff] [blame] | 257 | if pro and not local_dir: |
| 258 | sys.exit('Non-Express must be used with --local') |
[email protected] | 26cb836 | 2014-02-04 00:07:02 | [diff] [blame] | 259 | url = GetMainIsoUrl(pro) |
[email protected] | 7175632 | 2014-03-02 22:59:57 | [diff] [blame] | 260 | if local_dir: |
[email protected] | 26cb836 | 2014-02-04 00:07:02 | [diff] [blame] | 261 | wdk_path = (os.path.join(local_dir, os.path.basename(WDK_ISO_URL)) |
| 262 | if not pro else None) |
[email protected] | dfaf79d | 2014-01-16 20:50:19 | [diff] [blame] | 263 | return SourceImages(os.path.join(local_dir, os.path.basename(url)), |
[email protected] | 26cb836 | 2014-02-04 00:07:02 | [diff] [blame] | 264 | os.path.join(local_dir, 'Standalone'), |
| 265 | wdk_path=wdk_path) |
[email protected] | 7cc7a1c | 2014-01-15 22:05:05 | [diff] [blame] | 266 | else: |
[email protected] | dfaf79d | 2014-01-16 20:50:19 | [diff] [blame] | 267 | # Note that we do the SDK first, as it might cause an elevation prompt. |
| 268 | sdk8_path = DownloadSDK8() |
| 269 | vs_path = DownloadMainIso(url) |
[email protected] | 26cb836 | 2014-02-04 00:07:02 | [diff] [blame] | 270 | wdk_path = DownloadWDKIso() if not pro else None |
| 271 | return SourceImages(vs_path, sdk8_path, wdk_path=wdk_path) |
[email protected] | 7cc7a1c | 2014-01-15 22:05:05 | [diff] [blame] | 272 | |
| 273 | |
[email protected] | dfaf79d | 2014-01-16 20:50:19 | [diff] [blame] | 274 | def ExtractMsiList(root_dir, packages): |
[email protected] | 7cc7a1c | 2014-01-15 22:05:05 | [diff] [blame] | 275 | """Extracts the contents of a list of .msi files from an already extracted |
| 276 | .iso file. |
| 277 | |
| 278 | |packages| is a list of pairs (msi, required). If required is not True, the |
| 279 | msi is optional (this is set for packages that are in Pro but not Express). |
| 280 | """ |
| 281 | results = [] |
| 282 | for (package, required) in packages: |
[email protected] | dfaf79d | 2014-01-16 20:50:19 | [diff] [blame] | 283 | path_to_package = os.path.join(root_dir, package) |
[email protected] | 7cc7a1c | 2014-01-15 22:05:05 | [diff] [blame] | 284 | if not os.path.exists(path_to_package) and not required: |
| 285 | continue |
| 286 | results.append(ExtractMsi(path_to_package)) |
| 287 | return results |
| 288 | |
| 289 | |
| 290 | def ExtractComponents(image): |
[email protected] | dfaf79d | 2014-01-16 20:50:19 | [diff] [blame] | 291 | vs_packages = [ |
[email protected] | 7cc7a1c | 2014-01-15 22:05:05 | [diff] [blame] | 292 | (r'vcRuntimeAdditional_amd64\vc_runtimeAdditional_x64.msi', True), |
| 293 | (r'vcRuntimeAdditional_x86\vc_runtimeAdditional_x86.msi', True), |
| 294 | (r'vcRuntimeDebug_amd64\vc_runtimeDebug_x64.msi', True), |
| 295 | (r'vcRuntimeDebug_x86\vc_runtimeDebug_x86.msi', True), |
| 296 | (r'vcRuntimeMinimum_amd64\vc_runtimeMinimum_x64.msi', True), |
| 297 | (r'vcRuntimeMinimum_x86\vc_runtimeMinimum_x86.msi', True), |
| 298 | (r'vc_compilerCore86\vc_compilerCore86.msi', True), |
| 299 | (r'vc_compilerCore86res\vc_compilerCore86res.msi', True), |
| 300 | (r'vc_compilerx64nat\vc_compilerx64nat.msi', False), |
| 301 | (r'vc_compilerx64natres\vc_compilerx64natres.msi', False), |
| 302 | (r'vc_compilerx64x86\vc_compilerx64x86.msi', False), |
| 303 | (r'vc_compilerx64x86res\vc_compilerx64x86res.msi', False), |
| 304 | (r'vc_librarycore86\vc_librarycore86.msi', True), |
| 305 | (r'vc_libraryDesktop\x64\vc_LibraryDesktopX64.msi', True), |
| 306 | (r'vc_libraryDesktop\x86\vc_LibraryDesktopX86.msi', True), |
| 307 | (r'vc_libraryextended\vc_libraryextended.msi', False), |
[email protected] | 40167ed | 2014-02-13 18:24:52 | [diff] [blame] | 308 | (r'professionalcore\Setup\vs_professionalcore.msi', False), |
[email protected] | 4a56efe | 2014-02-22 07:45:07 | [diff] [blame] | 309 | (r'vc_libraryselectablemfc\vc_libraryselectablemfc.msi', False), |
[email protected] | 7cc7a1c | 2014-01-15 22:05:05 | [diff] [blame] | 310 | ] |
[email protected] | dfaf79d | 2014-01-16 20:50:19 | [diff] [blame] | 311 | extracted_iso = ExtractIso(image.vs_path) |
| 312 | result = ExtractMsiList(os.path.join(extracted_iso, 'packages'), vs_packages) |
| 313 | |
| 314 | sdk_packages = [ |
| 315 | (r'X86 Debuggers And Tools-x86_en-us.msi', True), |
| 316 | (r'X64 Debuggers And Tools-x64_en-us.msi', True), |
| 317 | (r'SDK Debuggers-x86_en-us.msi', True), |
[email protected] | 26cb836 | 2014-02-04 00:07:02 | [diff] [blame] | 318 | (r'Windows Software Development Kit-x86_en-us.msi', True), |
| 319 | (r'Windows Software Development Kit for Metro style Apps-x86_en-us.msi', |
| 320 | True), |
[email protected] | dfaf79d | 2014-01-16 20:50:19 | [diff] [blame] | 321 | ] |
| 322 | result.extend(ExtractMsiList(os.path.join(image.sdk8_path, 'Installers'), |
| 323 | sdk_packages)) |
| 324 | |
[email protected] | 26cb836 | 2014-02-04 00:07:02 | [diff] [blame] | 325 | if image.wdk_path: |
| 326 | # This image will only be set when using Express, when we need the WDK |
| 327 | # headers and libs to supplement Express with ATL. |
| 328 | wdk_packages = [ |
| 329 | (r'headers.msi', True), |
| 330 | (r'libs_x86fre.msi', True), |
| 331 | (r'libs_x64fre.msi', True), |
| 332 | ] |
| 333 | extracted_iso = ExtractIso(image.wdk_path) |
| 334 | result.extend(ExtractMsiList(os.path.join(extracted_iso, 'WDK'), |
| 335 | wdk_packages)) |
| 336 | |
[email protected] | dfaf79d | 2014-01-16 20:50:19 | [diff] [blame] | 337 | return result |
[email protected] | 7cc7a1c | 2014-01-15 22:05:05 | [diff] [blame] | 338 | |
| 339 | |
| 340 | def CopyToFinalLocation(extracted_dirs, target_dir): |
| 341 | sys.stdout.write('Copying to final location...\n') |
| 342 | mappings = { |
[email protected] | 40167ed | 2014-02-13 18:24:52 | [diff] [blame] | 343 | 'Program Files\\Microsoft Visual Studio 12.0\\VC\\': 'VC\\', |
| 344 | 'Program Files\\Microsoft Visual Studio 12.0\\DIA SDK\\': 'DIA SDK\\', |
[email protected] | 7cc7a1c | 2014-01-15 22:05:05 | [diff] [blame] | 345 | 'System64\\': 'sys64\\', |
| 346 | 'System\\': 'sys32\\', |
[email protected] | c60ecf9 | 2014-11-14 22:51:23 | [diff] [blame] | 347 | 'Windows Kits\\8.1\\': 'win8sdk\\', |
[email protected] | 26cb836 | 2014-02-04 00:07:02 | [diff] [blame] | 348 | 'WinDDK\\7600.16385.win7_wdk.100208-1538\\': 'wdk\\', |
[email protected] | 7cc7a1c | 2014-01-15 22:05:05 | [diff] [blame] | 349 | } |
| 350 | matches = [] |
| 351 | for extracted_dir in extracted_dirs: |
[email protected] | 9eabb22 | 2014-01-16 02:42:43 | [diff] [blame] | 352 | for root, _, filenames in os.walk(extracted_dir): |
[email protected] | 7cc7a1c | 2014-01-15 22:05:05 | [diff] [blame] | 353 | for filename in filenames: |
| 354 | matches.append((extracted_dir, os.path.join(root, filename))) |
| 355 | |
| 356 | copies = [] |
| 357 | for prefix, full_path in matches: |
| 358 | # +1 for trailing \. |
| 359 | partial_path = full_path[len(prefix) + 1:] |
| 360 | for map_from, map_to in mappings.iteritems(): |
| 361 | if partial_path.startswith(map_from): |
| 362 | target_path = os.path.join(map_to, partial_path[len(map_from):]) |
| 363 | copies.append((full_path, os.path.join(target_dir, target_path))) |
| 364 | |
| 365 | for full_source, full_target in copies: |
| 366 | target_dir = os.path.dirname(full_target) |
| 367 | if not os.path.isdir(target_dir): |
| 368 | os.makedirs(target_dir) |
| 369 | shutil.copy2(full_source, full_target) |
| 370 | |
| 371 | |
| 372 | def GenerateSetEnvCmd(target_dir, pro): |
| 373 | """Generate a batch file that gyp expects to exist to set up the compiler |
| 374 | environment. |
| 375 | |
| 376 | This is normally generated by a full install of the SDK, but we |
| 377 | do it here manually since we do not do a full install.""" |
| 378 | with open(os.path.join( |
| 379 | target_dir, r'win8sdk\bin\SetEnv.cmd'), 'w') as f: |
| 380 | f.write('@echo off\n' |
| 381 | ':: Generated by win_toolchain\\toolchain2013.py.\n' |
| 382 | # Common to x86 and x64 |
| 383 | 'set PATH=%~dp0..\\..\\Common7\\IDE;%PATH%\n' |
| 384 | 'set INCLUDE=%~dp0..\\..\\win8sdk\\Include\\um;' |
| 385 | '%~dp0..\\..\\win8sdk\\Include\\shared;' |
[email protected] | 7cc7a1c | 2014-01-15 22:05:05 | [diff] [blame] | 386 | '%~dp0..\\..\\VC\\include;' |
| 387 | '%~dp0..\\..\\VC\\atlmfc\\include\n' |
| 388 | 'if "%1"=="/x64" goto x64\n') |
| 389 | |
| 390 | # x86. If we're Pro, then use the amd64_x86 cross (we don't support x86 |
| 391 | # host at all). |
| 392 | if pro: |
| 393 | f.write('set PATH=%~dp0..\\..\\win8sdk\\bin\\x86;' |
| 394 | '%~dp0..\\..\\VC\\bin\\amd64_x86;' |
| 395 | '%~dp0..\\..\\VC\\bin\\amd64;' # Needed for mspdb120.dll. |
| 396 | '%PATH%\n') |
| 397 | else: |
| 398 | f.write('set PATH=%~dp0..\\..\\win8sdk\\bin\\x86;' |
| 399 | '%~dp0..\\..\\VC\\bin;%PATH%\n') |
| 400 | f.write('set LIB=%~dp0..\\..\\VC\\lib;' |
[email protected] | c60ecf9 | 2014-11-14 22:51:23 | [diff] [blame] | 401 | '%~dp0..\\..\\win8sdk\\Lib\\winv6.3\\um\\x86;' |
[email protected] | 7cc7a1c | 2014-01-15 22:05:05 | [diff] [blame] | 402 | '%~dp0..\\..\\VC\\atlmfc\\lib\n' |
| 403 | 'goto :EOF\n') |
| 404 | |
| 405 | # Express does not include a native 64 bit compiler, so we have to use |
| 406 | # the x86->x64 cross. |
| 407 | if not pro: |
| 408 | # x86->x64 cross. |
| 409 | f.write(':x64\n' |
| 410 | 'set PATH=%~dp0..\\..\\win8sdk\\bin\\x64;' |
| 411 | '%~dp0..\\..\\VC\\bin\\x86_amd64;' |
[email protected] | 26cb836 | 2014-02-04 00:07:02 | [diff] [blame] | 412 | # Needed for mspdb120.dll. Must be after above though, so |
| 413 | # that cl.exe is the x86_amd64 one. |
| 414 | '%~dp0..\\..\\VC\\bin;' |
[email protected] | 7cc7a1c | 2014-01-15 22:05:05 | [diff] [blame] | 415 | '%PATH%\n') |
| 416 | else: |
| 417 | # x64 native. |
| 418 | f.write(':x64\n' |
| 419 | 'set PATH=%~dp0..\\..\\win8sdk\\bin\\x64;' |
| 420 | '%~dp0..\\..\\VC\\bin\\amd64;' |
| 421 | '%PATH%\n') |
| 422 | f.write('set LIB=%~dp0..\\..\\VC\\lib\\amd64;' |
[email protected] | c60ecf9 | 2014-11-14 22:51:23 | [diff] [blame] | 423 | '%~dp0..\\..\\win8sdk\\Lib\\winv6.3\\um\\x64;' |
[email protected] | 7cc7a1c | 2014-01-15 22:05:05 | [diff] [blame] | 424 | '%~dp0..\\..\\VC\\atlmfc\\lib\\amd64\n') |
| 425 | |
| 426 | |
[email protected] | 7175632 | 2014-03-02 22:59:57 | [diff] [blame] | 427 | def DoTreeMirror(target_dir, tree_sha1): |
| 428 | """In order to save temporary space on bots that do not have enough space to |
| 429 | download ISOs, unpack them, and copy to the target location, the whole tree |
| 430 | is uploaded as a zip to internal storage, and then mirrored here.""" |
| 431 | local_zip = DownloadUsingGsutil(tree_sha1 + '.zip') |
| 432 | sys.stdout.write('Extracting %s...\n' % local_zip) |
| 433 | sys.stdout.flush() |
| 434 | RunOrDie('7z x "%s" -y "-o%s" >nul' % (local_zip, target_dir)) |
| 435 | |
| 436 | |
[email protected] | 7cc7a1c | 2014-01-15 22:05:05 | [diff] [blame] | 437 | def main(): |
| 438 | parser = optparse.OptionParser(description=sys.modules[__name__].__doc__) |
| 439 | parser.add_option('--targetdir', metavar='DIR', |
| 440 | help='put toolchain into DIR', |
| 441 | default=os.path.join(BASEDIR, 'win_toolchain_2013')) |
| 442 | parser.add_option('--noclean', action='store_false', dest='clean', |
| 443 | help='do not remove temp files', |
| 444 | default=True) |
| 445 | parser.add_option('--local', metavar='DIR', |
| 446 | help='use downloaded files from DIR') |
| 447 | parser.add_option('--express', |
| 448 | help='use VS Express instead of Pro', action='store_true') |
[email protected] | 7175632 | 2014-03-02 22:59:57 | [diff] [blame] | 449 | parser.add_option('--sha1', |
| 450 | help='tree sha1 that can be used to mirror an internal ' |
[email protected] | 0d23d77 | 2014-03-07 20:17:49 | [diff] [blame] | 451 | 'copy (used if --use-gs)') |
| 452 | parser.add_option('--use-gs', |
[email protected] | 26cb836 | 2014-02-04 00:07:02 | [diff] [blame] | 453 | help='Use internal servers to pull isos', |
| 454 | default=bool(int(os.environ.get('CHROME_HEADLESS', 0))), |
| 455 | action='store_true') |
[email protected] | 9eabb22 | 2014-01-16 02:42:43 | [diff] [blame] | 456 | options, _ = parser.parse_args() |
[email protected] | 7cc7a1c | 2014-01-15 22:05:05 | [diff] [blame] | 457 | try: |
| 458 | target_dir = os.path.abspath(options.targetdir) |
| 459 | if os.path.exists(target_dir): |
| 460 | parser.error('%s already exists. Please [re]move it or use ' |
[email protected] | 7175632 | 2014-03-02 22:59:57 | [diff] [blame] | 461 | '--targetdir to select a different target.\n' % |
| 462 | target_dir) |
[email protected] | 7cc7a1c | 2014-01-15 22:05:05 | [diff] [blame] | 463 | # Set the working directory to 7z subdirectory. 7-zip doesn't find its |
| 464 | # codec dll very well, so this is the simplest way to make sure it runs |
| 465 | # correctly, as we don't otherwise care about working directory. |
| 466 | os.chdir(os.path.join(BASEDIR, '7z')) |
[email protected] | 0d23d77 | 2014-03-07 20:17:49 | [diff] [blame] | 467 | if options.use_gs and options.sha1: |
[email protected] | 454f2ca | 2014-03-06 17:08:24 | [diff] [blame] | 468 | options.express = False |
[email protected] | 7175632 | 2014-03-02 22:59:57 | [diff] [blame] | 469 | DoTreeMirror(target_dir, options.sha1) |
| 470 | else: |
| 471 | images = GetSourceImages(options.local, not options.express) |
| 472 | extracted = ExtractComponents(images) |
| 473 | CopyToFinalLocation(extracted, target_dir) |
| 474 | GenerateSetEnvCmd(target_dir, not options.express) |
[email protected] | 7cc7a1c | 2014-01-15 22:05:05 | [diff] [blame] | 475 | |
[email protected] | 5d17b92 | 2014-02-22 01:47:53 | [diff] [blame] | 476 | data = { |
| 477 | 'path': target_dir, |
| 478 | 'version': '2013e' if options.express else '2013', |
| 479 | 'win8sdk': os.path.join(target_dir, 'win8sdk'), |
| 480 | 'wdk': os.path.join(target_dir, 'wdk'), |
| 481 | 'runtime_dirs': [ |
| 482 | os.path.join(target_dir, 'sys64'), |
| 483 | os.path.join(target_dir, 'sys32'), |
| 484 | ], |
| 485 | } |
[email protected] | 360f032 | 2014-02-22 03:21:23 | [diff] [blame] | 486 | with open(os.path.join(target_dir, '..', 'data.json'), 'w') as f: |
[email protected] | 5d17b92 | 2014-02-22 01:47:53 | [diff] [blame] | 487 | json.dump(data, f) |
[email protected] | 7cc7a1c | 2014-01-15 22:05:05 | [diff] [blame] | 488 | finally: |
| 489 | if options.clean: |
| 490 | DeleteAllTempDirs() |
| 491 | |
| 492 | |
| 493 | if __name__ == '__main__': |
| 494 | sys.exit(main()) |