Chromium Code Reviews| OLD | NEW |
|---|---|
| (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.
| |
| OLD | NEW |