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

Side by Side Diff: native_client_sdk/src/build_tools/manifest_util.py

Issue 11200002: update_nacl_manifest improvements (Closed) Base URL: svn://svn.chromium.org/chrome/trunk/src
Patch Set: Created 8 years, 2 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 | Annotate | Revision Log
OLDNEW
1 # Copyright (c) 2012 The Chromium Authors. All rights reserved. 1 # Copyright (c) 2012 The Chromium Authors. All rights reserved.
2 # Use of this source code is governed by a BSD-style license that can be 2 # Use of this source code is governed by a BSD-style license that can be
3 # found in the LICENSE file. 3 # found in the LICENSE file.
4 4
5 import copy 5 import copy
6 import hashlib 6 import hashlib
7 import json 7 import json
8 import string
8 import sys 9 import sys
10 import urllib2
9 11
10 MANIFEST_VERSION = 2 12 MANIFEST_VERSION = 2
11 13
12 # Some commonly-used key names. 14 # Some commonly-used key names.
13 ARCHIVES_KEY = 'archives' 15 ARCHIVES_KEY = 'archives'
14 BUNDLES_KEY = 'bundles' 16 BUNDLES_KEY = 'bundles'
15 NAME_KEY = 'name' 17 NAME_KEY = 'name'
16 REVISION_KEY = 'revision' 18 REVISION_KEY = 'revision'
17 VERSION_KEY = 'version' 19 VERSION_KEY = 'version'
18 20
(...skipping 31 matching lines...) Expand 10 before | Expand all | Expand 10 after
50 def DictToJSON(pydict): 52 def DictToJSON(pydict):
51 """Convert a dict to a JSON-formatted string.""" 53 """Convert a dict to a JSON-formatted string."""
52 pretty_string = json.dumps(pydict, sort_keys=False, indent=2) 54 pretty_string = json.dumps(pydict, sort_keys=False, indent=2)
53 # json.dumps sometimes returns trailing whitespace and does not put 55 # json.dumps sometimes returns trailing whitespace and does not put
54 # a newline at the end. This code fixes these problems. 56 # a newline at the end. This code fixes these problems.
55 pretty_lines = pretty_string.split('\n') 57 pretty_lines = pretty_string.split('\n')
56 return '\n'.join([line.rstrip() for line in pretty_lines]) + '\n' 58 return '\n'.join([line.rstrip() for line in pretty_lines]) + '\n'
57 59
58 60
59 def DownloadAndComputeHash(from_stream, to_stream=None, progress_func=None): 61 def DownloadAndComputeHash(from_stream, to_stream=None, progress_func=None):
60 ''' Download the archive data from from-stream and generate sha1 and 62 '''Download the archive data from from-stream and generate sha1 and
61 size info. 63 size info.
62 64
63 Args: 65 Args:
64 from_stream: An input stream that supports read. 66 from_stream: An input stream that supports read.
65 to_stream: [optional] the data is written to to_stream if it is 67 to_stream: [optional] the data is written to to_stream if it is
66 provided. 68 provided.
67 progress_func: [optional] A function used to report download progress. If 69 progress_func: [optional] A function used to report download progress. If
68 provided, progress_func is called with progress=0 at the 70 provided, progress_func is called with progress=0 at the
69 beginning of the download, periodically with progress=1 71 beginning of the download, periodically with progress=1
70 during the download, and progress=100 at the end. 72 during the download, and progress=100 at the end.
71 73
(...skipping 67 matching lines...) Expand 10 before | Expand all | Expand 10 after
139 raise Error('Archive "%s" has no checksum' % host_os) 141 raise Error('Archive "%s" has no checksum' % host_os)
140 elif not isinstance(checksum, dict): 142 elif not isinstance(checksum, dict):
141 raise Error('Archive "%s" has a checksum, but it is not a dict' % host_os) 143 raise Error('Archive "%s" has a checksum, but it is not a dict' % host_os)
142 elif not len(checksum): 144 elif not len(checksum):
143 raise Error('Archive "%s" has an empty checksum dict' % host_os) 145 raise Error('Archive "%s" has an empty checksum dict' % host_os)
144 # Verify that all key names are valid. 146 # Verify that all key names are valid.
145 for key in self: 147 for key in self:
146 if key not in VALID_ARCHIVE_KEYS: 148 if key not in VALID_ARCHIVE_KEYS:
147 raise Error('Archive "%s" has invalid attribute "%s"' % (host_os, key)) 149 raise Error('Archive "%s" has invalid attribute "%s"' % (host_os, key))
148 150
151 def UpdateVitals(self, revision):
152 """Update the size and checksum information for this archive
153 based on the content currently at the URL.
154
155 This allows the template mandifest to be maintained without
binji 2012/10/16 23:09:10 manifest
156 the need to size and checksums to be present.
157 """
158 template = string.Template(self['url'])
159 self['url'] = template.substitute({'revision': revision})
160 from_stream = urllib2.urlopen(self['url'])
161 sha1_hash, size = DownloadAndComputeHash(from_stream)
162 self['size'] = size
163 self['checksum'] = { 'sha1': sha1_hash }
164
149 def __getattr__(self, name): 165 def __getattr__(self, name):
150 """Retrieve values from this dict using attributes. 166 """Retrieve values from this dict using attributes.
151 167
152 This allows for foo.bar instead of foo['bar']. 168 This allows for foo.bar instead of foo['bar'].
153 169
154 Args: 170 Args:
155 name: the name of the key, 'bar' in the example above. 171 name: the name of the key, 'bar' in the example above.
156 Returns: 172 Returns:
157 The value associated with that key.""" 173 The value associated with that key."""
158 if name not in VALID_ARCHIVE_KEYS: 174 if name not in VALID_ARCHIVE_KEYS:
(...skipping 49 matching lines...) Expand 10 before | Expand all | Expand 10 after
208 bundle: The other bundle. Must be a dict. 224 bundle: The other bundle. Must be a dict.
209 """ 225 """
210 for k, v in bundle.iteritems(): 226 for k, v in bundle.iteritems():
211 if k == ARCHIVES_KEY: 227 if k == ARCHIVES_KEY:
212 for archive in v: 228 for archive in v:
213 self.RemoveArchive(archive['host_os']) 229 self.RemoveArchive(archive['host_os'])
214 self.get(k, []).append(archive) 230 self.get(k, []).append(archive)
215 else: 231 else:
216 self[k] = v 232 self[k] = v
217 233
234 def __str__(self):
235 return self.GetDataAsString()
236
218 def GetDataAsString(self): 237 def GetDataAsString(self):
219 """Returns the JSON bundle object, pretty-printed""" 238 """Returns the JSON bundle object, pretty-printed"""
220 return DictToJSON(self) 239 return DictToJSON(self)
221 240
222 def LoadDataFromString(self, json_string): 241 def LoadDataFromString(self, json_string):
223 """Load a JSON bundle string. Raises an exception if json_string 242 """Load a JSON bundle string. Raises an exception if json_string
224 is not well-formed JSON. 243 is not well-formed JSON.
225 244
226 Args: 245 Args:
227 json_string: a JSON-formatted string containing the bundle 246 json_string: a JSON-formatted string containing the bundle
(...skipping 10 matching lines...) Expand all
238 if key == ARCHIVES_KEY: 257 if key == ARCHIVES_KEY:
239 archives = [] 258 archives = []
240 for a in value: 259 for a in value:
241 new_archive = Archive(a['host_os']) 260 new_archive = Archive(a['host_os'])
242 new_archive.CopyFrom(a) 261 new_archive.CopyFrom(a)
243 archives.append(new_archive) 262 archives.append(new_archive)
244 self[ARCHIVES_KEY] = archives 263 self[ARCHIVES_KEY] = archives
245 else: 264 else:
246 self[key] = value 265 self[key] = value
247 266
248 def Validate(self): 267 def Validate(self, add_missing_info=False):
249 """Validate the content of the bundle. Raise an Error if an invalid or 268 """Validate the content of the bundle. Raise an Error if an invalid or
250 missing field is found. """ 269 missing field is found. """
251 # Check required fields. 270 # Check required fields.
252 if not self.get(NAME_KEY, None): 271 if not self.get(NAME_KEY):
253 raise Error('Bundle has no name') 272 raise Error('Bundle has no name')
254 if self.get(REVISION_KEY, None) == None: 273 if self.get(REVISION_KEY) == None:
255 raise Error('Bundle "%s" is missing a revision number' % self[NAME_KEY]) 274 raise Error('Bundle "%s" is missing a revision number' % self[NAME_KEY])
256 if self.get(VERSION_KEY, None) == None: 275 if self.get(VERSION_KEY) == None:
257 raise Error('Bundle "%s" is missing a version number' % self[NAME_KEY]) 276 raise Error('Bundle "%s" is missing a version number' % self[NAME_KEY])
258 if not self.get('description', None): 277 if not self.get('description'):
259 raise Error('Bundle "%s" is missing a description' % self[NAME_KEY]) 278 raise Error('Bundle "%s" is missing a description' % self[NAME_KEY])
260 if not self.get('stability', None): 279 if not self.get('stability'):
261 raise Error('Bundle "%s" is missing stability info' % self[NAME_KEY]) 280 raise Error('Bundle "%s" is missing stability info' % self[NAME_KEY])
262 if self.get('recommended', None) == None: 281 if self.get('recommended') == None:
263 raise Error('Bundle "%s" is missing the recommended field' % 282 raise Error('Bundle "%s" is missing the recommended field' %
264 self[NAME_KEY]) 283 self[NAME_KEY])
265 # Check specific values 284 # Check specific values
266 if self['stability'] not in STABILITY_LITERALS: 285 if self['stability'] not in STABILITY_LITERALS:
267 raise Error('Bundle "%s" has invalid stability field: "%s"' % 286 raise Error('Bundle "%s" has invalid stability field: "%s"' %
268 (self[NAME_KEY], self['stability'])) 287 (self[NAME_KEY], self['stability']))
269 if self['recommended'] not in YES_NO_LITERALS: 288 if self['recommended'] not in YES_NO_LITERALS:
270 raise Error( 289 raise Error(
271 'Bundle "%s" has invalid recommended field: "%s"' % 290 'Bundle "%s" has invalid recommended field: "%s"' %
272 (self[NAME_KEY], self['recommended'])) 291 (self[NAME_KEY], self['recommended']))
273 # Verify that all key names are valid. 292 # Verify that all key names are valid.
274 for key in self: 293 for key in self:
275 if key not in VALID_BUNDLES_KEYS: 294 if key not in VALID_BUNDLES_KEYS:
276 raise Error('Bundle "%s" has invalid attribute "%s"' % 295 raise Error('Bundle "%s" has invalid attribute "%s"' %
277 (self[NAME_KEY], key)) 296 (self[NAME_KEY], key))
278 # Validate the archives 297 # Validate the archives
279 for archive in self[ARCHIVES_KEY]: 298 for archive in self[ARCHIVES_KEY]:
299 if add_missing_info and 'size' not in archive:
300 archive.UpdateVitals(self[REVISION_KEY])
280 archive.Validate() 301 archive.Validate()
281 302
303
binji 2012/10/16 23:09:10 no newline necessary here
282 def GetArchive(self, host_os_name): 304 def GetArchive(self, host_os_name):
283 """Retrieve the archive for the given host os. 305 """Retrieve the archive for the given host os.
284 306
285 Args: 307 Args:
286 host_os_name: name of host os whose archive must be retrieved. 308 host_os_name: name of host os whose archive must be retrieved.
287 Return: 309 Return:
288 An Archive instance or None if it doesn't exist.""" 310 An Archive instance or None if it doesn't exist."""
289 for archive in self[ARCHIVES_KEY]: 311 for archive in self[ARCHIVES_KEY]:
290 if archive.host_os == host_os_name or archive.host_os == 'all': 312 if archive.host_os == host_os_name or archive.host_os == 'all':
291 return archive 313 return archive
(...skipping 88 matching lines...) Expand 10 before | Expand all | Expand 10 after
380 For ease of unit-testing, this class should not contain any file I/O. 402 For ease of unit-testing, this class should not contain any file I/O.
381 """ 403 """
382 404
383 def __init__(self): 405 def __init__(self):
384 """Create a new SDKManifest object with default contents""" 406 """Create a new SDKManifest object with default contents"""
385 self._manifest_data = { 407 self._manifest_data = {
386 "manifest_version": MANIFEST_VERSION, 408 "manifest_version": MANIFEST_VERSION,
387 "bundles": [], 409 "bundles": [],
388 } 410 }
389 411
390 def Validate(self): 412 def Validate(self, add_missing_info=False):
391 """Validate the Manifest file and raises an exception for problems""" 413 """Validate the Manifest file and raises an exception for problems"""
392 # Validate the manifest top level 414 # Validate the manifest top level
393 if self._manifest_data["manifest_version"] > MANIFEST_VERSION: 415 if self._manifest_data["manifest_version"] > MANIFEST_VERSION:
394 raise Error("Manifest version too high: %s" % 416 raise Error("Manifest version too high: %s" %
395 self._manifest_data["manifest_version"]) 417 self._manifest_data["manifest_version"])
396 # Verify that all key names are valid. 418 # Verify that all key names are valid.
397 for key in self._manifest_data: 419 for key in self._manifest_data:
398 if key not in VALID_MANIFEST_KEYS: 420 if key not in VALID_MANIFEST_KEYS:
399 raise Error('Manifest has invalid attribute "%s"' % key) 421 raise Error('Manifest has invalid attribute "%s"' % key)
400 # Validate each bundle 422 # Validate each bundle
401 for bundle in self._manifest_data[BUNDLES_KEY]: 423 for bundle in self._manifest_data[BUNDLES_KEY]:
402 bundle.Validate() 424 bundle.Validate(add_missing_info)
403 425
404 def GetBundle(self, name): 426 def GetBundle(self, name):
405 """Get a bundle from the array of bundles. 427 """Get a bundle from the array of bundles.
406 428
407 Args: 429 Args:
408 name: the name of the bundle to return. 430 name: the name of the bundle to return.
409 Return: 431 Return:
410 The first bundle with the given name, or None if it is not found.""" 432 The first bundle with the given name, or None if it is not found."""
411 if not BUNDLES_KEY in self._manifest_data: 433 if not BUNDLES_KEY in self._manifest_data:
412 return None 434 return None
(...skipping 37 matching lines...) Expand 10 before | Expand all | Expand 10 after
450 Returns: 472 Returns:
451 True if Bundle needs to be updated. 473 True if Bundle needs to be updated.
452 """ 474 """
453 if NAME_KEY not in bundle: 475 if NAME_KEY not in bundle:
454 raise KeyError("Bundle must have a 'name' key.") 476 raise KeyError("Bundle must have a 'name' key.")
455 local_bundle = self.GetBundle(bundle[NAME_KEY]) 477 local_bundle = self.GetBundle(bundle[NAME_KEY])
456 return (local_bundle == None) or ( 478 return (local_bundle == None) or (
457 (local_bundle[VERSION_KEY], local_bundle[REVISION_KEY]) < 479 (local_bundle[VERSION_KEY], local_bundle[REVISION_KEY]) <
458 (bundle[VERSION_KEY], bundle[REVISION_KEY])) 480 (bundle[VERSION_KEY], bundle[REVISION_KEY]))
459 481
460 def MergeBundle(self, bundle, allow_existing = True): 482 def MergeBundle(self, bundle, allow_existing=True):
461 """Merge a Bundle into this manifest. 483 """Merge a Bundle into this manifest.
462 484
463 The new bundle is added if not present, or merged into the existing bundle. 485 The new bundle is added if not present, or merged into the existing bundle.
464 486
465 Args: 487 Args:
466 bundle: The bundle to merge. 488 bundle: The bundle to merge.
467 """ 489 """
468 if NAME_KEY not in bundle: 490 if NAME_KEY not in bundle:
469 raise KeyError("Bundle must have a 'name' key.") 491 raise KeyError("Bundle must have a 'name' key.")
470 local_bundle = self.GetBundle(bundle.name) 492 local_bundle = self.GetBundle(bundle.name)
471 if not local_bundle: 493 if not local_bundle:
472 self.SetBundle(bundle) 494 self.SetBundle(bundle)
473 else: 495 else:
474 if not allow_existing: 496 if not allow_existing:
475 raise Error('cannot merge manifest bundle \'%s\', it already exists' 497 raise Error('cannot merge manifest bundle \'%s\', it already exists'
476 % bundle.name) 498 % bundle.name)
477 local_bundle.MergeWithBundle(bundle) 499 local_bundle.MergeWithBundle(bundle)
478 500
479 def MergeManifest(self, manifest): 501 def MergeManifest(self, manifest):
480 '''Merge another manifest into this manifest, disallowing overiding. 502 '''Merge another manifest into this manifest, disallowing overiding.
481 503
482 Args 504 Args
483 manifest: The manifest to merge. 505 manifest: The manifest to merge.
484 ''' 506 '''
485 for bundle in manifest.GetBundles(): 507 for bundle in manifest.GetBundles():
486 self.MergeBundle(bundle, allow_existing = False) 508 self.MergeBundle(bundle, allow_existing=False)
487 509
488 def FilterBundles(self, predicate): 510 def FilterBundles(self, predicate):
489 """Filter the list of bundles by |predicate|. 511 """Filter the list of bundles by |predicate|.
490 512
491 For all bundles in this manifest, if predicate(bundle) is False, the bundle 513 For all bundles in this manifest, if predicate(bundle) is False, the bundle
492 is removed from the manifest. 514 is removed from the manifest.
493 515
494 Args: 516 Args:
495 predicate: a function that take a bundle and returns whether True to keep 517 predicate: a function that take a bundle and returns whether True to keep
496 it or False to remove it. 518 it or False to remove it.
497 """ 519 """
498 self._manifest_data[BUNDLES_KEY] = filter(predicate, self.GetBundles()) 520 self._manifest_data[BUNDLES_KEY] = filter(predicate, self.GetBundles())
499 521
500 def LoadDataFromString(self, json_string): 522 def LoadDataFromString(self, json_string, add_missing_info=False):
501 """Load a JSON manifest string. Raises an exception if json_string 523 """Load a JSON manifest string. Raises an exception if json_string
502 is not well-formed JSON. 524 is not well-formed JSON.
503 525
504 Args: 526 Args:
505 json_string: a JSON-formatted string containing the previous manifest 527 json_string: a JSON-formatted string containing the previous manifest
506 all_hosts: True indicates that we should load bundles for all hosts. 528 all_hosts: True indicates that we should load bundles for all hosts.
507 False (default) says to only load bundles for the current host""" 529 False (default) says to only load bundles for the current host"""
508 new_manifest = json.loads(json_string) 530 new_manifest = json.loads(json_string)
509 for key, value in new_manifest.items(): 531 for key, value in new_manifest.items():
510 if key == BUNDLES_KEY: 532 if key == BUNDLES_KEY:
511 # Remap each bundle in |value| to a Bundle instance 533 # Remap each bundle in |value| to a Bundle instance
512 bundles = [] 534 bundles = []
513 for b in value: 535 for b in value:
514 new_bundle = Bundle(b[NAME_KEY]) 536 new_bundle = Bundle(b[NAME_KEY])
515 new_bundle.CopyFrom(b) 537 new_bundle.CopyFrom(b)
516 bundles.append(new_bundle) 538 bundles.append(new_bundle)
517 self._manifest_data[key] = bundles 539 self._manifest_data[key] = bundles
518 else: 540 else:
519 self._manifest_data[key] = value 541 self._manifest_data[key] = value
520 self.Validate() 542 self.Validate(add_missing_info)
543
544 def __str__(self):
545 return self.GetDataAsString()
521 546
522 def GetDataAsString(self): 547 def GetDataAsString(self):
523 """Returns the current JSON manifest object, pretty-printed""" 548 """Returns the current JSON manifest object, pretty-printed"""
524 return DictToJSON(self._manifest_data) 549 return DictToJSON(self._manifest_data)
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698