ci: Add CI command to include text files in the CI log

pull/6199/head
Daniel Mensinger 5 years ago
parent b4af3328a4
commit 9435f11b17
No known key found for this signature in database
GPG Key ID: 54DD94C131E277D4
  1. 1
      mesonbuild/backend/ninjabackend.py
  2. 2
      mesonbuild/cmake/fileapi.py
  3. 1
      mesonbuild/interpreter.py
  4. 10
      mesonbuild/mlog.py
  5. 62
      run_project_tests.py

@ -334,6 +334,7 @@ int dummy;
# Only overwrite the old build file after the new one has been
# fully created.
os.replace(tempfilename, outfilename)
mlog.cmd_ci_include(outfilename) # For CI debugging
self.generate_compdb()
# http://clang.llvm.org/docs/JSONCompilationDatabase.html

@ -14,6 +14,7 @@
from .common import CMakeException, CMakeBuildFile, CMakeConfiguration
from typing import Any, List, Tuple
from .. import mlog
import os
import json
import re
@ -76,6 +77,7 @@ class CMakeFileAPI:
debug_json = os.path.normpath(os.path.join(self.build_dir, '..', 'fileAPI.json'))
with open(debug_json, 'w') as fp:
json.dump(index, fp, indent=2)
mlog.cmd_ci_include(debug_json)
# parse the JSON
for i in index['objects']:

@ -2596,6 +2596,7 @@ external dependencies (including libraries) must go to "dependencies".''')
f.write(printer.result)
mlog.log('Build file:', meson_filename)
mlog.cmd_ci_include(meson_filename)
mlog.log()
result = self._do_subproject_meson(dirname, subdir, default_options, kwargs, ast, cm_int.bs_files)

@ -56,6 +56,7 @@ log_timestamp_start = None # type: Optional[float]
log_fatal_warnings = False # type: bool
log_disable_stdout = False # type: bool
log_errors_only = False # type: bool
_in_ci = 'CI' in os.environ # type: bool
def disable() -> None:
global log_disable_stdout
@ -186,6 +187,15 @@ def debug(*args: Union[str, AnsiDecorator], **kwargs: Any) -> None:
print(*arr, file=log_file, **kwargs)
log_file.flush()
def _debug_log_cmd(cmd: str, args: List[str]) -> None:
if not _in_ci:
return
args = ['"{}"'.format(x) for x in args] # Quote all args, just in case
debug('!meson_ci!/{} {}'.format(cmd, ' '.join(args)))
def cmd_ci_include(file: str) -> None:
_debug_log_cmd('ci_include', [file])
def log(*args: Union[str, AnsiDecorator], is_error: bool = False,
**kwargs: Any) -> None:
global log_errors_only

@ -21,6 +21,7 @@ import subprocess
import shutil
import sys
import signal
import shlex
from io import StringIO
from ast import literal_eval
from enum import Enum
@ -57,12 +58,13 @@ class BuildStep(Enum):
class TestResult:
def __init__(self, msg, step, stdo, stde, mlog, conftime=0, buildtime=0, testtime=0):
def __init__(self, msg, step, stdo, stde, mlog, cicmds, conftime=0, buildtime=0, testtime=0):
self.msg = msg
self.step = step
self.stdo = stdo
self.stde = stde
self.mlog = mlog
self.cicmds = cicmds
self.conftime = conftime
self.buildtime = buildtime
self.testtime = testtime
@ -271,6 +273,33 @@ def yellow(text):
return mlog.yellow(text).get_text(mlog.colorize_console)
def _run_ci_include(args: typing.List[str]) -> str:
if not args:
return 'At least one parameter required'
try:
file_path = Path(args[0])
data = file_path.open(errors='ignore', encoding='utf-8').read()
return 'Included file {}:\n{}\n'.format(args[0], data)
except Exception:
return 'Failed to open {} ({})'.format(args[0])
return 'Appended {} to the log'.format(args[0])
ci_commands = {
'ci_include': _run_ci_include
}
def run_ci_commands(raw_log: str) -> typing.List[str]:
res = []
for l in raw_log.splitlines():
if not l.startswith('!meson_ci!/'):
continue
cmd = shlex.split(l[11:])
if not cmd or cmd[0] not in ci_commands:
continue
res += ['CI COMMAND {}:\n{}\n'.format(cmd[0], ci_commands[cmd[0]](cmd[1:]))]
return res
def run_test_inprocess(testdir):
old_stdout = sys.stdout
sys.stdout = mystdout = StringIO()
@ -372,16 +401,17 @@ def _run_test(testdir, test_build_dir, install_dir, extra_args, compiler, backen
mesonlog = logfile.open(errors='ignore', encoding='utf-8').read()
except Exception:
mesonlog = no_meson_log_msg
cicmds = run_ci_commands(mesonlog)
gen_time = time.time() - gen_start
if should_fail == 'meson':
if returncode == 1:
return TestResult('', BuildStep.configure, stdo, stde, mesonlog, gen_time)
return TestResult('', BuildStep.configure, stdo, stde, mesonlog, cicmds, gen_time)
elif returncode != 0:
return TestResult('Test exited with unexpected status {}'.format(returncode), BuildStep.configure, stdo, stde, mesonlog, gen_time)
return TestResult('Test exited with unexpected status {}'.format(returncode), BuildStep.configure, stdo, stde, mesonlog, cicmds, gen_time)
else:
return TestResult('Test that should have failed succeeded', BuildStep.configure, stdo, stde, mesonlog, gen_time)
return TestResult('Test that should have failed succeeded', BuildStep.configure, stdo, stde, mesonlog, cicmds, gen_time)
if returncode != 0:
return TestResult('Generating the build system failed.', BuildStep.configure, stdo, stde, mesonlog, gen_time)
return TestResult('Generating the build system failed.', BuildStep.configure, stdo, stde, mesonlog, cicmds, gen_time)
builddata = build.load(test_build_dir)
# Touch the meson.build file to force a regenerate so we can test that
# regeneration works before a build is run.
@ -396,10 +426,10 @@ def _run_test(testdir, test_build_dir, install_dir, extra_args, compiler, backen
stde += e
if should_fail == 'build':
if pc.returncode != 0:
return TestResult('', BuildStep.build, stdo, stde, mesonlog, gen_time)
return TestResult('Test that should have failed to build succeeded', BuildStep.build, stdo, stde, mesonlog, gen_time)
return TestResult('', BuildStep.build, stdo, stde, mesonlog, cicmds, gen_time)
return TestResult('Test that should have failed to build succeeded', BuildStep.build, stdo, stde, mesonlog, cicmds, gen_time)
if pc.returncode != 0:
return TestResult('Compiling source code failed.', BuildStep.build, stdo, stde, mesonlog, gen_time, build_time)
return TestResult('Compiling source code failed.', BuildStep.build, stdo, stde, mesonlog, cicmds, gen_time, build_time)
# Touch the meson.build file to force a regenerate so we can test that
# regeneration works after a build is complete.
ensure_backend_detects_changes(backend)
@ -413,10 +443,10 @@ def _run_test(testdir, test_build_dir, install_dir, extra_args, compiler, backen
mesonlog += test_log
if should_fail == 'test':
if returncode != 0:
return TestResult('', BuildStep.test, stdo, stde, mesonlog, gen_time)
return TestResult('Test that should have failed to run unit tests succeeded', BuildStep.test, stdo, stde, mesonlog, gen_time)
return TestResult('', BuildStep.test, stdo, stde, mesonlog, cicmds, gen_time)
return TestResult('Test that should have failed to run unit tests succeeded', BuildStep.test, stdo, stde, mesonlog, cicmds, gen_time)
if returncode != 0:
return TestResult('Running unit tests failed.', BuildStep.test, stdo, stde, mesonlog, gen_time, build_time, test_time)
return TestResult('Running unit tests failed.', BuildStep.test, stdo, stde, mesonlog, cicmds, gen_time, build_time, test_time)
# Do installation, if the backend supports it
if install_commands:
env = os.environ.copy()
@ -426,18 +456,18 @@ def _run_test(testdir, test_build_dir, install_dir, extra_args, compiler, backen
stdo += o
stde += e
if pi.returncode != 0:
return TestResult('Running install failed.', BuildStep.install, stdo, stde, mesonlog, gen_time, build_time, test_time)
return TestResult('Running install failed.', BuildStep.install, stdo, stde, mesonlog, cicmds, gen_time, build_time, test_time)
# Clean with subprocess
env = os.environ.copy()
pi, o, e = Popen_safe(clean_commands + dir_args, cwd=test_build_dir, env=env)
stdo += o
stde += e
if pi.returncode != 0:
return TestResult('Running clean failed.', BuildStep.clean, stdo, stde, mesonlog, gen_time, build_time, test_time)
return TestResult('Running clean failed.', BuildStep.clean, stdo, stde, mesonlog, cicmds, gen_time, build_time, test_time)
if not install_commands:
return TestResult('', BuildStep.install, '', '', mesonlog, gen_time, build_time, test_time)
return TestResult('', BuildStep.install, '', '', mesonlog, cicmds, gen_time, build_time, test_time)
return TestResult(validate_install(testdir, install_dir, compiler, builddata.environment),
BuildStep.validate, stdo, stde, mesonlog, gen_time, build_time, test_time)
BuildStep.validate, stdo, stde, mesonlog, cicmds, gen_time, build_time, test_time)
def gather_tests(testdir: Path) -> typing.List[Path]:
test_names = [t.name for t in testdir.glob('*') if t.is_dir()]
@ -746,6 +776,8 @@ def _run_tests(all_tests: typing.List[typing.Tuple[str, typing.List[Path], bool]
failing_logs.append(result.stdo)
else:
failing_logs.append(result.stdo)
for cmd_res in result.cicmds:
failing_logs.append(cmd_res)
failing_logs.append(result.stde)
if failfast:
print("Cancelling the rest of the tests")

Loading…
Cancel
Save