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 }