• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1# Copyright 2012 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# Extracts a Windows toolchain suitable for building Chrome from various
6# downloadable pieces.
7
8
9import ctypes
10from optparse import OptionParser
11import os
12import shutil
13import subprocess
14import sys
15import tempfile
16import urllib2
17
18
19g_temp_dirs = []
20
21
22def GetLongPathName(path):
23  """Converts any 8dot3 names in the path to the full name."""
24  buf = ctypes.create_unicode_buffer(260)
25  size = ctypes.windll.kernel32.GetLongPathNameW(unicode(path), buf, 260)
26  if (size > 260):
27    raise SystemExit('Long form of path longer than 260 chars: %s' % path)
28  return buf.value
29
30
31def RunOrDie(command):
32  rc = subprocess.call(command, shell=True)
33  if rc != 0:
34    raise SystemExit('%s failed.' % command)
35
36
37def TempDir():
38  """Generate a temporary directory (for downloading or extracting to) and keep
39  track of the directory that's created for cleaning up later."""
40  global g_temp_dirs
41  temp = tempfile.mkdtemp()
42  g_temp_dirs.append(temp)
43  return temp
44
45
46def DeleteAllTempDirs():
47  """Remove all temporary directories created by |TempDir()|."""
48  global g_temp_dirs
49  if g_temp_dirs:
50    sys.stdout.write('Cleaning up temporaries...\n')
51  for temp in g_temp_dirs:
52    # shutil.rmtree errors out on read only attributes.
53    RunOrDie('rmdir /s/q "%s"' % temp)
54  g_temp_dirs = []
55
56
57def Download(url, local_path):
58  """Download a large-ish binary file and print some status information while
59  doing so."""
60  sys.stdout.write('Downloading %s...\n' % url)
61  req = urllib2.urlopen(url)
62  content_length = int(req.headers.get('Content-Length', 0))
63  bytes_read = 0
64  terminator = '\r' if sys.stdout.isatty() else '\n'
65  with open(local_path, 'wb') as file:
66    while True:
67      chunk = req.read(1024 * 1024)
68      if not chunk:
69        break
70      bytes_read += len(chunk)
71      file.write(chunk)
72      sys.stdout.write('... %d/%d%s' % (bytes_read, content_length, terminator))
73      sys.stdout.flush()
74  sys.stdout.write('\n')
75  if content_length and content_length != bytes_read:
76    raise SystemExit('Got incorrect number of bytes downloading %s' % url)
77
78
79def DownloadSDK71Iso():
80  sdk7_temp_dir = TempDir()
81  target_path = os.path.join(sdk7_temp_dir, 'GRMSDKX_EN_DVD.iso')
82  Download(
83      ('http://download.microsoft.com/download/'
84       'F/1/0/F10113F5-B750-4969-A255-274341AC6BCE/GRMSDKX_EN_DVD.iso'),
85      target_path)
86  return target_path
87
88
89def DownloadWDKIso():
90  wdk_temp_dir = TempDir()
91  target_path = os.path.join(wdk_temp_dir, 'GRMWDK_EN_7600_1.ISO')
92  Download(
93      ('http://download.microsoft.com/download/'
94       '4/A/2/4A25C7D5-EFBE-4182-B6A9-AE6850409A78/GRMWDK_EN_7600_1.ISO'),
95      target_path)
96  return target_path
97
98
99def DownloadSDKUpdate():
100  sdk_update_temp_dir = TempDir()
101  target_path = os.path.join(sdk_update_temp_dir, 'VC-Compiler-KB2519277.exe')
102  Download(
103      ('http://download.microsoft.com/download/'
104        '7/5/0/75040801-126C-4591-BCE4-4CD1FD1499AA/VC-Compiler-KB2519277.exe'),
105      target_path)
106  return target_path
107
108
109def DownloadDirectXSDK():
110  dxsdk_temp_dir = TempDir()
111  target_path = os.path.join(dxsdk_temp_dir, 'DXSDK_Jun10.exe')
112  Download(
113      ('http://download.microsoft.com/download/'
114       'A/E/7/AE743F1F-632B-4809-87A9-AA1BB3458E31/DXSDK_Jun10.exe'),
115      target_path)
116  return target_path
117
118
119def DownloadVS2012ExIso():
120  ex_temp_dir = TempDir()
121  target_path = os.path.join(ex_temp_dir, 'VS2012_WDX_ENU.iso')
122  Download(
123      ('http://download.microsoft.com/download/'
124       '1/F/5/1F519CC5-0B90-4EA3-8159-33BFB97EF4D9/VS2012_WDX_ENU.iso'),
125      target_path)
126  return target_path
127
128
129def DownloadSDK8():
130  """Download the Win8 SDK. This one is slightly different than the simple
131  ones above. There is no .ISO distribution for the Windows 8 SDK. Rather, a
132  tool is provided that is a download manager. This is used to download the
133  various .msi files to a target location. Unfortunately, this tool requires
134  elevation for no obvious reason even when only downloading, so this function
135  will trigger a UAC elevation if the script is not run from an elevated
136  prompt."""
137  # Use the long path name here because because 8dot3 names don't seem to work.
138  sdk_temp_dir = GetLongPathName(TempDir())
139  target_path = os.path.join(sdk_temp_dir, 'sdksetup.exe')
140  standalone_path = os.path.join(sdk_temp_dir, 'Standalone')
141  Download(
142      ('http://download.microsoft.com/download/'
143       'F/1/3/F1300C9C-A120-4341-90DF-8A52509B23AC/standalonesdk/sdksetup.exe'),
144      target_path)
145  sys.stdout.write(
146      'Running sdksetup.exe to download Win8 SDK (may request elevation)...\n')
147  count = 0
148  while count < 5:
149    rc = os.system(target_path + ' /quiet '
150                   '/features OptionId.WindowsDesktopSoftwareDevelopmentKit '
151                   '/layout ' + standalone_path)
152    if rc == 0:
153      return standalone_path
154    count += 1
155    sys.stdout.write('Windows 8 SDK failed to download, retrying.\n')
156  raise SystemExit("After multiple retries, couldn't download Win8 SDK")
157
158
159def DownloadVS2012Update3():
160  """Download Update3 to VS2012. See notes in DownloadSDK8."""
161  update3_dir = TempDir()
162  target_path = os.path.join(update3_dir, 'VS2012.3.iso')
163  Download(
164      ('http://download.microsoft.com/download/'
165       'D/4/8/D48D1AC2-A297-4C9E-A9D0-A218E6609F06/VS2012.3.iso'),
166      target_path)
167  return target_path
168
169
170class SourceImages2010(object):
171  def __init__(self, sdk8_path, wdk_iso, sdk7_update, sdk7_path, dxsdk_path):
172    self.sdk8_path = sdk8_path
173    self.wdk_iso = wdk_iso
174    self.sdk7_update = sdk7_update
175    self.sdk7_path = sdk7_path
176    self.dxsdk_path = dxsdk_path
177
178
179def GetSourceImages2010(local_dir):
180  """Download all distribution archives for the components we need."""
181  if local_dir:
182    return SourceImages2010(
183        sdk8_path=os.path.join(local_dir, 'Standalone'),
184        wdk_iso=os.path.join(local_dir, 'GRMWDK_EN_7600_1.ISO'),
185        sdk7_update=os.path.join(local_dir, 'VC-Compiler-KB2519277.exe'),
186        sdk7_path=os.path.join(local_dir, 'GRMSDKX_EN_DVD.ISO'),
187        dxsdk_path=os.path.join(local_dir, 'DXSDK_Jun10.exe'))
188  else:
189    # Note that we do the Win8 SDK first so that its silly UAC prompt
190    # happens before the user wanders off to get coffee.
191    sdk8_path = DownloadSDK8()
192    wdk_iso = DownloadWDKIso()
193    sdk7_update = DownloadSDKUpdate()
194    sdk7_path = DownloadSDK71Iso()
195    dxsdk_path = DownloadDirectXSDK()
196    return SourceImages2010(
197        sdk8_path, wdk_iso, sdk7_update, sdk7_path, dxsdk_path)
198
199
200class SourceImages2012():
201  def __init__(self, ex_path, update_path, wdk_iso):
202    self.ex_path = ex_path
203    self.update_path = update_path
204    self.wdk_iso = wdk_iso
205
206
207def GetSourceImages2012(local_dir):
208  """Download all distribution archives for the components we need."""
209  if local_dir:
210    return SourceImages2012(
211        ex_path=os.path.join(local_dir, 'VS2012_WDX_ENU.iso'),
212        update_path=os.path.join(local_dir, 'VS2012.3.iso'),
213        wdk_iso=os.path.join(local_dir, 'GRMWDK_EN_7600_1.ISO'))
214  else:
215    ex_path = DownloadVS2012ExIso()
216    wdk_iso = DownloadWDKIso()
217    update_path = DownloadVS2012Update3()
218    return SourceImages2012(
219        ex_path=ex_path,
220        update_path=update_path,
221        wdk_iso=wdk_iso)
222
223
224def ExtractIso(iso_path):
225  """Use 7zip to extract the contents of the given .iso (or self-extracting
226  .exe)."""
227  target_path = TempDir()
228  sys.stdout.write('Extracting %s...\n' % iso_path)
229  sys.stdout.flush()
230  # TODO(scottmg): Do this (and exe) manually with python code.
231  # Note that at the beginning of main() we set the working directory to 7z's
232  # location.
233  RunOrDie('7z x "%s" -y "-o%s" >nul' % (iso_path, target_path))
234  return target_path
235
236
237ExtractExe = ExtractIso
238
239
240def ExtractMsi(msi_path):
241  """Use msiexec to extract the contents of the given .msi file."""
242  sys.stdout.write('Extracting %s...\n' % msi_path)
243  target_path = TempDir()
244  RunOrDie('msiexec /a "%s" /qn TARGETDIR="%s"' % (msi_path, target_path))
245  return target_path
246
247
248class ExtractedComponents2010(object):
249  def __init__(self,
250      vc_x86, vc_x64,
251      buildtools_x86, buildtools_x64, libs_x86, libs_x64, headers,
252      update_x86, update_x64,
253      sdk_path, metro_sdk_path,
254      dxsdk):
255    self.vc_x86 = vc_x86
256    self.vc_x64 = vc_x64
257    self.buildtools_x86 = buildtools_x86
258    self.buildtools_x64 = buildtools_x64
259    self.libs_x86 = libs_x86
260    self.libs_x64 = libs_x64
261    self.headers = headers
262    self.update_x86 = update_x86
263    self.update_x64 = update_x64
264    self.sdk_path = sdk_path
265    self.metro_sdk_path = metro_sdk_path
266    self.dxsdk = dxsdk
267
268
269def ExtractComponents2010(images):
270  """Given the paths to the images, extract the required parts, and return
271  an object containing paths to all the pieces."""
272  extracted_sdk7 = ExtractIso(images.sdk7_path)
273  extracted_vc_x86 = \
274      ExtractMsi(os.path.join(extracted_sdk7,
275                              r'Setup\vc_stdx86\vc_stdx86.msi'))
276  extracted_vc_x64 = \
277      ExtractMsi(os.path.join(extracted_sdk7,
278                              r'Setup\vc_stdamd64\vc_stdamd64.msi'))
279
280  extracted_wdk = ExtractIso(images.wdk_iso)
281  extracted_buildtools_x86 = \
282      ExtractMsi(os.path.join(extracted_wdk, r'WDK\buildtools_x86fre.msi'))
283  extracted_buildtools_x64 = \
284      ExtractMsi(os.path.join(extracted_wdk, r'WDK\buildtools_x64fre.msi'))
285  extracted_libs_x86 = \
286      ExtractMsi(os.path.join(extracted_wdk, r'WDK\libs_x86fre.msi'))
287  extracted_libs_x64 = \
288      ExtractMsi(os.path.join(extracted_wdk, r'WDK\libs_x64fre.msi'))
289  extracted_headers = \
290      ExtractMsi(os.path.join(extracted_wdk, r'WDK\headers.msi'))
291
292  extracted_update = ExtractExe(images.sdk7_update)
293  extracted_update_x86 = \
294      ExtractMsi(os.path.join(extracted_update, 'vc_stdx86.msi'))
295  extracted_update_x64 = \
296      ExtractMsi(os.path.join(extracted_update, 'vc_stdamd64.msi'))
297
298  sdk_msi_path = os.path.join(
299      images.sdk8_path,
300      r'Installers\Windows Software Development Kit-x86_en-us.msi')
301  extracted_sdk_path = ExtractMsi(sdk_msi_path)
302
303  sdk_metro_msi_path = os.path.join(
304      images.sdk8_path,
305      'Installers',
306      'Windows Software Development Kit for Metro style Apps-x86_en-us.msi')
307  extracted_metro_sdk_path = ExtractMsi(sdk_metro_msi_path)
308
309  extracted_dxsdk = ExtractExe(images.dxsdk_path)
310
311  return ExtractedComponents2010(
312      vc_x86=extracted_vc_x86,
313      vc_x64=extracted_vc_x64,
314      buildtools_x86=extracted_buildtools_x86,
315      buildtools_x64=extracted_buildtools_x64,
316      libs_x86=extracted_libs_x86,
317      libs_x64=extracted_libs_x64,
318      headers=extracted_headers,
319      update_x86=extracted_update_x86,
320      update_x64=extracted_update_x64,
321      sdk_path=extracted_sdk_path,
322      metro_sdk_path=extracted_metro_sdk_path,
323      dxsdk=extracted_dxsdk)
324
325
326class ExtractedComponents2012(object):
327  def __init__(self,
328               vc_x86, vc_x86_res, librarycore,
329               vc_x86_update, vc_x86_res_update, librarycore_update,
330               sdk_path, metro_sdk_path,
331               buildtools_x86, buildtools_x64, libs_x86, libs_x64, headers):
332    self.vc_x86 = vc_x86
333    self.vc_x86_res = vc_x86_res
334    self.librarycore = librarycore
335    self.vc_x86_update = vc_x86_update
336    self.vc_x86_res_update = vc_x86_res_update
337    self.librarycore_update = librarycore_update
338    self.buildtools_x86 = buildtools_x86
339    self.buildtools_x64 = buildtools_x64
340    self.libs_x86 = libs_x86
341    self.libs_x64 = libs_x64
342    self.headers = headers
343    self.sdk_path = sdk_path
344    self.metro_sdk_path = metro_sdk_path
345
346
347def ExtractComponents2012(images):
348  """Given the paths to the images, extract the required parts and return an
349  object containing paths to all the pieces."""
350  extracted_ex = ExtractIso(images.ex_path)
351
352  extracted_compilercore = ExtractMsi(os.path.join(
353      extracted_ex,
354      r'packages\vc_compilerCore86\vc_compilerCore86.msi'))
355
356  extracted_compilercore_res = ExtractMsi(os.path.join(
357      extracted_ex,
358      r'packages\vc_compilerCore86res\vc_compilerCore86res.msi'))
359
360  extracted_librarycore = ExtractMsi(os.path.join(
361      extracted_ex,
362      r'packages\vc_librarycore86\vc_librarycore86.msi'))
363
364  extracted_wdk = ExtractIso(images.wdk_iso)
365  extracted_buildtools_x86 = \
366      ExtractMsi(os.path.join(extracted_wdk, r'WDK\buildtools_x86fre.msi'))
367  extracted_buildtools_x64 = \
368      ExtractMsi(os.path.join(extracted_wdk, r'WDK\buildtools_x64fre.msi'))
369  extracted_libs_x86 = \
370      ExtractMsi(os.path.join(extracted_wdk, r'WDK\libs_x86fre.msi'))
371  extracted_libs_x64 = \
372      ExtractMsi(os.path.join(extracted_wdk, r'WDK\libs_x64fre.msi'))
373  extracted_headers = \
374      ExtractMsi(os.path.join(extracted_wdk, r'WDK\headers.msi'))
375
376  sdk_msi_path = os.path.join(
377      extracted_ex, 'packages', 'Windows_SDK',
378      r'Windows Software Development Kit-x86_en-us.msi')
379  extracted_sdk_path = ExtractMsi(sdk_msi_path)
380
381  sdk_metro_msi_path = os.path.join(
382      extracted_ex, 'packages', 'Windows_SDK',
383      'Windows Software Development Kit for Metro style Apps-x86_en-us.msi')
384  extracted_metro_sdk_path = ExtractMsi(sdk_metro_msi_path)
385
386  extracted_update = ExtractIso(images.update_path)
387
388  extracted_compilercore_update = ExtractMsi(os.path.join(
389      extracted_update, r'packages\vc_compilercore86\vc_compilercore86.msi'))
390
391  extracted_compilercore_res_update = ExtractMsi(os.path.join(
392      extracted_update,
393      r'packages\vc_compilercore86res\enu\vc_compilercore86res.msi'))
394
395  extracted_librarycore_update = ExtractMsi(os.path.join(
396      extracted_update, r'packages\vc_librarycore86\vc_librarycore86.msi'))
397
398  return ExtractedComponents2012(
399      vc_x86=extracted_compilercore,
400      vc_x86_res=extracted_compilercore_res,
401      librarycore=extracted_librarycore,
402      vc_x86_update=extracted_compilercore_update,
403      vc_x86_res_update=extracted_compilercore_res_update,
404      librarycore_update=extracted_compilercore_update,
405      sdk_path=extracted_sdk_path,
406      metro_sdk_path=extracted_metro_sdk_path,
407      buildtools_x86=extracted_buildtools_x86,
408      buildtools_x64=extracted_buildtools_x64,
409      libs_x86=extracted_libs_x86,
410      libs_x64=extracted_libs_x64,
411      headers=extracted_headers)
412
413
414def PullFrom(list_of_path_pairs, source_root, target_dir):
415  """Each pair in |list_of_path_pairs| is (from, to). Join the 'from' with
416  |source_root| and the 'to' with |target_dir| and perform a recursive copy."""
417  for source, destination in list_of_path_pairs:
418    full_source = os.path.join(source_root, source)
419    full_target = os.path.join(target_dir, destination)
420    rc = os.system('robocopy /s "%s" "%s" >nul' % (full_source, full_target))
421    if (rc & 8) != 0 or (rc & 16) != 0:
422      # ref: http://ss64.com/nt/robocopy-exit.html
423      raise SystemExit("Couldn't copy %s to %s" % (full_source, full_target))
424
425
426def CopyToFinalLocation2010(extracted, target_dir):
427  """Copy all the directories we need to the target location."""
428  sys.stdout.write('Pulling together required pieces...\n')
429
430  # Note that order is important because some of the older ones are
431  # overwritten by updates.
432  from_sdk = [(r'Windows Kits\8.0', r'win8sdk')]
433  PullFrom(from_sdk, extracted.sdk_path, target_dir)
434
435  from_metro_sdk = [(r'Windows Kits\8.0', r'win8sdk')]
436  PullFrom(from_sdk, extracted.metro_sdk_path, target_dir)
437
438  from_buildtools_x86 = [
439      (r'WinDDK\7600.16385.win7_wdk.100208-1538\bin\x86', r'WDK\bin'),
440      ]
441  PullFrom(from_buildtools_x86, extracted.buildtools_x86, target_dir)
442
443  from_buildtools_x64 = [
444      (r'WinDDK\7600.16385.win7_wdk.100208-1538\bin\amd64', r'WDK\bin'),
445      ]
446  PullFrom(from_buildtools_x64, extracted.buildtools_x64, target_dir)
447
448  from_libs_x86 = [
449      (r'WinDDK\7600.16385.win7_wdk.100208-1538\lib', r'WDK\lib'),
450      ]
451  PullFrom(from_libs_x86, extracted.libs_x86, target_dir)
452
453  from_libs_x64 = [
454      (r'WinDDK\7600.16385.win7_wdk.100208-1538\lib', r'WDK\lib'),
455      ]
456  PullFrom(from_libs_x64, extracted.libs_x64, target_dir)
457
458  from_headers = [
459      (r'WinDDK\7600.16385.win7_wdk.100208-1538\inc', r'WDK\inc'),
460      ]
461  PullFrom(from_headers, extracted.headers, target_dir)
462
463  # The compiler update to get the SP1 compiler is a bit of a mess. See
464  # http://goo.gl/n1DeO. The summary is that update for the standalone compiler
465  # binary installs a broken set of headers. So, add an empty ammintrin.h since
466  # we don't actually need the contents of it (for Chromium).
467
468  from_sdk7_x86 = [
469      (r'Program Files\Microsoft Visual Studio 10.0', '.'),
470      (r'Win\System', r'VC\bin'),
471      ]
472  PullFrom(from_sdk7_x86, extracted.vc_x86, target_dir)
473
474  from_sdk7_x64 =[
475      (r'Program Files(64)\Microsoft Visual Studio 10.0', '.'),
476      (r'Win\System64', r'VC\bin\amd64'),
477      ]
478  PullFrom(from_sdk7_x64, extracted.vc_x64, target_dir)
479
480  from_vcupdate_x86 = [
481      (r'Program Files\Microsoft Visual Studio 10.0', '.'),
482      (r'Win\System', r'VC\bin'),
483      ]
484  PullFrom(from_vcupdate_x86, extracted.update_x86, target_dir)
485
486  from_vcupdate_x64 = [
487      (r'Program Files(64)\Microsoft Visual Studio 10.0', '.'),
488      (r'Win\System64', r'VC\bin\amd64'),
489      ]
490  PullFrom(from_vcupdate_x64, extracted.update_x64, target_dir)
491
492  sys.stdout.write('Stubbing ammintrin.h...\n')
493  open(os.path.join(target_dir, r'VC\include\ammintrin.h'), 'w').close()
494
495  from_dxsdk = [
496      (r'DXSDK\Include', r'DXSDK\Include'),
497      (r'DXSDK\Lib', r'DXSDK\Lib'),
498      (r'DXSDK\Redist', r'DXSDK\Redist'),
499      ]
500  PullFrom(from_dxsdk, extracted.dxsdk, target_dir)
501
502
503def CopyToFinalLocation2012(extracted, target_dir):
504  """Copy all directories we need to the target location."""
505  sys.stdout.write('Pulling together required pieces...\n')
506
507  # Note that order is important because some of the older ones are
508  # overwritten by updates.
509  from_sdk = [(r'Windows Kits\8.0', r'win8sdk')]
510  PullFrom(from_sdk, extracted.sdk_path, target_dir)
511
512  from_metro_sdk = [(r'Windows Kits\8.0', r'win8sdk')]
513  PullFrom(from_sdk, extracted.metro_sdk_path, target_dir)
514
515  # Stock compiler.
516  from_compiler = [(r'Program Files\Microsoft Visual Studio 11.0', '.')]
517  PullFrom(from_compiler, extracted.vc_x86, target_dir)
518
519  from_compiler_res = [(r'Program Files\Microsoft Visual Studio 11.0', '.')]
520  PullFrom(from_compiler_res, extracted.vc_x86_res, target_dir)
521
522  from_library = [(r'Program Files\Microsoft Visual Studio 11.0', '.')]
523  PullFrom(from_library, extracted.librarycore, target_dir)
524
525  # WDK.
526  from_buildtools_x86 = [
527      (r'WinDDK\7600.16385.win7_wdk.100208-1538\bin\x86', r'WDK\bin'),
528      ]
529  PullFrom(from_buildtools_x86, extracted.buildtools_x86, target_dir)
530
531  from_buildtools_x64 = [
532      (r'WinDDK\7600.16385.win7_wdk.100208-1538\bin\amd64', r'WDK\bin'),
533      ]
534  PullFrom(from_buildtools_x64, extracted.buildtools_x64, target_dir)
535
536  from_libs_x86 = [(r'WinDDK\7600.16385.win7_wdk.100208-1538\lib', r'WDK\lib')]
537  PullFrom(from_libs_x86, extracted.libs_x86, target_dir)
538
539  from_libs_x64 = [(r'WinDDK\7600.16385.win7_wdk.100208-1538\lib', r'WDK\lib')]
540  PullFrom(from_libs_x64, extracted.libs_x64, target_dir)
541
542  from_headers = [(r'WinDDK\7600.16385.win7_wdk.100208-1538\inc', r'WDK\inc')]
543  PullFrom(from_headers, extracted.headers, target_dir)
544
545  # Update bits.
546  from_compiler = [(r'Program Files\Microsoft Visual Studio 11.0', '.')]
547  PullFrom(from_compiler, extracted.vc_x86_update, target_dir)
548
549  from_compiler_res = [(r'Program Files\Microsoft Visual Studio 11.0', '.')]
550  PullFrom(from_compiler_res, extracted.vc_x86_res_update, target_dir)
551
552  from_library = [(r'Program Files\Microsoft Visual Studio 11.0', '.')]
553  PullFrom(from_library, extracted.librarycore_update, target_dir)
554
555
556def PatchAsyncInfo(target_dir):
557  """Apply patch from
558  http://www.chromium.org/developers/how-tos/build-instructions-windows for
559  asyncinfo.h."""
560  # This is only required for the 2010 compiler.
561  sys.stdout.write('Patching asyncinfo.h...\n')
562  asyncinfo_h_path = os.path.join(
563      target_dir, r'win8sdk\Include\winrt\asyncinfo.h')
564  with open(asyncinfo_h_path, 'rb') as f:
565    asyncinfo_h = f.read()
566  patched = asyncinfo_h.replace(
567      'enum class AsyncStatus {', 'enum AsyncStatus {')
568  with open(asyncinfo_h_path, 'wb') as f:
569    f.write(patched)
570
571
572def GenerateSetEnvCmd(target_dir, vsversion):
573  """Generate a batch file that gyp expects to exist to set up the compiler
574  environment. This is normally generated by a full install of the SDK, but we
575  do it here manually since we do not do a full install."""
576  with open(os.path.join(
577        target_dir, r'win8sdk\bin\SetEnv.cmd'), 'w') as file:
578    file.write('@echo off\n')
579    file.write(':: Generated by tools\\win\\toolchain\\toolchain.py.\n')
580    file.write(':: Targeting VS%s.\n' % vsversion)
581    # Common to x86 and x64
582    file.write('set PATH=%s;%%PATH%%\n' % (
583        os.path.join(target_dir, r'Common7\IDE')))
584    file.write('set INCLUDE=%s;%s;%s\n' % (
585        os.path.join(target_dir, r'win8sdk\Include\um'),
586        os.path.join(target_dir, r'win8sdk\Include\shared'),
587        os.path.join(target_dir, r'VC\include')))
588    file.write('if "%1"=="/x64" goto x64\n')
589
590    # x86 only.
591    file.write('set PATH=%s;%s;%s;%%PATH%%\n' % (
592        os.path.join(target_dir, r'win8sdk\bin\x86'),
593        os.path.join(target_dir, r'VC\bin'),
594        os.path.join(target_dir, r'WDK\bin')))
595    file.write('set LIB=%s;%s\n' % (
596        os.path.join(target_dir, r'VC\lib'),
597        os.path.join(target_dir, r'win8sdk\Lib\win8\um\x86')))
598    file.write('goto done\n')
599
600    # Unfortunately, 2012 Express does not include a native 64 bit compiler,
601    # so we have to use the x86->x64 cross.
602    if vsversion == '2012':
603      # x64 only.
604      file.write(':x64\n')
605      file.write('set PATH=%s;%s;%s;%%PATH%%\n' % (
606          os.path.join(target_dir, r'win8sdk\bin\x64'),
607          os.path.join(target_dir, r'VC\bin\x86_amd64'),
608          os.path.join(target_dir, r'WDK\bin\amd64')))
609      file.write('set LIB=%s;%s\n' % (
610          os.path.join(target_dir, r'VC\lib\amd64'),
611          os.path.join(target_dir, r'win8sdk\Lib\win8\um\x64')))
612    else:
613      # x64 only.
614      file.write(':x64\n')
615      file.write('set PATH=%s;%s;%s;%%PATH%%\n' % (
616          os.path.join(target_dir, r'win8sdk\bin\x64'),
617          os.path.join(target_dir, r'VC\bin\amd64'),
618          os.path.join(target_dir, r'WDK\bin\amd64')))
619      file.write('set LIB=%s;%s\n' % (
620          os.path.join(target_dir, r'VC\lib\amd64'),
621          os.path.join(target_dir, r'win8sdk\Lib\win8\um\x64')))
622
623    file.write(':done\n')
624
625
626def GenerateTopLevelEnv(target_dir, vsversion):
627  """Generate a batch file that sets up various environment variables that let
628  the Chromium build files and gyp find SDKs and tools."""
629  with open(os.path.join(target_dir, r'env.bat'), 'w') as file:
630    file.write('@echo off\n')
631    file.write(':: Generated by tools\\win\\toolchain\\toolchain.py.\n')
632    file.write(':: Targeting VS%s.\n' % vsversion)
633    file.write('set GYP_DEFINES=windows_sdk_path="%s" '
634                'component=shared_library\n' % (
635                    os.path.join(target_dir, 'win8sdk')))
636    file.write('set GYP_MSVS_VERSION=%se\n' % vsversion)
637    file.write('set GYP_MSVS_OVERRIDE_PATH=%s\n' % target_dir)
638    file.write('set GYP_GENERATORS=ninja\n')
639    file.write('set GYP_PARALLEL=1\n')
640    file.write('set WDK_DIR=%s\n' % os.path.join(target_dir, r'WDK'))
641    if vsversion == '2010':
642      file.write('set DXSDK_DIR=%s\n' % os.path.join(target_dir, r'DXSDK'))
643    file.write('set WindowsSDKDir=%s\n' %
644        os.path.join(target_dir, r'win8sdk'))
645    if vsversion == '2012':
646      # TODO: For 2010 too.
647      base = os.path.join(target_dir, r'VC\redist')
648      paths = [
649          r'Debug_NonRedist\x64\Microsoft.VC110.DebugCRT',
650          r'Debug_NonRedist\x86\Microsoft.VC110.DebugCRT',
651          r'x64\Microsoft.VC110.CRT',
652          r'x86\Microsoft.VC110.CRT',
653        ]
654      additions = ';'.join(os.path.join(base, x) for x in paths)
655      file.write('set PATH=%s;%%PATH%%\n' % additions)
656    file.write('echo Environment set for toolchain in %s.\n' % target_dir)
657    file.write('cd /d %s\\..\n' % target_dir)
658
659
660def main():
661  parser = OptionParser()
662  parser.add_option('--targetdir', metavar='DIR',
663                    help='put toolchain into DIR',
664                    default=os.path.abspath('win_toolchain'))
665  parser.add_option('--vsversion', metavar='VSVERSION',
666                    help='select VS version: 2010 or 2012', default='2010')
667  parser.add_option('--noclean', action='store_false', dest='clean',
668                    help='do not remove temp files',
669                    default=True)
670  parser.add_option('--local', metavar='DIR',
671                    help='use downloaded files from DIR')
672  options, args = parser.parse_args()
673  try:
674    target_dir = os.path.abspath(options.targetdir)
675    if os.path.exists(target_dir):
676      sys.stderr.write('%s already exists. Please [re]move it or use '
677                       '--targetdir to select a different target.\n' %
678                       target_dir)
679      return 1
680    # Set the working directory to 7z subdirectory. 7-zip doesn't find its
681    # codec dll very well, so this is the simplest way to make sure it runs
682    # correctly, as we don't otherwise care about working directory.
683    os.chdir(os.path.join(os.path.dirname(os.path.abspath(__file__)), '7z'))
684    assert options.vsversion in ('2010', '2012')
685    if options.vsversion == '2012':
686      images = GetSourceImages2012(options.local)
687      extracted = ExtractComponents2012(images)
688      CopyToFinalLocation2012(extracted, target_dir)
689    else:
690      images = GetSourceImages2010(options.local)
691      extracted = ExtractComponents2010(images)
692      CopyToFinalLocation2010(extracted, target_dir)
693      PatchAsyncInfo(target_dir)
694
695    GenerateSetEnvCmd(target_dir, options.vsversion)
696    GenerateTopLevelEnv(target_dir, options.vsversion)
697  finally:
698    if options.clean:
699      DeleteAllTempDirs()
700
701  sys.stdout.write(
702      '\nIn a (clean) cmd shell, you can now run\n\n'
703      '  %s\\env.bat\n\n'
704      'then\n\n'
705      "  gclient runhooks (or gclient sync if you haven't pulled deps yet)\n"
706      '  ninja -C out\Debug chrome\n\n'
707      'Note that this script intentionally does not modify any global\n'
708      'settings like the registry, or system environment variables, so you\n'
709      'will need to run the above env.bat whenever you start a new\n'
710      'shell.\n\n' % target_dir)
711
712
713if __name__ == '__main__':
714  main()
715