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

nickg / nvc / 19798036146

30 Nov 2025 10:13AM UTC coverage: 92.564% (+0.009%) from 92.555%
19798036146

push

github

nickg
Avoid code generation for processes in cloned blocks

38 of 49 new or added lines in 4 files covered. (77.55%)

1016 existing lines in 13 files now uncovered.

75314 of 81364 relevant lines covered (92.56%)

413525.48 hits per line

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

58.69
/src/make.c
1
//
2
//  Copyright (C) 2013-2022  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 "diag.h"
21
#include "hash.h"
22
#include "ident.h"
23
#include "lib.h"
24
#include "option.h"
25
#include "phase.h"
26

27
#include <limits.h>
28
#include <unistd.h>
29
#include <stdlib.h>
30
#include <string.h>
31
#include <ctype.h>
32
#include <assert.h>
33
#include <sys/types.h>
34
#include <sys/stat.h>
35
#include <unistd.h>
36

37
typedef enum {
38
   MAKE_TREE,
39
   MAKE_LIB,
40
   MAKE_FINAL_SO
41
} make_product_t;
42

43
typedef struct ident_list ident_list_t;
44

45
struct ident_list {
46
   ident_list_t *next;
47
   ident_t       ident;
48
};
49

50
typedef struct rule rule_t;
51

52
typedef enum {
53
   RULE_ANALYSE,
54
   RULE_ELABORATE
55
} rule_kind_t;
56

57
struct rule {
58
   rule_t       *next;
59
   rule_kind_t   kind;
60
   ident_list_t *outputs;
61
   ident_list_t *inputs;
62
   ident_t       source;
63
};
64

65
static hash_t *rule_map = NULL;
66

67
static void make_rule(tree_t t, rule_t **rules);
68

69
static void ident_list_add(ident_list_t **list, ident_t i)
27✔
70
{
71
   for (; *list && ident_compare(i, (*list)->ident) < 0;
48✔
72
        list = &((*list)->next))
21✔
73
      ;
74

75
   if (*list && (*list)->ident == i)
27✔
76
      return;
77

78
   ident_list_t *c = xmalloc(sizeof(ident_list_t));
18✔
79
   c->ident = i;
18✔
80
   c->next  = *list;
18✔
81

82
   *list = c;
18✔
83
}
84

85
static void ident_list_free(ident_list_t *list)
6✔
86
{
87
   ident_list_t *it = list;
6✔
88
   while (it != NULL) {
24✔
89
      ident_list_t *next = it->next;
18✔
90
      free(it);
18✔
91
      it = next;
18✔
92
   }
93
}
6✔
94

95
static lib_t make_get_lib(ident_t name)
39✔
96
{
97
   return lib_require(ident_until(name, '.'));
39✔
98
}
99

100
static const char *make_product(tree_t t, make_product_t product)
21✔
101
{
102
   static char buf[PATH_MAX];
21✔
103

104
   ident_t name = tree_ident(t);
21✔
105
   lib_t lib = make_get_lib(name);
21✔
106

107
   const char *path = lib_path(lib);
21✔
108

109
   switch (product) {
21✔
110
   case MAKE_TREE:
21✔
111
      checked_sprintf(buf, PATH_MAX, "%s/%s", path, istr(name));
21✔
112
      break;
21✔
113

114
   case MAKE_FINAL_SO:
×
115
      {
116
         ident_t final = ident_runtil(name, '.');
×
117
         checked_sprintf(buf, PATH_MAX, "%s/_%s.elab.so", path, istr(final));
×
118
      }
119
      break;
×
120

121
   case MAKE_LIB:
×
122
      checked_sprintf(buf, PATH_MAX, "%s", path);
×
123
      break;
×
124
   }
125

126
   return buf;
21✔
127
}
128

129
static void make_rule_add_input(rule_t *r, const char *input)
21✔
130
{
131
   ident_list_add(&(r->inputs), ident_new(input));
21✔
132
}
21✔
133

