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

Side by Side Diff: tools/download_and_extract.py

Issue 209853003: Adding binutils as a DEPS to allow DebugFission on Ubuntu Precise when compiling with clang. (Closed) Base URL: https://chromium.googlesource.com/chromium/src.git@master
Patch Set: Fixing review comments. Created 6 years, 8 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
« no previous file with comments | « third_party/binutils/upload.sh ('k') | no next file » | no next file with comments »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
OLDNEW
(Empty)
1 #!/usr/bin/env python
2 # Copyright 2014 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 # vim: set ts=2 sw=2 et sts=2 ai:
6
7 """Download files for your platfrom and extract its contents into a directory.
8
9 Uses download_from_google_storage for the actual download process.
10 Uses tar to extract the files from the archive.
11
12 The input is a list of sha1 files suitable for download_from_google_storage.
13 """
14
15 """
16 TODO(mithro): Replace the other download/extract scripts that exist in the
17 chrome tree.
18
19 Replace chrome/installer/linux/sysroot_scripts/install-debian.wheezy.sysroot.py
20 download_and_extract.py \\
21 --default-file-arch Linux \\
22 --bucket chrome-linux-sysroot \\
23 --extract ????/debian_wheezy_???-sysroot \\
24 <sha1 files??>
25
26 Replace build/linux/install-arm-sysroot.py
27 download_and_extract.py \\
28 --bucket nativeclient-archive2 \\
29 --extract build/linux/arm-sys-root \\
30 <sha1 files??>
31
32 Replace download part of tools/clang/scripts/update.(py|sh)
33 download_and_extract.py \\
34 --bucket chromium-browser-clang \\
35 --extract third_party/llvm-build/Release+Asserts \\
36 <sha1 files??>
37 """
38
39
40 import optparse
41 import os
42 import re
43 import shutil
44 import subprocess
45 import sys
46 import types
47
48
49 def MatchPatterns(mapping, string, error):
50 if not isinstance(string, types.StringTypes):
51 raise TypeError("Can't match %r (a %s)" % (string, type(string)))
52
53 for i, (pattern, result) in enumerate(mapping):
54 if not isinstance(pattern, types.StringTypes):
55 raise TypeError(
56 "%r (%s) is not valid regex pattern (pattern %i - %s)." % (
57 pattern, type(pattern), i, (pattern, result)))
58 if re.search(pattern, string, re.I):
59 return result
60 raise ValueError(error % string)
61
62
63 def ExtractAndNormalizeArch(string):
64 """Extract and normalize an architecture string.
65 >>> # Linux arch / uname output
66 >>> ExtractAndNormalizeArch('x86_64')
67 'amd64'
68 >>> ExtractAndNormalizeArch('i686')
69 'i386'
70 >>> ExtractAndNormalizeArch('i386')
71 'i386'
72
73 >>> # autoconf "platform" tuple
74 >>> ExtractAndNormalizeArch('i686-pc-linux-gnu')
75 'i386'
76 >>> ExtractAndNormalizeArch('x86_64-unknown-linux-gnu')
77 'amd64'
78
79 >>> # platform.machine()
80 >>> ExtractAndNormalizeArch('i386')
81 'i386'
82 >>> ExtractAndNormalizeArch('x86_64')
83 'amd64'
84
85 >>> # GYP Defines
86 >>> ExtractAndNormalizeArch('target_arch=x64')
87 'amd64'
88 >>> ExtractAndNormalizeArch('target_arch=ia32')
89 'i386'
90 >>> ExtractAndNormalizeArch('x64')
91 'amd64'
92 >>> ExtractAndNormalizeArch('ia32')
93 'i386'
94
95 >>> # Empty Arch
96 >>> ExtractAndNormalizeArch('')
97 ''
98 """
99 ARCH_MAPPING = [
100 # Linux 'arch' outputs
101 ('i[3456]?86', 'i386'),
102 ('i86pc', 'i386'),
103 ('x86_64', 'amd64'),
104 ('amd64', 'amd64'),
105 ('mips64', 'mips64'), # Must be before mips
106 ('mips', 'mips'),
107 ('arm', 'arm'),
108 ('aarch', 'arm64'),
109 # Windows
110 ('win32', 'i386'),
111 ('win64', 'amd64'),
112 # GYP defines
113 ('ia32', 'i386'),
114 ('x64', 'amd64'),
115 # Empty arch
116 ('^$', ''),
117 ]
118 return MatchPatterns(
119 ARCH_MAPPING, string,
120 'Was not able to extract architecture from %s')
121
122
123 def ExtractAndNormalizeOS(string):
124 """Extract and normalize an OS string.
125
126 >>> # Used by download_from_storage
127 >>> # sys.platform
128 >>> ExtractAndNormalizeOS('linux2')
129 'Linux'
130 >>> ExtractAndNormalizeOS('darwin')
131 'Mac'
132 >>> ExtractAndNormalizeOS('cygwin')
133 'Win'
134 >>> ExtractAndNormalizeOS('win32')
135 'Win'
136 >>> ExtractAndNormalizeOS('win64')
137 'Win'
138
139 >>> # platform.system()
140 >>> ExtractAndNormalizeOS('Linux')
141 'Linux'
142 >>> ExtractAndNormalizeOS('Windows')
143 'Win'
144
145 >>> # Used by tools/clang/scripts
146 >>> # uname -s
147 >>> ExtractAndNormalizeOS('Linux')
148 'Linux'
149 >>> ExtractAndNormalizeOS('Darwin')
150 'Mac'
151
152 >>> # GYP defines
153 >>> ExtractAndNormalizeOS('win')
154 'Win'
155 >>> ExtractAndNormalizeOS('linux')
156 'Linux'
157 >>> ExtractAndNormalizeOS('mac')
158 'Mac'
159
160 >>> # GNU triplets
161 >>> ExtractAndNormalizeOS('i686-pc-linux-gnu')
162 'Linux'
163 >>> ExtractAndNormalizeOS('x86_64-unknown-linux-gnu')
164 'Linux'
165 >>> ExtractAndNormalizeOS('i586-pc-mingw32')
166 'Win'
167 >>> ExtractAndNormalizeOS('i386-pc-cygwin')
168 'Win'
169 >>> ExtractAndNormalizeOS('i386-pc-win32')
170 'Win'
171 >>> ExtractAndNormalizeOS('x86_64-apple-darwin10')
172 'Mac'
173 """
174 PLATFORM_MAPPING = [
175 # Mac
176 ('darwin', 'Mac'),
177 ('mac', 'Mac'),
178 # Linux
179 ('linux.*', 'Linux'),
180 # Windows
181 ('cygwin', 'Win'),
182 ('mingw', 'Win'),
Lei Zhang 2014/04/02 08:32:22 Is this in use anywhere? download_from_google_stor
183 ('win', 'Win'),
184 ]
185 return MatchPatterns(
186 PLATFORM_MAPPING, string,
187 'Was not able to extract operating system from %s')
188
189
190 def GetSystemArch(os_str):
191 if os_str == 'Linux':
192 # Try calling arch first, then fall back to uname
193 try:
194 return subprocess.check_output(['arch']).strip()
195 except subprocess.CalledProcessError, e:
196 # We want the architecture, which is roughly the machine hardware name
197 # in uname.
198 # -m, --machine; print the machine hardware name
199 # These other two are possibilities?
200 # -p, --processor; print the processor type or 'unknown'
201 # -i, --hardware-platform; print the hardware platform or 'unknown'
202 return subprocess.check_output(['uname', '-m']).strip()
203 else:
204 # TODO(mithro): Make this work under Mac / Windows
205 return ''
206
207
208 def FilterFilesByPlatform(files, target_os, target_arch,
Lei Zhang 2014/04/02 08:32:22 Design concern: I feel this is trying too hard to
209 file_os_default=None, file_arch_default=''):
210 """Filter input files to given platform.
211
212 We assume the target arch is the host arch if not overridden.
213
214 >>> clang_style = [
215 ... 'abc/Linux_ia32/xxx',
216 ... 'abc/Linux_x64/xxx',
217 ... 'abc/Mac/xxx',
218 ... 'abc/Win/xxx',
219 ... ]
220 >>> FilterFilesByPlatform(clang_style, 'Linux', 'i386')
221 ['abc/Linux_ia32/xxx']
222 >>> FilterFilesByPlatform(clang_style, 'Linux', 'amd64')
223 ['abc/Linux_x64/xxx']
224 >>> FilterFilesByPlatform(clang_style, 'Win', '')
225 ['abc/Win/xxx']
226 >>> FilterFilesByPlatform(clang_style, 'Mac', '')
227 ['abc/Mac/xxx']
228
229 >>> gnu_style = [
230 ... 'XXX-i686-pc-linux-gnu.XXX',
231 ... 'XXX-x86_64-unknown-linux-gnu.XXX',
232 ... 'XXX-i586-pc-mingw32.XXX',
233 ... 'XXX-i386-pc-win32.XXX',
234 ... 'XXX-x86_64-apple-darwin10.XXX',
235 ... ]
236 >>> FilterFilesByPlatform(gnu_style, 'Linux', 'i386')
237 ['XXX-i686-pc-linux-gnu.XXX']
238 >>> FilterFilesByPlatform(gnu_style, 'Linux', 'amd64')
239 ['XXX-x86_64-unknown-linux-gnu.XXX']
240 >>> FilterFilesByPlatform(gnu_style, 'Win', '')
241 ['XXX-i586-pc-mingw32.XXX', 'XXX-i386-pc-win32.XXX']
242 >>> FilterFilesByPlatform(gnu_style, 'Mac', '')
243 ['XXX-x86_64-apple-darwin10.XXX']
244
245 >>> simple_no_os = [
246 ... 'XXXX_amd64_XXX',
247 ... 'XXXX_i386_XXX',
248 ... ]
249 >>> FilterFilesByPlatform(
250 ... simple_no_os, 'Linux', 'i386', file_os_default='Linux')
251 ['XXXX_i386_XXX']
252 >>> FilterFilesByPlatform(
253 ... simple_no_os, 'Linux', 'amd64', file_os_default='Linux')
254 ['XXXX_amd64_XXX']
255 >>> FilterFilesByPlatform(
256 ... simple_no_os, 'Win', '', file_os_default='Linux')
257 []
258 >>> # Fails when no default is provided and can't extract from filename.
259 >>> FilterFilesByPlatform(simple_no_os, 'Linux', 'i386')
260 Traceback (most recent call last):
261 ...
262 ValueError: Was not able to extract operating system from XXXX_amd64_XXX
263 """
264 files_for_platform = []
265 for filename in files:
266 try:
267 file_os = ExtractAndNormalizeOS(filename)
268 except ValueError, e:
269 if file_os_default is None:
270 raise
271 file_os = file_os_default
272
273 try:
274 file_arch = ExtractAndNormalizeArch(filename)
275 except ValueError:
276 if file_arch_default is None:
277 raise
278 file_arch = file_arch_default
279
280 match = True
281 if target_os != '' and file_os != '':
282 match = target_os == file_os
283
284 if target_arch != '' and file_arch != '':
285 match = match and target_arch == file_arch
286
287 if match:
288 files_for_platform.append(filename)
289
290 return files_for_platform
291
292
293 class StampFile(object):
294 """Stores a stamp for when an action occured.
295
296 This is normally a revision number or checksum of the input files to the
297 process. Proper usage of stamping will mean a partial action is never
298 considered successful.
299
300 Proper usage is;
301 * /Check/ -- Check the stamp file matches your revision/checksum.
302 * /Delete/ -- Delete the existing stamp file.
303 * /Clean up/ -- Clean up any partially failed previous attempt.
304 * /Do action/ -- Do your work.
305 * /Set/ -- Set the stamp file to your revision/checksum.
306
307 For example:
308 >>> stamp = 'mySHA1sum'
309 >>> action_stamp = StampFile('stamp.action')
310 >>> # Check stamp
311 >>> if stamp != action_stamp.get(): # doctest: +SKIP
312 ... # Delete stamp
313 ... action_stamp.delete()
314 ... # Clean up
315 ... if os.path.exists(filename):
316 ... os.unlink(filename)
317 ... # Action
318 ... do_download(filename)
319 ... # Set stamp
320 ... action_stamp.set(stamp)
321 """
322
323 def __init__(self, filename):
324 self.filename = filename
325
326 def get(self):
327 try:
328 return file(self.filename, 'r').read()
329 except IOError, e:
330 # We use NaN because it is not equal even to itself, so equality checks
331 # will always fail.
332 return float('NaN')
333
334 def delete(self):
335 if os.path.exists(self.filename):
336 os.unlink(self.filename)
337
338 def set(self, stamp):
339 if os.path.exists(self.filename):
340 raise IOError('Stamp file %r currently exist!' % filename)
341
342 f = file(self.filename, 'w')
343 f.write(stamp)
344 # Force a fsync so this ends up on disk and other processes can see it.
345 os.fsync(f)
346 f.close()
347
348
349 def main(args):
350 usage = (
351 'usage: %prog [options] targets\n\n'
352 'Downloads and extracts targets which match the platform.\n'
353 'Targets must be a list of a .sha1 file, containing a sha1 sum'
354 'used by download_from_google_storage tool.')
355
356 parser = optparse.OptionParser(usage)
357 parser.add_option(
358 '-b', '--bucket',
359 help='Google Storage bucket to fetch from.')
360 parser.add_option(
361 '-e', '--extract',
362 help='Directory to extract the downloaded files into.')
363 parser.add_option(
364 '-a', '--target-arch',
365 help='Override architecture to given value.')
366 parser.add_option(
367 '', '--default-file-arch',
368 help='Override input file architecture to given value.')
369 parser.add_option(
370 '', '--default-file-os',
371 help='Override input file operating system to given value.')
372 parser.add_option(
373 '', '--self-test', default=False, action="store_true",
374 help='Run the internal tests.')
375
376 (options, files) = parser.parse_args()
377
378 if options.self_test:
379 import doctest
380 return doctest.testmod()
381
382 errors = []
383 if not options.bucket:
384 errors.append('--bucket (-b) is required option.')
385
386 if not options.extract:
387 errors.append('--extract (-e) is required option.')
388
389 if not files:
390 errors.append('Need to specify files to download.')
391
392 for filename in files:
393 if not os.path.exists(filename):
394 errors.append('File %s does not exist.' % filename)
395
396 if errors:
397 parser.error('\n '.join(errors))
398
399 # Figure out which files we want to download. Filter by the current platform
400 # the tool is being run on.
401 target_os = ExtractAndNormalizeOS(sys.platform)
402
403 target_arch = None
404 if options.target_arch:
405 target_arch = options.target_arch
406 else:
407 # Try to get target_arch out of GYP
408 gyp_target_arch = re.search(
409 '(target_arch=[^ ]*)', os.environ.get('GYP_DEFINES', ''))
410 if gyp_target_arch:
411 target_arch = gyp_target_arch.groups(1)
412
413 if target_arch is None: # '' is a valid target_arch
414 target_arch = GetSystemArch(target_os)
415
416 target_arch = ExtractAndNormalizeArch(target_arch)
417
418 todo = FilterFilesByPlatform(
419 files,
420 target_os,
421 target_arch,
422 file_os_default=options.default_file_os,
423 file_arch_default=options.default_file_arch)
424
425 if len(todo) == 0:
426 print 'No files to download.'
427 return 0
428
429 elif len(todo) > 1:
430 # TODO(mithro): Support downloading and extracting multiple files.
431 parser.error(
432 'Matched multiple files on this platform!\n' + '\n'.join(todo))
433 return 1
434
435 # Process the files
436 for filename in todo:
437 filename = os.path.abspath(filename)
438
439 basename, ext = os.path.splitext(filename)
440 assert ext == '.sha1', 'Input filename %s does not end in .sha1' % filename
441 sha1 = file(filename, 'r').read().strip()
442
443 # Download the tarball
444 download_stamp = StampFile('%s.stamp.download' % basename)
445 if sha1 != download_stamp.get():
446 print "Downloading", basename
447 download_stamp.delete()
448 if os.path.exists(basename):
449 os.unlink(basename)
450
451 subprocess.check_call([
452 'download_from_google_storage',
453 '--no_resume',
454 '--no_auth',
455 '--bucket', options.bucket,
456 '-s', filename])
457
458 download_stamp.set(sha1)
459
460 # Extract the tarball
461 extract_stamp = StampFile('%s.stamp.untar' % basename)
462 if sha1 != extract_stamp.get():
463 print "Extracting", basename
464 extract_stamp.delete()
465 if os.path.exists(options.extract):
466 shutil.rmtree(options.extract)
467
468 os.makedirs(options.extract)
469
470 # TODO(mithro): Maybe use https://docs.python.org/2/library/tarfile.html
471 # rather than tar cmdline.
472 subprocess.check_call(
473 ['tar', 'axf', basename], cwd=options.extract)
474
475 extract_stamp.set(sha1)
476
477 return 0
478
479
480 if __name__ == '__main__':
481 sys.exit(main(sys.argv))
OLDNEW
« no previous file with comments | « third_party/binutils/upload.sh ('k') | no next file » | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698