1 
2 /**
3  * Entry point for DMD.
4  *
5  * This modules defines the entry point (main) for DMD, as well as related
6  * utilities needed for arguments parsing, path manipulation, etc...
7  * This file is not shared with other compilers which use the DMD front-end.
8  *
9  * Copyright:   Copyright (C) 1999-2023 by The D Language Foundation, All Rights Reserved
10  * Authors:     $(LINK2 https://www.digitalmars.com, Walter Bright)
11  * License:     $(LINK2 https://www.boost.org/LICENSE_1_0.txt, Boost License 1.0)
12  * Source:      $(LINK2 https://github.com/dlang/dmd/blob/master/src/dmd/mars.d, _mars.d)
13  * Documentation:  https://dlang.org/phobos/dmd_mars.html
14  * Coverage:    https://codecov.io/gh/dlang/dmd/src/master/src/dmd/mars.d
15  */
16 
17 module dmd.mars;
18 
19 import core.stdc.ctype;
20 import core.stdc.limits;
21 import core.stdc.stdio;
22 import core.stdc.stdlib;
23 import core.stdc.string;
24 
25 import dmd.arraytypes;
26 import dmd.astcodegen;
27 import dmd.astenums;
28 import dmd.builtin;
29 import dmd.cond;
30 import dmd.console;
31 import dmd.compiler;
32 import dmd.cpreprocess;
33 import dmd.dmdparams;
34 import dmd.dinifile;
35 import dmd.dinterpret;
36 import dmd.dmodule;
37 import dmd.doc;
38 import dmd.dsymbol;
39 import dmd.dsymbolsem;
40 import dmd.dtemplate;
41 import dmd.dtoh;
42 import dmd.errors;
43 import dmd.expression;
44 import dmd.file_manager;
45 import dmd.globals;
46 import dmd.hdrgen;
47 import dmd.id;
48 import dmd.identifier;
49 import dmd.inline;
50 import dmd.location;
51 import dmd.json;
52 version (NoMain) {} else
53 {
54     import dmd.glue : generateCodeAndWrite;
55     import dmd.dmsc : backend_init, backend_term;
56     import dmd.link;
57     import dmd.vsoptions;
58 }
59 import dmd.mtype;
60 import dmd.objc;
61 import dmd.root.array;
62 import dmd.root.file;
63 import dmd.root.filename;
64 import dmd.root.man;
65 import dmd.common.outbuffer;
66 import dmd.root.response;
67 import dmd.root.rmem;
68 import dmd.root.string;
69 import dmd.root.stringtable;
70 import dmd.semantic2;
71 import dmd.semantic3;
72 import dmd.target;
73 import dmd.utils;
74 
75 /**
76  * Print DMD's logo on stdout
77  */
78 private void logo()
79 {
80     printf("DMD%llu D Compiler %.*s\n%.*s %.*s\n",
81         cast(ulong)size_t.sizeof * 8,
82         cast(int) global.versionString().length, global.versionString().ptr,
83         cast(int)global.copyright.length, global.copyright.ptr,
84         cast(int)global.written.length, global.written.ptr
85     );
86 }
87 
88 /**
89 Print DMD's logo with more debug information and error-reporting pointers.
90 
91 Params:
92     stream = output stream to print the information on
93 */
94 private void printInternalFailure(FILE* stream)
95 {
96     fputs(("---\n" ~
97     "ERROR: This is a compiler bug.\n" ~
98             "Please report it via https://issues.dlang.org/enter_bug.cgi\n" ~
99             "with, preferably, a reduced, reproducible example and the information below.\n" ~
100     "DustMite (https://github.com/CyberShadow/DustMite/wiki) can help with the reduction.\n" ~
101     "---\n").ptr, stream);
102     stream.fprintf("DMD %.*s\n", cast(int) global.versionString().length, global.versionString().ptr);
103     stream.printPredefinedVersions;
104     stream.printGlobalConfigs();
105     fputs("---\n".ptr, stream);
106 }
107 
108 /**
109  * Print DMD's usage message on stdout
110  */
111 private void usage()
112 {
113     import dmd.cli : CLIUsage;
114     logo();
115     auto help = CLIUsage.usage;
116     const inifileCanon = FileName.canonicalName(global.inifilename);
117     printf("
118 Documentation: https://dlang.org/
119 Config file: %.*s
120 Usage:
121   dmd [<option>...] <file>...
122   dmd [<option>...] -run <file> [<arg>...]
123 
124 Where:
125   <file>           D source file
126   <arg>            Argument to pass when running the resulting program
127 
128 <option>:
129   @<cmdfile>       read arguments from cmdfile
130 %.*s", cast(int)inifileCanon.length, inifileCanon.ptr, cast(int)help.length, &help[0]);
131 }
132 
133 /**
134  * DMD's real entry point
135  *
136  * Parses command line arguments and config file, open and read all
137  * provided source file and do semantic analysis on them.
138  *
139  * Params:
140  *   argc = Number of arguments passed via command line
141  *   argv = Array of string arguments passed via command line
142  *
143  * Returns:
144  *   Application return code
145  */
146 version (NoMain) {} else
147 private int tryMain(size_t argc, const(char)** argv, ref Param params)
148 {
149     Strings files;
150     Strings libmodules;
151     global._init();
152 
153     if (parseCommandlineAndConfig(argc, argv, params, files))
154         return EXIT_FAILURE;
155 
156     global.compileEnv.previewIn = global.params.previewIn;
157     global.compileEnv.ddocOutput = global.params.ddoc.doOutput;
158     global.compileEnv.shortenedMethods = global.params.shortenedMethods;
159 
160     if (params.usage)
161     {
162         usage();
163         return EXIT_SUCCESS;
164     }
165 
166     if (params.logo)
167     {
168         logo();
169         return EXIT_SUCCESS;
170     }
171 
172     /*
173     Prints a supplied usage text to the console and
174     returns the exit code for the help usage page.
175 
176     Returns:
177         `EXIT_SUCCESS` if no errors occurred, `EXIT_FAILURE` otherwise
178     */
179     static int printHelpUsage(string help)
180     {
181         printf("%.*s", cast(int)help.length, &help[0]);
182         return global.errors ? EXIT_FAILURE : EXIT_SUCCESS;
183     }
184 
185     /*
186     Print a message to make it clear when warnings are treated as errors.
187     */
188     static void errorOnWarning()
189     {
190         error(Loc.initial, "warnings are treated as errors");
191         errorSupplemental(Loc.initial, "Use -wi if you wish to treat warnings only as informational.");
192     }
193 
194     /*
195     Generates code to check for all `params` whether any usage page
196     has been requested.
197     If so, the generated code will print the help page of the flag
198     and return with an exit code.
199 
200     Params:
201         params = parameters with `Usage` suffices in `params` for which
202         their truthness should be checked.
203 
204     Returns: generated code for checking the usage pages of the provided `params`.
205     */
206     static string generateUsageChecks(string[] params)
207     {
208         string s;
209         foreach (n; params)
210         {
211             s ~= q{
212                 if (params..}~n~q{Usage)
213                     return printHelpUsage(CLIUsage..}~n~q{Usage);
214             };
215         }
216         return s;
217     }
218     import dmd.cli : CLIUsage;
219     mixin(generateUsageChecks(["mcpu", "transition", "check", "checkAction",
220         "preview", "revert", "externStd", "hc"]));
221 
222     if (params.manual)
223     {
224         version (Windows)
225         {
226             browse("https://dlang.org/dmd-windows.html");
227         }
228         version (linux)
229         {
230             browse("https://dlang.org/dmd-linux.html");
231         }
232         version (OSX)
233         {
234             browse("https://dlang.org/dmd-osx.html");
235         }
236         version (FreeBSD)
237         {
238             browse("https://dlang.org/dmd-freebsd.html");
239         }
240         /*NOTE: No regular builds for openbsd/dragonflybsd (yet) */
241         /*
242         version (OpenBSD)
243         {
244             browse("https://dlang.org/dmd-openbsd.html");
245         }
246         version (DragonFlyBSD)
247         {
248             browse("https://dlang.org/dmd-dragonflybsd.html");
249         }
250         */
251         return EXIT_SUCCESS;
252     }
253 
254     if (params.color)
255         global.console = cast(void*) createConsole(core.stdc.stdio.stderr);
256 
257     target.setCPU();
258     Loc.set(params.showColumns, params.messageStyle);
259 
260     if (global.errors)
261     {
262         fatal();
263     }
264     if (files.length == 0)
265     {
266         if (params.jsonFieldFlags)
267         {
268             generateJson(null);
269             return EXIT_SUCCESS;
270         }
271         usage();
272         return EXIT_FAILURE;
273     }
274 
275     reconcileCommands(params, target);
276 
277     // Add in command line versions
278     if (params.versionids)
279         foreach (charz; *params.versionids)
280             VersionCondition.addGlobalIdent(charz.toDString());
281     if (params.debugids)
282         foreach (charz; *params.debugids)
283             DebugCondition.addGlobalIdent(charz.toDString());
284 
285     setDefaultLibrary(params, target);
286 
287     // Initialization
288     target._init(params);
289     Type._init();
290     Id.initialize();
291     Module._init();
292     Expression._init();
293     Objc._init();
294 
295     reconcileLinkRunLib(params, files.length, target.obj_ext);
296     version(CRuntime_Microsoft)
297     {
298         import dmd.root.longdouble;
299         initFPU();
300     }
301     import dmd.root.ctfloat : CTFloat;
302     CTFloat.initialize();
303 
304     // Predefined version identifiers
305     addDefaultVersionIdentifiers(params, target);
306 
307     if (params.verbose)
308     {
309         stdout.printPredefinedVersions();
310         stdout.printGlobalConfigs();
311     }
312     //printf("%d source files\n", cast(int) files.length);
313 
314     // Build import search path
315 
316     static Strings* buildPath(Strings* imppath)
317     {
318         Strings* result = null;
319         if (imppath)
320         {
321             foreach (const path; *imppath)
322             {
323                 Strings* a = FileName.splitPath(path);
324                 if (a)
325                 {
326                     if (!result)
327                         result = new Strings();
328                     result.append(a);
329                 }
330             }
331         }
332         return result;
333     }
334 
335     if (params.mixinOut.doOutput)
336     {
337         params.mixinOut.buffer = cast(OutBuffer*)Mem.check(calloc(1, OutBuffer.sizeof));
338         atexit(&flushMixins); // see comment for flushMixins
339     }
340     scope(exit) flushMixins();
341     global.path = buildPath(params.imppath);
342     global.filePath = buildPath(params.fileImppath);
343 
344     // Create Modules
345     Modules modules = createModules(files, libmodules, target);
346     // Read files
347     foreach (m; modules)
348     {
349         m.read(Loc.initial);
350     }
351 
352     // Parse files
353     bool anydocfiles = false;
354     size_t filecount = modules.length;
355     for (size_t filei = 0, modi = 0; filei < filecount; filei++, modi++)
356     {
357         Module m = modules[modi];
358         if (params.verbose)
359             message("parse     %s", m.toChars());
360         if (!Module.rootModule)
361             Module.rootModule = m;
362         m.importedFrom = m; // m.isRoot() == true
363 //        if (!driverParams.oneobj || modi == 0 || m.isDocFile)
364 //            m.deleteObjFile();
365 
366         m.parse();
367         if (m.filetype == FileType.dhdr)
368         {
369             // Remove m's object file from list of object files
370             for (size_t j = 0; j < params.objfiles.length; j++)
371             {
372                 if (m.objfile.toChars() == params.objfiles[j])
373                 {
374                     params.objfiles.remove(j);
375                     break;
376                 }
377             }
378             if (params.objfiles.length == 0)
379                 driverParams.link = false;
380         }
381         if (m.filetype == FileType.ddoc)
382         {
383             anydocfiles = true;
384             gendocfile(m);
385             // Remove m from list of modules
386             modules.remove(modi);
387             modi--;
388             // Remove m's object file from list of object files
389             for (size_t j = 0; j < params.objfiles.length; j++)
390             {
391                 if (m.objfile.toChars() == params.objfiles[j])
392                 {
393                     params.objfiles.remove(j);
394                     break;
395                 }
396             }
397             if (params.objfiles.length == 0)
398                 driverParams.link = false;
399         }
400     }
401 
402     if (anydocfiles && modules.length && (driverParams.oneobj || params.objname))
403     {
404         error(Loc.initial, "conflicting Ddoc and obj generation options");
405         fatal();
406     }
407     if (global.errors)
408         fatal();
409 
410     if (params.dihdr.doOutput)
411     {
412         /* Generate 'header' import files.
413          * Since 'header' import files must be independent of command
414          * line switches and what else is imported, they are generated
415          * before any semantic analysis.
416          */
417         foreach (m; modules)
418         {
419             if (m.filetype == FileType.dhdr)
420                 continue;
421             if (params.verbose)
422                 message("import    %s", m.toChars());
423             genhdrfile(m);
424         }
425     }
426     if (global.errors)
427         removeHdrFilesAndFail(params, modules);
428 
429     // load all unconditional imports for better symbol resolving
430     foreach (m; modules)
431     {
432         if (params.verbose)
433             message("importall %s", m.toChars());
434         m.importAll(null);
435     }
436     if (global.errors)
437         removeHdrFilesAndFail(params, modules);
438 
439     backend_init();
440 
441     // Do semantic analysis
442     foreach (m; modules)
443     {
444         if (params.verbose)
445             message("semantic  %s", m.toChars());
446         m.dsymbolSemantic(null);
447     }
448     //if (global.errors)
449     //    fatal();
450     Module.runDeferredSemantic();
451     if (Module.deferred.length)
452     {
453         for (size_t i = 0; i < Module.deferred.length; i++)
454         {
455             Dsymbol sd = Module.deferred[i];
456             sd.error("unable to resolve forward reference in definition");
457         }
458         //fatal();
459     }
460 
461     // Do pass 2 semantic analysis
462     foreach (m; modules)
463     {
464         if (params.verbose)
465             message("semantic2 %s", m.toChars());
466         m.semantic2(null);
467     }
468     Module.runDeferredSemantic2();
469     if (global.errors)
470         removeHdrFilesAndFail(params, modules);
471 
472     // Do pass 3 semantic analysis
473     foreach (m; modules)
474     {
475         if (params.verbose)
476             message("semantic3 %s", m.toChars());
477         m.semantic3(null);
478     }
479     if (includeImports)
480     {
481         // Note: DO NOT USE foreach here because Module.amodules.length can
482         //       change on each iteration of the loop
483         for (size_t i = 0; i < compiledImports.length; i++)
484         {
485             auto m = compiledImports[i];
486             assert(m.isRoot);
487             if (params.verbose)
488                 message("semantic3 %s", m.toChars());
489             m.semantic3(null);
490             modules.push(m);
491         }
492     }
493     Module.runDeferredSemantic3();
494     if (global.errors)
495         removeHdrFilesAndFail(params, modules);
496 
497     // Scan for functions to inline
498     foreach (m; modules)
499     {
500         if (params.useInline || m.hasAlwaysInlines)
501         {
502             if (params.verbose)
503                 message("inline scan %s", m.toChars());
504             inlineScanModule(m);
505         }
506     }
507 
508     if (global.warnings)
509         errorOnWarning();
510 
511     // Do not attempt to generate output files if errors or warnings occurred
512     if (global.errors || global.warnings)
513         removeHdrFilesAndFail(params, modules);
514 
515     // inlineScan incrementally run semantic3 of each expanded functions.
516     // So deps file generation should be moved after the inlining stage.
517     if (OutBuffer* ob = params.moduleDeps.buffer)
518     {
519         foreach (i; 1 .. modules[0].aimports.length)
520             semantic3OnDependencies(modules[0].aimports[i]);
521         Module.runDeferredSemantic3();
522 
523         const data = (*ob)[];
524         if (params.moduleDeps.name)
525             writeFile(Loc.initial, params.moduleDeps.name, data);
526         else
527             printf("%.*s", cast(int)data.length, data.ptr);
528     }
529 
530     printCtfePerformanceStats();
531     printTemplateStats();
532 
533     // Generate output files
534     if (params.json.doOutput)
535     {
536         generateJson(&modules);
537     }
538     if (!global.errors && params.ddoc.doOutput)
539     {
540         foreach (m; modules)
541         {
542             gendocfile(m);
543         }
544     }
545     if (params.vcg_ast)
546     {
547         import dmd.hdrgen;
548         foreach (mod; modules)
549         {
550             auto buf = OutBuffer();
551             buf.doindent = 1;
552             moduleToBuffer(&buf, mod);
553 
554             // write the output to $(filename).cg
555             auto cgFilename = FileName.addExt(mod.srcfile.toString(), "cg");
556             File.write(cgFilename.ptr, buf[]);
557         }
558     }
559 
560     if (global.params.cxxhdr.doOutput)
561         genCppHdrFiles(modules);
562 
563     if (global.errors)
564         fatal();
565 
566     if (driverParams.lib && params.objfiles.length == 0)
567     {
568         error(Loc.initial, "no input files");
569         return EXIT_FAILURE;
570     }
571 
572     if (params.addMain && !global.hasMainFunction)
573     {
574         auto mainModule = moduleWithEmptyMain();
575         modules.push(mainModule);
576         if (!driverParams.oneobj || modules.length == 1)
577             params.objfiles.push(mainModule.objfile.toChars());
578     }
579 
580     generateCodeAndWrite(modules[], libmodules[], params.libname, params.objdir,
581                          driverParams.lib, params.obj, driverParams.oneobj, params.multiobj,
582                          params.verbose);
583 
584     backend_term();
585 
586     if (global.errors)
587         fatal();
588     int status = EXIT_SUCCESS;
589     if (!params.objfiles.length)
590     {
591         if (driverParams.link)
592             error(Loc.initial, "no object files to link");
593     }
594     else
595     {
596         if (driverParams.link)
597             status = runLINK();
598         if (params.run)
599         {
600             if (!status)
601             {
602                 status = runProgram();
603                 /* Delete .obj files and .exe file
604                  */
605                 foreach (m; modules)
606                 {
607                     m.deleteObjFile();
608                     if (driverParams.oneobj)
609                         break;
610                 }
611                 params.exefile.toCStringThen!(ef => File.remove(ef.ptr));
612             }
613         }
614     }
615 
616     // Output the makefile dependencies
617     if (params.makeDeps.doOutput)
618         emitMakeDeps(params);
619 
620     if (global.warnings)
621         errorOnWarning();
622 
623     if (global.errors || global.warnings)
624         removeHdrFilesAndFail(params, modules);
625 
626     return status;
627 }
628 
629 /**
630  * Parses the command line arguments and configuration files
631  *
632  * Params:
633  *   argc = Number of arguments passed via command line
634  *   argv = Array of string arguments passed via command line
635  *   params = parametes from argv
636  *   files = files from argv
637  * Returns: true on faiure
638  */
639 version(NoMain) {} else
640 bool parseCommandlineAndConfig(size_t argc, const(char)** argv, ref Param params, ref Strings files)
641 {
642     // Detect malformed input
643     static bool badArgs()
644     {
645         error(Loc.initial, "missing or null command line arguments");
646         return true;
647     }
648 
649     if (argc < 1 || !argv)
650         return badArgs();
651     // Convert argc/argv into arguments[] for easier handling
652     Strings arguments = Strings(argc);
653     for (size_t i = 0; i < argc; i++)
654     {
655         if (!argv[i])
656             return badArgs();
657         arguments[i] = argv[i];
658     }
659     if (const(char)* missingFile = responseExpand(arguments)) // expand response files
660         error(Loc.initial, "cannot open response file '%s'", missingFile);
661     //for (size_t i = 0; i < arguments.length; ++i) printf("arguments[%d] = '%s'\n", i, arguments[i]);
662     files.reserve(arguments.length - 1);
663     // Set default values
664     params.argv0 = arguments[0].toDString;
665 
666     version (Windows)
667         enum iniName = "sc.ini";
668     else version (Posix)
669         enum iniName = "dmd.conf";
670     else
671         static assert(0, "fix this");
672 
673     global.inifilename = parse_conf_arg(&arguments);
674     if (global.inifilename)
675     {
676         // can be empty as in -conf=
677         if (global.inifilename.length && !FileName.exists(global.inifilename))
678             error(Loc.initial, "config file '%.*s' does not exist.",
679                   cast(int)global.inifilename.length, global.inifilename.ptr);
680     }
681     else
682     {
683         global.inifilename = findConfFile(params.argv0, iniName);
684     }
685     // Read the configuration file
686     const iniReadResult = File.read(global.inifilename);
687     const inifileBuffer = iniReadResult.buffer.data;
688     /* Need path of configuration file, for use in expanding @P macro
689      */
690     const(char)[] inifilepath = FileName.path(global.inifilename);
691     Strings sections;
692     StringTable!(char*) environment;
693     environment._init(7);
694     /* Read the [Environment] section, so we can later
695      * pick up any DFLAGS settings.
696      */
697     sections.push("Environment");
698     parseConfFile(environment, global.inifilename, inifilepath, inifileBuffer, &sections);
699 
700     const(char)[] arch = target.is64bit ? "64" : "32"; // use default
701     arch = parse_arch_arg(&arguments, arch);
702 
703     // parse architecture from DFLAGS read from [Environment] section
704     {
705         Strings dflags;
706         getenv_setargv(readFromEnv(environment, "DFLAGS"), &dflags);
707         environment.reset(7); // erase cached environment updates
708         arch = parse_arch_arg(&dflags, arch);
709     }
710 
711     bool is64bit = arch[0] == '6';
712 
713     version(Windows) // delete LIB entry in [Environment] (necessary for optlink) to allow inheriting environment for MS-COFF
714     if (arch != "32omf")
715         environment.update("LIB", 3).value = null;
716 
717     // read from DFLAGS in [Environment{arch}] section
718     char[80] envsection = void;
719     snprintf(envsection.ptr, envsection.length, "Environment%.*s", cast(int) arch.length, arch.ptr);
720     sections.push(envsection.ptr);
721     parseConfFile(environment, global.inifilename, inifilepath, inifileBuffer, &sections);
722     getenv_setargv(readFromEnv(environment, "DFLAGS"), &arguments);
723     updateRealEnvironment(environment);
724     environment.reset(1); // don't need environment cache any more
725 
726     if (parseCommandLine(arguments, argc, params, files, target))
727     {
728         Loc loc;
729         errorSupplemental(loc, "run `dmd` to print the compiler manual");
730         errorSupplemental(loc, "run `dmd -man` to open browser on manual");
731         return true;
732     }
733 
734     if (target.is64bit != is64bit)
735         error(Loc.initial, "the architecture must not be changed in the %s section of %.*s",
736               envsection.ptr, cast(int)global.inifilename.length, global.inifilename.ptr);
737 
738     global.preprocess = &preprocess;
739     return false;
740 }
741 /// Emit the makefile dependencies for the -makedeps switch
742 version (NoMain) {} else
743 {
744     void emitMakeDeps(ref Param params)
745     {
746         assert(params.makeDeps.doOutput);
747 
748         OutBuffer buf;
749 
750         // start by resolving and writing the target (which is sometimes resolved during link phase)
751         if (driverParams.link && params.exefile)
752         {
753             buf.writeEscapedMakePath(&params.exefile[0]);
754         }
755         else if (driverParams.lib)
756         {
757             const(char)[] libname = params.libname ? params.libname : FileName.name(params.objfiles[0].toDString);
758             libname = FileName.forceExt(libname,target.lib_ext);
759 
760             buf.writeEscapedMakePath(&libname[0]);
761         }
762         else if (params.objname)
763         {
764             buf.writeEscapedMakePath(&params.objname[0]);
765         }
766         else if (params.objfiles.length)
767         {
768             buf.writeEscapedMakePath(params.objfiles[0]);
769             foreach (of; params.objfiles[1 .. $])
770             {
771                 buf.writestring(" ");
772                 buf.writeEscapedMakePath(of);
773             }
774         }
775         else
776         {
777             assert(false, "cannot resolve makedeps target");
778         }
779 
780         buf.writestring(":");
781 
782         // then output every dependency
783         foreach (dep; params.makeDeps.files)
784         {
785             buf.writestringln(" \\");
786             buf.writestring("  ");
787             buf.writeEscapedMakePath(dep);
788         }
789         buf.writenl();
790 
791         const data = buf[];
792         if (params.makeDeps.name)
793             writeFile(Loc.initial, params.makeDeps.name, data);
794         else
795             printf("%.*s", cast(int) data.length, data.ptr);
796     }
797 }
798 
799 extern (C++) void generateJson(Modules* modules)
800 {
801     OutBuffer buf;
802     json_generate(&buf, modules);
803 
804     // Write buf to file
805     const(char)[] name = global.params.json.name;
806     if (name == "-")
807     {
808         // Write to stdout; assume it succeeds
809         size_t n = fwrite(buf[].ptr, 1, buf.length, stdout);
810         assert(n == buf.length); // keep gcc happy about return values
811     }
812     else
813     {
814         /* The filename generation code here should be harmonized with Module.setOutfilename()
815          */
816         const(char)[] jsonfilename;
817         if (name)
818         {
819             jsonfilename = FileName.defaultExt(name, json_ext);
820         }
821         else
822         {
823             if (global.params.objfiles.length == 0)
824             {
825                 error(Loc.initial, "cannot determine JSON filename, use `-Xf=<file>` or provide a source file");
826                 fatal();
827             }
828             // Generate json file name from first obj name
829             const(char)[] n = global.params.objfiles[0].toDString;
830             n = FileName.name(n);
831             //if (!FileName::absolute(name))
832             //    name = FileName::combine(dir, name);
833             jsonfilename = FileName.forceExt(n, json_ext);
834         }
835         writeFile(Loc.initial, jsonfilename, buf[]);
836     }
837 }
838 
839 version (DigitalMars)
840 {
841     private void installMemErrHandler()
842     {
843         // (only available on some platforms on DMD)
844         const shouldDoMemoryError = getenv("DMD_INSTALL_MEMERR_HANDLER");
845         if (shouldDoMemoryError !is null && *shouldDoMemoryError == '1')
846         {
847             import etc.linux.memoryerror;
848             static if (is(typeof(registerMemoryErrorHandler())))
849             {
850                 registerMemoryErrorHandler();
851             }
852             else
853             {
854                 printf("**WARNING** Memory error handler not supported on this platform!\n");
855             }
856         }
857     }
858 }
859 
860 version (NoMain)
861 {
862     version (DigitalMars)
863     {
864         shared static this()
865         {
866             installMemErrHandler();
867         }
868     }
869 }
870 else
871 {
872     // in druntime:
873     alias MainFunc = extern(C) int function(char[][] args);
874     extern (C) int _d_run_main(int argc, char** argv, MainFunc dMain);
875 
876 
877     // When using a C main, host DMD may not link against host druntime by default.
878     version (DigitalMars)
879     {
880         version (Win64)
881             pragma(lib, "phobos64");
882         else version (Win32)
883         {
884             version (CRuntime_Microsoft)
885                 pragma(lib, "phobos32mscoff");
886             else
887                 pragma(lib, "phobos");
888         }
889     }
890 
891     extern extern(C) __gshared string[] rt_options;
892 
893     /**
894      * DMD's entry point, C main.
895      *
896      * Without `-lowmem`, we need to switch to the bump-pointer allocation scheme
897      * right from the start, before any module ctors are run, so we need this hook
898      * before druntime is initialized and `_Dmain` is called.
899      *
900      * Returns:
901      *   Return code of the application
902      */
903     extern (C) int main(int argc, char** argv)
904     {
905         bool lowmem = false;
906         foreach (i; 1 .. argc)
907         {
908             if (strcmp(argv[i], "-lowmem") == 0)
909             {
910                 lowmem = true;
911                 break;
912             }
913         }
914         if (!lowmem)
915         {
916             __gshared string[] disable_options = [ "gcopt=disable:1" ];
917             rt_options = disable_options;
918             mem.disableGC();
919         }
920 
921         // initialize druntime and call _Dmain() below
922         return _d_run_main(argc, argv, &_Dmain);
923     }
924 
925     /**
926      * Manual D main (for druntime initialization), which forwards to `tryMain`.
927      *
928      * Returns:
929      *   Return code of the application
930      */
931     extern (C) int _Dmain(char[][])
932     {
933         // possibly install memory error handler
934         version (DigitalMars)
935         {
936             installMemErrHandler();
937         }
938 
939         import core.runtime;
940 
941         version(D_Coverage)
942         {
943             // for now we need to manually set the source path
944             string dirName(string path, char separator)
945             {
946                 for (size_t i = path.length - 1; i > 0; i--)
947                 {
948                     if (path[i] == separator)
949                         return path[0..i];
950                 }
951                 return path;
952             }
953             version (Windows)
954                 enum sourcePath = dirName(dirName(dirName(__FILE_FULL_PATH__, '\\'), '\\'), '\\');
955             else
956                 enum sourcePath = dirName(dirName(dirName(__FILE_FULL_PATH__, '/'), '/'), '/');
957 
958             dmd_coverSourcePath(sourcePath);
959             dmd_coverDestPath(sourcePath);
960             dmd_coverSetMerge(true);
961         }
962 
963         scope(failure) stderr.printInternalFailure;
964 
965         auto args = Runtime.cArgs();
966         return tryMain(args.argc, cast(const(char)**)args.argv, global.params);
967     }
968 } // !NoMain
969 
970 /**
971  * Parses an environment variable containing command-line flags
972  * and append them to `args`.
973  *
974  * This function is used to read the content of DFLAGS.
975  * Flags are separated based on spaces and tabs.
976  *
977  * Params:
978  *   envvalue = The content of an environment variable
979  *   args     = Array to append the flags to, if any.
980  */
981 void getenv_setargv(const(char)* envvalue, Strings* args)
982 {
983     if (!envvalue)
984         return;
985 
986     char* env = mem.xstrdup(envvalue); // create our own writable copy
987     //printf("env = '%s'\n", env);
988     while (1)
989     {
990         switch (*env)
991         {
992         case ' ':
993         case '\t':
994             env++;
995             break;
996 
997         case 0:
998             return;
999 
1000         default:
1001         {
1002             args.push(env); // append
1003             auto p = env;
1004             auto slash = 0;
1005             bool instring = false;
1006             while (1)
1007             {
1008                 auto c = *env++;
1009                 switch (c)
1010                 {
1011                 case '"':
1012                     p -= (slash >> 1);
1013                     if (slash & 1)
1014                     {
1015                         p--;
1016                         goto default;
1017                     }
1018                     instring ^= true;
1019                     slash = 0;
1020                     continue;
1021 
1022                 case ' ':
1023                 case '\t':
1024                     if (instring)
1025                         goto default;
1026                     *p = 0;
1027                     //if (wildcard)
1028                     //    wildcardexpand();     // not implemented
1029                     break;
1030 
1031                 case '\\':
1032                     slash++;
1033                     *p++ = c;
1034                     continue;
1035 
1036                 case 0:
1037                     *p = 0;
1038                     //if (wildcard)
1039                     //    wildcardexpand();     // not implemented
1040                     return;
1041 
1042                 default:
1043                     slash = 0;
1044                     *p++ = c;
1045                     continue;
1046                 }
1047                 break;
1048             }
1049             break;
1050         }
1051         }
1052     }
1053 }
1054 
1055 /**
1056  * Parse command line arguments for the last instance of -m32, -m64, -m32mscoff or -m32omfobj
1057  * to detect the desired architecture.
1058  *
1059  * Params:
1060  *   args = Command line arguments
1061  *   arch = Default value to use for architecture.
1062  *          Should be "32" or "64"
1063  *
1064  * Returns:
1065  *   "32", "64" or "32omf" if the "-m32", "-m64", "-m32omf" flags were passed,
1066  *   respectively. If they weren't, return `arch`.
1067  */
1068 const(char)[] parse_arch_arg(Strings* args, const(char)[] arch)
1069 {
1070     foreach (const p; *args)
1071     {
1072         const(char)[] arg = p.toDString;
1073 
1074         if (arg.length && arg[0] == '-')
1075         {
1076             if (arg[1 .. $] == "m32" || arg[1 .. $] == "m32omf" || arg[1 .. $] == "m64")
1077                 arch = arg[2 .. $];
1078             else if (arg[1 .. $] == "m32mscoff")
1079                 arch = "32";
1080             else if (arg[1 .. $] == "run")
1081                 break;
1082         }
1083     }
1084     return arch;
1085 }
1086 
1087 
1088 /**
1089  * Parse command line arguments for the last instance of -conf=path.
1090  *
1091  * Params:
1092  *   args = Command line arguments
1093  *
1094  * Returns:
1095  *   The 'path' in -conf=path, which is the path to the config file to use
1096  */
1097 const(char)[] parse_conf_arg(Strings* args)
1098 {
1099     const(char)[] conf;
1100     foreach (const p; *args)
1101     {
1102         const(char)[] arg = p.toDString;
1103         if (arg.length && arg[0] == '-')
1104         {
1105             if(arg.length >= 6 && arg[1 .. 6] == "conf="){
1106                 conf = arg[6 .. $];
1107             }
1108             else if (arg[1 .. $] == "run")
1109                 break;
1110         }
1111     }
1112     return conf;
1113 }
1114 
1115 
1116 /**
1117  * Set the default and debug libraries to link against, if not already set
1118  *
1119  * Must be called after argument parsing is done, as it won't
1120  * override any value.
1121  * Note that if `-defaultlib=` or `-debuglib=` was used,
1122  * we don't override that either.
1123  */
1124 private void setDefaultLibrary(ref Param params, const ref Target target)
1125 {
1126     if (driverParams.defaultlibname is null)
1127     {
1128         if (target.os == Target.OS.Windows)
1129         {
1130             if (target.is64bit)
1131                 driverParams.defaultlibname = "phobos64";
1132             else if (!target.omfobj)
1133                 driverParams.defaultlibname = "phobos32mscoff";
1134             else
1135                 driverParams.defaultlibname = "phobos";
1136         }
1137         else if (target.os & (Target.OS.linux | Target.OS.FreeBSD | Target.OS.OpenBSD | Target.OS.Solaris | Target.OS.DragonFlyBSD))
1138         {
1139             driverParams.defaultlibname = "libphobos2.a";
1140         }
1141         else if (target.os == Target.OS.OSX)
1142         {
1143             driverParams.defaultlibname = "phobos2";
1144         }
1145         else
1146         {
1147             assert(0, "fix this");
1148         }
1149     }
1150     else if (!driverParams.defaultlibname.length)  // if `-defaultlib=` (i.e. an empty defaultlib)
1151         driverParams.defaultlibname = null;
1152 
1153     if (driverParams.debuglibname is null)
1154         driverParams.debuglibname = driverParams.defaultlibname;
1155 }
1156 
1157 private void printPredefinedVersions(FILE* stream)
1158 {
1159     if (global.versionids)
1160     {
1161         OutBuffer buf;
1162         foreach (const str; *global.versionids)
1163         {
1164             buf.writeByte(' ');
1165             buf.writestring(str.toChars());
1166         }
1167         stream.fprintf("predefs  %s\n", buf.peekChars());
1168     }
1169 }
1170 
1171 extern(C) void printGlobalConfigs(FILE* stream)
1172 {
1173     stream.fprintf("binary    %.*s\n", cast(int)global.params.argv0.length, global.params.argv0.ptr);
1174     stream.fprintf("version   %.*s\n", cast(int) global.versionString().length, global.versionString().ptr);
1175     const iniOutput = global.inifilename ? global.inifilename : "(none)";
1176     stream.fprintf("config    %.*s\n", cast(int)iniOutput.length, iniOutput.ptr);
1177     // Print DFLAGS environment variable
1178     {
1179         StringTable!(char*) environment;
1180         environment._init(0);
1181         Strings dflags;
1182         getenv_setargv(readFromEnv(environment, "DFLAGS"), &dflags);
1183         environment.reset(1);
1184         OutBuffer buf;
1185         foreach (flag; dflags[])
1186         {
1187             bool needsQuoting;
1188             foreach (c; flag.toDString())
1189             {
1190                 if (!(isalnum(c) || c == '_'))
1191                 {
1192                     needsQuoting = true;
1193                     break;
1194                 }
1195             }
1196 
1197             if (flag.strchr(' '))
1198                 buf.printf("'%s' ", flag);
1199             else
1200                 buf.printf("%s ", flag);
1201         }
1202 
1203         auto res = buf[] ? buf[][0 .. $ - 1] : "(none)";
1204         stream.fprintf("DFLAGS    %.*s\n", cast(int)res.length, res.ptr);
1205     }
1206 }
1207 
1208 /**************************************
1209  * we want to write the mixin expansion file also on error, but there
1210  * are too many ways to terminate dmd (e.g. fatal() which calls exit(EXIT_FAILURE)),
1211  * so we can't rely on scope(exit) ... in tryMain() actually being executed
1212  * so we add atexit(&flushMixins); for those fatal exits (with the GC still valid)
1213  */
1214 extern(C) void flushMixins()
1215 {
1216     if (!global.params.mixinOut.buffer)
1217         return;
1218 
1219     assert(global.params.mixinOut.name);
1220     File.write(global.params.mixinOut.name, (*global.params.mixinOut.buffer)[]);
1221 
1222     global.params.mixinOut.buffer.destroy();
1223     global.params.mixinOut.buffer = null;
1224 }
1225 
1226 /****************************************************
1227  * Parse command line arguments.
1228  *
1229  * Prints message(s) if there are errors.
1230  *
1231  * Params:
1232  *      arguments = command line arguments
1233  *      argc = argument count
1234  *      params = set to result of parsing `arguments`
1235  *      files = set to files pulled from `arguments`
1236  *      target = more things set to result of parsing `arguments`
1237  * Returns:
1238  *      true if errors in command line
1239  */
1240 
1241 bool parseCommandLine(const ref Strings arguments, const size_t argc, ref Param params, ref Strings files, ref Target target)
1242 {
1243     bool errors;
1244 
1245     void error(Args ...)(const(char)* format, Args args)
1246     {
1247         dmd.errors.error(Loc.initial, format, args);
1248         errors = true;
1249     }
1250 
1251     /**
1252      * Print an error messsage about an invalid switch.
1253      * If an optional supplemental message has been provided,
1254      * it will be printed too.
1255      *
1256      * Params:
1257      *  p = 0 terminated string
1258      *  availableOptions = supplemental help message listing the available options
1259      */
1260     void errorInvalidSwitch(const(char)* p, string availableOptions = null)
1261     {
1262         error("switch `%s` is invalid", p);
1263         if (availableOptions !is null)
1264             errorSupplemental(Loc.initial, "%.*s", cast(int)availableOptions.length, availableOptions.ptr);
1265     }
1266 
1267     enum CheckOptions { success, error, help }
1268 
1269     /*
1270     Checks whether the CLI options contains a valid argument or a help argument.
1271     If a help argument has been used, it will set the `usageFlag`.
1272 
1273     Params:
1274         p = string as a D array
1275         usageFlag = parameter for the usage help page to set (by `ref`)
1276         missingMsg = error message to use when no argument has been provided
1277 
1278     Returns:
1279         `success` if a valid argument has been passed and it's not a help page
1280         `error` if an error occurred (e.g. `-foobar`)
1281         `help` if a help page has been request (e.g. `-flag` or `-flag=h`)
1282     */
1283     CheckOptions checkOptions(const(char)[] p, ref bool usageFlag, string missingMsg)
1284     {
1285         // Checks whether a flag has no options (e.g. -foo or -foo=)
1286         if (p.length == 0 || p == "=")
1287         {
1288             .error(Loc.initial, "%.*s", cast(int)missingMsg.length, missingMsg.ptr);
1289             errors = true;
1290             usageFlag = true;
1291             return CheckOptions.help;
1292         }
1293         if (p[0] != '=')
1294             return CheckOptions.error;
1295         p = p[1 .. $];
1296         /* Checks whether the option pointer supplied is a request
1297            for the help page, e.g. -foo=j */
1298         if ((p == "h" || p == "?") || // -flag=h || -flag=?
1299              p == "help")
1300         {
1301             usageFlag = true;
1302             return CheckOptions.help;
1303         }
1304         return CheckOptions.success;
1305     }
1306 
1307     static string checkOptionsMixin(string usageFlag, string missingMsg)
1308     {
1309         return q{
1310             final switch (checkOptions(arg[len - 1 .. $], params..}~usageFlag~","~
1311                           `"`~missingMsg~`"`~q{))
1312             {
1313                 case CheckOptions.error:
1314                     goto Lerror;
1315                 case CheckOptions.help:
1316                     return false;
1317                 case CheckOptions.success:
1318                     break;
1319             }
1320         };
1321     }
1322 
1323     import dmd.cli : Usage;
1324     bool parseCLIOption(string name, Usage.Feature[] features)(ref Param params, const(char)[] p)
1325     {
1326         // Parse:
1327         //      -<name>=<feature>
1328         const(char)[] ps = p[name.length + 1 .. $];
1329         const(char)[] ident = ps[1 .. $];
1330         if (Identifier.isValidIdentifier(ident))
1331         {
1332             string generateTransitionsText()
1333             {
1334                 import dmd.cli : Usage;
1335                 string buf = `case "all":`;
1336                 foreach (t; features)
1337                 {
1338                     if (t.deprecated_)
1339                         continue;
1340 
1341                     buf ~= `setFlagFor(name, params.`~t.paramName~`);`;
1342                 }
1343                 buf ~= "return true;\n";
1344 
1345                 foreach (t; features)
1346                 {
1347                     buf ~= `case "`~t.name~`":`;
1348                     if (t.deprecated_)
1349                         buf ~= "deprecation(Loc.initial, \"`-"~name~"="~t.name~"` no longer has any effect.\"); ";
1350                     buf ~= `setFlagFor(name, params.`~t.paramName~`); return true;`;
1351                 }
1352                 return buf;
1353             }
1354 
1355             switch (ident)
1356             {
1357                 mixin(generateTransitionsText());
1358             default:
1359                 return false;
1360             }
1361         }
1362         return false;
1363     }
1364 
1365     version (none)
1366     {
1367         for (size_t i = 0; i < arguments.length; i++)
1368         {
1369             printf("arguments[%d] = '%s'\n", i, arguments[i]);
1370         }
1371     }
1372     for (size_t i = 1; i < arguments.length; i++)
1373     {
1374         const(char)* p = arguments[i];
1375         const(char)[] arg = p.toDString();
1376         if (*p != '-')
1377         {
1378             if (target.os == Target.OS.Windows)
1379             {
1380                 const ext = FileName.ext(arg);
1381                 if (ext.length && FileName.equals(ext, "exe"))
1382                 {
1383                     params.objname = arg;
1384                     continue;
1385                 }
1386                 if (arg == "/?")
1387                 {
1388                     params.usage = true;
1389                     return false;
1390                 }
1391             }
1392             files.push(p);
1393             continue;
1394         }
1395 
1396         if (arg == "-allinst")               // https://dlang.org/dmd.html#switch-allinst
1397             params.allInst = true;
1398         else if (startsWith(p + 1, "cpp="))  // https://dlang.org/dmd.html#switch-cpp
1399         {
1400             if (p[5])
1401             {
1402                 params.cpp = p + 5;
1403             }
1404             else
1405             {
1406                 errorInvalidSwitch(p, "it must be followed by the filename of the desired C preprocessor");
1407                 return false;
1408             }
1409         }
1410         else if (arg == "-de")               // https://dlang.org/dmd.html#switch-de
1411             params.useDeprecated = DiagnosticReporting.error;
1412         else if (arg == "-d")                // https://dlang.org/dmd.html#switch-d
1413             params.useDeprecated = DiagnosticReporting.off;
1414         else if (arg == "-dw")               // https://dlang.org/dmd.html#switch-dw
1415             params.useDeprecated = DiagnosticReporting.inform;
1416         else if (arg == "-c")                // https://dlang.org/dmd.html#switch-c
1417             driverParams.link = false;
1418         else if (startsWith(p + 1, "checkaction")) // https://dlang.org/dmd.html#switch-checkaction
1419         {
1420             /* Parse:
1421              *    -checkaction=D|C|halt|context
1422              */
1423             enum len = "-checkaction=".length;
1424             mixin(checkOptionsMixin("checkActionUsage",
1425                 "`-check=<behavior>` requires a behavior"));
1426             switch (arg[len .. $])
1427             {
1428             case "D":
1429                 params.checkAction = CHECKACTION.D;
1430                 break;
1431             case "C":
1432                 params.checkAction = CHECKACTION.C;
1433                 break;
1434             case "halt":
1435                 params.checkAction = CHECKACTION.halt;
1436                 break;
1437             case "context":
1438                 params.checkAction = CHECKACTION.context;
1439                 break;
1440             default:
1441                 errorInvalidSwitch(p);
1442                 params.checkActionUsage = true;
1443                 return false;
1444             }
1445         }
1446         else if (startsWith(p + 1, "check")) // https://dlang.org/dmd.html#switch-check
1447         {
1448             enum len = "-check=".length;
1449             mixin(checkOptionsMixin("checkUsage",
1450                 "`-check=<action>` requires an action"));
1451             /* Parse:
1452              *    -check=[assert|bounds|in|invariant|out|switch][=[on|off]]
1453              */
1454 
1455             // Check for legal option string; return true if so
1456             static bool check(const(char)[] checkarg, string name, ref CHECKENABLE ce)
1457             {
1458                 if (checkarg.length >= name.length &&
1459                     checkarg[0 .. name.length] == name)
1460                 {
1461                     checkarg = checkarg[name.length .. $];
1462 
1463                     if (checkarg.length == 0 ||
1464                         checkarg == "=on")
1465                     {
1466                         ce = CHECKENABLE.on;
1467                         return true;
1468                     }
1469                     else if (checkarg == "=off")
1470                     {
1471                         ce = CHECKENABLE.off;
1472                         return true;
1473                     }
1474                 }
1475                 return false;
1476             }
1477 
1478             const(char)[] checkarg = arg[len .. $];
1479             if (checkarg == "on")
1480             {
1481                 params.useAssert        = CHECKENABLE.on;
1482                 params.useArrayBounds   = CHECKENABLE.on;
1483                 params.useIn            = CHECKENABLE.on;
1484                 params.useInvariants    = CHECKENABLE.on;
1485                 params.useOut           = CHECKENABLE.on;
1486                 params.useSwitchError   = CHECKENABLE.on;
1487             }
1488             else if (checkarg == "off")
1489             {
1490                 params.useAssert        = CHECKENABLE.off;
1491                 params.useArrayBounds   = CHECKENABLE.off;
1492                 params.useIn            = CHECKENABLE.off;
1493                 params.useInvariants    = CHECKENABLE.off;
1494                 params.useOut           = CHECKENABLE.off;
1495                 params.useSwitchError   = CHECKENABLE.off;
1496             }
1497             else if (!(check(checkarg, "assert",    params.useAssert) ||
1498                   check(checkarg, "bounds",    params.useArrayBounds) ||
1499                   check(checkarg, "in",        params.useIn         ) ||
1500                   check(checkarg, "invariant", params.useInvariants ) ||
1501                   check(checkarg, "out",       params.useOut        ) ||
1502                   check(checkarg, "switch",    params.useSwitchError)))
1503             {
1504                 errorInvalidSwitch(p);
1505                 params.checkUsage = true;
1506                 return false;
1507             }
1508         }
1509         else if (startsWith(p + 1, "color")) // https://dlang.org/dmd.html#switch-color
1510         {
1511             // Parse:
1512             //      -color
1513             //      -color=auto|on|off
1514             if (p[6] == '=')
1515             {
1516                 switch(arg[7 .. $])
1517                 {
1518                 case "on":
1519                     params.color = true;
1520                     break;
1521                 case "off":
1522                     params.color = false;
1523                     break;
1524                 case "auto":
1525                     break;
1526                 default:
1527                     errorInvalidSwitch(p, "Available options for `-color` are `on`, `off` and `auto`");
1528                     return true;
1529                 }
1530             }
1531             else if (p[6])
1532                 goto Lerror;
1533             else
1534                 params.color = true;
1535         }
1536         else if (startsWith(p + 1, "conf=")) // https://dlang.org/dmd.html#switch-conf
1537         {
1538             // ignore, already handled above
1539         }
1540         else if (startsWith(p + 1, "cov")) // https://dlang.org/dmd.html#switch-cov
1541         {
1542             params.cov = true;
1543             // Parse:
1544             //      -cov
1545             //      -cov=ctfe
1546             //      -cov=nnn
1547             if (arg == "-cov=ctfe")
1548             {
1549                 params.ctfe_cov = true;
1550             }
1551             else if (p[4] == '=')
1552             {
1553                 if (!params.covPercent.parseDigits(p.toDString()[5 .. $], 100))
1554                 {
1555                     errorInvalidSwitch(p, "Only a number between 0 and 100 can be passed to `-cov=<num>`");
1556                     return true;
1557                 }
1558             }
1559             else if (p[4])
1560                 goto Lerror;
1561         }
1562         else if (arg == "-shared")
1563             driverParams.dll = true;
1564         else if (arg == "-fPIC")
1565         {
1566             driverParams.pic = PIC.pic;
1567         }
1568         else if (arg == "-fPIE")
1569         {
1570             driverParams.pic = PIC.pie;
1571         }
1572         else if (arg == "-map") // https://dlang.org/dmd.html#switch-map
1573             driverParams.map = true;
1574         else if (arg == "-multiobj")
1575             params.multiobj = true;
1576         else if (startsWith(p + 1, "mixin="))
1577         {
1578             auto tmp = p + 6 + 1;
1579             if (!tmp[0])
1580                 goto Lnoarg;
1581             params.mixinOut.doOutput = true;
1582             params.mixinOut.name = mem.xstrdup(tmp).toDString;
1583         }
1584         else if (arg == "-g") // https://dlang.org/dmd.html#switch-g
1585             driverParams.symdebug = true;
1586         else if (startsWith(p + 1, "gdwarf")) // https://dlang.org/dmd.html#switch-gdwarf
1587         {
1588             if (driverParams.dwarf)
1589             {
1590                 error("`-gdwarf=<version>` can only be provided once");
1591                 break;
1592             }
1593             driverParams.symdebug = true;
1594 
1595             enum len = "-gdwarf=".length;
1596             // Parse:
1597             //      -gdwarf=version
1598             if (arg.length < len || !driverParams.dwarf.parseDigits(arg[len .. $], 5) || driverParams.dwarf < 3)
1599             {
1600                 error("`-gdwarf=<version>` requires a valid version [3|4|5]", p);
1601                 return false;
1602             }
1603         }
1604         else if (arg == "-gf")
1605         {
1606             driverParams.symdebug = true;
1607             driverParams.symdebugref = true;
1608         }
1609         else if (arg == "-gs")  // https://dlang.org/dmd.html#switch-gs
1610             driverParams.alwaysframe = true;
1611         else if (arg == "-gx")  // https://dlang.org/dmd.html#switch-gx
1612             driverParams.stackstomp = true;
1613         else if (arg == "-lowmem") // https://dlang.org/dmd.html#switch-lowmem
1614         {
1615             // ignore, already handled in C main
1616         }
1617         else if (arg.length > 6 && arg[0..6] == "--DRT-")
1618         {
1619             continue; // skip druntime options, e.g. used to configure the GC
1620         }
1621         else if (arg == "-m32") // https://dlang.org/dmd.html#switch-m32
1622         {
1623             target.is64bit = false;
1624             target.omfobj = false;
1625         }
1626         else if (arg == "-m64") // https://dlang.org/dmd.html#switch-m64
1627         {
1628             target.is64bit = true;
1629             target.omfobj = false;
1630         }
1631         else if (arg == "-m32mscoff") // https://dlang.org/dmd.html#switch-m32mscoff
1632         {
1633             target.is64bit = false;
1634             target.omfobj = false;
1635         }
1636         else if (arg == "-m32omf") // https://dlang.org/dmd.html#switch-m32omfobj
1637         {
1638             target.is64bit = false;
1639             target.omfobj = true;
1640         }
1641         else if (startsWith(p + 1, "mscrtlib="))
1642         {
1643             driverParams.mscrtlib = arg[10 .. $];
1644         }
1645         else if (startsWith(p + 1, "profile")) // https://dlang.org/dmd.html#switch-profile
1646         {
1647             // Parse:
1648             //      -profile
1649             //      -profile=gc
1650             if (p[8] == '=')
1651             {
1652                 if (arg[9 .. $] == "gc")
1653                     params.tracegc = true;
1654                 else
1655                 {
1656                     errorInvalidSwitch(p, "Only `gc` is allowed for `-profile`");
1657                     return true;
1658                 }
1659             }
1660             else if (p[8])
1661                 goto Lerror;
1662             else
1663                 params.trace = true;
1664         }
1665         else if (arg == "-v") // https://dlang.org/dmd.html#switch-v
1666             params.verbose = true;
1667         else if (arg == "-vcg-ast")
1668             params.vcg_ast = true;
1669         else if (arg == "-vasm") // https://dlang.org/dmd.html#switch-vasm
1670             driverParams.vasm = true;
1671         else if (arg == "-vtls") // https://dlang.org/dmd.html#switch-vtls
1672             params.vtls = true;
1673         else if (startsWith(p + 1, "vtemplates")) // https://dlang.org/dmd.html#switch-vtemplates
1674         {
1675             params.vtemplates = true;
1676             if (p[1 + "vtemplates".length] == '=')
1677             {
1678                 const(char)[] style = arg[1 + "vtemplates=".length .. $];
1679                 switch (style)
1680                 {
1681                 case "list-instances":
1682                     params.vtemplatesListInstances = true;
1683                     break;
1684                 default:
1685                     error("unknown vtemplates style '%.*s', must be 'list-instances'", cast(int) style.length, style.ptr);
1686                 }
1687             }
1688         }
1689         else if (arg == "-vcolumns") // https://dlang.org/dmd.html#switch-vcolumns
1690             params.showColumns = true;
1691         else if (arg == "-vgc") // https://dlang.org/dmd.html#switch-vgc
1692             params.vgc = true;
1693         else if (startsWith(p + 1, "verrors")) // https://dlang.org/dmd.html#switch-verrors
1694         {
1695             if (p[8] != '=')
1696             {
1697                 errorInvalidSwitch(p, "Expected argument following `-verrors , e.g. `-verrors=100`");
1698                 return true;
1699             }
1700             if (startsWith(p + 9, "spec"))
1701             {
1702                 params.showGaggedErrors = true;
1703             }
1704             else if (startsWith(p + 9, "context"))
1705             {
1706                 params.printErrorContext = true;
1707             }
1708             else if (!params.errorLimit.parseDigits(p.toDString()[9 .. $]))
1709             {
1710                 errorInvalidSwitch(p, "Only number, `spec`, or `context` are allowed for `-verrors`");
1711                 return true;
1712             }
1713         }
1714         else if (startsWith(p + 1, "verror-supplements"))
1715         {
1716             if (!params.errorSupplementLimit.parseDigits(p.toDString()[20 .. $]))
1717             {
1718                 errorInvalidSwitch(p, "Only a number is allowed for `-verror-supplements`");
1719                 return true;
1720             }
1721         }
1722         else if (startsWith(p + 1, "verror-style="))
1723         {
1724             const(char)[] style = arg["verror-style=".length + 1 .. $];
1725 
1726             switch (style)
1727             {
1728             case "digitalmars":
1729                 params.messageStyle = MessageStyle.digitalmars;
1730                 break;
1731             case "gnu":
1732                 params.messageStyle = MessageStyle.gnu;
1733                 break;
1734             default:
1735                 error("unknown error style '%.*s', must be 'digitalmars' or 'gnu'", cast(int) style.length, style.ptr);
1736             }
1737         }
1738         else if (startsWith(p + 1, "target"))
1739         {
1740             enum len = "-target=".length;
1741             const triple = Triple(p + len);
1742             target.setTriple(triple);
1743         }
1744         else if (startsWith(p + 1, "mcpu")) // https://dlang.org/dmd.html#switch-mcpu
1745         {
1746             enum len = "-mcpu=".length;
1747             // Parse:
1748             //      -mcpu=identifier
1749             mixin(checkOptionsMixin("mcpuUsage",
1750                 "`-mcpu=<architecture>` requires an architecture"));
1751             if (Identifier.isValidIdentifier(p + len))
1752             {
1753                 const ident = p + len;
1754                 switch (ident.toDString())
1755                 {
1756                 case "baseline":
1757                     target.cpu = CPU.baseline;
1758                     break;
1759                 case "avx":
1760                     target.cpu = CPU.avx;
1761                     break;
1762                 case "avx2":
1763                     target.cpu = CPU.avx2;
1764                     break;
1765                 case "native":
1766                     target.cpu = CPU.native;
1767                     break;
1768                 default:
1769                     errorInvalidSwitch(p, "Only `baseline`, `avx`, `avx2` or `native` are allowed for `-mcpu`");
1770                     params.mcpuUsage = true;
1771                     return false;
1772                 }
1773             }
1774             else
1775             {
1776                 errorInvalidSwitch(p, "Only `baseline`, `avx`, `avx2` or `native` are allowed for `-mcpu`");
1777                 params.mcpuUsage = true;
1778                 return false;
1779             }
1780         }
1781         else if (startsWith(p + 1, "os")) // https://dlang.org/dmd.html#switch-os
1782         {
1783             enum len = "-os=".length;
1784             // Parse:
1785             //      -os=identifier
1786             immutable string msg = "Only `host`, `linux`, `windows`, `osx`,`openbsd`, `freebsd`, `solaris`, `dragonflybsd` allowed for `-os`";
1787             if (Identifier.isValidIdentifier(p + len))
1788             {
1789                 const ident = p + len;
1790                 switch (ident.toDString())
1791                 {
1792                 case "host":         target.os = defaultTargetOS();      break;
1793                 case "linux":        target.os = Target.OS.linux;        break;
1794                 case "windows":      target.os = Target.OS.Windows;      break;
1795                 case "osx":          target.os = Target.OS.OSX;          break;
1796                 case "openbsd":      target.os = Target.OS.OpenBSD;      break;
1797                 case "freebsd":      target.os = Target.OS.FreeBSD;      break;
1798                 case "solaris":      target.os = Target.OS.Solaris;      break;
1799                 case "dragonflybsd": target.os = Target.OS.DragonFlyBSD; break;
1800                 default:
1801                     errorInvalidSwitch(p, msg);
1802                     return false;
1803                 }
1804             }
1805             else
1806             {
1807                 errorInvalidSwitch(p, msg);
1808                 return false;
1809             }
1810         }
1811         else if (startsWith(p + 1, "extern-std")) // https://dlang.org/dmd.html#switch-extern-std
1812         {
1813             enum len = "-extern-std=".length;
1814             // Parse:
1815             //      -extern-std=identifier
1816             mixin(checkOptionsMixin("externStdUsage",
1817                 "`-extern-std=<standard>` requires a standard"));
1818             const(char)[] cpprev = arg[len .. $];
1819 
1820             switch (cpprev)
1821             {
1822             case "c++98":
1823                 params.cplusplus = CppStdRevision.cpp98;
1824                 break;
1825             case "c++11":
1826                 params.cplusplus = CppStdRevision.cpp11;
1827                 break;
1828             case "c++14":
1829                 params.cplusplus = CppStdRevision.cpp14;
1830                 break;
1831             case "c++17":
1832                 params.cplusplus = CppStdRevision.cpp17;
1833                 break;
1834             case "c++20":
1835                 params.cplusplus = CppStdRevision.cpp20;
1836                 break;
1837             default:
1838                 error("switch `%s` is invalid", p);
1839                 params.externStdUsage = true;
1840                 return false;
1841             }
1842         }
1843         else if (startsWith(p + 1, "transition")) // https://dlang.org/dmd.html#switch-transition
1844         {
1845             enum len = "-transition=".length;
1846             // Parse:
1847             //      -transition=number
1848             mixin(checkOptionsMixin("transitionUsage",
1849                 "`-transition=<name>` requires a name"));
1850             if (!parseCLIOption!("transition", Usage.transitions)(params, arg))
1851             {
1852                 // Legacy -transition flags
1853                 // Before DMD 2.085, DMD `-transition` was used for all language flags
1854                 // These are kept for backwards compatibility, but no longer documented
1855                 if (isdigit(cast(char)p[len]))
1856                 {
1857                     uint num;
1858                     if (!num.parseDigits(p.toDString()[len .. $]))
1859                         goto Lerror;
1860 
1861                     // Bugzilla issue number
1862                     switch (num)
1863                     {
1864                         case 3449:
1865                             params.vfield = true;
1866                             break;
1867                         case 14_246:
1868                             params.dtorFields = FeatureState.enabled;
1869                             break;
1870                         case 14_488:
1871                             break;
1872                         case 16_997:
1873                             deprecation(Loc.initial, "`-transition=16997` is now the default behavior");
1874                             break;
1875                         default:
1876                             error("transition `%s` is invalid", p);
1877                             params.transitionUsage = true;
1878                             return false;
1879                     }
1880                 }
1881                 else if (Identifier.isValidIdentifier(p + len))
1882                 {
1883                     const ident = p + len;
1884                     switch (ident.toDString())
1885                     {
1886                         case "dtorfields":
1887                             params.dtorFields = FeatureState.enabled;
1888                             break;
1889                         case "intpromote":
1890                             deprecation(Loc.initial, "`-transition=intpromote` is now the default behavior");
1891                             break;
1892                         default:
1893                             error("transition `%s` is invalid", p);
1894                             params.transitionUsage = true;
1895                             return false;
1896                     }
1897                 }
1898                 errorInvalidSwitch(p);
1899                 params.transitionUsage = true;
1900                 return false;
1901             }
1902         }
1903         else if (startsWith(p + 1, "preview") ) // https://dlang.org/dmd.html#switch-preview
1904         {
1905             enum len = "-preview=".length;
1906             // Parse:
1907             //      -preview=name
1908             mixin(checkOptionsMixin("previewUsage",
1909                 "`-preview=<name>` requires a name"));
1910 
1911             if (!parseCLIOption!("preview", Usage.previews)(params, arg))
1912             {
1913                 error("preview `%s` is invalid", p);
1914                 params.previewUsage = true;
1915                 return false;
1916             }
1917 
1918             if (params.useDIP1021)
1919                 params.useDIP1000 = FeatureState.enabled;    // dip1021 implies dip1000
1920 
1921             // copy previously standalone flags from -transition
1922             // -preview=dip1000 implies -preview=dip25 too
1923             if (params.useDIP1000 == FeatureState.enabled)
1924                 params.useDIP25 = FeatureState.enabled;
1925         }
1926         else if (startsWith(p + 1, "revert") ) // https://dlang.org/dmd.html#switch-revert
1927         {
1928             enum len = "-revert=".length;
1929             // Parse:
1930             //      -revert=name
1931             mixin(checkOptionsMixin("revertUsage",
1932                 "`-revert=<name>` requires a name"));
1933 
1934             if (!parseCLIOption!("revert", Usage.reverts)(params, arg))
1935             {
1936                 error("revert `%s` is invalid", p);
1937                 params.revertUsage = true;
1938                 return false;
1939             }
1940         }
1941         else if (arg == "-w")   // https://dlang.org/dmd.html#switch-w
1942             params.warnings = DiagnosticReporting.error;
1943         else if (arg == "-wi")  // https://dlang.org/dmd.html#switch-wi
1944             params.warnings = DiagnosticReporting.inform;
1945         else if (arg == "-O")   // https://dlang.org/dmd.html#switch-O
1946             driverParams.optimize = true;
1947         else if (arg == "-o-")  // https://dlang.org/dmd.html#switch-o-
1948             params.obj = false;
1949         else if (p[1] == 'o')
1950         {
1951             const(char)* path;
1952             switch (p[2])
1953             {
1954             case 'd':                       // https://dlang.org/dmd.html#switch-od
1955                 if (!p[3])
1956                     goto Lnoarg;
1957                 path = p + 3 + (p[3] == '=');
1958                 version (Windows)
1959                 {
1960                     path = toWinPath(path);
1961                 }
1962                 params.objdir = path.toDString;
1963                 break;
1964             case 'f':                       // https://dlang.org/dmd.html#switch-of
1965                 if (!p[3])
1966                     goto Lnoarg;
1967                 path = p + 3 + (p[3] == '=');
1968                 version (Windows)
1969                 {
1970                     path = toWinPath(path);
1971                 }
1972                 params.objname = path.toDString;
1973                 break;
1974             case 'p':                       // https://dlang.org/dmd.html#switch-op
1975                 if (p[3])
1976                     goto Lerror;
1977                 params.preservePaths = true;
1978                 break;
1979             case 0:
1980                 error("-o no longer supported, use -of or -od");
1981                 break;
1982             default:
1983                 goto Lerror;
1984             }
1985         }
1986         else if (p[1] == 'D')       // https://dlang.org/dmd.html#switch-D
1987         {
1988             params.ddoc.doOutput = true;
1989             switch (p[2])
1990             {
1991             case 'd':               // https://dlang.org/dmd.html#switch-Dd
1992                 if (!p[3])
1993                     goto Lnoarg;
1994                 params.ddoc.dir = (p + 3 + (p[3] == '=')).toDString();
1995                 break;
1996             case 'f':               // https://dlang.org/dmd.html#switch-Df
1997                 if (!p[3])
1998                     goto Lnoarg;
1999                 params.ddoc.name = (p + 3 + (p[3] == '=')).toDString();
2000                 break;
2001             case 0:
2002                 break;
2003             default:
2004                 goto Lerror;
2005             }
2006         }
2007         else if (p[1] == 'H' && p[2] == 'C')  // https://dlang.org/dmd.html#switch-HC
2008         {
2009             params.cxxhdr.doOutput = true;
2010             switch (p[3])
2011             {
2012             case 'd':               // https://dlang.org/dmd.html#switch-HCd
2013                 if (!p[4])
2014                     goto Lnoarg;
2015                 params.cxxhdr.dir = (p + 4 + (p[4] == '=')).toDString;
2016                 break;
2017             case 'f':               // https://dlang.org/dmd.html#switch-HCf
2018                 if (!p[4])
2019                     goto Lnoarg;
2020                 params.cxxhdr.name = (p + 4 + (p[4] == '=')).toDString;
2021                 break;
2022             case '=':
2023                 enum len = "-HC=".length;
2024                 mixin(checkOptionsMixin("hcUsage", "`-HC=<mode>` requires a valid mode"));
2025                 const mode = arg[len .. $];
2026                 switch (mode)
2027                 {
2028                     case "silent":
2029                         /* already set above */
2030                         break;
2031                     case "verbose":
2032                         params.cxxhdr.fullOutput = true;
2033                         break;
2034                     default:
2035                         errorInvalidSwitch(p);
2036                         params.hcUsage = true;
2037                         return false;
2038                 }
2039                 break;
2040             case 0:
2041                 break;
2042             default:
2043                 goto Lerror;
2044             }
2045         }
2046         else if (p[1] == 'H')       // https://dlang.org/dmd.html#switch-H
2047         {
2048             params.dihdr.doOutput = true;
2049             switch (p[2])
2050             {
2051             case 'd':               // https://dlang.org/dmd.html#switch-Hd
2052                 if (!p[3])
2053                     goto Lnoarg;
2054                 params.dihdr.dir = (p + 3 + (p[3] == '=')).toDString;
2055                 break;
2056             case 'f':               // https://dlang.org/dmd.html#switch-Hf
2057                 if (!p[3])
2058                     goto Lnoarg;
2059                 params.dihdr.name = (p + 3 + (p[3] == '=')).toDString;
2060                 break;
2061             case 0:
2062                 break;
2063             default:
2064                 goto Lerror;
2065             }
2066         }
2067         else if (startsWith(p + 1, "Xcc="))
2068         {
2069             params.linkswitches.push(p + 5);
2070             params.linkswitchIsForCC.push(true);
2071         }
2072         else if (p[1] == 'X')       // https://dlang.org/dmd.html#switch-X
2073         {
2074             params.json.doOutput = true;
2075             switch (p[2])
2076             {
2077             case 'f':               // https://dlang.org/dmd.html#switch-Xf
2078                 if (!p[3])
2079                     goto Lnoarg;
2080                 params.json.name = (p + 3 + (p[3] == '=')).toDString;
2081                 break;
2082             case 'i':
2083                 if (!p[3])
2084                     goto Lnoarg;
2085                 if (p[3] != '=')
2086                     goto Lerror;
2087                 if (!p[4])
2088                     goto Lnoarg;
2089 
2090                 {
2091                     auto flag = tryParseJsonField(p + 4);
2092                     if (!flag)
2093                     {
2094                         error("unknown JSON field `-Xi=%s`, expected one of " ~ jsonFieldNames, p + 4);
2095                         continue;
2096                     }
2097                     global.params.jsonFieldFlags |= flag;
2098                 }
2099                 break;
2100             case 0:
2101                 break;
2102             default:
2103                 goto Lerror;
2104             }
2105         }
2106         else if (arg == "-ignore")      // https://dlang.org/dmd.html#switch-ignore
2107             params.ignoreUnsupportedPragmas = true;
2108         else if (arg == "-inline")      // https://dlang.org/dmd.html#switch-inline
2109         {
2110             params.useInline = true;
2111             params.dihdr.fullOutput = true;
2112         }
2113         else if (arg == "-i")
2114             includeImports = true;
2115         else if (startsWith(p + 1, "i="))
2116         {
2117             includeImports = true;
2118             if (!p[3])
2119             {
2120                 error("invalid option '%s', module patterns cannot be empty", p);
2121             }
2122             else
2123             {
2124                 // NOTE: we could check that the argument only contains valid "module-pattern" characters.
2125                 //       Invalid characters doesn't break anything but an error message to the user might
2126                 //       be nice.
2127                 includeModulePatterns.push(p + 3);
2128             }
2129         }
2130         else if (arg == "-dip25")       // https://dlang.org/dmd.html#switch-dip25
2131         {
2132             // @@@ DEPRECATION 2.112 @@@
2133             deprecation(Loc.initial, "`-dip25` no longer has any effect");
2134             params.useDIP25 =  FeatureState.enabled;
2135         }
2136         else if (arg == "-dip1000")
2137         {
2138             params.useDIP25 = FeatureState.enabled;
2139             params.useDIP1000 = FeatureState.enabled;
2140         }
2141         else if (arg == "-dip1008")
2142         {
2143             params.ehnogc = true;
2144         }
2145         else if (arg == "-lib")         // https://dlang.org/dmd.html#switch-lib
2146             driverParams.lib = true;
2147         else if (arg == "-nofloat")
2148             driverParams.nofloat = true;
2149         else if (arg == "-quiet")
2150         {
2151             // Ignore
2152         }
2153         else if (arg == "-release")     // https://dlang.org/dmd.html#switch-release
2154             params.release = true;
2155         else if (arg == "-betterC")     // https://dlang.org/dmd.html#switch-betterC
2156             params.betterC = true;
2157         else if (arg == "-noboundscheck") // https://dlang.org/dmd.html#switch-noboundscheck
2158         {
2159             params.boundscheck = CHECKENABLE.off;
2160         }
2161         else if (startsWith(p + 1, "boundscheck")) // https://dlang.org/dmd.html#switch-boundscheck
2162         {
2163             // Parse:
2164             //      -boundscheck=[on|safeonly|off]
2165             if (p[12] == '=')
2166             {
2167                 const(char)[] boundscheck = arg[13 .. $];
2168 
2169                 switch (boundscheck)
2170                 {
2171                 case "on":
2172                     params.boundscheck = CHECKENABLE.on;
2173                     break;
2174                 case "safeonly":
2175                     params.boundscheck = CHECKENABLE.safeonly;
2176                     break;
2177                 case "off":
2178                     params.boundscheck = CHECKENABLE.off;
2179                     break;
2180                 default:
2181                     goto Lerror;
2182                 }
2183             }
2184             else
2185                 goto Lerror;
2186         }
2187         else if (arg == "-unittest")
2188             params.useUnitTests = true;
2189         else if (p[1] == 'I')              // https://dlang.org/dmd.html#switch-I
2190         {
2191             if (!params.imppath)
2192                 params.imppath = new Strings();
2193             params.imppath.push(p + 2 + (p[2] == '='));
2194         }
2195         else if (p[1] == 'm' && p[2] == 'v' && p[3] == '=') // https://dlang.org/dmd.html#switch-mv
2196         {
2197             if (p[4] && strchr(p + 5, '='))
2198             {
2199                 params.modFileAliasStrings.push(p + 4);
2200             }
2201             else
2202                 goto Lerror;
2203         }
2204         else if (p[1] == 'J')             // https://dlang.org/dmd.html#switch-J
2205         {
2206             if (!params.fileImppath)
2207                 params.fileImppath = new Strings();
2208             params.fileImppath.push(p + 2 + (p[2] == '='));
2209         }
2210         else if (startsWith(p + 1, "debug") && p[6] != 'l') // https://dlang.org/dmd.html#switch-debug
2211         {
2212             // Parse:
2213             //      -debug
2214             //      -debug=number
2215             //      -debug=identifier
2216             if (p[6] == '=')
2217             {
2218                 if (isdigit(cast(char)p[7]))
2219                 {
2220                     if (!params.debuglevel.parseDigits(p.toDString()[7 .. $]))
2221                         goto Lerror;
2222 
2223                     // @@@DEPRECATED_2.111@@@
2224                     // Deprecated in 2.101, remove in 2.111
2225                     deprecation(Loc.initial, "`-debug=number` is deprecated, use debug identifiers instead");
2226                 }
2227                 else if (Identifier.isValidIdentifier(p + 7))
2228                 {
2229                     if (!params.debugids)
2230                         params.debugids = new Array!(const(char)*);
2231                     params.debugids.push(p + 7);
2232                 }
2233                 else
2234                     goto Lerror;
2235             }
2236             else if (p[6])
2237                 goto Lerror;
2238             else
2239                 params.debuglevel = 1;
2240         }
2241         else if (startsWith(p + 1, "version")) // https://dlang.org/dmd.html#switch-version
2242         {
2243             // Parse:
2244             //      -version=number
2245             //      -version=identifier
2246             if (p[8] == '=')
2247             {
2248                 if (isdigit(cast(char)p[9]))
2249                 {
2250                     if (!params.versionlevel.parseDigits(p.toDString()[9 .. $]))
2251                         goto Lerror;
2252 
2253                     // @@@DEPRECATED_2.111@@@
2254                     // Deprecated in 2.101, remove in 2.111
2255                     deprecation(Loc.initial, "`-version=number` is deprecated, use version identifiers instead");
2256                 }
2257                 else if (Identifier.isValidIdentifier(p + 9))
2258                 {
2259 
2260                     if (!params.versionids)
2261                         params.versionids = new Array!(const(char)*);
2262                     params.versionids.push(p + 9);
2263                 }
2264                 else
2265                     goto Lerror;
2266             }
2267             else
2268                 goto Lerror;
2269         }
2270         else if (arg == "--b")
2271             driverParams.debugb = true;
2272         else if (arg == "--c")
2273             driverParams.debugc = true;
2274         else if (arg == "--f")
2275             driverParams.debugf = true;
2276         else if (arg == "--help" ||
2277                  arg == "-h")
2278         {
2279             params.usage = true;
2280             return false;
2281         }
2282         else if (arg == "--r")
2283             driverParams.debugr = true;
2284         else if (arg == "--version")
2285         {
2286             params.logo = true;
2287             return false;
2288         }
2289         else if (arg == "--x")
2290             driverParams.debugx = true;
2291         else if (arg == "--y")
2292             driverParams.debugy = true;
2293         else if (p[1] == 'L')                        // https://dlang.org/dmd.html#switch-L
2294         {
2295             params.linkswitches.push(p + 2 + (p[2] == '='));
2296             params.linkswitchIsForCC.push(false);
2297         }
2298         else if (p[1] == 'P')                        // https://dlang.org/dmd.html#switch-P
2299         {
2300             params.cppswitches.push(p + 2 + (p[2] == '='));
2301         }
2302         else if (startsWith(p + 1, "defaultlib="))   // https://dlang.org/dmd.html#switch-defaultlib
2303         {
2304             driverParams.defaultlibname = (p + 1 + 11).toDString;
2305         }
2306         else if (startsWith(p + 1, "debuglib="))     // https://dlang.org/dmd.html#switch-debuglib
2307         {
2308             driverParams.debuglibname = (p + 1 + 9).toDString;
2309         }
2310         else if (startsWith(p + 1, "deps"))          // https://dlang.org/dmd.html#switch-deps
2311         {
2312             if (params.moduleDeps.doOutput)
2313             {
2314                 error("-deps[=file] can only be provided once!");
2315                 break;
2316             }
2317             if (p[5] == '=')
2318             {
2319                 params.moduleDeps.name = (p + 1 + 5).toDString;
2320                 if (!params.moduleDeps.name[0])
2321                     goto Lnoarg;
2322             }
2323             else if (p[5] != '\0')
2324             {
2325                 // Else output to stdout.
2326                 goto Lerror;
2327             }
2328             params.moduleDeps.buffer = new OutBuffer();
2329         }
2330         else if (startsWith(p + 1, "makedeps"))          // https://dlang.org/dmd.html#switch-makedeps
2331         {
2332             if (params.makeDeps.name)
2333             {
2334                 error("-makedeps[=file] can only be provided once!");
2335                 break;
2336             }
2337             if (p[9] == '=')
2338             {
2339                 if (p[10] == '\0')
2340                 {
2341                     error("expected filename after -makedeps=");
2342                     break;
2343                 }
2344                 params.makeDeps.name = (p + 10).toDString;
2345             }
2346             else if (p[9] != '\0')
2347             {
2348                 goto Lerror;
2349             }
2350             // Else output to stdout.
2351             params.makeDeps.doOutput = true;
2352         }
2353         else if (arg == "-main")             // https://dlang.org/dmd.html#switch-main
2354         {
2355             params.addMain = true;
2356         }
2357         else if (startsWith(p + 1, "man"))   // https://dlang.org/dmd.html#switch-man
2358         {
2359             params.manual = true;
2360             return false;
2361         }
2362         else if (arg == "-run")              // https://dlang.org/dmd.html#switch-run
2363         {
2364             params.run = true;
2365             size_t length = argc - i - 1;
2366             if (length)
2367             {
2368                 const(char)[] runarg = arguments[i + 1].toDString();
2369                 const(char)[] ext = FileName.ext(runarg);
2370                 if (ext &&
2371                     FileName.equals(ext, mars_ext) == 0 &&
2372                     FileName.equals(ext, hdr_ext) == 0 &&
2373                     FileName.equals(ext, i_ext) == 0 &&
2374                     FileName.equals(ext, c_ext) == 0)
2375                 {
2376                     error("-run must be followed by a source file, not '%s'", arguments[i + 1]);
2377                     break;
2378                 }
2379                 if (runarg == "-")
2380                     files.push("__stdin.d");
2381                 else
2382                     files.push(arguments[i + 1]);
2383                 params.runargs.setDim(length - 1);
2384                 for (size_t j = 0; j < length - 1; ++j)
2385                 {
2386                     params.runargs[j] = arguments[i + 2 + j];
2387                 }
2388                 i += length;
2389             }
2390             else
2391             {
2392                 params.run = false;
2393                 goto Lnoarg;
2394             }
2395         }
2396         else if (p[1] == '\0')
2397             files.push("__stdin.d");
2398         else
2399         {
2400         Lerror:
2401             error("unrecognized switch '%s'", arguments[i]);
2402             continue;
2403         Lnoarg:
2404             error("argument expected for switch '%s'", arguments[i]);
2405             continue;
2406         }
2407     }
2408     return errors;
2409 }
2410 
2411 /***********************************************
2412  * Adjust gathered command line switches and reconcile them.
2413  * Params:
2414  *      params = switches gathered from command line,
2415  *               and update in place
2416  *      target = more switches from the command line,
2417  *               update in place
2418  *      numSrcFiles = number of source files
2419  */
2420 version (NoMain) {} else
2421 private void reconcileCommands(ref Param params, ref Target target)
2422 {
2423     if (target.os == Target.OS.OSX)
2424     {
2425         driverParams.pic = PIC.pic;
2426     }
2427     else if (target.os == Target.OS.Windows)
2428     {
2429         if (driverParams.pic)
2430             error(Loc.initial, "`-fPIC` and `-fPIE` cannot be used when targetting windows");
2431         if (driverParams.dwarf)
2432             error(Loc.initial, "`-gdwarf` cannot be used when targetting windows");
2433     }
2434     else if (target.os == Target.OS.DragonFlyBSD)
2435     {
2436         if (!target.is64bit)
2437             error(Loc.initial, "`-m32` is not supported on DragonFlyBSD, it is 64-bit only");
2438     }
2439 
2440     if (target.os & (Target.OS.linux | Target.OS.FreeBSD | Target.OS.OpenBSD | Target.OS.Solaris | Target.OS.DragonFlyBSD))
2441     {
2442         if (driverParams.lib && driverParams.dll)
2443             error(Loc.initial, "cannot mix `-lib` and `-shared`");
2444     }
2445     if (target.os == Target.OS.Windows)
2446     {
2447         foreach(b; params.linkswitchIsForCC[])
2448         {
2449             if (b)
2450             {
2451                 // Linking code is guarded by version (Posix):
2452                 error(Loc.initial, "`Xcc=` link switches not available for this operating system");
2453                 break;
2454             }
2455         }
2456     }
2457     else
2458     {
2459         if (target.omfobj)
2460             error(Loc.initial, "`-m32omf` can only be used when targetting windows");
2461         if (driverParams.mscrtlib)
2462             error(Loc.initial, "`-mscrtlib` can only be used when targetting windows");
2463     }
2464 
2465     if (params.boundscheck != CHECKENABLE._default)
2466     {
2467         if (params.useArrayBounds == CHECKENABLE._default)
2468             params.useArrayBounds = params.boundscheck;
2469     }
2470 
2471     if (params.useUnitTests)
2472     {
2473         if (params.useAssert == CHECKENABLE._default)
2474             params.useAssert = CHECKENABLE.on;
2475     }
2476 
2477     if (params.release)
2478     {
2479         if (params.useInvariants == CHECKENABLE._default)
2480             params.useInvariants = CHECKENABLE.off;
2481 
2482         if (params.useIn == CHECKENABLE._default)
2483             params.useIn = CHECKENABLE.off;
2484 
2485         if (params.useOut == CHECKENABLE._default)
2486             params.useOut = CHECKENABLE.off;
2487 
2488         if (params.useArrayBounds == CHECKENABLE._default)
2489             params.useArrayBounds = CHECKENABLE.safeonly;
2490 
2491         if (params.useAssert == CHECKENABLE._default)
2492             params.useAssert = CHECKENABLE.off;
2493 
2494         if (params.useSwitchError == CHECKENABLE._default)
2495             params.useSwitchError = CHECKENABLE.off;
2496     }
2497     else
2498     {
2499         if (params.useInvariants == CHECKENABLE._default)
2500             params.useInvariants = CHECKENABLE.on;
2501 
2502         if (params.useIn == CHECKENABLE._default)
2503             params.useIn = CHECKENABLE.on;
2504 
2505         if (params.useOut == CHECKENABLE._default)
2506             params.useOut = CHECKENABLE.on;
2507 
2508         if (params.useArrayBounds == CHECKENABLE._default)
2509             params.useArrayBounds = CHECKENABLE.on;
2510 
2511         if (params.useAssert == CHECKENABLE._default)
2512             params.useAssert = CHECKENABLE.on;
2513 
2514         if (params.useSwitchError == CHECKENABLE._default)
2515             params.useSwitchError = CHECKENABLE.on;
2516     }
2517 
2518     if (params.betterC)
2519     {
2520         if (params.checkAction != CHECKACTION.halt)
2521             params.checkAction = CHECKACTION.C;
2522 
2523         params.useModuleInfo = false;
2524         params.useTypeInfo = false;
2525         params.useExceptions = false;
2526     }
2527 }
2528 
2529 /***********************************************
2530  * Adjust link, run and lib line switches and reconcile them.
2531  * Params:
2532  *      params = switches gathered from command line,
2533  *               and update in place
2534  *      numSrcFiles = number of source files
2535  *      obj_ext = object file extension
2536  */
2537 version (NoMain) {} else
2538 private void reconcileLinkRunLib(ref Param params, size_t numSrcFiles, const char[] obj_ext)
2539 {
2540     if (!params.obj || driverParams.lib)
2541         driverParams.link = false;
2542 
2543     if (target.os == Target.OS.Windows)
2544     {
2545         if (!driverParams.mscrtlib)
2546         {
2547             version (Windows)
2548             {
2549                 VSOptions vsopt;
2550                 vsopt.initialize();
2551                 driverParams.mscrtlib = vsopt.defaultRuntimeLibrary(target.is64bit).toDString;
2552             }
2553             else
2554             {
2555                 if (driverParams.link)
2556                     error(Loc.initial, "must supply `-mscrtlib` manually when cross compiling to windows");
2557             }
2558         }
2559     }
2560 
2561     if (driverParams.link)
2562     {
2563         params.exefile = params.objname;
2564         driverParams.oneobj = true;
2565         if (params.objname)
2566         {
2567             /* Use this to name the one object file with the same
2568              * name as the exe file.
2569              */
2570             params.objname = FileName.forceExt(params.objname, obj_ext);
2571             /* If output directory is given, use that path rather than
2572              * the exe file path.
2573              */
2574             if (params.objdir)
2575             {
2576                 const(char)[] name = FileName.name(params.objname);
2577                 params.objname = FileName.combine(params.objdir, name);
2578             }
2579         }
2580     }
2581     else if (params.run)
2582     {
2583         error(Loc.initial, "flags conflict with -run");
2584         fatal();
2585     }
2586     else if (driverParams.lib)
2587     {
2588         params.libname = params.objname;
2589         params.objname = null;
2590         // Haven't investigated handling these options with multiobj
2591         if (!params.cov && !params.trace)
2592             params.multiobj = true;
2593     }
2594     else
2595     {
2596         if (params.objname && numSrcFiles)
2597         {
2598             driverParams.oneobj = true;
2599             //error("multiple source files, but only one .obj name");
2600             //fatal();
2601         }
2602     }
2603 }
2604 
2605 /// Sets the boolean for a flag with the given name
2606 private static void setFlagFor(string name, ref bool b)
2607 {
2608     b = name != "revert";
2609 }
2610 
2611 /// Sets the FeatureState for a flag with the given name
2612 private static void setFlagFor(string name, ref FeatureState s)
2613 {
2614     s = name != "revert" ? FeatureState.enabled : FeatureState.disabled;
2615 }
2616 
2617 /**
2618 Creates the module based on the file provided
2619 
2620 The file is dispatched in one of the various arrays
2621 (global.params.{ddoc.files,dllfiles,jsonfiles,etc...})
2622 according to its extension.
2623 If it is a binary file, it is added to libmodules.
2624 
2625 Params:
2626   file = File name to dispatch
2627   libmodules = Array to which binaries (shared/static libs and object files)
2628                will be appended
2629   target = target system
2630 
2631 Returns:
2632   A D module
2633 */
2634 private
2635 Module createModule(const(char)* file, ref Strings libmodules, const ref Target target)
2636 {
2637     const(char)[] name;
2638     version (Windows)
2639     {
2640         file = toWinPath(file);
2641     }
2642     const(char)[] p = file.toDString();
2643     p = FileName.name(p); // strip path
2644     const(char)[] ext = FileName.ext(p);
2645     if (!ext)
2646     {
2647         if (!p.length)
2648         {
2649             error(Loc.initial, "invalid file name '%s'", file);
2650             fatal();
2651         }
2652         auto id = Identifier.idPool(p);
2653         return new Module(file.toDString, id, global.params.ddoc.doOutput, global.params.dihdr.doOutput);
2654     }
2655 
2656     /* Deduce what to do with a file based on its extension
2657         */
2658     if (FileName.equals(ext, target.obj_ext))
2659     {
2660         global.params.objfiles.push(file);
2661         libmodules.push(file);
2662         return null;
2663     }
2664     if (FileName.equals(ext, target.lib_ext))
2665     {
2666         global.params.libfiles.push(file);
2667         libmodules.push(file);
2668         return null;
2669     }
2670     if (target.os & (Target.OS.linux | Target.OS.OSX| Target.OS.FreeBSD | Target.OS.OpenBSD | Target.OS.Solaris | Target.OS.DragonFlyBSD))
2671     {
2672         if (FileName.equals(ext, target.dll_ext))
2673         {
2674             global.params.dllfiles.push(file);
2675             libmodules.push(file);
2676             return null;
2677         }
2678     }
2679     if (FileName.equals(ext, ddoc_ext))
2680     {
2681         global.params.ddoc.files.push(file);
2682         return null;
2683     }
2684     if (FileName.equals(ext, json_ext))
2685     {
2686         global.params.json.doOutput = true;
2687         global.params.json.name = file.toDString;
2688         return null;
2689     }
2690     if (FileName.equals(ext, map_ext))
2691     {
2692         global.params.mapfile = file.toDString;
2693         return null;
2694     }
2695     if (target.os == Target.OS.Windows)
2696     {
2697         if (FileName.equals(ext, "res"))
2698         {
2699             global.params.resfile = file.toDString;
2700             return null;
2701         }
2702         if (FileName.equals(ext, "def"))
2703         {
2704             global.params.deffile = file.toDString;
2705             return null;
2706         }
2707         if (FileName.equals(ext, "exe"))
2708         {
2709             assert(0); // should have already been handled
2710         }
2711     }
2712     /* Examine extension to see if it is a valid
2713      * D, Ddoc or C source file extension
2714      */
2715     if (FileName.equals(ext, mars_ext) ||
2716         FileName.equals(ext, hdr_ext ) ||
2717         FileName.equals(ext, dd_ext  ) ||
2718         FileName.equals(ext, c_ext   ) ||
2719         FileName.equals(ext, i_ext   ))
2720     {
2721         name = FileName.removeExt(p);
2722         if (!name.length || name == ".." || name == ".")
2723         {
2724             error(Loc.initial, "invalid file name '%s'", file);
2725             fatal();
2726         }
2727     }
2728     else
2729     {
2730         error(Loc.initial, "unrecognized file extension %.*s", cast(int)ext.length, ext.ptr);
2731         fatal();
2732     }
2733 
2734     /* At this point, name is the D source file name stripped of
2735      * its path and extension.
2736      */
2737     auto id = Identifier.idPool(name);
2738 
2739     return new Module(file.toDString, id, global.params.ddoc.doOutput, global.params.dihdr.doOutput);
2740 }
2741 
2742 /**
2743 Creates the list of modules based on the files provided
2744 
2745 Files are dispatched in the various arrays
2746 (global.params.{ddocfiles,dllfiles,jsonfiles,etc...})
2747 according to their extension.
2748 Binary files are added to libmodules.
2749 
2750 Params:
2751   files = File names to dispatch
2752   libmodules = Array to which binaries (shared/static libs and object files)
2753                will be appended
2754   target = target system
2755 
2756 Returns:
2757   An array of path to D modules
2758 */
2759 private
2760 Modules createModules(ref Strings files, ref Strings libmodules, const ref Target target)
2761 {
2762     Modules modules;
2763     modules.reserve(files.length);
2764     bool firstmodule = true;
2765     foreach(file; files)
2766     {
2767         auto m = createModule(file, libmodules, target);
2768 
2769         if (m is null)
2770             continue;
2771 
2772         modules.push(m);
2773         if (firstmodule)
2774         {
2775             global.params.objfiles.push(m.objfile.toChars());
2776             firstmodule = false;
2777         }
2778     }
2779     return modules;
2780 }
2781 
2782 /// Returns: a compiled module (semantic3) containing an empty main() function, for the -main flag
2783 Module moduleWithEmptyMain()
2784 {
2785     auto result = new Module("__main.d", Identifier.idPool("__main"), false, false);
2786     // need 2 trailing nulls for sentinel and 2 for lexer
2787     auto data = arraydup("version(D_BetterC)extern(C)int main(){return 0;}else int main(){return 0;}\0\0\0\0");
2788     result.src = cast(ubyte[]) data[0 .. $-4];
2789     result.parse();
2790     result.importedFrom = result;
2791     result.importAll(null);
2792     result.dsymbolSemantic(null);
2793     result.semantic2(null);
2794     result.semantic3(null);
2795     return result;
2796 }