134
static void make_rule_add_output(rule_t *r, const char *output)
6✔
135
{
136
   ident_list_add(&(r->outputs), ident_new(output));
6✔
137
}
6✔
138

139
static rule_t *make_rule_for_source(rule_t **all, rule_kind_t kind,
6✔
140
                                    const char *source)
141
{
142
   ident_t ident = ident_new(source);
6✔
143

144
   rule_t **ins;
6✔
145
   for (ins = all; *ins != NULL; ins = &((*ins)->next)) {
12✔
146
      int cmp = ident_compare((*ins)->source, ident);
3✔
147
      if (cmp == 0)
3✔
148
         return *ins;
3✔
149
      else if (cmp > 0)
×
150
         break;
151
   }
152

153
   rule_t *new = xmalloc(sizeof(rule_t));
3✔
154
   new->inputs  = NULL;
3✔
155
   new->outputs = NULL;
3✔
156
   new->kind    = kind;
3✔
157
   new->next    = *ins;
3✔
158
   new->source  = ident;
3✔
159

160
   *ins = new;
3✔
161
   return new;
3✔
162
}
163

164
static void make_free_rules(rule_t *list)
3✔
165
{
166
   while (list != NULL) {
6✔
167
      rule_t *tmp = list->next;
3✔
168
      ident_list_free(list->inputs);
3✔
169
      ident_list_free(list->outputs);
3✔
170
      free(list);
3✔
171
      list = tmp;
3✔
172
   }
173
}
3✔
174

175
static void make_instance_deps(tree_t t, void *context)
×
176
{
177
   rule_t *r = context;
×
178

179
   if (tree_class(t) == C_ENTITY) {
×
180
      ident_t name = tree_ident2(t);
×
181
      tree_t unit = lib_get(make_get_lib(name), name);
×
182
      if ((unit == NULL) || (tree_kind(unit) != T_ENTITY))
×
183
         warnf("cannot find entity %s", istr(name));
×
184
      else
185
         make_rule_add_input(r, make_product(unit, MAKE_TREE));
×
186
   }
187
}
×
188

189
static char *make_elab_name(tree_t t)
×
190
{
191
   const char *suffix = strchr(istr(tree_ident(t)), '.');
×
192
   assert(suffix != NULL);
×
193

194
   char *name = xstrdup(suffix + 1);
×
195
   for (char *p = name; *p != '\0'; p++) {
×
196
      if (*p == '.')
×
197
         *p = '\0';
×
198
      else
199
         *p = tolower((int)*p);
×
200
   }
201

202
   return name;
×
203
}
204

205
static void make_add_inputs_cb(tree_t unit, void *ctx)
15✔
206
{
207
   rule_t *r = ctx;
15✔
208
   make_rule_add_input(r, make_product(unit, MAKE_TREE));
15✔
209
}
15✔
210

211
static void make_dep_rules_cb(tree_t unit, void *ctx)
15✔
212
{
213
   rule_t **rules = ctx;
15✔
214
   make_rule(unit, rules);
15✔
215
}
15✔
216

