Skip to content

Commit

Permalink
Add command to export coverage data in Cobertura format
Browse files Browse the repository at this point in the history
  • Loading branch information
nickg committed Jul 23, 2023
1 parent fceb07d commit 9ca6ee7
Show file tree
Hide file tree
Showing 17 changed files with 686 additions and 43 deletions.
2 changes: 1 addition & 1 deletion .github/workflows/build-test.yml
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ jobs:
run: |
sudo apt-get install automake flex llvm-dev check lcov \
libdw-dev libffi-dev bison libreadline-dev tcl8.6-dev \
libzstd-dev libmicrohttpd-dev
libzstd-dev libmicrohttpd-dev libxml2-utils
- name: Generate configure script
run: ./autogen.sh
- name: Configure
Expand Down
2 changes: 2 additions & 0 deletions NEWS.md
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,8 @@
(#731).
- The format of fractional `time` values returned by the standard
`to_string` function was changed to match other simulators.
- New command `--cover-export` exports coverage data in the Cobertura
XML format which is supported by most CI environments such as GitLab.

## Version 1.10.0 - 2023-07-14
- The Zstandard compression library is now a build dependency. Install
Expand Down
7 changes: 7 additions & 0 deletions configure.ac
Original file line number Diff line number Diff line change
Expand Up @@ -145,6 +145,13 @@ AC_DEFINE_UNQUOTED([SH_PATH], ["`$pathprog $sh_path`"], [Path to POSIX shell])
AC_PATH_PROG([diff_path], ["diff"], ["/usr/bin/diff"])
AC_DEFINE_UNQUOTED([DIFF_PATH], ["`$pathprog $diff_path`"], [Path to diff program])

AC_PATH_PROG([xmllint_path], ["xmllint"])
if test -n "$xmllint_path"; then
# This is only used for regression tests
AC_DEFINE_UNQUOTED([XMLLINT_PATH], ["`$pathprog $xmllint_path`"],
[Path to xmllint program for tests])
fi

AC_DEFINE_UNQUOTED([DIR_SEP], ["$DIR_SEP"], [Directory separator])
AC_DEFINE_UNQUOTED([EXEEXT], ["$EXEEXT"], [Executable file extension])

Expand Down
49 changes: 42 additions & 7 deletions nvc.1
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,10 @@ Execute a previously elaborated top level design unit.
Process code coverage data from
.Ar file
and generate coverage report.
.\" --cover-export
.It Fl \-cover-export Ar unit
Export collected coverage information for a previously executed top
level design unit to an external format such as Cobertura XML.
.\" --dump
.It Fl \-dump Ar unit
Print out a pseudo-VHDL representation of an analysed unit. This is
Expand All @@ -79,21 +83,30 @@ Check input files for syntax errors only.
.El
.\"
.Pp
Commands can be chained together. For example to analyse a file
.Ar foo.vhd
Commands can be chained together arbitrarily and the top-level unit
name need only be specified once. For example to analyse a file
.Ql source.vhd
and then elaborate and run a top-level entity
.Ar bar :
.Ql tb :
.Bd -literal -offset indent
$ nvc -a foo.vhd -e bar -r
$ nvc -a source.vhd -e tb -r
.Ed
.Pp
Note that the
Or to elaborate again with coverage collection enabled, run the
simulation, and export the data to the Cobertura format:
.Bd -literal -offset indent
$ nvc -e --cover tb -r --cover-export --format=cobertura -o out.xml
.Ed
.Pp
Note how the
.Ar unit
argument for the
.Fl r
run command is taken from the earlier
and
.Fl \-cover-export
commands is taken from the earlier
.Fl e
elaborate command.
command.
.\" ------------------------------------------------------------
.\" Global options
.\" ------------------------------------------------------------
Expand Down Expand Up @@ -447,6 +460,28 @@ is 5000.
Prints detailed hierarchy coverage when generating code coverage report.
.El
.\" ------------------------------------------------------------
.\" Coverage export options
.\" ------------------------------------------------------------
.Ss Coverage export options
.Bl -tag -width Ds
.\" --format
.It Fl \-format= Ns Ar format
Output file format. Currently the only valid value is
.Ql cobertura
which is the Cobertura XML format widely supported by CI systems.
.\" --output
.It Fl o , Fl \-output= Ns Ar file
Write output to
.Ar file .
If this option is not specified the standard output stream is used.
.\" --relative
.It Fl \-relative Ns Op = Ns Ar path
Strip
.Ar path
or the current working directory from the front of any absolute path
names in the output.
.El
.\" ------------------------------------------------------------
.\" Make options
.\" ------------------------------------------------------------
.Ss Make options
Expand Down
127 changes: 115 additions & 12 deletions src/nvc.c
Original file line number Diff line number Diff line change
Expand Up @@ -88,6 +88,7 @@ static int scan_cmd(int start, int argc, char **argv)
const char *commands[] = {
"-a", "-e", "-r", "-c", "--dump", "--make", "--syntax", "--list",
"--init", "--install", "--print-deps", "--aotgen", "--do", "-i",
"--cover-export"
};

for (int i = start; i < argc; i++) {
Expand Down Expand Up @@ -266,6 +267,9 @@ static void set_top_level(char **argv, int next_cmd)
if (top_level == NULL)
fatal("missing top-level unit name");
}
else if (optind != next_cmd - 1)
fatal("excess positional argument '%s' following top-level unit name",
argv[optind + 1]);
else {
free(top_level_orig);
top_level_orig = xstrdup(argv[optind]);
Expand Down Expand Up @@ -1292,6 +1296,7 @@ static int coverage(int argc, char **argv)
static struct option long_options[] = {
{ "report", required_argument, 0, 'r' },
{ "exclude-file", required_argument, 0, 'e' },
{ "export", required_argument, 0, 'E' },
{ "merge", required_argument, 0, 'm' },
{ "dont-print", required_argument, 0, 'd' },
{ "item-limit", required_argument, 0, 'l' },
Expand All @@ -1300,6 +1305,7 @@ static int coverage(int argc, char **argv)
};

const char *out_db = NULL, *rpt_file = NULL, *exclude_file = NULL;
const char *export_file = NULL;
int c, index;
const char *spec = ":V";
cover_mask_t rpt_mask = 0;
Expand All @@ -1316,6 +1322,9 @@ static int coverage(int argc, char **argv)
case 'e':
exclude_file = optarg;
break;
case 'E':
export_file = optarg;
break;
case 'd':
rpt_mask = parse_cover_print_spec(optarg);
break;
Expand Down Expand Up @@ -1375,19 +1384,104 @@ static int coverage(int argc, char **argv)
cover_report(rpt_file, cover, item_limit);
}

if (export_file && cover) {
progress("Exporting XML coverage report");

FILE *f = fopen(export_file, "w");
if (f == NULL)
fatal_errno("cannot open %s", export_file);

cover_export_cobertura(cover, f, NULL);
fclose(f);
}

return 0;
}

