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