summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorTristan Van Berkom <tristan.vanberkom@codethink.co.uk>2018-02-20 18:39:36 +0900
committerTristan Van Berkom <tristan.vanberkom@codethink.co.uk>2018-03-20 17:46:41 +0900
commita39927a8ffb3a86489cfca5bc4953dfeab4b9fee (patch)
tree3fb9965520cd12c1d647189ee007c46e8a7b0515
parent1fbedea8ce1a32e5e9bb86479add3c24ba8b1f36 (diff)
downloadbuildstream-a39927a8ffb3a86489cfca5bc4953dfeab4b9fee.tar.gz
_projectrefs.py: Adding the ProjectRefs refs management object
This object manages the project.refs file in a project directory.
-rw-r--r--buildstream/_projectrefs.py165
1 files changed, 165 insertions, 0 deletions
diff --git a/buildstream/_projectrefs.py b/buildstream/_projectrefs.py
new file mode 100644
index 000000000..bf58e1e81
--- /dev/null
+++ b/buildstream/_projectrefs.py
@@ -0,0 +1,165 @@
+#!/usr/bin/env python3
+#
+# Copyright (C) 2018 Codethink Limited
+#
+# This program is free software; you can redistribute it and/or
+# modify it under the terms of the GNU Lesser General Public
+# License as published by the Free Software Foundation; either
+# version 2 of the License, or (at your option) any later version.
+#
+# This library is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+# Lesser General Public License for more details.
+#
+# You should have received a copy of the GNU Lesser General Public
+# License along with this library. If not, see <http://www.gnu.org/licenses/>.
+#
+# Authors:
+# Tristan Van Berkom <tristan.vanberkom@codethink.co.uk>
+import os
+from collections import Mapping
+
+from . import _yaml
+from ._exceptions import LoadError, LoadErrorReason
+
+
+# ProjectRefStorage()
+#
+# Indicates the type of ref storage
+class ProjectRefStorage():
+
+ # Source references are stored inline
+ #
+ INLINE = 'inline'
+
+ # Source references are stored in a central project.refs file
+ #
+ PROJECT_REFS = 'project.refs'
+
+
+# ProjectRefs()
+#
+# The project.refs file management
+#
+class ProjectRefs():
+
+ def __init__(self, directory):
+
+ self.directory = os.path.abspath(directory)
+ self.fullpath = os.path.join(self.directory, "project.refs")
+
+ self.toplevel_node = None
+ self.toplevel_save = None
+
+ # load()
+ #
+ # Load the project.refs file
+ #
+ # Args:
+ # options (OptionPool): To resolve conditional statements
+ #
+ def load(self, options):
+
+ try:
+ self.toplevel_node = _yaml.load(self.fullpath, shortname='project.refs', copy_tree=True)
+ provenance = _yaml.node_get_provenance(self.toplevel_node)
+ self.toplevel_save = provenance.toplevel
+
+ # Process any project options immediately
+ options.process_node(self.toplevel_node)
+
+ # Run any final assertions on the project.refs, just incase there
+ # are list composition directives or anything left unprocessed.
+ _yaml.node_final_assertions(self.toplevel_node)
+
+ except LoadError as e:
+ if e.reason != LoadErrorReason.MISSING_FILE:
+ raise
+
+ # Ignore failure if the file doesnt exist, it'll be created and
+ # for now just assumed to be empty
+ self.toplevel_node = {}
+ self.toplevel_save = self.toplevel_node
+
+ _yaml.node_validate(self.toplevel_node, ['projects'])
+
+ # Ensure we create our toplevel entry point on the fly here
+ for node in [self.toplevel_node, self.toplevel_save]:
+ if 'projects' not in node:
+ node['projects'] = {}
+
+ # save()
+ #
+ # Save the project.refs file with any local changes
+ #
+ def save(self):
+ _yaml.dump(self.toplevel_save, self.fullpath)
+
+ # lookup_ref()
+ #
+ # Fetch the ref node for a given Source. If the ref node does not
+ # exist and `write` is specified, it will be automatically created.
+ #
+ # Args:
+ # project (str): The project to lookup
+ # element (str): The element name to lookup
+ # source_index (int): The index of the Source in the specified element
+ # write (bool): Whether we want to read the node or write to it
+ #
+ # Returns:
+ # (node): The YAML dictionary where the ref is stored
+ #
+ def lookup_ref(self, project, element, source_index, *, write=False):
+
+ node = self._lookup(self.toplevel_node, project, element, source_index)
+
+ if write:
+
+ if node is not None:
+ provenance = _yaml.node_get_provenance(node)
+ if provenance:
+ node = provenance.node
+
+ # If we couldnt find the orignal, create a new one.
+ #
+ if node is None:
+ node = self._lookup(self.toplevel_save, project, element, source_index, ensure=True)
+
+ return node
+
+ # _lookup()
+ #
+ # Looks up a ref node in the project.refs file, creates one if ensure is True.
+ #
+ def _lookup(self, toplevel, project, element, source_index, *, ensure=False):
+
+ # Fetch the project
+ try:
+ project_node = toplevel['projects'][project]
+ except KeyError:
+ if not ensure:
+ return None
+ project_node = toplevel['projects'][project] = {}
+
+ # Fetch the element
+ try:
+ element_list = project_node[element]
+ except KeyError:
+ if not ensure:
+ return None
+ element_list = project_node[element] = []
+
+ # Fetch the source index
+ try:
+ node = element_list[source_index]
+ except IndexError:
+ if not ensure:
+ return None
+
+ # Pad the list with empty newly created dictionaries
+ element_list.extend({} for _ in range(len(element_list), source_index + 1))
+
+ node = element_list[source_index]
+
+ return node