• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

nickg / nvc / 13775379932

10 Mar 2025 09:11PM UTC coverage: 92.318% (-0.001%) from 92.319%
13775379932

push

github

nickg
Fix crash when nvc -i called with no arguments

4 of 6 new or added lines in 1 file covered. (66.67%)

34 existing lines in 2 files now uncovered.

68075 of 73740 relevant lines covered (92.32%)

432883.26 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

63.77
/src/nvc.c
1
//
2
//  Copyright (C) 2011-2025  Nick Gasson
3
//
4
//  This program is free software: you can redistribute it and/or modify
5
//  it under the terms of the GNU General Public License as published by
6
//  the Free Software Foundation, either version 3 of the License, or
7
//  (at your option) any later version.
8
//
9
//  This program is distributed in the hope that it will be useful,
10
//  but WITHOUT ANY WARRANTY; without even the implied warranty of
11
//  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12
//  GNU General Public License for more details.
13
//
14
//  You should have received a copy of the GNU General Public License
15
//  along with this program.  If not, see <http://www.gnu.org/licenses/>.
16
//
17

18
#include "util.h"
19
#include "common.h"
20
#include "cov/cov-api.h"
21
#include "diag.h"
22
#include "jit/jit-llvm.h"
23
#include "jit/jit.h"
24
#include "lib.h"
25
#include "lower.h"
26
#include "mir/mir-unit.h"
27
#include "option.h"
28
#include "phase.h"
29
#include "rt/assert.h"
30
#include "rt/model.h"
31
#include "rt/mspace.h"
32
#include "rt/rt.h"
33
#include "rt/shell.h"
34
#include "rt/wave.h"
35
#include "scan.h"
36
#include "server.h"
37
#include "thread.h"
38
#include "vhpi/vhpi-util.h"
39
#include "vlog/vlog-node.h"
40
#include "vlog/vlog-phase.h"
41
#include "vpi/vpi-model.h"
42

43
#include <getopt.h>
44
#include <stdlib.h>
45
#include <string.h>
46
#include <ctype.h>
47
#include <assert.h>
48
#include <sys/types.h>
49
#include <limits.h>
50
#include <unistd.h>
51
#include <dirent.h>
52
#include <time.h>
53

54
#if HAVE_GIT_SHA
55
#include "gitsha.h"
56
#define GIT_SHA_ONLY(x) x
57
#else
58
#define GIT_SHA_ONLY(x)
59
#endif
60

61
#if !defined HAVE_LLVM || !defined SYSTEM_CC
62
#define DEFAULT_JIT true
63
#else
64
#define DEFAULT_JIT false
65
#endif
66

67
typedef struct {
68
   jit_t           *jit;
69
   unit_registry_t *registry;
70
   mir_context_t   *mir;
71
   bool             user_set_std;
72
   vhpi_context_t  *vhpi;
73
   vpi_context_t   *vpi;
74
   const char      *plugins;
75
   rt_model_t      *model;
76
   cover_data_t    *cover;
77
   ident_t          top_level;
78
   const char      *top_level_arg;
79
   lib_t            work;
80
} cmd_state_t;
81

82
const char copy_string[] =
83
   "Copyright (C) 2011-2025  Nick Gasson\n"
84
   "This program comes with ABSOLUTELY NO WARRANTY. This is free software, "
85
   "and\nyou are welcome to redistribute it under certain conditions. See "
86
   "the GNU\nGeneral Public Licence for details.";
87
const char version_string[] =
88
   PACKAGE_STRING GIT_SHA_ONLY(" (" GIT_SHA ")")
89
   LLVM_ONLY(" (Using LLVM " LLVM_VERSION ")") DEBUG_ONLY(" [debug]");
90

91
static int process_command(int argc, char **argv, cmd_state_t *state);
92
static int parse_int(const char *str);
93
static jit_t *get_jit(unit_registry_t *ur, mir_context_t *mc);
94

95
static ident_t to_unit_name(const char *str)
6,536✔
96
{
97
   char *name LOCAL = xstrdup(str);
13,069✔
98
   for (char *p = name; *p; p++) {
51,613✔
99
      if (!isalnum_iso88591(*p) && (p == name || (*p != '_' && *p != '-')))
45,080✔
100
         fatal("'%s' is not a valid design unit name", str);
3✔
101

102
      *p = toupper_iso88591(*p);
45,077✔
103
   }
104

105
   return ident_prefix(lib_name(lib_work()), ident_new(name), '.');
6,533✔
106
}
107

108
static int scan_cmd(int start, int argc, char **argv)
14,538✔
109
{
110
   const char *commands[] = {
14,538✔
111
      "-a", "-e", "-r", "-c", "--dump", "--make", "--syntax", "--list",
112
      "--init", "--install", "--print-deps", "--aotgen", "--do", "-i",
113
      "--cover-export", "--preprocess", "--gui", "--cover-merge",
114
      "--cover-report",
115
   };
116

117
   for (int i = start; i < argc; i++) {
38,304✔
118
      for (size_t j = 0; j < ARRAY_LEN(commands); j++) {
498,909✔
119
         if (commands[j][1] == '-') {
475,143✔
120
            if (strcmp(argv[i], commands[j]) == 0)
335,100✔
121
               return i;
195✔
122
         }
123
         else if (argv[i][0] == '-' && commands[j][1] == argv[i][1])
140,043✔
124
            return i;
10,218✔
125
      }
126
   }
127

128
   return argc;
129
}
130

131
static void bad_option(const char *what, char **argv)
6✔
132
{
133
   if (optopt == 0)
6✔
134
      fatal("unrecognised %s option $bold$%s$$", what, argv[optind - 1]);
3✔
135
   else
136
      fatal("unrecognised %s option $bold$-%c$$", what, optopt);
3✔
137
}
138

139
static void missing_argument(const char *what, char **argv)
3✔
140
{
141
   fatal("%s option $bold$%s$$ requires an argument", what, argv[optind - 1]);
3✔
142
}
143

144
static void parse_pp_define(char *optarg)
15✔
145
{
146
   char *eq = strchr(optarg, '=');
15✔
147
   if (eq == NULL)
15✔
148
      pp_defines_add(optarg, "");
6✔
149
   else {
150
      *eq = '\0';
9✔
151
      pp_defines_add(optarg, eq + 1);
9✔
152
   }
153
}
15✔
154

155
static void do_file_list(const char *file, jit_t *jit, unit_registry_t *ur)
9✔
156
{
157
   FILE *f;
9✔
158
   if (strcmp(file, "-") == 0)
9✔
159
      f = stdin;
3✔
160
   else if ((f = fopen(file, "r")) == NULL)
6✔
161
      fatal_errno("failed to open %s", file);
×
162

163
   char *line = NULL;
9✔
164
   size_t nchars, bufsz = 0;
9✔
165
   while ((nchars = getline(&line, &bufsz, f)) != -1) {
45✔
166
      char *stop = strpbrk(line, "\r\n#") ?: line + nchars;
36✔
167
      *stop = '\0';
36✔
168

169
      // Trim trailing whitespace
170
      while (stop > line && isspace_iso88591(*--stop))
63✔
171
         *stop = '\0';
27✔
172

173
      if (strlen(line) == 0)
36✔
174
         continue;
18✔
175

176
      analyse_file(line, jit, ur);
18✔
177
   }
178

179
   free(line);
9✔
180
   fclose(f);
9✔
181
}
9✔
182

183
static int analyse(int argc, char **argv, cmd_state_t *state)
3,526✔
184
{
185
   static struct option long_options[] = {
3,526✔
186
      { "bootstrap",       no_argument,       0, 'b' },
187
      { "error-limit",     required_argument, 0, 'l' },
188
      { "dump-vcode",      optional_argument, 0, 'v' },
189
      { "psl",             no_argument,       0, 'P' },
190
      { "relax",           required_argument, 0, 'X' },
191
      { "relaxed",         no_argument,       0, 'R' },
192
      { "define",          required_argument, 0, 'D' },
193
      { "files",           required_argument, 0, 'f' },
194
      { "check-synthesis", no_argument,       0, 's' },
195
      { "no-save",         no_argument,       0, 'N' },
196
      { "single-unit",     no_argument,       0, 'u' },
197
      { "preserve-case",   no_argument,       0, 'p' },
198
      { 0, 0, 0, 0 }
199
   };
200

201
   const int next_cmd = scan_cmd(2, argc, argv);
3,526✔
202
   int c, index = 0, error_limit = 20;
3,526✔
203
   const char *file_list = NULL;
3,526✔
204
   const char *spec = ":D:f:";
3,526✔
205
   bool no_save = false;
3,526✔
206

207
   while ((c = getopt_long(next_cmd, argv, spec, long_options, &index)) != -1) {
3,719✔
208
      switch (c) {
193✔
209
      case 0:
210
         // Set a flag
211
         break;
212
      case '?':
×
213
         bad_option("analysis", argv);
×
214
      case ':':
×
215
         missing_argument("analysis", argv);
×
216
      case 'b':
3✔
217
         opt_set_int(OPT_BOOTSTRAP, 1);
3✔
218
         break;
3✔
219
      case 'v':
×
220
         opt_set_str(OPT_DUMP_VCODE, optarg ?: "");
×
221
         break;
×
222
      case 'X':
6✔
223
         warnf("The $bold$--relax=$$ option is deprecated: use the combined "
6✔
224
               "$bold$--relaxed$$ option instead");
225
         opt_set_int(OPT_RELAXED, 1);
6✔
226
         break;
6✔
227
      case 'l':
×
228
         error_limit = parse_int(optarg);
×
229
         break;
×
230
      case 'P':
60✔
231
         opt_set_int(OPT_PSL_COMMENTS, 1);
60✔
232
         break;
60✔
233
      case 'R':
28✔
234
         opt_set_int(OPT_RELAXED, 1);
28✔
235
         break;
28✔
236
      case 'D':
9✔
237
         parse_pp_define(optarg);
9✔
238
         break;
9✔
239
      case 'f':
6✔
240
         file_list = optarg;
6✔
241
         break;
6✔
242
      case 's':
×
243
         opt_set_int(OPT_CHECK_SYNTHESIS, 1);
×
244
         break;
×
245
      case 'N':
3✔
246
         no_save = true;
3✔
247
         break;
3✔
248
      case 'u':
3✔
249
         opt_set_int(OPT_SINGLE_UNIT, 1);
3✔
250
         break;
3✔
251
      case 'p':
75✔
252
         opt_set_int(OPT_PRESERVE_CASE, 1);
75✔
253
         break;
75✔
254
      default:
×
255
         should_not_reach_here();
256
      }
257
   }
258

259
   set_error_limit(error_limit);
3,526✔
260

261
   if (state->mir == NULL)
3,526✔
262
      state->mir = mir_context_new();
3,526✔
263

264
   if (state->registry == NULL)
3,526✔
265
      state->registry = unit_registry_new();
3,526✔
266

267
   jit_t *jit = jit_new(state->registry, state->mir);
3,526✔
268

269
   if (file_list != NULL)
3,526✔
270
      do_file_list(file_list, jit, state->registry);
6✔
271
   else if (optind == next_cmd)
3,520✔
NEW
272
      fatal("missing file name");
×
273

274
   for (int i = optind; i < next_cmd; i++) {
7,067✔
275
      if (argv[i][0] == '@')
3,544✔
276
         do_file_list(argv[i] + 1, jit, state->registry);
3✔
277
      else
278
         analyse_file(argv[i], jit, state->registry);
3,541✔
279
   }
280

281
   jit_free(jit);
3,523✔
282
   set_error_limit(0);
3,523✔
283

284
   if (error_count() > 0)
3,523✔
285
      return EXIT_FAILURE;
286

287
   if (!no_save)
3,523✔
288
      lib_save(state->work);
3,520✔
289

290
   if (error_count() > 0)
3,523✔
291
      return EXIT_FAILURE;   // May have errors saving library
292

293
   argc -= next_cmd - 1;
3,520✔
294
   argv += next_cmd - 1;
3,520✔
295

296
   return argc > 1 ? process_command(argc, argv, state) : EXIT_SUCCESS;
3,520✔
297
}
298

299
static void parse_generic(const char *str)
51✔
300
{
301
   char *copy LOCAL = xstrdup(str);
102✔
302

303
   char *split = strchr(copy, '=');
51✔
304
   if (split == NULL || *(split + 1) == '\0' || *copy == '\0')
51✔
305
      fatal("invalid generic specification '%s' (use -gNAME=VALUE)", str);
×
306

307
   *split = '\0';
51✔
308

309
   for (char *p = copy; *p != '\0'; p++)
222✔
310
      *p = toupper_iso88591(*p);
171✔
311

312
   elab_set_generic(copy, split + 1);
51✔
313
}
51✔
314

315
static void set_top_level(char **argv, int next_cmd, cmd_state_t *state)
6,665✔
316
{
317
   if (optind == next_cmd) {
6,665✔
318
      if (state->top_level == NULL)
141✔
319
         fatal("missing top-level unit name");
×
320
   }
321
   else if (optind != next_cmd - 1)
6,524✔
322
      fatal("excess positional argument '%s' following top-level unit name",
×
323
            argv[optind + 1]);
×
324
   else {
325
      state->top_level_arg = argv[optind];
6,524✔
326
      state->top_level = to_unit_name(argv[optind]);
6,524✔
327
   }
328
}
6,662✔
329

330
static void parse_cover_options(const char *str, cover_mask_t *mask,
93✔
331
                                int *array_limit, int *threshold)
