blob: f631fccb8bb5655c1aff5cf254582d5acafbe35c [file] [log] [blame]
#!/usr/bin/env python
# Copyright 2016 The Chromium Authors. All rights reserved.
# Use of this source code is governed by a BSD-style license that can be
# found in the LICENSE file.
""" A collator for Service Manifests """
import argparse
import json
import os
import shutil
import sys
import urlparse
sys.path.append(os.path.join(
os.path.dirname(__file__), os.pardir, os.pardir, os.pardir, os.pardir,
os.pardir, "build", "android", "gyp"))
from util import build_utils
# Keys which are completely overridden by manifest overlays
_MANIFEST_OVERLAY_OVERRIDE_KEYS = [
"display_name",
]
# Keys which are merged with content from manifest overlays
_MANIFEST_OVERLAY_MERGE_KEYS = [
"interface_provider_specs",
"required_files",
]
eater_relative = "../../../../../../tools/json_comment_eater"
eater_relative = os.path.join(os.path.abspath(__file__), eater_relative)
sys.path.insert(0, os.path.normpath(eater_relative))
try:
import json_comment_eater
finally:
sys.path.pop(0)
def ParseJSONFile(filename):
with open(filename) as json_file:
try:
return json.loads(json_comment_eater.Nom(json_file.read()))
except ValueError as e:
print "%s is not a valid JSON document" % filename
raise e
def MergeDicts(left, right):
for k, v in right.iteritems():
if k not in left:
left[k] = v
else:
if isinstance(v, dict):
assert isinstance(left[k], dict)
MergeDicts(left[k], v)
elif isinstance(v, list):
assert isinstance(left[k], list)
left[k].extend(v)
else:
raise "Refusing to merge conflicting non-collection values."
return left
def MergeManifestOverlay(manifest, overlay):
for key in _MANIFEST_OVERLAY_MERGE_KEYS:
if key in overlay:
MergeDicts(manifest[key], overlay[key])
if "services" in overlay:
if "services" not in manifest:
manifest["services"] = []
manifest["services"].extend(overlay["services"])
for key in _MANIFEST_OVERLAY_OVERRIDE_KEYS:
if key in overlay:
manifest[key] = overlay[key]
def SanityCheckManifestServices(manifest):
"""Ensures any given service name appears only once within a manifest."""
known_services = set()
def has_no_dupes(root):
if "name" in root:
name = root["name"]
if name in known_services:
raise ValueError(
"Duplicate manifest entry found for service: %s" % name)
known_services.add(name)
if "services" not in root:
return True
return all(has_no_dupes(service) for service in root["services"])
return has_no_dupes(manifest)
def main():
parser = argparse.ArgumentParser(
description="Collate Service Manifests.")
parser.add_argument("--parent")
parser.add_argument("--output")
parser.add_argument("--name")
parser.add_argument("--pretty", action="store_true")
parser.add_argument("--overlays", nargs="+", dest="overlays", default=[])
parser.add_argument("--packaged-services", nargs="+",
dest="packaged_services", default=[])
args, _ = parser.parse_known_args()
parent = ParseJSONFile(args.parent)
service_name = parent["name"] if "name" in parent else ""
if args.name and args.name != service_name:
raise ValueError("Service name '%s' specified in build file does not " \
"match name '%s' specified in manifest." %
(args.name, service_name))
packaged_services = []
for child_manifest in args.packaged_services:
packaged_services.append(ParseJSONFile(child_manifest))
if len(packaged_services) > 0:
if "services" not in parent:
parent["services"] = packaged_services
else:
parent["services"].extend(packaged_services)
for overlay_path in args.overlays:
MergeManifestOverlay(parent, ParseJSONFile(overlay_path))
with build_utils.AtomicOutput(args.output) as f:
json.dump(parent, f, indent=2 if args.pretty else -1)
# NOTE: We do the sanity check and possible failure *after* outputting the
# aggregate manifest so it's easier to inspect erroneous output.
SanityCheckManifestServices(parent)
return 0
if __name__ == "__main__":
sys.exit(main())