phosphor-settings: Add task to handle settings overrides

Add a BitBake task that merges YAML fragments that overwrite the default
settings YAML file. These fragments have the suffix ".override.yml" and
allow e.g. machine layers to override the values in settings.yaml
without replacing its entire contents.

Fixes openbmc/openbmc#1269.

Signed-off-by: Xo Wang <xow@google.com>
Change-Id: I8d463630deee74ed6d5c18aa98e837a6ca7f7633
diff --git a/common/recipes-phosphor/settings/phosphor-settings.bb b/common/recipes-phosphor/settings/phosphor-settings.bb
index 686f3c0..d03eca8 100644
--- a/common/recipes-phosphor/settings/phosphor-settings.bb
+++ b/common/recipes-phosphor/settings/phosphor-settings.bb
@@ -7,6 +7,7 @@
 inherit obmc-phosphor-license
 inherit setuptools
 inherit obmc-phosphor-dbus-service
+inherit pythonnative
 
 DBUS_SERVICE_${PN} = "org.openbmc.settings.Host.service"
 
@@ -16,7 +17,35 @@
 RPROVIDES_${PN} += "virtual-obmc-settings-mgmt"
 
 SRC_URI += "git://github.com/openbmc/phosphor-settingsd"
+SRC_URI += "file://merge_settings.py"
 
 SRCREV = "3b8d055ca761a68c74dad01a306f238674d71878"
 
 S = "${WORKDIR}/git"
+
+# Collect files in SRC_URI that end in ".override.yml" and call a script that
+# writes their contents over that of settings.yaml, which is then updated to
+# the merged data values.
+# This doesn't correctly handle globs in ".override.yml" entries in SRC_URI.
+python do_merge_settings () {
+    import subprocess
+
+    # TODO: Perform the merge in a temporary directory?
+    workdir = d.getVar('WORKDIR', True)
+    cmd = []
+    cmd.append(os.path.join(workdir, 'merge_settings.py'))
+    cmd.append(os.path.join(d.getVar('S', True), 'settings.yaml'))
+
+    fetch = bb.fetch2.Fetch([], d)
+    override_urls = filter(lambda f: f.endswith('.override.yml'), fetch.urls)
+    for url in override_urls:
+        bb.debug(2, 'Overriding with source: ' + url)
+        local_base = os.path.basename(fetch.localpath(url))
+        filename = os.path.join(workdir, local_base)
+        cmd.append(filename)
+
+    # Invoke the script and don't catch any resulting exception.
+    subprocess.check_call(cmd)
+}
+# python-pyyaml-native is installed by do_configure, so put this task after
+addtask merge_settings after do_configure before do_compile