static int cover_export_cmd(int argc, char **argv)
{
static struct option long_options[] = {
{ "format", required_argument, 0, 'f' },
{ "output", required_argument, 0, 'o' },
{ "relative", optional_argument, 0, 'r' },
{ 0, 0, 0, 0 }
};

const int next_cmd = scan_cmd(2, argc, argv);

enum { UNSET, COBERTURA } format = UNSET;
const char *output = NULL, *relative = NULL;
int c, index;
const char *spec = ":o";
while ((c = getopt_long(argc, argv, spec, long_options, &index)) != -1) {
switch (c) {
case 'f':
if (strcasecmp(optarg, "cobertura") == 0)
format = COBERTURA;
else
fatal("unknown format '%s', valid formats are: cobertura", optarg);
break;
case 'o':
output = optarg;
break;
case 'r':
relative = optarg ?: ".";
break;
case '?':
bad_option("coverage", argv);
case ':':
missing_argument("coverage", argv);
default:
abort();
}
}

set_top_level(argv, next_cmd);

if (format == UNSET) {
diag_t *d = diag_new(DIAG_FATAL, NULL);
diag_printf(d, "the $bold$--format$$ option is required");
diag_hint(d, NULL, "pass $bold$--format=cobertura$$ for Cobertura XML");
diag_emit(d);
return EXIT_FAILURE;
}

char *fname LOCAL = xasprintf("_%s.elab.covdb", istr(top_level));
fbuf_t *f = lib_fbuf_open(lib_work(), fname, FBUF_IN, FBUF_CS_NONE);

if (f == NULL)
fatal("no coverage database for %s", istr(top_level));

int rpt_mask = 0;
cover_tagging_t *cover = cover_read_tags(f, rpt_mask);
fbuf_close(f, NULL);

FILE *file = stdout;
if (output != NULL && (file = fopen(output, "w")) == NULL)
fatal_errno("cannot create %s", output);

cover_export_cobertura(cover, file, relative);

if (file != stdout)
fclose(file);

argc -= next_cmd - 1;
argv += next_cmd - 1;

return argc > 1 ? process_command(argc, argv) : 0;
}

