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