332
{
333
   static const struct {
93✔
334
      const char *name;
335
      cover_mask_t mask;
336
   } options[] = {
337
      { "statement",             COVER_MASK_STMT                        },
338
      { "toggle",                COVER_MASK_TOGGLE                      },
339
      { "branch",                COVER_MASK_BRANCH                      },
340
      { "expression",            COVER_MASK_EXPRESSION                  },
341
      { "fsm-state",             COVER_MASK_STATE                       },
342
      { "functional",            COVER_MASK_FUNCTIONAL                  },
343
      { "all",                   COVER_MASK_ALL                         },
344
      { "count-from-undefined",  COVER_MASK_TOGGLE_COUNT_FROM_UNDEFINED },
345
      { "count-from-to-z",       COVER_MASK_TOGGLE_COUNT_FROM_TO_Z      },
346
      { "include-mems",          COVER_MASK_TOGGLE_INCLUDE_MEMS         },
347
      { "exclude-unreachable",   COVER_MASK_EXCLUDE_UNREACHABLE         },
348
      { "fsm-no-default-enums",  COVER_MASK_FSM_NO_DEFAULT_ENUMS        }
349
   };
350

351
   for (const char *start = str; ; str++) {
1,203✔
352
      if (*str == ',' || *str == '\0') {
1,203✔
353
         if (strncmp(start, "ignore-arrays-from-", 19) == 0)
123✔
354
            *array_limit = parse_int(start + 19);
×
355
         else if (strncmp(start, "threshold-", 10) == 0)
123✔
356
            *threshold = parse_int(start + 10);
×
357
         else {
358
            int pos = 0;
359
            for (; pos < ARRAY_LEN(options); pos++) {
573✔
360
               if (!strncmp(options[pos].name, start, str - start))
573✔
361
                  break;
362
            }
363

364
            if (pos == ARRAY_LEN(options)) {
123✔
365
               diag_t *d = diag_new(DIAG_FATAL, NULL);
×
366
               diag_printf(d, "unknown coverage type '%.*s'",
×
367
                           (int)(str - start), start);
×
368
               diag_hint(d, NULL, "valid coverage types are: statement, "
×
369
                         "toggle, branch, and expression");
370
               diag_hint(d, NULL, "selected coverage types should be "
×
371
                         "comma separated e.g $bold$--cover=toggle,branch$$");
372
               diag_emit(d);
×
373
               fatal_exit(EXIT_FAILURE);
×
374
            }
375
            else
376
               *mask |= options[pos].mask;
123✔
377
         }
378

379
         if (*str == '\0')
123✔
380
            break;
381

382
         start = str + 1;
30✔
383
      }
384
   }
385
}
93✔
386

387
static int parse_optimise_level(const char *str)
3,164✔
388
{
389
   char *eptr;
3,164✔
390
   const int level = strtoul(optarg, &eptr, 10);
3,164✔
391
   if (level > 3 || *eptr != '\0')
3,164✔
392
      fatal("invalid optimisation level %s", optarg);
×
393
   return level;
3,164✔
394
}
395

396
static int elaborate(int argc, char **argv, cmd_state_t *state)
3,349✔
397
{
398
   static struct option long_options[] = {
3,349✔
399
      { "dump-llvm",       no_argument,       0, 'd' },
400
      { "dump-vcode",      optional_argument, 0, 'v' },
401
      { "cover",           optional_argument, 0, 'c' },
402
      { "cover-file",      required_argument, 0, 'F' },
403
      { "cover-spec",      required_argument, 0, 's' },
404
      { "sdf",             required_argument, 0, 'f' },
405
      { "verbose",         no_argument,       0, 'V' },
406
      { "no-save",         no_argument,       0, 'N' },
407
      { "jit",             no_argument,       0, 'j' },
408
      { "no-collapse",     no_argument,       0, 'C' },
409
      { "trace",           no_argument,       0, 't' },
410
      { 0, 0, 0, 0 }
411
   };
412

413
   bool use_jit = DEFAULT_JIT, no_save = false;
3,349✔
414
   unit_meta_t meta = {};
3,349✔
415
   cover_mask_t cover_mask = 0;
3,349✔
416
   const char *cover_spec_file = NULL, *sdf_args = NULL;
3,349✔
417
   int cover_array_limit = 0;
3,349✔
418
   int threshold = 1;
3,349✔
419
   const int next_cmd = scan_cmd(2, argc, argv);
3,349✔
420
   int c, index = 0;
3,349✔
421
   const char *spec = ":Vg:O:jt";
3,349✔
422
   while ((c = getopt_long(next_cmd, argv, spec, long_options, &index)) != -1) {
11,690✔
423
      switch (c) {
8,341✔
424
      case 'O':
3,161✔
425
         opt_set_int(OPT_OPTIMISE, parse_optimise_level(optarg));
3,161✔
426
         break;
3,161✔
427
      case 'd':
×
428
         opt_set_int(OPT_DUMP_LLVM, 1);
×
429
         break;
×
430
      case 'v':
×
431
         opt_set_str(OPT_DUMP_VCODE, optarg ?: "");
×
432
         break;
×
433
      case 'c':
120✔
434
         if (optarg)
120✔
435
            parse_cover_options(optarg, &(cover_mask), &(cover_array_limit),
93✔
436
                                &(threshold));
437
         else
438
            cover_mask = COVER_MASK_ALL;
27✔
439
         break;
440
      case 'V':
×
441
         opt_set_int(OPT_VERBOSE, 1);
×
442
         break;
×
443
      case 'N':
2,848✔
444
         opt_set_int(OPT_NO_SAVE, 1);
2,848✔
445
         no_save = true;
2,848✔
446
         break;
2,848✔
447
      case 'C':
3✔
448
         opt_set_int(OPT_NO_COLLAPSE, 1);
3✔
449
         break;
3✔
450
      case 'j':
2,122✔
451
         use_jit = true;
2,122✔
452
         break;
2,122✔
453
      case 'g':
51✔
454
         parse_generic(optarg);
51✔
455
         break;
51✔
456
      case 's':
6✔
457
         cover_spec_file = optarg;
6✔
458
         break;
6✔
459
      case 'F':
30✔
460
         meta.cover_file = optarg;
30✔
461
         break;
30✔
462
      case 'f':
×
463
         sdf_args = optarg;
×
464
         break;
×
465
      case 't':
×
466
         opt_set_int(OPT_RT_TRACE, 1);
×
467
         break;
×
468
      case 0:
469
         // Set a flag
470
         break;
471
      case '?':
×
472
         bad_option("elaboration", argv);
×
473
      case ':':
×
474
         missing_argument("elaboration", argv);
×
475
      default:
×
476
         should_not_reach_here();
477
      }
478
   }
479

480
   set_top_level(argv, next_cmd, state);
3,349✔
481

482
   progress("initialising");
3,346✔
483

484
   object_t *obj = lib_get_generic(state->work, state->top_level, NULL);
3,346✔
485
   if (obj == NULL)
3,340✔
486
      fatal("cannot find unit %s in library %s",
×
487
            istr(state->top_level), istr(lib_name(state->work)));
488

489
   progress("loading top-level unit");
3,340✔
490

491
   char *cover_default LOCAL = NULL;
6,674✔
492
   cover_data_t *cover = NULL;
3,340✔
493
   if (cover_mask != 0) {
3,340✔
494
      cover = cover_data_init(cover_mask, cover_array_limit, threshold);
120✔
495

496
      if (cover_spec_file != NULL)
120✔
497
         cover_load_spec_file(cover, cover_spec_file);
6✔
498

499
      if (meta.cover_file == NULL) {
120✔
500
         cover_default = xasprintf("%s.ncdb", state->top_level_arg);
90✔
501
         meta.cover_file = cover_default;
90✔
502
      }
503
   }
504

505
   if (sdf_args != NULL) {
3,340✔
506
      // TODO: Pass min-max spec to underlying sdf_parse somehow
507
      analyse_file(sdf_args, NULL, NULL);
×
508
   }
509

510
   if (state->model != NULL) {
3,340✔
511
      model_free(state->model);
×
512
      state->model = NULL;
×
513
   }
514

515
   if (state->jit != NULL) {
3,340✔
516
      jit_free(state->jit);
×
517
      state->jit = NULL;
×
518
   }
519

520
   if (state->registry != NULL) {
3,340✔
521
      unit_registry_free(state->registry);
3,269✔
522
      state->registry = NULL;
3,269✔
523
   }
524

525
   if (state->mir != NULL) {
3,340✔
526
      mir_context_free(state->mir);
3,269✔
527
      state->mir = NULL;
3,269✔
528
   }
529

530
   state->mir = mir_context_new();
3,340✔
531
   state->registry = unit_registry_new();
3,340✔
532
   state->jit = get_jit(state->registry, state->mir);
3,340✔
533
   state->model = model_new(state->jit, cover);
3,340✔
534

535
   if (state->vhpi == NULL)
3,340✔
536
      state->vhpi = vhpi_context_new();
3,265✔
537

538
   tree_t top = elab(obj, state->jit, state->registry, cover,
3,340✔
539
                     NULL, state->model);
540

541
   if (top == NULL)
3,337✔
542
      return EXIT_FAILURE;
543

544
   lib_put_meta(state->work, top, &meta);
3,337✔
545

546
   progress("elaborating design");
3,337✔
547

548
   if (cover != NULL) {
3,337✔
549
      fbuf_t *covdb = fbuf_open(meta.cover_file, FBUF_OUT, FBUF_CS_NONE);
120✔
550
      cover_dump_items(cover, covdb, COV_DUMP_ELAB, NULL);
120✔
551
      fbuf_close(covdb, NULL);
120✔
552

553
      progress("dumping coverage data");
120✔
554
   }
555

556
   if (error_count() > 0)
3,337✔
557
      return EXIT_FAILURE;
558

559
   char *pack_name LOCAL = xasprintf("_%s.pack", istr(state->top_level));
6,671✔
560
   char *dll_name LOCAL = xasprintf("_%s." DLL_EXT, istr(tree_ident(top)));
6,671✔
561

562
   // Delete any existing generated code to avoid accidentally loading
563
   // the wrong version later
564
   lib_delete(state->work, pack_name);
3,337✔
565
   lib_delete(state->work, dll_name);
3,337✔
566

567
   if (!no_save) {
3,337✔
568
      lib_save(state->work);
489✔
569
      progress("saving library");
489✔
570
   }
571

572
   if (use_jit && !no_save) {
3,337✔
573
      FILE *f = lib_fopen(state->work, pack_name, "wb");
235✔
574
      if (f == NULL)
235✔
575
         fatal_errno("fopen: %s", pack_name);
×
576

577
      ident_t b0 = tree_ident(tree_stmt(top, 0));
235✔
578
      ident_t root = ident_prefix(lib_name(state->work), b0, '.');
235✔
579

580
      vcode_unit_t vu = unit_registry_get(state->registry, root);
235✔
581
      assert(vu != NULL);
235✔
582

583
      jit_write_pack(state->jit, vu, f);
235✔
584
      fclose(f);
235✔
585

586
      progress("writing JIT pack");
235✔
587
   }
588

589
   if (!use_jit)
3,337✔
590
      LLVM_ONLY(cgen(top, state->registry, state->jit));
1,215✔
591

592
   if (!use_jit || cover != NULL) {
3,337✔
593
      // Must discard current JIT state to load AOT library later
594
      model_free(state->model);
1,244✔
595
      jit_free(state->jit);
1,244✔
596
      mir_context_free(state->mir);
1,244✔
597
      state->jit = NULL;
1,244✔
598
      state->model = NULL;
1,244✔
599
      state->mir = NULL;
1,244✔
600
   }
601

602
   argc -= next_cmd - 1;
3,337✔
603
   argv += next_cmd - 1;
3,337✔
604

605
   return argc > 1 ? process_command(argc, argv, state) : EXIT_SUCCESS;
3,337✔
606
}
607

608
static uint64_t parse_time(const char *str)
16✔
609
{
610
   char     unit[4];
16✔
611
   unsigned base;
16✔
612
   uint64_t mult = 1;
16✔
613

614
   if (sscanf(str, "%u%3s", &base, unit) != 2)
16✔
615
      fatal("invalid time format: %s", str);
×
616

617
   if      (strcmp(unit, "fs") == 0)  mult = 1;
16✔
618
   else if (strcmp(unit, "ps") == 0)  mult = 1000;
16✔
619
   else if (strcmp(unit, "ns") == 0)  mult = 1000000;
16✔
620
   else if (strcmp(unit, "us") == 0)  mult = 1000000000;
6✔
621
   else if (strcmp(unit, "ms") == 0)  mult = 1000000000000;
×
622
   else if (strcmp(unit, "sec") == 0) mult = 1000000000000000;
×
623
   else
624
      fatal("invalid unit: %s", unit);
×
625

626
   return base * mult;
16✔
627
}
628

629
static int parse_int(const char *str)
12✔
630
{
631
   char *eptr = NULL;
12✔
632
   int n = strtol(str, &eptr, 0);
12✔
633
   if ((eptr == NULL) || (*eptr != '\0'))
12✔
634
      fatal("invalid integer: %s", str);
×
635
   return n;
12✔
636
}
637

638
static bool parse_on_off(const char *str)
6✔
639
{
640
   if (strcasecmp(str, "on") == 0)
6✔
641
      return true;
642
   else if (strcasecmp(str, "off") == 0)
3✔
643
      return false;
644

645
   fatal("specifiy 'on' or 'off' instead of '%s'", str);
×
646
}
647

648
static vhdl_severity_t parse_severity(const char *str)
6✔
649
{
650
   if (strcasecmp(str, "note") == 0)
6✔
651
      return SEVERITY_NOTE;
652
   else if (strcasecmp(str, "warning") == 0)
6✔
653
      return SEVERITY_WARNING;
654
   else if (strcasecmp(str, "error") == 0)
6✔
655
      return SEVERITY_ERROR;
656
   else if (strcasecmp(str, "failure") == 0)
6✔
657
      return SEVERITY_FAILURE;
658
   else
659
      fatal("invalid severity level: %s", str);
×
660
}
661

