Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(650)

Side by Side Diff: win_toolchain/toolchain2013.py

Issue 135933002: Automatic Windows toolchain (Closed) Base URL: https://chromium.googlesource.com/chromium/tools/depot_tools.git@master
Patch Set: opt-in for now Created 6 years, 11 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
OLDNEW
(Empty)
1 # Copyright 2013 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 VS2013 toolchain from various downloadable pieces.
M-A Ruel 2014/01/12 16:39:13 comment -> docstring
scottmg 2014/01/13 18:21:25 Done.
6
7
8 import ctypes
9 from optparse import OptionParser
10 import os
11 import shutil
12 import subprocess
13 import sys
14 import tempfile
15 import urllib2
16
17
18 g_temp_dirs = []
19
20
21 def GetLongPathName(path):
22 """Converts any 8dot3 names in the path to the full name."""
23 buf = ctypes.create_unicode_buffer(260)
24 size = ctypes.windll.kernel32.GetLongPathNameW(unicode(path), buf, 260)
25 if (size > 260):
26 raise SystemExit('Long form of path longer than 260 chars: %s' % path)
27 return buf.value
28
29
30 def RunOrDie(command):
31 rc = subprocess.call(command, shell=True)
32 if rc != 0:
33 raise SystemExit('%s failed.' % command)
34
35
36 def TempDir():
37 """Generate a temporary directory (for downloading or extracting to) and keep
M-A Ruel 2014/01/12 16:39:13 Generates
scottmg 2014/01/13 18:21:25 Done.
38 track of the directory that's created for cleaning up later."""
39 global g_temp_dirs
40 temp = tempfile.mkdtemp()
41 g_temp_dirs.append(temp)
42 return temp
43
44
45 def DeleteAllTempDirs():
46 """Remove all temporary directories created by |TempDir()|."""
M-A Ruel 2014/01/12 16:39:13 Removes Imperative everywhere
scottmg 2014/01/13 18:21:25 Done.
47 global g_temp_dirs
48 if g_temp_dirs:
49 sys.stdout.write('Cleaning up temporaries...\n')
50 for temp in g_temp_dirs:
51 # shutil.rmtree errors out on read only attributes.
52 RunOrDie('rmdir /s/q "%s"' % temp)
53 g_temp_dirs = []
54
55
56 def GetIsoUrl(pro):
57 """Get the .iso path."""
M-A Ruel 2014/01/12 16:39:13 \n\nIf pro is False, downloads the express edition
scottmg 2014/01/13 18:21:25 Done.
58 prefix = 'http://download.microsoft.com/download/'
59 if pro:
60 return (prefix +
61 'A/F/1/AF128362-A6A8-4DB3-A39A-C348086472CC/VS2013_RTM_PRO_ENU.iso')
62 else:
63 return (prefix +
64 '7/2/E/72E0F986-D247-4289-B9DC-C4FB07374894/VS2013_RTM_DskExp_ENU.iso')
65
66
67 def Download(url, local_path):
68 """Download a large-ish binary file and print some status information while
69 doing so."""
70 sys.stdout.write('Downloading %s...\n' % url)
71 req = urllib2.urlopen(url)
72 content_length = int(req.headers.get('Content-Length', 0))
73 bytes_read = 0
M-A Ruel 2014/01/12 16:39:13 0L otherwise it'll overflow on python 32 bits on 2
scottmg 2014/01/13 18:21:25 Done.
74 terminator = '\r' if sys.stdout.isatty() else '\n'
75 with open(local_path, 'wb') as file:
76 while True:
77 chunk = req.read(1024 * 1024)
78 if not chunk:
79 break
80 bytes_read += len(chunk)
81 file.write(chunk)
82 sys.stdout.write('... %d/%d%s' % (bytes_read, content_length, terminator))
83 sys.stdout.flush()
84 sys.stdout.write('\n')
85 if content_length and content_length != bytes_read:
86 raise SystemExit('Got incorrect number of bytes downloading %s' % url)
87
88
89 def ExtractIso(iso_path):
90 """Use 7zip to extract the contents of the given .iso (or self-extracting
91 .exe)."""
92 target_path = TempDir()
93 sys.stdout.write('Extracting %s...\n' % iso_path)
94 sys.stdout.flush()
95 # TODO(scottmg): Do this (and exe) manually with python code.
96 # Note that at the beginning of main() we set the working directory to 7z's
97 # location.
98 RunOrDie('7z x "%s" -y "-o%s" >nul' % (iso_path, target_path))
99 return target_path
100
101
102 ExtractExe = ExtractIso
M-A Ruel 2014/01/12 16:39:13 Why?
scottmg 2014/01/13 18:21:25 Oops, removed. It was necessary in the 2010/2012 v
103
104
105 def ExtractMsi(msi_path):
106 """Use msiexec to extract the contents of the given .msi file."""
107 sys.stdout.write('Extracting %s...\n' % msi_path)
108 target_path = TempDir()
109 RunOrDie('msiexec /a "%s" /qn TARGETDIR="%s"' % (msi_path, target_path))
110 return target_path
111
112
113 def DownloadMainIso(url):
114 temp_dir = TempDir()
115 target_path = os.path.join(temp_dir, os.path.basename(url))
116 Download(url, target_path)
117 return target_path
118
119
120 def GetSourceImage(local_dir, pro):
121 url = GetIsoUrl(pro)
122 if local_dir:
123 return os.path.join(local_dir, os.path.basename(url))
124 else:
125 return DownloadMainIso(url)
126
127
128 def ExtractMsiList(iso_dir, packages):
129 results = []
130 for (package, skippable) in packages:
M-A Ruel 2014/01/12 16:39:13 I'd prefer 'required' to 'skippable'. Also a docst
scottmg 2014/01/13 18:21:25 Done.
131 path_to_package = os.path.join(iso_dir, 'packages', package)
132 if not os.path.exists(path_to_package) and skippable:
133 sys.stdout.write('Pro-only %s skipped.\n' % package)
134 continue
135 results.append(ExtractMsi(path_to_package))
136 return results
137
138
139 def ExtractComponents(image):
140 extracted_iso = ExtractIso(image)
M-A Ruel 2014/01/12 16:39:13 Could you just extract the files needed? It'd save
scottmg 2014/01/13 18:21:25 I tried this thinking it sounded like a good idea
141 results = ExtractMsiList(extracted_iso, [
M-A Ruel 2014/01/12 16:39:13 Create the list as a named variable, then call the
scottmg 2014/01/13 18:21:25 Done.
142 (r'vcRuntimeAdditional_amd64\vc_runtimeAdditional_x64.msi', False),
143 (r'vcRuntimeAdditional_x86\vc_runtimeAdditional_x86.msi', False),
144 (r'vcRuntimeDebug_amd64\vc_runtimeDebug_x64.msi', False),
145 (r'vcRuntimeDebug_x86\vc_runtimeDebug_x86.msi', False),
146 (r'vcRuntimeMinimum_amd64\vc_runtimeMinimum_x64.msi', False),
147 (r'vcRuntimeMinimum_x86\vc_runtimeMinimum_x86.msi', False),
148 (r'vc_compilerCore86\vc_compilerCore86.msi', False),
149 (r'vc_compilerCore86res\vc_compilerCore86res.msi', False),
150 (r'vc_compilerx64nat\vc_compilerx64nat.msi', True),
151 (r'vc_compilerx64natres\vc_compilerx64natres.msi', True),
152 (r'vc_compilerx64x86\vc_compilerx64x86.msi', True),
153 (r'vc_compilerx64x86res\vc_compilerx64x86res.msi', True),
154 (r'vc_librarycore86\vc_librarycore86.msi', False),
155 (r'vc_libraryDesktop\x64\vc_LibraryDesktopX64.msi', False),
156 (r'vc_libraryDesktop\x86\vc_LibraryDesktopX86.msi', False),
157 (r'vc_libraryextended\vc_libraryextended.msi', True),
158 (r'Windows_SDK\Windows Software Development Kit-x86_en-us.msi', False),
159 ('Windows_SDK\\'
160 r'Windows Software Development Kit for Metro style Apps-x86_en-us.msi',
161 False),
162 ])
163 return results
164
165
166 def CopyToFinalLocation(extracted_dirs, target_dir):
167 sys.stdout.write('Copying to final location...\n')
168 mappings = {
169 'Program Files\\Microsoft Visual Studio 12.0\\': '.\\',
M-A Ruel 2014/01/12 16:39:13 Sort keys
scottmg 2014/01/13 18:21:25 Done.
170 'Windows Kits\\8.0\\': 'win8sdk\\',
171 'System64\\': 'sys64\\',
172 'System\\': 'sys32\\',
173 }
174 matches = []
175 for extracted_dir in extracted_dirs:
176 for root, dirnames, filenames in os.walk(extracted_dir):
177 for filename in filenames:
178 matches.append((extracted_dir, os.path.join(root, filename)))
179
180 copies = []
181 for prefix, full_path in matches:
182 partial_path = full_path[len(prefix) + 1:] # +1 for trailing \
M-A Ruel 2014/01/12 16:39:13 comment on line before. Note that the trailing \ m
scottmg 2014/01/13 18:21:25 How about a trailing '.'? Done.
183 #print 'partial_path', partial_path
M-A Ruel 2014/01/12 16:39:13 Remove these, or use logging.info() A --verbose fl
scottmg 2014/01/13 18:21:25 Done.
184 for map_from, map_to in mappings.iteritems():
185 #print 'map_from:', map_from, ', map_to:', map_to
186 if partial_path.startswith(map_from):
187 target_path = os.path.join(map_to, partial_path[len(map_from):])
188 copies.append((full_path, os.path.join(target_dir, target_path)))
189
190 for full_source, full_target in copies:
191 target_dir = os.path.dirname(full_target)
192 if not os.path.isdir(target_dir):
193 os.makedirs(target_dir)
194 shutil.copy2(full_source, full_target)
195
196
197 def GenerateSetEnvCmd(target_dir, pro):
198 """Generate a batch file that gyp expects to exist to set up the compiler
199 environment. This is normally generated by a full install of the SDK, but we
M-A Ruel 2014/01/12 16:39:13 empty line between 1 liner summary and description
scottmg 2014/01/13 18:21:25 Done.
200 do it here manually since we do not do a full install."""
201 with open(os.path.join(
202 target_dir, r'win8sdk\bin\SetEnv.cmd'), 'w') as file:
M-A Ruel 2014/01/12 16:39:13 nit: Personally, I'd use a single call per functio
scottmg 2014/01/13 18:21:25 Done.
203 file.write('@echo off\n')
204 file.write(':: Generated by win_toolchain\\toolchain2013.py.\n')
205 # Common to x86 and x64
206 file.write('set PATH=%~dp0..\\..\\Common7\\IDE;%PATH%\n')
207 file.write('set INCLUDE=%~dp0..\\..\\win8sdk\\Include\\um;'
208 '%~dp0..\\..\\win8sdk\\Include\\shared;'
209 '%~dp0..\\..\\VC\\include;'
210 '%~dp0..\\..\\VC\\atlmfc\\include\n')
211 file.write('if "%1"=="/x64" goto x64\n')
212
213 # x86. If we're Pro, then use the amd64_x86 cross (we don't support x86
M-A Ruel 2014/01/12 16:39:13 Would it be possible to write the file uncondition
scottmg 2014/01/13 18:21:25 Do you mean having a flag passed to the batch file
M-A Ruel 2014/01/13 18:29:05 No I meant always write the same file except for a
214 # host at all).
215 if pro:
216 file.write('set PATH=%~dp0..\\..\\win8sdk\\bin\\x86;'
217 '%~dp0..\\..\\VC\\bin\\amd64_x86;'
218 '%~dp0..\\..\\VC\\bin\\amd64;' # Needed for mspdb120.dll.
219 '%PATH%\n')
220 else:
221 file.write('set PATH=%~dp0..\\..\\win8sdk\\bin\\x86;'
222 '%~dp0..\\..\\VC\\bin;%PATH%\n')
223 file.write('set LIB=%~dp0..\\..\\VC\\lib;'
224 '%~dp0..\\..\\win8sdk\\Lib\\win8\\um\\x86;'
225 '%~dp0..\\..\\VC\\atlmfc\\lib\n')
226 file.write('goto done\n')
M-A Ruel 2014/01/12 16:39:13 FYI, you can use 'goto :EOF\n'
scottmg 2014/01/13 18:21:25 Done.
227
228 # Express does not include a native 64 bit compiler, so we have to use
229 # the x86->x64 cross.
230 if not pro:
231 # x86->x64 cross.
232 file.write(':x64\n')
233 file.write('set PATH=%~dp0..\\..\\win8sdk\\bin\\x64;'
234 '%~dp0..\\..\\VC\\bin\\x86_amd64;'
235 '%PATH%\n')
236 else:
237 # x64 native.
238 file.write(':x64\n')
239 file.write('set PATH=%~dp0..\\..\\win8sdk\\bin\\x64;'
240 '%~dp0..\\..\\VC\\bin\\amd64;'
241 '%PATH%\n')
242 file.write('set LIB=%~dp0..\\..\\VC\\lib\\amd64;'
243 '%~dp0..\\..\\win8sdk\\Lib\\win8\\um\\x64;'
244 '%~dp0..\\..\\VC\\atlmfc\\lib\\amd64\n')
245 file.write(':done\n')
246
247
248 def main():
249 parser = OptionParser()
M-A Ruel 2014/01/12 16:39:13 parser = OptionParser(description=sys.moduels[__na
scottmg 2014/01/13 18:21:25 Done.
250 parser.add_option('--targetdir', metavar='DIR',
251 help='put toolchain into DIR',
252 default=os.path.abspath('win_toolchain_2013'))
M-A Ruel 2014/01/12 16:39:13 you probably want os.path.join(FILE_PATH, 'win_too
scottmg 2014/01/13 18:21:25 Done.
253 parser.add_option('--noclean', action='store_false', dest='clean',
254 help='do not remove temp files',
255 default=True)
256 parser.add_option('--local', metavar='DIR',
257 help='use downloaded files from DIR')
258 parser.add_option('--express', metavar='EXPRESS',
M-A Ruel 2014/01/12 16:39:13 do not use metavar on a store_true flag, it's conf
scottmg 2014/01/13 18:21:25 Done.
259 help='use VS Express instead of Pro', action='store_true')
260 options, args = parser.parse_args()
261 try:
262 target_dir = os.path.abspath(options.targetdir)
263 if os.path.exists(target_dir):
264 sys.stderr.write('%s already exists. Please [re]move it or use '
M-A Ruel 2014/01/12 16:39:13 parser.error() would work fine.
scottmg 2014/01/13 18:21:25 Done.
265 '--targetdir to select a different target.\n' %
266 target_dir)
267 return 1
268 pro = not options.express
M-A Ruel 2014/01/12 16:39:13 Why use options.clean but then use pro as a standa
scottmg 2014/01/13 18:21:25 Done.
269 # Set the working directory to 7z subdirectory. 7-zip doesn't find its
270 # codec dll very well, so this is the simplest way to make sure it runs
271 # correctly, as we don't otherwise care about working directory.
272 os.chdir(os.path.join(os.path.dirname(os.path.abspath(__file__)), '7z'))
273 image = GetSourceImage(options.local, pro)
274 extracted = ExtractComponents(image)
275 CopyToFinalLocation(extracted, target_dir)
276
277 GenerateSetEnvCmd(target_dir, pro)
278 finally:
279 if options.clean:
280 DeleteAllTempDirs()
281
282
283 if __name__ == '__main__':
284 main()
M-A Ruel 2014/01/12 16:39:13 sys.exit(main()) otherwise you are ditching the re
scottmg 2014/01/13 18:21:25 Done.
OLDNEW
« win_toolchain/get_toolchain_if_necessary.py ('K') | « win_toolchain/toolchain.sha1 ('k') | no next file » | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698