Commit 72260137 authored by bst-marge-bot's avatar bst-marge-bot

Merge branch 'chandan/pseudo-junction' into 'master'

Add support for defining target for junction elements

See merge request !1293
parents e570c681 28aee4c6
Pipeline #57748017 failed with stage
in 3 minutes and 18 seconds
......@@ -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.
......
......@@ -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):
......
......@@ -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
......
......@@ -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
......
......@@ -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
kind: junction
sources:
- kind: local
path: subproject/subsubproject
config:
target: subproject.bst:subsubproject-junction.bst
kind: junction
config:
target: subproject.bst:subsubproject.bst:hello.bst
kind: junction
config:
target: subproject.bst
kind: junction
sources:
- kind: local
path: subproject
kind: junction
config:
target: subproject.bst:subsubproject-junction.bst
kind: stack
depends:
- subsubproject.bst:hello.bst
name: config-target
element-path: elements
kind: junction
sources:
- kind: local
path: subsubproject
kind: import
sources:
- kind: local
path: files/hello.txt
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment