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 2018, Richard Lowe.
25 */
26 /*
27 * Copyright 2010 Sun Microsystems, Inc. All rights reserved.
28 * Use is subject to license terms.
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 /* If you modify this file, you must increment CW_VERSION */
40 #define CW_VERSION "4.0"
41
42 /*
43 * -# Verbose mode
44 * -### Show compiler commands built by driver, no compilation
45 * -A<name[(tokens)]> Preprocessor predicate assertion
46 * -B<[static|dynamic]> Specify dynamic or static binding
47 * -C Prevent preprocessor from removing comments
48 * -c Compile only - produce .o files, suppress linking
49 * -cg92 Alias for -xtarget=ss1000
50 * -D<name[=token]> Associate name with token as if by #define
51 * -d[y|n] dynamic [-dy] or static [-dn] option to linker
52 * -E Compile source through preprocessor only, output to stdout
53 * -erroff=<t> Suppress warnings specified by tags t(%none, %all, <tag list>)
54 * -errtags=<a> Display messages with tags a(no, yes)
55 * -errwarn=<t> Treats warnings specified by tags t(%none, %all, <tag list>)
56 * as errors
57 * -fast Optimize using a selection of options
58 * -fd Report old-style function definitions and declarations
59 * -fnonstd Initialize floating-point hardware to non-standard preferences
60 * -fns[=<yes|no>] Select non-standard floating point mode
61 * -fprecision=<p> Set FP rounding precision mode p(single, double, extended)
62 * -fround=<r> Select the IEEE rounding mode in effect at startup
63 * -fsimple[=<n>] Select floating-point optimization preferences <n>
64 * -fsingle Use single-precision arithmetic (-Xt and -Xs modes only)
65 * -ftrap=<t> Select floating-point trapping mode in effect at startup
66 * -fstore force floating pt. values to target precision on assignment
67 * -G Build a dynamic shared library
68 * -g Compile for debugging
69 * -H Print path name of each file included during compilation
70 * -h <name> Assign <name> to generated dynamic shared library
71 * -I<dir> Add <dir> to preprocessor #include file search path
72 * -i Passed to linker to ignore any LD_LIBRARY_PATH setting
73 * -keeptmp Keep temporary files created during compilation
74 * -L<dir> Pass to linker to add <dir> to the library search path
75 * -l<name> Link with library lib<name>.a or lib<name>.so
76 * -mc Remove duplicate strings from .comment section of output files
77 * -mr Remove all strings from .comment section of output files
78 * -mr,"string" Remove all strings and append "string" to .comment section
79 * -mt Specify options needed when compiling multi-threaded code
80 * -native Find available processor, generate code accordingly
81 * -nofstore Do not force floating pt. values to target precision
82 * on assignment
83 * -norunpath Do not build in a runtime path for shared libraries
84 * -O Use default optimization level (-xO2 or -xO3. Check man page.)
85 * -o <outputfile> Set name of output file to <outputfile>
86 * -P Compile source through preprocessor only, output to .i file
87 * -p Compile for profiling with prof
88 * -Q[y|n] Emit/don't emit identification info to output file
89 * -R<dir[:dir]> Build runtime search path list into executable
90 * -S Compile and only generate assembly code (.s)
91 * -s Strip symbol table from the executable file
92 * -t Turn off duplicate symbol warnings when linking
93 * -U<name> Delete initial definition of preprocessor symbol <name>
94 * -V Report version number of each compilation phase
95 * -v Do stricter semantic checking
96 * -W<c>,<arg> Pass <arg> to specified component <c> (a,l,m,p,0,2,h,i,u)
97 * -w Suppress compiler warning messages
98 * -Xa Compile assuming ANSI C conformance, allow K & R extensions
99 * (default mode)
100 * -Xs Compile assuming (pre-ANSI) K & R C style code
101 * -Xt Compile assuming K & R conformance, allow ANSI C
102 * -xarch=<a> Specify target architecture instruction set
103 * -xbuiltin[=<b>] When profitable inline, or substitute intrinisic functions
104 * for system functions, b={%all,%none}
105 * -xCC Accept C++ style comments
106 * -xchip=<c> Specify the target processor for use by the optimizer
107 * -xcode=<c> Generate different code for forming addresses
108 * -xcrossfile[=<n>] Enable optimization and inlining across source files,
109 * n={0|1}
110 * -xe Perform only syntax/semantic checking, no code generation
111 * -xF Compile for later mapfile reordering or unused section
112 * elimination
113 * -xhelp=<f> Display on-line help information f(flags, readme, errors)
114 * -xildoff Cancel -xildon
115 * -xildon Enable use of the incremental linker, ild
116 * -xinline=[<a>,...,<a>] Attempt inlining of specified user routines,
117 * <a>={%auto,func,no%func}
118 * -xlibmieee Force IEEE 754 return values for math routines in
119 * exceptional cases
120 * -xlibmil Inline selected libm math routines for optimization
121 * -xlic_lib=sunperf Link in the Sun supplied performance libraries
122 * -xlicinfo Show license server information
123 * -xmaxopt=[off,1,2,3,4,5] maximum optimization level allowed on #pragma opt
124 * -xO<n> Generate optimized code (n={1|2|3|4|5})
125 * -xP Print prototypes for function definitions
126 * -xprofile=<p> Collect data for a profile or use a profile to optimize
127 * <p>={{collect,use}[:<path>],tcov}
128 * -xregs=<r> Control register allocation
129 * -xs Allow debugging without object (.o) files
130 * -xsb Compile for use with the WorkShop source browser
131 * -xsbfast Generate only WorkShop source browser info, no compilation
132 * -xsfpconst Represent unsuffixed floating point constants as single
133 * precision
134 * -xspace Do not do optimizations that increase code size
135 * -xstrconst Place string literals into read-only data segment
136 * -xtarget=<t> Specify target system for optimization
137 * -xtemp=<dir> Set directory for temporary files to <dir>
138 * -xtime Report the execution time for each compilation phase
139 * -xunroll=n Enable unrolling loops n times where possible
140 * -Y<c>,<dir> Specify <dir> for location of component <c> (a,l,m,p,0,h,i,u)
141 * -YA,<dir> Change default directory searched for components
142 * -YI,<dir> Change default directory searched for include files
143 * -YP,<dir> Change default directory for finding libraries files
144 * -YS,<dir> Change default directory for startup object files
145 */
146
147 /*
148 * Translation table:
149 */
150 /*
151 * -# -v
152 * -### error
153 * -A<name[(tokens)]> pass-thru
154 * -B<[static|dynamic]> pass-thru (syntax error for anything else)
155 * -C pass-thru
156 * -c pass-thru
157 * -cg92 -m32 -mcpu=v8 -mtune=supersparc (SPARC only)
158 * -D<name[=token]> pass-thru
159 * -dy or -dn -Wl,-dy or -Wl,-dn
160 * -E pass-thru
161 * -erroff=E_EMPTY_TRANSLATION_UNIT ignore
162 * -errtags=%all -Wall
163 * -errwarn=%all -Werror else -Wno-error
164 * -fast error
165 * -fd error
166 * -fnonstd error
167 * -fns[=<yes|no>] error
168 * -fprecision=<p> error
169 * -fround=<r> error
170 * -fsimple[=<n>] error
171 * -fsingle[=<n>] error
172 * -ftrap=<t> error
173 * -fstore error
174 * -G pass-thru
175 * -g pass-thru
176 * -H pass-thru
177 * -h <name> pass-thru
178 * -I<dir> pass-thru
179 * -i pass-thru
180 * -keeptmp -save-temps
181 * -L<dir> pass-thru
182 * -l<name> pass-thru
183 * -mc error
184 * -mr error
185 * -mr,"string" error
186 * -mt -D_REENTRANT
187 * -native error
188 * -nofstore error
189 * -nolib -nodefaultlibs
190 * -norunpath ignore
191 * -O -O1 (Check the man page to be certain)
192 * -o <outputfile> pass-thru
193 * -P -E -o filename.i (or error)
194 * -p pass-thru
195 * -Q[y|n] error
196 * -R<dir[:dir]> pass-thru
197 * -S pass-thru
198 * -s -Wl,-s
199 * -t -Wl,-t
200 * -U<name> pass-thru
201 * -V --version
202 * -v -Wall
203 * -Wa,<arg> pass-thru
204 * -Wp,<arg> pass-thru except -xc99=<a>
205 * -Wl,<arg> pass-thru
206 * -W{m,0,2,h,i,u> error/ignore
207 * -xmodel=kernel -ffreestanding -mcmodel=kernel -mno-red-zone
208 * -Wu,-save_args -msave-args
209 * -w pass-thru
210 * -Xa -std=iso9899:199409 or -ansi
211 * -Xt error
212 * -Xs -traditional -std=c89
213 * -xarch=<a> table
214 * -xbuiltin[=<b>] -fbuiltin (-fno-builtin otherwise)
215 * -xCC ignore
216 * -xchip=<c> table
217 * -xcode=<c> table
218 * -xcrossfile[=<n>] ignore
219 * -xe error
220 * -xF error
221 * -xhelp=<f> error
222 * -xildoff ignore
223 * -xildon ignore
224 * -xinline ignore
225 * -xlibmieee error
226 * -xlibmil error
227 * -xlic_lib=sunperf error
228 * -xmaxopt=[...] error
229 * -xO<n> -O<n>
230 * -xP error
231 * -xprofile=<p> error
232 * -xregs=<r> table
233 * -xs error
234 * -xsb error
235 * -xsbfast error
236 * -xsfpconst error
237 * -xspace ignore (-not -Os)
238 * -xstrconst ignore
239 * -xtarget=<t> table
240 * -xtemp=<dir> error
241 * -xtime error
242 * -xtransition -Wtransition
243 * -xunroll=n error
244 * -W0,-xdbggen=no%usedonly -fno-eliminate-unused-debug-symbols
245 * -fno-eliminate-unused-debug-types
246 * -Y<c>,<dir> error
247 * -YA,<dir> error
248 * -YI,<dir> -nostdinc -I<dir>
249 * -YP,<dir> error
250 * -YS,<dir> error
251 */
252
253 #include <ctype.h>
254 #include <err.h>
255 #include <errno.h>
256 #include <fcntl.h>
257 #include <getopt.h>
258 #include <stdio.h>
259 #include <stdlib.h>
260 #include <string.h>
261 #include <unistd.h>
262
263 #include <sys/param.h>
264 #include <sys/stat.h>
265 #include <sys/types.h>
266 #include <sys/utsname.h>
267 #include <sys/wait.h>
268
269 #define CW_F_CXX 0x01
270 #define CW_F_SHADOW 0x02
271 #define CW_F_EXEC 0x04
272 #define CW_F_ECHO 0x08
273 #define CW_F_XLATE 0x10
274 #define CW_F_PROG 0x20
275
276 typedef enum cw_op {
277 CW_O_NONE = 0,
278 CW_O_PREPROCESS,
279 CW_O_COMPILE,
280 CW_O_LINK
281 } cw_op_t;
282
283 struct aelist {
284 struct ae {
285 struct ae *ae_next;
286 char *ae_arg;
287 } *ael_head, *ael_tail;
288 int ael_argc;
289 };
290
291 typedef enum {
292 GNU,
293 SUN
294 } compiler_style_t;
295
296 typedef struct {
297 char *c_name;
298 char *c_path;
299 compiler_style_t c_style;
300 } cw_compiler_t;
301
302 typedef struct cw_ictx {
303 struct cw_ictx *i_next;
304 cw_compiler_t *i_compiler;
305 struct aelist *i_ae;
306 uint32_t i_flags;
307 int i_oldargc;
308 char **i_oldargv;
309 pid_t i_pid;
310 char *i_discard;
311 char *i_stderr;
312 } cw_ictx_t;
313
314 /*
315 * Status values to indicate which Studio compiler and associated
316 * flags are being used.
317 */
318 #define M32 0x01 /* -m32 - only on Studio 12 */
319 #define M64 0x02 /* -m64 - only on Studio 12 */
320 #define SS11 0x100 /* Studio 11 */
321 #define SS12 0x200 /* Studio 12 */
322
323 #define TRANS_ENTRY 5
324 /*
325 * Translation table definition for the -xarch= flag. The "x_arg"
326 * value is translated into the appropriate gcc flags according
327 * to the values in x_trans[n]. The x_flags indicates what compiler
328 * is being used and what flags have been set via the use of
329 * "x_arg".
330 */
331 typedef struct xarch_table {
332 char *x_arg;
333 int x_flags;
334 char *x_trans[TRANS_ENTRY];
335 } xarch_table_t;
336
337 /*
338 * The translation table for the -xarch= flag used in the Studio compilers.
339 */
340 static const xarch_table_t xtbl[] = {
341 #if defined(__x86)
342 { "generic", SS11, {NULL} },
343 { "generic64", (SS11|M64), { "-m64", "-mtune=opteron" } },
344 { "amd64", (SS11|M64), { "-m64", "-mtune=opteron" } },
345 { "386", SS11, { "-march=i386" } },
346 { "pentium_pro", SS11, { "-march=pentiumpro" } },
347 { "sse", SS11, { "-msse", "-mfpmath=sse" } },
348 { "sse2", SS11, { "-msse2", "-mfpmath=sse" } },
349 #elif defined(__sparc)
350 { "generic", (SS11|M32), { "-m32", "-mcpu=v8" } },
351 { "generic64", (SS11|M64), { "-m64", "-mcpu=v9" } },
352 { "v8", (SS11|M32), { "-m32", "-mcpu=v8", "-mno-v8plus" } },
353 { "v8plus", (SS11|M32), { "-m32", "-mcpu=v9", "-mv8plus" } },
354 { "v8plusa", (SS11|M32), { "-m32", "-mcpu=ultrasparc", "-mv8plus",
355 "-mvis" } },
356 { "v8plusb", (SS11|M32), { "-m32", "-mcpu=ultrasparc3", "-mv8plus",
357 "-mvis" } },
358 { "v9", (SS11|M64), { "-m64", "-mcpu=v9" } },
359 { "v9a", (SS11|M64), { "-m64", "-mcpu=ultrasparc", "-mvis" } },
360 { "v9b", (SS11|M64), { "-m64", "-mcpu=ultrasparc3", "-mvis" } },
361 { "sparc", SS12, { "-mcpu=v9", "-mv8plus" } },
362 { "sparcvis", SS12, { "-mcpu=ultrasparc", "-mvis" } },
363 { "sparcvis2", SS12, { "-mcpu=ultrasparc3", "-mvis" } }
364 #endif
365 };
366
367 static int xtbl_size = sizeof (xtbl) / sizeof (xarch_table_t);
368
369 static const char *xchip_tbl[] = {
370 #if defined(__x86)
371 "386", "-mtune=i386", NULL,
372 "486", "-mtune=i486", NULL,
373 "pentium", "-mtune=pentium", NULL,
374 "pentium_pro", "-mtune=pentiumpro", NULL,
375 #elif defined(__sparc)
376 "super", "-mtune=supersparc", NULL,
377 "ultra", "-mtune=ultrasparc", NULL,
378 "ultra3", "-mtune=ultrasparc3", NULL,
379 #endif
380 NULL, NULL
381 };
382
383 static const char *xcode_tbl[] = {
384 #if defined(__sparc)
385 "abs32", "-fno-pic", "-mcmodel=medlow", NULL,
386 "abs44", "-fno-pic", "-mcmodel=medmid", NULL,
387 "abs64", "-fno-pic", "-mcmodel=medany", NULL,
388 "pic13", "-fpic", NULL,
389 "pic32", "-fPIC", NULL,
390 #endif
391 NULL, NULL
392 };
393
394 static const char *xtarget_tbl[] = {
395 #if defined(__x86)
396 "pentium_pro", "-march=pentiumpro", NULL,
397 #endif /* __x86 */
398 NULL, NULL
399 };
400
401 static const char *xregs_tbl[] = {
402 #if defined(__sparc)
403 "appl", "-mapp-regs", NULL,
404 "no%appl", "-mno-app-regs", NULL,
405 "float", "-mfpu", NULL,
406 "no%float", "-mno-fpu", NULL,
407 #endif /* __sparc */
408 NULL, NULL
409 };
410
411 static void
412 nomem(void)
413 {
414 errx(1, "out of memory");
415 }
416
417 static void
418 newae(struct aelist *ael, const char *arg)
419 {
420 struct ae *ae;
421
422 if ((ae = calloc(sizeof (*ae), 1)) == NULL)
423 nomem();
424 ae->ae_arg = strdup(arg);
425 if (ael->ael_tail == NULL)
426 ael->ael_head = ae;
427 else
428 ael->ael_tail->ae_next = ae;
429 ael->ael_tail = ae;
430 ael->ael_argc++;
431 }
432
433 static cw_ictx_t *
434 newictx(void)
435 {
436 cw_ictx_t *ctx = calloc(sizeof (cw_ictx_t), 1);
437 if (ctx)
438 if ((ctx->i_ae = calloc(sizeof (struct aelist), 1)) == NULL) {
439 free(ctx);
440 return (NULL);
441 }
442
443 return (ctx);
444 }
445
446 static void
447 error(const char *arg)
448 {
449 errx(2, "error: mapping failed at or near arg '%s'", arg);
450 }
451
452 /*
453 * Add the current favourite set of warnings to the gcc invocation.
454 */
455 static void
456 warnings(struct aelist *h)
457 {
458 static int warningsonce;
459
460 if (warningsonce++)
461 return;
462
463 /*
464 * Enable as many warnings as exist, then disable those that we never
465 * ever want.
466 */
467 newae(h, "-Wall");
468 newae(h, "-Wextra");
469 }
470
471 static void
472 optim_disable(struct aelist *h, int level)
473 {
474 if (level >= 2) {
475 newae(h, "-fno-strict-aliasing");
476 newae(h, "-fno-unit-at-a-time");
477 newae(h, "-fno-optimize-sibling-calls");
478 }
479 }
480
481 static void
482 Xsmode(struct aelist *h)
483 {
484 static int xsonce;
485
486 if (xsonce++)
487 return;
488
489 newae(h, "-traditional");
490 newae(h, "-traditional-cpp");
491 }
492
493 static void
494 usage()
495 {
496 extern char *__progname;
497 (void) fprintf(stderr,
498 "usage: %s [-C] [--versions] --primary <compiler> "
499 "[--shadow <compiler>]... -- cflags...\n",
500 __progname);
501 (void) fprintf(stderr, "compilers take the form: name,path,style\n"
502 " - name: a unique name usable in flag specifiers\n"
503 " - path: path to the compiler binary\n"
504 " - style: the style of flags expected: either sun or gnu\n");
505 exit(2);
506 }
507
508 static int
509 xlate_xtb(struct aelist *h, const char *xarg)
510 {
511 int i, j;
512
513 for (i = 0; i < xtbl_size; i++) {
514 if (strcmp(xtbl[i].x_arg, xarg) == 0)
515 break;
516 }
517
518 /*
519 * At the end of the table and so no matching "arg" entry
520 * found and so this must be a bad -xarch= flag.
521 */
522 if (i == xtbl_size)
523 error(xarg);
524
525 for (j = 0; j < TRANS_ENTRY; j++) {
526 if (xtbl[i].x_trans[j] != NULL)
527 newae(h, xtbl[i].x_trans[j]);
528 else
529 break;
530 }
531 return (xtbl[i].x_flags);
532
533 }
534
535 static void
536 xlate(struct aelist *h, const char *xarg, const char **table)
537 {
538 while (*table != NULL && strcmp(xarg, *table) != 0) {
539 while (*table != NULL)
540 table++;
541 table++;
542 }
543
544 if (*table == NULL)
545 error(xarg);
546
547 table++;
548
549 while (*table != NULL) {
550 newae(h, *table);
551 table++;
552 }
553 }
554
555 /*
556 * The compiler wants the output file to end in appropriate extension. If
557 * we're generating a name from whole cloth (path == NULL), we assume that
558 * extension to be .o, otherwise we match the extension of the caller.
559 */
560 static char *
561 discard_file_name(const char *path)
562 {
563 char *ret, *ext, *file;
564
565 if (path == NULL) {
566 ext = ".o";
567 } else {
568 ext = strrchr(path, '.');
569 }
570
571 if ((ret = calloc(MAXPATHLEN, sizeof (char))) == NULL)
572 nomem();
573
574 if ((file = tempnam(NULL, ".cw")) == NULL)
575 nomem();
576
577 (void) strlcpy(ret, file, MAXPATHLEN);
578 if (ext != NULL)
579 (void) strlcat(ret, ext, MAXPATHLEN);
580 free(file);
581 return (ret);
582 }
583
584 static void
585 do_gcc(cw_ictx_t *ctx)
586 {
587 int c;
588 int nolibc = 0;
589 int in_output = 0, seen_o = 0, c_files = 0;
590 cw_op_t op = CW_O_LINK;
591 char *model = NULL;
592 char *nameflag;
593 int mflag = 0;
594
595 if (ctx->i_flags & CW_F_PROG) {
596 newae(ctx->i_ae, "--version");
597 return;
598 }
599
600 newae(ctx->i_ae, "-fident");
601 newae(ctx->i_ae, "-finline");
602 newae(ctx->i_ae, "-fno-inline-functions");
603 newae(ctx->i_ae, "-fno-builtin");
604 newae(ctx->i_ae, "-fno-asm");
605 newae(ctx->i_ae, "-fdiagnostics-show-option");
606 newae(ctx->i_ae, "-nodefaultlibs");
607
608 #if defined(__sparc)
609 /*
610 * The SPARC ldd and std instructions require 8-byte alignment of
611 * their address operand. gcc correctly uses them only when the
612 * ABI requires 8-byte alignment; unfortunately we have a number of
613 * pieces of buggy code that doesn't conform to the ABI. This
614 * flag makes gcc work more like Studio with -xmemalign=4.
615 */
616 newae(ctx->i_ae, "-mno-integer-ldd-std");
617 #endif
618
619 /*
620 * This is needed because 'u' is defined
621 * under a conditional on 'sun'. Should
622 * probably just remove the conditional,
623 * or make it be dependent on '__sun'.
624 *
625 * -Dunix is also missing in enhanced ANSI mode
626 */
627 newae(ctx->i_ae, "-D__sun");
628
629 if (asprintf(&nameflag, "-_%s=", ctx->i_compiler->c_name) == -1)
630 nomem();
631
632 /*
633 * Walk the argument list, translating as we go ..
634 */
635 while (--ctx->i_oldargc > 0) {
636 char *arg = *++ctx->i_oldargv;
637 size_t arglen = strlen(arg);
638
639 if (*arg == '-') {
640 arglen--;
641 } else {
642 /*
643 * Discard inline files that gcc doesn't grok
644 */
645 if (!in_output && arglen > 3 &&
646 strcmp(arg + arglen - 3, ".il") == 0)
647 continue;
648
649 if (!in_output && arglen > 2 &&
650 arg[arglen - 2] == '.' &&
651 (arg[arglen - 1] == 'S' || arg[arglen - 1] == 's' ||
652 arg[arglen - 1] == 'c' || arg[arglen - 1] == 'i'))
653 c_files++;
654
655 /*
656 * Otherwise, filenames and partial arguments
657 * are passed through for gcc to chew on. However,
658 * output is always discarded for the secondary
659 * compiler.
660 */
661 if ((ctx->i_flags & CW_F_SHADOW) && in_output) {
662 ctx->i_discard = discard_file_name(arg);
663 newae(ctx->i_ae, ctx->i_discard);
664 } else {
665 newae(ctx->i_ae, arg);
666 }
667 in_output = 0;
668 continue;
669 }
670
671 if (ctx->i_flags & CW_F_CXX) {
672 if (strncmp(arg, "-_g++=", 6) == 0) {
673 newae(ctx->i_ae, strchr(arg, '=') + 1);
674 continue;
675 }
676 if (strncmp(arg, "-compat=", 8) == 0) {
677 /* discard -compat=4 and -compat=5 */
678 continue;
679 }
680 if (strcmp(arg, "-Qoption") == 0) {
681 /* discard -Qoption and its two arguments */
682 if (ctx->i_oldargc < 3)
683 error(arg);
684 ctx->i_oldargc -= 2;
685 ctx->i_oldargv += 2;
686 continue;
687 }
688 if (strcmp(arg, "-xwe") == 0) {
689 /* turn warnings into errors */
690 newae(ctx->i_ae, "-Werror");
691 continue;
692 }
693 if (strcmp(arg, "-norunpath") == 0) {
694 /* gcc has no corresponding option */
695 continue;
696 }
697 if (strcmp(arg, "-nolib") == 0) {
698 /* -nodefaultlibs is on by default */
699 nolibc = 1;
700 continue;
701 }
702 #if defined(__sparc)
703 if (strcmp(arg, "-cg92") == 0) {
704 mflag |= xlate_xtb(ctx->i_ae, "v8");
705 xlate(ctx->i_ae, "super", xchip_tbl);
706 continue;
707 }
708 #endif /* __sparc */
709 }
710
711 switch ((c = arg[1])) {
712 case '_':
713 if ((strncmp(arg, nameflag, strlen(nameflag)) == 0) ||
714 (strncmp(arg, "-_gcc=", 6) == 0) ||
715 (strncmp(arg, "-_gnu=", 6) == 0)) {
716 newae(ctx->i_ae, strchr(arg, '=') + 1);
717 }
718 break;
719 case '#':
720 if (arglen == 1) {
721 newae(ctx->i_ae, "-v");
722 break;
723 }
724 error(arg);
725 break;
726 case 'f':
727 if ((strcmp(arg, "-fpic") == 0) ||
728 (strcmp(arg, "-fPIC") == 0)) {
729 newae(ctx->i_ae, arg);
730 break;
731 }
732 error(arg);
733 break;
734 case 'E':
735 if (arglen == 1) {
736 newae(ctx->i_ae, "-xc");
737 newae(ctx->i_ae, arg);
738 op = CW_O_PREPROCESS;
739 nolibc = 1;
740 break;
741 }
742 error(arg);
743 break;
744 case 'c':
745 case 'S':
746 if (arglen == 1) {
747 op = CW_O_COMPILE;
748 nolibc = 1;
749 }
750 /* FALLTHROUGH */
751 case 'C':
752 case 'H':
753 case 'p':
754 if (arglen == 1) {
755 newae(ctx->i_ae, arg);
756 break;
757 }
758 error(arg);
759 break;
760 case 'A':
761 case 'g':
762 case 'h':
763 case 'I':
764 case 'i':
765 case 'L':
766 case 'l':
767 case 'R':
768 case 'U':
769 case 'u':
770 case 'w':
771 newae(ctx->i_ae, arg);
772 break;
773 case 'o':
774 seen_o = 1;
775 if (arglen == 1) {
776 in_output = 1;
777 newae(ctx->i_ae, arg);
778 } else if (ctx->i_flags & CW_F_SHADOW) {
779 newae(ctx->i_ae, "-o");
780 ctx->i_discard = discard_file_name(arg);
781 newae(ctx->i_ae, ctx->i_discard);
782 } else {
783 newae(ctx->i_ae, arg);
784 }
785 break;
786 case 'D':
787 newae(ctx->i_ae, arg);
788 /*
789 * XXX Clearly a hack ... do we need _KADB too?
790 */
791 if (strcmp(arg, "-D_KERNEL") == 0 ||
792 strcmp(arg, "-D_BOOT") == 0)
793 newae(ctx->i_ae, "-ffreestanding");
794 break;
795 case 'd':
796 if (arglen == 2) {
797 if (strcmp(arg, "-dy") == 0) {
798 newae(ctx->i_ae, "-Wl,-dy");
799 break;
800 }
801 if (strcmp(arg, "-dn") == 0) {
802 newae(ctx->i_ae, "-Wl,-dn");
803 break;
804 }
805 }
806 if (strcmp(arg, "-dalign") == 0) {
807 /*
808 * -dalign forces alignment in some cases;
809 * gcc does not need any flag to do this.
810 */
811 break;
812 }
813 error(arg);
814 break;
815 case 'e':
816 if (strcmp(arg,
817 "-erroff=E_EMPTY_TRANSLATION_UNIT") == 0) {
818 /*
819 * Accept but ignore this -- gcc doesn't
820 * seem to complain about empty translation
821 * units
822 */
823 break;
824 }
825 /* XX64 -- ignore all -erroff= options, for now */
826 if (strncmp(arg, "-erroff=", 8) == 0)
827 break;
828 if (strcmp(arg, "-errtags=yes") == 0) {
829 warnings(ctx->i_ae);
830 break;
831 }
832 if (strcmp(arg, "-errwarn=%all") == 0) {
833 newae(ctx->i_ae, "-Werror");
834 break;
835 }
836 error(arg);
837 break;
838 case 'G':
839 newae(ctx->i_ae, "-shared");
840 nolibc = 1;
841 break;
842 case 'k':
843 if (strcmp(arg, "-keeptmp") == 0) {
844 newae(ctx->i_ae, "-save-temps");
845 break;
846 }
847 error(arg);
848 break;
849 case 'm':
850 if (strcmp(arg, "-mt") == 0) {
851 newae(ctx->i_ae, "-D_REENTRANT");
852 break;
853 }
854 if (strcmp(arg, "-m64") == 0) {
855 newae(ctx->i_ae, "-m64");
856 #if defined(__x86)
857 newae(ctx->i_ae, "-mtune=opteron");
858 #endif
859 mflag |= M64;
860 break;
861 }
862 if (strcmp(arg, "-m32") == 0) {
863 newae(ctx->i_ae, "-m32");
864 mflag |= M32;
865 break;
866 }
867 error(arg);
868 break;
869 case 'B': /* linker options */
870 case 'M':
871 case 'z':
872 {
873 char *opt;
874 size_t len;
875 char *s;
876
877 if (arglen == 1) {
878 opt = *++ctx->i_oldargv;
879 if (opt == NULL || *opt == '\0')
880 error(arg);
881 ctx->i_oldargc--;
882 } else {
883 opt = arg + 2;
884 }
885 len = strlen(opt) + 7;
886 if ((s = malloc(len)) == NULL)
887 nomem();
888 (void) snprintf(s, len, "-Wl,-%c%s", c, opt);
889 newae(ctx->i_ae, s);
890 free(s);
891 }
892 break;
893 case 'O':
894 if (arglen == 1) {
895 newae(ctx->i_ae, "-O");
896 break;
897 }
898 error(arg);
899 break;
900 case 'P':
901 /*
902 * We could do '-E -o filename.i', but that's hard,
903 * and we don't need it for the case that's triggering
904 * this addition. We'll require the user to specify
905 * -o in the Makefile. If they don't they'll find out
906 * in a hurry.
907 */
908 newae(ctx->i_ae, "-E");
909 op = CW_O_PREPROCESS;
910 nolibc = 1;
911 break;
912 case 's':
913 if (arglen == 1) {
914 newae(ctx->i_ae, "-Wl,-s");
915 break;
916 }
917 error(arg);
918 break;
919 case 't':
920 if (arglen == 1) {
921 newae(ctx->i_ae, "-Wl,-t");
922 break;
923 }
924 error(arg);
925 break;
926 case 'V':
927 if (arglen == 1) {
928 ctx->i_flags &= ~CW_F_ECHO;
929 newae(ctx->i_ae, "--version");
930 break;
931 }
932 error(arg);
933 break;
934 case 'v':
935 if (arglen == 1) {
936 warnings(ctx->i_ae);
937 break;
938 }
939 error(arg);
940 break;
941 case 'W':
942 if (strncmp(arg, "-Wp,-xc99", 9) == 0) {
943 /*
944 * gcc's preprocessor will accept c99
945 * regardless, so accept and ignore.
946 */
947 break;
948 }
949 if (strncmp(arg, "-Wa,", 4) == 0 ||
950 strncmp(arg, "-Wp,", 4) == 0 ||
951 strncmp(arg, "-Wl,", 4) == 0) {
952 newae(ctx->i_ae, arg);
953 break;
954 }
955 if (strcmp(arg, "-W0,-noglobal") == 0 ||
956 strcmp(arg, "-W0,-xglobalstatic") == 0) {
957 /*
958 * gcc doesn't prefix local symbols
959 * in debug mode, so this is not needed.
960 */
961 break;
962 }
963 if (strcmp(arg, "-W0,-Lt") == 0) {
964 /*
965 * Generate tests at the top of loops.
966 * There is no direct gcc equivalent, ignore.
967 */
968 break;
969 }
970 if (strcmp(arg, "-W0,-xdbggen=no%usedonly") == 0) {
971 newae(ctx->i_ae,
972 "-fno-eliminate-unused-debug-symbols");
973 newae(ctx->i_ae,
974 "-fno-eliminate-unused-debug-types");
975 break;
976 }
977 if (strcmp(arg, "-W2,-xwrap_int") == 0) {
978 /*
979 * Use the legacy behaviour (pre-SS11)
980 * for integer wrapping.
981 * gcc does not need this.
982 */
983 break;
984 }
985 if (strcmp(arg, "-Wd,-xsafe=unboundsym") == 0) {
986 /*
987 * Prevents optimizing away checks for
988 * unbound weak symbol addresses. gcc does
989 * not do this, so it's not needed.
990 */
991 break;
992 }
993 if (strncmp(arg, "-Wc,-xcode=", 11) == 0) {
994 xlate(ctx->i_ae, arg + 11, xcode_tbl);
995 break;
996 }
997 if (strncmp(arg, "-Wc,-Qiselect", 13) == 0) {
998 /*
999 * Prevents insertion of register symbols.
1000 * gcc doesn't do this, so ignore it.
1001 */
1002 break;
1003 }
1004 if (strcmp(arg, "-Wc,-Qassembler-ounrefsym=0") == 0) {
1005 /*
1006 * Prevents optimizing away of static variables.
1007 * gcc does not do this, so it's not needed.
1008 */
1009 break;
1010 }
1011 #if defined(__x86)
1012 if (strcmp(arg, "-Wu,-save_args") == 0) {
1013 newae(ctx->i_ae, "-msave-args");
1014 break;
1015 }
1016 #endif /* __x86 */
1017 error(arg);
1018 break;
1019 case 'X':
1020 if (strcmp(arg, "-Xa") == 0 ||
1021 strcmp(arg, "-Xt") == 0) {
1022 break;
1023 }
1024 if (strcmp(arg, "-Xs") == 0) {
1025 Xsmode(ctx->i_ae);
1026 break;
1027 }
1028 error(arg);
1029 break;
1030 case 'x':
1031 if (arglen == 1)
1032 error(arg);
1033 switch (arg[2]) {
1034 case 'a':
1035 if (strncmp(arg, "-xarch=", 7) == 0) {
1036 mflag |= xlate_xtb(ctx->i_ae, arg + 7);
1037 break;
1038 }
1039 error(arg);
1040 break;
1041 case 'b':
1042 if (strncmp(arg, "-xbuiltin=", 10) == 0) {
1043 if (strcmp(arg + 10, "%all"))
1044 newae(ctx->i_ae, "-fbuiltin");
1045 break;
1046 }
1047 error(arg);
1048 break;
1049 case 'C':
1050 /* Accept C++ style comments -- ignore */
1051 if (strcmp(arg, "-xCC") == 0)
1052 break;
1053 error(arg);
1054 break;
1055 case 'c':
1056 if (strncmp(arg, "-xc99=%all", 10) == 0) {
1057 newae(ctx->i_ae, "-std=gnu99");
1058 break;
1059 }
1060 if (strncmp(arg, "-xc99=%none", 11) == 0) {
1061 newae(ctx->i_ae, "-std=gnu89");
1062 break;
1063 }
1064 if (strncmp(arg, "-xchip=", 7) == 0) {
1065 xlate(ctx->i_ae, arg + 7, xchip_tbl);
1066 break;
1067 }
1068 if (strncmp(arg, "-xcode=", 7) == 0) {
1069 xlate(ctx->i_ae, arg + 7, xcode_tbl);
1070 break;
1071 }
1072 if (strncmp(arg, "-xcrossfile", 11) == 0)
1073 break;
1074 error(arg);
1075 break;
1076 case 'F':
1077 /*
1078 * Compile for mapfile reordering, or unused
1079 * section elimination, syntax can be -xF or
1080 * more complex, like -xF=%all -- ignore.
1081 */
1082 if (strncmp(arg, "-xF", 3) == 0)
1083 break;
1084 error(arg);
1085 break;
1086 case 'i':
1087 if (strncmp(arg, "-xinline", 8) == 0)
1088 /* No inlining; ignore */
1089 break;
1090 if (strcmp(arg, "-xildon") == 0 ||
1091 strcmp(arg, "-xildoff") == 0)
1092 /* No incremental linking; ignore */
1093 break;
1094 error(arg);
1095 break;
1096 #if defined(__x86)
1097 case 'm':
1098 if (strcmp(arg, "-xmodel=kernel") == 0) {
1099 newae(ctx->i_ae, "-ffreestanding");
1100 newae(ctx->i_ae, "-mno-red-zone");
1101 model = "-mcmodel=kernel";
1102 nolibc = 1;
1103 break;
1104 }
1105 error(arg);
1106 break;
1107 #endif /* __x86 */
1108 case 'O':
1109 if (strncmp(arg, "-xO", 3) == 0) {
1110 size_t len = strlen(arg);
1111 char *s = NULL;
1112 int c = *(arg + 3);
1113 int level;
1114
1115 if (len != 4 || !isdigit(c))
1116 error(arg);
1117
1118 level = atoi(arg + 3);
1119 if (level > 5)
1120 error(arg);
1121 if (level >= 2) {
1122 /*
1123 * For gcc-3.4.x at -O2 we
1124 * need to disable optimizations
1125 * that break ON.
1126 */
1127 optim_disable(ctx->i_ae, level);
1128 /*
1129 * limit -xO3 to -O2 as well.
1130 */
1131 level = 2;
1132 }
1133 if (asprintf(&s, "-O%d", level) == -1)
1134 nomem();
1135 newae(ctx->i_ae, s);
1136 free(s);
1137 break;
1138 }
1139 error(arg);
1140 break;
1141 case 'r':
1142 if (strncmp(arg, "-xregs=", 7) == 0) {
1143 xlate(ctx->i_ae, arg + 7, xregs_tbl);
1144 break;
1145 }
1146 error(arg);
1147 break;
1148 case 's':
1149 if (strcmp(arg, "-xs") == 0 ||
1150 strcmp(arg, "-xspace") == 0 ||
1151 strcmp(arg, "-xstrconst") == 0)
1152 break;
1153 error(arg);
1154 break;
1155 case 't':
1156 if (strncmp(arg, "-xtarget=", 9) == 0) {
1157 xlate(ctx->i_ae, arg + 9, xtarget_tbl);
1158 break;
1159 }
1160 error(arg);
1161 break;
1162 case 'e':
1163 case 'h':
1164 case 'l':
1165 default:
1166 error(arg);
1167 break;
1168 }
1169 break;
1170 case 'Y':
1171 if (arglen == 1) {
1172 if ((arg = *++ctx->i_oldargv) == NULL ||
1173 *arg == '\0')
1174 error("-Y");
1175 ctx->i_oldargc--;
1176 arglen = strlen(arg + 1);
1177 } else {
1178 arg += 2;
1179 }
1180 /* Just ignore -YS,... for now */
1181 if (strncmp(arg, "S,", 2) == 0)
1182 break;
1183 if (strncmp(arg, "l,", 2) == 0) {
1184 char *s = strdup(arg);
1185 s[0] = '-';
1186 s[1] = 'B';
1187 newae(ctx->i_ae, s);
1188 free(s);
1189 break;
1190 }
1191 if (strncmp(arg, "I,", 2) == 0) {
1192 char *s = strdup(arg);
1193 s[0] = '-';
1194 s[1] = 'I';
1195 newae(ctx->i_ae, "-nostdinc");
1196 newae(ctx->i_ae, s);
1197 free(s);
1198 break;
1199 }
1200 error(arg);
1201 break;
1202 case 'Q':
1203 /*
1204 * We could map -Qy into -Wl,-Qy etc.
1205 */
1206 default:
1207 error(arg);
1208 break;
1209 }
1210 }
1211
1212 free(nameflag);
1213
1214 /*
1215 * When compiling multiple source files in a single invocation some
1216 * compilers output objects into the current directory with
1217 * predictable and conventional names.
1218 *
1219 * We prevent any attempt to compile multiple files at once so that
1220 * any such objects created by a shadow can't escape into a later
1221 * link-edit.
1222 */
1223 if (c_files > 1 && op != CW_O_PREPROCESS) {
1224 errx(2, "multiple source files are "
1225 "allowed only with -E or -P");
1226 }
1227
1228 /*
1229 * Make sure that we do not have any unintended interactions between
1230 * the xarch options passed in and the version of the Studio compiler
1231 * used.
1232 */
1233 if ((mflag & (SS11|SS12)) == (SS11|SS12)) {
1234 errx(2,
1235 "Conflicting \"-xarch=\" flags (both Studio 11 and 12)\n");
1236 }
1237
1238 switch (mflag) {
1239 case 0:
1240 /* FALLTHROUGH */
1241 case M32:
1242 #if defined(__sparc)
1243 /*
1244 * Only -m32 is defined and so put in the missing xarch
1245 * translation.
1246 */
1247 newae(ctx->i_ae, "-mcpu=v8");
1248 newae(ctx->i_ae, "-mno-v8plus");
1249 #endif
1250 break;
1251 case M64:
1252 #if defined(__sparc)
1253 /*
1254 * Only -m64 is defined and so put in the missing xarch
1255 * translation.
1256 */
1257 newae(ctx->i_ae, "-mcpu=v9");
1258 #endif
1259 break;
1260 case SS12:
1261 #if defined(__sparc)
1262 /* no -m32/-m64 flag used - this is an error for sparc builds */
1263 (void) fprintf(stderr, "No -m32/-m64 flag defined\n");
1264 exit(2);
1265 #endif
1266 break;
1267 case SS11:
1268 /* FALLTHROUGH */
1269 case (SS11|M32):
1270 case (SS11|M64):
1271 break;
1272 case (SS12|M32):
1273 #if defined(__sparc)
1274 /*
1275 * Need to add in further 32 bit options because with SS12
1276 * the xarch=sparcvis option can be applied to 32 or 64
1277 * bit, and so the translatation table (xtbl) cannot handle
1278 * that.
1279 */
1280 newae(ctx->i_ae, "-mv8plus");
1281 #endif
1282 break;
1283 case (SS12|M64):
1284 break;
1285 default:
1286 (void) fprintf(stderr,
1287 "Incompatible -xarch= and/or -m32/-m64 options used.\n");
1288 exit(2);
1289 }
1290
1291 if (ctx->i_flags & CW_F_SHADOW) {
1292 if (op == CW_O_PREPROCESS)
1293 exit(0);
1294 else if (op == CW_O_LINK && c_files == 0)
1295 exit(0);
1296 }
1297
1298 if (model != NULL)
1299 newae(ctx->i_ae, model);
1300 if (!nolibc)
1301 newae(ctx->i_ae, "-lc");
1302 if (!seen_o && (ctx->i_flags & CW_F_SHADOW)) {
1303 ctx->i_discard = discard_file_name(NULL);
1304 newae(ctx->i_ae, "-o");
1305 newae(ctx->i_ae, ctx->i_discard);
1306 }
1307 }
1308
1309 static void
1310 do_cc(cw_ictx_t *ctx)
1311 {
1312 int in_output = 0, seen_o = 0, c_files = 0;
1313 cw_op_t op = CW_O_LINK;
1314 char *nameflag;
1315
1316 if (ctx->i_flags & CW_F_PROG) {
1317 newae(ctx->i_ae, "-V");
1318 return;
1319 }
1320
1321 if (asprintf(&nameflag, "-_%s=", ctx->i_compiler->c_name) == -1)
1322 nomem();
1323
1324 while (--ctx->i_oldargc > 0) {
1325 char *arg = *++ctx->i_oldargv;
1326 size_t arglen = strlen(arg);
1327
1328 if (strncmp(arg, "-_CC=", 5) == 0) {
1329 newae(ctx->i_ae, strchr(arg, '=') + 1);
1330 continue;
1331 }
1332
1333 if (*arg != '-') {
1334 if (!in_output && arglen > 2 &&
1335 arg[arglen - 2] == '.' &&
1336 (arg[arglen - 1] == 'S' || arg[arglen - 1] == 's' ||
1337 arg[arglen - 1] == 'c' || arg[arglen - 1] == 'i'))
1338 c_files++;
1339
1340 if (in_output == 0 || !(ctx->i_flags & CW_F_SHADOW)) {
1341 newae(ctx->i_ae, arg);
1342 } else {
1343 in_output = 0;
1344 ctx->i_discard = discard_file_name(arg);
1345 newae(ctx->i_ae, ctx->i_discard);
1346 }
1347 continue;
1348 }
1349 switch (*(arg + 1)) {
1350 case '_':
1351 if ((strncmp(arg, nameflag, strlen(nameflag)) == 0) ||
1352 (strncmp(arg, "-_cc=", 5) == 0) ||
1353 (strncmp(arg, "-_sun=", 6) == 0)) {
1354 newae(ctx->i_ae, strchr(arg, '=') + 1);
1355 }
1356 break;
1357
1358 case 'V':
1359 ctx->i_flags &= ~CW_F_ECHO;
1360 newae(ctx->i_ae, arg);
1361 break;
1362 case 'o':
1363 seen_o = 1;
1364 if (strlen(arg) == 2) {
1365 in_output = 1;
1366 newae(ctx->i_ae, arg);
1367 } else if (ctx->i_flags & CW_F_SHADOW) {
1368 newae(ctx->i_ae, "-o");
1369 ctx->i_discard = discard_file_name(arg);
1370 newae(ctx->i_ae, ctx->i_discard);
1371 } else {
1372 newae(ctx->i_ae, arg);
1373 }
1374 break;
1375 case 'c':
1376 case 'S':
1377 if (strlen(arg) == 2)
1378 op = CW_O_COMPILE;
1379 newae(ctx->i_ae, arg);
1380 break;
1381 case 'E':
1382 case 'P':
1383 if (strlen(arg) == 2)
1384 op = CW_O_PREPROCESS;
1385 /*FALLTHROUGH*/
1386 default:
1387 newae(ctx->i_ae, arg);
1388 }
1389 }
1390
1391 free(nameflag);
1392
1393 /* See the comment on this same code in do_gcc() */
1394 if (c_files > 1 && op != CW_O_PREPROCESS) {
1395 errx(2, "multiple source files are "
1396 "allowed only with -E or -P");
1397 }
1398
1399 if (ctx->i_flags & CW_F_SHADOW) {
1400 if (op == CW_O_PREPROCESS)
1401 exit(0);
1402 else if (op == CW_O_LINK && c_files == 0)
1403 exit(0);
1404 }
1405
1406 if (!seen_o && (ctx->i_flags & CW_F_SHADOW)) {
1407 newae(ctx->i_ae, "-o");
1408 ctx->i_discard = discard_file_name(NULL);
1409 newae(ctx->i_ae, ctx->i_discard);
1410 }
1411 }
1412
1413 static void
1414 prepctx(cw_ictx_t *ctx)
1415 {
1416 newae(ctx->i_ae, ctx->i_compiler->c_path);
1417
1418 if (ctx->i_flags & CW_F_PROG) {
1419 (void) printf("%s: %s\n", (ctx->i_flags & CW_F_SHADOW) ?
1420 "shadow" : "primary", ctx->i_compiler->c_path);
1421 (void) fflush(stdout);
1422 }
1423
1424 if (!(ctx->i_flags & CW_F_XLATE))
1425 return;
1426
1427 switch (ctx->i_compiler->c_style) {
1428 case SUN:
1429 do_cc(ctx);
1430 break;
1431 case GNU:
1432 do_gcc(ctx);
1433 break;
1434 }
1435 }
1436
1437 static int
1438 invoke(cw_ictx_t *ctx)
1439 {
1440 char **newargv;
1441 int ac;
1442 struct ae *a;
1443
1444 if ((newargv = calloc(sizeof (*newargv), ctx->i_ae->ael_argc + 1)) ==
1445 NULL)
1446 nomem();
1447
1448 if (ctx->i_flags & CW_F_ECHO)
1449 (void) fprintf(stderr, "+ ");
1450
1451 for (ac = 0, a = ctx->i_ae->ael_head; a; a = a->ae_next, ac++) {
1452 newargv[ac] = a->ae_arg;
1453 if (ctx->i_flags & CW_F_ECHO)
1454 (void) fprintf(stderr, "%s ", a->ae_arg);
1455 if (a == ctx->i_ae->ael_tail)
1456 break;
1457 }
1458
1459 if (ctx->i_flags & CW_F_ECHO) {
1460 (void) fprintf(stderr, "\n");
1461 (void) fflush(stderr);
1462 }
1463
1464 if (!(ctx->i_flags & CW_F_EXEC))
1465 return (0);
1466
1467 /*
1468 * We must fix up the environment here so that the dependency files are
1469 * not trampled by the shadow compiler. Also take care of GCC
1470 * environment variables that will throw off gcc. This assumes a primary
1471 * gcc.
1472 */
1473 if ((ctx->i_flags & CW_F_SHADOW) &&
1474 (unsetenv("SUNPRO_DEPENDENCIES") != 0 ||
1475 unsetenv("DEPENDENCIES_OUTPUT") != 0 ||
1476 unsetenv("GCC_ROOT") != 0)) {
1477 (void) fprintf(stderr, "error: environment setup failed: %s\n",
1478 strerror(errno));
1479 return (-1);
1480 }
1481
1482 (void) execv(newargv[0], newargv);
1483 warn("couldn't run %s", newargv[0]);
1484
1485 return (-1);
1486 }
1487
1488 static int
1489 reap(cw_ictx_t *ctx)
1490 {
1491 int status, ret = 0;
1492 char buf[1024];
1493 struct stat s;
1494
1495 /*
1496 * Only wait for one specific child.
1497 */
1498 if (ctx->i_pid <= 0)
1499 return (-1);
1500
1501 do {
1502 if (waitpid(ctx->i_pid, &status, 0) < 0) {
1503 warn("cannot reap child");
1504 return (-1);
1505 }
1506 if (status != 0) {
1507 if (WIFSIGNALED(status)) {
1508 ret = -WTERMSIG(status);
1509 break;
1510 } else if (WIFEXITED(status)) {
1511 ret = WEXITSTATUS(status);
1512 break;
1513 }
1514 }
1515 } while (!WIFEXITED(status) && !WIFSIGNALED(status));
1516
1517 (void) unlink(ctx->i_discard);
1518 free(ctx->i_discard);
1519
1520 if (stat(ctx->i_stderr, &s) < 0) {
1521 warn("stat failed on child cleanup");
1522 return (-1);
1523 }
1524 if (s.st_size != 0) {
1525 FILE *f;
1526
1527 if ((f = fopen(ctx->i_stderr, "r")) != NULL) {
1528 while (fgets(buf, sizeof (buf), f))
1529 (void) fprintf(stderr, "%s", buf);
1530 (void) fflush(stderr);
1531 (void) fclose(f);
1532 }
1533 }
1534 (void) unlink(ctx->i_stderr);
1535 free(ctx->i_stderr);
1536
1537 /*
1538 * cc returns an error code when given -V; we want that to succeed.
1539 */
1540 if (ctx->i_flags & CW_F_PROG)
1541 return (0);
1542
1543 return (ret);
1544 }
1545
1546 static int
1547 exec_ctx(cw_ictx_t *ctx, int block)
1548 {
1549 if ((ctx->i_stderr = tempnam(NULL, ".cw")) == NULL) {
1550 nomem();
1551 return (-1);
1552 }
1553
1554 if ((ctx->i_pid = fork()) == 0) {
1555 int fd;
1556
1557 (void) fclose(stderr);
1558 if ((fd = open(ctx->i_stderr, O_WRONLY | O_CREAT | O_EXCL,
1559 0666)) < 0) {
1560 err(1, "open failed for standard error");
1561 }
1562 if (dup2(fd, 2) < 0) {
1563 err(1, "dup2 failed for standard error");
1564 }
1565 if (fd != 2)
1566 (void) close(fd);
1567 if (freopen("/dev/fd/2", "w", stderr) == NULL) {
1568 err(1, "freopen failed for /dev/fd/2");
1569 }
1570
1571 prepctx(ctx);
1572 exit(invoke(ctx));
1573 }
1574
1575 if (ctx->i_pid < 0) {
1576 err(1, "fork failed");
1577 }
1578
1579 if (block)
1580 return (reap(ctx));
1581
1582 return (0);
1583 }
1584
1585 static void
1586 parse_compiler(const char *spec, cw_compiler_t *compiler)
1587 {
1588 char *tspec, *token;
1589
1590 if ((tspec = strdup(spec)) == NULL)
1591 nomem();
1592
1593 if ((token = strsep(&tspec, ",")) == NULL)
1594 errx(1, "Compiler is missing a name: %s", spec);
1595 compiler->c_name = token;
1596
1597 if ((token = strsep(&tspec, ",")) == NULL)
1598 errx(1, "Compiler is missing a path: %s", spec);
1599 compiler->c_path = token;
1600
1601 if ((token = strsep(&tspec, ",")) == NULL)
1602 errx(1, "Compiler is missing a style: %s", spec);
1603
1604 if ((strcasecmp(token, "gnu") == 0) ||
1605 (strcasecmp(token, "gcc") == 0))
1606 compiler->c_style = GNU;
1607 else if ((strcasecmp(token, "sun") == 0) ||
1608 (strcasecmp(token, "cc") == 0))
1609 compiler->c_style = SUN;
1610 else
1611 errx(1, "unknown compiler style: %s", token);
1612
1613 if (tspec != NULL)
1614 errx(1, "Excess tokens in compiler: %s", spec);
1615 }
1616
1617 int
1618 main(int argc, char **argv)
1619 {
1620 int ch;
1621 cw_compiler_t primary = { NULL, NULL, 0 };
1622 cw_compiler_t shadows[10];
1623 int nshadows = 0;
1624 int ret = 0;
1625 boolean_t do_serial = B_FALSE;
1626 boolean_t do_exec = B_FALSE;
1627 boolean_t vflg = B_FALSE;
1628 boolean_t Cflg = B_FALSE;
1629 boolean_t cflg = B_FALSE;
1630 boolean_t nflg = B_FALSE;
1631
1632 cw_ictx_t *main_ctx;
1633
1634 static struct option longopts[] = {
1635 { "compiler", no_argument, NULL, 'c' },
1636 { "noecho", no_argument, NULL, 'n' },
1637 { "primary", required_argument, NULL, 'p' },
1638 { "shadow", required_argument, NULL, 's' },
1639 { "versions", no_argument, NULL, 'v' },
1640 { NULL, 0, NULL, 0 },
1641 };
1642
1643
1644 if ((main_ctx = newictx()) == NULL)
1645 nomem();
1646
1647 while ((ch = getopt_long(argc, argv, "C", longopts, NULL)) != -1) {
1648 switch (ch) {
1649 case 'c':
1650 cflg = B_TRUE;
1651 break;
1652 case 'C':
1653 Cflg = B_TRUE;
1654 break;
1655 case 'n':
1656 nflg = B_TRUE;
1657 break;
1658 case 'p':
1659 if (primary.c_path != NULL) {
1660 warnx("Only one primary compiler may "
1661 "be specified");
1662 usage();
1663 }
1664
1665 parse_compiler(optarg, &primary);
1666 break;
1667 case 's':
1668 if (nshadows >= 10)
1669 errx(1, "May only use 10 shadows at "
1670 "the moment");
1671 parse_compiler(optarg, &shadows[nshadows]);
1672 nshadows++;
1673 break;
1674 case 'v':
1675 vflg = B_TRUE;
1676 break;
1677 default:
1678 (void) fprintf(stderr, "Did you forget '--'?\n");
1679 usage();
1680 }
1681 }
1682
1683 if (primary.c_path == NULL) {
1684 warnx("A primary compiler must be specified");
1685 usage();
1686 }
1687
1688 do_serial = (getenv("CW_SHADOW_SERIAL") == NULL) ? B_FALSE : B_TRUE;
1689 do_exec = (getenv("CW_NO_EXEC") == NULL) ? B_TRUE : B_FALSE;
1690
1691 /* Leave room for argv[0] */
1692 argc -= (optind - 1);
1693 argv += (optind - 1);
1694
1695 main_ctx->i_oldargc = argc;
1696 main_ctx->i_oldargv = argv;
1697 main_ctx->i_flags = CW_F_XLATE;
1698 if (nflg == 0)
1699 main_ctx->i_flags |= CW_F_ECHO;
1700 if (do_exec)
1701 main_ctx->i_flags |= CW_F_EXEC;
1702 if (Cflg)
1703 main_ctx->i_flags |= CW_F_CXX;
1704 main_ctx->i_compiler = &primary;
1705
1706 if (cflg) {
1707 (void) fputs(primary.c_path, stdout);
1708 }
1709
1710 if (vflg) {
1711 (void) printf("cw version %s\n", CW_VERSION);
1712 (void) fflush(stdout);
1713 main_ctx->i_flags &= ~CW_F_ECHO;
1714 main_ctx->i_flags |= CW_F_PROG | CW_F_EXEC;
1715 do_serial = 1;
1716 }
1717
1718 ret |= exec_ctx(main_ctx, do_serial);
1719
1720 for (int i = 0; i < nshadows; i++) {
1721 int r;
1722 cw_ictx_t *shadow_ctx;
1723
1724 if ((shadow_ctx = newictx()) == NULL)
1725 nomem();
1726
1727 memcpy(shadow_ctx, main_ctx, sizeof (cw_ictx_t));
1728
1729 shadow_ctx->i_flags |= CW_F_SHADOW;
1730 shadow_ctx->i_compiler = &shadows[i];
1731
1732 r = exec_ctx(shadow_ctx, do_serial);
1733 if (r == 0) {
1734 shadow_ctx->i_next = main_ctx->i_next;
1735 main_ctx->i_next = shadow_ctx;
1736 }
1737 ret |= r;
1738 }
1739
1740 if (!do_serial) {
1741 cw_ictx_t *next = main_ctx;
1742 while (next != NULL) {
1743 cw_ictx_t *toreap = next;
1744 next = next->i_next;
1745 ret |= reap(toreap);
1746 }
1747 }
1748
1749 return (ret);
1750 }