662
static void parse_exit_severity(const char *str)
3✔
663
{
664
   const vhdl_severity_t s = parse_severity(optarg);
3✔
665
   set_exit_severity(s);
3✔
666
   set_status_severity(s);
3✔
667
}
3✔
668

669
static int parse_stop_delta(const char *str)
6✔
670
{
671
   const int ival = parse_int(str);
6✔
672
   if (ival < 1)
6✔
673
      fatal("$bold$--stop-delta$$ argument must be greater than zero");
3✔
674
   else if (ival > DELTA_CYCLE_MAX) {
3✔
675
      warnf("the maxmimum number of supported delta cycles is %d",
3✔
676
            DELTA_CYCLE_MAX);
677
      return DELTA_CYCLE_MAX;
3✔
678
   }
679
   else
680
      return ival;
681
}
682

683
static void ctrl_c_handler(void *arg)
×
684
{
685
   rt_model_t *model = arg;
×
686
   model_interrupt(model);
×
687
}
×
688

689
static jit_t *get_jit(unit_registry_t *ur, mir_context_t *mc)
4,813✔
690
{
691
   jit_t *jit = jit_new(ur, mc);
4,813✔
692

693
#ifdef HAVE_LLVM
694
   jit_preload(jit);
4,813✔
695
#endif
696

697
#if defined HAVE_LLVM && 1
698
   jit_register_llvm_plugin(jit);
4,813✔
699
#elif defined ARCH_X86_64 && 0
700
   jit_register_native_plugin(jit);
701
#endif
702

703
   _std_standard_init();
4,813✔
704
   _std_env_init();
4,813✔
705
   _std_reflection_init();
4,813✔
706
   _file_io_init();
4,813✔
707
   _nvc_sim_pkg_init();
4,813✔
708

709
   return jit;
4,813✔
710
}
711

712
static int plusarg_cmp(const void *lptr, const void *rptr)
6✔
713
{
714
   const char *lstr = *(const char **)lptr;
6✔
715
   const char *rstr = *(const char **)rptr;
6✔
716

717
   if (lstr[0] == '+' && rstr[0] != '+')
6✔
718
      return -1;
719
   else if (lstr[0] != '+' && rstr[0] == '+')
×
720
      return 1;
721
   else
722
      return lptr - rptr;
×
723
}
724

725
static cover_data_t *load_coverage(const unit_meta_t *meta, jit_t *j)
3,316✔
726
{
727
   if (meta->cover_file == NULL)
3,316✔
728
      return NULL;
729

730
   fbuf_t *f = fbuf_open(meta->cover_file, FBUF_IN, FBUF_CS_NONE);
120✔
731
   if (f == NULL)
120✔
732
      fatal_errno("failed to open coverage database: %s", meta->cover_file);
×
733

734
   cover_data_t *db = cover_read_items(f, 0);
120✔
735

736
   // Pre-allocate coverage counters
737
   const int n_tags = cover_count_items(db);
120✔
738
   jit_get_cover_mem(j, n_tags);
120✔
739

740
   fbuf_close(f, NULL);
120✔
741
   return db;
120✔
742
}
743

744
static void emit_coverage(const unit_meta_t *meta, jit_t *j, cover_data_t *db)
120✔
745
{
746
   assert(meta->cover_file != NULL);
120✔
747

748
   fbuf_t *f = fbuf_open(meta->cover_file, FBUF_OUT, FBUF_CS_NONE);
120✔
749
   if (f == NULL)
120✔
750
      fatal_errno("failed to open coverage database: %s", meta->cover_file);
×
751

752
   const int n_tags = cover_count_items(db);
120✔
753
   const int32_t *counts = jit_get_cover_mem(j, n_tags);
120✔
754
   cover_dump_items(db, f, COV_DUMP_RUNTIME, counts);
120✔
755

756
   fbuf_close(f, NULL);
120✔
757
}
120✔
758

759
static int run_cmd(int argc, char **argv, cmd_state_t *state)
3,325✔
760
{
761
   static struct option long_options[] = {
3,325✔
762
      { "trace",         no_argument,       0, 't' },
763
      { "profile",       no_argument,       0, 'p' },   // DEPRECATED 1.14
764
      { "stop-time",     required_argument, 0, 's' },
765
      { "stats",         no_argument,       0, 'S' },
766
      { "wave",          optional_argument, 0, 'w' },
767
      { "stop-delta",    required_argument, 0, 'd' },
768
      { "format",        required_argument, 0, 'f' },
769
      { "include",       required_argument, 0, 'i' },
770
      { "ieee-warnings", required_argument, 0, 'I' },   // DEPRECATED 1.16
771
      { "exclude",       required_argument, 0, 'e' },
772
      { "exit-severity", required_argument, 0, 'x' },
773
      { "dump-arrays",   optional_argument, 0, 'a' },
774
      { "load",          required_argument, 0, 'l' },
775
      { "vhpi-debug",    no_argument,       0, 'D' },
776
      { "vhpi-trace",    no_argument,       0, 'T' },
777
      { "gtkw",          optional_argument, 0, 'g' },
778
      { "shuffle",       no_argument,       0, 'H' },
779
      { 0, 0, 0, 0 }
780
   };
781

782
   wave_format_t wave_fmt = WAVE_FORMAT_FST;
3,325✔
783
   uint64_t      stop_time = TIME_HIGH;
3,325✔
784
   const char   *wave_fname = NULL;
3,325✔
785
   const char   *gtkw_fname = NULL;
3,325✔
786
   const char   *pli_plugins = NULL;
3,325✔
787

788
   static bool have_run = false;
3,325✔
789
   if (have_run)
3,325✔
790
      fatal("multiple run commands are not supported");
×
791

792
   have_run = true;
3,325✔
793

794
   const int next_cmd = scan_cmd(2, argc, argv);
3,325✔
795

796
   int c, index = 0;
3,325✔
797
   const char *spec = ":w::l:gi";
3,325✔
798
   while ((c = getopt_long(next_cmd, argv, spec, long_options, &index)) != -1) {
3,506✔
799
      switch (c) {
190✔
800
      case 0:
801
         // Set a flag
802
         break;
803
      case '?':
3✔
804
         bad_option("run", argv);
3✔
805
      case ':':
3✔
806
         missing_argument("run", argv);
3✔
807
      case 't':
×
808
         opt_set_int(OPT_RT_TRACE, 1);
×
809
         break;
×
810
      case 'p':
×
811
         warnf("the $bold$--profile$$ option is deprecated and has no effect");
×
812
         break;
×
813
      case 'T':
×
814
         opt_set_str(OPT_PLI_TRACE, "1");
×
815
         opt_set_int(OPT_PLI_DEBUG, 1);
×
816
         break;
×
817
      case 'D':
×
818
         opt_set_int(OPT_PLI_DEBUG, 1);
×
819
         break;
×
820
      case 's':
16✔
821
         stop_time = parse_time(optarg);
16✔
822
         break;
16✔
823
      case 'f':
×
824
         if (strcmp(optarg, "vcd") == 0)
×
825
            wave_fmt = WAVE_FORMAT_VCD;
826
         else if (strcmp(optarg, "fst") == 0)
×
827
            wave_fmt = WAVE_FORMAT_FST;
828
         else
829
            fatal("invalid waveform format: %s", optarg);
×
830
         break;
831
      case 'S':
3✔
832
         opt_set_int(OPT_RT_STATS, 1);
3✔
833
         break;
3✔
834
      case 'w':
99✔
835
         if (optarg == NULL)
99✔
836
            wave_fname = "";
837
         else
838
            wave_fname = optarg;
×
839
         break;
840
      case 'g':
9✔
841
         if (optarg == NULL)
9✔
842
            gtkw_fname = "";
843
         else
844
            gtkw_fname = optarg;
×
845
         break;
846
      case 'd':
6✔
847
         opt_set_int(OPT_STOP_DELTA, parse_stop_delta(optarg));
6✔
848
         break;
3✔
849
      case 'i':
3✔
850
         wave_include_glob(optarg);
3✔
851
         break;
3✔
852
      case 'e':
3✔
853
         wave_exclude_glob(optarg);
3✔
854
         break;
3✔
855
      case 'l':
×
856
         pli_plugins = optarg;
×
857
         break;
×
858
      case 'x':
3✔
859
         parse_exit_severity(optarg);
3✔
860
         break;
3✔
861
      case 'I':
×
862
         {
863
            const bool on = parse_on_off(optarg);
×
864

865
            // TODO: add an unconditional warning after 1.16
866
            if (state->jit != NULL && opt_get_int(OPT_IEEE_WARNINGS) != on)
×
867
               warnf("the $bold$--ieee-warnings$$ option may have no affect "
×
868
                     "as the IEEE packages have already been initialised, pass "
869
                     "$bold$--ieee-warnings$$ as a global option instead");
870

871
            opt_set_int(OPT_IEEE_WARNINGS, on);
×
872
         }
873
         break;
×
874
      case 'a':
39✔
875
         if (optarg == NULL)
39✔
876
            opt_set_int(OPT_DUMP_ARRAYS, INT_MAX);
36✔
877
         else
878
            opt_set_int(OPT_DUMP_ARRAYS, parse_int(optarg));
3✔
879
         break;
880
      case 'H':
3✔
881
         warnf("the $bold$--shuffle$$ option is intended for debug use only "
3✔
882
               "and may introduce significant performance overhead as well "
883
               "as non-deterministic behaviour");
884
         opt_set_int(OPT_SHUFFLE_PROCS, 1);
3✔
885
         break;
3✔
886
      default:
×
887
         should_not_reach_here();
888
      }
889
   }
890

891
   // Shuffle the arguments to put all the plusargs first
892
   qsort(argv + optind, next_cmd - optind, sizeof(char *), plusarg_cmp);
3,316✔
893

894
   int nplusargs = 0;
3,316✔
895
   char **plusargs = argv + optind;
3,316✔
896
   for (int i = optind; i < next_cmd; i++) {
6,494✔
897
      if (argv[i][0] == '+')
3,178✔
898
         nplusargs++, optind++;
3✔
899
   }
900

901
   set_top_level(argv, next_cmd, state);
3,316✔
902

903
   ident_t ename = ident_prefix(state->top_level, well_known(W_ELAB), '.');
3,316✔
904

905
   const unit_meta_t *meta = NULL;
3,316✔
906
   object_t *obj = lib_get_generic(state->work, ename, &meta);
3,316✔
907
   if (obj == NULL)
3,316✔
908
      fatal("%s not elaborated", istr(state->top_level));
×
909

910
   tree_t top = tree_from_object(obj);
3,316✔
911
   assert(top != NULL);
3,316✔
912

913
   wave_dumper_t *dumper = NULL;
3,316✔
914
   if (wave_fname != NULL) {
3,316✔
915
      const char *name_map[] = { "FST", "VCD" };
96✔
916
      const char *ext_map[]  = { "fst", "vcd" };
96✔
917
      char *tmp LOCAL = NULL, *tmp2 LOCAL = NULL;
192✔
918

919
      if (*wave_fname == '\0') {
96✔
920
         tmp = xasprintf("%s.%s", state->top_level_arg, ext_map[wave_fmt]);
96✔
921
         wave_fname = tmp;
96✔
922
         notef("writing %s waveform data to %s", name_map[wave_fmt], tmp);
96✔
923
      }
924

925
      if (gtkw_fname != NULL && *gtkw_fname == '\0') {
96✔
926
         tmp2 = xasprintf("%s.gtkw", state->top_level_arg);
9✔
927
         gtkw_fname = tmp2;
9✔
928
      }
929

930
      wave_include_file(argv[optind]);
96✔
931
      dumper = wave_dumper_new(wave_fname, gtkw_fname, top, wave_fmt);
96✔
932
   }
933
   else if (gtkw_fname != NULL)
3,220✔
934
      warnf("$bold$--gtkw$$ option has no effect without $bold$--wave$$");
×
935

936
   if (opt_get_size(OPT_HEAP_SIZE) < 0x100000)
3,316✔
937
      warnf("recommended heap size is at least 1M");
×
938

939
   if (state->mir == NULL)
3,316✔
940
      state->mir = mir_context_new();
1,458✔
941

942
   if (state->registry == NULL)
3,316✔
943
      state->registry = unit_registry_new();
360✔
944

945
   if (state->jit == NULL)
3,316✔
946
      state->jit = get_jit(state->registry, state->mir);
1,458✔
947

948
#ifdef ENABLE_LLVM
949
   jit_load_dll(state->jit, tree_ident(top));
3,316✔
950
#endif
951

952
   char *name LOCAL = xasprintf("_%s.pack", istr(state->top_level));
6,632✔
953
   FILE *f = lib_fopen(state->work, name, "rb");
3,316✔
954
   if (f != NULL) {
3,316✔
955
      jit_load_pack(state->jit, f);
235✔
956
      fclose(f);
235✔
957
   }
958

959
   if (state->cover == NULL)
3,316✔
960
      state->cover = load_coverage(meta, state->jit);
3,316✔
961

962
   if (state->model == NULL) {
3,316✔
963
      state->model = model_new(state->jit, state->cover);
1,458✔
964
      create_scope(state->model, top, NULL);
1,458✔
965
   }
966

967
   if (state->vhpi == NULL)
3,316✔
968
      state->vhpi = vhpi_context_new();
357✔
969

970
   if (state->vpi == NULL)
3,316✔
971
      state->vpi = vpi_context_new();
3,316✔
972

973
   if (pli_plugins != NULL || state->plugins != NULL) {
3,316✔
974
      vhpi_context_initialise(state->vhpi, top, state->model, state->jit,
75✔
975
                              nplusargs, plusargs);
976
      vpi_context_initialise(state->vpi, top, state->model, state->jit,
75✔
977
                             nplusargs, plusargs);
978
   }
979
   else if (nplusargs > 0)
3,241✔
980
      warnf("found plusargs on command line but no VHPI plugin was loaded");
×
981

982
   if (pli_plugins != NULL)
3,316✔
983
      vhpi_load_plugins(pli_plugins);
×
984

985
   set_ctrl_c_handler(ctrl_c_handler, state->model);
3,316✔
986

987
   model_reset(state->model);
3,316✔
988

989
   if (dumper != NULL)
3,316✔
990
      wave_dumper_restart(dumper, state->model, state->jit);
96✔
991

992
   model_run(state->model, stop_time);
3,316✔
993

994
   set_ctrl_c_handler(NULL, NULL);
3,316✔
995

996
   const int rc = model_exit_status(state->model);
3,316✔
997

998
   if (dumper != NULL)
3,316✔
999
      wave_dumper_free(dumper);
96✔
1000

1001
   if (state->cover != NULL)
3,316✔
1002
      emit_coverage(meta, state->jit, state->cover);
120✔
1003

1004
   vhpi_context_free(state->vhpi);
3,316✔
1005
   state->vhpi = NULL;
3,316✔
1006

1007
   vpi_context_free(state->vpi);
3,316✔
1008
   state->vpi = NULL;
3,316✔
1009

1010
   model_free(state->model);
3,316✔
1011
   state->model = NULL;
3,316✔
1012

1013
   argc -= next_cmd - 1;
3,316✔
1014
   argv += next_cmd - 1;
3,316✔
1015

1016
   return rc == 0 && argc > 1 ? process_command(argc, argv, state) : rc;
3,316✔
1017
}
1018

