scc

simple C compiler
git clone git://git.2f30.org/scc
Log | Files | Refs | README | LICENSE

scc.c (11018B)


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