blob: bdc90968db3ffd16ddb3461f5f629dd8e46c6f4c [file] [log] [blame]
#
# Copyright (C) 2020 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:
# Tristan Van Berkom <tristan.vanberkom@codethink.co.uk>
"""
stack - Symbolic Element for dependency grouping
================================================
Stack elements are simply a symbolic element used for representing
a logical group of elements.
All dependencies declared in stack elements must always be both
:ref:`build and runtime dependencies <format_dependencies_types>`.
**Example:**
.. code:: yaml
kind: stack
# Declare all of your dependencies in the `depends` list.
depends:
- libc.bst
- coreutils.bst
.. note::
Unlike other elements, whose cache keys are a unique identifier
of the contents of the artifacts they produce, stack elements do
not produce any artifact content. Instead, the cache key of an artifact
is a unique identifier for the assembly of its own dependencies.
Using intermediate stacks
-------------------------
Using a stack element at intermediate levels of your build graph
allows you to abstract away some parts of your project into logical
subsystems which elements can more conveniently depend on as a whole.
In addition to the added convenience, it will allow you to more
easily change the implementation of a subsystem later on, without needing
to update many reverse dependencies to depend on new elements, or even
allow you to conditionally implement a subsystem with various implementations
depending on what :ref:`project options <project_options>` were specified at
build time.
Using toplevel stacks
---------------------
Stack elements can also be useful as toplevel targets in your build graph
to simply indicate all of the components which need to be built for a given
system to be complete, or for your integration pipeline to be successful.
Checking out and deploying toplevel stacks
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
In case that your software is built remotely, it is possible to checkout
the built content of a stack on your own machine for the purposes of
inspection or further deployment.
To accomplish this, you will need to know the cache key of the stack element
which was built remotely, possibly by inspecting the remote build log or by
deriving it with an equally configured BuildStream project, and you will
need read access to the artifact cache server which the build was uploaded to,
this should be configured in your :ref:`user configuration file <config_artifact_caches>`.
You can then checkout the remotely built stack using the
:ref:`bst artifact checkout <invoking_artifact_checkout>` command and providing
it with the :ref:`artifact name <artifact_names>`:
**Example:**
.. code:: shell
bst artifact checkout --deps build --pull --integrate \\
--directory `pwd`/checkout \\
project/stack/788da21e7c1b5818b7e7b60f7eb75841057ff7e45d362cc223336c606fe47f27
.. note::
It is possible to checkout other elements in the same way, however stack
elements are uniquely suited to this purpose, as they cannot have
:ref:`runtime only dependencies <format_dependencies_types>`, and consequently
their cache keys are always a unique representation of their collective
dependencies.
"""
from buildstream import Element, ElementError
from buildstream.types import _Scope
# Element implementation for the 'stack' kind.
class StackElement(Element):
# pylint: disable=attribute-defined-outside-init
BST_MIN_VERSION = "2.0"
# This plugin does not produce any artifacts when built
BST_ELEMENT_HAS_ARTIFACT = False
# This element does not allow sources
BST_FORBID_SOURCES = True
# Stack elements do not run any commands
BST_RUN_COMMANDS = False
def configure(self, node):
pass
def preflight(self):
# Assert that all dependencies are both build and runtime dependencies.
#
all_deps = list(self._dependencies(_Scope.ALL, recurse=False))
run_deps = list(self._dependencies(_Scope.RUN, recurse=False))
build_deps = list(self._dependencies(_Scope.BUILD, recurse=False))
if any(dep not in run_deps for dep in all_deps) or any(dep not in build_deps for dep in all_deps):
# There is no need to specify the `self` provenance here in preflight() errors, as the base class
# will take care of prefixing these for plugin author convenience.
raise ElementError(
"All dependencies of 'stack' elements must be both build and runtime dependencies",
detail="Make sure you declare all dependencies in the `depends` list, without specifying any `type`.",
reason="stack-requires-build-and-run",
)
def get_unique_key(self):
# We do not add anything to the build, only our dependencies
# do, so our unique key is just a constant.
return 1
def configure_sandbox(self, sandbox):
pass
def stage(self, sandbox):
pass
def assemble(self, sandbox):
# Just create a dummy empty artifact, its existence is a statement
# that all this stack's dependencies are built.
vrootdir = sandbox.get_virtual_directory()
vrootdir.descend("output", create=True)
# And we're done
return "/output"
# Plugin entry point
def setup():
return StackElement