static void usage(void)
{
printf("Usage: %s [OPTION]... COMMAND [OPTION]...\n"
"\n"
"COMMAND is one of:\n"
" -a [OPTION]... FILE...\t\tAnalyse FILEs into work library\n"
" -e [OPTION]... UNIT\t\tElaborate and generate code for UNIT\n"
" -r [OPTION]... UNIT\t\tExecute previously elaborated UNIT\n"
" -e [OPTION]... TOP\t\tElaborate design unit TOP\n"
" -r [OPTION]... TOP\t\tExecute previously elaborated TOP\n"
" -c [OPTION]... FILE...\t\tProcess code coverage from FILEs\n"
" -i\t\t\t\tLaunch interactive TCL shell\n"
" --cover-export TOP\t\tExport code coverage statistics for TOP\n"
" --do SCRIPT\t\t\tEvaluate TCL script\n"
" --dump [OPTION]... UNIT\tPrint out previously analysed UNIT\n"
" --init\t\t\t\tInitialise work library directory\n"
Expand All @@ -1414,7 +1508,7 @@ static void usage(void)
" --bootstrap\tAllow compilation of STANDARD package\n"
" -D, --define NAME=VAL\tSet preprocessor symbol NAME to VAL\n"
" --error-limit=NUM\tStop after NUM errors\n"
" -f, --files=LIST\t\tRead files to analyse from LIST\n"
" -f, --files=LIST\tRead files to analyse from LIST\n"
" --psl\t\tEnable parsing of PSL directives in comments\n"
" --relaxed\t\tDisable certain pedantic rule checks\n"
"\n"
Expand Down Expand Up @@ -1458,6 +1552,7 @@ static void usage(void)
" --merge=OUTPUT\tMerge all input coverage databases from FILEs\n"
" \tto OUTPUT coverage database\n"
" --exclude-file=\tApply exclude file when generating report\n"
" --export=FILE\tEquivalent to `--cover-export -o FILE'\n"
" --dont-print=\tDo not include specified tags in generated "
"code\n"
" \tcoverage report. Argument is a list of:\n"
Expand All @@ -1467,6 +1562,11 @@ static void usage(void)
" --report=DIR\tGenerate HTML report with code coverage results\n"
" \tto DIR folder.\n"
"\n"
"Coverage export options:\n"
" --format=FMT\tFile format (must be 'cobertura')\n"
" -o, --output=FILE\tOutput file name\n"
" --relative=PATH\tStrip PATH from prefix of absolute paths\n"
"\n"
"Dump options:\n"
" -e, --elab\t\tDump an elaborated unit\n"
" -b, --body\t\tDump package body\n"
Expand Down Expand Up @@ -1568,15 +1668,16 @@ static void parse_library_map(char *str)
static int process_command(int argc, char **argv)
{
static struct option long_options[] = {
{ "dump", no_argument, 0, 'd' },
{ "make", no_argument, 0, 'm' },
{ "syntax", no_argument, 0, 's' },
{ "list", no_argument, 0, 'l' },
{ "init", no_argument, 0, 'n' },
{ "install", no_argument, 0, 'I' },
{ "print-deps", no_argument, 0, 'P' },
{ "aotgen", no_argument, 0, 'A' },
{ "do", no_argument, 0, 'D' },
{ "dump", no_argument, 0, 'd' },
{ "make", no_argument, 0, 'm' },
{ "syntax", no_argument, 0, 's' },
{ "list", no_argument, 0, 'l' },
{ "init", no_argument, 0, 'n' },
{ "install", no_argument, 0, 'I' },
{ "print-deps", no_argument, 0, 'P' },
{ "aotgen", no_argument, 0, 'A' },
{ "do", no_argument, 0, 'D' },
{ "cover-export", no_argument, 0, 'E' },
{ 0, 0, 0, 0 }
};

Expand Down Expand Up @@ -1614,6 +1715,8 @@ static int process_command(int argc, char **argv)
return do_cmd(argc, argv);
case 'i':
return interact_cmd(argc, argv);
case 'E':
return cover_export_cmd(argc, argv);
default:
fatal("missing command, try %s --help for usage", PACKAGE);
return EXIT_FAILURE;
Expand Down
1 change: 1 addition & 0 deletions src/option.c
Original file line number Diff line number Diff line change
Expand Up @@ -164,4 +164,5 @@ void set_default_options(void)
opt_set_int(OPT_PSL_COMMENTS, 0);
opt_set_int(OPT_NO_COLLAPSE, 0);
opt_set_int(OPT_COVER_VERBOSE, get_int_env("NVC_COVER_VERBOSE", 0));
opt_set_int(OPT_COVER_TIMESTAMP, get_int_env("NVC_COVER_TIMESTAMP", -1));
}
1 change: 1 addition & 0 deletions src/option.h
Original file line number Diff line number Diff line change
Expand Up @@ -60,6 +60,7 @@ typedef enum {
OPT_PSL_COMMENTS,
OPT_NO_COLLAPSE,
OPT_COVER_VERBOSE,
OPT_COVER_TIMESTAMP,

OPT_LAST_NAME
} opt_name_t;
Expand Down
Loading

0 comments on commit 9ca6ee7

Please sign in to comment.