CDash report for configure step

Generate CTest XML file containing configure output
This commit is contained in:
Zack Galbreath 2018-04-19 15:20:10 -04:00 committed by Todd Gamblin
parent f35d5bbf2b
commit d7581697a5
4 changed files with 127 additions and 8 deletions

View file

@ -191,7 +191,8 @@ def install(parser, args, **kwargs):
tty.warn("Deprecated option: --run-tests: use --test=all instead")
# 1. Abstract specs from cli
reporter = spack.report.collect_info(args.log_format)
reporter = spack.report.collect_info(args.log_format,
' '.join(args.package))
if args.log_file:
reporter.filename = args.log_file

View file

@ -27,13 +27,19 @@
import functools
import itertools
import os.path
import platform
import re
import socket
import time
import traceback
import xml.sax.saxutils
import llnl.util.lang
import spack.build_environment
import spack.fetch_strategy
import spack.package
from spack.util.log_parse import parse_log_events
templates = {
'junit': os.path.join('reports', 'junit.xml'),
@ -51,7 +57,7 @@
def fetch_package_log(pkg):
try:
with open(pkg.build_log_path, 'r') as f:
with codecs.open(pkg.build_log_path, 'r', 'utf-8') as f:
return ''.join(f.readlines())
except Exception:
return 'Cannot open build log for {0}'.format(
@ -250,8 +256,17 @@ class collect_info(object):
Raises:
ValueError: when ``format_name`` is not in ``valid_formats``
"""
def __init__(self, format_name):
def __init__(self, format_name, install_command):
self.format_name = format_name
# Consider setting these properties in a more CDash specific place.
self.install_command = install_command
self.hostname = socket.gethostname()
self.osname = platform.system()
self.starttime = int(time.time())
# TODO: remove hardcoded use of Experimental here.
# Make the submission model configurable.
self.buildstamp = time.strftime("%Y%m%d-%H%M-Experimental",
time.localtime(self.starttime))
# Check that the format is valid
if format_name not in itertools.chain(valid_formats, [None]):
@ -263,14 +278,102 @@ def __enter__(self):
self.collector = InfoCollector(self.specs)
self.collector.__enter__()
def cdash_initialize_report(self, report_data):
if not os.path.exists(self.filename):
os.mkdir(self.filename)
report_data['install_command'] = self.install_command
report_data['buildstamp'] = self.buildstamp
report_data['hostname'] = self.hostname
report_data['osname'] = self.osname
def cdash_build_report(self, report_data):
self.cdash_initialize_report(report_data)
# Mapping Spack phases to the corresponding CTest/CDash phase.
map_phases_to_cdash = {
'autoreconf': 'configure',
'cmake': 'configure',
'configure': 'configure',
'edit': 'configure'
}
# Initialize data structures common to each phase's report.
cdash_phases = set(map_phases_to_cdash.values())
for phase in cdash_phases:
report_data[phase] = {}
report_data[phase]['log'] = ""
report_data[phase]['status'] = 0
report_data[phase]['starttime'] = self.starttime
report_data[phase]['endtime'] = self.starttime
# Track the phases we perform so we know what reports to create.
phases_encountered = []
# Parse output phase-by-phase.
phase_regexp = re.compile(r"Executing phase: '(.*)'")
for spec in self.collector.specs:
for package in spec['packages']:
if 'stdout' in package:
current_phase = ''
for line in package['stdout'].splitlines():
match = phase_regexp.search(line)
if match:
current_phase = match.group(1)
if current_phase not in map_phases_to_cdash:
current_phase = ''
continue
beginning_of_phase = True
else:
if beginning_of_phase:
cdash_phase = \
map_phases_to_cdash[current_phase]
if cdash_phase not in phases_encountered:
phases_encountered.append(cdash_phase)
report_data[cdash_phase]['log'] += \
text_type("{0} output for {1}:\n".format(
cdash_phase, package['name']))
beginning_of_phase = False
report_data[cdash_phase]['log'] += \
xml.sax.saxutils.escape(line) + "\n"
for phase in phases_encountered:
errors, warnings = parse_log_events(
report_data[phase]['log'].splitlines())
nerrors = len(errors)
if phase == 'configure' and nerrors > 0:
report_data[phase]['status'] = 1
# Write the report.
report_name = phase.capitalize() + ".xml"
phase_report = os.path.join(self.filename, report_name)
with open(phase_report, 'w') as f:
env = spack.tengine.make_environment()
site_template = os.path.join(templates[self.format_name],
'Site.xml')
t = env.get_template(site_template)
f.write(t.render(report_data))
phase_template = os.path.join(templates[self.format_name],
report_name)
t = env.get_template(phase_template)
f.write(t.render(report_data))
def __exit__(self, exc_type, exc_val, exc_tb):
if self.format_name:
# Close the collector and restore the
# original PackageBase.do_install
self.collector.__exit__(exc_type, exc_val, exc_tb)
# Write the report
with open(self.filename, 'w') as f:
env = spack.tengine.make_environment()
t = env.get_template(templates[self.format_name])
f.write(t.render({'specs': self.collector.specs}))
report_data = {'specs': self.collector.specs}
if self.format_name == 'cdash':
# CDash reporting results are split across multiple files.
self.cdash_build_report(report_data)
else:
# Write the report
with open(self.filename, 'w') as f:
env = spack.tengine.make_environment()
t = env.get_template(templates[self.format_name])
f.write(t.render(report_data))

View file

@ -0,0 +1,8 @@
<Configure>
<StartConfigureTime>{{ configure.starttime }}</StartConfigureTime>
<ConfigureCommand>{{ install_command }}</ConfigureCommand>
<Log>{{ configure.log }}</Log>
<ConfigureStatus>{{ configure.status }}</ConfigureStatus>
<EndConfigureTime>{{ configure.endtime }}</EndConfigureTime>
</Configure>
</Site>

View file

@ -0,0 +1,7 @@
<?xml version="1.0" encoding="UTF-8"?>
<Site BuildName="{{ install_command }}"
BuildStamp="{{ buildstamp }}"
Name="{{ hostname }}"
OSName="{{ osname }}"
>