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