1 
2 /**
3  * This modules defines related
4  * utilities needed for arguments parsing, path manipulation, etc...
5  * This file is not shared with other compilers which use the DMD front-end.
6  *
7  * Copyright:   Copyright (C) 1999-2023 by The D Language Foundation, All Rights Reserved
8  * Authors:     $(LINK2 https://www.digitalmars.com, Walter Bright)
9  * License:     $(LINK2 https://www.boost.org/LICENSE_1_0.txt, Boost License 1.0)
10  * Source:      $(LINK2 https://github.com/dlang/dmd/blob/master/src/dmd/mars.d, _mars.d)
11  * Documentation:  https://dlang.org/phobos/dmd_mars.html
12  * Coverage:    https://codecov.io/gh/dlang/dmd/src/master/src/dmd/mars.d
13  */
14 
15 module dmd.mars;
16 
17 import core.stdc.ctype;
18 import core.stdc.stdio;
19 import core.stdc.stdlib;
20 import core.stdc.string;
21 
22 import dmd.arraytypes;
23 import dmd.astenums;
24 import dmd.cond;
25 import dmd.console;
26 import dmd.compiler;
27 import dmd.cpreprocess;
28 import dmd.dmdparams;
29 import dmd.dinifile;
30 import dmd.dinterpret;
31 import dmd.dmodule;
32 import dmd.doc;
33 import dmd.dsymbol;
34 import dmd.dsymbolsem;
35 import dmd.dtemplate;
36 import dmd.dtoh;
37 import dmd.errors;
38 import dmd.expression;
39 import dmd.globals;
40 import dmd.hdrgen;
41 import dmd.id;
42 import dmd.identifier;
43 import dmd.inline;
44 import dmd.location;
45 import dmd.json;
46 import dmd.mtype;
47 import dmd.objc;
48 import dmd.root.array;
49 import dmd.root.file;
50 import dmd.root.filename;
51 import dmd.root.man;
52 import dmd.common.outbuffer;
53 import dmd.root.response;
54 import dmd.root.rmem;
55 import dmd.root.string;
56 import dmd.root.stringtable;
57 import dmd.semantic2;
58 import dmd.semantic3;
59 import dmd.target;
60 import dmd.utils;
61 
62 /**
63  * Print DMD's logo on stdout
64  */
65 void logo()
66 {
67     printf("DMD%llu D Compiler %.*s\n%.*s %.*s\n",
68         cast(ulong)size_t.sizeof * 8,
69         cast(int) global.versionString().length, global.versionString().ptr,
70         cast(int)global.copyright.length, global.copyright.ptr,
71         cast(int)global.written.length, global.written.ptr
72     );
73 }
74 
75 /**
76 Print DMD's logo with more debug information and error-reporting pointers.
77 
78 Params:
79     stream = output stream to print the information on
80 */
81 void printInternalFailure(FILE* stream)
82 {
83     fputs(("---\n" ~
84     "ERROR: This is a compiler bug.\n" ~
85             "Please report it via https://issues.dlang.org/enter_bug.cgi\n" ~
86             "with, preferably, a reduced, reproducible example and the information below.\n" ~
87     "DustMite (https://github.com/CyberShadow/DustMite/wiki) can help with the reduction.\n" ~
88     "---\n").ptr, stream);
89     stream.fprintf("DMD %.*s\n", cast(int) global.versionString().length, global.versionString().ptr);
90     stream.printPredefinedVersions;
91     stream.printGlobalConfigs();
92     fputs("---\n".ptr, stream);
93 }
94 
95 /**
96  * Print DMD's usage message on stdout
97  */
98 void usage()
99 {
100     import dmd.cli : CLIUsage;
101     logo();
102     auto help = CLIUsage.usage;
103     const inifileCanon = FileName.canonicalName(global.inifilename);
104     printf("
105 Documentation: https://dlang.org/
106 Config file: %.*s
107 Usage:
108   dmd [<option>...] <file>...
109   dmd [<option>...] -run <file> [<arg>...]
110 
111 Where:
112   <file>           D source file
113   <arg>            Argument to pass when running the resulting program
114 
115 <option>:
116   @<cmdfile>       read arguments from cmdfile
117 %.*s", cast(int)inifileCanon.length, inifileCanon.ptr, cast(int)help.length, &help[0]);
118 }
119 
120 extern (C++) void generateJson(ref Modules modules)
121 {
122     OutBuffer buf;
123     json_generate(modules, buf);
124 
125     // Write buf to file
126     const(char)[] name = global.params.json.name;
127     if (name == "-")
128     {
129         // Write to stdout; assume it succeeds
130         size_t n = fwrite(buf[].ptr, 1, buf.length, stdout);
131         assert(n == buf.length); // keep gcc happy about return values
132     }
133     else
134     {
135         /* The filename generation code here should be harmonized with Module.setOutfilename()
136          */
137         const(char)[] jsonfilename;
138         if (name)
139         {
140             jsonfilename = FileName.defaultExt(name, json_ext);
141         }
142         else
143         {
144             if (global.params.objfiles.length == 0)
145             {
146                 error(Loc.initial, "cannot determine JSON filename, use `-Xf=<file>` or provide a source file");
147                 fatal();
148             }
149             // Generate json file name from first obj name
150             const(char)[] n = global.params.objfiles[0].toDString;
151             n = FileName.name(n);
152             //if (!FileName::absolute(name))
153             //    name = FileName::combine(dir, name);
154             jsonfilename = FileName.forceExt(n, json_ext);
155         }
156         if (!writeFile(Loc.initial, jsonfilename, buf[]))
157             fatal();
158     }
159 }
160 
161 version (DigitalMars)
162 {
163     void installMemErrHandler()
164     {
165         // (only available on some platforms on DMD)
166         const shouldDoMemoryError = getenv("DMD_INSTALL_MEMERR_HANDLER");
167         if (shouldDoMemoryError !is null && *shouldDoMemoryError == '1')
168         {
169             import etc.linux.memoryerror;
170             static if (is(typeof(registerMemoryErrorHandler())))
171             {
172                 registerMemoryErrorHandler();
173             }
174             else
175             {
176                 printf("**WARNING** Memory error handler not supported on this platform!\n");
177             }
178         }
179     }
180 }
181 
182 version (NoMain)
183 {
184     version (DigitalMars)
185     {
186         shared static this()
187         {
188             installMemErrHandler();
189         }
190     }
191 }
192 
193 /**
194  * Parses an environment variable containing command-line flags
195  * and append them to `args`.
196  *
197  * This function is used to read the content of DFLAGS.
198  * Flags are separated based on spaces and tabs.
199  *
200  * Params:
201  *   envvalue = The content of an environment variable
202  *   args     = Array to append the flags to, if any.
203  */
204 void getenv_setargv(const(char)* envvalue, Strings* args)
205 {
206     if (!envvalue)
207         return;
208 
209     char* env = mem.xstrdup(envvalue); // create our own writable copy
210     //printf("env = '%s'\n", env);
211     while (1)
212     {
213         switch (*env)
214         {
215         case ' ':
216         case '\t':
217             env++;
218             break;
219 
220         case 0:
221             return;
222 
223         default:
224         {
225             args.push(env); // append
226             auto p = env;
227             auto slash = 0;
228             bool instring = false;
229             while (1)
230             {
231                 auto c = *env++;
232                 switch (c)
233                 {
234                 case '"':
235                     p -= (slash >> 1);
236                     if (slash & 1)
237                     {
238                         p--;
239                         goto default;
240                     }
241                     instring ^= true;
242                     slash = 0;
243                     continue;
244 
245                 case ' ':
246                 case '\t':
247                     if (instring)
248                         goto default;
249                     *p = 0;
250                     //if (wildcard)
251                     //    wildcardexpand();     // not implemented
252                     break;
253 
254                 case '\\':
255                     slash++;
256                     *p++ = c;
257                     continue;
258 
259                 case 0:
260                     *p = 0;
261                     //if (wildcard)
262                     //    wildcardexpand();     // not implemented
263                     return;
264 
265                 default:
266                     slash = 0;
267                     *p++ = c;
268                     continue;
269                 }
270                 break;
271             }
272             break;
273         }
274         }
275     }
276 }
277 
278 /**
279  * Parse command line arguments for the last instance of -m32, -m64, -m32mscoff or -m32omfobj
280  * to detect the desired architecture.
281  *
282  * Params:
283  *   args = Command line arguments
284  *   arch = Default value to use for architecture.
285  *          Should be "32" or "64"
286  *
287  * Returns:
288  *   "32", "64" or "32omf" if the "-m32", "-m64", "-m32omf" flags were passed,
289  *   respectively. If they weren't, return `arch`.
290  */
291 const(char)[] parse_arch_arg(Strings* args, const(char)[] arch)
292 {
293     foreach (const p; *args)
294     {
295         const(char)[] arg = p.toDString;
296 
297         if (arg.length && arg[0] == '-')
298         {
299             if (arg[1 .. $] == "m32" || arg[1 .. $] == "m32omf" || arg[1 .. $] == "m64")
300                 arch = arg[2 .. $];
301             else if (arg[1 .. $] == "m32mscoff")
302                 arch = "32";
303             else if (arg[1 .. $] == "run")
304                 break;
305         }
306     }
307     return arch;
308 }
309 
310 
311 /**
312  * Parse command line arguments for the last instance of -conf=path.
313  *
314  * Params:
315  *   args = Command line arguments
316  *
317  * Returns:
318  *   The 'path' in -conf=path, which is the path to the config file to use
319  */
320 const(char)[] parse_conf_arg(Strings* args)
321 {
322     const(char)[] conf;
323     foreach (const p; *args)
324     {
325         const(char)[] arg = p.toDString;
326         if (arg.length && arg[0] == '-')
327         {
328             if(arg.length >= 6 && arg[1 .. 6] == "conf="){
329                 conf = arg[6 .. $];
330             }
331             else if (arg[1 .. $] == "run")
332                 break;
333         }
334     }
335     return conf;
336 }
337 
338 
339 /**
340  * Set the default and debug libraries to link against, if not already set
341  *
342  * Must be called after argument parsing is done, as it won't
343  * override any value.
344  * Note that if `-defaultlib=` or `-debuglib=` was used,
345  * we don't override that either.
346  */
347 void setDefaultLibrary(ref Param params, const ref Target target)
348 {
349     if (driverParams.defaultlibname is null)
350     {
351         if (target.os == Target.OS.Windows)
352         {
353             if (target.isX86_64)
354                 driverParams.defaultlibname = "phobos64";
355             else if (!target.omfobj)
356                 driverParams.defaultlibname = "phobos32mscoff";
357             else
358                 driverParams.defaultlibname = "phobos";
359         }
360         else if (target.os & (Target.OS.linux | Target.OS.FreeBSD | Target.OS.OpenBSD | Target.OS.Solaris | Target.OS.DragonFlyBSD))
361         {
362             driverParams.defaultlibname = "libphobos2.a";
363         }
364         else if (target.os == Target.OS.OSX)
365         {
366             driverParams.defaultlibname = "phobos2";
367         }
368         else
369         {
370             assert(0, "fix this");
371         }
372     }
373     else if (!driverParams.defaultlibname.length)  // if `-defaultlib=` (i.e. an empty defaultlib)
374         driverParams.defaultlibname = null;
375 
376     if (driverParams.debuglibname is null)
377         driverParams.debuglibname = driverParams.defaultlibname;
378 }
379 
380 void printPredefinedVersions(FILE* stream)
381 {
382     if (global.versionids)
383     {
384         OutBuffer buf;
385         foreach (const str; *global.versionids)
386         {
387             buf.writeByte(' ');
388             buf.writestring(str.toChars());
389         }
390         stream.fprintf("predefs  %s\n", buf.peekChars());
391     }
392 }
393 
394 extern(C) void printGlobalConfigs(FILE* stream)
395 {
396     stream.fprintf("binary    %.*s\n", cast(int)global.params.argv0.length, global.params.argv0.ptr);
397     stream.fprintf("version   %.*s\n", cast(int) global.versionString().length, global.versionString().ptr);
398     const iniOutput = global.inifilename ? global.inifilename : "(none)";
399     stream.fprintf("config    %.*s\n", cast(int)iniOutput.length, iniOutput.ptr);
400     // Print DFLAGS environment variable
401     {
402         StringTable!(char*) environment;
403         environment._init(0);
404         Strings dflags;
405         getenv_setargv(readFromEnv(environment, "DFLAGS"), &dflags);
406         environment.reset(1);
407         OutBuffer buf;
408         foreach (flag; dflags[])
409         {
410             bool needsQuoting;
411             foreach (c; flag.toDString())
412             {
413                 if (!(isalnum(c) || c == '_'))
414                 {
415                     needsQuoting = true;
416                     break;
417                 }
418             }
419 
420             if (flag.strchr(' '))
421                 buf.printf("'%s' ", flag);
422             else
423                 buf.printf("%s ", flag);
424         }
425 
426         auto res = buf[] ? buf[][0 .. $ - 1] : "(none)";
427         stream.fprintf("DFLAGS    %.*s\n", cast(int)res.length, res.ptr);
428     }
429 }
430 
431 /**************************************
432  * we want to write the mixin expansion file also on error, but there
433  * are too many ways to terminate dmd (e.g. fatal() which calls exit(EXIT_FAILURE)),
434  * so we can't rely on scope(exit) ... in tryMain() actually being executed
435  * so we add atexit(&flushMixins); for those fatal exits (with the GC still valid)
436  */
437 extern(C) void flushMixins()
438 {
439     if (!global.params.mixinOut.buffer)
440         return;
441 
442     assert(global.params.mixinOut.name);
443     File.write(global.params.mixinOut.name, (*global.params.mixinOut.buffer)[]);
444 
445     global.params.mixinOut.buffer.destroy();
446     global.params.mixinOut.buffer = null;
447 }
448 
449 /****************************************************
450  * Parse command line arguments.
451  *
452  * Prints message(s) if there are errors.
453  *
454  * Params:
455  *      arguments = command line arguments
456  *      argc = argument count
457  *      params = set to result of parsing `arguments`
458  *      files = set to files pulled from `arguments`
459  *      target = more things set to result of parsing `arguments`
460  * Returns:
461  *      true if errors in command line
462  */
463 
464 bool parseCommandLine(const ref Strings arguments, const size_t argc, ref Param params, ref Strings files, ref Target target)
465 {
466     bool errors;
467 
468     void error(Args ...)(const(char)* format, Args args)
469     {
470         dmd.errors.error(Loc.initial, format, args);
471         errors = true;
472     }
473 
474     /**
475      * Print an error messsage about an invalid switch.
476      * If an optional supplemental message has been provided,
477      * it will be printed too.
478      *
479      * Params:
480      *  p = 0 terminated string
481      *  availableOptions = supplemental help message listing the available options
482      */
483     void errorInvalidSwitch(const(char)* p, string availableOptions = null)
484     {
485         error("switch `%s` is invalid", p);
486         if (availableOptions !is null)
487             errorSupplemental(Loc.initial, "%.*s", cast(int)availableOptions.length, availableOptions.ptr);
488     }
489 
490     enum CheckOptions { success, error, help }
491 
492     /*
493     Checks whether the CLI options contains a valid argument or a help argument.
494     If a help argument has been used, it will set the `usageFlag`.
495 
496     Params:
497         p = string as a D array
498         usageFlag = parameter for the usage help page to set (by `ref`)
499         missingMsg = error message to use when no argument has been provided
500 
501     Returns:
502         `success` if a valid argument has been passed and it's not a help page
503         `error` if an error occurred (e.g. `-foobar`)
504         `help` if a help page has been request (e.g. `-flag` or `-flag=h`)
505     */
506     CheckOptions checkOptions(const(char)[] p, ref bool usageFlag, string missingMsg)
507     {
508         // Checks whether a flag has no options (e.g. -foo or -foo=)
509         if (p.length == 0 || p == "=")
510         {
511             .error(Loc.initial, "%.*s", cast(int)missingMsg.length, missingMsg.ptr);
512             errors = true;
513             usageFlag = true;
514             return CheckOptions.help;
515         }
516         if (p[0] != '=')
517             return CheckOptions.error;
518         p = p[1 .. $];
519         /* Checks whether the option pointer supplied is a request
520            for the help page, e.g. -foo=j */
521         if ((p == "h" || p == "?") || // -flag=h || -flag=?
522              p == "help")
523         {
524             usageFlag = true;
525             return CheckOptions.help;
526         }
527         return CheckOptions.success;
528     }
529 
530     static string checkOptionsMixin(string usageFlag, string missingMsg)
531     {
532         return q{
533             final switch (checkOptions(arg[len - 1 .. $], params.help..}~usageFlag~","~
534                           `"`~missingMsg~`"`~q{))
535             {
536                 case CheckOptions.error:
537                     goto Lerror;
538                 case CheckOptions.help:
539                     return false;
540                 case CheckOptions.success:
541                     break;
542             }
543         };
544     }
545 
546     import dmd.cli : Usage;
547     bool parseCLIOption(string name, Usage.Feature[] features)(ref Param params, const(char)[] p)
548     {
549         // Parse:
550         //      -<name>=<feature>
551         const(char)[] ps = p[name.length + 1 .. $];
552         const(char)[] ident = ps[1 .. $];
553         if (Identifier.isValidIdentifier(ident))
554         {
555             string generateTransitionsText()
556             {
557                 import dmd.cli : Usage;
558                 string buf = `case "all":`;
559                 foreach (t; features)
560                 {
561                     if (t.deprecated_)
562                         continue;
563 
564                     buf ~= `setFlagFor(name, params.`~t.paramName~`);`;
565                 }
566                 buf ~= "return true;\n";
567 
568                 foreach (t; features)
569                 {
570                     buf ~= `case "`~t.name~`":`;
571                     if (t.deprecated_)
572                         buf ~= "deprecation(Loc.initial, \"`-"~name~"="~t.name~"` no longer has any effect.\"); ";
573                     buf ~= `setFlagFor(name, params.`~t.paramName~`); return true;`;
574                 }
575                 return buf;
576             }
577 
578             switch (ident)
579             {
580                 mixin(generateTransitionsText());
581             default:
582                 return false;
583             }
584         }
585         return false;
586     }
587 
588     version (none)
589     {
590         for (size_t i = 0; i < arguments.length; i++)
591         {
592             printf("arguments[%d] = '%s'\n", i, arguments[i]);
593         }
594     }
595 
596     files.reserve(arguments.length - 1);
597 
598     for (size_t i = 1; i < arguments.length; i++)
599     {
600         const(char)* p = arguments[i];
601         const(char)[] arg = p.toDString();
602         if (*p != '-')
603         {
604             if (target.os == Target.OS.Windows)
605             {
606                 const ext = FileName.ext(arg);
607                 if (ext.length && FileName.equals(ext, "exe"))
608                 {
609                     params.objname = arg;
610                     continue;
611                 }
612                 if (arg == "/?")
613                 {
614                     params.help.usage = true;
615                     return false;
616                 }
617             }
618             files.push(p);
619             continue;
620         }
621 
622         if (arg == "-allinst")               // https://dlang.org/dmd.html#switch-allinst
623             params.allInst = true;
624         else if (startsWith(p + 1, "cpp="))  // https://dlang.org/dmd.html#switch-cpp
625         {
626             if (p[5])
627             {
628                 params.cpp = p + 5;
629             }
630             else
631             {
632                 errorInvalidSwitch(p, "it must be followed by the filename of the desired C preprocessor");
633                 return false;
634             }
635         }
636         else if (arg == "-de")               // https://dlang.org/dmd.html#switch-de
637             params.useDeprecated = DiagnosticReporting.error;
638         else if (arg == "-d")                // https://dlang.org/dmd.html#switch-d
639             params.useDeprecated = DiagnosticReporting.off;
640         else if (arg == "-dw")               // https://dlang.org/dmd.html#switch-dw
641             params.useDeprecated = DiagnosticReporting.inform;
642         else if (arg == "-c")                // https://dlang.org/dmd.html#switch-c
643             driverParams.link = false;
644         else if (startsWith(p + 1, "checkaction")) // https://dlang.org/dmd.html#switch-checkaction
645         {
646             /* Parse:
647              *    -checkaction=D|C|halt|context
648              */
649             enum len = "-checkaction=".length;
650             mixin(checkOptionsMixin("checkAction",
651                 "`-check=<behavior>` requires a behavior"));
652             switch (arg[len .. $])
653             {
654             case "D":
655                 params.checkAction = CHECKACTION.D;
656                 break;
657             case "C":
658                 params.checkAction = CHECKACTION.C;
659                 break;
660             case "halt":
661                 params.checkAction = CHECKACTION.halt;
662                 break;
663             case "context":
664                 params.checkAction = CHECKACTION.context;
665                 break;
666             default:
667                 errorInvalidSwitch(p);
668                 params.help.checkAction = true;
669                 return false;
670             }
671         }
672         else if (startsWith(p + 1, "check")) // https://dlang.org/dmd.html#switch-check
673         {
674             enum len = "-check=".length;
675             mixin(checkOptionsMixin("check",
676                 "`-check=<action>` requires an action"));
677             /* Parse:
678              *    -check=[assert|bounds|in|invariant|out|switch][=[on|off]]
679              */
680 
681             // Check for legal option string; return true if so
682             static bool check(const(char)[] checkarg, string name, ref CHECKENABLE ce)
683             {
684                 if (checkarg.length >= name.length &&
685                     checkarg[0 .. name.length] == name)
686                 {
687                     checkarg = checkarg[name.length .. $];
688 
689                     if (checkarg.length == 0 ||
690                         checkarg == "=on")
691                     {
692                         ce = CHECKENABLE.on;
693                         return true;
694                     }
695                     else if (checkarg == "=off")
696                     {
697                         ce = CHECKENABLE.off;
698                         return true;
699                     }
700                 }
701                 return false;
702             }
703 
704             const(char)[] checkarg = arg[len .. $];
705             if (checkarg == "on")
706             {
707                 params.useAssert        = CHECKENABLE.on;
708                 params.useArrayBounds   = CHECKENABLE.on;
709                 params.useIn            = CHECKENABLE.on;
710                 params.useInvariants    = CHECKENABLE.on;
711                 params.useOut           = CHECKENABLE.on;
712                 params.useSwitchError   = CHECKENABLE.on;
713             }
714             else if (checkarg == "off")
715             {
716                 params.useAssert        = CHECKENABLE.off;
717                 params.useArrayBounds   = CHECKENABLE.off;
718                 params.useIn            = CHECKENABLE.off;
719                 params.useInvariants    = CHECKENABLE.off;
720                 params.useOut           = CHECKENABLE.off;
721                 params.useSwitchError   = CHECKENABLE.off;
722             }
723             else if (!(check(checkarg, "assert",    params.useAssert) ||
724                   check(checkarg, "bounds",    params.useArrayBounds) ||
725                   check(checkarg, "in",        params.useIn         ) ||
726                   check(checkarg, "invariant", params.useInvariants ) ||
727                   check(checkarg, "out",       params.useOut        ) ||
728                   check(checkarg, "switch",    params.useSwitchError)))
729             {
730                 errorInvalidSwitch(p);
731                 params.help.check = true;
732                 return false;
733             }
734         }
735         else if (startsWith(p + 1, "color")) // https://dlang.org/dmd.html#switch-color
736         {
737             // Parse:
738             //      -color
739             //      -color=auto|on|off
740             if (p[6] == '=')
741             {
742                 switch(arg[7 .. $])
743                 {
744                 case "on":
745                     params.v.color = true;
746                     break;
747                 case "off":
748                     params.v.color = false;
749                     break;
750                 case "auto":
751                     break;
752                 default:
753                     errorInvalidSwitch(p, "Available options for `-color` are `on`, `off` and `auto`");
754                     return true;
755                 }
756             }
757             else if (p[6])
758                 goto Lerror;
759             else
760                 params.v.color = true;
761         }
762         else if (startsWith(p + 1, "conf=")) // https://dlang.org/dmd.html#switch-conf
763         {
764             // ignore, already handled above
765         }
766         else if (startsWith(p + 1, "cov")) // https://dlang.org/dmd.html#switch-cov
767         {
768             params.cov = true;
769             // Parse:
770             //      -cov
771             //      -cov=ctfe
772             //      -cov=nnn
773             if (arg == "-cov=ctfe")
774             {
775                 params.ctfe_cov = true;
776             }
777             else if (p[4] == '=')
778             {
779                 if (!params.covPercent.parseDigits(p.toDString()[5 .. $], 100))
780                 {
781                     errorInvalidSwitch(p, "Only a number between 0 and 100 can be passed to `-cov=<num>`");
782                     return true;
783                 }
784             }
785             else if (p[4])
786                 goto Lerror;
787         }
788         else if (arg == "-shared")
789             driverParams.dll = true;
790         else if (arg == "-fIBT")
791         {
792             driverParams.ibt = true;
793         }
794         else if (arg == "-fPIC")
795         {
796             driverParams.pic = PIC.pic;
797         }
798         else if (arg == "-fPIE")
799         {
800             driverParams.pic = PIC.pie;
801         }
802         else if (arg == "-map") // https://dlang.org/dmd.html#switch-map
803             driverParams.map = true;
804         else if (arg == "-multiobj")
805             params.multiobj = true;
806         else if (startsWith(p + 1, "mixin="))
807         {
808             auto tmp = p + 6 + 1;
809             if (!tmp[0])
810                 goto Lnoarg;
811             params.mixinOut.doOutput = true;
812             params.mixinOut.name = mem.xstrdup(tmp).toDString;
813         }
814         else if (arg == "-g") // https://dlang.org/dmd.html#switch-g
815             driverParams.symdebug = true;
816         else if (startsWith(p + 1, "gdwarf")) // https://dlang.org/dmd.html#switch-gdwarf
817         {
818             if (driverParams.dwarf)
819             {
820                 error("`-gdwarf=<version>` can only be provided once");
821                 break;
822             }
823             driverParams.symdebug = true;
824 
825             enum len = "-gdwarf=".length;
826             // Parse:
827             //      -gdwarf=version
828             if (arg.length < len || !driverParams.dwarf.parseDigits(arg[len .. $], 5) || driverParams.dwarf < 3)
829             {
830                 error("`-gdwarf=<version>` requires a valid version [3|4|5]", p);
831                 return false;
832             }
833         }
834         else if (arg == "-gf")
835         {
836             driverParams.symdebug = true;
837             driverParams.symdebugref = true;
838         }
839         else if (arg == "-gs")  // https://dlang.org/dmd.html#switch-gs
840             driverParams.alwaysframe = true;
841         else if (arg == "-gx")  // https://dlang.org/dmd.html#switch-gx
842             driverParams.stackstomp = true;
843         else if (arg == "-lowmem") // https://dlang.org/dmd.html#switch-lowmem
844         {
845             // ignore, already handled in C main
846         }
847         else if (arg.length > 6 && arg[0..6] == "--DRT-")
848         {
849             continue; // skip druntime options, e.g. used to configure the GC
850         }
851         else if (arg == "-m32") // https://dlang.org/dmd.html#switch-m32
852         {
853             target.isX86_64 = false;
854             target.omfobj = false;
855         }
856         else if (arg == "-m64") // https://dlang.org/dmd.html#switch-m64
857         {
858             target.isX86_64 = true;
859             target.omfobj = false;
860         }
861         else if (arg == "-m32mscoff") // https://dlang.org/dmd.html#switch-m32mscoff
862         {
863             target.isX86_64 = false;
864             target.omfobj = false;
865         }
866         else if (arg == "-m32omf") // https://dlang.org/dmd.html#switch-m32omfobj
867         {
868             target.isX86_64 = false;
869             target.omfobj = true;
870         }
871         else if (startsWith(p + 1, "mscrtlib="))
872         {
873             driverParams.mscrtlib = arg[10 .. $];
874         }
875         else if (startsWith(p + 1, "profile")) // https://dlang.org/dmd.html#switch-profile
876         {
877             // Parse:
878             //      -profile
879             //      -profile=gc
880             if (p[8] == '=')
881             {
882                 if (arg[9 .. $] == "gc")
883                     params.tracegc = true;
884                 else
885                 {
886                     errorInvalidSwitch(p, "Only `gc` is allowed for `-profile`");
887                     return true;
888                 }
889             }
890             else if (p[8])
891                 goto Lerror;
892             else
893                 params.trace = true;
894         }
895         else if (arg == "-v") // https://dlang.org/dmd.html#switch-v
896             params.v.verbose = true;
897         else if (arg == "-vcg-ast")
898             params.vcg_ast = true;
899         else if (arg == "-vasm") // https://dlang.org/dmd.html#switch-vasm
900             driverParams.vasm = true;
901         else if (arg == "-vtls") // https://dlang.org/dmd.html#switch-vtls
902             params.v.tls = true;
903         else if (startsWith(p + 1, "vtemplates")) // https://dlang.org/dmd.html#switch-vtemplates
904         {
905             params.v.templates = true;
906             if (p[1 + "vtemplates".length] == '=')
907             {
908                 const(char)[] style = arg[1 + "vtemplates=".length .. $];
909                 switch (style)
910                 {
911                 case "list-instances":
912                     params.v.templatesListInstances = true;
913                     break;
914                 default:
915                     error("unknown vtemplates style '%.*s', must be 'list-instances'", cast(int) style.length, style.ptr);
916                 }
917             }
918         }
919         else if (arg == "-vcolumns") // https://dlang.org/dmd.html#switch-vcolumns
920             params.v.showColumns = true;
921         else if (arg == "-vgc") // https://dlang.org/dmd.html#switch-vgc
922             params.v.gc = true;
923         else if (startsWith(p + 1, "verrors")) // https://dlang.org/dmd.html#switch-verrors
924         {
925             if (p[8] != '=')
926             {
927                 errorInvalidSwitch(p, "Expected argument following `-verrors , e.g. `-verrors=100`");
928                 return true;
929             }
930             if (startsWith(p + 9, "spec"))
931             {
932                 params.v.showGaggedErrors = true;
933             }
934             else if (startsWith(p + 9, "context"))
935             {
936                 params.v.printErrorContext = true;
937             }
938             else if (!params.v.errorLimit.parseDigits(p.toDString()[9 .. $]))
939             {
940                 errorInvalidSwitch(p, "Only number, `spec`, or `context` are allowed for `-verrors`");
941                 return true;
942             }
943         }
944         else if (startsWith(p + 1, "verror-supplements"))
945         {
946             if (!params.v.errorSupplementLimit.parseDigits(p.toDString()[20 .. $]))
947             {
948                 errorInvalidSwitch(p, "Only a number is allowed for `-verror-supplements`");
949                 return true;
950             }
951         }
952         else if (startsWith(p + 1, "verror-style="))
953         {
954             const(char)[] style = arg["verror-style=".length + 1 .. $];
955 
956             switch (style)
957             {
958             case "digitalmars":
959                 params.v.messageStyle = MessageStyle.digitalmars;
960                 break;
961             case "gnu":
962                 params.v.messageStyle = MessageStyle.gnu;
963                 break;
964             default:
965                 error("unknown error style '%.*s', must be 'digitalmars' or 'gnu'", cast(int) style.length, style.ptr);
966             }
967         }
968         else if (startsWith(p + 1, "target"))
969         {
970             enum len = "-target=".length;
971             const triple = Triple(p + len);
972             target.setTriple(triple);
973         }
974         else if (startsWith(p + 1, "mcpu")) // https://dlang.org/dmd.html#switch-mcpu
975         {
976             enum len = "-mcpu=".length;
977             // Parse:
978             //      -mcpu=identifier
979             mixin(checkOptionsMixin("mcpu",
980                 "`-mcpu=<architecture>` requires an architecture"));
981             if (Identifier.isValidIdentifier(p + len))
982             {
983                 const ident = p + len;
984                 switch (ident.toDString())
985                 {
986                 case "baseline":
987                     target.cpu = CPU.baseline;
988                     break;
989                 case "avx":
990                     target.cpu = CPU.avx;
991                     break;
992                 case "avx2":
993                     target.cpu = CPU.avx2;
994                     break;
995                 case "native":
996                     target.cpu = CPU.native;
997                     break;
998                 default:
999                     errorInvalidSwitch(p, "Only `baseline`, `avx`, `avx2` or `native` are allowed for `-mcpu`");
1000                     params.help.mcpu = true;
1001                     return false;
1002                 }
1003             }
1004             else
1005             {
1006                 errorInvalidSwitch(p, "Only `baseline`, `avx`, `avx2` or `native` are allowed for `-mcpu`");
1007                 params.help.mcpu = true;
1008                 return false;
1009             }
1010         }
1011         else if (startsWith(p + 1, "os")) // https://dlang.org/dmd.html#switch-os
1012         {
1013             enum len = "-os=".length;
1014             // Parse:
1015             //      -os=identifier
1016             immutable string msg = "Only `host`, `linux`, `windows`, `osx`,`openbsd`, `freebsd`, `solaris`, `dragonflybsd` allowed for `-os`";
1017             if (Identifier.isValidIdentifier(p + len))
1018             {
1019                 const ident = p + len;
1020                 switch (ident.toDString())
1021                 {
1022                 case "host":         target.os = defaultTargetOS();      break;
1023                 case "linux":        target.os = Target.OS.linux;        break;
1024                 case "windows":      target.os = Target.OS.Windows;      break;
1025                 case "osx":          target.os = Target.OS.OSX;          break;
1026                 case "openbsd":      target.os = Target.OS.OpenBSD;      break;
1027                 case "freebsd":      target.os = Target.OS.FreeBSD;      break;
1028                 case "solaris":      target.os = Target.OS.Solaris;      break;
1029                 case "dragonflybsd": target.os = Target.OS.DragonFlyBSD; break;
1030                 default:
1031                     errorInvalidSwitch(p, msg);
1032                     return false;
1033                 }
1034             }
1035             else
1036             {
1037                 errorInvalidSwitch(p, msg);
1038                 return false;
1039             }
1040         }
1041         else if (startsWith(p + 1, "extern-std")) // https://dlang.org/dmd.html#switch-extern-std
1042         {
1043             enum len = "-extern-std=".length;
1044             // Parse:
1045             //      -extern-std=identifier
1046             mixin(checkOptionsMixin("externStd",
1047                 "`-extern-std=<standard>` requires a standard"));
1048             const(char)[] cpprev = arg[len .. $];
1049 
1050             switch (cpprev)
1051             {
1052             case "c++98":
1053                 params.cplusplus = CppStdRevision.cpp98;
1054                 break;
1055             case "c++11":
1056                 params.cplusplus = CppStdRevision.cpp11;
1057                 break;
1058             case "c++14":
1059                 params.cplusplus = CppStdRevision.cpp14;
1060                 break;
1061             case "c++17":
1062                 params.cplusplus = CppStdRevision.cpp17;
1063                 break;
1064             case "c++20":
1065                 params.cplusplus = CppStdRevision.cpp20;
1066                 break;
1067             default:
1068                 error("switch `%s` is invalid", p);
1069                 params.help.externStd = true;
1070                 return false;
1071             }
1072         }
1073         else if (startsWith(p + 1, "transition")) // https://dlang.org/dmd.html#switch-transition
1074         {
1075             enum len = "-transition=".length;
1076             // Parse:
1077             //      -transition=number
1078             mixin(checkOptionsMixin("transition",
1079                 "`-transition=<name>` requires a name"));
1080             if (!parseCLIOption!("transition", Usage.transitions)(params, arg))
1081             {
1082                 // Legacy -transition flags
1083                 // Before DMD 2.085, DMD `-transition` was used for all language flags
1084                 // These are kept for backwards compatibility, but no longer documented
1085                 if (isdigit(cast(char)p[len]))
1086                 {
1087                     uint num;
1088                     if (!num.parseDigits(p.toDString()[len .. $]))
1089                         goto Lerror;
1090 
1091                     // Bugzilla issue number
1092                     switch (num)
1093                     {
1094                         case 3449:
1095                             params.v.field = true;
1096                             break;
1097                         case 14_246:
1098                             params.dtorFields = FeatureState.enabled;
1099                             break;
1100                         case 14_488:
1101                             break;
1102                         case 16_997:
1103                             deprecation(Loc.initial, "`-transition=16997` is now the default behavior");
1104                             break;
1105                         default:
1106                             error("transition `%s` is invalid", p);
1107                             params.help.transition = true;
1108                             return false;
1109                     }
1110                 }
1111                 else if (Identifier.isValidIdentifier(p + len))
1112                 {
1113                     const ident = p + len;
1114                     switch (ident.toDString())
1115                     {
1116                         case "dtorfields":
1117                             params.dtorFields = FeatureState.enabled;
1118                             break;
1119                         case "intpromote":
1120                             deprecation(Loc.initial, "`-transition=intpromote` is now the default behavior");
1121                             break;
1122                         default:
1123                             error("transition `%s` is invalid", p);
1124                             params.help.transition = true;
1125                             return false;
1126                     }
1127                 }
1128                 errorInvalidSwitch(p);
1129                 params.help.transition = true;
1130                 return false;
1131             }
1132         }
1133         else if (startsWith(p + 1, "preview") ) // https://dlang.org/dmd.html#switch-preview
1134         {
1135             enum len = "-preview=".length;
1136             // Parse:
1137             //      -preview=name
1138             mixin(checkOptionsMixin("preview",
1139                 "`-preview=<name>` requires a name"));
1140 
1141             if (!parseCLIOption!("preview", Usage.previews)(params, arg))
1142             {
1143                 error("preview `%s` is invalid", p);
1144                 params.help.preview = true;
1145                 return false;
1146             }
1147 
1148             if (params.useDIP1021)
1149                 params.useDIP1000 = FeatureState.enabled;    // dip1021 implies dip1000
1150 
1151             // copy previously standalone flags from -transition
1152             // -preview=dip1000 implies -preview=dip25 too
1153             if (params.useDIP1000 == FeatureState.enabled)
1154                 params.useDIP25 = FeatureState.enabled;
1155         }
1156         else if (startsWith(p + 1, "revert") ) // https://dlang.org/dmd.html#switch-revert
1157         {
1158             enum len = "-revert=".length;
1159             // Parse:
1160             //      -revert=name
1161             mixin(checkOptionsMixin("revert",
1162                 "`-revert=<name>` requires a name"));
1163 
1164             if (!parseCLIOption!("revert", Usage.reverts)(params, arg))
1165             {
1166                 error("revert `%s` is invalid", p);
1167                 params.help.revert = true;
1168                 return false;
1169             }
1170         }
1171         else if (arg == "-w")   // https://dlang.org/dmd.html#switch-w
1172             params.warnings = DiagnosticReporting.error;
1173         else if (arg == "-wi")  // https://dlang.org/dmd.html#switch-wi
1174             params.warnings = DiagnosticReporting.inform;
1175         else if (arg == "-wo")  // https://dlang.org/dmd.html#switch-wo
1176         {
1177             // Obsolete features has been obsoleted until a DIP for "additions"
1178             // has been drafted and ratified in the language spec.
1179             // Rather, these old features will just be accepted without warning.
1180         }
1181         else if (arg == "-O")   // https://dlang.org/dmd.html#switch-O
1182             driverParams.optimize = true;
1183         else if (arg == "-o-")  // https://dlang.org/dmd.html#switch-o-
1184             params.obj = false;
1185         else if (p[1] == 'o')
1186         {
1187             const(char)* path;
1188             switch (p[2])
1189             {
1190             case 'd':                       // https://dlang.org/dmd.html#switch-od
1191                 if (!p[3])
1192                     goto Lnoarg;
1193                 path = p + 3 + (p[3] == '=');
1194                 version (Windows)
1195                 {
1196                     path = toWinPath(path);
1197                 }
1198                 params.objdir = path.toDString;
1199                 break;
1200             case 'f':                       // https://dlang.org/dmd.html#switch-of
1201                 if (!p[3])
1202                     goto Lnoarg;
1203                 path = p + 3 + (p[3] == '=');
1204                 version (Windows)
1205                 {
1206                     path = toWinPath(path);
1207                 }
1208                 params.objname = path.toDString;
1209                 break;
1210             case 'p':                       // https://dlang.org/dmd.html#switch-op
1211                 if (p[3])
1212                     goto Lerror;
1213                 params.preservePaths = true;
1214                 break;
1215             case 0:
1216                 error("-o no longer supported, use -of or -od");
1217                 break;
1218             default:
1219                 goto Lerror;
1220             }
1221         }
1222         else if (p[1] == 'D')       // https://dlang.org/dmd.html#switch-D
1223         {
1224             params.ddoc.doOutput = true;
1225             switch (p[2])
1226             {
1227             case 'd':               // https://dlang.org/dmd.html#switch-Dd
1228                 if (!p[3])
1229                     goto Lnoarg;
1230                 params.ddoc.dir = (p + 3 + (p[3] == '=')).toDString();
1231                 break;
1232             case 'f':               // https://dlang.org/dmd.html#switch-Df
1233                 if (!p[3])
1234                     goto Lnoarg;
1235                 params.ddoc.name = (p + 3 + (p[3] == '=')).toDString();
1236                 break;
1237             case 0:
1238                 break;
1239             default:
1240                 goto Lerror;
1241             }
1242         }
1243         else if (p[1] == 'H' && p[2] == 'C')  // https://dlang.org/dmd.html#switch-HC
1244         {
1245             params.cxxhdr.doOutput = true;
1246             switch (p[3])
1247             {
1248             case 'd':               // https://dlang.org/dmd.html#switch-HCd
1249                 if (!p[4])
1250                     goto Lnoarg;
1251                 params.cxxhdr.dir = (p + 4 + (p[4] == '=')).toDString;
1252                 break;
1253             case 'f':               // https://dlang.org/dmd.html#switch-HCf
1254                 if (!p[4])
1255                     goto Lnoarg;
1256                 params.cxxhdr.name = (p + 4 + (p[4] == '=')).toDString;
1257                 break;
1258             case '=':
1259                 enum len = "-HC=".length;
1260                 mixin(checkOptionsMixin("hc", "`-HC=<mode>` requires a valid mode"));
1261                 const mode = arg[len .. $];
1262                 switch (mode)
1263                 {
1264                     case "silent":
1265                         /* already set above */
1266                         break;
1267                     case "verbose":
1268                         params.cxxhdr.fullOutput = true;
1269                         break;
1270                     default:
1271                         errorInvalidSwitch(p);
1272                         params.help.hc = true;
1273                         return false;
1274                 }
1275                 break;
1276             case 0:
1277                 break;
1278             default:
1279                 goto Lerror;
1280             }
1281         }
1282         else if (p[1] == 'H')       // https://dlang.org/dmd.html#switch-H
1283         {
1284             params.dihdr.doOutput = true;
1285             switch (p[2])
1286             {
1287             case 'd':               // https://dlang.org/dmd.html#switch-Hd
1288                 if (!p[3])
1289                     goto Lnoarg;
1290                 params.dihdr.dir = (p + 3 + (p[3] == '=')).toDString;
1291                 break;
1292             case 'f':               // https://dlang.org/dmd.html#switch-Hf
1293                 if (!p[3])
1294                     goto Lnoarg;
1295                 params.dihdr.name = (p + 3 + (p[3] == '=')).toDString;
1296                 break;
1297             case 0:
1298                 break;
1299             default:
1300                 goto Lerror;
1301             }
1302         }
1303         else if (startsWith(p + 1, "Xcc="))
1304         {
1305             params.linkswitches.push(p + 5);
1306             params.linkswitchIsForCC.push(true);
1307         }
1308         else if (p[1] == 'X')       // https://dlang.org/dmd.html#switch-X
1309         {
1310             params.json.doOutput = true;
1311             switch (p[2])
1312             {
1313             case 'f':               // https://dlang.org/dmd.html#switch-Xf
1314                 if (!p[3])
1315                     goto Lnoarg;
1316                 params.json.name = (p + 3 + (p[3] == '=')).toDString;
1317                 break;
1318             case 'i':
1319                 if (!p[3])
1320                     goto Lnoarg;
1321                 if (p[3] != '=')
1322                     goto Lerror;
1323                 if (!p[4])
1324                     goto Lnoarg;
1325 
1326                 {
1327                     auto flag = tryParseJsonField(p + 4);
1328                     if (!flag)
1329                     {
1330                         error("unknown JSON field `-Xi=%s`, expected one of " ~ jsonFieldNames, p + 4);
1331                         continue;
1332                     }
1333                     global.params.jsonFieldFlags |= flag;
1334                 }
1335                 break;
1336             case 0:
1337                 break;
1338             default:
1339                 goto Lerror;
1340             }
1341         }
1342         else if (arg == "-ignore")      // https://dlang.org/dmd.html#switch-ignore
1343             params.ignoreUnsupportedPragmas = true;
1344         else if (arg == "-inline")      // https://dlang.org/dmd.html#switch-inline
1345         {
1346             params.useInline = true;
1347             params.dihdr.fullOutput = true;
1348         }
1349         else if (arg == "-i")
1350             includeImports = true;
1351         else if (startsWith(p + 1, "i="))
1352         {
1353             includeImports = true;
1354             if (!p[3])
1355             {
1356                 error("invalid option '%s', module patterns cannot be empty", p);
1357             }
1358             else
1359             {
1360                 // NOTE: we could check that the argument only contains valid "module-pattern" characters.
1361                 //       Invalid characters doesn't break anything but an error message to the user might
1362                 //       be nice.
1363                 includeModulePatterns.push(p + 3);
1364             }
1365         }
1366         else if (arg == "-dip25")       // https://dlang.org/dmd.html#switch-dip25
1367         {
1368             // @@@ DEPRECATION 2.112 @@@
1369             deprecation(Loc.initial, "`-dip25` no longer has any effect");
1370             params.useDIP25 =  FeatureState.enabled;
1371         }
1372         else if (arg == "-dip1000")
1373         {
1374             params.useDIP25 = FeatureState.enabled;
1375             params.useDIP1000 = FeatureState.enabled;
1376         }
1377         else if (arg == "-dip1008")
1378         {
1379             params.ehnogc = true;
1380         }
1381         else if (arg == "-lib")         // https://dlang.org/dmd.html#switch-lib
1382             driverParams.lib = true;
1383         else if (arg == "-nofloat")
1384             driverParams.nofloat = true;
1385         else if (arg == "-quiet")
1386         {
1387             // Ignore
1388         }
1389         else if (arg == "-release")     // https://dlang.org/dmd.html#switch-release
1390             params.release = true;
1391         else if (arg == "-betterC")     // https://dlang.org/dmd.html#switch-betterC
1392         {
1393             params.betterC = true;
1394             params.allInst = true;
1395         }
1396         else if (arg == "-noboundscheck") // https://dlang.org/dmd.html#switch-noboundscheck
1397         {
1398             params.boundscheck = CHECKENABLE.off;
1399         }
1400         else if (startsWith(p + 1, "boundscheck")) // https://dlang.org/dmd.html#switch-boundscheck
1401         {
1402             // Parse:
1403             //      -boundscheck=[on|safeonly|off]
1404             if (p[12] == '=')
1405             {
1406                 const(char)[] boundscheck = arg[13 .. $];
1407 
1408                 switch (boundscheck)
1409                 {
1410                 case "on":
1411                     params.boundscheck = CHECKENABLE.on;
1412                     break;
1413                 case "safeonly":
1414                     params.boundscheck = CHECKENABLE.safeonly;
1415                     break;
1416                 case "off":
1417                     params.boundscheck = CHECKENABLE.off;
1418                     break;
1419                 default:
1420                     goto Lerror;
1421                 }
1422             }
1423             else
1424                 goto Lerror;
1425         }
1426         else if (arg == "-nothrow") // https://dlang.org/dmd.html#switch-nothrow
1427         {
1428             params.useExceptions = false;
1429         }
1430         else if (arg == "-unittest")
1431             params.useUnitTests = true;
1432         else if (p[1] == 'I')              // https://dlang.org/dmd.html#switch-I
1433         {
1434             if (!params.imppath)
1435                 params.imppath = new Strings();
1436             params.imppath.push(p + 2 + (p[2] == '='));
1437         }
1438         else if (p[1] == 'm' && p[2] == 'v' && p[3] == '=') // https://dlang.org/dmd.html#switch-mv
1439         {
1440             if (p[4] && strchr(p + 5, '='))
1441             {
1442                 params.modFileAliasStrings.push(p + 4);
1443             }
1444             else
1445                 goto Lerror;
1446         }
1447         else if (p[1] == 'J')             // https://dlang.org/dmd.html#switch-J
1448         {
1449             if (!params.fileImppath)
1450                 params.fileImppath = new Strings();
1451             params.fileImppath.push(p + 2 + (p[2] == '='));
1452         }
1453         else if (startsWith(p + 1, "debug") && p[6] != 'l') // https://dlang.org/dmd.html#switch-debug
1454         {
1455             // Parse:
1456             //      -debug
1457             //      -debug=number
1458             //      -debug=identifier
1459             if (p[6] == '=')
1460             {
1461                 if (isdigit(cast(char)p[7]))
1462                 {
1463                     if (!params.debuglevel.parseDigits(p.toDString()[7 .. $]))
1464                         goto Lerror;
1465 
1466                     // @@@DEPRECATED_2.111@@@
1467                     // Deprecated in 2.101, remove in 2.111
1468                     deprecation(Loc.initial, "`-debug=number` is deprecated, use debug identifiers instead");
1469                 }
1470                 else if (Identifier.isValidIdentifier(p + 7))
1471                 {
1472                     DebugCondition.addGlobalIdent((p + 7).toDString());
1473                 }
1474                 else
1475                     goto Lerror;
1476             }
1477             else if (p[6])
1478                 goto Lerror;
1479             else
1480                 params.debuglevel = 1;
1481         }
1482         else if (startsWith(p + 1, "version")) // https://dlang.org/dmd.html#switch-version
1483         {
1484             // Parse:
1485             //      -version=number
1486             //      -version=identifier
1487             if (p[8] == '=')
1488             {
1489                 if (isdigit(cast(char)p[9]))
1490                 {
1491                     if (!params.versionlevel.parseDigits(p.toDString()[9 .. $]))
1492                         goto Lerror;
1493 
1494                     // @@@DEPRECATED_2.111@@@
1495                     // Deprecated in 2.101, remove in 2.111
1496                     deprecation(Loc.initial, "`-version=number` is deprecated, use version identifiers instead");
1497                 }
1498                 else if (Identifier.isValidIdentifier(p + 9))
1499                 {
1500                     VersionCondition.addGlobalIdent((p+9).toDString());
1501                 }
1502                 else
1503                     goto Lerror;
1504             }
1505             else
1506                 goto Lerror;
1507         }
1508         else if (arg == "--b")
1509             driverParams.debugb = true;
1510         else if (arg == "--c")
1511             driverParams.debugc = true;
1512         else if (arg == "--f")
1513             driverParams.debugf = true;
1514         else if (arg == "--help" ||
1515                  arg == "-h")
1516         {
1517             params.help.usage = true;
1518             return false;
1519         }
1520         else if (arg == "--r")
1521             driverParams.debugr = true;
1522         else if (arg == "--version")
1523         {
1524             params.v.logo = true;
1525             return false;
1526         }
1527         else if (arg == "--x")
1528             driverParams.debugx = true;
1529         else if (arg == "--y")
1530             driverParams.debugy = true;
1531         else if (p[1] == 'L')                        // https://dlang.org/dmd.html#switch-L
1532         {
1533             params.linkswitches.push(p + 2 + (p[2] == '='));
1534             params.linkswitchIsForCC.push(false);
1535         }
1536         else if (p[1] == 'P')                        // https://dlang.org/dmd.html#switch-P
1537         {
1538             params.cppswitches.push(p + 2 + (p[2] == '='));
1539         }
1540         else if (startsWith(p + 1, "defaultlib="))   // https://dlang.org/dmd.html#switch-defaultlib
1541         {
1542             driverParams.defaultlibname = (p + 1 + 11).toDString;
1543         }
1544         else if (startsWith(p + 1, "debuglib="))     // https://dlang.org/dmd.html#switch-debuglib
1545         {
1546             driverParams.debuglibname = (p + 1 + 9).toDString;
1547         }
1548         else if (startsWith(p + 1, "deps"))          // https://dlang.org/dmd.html#switch-deps
1549         {
1550             if (params.moduleDeps.doOutput)
1551             {
1552                 error("-deps[=file] can only be provided once!");
1553                 break;
1554             }
1555             if (p[5] == '=')
1556             {
1557                 params.moduleDeps.name = (p + 1 + 5).toDString;
1558                 if (!params.moduleDeps.name[0])
1559                     goto Lnoarg;
1560             }
1561             else if (p[5] != '\0')
1562             {
1563                 // Else output to stdout.
1564                 goto Lerror;
1565             }
1566             params.moduleDeps.buffer = new OutBuffer();
1567         }
1568         else if (startsWith(p + 1, "makedeps"))          // https://dlang.org/dmd.html#switch-makedeps
1569         {
1570             if (params.makeDeps.name)
1571             {
1572                 error("-makedeps[=file] can only be provided once!");
1573                 break;
1574             }
1575             if (p[9] == '=')
1576             {
1577                 if (p[10] == '\0')
1578                 {
1579                     error("expected filename after -makedeps=");
1580                     break;
1581                 }
1582                 params.makeDeps.name = (p + 10).toDString;
1583             }
1584             else if (p[9] != '\0')
1585             {
1586                 goto Lerror;
1587             }
1588             // Else output to stdout.
1589             params.makeDeps.doOutput = true;
1590         }
1591         else if (arg == "-main")             // https://dlang.org/dmd.html#switch-main
1592         {
1593             params.addMain = true;
1594         }
1595         else if (startsWith(p + 1, "man"))   // https://dlang.org/dmd.html#switch-man
1596         {
1597             params.help.manual = true;
1598             return false;
1599         }
1600         else if (arg == "-run")              // https://dlang.org/dmd.html#switch-run
1601         {
1602             params.run = true;
1603             size_t length = argc - i - 1;
1604             if (length)
1605             {
1606                 const(char)[] runarg = arguments[i + 1].toDString();
1607                 const(char)[] ext = FileName.ext(runarg);
1608                 if (ext &&
1609                     FileName.equals(ext, mars_ext) == 0 &&
1610                     FileName.equals(ext, hdr_ext) == 0 &&
1611                     FileName.equals(ext, i_ext) == 0 &&
1612                     FileName.equals(ext, c_ext) == 0)
1613                 {
1614                     error("-run must be followed by a source file, not '%s'", arguments[i + 1]);
1615                     break;
1616                 }
1617                 if (runarg == "-")
1618                     files.push("__stdin.d");
1619                 else
1620                     files.push(arguments[i + 1]);
1621                 params.runargs.setDim(length - 1);
1622                 for (size_t j = 0; j < length - 1; ++j)
1623                 {
1624                     params.runargs[j] = arguments[i + 2 + j];
1625                 }
1626                 i += length;
1627             }
1628             else
1629             {
1630                 params.run = false;
1631                 goto Lnoarg;
1632             }
1633         }
1634         else if (p[1] == '\0')
1635             files.push("__stdin.d");
1636         else
1637         {
1638         Lerror:
1639             error("unrecognized switch '%s'", arguments[i]);
1640             continue;
1641         Lnoarg:
1642             error("argument expected for switch '%s'", arguments[i]);
1643             continue;
1644         }
1645     }
1646     return errors;
1647 }
1648 
1649 /// Sets the boolean for a flag with the given name
1650 private static void setFlagFor(string name, ref bool b) @safe
1651 {
1652     b = name != "revert";
1653 }
1654 
1655 /// Sets the FeatureState for a flag with the given name
1656 private static void setFlagFor(string name, ref FeatureState s) @safe
1657 {
1658     s = name != "revert" ? FeatureState.enabled : FeatureState.disabled;
1659 }
1660 
1661 /**
1662 Creates the module based on the file provided
1663 
1664 The file is dispatched in one of the various arrays
1665 (global.params.{ddoc.files,dllfiles,jsonfiles,etc...})
1666 according to its extension.
1667 If it is a binary file, it is added to libmodules.
1668 
1669 Params:
1670   file = File name to dispatch
1671   libmodules = Array to which binaries (shared/static libs and object files)
1672                will be appended
1673   target = target system
1674 
1675 Returns:
1676   A D module
1677 */
1678 private
1679 Module createModule(const(char)* file, ref Strings libmodules, const ref Target target)
1680 {
1681     const(char)[] name;
1682     version (Windows)
1683     {
1684         file = toWinPath(file);
1685     }
1686     const(char)[] p = file.toDString();
1687     p = FileName.name(p); // strip path
1688     const(char)[] ext = FileName.ext(p);
1689     if (!ext)
1690     {
1691         if (!p.length)
1692         {
1693             error(Loc.initial, "invalid file name '%s'", file);
1694             fatal();
1695         }
1696         auto id = Identifier.idPool(p);
1697         return new Module(file.toDString, id, global.params.ddoc.doOutput, global.params.dihdr.doOutput);
1698     }
1699 
1700     /* Deduce what to do with a file based on its extension
1701         */
1702     if (FileName.equals(ext, target.obj_ext))
1703     {
1704         global.params.objfiles.push(file);
1705         libmodules.push(file);
1706         return null;
1707     }
1708     if (FileName.equals(ext, target.lib_ext))
1709     {
1710         global.params.libfiles.push(file);
1711         libmodules.push(file);
1712         return null;
1713     }
1714     if (target.os & (Target.OS.linux | Target.OS.OSX| Target.OS.FreeBSD | Target.OS.OpenBSD | Target.OS.Solaris | Target.OS.DragonFlyBSD))
1715     {
1716         if (FileName.equals(ext, target.dll_ext))
1717         {
1718             global.params.dllfiles.push(file);
1719             libmodules.push(file);
1720             return null;
1721         }
1722     }
1723     if (FileName.equals(ext, ddoc_ext))
1724     {
1725         global.params.ddoc.files.push(file);
1726         return null;
1727     }
1728     if (FileName.equals(ext, json_ext))
1729     {
1730         global.params.json.doOutput = true;
1731         global.params.json.name = file.toDString;
1732         return null;
1733     }
1734     if (FileName.equals(ext, map_ext))
1735     {
1736         global.params.mapfile = file.toDString;
1737         return null;
1738     }
1739     if (target.os == Target.OS.Windows)
1740     {
1741         if (FileName.equals(ext, "res"))
1742         {
1743             global.params.resfile = file.toDString;
1744             return null;
1745         }
1746         if (FileName.equals(ext, "def"))
1747         {
1748             global.params.deffile = file.toDString;
1749             return null;
1750         }
1751         if (FileName.equals(ext, "exe"))
1752         {
1753             assert(0); // should have already been handled
1754         }
1755     }
1756     /* Examine extension to see if it is a valid
1757      * D, Ddoc or C source file extension
1758      */
1759     if (FileName.equals(ext, mars_ext) ||
1760         FileName.equals(ext, hdr_ext ) ||
1761         FileName.equals(ext, dd_ext  ) ||
1762         FileName.equals(ext, c_ext   ) ||
1763         FileName.equals(ext, i_ext   ))
1764     {
1765         name = FileName.removeExt(p);
1766         if (!name.length || name == ".." || name == ".")
1767         {
1768             error(Loc.initial, "invalid file name '%s'", file);
1769             fatal();
1770         }
1771     }
1772     else
1773     {
1774         error(Loc.initial, "unrecognized file extension %.*s", cast(int)ext.length, ext.ptr);
1775         fatal();
1776     }
1777 
1778     /* At this point, name is the D source file name stripped of
1779      * its path and extension.
1780      */
1781     auto id = Identifier.idPool(name);
1782 
1783     return new Module(file.toDString, id, global.params.ddoc.doOutput, global.params.dihdr.doOutput);
1784 }
1785 
1786 /**
1787 Creates the list of modules based on the files provided
1788 
1789 Files are dispatched in the various arrays
1790 (global.params.{ddocfiles,dllfiles,jsonfiles,etc...})
1791 according to their extension.
1792 Binary files are added to libmodules.
1793 
1794 Params:
1795   files = File names to dispatch
1796   libmodules = Array to which binaries (shared/static libs and object files)
1797                will be appended
1798   target = target system
1799 
1800 Returns:
1801   An array of path to D modules
1802 */
1803 Modules createModules(ref Strings files, ref Strings libmodules, const ref Target target)
1804 {
1805     Modules modules;
1806     modules.reserve(files.length);
1807     bool firstmodule = true;
1808     foreach(file; files)
1809     {
1810         auto m = createModule(file, libmodules, target);
1811 
1812         if (m is null)
1813             continue;
1814 
1815         modules.push(m);
1816         if (firstmodule)
1817         {
1818             global.params.objfiles.push(m.objfile.toChars());
1819             firstmodule = false;
1820         }
1821     }
1822     return modules;
1823 }
1824 
1825 /// Returns: a compiled module (semantic3) containing an empty main() function, for the -main flag
1826 Module moduleWithEmptyMain()
1827 {
1828     auto result = new Module("__main.d", Identifier.idPool("__main"), false, false);
1829     // need 2 trailing nulls for sentinel and 2 for lexer
1830     auto data = arraydup("version(D_BetterC)extern(C)int main(){return 0;}else int main(){return 0;}\0\0\0\0");
1831     result.src = cast(ubyte[]) data[0 .. $-4];
1832     result.parse();
1833     result.importedFrom = result;
1834     result.importAll(null);
1835     result.dsymbolSemantic(null);
1836     result.semantic2(null);
1837     result.semantic3(null);
1838     return result;
1839 }