diff options
Diffstat (limited to 'src/buildstream/plugins/elements/junction.py')
-rw-r--r-- | src/buildstream/plugins/elements/junction.py | 229 |
1 files changed, 229 insertions, 0 deletions
diff --git a/src/buildstream/plugins/elements/junction.py b/src/buildstream/plugins/elements/junction.py new file mode 100644 index 000000000..15ef115d9 --- /dev/null +++ b/src/buildstream/plugins/elements/junction.py @@ -0,0 +1,229 @@ +# +# Copyright (C) 2017 Codethink Limited +# +# This program is free software; you can redistribute it and/or +# modify it under the terms of the GNU Lesser General Public +# License as published by the Free Software Foundation; either +# version 2 of the License, or (at your option) any later version. +# +# This library is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +# Lesser General Public License for more details. +# +# You should have received a copy of the GNU Lesser General Public +# License along with this library. If not, see <http://www.gnu.org/licenses/>. +# +# Authors: +# Jürg Billeter <juerg.billeter@codethink.co.uk> + +""" +junction - Integrate subprojects +================================ +This element is a link to another BuildStream project. It allows integration +of multiple projects into a single pipeline. + +Overview +-------- + +.. code:: yaml + + kind: junction + + # Specify the BuildStream project source + sources: + - kind: git + url: upstream:projectname.git + track: master + ref: d0b38561afb8122a3fc6bafc5a733ec502fcaed6 + + # Specify the junction configuration + config: + + # Override project options + options: + machine_arch: "%{machine_arch}" + debug: True + + # 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 + links to other projects and are not in the dependency graph on their own. + +With a junction element in place, local elements can depend on elements in +the other BuildStream project using the additional ``junction`` attribute in the +dependency dictionary: + +.. code:: yaml + + depends: + - junction: toolchain.bst + filename: gcc.bst + type: build + +While junctions are elements, only a limited set of element operations is +supported. They can be tracked and fetched like other elements. +However, junction elements do not produce any artifacts, which means that +they cannot be built or staged. It also means that another element cannot +depend on a junction element itself. + +.. note:: + + BuildStream does not implicitly track junction elements. This means + that if we were to invoke: `bst build --track-all ELEMENT` on an element + which uses a junction element, the ref of the junction element + will not automatically be updated if a more recent version exists. + + Therefore, if you require the most up-to-date version of a subproject, + you must explicitly track the junction element by invoking: + `bst source track JUNCTION_ELEMENT`. + + Furthermore, elements within the subproject are also not tracked by default. + For this, we must specify the `--track-cross-junctions` option. This option + must be preceeded by `--track ELEMENT` or `--track-all`. + + +Sources +------- +``bst show`` does not implicitly fetch junction sources if they haven't been +cached yet. However, they can be fetched explicitly: + +.. code:: + + bst source fetch junction.bst + +Other commands such as ``bst build`` implicitly fetch junction sources. + +Options +------- +.. code:: yaml + + options: + machine_arch: "%{machine_arch}" + debug: True + +Junctions can configure options of the linked project. Options are never +implicitly inherited across junctions, however, variables can be used to +explicitly assign the same value to a subproject option. + +.. _core_junction_nested: + +Nested Junctions +---------------- +Junctions can be nested. That is, subprojects are allowed to have junctions on +their own. Nested junctions in different subprojects may point to the same +project, however, in most use cases the same project should be loaded only once. +BuildStream uses the junction element name as key to determine which junctions +to merge. It is recommended that the name of a junction is set to the same as +the name of the linked project. + +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, ElementError +from buildstream._pipeline import PipelineError + + +# Element implementation for the 'junction' kind. +class JunctionElement(Element): + # pylint: disable=attribute-defined-outside-init + + # Junctions are not allowed any dependencies + BST_FORBID_BDEPENDS = True + BST_FORBID_RDEPENDS = True + + 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): + # "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 + # is still required for `bst source fetch`. + return 1 + + def configure_sandbox(self, sandbox): + raise PipelineError("Cannot build junction elements") + + def stage(self, sandbox): + raise PipelineError("Cannot stage junction elements") + + def generate_script(self): + raise PipelineError("Cannot build junction elements") + + def assemble(self, sandbox): + raise PipelineError("Cannot build junction elements") + + +# Plugin entry point +def setup(): + return JunctionElement |