1 2 /* 3 * CDDL HEADER START 4 * 5 * The contents of this file are subject to the terms of the 6 * Common Development and Distribution License (the "License"). 7 * You may not use this file except in compliance with the License. 8 * 9 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE 10 * or http://www.opensolaris.org/os/licensing. 11 * See the License for the specific language governing permissions 12 * and limitations under the License. 13 * 14 * When distributing Covered Code, include this CDDL HEADER in each 15 * file and include the License file at usr/src/OPENSOLARIS.LICENSE. 16 * If applicable, add the following below this CDDL HEADER, with the 17 * fields enclosed by brackets "[]" replaced with your own identifying 18 * information: Portions Copyright [yyyy] [name of copyright owner] 19 * 20 * CDDL HEADER END 21 */ 22 23 /* 24 * Copyright 2010 Sun Microsystems, Inc. All rights reserved. 25 * Use is subject to license terms. 26 * 27 * Copyright 2018 Richard Lowe. 28 * Copyright 2019 Joyent, Inc. 29 * Copyright 2025 Oxide Computer Company 30 */ 31 32 /* 33 * Wrapper for the GNU C compiler to make it accept the Sun C compiler 34 * arguments where possible. 35 * 36 * Since the translation is inexact, this is something of a work-in-progress. 37 * 38 */ 39 40 /* 41 * If you modify this file, you must increment CW_VERSION. This is a semver, 42 * incompatible changes should bump the major, anything else the minor. 43 */ 44 #define CW_VERSION "10.0" 45 46 /* 47 * -# Verbose mode 48 * -### Show compiler commands built by driver, no compilation 49 * -A<name[(tokens)]> Preprocessor predicate assertion 50 * -C Prevent preprocessor from removing comments 51 * -c Compile only - produce .o files, suppress linking 52 * -D<name[=token]> Associate name with token as if by #define 53 * -d[y|n] dynamic [-dy] or static [-dn] option to linker 54 * -E Compile source through preprocessor only, output to stdout 55 * -erroff=<t> Suppress warnings specified by tags t(%none, %all, <tag list>) 56 * -errtags=<a> Display messages with tags a(no, yes) 57 * -errwarn=<t> Treats warnings specified by tags t(%none, %all, <tag list>) 58 * as errors 59 * -g Compile for debugging 60 * -H Print path name of each file included during compilation 61 * -h <name> Assign <name> to generated dynamic shared library 62 * -I<dir> Add <dir> to preprocessor #include file search path 63 * -i Passed to linker to ignore any LD_LIBRARY_PATH setting 64 * -keeptmp Keep temporary files created during compilation 65 * -L<dir> Pass to linker to add <dir> to the library search path 66 * -l<name> Link with library lib<name>.a or lib<name>.so 67 * -mt Specify options needed when compiling multi-threaded code 68 * -O Use default optimization level (-xO2 or -xO3. Check man page.) 69 * -o <outputfile> Set name of output file to <outputfile> 70 * -P Compile source through preprocessor only, output to .i file 71 * -p Compile for profiling with prof 72 * -R<dir[:dir]> Build runtime search path list into executable 73 * -S Compile and only generate assembly code (.s) 74 * -s Strip symbol table from the executable file 75 * -std= Set the C standard (note this is in the GNU syntax) 76 * -t Turn off duplicate symbol warnings when linking 77 * -U<name> Delete initial definition of preprocessor symbol <name> 78 * -V Report version number of each compilation phase 79 * -v Do stricter semantic checking 80 * -W<c>,<arg> Pass <arg> to specified component <c> (a,l,m,p,0,2,h,i,u) 81 * -w Suppress compiler warning messages 82 * -Xa Compile assuming ANSI C conformance, allow K & R extensions 83 * (default mode) 84 * -Xs Compile assuming (pre-ANSI) K & R C style code 85 * -xarch=<a> Specify target architecture instruction set 86 * -xbuiltin[=<b>] When profitable inline, or substitute intrinisic functions 87 * for system functions, b={%all,%none} 88 * -xchip=<c> Specify the target processor for use by the optimizer 89 * -xO<n> Generate optimized code (n={1|2|3|4|5}) 90 * -xtarget=<t> Specify target system for optimization 91 * -YI,<dir> Specify <dir> for location of headers 92 */ 93 94 /* 95 * Translation table: 96 */ 97 /* 98 * -# -v 99 * -### error 100 * -A<name[(tokens)]> pass-thru 101 * -B<[static|dynamic]> pass-thru (syntax error for anything else) 102 * -C pass-thru 103 * -c pass-thru 104 * -D<name[=token]> pass-thru 105 * -E pass-thru 106 * -errtags=%all -Wall 107 * -errwarn=%all -Werror else -Wno-error 108 * -g pass-thru 109 * -H pass-thru 110 * -I<dir> pass-thru 111 * -i pass-thru 112 * -keeptmp -save-temps 113 * -L<dir> pass-thru 114 * -l<name> pass-thru 115 * -mt -D_REENTRANT 116 * -nolib -nodefaultlibs 117 * -O -O1 (Check the man page to be certain) 118 * -o <outputfile> pass-thru 119 * -P -E -o filename.i (or error) 120 * -p pass-thru 121 * -R<dir[:dir]> pass-thru 122 * -S pass-thru 123 * -std= pass-thru 124 * -U<name> pass-thru 125 * -V --version 126 * -v -Wall 127 * -Wa,<arg> pass-thru 128 * -Wp,<arg> pass-thru 129 * -Wl,<arg> pass-thru 130 * -xmodel=kernel -ffreestanding -mcmodel=kernel -mno-red-zone 131 * -Wu,-save_args -msave-args 132 * -w pass-thru 133 * -Xa -std=iso9899:199409 or -ansi 134 * -Xs -traditional -std=c89 135 * -xarch=<a> table 136 * -xbuiltin[=<b>] -fbuiltin (-fno-builtin otherwise) 137 * -xchip=<c> table 138 * -xO<n> -O<n> 139 * -xtarget=<t> table 140 * -xtransition -Wtransition 141 * -YI,<dir> -nostdinc -I<dir> 142 */ 143 144 #include <ctype.h> 145 #include <err.h> 146 #include <errno.h> 147 #include <fcntl.h> 148 #include <getopt.h> 149 #include <stdio.h> 150 #include <stdlib.h> 151 #include <stdbool.h> 152 #include <string.h> 153 #include <unistd.h> 154 #include <dirent.h> 155 156 #include <sys/param.h> 157 #include <sys/stat.h> 158 #include <sys/types.h> 159 #include <sys/utsname.h> 160 #include <sys/wait.h> 161 162 #define CW_F_CXX 0x01 163 #define CW_F_SHADOW 0x02 164 #define CW_F_EXEC 0x04 165 #define CW_F_ECHO 0x08 166 #define CW_F_XLATE 0x10 167 #define CW_F_PROG 0x20 168 #define CW_F_TIME 0x40 169 170 typedef enum cw_op { 171 CW_O_NONE = 0, 172 CW_O_PREPROCESS, 173 CW_O_COMPILE, 174 CW_O_LINK 175 } cw_op_t; 176 177 struct aelist { 178 struct ae { 179 struct ae *ae_next; 180 char *ae_arg; 181 } *ael_head, *ael_tail; 182 int ael_argc; 183 }; 184 185 typedef enum { 186 GNU, 187 SUN, 188 SMATCH 189 } compiler_style_t; 190 191 typedef enum { 192 SOURCE_FILE_T_NONE, 193 SOURCE_FILE_T_C, 194 SOURCE_FILE_T_S, 195 SOURCE_FILE_T_CPP, 196 SOURCE_FILE_T_CCX, 197 SOURCE_FILE_T_I 198 } source_file_type_t; 199 200 typedef struct { 201 char *c_name; 202 char *c_path; 203 compiler_style_t c_style; 204 } cw_compiler_t; 205 206 typedef struct cw_ictx { 207 struct cw_ictx *i_next; 208 cw_compiler_t *i_compiler; 209 char *i_linker; 210 struct aelist *i_ae; 211 uint32_t i_flags; 212 int i_oldargc; 213 char **i_oldargv; 214 pid_t i_pid; 215 char *i_tmpdir; 216 char *i_stderr; 217 } cw_ictx_t; 218 219 /* 220 * Status values to indicate which Studio compiler and associated 221 * flags are being used. 222 */ 223 #define M32 0x01 /* -m32 - only on Studio 12 */ 224 #define M64 0x02 /* -m64 - only on Studio 12 */ 225 #define SS11 0x100 /* Studio 11 */ 226 #define SS12 0x200 /* Studio 12 */ 227 228 #define TRANS_ENTRY 5 229 /* 230 * Translation table definition for the -xarch= flag. The "x_arg" 231 * value is translated into the appropriate gcc flags according 232 * to the values in x_trans[n]. The x_flags indicates what compiler 233 * is being used and what flags have been set via the use of 234 * "x_arg". 235 */ 236 typedef struct xarch_table { 237 char *x_arg; 238 int x_flags; 239 char *x_trans[TRANS_ENTRY]; 240 } xarch_table_t; 241 242 /* 243 * The translation table for the -xarch= flag used in the Studio compilers. 244 */ 245 static const xarch_table_t xtbl[] = { 246 #if defined(__x86) 247 { "generic", SS11, {NULL} }, 248 { "generic64", (SS11|M64), { "-m64", "-mtune=opteron" } }, 249 { "amd64", (SS11|M64), { "-m64", "-mtune=opteron" } }, 250 { "386", SS11, { "-march=i386" } }, 251 { "pentium_pro", SS11, { "-march=pentiumpro" } }, 252 { "sse", SS11, { "-msse", "-mfpmath=sse" } }, 253 { "sse2", SS11, { "-msse2", "-mfpmath=sse" } }, 254 #endif 255 }; 256 257 static int xtbl_size = sizeof (xtbl) / sizeof (xarch_table_t); 258 259 static const char *xchip_tbl[] = { 260 #if defined(__x86) 261 "386", "-mtune=i386", NULL, 262 "486", "-mtune=i486", NULL, 263 "pentium", "-mtune=pentium", NULL, 264 "pentium_pro", "-mtune=pentiumpro", NULL, 265 #endif 266 NULL, NULL 267 }; 268 269 static const char *xtarget_tbl[] = { 270 #if defined(__x86) 271 "pentium_pro", "-march=pentiumpro", NULL, 272 #endif /* __x86 */ 273 NULL, NULL 274 }; 275 276 static void 277 nomem(void) 278 { 279 errx(1, "out of memory"); 280 } 281 282 static void 283 newae(struct aelist *ael, const char *arg) 284 { 285 struct ae *ae; 286 287 if ((ae = calloc(1, sizeof (*ae))) == NULL) 288 nomem(); 289 ae->ae_arg = strdup(arg); 290 if (ael->ael_tail == NULL) 291 ael->ael_head = ae; 292 else 293 ael->ael_tail->ae_next = ae; 294 ael->ael_tail = ae; 295 ael->ael_argc++; 296 } 297 298 static cw_ictx_t * 299 newictx(void) 300 { 301 cw_ictx_t *ctx = calloc(1, sizeof (cw_ictx_t)); 302 if (ctx) 303 if ((ctx->i_ae = calloc(1, sizeof (struct aelist))) == NULL) { 304 free(ctx); 305 return (NULL); 306 } 307 308 return (ctx); 309 } 310 311 static void 312 error(const char *arg) 313 { 314 errx(2, "error: mapping failed at or near arg '%s'", arg); 315 } 316 317 /* 318 * Add the current favourite set of warnings to the gcc invocation. 319 */ 320 static void 321 warnings(struct aelist *h) 322 { 323 static int warningsonce; 324 325 if (warningsonce++) 326 return; 327 328 /* 329 * Enable as many warnings as exist, then disable those that we never 330 * ever want. 331 */ 332 newae(h, "-Wall"); 333 newae(h, "-Wextra"); 334 } 335 336 static void 337 optim_disable(struct aelist *h, int level) 338 { 339 if (level >= 2) { 340 newae(h, "-fno-strict-aliasing"); 341 newae(h, "-fno-unit-at-a-time"); 342 newae(h, "-fno-optimize-sibling-calls"); 343 } 344 } 345 346 static void 347 Xsmode(struct aelist *h) 348 { 349 static int xsonce; 350 351 if (xsonce++) 352 return; 353 354 newae(h, "-traditional"); 355 newae(h, "-traditional-cpp"); 356 } 357 358 static void 359 usage(void) 360 { 361 extern char *__progname; 362 (void) fprintf(stderr, 363 "usage: %s [-C] [--tag arg] [--versions] --primary <compiler> " 364 "[--shadow <compiler>]... -- cflags...\n", 365 __progname); 366 (void) fprintf(stderr, "compilers take the form: name,path,style\n" 367 " - name: a unique name usable in flag specifiers\n" 368 " - path: path to the compiler binary\n" 369 " - style: the style of flags expected: either sun or gnu\n"); 370 exit(2); 371 } 372 373 static int 374 xlate_xtb(struct aelist *h, const char *xarg) 375 { 376 int i, j; 377 378 for (i = 0; i < xtbl_size; i++) { 379 if (strcmp(xtbl[i].x_arg, xarg) == 0) 380 break; 381 } 382 383 /* 384 * At the end of the table and so no matching "arg" entry 385 * found and so this must be a bad -xarch= flag. 386 */ 387 if (i == xtbl_size) 388 error(xarg); 389 390 for (j = 0; j < TRANS_ENTRY; j++) { 391 if (xtbl[i].x_trans[j] != NULL) 392 newae(h, xtbl[i].x_trans[j]); 393 else 394 break; 395 } 396 return (xtbl[i].x_flags); 397 398 } 399 400 static void 401 xlate(struct aelist *h, const char *xarg, const char **table) 402 { 403 while (*table != NULL && strcmp(xarg, *table) != 0) { 404 while (*table != NULL) 405 table++; 406 table++; 407 } 408 409 if (*table == NULL) 410 error(xarg); 411 412 table++; 413 414 while (*table != NULL) { 415 newae(h, *table); 416 table++; 417 } 418 } 419 420 /* 421 * The compiler wants the output file to end in appropriate extension. If 422 * we're generating a name from whole cloth (path == NULL), we assume that 423 * extension to be .o, otherwise we match the extension of the caller. 424 */ 425 static char * 426 discard_file_name(cw_ictx_t *ctx, const char *path) 427 { 428 char *ret, *ext; 429 char tmpl[] = "cwXXXXXX"; 430 431 if (path == NULL) { 432 ext = ".o"; 433 } else { 434 ext = strrchr(path, '.'); 435 } 436 437 /* 438 * We need absolute control over where the temporary file goes, since 439 * we rely on it for cleanup so tempnam(3C) and tmpnam(3C) are 440 * inappropriate (they use TMPDIR, preferentially). 441 * 442 * mkstemp(3C) doesn't actually help us, since the temporary file 443 * isn't used by us, only its name. 444 */ 445 if (mktemp(tmpl) == NULL) 446 nomem(); 447 448 (void) asprintf(&ret, "%s/%s%s", ctx->i_tmpdir, tmpl, 449 (ext != NULL) ? ext : ""); 450 451 if (ret == NULL) 452 nomem(); 453 454 return (ret); 455 } 456 457 static source_file_type_t 458 id_source_file(const char *path) 459 { 460 const char *ext = strrchr(path, '.'); 461 462 if ((ext == NULL) || (*(ext + 1) == '\0')) 463 return (SOURCE_FILE_T_NONE); 464 465 ext += 1; 466 467 if (strcasecmp(ext, "c") == 0) { 468 return (SOURCE_FILE_T_C); 469 } else if (strcmp(ext, "cc") == 0) { 470 return (SOURCE_FILE_T_CCX); 471 } else if (strcmp(ext, "i") == 0) { 472 return (SOURCE_FILE_T_I); 473 } else if (strcasecmp(ext, "s") == 0) { 474 return (SOURCE_FILE_T_S); 475 } else if (strcmp(ext, "cpp") == 0) { 476 return (SOURCE_FILE_T_CPP); 477 } 478 479 return (SOURCE_FILE_T_NONE); 480 481 } 482 483 static bool 484 is_asm_file(const char *path) 485 { 486 return (id_source_file(path) == SOURCE_FILE_T_S); 487 } 488 489 static void 490 do_gcc(cw_ictx_t *ctx) 491 { 492 int c; 493 int nolibc = 0; 494 int in_output = 0, seen_o = 0, src_files = 0; 495 cw_op_t op = CW_O_LINK; 496 char *model = NULL; 497 char *nameflag; 498 int mflag = 0; 499 bool seen_cstd = false, check_cstd = false; 500 501 if (ctx->i_flags & CW_F_PROG) { 502 newae(ctx->i_ae, "--version"); 503 return; 504 } 505 506 newae(ctx->i_ae, "-fident"); 507 newae(ctx->i_ae, "-finline"); 508 newae(ctx->i_ae, "-fno-inline-functions"); 509 newae(ctx->i_ae, "-fno-builtin"); 510 newae(ctx->i_ae, "-fno-asm"); 511 newae(ctx->i_ae, "-fdiagnostics-show-option"); 512 newae(ctx->i_ae, "-nodefaultlibs"); 513 514 /* 515 * This is needed because 'u' is defined 516 * under a conditional on 'sun'. Should 517 * probably just remove the conditional, 518 * or make it be dependent on '__sun'. 519 * 520 * -Dunix is also missing in enhanced ANSI mode 521 */ 522 newae(ctx->i_ae, "-D__sun"); 523 524 if (asprintf(&nameflag, "-_%s=", ctx->i_compiler->c_name) == -1) 525 nomem(); 526 527 /* 528 * Walk the argument list, translating as we go .. 529 */ 530 while (--ctx->i_oldargc > 0) { 531 char *arg = *++ctx->i_oldargv; 532 size_t arglen = strlen(arg); 533 534 if (*arg == '-') { 535 arglen--; 536 } else { 537 if (!in_output) { 538 source_file_type_t type = id_source_file(arg); 539 if (type != SOURCE_FILE_T_NONE) 540 src_files++; 541 if (type == SOURCE_FILE_T_C) 542 check_cstd = true; 543 } 544 545 /* 546 * Otherwise, filenames and partial arguments 547 * are passed through for gcc to chew on. However, 548 * output is always discarded for the secondary 549 * compiler. 550 */ 551 if ((ctx->i_flags & CW_F_SHADOW) && in_output) { 552 newae(ctx->i_ae, discard_file_name(ctx, arg)); 553 } else { 554 newae(ctx->i_ae, arg); 555 } 556 in_output = 0; 557 continue; 558 } 559 560 if (ctx->i_flags & CW_F_CXX) { 561 if (strncmp(arg, "-_g++=", 6) == 0) { 562 newae(ctx->i_ae, strchr(arg, '=') + 1); 563 continue; 564 } 565 566 if (strcmp(arg, "-xwe") == 0) { 567 /* turn warnings into errors */ 568 newae(ctx->i_ae, "-Werror"); 569 continue; 570 } 571 if (strcmp(arg, "-nolib") == 0) { 572 /* -nodefaultlibs is on by default */ 573 nolibc = 1; 574 continue; 575 } 576 } 577 578 switch ((c = arg[1])) { 579 case '_': 580 if ((strncmp(arg, nameflag, strlen(nameflag)) == 0) || 581 (strncmp(arg, "-_gcc=", 6) == 0) || 582 (strncmp(arg, "-_gnu=", 6) == 0)) { 583 newae(ctx->i_ae, strchr(arg, '=') + 1); 584 } 585 break; 586 case '#': 587 if (arglen == 1) { 588 newae(ctx->i_ae, "-v"); 589 break; 590 } 591 error(arg); 592 break; 593 case 'f': 594 if ((strcmp(arg, "-fpic") == 0) || 595 (strcmp(arg, "-fPIC") == 0)) { 596 newae(ctx->i_ae, arg); 597 break; 598 } 599 error(arg); 600 break; 601 case 'E': 602 if (arglen == 1) { 603 newae(ctx->i_ae, "-xc"); 604 newae(ctx->i_ae, arg); 605 op = CW_O_PREPROCESS; 606 nolibc = 1; 607 break; 608 } 609 error(arg); 610 break; 611 case 'c': 612 case 'S': 613 if (arglen == 1) { 614 op = CW_O_COMPILE; 615 nolibc = 1; 616 } 617 /* FALLTHROUGH */ 618 case 'C': 619 case 'H': 620 case 'p': 621 if (arglen == 1) { 622 newae(ctx->i_ae, arg); 623 break; 624 } 625 error(arg); 626 break; 627 case 'A': 628 case 'g': 629 case 'I': 630 case 'i': 631 case 'L': 632 case 'l': 633 case 'R': 634 case 'U': 635 case 'u': 636 case 'w': 637 newae(ctx->i_ae, arg); 638 break; 639 case 'o': 640 seen_o = 1; 641 if (arglen == 1) { 642 in_output = 1; 643 newae(ctx->i_ae, arg); 644 } else if (ctx->i_flags & CW_F_SHADOW) { 645 newae(ctx->i_ae, "-o"); 646 newae(ctx->i_ae, discard_file_name(ctx, arg)); 647 } else { 648 newae(ctx->i_ae, arg); 649 } 650 break; 651 case 'D': 652 newae(ctx->i_ae, arg); 653 /* 654 * XXX Clearly a hack ... do we need _KADB too? 655 */ 656 if (strcmp(arg, "-D_KERNEL") == 0 || 657 strcmp(arg, "-D_BOOT") == 0) 658 newae(ctx->i_ae, "-ffreestanding"); 659 break; 660 case 'e': 661 if (strcmp(arg, "-errtags=yes") == 0) { 662 warnings(ctx->i_ae); 663 break; 664 } 665 if (strcmp(arg, "-errwarn=%all") == 0) { 666 newae(ctx->i_ae, "-Werror"); 667 break; 668 } 669 error(arg); 670 break; 671 case 'k': 672 if (strcmp(arg, "-keeptmp") == 0) { 673 newae(ctx->i_ae, "-save-temps"); 674 break; 675 } 676 error(arg); 677 break; 678 case 'm': 679 if (strcmp(arg, "-mt") == 0) { 680 newae(ctx->i_ae, "-D_REENTRANT"); 681 break; 682 } 683 if (strcmp(arg, "-m64") == 0) { 684 newae(ctx->i_ae, "-m64"); 685 #if defined(__x86) 686 newae(ctx->i_ae, "-mtune=opteron"); 687 #endif 688 mflag |= M64; 689 break; 690 } 691 if (strcmp(arg, "-m32") == 0) { 692 newae(ctx->i_ae, "-m32"); 693 mflag |= M32; 694 break; 695 } 696 error(arg); 697 break; 698 case 'O': 699 if (arglen == 1) { 700 newae(ctx->i_ae, "-O"); 701 break; 702 } 703 error(arg); 704 break; 705 case 'P': 706 /* 707 * We could do '-E -o filename.i', but that's hard, 708 * and we don't need it for the case that's triggering 709 * this addition. We'll require the user to specify 710 * -o in the Makefile. If they don't they'll find out 711 * in a hurry. 712 */ 713 newae(ctx->i_ae, "-E"); 714 op = CW_O_PREPROCESS; 715 nolibc = 1; 716 break; 717 case 's': 718 if (strcmp(arg, "-shared") == 0) { 719 newae(ctx->i_ae, "-shared"); 720 nolibc = 1; 721 break; 722 } 723 724 if (strncmp(arg, "-std=", 4) == 0) { 725 seen_cstd = true; 726 newae(ctx->i_ae, arg); 727 break; 728 } 729 error(arg); 730 break; 731 732 case 'V': 733 if (arglen == 1) { 734 ctx->i_flags &= ~CW_F_ECHO; 735 newae(ctx->i_ae, "--version"); 736 break; 737 } 738 error(arg); 739 break; 740 case 'v': 741 if (arglen == 1) { 742 warnings(ctx->i_ae); 743 break; 744 } 745 error(arg); 746 break; 747 case 'W': 748 if (strncmp(arg, "-Wa,", 4) == 0 || 749 strncmp(arg, "-Wp,", 4) == 0 || 750 strncmp(arg, "-Wl,", 4) == 0) { 751 newae(ctx->i_ae, arg); 752 break; 753 } 754 755 #if defined(__x86) 756 if (strcmp(arg, "-Wu,-save_args") == 0) { 757 newae(ctx->i_ae, "-msave-args"); 758 break; 759 } 760 #endif /* __x86 */ 761 error(arg); 762 break; 763 case 'X': 764 if (strcmp(arg, "-Xs") == 0) { 765 Xsmode(ctx->i_ae); 766 break; 767 } 768 error(arg); 769 break; 770 case 'x': 771 if (arglen == 1) 772 error(arg); 773 switch (arg[2]) { 774 case 'a': 775 if (strncmp(arg, "-xarch=", 7) == 0) { 776 mflag |= xlate_xtb(ctx->i_ae, arg + 7); 777 break; 778 } 779 error(arg); 780 break; 781 case 'b': 782 if (strncmp(arg, "-xbuiltin=", 10) == 0) { 783 if (strcmp(arg + 10, "%all")) 784 newae(ctx->i_ae, "-fbuiltin"); 785 break; 786 } 787 error(arg); 788 break; 789 case 'c': 790 if (strncmp(arg, "-xc99=%all", 10) == 0) { 791 seen_cstd = true; 792 newae(ctx->i_ae, "-std=gnu99"); 793 break; 794 } 795 if (strncmp(arg, "-xc99=%none", 11) == 0) { 796 seen_cstd = true; 797 newae(ctx->i_ae, "-std=gnu89"); 798 break; 799 } 800 if (strncmp(arg, "-xchip=", 7) == 0) { 801 xlate(ctx->i_ae, arg + 7, xchip_tbl); 802 break; 803 } 804 805 error(arg); 806 break; 807 #if defined(__x86) 808 case 'm': 809 if (strcmp(arg, "-xmodel=kernel") == 0) { 810 newae(ctx->i_ae, "-ffreestanding"); 811 newae(ctx->i_ae, "-mno-red-zone"); 812 model = "-mcmodel=kernel"; 813 nolibc = 1; 814 break; 815 } 816 error(arg); 817 break; 818 #endif /* __x86 */ 819 case 'O': 820 if (strncmp(arg, "-xO", 3) == 0) { 821 size_t len = strlen(arg); 822 char *s = NULL; 823 int c = *(arg + 3); 824 int level; 825 826 if (len != 4 || !isdigit(c)) 827 error(arg); 828 829 level = atoi(arg + 3); 830 if (level > 5) 831 error(arg); 832 if (level >= 2) { 833 /* 834 * For gcc-3.4.x at -O2 we 835 * need to disable optimizations 836 * that break ON. 837 */ 838 optim_disable(ctx->i_ae, level); 839 /* 840 * limit -xO3 to -O2 as well. 841 */ 842 level = 2; 843 } 844 if (asprintf(&s, "-O%d", level) == -1) 845 nomem(); 846 newae(ctx->i_ae, s); 847 free(s); 848 break; 849 } 850 error(arg); 851 break; 852 case 't': 853 if (strncmp(arg, "-xtarget=", 9) == 0) { 854 xlate(ctx->i_ae, arg + 9, xtarget_tbl); 855 break; 856 } 857 error(arg); 858 break; 859 default: 860 error(arg); 861 break; 862 } 863 break; 864 case 'Y': 865 if (arglen == 1) { 866 if ((arg = *++ctx->i_oldargv) == NULL || 867 *arg == '\0') 868 error("-Y"); 869 ctx->i_oldargc--; 870 arglen = strlen(arg + 1); 871 } else { 872 arg += 2; 873 } 874 if (strncmp(arg, "I,", 2) == 0) { 875 char *s = strdup(arg); 876 s[0] = '-'; 877 s[1] = 'I'; 878 newae(ctx->i_ae, "-nostdinc"); 879 newae(ctx->i_ae, s); 880 free(s); 881 break; 882 } 883 error(arg); 884 break; 885 default: 886 error(arg); 887 break; 888 } 889 } 890 891 free(nameflag); 892 893 /* 894 * We would like to catch occurrences where we have left out a -std 895 * argument so we don't end up with the compiler's default. The use of 896 * check_cstd keys on seeing a .c file, which ensures that we don't 897 * check C++, assembly, or other files, though we should check C++ at 898 * some point. In addition, if we do a link in a single action, it will 899 * actually catch it as well because we will see a .c file. However, if 900 * we're doing a normal link of multiple files then it won't misfire. 901 */ 902 if (check_cstd && !seen_cstd) { 903 errx(2, "missing required -std= specification"); 904 } 905 906 /* 907 * When compiling multiple source files in a single invocation some 908 * compilers output objects into the current directory with 909 * predictable and conventional names. 910 * 911 * We prevent any attempt to compile multiple files at once so that 912 * any such objects created by a shadow can't escape into a later 913 * link-edit. 914 */ 915 if (src_files > 1 && op != CW_O_PREPROCESS) { 916 errx(2, "multiple source files are " 917 "allowed only with -E or -P"); 918 } 919 920 /* 921 * Make sure that we do not have any unintended interactions between 922 * the xarch options passed in and the version of the Studio compiler 923 * used. 924 */ 925 if ((mflag & (SS11|SS12)) == (SS11|SS12)) { 926 errx(2, 927 "Conflicting \"-xarch=\" flags (both Studio 11 and 12)\n"); 928 } 929 930 switch (mflag) { 931 case 0: 932 case M32: 933 case M64: 934 case SS12: 935 case SS11: 936 case (SS11|M32): 937 case (SS11|M64): 938 case (SS12|M32): 939 case (SS12|M64): 940 break; 941 default: 942 (void) fprintf(stderr, 943 "Incompatible -xarch= and/or -m32/-m64 options used.\n"); 944 exit(2); 945 } 946 947 if (ctx->i_flags & CW_F_SHADOW) { 948 if (op == CW_O_PREPROCESS) 949 exit(0); 950 else if (op == CW_O_LINK && src_files == 0) 951 exit(0); 952 } 953 954 if (model != NULL) 955 newae(ctx->i_ae, model); 956 if (!nolibc) 957 newae(ctx->i_ae, "-lc"); 958 if (!seen_o && (ctx->i_flags & CW_F_SHADOW)) { 959 newae(ctx->i_ae, "-o"); 960 newae(ctx->i_ae, discard_file_name(ctx, NULL)); 961 } 962 } 963 964 static void 965 do_smatch(cw_ictx_t *ctx) 966 { 967 if (ctx->i_flags & CW_F_PROG) { 968 newae(ctx->i_ae, "--version"); 969 return; 970 } 971 972 /* 973 * Some sources shouldn't run smatch at all. 974 */ 975 for (int i = 0; i < ctx->i_oldargc; i++) { 976 char *arg = ctx->i_oldargv[i]; 977 978 if (strcmp(arg, "-_smatch=off") == 0) { 979 ctx->i_flags &= ~(CW_F_EXEC | CW_F_ECHO); 980 return; 981 } 982 983 /* smatch can't handle asm */ 984 if ((arg[0] != '-') && is_asm_file(arg)) { 985 ctx->i_flags &= ~(CW_F_EXEC | CW_F_ECHO); 986 return; 987 } 988 } 989 990 /* 991 * smatch can handle gcc's options. 992 */ 993 do_gcc(ctx); 994 } 995 996 static void 997 do_cc(cw_ictx_t *ctx) 998 { 999 int in_output = 0, seen_o = 0, c_files = 0; 1000 cw_op_t op = CW_O_LINK; 1001 char *nameflag; 1002 1003 if (ctx->i_flags & CW_F_PROG) { 1004 newae(ctx->i_ae, "-V"); 1005 return; 1006 } 1007 1008 if (asprintf(&nameflag, "-_%s=", ctx->i_compiler->c_name) == -1) 1009 nomem(); 1010 1011 while (--ctx->i_oldargc > 0) { 1012 char *arg = *++ctx->i_oldargv; 1013 1014 if (strncmp(arg, "-_CC=", 5) == 0) { 1015 newae(ctx->i_ae, strchr(arg, '=') + 1); 1016 continue; 1017 } 1018 1019 if (*arg != '-') { 1020 if (!in_output && id_source_file(arg) != 1021 SOURCE_FILE_T_NONE) 1022 c_files++; 1023 1024 if (in_output == 0 || !(ctx->i_flags & CW_F_SHADOW)) { 1025 newae(ctx->i_ae, arg); 1026 } else { 1027 in_output = 0; 1028 newae(ctx->i_ae, discard_file_name(ctx, arg)); 1029 } 1030 continue; 1031 } 1032 switch (*(arg + 1)) { 1033 case '_': 1034 if ((strncmp(arg, nameflag, strlen(nameflag)) == 0) || 1035 (strncmp(arg, "-_cc=", 5) == 0) || 1036 (strncmp(arg, "-_sun=", 6) == 0)) { 1037 newae(ctx->i_ae, strchr(arg, '=') + 1); 1038 } 1039 break; 1040 1041 case 'V': 1042 ctx->i_flags &= ~CW_F_ECHO; 1043 newae(ctx->i_ae, arg); 1044 break; 1045 case 'o': 1046 seen_o = 1; 1047 if (strlen(arg) == 2) { 1048 in_output = 1; 1049 newae(ctx->i_ae, arg); 1050 } else if (ctx->i_flags & CW_F_SHADOW) { 1051 newae(ctx->i_ae, "-o"); 1052 newae(ctx->i_ae, discard_file_name(ctx, arg)); 1053 } else { 1054 newae(ctx->i_ae, arg); 1055 } 1056 break; 1057 case 'c': 1058 case 'S': 1059 if (strlen(arg) == 2) 1060 op = CW_O_COMPILE; 1061 newae(ctx->i_ae, arg); 1062 break; 1063 case 'E': 1064 case 'P': 1065 if (strlen(arg) == 2) 1066 op = CW_O_PREPROCESS; 1067 /*FALLTHROUGH*/ 1068 default: 1069 newae(ctx->i_ae, arg); 1070 } 1071 } 1072 1073 free(nameflag); 1074 1075 /* See the comment on this same code in do_gcc() */ 1076 if (c_files > 1 && op != CW_O_PREPROCESS) { 1077 errx(2, "multiple source files are " 1078 "allowed only with -E or -P"); 1079 } 1080 1081 if (ctx->i_flags & CW_F_SHADOW) { 1082 if (op == CW_O_PREPROCESS) 1083 exit(0); 1084 else if (op == CW_O_LINK && c_files == 0) 1085 exit(0); 1086 } 1087 1088 if (!seen_o && (ctx->i_flags & CW_F_SHADOW)) { 1089 newae(ctx->i_ae, "-o"); 1090 newae(ctx->i_ae, discard_file_name(ctx, NULL)); 1091 } 1092 } 1093 1094 static void 1095 prepctx(cw_ictx_t *ctx) 1096 { 1097 newae(ctx->i_ae, ctx->i_compiler->c_path); 1098 1099 if (ctx->i_flags & CW_F_PROG) { 1100 (void) printf("%s: %s\n", (ctx->i_flags & CW_F_SHADOW) ? 1101 "shadow" : "primary", ctx->i_compiler->c_path); 1102 (void) fflush(stdout); 1103 } 1104 1105 /* 1106 * If LD_ALTEXEC is already set, the expectation would be that that 1107 * link-editor is run, as such we need to leave it the environment 1108 * alone and let that happen. 1109 */ 1110 if ((ctx->i_linker != NULL) && (getenv("LD_ALTEXEC") == NULL)) 1111 setenv("LD_ALTEXEC", ctx->i_linker, 1); 1112 1113 if (!(ctx->i_flags & CW_F_XLATE)) 1114 return; 1115 1116 switch (ctx->i_compiler->c_style) { 1117 case SUN: 1118 do_cc(ctx); 1119 break; 1120 case GNU: 1121 do_gcc(ctx); 1122 break; 1123 case SMATCH: 1124 do_smatch(ctx); 1125 break; 1126 } 1127 } 1128 1129 static int 1130 invoke(cw_ictx_t *ctx) 1131 { 1132 char **newargv, *makeflags; 1133 int ac; 1134 struct ae *a; 1135 1136 newargv = calloc(ctx->i_ae->ael_argc + 1, sizeof (*newargv)); 1137 if (newargv == NULL) 1138 nomem(); 1139 1140 /* 1141 * Check to see if the silent make flag is present (-s), if so, do not 1142 * echo. The MAKEFLAGS environment variable is set by dmake. By 1143 * observation it appears to place short flags without any arguments 1144 * first followed by any long form flags or flags with arguments. 1145 */ 1146 makeflags = getenv("MAKEFLAGS"); 1147 if (makeflags != NULL) { 1148 size_t makeflags_len = strlen(makeflags); 1149 for (size_t i = 0; i < makeflags_len; i++) { 1150 if (makeflags[i] == 's') { 1151 ctx->i_flags &= ~CW_F_ECHO; 1152 break; 1153 } 1154 /* end of short flags */ 1155 if (makeflags[i] == ' ') 1156 break; 1157 } 1158 } 1159 1160 if (ctx->i_flags & CW_F_ECHO) 1161 (void) fprintf(stderr, "+ "); 1162 1163 for (ac = 0, a = ctx->i_ae->ael_head; a; a = a->ae_next, ac++) { 1164 newargv[ac] = a->ae_arg; 1165 if (ctx->i_flags & CW_F_ECHO) 1166 (void) fprintf(stderr, "%s ", a->ae_arg); 1167 if (a == ctx->i_ae->ael_tail) 1168 break; 1169 } 1170 1171 if (ctx->i_flags & CW_F_ECHO) { 1172 (void) fprintf(stderr, "\n"); 1173 (void) fflush(stderr); 1174 } 1175 1176 if (!(ctx->i_flags & CW_F_EXEC)) 1177 return (0); 1178 1179 /* 1180 * We must fix up the environment here so that the dependency files are 1181 * not trampled by the shadow compiler. Also take care of GCC 1182 * environment variables that will throw off gcc. This assumes a primary 1183 * gcc. 1184 */ 1185 if ((ctx->i_flags & CW_F_SHADOW) && 1186 (unsetenv("SUNPRO_DEPENDENCIES") != 0 || 1187 unsetenv("DEPENDENCIES_OUTPUT") != 0 || 1188 unsetenv("GCC_ROOT") != 0)) { 1189 (void) fprintf(stderr, "error: environment setup failed: %s\n", 1190 strerror(errno)); 1191 return (-1); 1192 } 1193 1194 (void) execv(newargv[0], newargv); 1195 warn("couldn't run %s", newargv[0]); 1196 1197 return (-1); 1198 } 1199 1200 static int 1201 reap(cw_ictx_t *ctx) 1202 { 1203 int status, ret = 0; 1204 char buf[1024]; 1205 struct stat s; 1206 struct rusage res; 1207 1208 /* 1209 * Only wait for one specific child. 1210 */ 1211 if (ctx->i_pid <= 0) 1212 return (-1); 1213 1214 do { 1215 if (wait4(ctx->i_pid, &status, 0, &res) < 0) { 1216 warn("cannot reap child"); 1217 return (-1); 1218 } 1219 if (status != 0) { 1220 if (WIFSIGNALED(status)) { 1221 ret = -WTERMSIG(status); 1222 break; 1223 } else if (WIFEXITED(status)) { 1224 ret = WEXITSTATUS(status); 1225 break; 1226 } 1227 } 1228 } while (!WIFEXITED(status) && !WIFSIGNALED(status)); 1229 if (ctx->i_flags & CW_F_TIME) { 1230 (void) fprintf(stderr, "+ %s: user time: %ld.%6.6ld " 1231 "system time: %ld.%6.6ld\n", 1232 ctx->i_compiler->c_name, 1233 res.ru_utime.tv_sec, 1234 res.ru_utime.tv_usec, 1235 res.ru_stime.tv_sec, 1236 res.ru_stime.tv_usec); 1237 } 1238 1239 if (stat(ctx->i_stderr, &s) < 0) { 1240 warn("stat failed on child cleanup"); 1241 return (-1); 1242 } 1243 if (s.st_size != 0) { 1244 FILE *f; 1245 1246 if ((f = fopen(ctx->i_stderr, "r")) != NULL) { 1247 while (fgets(buf, sizeof (buf), f)) 1248 (void) fprintf(stderr, "%s", buf); 1249 (void) fflush(stderr); 1250 (void) fclose(f); 1251 } 1252 } 1253 (void) unlink(ctx->i_stderr); 1254 free(ctx->i_stderr); 1255 1256 /* 1257 * cc returns an error code when given -V; we want that to succeed. 1258 */ 1259 if (ctx->i_flags & CW_F_PROG) 1260 return (0); 1261 1262 return (ret); 1263 } 1264 1265 static int 1266 exec_ctx(cw_ictx_t *ctx, int block) 1267 { 1268 if ((ctx->i_stderr = tempnam(ctx->i_tmpdir, "cw")) == NULL) { 1269 nomem(); 1270 return (-1); 1271 } 1272 1273 if ((ctx->i_pid = fork()) == 0) { 1274 int fd; 1275 1276 (void) fclose(stderr); 1277 if ((fd = open(ctx->i_stderr, O_WRONLY | O_CREAT | O_EXCL, 1278 0666)) < 0) { 1279 err(1, "open failed for standard error"); 1280 } 1281 if (dup2(fd, 2) < 0) { 1282 err(1, "dup2 failed for standard error"); 1283 } 1284 if (fd != 2) 1285 (void) close(fd); 1286 if (freopen("/dev/fd/2", "w", stderr) == NULL) { 1287 err(1, "freopen failed for /dev/fd/2"); 1288 } 1289 1290 prepctx(ctx); 1291 exit(invoke(ctx)); 1292 } 1293 1294 if (ctx->i_pid < 0) { 1295 err(1, "fork failed"); 1296 } 1297 1298 if (block) 1299 return (reap(ctx)); 1300 1301 return (0); 1302 } 1303 1304 static void 1305 parse_compiler(const char *spec, cw_compiler_t *compiler) 1306 { 1307 char *tspec, *token; 1308 1309 if ((tspec = strdup(spec)) == NULL) 1310 nomem(); 1311 1312 if ((token = strsep(&tspec, ",")) == NULL) 1313 errx(1, "Compiler is missing a name: %s", spec); 1314 compiler->c_name = token; 1315 1316 if ((token = strsep(&tspec, ",")) == NULL) 1317 errx(1, "Compiler is missing a path: %s", spec); 1318 compiler->c_path = token; 1319 1320 if ((token = strsep(&tspec, ",")) == NULL) 1321 errx(1, "Compiler is missing a style: %s", spec); 1322 1323 if ((strcasecmp(token, "gnu") == 0) || 1324 (strcasecmp(token, "gcc") == 0)) { 1325 compiler->c_style = GNU; 1326 } else if ((strcasecmp(token, "sun") == 0) || 1327 (strcasecmp(token, "cc") == 0)) { 1328 compiler->c_style = SUN; 1329 } else if ((strcasecmp(token, "smatch") == 0)) { 1330 compiler->c_style = SMATCH; 1331 } else { 1332 errx(1, "unknown compiler style: %s", token); 1333 } 1334 1335 if (tspec != NULL) 1336 errx(1, "Excess tokens in compiler: %s", spec); 1337 } 1338 1339 static void 1340 cleanup(cw_ictx_t *ctx) 1341 { 1342 DIR *dirp; 1343 struct dirent *dp; 1344 char buf[MAXPATHLEN]; 1345 1346 if ((dirp = opendir(ctx->i_tmpdir)) == NULL) { 1347 if (errno != ENOENT) { 1348 err(1, "couldn't open temp directory: %s", 1349 ctx->i_tmpdir); 1350 } else { 1351 return; 1352 } 1353 } 1354 1355 errno = 0; 1356 while ((dp = readdir(dirp)) != NULL) { 1357 (void) snprintf(buf, MAXPATHLEN, "%s/%s", ctx->i_tmpdir, 1358 dp->d_name); 1359 1360 if (strcmp(dp->d_name, ".") == 0 || 1361 strcmp(dp->d_name, "..") == 0) { 1362 continue; 1363 } 1364 1365 if (unlink(buf) == -1) 1366 err(1, "failed to unlink temp file: %s", dp->d_name); 1367 errno = 0; 1368 } 1369 1370 if (errno != 0) { 1371 err(1, "failed to read temporary directory: %s", 1372 ctx->i_tmpdir); 1373 } 1374 1375 (void) closedir(dirp); 1376 if (rmdir(ctx->i_tmpdir) != 0) { 1377 err(1, "failed to unlink temporary directory: %s", 1378 ctx->i_tmpdir); 1379 } 1380 } 1381 1382 int 1383 main(int argc, char **argv) 1384 { 1385 int ch; 1386 cw_compiler_t primary = { NULL, NULL, 0 }; 1387 cw_compiler_t shadows[10]; 1388 int nshadows = 0; 1389 int ret = 0; 1390 bool do_serial; 1391 bool do_exec; 1392 bool vflg = false; 1393 bool Cflg = false; 1394 bool cflg = false; 1395 bool nflg = false; 1396 bool Tflg = false; 1397 char *tmpdir; 1398 1399 cw_ictx_t *main_ctx; 1400 1401 static struct option longopts[] = { 1402 { "compiler", no_argument, NULL, 'c' }, 1403 { "linker", required_argument, NULL, 'l' }, 1404 { "noecho", no_argument, NULL, 'n' }, 1405 { "primary", required_argument, NULL, 'p' }, 1406 { "shadow", required_argument, NULL, 's' }, 1407 { "tag", required_argument, NULL, 't' }, 1408 { "time", no_argument, NULL, 'T' }, 1409 { "versions", no_argument, NULL, 'v' }, 1410 { NULL, 0, NULL, 0 }, 1411 }; 1412 1413 1414 if ((main_ctx = newictx()) == NULL) 1415 nomem(); 1416 1417 while ((ch = getopt_long(argc, argv, "C", longopts, NULL)) != -1) { 1418 switch (ch) { 1419 case 'c': 1420 cflg = true; 1421 break; 1422 case 'C': 1423 Cflg = true; 1424 break; 1425 case 'l': 1426 if ((main_ctx->i_linker = strdup(optarg)) == NULL) 1427 nomem(); 1428 break; 1429 case 'n': 1430 nflg = true; 1431 break; 1432 case 'p': 1433 if (primary.c_path != NULL) { 1434 warnx("Only one primary compiler may " 1435 "be specified"); 1436 usage(); 1437 } 1438 1439 parse_compiler(optarg, &primary); 1440 break; 1441 case 's': 1442 if (nshadows >= 10) 1443 errx(1, "May only use 10 shadows at " 1444 "the moment"); 1445 parse_compiler(optarg, &shadows[nshadows]); 1446 nshadows++; 1447 break; 1448 case 't': /* Ignored, a diagnostic in build logs only */ 1449 break; 1450 case 'T': 1451 Tflg = true; 1452 break; 1453 case 'v': 1454 vflg = true; 1455 break; 1456 default: 1457 (void) fprintf(stderr, "Did you forget '--'?\n"); 1458 usage(); 1459 } 1460 } 1461 1462 if (primary.c_path == NULL) { 1463 warnx("A primary compiler must be specified"); 1464 usage(); 1465 } 1466 1467 do_serial = getenv("CW_SHADOW_SERIAL") != NULL; 1468 do_exec = getenv("CW_NO_EXEC") == NULL; 1469 1470 /* Leave room for argv[0] */ 1471 argc -= (optind - 1); 1472 argv += (optind - 1); 1473 1474 main_ctx->i_oldargc = argc; 1475 main_ctx->i_oldargv = argv; 1476 main_ctx->i_flags = CW_F_XLATE; 1477 if (nflg == 0) 1478 main_ctx->i_flags |= CW_F_ECHO; 1479 if (do_exec) 1480 main_ctx->i_flags |= CW_F_EXEC; 1481 if (Cflg) 1482 main_ctx->i_flags |= CW_F_CXX; 1483 if (Tflg) 1484 main_ctx->i_flags |= CW_F_TIME; 1485 main_ctx->i_compiler = &primary; 1486 1487 if (cflg) { 1488 (void) fputs(primary.c_path, stdout); 1489 } 1490 1491 if (vflg) { 1492 (void) printf("cw version %s\n", CW_VERSION); 1493 (void) fflush(stdout); 1494 main_ctx->i_flags &= ~CW_F_ECHO; 1495 main_ctx->i_flags |= CW_F_PROG | CW_F_EXEC; 1496 do_serial = 1; 1497 } 1498 1499 tmpdir = getenv("TMPDIR"); 1500 if (tmpdir == NULL) 1501 tmpdir = "/tmp"; 1502 1503 if (asprintf(&main_ctx->i_tmpdir, "%s/cw.XXXXXX", tmpdir) == -1) 1504 nomem(); 1505 1506 if ((main_ctx->i_tmpdir = mkdtemp(main_ctx->i_tmpdir)) == NULL) 1507 errx(1, "failed to create temporary directory"); 1508 1509 ret |= exec_ctx(main_ctx, do_serial); 1510 1511 for (int i = 0; i < nshadows; i++) { 1512 int r; 1513 cw_ictx_t *shadow_ctx; 1514 1515 if ((shadow_ctx = newictx()) == NULL) 1516 nomem(); 1517 1518 (void) memcpy(shadow_ctx, main_ctx, sizeof (cw_ictx_t)); 1519 1520 shadow_ctx->i_flags |= CW_F_SHADOW; 1521 shadow_ctx->i_compiler = &shadows[i]; 1522 1523 r = exec_ctx(shadow_ctx, do_serial); 1524 if (r == 0) { 1525 shadow_ctx->i_next = main_ctx->i_next; 1526 main_ctx->i_next = shadow_ctx; 1527 } 1528 ret |= r; 1529 } 1530 1531 if (!do_serial) { 1532 cw_ictx_t *next = main_ctx; 1533 while (next != NULL) { 1534 cw_ictx_t *toreap = next; 1535 next = next->i_next; 1536 ret |= reap(toreap); 1537 } 1538 } 1539 1540 cleanup(main_ctx); 1541 return (ret); 1542 } 1543