summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorbst-marge-bot <marge-bot@buildstream.build>2019-04-18 17:15:12 +0000
committerbst-marge-bot <marge-bot@buildstream.build>2019-04-18 17:15:12 +0000
commit722601379f68ad2921204f24963755b2ecfc1744 (patch)
tree94f8b0554aebc72bf3a259eb2254827307f1ab8c
parente570c681a44cf86d22a93e6893e122d97d4b51ff (diff)
parent28aee4c66a5ba170c4d9a43e7905e63a83134c51 (diff)
downloadbuildstream-722601379f68ad2921204f24963755b2ecfc1744.tar.gz
Merge branch 'chandan/pseudo-junction' into 'master'
Add support for defining target for junction elements See merge request BuildStream/buildstream!1293
-rw-r--r--NEWS3
-rw-r--r--buildstream/_loader/loader.py12
-rw-r--r--buildstream/_versions.py2
-rw-r--r--buildstream/plugins/elements/junction.py64
-rw-r--r--tests/format/junctions.py54
-rw-r--r--tests/format/junctions/config-target/elements/invalid-source-target.bst8
-rw-r--r--tests/format/junctions/config-target/elements/nested-junction-target.bst4
-rw-r--r--tests/format/junctions/config-target/elements/no-junction.bst4
-rw-r--r--tests/format/junctions/config-target/elements/subproject.bst5
-rw-r--r--tests/format/junctions/config-target/elements/subsubproject.bst4
-rw-r--r--tests/format/junctions/config-target/elements/target.bst4
-rw-r--r--tests/format/junctions/config-target/project.conf3
-rw-r--r--tests/format/junctions/config-target/subproject/elements/subsubproject-junction.bst5
-rw-r--r--tests/format/junctions/config-target/subproject/project.conf3
-rw-r--r--tests/format/junctions/config-target/subproject/subsubproject/elements/hello.bst5
-rw-r--r--tests/format/junctions/config-target/subproject/subsubproject/files/hello.txt1
-rw-r--r--tests/format/junctions/config-target/subproject/subsubproject/project.conf3
17 files changed, 181 insertions, 3 deletions
diff --git a/NEWS b/NEWS
index 7e5cbeb98..75e6f1f9f 100644
--- a/NEWS
+++ b/NEWS
@@ -85,6 +85,9 @@ buildstream 1.3.1
o Elements may now specify cross-junction dependencies as simple strings
using the format '{junction-name}:{element-name}'.
+ o Junction elements may now specify another junction as their target, using
+ the `target` configuration option.
+
o Source plugins may now request access access to previous during track and
fetch by setting `BST_REQUIRES_PREVIOUS_SOURCES_TRACK` and/or
`BST_REQUIRES_PREVIOUS_SOURCES_FETCH` attributes.
diff --git a/buildstream/_loader/loader.py b/buildstream/_loader/loader.py
index 6d8310cfa..31d721b07 100644
--- a/buildstream/_loader/loader.py
+++ b/buildstream/_loader/loader.py
@@ -519,6 +519,18 @@ class Loader():
element = Element._new_from_meta(meta_element)
element._preflight()
+ # If this junction element points to a sub-sub-project, we need to
+ # find loader for that project.
+ if element.target:
+ subproject_loader = self._get_loader(element.target_junction, rewritable=rewritable, ticker=ticker,
+ level=level, fetch_subprojects=fetch_subprojects,
+ provenance=provenance)
+ loader = subproject_loader._get_loader(element.target_element, rewritable=rewritable, ticker=ticker,
+ level=level, fetch_subprojects=fetch_subprojects,
+ provenance=provenance)
+ self._loaders[filename] = loader
+ return loader
+
sources = list(element.sources())
if not element._source_cached():
for idx, source in enumerate(sources):
diff --git a/buildstream/_versions.py b/buildstream/_versions.py
index 56fd95223..c439f59fb 100644
--- a/buildstream/_versions.py
+++ b/buildstream/_versions.py
@@ -23,7 +23,7 @@
# This version is bumped whenever enhancements are made
# to the `project.conf` format or the core element format.
#
-BST_FORMAT_VERSION = 23
+BST_FORMAT_VERSION = 24
# The base BuildStream artifact version
diff --git a/buildstream/plugins/elements/junction.py b/buildstream/plugins/elements/junction.py
index ec7832bc6..15ef115d9 100644
--- a/buildstream/plugins/elements/junction.py
+++ b/buildstream/plugins/elements/junction.py
@@ -48,6 +48,18 @@ Overview
# Optionally look in a subpath of the source repository for the project
path: projects/hello
+ # Optionally specify another junction element to serve as a target for
+ # this element. Target should be defined using the syntax
+ # ``{junction-name}:{element-name}``.
+ #
+ # Note that this option cannot be used in conjunction with sources.
+ target: sub-project.bst:sub-sub-project.bst
+
+.. note::
+
+ The configuration option to allow specifying junction targets is available
+ since :ref:`format version 24 <project_format_version>`.
+
.. note::
Junction elements may not specify any dependencies as they are simply
@@ -124,10 +136,34 @@ As the junctions may differ in source version and options, BuildStream cannot
simply use one junction and ignore the others. Due to this, BuildStream requires
the user to resolve possibly conflicting nested junctions by creating a junction
with the same name in the top-level project, which then takes precedence.
+
+Targeting other junctions
+~~~~~~~~~~~~~~~~~~~~~~~~~
+When working with nested junctions, you can also create a junction element that
+targets another junction element in the sub-project. This can be useful if you
+need to ensure that both the top-level project and the sub-project are using
+the same version of the sub-sub-project.
+
+This can be done using the ``target`` configuration option. See below for an
+example:
+
+.. code:: yaml
+
+ kind: junction
+
+ config:
+ target: subproject.bst:subsubproject.bst
+
+In the above example, this junction element would be targeting the junction
+element named ``subsubproject.bst`` in the subproject referred to by
+``subproject.bst``.
+
+Note that when targeting another junction, the names of the junction element
+must not be the same as the name of the target.
"""
from collections.abc import Mapping
-from buildstream import Element
+from buildstream import Element, ElementError
from buildstream._pipeline import PipelineError
@@ -142,9 +178,33 @@ 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.target = self.node_get_member(node, str, 'target', default=None)
+ self.target_element = None
+ self.target_junction = None
def preflight(self):
- pass
+ # "target" cannot be used in conjunction with:
+ # 1. sources
+ # 2. config['options']
+ # 3. config['path']
+ if self.target and any(self.sources()):
+ raise ElementError("junction elements cannot define both 'sources' and 'target' config option")
+ if self.target and any(self.node_items(self.options)):
+ raise ElementError("junction elements cannot define both 'options' and 'target'")
+ if self.target and self.path:
+ raise ElementError("junction elements cannot define both 'path' and 'target'")
+
+ # Validate format of target, if defined
+ if self.target:
+ try:
+ self.target_junction, self.target_element = self.target.split(":")
+ except ValueError:
+ raise ElementError("'target' option must be in format '{junction-name}:{element-name}'")
+
+ # We cannot target a junction that has the same name as us, since that
+ # will cause an infinite recursion while trying to load it.
+ if self.name == self.target_element:
+ raise ElementError("junction elements cannot target an element with the same name")
def get_unique_key(self):
# Junctions do not produce artifacts. get_unique_key() implementation
diff --git a/tests/format/junctions.py b/tests/format/junctions.py
index 30891433b..bc85f182d 100644
--- a/tests/format/junctions.py
+++ b/tests/format/junctions.py
@@ -446,3 +446,57 @@ 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_config_target(cli, tmpdir, datafiles):
+ project = os.path.join(str(datafiles), 'config-target')
+ checkoutdir = os.path.join(str(tmpdir), 'checkout')
+
+ # Build, checkout
+ result = cli.run(project=project, args=['build', 'target.bst'])
+ result.assert_success()
+ result = cli.run(project=project, args=['artifact', 'checkout', 'target.bst', '--directory', checkoutdir])
+ result.assert_success()
+
+ # Check that the checkout contains the expected files from sub-sub-project
+ assert os.path.exists(os.path.join(checkoutdir, 'hello.txt'))
+
+
+@pytest.mark.datafiles(DATA_DIR)
+def test_invalid_sources_and_target(cli, tmpdir, datafiles):
+ project = os.path.join(str(datafiles), 'config-target')
+
+ result = cli.run(project=project, args=['show', 'invalid-source-target.bst'])
+ result.assert_main_error(ErrorDomain.ELEMENT, None)
+
+ assert "junction elements cannot define both 'sources' and 'target' config option" in result.stderr
+
+
+@pytest.mark.datafiles(DATA_DIR)
+def test_invalid_target_name(cli, tmpdir, datafiles):
+ project = os.path.join(str(datafiles), 'config-target')
+
+ # Rename our junction element to the same name as its target
+ old_path = os.path.join(project, 'elements/subsubproject.bst')
+ new_path = os.path.join(project, 'elements/subsubproject-junction.bst')
+ os.rename(old_path, new_path)
+
+ # This should fail now
+ result = cli.run(project=project, args=['show', 'subsubproject-junction.bst'])
+ result.assert_main_error(ErrorDomain.ELEMENT, None)
+
+ assert "junction elements cannot target an element with the same name" in result.stderr
+
+
+# We cannot exhaustively test all possible ways in which this can go wrong, so
+# test a couple of common ways in which we expect this to go wrong.
+@pytest.mark.parametrize('target', ['no-junction.bst', 'nested-junction-target.bst'])
+@pytest.mark.datafiles(DATA_DIR)
+def test_invalid_target_format(cli, tmpdir, datafiles, target):
+ project = os.path.join(str(datafiles), 'config-target')
+
+ result = cli.run(project=project, args=['show', target])
+ result.assert_main_error(ErrorDomain.ELEMENT, None)
+
+ assert "'target' option must be in format '{junction-name}:{element-name}'" in result.stderr
diff --git a/tests/format/junctions/config-target/elements/invalid-source-target.bst b/tests/format/junctions/config-target/elements/invalid-source-target.bst
new file mode 100644
index 000000000..b97d09034
--- /dev/null
+++ b/tests/format/junctions/config-target/elements/invalid-source-target.bst
@@ -0,0 +1,8 @@
+kind: junction
+
+sources:
+- kind: local
+ path: subproject/subsubproject
+
+config:
+ target: subproject.bst:subsubproject-junction.bst
diff --git a/tests/format/junctions/config-target/elements/nested-junction-target.bst b/tests/format/junctions/config-target/elements/nested-junction-target.bst
new file mode 100644
index 000000000..f76a264e5
--- /dev/null
+++ b/tests/format/junctions/config-target/elements/nested-junction-target.bst
@@ -0,0 +1,4 @@
+kind: junction
+
+config:
+ target: subproject.bst:subsubproject.bst:hello.bst
diff --git a/tests/format/junctions/config-target/elements/no-junction.bst b/tests/format/junctions/config-target/elements/no-junction.bst
new file mode 100644
index 000000000..15d1842f6
--- /dev/null
+++ b/tests/format/junctions/config-target/elements/no-junction.bst
@@ -0,0 +1,4 @@
+kind: junction
+
+config:
+ target: subproject.bst
diff --git a/tests/format/junctions/config-target/elements/subproject.bst b/tests/format/junctions/config-target/elements/subproject.bst
new file mode 100644
index 000000000..6664eeec6
--- /dev/null
+++ b/tests/format/junctions/config-target/elements/subproject.bst
@@ -0,0 +1,5 @@
+kind: junction
+
+sources:
+- kind: local
+ path: subproject
diff --git a/tests/format/junctions/config-target/elements/subsubproject.bst b/tests/format/junctions/config-target/elements/subsubproject.bst
new file mode 100644
index 000000000..20dc4a0c4
--- /dev/null
+++ b/tests/format/junctions/config-target/elements/subsubproject.bst
@@ -0,0 +1,4 @@
+kind: junction
+
+config:
+ target: subproject.bst:subsubproject-junction.bst
diff --git a/tests/format/junctions/config-target/elements/target.bst b/tests/format/junctions/config-target/elements/target.bst
new file mode 100644
index 000000000..50d74489a
--- /dev/null
+++ b/tests/format/junctions/config-target/elements/target.bst
@@ -0,0 +1,4 @@
+kind: stack
+
+depends:
+- subsubproject.bst:hello.bst
diff --git a/tests/format/junctions/config-target/project.conf b/tests/format/junctions/config-target/project.conf
new file mode 100644
index 000000000..4049c3739
--- /dev/null
+++ b/tests/format/junctions/config-target/project.conf
@@ -0,0 +1,3 @@
+name: config-target
+
+element-path: elements
diff --git a/tests/format/junctions/config-target/subproject/elements/subsubproject-junction.bst b/tests/format/junctions/config-target/subproject/elements/subsubproject-junction.bst
new file mode 100644
index 000000000..018fb8ec4
--- /dev/null
+++ b/tests/format/junctions/config-target/subproject/elements/subsubproject-junction.bst
@@ -0,0 +1,5 @@
+kind: junction
+
+sources:
+- kind: local
+ path: subsubproject
diff --git a/tests/format/junctions/config-target/subproject/project.conf b/tests/format/junctions/config-target/subproject/project.conf
new file mode 100644
index 000000000..1097282c3
--- /dev/null
+++ b/tests/format/junctions/config-target/subproject/project.conf
@@ -0,0 +1,3 @@
+name: subproject
+
+element-path: elements
diff --git a/tests/format/junctions/config-target/subproject/subsubproject/elements/hello.bst b/tests/format/junctions/config-target/subproject/subsubproject/elements/hello.bst
new file mode 100644
index 000000000..a04a856cd
--- /dev/null
+++ b/tests/format/junctions/config-target/subproject/subsubproject/elements/hello.bst
@@ -0,0 +1,5 @@
+kind: import
+
+sources:
+- kind: local
+ path: files/hello.txt
diff --git a/tests/format/junctions/config-target/subproject/subsubproject/files/hello.txt b/tests/format/junctions/config-target/subproject/subsubproject/files/hello.txt
new file mode 100644
index 000000000..ce0136250
--- /dev/null
+++ b/tests/format/junctions/config-target/subproject/subsubproject/files/hello.txt
@@ -0,0 +1 @@
+hello
diff --git a/tests/format/junctions/config-target/subproject/subsubproject/project.conf b/tests/format/junctions/config-target/subproject/subsubproject/project.conf
new file mode 100644
index 000000000..9d75121a0
--- /dev/null
+++ b/tests/format/junctions/config-target/subproject/subsubproject/project.conf
@@ -0,0 +1,3 @@
+name: subsubproject
+
+element-path: elements