summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorRebecca Grayson <becky.grayson1@hotmail.co.uk>2019-08-20 09:55:54 +0100
committerRebecca Grayson <becky.grayson1@hotmail.co.uk>2019-08-29 14:54:37 +0100
commit20a8f9e812c9f7dc2ad9c44325afcc1732c1de52 (patch)
treebba12beb9853522b0eb6c98000e84c5570b342af
parentb794f058c0ff2709ae6a382fff5744a98da97c76 (diff)
downloadbuildstream-becky/artifact_log_file_option.tar.gz
Addition of --out option to bst artifact log:becky/artifact_log_file_option
A --out option has been added, allowing an artifact log to be written to a logfile. This is particularly useful when more than one artifact's log is wanting to be read; It will write a file for each log. A test and NEWS entry have also been added.
-rw-r--r--src/buildstream/_frontend/cli.py43
-rw-r--r--src/buildstream/_stream.py6
-rw-r--r--tests/frontend/artifact.py35
3 files changed, 71 insertions, 13 deletions
diff --git a/src/buildstream/_frontend/cli.py b/src/buildstream/_frontend/cli.py
index 661e39575..c1b06b2d1 100644
--- a/src/buildstream/_frontend/cli.py
+++ b/src/buildstream/_frontend/cli.py
@@ -4,6 +4,7 @@ import sys
from functools import partial
import fcntl
+import shutil
import click
from .. import _yaml
from .._exceptions import BstError, LoadError, AppError
@@ -1219,19 +1220,45 @@ def artifact_push(app, elements, deps, remote):
# Artifact Log Command #
################################################################
@artifact.command(name='log', short_help="Show logs of artifacts")
+@click.option('--out',
+ type=click.Path(file_okay=True, writable=True),
+ help="Output logs to individual files in the specified path. If absent, logs are written to stdout.")
@click.argument('artifacts', type=click.Path(), nargs=-1)
@click.pass_obj
-def artifact_log(app, artifacts):
+def artifact_log(app, artifacts, out):
"""Show build logs of artifacts"""
-
with app.initialized():
- log_file_paths = app.stream.artifact_log(artifacts)
-
- for log in log_file_paths:
- with open(log) as f:
- data = f.read()
+ artifact_logs = app.stream.artifact_log(artifacts)
+
+ if not out:
+ try:
+ for log in list(artifact_logs.values()):
+ with open(log[0], 'r') as f:
+ data = f.read()
+ click.echo_via_pager(data)
+ except (OSError, FileNotFoundError):
+ click.echo("Error: file cannot be opened", err=True)
+ sys.exit(1)
- click.echo_via_pager(data)
+ else:
+ try:
+ os.mkdir(out)
+ except FileExistsError:
+ click.echo("Error: {} already exists".format(out), err=True)
+ sys.exit(1)
+
+ for name, log_files in artifact_logs.items():
+ if len(log_files) > 1:
+ os.mkdir(name)
+ for log in log_files:
+ dest = os.path.join(out, name, log)
+ shutil.copy(log, dest)
+ # make a dir and write in log files
+ else:
+ log_name = os.path.splitext(name)[0] + '.log'
+ dest = os.path.join(out, log_name)
+ shutil.copy(log_files[0], dest)
+ # write a log file
################################################################
diff --git a/src/buildstream/_stream.py b/src/buildstream/_stream.py
index 554debaae..95b306b47 100644
--- a/src/buildstream/_stream.py
+++ b/src/buildstream/_stream.py
@@ -648,7 +648,7 @@ class Stream():
# Return list of Element and/or ArtifactElement objects
target_objects = self.load_selection(targets, selection=PipelineSelection.NONE, load_refs=True)
- log_file_paths = []
+ artifact_logs = {}
for obj in target_objects:
ref = obj.get_artifact_name()
if not obj._cached():
@@ -658,9 +658,9 @@ class Stream():
self._message(MessageType.WARN, "{} is cached without log files".format(ref))
continue
- log_file_paths.extend(obj.get_logs())
+ artifact_logs[obj.name] = obj.get_logs()
- return log_file_paths
+ return artifact_logs
# artifact_list_contents()
#
diff --git a/tests/frontend/artifact.py b/tests/frontend/artifact.py
index cbc9ab022..9ad03909e 100644
--- a/tests/frontend/artifact.py
+++ b/tests/frontend/artifact.py
@@ -69,8 +69,7 @@ def test_artifact_log(cli, datafiles):
# Read the log via glob
result = cli.run(project=project, args=['artifact', 'log', 'test/target/*'])
assert result.exit_code == 0
- # The artifact is cached under both a strong key and a weak key
- assert (log + log) == result.output
+ assert log == result.output
@pytest.mark.datafiles(DATA_DIR)
@@ -140,6 +139,38 @@ def test_artifact_list_exact_contents_glob(cli, datafiles):
assert artifact in result.output
+@pytest.mark.datafiles(DATA_DIR)
+def test_artifact_log_files(cli, datafiles):
+ project = str(datafiles)
+
+ # Ensure we have an artifact to read
+ result = cli.run(project=project, args=['build', 'target.bst'])
+ assert result.exit_code == 0
+
+ logfiles = os.path.join(project, "logfiles")
+ target = os.path.join(project, logfiles, "target.log")
+ import_bin = os.path.join(project, logfiles, "import-bin.log")
+ # Ensure the logfile doesn't exist before the command is run
+ assert not os.path.exists(logfiles)
+ assert not os.path.exists(target)
+ assert not os.path.exists(import_bin)
+
+ # Run the command and ensure the file now exists
+ result = cli.run(project=project, args=['artifact', 'log', '--out', logfiles, 'target.bst', 'import-bin.bst'])
+ assert result.exit_code == 0
+ assert os.path.exists(logfiles)
+ assert os.path.exists(target)
+ assert os.path.exists(import_bin)
+
+ # Ensure the file contains the logs by checking for the LOG line
+ with open(target, 'r') as f:
+ data = f.read()
+ assert "LOG target.bst" in data
+ with open(import_bin, 'r') as f:
+ data = f.read()
+ assert "LOG import-bin.bst" in data
+
+
# Test that we can delete the artifact of the element which corresponds
# to the current project state
@pytest.mark.datafiles(DATA_DIR)