Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
49 changes: 46 additions & 3 deletions src/buildstream/_frontend/cli.py
Original file line number Diff line number Diff line change
Expand Up @@ -1346,16 +1346,33 @@ def artifact_show(app, deps, artifact_remotes, ignore_project_artifact_remotes,
@click.option(
"--deps",
"-d",
default=str(_PipelineSelection.RUN),
show_default=True,
default=None,
type=FastEnumType(
_PipelineSelection,
[_PipelineSelection.RUN, _PipelineSelection.BUILD, _PipelineSelection.NONE, _PipelineSelection.ALL],
),
help="The dependencies to checkout",
help="The dependencies to checkout [default: run]",
)
@click.option("--integrate/--no-integrate", default=None, is_flag=True, help="Whether to run integration commands")
@click.option("--hardlinks", is_flag=True, help="Checkout hardlinks instead of copying if possible")
@click.option(
"--buildtree",
is_flag=True,
help=(
"Checkout the cached build tree of the element instead of the artifact. "
"Will fail if a build tree is not available. "
"pull-buildtrees configuration is needed if the build tree is not available locally."
),
)
@click.option(
"--buildroot",
is_flag=True,
help=(
"Checkout the cached build root (the whole build sandbox) of the element instead of the artifact. "
"Will fail if a build root is not available. "
"pull-buildtrees configuration is needed if the build root is not available locally."
),
)
@click.option(
"--tar",
default=None,
Expand Down Expand Up @@ -1394,6 +1411,8 @@ def artifact_checkout(
deps,
integrate,
hardlinks,
buildtree,
buildroot,
tar,
compression,
directory,
Expand All @@ -1405,9 +1424,31 @@ def artifact_checkout(

When this command is executed from a workspace directory, the default
is to checkout the artifact of the workspace element.

Use the --buildtree option to checkout the build directory of the
element as it was cached at the end of the build, or the --buildroot
option to checkout the whole build sandbox. Build trees and build roots
are only cached according to the cache-buildtrees configuration, and
only build elements have a build tree.
"""
from .. import utils

if buildtree and buildroot:
click.echo("ERROR: options --buildtree and --buildroot conflict", err=True)
sys.exit(-1)

if buildtree or buildroot:
option = "--buildtree" if buildtree else "--buildroot"
if deps is not None:
click.echo("ERROR: options --deps and {} conflict".format(option), err=True)
sys.exit(-1)
if integrate is not None:
click.echo("ERROR: options --integrate/--no-integrate and {} conflict".format(option), err=True)
sys.exit(-1)

if deps is None:
deps = _PipelineSelection.RUN

if hardlinks and tar:
click.echo("ERROR: options --hardlinks and --tar conflict", err=True)
sys.exit(-1)
Expand Down Expand Up @@ -1459,6 +1500,8 @@ def artifact_checkout(
hardlinks=hardlinks,
compression=compression,
tar=bool(tar),
buildtree=buildtree,
buildroot=buildroot,
artifact_remotes=artifact_remotes,
ignore_project_artifact_remotes=ignore_project_artifact_remotes,
)
Expand Down
82 changes: 82 additions & 0 deletions src/buildstream/_stream.py
Original file line number Diff line number Diff line change
Expand Up @@ -679,6 +679,8 @@ def push(
# will be placed at the given location. If true and
# location is '-', the tarball will be dumped on the
# standard output.
# buildtree: Checkout the cached build tree instead of the artifact
# buildroot: Checkout the cached build root instead of the artifact
# artifact_remotes: Artifact cache remotes specified on the commmand line
# ignore_project_artifact_remotes: Whether to ignore artifact remotes specified by projects
#
Expand All @@ -693,9 +695,16 @@ def checkout(
hardlinks: bool = False,
compression: str = "",
tar: bool = False,
buildtree: bool = False,
buildroot: bool = False,
artifact_remotes: Iterable[RemoteSpec] = (),
ignore_project_artifact_remotes: bool = False,
):
# The build tree and build root are stored in the artifact of the
# target element itself, dependencies are not needed.
assert not (buildtree and buildroot)
if buildtree or buildroot:
selection = _PipelineSelection.NONE

elements = self._load(
(target,),
Expand All @@ -721,6 +730,10 @@ def checkout(
self.query_cache(elements)
self._pull_missing_artifacts(elements)

if buildtree or buildroot:
self._checkout_buildtree(element, location, compression, tar, hardlinks, buildroot=buildroot)
return

try:
scope = {
_PipelineSelection.RUN: _Scope.RUN,
Expand All @@ -737,6 +750,75 @@ def checkout(
"Error while staging dependencies into a sandbox" ": '{}'".format(e), detail=e.detail, reason=e.reason
) from e

# _checkout_buildtree()
#
# Checkout the build tree or the build root of a cached artifact
#
# Args:
# element (Element/ArtifactElement): The Element/ArtifactElement we want to checkout
# location (str): The name of the directory/the tarfile we want to export to/create
# compression (str): The type of compression for the tarball
# tar (bool): Whether we want to create a tarfile
# hardlinks (bool): Whether to checkout hardlinks instead of copying
# buildroot (bool): Whether to checkout the build root instead of the build tree
#
def _checkout_buildtree(self, element, location, compression, tar, hardlinks, *, buildroot=False):
pull_detail = "pull-buildtrees configuration is needed if the buildtree is not available locally"

if not element._cached():
raise StreamError("Artifact not cached locally or in available remotes", reason="missing-artifact")

if buildroot:
if not element._cached_buildroot():
if element._buildroot_exists():
raise StreamError(
"Buildroot is not cached locally or in available remotes",
detail=pull_detail,
reason="missing-buildroot-not-cached",
)
raise StreamError(
"Artifact was created without buildroot",
detail="Use the cache-buildtrees configuration to cache buildroots when building",
reason="missing-buildroot-artifact-created-without-buildroot",
)
elif not element._cached_buildtree():
if element._buildtree_exists():
raise StreamError(
"Buildtree is not cached locally or in available remotes",
detail=pull_detail,
reason="missing-buildtree-not-cached",
)
if element._buildroot_exists():
# Only build elements have a build tree, other elements
# such as script or compose elements only have a build root
raise StreamError(
"Artifact was created without buildtree, but a buildroot is available",
detail="Use the --buildroot option to checkout the buildroot instead",
reason="missing-buildtree-buildroot-available",
)
raise StreamError(
"Artifact was created without buildtree",
detail="Use the cache-buildtrees configuration to cache buildtrees when building",
reason="missing-buildtree-artifact-created-without-buildtree",
)

# Raise warning if the element is cached in a failed state
if element._cached_failure():
self._context.messenger.warn(
"using a {} from a failed build.".format("buildroot" if buildroot else "buildtree")
)

try:
artifact = element._get_artifact()
virdir = artifact.get_buildroot() if buildroot else artifact.get_buildtree()
self._export_artifact(tar, location, compression, element, hardlinks, virdir)
except BstError as e:
raise StreamError(
"Error while exporting {}: '{}'".format("buildroot" if buildroot else "buildtree", e),
detail=e.detail,
reason=e.reason,
) from e

# _export_artifact()
#
# Export the files of the artifact/a tarball to a virtual directory
Expand Down
Loading
Loading