217
static void make_rule(tree_t t, rule_t **rules)
21✔
218
{
219
   if (hash_get(rule_map, t))
21✔
220
      return;
221

222
   lib_t work = make_get_lib(tree_ident(t));
18✔
223
   if (work != lib_work())
18✔
224
      return;
225

226
   tree_kind_t kind = tree_kind(t);
6✔
227

228
   rule_t *r;
6✔
229
   if (kind == T_ELAB) {
6✔
UNCOV
230
      char *name = make_elab_name(t);
×
UNCOV
231
      r = make_rule_for_source(rules, RULE_ELABORATE, name);
×
UNCOV
232
      free(name);
×
233
   }
234
   else {
235
      const char *file = loc_file_str(tree_loc(t));
6✔
236
      r = make_rule_for_source(rules, RULE_ANALYSE, file);
6✔
237
      make_rule_add_input(r, file);
6✔
238

239
      if (kind == T_PACK_BODY)
6✔
UNCOV
240
         make_rule_add_input(r, make_product(tree_primary(t), MAKE_TREE));
×
241
   }
242

243
   hash_put(rule_map, t, r);
6✔
244

245
   switch (kind) {
6✔
246
   case T_ELAB:
×
UNCOV
247
      make_rule_add_output(r, make_product(t, MAKE_TREE));
×
UNCOV
248
      make_rule_add_output(r, make_product(t, MAKE_FINAL_SO));
×
UNCOV
249
      break;
×
250

251
   case T_PACKAGE:
6✔
252
   case T_PACK_BODY:
253
   case T_ENTITY:
254
   case T_ARCH:
255
   case T_CONFIGURATION:
256
   case T_PACK_INST:
257
   case T_CONTEXT:
258
      make_rule_add_output(r, make_product(t, MAKE_TREE));
6✔
259
      break;
6✔
260

UNCOV
261
   default:
×
UNCOV
262
      fatal("cannot get products for %s", tree_kind_str(tree_kind(t)));
×
263
   }
264

265
   tree_walk_deps(t, make_add_inputs_cb, r);
6✔
266

267
   if (kind == T_ARCH)
6✔
268
      tree_visit_only(t, make_instance_deps, r, T_INSTANCE);
3✔
269
   else if (kind == T_CONTEXT) {
3✔
270
      // TODO: T_USE should store a reference to the library unit
UNCOV
271
      const int nctx = tree_contexts(t);
×
UNCOV
272
      for (int i = 0; i < nctx; i++) {
×
UNCOV
273
         tree_t d = tree_context(t, i);
×
UNCOV
274
         if (tree_kind(d) == T_USE)
×
UNCOV
275
            make_add_inputs_cb(d, r);
×
276
      }
277

278
   }
279

280
   tree_walk_deps(t, make_dep_rules_cb, rules);
6✔
281
}
282

283
static void make_header(tree_t *targets, int count, FILE *out)
3✔
284
{
285
   fprintf(out, "# Generated by " PACKAGE_STRING "\n\n");
3✔
286

287
   if (!opt_get_int(OPT_MAKE_DEPS_ONLY)) {
3✔
UNCOV
288
      fprintf(out, "all:");
×
UNCOV
289
      for (int i = 0; i < count; i++)
×
UNCOV
290
         fprintf(out, " %s", make_product(targets[i], MAKE_TREE));
×
UNCOV
291
      fprintf(out, "\n\n");
×
292
   }
293
}
3✔
294

295
static void make_clean(tree_t dummy, FILE *out)
×
296
{
297
   fprintf(out, "clean:\n");
×
UNCOV
298
   fprintf(out, "\trm -r %s\n", make_product(dummy, MAKE_LIB));
×
UNCOV
299
}
×
300

301
static void make_print_inputs(rule_t *r, FILE *out)
6✔
302
{
303
   for (ident_list_t *it = r->inputs; it != NULL; it = it->next) {
30✔
304
      bool circular = false;
24✔
305
      for (ident_list_t *o = r->outputs; o != NULL; o = o->next) {
72✔
306
         if (it->ident == o->ident)
48✔
307
            circular = true;
6✔
308
      }
309

310
      if (!circular)
24✔
311
         fprintf(out, " %s", istr(it->ident));
24✔
312
   }
313
}
6✔
314

