scc

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

scc.c (11491B)


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