1019
static int print_deps_cmd(int argc, char **argv, cmd_state_t *state)
3✔
1020
{
1021
   static struct option long_options[] = {
3✔
1022
      { 0, 0, 0, 0 }
1023
   };
1024

1025
   opt_set_int(OPT_MAKE_DEPS_ONLY, 1);
3✔
1026

1027
   const int next_cmd = scan_cmd(2, argc, argv);
3✔
1028
   int c, index = 0;
3✔
1029
   const char *spec = ":";
3✔
1030
   while ((c = getopt_long(next_cmd, argv, spec, long_options, &index)) != -1) {
3✔
1031
      switch (c) {
×
1032
      case 0: break;  // Set a flag
1033
      case '?': bad_option("make", argv);
×
1034
      case ':': missing_argument("make", argv);
×
1035
      default: abort();
×
1036
      }
1037
   }
1038

1039
   const int count = next_cmd - optind;
3✔
1040
   tree_t *targets = NULL;
3✔
1041
   if (count > 0)
3✔
1042
      targets = xmalloc_array(count, sizeof(tree_t));
3✔
1043

1044
   for (int i = optind; i < next_cmd; i++) {
9✔
1045
      ident_t name = to_unit_name(argv[i]);
6✔
1046
      ident_t elab = ident_prefix(name, well_known(W_ELAB), '.');
6✔
1047
      if ((targets[i - optind] = lib_get(state->work, elab)) == NULL) {
6✔
1048
         if ((targets[i - optind] = lib_get(state->work, name)) == NULL)
6✔
1049
            fatal("cannot find unit %s in library %s",
×
1050
                  istr(name), istr(lib_name(state->work)));
1051
      }
1052
   }
1053

1054
   make(targets, count, stdout);
3✔
1055

1056
   argc -= next_cmd - 1;
3✔
1057
   argv += next_cmd - 1;
3✔
1058

1059
   return argc > 1 ? process_command(argc, argv, state) : EXIT_SUCCESS;
3✔
1060
}
1061

1062
static int make_cmd(int argc, char **argv, cmd_state_t *state)
×
1063
{
1064
   static struct option long_options[] = {
×
1065
      { "deps-only", no_argument, 0, 'd' },
1066
      { "posix",     no_argument, 0, 'p' },
1067
      { 0, 0, 0, 0 }
1068
   };
1069

1070
   warnf("the $bold$--make$$ command is deprecated and may be repurposed in a "
×
1071
         "future release");
1072
   notef("use $bold$--print-deps$$ to print Makefile dependencies instead");
×
1073

1074
   const int next_cmd = scan_cmd(2, argc, argv);
×
1075
   int c, index = 0;
×
1076
   const char *spec = ":";
×
1077
   while ((c = getopt_long(next_cmd, argv, spec, long_options, &index)) != -1) {
×
1078
      switch (c) {
×
1079
      case 0:
1080
         // Set a flag
1081
         break;
1082
      case 'd':
×
1083
         opt_set_int(OPT_MAKE_DEPS_ONLY, 1);
×
1084
         break;
×
1085
      case 'p':
1086
         // Does nothing
1087
         break;
1088
      case '?':
×
1089
         bad_option("make", argv);
×
1090
      case ':':
×
1091
         missing_argument("make", argv);
×
1092
      default:
×
1093
         abort();
×
1094
      }
1095
   }
1096

1097
   const int count = next_cmd - optind;
×
1098
   tree_t *targets = xmalloc_array(count, sizeof(tree_t));
×
1099

1100
   for (int i = optind; i < next_cmd; i++) {
×
1101
      ident_t name = to_unit_name(argv[i]);
×
1102
      ident_t elab = ident_prefix(name, well_known(W_ELAB), '.');
×
1103
      if ((targets[i - optind] = lib_get(state->work, elab)) == NULL) {
×
1104
         if ((targets[i - optind] = lib_get(state->work, name)) == NULL)
×
1105
            fatal("cannot find unit %s in library %s",
×
1106
                  istr(name), istr(lib_name(state->work)));
1107
      }
1108
   }
1109

1110
   make(targets, count, stdout);
×
1111

1112
   argc -= next_cmd - 1;
×
1113
   argv += next_cmd - 1;
×
1114

1115
   return argc > 1 ? process_command(argc, argv, state) : EXIT_SUCCESS;
×
1116
}
1117

1118
static void list_walk_fn(lib_t lib, ident_t ident, int kind, void *context)
12✔
1119
{
1120
   const char *pretty = "???";
12✔
1121
   switch (kind) {
12✔
1122
   case T_ELAB: pretty = "Elaborated"; break;
×
1123
   case T_ARCH: pretty = "Architecture"; break;
×
1124
   case T_ENTITY: pretty = "Entity"; break;
3✔
1125
   case T_PACKAGE: pretty = "Package"; break;
3✔
1126
   case T_PACK_BODY: pretty = "Package body"; break;
×
1127
   case T_PACK_INST: pretty = "Instantiated package"; break;
3✔
1128
   case T_CONFIGURATION: pretty = "Configuration"; break;
×
1129
   case T_CONTEXT: pretty = "Context"; break;
3✔
1130
   }
1131

1132
   printf("%-30s  : %s\n", istr(ident), pretty);
12✔
1133
}
12✔
1134

1135
static int list_cmd(int argc, char **argv, cmd_state_t *state)
3✔
1136
{
1137
   static struct option long_options[] = {
3✔
1138
      { 0, 0, 0, 0 }
1139
   };
1140

1141
   const int next_cmd = scan_cmd(2, argc, argv);
3✔
1142
   int c, index = 0;
3✔
1143
   const char *spec = ":";
3✔
1144
   while ((c = getopt_long(next_cmd, argv, spec, long_options, &index)) != -1) {
3✔
1145
      switch (c) {
×
1146
      case 0:
1147
         // Set a flag
1148
         break;
1149
      case '?':
×
1150
         bad_option("list", argv);
×
1151
      case ':':
×
1152
         missing_argument("list", argv);
×
1153
      default:
×
1154
         abort();
×
1155
      }
1156
   }
1157

1158
   lib_walk_index(state->work, list_walk_fn, NULL);
3✔
1159

1160
   argc -= next_cmd - 1;
3✔
1161
   argv += next_cmd - 1;
3✔
1162

1163
   return argc > 1 ? process_command(argc, argv, state) : EXIT_SUCCESS;
3✔
1164
}
1165

1166
static int init_cmd(int argc, char **argv, cmd_state_t *state)
9✔
1167
{
1168
   static struct option long_options[] = {
9✔
1169
      { 0, 0, 0, 0 }
1170
   };
1171

1172
   const int next_cmd = scan_cmd(2, argc, argv);
9✔
1173
   int c, index = 0;
9✔
1174
   const char *spec = ":";
9✔
1175
   while ((c = getopt_long(next_cmd, argv, spec, long_options, &index)) != -1) {
9✔
1176
      switch (c) {
×
1177
      case 0:
1178
         // Set a flag
1179
         break;
1180
      case '?':
×
1181
         bad_option("init", argv);
×
1182
      case ':':
×
1183
         missing_argument("init", argv);
×
1184
      }
1185
   }
1186

1187
   if (argc != optind)
9✔
1188
      fatal("$bold$--init$$ command takes no positional arguments");
3✔
1189

1190
   lib_save(state->work);
6✔
1191

1192
   argc -= next_cmd - 1;
6✔
1193
   argv += next_cmd - 1;
6✔
1194

1195
   return argc > 1 ? process_command(argc, argv, state) : EXIT_SUCCESS;
6✔
1196
}
1197

1198
static void list_packages(void)
×
1199
{
1200
   LOCAL_TEXT_BUF tb = tb_new();
×
1201
   get_libexec_dir(tb);
×
1202

1203
   DIR *dir = opendir(tb_get(tb));
×
1204
   tb_rewind(tb);
×
1205

1206
   if (dir != NULL) {
×
1207
      struct dirent *d;
1208
      while ((d = readdir(dir))) {
×
1209
         if (strncmp(d->d_name, "install-", 8))
×
1210
            continue;
×
1211

1212
         const char *dot = strrchr(d->d_name, '.');
×
1213
         if (dot == NULL)
×
1214
            continue;
×
1215

1216
         const int nchar = dot - d->d_name - 8;
×
1217
         tb_printf(tb, " %.*s", nchar, d->d_name + 8);
×
1218
      }
1219

1220
      closedir(dir);
×
1221
   }
1222

1223
   notef("the following packages can be installed:%s", tb_get(tb));
×
1224
}
×
1225

1226
static int install_cmd(int argc, char **argv, cmd_state_t *state)
×
1227
{
1228
   static struct option long_options[] = {
×
1229
      { "dest", required_argument, 0, 'd' },
1230
      { 0, 0, 0, 0 }
1231
   };
1232

1233
   const int next_cmd = scan_cmd(2, argc, argv);
×
1234
   int c, index = 0;
×
1235
   const char *spec = ":";
×
1236
   while ((c = getopt_long(next_cmd, argv, spec, long_options, &index)) != -1) {
×
1237
      switch (c) {
×
1238
      case 0:
1239
         // Set a flag
1240
         break;
1241
      case '?':
×
1242
         bad_option("install", argv);
×
1243
      case ':':
×
1244
         missing_argument("install", argv);
×
1245
      case 'd':
×
1246
         setenv("NVC_INSTALL_DEST", optarg , 1);
×
1247
         break;
×
1248
      }
1249
   }
1250

1251
   if (argc == optind) {
×
1252
      errorf("missing argument to $bold$--install$$ command");
×
1253
      list_packages();
×
1254
      return EXIT_FAILURE;
×
1255
   }
1256

1257
   LOCAL_TEXT_BUF tb = tb_new();
×
1258
   if (get_exe_path(tb))
×
1259
      setenv("NVC", tb_get(tb), 1);
×
1260

1261
   if (state->user_set_std)
×
1262
      setenv("NVC_STD", standard_text(standard()), 1);
×
1263

1264
   for (int i = optind; i < next_cmd; i++) {
×
1265
      tb_rewind(tb);
×
1266
      get_libexec_dir(tb);
×
1267
      tb_printf(tb, DIR_SEP "install-%s.sh", argv[i]);
×
1268

1269
      file_info_t info;
×
1270
      if (!get_file_info(tb_get(tb), &info) || info.type != FILE_REGULAR) {
×
1271
         errorf("%s is not an executable script", tb_get(tb));
×
1272
         list_packages();
×
1273
         return EXIT_FAILURE;
×
1274
      }
1275

1276
      const char *args[] = {
×
1277
#ifdef __MINGW32__
1278
         "bash",
1279
#endif
1280
         tb_get(tb),
×
1281
         NULL
1282
      };
1283
      run_program(args);
×
1284
   }
1285

1286
   argc -= next_cmd - 1;
×
1287
   argv += next_cmd - 1;
×
1288

1289
   return argc > 1 ? process_command(argc, argv, state) : EXIT_SUCCESS;
×
1290
}
1291

1292
static int syntax_cmd(int argc, char **argv, cmd_state_t *state)
×
1293
{
1294
   static struct option long_options[] = {
×
1295
      { 0, 0, 0, 0 }
1296
   };
1297

1298
   warnf("the $bold$--syntax$$ command is deprecated, use "
×
1299
         "$bold$-a --no-save$$ instead");
1300

1301
   const int next_cmd = scan_cmd(2, argc, argv);
×
1302
   int c, index = 0;
×
1303
   const char *spec = ":";
×
1304
   while ((c = getopt_long(next_cmd, argv, spec, long_options, &index)) != -1) {
×
1305
      switch (c) {
×
1306
      case 0:
1307
         // Set a flag
1308
         break;
1309
      case '?':
×
1310
         bad_option("syntax", argv);
×
1311
      case ':':
×
1312
         missing_argument("syntax", argv);
×
1313
      }
1314
   }
1315

1316
   for (int i = optind; i < next_cmd; i++) {
×
1317
      input_from_file(argv[i]);
×
1318
      while (parse())
×
1319
         ;
1320
   }
1321

1322
   if (error_count() > 0)
×
1323
      return EXIT_FAILURE;
1324

1325
   argc -= next_cmd - 1;
×
1326
   argv += next_cmd - 1;
×
1327

1328
   return argc > 1 ? process_command(argc, argv, state) : EXIT_SUCCESS;
×
1329
}
1330

