Print this page
9128 cw(1onbld) should be able to run multiple shadows
9129 file-locking tests shouldn't build multiple source files in one compiler invocation
9130 DTrace tst.gcc.d isn't useful
9132 cw(1onbld) shouldn't shadow pure preprocessing
Reviewed by: Toomas Soome <tsoome@me.com>
Reviewed? by: Yuri Pankov <yuripv@yuripv.net>
Reviewed? by: Robert Mustacchi <rm@joyent.com>
Reviewed? by: Jason King <jason.king@joyent.com>

@@ -1,5 +1,6 @@
+
 /*
  * CDDL HEADER START
  *
  * The contents of this file are subject to the terms of the
  * Common Development and Distribution License (the "License").

@@ -18,11 +19,11 @@
  *
  * CDDL HEADER END
  */
 
 /*
- * Copyright 2011, Richard Lowe.
+ * Copyright 2018, Richard Lowe.
  */
 /*
  * Copyright 2010 Sun Microsystems, Inc.  All rights reserved.
  * Use is subject to license terms.
  */

@@ -34,11 +35,11 @@
  * Since the translation is inexact, this is something of a work-in-progress.
  *
  */
 
 /* If you modify this file, you must increment CW_VERSION */
-#define CW_VERSION      "1.30"
+#define CW_VERSION      "2.0"
 
 /*
  * -#           Verbose mode
  * -###         Show compiler commands built by driver, no compilation
  * -A<name[(tokens)]>   Preprocessor predicate assertion

@@ -289,55 +290,33 @@
  * -YI,<dir>                    -nostdinc -I<dir>
  * -YP,<dir>                    error
  * -YS,<dir>                    error
  */
 
-#include <stdio.h>
-#include <sys/types.h>
-#include <unistd.h>
-#include <string.h>
-#include <stdlib.h>
 #include <ctype.h>
-#include <fcntl.h>
+#include <err.h>
 #include <errno.h>
-#include <stdarg.h>
-#include <sys/utsname.h>
+#include <fcntl.h>
+#include <getopt.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+
 #include <sys/param.h>
-#include <sys/isa_defs.h>
-#include <sys/wait.h>
 #include <sys/stat.h>
+#include <sys/types.h>
+#include <sys/utsname.h>
+#include <sys/wait.h>
 
 #define CW_F_CXX        0x01
 #define CW_F_SHADOW     0x02
 #define CW_F_EXEC       0x04
 #define CW_F_ECHO       0x08
 #define CW_F_XLATE      0x10
 #define CW_F_PROG       0x20
 
-typedef enum cw_compiler {
-        CW_C_CC = 0,
-        CW_C_GCC
-} cw_compiler_t;
-
-static const char *cmds[] = {
-        "cc", "CC",
-        "gcc", "g++"
-};
-
-static char default_dir[2][MAXPATHLEN] = {
-        DEFAULT_CC_DIR,
-        DEFAULT_GCC_DIR,
-};
-
-#define CC(ctx) \
-        (((ctx)->i_flags & CW_F_SHADOW) ? \
-            ((ctx)->i_compiler == CW_C_CC ? CW_C_GCC : CW_C_CC) : \
-            (ctx)->i_compiler)
-
-#define CIDX(compiler, flags)   \
-        ((int)(compiler) << 1) + ((flags) & CW_F_CXX ? 1 : 0)
-
 typedef enum cw_op {
         CW_O_NONE = 0,
         CW_O_PREPROCESS,
         CW_O_COMPILE,
         CW_O_LINK

@@ -349,12 +328,23 @@
                 char *ae_arg;
         } *ael_head, *ael_tail;
         int ael_argc;
 };
 
+typedef enum {
+        GNU,
+        SUN
+} compiler_style_t;
+
+typedef struct {
+        char *c_name;
+        char *c_path;
+        compiler_style_t c_style;
+} cw_compiler_t;
+
 typedef struct cw_ictx {
-        cw_compiler_t   i_compiler;
+        cw_compiler_t   *i_compiler;
         struct aelist   *i_ae;
         uint32_t        i_flags;
         int             i_oldargc;
         char            **i_oldargv;
         pid_t           i_pid;

@@ -388,11 +378,11 @@
 /*
  * The translation table for the -xarch= flag used in the Studio compilers.
  */
 static const xarch_table_t xtbl[] = {
 #if defined(__x86)
-        { "generic",    SS11 },
+        { "generic",    SS11, {NULL} },
         { "generic64",  (SS11|M64), { "-m64", "-mtune=opteron" } },
         { "amd64",      (SS11|M64), { "-m64", "-mtune=opteron" } },
         { "386",        SS11,   { "-march=i386" } },
         { "pentium_pro", SS11,  { "-march=pentiumpro" } },
         { "sse",        SS11, { "-msse", "-mfpmath=sse" } },

@@ -415,12 +405,10 @@
 #endif
 };
 
 static int xtbl_size = sizeof (xtbl) / sizeof (xarch_table_t);
 
-static const char *progname;
-
 static const char *xchip_tbl[] = {
 #if defined(__x86)
         "386",          "-mtune=i386", NULL,
         "486",          "-mtune=i486", NULL,
         "pentium",      "-mtune=pentium", NULL,

@@ -462,27 +450,11 @@
 };
 
 static void
 nomem(void)
 {
-        (void) fprintf(stderr, "%s: error: out of memory\n", progname);
-        exit(1);
-}
-
-static void
-cw_perror(const char *fmt, ...)
-{
-        va_list ap;
-        int saved_errno = errno;
-
-        (void) fprintf(stderr, "%s: error: ", progname);
-
-        va_start(ap, fmt);
-        (void) vfprintf(stderr, fmt, ap);
-        va_end(ap);
-
-        (void) fprintf(stderr, " (%s)\n", strerror(saved_errno));
+        errx(1, "out of memory");
 }
 
 static void
 newae(struct aelist *ael, const char *arg)
 {

@@ -513,13 +485,11 @@
 }
 
 static void
 error(const char *arg)
 {
-        (void) fprintf(stderr,
-            "%s: error: mapping failed at or near arg '%s'\n", progname, arg);
-        exit(2);
+        errx(2, "error: mapping failed at or near arg '%s'\n", arg);
 }
 
 /*
  * Add the current favourite set of warnings to the gcc invocation.
  */

@@ -549,11 +519,11 @@
         }
 }
 
 /* ARGSUSED */
 static void
-Xamode(struct aelist *h)
+Xamode(struct aelist __unused *h)
 {
 }
 
 static void
 Xcmode(struct aelist *h)

@@ -580,13 +550,19 @@
 }
 
 static void
 usage()
 {
+        extern char *__progname;
         (void) fprintf(stderr,
-            "usage: %s { -_cc | -_gcc | -_CC | -_g++ } [ -_compiler | ... ]\n",
-            progname);
+            "usage: %s [-C] [--versions] --primary <compiler> "
+            "[--shadow <compiler>]... -- cflags...\n",
+            __progname);
+        (void) fprintf(stderr, "compilers take the form: name,path,style\n"
+            " - name: a unique name usable in flag specifiers\n"
+            " - path: path to the compiler binary\n"
+            " - style: the style of flags expected: either sun or gnu\n");
         exit(2);
 }
 
 static int
 xlate_xtb(struct aelist *h, const char *xarg)

@@ -641,10 +617,11 @@
         int c;
         int pic = 0, nolibc = 0;
         int in_output = 0, seen_o = 0, c_files = 0;
         cw_op_t op = CW_O_LINK;
         char *model = NULL;
+        char *nameflag;
         int     mflag = 0;
 
         if (ctx->i_flags & CW_F_PROG) {
                 newae(ctx->i_ae, "--version");
                 return;

@@ -677,14 +654,16 @@
          *
          * -Dunix is also missing in enhanced ANSI mode
          */
         newae(ctx->i_ae, "-D__sun");
 
+        if (asprintf(&nameflag, "-_%s=", ctx->i_compiler->c_name) == -1)
+                nomem();
+
         /*
          * Walk the argument list, translating as we go ..
          */
-
         while (--ctx->i_oldargc > 0) {
                 char *arg = *++ctx->i_oldargv;
                 size_t arglen = strlen(arg);
 
                 if (*arg == '-') {

@@ -716,10 +695,14 @@
                         in_output = 0;
                         continue;
                 }
 
                 if (ctx->i_flags & CW_F_CXX) {
+                        if (strncmp(arg, "-_g++=", 6) == 0) {
+                                newae(ctx->i_ae, strchr(arg, '=') + 1);
+                                continue;
+                        }
                         if (strncmp(arg, "-compat=", 8) == 0) {
                                 /* discard -compat=4 and -compat=5 */
                                 continue;
                         }
                         if (strcmp(arg, "-Qoption") == 0) {

@@ -770,20 +753,15 @@
 #endif  /* __sparc */
                 }
 
                 switch ((c = arg[1])) {
                 case '_':
-                        if (strcmp(arg, "-_noecho") == 0)
-                                ctx->i_flags &= ~CW_F_ECHO;
-                        else if (strncmp(arg, "-_cc=", 5) == 0 ||
-                            strncmp(arg, "-_CC=", 5) == 0)
-                                /* EMPTY */;
-                        else if (strncmp(arg, "-_gcc=", 6) == 0 ||
-                            strncmp(arg, "-_g++=", 6) == 0)
-                                newae(ctx->i_ae, arg + 6);
-                        else
-                                error(arg);
+                        if ((strncmp(arg, nameflag, strlen(nameflag)) == 0) ||
+                            (strncmp(arg, "-_gcc=", 6) == 0) ||
+                            (strncmp(arg, "-_gnu=", 6) == 0)) {
+                                newae(ctx->i_ae, strchr(arg, '=') + 1);
+                        }
                         break;
                 case '#':
                         if (arglen == 1) {
                                 newae(ctx->i_ae, "-v");
                                 break;

@@ -1421,26 +1399,26 @@
                         error(arg);
                         break;
                 }
         }
 
+        free(nameflag);
+
         if (c_files > 1 && (ctx->i_flags & CW_F_SHADOW) &&
             op != CW_O_PREPROCESS) {
-                (void) fprintf(stderr, "%s: error: multiple source files are "
-                    "allowed only with -E or -P\n", progname);
-                exit(2);
+                errx(2, "multiple source files are "
+                    "allowed only with -E or -P");
         }
 
         /*
          * Make sure that we do not have any unintended interactions between
          * the xarch options passed in and the version of the Studio compiler
          * used.
          */
         if ((mflag & (SS11|SS12)) == (SS11|SS12)) {
-                (void) fprintf(stderr,
+                errx(2,
                     "Conflicting \"-xarch=\" flags (both Studio 11 and 12)\n");
-                exit(2);
         }
 
         switch (mflag) {
         case 0:
                 /* FALLTHROUGH */

@@ -1491,11 +1469,13 @@
         default:
                 (void) fprintf(stderr,
                     "Incompatible -xarch= and/or -m32/-m64 options used.\n");
                 exit(2);
         }
-        if (op == CW_O_LINK && (ctx->i_flags & CW_F_SHADOW))
+
+        if ((op == CW_O_LINK || op == CW_O_PREPROCESS) &&
+            (ctx->i_flags & CW_F_SHADOW))
                 exit(0);
 
         if (model && !pic)
                 newae(ctx->i_ae, model);
         if (!nolibc)

@@ -1509,19 +1489,28 @@
 static void
 do_cc(cw_ictx_t *ctx)
 {
         int in_output = 0, seen_o = 0;
         cw_op_t op = CW_O_LINK;
+        char *nameflag;
 
         if (ctx->i_flags & CW_F_PROG) {
                 newae(ctx->i_ae, "-V");
                 return;
         }
 
+        if (asprintf(&nameflag, "-_%s=", ctx->i_compiler->c_name) == -1)
+                nomem();
+
         while (--ctx->i_oldargc > 0) {
                 char *arg = *++ctx->i_oldargv;
 
+                if (strncmp(arg, "-_CC=", 5) == 0) {
+                        newae(ctx->i_ae, strchr(arg, '=') + 1);
+                        continue;
+                }
+
                 if (*arg != '-') {
                         if (in_output == 0 || !(ctx->i_flags & CW_F_SHADOW)) {
                                 newae(ctx->i_ae, arg);
                         } else {
                                 in_output = 0;

@@ -1529,23 +1518,17 @@
                         }
                         continue;
                 }
                 switch (*(arg + 1)) {
                 case '_':
-                        if (strcmp(arg, "-_noecho") == 0) {
-                                ctx->i_flags &= ~CW_F_ECHO;
-                        } else if (strncmp(arg, "-_cc=", 5) == 0 ||
-                            strncmp(arg, "-_CC=", 5) == 0) {
-                                newae(ctx->i_ae, arg + 5);
-                        } else if (strncmp(arg, "-_gcc=", 6) != 0 &&
-                            strncmp(arg, "-_g++=", 6) != 0) {
-                                (void) fprintf(stderr,
-                                    "%s: invalid argument '%s'\n", progname,
-                                    arg);
-                                exit(2);
+                        if ((strncmp(arg, nameflag, strlen(nameflag)) == 0) ||
+                            (strncmp(arg, "-_cc=", 5) == 0) ||
+                            (strncmp(arg, "-_sun=", 6) == 0)) {
+                                newae(ctx->i_ae, strchr(arg, '=') + 1);
                         }
                         break;
+
                 case 'V':
                         ctx->i_flags &= ~CW_F_ECHO;
                         newae(ctx->i_ae, arg);
                         break;
                 case 'o':

@@ -1574,10 +1557,12 @@
                 default:
                         newae(ctx->i_ae, arg);
                 }
         }
 
+        free(nameflag);
+
         if ((op == CW_O_LINK || op == CW_O_PREPROCESS) &&
             (ctx->i_flags & CW_F_SHADOW))
                 exit(0);
 
         if (!seen_o && (ctx->i_flags & CW_F_SHADOW)) {

@@ -1587,59 +1572,26 @@
 }
 
 static void
 prepctx(cw_ictx_t *ctx)
 {
-        const char *dir = NULL, *cmd;
-        char *program = NULL;
-        size_t len;
-
-        switch (CIDX(CC(ctx), ctx->i_flags)) {
-                case CIDX(CW_C_CC, 0):
-                        program = getenv("CW_CC");
-                        dir = getenv("CW_CC_DIR");
-                        break;
-                case CIDX(CW_C_CC, CW_F_CXX):
-                        program = getenv("CW_CPLUSPLUS");
-                        dir = getenv("CW_CPLUSPLUS_DIR");
-                        break;
-                case CIDX(CW_C_GCC, 0):
-                        program = getenv("CW_GCC");
-                        dir = getenv("CW_GCC_DIR");
-                        break;
-                case CIDX(CW_C_GCC, CW_F_CXX):
-                        program = getenv("CW_GPLUSPLUS");
-                        dir = getenv("CW_GPLUSPLUS_DIR");
-                        break;
-        }
-
-        if (program == NULL) {
-                if (dir == NULL)
-                        dir = default_dir[CC(ctx)];
-                cmd = cmds[CIDX(CC(ctx), ctx->i_flags)];
-                len = strlen(dir) + strlen(cmd) + 2;
-                if ((program = malloc(len)) == NULL)
-                        nomem();
-                (void) snprintf(program, len, "%s/%s", dir, cmd);
-        }
-
-        newae(ctx->i_ae, program);
+        newae(ctx->i_ae, ctx->i_compiler->c_path);
 
         if (ctx->i_flags & CW_F_PROG) {
                 (void) printf("%s: %s\n", (ctx->i_flags & CW_F_SHADOW) ?
-                    "shadow" : "primary", program);
+                    "shadow" : "primary", ctx->i_compiler->c_path);
                 (void) fflush(stdout);
         }
 
         if (!(ctx->i_flags & CW_F_XLATE))
                 return;
 
-        switch (CC(ctx)) {
-        case CW_C_CC:
+        switch (ctx->i_compiler->c_style) {
+        case SUN:
                 do_cc(ctx);
                 break;
-        case CW_C_GCC:
+        case GNU:
                 do_gcc(ctx);
                 break;
         }
 }
 

@@ -1684,11 +1636,11 @@
                     strerror(errno));
                 return (-1);
         }
 
         (void) execv(newargv[0], newargv);
-        cw_perror("couldn't run %s", newargv[0]);
+        warn("couldn't run %s", newargv[0]);
 
         return (-1);
 }
 
 static int

@@ -1704,11 +1656,11 @@
         if (ctx->i_pid <= 0)
                 return (-1);
 
         do {
                 if (waitpid(ctx->i_pid, &status, 0) < 0) {
-                        cw_perror("cannot reap child");
+                        warn("cannot reap child");
                         return (-1);
                 }
                 if (status != 0) {
                         if (WIFSIGNALED(status)) {
                                 ret = -WTERMSIG(status);

@@ -1721,11 +1673,11 @@
         } while (!WIFEXITED(status) && !WIFSIGNALED(status));
 
         (void) unlink(ctx->i_discard);
 
         if (stat(ctx->i_stderr, &s) < 0) {
-                cw_perror("stat failed on child cleanup");
+                warn("stat failed on child cleanup");
                 return (-1);
         }
         if (s.st_size != 0) {
                 FILE *f;
 

@@ -1774,145 +1726,191 @@
                 int fd;
 
                 (void) fclose(stderr);
                 if ((fd = open(ctx->i_stderr, O_WRONLY | O_CREAT | O_EXCL,
                     0666)) < 0) {
-                        cw_perror("open failed for standard error");
-                        exit(1);
+                        err(1, "open failed for standard error");
                 }
                 if (dup2(fd, 2) < 0) {
-                        cw_perror("dup2 failed for standard error");
-                        exit(1);
+                        err(1, "dup2 failed for standard error");
                 }
                 if (fd != 2)
                         (void) close(fd);
                 if (freopen("/dev/fd/2", "w", stderr) == NULL) {
-                        cw_perror("freopen failed for /dev/fd/2");
-                        exit(1);
+                        err(1, "freopen failed for /dev/fd/2");
                 }
                 prepctx(ctx);
                 exit(invoke(ctx));
         }
 
         if (ctx->i_pid < 0) {
-                cw_perror("fork failed");
-                return (1);
+                err(1, "fork failed");
         }
 
         if (block)
                 return (reap(ctx));
 
         return (0);
 }
 
+static int
+parse_compiler(const char *spec, cw_compiler_t *compiler)
+{
+        char *tspec, *token;
+
+        if ((tspec = strdup(spec)) == NULL)
+                nomem();
+
+        if ((token = strsep(&tspec, ",")) == NULL)
+                errx(1, "Compiler is missing a name: %s", spec);
+        compiler->c_name = token;
+
+        if ((token = strsep(&tspec, ",")) == NULL)
+                errx(1, "Compiler is missing a path: %s", spec);
+        compiler->c_path = token;
+
+        if ((token = strsep(&tspec, ",")) == NULL)
+                errx(1, "Compiler is missing a style: %s", spec);
+
+        if ((strcasecmp(token, "gnu") == 0) ||
+            (strcasecmp(token, "gcc") == 0))
+                compiler->c_style = GNU;
+        else if ((strcasecmp(token, "sun") == 0) ||
+            (strcasecmp(token, "cc") == 0))
+                compiler->c_style = SUN;
+        else
+                errx(1, "unknown compiler style: %s", token);
+
+        if (tspec != NULL)
+                errx(1, "Excess tokens in compiler: %s", spec);
+
+        return (0);
+}
+
 int
 main(int argc, char **argv)
 {
-        cw_ictx_t *ctx = newictx();
-        cw_ictx_t *ctx_shadow = newictx();
-        const char *dir;
-        int do_serial, do_shadow;
+        int ch;
+        cw_compiler_t primary = { NULL, NULL, 0 };
+        cw_compiler_t shadows[10];
+        int nshadows = 0;
         int ret = 0;
+        boolean_t do_serial = B_FALSE;
+        boolean_t do_exec = B_FALSE;
+        boolean_t vflg = B_FALSE;
+        boolean_t Cflg = B_FALSE;
+        boolean_t cflg = B_FALSE;
+        boolean_t nflg = B_FALSE;
+
+        cw_ictx_t *main_ctx;
+
+        static struct option longopts[] = {
+                { "compiler", no_argument, NULL, 'c' },
+                { "noecho", no_argument, NULL, 'n' },
+                { "primary", required_argument, NULL, 'p' },
+                { "shadow", required_argument, NULL, 's' },
+                { "versions", no_argument, NULL, 'v' },
+                { NULL, 0, NULL, 0 },
+        };
 
-        if ((progname = strrchr(argv[0], '/')) == NULL)
-                progname = argv[0];
-        else
-                progname++;
 
-        if (ctx == NULL || ctx_shadow == NULL)
+        if ((main_ctx = newictx()) == NULL)
                 nomem();
 
-        ctx->i_flags = CW_F_ECHO|CW_F_XLATE;
 
-        /*
-         * Figure out where to get our tools from.  This depends on
-         * the environment variables set at run time.
-         */
-        if ((dir = getenv("SPRO_VROOT")) != NULL) {
-                (void) snprintf(default_dir[CW_C_CC], MAXPATHLEN,
-                    "%s/bin", dir);
-        } else if ((dir = getenv("SPRO_ROOT")) != NULL) {
-                (void) snprintf(default_dir[CW_C_CC], MAXPATHLEN,
-                    "%s/SS12/bin", dir);
-        } else if ((dir = getenv("BUILD_TOOLS")) != NULL) {
-                (void) snprintf(default_dir[CW_C_CC], MAXPATHLEN,
-                    "%s/SUNWspro/SS12/bin", dir);
+        while ((ch = getopt_long(argc, argv, "C", longopts, NULL)) != -1) {
+                switch (ch) {
+                case 'c':
+                        cflg = B_TRUE;
+                        break;
+                case 'C':
+                        Cflg = B_TRUE;
+                        break;
+                case 'n':
+                        nflg = B_TRUE;
+                        break;
+                case 'p':
+                        if (primary.c_path != NULL) {
+                                warnx("Only one primary compiler may "
+                                    "be specified");
+                                usage();
         }
 
-        if ((dir = getenv("GCC_ROOT")) != NULL) {
-                (void) snprintf(default_dir[CW_C_GCC], MAXPATHLEN,
-                    "%s/bin", dir);
+                        if (parse_compiler(optarg, &primary) != 0)
+                                errx(1, "Couldn't parse %s as a compiler spec",
+                                    optarg);
+                        break;
+                case 's':
+                        if (nshadows >= 10)
+                                errx(1, "May only use 10 shadows at "
+                                    "the moment");
+                        if (parse_compiler(optarg, &shadows[nshadows]) != 0)
+                                errx(1, "Couldn't parse %s as a compiler spec",
+                                    optarg);
+                        nshadows++;
+                        break;
+                case 'v':
+                        vflg = B_TRUE;
+                        break;
+                default:
+                        (void) fprintf(stderr, "Did you forget '--'?\n");
+                        usage();
+                }
         }
 
-        do_shadow = (getenv("CW_NO_SHADOW") ? 0 : 1);
-        do_serial = (getenv("CW_SHADOW_SERIAL") ? 1 : 0);
-
-        if (getenv("CW_NO_EXEC") == NULL)
-                ctx->i_flags |= CW_F_EXEC;
-
-        /*
-         * The first argument must be one of "-_cc", "-_gcc", "-_CC", or "-_g++"
-         */
-        if (argc == 1)
+        if (primary.c_path == NULL) {
+                warnx("A primary compiler must be specified");
                 usage();
-        argc--;
-        argv++;
-        if (strcmp(argv[0], "-_cc") == 0) {
-                ctx->i_compiler = CW_C_CC;
-        } else if (strcmp(argv[0], "-_gcc") == 0) {
-                ctx->i_compiler = CW_C_GCC;
-        } else if (strcmp(argv[0], "-_CC") == 0) {
-                ctx->i_compiler = CW_C_CC;
-                ctx->i_flags |= CW_F_CXX;
-        } else if (strcmp(argv[0], "-_g++") == 0) {
-                ctx->i_compiler = CW_C_GCC;
-                ctx->i_flags |= CW_F_CXX;
-        } else {
-                /* assume "-_gcc" by default */
-                argc++;
-                argv--;
-                ctx->i_compiler = CW_C_GCC;
         }
 
-        /*
-         * -_compiler - tell us the path to the primary compiler only
-         */
-        if (argc > 1 && strcmp(argv[1], "-_compiler") == 0) {
-                ctx->i_flags &= ~CW_F_XLATE;
-                prepctx(ctx);
-                (void) printf("%s\n", ctx->i_ae->ael_head->ae_arg);
-                return (0);
+        do_serial = (getenv("CW_SHADOW_SERIAL") == NULL) ? B_FALSE : B_TRUE;
+        do_exec = (getenv("CW_NO_EXEC") == NULL) ? B_TRUE : B_FALSE;
+
+        /* Leave room for argv[0] */
+        argc -= (optind - 1);
+        argv += (optind - 1);
+
+        main_ctx->i_oldargc = argc;
+        main_ctx->i_oldargv = argv;
+        main_ctx->i_flags = CW_F_XLATE;
+        if (nflg == 0)
+                main_ctx->i_flags |= CW_F_ECHO;
+        if (do_exec)
+                main_ctx->i_flags |= CW_F_EXEC;
+        if (Cflg)
+                main_ctx->i_flags |= CW_F_CXX;
+        main_ctx->i_compiler = &primary;
+
+        if (cflg) {
+                (void) fputs(primary.c_path, stdout);
         }
 
-        /*
-         * -_versions - tell us the cw version, paths to all compilers, and
-         *              ask each for its version if we know how.
-         */
-        if (argc > 1 && strcmp(argv[1], "-_versions") == 0) {
-                (void) printf("cw version %s", CW_VERSION);
-                if (!do_shadow)
-                        (void) printf(" (SHADOW MODE DISABLED)");
-                (void) printf("\n");
+        if (vflg) {
+                (void) printf("cw version %s\n", CW_VERSION);
                 (void) fflush(stdout);
-                ctx->i_flags &= ~CW_F_ECHO;
-                ctx->i_flags |= CW_F_PROG|CW_F_EXEC;
-                argc--;
-                argv++;
+                main_ctx->i_flags &= ~CW_F_ECHO;
+                main_ctx->i_flags |= CW_F_PROG|CW_F_EXEC;
                 do_serial = 1;
         }
 
-        ctx->i_oldargc = argc;
-        ctx->i_oldargv = argv;
+        ret |= exec_ctx(main_ctx, do_serial);
+
+        for (int i = 0; i < nshadows; i++) {
+                cw_ictx_t *shadow_ctx;
+
+                if ((shadow_ctx = newictx()) == NULL)
+                        nomem();
+
+                memcpy(shadow_ctx, main_ctx, sizeof (cw_ictx_t));
 
-        ret |= exec_ctx(ctx, do_serial);
+                shadow_ctx->i_flags |= CW_F_SHADOW;
+                shadow_ctx->i_compiler = &shadows[i];
 
-        if (do_shadow) {
-                (void) memcpy(ctx_shadow, ctx, sizeof (cw_ictx_t));
-                ctx_shadow->i_flags |= CW_F_SHADOW;
-                ret |= exec_ctx(ctx_shadow, 1);
+                /* XXX: Would be nice to run these parallel, too */
+                ret |= exec_ctx(shadow_ctx, 1);
         }
 
         if (!do_serial)
-                ret |= reap(ctx);
+                ret |= reap(main_ctx);
 
         return (ret);
 }