From cd6d0572ef70b93636d7940d06ad0b13e0aca486 Mon Sep 17 00:00:00 2001 From: Valentin David Date: Sun, 10 May 2020 17:07:06 +0200 Subject: Allow junctions to inject dependencies from current project to junctioned one There are cases where downstream projects want to overwrite upstream elements. So far, either the downstream project just allows overlaps and hopes that ABI is compatible. There are also files left from original elements when not overwritten. Or downstream adds an "overlay" with local source to the junctions. However on the latter, elements in the overlay are considered as part of upstream, which means in cannot depend on element outside of upstream (unless cyclic junction maybe?). Here we add a feature to junctions to allow injecting elements from current project to junctions. This is useful for example in the case of GNOME SDK needing to override GLib, GObjectIntrospection, libsoup, etc. from Freedesktop SDK. --- buildstream/_loader/loader.py | 13 +++++++++++++ buildstream/plugins/elements/junction.py | 1 + tests/loader/junctions.py | 16 ++++++++++++++++ tests/loader/junctions/replacements-base/a.bst | 4 ++++ tests/loader/junctions/replacements-base/compose.bst | 3 +++ tests/loader/junctions/replacements-base/original.txt | 1 + tests/loader/junctions/replacements-base/project.conf | 1 + tests/loader/junctions/replacements/base.bst | 7 +++++++ tests/loader/junctions/replacements/injected.bst | 6 ++++++ tests/loader/junctions/replacements/injected.txt | 1 + tests/loader/junctions/replacements/project.conf | 5 +++++ tests/loader/junctions/replacements/stack.bst | 3 +++ 12 files changed, 61 insertions(+) create mode 100644 tests/loader/junctions/replacements-base/a.bst create mode 100644 tests/loader/junctions/replacements-base/compose.bst create mode 100644 tests/loader/junctions/replacements-base/original.txt create mode 100644 tests/loader/junctions/replacements-base/project.conf create mode 100644 tests/loader/junctions/replacements/base.bst create mode 100644 tests/loader/junctions/replacements/injected.bst create mode 100644 tests/loader/junctions/replacements/injected.txt create mode 100644 tests/loader/junctions/replacements/project.conf create mode 100644 tests/loader/junctions/replacements/stack.bst diff --git a/buildstream/_loader/loader.py b/buildstream/_loader/loader.py index ec929eadb..dc15109c8 100644 --- a/buildstream/_loader/loader.py +++ b/buildstream/_loader/loader.py @@ -217,6 +217,13 @@ class Loader(): if ticker: ticker(filename) + if self.project.junction and filename in self.project.junction.replacements: + replacement = _yaml.node_get(self.project.junction.replacements, str, filename) + elt = self._parent._load_file(replacement, + rewritable, ticker, fetch_subprojects, provenance=provenance) + self._elements[filename] = elt + return elt + # Load the data and process any conditional statements therein fullpath = os.path.join(self._basedir, filename) try: @@ -311,6 +318,8 @@ class Loader(): validated = {} element = self._elements[element_name] + if getattr(element, '_loader', self) != self: + return element._loader._check_circular_deps(element.name, check_elements, validated) # element name must be unique across projects # to be usable as key for the check_elements and validated dicts @@ -353,6 +362,8 @@ class Loader(): visited = {} element = self._elements[element_name] + if getattr(element, '_loader', self) != self: + return element._loader._sort_dependencies(element.name, visited) # element name must be unique across projects # to be usable as key for the visited dict @@ -424,6 +435,8 @@ class Loader(): def _collect_element(self, element_name): element = self._elements[element_name] + if element._loader != self: + return element._loader._collect_element(element.name) # Return the already built one, if we already built it meta_element = self._meta_elements.get(element_name) diff --git a/buildstream/plugins/elements/junction.py b/buildstream/plugins/elements/junction.py index d2c62fe48..f87ac2273 100644 --- a/buildstream/plugins/elements/junction.py +++ b/buildstream/plugins/elements/junction.py @@ -142,6 +142,7 @@ class JunctionElement(Element): def configure(self, node): self.path = self.node_get_member(node, str, 'path', default='') self.options = self.node_get_member(node, Mapping, 'options', default={}) + self.replacements = self.node_get_member(node, Mapping, 'replacements', default={}) def preflight(self): pass diff --git a/tests/loader/junctions.py b/tests/loader/junctions.py index aee164396..0daa7e9f6 100644 --- a/tests/loader/junctions.py +++ b/tests/loader/junctions.py @@ -330,3 +330,19 @@ def test_build_git_cross_junction_names(cli, tmpdir, datafiles): # Check that the checkout contains the expected files from both projects assert(os.path.exists(os.path.join(checkoutdir, 'base.txt'))) + + +@pytest.mark.datafiles(DATA_DIR) +def test_replacements(cli, tmpdir, datafiles): + project = os.path.join(str(datafiles), "replacements") + copy_subprojects(project, datafiles, ["replacements-base"]) + checkoutdir = os.path.join(str(tmpdir), "checkout") + + # Build, checkout + result = cli.run(project=project, args=["build", "stack.bst"]) + result.assert_success() + result = cli.run(project=project, args=["checkout", "stack.bst", checkoutdir]) + result.assert_success() + + assert os.path.exists(os.path.join(checkoutdir, "destination/injected.txt")) + assert not os.path.exists(os.path.join(checkoutdir, "original.txt")) diff --git a/tests/loader/junctions/replacements-base/a.bst b/tests/loader/junctions/replacements-base/a.bst new file mode 100644 index 000000000..61edc6467 --- /dev/null +++ b/tests/loader/junctions/replacements-base/a.bst @@ -0,0 +1,4 @@ +kind: import +sources: +- kind: local + path: original.txt diff --git a/tests/loader/junctions/replacements-base/compose.bst b/tests/loader/junctions/replacements-base/compose.bst new file mode 100644 index 000000000..1b4024228 --- /dev/null +++ b/tests/loader/junctions/replacements-base/compose.bst @@ -0,0 +1,3 @@ +kind: compose +build-depends: +- a.bst diff --git a/tests/loader/junctions/replacements-base/original.txt b/tests/loader/junctions/replacements-base/original.txt new file mode 100644 index 000000000..5762c898d --- /dev/null +++ b/tests/loader/junctions/replacements-base/original.txt @@ -0,0 +1 @@ +Original diff --git a/tests/loader/junctions/replacements-base/project.conf b/tests/loader/junctions/replacements-base/project.conf new file mode 100644 index 000000000..9e10ecac4 --- /dev/null +++ b/tests/loader/junctions/replacements-base/project.conf @@ -0,0 +1 @@ +name: replacements-base diff --git a/tests/loader/junctions/replacements/base.bst b/tests/loader/junctions/replacements/base.bst new file mode 100644 index 000000000..d128ae54d --- /dev/null +++ b/tests/loader/junctions/replacements/base.bst @@ -0,0 +1,7 @@ +kind: junction +config: + replacements: + a.bst: injected.bst +sources: +- kind: local + path: replacements-base diff --git a/tests/loader/junctions/replacements/injected.bst b/tests/loader/junctions/replacements/injected.bst new file mode 100644 index 000000000..3f822c711 --- /dev/null +++ b/tests/loader/junctions/replacements/injected.bst @@ -0,0 +1,6 @@ +kind: import +config: + target: "%{dest}" +sources: +- kind: local + path: injected.txt diff --git a/tests/loader/junctions/replacements/injected.txt b/tests/loader/junctions/replacements/injected.txt new file mode 100644 index 000000000..43a3eba08 --- /dev/null +++ b/tests/loader/junctions/replacements/injected.txt @@ -0,0 +1 @@ +Injected diff --git a/tests/loader/junctions/replacements/project.conf b/tests/loader/junctions/replacements/project.conf new file mode 100644 index 000000000..b55220934 --- /dev/null +++ b/tests/loader/junctions/replacements/project.conf @@ -0,0 +1,5 @@ +name: replacements + +variables: + # This variable is not defined in the junctioned project + dest: "/destination" diff --git a/tests/loader/junctions/replacements/stack.bst b/tests/loader/junctions/replacements/stack.bst new file mode 100644 index 000000000..0ab52e878 --- /dev/null +++ b/tests/loader/junctions/replacements/stack.bst @@ -0,0 +1,3 @@ +kind: stack +runtime-depends: +- base.bst:compose.bst -- cgit v1.2.1