315
static void make_print_rules(rule_t *rules, FILE *out)
3✔
316
{
317
   const bool deps_only = opt_get_int(OPT_MAKE_DEPS_ONLY);
3✔
318

319
   if (deps_only) {
3✔
320
      for (rule_t *r = rules; r != NULL; r = r->next) {
6✔
321
         for (ident_list_t *it = r->outputs; it != NULL; it = it->next) {
9✔
322
            fprintf(out, "%s:", istr(it->ident));
6✔
323
            make_print_inputs(r, out);
6✔
324
            fprintf(out, "\n\n");
6✔
325
         }
326
      }
327
   }
328
   else {
UNCOV
329
      for (rule_t *r = rules; r != NULL; r = r->next) {
×
UNCOV
330
         for (ident_list_t *it = r->outputs; it != NULL; it = it->next)
×
UNCOV
331
            fprintf(out, "%s%s", istr(it->ident),
×
UNCOV
332
                    (it->next == NULL) ? "" : " ");
×
333

UNCOV
334
         fprintf(out, ":");
×
335
         make_print_inputs(r, out);
×
336

337
         fprintf(out, "\n\tnvc ");
×
338

UNCOV
339
         switch (r->kind) {
×
340
         case RULE_ANALYSE:
×
341
            fprintf(out, "-a %s\n\n", istr(r->source));
×
342
            break;
343

UNCOV
344
         case RULE_ELABORATE:
×
345
            fprintf(out, "-e %s\n\n", istr(r->source));
×
346
            break;
347
         }
348
      }
349
   }
350
}
3✔
351

UNCOV
352
static void make_run(tree_t *targets, int count, FILE *out)
×
353
{
UNCOV
354
   int selected = -1;
×
UNCOV
355
   for (int i = 0; i < count; i++) {
×
UNCOV
356
      if (tree_kind(targets[i]) == T_ELAB) {
×
UNCOV
357
         if (selected != -1) {
×
358
            warnf("multiple elaborarted units found: %s is selected as "
×
359
                  "run target",
360
                  istr(ident_runtil(tree_ident(targets[selected]), '.')));
×
361
            return;
×
362
         }
363
         else {
364
            char *name LOCAL = make_elab_name(targets[i]);
×
UNCOV
365
            fprintf(out, "\nrun: all\n");
×
366
            fprintf(out, "\tnvc -r %s\n", name);
×
367
            fprintf(out, "\nwave: all\n");
×
UNCOV
368
            fprintf(out, "\tnvc -r -w %s\n", name);
×
UNCOV
369
            fprintf(out, "\n.PHONY: all run wave clean\n");
×
370
            selected = i;
×
371
         }
372
      }
373
   }
374
}
375

376
static void make_add_target(lib_t lib, ident_t name, int kind, void *context)
×
377
{
UNCOV
378
   tree_t **outp = context;
×
UNCOV
379
   *(*outp)++ = lib_get(lib, name);
×
UNCOV
380
}
×
381

382
void make(tree_t *targets, int count, FILE *out)
3✔
383
{
384
   rule_map = hash_new(256);
3✔
385

386
   if (count == 0) {
3✔
UNCOV
387
      lib_t work = lib_work();
×
UNCOV
388
      count = lib_index_size(work);
×
UNCOV
389
      targets = xmalloc_array(count, sizeof(tree_t));
×
UNCOV
390
      tree_t *outp = targets;
×
UNCOV
391
      lib_walk_index(work, make_add_target, &outp);
×
392
   }
393

394
   make_header(targets, count, out);
3✔
395

396
   rule_t *rules = NULL;
3✔
397
   for (int i = 0; i < count; i++)
9✔
398
      make_rule(targets[i], &rules);
6✔
399

400
   make_print_rules(rules, out);
3✔
401
   make_free_rules(rules);
3✔
402

403
   if (!opt_get_int(OPT_MAKE_DEPS_ONLY) && count > 0) {
3✔
UNCOV
404
      make_clean(targets[0], out);
×
UNCOV
405
      make_run(targets, count, out);
×
406
   }
407

408
   if (!opt_get_int(OPT_MAKE_DEPS_ONLY))
3✔
UNCOV
409
      fprintf(out, "\n-include local.mk\n");
×
410
   else {
411
      file_info_t info;
3✔
412
      if (get_file_info("local.mk", &info))
3✔
413
         fprintf(out, "\ninclude local.mk\n");
3✔
414
   }
415

416
   free(targets);
3✔
417

418
   hash_free(rule_map);
3✔
419
   rule_map = NULL;
3✔
420
}
3✔
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

© 2026 Coveralls, Inc