1331
static void dump_one_unit(ident_t name, bool add_elab, bool add_body)
×
1332
{
1333
   if (add_elab)
×
1334
      name = ident_prefix(name, well_known(W_ELAB), '.');
×
1335
   else if (add_body)
×
1336
      name = ident_prefix(name, well_known(W_BODY), '-');
×
1337

1338
   tree_t top = lib_get(lib_work(), name);
×
1339
   if (top == NULL)
×
1340
      fatal("%s not analysed", istr(name));
×
1341

1342
   dump(top);
×
1343
}
×
1344

1345
static int dump_cmd(int argc, char **argv, cmd_state_t *state)
×
1346
{
1347
   static struct option long_options[] = {
×
1348
      { "elab", no_argument, 0, 'E' },
1349
      { "body", no_argument, 0, 'b' },
1350
      { 0, 0, 0, 0 }
1351
   };
1352

1353
   const int next_cmd = scan_cmd(2, argc, argv);
×
1354
   bool add_elab = false, add_body = false;
×
1355
   int c, index = 0;
×
1356
   const char *spec = ":Eb";
×
1357
   while ((c = getopt_long(next_cmd, argv, spec, long_options, &index)) != -1) {
×
1358
      switch (c) {
×
1359
      case 0:
1360
         // Set a flag
1361
         break;
1362
      case '?':
×
1363
         bad_option("dump", argv);
×
1364
      case ':':
×
1365
         missing_argument("dump", argv);
×
1366
      case 'E':
1367
         add_elab = true;
1368
         break;
1369
      case 'b':
×
1370
         add_body = true;
×
1371
         break;
×
1372
      default:
×
1373
         abort();
×
1374
      }
1375
   }
1376

1377
   if (optind == next_cmd) {
×
1378
      if (state->top_level == NULL)
×
1379
         fatal("missing top-level unit name");
×
1380
      else
1381
         dump_one_unit(state->top_level, add_elab, add_body);
×
1382
   }
1383
   else {
1384
      for (int i = optind; i < next_cmd; i++)
×
1385
         dump_one_unit(to_unit_name(argv[i]), add_elab, add_body);
×
1386
   }
1387

1388
   argc -= next_cmd - 1;
×
1389
   argv += next_cmd - 1;
×
1390

1391
   return argc > 1 ? process_command(argc, argv, state) : EXIT_SUCCESS;
×
1392
}
1393

1394
#if ENABLE_LLVM
1395
static int aotgen_cmd(int argc, char **argv, cmd_state_t *state)
3✔
1396
{
1397
   static struct option long_options[] = {
3✔
1398
      { 0, 0, 0, 0 }
1399
   };
1400

1401
   const char *outfile = "preload." DLL_EXT;
3✔
1402

1403
   const int next_cmd = scan_cmd(2, argc, argv);
3✔
1404
   int c, index = 0;
3✔
1405
   const char *spec = ":o:VO:";
3✔
1406
   while ((c = getopt_long(next_cmd, argv, spec, long_options, &index)) != -1) {
9✔
1407
      switch (c) {
6✔
1408
      case 0: break;  // Set a flag
1409
      case 'V':
×
1410
         opt_set_int(OPT_VERBOSE, 1);
×
1411
         break;
×
1412
      case 'O':
3✔
1413
         opt_set_int(OPT_OPTIMISE, parse_optimise_level(optarg));
3✔
1414
         break;
3✔
1415
      case 'o': outfile = optarg; break;
3✔
1416
      case '?': bad_option("aotgen", argv);
×
1417
      case ':': missing_argument("aotgen", argv);
×
1418
      default: abort();
×
1419
      }
1420
   }
1421

1422
   const int count = next_cmd - optind;
3✔
1423
   aotgen(outfile, argv + optind, count);
3✔
1424

1425
   argc -= next_cmd - 1;
3✔
1426
   argv += next_cmd - 1;
3✔
1427

1428
   return argc > 1 ? process_command(argc, argv, state) : EXIT_SUCCESS;
3✔
1429
}
1430
#else
1431
static int aotgen_cmd(int argc, char **argv, cmd_state_t *state)
1432
{
1433
   fatal("$bold$--aotgen$$ not supported without LLVM");
1434
}
1435
#endif
1436

1437
static int do_cmd(int argc, char **argv, cmd_state_t *state)
12✔
1438
{
1439
   static struct option long_options[] = {
12✔
1440
      { 0, 0, 0, 0 }
1441
   };
1442

1443
   const int next_cmd = scan_cmd(2, argc, argv);
12✔
1444
   int c, index = 0;
12✔
1445
   const char *spec = ":";
12✔
1446
   while ((c = getopt_long(next_cmd, argv, spec, long_options, &index)) != -1) {
12✔
1447
      switch (c) {
×
1448
      case 0: break;  // Set a flag
1449
      case '?': bad_option("do", argv);
×
1450
      case ':': missing_argument("do", argv);
×
1451
      default: abort();
×
1452
      }
1453
   }
1454

1455
   if (state->mir == NULL)
12✔
1456
      state->mir = mir_context_new();
12✔
1457

1458
   if (state->registry == NULL)
12✔
1459
      state->registry = unit_registry_new();
6✔
1460

1461
   if (state->jit == NULL)
12✔
1462
      state->jit = get_jit(state->registry, state->mir);
12✔
1463

1464
#ifdef ENABLE_TCL
1465
   tcl_shell_t *sh = shell_new(state->jit);
12✔
1466

1467
   if (optind < next_cmd && strpbrk(argv[optind], "./\\") == NULL) {
12✔
1468
      ident_t unit_name = to_unit_name(argv[optind]);
3✔
1469
      if (lib_get(state->work, unit_name) != NULL) {
3✔
1470
         state->top_level_arg = xstrdup(argv[optind]);
3✔
1471
         state->top_level = unit_name;
3✔
1472
         optind++;
3✔
1473
      }
1474
   }
1475

1476
   if (state->top_level != NULL) {
12✔
1477
      ident_t ename = ident_prefix(state->top_level, well_known(W_ELAB), '.');
9✔
1478
      tree_t top = lib_get(state->work, ename);
9✔
1479
      if (top == NULL)
9✔
1480
         fatal("%s not elaborated", istr(state->top_level));
×
1481

1482
#ifdef ENABLE_LLVM
1483
      jit_load_dll(state->jit, tree_ident(top));
9✔
1484
#endif
1485

1486
      char *name LOCAL = xasprintf("_%s.pack", istr(state->top_level));
18✔
1487
      FILE *f = lib_fopen(state->work, name, "rb");
9✔
1488
      if (f != NULL) {
9✔
1489
         jit_load_pack(state->jit, f);
×
1490
         fclose(f);
×
1491
      }
1492

1493
      shell_reset(sh, top);
9✔
1494
   }
1495

1496
   if (optind == next_cmd)
12✔
NEW
1497
      fatal("no script file specified");
×
1498

1499
   for (int i = optind; i < next_cmd; i++) {
21✔
1500
      if (!shell_do(sh, argv[i])) {
12✔
1501
         shell_free(sh);
3✔
1502
         return EXIT_FAILURE;
3✔
1503
      }
1504
   }
1505

1506
   shell_free(sh);
9✔
1507
#else
1508
   fatal("compiled without TCL support");
1509
#endif
1510

1511
   argc -= next_cmd - 1;
9✔
1512
   argv += next_cmd - 1;
9✔
1513

1514
   return argc > 1 ? process_command(argc, argv, state) : EXIT_SUCCESS;
9✔
1515
}
1516

1517
static int interact_cmd(int argc, char **argv, cmd_state_t *state)
3✔
1518
{
1519
   static struct option long_options[] = {
3✔
1520
      { 0, 0, 0, 0 }
1521
   };
1522

1523
   const int next_cmd = scan_cmd(2, argc, argv);
3✔
1524
   int c, index = 0;
3✔
1525
   const char *spec = ":";
3✔
1526
   while ((c = getopt_long(next_cmd, argv, spec, long_options, &index)) != -1) {
3✔
1527
      switch (c) {
×
1528
      case 0: break;  // Set a flag
1529
      case '?': bad_option("do", argv);
×
1530
      case ':': missing_argument("do", argv);
×
1531
      default: abort();
×
1532
      }
1533
   }
1534

1535
   if (state->mir == NULL)
3✔
1536
      state->mir = mir_context_new();
3✔
1537

1538
   if (state->registry == NULL)
3✔
1539
      state->registry = unit_registry_new();
3✔
1540

1541
   if (state->jit == NULL)
3✔
1542
      state->jit = get_jit(state->registry, state->mir);
3✔
1543

1544
#ifdef ENABLE_TCL
1545
   tcl_shell_t *sh = shell_new(state->jit);
3✔
1546

1547
   if (optind < next_cmd && strpbrk(argv[optind], "./\\") == NULL) {
3✔
1548
      ident_t unit_name = to_unit_name(argv[optind]);
3✔
1549
      if (lib_get(state->work, unit_name) != NULL) {
3✔
1550
         state->top_level_arg = xstrdup(argv[optind]);
3✔
1551
         state->top_level = unit_name;
3✔
1552
         optind++;
3✔
1553
      }
1554
   }
1555

1556
   if (optind != next_cmd)
3✔
1557
      fatal("unexpected argument \"%s\"", argv[optind]);
×
1558

1559
   if (state->top_level != NULL) {
3✔
1560
      ident_t ename = ident_prefix(state->top_level, well_known(W_ELAB), '.');
3✔
1561
      tree_t top = lib_get(state->work, ename);
3✔
1562
      if (top == NULL)
3✔
1563
         fatal("%s not elaborated", istr(state->top_level));
×
1564

1565
#ifdef ENABLE_LLVM
1566
      jit_load_dll(state->jit, tree_ident(top));
3✔
1567
#endif
1568

1569
      char *name LOCAL = xasprintf("_%s.pack", istr(state->top_level));
6✔
1570
      FILE *f = lib_fopen(state->work, name, "rb");
3✔
1571
      if (f != NULL) {
3✔
1572
         jit_load_pack(state->jit, f);
×
1573
         fclose(f);
×
1574
      }
1575

1576
      shell_reset(sh, top);
3✔
1577
   }
1578

1579
   shell_interact(sh);
3✔
1580

1581
   shell_free(sh);
3✔
1582
#else
1583
   fatal("compiled without TCL support");
1584
#endif
1585

1586
   argc -= next_cmd - 1;
3✔
1587
   argv += next_cmd - 1;
3✔
1588

1589
   return argc > 1 ? process_command(argc, argv, state) : EXIT_SUCCESS;
3✔
1590
}
1591

1592
static uint32_t parse_cover_print_spec(char *str)
9✔
1593
{
1594
   uint32_t mask = 0;
9✔
1595
   const char *delim = ",";
9✔
1596
   for (char *tok = strtok(str, delim); tok; tok = strtok(NULL, delim)) {
27✔
1597
      if (!strcmp(tok, "covered"))
18✔
1598
         mask |= COVER_MASK_DONT_PRINT_COVERED;
6✔
1599
      else if (!strcmp(tok, "uncovered"))
12✔
1600
         mask |= COVER_MASK_DONT_PRINT_UNCOVERED;
6✔
1601
      else if (!strcmp(tok, "excluded"))
6✔
1602
         mask |= COVER_MASK_DONT_PRINT_EXCLUDED;
6✔
1603
      else {
1604
         diag_t *d = diag_new(DIAG_FATAL, NULL);
×
1605
         diag_printf(d, "invalid option: '%s' for $bold$--dont-print$$", tok);
×
1606
         diag_hint(d, NULL, "valid options are: 'covered', 'uncovered', "
×
1607
                   "'excluded'");
1608
         diag_emit(d);
×
1609
         fatal_exit(EXIT_FAILURE);
×
1610
      }
1611
   }
1612
   return mask;
9✔
1613
}
1614

