summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorValentin David <valentin.david@codethink.co.uk>2018-10-03 19:42:35 +0200
committerValentin David <valentin.david@codethink.co.uk>2018-10-03 19:42:35 +0200
commit85b65dc7ff194f4cae471e66a2e338f8be42a178 (patch)
tree1edcfc8c0be22c71702f7eb0e9ee39b1122b219a
parentdae842fd0ba159ab8443fe279e8c8ba1de8871f8 (diff)
downloadbuildstream-85b65dc7ff194f4cae471e66a2e338f8be42a178.tar.gz
Add support for importing dependencies in sysroots.
This feature is useful when bootstraping a system.
-rw-r--r--buildstream/_loader/loadelement.py8
-rw-r--r--buildstream/_loader/loader.py28
-rw-r--r--buildstream/_loader/metaelement.py2
-rw-r--r--buildstream/element.py114
4 files changed, 117 insertions, 35 deletions
diff --git a/buildstream/_loader/loadelement.py b/buildstream/_loader/loadelement.py
index 4104dfd59..68f9dad92 100644
--- a/buildstream/_loader/loadelement.py
+++ b/buildstream/_loader/loadelement.py
@@ -72,8 +72,16 @@ class LoadElement():
'variables', 'environment', 'environment-nocache',
'config', 'public', 'description',
'build-depends', 'runtime-depends',
+ 'sysroots',
])
+ self.sysroots = []
+ sysroots = _yaml.node_get(node, list, 'sysroots', default_value=[])
+ for sysroot in sysroots:
+ _yaml.node_validate(sysroot, ['path', 'depends', 'build-depends', 'runtime-depends'])
+ path = _yaml.node_get(sysroot, str, 'path')
+ self.sysroots.append((path, _extract_depends_from_node(sysroot)))
+
# Extract the Dependencies
self.deps = _extract_depends_from_node(self.node)
diff --git a/buildstream/_loader/loader.py b/buildstream/_loader/loader.py
index 1b27d9d55..2335cb924 100644
--- a/buildstream/_loader/loader.py
+++ b/buildstream/_loader/loader.py
@@ -36,6 +36,14 @@ from . import MetaElement
from . import MetaSource
+def _dependencies(element):
+ for dep in element.deps:
+ yield dep
+ for path, deps in element.sysroots:
+ for dep in deps:
+ yield dep
+
+
# Loader():
#
# The Loader class does the heavy lifting of parsing target
@@ -122,9 +130,9 @@ 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'])
+ DummyTarget = namedtuple('DummyTarget', ['name', 'full_name', 'deps', 'sysroots'])
- dummy = DummyTarget(name='', full_name='', deps=deps)
+ dummy = DummyTarget(name='', full_name='', deps=deps, sysroots=[])
self._elements[''] = dummy
profile_key = "_".join(t for t in targets)
@@ -259,7 +267,7 @@ class Loader():
self._elements[filename] = element
# Load all dependency files for the new LoadElement
- for dep in element.deps:
+ for dep in _dependencies(element):
if dep.junction:
self._load_file(dep.junction, rewritable, ticker, fetch_subprojects)
loader = self._get_loader(dep.junction, rewritable=rewritable, ticker=ticker,
@@ -276,6 +284,7 @@ class Loader():
return element
+
# _check_circular_deps():
#
# Detect circular dependencies on LoadElements with
@@ -311,7 +320,7 @@ class Loader():
# Push / Check each dependency / Pop
check_elements[element_name] = True
- for dep in element.deps:
+ for dep in _dependencies(element):
loader = self._get_loader_for_dep(dep)
loader._check_circular_deps(dep.name, check_elements, validated)
del check_elements[element_name]
@@ -392,6 +401,8 @@ class Loader():
# directly or indirectly depends on another direct dependency,
# it is found later in the list.
element.deps.sort(key=cmp_to_key(dependency_cmp))
+ for path, deps in element.sysroots:
+ deps.sort(key=cmp_to_key(dependency_cmp))
visited[element_name] = True
@@ -459,6 +470,15 @@ class Loader():
if dep.dep_type != 'build':
meta_element.dependencies.append(meta_dep)
+ for path, deps in element.sysroots:
+ for dep in deps:
+ loader = self._get_loader_for_dep(dep)
+ meta_dep = loader._collect_element(dep.name)
+ if dep.dep_type != 'runtime':
+ meta_element.sysroot_build_dependencies.append((path, meta_dep))
+ if dep.dep_type != 'build':
+ meta_element.sysroot_dependencies.append((path, meta_dep))
+
return meta_element
# _get_loader():
diff --git a/buildstream/_loader/metaelement.py b/buildstream/_loader/metaelement.py
index c13d5591e..2a1a897c9 100644
--- a/buildstream/_loader/metaelement.py
+++ b/buildstream/_loader/metaelement.py
@@ -54,4 +54,6 @@ class MetaElement():
self.sandbox = sandbox
self.build_dependencies = []
self.dependencies = []
+ self.sysroot_build_dependencies = []
+ self.sysroot_dependencies = []
self.first_pass = first_pass
diff --git a/buildstream/element.py b/buildstream/element.py
index 320ba7a8a..331e32909 100644
--- a/buildstream/element.py
+++ b/buildstream/element.py
@@ -195,6 +195,7 @@ class Element(Plugin):
self.__runtime_dependencies = [] # Direct runtime dependency Elements
self.__build_dependencies = [] # Direct build dependency Elements
+ self.__sysroots = {}
self.__sources = [] # List of Sources
self.__weak_cache_key = None # Our cached weak cache key
self.__strict_cache_key = None # Our cached cache key for strict builds
@@ -374,7 +375,9 @@ class Element(Plugin):
for source in self.__sources:
yield source
- def dependencies(self, scope, *, recurse=True, visited=None, recursed=False):
+
+ def dependencies(self, scope, *, recurse=True, visited=None, recursed=False,
+ with_sysroot=False, sysroot='/'):
"""dependencies(scope, *, recurse=True)
A generator function which yields the dependencies of the given element.
@@ -399,40 +402,75 @@ class Element(Plugin):
scope_set = set((Scope.BUILD, Scope.RUN)) if scope == Scope.ALL else set((scope,))
- if full_name in visited and scope_set.issubset(visited[full_name]):
+ if (sysroot, full_name) in visited and scope_set.issubset(visited[(sysroot, full_name)]):
return
should_yield = False
if full_name not in visited:
- visited[full_name] = scope_set
+ visited[(sysroot, full_name)] = scope_set
should_yield = True
else:
- visited[full_name] |= scope_set
+ visited[(sysroot, full_name)] |= scope_set
if recurse or not recursed:
if scope == Scope.ALL:
for dep in self.__build_dependencies:
yield from dep.dependencies(Scope.ALL, recurse=recurse,
- visited=visited, recursed=True)
+ visited=visited, recursed=True,
+ sysroot=sysroot, with_sysroot=with_sysroot)
for dep in self.__runtime_dependencies:
if dep not in self.__build_dependencies:
yield from dep.dependencies(Scope.ALL, recurse=recurse,
- visited=visited, recursed=True)
+ visited=visited, recursed=True,
+ sysroot=sysroot, with_sysroot=with_sysroot)
+
+ for path, value in self.__sysroots.items():
+ new_sysroot = path if not recursed else sysroot
+ run_deps, build_deps = value
+ for dep in build_deps:
+ yield from dep.dependencies(Scope.ALL, recurse=recurse,
+ visited=visited, recursed=True,
+ sysroot=new_sysroot, with_sysroot=with_sysroot)
+ for dep in run_deps:
+ if dep not in build_deps:
+ yield from dep.dependencies(Scope.ALL, recurse=recurse,
+ visited=visited, recursed=True,
+ sysroot=new_sysroot, with_sysroot=with_sysroot)
elif scope == Scope.BUILD:
for dep in self.__build_dependencies:
yield from dep.dependencies(Scope.RUN, recurse=recurse,
- visited=visited, recursed=True)
+ visited=visited, recursed=True,
+ sysroot=sysroot, with_sysroot=with_sysroot)
+ for path, value in self.__sysroots.items():
+ new_sysroot = path if not recursed else sysroot
+ run_deps, build_deps = value
+ for dep in build_deps:
+ yield from dep.dependencies(Scope.RUN, recurse=recurse,
+ visited=visited, recursed=True,
+ sysroot=new_sysroot, with_sysroot=with_sysroot)
elif scope == Scope.RUN:
for dep in self.__runtime_dependencies:
yield from dep.dependencies(Scope.RUN, recurse=recurse,
- visited=visited, recursed=True)
+ visited=visited, recursed=True,
+ sysroot=sysroot, with_sysroot=with_sysroot)
+ for path, value in self.__sysroots.items():
+ new_sysroot = path if not recursed else sysroot
+ run_deps, build_deps = value
+ for dep in run_deps:
+ yield from dep.dependencies(Scope.RUN, recurse=recurse,
+ visited=visited, recursed=True,
+ sysroot=new_sysroot, with_sysroot=with_sysroot)
# Yeild self only at the end, after anything needed has been traversed
if should_yield and (recurse or recursed) and (scope == Scope.ALL or scope == Scope.RUN):
- yield self
+ if with_sysroot:
+ yield sysroot, self
+ else:
+ yield self
+
def search(self, scope, name):
"""Search for a dependency by name
@@ -631,7 +669,7 @@ class Element(Plugin):
vbasedir = sandbox.get_virtual_directory()
vstagedir = vbasedir \
if path is None \
- else vbasedir.descend(path.lstrip(os.sep).split(os.sep))
+ else vbasedir.descend(path.lstrip(os.sep).split(os.sep), create=True)
files = list(self.__compute_splits(include, exclude, orphans))
@@ -649,7 +687,8 @@ class Element(Plugin):
return link_result.combine(copy_result)
def stage_dependency_artifacts(self, sandbox, scope, *, path=None,
- include=None, exclude=None, orphans=True):
+ include=None, exclude=None, orphans=True,
+ build=True):
"""Stage element dependencies in scope
This is primarily a convenience wrapper around
@@ -679,7 +718,7 @@ class Element(Plugin):
if self.__can_build_incrementally() and workspace.last_successful:
old_dep_keys = self.__get_artifact_metadata_dependencies(workspace.last_successful)
- for dep in self.dependencies(scope):
+ for sysroot, dep in self.dependencies(scope, with_sysroot=True):
# If we are workspaced, and we therefore perform an
# incremental build, we must ensure that we update the mtimes
# of any files created by our dependencies since the last
@@ -704,8 +743,13 @@ class Element(Plugin):
if utils._is_main_process():
self._get_context().get_workspaces().save_config()
+ if build:
+ sub_path = os.path.join(path, os.path.relpath(sysroot, '/')) if path else sysroot
+ else:
+ sub_path = path
+
result = dep.stage_artifact(sandbox,
- path=path,
+ path=sub_path,
include=include,
exclude=exclude,
orphans=orphans,
@@ -909,6 +953,17 @@ class Element(Plugin):
dependency = Element._new_from_meta(meta_dep, artifacts)
element.__build_dependencies.append(dependency)
+ for path, meta_dep in meta.sysroot_dependencies:
+ if path not in element.__sysroots:
+ element.__sysroots[path] = ([], [])
+ dependency = Element._new_from_meta(meta_dep, artifacts)
+ element.__sysroots[path][0].append(dependency)
+ for path, meta_dep in meta.sysroot_build_dependencies:
+ if path not in element.__sysroots:
+ element.__sysroots[path] = ([], [])
+ dependency = Element._new_from_meta(meta_dep, artifacts)
+ element.__sysroots[path][1].append(dependency)
+
return element
# _get_redundant_source_refs()
@@ -1086,17 +1141,17 @@ class Element(Plugin):
# Calculate weak cache key
# Weak cache key includes names of direct build dependencies
# but does not include keys of dependencies.
+ dependencies = []
if self.BST_STRICT_REBUILD:
- dependencies = [
- e._get_cache_key(strength=_KeyStrength.WEAK)
- for e in self.dependencies(Scope.BUILD)
- ]
+ for sysroot, e in self.dependencies(Scope.BUILD, with_sysroot=True):
+ dependencies = [(sysroot, e._get_cache_key(strength=_KeyStrength.WEAK))
+ for sysroot, e in self.dependencies(Scope.BUILD, with_sysroot=True)]
else:
- dependencies = [
- e.name for e in self.dependencies(Scope.BUILD, recurse=False)
- ]
+ for sysroot, e in self.dependencies(Scope.BUILD, with_sysroot=True):
+ dependencies = [(sysroot, e.name)
+ for sysroot, e in self.dependencies(Scope.BUILD, with_sysroot=True)]
- self.__weak_cache_key = self.__calculate_cache_key(dependencies)
+ self.__weak_cache_key = self.__calculate_cache_key(sorted(dependencies))
if self.__weak_cache_key is None:
# Weak cache key could not be calculated yet
@@ -1122,10 +1177,9 @@ class Element(Plugin):
return
if self.__strict_cache_key is None:
- dependencies = [
- e.__strict_cache_key for e in self.dependencies(Scope.BUILD)
- ]
- self.__strict_cache_key = self.__calculate_cache_key(dependencies)
+ dependencies = [(sysroot, e.__strict_cache_key)
+ for sysroot, e in self.dependencies(Scope.BUILD, with_sysroot=True)]
+ self.__strict_cache_key = self.__calculate_cache_key(sorted(dependencies))
if self.__strict_cache_key is None:
# Strict cache key could not be calculated yet
@@ -1164,11 +1218,9 @@ class Element(Plugin):
strong_key, _ = self.__get_artifact_metadata_keys()
self.__cache_key = strong_key
elif self.__assemble_scheduled or self.__assemble_done:
- # Artifact will or has been built, not downloaded
- dependencies = [
- e._get_cache_key() for e in self.dependencies(Scope.BUILD)
- ]
- self.__cache_key = self.__calculate_cache_key(dependencies)
+ dependencies = [(sysroot, e._get_cache_key())
+ for sysroot, e in self.dependencies(Scope.BUILD, with_sysroot=True)]
+ self.__cache_key = self.__calculate_cache_key(sorted(dependencies))
if self.__cache_key is None:
# Strong cache key could not be calculated yet
@@ -1329,7 +1381,7 @@ class Element(Plugin):
# Stage deps in the sandbox root
if deps == 'run':
with self.timed_activity("Staging dependencies", silent_nested=True):
- self.stage_dependency_artifacts(sandbox, scope)
+ self.stage_dependency_artifacts(sandbox, scope, build=False)
# Run any integration commands provided by the dependencies
# once they are all staged and ready