1 //
2 // Copyright (C) 2011-2020 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 "lib.h"
20 #include "tree.h"
21 #include "common.h"
22 #include "vcode.h"
23
24 #include <assert.h>
25 #include <limits.h>
26 #include <stdlib.h>
27 #include <ctype.h>
28 #include <string.h>
29 #include <unistd.h>
30 #include <dirent.h>
31 #include <errno.h>
32 #include <time.h>
33 #include <fcntl.h>
34 #include <sys/stat.h>
35 #include <sys/types.h>
36 #include <sys/time.h>
37
38 typedef struct search_path search_path_t;
39 typedef struct lib_unit lib_unit_t;
40 typedef struct lib_index lib_index_t;
41 typedef struct lib_list lib_list_t;
42 typedef struct lib_map lib_map_t;
43
44 struct lib_unit {
45 tree_t top;
46 tree_kind_t kind;
47 tree_rd_ctx_t read_ctx;
48 bool dirty;
49 lib_mtime_t mtime;
50 };
51
52 struct lib_index {
53 ident_t name;
54 tree_kind_t kind;
55 lib_index_t *next;
56 };
57
58 struct lib {
59 char path[PATH_MAX];
60 ident_t name;
61 unsigned n_units;
62 unsigned units_alloc;
63 lib_unit_t *units;
64 lib_index_t *index;
65 lib_mtime_t index_mtime;
66 off_t index_size;
67 int lock_fd;
68 bool readonly;
69 };
70
71 struct lib_list {
72 lib_t item;
73 lib_list_t *next;
74 };
75
76 struct search_path {
77 search_path_t *next;
78 const char *path;
79 };
80
81 static lib_t work = NULL;
82 static lib_list_t *loaded = NULL;
83 static search_path_t *search_paths = NULL;
84
85 static const char *lib_file_path(lib_t lib, const char *name);
86 static lib_mtime_t lib_stat_mtime(struct stat *st);
87
standard_suffix(vhdl_standard_t std)88 static const char *standard_suffix(vhdl_standard_t std)
89 {
90 static const char *ext[] = {
91 "87", "93", "00", "02", "08"
92 };
93
94 assert(std < ARRAY_LEN(ext));
95 return ext[std];
96 }
97
upcase_name(const char * name)98 static ident_t upcase_name(const char * name)
99 {
100 char *name_copy LOCAL = xstrdup(name);
101
102 char *last_slash = strrchr(name_copy, '/');
103 while ((last_slash != NULL) && (*(last_slash + 1) == '\0')) {
104 *last_slash = '\0';
105 last_slash = strrchr(name_copy, '/');
106 }
107
108 char *name_up = (last_slash != NULL) ? last_slash + 1 : name_copy;
109 for (char *p = name_up; *p != '\0'; p++)
110 *p = toupper((int)*p);
111
112 char *last_dot = strrchr(name_up, '.');
113 if (last_dot != NULL)
114 *last_dot = '\0';
115
116 ident_t i = ident_new(name_up);
117 return i;
118 }
119
lib_add_to_index(lib_t lib,ident_t name,tree_kind_t kind)120 static void lib_add_to_index(lib_t lib, ident_t name, tree_kind_t kind)
121 {
122 // Keep the index in sorted order to make library builds reproducible
123 lib_index_t **it;
124 for (it = &(lib->index);
125 *it != NULL && ident_compare((*it)->name, name) < 0;
126 it = &((*it)->next))
127 ;
128
129 if (*it != NULL && (*it)->name == name) {
130 // Already in the index
131 (*it)->kind = kind;
132 }
133 else {
134 lib_index_t *new = xmalloc(sizeof(lib_index_t));
135 new->name = name;
136 new->kind = kind;
137 new->next = *it;
138
139 *it = new;
140 }
141 }
142
lib_read_index(lib_t lib)143 static void lib_read_index(lib_t lib)
144 {
145 fbuf_t *f = lib_fbuf_open(lib, "_index", FBUF_IN);
146 if (f != NULL) {
147 struct stat st;
148 if (stat(fbuf_file_name(f), &st) < 0)
149 fatal_errno("%s", fbuf_file_name(f));
150
151 lib->index_mtime = lib_stat_mtime(&st);
152 lib->index_size = st.st_size;
153
154 ident_rd_ctx_t ictx = ident_read_begin(f);
155 lib_index_t **insert = &(lib->index);
156
157 const int entries = read_u32(f);
158 for (int i = 0; i < entries; i++) {
159 ident_t name = ident_read(ictx);
160 tree_kind_t kind = read_u16(f);
161 assert(kind < T_LAST_TREE_KIND);
162
163 while (*insert && ident_compare((*insert)->name, name) < 0)
164 insert = &((*insert)->next);
165
166 if (*insert && (*insert)->name == name) {
167 (*insert)->kind = kind;
168 insert = &((*insert)->next);
169 }
170 else {
171 lib_index_t *new = xmalloc(sizeof(lib_index_t));
172 new->name = name;
173 new->kind = kind;
174 new->next = *insert;
175
176 *insert = new;
177 insert = &(new->next);
178 }
179 }
180
181 ident_read_end(ictx);
182 fbuf_close(f);
183 }
184 }
185
lib_init(const char * name,const char * rpath,int lock_fd)186 static lib_t lib_init(const char *name, const char *rpath, int lock_fd)
187 {
188 struct lib *l = xcalloc(sizeof(struct lib));
189 l->n_units = 0;
190 l->units = NULL;
191 l->name = upcase_name(name);
192 l->index = NULL;
193 l->lock_fd = lock_fd;
194 l->readonly = false;
195
196 if (rpath == NULL)
197 l->path[0] = '\0';
198 else if (realpath(rpath, l->path) == NULL)
199 checked_sprintf(l->path, PATH_MAX, "%s", rpath);
200
201 lib_list_t *el = xmalloc(sizeof(lib_list_t));
202 el->item = l;
203 el->next = loaded;
204 loaded = el;
205
206 if (l->lock_fd == -1 && rpath != NULL) {
207 const char *lock_path = lib_file_path(l, "_NVC_LIB");
208
209 // Try to open the lock file read-write as this is required for
210 // exlusive locking on some NFS implementations
211 int mode = O_RDWR;
212 if (access(lock_path, mode) != 0) {
213 if (errno == EACCES || errno == EPERM) {
214 mode = O_RDONLY;
215 l->readonly = true;
216 }
217 else
218 fatal_errno("access: %s", lock_path);
219 }
220
221 if ((l->lock_fd = open(lock_path, mode)) < 0)
222 fatal_errno("open: %s", lock_path);
223
224 file_read_lock(l->lock_fd);
225 }
226
227 lib_read_index(l);
228
229 if (l->lock_fd != -1)
230 file_unlock(l->lock_fd);
231
232 return l;
233 }
234
lib_find_in_index(lib_t lib,ident_t name)235 static lib_index_t *lib_find_in_index(lib_t lib, ident_t name)
236 {
237 lib_index_t *it;
238 for (it = lib->index;
239 (it != NULL) && (it->name != name);
240 it = it->next)
241 ;
242
243 return it;
244 }
245
lib_put_aux(lib_t lib,tree_t unit,tree_rd_ctx_t ctx,bool dirty,lib_mtime_t mtime)246 static lib_unit_t *lib_put_aux(lib_t lib, tree_t unit,
247 tree_rd_ctx_t ctx, bool dirty,
248 lib_mtime_t mtime)
249 {
250 assert(lib != NULL);
251 assert(unit != NULL);
252
253 lib_unit_t *where = NULL;
254 ident_t name = tree_ident(unit);
255
256 for (unsigned i = 0; (where == NULL) && (i < lib->n_units); i++) {
257 if (tree_ident(lib->units[i].top) == tree_ident(unit))
258 where = &(lib->units[i]);
259 }
260
261 if (where == NULL) {
262 if (lib->n_units == 0) {
263 lib->units_alloc = 16;
264 lib->units = xmalloc(sizeof(lib_unit_t) * lib->units_alloc);
265 }
266 else if (lib->n_units == lib->units_alloc) {
267 lib->units_alloc *= 2;
268 lib->units = xrealloc(lib->units,
269 sizeof(lib_unit_t) * lib->units_alloc);
270 }
271
272 where = &(lib->units[lib->n_units++]);
273 }
274
275 where->top = unit;
276 where->read_ctx = ctx;
277 where->dirty = dirty;
278 where->mtime = mtime;
279 where->kind = tree_kind(unit);
280
281 lib_add_to_index(lib, name, tree_kind(unit));
282
283 return where;
284 }
285
lib_find_at(const char * name,const char * path,bool exact)286 static lib_t lib_find_at(const char *name, const char *path, bool exact)
287 {
288 char dir[PATH_MAX];
289 char *p = dir + checked_sprintf(dir, sizeof(dir) - 4 - strlen(name),
290 "%s" PATH_SEP, path);
291 bool found = false;
292
293 if (!exact) {
294 // Append library name converting to lower case
295 for (const char *n = name; *n != '\0'; n++)
296 *p++ = tolower((int)*n);
297
298 // Try suffixing standard revision extensions first
299 for (vhdl_standard_t s = standard(); (s > STD_87) && !found; s--) {
300 checked_sprintf(p, 4, ".%s", standard_suffix(s));
301 found = (access(dir, F_OK) == 0);
302 }
303 }
304
305 if (!found) {
306 *p = '\0';
307 if (access(dir, F_OK) < 0)
308 return NULL;
309 }
310
311 char marker[PATH_MAX];
312 checked_sprintf(marker, sizeof(marker), "%s" PATH_SEP "_NVC_LIB", dir);
313 if (access(marker, F_OK) < 0)
314 return NULL;
315
316 return lib_init(name, dir, -1);
317 }
318
lib_file_path(lib_t lib,const char * name)319 static const char *lib_file_path(lib_t lib, const char *name)
320 {
321 static char buf[PATH_MAX];
322 checked_sprintf(buf, sizeof(buf), "%s" PATH_SEP "%s", lib->path, name);
323 return buf;
324 }
325
lib_loaded(ident_t name_i)326 lib_t lib_loaded(ident_t name_i)
327 {
328 if (name_i == work_i && work != NULL)
329 return work;
330
331 for (lib_list_t *it = loaded; it != NULL; it = it->next) {
332 if (lib_name(it->item) == name_i)
333 return it->item;
334 }
335
336 return NULL;
337 }
338
lib_new(const char * name,const char * path)339 lib_t lib_new(const char *name, const char *path)
340 {
341 ident_t name_i = upcase_name(name);
342
343 lib_t lib = lib_loaded(name_i);
344 if (lib != NULL)
345 return lib;
346
347 char *name_copy LOCAL = xstrdup(name);
348 char *sep = strrchr(name_copy, '/');
349
350 // Ignore trailing slashes
351 while ((sep != NULL) && (*(sep + 1) == '\0')) {
352 *sep = '\0';
353 sep = strrchr(name_copy, '/');
354 }
355
356 if (sep != NULL) {
357 // Work library contains explicit path
358 *sep = '\0';
359 name = sep + 1;
360 lib = lib_find_at(name, name_copy, false);
361 }
362 else {
363 // Look only in working directory
364 lib = lib_find_at(name, ".", false);
365 }
366
367 if (lib != NULL)
368 return lib;
369
370 const char *last_slash = strrchr(name, '/');
371 const char *last_dot = strrchr(name, '.');
372
373 if ((last_dot != NULL) && (last_dot > last_slash)) {
374 const char *ext = standard_suffix(standard());
375 if (strcmp(last_dot + 1, ext) != 0)
376 fatal("library directory suffix must be '%s' for this standard", ext);
377 }
378
379 for (const char *p = (last_slash ? last_slash + 1 : name);
380 (*p != '\0') && (p != last_dot);
381 p++) {
382 if (!isalnum((int)*p) && (*p != '_'))
383 fatal("invalid character '%c' in library name", *p);
384 }
385
386 char *lockf LOCAL = xasprintf("%s" PATH_SEP "%s", path, "_NVC_LIB");
387
388 struct stat buf;
389 if (stat(path, &buf) == 0) {
390 if (S_ISDIR(buf.st_mode)) {
391 struct stat sb;
392 if (stat(lockf, &sb) != 0 && !opt_get_int("force-init"))
393 fatal("directory %s already exists and is not an NVC library "
394 "(use --force-init to override this check)", path);
395 }
396 else
397 fatal("path %s already exists and is not a directory", path);
398 }
399
400 make_dir(path);
401
402 int fd = open(lockf, O_CREAT | O_EXCL | O_RDWR, 0777);
403 if (fd < 0) {
404 // If errno is EEXIST we raced with another process to create the
405 // lock file. Calling into lib_init with fd as -1 will cause it
406 // to be opened again without O_CREAT.
407 if (errno != EEXIST)
408 fatal_errno("lib_new: %s", lockf);
409 }
410 else {
411 file_write_lock(fd);
412
413 const char *marker = PACKAGE_STRING "\n";
414 if (write(fd, marker, strlen(marker)) < 0)
415 fatal_errno("write: %s", path);
416 }
417
418 return lib_init(name, path, fd);
419 }
420
lib_tmp(const char * name)421 lib_t lib_tmp(const char *name)
422 {
423 // For unit tests, avoids creating files
424 return lib_init(name, NULL, -1);
425 }
426
push_path(const char * path)427 static void push_path(const char *path)
428 {
429 for (search_path_t *it = search_paths; it != NULL; it = it->next) {
430 if (strcmp(it->path, path) == 0)
431 return;
432 }
433
434 search_path_t *s = xmalloc(sizeof(search_path_t));
435 s->next = search_paths;
436 s->path = path;
437
438 search_paths = s;
439 }
440
lib_default_search_paths(void)441 static void lib_default_search_paths(void)
442 {
443 if (search_paths == NULL) {
444 push_path(DATADIR);
445
446 const char *home_env = getenv("HOME");
447 if (home_env) {
448 char *path;
449 if (asprintf(&path, "%s/.%s/lib", home_env, PACKAGE) < 0)
450 fatal_errno("asprintf");
451 push_path(path);
452 }
453
454 char *env_copy = NULL;
455 const char *libpath_env = getenv("NVC_LIBPATH");
456 if (libpath_env) {
457 env_copy = strdup(libpath_env);
458
459 char *path_tok = strtok(env_copy, ":");
460 do {
461 push_path(path_tok);
462 } while ((path_tok = strtok(NULL, ":")));
463 }
464 }
465 }
466
lib_enum_search_paths(void ** token)467 const char *lib_enum_search_paths(void **token)
468 {
469 if (*token == NULL) {
470 lib_default_search_paths();
471 *token = search_paths;
472 }
473
474 if (*token == (void *)-1)
475 return NULL;
476 else {
477 search_path_t *old = *token;
478 if ((*token = old->next) == NULL)
479 *token = (void *)-1;
480 return old->path;
481 }
482 }
483
lib_add_search_path(const char * path)484 void lib_add_search_path(const char *path)
485 {
486 lib_default_search_paths();
487 push_path(strdup(path));
488 }
489
lib_add_map(const char * name,const char * path)490 void lib_add_map(const char *name, const char *path)
491 {
492 lib_t lib = lib_find_at(name, path, true);
493 if (lib == NULL)
494 warnf("library %s not found at %s", name, path);
495 }
496
lib_find(ident_t name_i,bool required)497 lib_t lib_find(ident_t name_i, bool required)
498 {
499 lib_t lib = lib_loaded(name_i);
500 if (lib != NULL)
501 return lib;
502
503 lib_default_search_paths();
504
505 const char *name_str = istr(name_i);
506 for (search_path_t *it = search_paths; it != NULL; it = it->next) {
507 if ((lib = lib_find_at(name_str, it->path, false)))
508 break;
509 }
510
511 if (lib == NULL) {
512 text_buf_t *tb = tb_new();
513 tb_printf(tb, "library %s not found in:\n", name_str);
514 for (search_path_t *it = search_paths; it != NULL; it = it->next)
515 tb_printf(tb, " %s\n", it->path);
516 if (required)
517 fatal("%s", tb_get(tb));
518 else
519 errorf("%s", tb_get(tb));
520 }
521
522 return lib;
523 }
524
lib_fopen(lib_t lib,const char * name,const char * mode)525 FILE *lib_fopen(lib_t lib, const char *name, const char *mode)
526 {
527 assert(lib != NULL);
528 return fopen(lib_file_path(lib, name), mode);
529 }
530
lib_fbuf_open(lib_t lib,const char * name,fbuf_mode_t mode)531 fbuf_t *lib_fbuf_open(lib_t lib, const char *name, fbuf_mode_t mode)
532 {
533 assert(lib != NULL);
534 if (lib->path[0] == '\0')
535 return NULL;
536 else
537 return fbuf_open(lib_file_path(lib, name), mode);
538 }
539
lib_free(lib_t lib)540 void lib_free(lib_t lib)
541 {
542 assert(lib != NULL);
543 assert(lib != work);
544
545 if (lib->lock_fd != -1)
546 close(lib->lock_fd);
547
548 for (lib_list_t *it = loaded, *prev = NULL;
549 it != NULL; loaded = it, it = it->next) {
550
551 if (it->item == lib) {
552 if (prev)
553 prev->next = it->next;
554 else
555 loaded = it->next;
556 free(it);
557 break;
558 }
559 }
560
561 while (lib->index) {
562 lib_index_t *tmp = lib->index->next;
563 free(lib->index);
564 lib->index = tmp;
565 }
566
567 if (lib->units != NULL)
568 free(lib->units);
569 free(lib);
570 }
571
lib_destroy(lib_t lib)572 void lib_destroy(lib_t lib)
573 {
574 // This is convenience function for testing: remove all
575 // files associated with a library
576
577 assert(lib != NULL);
578
579 close(lib->lock_fd);
580 lib->lock_fd = -1;
581
582 DIR *d = opendir(lib->path);
583 if (d == NULL) {
584 perror("opendir");
585 return;
586 }
587
588 char buf[PATH_MAX];
589 struct dirent *e;
590 while ((e = readdir(d))) {
591 if (e->d_name[0] != '.') {
592 checked_sprintf(buf, sizeof(buf), "%s" PATH_SEP "%s",
593 lib->path, e->d_name);
594 if (unlink(buf) < 0)
595 perror("unlink");
596 }
597 }
598
599 closedir(d);
600
601 if (rmdir(lib->path) < 0)
602 perror("rmdir");
603 }
604
lib_work(void)605 lib_t lib_work(void)
606 {
607 assert(work != NULL);
608 return work;
609 }
610
lib_set_work(lib_t lib)611 void lib_set_work(lib_t lib)
612 {
613 work = lib;
614 }
615
lib_path(lib_t lib)616 const char *lib_path(lib_t lib)
617 {
618 assert(lib != NULL);
619 return lib->path;
620 }
621
lib_time_to_usecs(time_t t)622 static lib_mtime_t lib_time_to_usecs(time_t t)
623 {
624 return (lib_mtime_t)t * 1000 * 1000;
625 }
626
lib_put(lib_t lib,tree_t unit)627 void lib_put(lib_t lib, tree_t unit)
628 {
629 struct timeval tv;
630 if (gettimeofday(&tv, NULL) != 0)
631 fatal_errno("gettimeofday");
632
633 lib_mtime_t usecs = ((lib_mtime_t)tv.tv_sec * 1000000) + tv.tv_usec;
634 lib_put_aux(lib, unit, NULL, true, usecs);
635 }
636
lib_stat_mtime(struct stat * st)637 static lib_mtime_t lib_stat_mtime(struct stat *st)
638 {
639 lib_mtime_t mt = lib_time_to_usecs(st->st_mtime);
640 #if defined HAVE_STRUCT_STAT_ST_MTIMESPEC_TV_NSEC
641 mt += st->st_mtimespec.tv_nsec / 1000;
642 #elif defined HAVE_STRUCT_STAT_ST_MTIM_TV_NSEC
643 mt += st->st_mtim.tv_nsec / 1000;
644 #endif
645 return mt;
646 }
647
lib_get_aux(lib_t lib,ident_t ident)648 static lib_unit_t *lib_get_aux(lib_t lib, ident_t ident)
649 {
650 assert(lib != NULL);
651
652 // Handle aliased library names
653 ident_t lname = ident_until(ident, '.');
654 if ((lname != NULL) && (lname != lib->name)) {
655 ident_t uname = ident_rfrom(ident, '.');
656 if (uname != NULL)
657 ident = ident_prefix(lib->name, uname, '.');
658 }
659
660 // Search in the list of already loaded units
661 for (unsigned n = 0; n < lib->n_units; n++) {
662 if (tree_ident(lib->units[n].top) == ident)
663 return &(lib->units[n]);
664 }
665
666 if (*(lib->path) == '\0') // Temporary library
667 return NULL;
668
669 assert(lib->lock_fd != -1); // Should not be called in unit tests
670 file_read_lock(lib->lock_fd);
671
672 // Otherwise search in the filesystem
673 DIR *d = opendir(lib->path);
674 if (d == NULL)
675 fatal("%s: %s", lib->path, strerror(errno));
676
677 lib_unit_t *unit = NULL;
678 const char *search = istr(ident);
679 struct dirent *e;
680 while ((e = readdir(d))) {
681 if (strcmp(e->d_name, search) == 0) {
682 fbuf_t *f = lib_fbuf_open(lib, e->d_name, FBUF_IN);
683 tree_rd_ctx_t ctx = tree_read_begin(f, lib_file_path(lib, e->d_name));
684 tree_t top = tree_read(ctx);
685 fbuf_close(f);
686
687 struct stat st;
688 if (stat(lib_file_path(lib, e->d_name), &st) < 0)
689 fatal_errno("%s", e->d_name);
690
691 lib_mtime_t mt = lib_stat_mtime(&st);
692
693 unit = lib_put_aux(lib, top, ctx, false, mt);
694 break;
695 }
696 }
697
698 closedir(d);
699 file_unlock(lib->lock_fd);
700
701 if (unit == NULL && lib_find_in_index(lib, ident) != NULL)
702 fatal("library %s corrupt: unit %s present in index but missing "
703 "on disk", istr(lib->name), istr(ident));
704
705 return unit;
706 }
707
lib_ensure_writable(lib_t lib)708 static void lib_ensure_writable(lib_t lib)
709 {
710 if (lib->readonly)
711 fatal("cannot write to read-only library %s", istr(lib->name));
712 }
713
lib_load_vcode(lib_t lib,ident_t unit_name)714 bool lib_load_vcode(lib_t lib, ident_t unit_name)
715 {
716 if (lib->lock_fd != -1)
717 file_read_lock(lib->lock_fd);
718
719 char *name LOCAL = vcode_file_name(unit_name);
720 fbuf_t *f = lib_fbuf_open(lib, name, FBUF_IN);
721 if (f != NULL) {
722 vcode_read(f);
723 fbuf_close(f);
724 }
725
726 if (lib->lock_fd != -1)
727 file_unlock(lib->lock_fd);
728 return f != NULL;
729 }
730
lib_save_vcode(lib_t lib,vcode_unit_t vu,ident_t unit_name)731 void lib_save_vcode(lib_t lib, vcode_unit_t vu, ident_t unit_name)
732 {
733 lib_ensure_writable(lib);
734
735 if (lib->lock_fd != -1)
736 file_write_lock(lib->lock_fd);
737
738 char *name LOCAL = vcode_file_name(unit_name);
739 fbuf_t *fbuf = lib_fbuf_open(lib, name, FBUF_OUT);
740 vcode_write(vu, fbuf);
741 fbuf_close(fbuf);
742
743 if (lib->lock_fd != -1)
744 file_unlock(lib->lock_fd);
745 }
746
lib_mtime(lib_t lib,ident_t ident)747 lib_mtime_t lib_mtime(lib_t lib, ident_t ident)
748 {
749 lib_unit_t *lu = lib_get_aux(lib, ident);
750 assert(lu != NULL);
751 return lu->mtime;
752 }
753
lib_stat(lib_t lib,const char * name,lib_mtime_t * mt)754 bool lib_stat(lib_t lib, const char *name, lib_mtime_t *mt)
755 {
756 struct stat buf;
757 if (stat(lib_file_path(lib, name), &buf) == 0) {
758 if (mt != NULL)
759 *mt = lib_stat_mtime(&buf);
760 return true;
761 }
762 else
763 return false;
764 }
765
lib_get(lib_t lib,ident_t ident)766 tree_t lib_get(lib_t lib, ident_t ident)
767 {
768 lib_unit_t *lu = lib_get_aux(lib, ident);
769 if (lu != NULL) {
770 if (lu->read_ctx != NULL) {
771 tree_read_end(lu->read_ctx);
772 lu->read_ctx = NULL;
773 }
774 return lu->top;
775 }
776 else
777 return NULL;
778 }
779
lib_get_check_stale(lib_t lib,ident_t ident)780 tree_t lib_get_check_stale(lib_t lib, ident_t ident)
781 {
782 lib_unit_t *lu = lib_get_aux(lib, ident);
783 if (lu != NULL) {
784 if (lu->read_ctx != NULL) {
785 tree_read_end(lu->read_ctx);
786 lu->read_ctx = NULL;
787 }
788
789 if (!opt_get_int("ignore-time")) {
790 const loc_t *loc = tree_loc(lu->top);
791
792 struct stat st;
793 if (stat(istr(loc->file), &st) == 0 && lu->mtime < lib_stat_mtime(&st))
794 fatal("design unit %s is older than its source file %s and must "
795 "be reanalysed\n(You can use the --ignore-time option to "
796 "skip this check)", istr(ident), istr(loc->file));
797 }
798
799 return lu->top;
800 }
801 else
802 return NULL;
803 }
804
lib_name(lib_t lib)805 ident_t lib_name(lib_t lib)
806 {
807 assert(lib != NULL);
808 return lib->name;
809 }
810
lib_save(lib_t lib)811 void lib_save(lib_t lib)
812 {
813 assert(lib != NULL);
814
815 assert(lib->lock_fd != -1); // Should not be called in unit tests
816 lib_ensure_writable(lib);
817 file_write_lock(lib->lock_fd);
818
819 for (unsigned n = 0; n < lib->n_units; n++) {
820 if (lib->units[n].dirty) {
821 const char *name = istr(tree_ident(lib->units[n].top));
822 fbuf_t *f = lib_fbuf_open(lib, name, FBUF_OUT);
823 if (f == NULL)
824 fatal("failed to create %s in library %s", name, istr(lib->name));
825 tree_wr_ctx_t ctx = tree_write_begin(f);
826 tree_write(lib->units[n].top, ctx);
827 tree_write_end(ctx);
828 fbuf_close(f);
829
830 lib->units[n].dirty = false;
831 }
832 }
833
834 const char *index_path = lib_file_path(lib, "_index");
835 struct stat st;
836 if (stat(index_path, &st) == 0
837 && (lib_stat_mtime(&st) != lib->index_mtime
838 || st.st_size != lib->index_size)) {
839 // Library was updated concurrently: re-read the index while we
840 // have the lock
841 lib_read_index(lib);
842 }
843
844 int index_sz = lib_index_size(lib);
845
846 fbuf_t *f = lib_fbuf_open(lib, "_index", FBUF_OUT);
847 if (f == NULL)
848 fatal("failed to create library %s index", istr(lib->name));
849
850 ident_wr_ctx_t ictx = ident_write_begin(f);
851
852 write_u32(index_sz, f);
853 for (lib_index_t *it = lib->index; it != NULL; it = it->next) {
854 ident_write(it->name, ictx);
855 write_u16(it->kind, f);
856 }
857
858 ident_write_end(ictx);
859 fbuf_close(f);
860
861 if (stat(index_path, &st) != 0)
862 fatal_errno("stat: %s", index_path);
863 lib->index_mtime = lib_stat_mtime(&st);
864 lib->index_size = st.st_size;
865
866 file_unlock(lib->lock_fd);
867 }
868
lib_index_kind(lib_t lib,ident_t ident)869 int lib_index_kind(lib_t lib, ident_t ident)
870 {
871 lib_index_t *it = lib_find_in_index(lib, ident);
872 return it != NULL ? it->kind : T_LAST_TREE_KIND;
873 }
874
lib_walk_index(lib_t lib,lib_index_fn_t fn,void * context)875 void lib_walk_index(lib_t lib, lib_index_fn_t fn, void *context)
876 {
877 assert(lib != NULL);
878
879 lib_index_t *it;
880 for (it = lib->index; it != NULL; it = it->next)
881 (*fn)(it->name, it->kind, context);
882 }
883
lib_index_size(lib_t lib)884 unsigned lib_index_size(lib_t lib)
885 {
886 assert(lib != NULL);
887
888 unsigned n = 0;
889 for (lib_index_t *it = lib->index; it != NULL; it = it->next)
890 n++;
891
892 return n;
893 }
894
lib_realpath(lib_t lib,const char * name,char * buf,size_t buflen)895 void lib_realpath(lib_t lib, const char *name, char *buf, size_t buflen)
896 {
897 assert(lib != NULL);
898
899 if (name)
900 checked_sprintf(buf, buflen, "%s" PATH_SEP "%s", lib->path, name);
901 else
902 strncpy(buf, lib->path, buflen);
903 }
904
lib_mkdir(lib_t lib,const char * name)905 void lib_mkdir(lib_t lib, const char *name)
906 {
907 make_dir(lib_file_path(lib, name));
908 }
909
lib_delete(lib_t lib,const char * name)910 void lib_delete(lib_t lib, const char *name)
911 {
912 assert(lib != NULL);
913 if (remove(lib_file_path(lib, name)) != 0 && errno != ENOENT)
914 fatal_errno("remove: %s", name);
915 }
916