1615
static int coverage_cmd(int argc, char **argv, cmd_state_t *state)
9✔
1616
{
1617
   warnf("the $bold$-c$$ sub-command is deprecated, use $bold$--cover-report$$ "
9✔
1618
         "or $bold$--cover-merge$$ instead");
1619

1620
   static struct option long_options[] = {
9✔
1621
      { "report",       required_argument, 0, 'r' },
1622
      { "exclude-file", required_argument, 0, 'e' },
1623
      { "export",       required_argument, 0, 'E' },
1624
      { "merge",        required_argument, 0, 'm' },
1625
      { "dont-print",   required_argument, 0, 'd' },
1626
      { "item-limit",   required_argument, 0, 'l' },
1627
      { "verbose",      no_argument,       0, 'V' },
1628
      { 0, 0, 0, 0 }
1629
   };
1630

1631
   const char *out_db = NULL, *rpt_file = NULL, *exclude_file = NULL;
9✔
1632
   const char *export_file = NULL;
9✔
1633
   int c, index;
9✔
1634
   const char *spec = ":V";
9✔
1635
   cover_mask_t rpt_mask = 0;
9✔
1636
   int item_limit = 5000;
9✔
1637

1638
   while ((c = getopt_long(argc, argv, spec, long_options, &index)) != -1) {
24✔
1639
      switch (c) {
15✔
1640
      case 'r':
9✔
1641
         rpt_file = optarg;
9✔
1642
         break;
9✔
1643
      case 'm':
×
1644
         out_db = optarg;
×
1645
         break;
×
1646
      case 'e':
3✔
1647
         exclude_file = optarg;
3✔
1648
         break;
3✔
1649
      case 'E':
×
1650
         export_file = optarg;
×
1651
         break;
×
1652
      case 'd':
×
1653
         rpt_mask = parse_cover_print_spec(optarg);
×
1654
         break;
×
1655
      case 'l':
3✔
1656
         item_limit = parse_int(optarg);
3✔
1657
         break;
3✔
1658
      case 'V':
×
1659
         opt_set_int(OPT_VERBOSE, 1);
×
1660
         break;
×
1661
      case '?':
×
1662
         bad_option("coverage", argv);
×
1663
      case ':':
×
1664
         missing_argument("coverage", argv);
×
1665
      default:
×
1666
         abort();
×
1667
      }
1668
   }
1669

1670
   progress("initialising");
9✔
1671

1672
   if (optind == argc)
9✔
1673
      fatal("no input coverage database FILE specified");
×
1674

1675
   cover_data_t *cover = NULL;
1676

1677
   // Rest of inputs are coverage input files
1678
   for (int i = optind; i < argc; i++) {
21✔
1679
      fbuf_t *f = fbuf_open(argv[i], FBUF_IN, FBUF_CS_NONE);
12✔
1680

1681
      if (f != NULL) {
12✔
1682
         progress("Loading input coverage database: %s", argv[i]);
12✔
1683
         if (i == optind)
12✔
1684
            cover = cover_read_items(f, rpt_mask);
9✔
1685
         else
1686
            cover_merge_items(f, cover);
3✔
1687
      }
1688
      else
1689
         fatal("Could not open coverage database: %s", argv[i]);
×
1690

1691
      fbuf_close(f, NULL);
12✔
1692
   }
1693

1694
   if (out_db) {
9✔
1695
      progress("Saving merged coverage database to: %s", out_db);
×
1696
      fbuf_t *f = fbuf_open(out_db, FBUF_OUT, FBUF_CS_NONE);
×
1697
      cover_dump_items(cover, f, COV_DUMP_PROCESSING, NULL);
×
1698
      fbuf_close(f, NULL);
×
1699
   }
1700

1701
   if (exclude_file && cover) {
9✔
1702
      progress("Loading exclude file: %s", exclude_file);
3✔
1703
      cover_load_exclude_file(exclude_file, cover);
3✔
1704
   }
1705

1706
   if (rpt_file && cover) {
9✔
1707
      progress("Generating code coverage report.");
9✔
1708
      cover_report(rpt_file, cover, item_limit);
9✔
1709
   }
1710

1711
   if (export_file && cover) {
9✔
1712
      progress("Exporting XML coverage report");
×
1713

1714
      FILE *f = fopen(export_file, "w");
×
1715
      if (f == NULL)
×
1716
         fatal_errno("cannot open %s", export_file);
×
1717

1718
      cover_export_cobertura(cover, f, NULL);
×
1719
      fclose(f);
×
1720
   }
1721

1722
   return 0;
9✔
1723
}
1724

1725
#ifdef ENABLE_GUI
1726
static int gui_cmd(int argc, char **argv, cmd_state_t *state)
×
1727
{
1728
   static struct option long_options[] = {
×
1729
      { "init",     required_argument, 0, 'i' },
1730
      { "port",     required_argument, 0, 'p' },
1731
      { "protocol", required_argument, 0, 'o' },
1732
      { 0, 0, 0, 0 }
1733
   };
1734

1735
   const int next_cmd = scan_cmd(2, argc, argv);
×
1736
   server_kind_t kind = SERVER_HTTP;
×
1737
   int c, index = 0;
×
1738
   const char *spec = ":", *init_cmd = NULL;
×
1739
   while ((c = getopt_long(next_cmd, argv, spec, long_options, &index)) != -1) {
×
1740
      switch (c) {
×
1741
      case 0: break;  // Set a flag
1742
      case 'i': init_cmd = optarg; break;
×
1743
      case 'p':
×
1744
         {
1745
            const int port = parse_int(optarg);
×
1746
            if (port < 0 || port > UINT16_MAX)
×
1747
               fatal("invalid port number %d", port);
×
1748

1749
            opt_set_int(OPT_SERVER_PORT, port);
×
1750
         }
1751
         break;
×
1752
      case 'o':
×
1753
         {
1754
            if (strcmp(optarg, "http") == 0)
×
1755
               kind = SERVER_HTTP;
1756
            else if (strcmp(optarg, "cxxrtl") == 0)
×
1757
               kind = SERVER_CXXRTL;
1758
            else
1759
               fatal("invalid protocol '%s', valid choices are "
×
1760
                     "'http' and 'cxxrtl'", optarg);
1761
         }
1762
         break;
1763
      case '?': bad_option("gui", argv);
×
1764
      case ':': missing_argument("gui", argv);
×
1765
      default: abort();
×
1766
      }
1767
   }
1768

1769
   if (argc != optind)
×
1770
      fatal("$bold$--gui$$ command takes no positional arguments");
×
1771

1772
   tree_t top = NULL;
×
1773
   if (state->top_level != NULL) {
×
1774
      ident_t ename = ident_prefix(state->top_level, well_known(W_ELAB), '.');
×
1775
      if ((top = lib_get(state->work, ename)) == NULL)
×
1776
         fatal("%s not elaborated", istr(state->top_level));
×
1777
   }
1778

1779
   if (state->mir == NULL)
×
1780
      state->mir = mir_context_new();
×
1781

1782
   if (state->jit == NULL)
×
1783
      state->jit = get_jit(state->registry, state->mir);
×
1784

1785
   start_server(kind, state->jit, top, NULL, NULL, init_cmd);
×
1786

1787
   argc -= next_cmd - 1;
×
1788
   argv += next_cmd - 1;
×
1789

1790
   return argc > 1 ? process_command(argc, argv, state) : EXIT_SUCCESS;
×
1791
}
1792
#endif
1793

1794
static cover_data_t *merge_coverage_files(int argc, int next_cmd, char **argv,
162✔
1795
                                          cover_mask_t rpt_mask)
1796
{
1797
   // Merge all input coverage databases given on command line
1798

1799
   if (optind == next_cmd)
162✔
1800
      fatal("no input coverage database specified");
×
1801

1802
   cover_data_t *cover = NULL;
1803

1804
   for (int i = optind; i < next_cmd; i++) {
339✔
1805
      fbuf_t *f = fbuf_open(argv[i], FBUF_IN, FBUF_CS_NONE);
177✔
1806
      if (f == NULL) {
177✔
1807
         // Attempt to redirect the old file name to the new one
1808
         // TODO: this should be removed at some point
1809
         char *slash = strrchr(argv[i], *DIR_SEP) ?: strrchr(argv[i], '/');
12✔
1810
         if (slash != NULL && slash[1] == '_') {
12✔
1811
            char *tail = strstr(slash, ".covdb");
12✔
1812
            if (tail != NULL && tail[6] == '\0') {
12✔
1813
               ident_t unit_name = ident_new_n(slash + 2, tail - slash - 2);
12✔
1814
               lib_t lib = lib_find(ident_until(unit_name, '.'));
12✔
1815
               if (lib != NULL) {
12✔
1816
                  const unit_meta_t *meta;
12✔
1817
                  object_t *obj = lib_get_generic(lib, unit_name, &meta);
12✔
1818
                  if (obj != NULL && meta->cover_file != NULL) {
12✔
1819
                     warnf("redirecting %s to %s, please update your scripts",
12✔
1820
                           argv[i], meta->cover_file);
1821
                     f = fbuf_open(meta->cover_file, FBUF_IN, FBUF_CS_NONE);
12✔
1822
                  }
1823
               }
1824
            }
1825
         }
1826
      }
1827

1828
      if (f == NULL)
177✔
1829
         fatal_errno("could not open %s", argv[i]);
×
1830

1831
      progress("loading input coverage database %s", argv[i]);
177✔
1832

1833
      if (i == optind)
177✔
1834
         cover = cover_read_items(f, rpt_mask);
162✔
1835
      else
1836
         cover_merge_items(f, cover);
15✔
1837

1838
      fbuf_close(f, NULL);
177✔
1839
   }
1840

1841
   return cover;
162✔
1842
}
1843

1844
static int cover_export_cmd(int argc, char **argv, cmd_state_t *state)
39✔
1845
{
1846
   static struct option long_options[] = {
39✔
1847
      { "format",   required_argument, 0, 'f' },
1848
      { "output",   required_argument, 0, 'o' },
1849
      { "relative", optional_argument, 0, 'r' },
1850
      { 0, 0, 0, 0 }
1851
   };
1852

1853
   const int next_cmd = scan_cmd(2, argc, argv);
39✔
1854

1855
   enum { UNSET, COBERTURA, XML } format = UNSET;
39✔
1856
   const char *output = NULL, *relative = NULL;
39✔
1857
   int c, index;
39✔
1858
   const char *spec = ":o:";
39✔
1859
   while ((c = getopt_long(next_cmd, argv, spec, long_options, &index)) != -1) {
156✔
1860
      switch (c) {
117✔
1861
      case 'f':
39✔
1862
         if (strcasecmp(optarg, "cobertura") == 0)
39✔
1863
            format = COBERTURA;
1864
         else if (strcasecmp(optarg, "xml") == 0)
36✔
1865
            format = XML;
1866
         else
1867
            fatal("unknown format '%s', valid formats are: cobertura, xml",
×
1868
                  optarg);
1869
         break;
1870
      case 'o':
39✔
1871
         output = optarg;
39✔
1872
         break;
39✔
1873
      case 'r':
39✔
1874
         relative = optarg ?: ".";
39✔
1875
         break;
1876
      case '?':
×
1877
         bad_option("coverage export", argv);
×
1878
      case ':':
×
1879
         missing_argument("coverage export", argv);
×
1880
      default:
×
1881
         abort();
×
1882
      }
1883
   }
1884

1885
   if (format == UNSET) {
39✔
1886
      diag_t *d = diag_new(DIAG_FATAL, NULL);
×
1887
      diag_printf(d, "the $bold$--format$$ option is required");
×
1888
      diag_hint(d, NULL, "pass $bold$--format=cobertura$$ for Cobertura XML");
×
1889
      diag_emit(d);
×
1890
      return EXIT_FAILURE;
×
1891
   }
1892

1893
   // DEPRECATED 1.14
1894
   // Handle the old-style --cover-export which accepted a top-level unit name
1895
   bool looks_like_file = false;
39✔
1896
   if (argc != optind) {
39✔
1897
      file_info_t info;
39✔
1898
      if (get_file_info(argv[optind], &info))
39✔
1899
         looks_like_file = true;
1900
      else if (strstr(argv[optind], "/"))
×
1901
         looks_like_file = true;
×
1902
   }
1903

1904
   cover_data_t *cover;
39✔
1905
   if (looks_like_file)
39✔
1906
      cover = merge_coverage_files(argc, next_cmd, argv, 0);
39✔
1907
   else {
1908
      set_top_level(argv, next_cmd, state);
×
1909

1910
      char *fname LOCAL = xasprintf("_%s.elab.covdb", istr(state->top_level));
×
1911
      fbuf_t *f = lib_fbuf_open(state->work, fname, FBUF_IN, FBUF_CS_NONE);
×
1912

1913
      if (f == NULL)
×
1914
         fatal("no coverage database for %s", istr(state->top_level));
×
1915

1916
      cover = cover_read_items(f, 0);
×
1917
      fbuf_close(f, NULL);
×
1918

1919
      warnf("exporting the coverage database using the top-level unit name "
×
1920
            "is deprecated, pass the path to the coverage database instead");
1921
   }
1922

1923
   FILE *file = stdout;
39✔
1924
   if (output != NULL && (file = fopen(output, "w")) == NULL)
39✔
1925
      fatal_errno("cannot create %s", output);
×
1926

1927
   switch (format) {
39✔
1928
   case COBERTURA:
3✔
1929
      cover_export_cobertura(cover, file, relative);
3✔
1930
      break;
3✔
1931
   case XML:
36✔
1932
      cover_export_xml(cover, file, relative);
36✔
1933
      break;
36✔
1934
   case UNSET:
1935
      should_not_reach_here();
1936
   }
1937

1938
   if (file != stdout)
39✔
1939
      fclose(file);
39✔
1940

1941
   argc -= next_cmd - 1;
39✔
1942
   argv += next_cmd - 1;
39✔
1943

1944
   return argc > 1 ? process_command(argc, argv, state) : 0;
39✔
1945
}
1946

1947
static int cover_report_cmd(int argc, char **argv, cmd_state_t *state)
117✔
1948
{
1949
   static struct option long_options[] = {
117✔
1950
      { "report",       required_argument, 0, 'r' },   // DEPRECATED 1.14
1951
      { "output",       required_argument, 0, 'o' },
1952
      { "exclude-file", required_argument, 0, 'e' },
1953
      { "dont-print",   required_argument, 0, 'd' },
1954
      { "item-limit",   required_argument, 0, 'l' },
1955
      { "per-file",     no_argument,       0, 'f' },
1956
      { "verbose",      no_argument,       0, 'V' },
1957
      { 0, 0, 0, 0 }
1958
   };
1959

1960
   const int next_cmd = scan_cmd(2, argc, argv);
117✔
1961

1962
   const char *outdir = NULL, *exclude_file = NULL;
117✔
1963
   int c, index;
117✔
1964
   const char *spec = ":Vo:";
117✔
1965
   cover_mask_t rpt_mask = 0;
117✔
1966
   int item_limit = 5000;
117✔
1967

1968
   while ((c = getopt_long(next_cmd, argv, spec, long_options, &index)) != -1) {
279✔
1969
      switch (c) {
162✔
1970
      case 'r':
×
1971
         warnf("the $bold$--report$$ option is deprecated, use "
×
1972
               "$bold$--output$$ instead");
1973
         // Fall-through
1974
      case 'o':
117✔
1975
         outdir = optarg;
117✔
1976
         break;
117✔
1977
      case 'e':
30✔
1978
         exclude_file = optarg;
30✔
1979
         break;
30✔
1980
      case 'd':
9✔
1981
         rpt_mask |= parse_cover_print_spec(optarg);
9✔
1982
         break;
9✔
1983
      case 'l':
×
1984
         item_limit = parse_int(optarg);
×
1985
         break;
×
1986
      case 'f':
3✔
1987
         rpt_mask |= COVER_MASK_PER_FILE_REPORT;
3✔
1988
         break;
3✔
1989
      case 'V':
3✔
1990
         opt_set_int(OPT_VERBOSE, 1);
3✔
1991
         break;
3✔
1992
      case '?':
×
1993
         bad_option("coverage report", argv);
×
1994
      case ':':
×
1995
         missing_argument("coverage report", argv);
×
1996
      default:
×
1997
         should_not_reach_here();
1998
      }
1999
   }
2000

2001
   if (outdir == NULL)
117✔
2002
      fatal("the output directory must be specified with $bold$--output$$");
×
2003

2004
   progress("initialising");
117✔
2005

2006
   cover_data_t *cover = merge_coverage_files(argc, next_cmd, argv, rpt_mask);
117✔
2007

2008
   if (exclude_file && cover) {
117✔
2009
      progress("loading exclude file %s", exclude_file);
30✔
2010
      cover_load_exclude_file(exclude_file, cover);
30✔
2011
   }
2012

2013
   progress("generating code coverage report");
117✔
2014
   cover_report(outdir, cover, item_limit);
117✔
2015

2016
   argc -= next_cmd - 1;
117✔
2017
   argv += next_cmd - 1;
117✔
2018

2019
   return argc > 1 ? process_command(argc, argv, state) : 0;
117✔
2020
}
2021

