summaryrefslogtreecommitdiff
path: root/buildstream/_loader/loader.py
diff options
context:
space:
mode:
Diffstat (limited to 'buildstream/_loader/loader.py')
-rw-r--r--buildstream/_loader/loader.py76
1 files changed, 68 insertions, 8 deletions
diff --git a/buildstream/_loader/loader.py b/buildstream/_loader/loader.py
index e0ceb4fb9..07b0de996 100644
--- a/buildstream/_loader/loader.py
+++ b/buildstream/_loader/loader.py
@@ -1,4 +1,3 @@
-#!/usr/bin/env python3
#
# Copyright (C) 2018 Codethink Limited
#
@@ -107,9 +106,13 @@ class Loader():
# First pass, recursively load files and populate our table of LoadElements
#
+ deps = []
+
for target in self._targets:
profile_start(Topics.LOAD_PROJECT, target)
- self._load_file(target, rewritable, ticker)
+ junction, name, loader = self._parse_name(target, rewritable, ticker)
+ loader._load_file(name, rewritable, ticker)
+ deps.append(Dependency(name, junction=junction))
profile_end(Topics.LOAD_PROJECT, target)
#
@@ -119,7 +122,8 @@ class Loader():
# Set up a dummy element that depends on all top-level targets
# to resolve potential circular dependencies between them
DummyTarget = namedtuple('DummyTarget', ['name', 'full_name', 'deps'])
- dummy = DummyTarget(name='', full_name='', deps=[Dependency(e) for e in self._targets])
+
+ dummy = DummyTarget(name='', full_name='', deps=deps)
self._elements[''] = dummy
profile_key = "_".join(t for t in self._targets)
@@ -127,17 +131,20 @@ class Loader():
self._check_circular_deps('')
profile_end(Topics.CIRCULAR_CHECK, profile_key)
+ ret = []
#
# Sort direct dependencies of elements by their dependency ordering
#
for target in self._targets:
profile_start(Topics.SORT_DEPENDENCIES, target)
- self._sort_dependencies(target)
+ junction, name, loader = self._parse_name(target, rewritable, ticker)
+ loader._sort_dependencies(name)
profile_end(Topics.SORT_DEPENDENCIES, target)
+ # Finally, wrap what we have into LoadElements and return the target
+ #
+ ret.append(loader._collect_element(name))
- # Finally, wrap what we have into LoadElements and return the target
- #
- return [self._collect_element(target) for target in self._targets]
+ return ret
# cleanup():
#
@@ -207,7 +214,33 @@ class Loader():
# Load the data and process any conditional statements therein
fullpath = os.path.join(self._basedir, filename)
- node = _yaml.load(fullpath, shortname=filename, copy_tree=rewritable)
+ try:
+ node = _yaml.load(fullpath, shortname=filename, copy_tree=rewritable)
+ except LoadError as e:
+ if e.reason == LoadErrorReason.MISSING_FILE:
+ # If we can't find the file, try to suggest plausible
+ # alternatives by stripping the element-path from the given
+ # filename, and verifying that it exists.
+ message = "Could not find element '{}' in elements directory '{}'".format(filename, self._basedir)
+ detail = None
+ elements_dir = os.path.relpath(self._basedir, self.project.directory)
+ element_relpath = os.path.relpath(filename, elements_dir)
+ if filename.startswith(elements_dir) and os.path.exists(os.path.join(self._basedir, element_relpath)):
+ detail = "Did you mean '{}'?".format(element_relpath)
+ raise LoadError(LoadErrorReason.MISSING_FILE,
+ message, detail=detail) from e
+ elif e.reason == LoadErrorReason.LOADING_DIRECTORY:
+ # If a <directory>.bst file exists in the element path,
+ # let's suggest this as a plausible alternative.
+ message = str(e)
+ detail = None
+ if os.path.exists(os.path.join(self._basedir, filename + '.bst')):
+ element_name = filename + '.bst'
+ detail = "Did you mean '{}'?\n".format(element_name)
+ raise LoadError(LoadErrorReason.LOADING_DIRECTORY,
+ message, detail=detail) from e
+ else:
+ raise
self._options.process_node(node)
element = LoadElement(node, filename, self)
@@ -538,3 +571,30 @@ class Loader():
return self._loaders[dep.junction]
else:
return self
+
+ # _parse_name():
+ #
+ # Get junction and base name of element along with loader for the sub-project
+ #
+ # Args:
+ # name (str): Name of target
+ # rewritable (bool): Whether the loaded files should be rewritable
+ # this is a bit more expensive due to deep copies
+ # ticker (callable): An optional function for tracking load progress
+ #
+ # Returns:
+ # (tuple): - (str): name of the junction element
+ # - (str): name of the element
+ # - (Loader): loader for sub-project
+ #
+ def _parse_name(self, name, rewritable, ticker):
+ # We allow to split only once since deep junctions names are forbidden.
+ # Users who want to refer to elements in sub-sub-projects are required
+ # to create junctions on the top level project.
+ junction_path = name.rsplit(':', 1)
+ if len(junction_path) == 1:
+ return None, junction_path[-1], self
+ else:
+ self._load_file(junction_path[-2], rewritable, ticker)
+ loader = self._get_loader(junction_path[-2], rewritable=rewritable, ticker=ticker)
+ return junction_path[-2], junction_path[-1], loader