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