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