2022
static int cover_merge_cmd(int argc, char **argv, cmd_state_t *state)
6✔
2023
{
2024
   static struct option long_options[] = {
6✔
2025
      { "output",       required_argument, 0, 'o' },
2026
      { "verbose",      no_argument,       0, 'V' },
2027
      { 0, 0, 0, 0 }
2028
   };
2029

2030
   const int next_cmd = scan_cmd(2, argc, argv);
6✔
2031

2032
   const char *out_db = NULL;
6✔
2033
   int c, index;
6✔
2034
   const char *spec = ":Vo:";
6✔
2035

2036
   while ((c = getopt_long(next_cmd, argv, spec, long_options, &index)) != -1) {
12✔
2037
      switch (c) {
6✔
2038
      case 'o':
6✔
2039
         out_db = optarg;
6✔
2040
         break;
6✔
2041
      case 'V':
×
2042
         opt_set_int(OPT_VERBOSE, 1);
×
2043
         break;
×
2044
      case '?':
×
2045
         bad_option("coverage merge", argv);
×
2046
      case ':':
×
2047
         missing_argument("coverage merge", argv);
×
2048
      default:
×
2049
         should_not_reach_here();
2050
      }
2051
   }
2052

2053
   if (out_db == NULL)
6✔
2054
      fatal("the output database must be specified with $bold$--output$$");
×
2055

2056
   progress("initialising");
6✔
2057

2058
   cover_data_t *cover = merge_coverage_files(argc, next_cmd, argv, 0);
6✔
2059

2060
   progress("saving merged coverage database to %s", out_db);
6✔
2061

2062
   fbuf_t *f = fbuf_open(out_db, FBUF_OUT, FBUF_CS_NONE);
6✔
2063
   cover_dump_items(cover, f, COV_DUMP_PROCESSING, NULL);
6✔
2064
   fbuf_close(f, NULL);
6✔
2065

2066
   argc -= next_cmd - 1;
6✔
2067
   argv += next_cmd - 1;
6✔
2068

2069
   return argc > 1 ? process_command(argc, argv, state) : 0;
6✔
2070
}
2071

2072
static int preprocess_cmd(int argc, char **argv, cmd_state_t *state)
3✔
2073
{
2074
   static struct option long_options[] = {
3✔
2075
      { "define",      required_argument, 0, 'D' },
2076
      { "single-unit", no_argument,       0, 'u' },
2077
      { 0, 0, 0, 0 }
2078
   };
2079

2080
   const int next_cmd = scan_cmd(2, argc, argv);
3✔
2081

2082
   int c, index;
3✔
2083
   const char *spec = ":D:";
3✔
2084
   while ((c = getopt_long(argc, argv, spec, long_options, &index)) != -1) {
12✔
2085
      switch (c) {
9✔
2086
      case 'u':
3✔
2087
         opt_set_int(OPT_SINGLE_UNIT, 1);
3✔
2088
         break;
3✔
2089
      case 'D':
6✔
2090
         parse_pp_define(optarg);
6✔
2091
         break;
6✔
2092
      case ':':
×
2093
         missing_argument("preprocess", argv);
×
2094
      case '?':
×
2095
      default:
2096
         bad_option("preprocess", argv);
×
2097
      }
2098
   }
2099

2100
   if (optind == next_cmd)
3✔
2101
      fatal("no input files");
×
2102

2103
   LOCAL_TEXT_BUF tb = tb_new();
6✔
2104
   for (int i = optind; i < next_cmd; i++) {
9✔
2105
      input_from_file(argv[i]);
6✔
2106

2107
      tb_rewind(tb);
6✔
2108
      vlog_preprocess(tb, false);
6✔
2109

2110
      fputs(tb_get(tb), stdout);
6✔
2111
   }
2112

2113
   argc -= next_cmd - 1;
3✔
2114
   argv += next_cmd - 1;
3✔
2115

2116
   return argc > 1 ? process_command(argc, argv, state) : 0;
3✔
2117
}
2118

2119
static void usage(void)
×
2120
{
2121
   color_printf("$!cyan$Usage:$$ $bold$%s [OPTION]... "
×
2122
                "COMMAND [OPTION]...$$\n\n",  PACKAGE);
2123

2124
   wrapped_printf("Global options are placed before COMMAND, and "
×
2125
                  "command-specific options are placed afterwards. "
2126
                  "Multiple commands can be given and will be performed "
2127
                  "in order.\n\n");
2128

2129
   static const struct {
×
2130
      const char *group;
2131
      struct {
2132
         const char *args;
2133
         const char *usage;
2134
      } options[16];
2135
   } groups[] = {
2136
      { "Commands",
2137
        {
2138
           { "-a [OPTION]... FILE...", "Analyse FILEs into work library" },
2139
           { "-e [OPTION]... TOP", "Elaborate design unit TOP" },
2140
           { "-r [OPTION]... TOP", "Execute previously elaborated TOP" },
2141
#ifdef ENABLE_TCL
2142
           { "-i [TOP]", "Launch interactive TCL shell" },
2143
#endif
2144
           { "--cover-export FILE...",
2145
             "Export coverage database to external format" },
2146
           { "--cover-report FILE...",
2147
             "Generate HTML report from coverage database" },
2148
           { "--cover-merge FILE...", "Merge multiple coverage databases" },
2149
#ifdef ENABLE_TCL
2150
           { "--do [TOP] SCRIPT...", "Evaluate TCL script" },
2151
#endif
2152
#ifdef ENABLE_GUI
2153
           { "--gui", "Launch browser-based GUI" },
2154
#endif
2155
           { "--init", "Initialise work library directory" },
2156
           { "--install PKG", "Install third-party packages" },
2157
           { "--list", "Print all units in the library" },
2158
           { "--preprocess FILE...",
2159
             "Expand FILEs with Verilog preprocessor" },
2160
           { "--print-deps [UNIT]...",
2161
             "Print dependencies in Makefile format" },
2162
        }
2163
      },
2164
      { "Global options",
2165
        {
2166
           { "-h, --help", "Display this message and exit" },
2167
           { "-H SIZE", "Set the maximum heap size to SIZE bytes" },
2168
           { "--ieee-warnings={on,off}",
2169
             "Enable or disable warnings from IEEE packages" },
2170
           { "--ignore-time", "Skip source file timestamp check" },
2171
           { "--load=PLUGIN", "Load VHPI plugin at startup" },
2172
           { "-L PATH", "Add PATH to library search paths" },
2173
           { "-M SIZE", "Limit design unit heap space to SIZE bytes" },
2174
           { "--map=LIB:PATH", "Map library LIB to PATH" },
2175
           { "--messages={full,compact}",
2176
             "Diagnostic message style, compact is less verbose" },
2177
           { "--std={1993,..,2019}", "VHDL standard revision to use" },
2178
           { "--stderr={note,warning,error,failure}",
2179
             "Print messages of this severity level or higher to stderr" },
2180
           { "-v, --version", "Display version and copyright information" },
2181
           { "--vhpi-debug", "Report VHPI errors as diagnostic messages" },
2182
           { "--vhpi-trace", "Trace VHPI calls and events" },
2183
           { "--work=NAME", "Use NAME as the work library" },
2184
        }
2185
      },
2186
      { "Analysis options",
2187
        {
2188
           { "--bootstrap", "Allow compilation of STANDARD package" },
2189
           { "--check-synthesis", "Warn on common synthesis mistakes" },
2190
           { "-D, --define NAME=VALUE",
2191
             "Set preprocessor symbol NAME to VALUE" },
2192
           { "--error-limit=NUM", "Stop after NUM errors" },
2193
           { "-f, --files=LIST", "Read files to analyse from LIST" },
2194
           { "--no-save", "Do not save analysed design units" },
2195
           { "--preserve-case",
2196
             "Preserve the original case of VHDL identifiers" },
2197
           { "--psl", "Enable parsing of PSL directives in comments" },
2198
           { "--relaxed", "Disable certain pedantic rule checks" },
2199
           { "--single-unit",
2200
             "Treat all Verilog files as a single compilation unit" },
2201
        }
2202
      },
2203
      { "Elaboration options",
2204
        {
2205
           { "--cover[={statement,branch,expression,toggle,...}]",
2206
             "Enable code coverage collection" },
2207
           { "--cover-file=FILE",
2208
             "Set the file name of the coverage database" },
2209
           { "--cover-spec=FILE",
2210
             "Fine-grained coverage collection specification, see the manual "
2211
             "for details" },
2212
           { "-g NAME=VALUE", "Set top level generic NAME to VALUE" },
2213
           { "-j, --jit", "Enable just-in-time compilation during simulation" },
2214
           { "-O0, -O1, -O2, -O3", "Set optimisation level (default is -O2)" },
2215
           { "--no-collapse", "Do not collapse multiple signals into one" },
2216
           { "--no-save", "Do not save the elaborated design to disk" },
2217
           { "-V, --verbose", "Print resource usage at each step" },
2218
        }
2219
      },
2220
      { "Run options",
2221
        {
2222
           { "--dump-arrays[=N]",
2223
             "Include nested arrays with up to N elements in waveform dump" },
2224
           { "--exclude=GLOB",
2225
             "Exclude signals matching GLOB from waveform dump" },
2226
           { "--exit-severity={note,warning,error,failure}",
2227
             "Exit after an assertion failure of this severity" },
2228
           { "--format={fst,vcd}", "Waveform dump format" },
2229
           { "--include=GLOB",
2230
             "Include signals matching GLOB in waveform dump" },
2231
           { "--shuffle", "Run processes in random order" },
2232
           { "--stats", "Print time and memory usage at end of run" },
2233
           { "--stop-delta=N", "Stop after N delta cycles (default 10000)" },
2234
           { "--stop-after=T", "Stop after simulation time T (e.g. 5ns)" },
2235
           { "--trace", "Trace simulation events" },
2236
           { "-w, --wave[=FILE]", "Write waveform dump to FILE" },
2237
        }
2238
      },
2239
#ifdef ENABLE_GUI
2240
      { "GUI options",
2241
        {
2242
           { "--init=CMDS", "Evaluate TCL commands on startup" },
2243
           { "--port=PORT", "Specify port for HTTP server" },
2244
        }
2245
      },
2246
#endif
2247
      { "Coverage report options",
2248
        {
2249
           { "--exclude-file=FILE",
2250
             "Apply exclude file when generating report, see manual for syntax"
2251
           },
2252
           { "--dont-print={covered,uncovered,excluded}",
2253
             "Exclude specified items from coverage report" },
2254
        }
2255
      },
2256
      { "Coverage merge options",
2257
        {
2258
           { "-o, --output=FILE", "Output database file name" },
2259
        }
2260
      },
2261
      { "Coverage export options",
2262
        {
2263
           { "--format=FMT", "File format (must be 'cobertura')" },
2264
           { "-o, --output=FILE", "Output file name" },
2265
           { "--relative=PATH", "Strip PATH from prefix of absolute paths" },
2266
        }
2267
      },
2268
      { "Install options",
2269
        {
2270
           { "--dest=DIR", "Compile libraries into directory DIR" }
2271
        },
2272
      }
2273
   };
2274

2275
   const int right = MAX(60, terminal_width());
×
2276

2277
   for (int i = 0; i < ARRAY_LEN(groups); i++) {
×
2278
      color_printf("$bold$$cyan$%s:$$\n", groups[i].group);
×
2279

2280
      for (int j = 0; j < ARRAY_LEN(groups[i].options); j++) {
×
2281
         const char *args  = groups[i].options[j].args;
×
2282
         const char *usage = groups[i].options[j].usage;
×
2283

2284
         if (args == NULL || usage == NULL)
×
2285
            break;
2286

2287
         int col = 0;
×
2288
         if (args[0] == '-' && args[1] == '-' && i > 0)
×
2289
            col += color_printf("     $bold$%s$$ ", args);
×
2290
         else
2291
            col += color_printf(" $bold$%s$$ ", args);
×
2292

2293
         const int indent = i == 0 ? 30 : 20;
×
2294
         if (col > indent)
×
2295
            printf("\n%*.s", indent, "");
×
2296
         else
2297
            col += printf("%*.s", indent - col, "");
×
2298

2299
         col = indent;
2300

2301
         const char *p = usage, *begin = usage;
2302
         for (; *p != '\0'; p++) {
×
2303
            if (col + 1 >= right && p - begin + 1 < right - indent) {
×
2304
               printf("\n%*s", indent, "");
×
2305
               col = indent + p - begin;
×
2306
            }
2307
            else if (isspace_iso88591(*p)) {
×
2308
               fwrite(begin, 1, p - begin + 1, stdout);
×
2309
               col++;
×
2310
               begin = p + 1;
×
2311
            }
2312
            else
2313
               ++col;
2314
         }
2315

2316
         if (begin < p)
×
2317
            fwrite(begin, 1, p - begin, stdout);
×
2318

2319
         printf("\n");
×
2320
      }
2321

2322
      printf("\n");
×
2323
   }
2324

2325
   LOCAL_TEXT_BUF tb = tb_new();
×
2326
   lib_print_search_paths(tb);
×
2327
   color_printf("$!cyan$Library search paths:$$%s\n\n", tb_get(tb));
×
2328

2329
   wrapped_printf("The full manual can be read with $bold$man 1 %s$$ and "
×
2330
                  "contains detailed explanations of the commands and options "
2331
                  "above as well as examples.\n", PACKAGE_NAME);
2332
   color_printf("\nReport bugs at $link:%s\07%s$\n", PACKAGE_BUGREPORT,
×
2333
                PACKAGE_BUGREPORT);
2334
}
×
2335

