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

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: nits 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
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
282 def GetArchive(self, host_os_name): 303 def GetArchive(self, host_os_name):
283 """Retrieve the archive for the given host os. 304 """Retrieve the archive for the given host os.
284 305
285 Args: 306 Args:
286 host_os_name: name of host os whose archive must be retrieved. 307 host_os_name: name of host os whose archive must be retrieved.
287 Return: 308 Return:
288 An Archive instance or None if it doesn't exist.""" 309 An Archive instance or None if it doesn't exist."""
289 for archive in self[ARCHIVES_KEY]: 310 for archive in self[ARCHIVES_KEY]:
(...skipping 90 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. 401 For ease of unit-testing, this class should not contain any file I/O.
381 """ 402 """
382 403
383 def __init__(self): 404 def __init__(self):
384 """Create a new SDKManifest object with default contents""" 405 """Create a new SDKManifest object with default contents"""
385 self._manifest_data = { 406 self._manifest_data = {
386 "manifest_version": MANIFEST_VERSION, 407 "manifest_version": MANIFEST_VERSION,
387 "bundles": [], 408 "bundles": [],
388 } 409 }
389 410
390 def Validate(self): 411 def Validate(self, add_missing_info=False):
391 """Validate the Manifest file and raises an exception for problems""" 412 """Validate the Manifest file and raises an exception for problems"""
392 # Validate the manifest top level 413 # Validate the manifest top level
393 if self._manifest_data["manifest_version"] > MANIFEST_VERSION: 414 if self._manifest_data["manifest_version"] > MANIFEST_VERSION:
394 raise Error("Manifest version too high: %s" % 415 raise Error("Manifest version too high: %s" %
395 self._manifest_data["manifest_version"]) 416 self._manifest_data["manifest_version"])
396 # Verify that all key names are valid. 417 # Verify that all key names are valid.
397 for key in self._manifest_data: 418 for key in self._manifest_data:
398 if key not in VALID_MANIFEST_KEYS: 419 if key not in VALID_MANIFEST_KEYS:
399 raise Error('Manifest has invalid attribute "%s"' % key) 420 raise Error('Manifest has invalid attribute "%s"' % key)
400 # Validate each bundle 421 # Validate each bundle
401 for bundle in self._manifest_data[BUNDLES_KEY]: 422 for bundle in self._manifest_data[BUNDLES_KEY]:
402 bundle.Validate() 423 bundle.Validate(add_missing_info)
403 424
404 def GetBundle(self, name): 425 def GetBundle(self, name):
405 """Get a bundle from the array of bundles. 426 """Get a bundle from the array of bundles.
406 427
407 Args: 428 Args:
408 name: the name of the bundle to return. 429 name: the name of the bundle to return.
409 Return: 430 Return:
410 The first bundle with the given name, or None if it is not found.""" 431 The first bundle with the given name, or None if it is not found."""
411 if not BUNDLES_KEY in self._manifest_data: 432 if not BUNDLES_KEY in self._manifest_data:
412 return None 433 return None
(...skipping 37 matching lines...) Expand 10 before | Expand all | Expand 10 after
450 Returns: 471 Returns:
451 True if Bundle needs to be updated. 472 True if Bundle needs to be updated.
452 """ 473 """
453 if NAME_KEY not in bundle: 474 if NAME_KEY not in bundle:
454 raise KeyError("Bundle must have a 'name' key.") 475 raise KeyError("Bundle must have a 'name' key.")
455 local_bundle = self.GetBundle(bundle[NAME_KEY]) 476 local_bundle = self.GetBundle(bundle[NAME_KEY])
456 return (local_bundle == None) or ( 477 return (local_bundle == None) or (
457 (local_bundle[VERSION_KEY], local_bundle[REVISION_KEY]) < 478 (local_bundle[VERSION_KEY], local_bundle[REVISION_KEY]) <
458 (bundle[VERSION_KEY], bundle[REVISION_KEY])) 479 (bundle[VERSION_KEY], bundle[REVISION_KEY]))
459 480
460 def MergeBundle(self, bundle, allow_existing = True): 481 def MergeBundle(self, bundle, allow_existing=True):
461 """Merge a Bundle into this manifest. 482 """Merge a Bundle into this manifest.
462 483
463 The new bundle is added if not present, or merged into the existing bundle. 484 The new bundle is added if not present, or merged into the existing bundle.
464 485
465 Args: 486 Args:
466 bundle: The bundle to merge. 487 bundle: The bundle to merge.
467 """ 488 """
468 if NAME_KEY not in bundle: 489 if NAME_KEY not in bundle:
469 raise KeyError("Bundle must have a 'name' key.") 490 raise KeyError("Bundle must have a 'name' key.")
470 local_bundle = self.GetBundle(bundle.name) 491 local_bundle = self.GetBundle(bundle.name)
471 if not local_bundle: 492 if not local_bundle:
472 self.SetBundle(bundle) 493 self.SetBundle(bundle)
473 else: 494 else:
474 if not allow_existing: 495 if not allow_existing:
475 raise Error('cannot merge manifest bundle \'%s\', it already exists' 496 raise Error('cannot merge manifest bundle \'%s\', it already exists'
476 % bundle.name) 497 % bundle.name)
477 local_bundle.MergeWithBundle(bundle) 498 local_bundle.MergeWithBundle(bundle)
478 499
479 def MergeManifest(self, manifest): 500 def MergeManifest(self, manifest):
480 '''Merge another manifest into this manifest, disallowing overiding. 501 '''Merge another manifest into this manifest, disallowing overiding.
481 502
482 Args 503 Args
483 manifest: The manifest to merge. 504 manifest: The manifest to merge.
484 ''' 505 '''
485 for bundle in manifest.GetBundles(): 506 for bundle in manifest.GetBundles():
486 self.MergeBundle(bundle, allow_existing = False) 507 self.MergeBundle(bundle, allow_existing=False)
487 508
488 def FilterBundles(self, predicate): 509 def FilterBundles(self, predicate):
489 """Filter the list of bundles by |predicate|. 510 """Filter the list of bundles by |predicate|.
490 511
491 For all bundles in this manifest, if predicate(bundle) is False, the bundle 512 For all bundles in this manifest, if predicate(bundle) is False, the bundle
492 is removed from the manifest. 513 is removed from the manifest.
493 514
494 Args: 515 Args:
495 predicate: a function that take a bundle and returns whether True to keep 516 predicate: a function that take a bundle and returns whether True to keep
496 it or False to remove it. 517 it or False to remove it.
497 """ 518 """
498 self._manifest_data[BUNDLES_KEY] = filter(predicate, self.GetBundles()) 519 self._manifest_data[BUNDLES_KEY] = filter(predicate, self.GetBundles())
499 520
500 def LoadDataFromString(self, json_string): 521 def LoadDataFromString(self, json_string, add_missing_info=False):
501 """Load a JSON manifest string. Raises an exception if json_string 522 """Load a JSON manifest string. Raises an exception if json_string
502 is not well-formed JSON. 523 is not well-formed JSON.
503 524
504 Args: 525 Args:
505 json_string: a JSON-formatted string containing the previous manifest 526 json_string: a JSON-formatted string containing the previous manifest
506 all_hosts: True indicates that we should load bundles for all hosts. 527 all_hosts: True indicates that we should load bundles for all hosts.
507 False (default) says to only load bundles for the current host""" 528 False (default) says to only load bundles for the current host"""
508 new_manifest = json.loads(json_string) 529 new_manifest = json.loads(json_string)
509 for key, value in new_manifest.items(): 530 for key, value in new_manifest.items():
510 if key == BUNDLES_KEY: 531 if key == BUNDLES_KEY:
511 # Remap each bundle in |value| to a Bundle instance 532 # Remap each bundle in |value| to a Bundle instance
512 bundles = [] 533 bundles = []
513 for b in value: 534 for b in value:
514 new_bundle = Bundle(b[NAME_KEY]) 535 new_bundle = Bundle(b[NAME_KEY])
515 new_bundle.CopyFrom(b) 536 new_bundle.CopyFrom(b)
516 bundles.append(new_bundle) 537 bundles.append(new_bundle)
517 self._manifest_data[key] = bundles 538 self._manifest_data[key] = bundles
518 else: 539 else:
519 self._manifest_data[key] = value 540 self._manifest_data[key] = value
520 self.Validate() 541 self.Validate(add_missing_info)
542
543 def __str__(self):
544 return self.GetDataAsString()
521 545
522 def GetDataAsString(self): 546 def GetDataAsString(self):
523 """Returns the current JSON manifest object, pretty-printed""" 547 """Returns the current JSON manifest object, pretty-printed"""
524 return DictToJSON(self._manifest_data) 548 return DictToJSON(self._manifest_data)
OLDNEW
« no previous file with comments | « native_client_sdk/src/build_tools/json/naclsdk_manifest2.json ('k') | native_client_sdk/src/build_tools/nacl-mono-buildbot.py » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698