scc

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

scc.c (11439B)


      1 #define _POSIX_SOURCE
      2 #define _XOPEN_SOURCE 500
      3 #include <sys/types.h>
      4 #include <sys/wait.h>
      5 #include <unistd.h>
      6 
      7 #include <errno.h>
      8 #include <fcntl.h>
      9 #include <limits.h>
     10 #include <signal.h>
     11 #include <stdio.h>
     12 #include <stdlib.h>
     13 #include <string.h>
     14 
     15 #include "config.h"
     16 #include <scc/arg.h>
     17 #include <scc/scc.h>
     18 #include <scc/syscrts.h>
     19 #include <scc/sysincludes.h>
     20 #include <scc/syslibs.h>
     21 #include <scc/ldflags.h>
     22 
     23 enum {
     24 	CC1,
     25 	TEEIR,
     26 	CC2,
     27 	TEEQBE,
     28 	QBE,
     29 	TEEAS,
     30 	AS,
     31 	LD,
     32 	STRIP,
     33 	LAST_TOOL,
     34 };
     35 
     36 static struct tool {
     37 	char   cmd[PATH_MAX];
     38 	char   bin[32];
     39 	char  *outfile;
     40 	struct items args;
     41 	unsigned nparams;
     42 	int    in, out, init;
     43 	pid_t  pid;
     44 } tools[] = {
     45 	[CC1]    = { .cmd = "cc1" },
     46 	[TEEIR]  = { .bin = "tee",   .cmd = "tee", },
     47 	[CC2]    = { .cmd = "cc2" },
     48 	[TEEQBE] = { .bin = "tee",   .cmd = "tee", },
     49 	[QBE]    = { .bin = "qbe",   .cmd = "qbe", },
     50 	[TEEAS]  = { .bin = "tee",   .cmd = "tee", },
     51 	[AS]     = { .bin = "as",    .cmd = "as", },
     52 	[LD]     = { .bin = "ld",    .cmd = "ld", },
     53 	[STRIP]  = { .bin = "strip", .cmd = "strip", },
     54 };
     55 
     56 char *argv0;
     57 static char *arch, *sys, *abi, *format;
     58 static char *prefix, *objfile, *outfile;
     59 static char *tmpdir;
     60 static size_t tmpdirln;
     61 static struct items objtmp, objout;
     62 static int Mflag, Eflag, Sflag, Wflag,
     63            cflag, dflag, kflag, sflag, Qflag = 1; /* TODO: Remove Qflag */
     64 static int devnullfd = -1;
     65 
     66 extern int failure;
     67 
     68 static void
     69 terminate(void)
     70 {
     71 	unsigned i;
     72 
     73 	if (!kflag) {
     74 		for (i = 0; i < objtmp.n; ++i)
     75 			unlink(objtmp.s[i]);
     76 	}
     77 }
     78 
     79 static void
     80 addarg(int tool, char *arg)
     81 {
     82 	struct tool *t = &tools[tool];
     83 
     84 	if (t->args.n < 1)
     85 		t->args.n = 1;
     86 
     87 	newitem(&t->args, arg);
     88 }
     89 
     90 static void
     91 setargv0(int tool, char *arg)
     92 {
     93 	struct tool *t = &tools[tool];
     94 
     95 	if (t->args.n > 0)
     96 		t->args.s[0] = arg;
     97 	else
     98 		newitem(&t->args, arg);
     99 }
    100 
    101 static int
    102 qbe(int tool)
    103 {
    104 	if (tool != CC2 || !Qflag)
    105 		return 0;
    106 	if (!strcmp(arch, "amd64") && !strcmp(abi, "sysv"))
    107 		return 1;
    108 	return 0;
    109 }
    110 
    111 static int
    112 inittool(int tool)
    113 {
    114 	struct tool *t = &tools[tool];
    115 	char *crt, *fmt;
    116 	int n;
    117 
    118 	if (t->init)
    119 		return tool;
    120 
    121 	switch (tool) {
    122 	case CC1:
    123 		if (Wflag)
    124 			addarg(tool, "-w");
    125 		for (n = 0; sysincludes[n]; ++n) {
    126 			addarg(tool, "-I");
    127 			addarg(tool, sysincludes[n]);
    128 		}
    129 	case CC2:
    130 		fmt = (qbe(tool)) ? "%s-qbe_%s-%s" : "%s-%s-%s";
    131 		n = snprintf(t->bin, sizeof(t->bin), fmt, t->cmd, arch, abi);
    132 		if (n < 0 || n >= sizeof(t->bin))
    133 			die("scc: target tool name too long");
    134 
    135 		n = snprintf(t->cmd, sizeof(t->cmd),
    136 		             "%s/libexec/scc/%s", prefix, t->bin);
    137 		if (n < 0 || n >= sizeof(t->cmd))
    138 			die("scc: target tool path too long");
    139 		break;
    140 	case LD:
    141 		for (n = 0; ldflags[n]; ++n)
    142 			addarg(tool, ldflags[n]);
    143 		addarg(tool, "-o");
    144 		t->outfile = outfile ? outfile : xstrdup("a.out");
    145 		addarg(tool, t->outfile);
    146 		for (n = 0; syslibs[n]; ++n) {
    147 			addarg(tool, "-L");
    148 			addarg(tool, syslibs[n]);
    149 		}
    150 		if (syscrts[0]) {
    151 			for (n = 0; syscrts[n]; ++n)
    152 				addarg(tool, syscrts[n]);
    153 			break;
    154 		}
    155 		n = snprintf(NULL, 0,
    156 		             "%s/lib/scc/%s-%s-%s/crt.o",
    157 		             prefix, arch, abi, sys);
    158 		if (n < 0)
    159 			die("scc: wrong crt file name");
    160 		crt = xmalloc(++n);
    161 		sprintf(crt,
    162 		        "%s/lib/scc/%s-%s-%s/crt.o",
    163 		        prefix, arch, abi, sys);
    164 		addarg(tool, crt);
    165 		break;
    166 	case AS:
    167 		addarg(tool, "-o");
    168 		break;
    169 	default:
    170 		break;
    171 	}
    172 
    173 	setargv0(tool, t->bin);
    174 	t->nparams = t->args.n;
    175 	t->init = 1;
    176 
    177 	return tool;
    178 }
    179 
    180 static char *
    181 outfname(char *path, char *type)
    182 {
    183 	char *new, sep, *p;
    184 	size_t newsz, pathln;
    185 	int tmpfd, n;
    186 
    187 	if (path) {
    188 		sep = '.';
    189 		if (p = strrchr(path, '/'))
    190 			path = p + 1;
    191 		pathln = strlen(path);
    192 		if (p = strrchr(path, '.'))
    193 			pathln -= strlen(p);
    194 	} else {
    195 		sep = '/';
    196 		type = "scc-XXXXXX";
    197 		path = tmpdir;
    198 		pathln = tmpdirln;
    199 	}
    200 
    201 	newsz = pathln + 1 + strlen(type) + 1;
    202 	new = xmalloc(newsz);
    203 	n = snprintf(new, newsz, "%.*s%c%s", (int)pathln, path, sep, type);
    204 	if (n < 0 || n >= newsz)
    205 		die("scc: wrong output filename");
    206 	if (sep == '/') {
    207 		if ((tmpfd = mkstemp(new)) < 0)
    208 			die("scc: could not create output file '%s': %s",
    209 			    new, strerror(errno));
    210 		close(tmpfd);
    211 	}
    212 
    213 	return new;
    214 }
    215 
    216 static int
    217 settool(int tool, char *infile, int nexttool)
    218 {
    219 	struct tool *t = &tools[tool];
    220 	unsigned i;
    221 	int fds[2];
    222 	static int fdin = -1;
    223 
    224 	switch (tool) {
    225 	case TEEIR:
    226 		t->outfile = outfname(infile, "ir");
    227 		addarg(tool, t->outfile);
    228 		break;
    229 	case TEEQBE:
    230 		t->outfile = outfname(infile, "qbe");
    231 		addarg(tool, t->outfile);
    232 		break;
    233 	case TEEAS:
    234 		t->outfile = outfname(infile, "s");
    235 		addarg(tool, t->outfile);
    236 		break;
    237 	case AS:
    238 		if (cflag && outfile) {
    239 			objfile = outfile;
    240 		} else {
    241 			objfile = (cflag || kflag) ? infile : NULL;
    242 			objfile = outfname(objfile, "o");
    243 		}
    244 		t->outfile = xstrdup(objfile);
    245 		addarg(tool, t->outfile);
    246 		break;
    247 	case STRIP:
    248 		if (cflag || kflag) {
    249 			for (i = 0; i < objout.n; ++i)
    250 				addarg(tool, xstrdup(objout.s[i]));
    251 		}
    252 		if (!cflag && tools[LD].outfile)
    253 			addarg(tool, tools[LD].outfile);
    254 		break;
    255 	default:
    256 		break;
    257 	}
    258 
    259 	if (fdin > -1) {
    260 		t->in = fdin;
    261 		fdin = -1;
    262 	} else {
    263 		t->in = -1;
    264 		if (infile)
    265 			addarg(tool, xstrdup(infile));
    266 	}
    267 
    268 	if (nexttool < LAST_TOOL) {
    269 		if (pipe(fds))
    270 			die("scc: pipe: %s", strerror(errno));
    271 		t->out = fds[1];
    272 		fdin = fds[0];
    273 	} else {
    274 		t->out = -1;
    275 	}
    276 
    277 	addarg(tool, NULL);
    278 
    279 	return tool;
    280 }
    281 
    282 static void
    283 spawn(int tool)
    284 {
    285 	struct tool *t = &tools[tool];
    286 
    287 	switch (t->pid = fork()) {
    288 	case -1:
    289 		die("scc: %s: %s", t->bin, strerror(errno));
    290 	case 0:
    291 		if (t->out > -1)
    292 			dup2(t->out, 1);
    293 		if (t->in > -1)
    294 			dup2(t->in, 0);
    295 		if (!dflag && tool != CC1 && tool != LD)
    296 			dup2(devnullfd, 2);
    297 		execvp(t->cmd, t->args.s);
    298 		if (dflag) {
    299 			fprintf(stderr,
    300 			        "scc: execvp %s: %s\n",
    301 				t->cmd,
    302 			        strerror(errno));
    303 		}
    304 		abort();
    305 	default:
    306 		if (t->in > -1)
    307 			close(t->in);
    308 		if (t->out > -1)
    309 			close(t->out);
    310 		break;
    311 	}
    312 }
    313 
    314 static int
    315 toolfor(char *file)
    316 {
    317 	char *dot = strrchr(file, '.');
    318 
    319 	if (dot) {
    320 		if (!strcmp(dot, ".c"))
    321 			return CC1;
    322 		if (!strcmp(dot, ".ir"))
    323 			return CC2;
    324 		if (!strcmp(dot, ".qbe"))
    325 			return QBE;
    326 		if (!strcmp(dot, ".s"))
    327 			return AS;
    328 		if (!strcmp(dot, ".o"))
    329 			return LD;
    330 		if (!strcmp(dot, ".a"))
    331 			return LD;
    332 	} else if (!strcmp(file, "-")) {
    333 		return CC1;
    334 	}
    335 
    336 	die("scc: do not recognize filetype of %s", file);
    337 }
    338 
    339 static int
    340 valid(int tool, struct tool *t)
    341 {
    342 	int st;
    343 
    344 	if (waitpid(t->pid, &st, 0) == -1 || WIFSIGNALED(st))
    345 		goto internal;
    346 	if (WIFEXITED(st) && WEXITSTATUS(st) == 0)
    347 		return 1;
    348 	if (!failure && (tool == CC1 || tool == LD))
    349 		goto fail;
    350 
    351 internal:
    352 	fprintf(stderr, "scc:%s: internal error\n", t->bin);
    353 fail:
    354 	failure = 1;
    355 	return 0;
    356 }
    357 
    358 static int
    359 validatetools(void)
    360 {
    361 	struct tool *t;
    362 	unsigned i;
    363 	int tool, st, failed = LAST_TOOL;
    364 
    365 	for (tool = 0; tool < LAST_TOOL; ++tool) {
    366 		t = &tools[tool];
    367 		if (!t->pid)
    368 			continue;
    369 		if (!valid(tool, t))
    370 			failed = tool;
    371 		if (tool >= failed && t->outfile)
    372 			unlink(t->outfile);
    373 		for (i = t->nparams; i < t->args.n; ++i)
    374 			free(t->args.s[i]);
    375 		t->args.n = t->nparams;
    376 		t->pid = 0;
    377 	}
    378 	if (failed < LAST_TOOL) {
    379 		unlink(objfile);
    380 		free(objfile);
    381 		objfile = NULL;
    382 		return 0;
    383 	}
    384 
    385 	return 1;
    386 }
    387 
    388 static int
    389 buildfile(char *file, int tool)
    390 {
    391 	int nexttool;
    392 
    393 	for (; tool < LAST_TOOL; tool = nexttool) {
    394 		switch (tool) {
    395 		case CC1:
    396 			if (Eflag || Mflag)
    397 				nexttool = LAST_TOOL;
    398 			else
    399 				nexttool = kflag ? TEEIR : CC2;
    400 			break;
    401 		case TEEIR:
    402 			nexttool = CC2;
    403 			break;
    404 		case CC2:
    405 			if (Qflag)
    406 				nexttool = kflag ? TEEQBE : QBE;
    407 			else
    408 				nexttool = (Sflag || kflag) ? TEEAS : AS;
    409 			break;
    410 		case TEEQBE:
    411 			nexttool = QBE;
    412 			break;
    413 		case QBE:
    414 			nexttool = (Sflag || kflag) ? TEEAS : AS;
    415 			break;
    416 		case TEEAS:
    417 			nexttool = Sflag ? LAST_TOOL : AS;
    418 			break;
    419 		case AS:
    420 			nexttool = LAST_TOOL;
    421 			break;
    422 		default:
    423 			nexttool = LAST_TOOL;
    424 			continue;
    425 		}
    426 
    427 		spawn(settool(inittool(tool), file, nexttool));
    428 	}
    429 
    430 	return validatetools();
    431 }
    432 
    433 static void
    434 build(struct items *chain, int link)
    435 {
    436 	int i, tool;
    437 
    438 	if (link)
    439 		inittool(LD);
    440 
    441 	for (i = 0; i < chain->n; ++i) {
    442 		if (!strcmp(chain->s[i], "-l")) {
    443 			if (link) {
    444 				addarg(LD, xstrdup(chain->s[i++]));
    445 				addarg(LD, xstrdup(chain->s[i]));
    446 			} else {
    447 				++i;
    448 			}
    449 			continue;
    450 		}
    451 		tool = toolfor(chain->s[i]);
    452 		if (tool == LD) {
    453 			if (link)
    454 				addarg(LD, xstrdup(chain->s[i]));
    455 			continue;
    456 		}
    457 		if (buildfile(chain->s[i], tool)) {
    458 			if (link)
    459 				addarg(LD, xstrdup(objfile));
    460 			newitem((!link || kflag) ? &objout : &objtmp, objfile);
    461 		}
    462 	}
    463 }
    464 
    465 static void
    466 usage(void)
    467 {
    468 	fputs("usage: scc [-D def[=val]]... [-U def]... [-I dir]... "
    469 	       "[-L dir]... [-l dir]...\n"
    470 	       "           [-dgksw] [-m arch] [-M|-E|-S] [-o outfile] file...\n"
    471 	       "       scc [-D def[=val]]... [-U def]... [-I dir]... "
    472 	       "[-L dir]... [-l dir]...\n"
    473 	       "           [-dgksw] [-m arch] [-M|-E|-S] -c file...\n"
    474 	       "       scc [-D def[=val]]... [-U def]... [-I dir]... "
    475 	       "[-L dir]... [-l dir]...\n"
    476 	       "           [-dgksw] [-m arch] -c -o outfile file\n", stderr);
    477 	exit(1);
    478 }
    479 
    480 int
    481 main(int argc, char *argv[])
    482 {
    483 	struct items linkchain = { .n = 0, };
    484 	int link;
    485 
    486 	atexit(terminate);
    487 
    488 	if (!(arch = getenv("ARCH")))
    489 		arch = ARCH;
    490 	if (!(sys = getenv("SYS")))
    491 		sys = SYS;
    492 	if (!(abi = getenv("ABI")))
    493 		abi = ABI;
    494 	if (!(format = getenv("FORMAT")))
    495 		format = FORMAT;
    496 	if (!(prefix = getenv("SCCPREFIX")))
    497 		prefix = PREFIX;
    498 
    499 	ARGBEGIN {
    500 	case 'D':
    501 		addarg(CC1, "-D");
    502 		addarg(CC1, EARGF(usage()));
    503 		break;
    504 	case 'M':
    505 		Mflag = 1;
    506 		addarg(CC1, "-M");
    507 		break;
    508 	case 'E':
    509 		Eflag = 1;
    510 		addarg(CC1, "-E");
    511 		break;
    512 	case 'I':
    513 		addarg(CC1, "-I");
    514 		addarg(CC1, EARGF(usage()));
    515 		break;
    516 	case 'L':
    517 		addarg(LD, "-L");
    518 		addarg(LD, EARGF(usage()));
    519 		break;
    520 	case 'O':
    521 		EARGF(usage());
    522 		break;
    523 	case 'S':
    524 		Sflag = 1;
    525 		break;
    526 	case 'U':
    527 		addarg(CC1, "-U");
    528 		addarg(CC1, EARGF(usage()));
    529 		break;
    530 	case 'c':
    531 		cflag = 1;
    532 		break;
    533 	case 'd':
    534 		dflag = 1;
    535 		break;
    536 	case 'g':
    537 		addarg(AS, "-g");
    538 		addarg(LD, "-g");
    539 		break;
    540 	case 'k':
    541 		kflag = 1;
    542 		break;
    543 	case 'l':
    544 		newitem(&linkchain, "-l");
    545 		newitem(&linkchain, EARGF(usage()));
    546 		break;
    547 	case 'm':
    548 		arch = EARGF(usage());
    549 		break;
    550 	case 'o':
    551 		outfile = xstrdup(EARGF(usage()));
    552 		break;
    553 	case 's':
    554 		sflag = 1;
    555 		break;
    556 	case 't':
    557 		sys = EARGF(usage());
    558 		break;
    559 	case 'w':
    560 		Wflag = 0;
    561 		break;
    562 	case 'W':
    563 		Wflag = 1;
    564 		break;
    565 	case 'q':
    566 		Qflag = 0;
    567 		break;
    568 	case 'Q':
    569 		Qflag = 1;
    570 		break;
    571 	case '-':
    572 		fprintf(stderr,
    573 		        "scc: ignored parameter --%s\n", EARGF(usage()));
    574 		break;
    575 	default:
    576 		usage();
    577 	} ARGOPERAND {
    578 operand:
    579 		newitem(&linkchain, ARGOP());
    580 	} ARGEND
    581 
    582 	for (; *argv; --argc, ++argv)
    583 		goto operand;
    584 
    585 	if (Eflag && linkchain.n == 0)
    586 		newitem(&linkchain, "-");
    587 
    588 	if (Eflag && Mflag ||
    589             (Eflag || Mflag) && (Sflag || kflag) ||
    590 	    linkchain.n == 0 ||
    591 	    linkchain.n > 1 && cflag && outfile)
    592 		usage();
    593 
    594 	if (!dflag) {
    595 		if ((devnullfd = open("/dev/null", O_WRONLY)) < 0)
    596 			fputs("scc: could not open /dev/null\n", stderr);
    597 	}
    598 
    599 	if (!(tmpdir = getenv("TMPDIR")) || !tmpdir[0])
    600 		tmpdir = ".";
    601 	tmpdirln = strlen(tmpdir);
    602 
    603 	build(&linkchain, (link = !(Mflag || Eflag || Sflag || cflag)));
    604 
    605 	if (!(link || cflag))
    606 		return failure;
    607 
    608 	if (link && !failure) {
    609 		addarg(LD, xstrdup("-lc"));
    610 		spawn(settool(LD, NULL, LAST_TOOL));
    611 		validatetools();
    612 	}
    613 
    614 	if (sflag) {
    615 		spawn(settool(inittool(STRIP), NULL, LAST_TOOL));
    616 		validatetools();
    617 	}
    618 
    619 	return failure;
    620 }