2336
static vhdl_standard_t parse_standard(const char *str)
3,708✔
2337
{
2338
   char *eptr = NULL;
3,708✔
2339
   const int year = strtol(str, &eptr, 10);
3,708✔
2340
   if ((eptr != NULL) && (*eptr == '\0')) {
3,708✔
2341
      switch (year) {
3,708✔
2342
      case 1987:
×
2343
      case 87:
2344
         fatal("VHDL standard 1076-1987 is not supported");
×
2345
      case 1993:
2346
      case 93:
2347
         return STD_93;
2348
      case 2000:
58✔
2349
      case 0:
2350
         return STD_00;
58✔
2351
      case 2002:
27✔
2352
      case 2:
2353
         return STD_02;
27✔
2354
      case 2008:
954✔
2355
      case 8:
2356
         return STD_08;
954✔
2357
      case 2019:
244✔
2358
      case 19:
2359
         return STD_19;
244✔
2360
      }
2361
   }
2362

2363
   fatal("invalid standard revision: %s (allowed 1993, 2000, 2002, "
×
2364
         "2008, 2019)", str);
2365
}
2366

2367
static message_style_t parse_message_style(const char *str)
×
2368
{
2369
   if (strcmp(optarg, "full") == 0)
×
2370
      return MESSAGE_FULL;
2371
   else if (strcmp(optarg, "compact") == 0)
×
2372
      return MESSAGE_COMPACT;
2373

2374
   fatal("invalid message style '%s' (allowed are 'full' and 'compact')", str);
×
2375
}
2376

2377
static size_t parse_size(const char *str)
6✔
2378
{
2379
   char *eptr;
6✔
2380
   const ssize_t size = strtoll(str, &eptr, 0);
6✔
2381

2382
   if (size <= 0)
6✔
2383
      fatal("invalid size '%s' (must be positive)", str);
×
2384
   else if (*eptr == '\0')
6✔
2385
      return size;
×
2386
   else if (strcasecmp(eptr, "k") == 0)
6✔
2387
      return size * 1024;
×
2388
   else if (strcasecmp(eptr, "m") == 0)
6✔
2389
      return size * 1024 * 1024;
6✔
2390
   else if (strcasecmp(eptr, "g") == 0)
×
2391
      return size * 1024 * 1024 * 1024;
×
2392

2393
   fatal("invalid size '%s' (expected a number with optional k, m, "
×
2394
         "or g suffix)", str);
2395
}
2396

2397
static void parse_library_map(char *str)
9✔
2398
{
2399
#ifdef __MINGW32__
2400
   char *split = strpbrk(str, ";:");
2401
#else
2402
   char *split = strchr(str, ':');
9✔
2403
#endif
2404

2405
   if (split == NULL)
9✔
2406
      fatal("invalid library map syntax '%s': use NAME:PATH", str);
×
2407

2408
   *split = '\0';
9✔
2409

2410
   if (strcasecmp(str, "work") == 0)
9✔
2411
      fatal("use --work option to specify work library name and path");
×
2412

2413
   lib_add_map(str, split + 1);
9✔
2414
}
9✔
2415

2416
static int process_command(int argc, char **argv, cmd_state_t *state)
10,407✔
2417
{
2418
   static struct option long_options[] = {
10,407✔
2419
      { "dump",         no_argument, 0, 'd' },
2420
      { "make",         no_argument, 0, 'm' },   // DEPRECATED 1.8
2421
      { "syntax",       no_argument, 0, 's' },   // DEPRECATED 1.15
2422
      { "list",         no_argument, 0, 'l' },
2423
      { "init",         no_argument, 0, 'n' },
2424
      { "install",      no_argument, 0, 'I' },
2425
      { "print-deps",   no_argument, 0, 'P' },
2426
      { "aotgen",       no_argument, 0, 'A' },
2427
      { "do",           no_argument, 0, 'D' },
2428
      { "cover-export", no_argument, 0, 'E' },
2429
      { "cover-merge",  no_argument, 0, 'M' },
2430
      { "cover-report", no_argument, 0, 'p' },
2431
      { "preprocess",   no_argument, 0, 'R' },
2432
#ifdef ENABLE_GUI
2433
      { "gui",          no_argument, 0, 'g' },
2434
#endif
2435
      { 0, 0, 0, 0 }
2436
   };
2437

2438
   opterr = 0;
10,407✔
2439
   optind = 1;
10,407✔
2440

2441
   int index = 0;
10,407✔
2442
   const char *spec = "aerci";
10,407✔
2443
   switch (getopt_long(MIN(argc, 2), argv, spec, long_options, &index)) {
10,407✔
2444
   case 'a':
3,526✔
2445
      return analyse(argc, argv, state);
3,526✔
2446
   case 'e':
3,349✔
2447
      return elaborate(argc, argv, state);
3,349✔
2448
   case 'r':
3,325✔
2449
      return run_cmd(argc, argv, state);
3,325✔
2450
   case 'c':    // DEPRECATED 1.14
9✔
2451
      return coverage_cmd(argc, argv, state);
9✔
2452
   case 'd':
×
2453
      return dump_cmd(argc, argv, state);
×
2454
   case 'm':
×
2455
      return make_cmd(argc, argv, state);
×
2456
   case 's':
×
2457
      return syntax_cmd(argc, argv, state);
×
2458
   case 'l':
3✔
2459
      return list_cmd(argc, argv, state);
3✔
2460
   case 'n':
9✔
2461
      return init_cmd(argc, argv, state);
9✔
2462
   case 'I':
×
2463
      return install_cmd(argc, argv, state);
×
2464
   case 'P':
3✔
2465
      return print_deps_cmd(argc, argv, state);
3✔
2466
   case 'A':
3✔
2467
      return aotgen_cmd(argc, argv, state);
3✔
2468
   case 'D':
12✔
2469
      return do_cmd(argc, argv, state);
12✔
2470
   case 'i':
3✔
2471
      return interact_cmd(argc, argv, state);
3✔
2472
   case 'E':
39✔
2473
      return cover_export_cmd(argc, argv, state);
39✔
2474
   case 'M':
6✔
2475
      return cover_merge_cmd(argc, argv, state);
6✔
2476
   case 'p':
117✔
2477
      return cover_report_cmd(argc, argv, state);
117✔
2478
   case 'R':
3✔
2479
      return preprocess_cmd(argc, argv, state);
3✔
2480
#ifdef ENABLE_GUI
2481
   case 'g':
×
2482
      return gui_cmd(argc, argv, state);
×
2483
#endif
2484
   default:
×
2485
      fatal("missing command, try $bold$%s --help$$ for usage", PACKAGE);
×
2486
      return EXIT_FAILURE;
2487
   }
2488
}
2489

2490
int main(int argc, char **argv)
4,140✔
2491
{
2492
   term_init();
4,140✔
2493
   thread_init();
4,140✔
2494
   set_default_options();
4,140✔
2495
   intern_strings();
4,140✔
2496
   register_signal_handlers();
4,140✔
2497
   mspace_stack_limit(MSPACE_CURRENT_FRAME);
4,140✔
2498
   check_cpu_features();
4,140✔
2499

2500
   srand((unsigned)time(NULL));
4,140✔
2501
   atexit(fbuf_cleanup);
4,140✔
2502

2503
   static struct option long_options[] = {
4,140✔
2504
      { "help",          no_argument,       0, 'h' },
2505
      { "version",       no_argument,       0, 'v' },
2506
      { "work",          required_argument, 0, 'w' },
2507
      { "std",           required_argument, 0, 's' },
2508
      { "messages",      required_argument, 0, 'I' },
2509
      { "native",        no_argument,       0, 'n' }, // DEPRECATED 1.4
2510
      { "map",           required_argument, 0, 'p' },
2511
      { "ieee-warnings", required_argument, 0, 'W' },
2512
      { "ignore-time",   no_argument,       0, 'i' },
2513
      { "force-init",    no_argument,       0, 'f' }, // DEPRECATED 1.7
2514
      { "stderr",        required_argument, 0, 'E' },
2515
      { "load",          required_argument, 0, 'l' },
2516
      { "vhpi-debug",    no_argument,       0, 'D' },
2517
      { "vhpi-trace",    no_argument,       0, 'T' },
2518
      { 0, 0, 0, 0 }
2519
   };
2520

2521
   opterr = 0;
4,140✔
2522

2523
   const char *work_name = "work";
4,140✔
2524
   cmd_state_t state = {};
4,140✔
2525

2526
   const int next_cmd = scan_cmd(1, argc, argv);
4,140✔
2527
   int c, index = 0;
4,140✔
2528
   const char *spec = ":hivL:M:P:G:H:";
4,140✔
2529
   while ((c = getopt_long(next_cmd, argv, spec, long_options, &index)) != -1) {
8,301✔
2530
      switch (c) {
4,164✔
2531
      case 0:
2532
         // Set a flag
2533
         break;
2534
      case 'h':
×
2535
         usage();
×
2536
         exit(EXIT_SUCCESS);
×
2537
      case 'v':
2538
         printf("%s\n%s\n", version_string, copy_string);
×
2539
         exit(EXIT_SUCCESS);
×
2540
      case 'w':
204✔
2541
         work_name = optarg;
204✔
2542
         break;
204✔
2543
      case 'L':
147✔
2544
         lib_add_search_path(optarg);
147✔
2545
         break;
147✔
2546
      case 's':
3,708✔
2547
         set_standard(parse_standard(optarg));
3,708✔
2548
         state.user_set_std = true;
3,708✔
2549
         break;
3,708✔
2550
      case 'I':
×
2551
         set_message_style(parse_message_style(optarg));
×
2552
         break;
×
2553
      case 'p':
9✔
2554
         parse_library_map(optarg);
9✔
2555
         break;
9✔
2556
      case 'i':
×
2557
         opt_set_int(OPT_IGNORE_TIME, 1);
×
2558
         break;
×
2559
      case 'f':
×
2560
         warnf("the --force-init option is deprecated and has no effect");
×
2561
         break;
×
2562
      case 'n':
×
2563
         warnf("the --native option is deprecated and has no effect");
×
2564
         break;
×
2565
      case 'M':
×
2566
         opt_set_size(OPT_ARENA_SIZE, parse_size(optarg));
×
2567
         break;
×
2568
      case 'P':
×
2569
      case 'G':
2570
         warnf("the -%c option is deprecated and has no effect (the new "
×
2571
               "-H option sets a unified heap size)", c);
2572
         break;
×
2573
      case 'H':
6✔
2574
         opt_set_size(OPT_HEAP_SIZE, parse_size(optarg));
6✔
2575
         break;
6✔
2576
      case 'E':
3✔
2577
         set_stderr_severity(parse_severity(optarg));
3✔
2578
         break;
3✔
2579
      case 'l':
78✔
2580
         state.plugins = optarg;
78✔
2581
         break;
78✔
2582
      case 'T':
×
2583
         opt_set_str(OPT_PLI_TRACE, "1");
×
2584
         opt_set_int(OPT_PLI_DEBUG, 1);
×
2585
         break;
×
2586
      case 'D':
×
2587
         opt_set_int(OPT_PLI_DEBUG, 1);
×
2588
         break;
×
2589
      case 'W':
6✔
2590
         opt_set_int(OPT_IEEE_WARNINGS, parse_on_off(optarg));
6✔
2591
         break;
6✔
2592
      case '?':
3✔
2593
         bad_option("global", argv);
3✔
2594
      case ':':
×
2595
         missing_argument("global", argv);
×
2596
      default:
×
2597
         should_not_reach_here();
2598
      }
2599
   }
2600

2601
   state.work = lib_new(work_name);
4,137✔
2602
   lib_set_work(state.work);
4,131✔
2603

2604
   argc -= next_cmd - 1;
4,131✔
2605
   argv += next_cmd - 1;
4,131✔
2606

2607
   if (state.plugins != NULL) {
4,131✔
2608
      state.vhpi = vhpi_context_new();
78✔
2609
      vhpi_load_plugins(state.plugins);
78✔
2610
   }
2611

2612
   const int ret = process_command(argc, argv, &state);
4,131✔
2613

2614
   if (state.model != NULL)
4,104✔
2615
      model_free(state.model);
235✔
2616

2617
   if (state.jit != NULL)
4,104✔
2618
      jit_free(state.jit);   // JIT must be shut down before exiting
3,566✔
2619

2620
   if (state.registry != NULL)
4,104✔
2621
      unit_registry_free(state.registry);
3,954✔
2622

2623
   if (state.mir != NULL)
4,104✔
2624
      mir_context_free(state.mir);
3,817✔
2625

2626
   return ret;
4,104✔
2627
}
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2025 Coveralls, Inc