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 |