scc

simple c99 compiler
git clone git://git.simple-cc.org/scc
Log | Files | Refs | Submodules | README | LICENSE

cc.c (13477B)


      1 /*
      2  * Scc is a pure C99 source code, except this file that is
      3  * intended for a POSIX.2008 environment. This situation  creates
      4  * a problem where some systems require the macro _ANSI_SOURCE to
      5  * limit the namespace to a pure ISO namespace. NetBSD had a  bug
      6  * and using POSIX_C_SOURCE  and  _ANSI_SOURCE  at  the same time
      7  * generates a syntax  error in the system headers. The bug was
      8  * already fixed in NetBSD but keeping the workaround here does
      9  * not hurt and it helps to keep back compatibility.
     10  */
     11 #undef _POSIX_C_SOURCE
     12 #undef _ANSI_SOURCE
     13 #define _POSIX_C_SOURCE 200809L
     14 
     15 #include <fcntl.h>
     16 #include <sys/types.h>
     17 #include <sys/wait.h>
     18 #include <unistd.h>
     19 
     20 #include <assert.h>
     21 #include <errno.h>
     22 #include <limits.h>
     23 #include <signal.h>
     24 #include <stdio.h>
     25 #include <stdlib.h>
     26 #include <string.h>
     27 
     28 #include <scc/config.h>
     29 #include <scc/scc.h>
     30 #include <scc/sys.h>
     31 
     32 enum {
     33 	CC1,
     34 	TEEIR,
     35 	CC2,
     36 	TEEQBE,
     37 	QBE,
     38 	TEEAS,
     39 	AS,
     40 	LD,
     41 	LAST_TOOL,
     42 };
     43 
     44 static struct tool {
     45 	char   cmd[PATH_MAX];
     46 	char   bin[32];
     47 	char  *outfile;
     48 	struct items args;
     49 	int    in, out;
     50 	pid_t  pid;
     51 } tools[] = {
     52 	[CC1]    = {.bin = "cc1"},
     53 	[TEEIR]  = {.bin = "tee"},
     54 	[CC2]    = {.bin = "cc2"},
     55 	[TEEQBE] = {.bin = "tee"},
     56 	[QBE]    = {.bin = "qbe"},
     57 	[TEEAS]  = {.bin = "tee"},
     58 	[AS]     = {.bin = ASBIN},
     59 	[LD]     = {.bin = LDBIN},
     60 };
     61 
     62 static char *arch, *sys, *abi, *format;
     63 static char *objfile, *outfile;
     64 static char *prefix, *libprefix;
     65 static char *tmpdir;
     66 static size_t tmpdirln;
     67 static struct items objtmp, objout;
     68 static struct items linkargs, cc1args;
     69 
     70 static int Mflag, Eflag, Sflag, Wflag,
     71            cflag, dflag, kflag, sflag, Qflag = 1,
     72            gflag;
     73 
     74 static int devnullfd = -1;
     75 
     76 extern int failure;
     77 
     78 static void
     79 terminate(void)
     80 {
     81 	unsigned i;
     82 
     83 	if (!kflag) {
     84 		for (i = 0; i < objtmp.n; ++i)
     85 			unlink(objtmp.s[i]);
     86 	}
     87 }
     88 
     89 static void
     90 sighandler(int sig)
     91 {
     92 	terminate();
     93 	_exit(1);
     94 }
     95 
     96 static void
     97 addarg(int tool, char *arg)
     98 {
     99 	struct tool *t = &tools[tool];
    100 	char *p, buff[FILENAME_MAX];
    101 	size_t len, cnt;
    102 	int n;
    103 
    104 	if (!arg) {
    105 		newitem(&t->args, NULL);
    106 		return;
    107 	}
    108 
    109 	if (arg[0] == '-') {
    110 		newitem(&t->args, xstrdup(arg));
    111 		return;
    112 	}
    113 
    114 	if (!strcmp(arg, "%c")) {
    115 		for (n = 0; n < linkargs.n; ++n)
    116 			newitem(&t->args, xstrdup(linkargs.s[n]));
    117 		return;
    118 	}
    119 
    120 	for (cnt = 0 ; *arg && cnt < FILENAME_MAX; ++arg) {
    121 		if (*arg != '%') {
    122 			buff[cnt++] = *arg;
    123 			continue;
    124 		}
    125 
    126 		switch (*++arg) {
    127 		case 'a':
    128 			p = arch;
    129 			break;
    130 		case 's':
    131 			p = sys;
    132 			break;
    133 		case 'p':
    134 			p = libprefix;
    135 			break;
    136 		case 'b':
    137 			p = abi;
    138 			break;
    139 		case 'o':
    140 			p = t->outfile;
    141 			break;
    142 		default:
    143 			buff[cnt++] = *arg;
    144 			continue;
    145 		}
    146 
    147 		len = strlen(p);
    148 		if (len + cnt >= FILENAME_MAX)
    149 			die("scc-cc: pathname too long");
    150 		memcpy(buff+cnt, p, len);
    151 		cnt += len;
    152 	}
    153 
    154 	if (cnt >= FILENAME_MAX)
    155 		abort();
    156 
    157 	buff[cnt] = '\0';
    158 	newitem(&t->args, xstrdup(buff));
    159 }
    160 
    161 static char *
    162 cc2fmt(int tool)
    163 {
    164 	if (Qflag)
    165 		return "%s/libexec/scc/%s-qbe_%s-%s";
    166 	return "%s/libexec/scc/%s-%s-%s";
    167 }
    168 
    169 static char *
    170 outfname(char *path, char *type)
    171 {
    172 	char *new, sep, *p;
    173 	size_t newsz, pathln;
    174 	int tmpfd, n;
    175 
    176 	if (path) {
    177 		sep = '.';
    178 		if (p = strrchr(path, '/'))
    179 			path = p + 1;
    180 		pathln = strlen(path);
    181 		if (p = strrchr(path, '.'))
    182 			pathln -= strlen(p);
    183 	} else {
    184 		sep = '/';
    185 		type = "scc-XXXXXX";
    186 		path = tmpdir;
    187 		pathln = tmpdirln;
    188 	}
    189 
    190 	newsz = pathln + 1 + strlen(type) + 1;
    191 	new = xmalloc(newsz);
    192 	n = snprintf(new, newsz, "%.*s%c%s", (int)pathln, path, sep, type);
    193 	if (n < 0 || n >= newsz)
    194 		die("scc-cc: wrong output filename");
    195 	if (sep == '/') {
    196 		if ((tmpfd = mkstemp(new)) < 0)
    197 			die("scc-cc: could not create output file '%s': %s",
    198 			    new, strerror(errno));
    199 		close(tmpfd);
    200 	}
    201 
    202 	return new;
    203 }
    204 
    205 static int
    206 settool(int tool, char *infile, int nexttool)
    207 {
    208 	struct tool *t = &tools[tool];
    209 	char *fmt;
    210 	int n, fds[2];
    211 	static int fdin = -1;
    212 
    213 	strcpy(t->cmd, t->bin);
    214 	assert(t->args.n == 0);
    215 	newitem(&t->args, xstrdup(t->bin));
    216 
    217 	switch (tool) {
    218 	case CC1:
    219 		if (Eflag)
    220 			addarg(tool, "-E");
    221 		if (Mflag)
    222 			addarg(tool, "-M");
    223 		if (Wflag)
    224 			addarg(tool, "-w");
    225 		if (Qflag)
    226 			addarg(tool, "-D__QBE__");
    227 
    228 		for (n = 0; n < cc1args.n; ++n)
    229 			addarg(tool, cc1args.s[n]);
    230 		for (n = 0; sysincludes[n]; ++n) {
    231 			addarg(tool, "-I");
    232 			addarg(tool, sysincludes[n]);
    233 		}
    234 		fmt = "%s/libexec/scc/%s";
    235 		goto local_tool;
    236 	case CC2:
    237 		fmt = cc2fmt(tool);
    238 	local_tool:
    239 		n = snprintf(t->cmd, sizeof(t->cmd),
    240 		             fmt, prefix, t->bin, arch, abi);
    241 		if (n < 0 || n >= sizeof(t->cmd))
    242 			die("scc-cc: target tool path is too long");
    243 		break;
    244 	case QBE:
    245 		strcpy(t->cmd, "qbe");
    246 
    247 		if (!strcmp(arch, "amd64") && !strcmp(abi, "sysv"))
    248 			fmt = "amd64_sysv";
    249 		else if (!strcmp(arch, "arm64") && !strcmp(abi, "sysv"))
    250 			fmt = "arm64";
    251 		else if (!strcmp(arch, "riscv64") && !strcmp(abi, "sysv"))
    252 			fmt = "rv64";
    253 		else
    254 			die("not supported arch-abi (%s-%s) for qbe", arch, abi);
    255 
    256 		addarg(tool, "-t");
    257 		addarg(tool, fmt);
    258 		break;
    259 	case LD:
    260 		if (!outfile)
    261 			outfile = "a.out";
    262 		t->outfile = xstrdup(outfile);
    263 		if (gflag)
    264 			addarg(tool, "-g");
    265 		if (sflag)
    266 			addarg(tool, "-s");
    267 		for (n = 0; ldcmd[n]; n++)
    268 			addarg(tool, ldcmd[n]);
    269                 break;
    270 	case TEEIR:
    271 		if (Eflag && outfile)
    272 			t->outfile = xstrdup(outfile);
    273 		else
    274 			t->outfile = outfname(infile, "ir");
    275 		addarg(tool, t->outfile);
    276 		break;
    277 	case TEEQBE:
    278 		t->outfile = outfname(infile, "qbe");
    279 		addarg(tool, t->outfile);
    280 		break;
    281 	case TEEAS:
    282 		t->outfile = outfname(infile, "s");
    283 		addarg(tool, t->outfile);
    284 		break;
    285 	case AS:
    286 		if (cflag && outfile) {
    287 			objfile = xstrdup(outfile);
    288 		} else {
    289 			objfile = (cflag || kflag) ? infile : NULL;
    290 			objfile = outfname(objfile, "o");
    291 		}
    292 		t->outfile = xstrdup(objfile);
    293 		if (gflag)
    294 			addarg(tool, "-g");
    295 		for (n = 0; ascmd[n]; n++)
    296 			addarg(tool, ascmd[n]);
    297                 break;
    298 		break;
    299 	default:
    300 		break;
    301 	}
    302 
    303 	if (fdin > -1) {
    304 		t->in = fdin;
    305 		fdin = -1;
    306 	} else {
    307 		t->in = -1;
    308 		if (infile)
    309 			addarg(tool, infile);
    310 	}
    311 
    312 	if (nexttool < LAST_TOOL) {
    313 		if (pipe(fds))
    314 			die("scc-cc: pipe: %s", strerror(errno));
    315 		t->out = fds[1];
    316 		fdin = fds[0];
    317 	} else {
    318 		t->out = -1;
    319 	}
    320 
    321 	addarg(tool, NULL);
    322 
    323 	return tool;
    324 }
    325 
    326 /*
    327  * We cannot call exit() because it would call the atexit()
    328  * handlers. If it finishes correctly we already called
    329  * fflush() in the output buffers so it is not a problem
    330  * not following the normal exit path.
    331  */
    332 static void
    333 tee(char *fname)
    334 {
    335 	FILE *fp;
    336 	int ch;
    337 
    338 	if ((fp = fopen(fname, "w")) == NULL)
    339 		goto err;
    340 
    341 	while ((ch = getchar()) != EOF) {
    342 		putc(ch, stdout);
    343 		putc(ch, fp);
    344 	}
    345 
    346 	fflush(stdout);
    347 	fflush(fp);
    348 
    349 	if (ferror(stdin) || ferror(stdout) || ferror(fp))
    350 		goto err;
    351 	_exit(0);
    352 
    353 err:
    354 	fprintf(stderr,
    355 		"tee: teeing %s: %s\n",
    356 		fname,
    357 		strerror(errno));
    358 
    359 	_exit(1);
    360 }
    361 
    362 static void
    363 spawn(int tool)
    364 {
    365 	int i;
    366 	char **ap;
    367 	struct tool *t = &tools[tool];
    368 
    369 	switch (t->pid = fork()) {
    370 	case -1:
    371 		die("scc-cc: %s: %s", t->bin, strerror(errno));
    372 	case 0:
    373 		if (t->out > -1)
    374 			dup2(t->out, 1);
    375 		if (t->in > -1)
    376 			dup2(t->in, 0);
    377 
    378 		if (tool == TEEAS && Sflag)
    379 			dup2(devnullfd, 1);
    380 		if (!dflag && tool != CC1 && tool != LD)
    381 			dup2(devnullfd, 2);
    382 
    383 		if (dflag) {
    384 			fprintf(stderr, "%s", t->cmd);
    385 			for (ap = t->args.s+1; *ap; ap++)
    386 				fprintf(stderr, " %s", *ap);
    387 			putc('\n', stderr);
    388 		}
    389 		if (strcmp(t->cmd, "tee") == 0)
    390 			tee(t->outfile);
    391 		execvp(t->cmd, t->args.s);
    392 		if (dflag) {
    393 			fprintf(stderr,
    394 			        "scc-cc: execvp %s: %s\n",
    395 				t->cmd,
    396 			        strerror(errno));
    397 		}
    398 		abort();
    399 	default:
    400 		if (t->in > -1)
    401 			close(t->in);
    402 		if (t->out > -1)
    403 			close(t->out);
    404 
    405 		for (i = 0; i < t->args.n; i++)
    406 			free(t->args.s[i]);
    407 		t->args.n = 0;
    408 
    409 		break;
    410 	}
    411 }
    412 
    413 static int
    414 toolfor(char *file)
    415 {
    416 	char *dot = strrchr(file, '.');
    417 
    418 	if (Eflag)
    419 		return CC1;
    420 
    421 	if (dot) {
    422 		if (!strcmp(dot, ".c"))
    423 			return CC1;
    424 		if (!strcmp(dot, ".ir"))
    425 			return CC2;
    426 		if (!strcmp(dot, ".qbe"))
    427 			return QBE;
    428 		if (!strcmp(dot, ".s"))
    429 			return AS;
    430 		if (!strcmp(dot, ".o"))
    431 			return LD;
    432 		if (!strcmp(dot, ".a"))
    433 			return LD;
    434 	} else if (!strcmp(file, "-")) {
    435 		return CC1;
    436 	}
    437 
    438 	die("scc-cc: unrecognized filetype of %s", file);
    439 }
    440 
    441 static int
    442 valid(int tool, struct tool *t)
    443 {
    444 	int st;
    445 
    446 	if (waitpid(t->pid, &st, 0) == -1 || WIFSIGNALED(st))
    447 		goto internal;
    448 	if (WIFEXITED(st) && WEXITSTATUS(st) == 0)
    449 		return 1;
    450 	if (!failure && (tool == CC1 || tool == LD))
    451 		goto fail;
    452 
    453 internal:
    454 	if (!failure)
    455 		fprintf(stderr, "scc-cc:%s: internal error\n", t->bin);
    456 fail:
    457 	failure = 1;
    458 	return 0;
    459 }
    460 
    461 static int
    462 validatetools(void)
    463 {
    464 	struct tool *t;
    465 	unsigned i;
    466 	int tool, st, failed = LAST_TOOL;
    467 
    468 	for (tool = 0; tool < LAST_TOOL; ++tool) {
    469 		t = &tools[tool];
    470 		if (!t->pid)
    471 			continue;
    472 		if (!valid(tool, t))
    473 			failed = tool;
    474 		if (tool >= failed && t->outfile)
    475 			unlink(t->outfile);
    476 		free(t->outfile);
    477 		t->pid = 0;
    478 	}
    479 	if (failed < LAST_TOOL) {
    480 		unlink(objfile);
    481 		return 0;
    482 	}
    483 
    484 	return 1;
    485 }
    486 
    487 static int
    488 buildfile(char *file, int tool)
    489 {
    490 	int nexttool;
    491 
    492 	for (; tool < LAST_TOOL; tool = nexttool) {
    493 		switch (tool) {
    494 		case CC1:
    495 			if (Eflag && outfile)
    496 				nexttool = TEEIR;
    497 			else if (Eflag || Mflag)
    498 				nexttool = LAST_TOOL;
    499 			else
    500 				nexttool = kflag ? TEEIR : CC2;
    501 			break;
    502 		case TEEIR:
    503 			nexttool = (Eflag) ? LAST_TOOL : CC2;
    504 			break;
    505 		case CC2:
    506 			if (Qflag)
    507 				nexttool = kflag ? TEEQBE : QBE;
    508 			else
    509 				nexttool = (Sflag || kflag) ? TEEAS : AS;
    510 			break;
    511 		case TEEQBE:
    512 			nexttool = QBE;
    513 			break;
    514 		case QBE:
    515 			nexttool = (Sflag || kflag) ? TEEAS : AS;
    516 			break;
    517 		case TEEAS:
    518 			nexttool = Sflag ? LAST_TOOL : AS;
    519 			break;
    520 		case AS:
    521 			nexttool = LAST_TOOL;
    522 			break;
    523 		default:
    524 			nexttool = LAST_TOOL;
    525 			continue;
    526 		}
    527 
    528 		spawn(settool(tool, file, nexttool));
    529 	}
    530 
    531 	return validatetools();
    532 }
    533 
    534 static void
    535 build(struct items *chain, int link)
    536 {
    537 	int i, tool;
    538 
    539 	for (i = 0; i < chain->n; ++i) {
    540 		if (!strcmp(chain->s[i], "-l")) {
    541 			newitem(&linkargs, chain->s[i++]);
    542 			newitem(&linkargs, chain->s[i]);
    543 			continue;
    544 		}
    545 		tool = toolfor(chain->s[i]);
    546 		if (tool == LD) {
    547 			newitem(&linkargs, chain->s[i]);
    548 			continue;
    549 		}
    550 		if (buildfile(chain->s[i], tool)) {
    551 			newitem(&linkargs, objfile);
    552 			newitem((!link || kflag) ? &objout : &objtmp, objfile);
    553 		}
    554 	}
    555 }
    556 
    557 static void
    558 usage(void)
    559 {
    560 	fputs("usage: scc-cc [options] file...\n", stderr);
    561 	exit(1);
    562 }
    563 
    564 static char *
    565 param(char **argp, char ***argv)
    566 {
    567 	char *s;
    568 
    569 	if ((*argp)[1] != '\0') {
    570 		s = (*argp) + 1;
    571 		*argp += strlen(*argp) - 1;
    572 	} else if ((*argv)[1] != NULL) {
    573 		s = (*argv)[1];
    574 		(*argv)++;
    575 	} else {
    576 		usage();
    577 	}
    578 
    579 	return s;
    580 }
    581 
    582 int
    583 main(int argc, char *argv[])
    584 {
    585 	struct items linkchain = { .n = 0, };
    586 	int link, n;
    587 
    588 	atexit(terminate);
    589 	signal(SIGHUP, sighandler);
    590 	signal(SIGINT, sighandler);
    591 	signal(SIGTERM, sighandler);
    592 
    593 	if (!(arch = getenv("ARCH")))
    594 		arch = ARCH;
    595 	if (!(sys = getenv("SYS")))
    596 		sys = SYS;
    597 	if (!(abi = getenv("ABI")))
    598 		abi = ABI;
    599 	if (!(format = getenv("FORMAT")))
    600 		format = FORMAT;
    601 	if (!(prefix = getenv("SCCPREFIX")))
    602 		prefix = PREFIX;
    603 	if (!(libprefix = getenv("SCCLIBPREFIX")))
    604 		libprefix = LIBPREFIX;
    605 
    606 	for (++argv; *argv; ++argv) {
    607 		char *arg = *argv;
    608 		if (arg[0] == '-' && arg[1] == '-')
    609 			break;
    610 		if (arg[0] != '-') {
    611 			newitem(&linkchain, arg);
    612 			continue;
    613 		}
    614 
    615 		if (!strcmp(arg, "-static") ||
    616 		    !strcmp(arg, "-dynamic") ||
    617 		    !strcmp(arg, "-pedantic") ||
    618 		    !strcmp(arg, "-ansi") ||
    619 		    !strncmp(arg, "-std=", 5)) {
    620 			continue;
    621 		}
    622 
    623 		for (++arg; *arg; ++arg) {
    624 			switch (*arg) {
    625 			case 'D':
    626 				newitem(&cc1args, "-D");
    627 				newitem(&cc1args, param(&arg, &argv));
    628 				break;
    629 			case 'M':
    630 				Mflag = 1;
    631 				break;
    632 			case 'E':
    633 				Eflag = 1;
    634 				break;
    635 			case 'I':
    636 				newitem(&cc1args, "-I");
    637 				newitem(&cc1args, param(&arg, &argv));
    638 				break;
    639 			case 'L':
    640 				newitem(&linkargs, "-L");
    641 				newitem(&linkargs, param(&arg, &argv));
    642 				break;
    643 			case 'O':
    644 				param(&arg, &argv);
    645 				break;
    646 			case 'S':
    647 				Sflag = 1;
    648 				break;
    649 			case 'U':
    650 				newitem(&cc1args, "-U");
    651 				newitem(&cc1args, param(&arg, &argv));
    652 				break;
    653 			case 'c':
    654 				cflag = 1;
    655 				break;
    656 			case 'd':
    657 				dflag = 1;
    658 				break;
    659 			case 'g':
    660 				gflag = 1;
    661 				break;
    662 			case 'k':
    663 				kflag = 1;
    664 				break;
    665 			case 'l':
    666 				newitem(&linkchain, "-l");
    667 				newitem(&linkchain, param(&arg, &argv));
    668 				break;
    669 			case 'a':
    670 				arch = param(&arg, &argv);
    671 				break;
    672 			case 't':
    673 				sys = param(&arg, &argv);
    674 				break;
    675 			case 'o':
    676 				outfile = param(&arg, &argv);
    677 				break;
    678 			case 's':
    679 				sflag = 1;
    680 				break;
    681 			case 'w':
    682 				Wflag = 1;
    683 				break;
    684 			case 'W':
    685 				param(&arg, &argv);
    686 				Wflag = 1;
    687 				break;
    688 			case 'q':
    689 				Qflag = 0;
    690 				break;
    691 			case 'Q':
    692 				Qflag = 1;
    693 				break;
    694 			case 'm':
    695 			case 'f':
    696 				/* ignore any -f or -m option */
    697 				param(&arg, &argv);
    698 				break;
    699 			default:
    700 				usage();
    701 			}
    702 		}
    703 	}
    704 
    705 	for (; *argv; ++argv)
    706 		newitem(&linkchain, *argv);
    707 
    708 	if (Eflag && linkchain.n == 0)
    709 		newitem(&linkchain, "-");
    710 
    711 	if (Eflag && Mflag ||
    712             (Eflag || Mflag) && (Sflag || kflag) ||
    713 	    linkchain.n == 0 ||
    714 	    linkchain.n > 1 && cflag && outfile)
    715 		usage();
    716 
    717 	if (!dflag) {
    718 		if ((devnullfd = open("/dev/null", O_WRONLY)) < 0)
    719 			fputs("scc-cc: could not open /dev/null\n", stderr);
    720 	}
    721 
    722 	if (!(tmpdir = getenv("TMPDIR")) || !tmpdir[0])
    723 		tmpdir = ".";
    724 	tmpdirln = strlen(tmpdir);
    725 
    726 	build(&linkchain, (link = !(Mflag || Eflag || Sflag || cflag)));
    727 
    728 	if (!(link || cflag))
    729 		return failure;
    730 
    731 	if (link && !failure) {
    732 		spawn(settool(LD, NULL, LAST_TOOL));
    733 		validatetools();
    734 	}
    735 
    736 	return failure;
    737 }