1 /*
2 * This file and its contents are supplied under the terms of the
3 * Common Development and Distribution License ("CDDL"), version 1.0.
4 * You may only use this file in accordance with the terms of version
5 * 1.0 of the CDDL.
6 *
7 * A full copy of the text of the CDDL should have accompanied this
8 * source. A copy of the CDDL is also available via the Internet at
9 * http://www.illumos.org/license/CDDL.
10 */
11
12 /*
13 * Copyright 2015 Garrett D'Amore <garrett@damore.org>
14 * Copyright 2018 Joyent, Inc.
15 */
16
17 /*
18 * This program tests symbol visibility in different compilation environments.
19 */
20
21 #include <stdio.h>
22 #include <stdlib.h>
23 #include <string.h>
24 #include <errno.h>
25 #include <err.h>
26 #include <unistd.h>
27 #include <sys/types.h>
28 #include <sys/stat.h>
29 #include <note.h>
30 #include <libcustr.h>
31 #include <sys/wait.h>
32 #include "test_common.h"
33
34 char *dname;
35 char *cfile;
36 char *ofile;
37 char *lfile;
38 char *efile;
39
40 const char *sym = NULL;
41
42 static int good_count = 0;
43 static int fail_count = 0;
44 static int full_count = 0;
45 static int extra_debug = 0;
46 static char *compilation = "compilation.cfg";
47
48 #if defined(_LP64)
49 #define MFLAG "-m64"
50 #elif defined(_ILP32)
51 #define MFLAG "-m32"
52 #endif
53
54 const char *compilers[] = {
55 "cc",
56 "gcc",
57 "/opt/SUNWspro/bin/cc",
58 "/opt/gcc/4.4.4/bin/gcc",
59 "/opt/sunstudio12.1/bin/cc",
60 "/opt/sfw/bin/gcc",
61 "/usr/local/bin/gcc",
62 NULL
63 };
64
65 char *compiler = NULL;
66 const char *c89flags = NULL;
67 const char *c99flags = NULL;
68 const char *c11flags = NULL;
69
70 #define MAXENV 64 /* maximum number of environments (bitmask width) */
71 #define MAXHDR 10 /* maximum # headers to require to access symbol */
72 #define MAXARG 20 /* maximum # of arguments */
73
74 #define WS " \t"
75
76 static int next_env = 0;
77
78 struct compile_env {
79 char *ce_name;
80 char *ce_lang;
81 char *ce_defs;
82 int ce_index;
83 };
84
85 static struct compile_env compile_env[MAXENV];
86
87 struct env_group {
88 char *eg_name;
89 uint64_t eg_mask;
90 struct env_group *eg_next;
91 };
92
93 typedef enum {
94 SYM_TYPE,
95 SYM_VALUE,
96 SYM_DEFINE,
97 SYM_FUNC
98 } sym_type_t;
99
100 struct sym_test {
101 char *st_name;
102 sym_type_t st_type;
103 char *st_hdrs[MAXHDR];
104 char *st_rtype;
105 char *st_atypes[MAXARG];
106 char *st_defval;
107 uint64_t st_test_mask;
108 uint64_t st_need_mask;
109 const char *st_prog;
110 struct sym_test *st_next;
111 };
112
113 struct env_group *env_groups = NULL;
114
115 struct sym_test *sym_tests = NULL;
116 struct sym_test **sym_insert = &sym_tests;
117
118 static char *
mystrdup(const char * s)119 mystrdup(const char *s)
120 {
121 char *r;
122 if ((r = strdup(s)) == NULL) {
123 perror("strdup");
124 exit(1);
125 }
126 return (r);
127 }
128
129 static void *
myzalloc(size_t sz)130 myzalloc(size_t sz)
131 {
132 void *buf;
133 if ((buf = calloc(1, sz)) == NULL) {
134 perror("calloc");
135 exit(1);
136 }
137 return (buf);
138 }
139
140 static void
myasprintf(char ** buf,const char * fmt,...)141 myasprintf(char **buf, const char *fmt, ...)
142 {
143 int rv;
144 va_list va;
145 va_start(va, fmt);
146 rv = vasprintf(buf, fmt, va);
147 va_end(va);
148 if (rv < 0) {
149 perror("vasprintf");
150 exit(1);
151 }
152 }
153
154 static void
append_sym_test(struct sym_test * st)155 append_sym_test(struct sym_test *st)
156 {
157 *sym_insert = st;
158 sym_insert = &st->st_next;
159 }
160
161 static int
find_env_mask(const char * name,uint64_t * mask)162 find_env_mask(const char *name, uint64_t *mask)
163 {
164 for (int i = 0; i < MAXENV; i++) {
165 if (compile_env[i].ce_name != NULL &&
166 strcmp(compile_env[i].ce_name, name) == 0) {
167 *mask |= (1ULL << i);
168 return (0);
169 }
170 }
171
172 for (struct env_group *eg = env_groups; eg != NULL; eg = eg->eg_next) {
173 if (strcmp(name, eg->eg_name) == 0) {
174 *mask |= eg->eg_mask;
175 return (0);
176 }
177 }
178 return (-1);
179 }
180
181
182 static int
expand_env(char * list,uint64_t * mask,char ** erritem)183 expand_env(char *list, uint64_t *mask, char **erritem)
184 {
185 char *item;
186 for (item = strtok(list, WS); item != NULL; item = strtok(NULL, WS)) {
187 if (find_env_mask(item, mask) < 0) {
188 if (erritem != NULL) {
189 *erritem = item;
190 }
191 return (-1);
192 }
193 }
194 return (0);
195 }
196
197 static int
expand_env_list(char * list,uint64_t * test,uint64_t * need,char ** erritem)198 expand_env_list(char *list, uint64_t *test, uint64_t *need, char **erritem)
199 {
200 uint64_t mask = 0;
201 int act;
202 char *item;
203 for (item = strtok(list, WS); item != NULL; item = strtok(NULL, WS)) {
204 switch (item[0]) {
205 case '+':
206 act = 1;
207 item++;
208 break;
209 case '-':
210 act = 0;
211 item++;
212 break;
213 default:
214 act = 1;
215 break;
216 }
217
218 mask = 0;
219 if (find_env_mask(item, &mask) < 0) {
220 if (erritem != NULL) {
221 *erritem = item;
222 }
223 return (-1);
224 }
225 *test |= mask;
226 if (act) {
227 *need |= mask;
228 } else {
229 *need &= ~(mask);
230 }
231 }
232 return (0);
233 }
234
235 static int
do_env(char ** fields,int nfields,char ** err)236 do_env(char **fields, int nfields, char **err)
237 {
238 char *name;
239 char *lang;
240 char *defs;
241
242 if (nfields != 3) {
243 myasprintf(err, "number of fields (%d) != 3", nfields);
244 return (-1);
245 }
246
247 if (next_env >= MAXENV) {
248 myasprintf(err, "too many environments");
249 return (-1);
250 }
251
252 name = fields[0];
253 lang = fields[1];
254 defs = fields[2];
255
256 compile_env[next_env].ce_name = mystrdup(name);
257 compile_env[next_env].ce_lang = mystrdup(lang);
258 compile_env[next_env].ce_defs = mystrdup(defs);
259 compile_env[next_env].ce_index = next_env;
260 next_env++;
261 return (0);
262 }
263
264 static int
do_env_group(char ** fields,int nfields,char ** err)265 do_env_group(char **fields, int nfields, char **err)
266 {
267 char *name;
268 char *list;
269 struct env_group *eg;
270 uint64_t mask;
271 char *item;
272
273 if (nfields != 2) {
274 myasprintf(err, "number of fields (%d) != 2", nfields);
275 return (-1);
276 }
277
278 name = fields[0];
279 list = fields[1];
280 mask = 0;
281
282 if (expand_env(list, &mask, &item) < 0) {
283 myasprintf(err, "reference to undefined env %s", item);
284 return (-1);
285 }
286
287 eg = myzalloc(sizeof (*eg));
288 eg->eg_name = mystrdup(name);
289 eg->eg_mask = mask;
290 eg->eg_next = env_groups;
291 env_groups = eg;
292 return (0);
293 }
294
295 static custr_t *st_custr;
296
297 static void
addprogch(char c)298 addprogch(char c)
299 {
300 if (custr_appendc(st_custr, c) == -1) {
301 perror("custr_appendc");
302 exit(1);
303 }
304 }
305
306 static void
addprogstr(char * s)307 addprogstr(char *s)
308 {
309 if (custr_append(st_custr, s) == -1) {
310 perror("custr_append");
311 exit(1);
312 }
313 }
314
315 static void
addprogfmt(const char * fmt,...)316 addprogfmt(const char *fmt, ...)
317 {
318 va_list va;
319 va_start(va, fmt);
320 if (custr_append_vprintf(st_custr, fmt, va) == -1) {
321 perror("custr_append_vprintf");
322 exit(1);
323 }
324 va_end(va);
325 }
326
327 static void
mkprog(struct sym_test * st)328 mkprog(struct sym_test *st)
329 {
330 char *s = NULL;
331
332 custr_reset(st_custr);
333
334 for (int i = 0; i < MAXHDR && st->st_hdrs[i] != NULL; i++) {
335 addprogfmt("#include <%s>\n", st->st_hdrs[i]);
336 }
337
338 if (st->st_rtype != NULL) {
339 for (s = st->st_rtype; *s; s++) {
340 addprogch(*s);
341 if (*s == '(') {
342 s++;
343 addprogch(*s);
344 s++;
345 break;
346 }
347 }
348 addprogch(' ');
349 }
350
351 /* for function pointers, s is closing suffix, otherwise empty */
352
353 switch (st->st_type) {
354 case SYM_TYPE:
355 addprogstr("test_type;");
356 break;
357
358 case SYM_VALUE:
359 addprogfmt("test_value%s;\n", s); /* s usually empty */
360 addprogstr("void\ntest_func(void)\n{\n");
361 addprogfmt("\ttest_value = %s;\n}", st->st_name);
362 break;
363
364 case SYM_DEFINE:
365 addprogfmt("#if !defined(%s)", st->st_name);
366 if (st->st_defval != NULL)
367 addprogfmt("|| %s != %s", st->st_name, st->st_defval);
368 addprogfmt("\n#error %s is not defined or has the wrong value",
369 st->st_name);
370 addprogfmt("\n#endif\n");
371 break;
372
373 case SYM_FUNC:
374 addprogstr("\ntest_func(");
375 for (int i = 0; st->st_atypes[i] != NULL && i < MAXARG; i++) {
376 int didname = 0;
377 if (i > 0) {
378 addprogstr(", ");
379 }
380 if (strcmp(st->st_atypes[i], "void") == 0) {
381 didname = 1;
382 }
383 if (strcmp(st->st_atypes[i], "") == 0) {
384 didname = 1;
385 addprogstr("void");
386 }
387
388 /* print the argument list */
389 for (char *a = st->st_atypes[i]; *a; a++) {
390 if (*a == '(' && a[1] == '*' && !didname) {
391 addprogfmt("(*a%d", i);
392 didname = 1;
393 a++;
394 } else if (*a == '[' && !didname) {
395 addprogfmt("a%d[", i);
396 didname = 1;
397 } else {
398 addprogch(*a);
399 }
400 }
401 if (!didname) {
402 addprogfmt(" a%d", i);
403 }
404 }
405
406 if (st->st_atypes[0] == NULL) {
407 addprogstr("void");
408 }
409
410 /*
411 * Close argument list, and closing ")" for func ptrs.
412 * Note that for non-function pointers, s will be empty
413 * below, otherwise it points to the trailing argument
414 * list.
415 */
416 addprogfmt(")%s\n{\n\t", s);
417
418 if (strcmp(st->st_rtype, "") != 0 &&
419 strcmp(st->st_rtype, "void") != 0) {
420 addprogstr("return ");
421 }
422
423 /* add the function call */
424 addprogfmt("%s(", st->st_name);
425 for (int i = 0; st->st_atypes[i] != NULL && i < MAXARG; i++) {
426 if (strcmp(st->st_atypes[i], "") != 0 &&
427 strcmp(st->st_atypes[i], "void") != 0) {
428 addprogfmt("%sa%d", i > 0 ? ", " : "", i);
429 }
430 }
431
432 addprogstr(");\n}");
433 break;
434 }
435
436 addprogch('\n');
437
438 st->st_prog = custr_cstr(st_custr);
439 }
440
441 static int
add_envs(struct sym_test * st,char * envs,char ** err)442 add_envs(struct sym_test *st, char *envs, char **err)
443 {
444 char *item;
445 if (expand_env_list(envs, &st->st_test_mask, &st->st_need_mask,
446 &item) < 0) {
447 myasprintf(err, "bad env action %s", item);
448 return (-1);
449 }
450 return (0);
451 }
452
453 static int
add_headers(struct sym_test * st,char * hdrs,char ** err)454 add_headers(struct sym_test *st, char *hdrs, char **err)
455 {
456 int i = 0;
457
458 for (char *h = strsep(&hdrs, ";"); h != NULL; h = strsep(&hdrs, ";")) {
459 if (i >= MAXHDR) {
460 myasprintf(err, "too many headers");
461 return (-1);
462 }
463 test_trim(&h);
464 st->st_hdrs[i++] = mystrdup(h);
465 }
466
467 return (0);
468 }
469
470 static int
add_arg_types(struct sym_test * st,char * atype,char ** err)471 add_arg_types(struct sym_test *st, char *atype, char **err)
472 {
473 int i = 0;
474 char *a;
475 for (a = strsep(&atype, ";"); a != NULL; a = strsep(&atype, ";")) {
476 if (i >= MAXARG) {
477 myasprintf(err, "too many arguments");
478 return (-1);
479 }
480 test_trim(&a);
481 st->st_atypes[i++] = mystrdup(a);
482 }
483
484 return (0);
485 }
486
487 static int
do_type(char ** fields,int nfields,char ** err)488 do_type(char **fields, int nfields, char **err)
489 {
490 char *decl;
491 char *hdrs;
492 char *envs;
493 struct sym_test *st;
494
495 if (nfields != 3) {
496 myasprintf(err, "number of fields (%d) != 3", nfields);
497 return (-1);
498 }
499 decl = fields[0];
500 hdrs = fields[1];
501 envs = fields[2];
502
503 st = myzalloc(sizeof (*st));
504 st->st_type = SYM_TYPE;
505 st->st_name = mystrdup(decl);
506 st->st_rtype = mystrdup(decl);
507
508 if ((add_envs(st, envs, err) < 0) ||
509 (add_headers(st, hdrs, err) < 0)) {
510 return (-1);
511 }
512 append_sym_test(st);
513
514 return (0);
515 }
516
517 static int
do_value(char ** fields,int nfields,char ** err)518 do_value(char **fields, int nfields, char **err)
519 {
520 char *name;
521 char *type;
522 char *hdrs;
523 char *envs;
524 struct sym_test *st;
525
526 if (nfields != 4) {
527 myasprintf(err, "number of fields (%d) != 4", nfields);
528 return (-1);
529 }
530 name = fields[0];
531 type = fields[1];
532 hdrs = fields[2];
533 envs = fields[3];
534
535 st = myzalloc(sizeof (*st));
536 st->st_type = SYM_VALUE;
537 st->st_name = mystrdup(name);
538 st->st_rtype = mystrdup(type);
539
540 if ((add_envs(st, envs, err) < 0) ||
541 (add_headers(st, hdrs, err) < 0)) {
542 return (-1);
543 }
544 append_sym_test(st);
545
546 return (0);
547 }
548
549 static int
do_define(char ** fields,int nfields,char ** err)550 do_define(char **fields, int nfields, char **err)
551 {
552 char *name, *value, *hdrs, *envs;
553 struct sym_test *st;
554
555 if (nfields != 4) {
556 myasprintf(err, "number of fields (%d) != 4", nfields);
557 return (-1);
558 }
559
560 name = fields[0];
561 value = fields[1];
562 hdrs = fields[2];
563 envs = fields[3];
564
565 st = myzalloc(sizeof (*st));
566 st->st_type = SYM_DEFINE;
567 st->st_name = mystrdup(name);
568
569 /*
570 * A value to compare against is optional. trim will leave it as a null
571 * pointer if there's nothing there.
572 */
573 test_trim(&value);
574 if (*value != '\0')
575 st->st_defval = mystrdup(value);
576
577 if ((add_envs(st, envs, err) < 0) ||
578 (add_headers(st, hdrs, err) < 0)) {
579 return (-1);
580 }
581
582 append_sym_test(st);
583
584 return (0);
585 }
586
587 static int
do_func(char ** fields,int nfields,char ** err)588 do_func(char **fields, int nfields, char **err)
589 {
590 char *name;
591 char *rtype;
592 char *atype;
593 char *hdrs;
594 char *envs;
595 struct sym_test *st;
596
597 if (nfields != 5) {
598 myasprintf(err, "number of fields (%d) != 5", nfields);
599 return (-1);
600 }
601 name = fields[0];
602 rtype = fields[1];
603 atype = fields[2];
604 hdrs = fields[3];
605 envs = fields[4];
606
607 st = myzalloc(sizeof (*st));
608 st->st_type = SYM_FUNC;
609 st->st_name = mystrdup(name);
610 st->st_rtype = mystrdup(rtype);
611
612 if ((add_envs(st, envs, err) < 0) ||
613 (add_headers(st, hdrs, err) < 0) ||
614 (add_arg_types(st, atype, err) < 0)) {
615 return (-1);
616 }
617 append_sym_test(st);
618
619 return (0);
620 }
621
622 struct sym_test *
next_sym_test(struct sym_test * st)623 next_sym_test(struct sym_test *st)
624 {
625 return (st == NULL ? sym_tests : st->st_next);
626 }
627
628 const char *
sym_test_prog(struct sym_test * st)629 sym_test_prog(struct sym_test *st)
630 {
631 if (st->st_prog == NULL) {
632 mkprog(st);
633 }
634 return (st->st_prog);
635 }
636
637 const char *
sym_test_name(struct sym_test * st)638 sym_test_name(struct sym_test *st)
639 {
640 return (st->st_name);
641 }
642
643 /*
644 * Iterate through tests. Pass in NULL for cenv to begin the iteration. For
645 * subsequent iterations, use the return value from the previous iteration.
646 * Returns NULL when there are no more environments.
647 */
648 struct compile_env *
sym_test_env(struct sym_test * st,struct compile_env * cenv,int * need)649 sym_test_env(struct sym_test *st, struct compile_env *cenv, int *need)
650 {
651 int i = cenv ? cenv->ce_index + 1: 0;
652 uint64_t b = 1ULL << i;
653
654 while ((i < MAXENV) && (b != 0)) {
655 cenv = &compile_env[i];
656 if (b & st->st_test_mask) {
657 *need = (st->st_need_mask & b) ? 1 : 0;
658 return (cenv);
659 }
660 b <<= 1;
661 i++;
662 }
663 return (NULL);
664 }
665
666 const char *
env_name(struct compile_env * cenv)667 env_name(struct compile_env *cenv)
668 {
669 return (cenv->ce_name);
670 }
671
672 const char *
env_lang(struct compile_env * cenv)673 env_lang(struct compile_env *cenv)
674 {
675 return (cenv->ce_lang);
676 }
677
678 const char *
env_defs(struct compile_env * cenv)679 env_defs(struct compile_env *cenv)
680 {
681 return (cenv->ce_defs);
682 }
683
684 static void
show_file(test_t t,const char * path)685 show_file(test_t t, const char *path)
686 {
687 FILE *f;
688 char *buf = NULL;
689 size_t cap = 0;
690 int line = 1;
691
692 f = fopen(path, "r");
693 if (f == NULL) {
694 test_debugf(t, "fopen(%s): %s", path, strerror(errno));
695 return;
696 }
697
698 test_debugf(t, "----->> begin (%s) <<------", path);
699 while (getline(&buf, &cap, f) >= 0) {
700 (void) strtok(buf, "\r\n");
701 test_debugf(t, "%d: %s", line, buf);
702 line++;
703 }
704 test_debugf(t, "----->> end (%s) <<------", path);
705 (void) fclose(f);
706 }
707
708 static void
cleanup(void)709 cleanup(void)
710 {
711 if (ofile != NULL) {
712 (void) unlink(ofile);
713 free(ofile);
714 ofile = NULL;
715 }
716 if (lfile != NULL) {
717 (void) unlink(lfile);
718 free(lfile);
719 lfile = NULL;
720 }
721 if (cfile != NULL) {
722 (void) unlink(cfile);
723 free(cfile);
724 cfile = NULL;
725 }
726 if (efile != NULL) {
727 (void) unlink(efile);
728 free(efile);
729 efile = NULL;
730 }
731 if (dname) {
732 (void) rmdir(dname);
733 free(dname);
734 dname = NULL;
735 }
736 }
737
738 static int
mkworkdir(void)739 mkworkdir(void)
740 {
741 char b[32];
742 char *d;
743
744 cleanup();
745
746 (void) strlcpy(b, "/tmp/symbols_testXXXXXX", sizeof (b));
747 if ((d = mkdtemp(b)) == NULL) {
748 perror("mkdtemp");
749 return (-1);
750 }
751 dname = mystrdup(d);
752 myasprintf(&cfile, "%s/compile_test.c", d);
753 myasprintf(&ofile, "%s/compile_test.o", d);
754 myasprintf(&lfile, "%s/compile_test.log", d);
755 myasprintf(&efile, "%s/compile_test.exe", d);
756 return (0);
757 }
758
759 void
find_compiler(void)760 find_compiler(void)
761 {
762 test_t t;
763 int i;
764 FILE *cf;
765
766 t = test_start("finding compiler");
767
768 if ((cf = fopen(cfile, "w+")) == NULL) {
769 test_failed(t, "Unable to open %s for write: %s", cfile,
770 strerror(errno));
771 return;
772 }
773 (void) fprintf(cf, "#include <stdio.h>\n");
774 (void) fprintf(cf, "int main(int argc, char **argv) {\n");
775 (void) fprintf(cf, "#if defined(__SUNPRO_C)\n");
776 (void) fprintf(cf, "exit(51);\n");
777 (void) fprintf(cf, "#elif defined(__GNUC__)\n");
778 (void) fprintf(cf, "exit(52);\n");
779 (void) fprintf(cf, "#else\n");
780 (void) fprintf(cf, "exit(99)\n");
781 (void) fprintf(cf, "#endif\n}\n");
782 (void) fclose(cf);
783
784 for (i = 0; compilers[i] != NULL; i++) {
785 char cmd[256];
786 int rv;
787
788 (void) snprintf(cmd, sizeof (cmd),
789 "%s %s %s -o %s >/dev/null 2>&1",
790 compilers[i], MFLAG, cfile, efile);
791 test_debugf(t, "trying %s", cmd);
792 rv = system(cmd);
793
794 test_debugf(t, "result: %d", rv);
795
796 if ((rv < 0) || !WIFEXITED(rv) || WEXITSTATUS(rv) != 0)
797 continue;
798
799 rv = system(efile);
800 if (rv >= 0 && WIFEXITED(rv)) {
801 rv = WEXITSTATUS(rv);
802 } else {
803 rv = -1;
804 }
805
806 switch (rv) {
807 case 51: /* STUDIO */
808 test_debugf(t, "Found Studio C");
809 c89flags = "-Xc -errwarn=%all -v -xc99=%none " MFLAG;
810 c99flags = "-Xc -errwarn=%all -v -xc99=%all " MFLAG;
811 c11flags = NULL;
812 if (extra_debug) {
813 test_debugf(t, "c89flags: %s", c89flags);
814 test_debugf(t, "c99flags: %s", c99flags);
815 }
816 test_passed(t);
817 break;
818 case 52: /* GCC */
819 test_debugf(t, "Found GNU C");
820 c89flags = "-Wall -Werror -std=c89 -nostdinc "
821 "-isystem /usr/include " MFLAG;
822 c99flags = "-Wall -Werror -std=c99 -nostdinc "
823 "-isystem /usr/include " MFLAG;
824 c11flags = "-Wall -Werror -std=c11 -nostdinc "
825 "-isystem /usr/include " MFLAG;
826 if (extra_debug) {
827 test_debugf(t, "c89flags: %s", c89flags);
828 test_debugf(t, "c99flags: %s", c99flags);
829 }
830 test_passed(t);
831 break;
832 case 99:
833 test_debugf(t, "Found unknown (unsupported) compiler");
834 continue;
835 default:
836 continue;
837 }
838 myasprintf(&compiler, "%s", compilers[i]);
839 test_debugf(t, "compiler: %s", compiler);
840 return;
841 }
842 test_failed(t, "No compiler found.");
843 }
844
845 int
do_compile(test_t t,struct sym_test * st,struct compile_env * cenv,int need)846 do_compile(test_t t, struct sym_test *st, struct compile_env *cenv, int need)
847 {
848 char *cmd;
849 FILE *logf;
850 FILE *dotc;
851 const char *prog, *cflags, *lang;
852
853 full_count++;
854
855 if ((dotc = fopen(cfile, "w+")) == NULL) {
856 test_failed(t, "fopen(%s): %s", cfile, strerror(errno));
857 return (-1);
858 }
859 prog = sym_test_prog(st);
860 if (fwrite(prog, 1, strlen(prog), dotc) < strlen(prog)) {
861 test_failed(t, "fwrite: %s", strerror(errno));
862 (void) fclose(dotc);
863 return (-1);
864 }
865 if (fclose(dotc) < 0) {
866 test_failed(t, "fclose: %s", strerror(errno));
867 return (-1);
868 }
869
870 (void) unlink(ofile);
871
872 if (strcmp(env_lang(cenv), "c99") == 0) {
873 lang = "c99";
874 cflags = c99flags;
875 } else if (strcmp(env_lang(cenv), "c11") == 0) {
876 lang = "c11";
877 cflags = c11flags;
878 } else {
879 lang = "c89";
880 cflags = c89flags;
881 }
882
883 if (cflags == NULL) {
884 test_failed(t, "compiler %s does not support %s", compiler,
885 lang);
886 return (-1);
887 }
888
889 myasprintf(&cmd, "%s %s %s -c %s -o %s >>%s 2>&1",
890 compiler, cflags, env_defs(cenv), cfile, ofile, lfile);
891
892 if (extra_debug) {
893 test_debugf(t, "command: %s", cmd);
894 }
895
896 if ((logf = fopen(lfile, "w+")) == NULL) {
897 test_failed(t, "fopen: %s", strerror(errno));
898 return (-1);
899 }
900 (void) fprintf(logf, "===================\n");
901 (void) fprintf(logf, "PROGRAM:\n%s\n", sym_test_prog(st));
902 (void) fprintf(logf, "COMMAND: %s\n", cmd);
903 (void) fprintf(logf, "EXPECT: %s\n", need ? "OK" : "FAIL");
904 (void) fclose(logf);
905
906 switch (system(cmd)) {
907 case -1:
908 test_failed(t, "error compiling in %s: %s", env_name(cenv),
909 strerror(errno));
910 return (-1);
911 case 0:
912 if (!need) {
913 fail_count++;
914 show_file(t, lfile);
915 test_failed(t, "symbol visible in %s", env_name(cenv));
916 return (-1);
917 }
918 break;
919 default:
920 if (need) {
921 fail_count++;
922 show_file(t, lfile);
923 test_failed(t, "error compiling in %s", env_name(cenv));
924 return (-1);
925 }
926 break;
927 }
928 good_count++;
929 return (0);
930 }
931
932 void
test_compile(void)933 test_compile(void)
934 {
935 struct sym_test *st;
936 struct compile_env *cenv;
937 test_t t;
938 int need;
939
940 for (st = next_sym_test(NULL); st; st = next_sym_test(st)) {
941 if ((sym != NULL) && strcmp(sym, sym_test_name(st))) {
942 continue;
943 }
944 /* XXX: we really want a sym_test_desc() */
945 for (cenv = sym_test_env(st, NULL, &need);
946 cenv != NULL;
947 cenv = sym_test_env(st, cenv, &need)) {
948 t = test_start("%s : %c%s", sym_test_name(st),
949 need ? '+' : '-', env_name(cenv));
950 if (do_compile(t, st, cenv, need) == 0) {
951 test_passed(t);
952 }
953 }
954 }
955
956 if (full_count > 0) {
957 test_summary();
958 }
959 }
960
961 int
main(int argc,char ** argv)962 main(int argc, char **argv)
963 {
964 int optc;
965 int optC = 0;
966
967 while ((optc = getopt(argc, argv, "DdfCs:c:")) != EOF) {
968 switch (optc) {
969 case 'd':
970 test_set_debug();
971 break;
972 case 'f':
973 test_set_force();
974 break;
975 case 'D':
976 test_set_debug();
977 extra_debug++;
978 break;
979 case 'c':
980 compilation = optarg;
981 break;
982 case 'C':
983 optC++;
984 break;
985 case 's':
986 sym = optarg;
987 break;
988 default:
989 (void) fprintf(stderr, "Usage: %s [-df]\n", argv[0]);
990 exit(1);
991 }
992 }
993
994 if (test_load_config(NULL, compilation,
995 "env", do_env, "env_group", do_env_group, NULL) < 0) {
996 exit(1);
997 }
998
999 while (optind < argc) {
1000 if (test_load_config(NULL, argv[optind++],
1001 "type", do_type,
1002 "value", do_value,
1003 "define", do_define,
1004 "func", do_func,
1005 NULL) < 0) {
1006 exit(1);
1007 }
1008 }
1009
1010 if (atexit(cleanup) != 0) {
1011 perror("atexit");
1012 exit(1);
1013 }
1014
1015 if (custr_alloc(&st_custr) == -1) {
1016 perror("custr");
1017 exit(1);
1018 }
1019
1020 if (mkworkdir() < 0) {
1021 perror("mkdir");
1022 exit(1);
1023 }
1024
1025 find_compiler();
1026 if (!optC)
1027 test_compile();
1028
1029 exit(0);
1030 }
1031