OLD | NEW |
---|---|
(Empty) | |
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 | |
9 import os | |
10 import shutil | |
11 import sys | |
12 import tempfile | |
13 import urllib2 | |
14 | |
15 | |
16 g_temp_dirs = [] | |
17 | |
18 | |
19 def TempDir(): | |
20 """Generate a temporary directory (for downloading or extracting to) and keep | |
21 track of the directory that's created for cleaning up later.""" | |
22 global g_temp_dirs | |
23 temp = tempfile.mkdtemp() | |
24 g_temp_dirs.append(temp) | |
25 return temp | |
26 | |
27 | |
28 def DeleteAllTempDirs(): | |
29 """Remove all temporary directories created by |TempDir()|.""" | |
30 global g_temp_dirs | |
31 sys.stdout.write('Cleaning up temporaries...\n') | |
32 for temp in g_temp_dirs: | |
33 # shutil.rmtree errors out on read only attributes. | |
34 os.system('rmdir /s/q "%s"' % temp) | |
35 g_temp_dirs = [] | |
36 | |
37 | |
38 def Download(url, local_path): | |
39 """Download a large-ish binary file and print some status information while | |
40 doing so.""" | |
41 sys.stdout.write('Downloading %s...' % url) | |
42 req = urllib2.urlopen(url) | |
43 content_length = int(req.headers.get('Content-Length', 0)) | |
44 bytes_read = 0 | |
45 with open(local_path, 'wb') as file: | |
46 while True: | |
47 chunk = req.read(1024 * 1024) | |
48 if not chunk: | |
49 break | |
50 bytes_read += len(chunk) | |
51 file.write(chunk) | |
52 sys.stdout.write('.') | |
53 sys.stdout.write('\n') | |
54 if content_length and content_length != bytes_read: | |
55 raise SystemExit('Got incorrect number of bytes downloading %s' % url) | |
56 | |
57 | |
58 def DownloadSDK71Iso(): | |
59 sdk7_temp_dir = TempDir() | |
60 target_path = os.path.join(sdk7_temp_dir, 'GRMSDKX_EN_DVD.iso') | |
61 Download( | |
62 ('http://download.microsoft.com/download/' | |
63 'F/1/0/F10113F5-B750-4969-A255-274341AC6BCE/GRMSDKX_EN_DVD.iso'), | |
64 target_path) | |
65 return target_path | |
66 | |
67 | |
68 def DownloadWDKIso(): | |
69 wdk_temp_dir = TempDir() | |
70 target_path = os.path.join(wdk_temp_dir, 'GRMWDK_EN_7600_1.ISO') | |
71 Download( | |
72 ('http://download.microsoft.com/download/' | |
73 '4/A/2/4A25C7D5-EFBE-4182-B6A9-AE6850409A78/GRMWDK_EN_7600_1.ISO'), | |
74 target_path) | |
75 return target_path | |
76 | |
77 | |
78 def DownloadSDKUpdate(): | |
79 sdk_update_temp_dir = TempDir() | |
80 target_path = os.path.join(sdk_update_temp_dir, 'VC-Compiler-KB2519277.exe') | |
81 Download( | |
82 ('http://download.microsoft.com/download/' | |
83 '7/5/0/75040801-126C-4591-BCE4-4CD1FD1499AA/VC-Compiler-KB2519277.exe'), | |
84 target_path) | |
85 return target_path | |
86 | |
87 | |
88 def DownloadDirectXSDK(): | |
89 dxsdk_temp_dir = TempDir() | |
90 target_path = os.path.join(dxsdk_temp_dir, 'DXSDK_Jun10.exe') | |
91 Download( | |
92 ('http://download.microsoft.com/download/' | |
93 'A/E/7/AE743F1F-632B-4809-87A9-AA1BB3458E31/DXSDK_Jun10.exe'), | |
94 target_path) | |
95 return target_path | |
96 | |
97 | |
98 def DownloadSDK8(): | |
99 """Download the Win8 SDK. This one is slightly different than the simple | |
100 ones above. There is no .ISO distribution for the Windows 8 SDK. Rather, a | |
101 tool is provided that is a download manager. This is used to download the | |
102 various .msi files to a target location. Unfortunately, this tool requires | |
103 elevation for no obvious reason even when only downloading, so this function | |
104 will trigger a UAC elevation if the script is not run from an elevated | |
105 prompt.""" | |
106 sdk_temp_dir = TempDir() | |
107 target_path = os.path.join(sdk_temp_dir, 'sdksetup.exe') | |
108 standalone_path = os.path.join(sdk_temp_dir, 'Standalone') | |
109 Download( | |
110 ('http://download.microsoft.com/download/' | |
111 'F/1/3/F1300C9C-A120-4341-90DF-8A52509B23AC/standalonesdk/sdksetup.exe'), | |
112 target_path) | |
113 sys.stdout.write( | |
114 'Running sdksetup.exe to download Win8 SDK (may request elevation)...\n') | |
115 count = 0 | |
116 while count < 5: | |
117 rc = os.system(target_path + ' /quiet ' | |
118 '/features OptionId.WindowsDesktopSoftwareDevelopmentKit ' | |
119 '/layout ' + standalone_path) | |
120 if rc == 0: | |
121 return standalone_path | |
122 break | |
123 count += 1 | |
124 sys.stdout.write('Windows 8 SDK failed to download, retrying.\n') | |
125 raise SystemExit("After multiple retries, couldn't download Win8 SDK") | |
126 | |
127 | |
128 class SourceImages(object): | |
129 def __init__(self, sdk8_path, wdk_iso, sdk7_update, sdk7_path, dxsdk_path): | |
130 self.sdk8_path = sdk8_path | |
131 self.wdk_iso = wdk_iso | |
132 self.sdk7_update = sdk7_update | |
133 self.sdk7_path = sdk7_path | |
134 self.dxsdk_path = dxsdk_path | |
135 | |
136 | |
137 def GetSourceImages(): | |
138 """Download all distribution archives for the components we need.""" | |
139 if len(sys.argv) == 2 and sys.argv[1] == 'local': | |
140 return SourceImages( | |
141 sdk8_path=r'C:\Users\Scott\Desktop\wee\Standalone', | |
142 wdk_iso=r'c:\users\scott\desktop\wee\GRMWDK_EN_7600_1.ISO', | |
143 sdk7_update=r'c:\users\scott\desktop\wee\VC-Compiler-KB2519277.exe', | |
144 sdk7_path=r'C:\Users\Scott\Desktop\wee\GRMSDKX_EN_DVD.ISO', | |
145 dxsdk_path=r'C:\Users\Scott\Desktop\wee\DXSDK_Jun10.exe') | |
146 else: | |
147 # Note that we do the Win8 SDK first so that its silly UAC prompt | |
148 # happens before the user wanders off to get coffee. | |
149 sdk8_path = DownloadSDK8() | |
150 wdk_iso = DownloadWDKIso() | |
151 sdk7_update = DownloadSDKUpdate() | |
152 sdk7_path = DownloadSDK71Iso() | |
153 dxsdk_path = DownloadDirectXSDK() | |
154 return SourceImages(sdk8_path, wdk_iso, sdk7_update, sdk7_path, dxsdk_path) | |
155 | |
156 | |
157 def ExtractIso(iso_path): | |
158 """Use 7zip to extract the contents of the given .iso (or self-extracting | |
159 .exe).""" | |
160 target_path = TempDir() | |
161 sys.stdout.write('Extracting %s...\n' % iso_path) | |
162 sz_path = os.path.join(os.path.dirname(os.path.abspath(__file__)), '7z.exe') | |
163 # TODO(scottmg): Do this (and exe) manually with python code. | |
164 if os.system( | |
165 '%s x "%s" -y "-o%s" > nul' % (sz_path, iso_path, target_path)) != 0: | |
Roger McFarlane (Chromium)
2013/01/02 22:41:31
quote the path to 7z
scottmg
2013/01/02 23:31:09
That seemed like a great idea, but wow! In doing t
| |
166 raise SystemExit("Couldn't extract %s" % iso_path) | |
167 return target_path | |
168 | |
169 | |
170 ExtractExe = ExtractIso | |
171 | |
172 | |
173 def ExtractMsi(msi_path): | |
174 """Use msiexec to extract the contents of the given .msi file.""" | |
175 sys.stdout.write('Extracting %s...\n' % msi_path) | |
176 target_path = TempDir() | |
177 if os.system( | |
178 'msiexec /a "%s" /qn TARGETDIR=%s' % (msi_path, target_path)) != 0: | |
179 raise SystemExit("Couldn't extract %s" % msi_path) | |
180 return target_path | |
181 | |
182 | |
183 class ExtractedComponents(object): | |
184 def __init__(self, | |
185 vc_x86, vc_x64, | |
186 buildtools_x86, buildtools_x64, libs_x86, libs_x64, headers, | |
187 update_x86, update_x64, | |
188 sdk_path, metro_sdk_path, | |
189 dxsdk): | |
190 self.vc_x86 = vc_x86 | |
191 self.vc_x64 = vc_x64 | |
192 self.buildtools_x86 = buildtools_x86 | |
193 self.buildtools_x64 = buildtools_x64 | |
194 self.libs_x86 = libs_x86 | |
195 self.libs_x64 = libs_x64 | |
196 self.headers = headers | |
197 self.update_x86 = update_x86 | |
198 self.update_x64 = update_x64 | |
199 self.sdk_path = sdk_path | |
200 self.metro_sdk_path = metro_sdk_path | |
201 self.dxsdk = dxsdk | |
202 | |
203 | |
204 def ExtractComponents(images): | |
205 """Given the paths to the images, extract the required parts, and return the | |
206 an object containing paths to all the pieces.""" | |
207 extracted_sdk7 = ExtractIso(images.sdk7_path) | |
208 extracted_vc_x86 = \ | |
209 ExtractMsi(os.path.join(extracted_sdk7, | |
210 r'Setup\vc_stdx86\vc_stdx86.msi')) | |
211 extracted_vc_x64 = \ | |
212 ExtractMsi(os.path.join(extracted_sdk7, | |
213 r'Setup\vc_stdamd64\vc_stdamd64.msi')) | |
214 | |
215 extracted_wdk = ExtractIso(images.wdk_iso) | |
216 extracted_buildtools_x86 = \ | |
217 ExtractMsi(os.path.join(extracted_wdk, r'WDK\buildtools_x86fre.msi')) | |
218 extracted_buildtools_x64 = \ | |
219 ExtractMsi(os.path.join(extracted_wdk, r'WDK\buildtools_x64fre.msi')) | |
220 extracted_libs_x86 = \ | |
221 ExtractMsi(os.path.join(extracted_wdk, r'WDK\libs_x86fre.msi')) | |
222 extracted_libs_x64 = \ | |
223 ExtractMsi(os.path.join(extracted_wdk, r'WDK\libs_x64fre.msi')) | |
224 extracted_headers = \ | |
225 ExtractMsi(os.path.join(extracted_wdk, r'WDK\headers.msi')) | |
226 | |
227 extracted_update = ExtractExe(images.sdk7_update) | |
228 extracted_update_x86 = \ | |
229 ExtractMsi(os.path.join(extracted_update, 'vc_stdx86.msi')) | |
230 extracted_update_x64 = \ | |
231 ExtractMsi(os.path.join(extracted_update, 'vc_stdamd64.msi')) | |
232 | |
233 sdk_msi_path = os.path.join( | |
234 images.sdk8_path, | |
235 r'Installers\Windows Software Development Kit-x86_en-us.msi') | |
236 extracted_sdk_path = ExtractMsi(sdk_msi_path) | |
237 | |
238 sdk_metro_msi_path = os.path.join( | |
239 images.sdk8_path, | |
240 'Installers', | |
241 'Windows Software Development Kit for Metro style Apps-x86_en-us.msi') | |
242 extracted_metro_sdk_path = ExtractMsi(sdk_metro_msi_path) | |
243 | |
244 extracted_dxsdk = ExtractExe(images.dxsdk_path) | |
245 | |
246 return ExtractedComponents( | |
247 vc_x86=extracted_vc_x86, | |
248 vc_x64=extracted_vc_x64, | |
249 buildtools_x86=extracted_buildtools_x86, | |
250 buildtools_x64=extracted_buildtools_x64, | |
251 libs_x86=extracted_libs_x86, | |
252 libs_x64=extracted_libs_x64, | |
253 headers=extracted_headers, | |
254 update_x86=extracted_update_x86, | |
255 update_x64=extracted_update_x64, | |
256 sdk_path=extracted_sdk_path, | |
257 metro_sdk_path=extracted_metro_sdk_path, | |
258 dxsdk=extracted_dxsdk) | |
259 | |
260 | |
261 def CopyToFinalLocation(extracted, target_dir): | |
262 """Copy all the directories we need to the target location.""" | |
263 sys.stdout.write('Pulling together required pieces...\n') | |
264 | |
265 # Note that order is important because some of the older ones are | |
266 # overwritten by updates. | |
267 from_sdk7_x86 =[ | |
268 (r'Program Files\Microsoft Visual Studio 10.0', '.'), | |
269 (r'Win\System', r'VC\bin'), | |
270 ] | |
271 PullFrom(from_sdk7_x86, extracted.vc_x86, target_dir) | |
272 | |
273 from_sdk7_x64 =[ | |
274 (r'Program Files(64)\Microsoft Visual Studio 10.0', '.'), | |
275 (r'Win\System64', r'VC\bin\amd64'), | |
276 ] | |
277 PullFrom(from_sdk7_x64, extracted.vc_x64, target_dir) | |
278 | |
279 from_vcupdate_x86 = [ | |
280 (r'Program Files\Microsoft Visual Studio 10.0', '.'), | |
281 (r'Win\System', r'VC\bin'), | |
282 ] | |
283 PullFrom(from_vcupdate_x86, extracted.update_x86, target_dir) | |
284 | |
285 from_vcupdate_x64 = [ | |
286 (r'Program Files(64)\Microsoft Visual Studio 10.0', '.'), | |
287 (r'Win\System64', r'VC\bin\amd64'), | |
288 ] | |
289 PullFrom(from_vcupdate_x64, extracted.update_x64, target_dir) | |
290 | |
291 from_sdk = [(r'Windows Kits\8.0', r'win8sdk')] | |
292 PullFrom(from_sdk, extracted.sdk_path, target_dir) | |
293 | |
294 from_metro_sdk = [(r'Windows Kits\8.0', r'win8sdk')] | |
295 PullFrom(from_sdk, extracted.metro_sdk_path, target_dir) | |
296 | |
297 from_buildtools_x86 = [ | |
298 (r'WinDDK\7600.16385.win7_wdk.100208-1538\bin\x86', r'WDK\bin'), | |
299 (r'WinDDK\7600.16385.win7_wdk.100208-1538\bin\amd64', r'WDK\bin'), | |
300 ] | |
301 PullFrom(from_buildtools_x86, extracted.buildtools_x86, target_dir) | |
302 | |
303 from_buildtools_x64 = [ | |
304 (r'WinDDK\7600.16385.win7_wdk.100208-1538\bin\amd64', r'WDK\bin'), | |
305 ] | |
306 PullFrom(from_buildtools_x64, extracted.buildtools_x64, target_dir) | |
307 | |
308 from_libs_x86 = [ | |
309 (r'WinDDK\7600.16385.win7_wdk.100208-1538\lib', r'WDK\lib'), | |
310 ] | |
311 PullFrom(from_libs_x86, extracted.libs_x86, target_dir) | |
312 | |
313 from_libs_x64 = [ | |
314 (r'WinDDK\7600.16385.win7_wdk.100208-1538\lib', r'WDK\lib'), | |
315 ] | |
316 PullFrom(from_libs_x64, extracted.libs_x64, target_dir) | |
317 | |
318 from_headers = [ | |
319 (r'WinDDK\7600.16385.win7_wdk.100208-1538\inc', r'WDK\inc'), | |
320 ] | |
321 PullFrom(from_headers, extracted.headers, target_dir) | |
322 | |
323 from_dxsdk = [ | |
324 (r'DXSDK\Include', r'DXSDK\Include'), | |
325 (r'DXSDK\Lib', r'DXSDK\Lib'), | |
326 (r'DXSDK\Redist', r'DXSDK\Redist'), | |
327 ] | |
328 PullFrom(from_dxsdk, extracted.dxsdk, target_dir) | |
329 | |
330 | |
331 def PullFrom(list_of_path_pairs, source_root, target_dir): | |
332 """Each pair in |list_of_path_pairs| is (from, to). Join the 'from' with | |
333 |source_root| and the 'to' with |target_dir| and perform a recursive copy.""" | |
334 for source, destination in list_of_path_pairs: | |
335 full_source = os.path.join(source_root, source) | |
336 full_target = os.path.join(target_dir, destination) | |
337 rc = os.system('robocopy /s "%s" "%s" >nul' % (full_source, full_target)) | |
338 if (rc & 8) != 0 or (rc & 16) != 0: | |
339 # ref: http://ss64.com/nt/robocopy-exit.html | |
340 raise SystemExit("Couldn't copy %s to %s" % (full_source, full_target)) | |
341 | |
342 | |
343 def PatchAsyncInfo(target_dir): | |
344 """Apply patch from | |
345 http://www.chromium.org/developers/how-tos/build-instructions-windows for | |
346 asyncinfo.h.""" | |
347 sys.stdout.write('Patching asyncinfo.h...\n') | |
348 asyncinfo_h_path = os.path.join( | |
349 target_dir, r'win8sdk\Include\winrt\asyncinfo.h') | |
350 with open(asyncinfo_h_path, 'rb') as f: | |
351 asyncinfo_h = f.read() | |
352 patched = asyncinfo_h.replace( | |
353 'enum class AsyncStatus {', 'enum AsyncStatus {') | |
354 with open(asyncinfo_h_path, 'wb') as f: | |
355 f.write(patched) | |
356 | |
357 | |
358 def GenerateSetEnvCmd(target_dir): | |
359 """Generate a batch file that gyp expects to exist to set up the compiler | |
360 environment. This is normally generated by a full install of the SDK, but we | |
361 do it here manually since we do not do a full install.""" | |
362 with open(os.path.join( | |
363 target_dir, r'win8sdk\bin\SetEnv.cmd'), 'w') as file: | |
364 file.write('@echo off\n') | |
365 file.write(':: Generated by tools\\win\\toolchain\\toolchain.py.\n') | |
366 # Common to x86 and x64 | |
367 file.write('set PATH=%s;%%PATH%%\n' % ( | |
368 os.path.join(target_dir, r'Common7\IDE'))) | |
369 file.write('set INCLUDE=%s;%s;%s\n' % ( | |
370 os.path.join(target_dir, r'win8sdk\Include\um'), | |
371 os.path.join(target_dir, r'win8sdk\Include\shared'), | |
372 os.path.join(target_dir, r'VC\include'))) | |
373 file.write('if "%1"=="/x64" goto x64\n') | |
374 | |
375 # x86 only. | |
376 file.write('set PATH=%s;%s;%s;%%PATH%%\n' % ( | |
377 os.path.join(target_dir, r'win8sdk\bin\x86'), | |
378 os.path.join(target_dir, r'VC\bin'), | |
379 os.path.join(target_dir, r'WDK\bin'))) | |
380 file.write('set LIB=%s;%s\n' % ( | |
381 os.path.join(target_dir, r'VC\lib'), | |
382 os.path.join(target_dir, r'win8sdk\Lib\win8\um\x86'))) | |
383 file.write('goto done\n') | |
384 | |
385 # x64 only. | |
386 file.write(':x64\n') | |
387 file.write('set PATH=%s;%s;%s;%%PATH%%\n' % ( | |
388 os.path.join(target_dir, r'win8sdk\bin\x64'), | |
389 os.path.join(target_dir, r'VC\bin\amd64'), | |
390 os.path.join(target_dir, r'WDK\bin\amd64'))) | |
391 file.write('set LIB=%s;%s\n' % ( | |
392 os.path.join(target_dir, r'VC\lib\amd64'), | |
393 os.path.join(target_dir, r'win8sdk\Lib\win8\um\x64'))) | |
394 | |
395 file.write(':done\n') | |
396 | |
397 | |
398 def GenerateTopLevelEnv(target_dir): | |
399 """Generate a batch file that sets up various environment variables that let | |
400 the Chromium build files and gyp find SDKs and tools.""" | |
401 with open(os.path.join(target_dir, r'env.bat'), 'w') as file: | |
402 file.write('@echo off\n') | |
403 file.write(':: Generated by tools\\win\\toolchain\\toolchain.py.\n') | |
404 file.write('set GYP_DEFINES=windows_sdk_path="%s" ' | |
405 'component=shared_library\n' % ( | |
406 os.path.join(target_dir, 'win8sdk'))) | |
407 file.write('set GYP_MSVS_VERSION=2010e\n') | |
408 file.write('set GYP_MSVS_OVERRIDE_PATH=%s\n' % target_dir) | |
409 file.write('set GYP_GENERATORS=ninja\n') | |
410 file.write('set WDK_DIR=%s\n' % os.path.join(target_dir, r'WDK')) | |
411 file.write('set DXSDK_DIR=%s\n' % os.path.join(target_dir, r'DXSDK')) | |
412 file.write('set WindowsSDKDir=%s\n' % | |
413 os.path.join(target_dir, r'win8sdk')) | |
414 file.write('echo Environment set for toolchain in %s.\n' % target_dir) | |
415 file.write('cd /d %s\\..\n' % target_dir) | |
416 | |
417 | |
418 def main(): | |
419 try: | |
420 target_dir = os.path.abspath('win_toolchain') | |
Roger McFarlane (Chromium)
2013/01/02 22:41:31
optional: command line param? maybe keeping curren
scottmg
2013/01/02 23:31:09
Done.
| |
421 images = GetSourceImages() | |
422 extracted = ExtractComponents(images) | |
423 CopyToFinalLocation(extracted, target_dir) | |
424 PatchAsyncInfo(target_dir) | |
425 GenerateSetEnvCmd(target_dir) | |
426 GenerateTopLevelEnv(target_dir) | |
427 finally: | |
428 DeleteAllTempDirs() | |
429 | |
430 sys.stdout.write( | |
431 '\nIn a (clean) cmd shell, you can now run\n\n' | |
432 ' %s\\env.bat\n\n' | |
433 'then\n\n' | |
434 " gclient runhooks (or gclient sync if you haven't pulled deps yet)\n" | |
435 ' ninja -C out\Debug chrome\n\n' | |
436 'Note that this script intentionally does not modify any global\n' | |
437 'settings like the registry, or system environment variables, so you\n' | |
438 'will need to run the above env.bat whenever you start a new\n' | |
439 'shell.\n\n' % target_dir) | |
440 | |
441 | |
442 if __name__ == '__main__': | |
443 main() | |
OLD | NEW |