| OLD | NEW |
| 1 #!/usr/bin/env python | 1 #!/usr/bin/env python |
| 2 # Copyright 2016 The Chromium Authors. All rights reserved. | 2 # Copyright 2016 The Chromium Authors. All rights reserved. |
| 3 # Use of this source code is governed by a BSD-style license that can be | 3 # Use of this source code is governed by a BSD-style license that can be |
| 4 # found in the LICENSE file. | 4 # found in the LICENSE file. |
| 5 | 5 |
| 6 """ A collator for Service Manifests """ | 6 """ A collator for Service Manifests """ |
| 7 | 7 |
| 8 import argparse | 8 import argparse |
| 9 import json | 9 import json |
| 10 import os | 10 import os |
| 11 import shutil | 11 import shutil |
| 12 import sys | 12 import sys |
| 13 import urlparse | 13 import urlparse |
| 14 | 14 |
| 15 |
| 16 # Keys which are completely overridden by manifest overlays |
| 17 _MANIFEST_OVERLAY_OVERRIDE_KEYS = [ |
| 18 "display_name", |
| 19 "process-group", |
| 20 ] |
| 21 |
| 15 eater_relative = '../../../../../../tools/json_comment_eater' | 22 eater_relative = '../../../../../../tools/json_comment_eater' |
| 16 eater_relative = os.path.join(os.path.abspath(__file__), eater_relative) | 23 eater_relative = os.path.join(os.path.abspath(__file__), eater_relative) |
| 17 sys.path.insert(0, os.path.normpath(eater_relative)) | 24 sys.path.insert(0, os.path.normpath(eater_relative)) |
| 18 try: | 25 try: |
| 19 import json_comment_eater | 26 import json_comment_eater |
| 20 finally: | 27 finally: |
| 21 sys.path.pop(0) | 28 sys.path.pop(0) |
| 22 | 29 |
| 30 |
| 23 def ParseJSONFile(filename): | 31 def ParseJSONFile(filename): |
| 24 with open(filename) as json_file: | 32 with open(filename) as json_file: |
| 25 try: | 33 try: |
| 26 return json.loads(json_comment_eater.Nom(json_file.read())) | 34 return json.loads(json_comment_eater.Nom(json_file.read())) |
| 27 except ValueError: | 35 except ValueError as e: |
| 28 print "%s is not a valid JSON document" % filename | 36 print "%s is not a valid JSON document" % filename |
| 29 return None | 37 raise e |
| 38 |
| 30 | 39 |
| 31 def MergeDicts(left, right): | 40 def MergeDicts(left, right): |
| 32 for k, v in right.iteritems(): | 41 for k, v in right.iteritems(): |
| 33 if k not in left: | 42 if k not in left: |
| 34 left[k] = v | 43 left[k] = v |
| 35 else: | 44 else: |
| 36 if isinstance(v, dict): | 45 if isinstance(v, dict): |
| 37 assert isinstance(left[k], dict) | 46 assert isinstance(left[k], dict) |
| 38 MergeDicts(left[k], v) | 47 MergeDicts(left[k], v) |
| 39 elif isinstance(v, list): | 48 elif isinstance(v, list): |
| 40 assert isinstance(left[k], list) | 49 assert isinstance(left[k], list) |
| 41 left[k].extend(v) | 50 left[k].extend(v) |
| 42 else: | 51 else: |
| 43 raise "Refusing to merge conflicting non-collection values." | 52 raise "Refusing to merge conflicting non-collection values." |
| 44 return left | 53 return left |
| 45 | 54 |
| 46 | 55 |
| 47 def MergeBaseManifest(parent, base): | 56 def MergeManifestOverlay(manifest, overlay): |
| 48 MergeDicts(parent["capabilities"], base["capabilities"]) | 57 MergeDicts(manifest["capabilities"], overlay["capabilities"]) |
| 49 | 58 |
| 50 if "services" in base: | 59 if "services" in overlay: |
| 51 if "services" not in parent: | 60 if "services" not in manifest: |
| 52 parent["services"] = [] | 61 manifest["services"] = [] |
| 53 parent["services"].extend(base["services"]) | 62 manifest["services"].extend(overlay["services"]) |
| 54 | 63 |
| 55 if "process-group" in base: | 64 for key in _MANIFEST_OVERLAY_OVERRIDE_KEYS: |
| 56 parent["process-group"] = base["process-group"] | 65 if key in overlay: |
| 66 manifest[key] = overlay[key] |
| 57 | 67 |
| 58 | 68 |
| 59 def main(): | 69 def main(): |
| 60 parser = argparse.ArgumentParser( | 70 parser = argparse.ArgumentParser( |
| 61 description="Collate Service Manifests.") | 71 description="Collate Service Manifests.") |
| 62 parser.add_argument("--parent") | 72 parser.add_argument("--parent") |
| 63 parser.add_argument("--output") | 73 parser.add_argument("--output") |
| 64 parser.add_argument("--name") | 74 parser.add_argument("--name") |
| 65 parser.add_argument("--base-manifest", default=None) | 75 parser.add_argument("--overlays", nargs="+", dest="overlays", default=[]) |
| 66 args, children = parser.parse_known_args() | 76 args, children = parser.parse_known_args() |
| 67 | 77 |
| 68 parent = ParseJSONFile(args.parent) | 78 parent = ParseJSONFile(args.parent) |
| 69 if parent == None: | |
| 70 return 1 | |
| 71 | |
| 72 if args.base_manifest: | |
| 73 base = ParseJSONFile(args.base_manifest) | |
| 74 if base == None: | |
| 75 return 1 | |
| 76 MergeBaseManifest(parent, base) | |
| 77 | 79 |
| 78 service_path = parent['name'].split(':')[1] | 80 service_path = parent['name'].split(':')[1] |
| 79 if service_path.startswith('//'): | 81 if service_path.startswith('//'): |
| 80 raise ValueError("Service name path component '%s' must not start " \ | 82 raise ValueError("Service name path component '%s' must not start " \ |
| 81 "with //" % service_path) | 83 "with //" % service_path) |
| 82 | 84 |
| 83 if args.name != service_path: | 85 if args.name != service_path: |
| 84 raise ValueError("Service name '%s' specified in build file does not " \ | 86 raise ValueError("Service name '%s' specified in build file does not " \ |
| 85 "match name '%s' specified in manifest." % | 87 "match name '%s' specified in manifest." % |
| 86 (args.name, service_path)) | 88 (args.name, service_path)) |
| 87 | 89 |
| 88 services = [] | 90 services = [] |
| 89 for child in children: | 91 for child in children: |
| 90 service = ParseJSONFile(child) | 92 services.append(ParseJSONFile(child)) |
| 91 if service == None: | |
| 92 return 1 | |
| 93 services.append(service) | |
| 94 | 93 |
| 95 if len(services) > 0: | 94 if len(services) > 0: |
| 96 parent['services'] = services | 95 parent['services'] = services |
| 97 | 96 |
| 97 for overlay_path in args.overlays: |
| 98 MergeManifestOverlay(parent, ParseJSONFile(overlay_path)) |
| 99 |
| 98 with open(args.output, 'w') as output_file: | 100 with open(args.output, 'w') as output_file: |
| 99 json.dump(parent, output_file) | 101 json.dump(parent, output_file) |
| 100 | 102 |
| 101 return 0 | 103 return 0 |
| 102 | 104 |
| 103 if __name__ == "__main__": | 105 if __name__ == "__main__": |
| 104 sys.exit(main()) | 106 sys.exit(main()) |
| OLD | NEW |