1 
2 /**
3  * Entry point for DMD console version.
4  *
5  * This modules defines the entry point (main) for DMD, as well as related
6  * utilities needed for arguments parsing, path manipulation, etc...
7  * This file is not shared with other compilers which use the DMD front-end.
8  *
9  * Copyright:   Copyright (C) 1999-2023 by The D Language Foundation, All Rights Reserved
10  * Authors:     $(LINK2 https://www.digitalmars.com, Walter Bright)
11  * License:     $(LINK2 https://www.boost.org/LICENSE_1_0.txt, Boost License 1.0)
12  * Source:      $(LINK2 https://github.com/dlang/dmd/blob/master/src/dmd/main.d, _main.d)
13  * Documentation:  https://dlang.org/phobos/dmd_main.html
14  * Coverage:    https://codecov.io/gh/dlang/dmd/src/master/src/dmd/main.d
15  */
16 
17 module dmd.main;
18 
19 version (NoMain) {} else
20 {
21 
22 import core.stdc.stdio;
23 import core.stdc.stdlib;
24 import core.stdc.string;
25 
26 import dmd.arraytypes : Modules, Strings;
27 import dmd.astenums;
28 import dmd.common.outbuffer;
29 import dmd.compiler;
30 import dmd.cond;
31 import dmd.console;
32 import dmd.cpreprocess;
33 import dmd.dinifile;
34 import dmd.dinterpret;
35 import dmd.dmdparams;
36 import dmd.dsymbolsem;
37 import dmd.dtemplate;
38 import dmd.dtoh;
39 import dmd.glue : generateCodeAndWrite;
40 import dmd.dmodule;
41 import dmd.dmsc : backend_init, backend_term;
42 import dmd.doc;
43 import dmd.dsymbol;
44 import dmd.errors;
45 import dmd.expression;
46 import dmd.file_manager;
47 import dmd.hdrgen;
48 import dmd.globals;
49 import dmd.hdrgen;
50 import dmd.id;
51 import dmd.identifier;
52 import dmd.inline;
53 import dmd.link;
54 import dmd.location;
55 import dmd.mars;
56 import dmd.mtype;
57 import dmd.objc;
58 import dmd.root.file;
59 import dmd.root.filename;
60 import dmd.root.man;
61 import dmd.root.response;
62 import dmd.root.rmem;
63 import dmd.root.string;
64 import dmd.root.stringtable;
65 import dmd.semantic2;
66 import dmd.semantic3;
67 import dmd.target;
68 import dmd.utils;
69 import dmd.vsoptions;
70 
71 /**
72  * DMD's entry point, C main.
73  *
74  * Without `-lowmem`, we need to switch to the bump-pointer allocation scheme
75  * right from the start, before any module ctors are run, so we need this hook
76  * before druntime is initialized and `_Dmain` is called.
77  *
78  * Returns:
79  * Return code of the application
80  */ extern (C) int main(int argc, char** argv) {
81     bool lowmem = false;
82     foreach (i; 1 .. argc)
83     {
84         if (strcmp(argv[i], "-lowmem") == 0)
85         {
86             lowmem = true;
87             break;
88         }
89     }
90     if (!lowmem)
91     {
92         __gshared string[] disable_options = [ "gcopt=disable:1" ];
93         rt_options = disable_options;
94         mem.disableGC();
95     }
96     // initialize druntime and call _Dmain() below
97     return _d_run_main(argc, argv, &_Dmain);
98 }
99 /**
100  * Manual D main (for druntime initialization), which forwards to `tryMain`.
101  *
102  * Returns:
103  * Return code of the application
104  */ extern (C) int _Dmain(char[][]) {
105     // possibly install memory error handler
106     version (DigitalMars)
107     {
108         installMemErrHandler();
109     }
110     import core.runtime;
111     version(D_Coverage)
112     {
113         // for now we need to manually set the source path
114         string dirName(string path, char separator)
115         {
116             for (size_t i = path.length - 1; i > 0; i--)
117             {
118                 if (path[i] == separator)
119                     return path[0..i];
120             }
121             return path;
122         }
123         version (Windows)
124             enum sourcePath = dirName(dirName(dirName(__FILE_FULL_PATH__, '\\'), '\\'), '\\');
125         else
126             enum sourcePath = dirName(dirName(dirName(__FILE_FULL_PATH__, '/'), '/'), '/');
127         dmd_coverSourcePath(sourcePath);
128         dmd_coverDestPath(sourcePath);
129         dmd_coverSetMerge(true);
130     }
131     scope(failure) stderr.printInternalFailure;
132     auto args = Runtime.cArgs();
133     return tryMain(args.argc, cast(const(char)**)args.argv, global.params);
134 }
135 
136 /************************************************************************************/
137 
138 private:
139 
140 /**
141  * DMD's real entry point
142  *
143  * Parses command line arguments and config file, open and read all
144  * provided source file and do semantic analysis on them.
145  *
146  * Params:
147  *   argc = Number of arguments passed via command line
148  *   argv = Array of string arguments passed via command line
149  *
150  * Returns:
151  *   Application return code
152  */
153 private int tryMain(size_t argc, const(char)** argv, ref Param params)
154 {
155     Strings files;
156     Strings libmodules;
157     global._init();
158 
159     if (parseCommandlineAndConfig(argc, argv, params, files))
160         return EXIT_FAILURE;
161 
162     global.compileEnv.previewIn        = global.params.previewIn;
163     global.compileEnv.ddocOutput       = global.params.ddoc.doOutput;
164 
165     if (params.help.usage)
166     {
167         usage();
168         return EXIT_SUCCESS;
169     }
170 
171     if (params.v.logo)
172     {
173         logo();
174         return EXIT_SUCCESS;
175     }
176 
177     /*
178     Prints a supplied usage text to the console and
179     returns the exit code for the help usage page.
180 
181     Returns:
182         `EXIT_SUCCESS` if no errors occurred, `EXIT_FAILURE` otherwise
183     */
184     static int printHelpUsage(string help)
185     {
186         printf("%.*s", cast(int)help.length, &help[0]);
187         return global.errors ? EXIT_FAILURE : EXIT_SUCCESS;
188     }
189 
190     /*
191     Print a message to make it clear when warnings are treated as errors.
192     */
193     static void errorOnWarning()
194     {
195         error(Loc.initial, "warnings are treated as errors");
196         errorSupplemental(Loc.initial, "Use -wi if you wish to treat warnings only as informational.");
197     }
198 
199     /*
200     Generates code to check for all `params` whether any usage page
201     has been requested.
202     If so, the generated code will print the help page of the flag
203     and return with an exit code.
204 
205     Params:
206         params = parameters with `Usage` suffices in `params` for which
207         their truthness should be checked.
208 
209     Returns: generated code for checking the usage pages of the provided `params`.
210     */
211     static string generateUsageChecks(string[] params)
212     {
213         string s;
214         foreach (n; params)
215         {
216             s ~= q{
217                 if (params.help..}~n~q{)
218                     return printHelpUsage(CLIUsage..}~n~q{Usage);
219             };
220         }
221         return s;
222     }
223     import dmd.cli : CLIUsage;
224     mixin(generateUsageChecks(["mcpu", "transition", "check", "checkAction",
225         "preview", "revert", "externStd", "hc"]));
226 
227     if (params.help.manual)
228     {
229         version (Windows)
230         {
231             browse("https://dlang.org/dmd-windows.html");
232         }
233         version (linux)
234         {
235             browse("https://dlang.org/dmd-linux.html");
236         }
237         version (OSX)
238         {
239             browse("https://dlang.org/dmd-osx.html");
240         }
241         version (FreeBSD)
242         {
243             browse("https://dlang.org/dmd-freebsd.html");
244         }
245         /*NOTE: No regular builds for openbsd/dragonflybsd (yet) */
246         /*
247         version (OpenBSD)
248         {
249             browse("https://dlang.org/dmd-openbsd.html");
250         }
251         version (DragonFlyBSD)
252         {
253             browse("https://dlang.org/dmd-dragonflybsd.html");
254         }
255         */
256         return EXIT_SUCCESS;
257     }
258 
259     if (params.v.color)
260         global.console = cast(void*) createConsole(core.stdc.stdio.stderr);
261 
262     target.setCPU();
263     Loc.set(params.v.showColumns, params.v.messageStyle);
264 
265     if (global.errors)
266     {
267         fatal();
268     }
269     if (files.length == 0)
270     {
271         if (params.jsonFieldFlags)
272         {
273             Modules modules;            // empty
274             generateJson(modules);
275             return EXIT_SUCCESS;
276         }
277         usage();
278         return EXIT_FAILURE;
279     }
280 
281     reconcileCommands(params, target);
282     setDefaultLibrary(params, target);
283 
284     // Initialization
285     target._init(params);
286     Type._init();
287     Id.initialize();
288     Module._init();
289     Expression._init();
290     Objc._init();
291 
292     reconcileLinkRunLib(params, files.length, target.obj_ext);
293     version(CRuntime_Microsoft)
294     {
295         import dmd.root.longdouble;
296         initFPU();
297     }
298     import dmd.root.ctfloat : CTFloat;
299     CTFloat.initialize();
300 
301     // Predefined version identifiers
302     addDefaultVersionIdentifiers(params, target);
303 
304     if (params.v.verbose)
305     {
306         stdout.printPredefinedVersions();
307         stdout.printGlobalConfigs();
308     }
309     //printf("%d source files\n", cast(int) files.length);
310 
311     // Build import search path
312 
313     static Strings* buildPath(Strings* imppath)
314     {
315         Strings* result = null;
316         if (imppath)
317         {
318             foreach (const path; *imppath)
319             {
320                 Strings* a = FileName.splitPath(path);
321                 if (a)
322                 {
323                     if (!result)
324                         result = new Strings();
325                     result.append(a);
326                 }
327             }
328         }
329         return result;
330     }
331 
332     if (params.mixinOut.doOutput)
333     {
334         params.mixinOut.buffer = cast(OutBuffer*)Mem.check(calloc(1, OutBuffer.sizeof));
335         atexit(&flushMixins); // see comment for flushMixins
336     }
337     scope(exit) flushMixins();
338     global.path = buildPath(params.imppath);
339     global.filePath = buildPath(params.fileImppath);
340 
341     // Create Modules
342     Modules modules = createModules(files, libmodules, target);
343     // Read files
344     foreach (m; modules)
345     {
346         m.read(Loc.initial);
347     }
348 
349     OutBuffer ddocbuf;          // buffer for contents of .ddoc files
350     bool ddocbufIsRead;         // set when ddocbuf is filled
351 
352     /* Read ddoc macro files named by the DDOCFILE environment variable and command line
353      * and concatenate the text into ddocbuf
354      */
355     void readDdocFiles(ref const Loc loc, ref const Strings ddocfiles, ref OutBuffer ddocbuf)
356     {
357         foreach (file; ddocfiles)
358         {
359             auto buffer = readFile(loc, file.toDString());
360             // BUG: convert file contents to UTF-8 before use
361             const data = buffer.data;
362             //printf("file: '%.*s'\n", cast(int)data.length, data.ptr);
363             ddocbuf.write(data);
364         }
365         ddocbufIsRead = true;
366     }
367 
368     // Parse files
369     bool anydocfiles = false;
370     OutBuffer ddocOutputText;
371     size_t filecount = modules.length;
372     for (size_t filei = 0, modi = 0; filei < filecount; filei++, modi++)
373     {
374         Module m = modules[modi];
375         if (params.v.verbose)
376             message("parse     %s", m.toChars());
377         if (!Module.rootModule)
378             Module.rootModule = m;
379         m.importedFrom = m; // m.isRoot() == true
380 //        if (!driverParams.oneobj || modi == 0 || m.isDocFile)
381 //            m.deleteObjFile();
382 
383         m.parse();
384         if (m.filetype == FileType.dhdr)
385         {
386             // Remove m's object file from list of object files
387             for (size_t j = 0; j < params.objfiles.length; j++)
388             {
389                 if (m.objfile.toChars() == params.objfiles[j])
390                 {
391                     params.objfiles.remove(j);
392                     break;
393                 }
394             }
395             if (params.objfiles.length == 0)
396                 driverParams.link = false;
397         }
398         if (m.filetype == FileType.ddoc)
399         {
400             anydocfiles = true;
401             if (!ddocbufIsRead)
402                 readDdocFiles(m.loc, global.params.ddoc.files, ddocbuf);
403 
404             ddocOutputText.setsize(0);
405             gendocfile(m, ddocbuf[], global.datetime.ptr, global.errorSink, ddocOutputText);
406 
407             if (!writeFile(m.loc, m.docfile.toString(), ddocOutputText[]))
408                 fatal();
409 
410             // Remove m from list of modules
411             modules.remove(modi);
412             modi--;
413             // Remove m's object file from list of object files
414             for (size_t j = 0; j < params.objfiles.length; j++)
415             {
416                 if (m.objfile.toChars() == params.objfiles[j])
417                 {
418                     params.objfiles.remove(j);
419                     break;
420                 }
421             }
422             if (params.objfiles.length == 0)
423                 driverParams.link = false;
424         }
425     }
426 
427     if (anydocfiles && modules.length && (driverParams.oneobj || params.objname))
428     {
429         error(Loc.initial, "conflicting Ddoc and obj generation options");
430         fatal();
431     }
432     if (global.errors)
433         fatal();
434 
435     if (params.dihdr.doOutput)
436     {
437         /* Generate 'header' import files.
438          * Since 'header' import files must be independent of command
439          * line switches and what else is imported, they are generated
440          * before any semantic analysis.
441          */
442         OutBuffer buf;
443         foreach (m; modules)
444         {
445             if (m.filetype == FileType.dhdr)
446                 continue;
447             if (params.v.verbose)
448                 message("import    %s", m.toChars());
449 
450             buf.reset();         // reuse the buffer
451             genhdrfile(m, buf);
452             if (!writeFile(m.loc, m.hdrfile.toString(), buf[]))
453                 fatal();
454         }
455     }
456     if (global.errors)
457         removeHdrFilesAndFail(params, modules);
458 
459     // load all unconditional imports for better symbol resolving
460     foreach (m; modules)
461     {
462         if (params.v.verbose)
463             message("importall %s", m.toChars());
464         m.importAll(null);
465     }
466     if (global.errors)
467         removeHdrFilesAndFail(params, modules);
468 
469     backend_init();
470 
471     // Do semantic analysis
472     foreach (m; modules)
473     {
474         if (params.v.verbose)
475             message("semantic  %s", m.toChars());
476         m.dsymbolSemantic(null);
477     }
478     //if (global.errors)
479     //    fatal();
480     Module.runDeferredSemantic();
481     if (Module.deferred.length)
482     {
483         for (size_t i = 0; i < Module.deferred.length; i++)
484         {
485             Dsymbol sd = Module.deferred[i];
486             error(sd.loc, "%s `%s` unable to resolve forward reference in definition", sd.kind(), sd.toPrettyChars());
487         }
488         //fatal();
489     }
490 
491     // Do pass 2 semantic analysis
492     foreach (m; modules)
493     {
494         if (params.v.verbose)
495             message("semantic2 %s", m.toChars());
496         m.semantic2(null);
497     }
498     Module.runDeferredSemantic2();
499     if (global.errors)
500         removeHdrFilesAndFail(params, modules);
501 
502     // Do pass 3 semantic analysis
503     foreach (m; modules)
504     {
505         if (params.v.verbose)
506             message("semantic3 %s", m.toChars());
507         m.semantic3(null);
508     }
509     if (includeImports)
510     {
511         // Note: DO NOT USE foreach here because Module.amodules.length can
512         //       change on each iteration of the loop
513         for (size_t i = 0; i < compiledImports.length; i++)
514         {
515             auto m = compiledImports[i];
516             assert(m.isRoot);
517             if (params.v.verbose)
518                 message("semantic3 %s", m.toChars());
519             m.semantic3(null);
520             modules.push(m);
521         }
522     }
523     Module.runDeferredSemantic3();
524     if (global.errors)
525         removeHdrFilesAndFail(params, modules);
526 
527     // Scan for functions to inline
528     foreach (m; modules)
529     {
530         if (params.useInline || m.hasAlwaysInlines)
531         {
532             if (params.v.verbose)
533                 message("inline scan %s", m.toChars());
534             inlineScanModule(m);
535         }
536     }
537 
538     if (global.warnings)
539         errorOnWarning();
540 
541     // Do not attempt to generate output files if errors or warnings occurred
542     if (global.errors || global.warnings)
543         removeHdrFilesAndFail(params, modules);
544 
545     // inlineScan incrementally run semantic3 of each expanded functions.
546     // So deps file generation should be moved after the inlining stage.
547     if (OutBuffer* ob = params.moduleDeps.buffer)
548     {
549         foreach (i; 1 .. modules[0].aimports.length)
550             semantic3OnDependencies(modules[0].aimports[i]);
551         Module.runDeferredSemantic3();
552 
553         const data = (*ob)[];
554         if (params.moduleDeps.name)
555         {
556             if (!writeFile(Loc.initial, params.moduleDeps.name, data))
557                 fatal();
558         }
559         else
560             printf("%.*s", cast(int)data.length, data.ptr);
561     }
562 
563     printCtfePerformanceStats();
564     printTemplateStats();
565 
566     // Generate output files
567     if (params.json.doOutput)
568     {
569         generateJson(modules);
570     }
571     if (!global.errors && params.ddoc.doOutput)
572     {
573         foreach (m; modules)
574         {
575             if (!ddocbufIsRead)
576                 readDdocFiles(m.loc, global.params.ddoc.files, ddocbuf);
577 
578             ddocOutputText.setsize(0);
579             gendocfile(m, ddocbuf[], global.datetime.ptr, global.errorSink, ddocOutputText);
580 
581             if (!writeFile(m.loc, m.docfile.toString(), ddocOutputText[]))
582                 fatal();
583         }
584     }
585     if (params.vcg_ast)
586     {
587         import dmd.hdrgen;
588         foreach (mod; modules)
589         {
590             auto buf = OutBuffer();
591             buf.doindent = 1;
592             moduleToBuffer(buf, mod);
593 
594             // write the output to $(filename).cg
595             auto cgFilename = FileName.addExt(mod.srcfile.toString(), "cg");
596             File.write(cgFilename.ptr, buf[]);
597         }
598     }
599 
600     if (global.params.cxxhdr.doOutput)
601         genCppHdrFiles(modules);
602 
603     if (global.errors)
604         fatal();
605 
606     if (driverParams.lib && params.objfiles.length == 0)
607     {
608         error(Loc.initial, "no input files");
609         return EXIT_FAILURE;
610     }
611 
612     if (params.addMain && !global.hasMainFunction)
613     {
614         auto mainModule = moduleWithEmptyMain();
615         modules.push(mainModule);
616         if (!driverParams.oneobj || modules.length == 1)
617             params.objfiles.push(mainModule.objfile.toChars());
618     }
619 
620     generateCodeAndWrite(modules[], libmodules[], params.libname, params.objdir,
621                          driverParams.lib, params.obj, driverParams.oneobj, params.multiobj,
622                          params.v.verbose);
623 
624     backend_term();
625 
626     if (global.errors)
627         fatal();
628     int status = EXIT_SUCCESS;
629     if (!params.objfiles.length)
630     {
631         if (driverParams.link)
632             error(Loc.initial, "no object files to link");
633     }
634     else
635     {
636         if (driverParams.link)
637             status = runLINK();
638         if (params.run)
639         {
640             if (!status)
641             {
642                 status = runProgram();
643                 /* Delete .obj files and .exe file
644                  */
645                 foreach (m; modules)
646                 {
647                     m.deleteObjFile();
648                     if (driverParams.oneobj)
649                         break;
650                 }
651                 params.exefile.toCStringThen!(ef => File.remove(ef.ptr));
652             }
653         }
654     }
655 
656     // Output the makefile dependencies
657     if (params.makeDeps.doOutput)
658         emitMakeDeps(params);
659 
660     if (global.warnings)
661         errorOnWarning();
662 
663     if (global.errors || global.warnings)
664         removeHdrFilesAndFail(params, modules);
665 
666     return status;
667 }
668 
669 /**
670  * Parses the command line arguments and configuration files
671  *
672  * Params:
673  *   argc = Number of arguments passed via command line
674  *   argv = Array of string arguments passed via command line
675  *   params = parameters from argv
676  *   files = files from argv
677  * Returns: true on faiure
678  */
679 bool parseCommandlineAndConfig(size_t argc, const(char)** argv, ref Param params, ref Strings files)
680 {
681     // Detect malformed input
682     static bool badArgs()
683     {
684         error(Loc.initial, "missing or null command line arguments");
685         return true;
686     }
687 
688     if (argc < 1 || !argv)
689         return badArgs();
690     // Convert argc/argv into arguments[] for easier handling
691     Strings arguments = Strings(argc);
692     for (size_t i = 0; i < argc; i++)
693     {
694         if (!argv[i])
695             return badArgs();
696         arguments[i] = argv[i];
697     }
698     if (const(char)* missingFile = responseExpand(arguments)) // expand response files
699         error(Loc.initial, "cannot open response file '%s'", missingFile);
700     //for (size_t i = 0; i < arguments.length; ++i) printf("arguments[%d] = '%s'\n", i, arguments[i]);
701     // Set default values
702     params.argv0 = arguments[0].toDString;
703 
704     version (Windows)
705         enum iniName = "sc.ini";
706     else version (Posix)
707         enum iniName = "dmd.conf";
708     else
709         static assert(0, "fix this");
710 
711     global.inifilename = parse_conf_arg(&arguments);
712     if (global.inifilename)
713     {
714         // can be empty as in -conf=
715         if (global.inifilename.length && !FileName.exists(global.inifilename))
716             error(Loc.initial, "config file '%.*s' does not exist.",
717                   cast(int)global.inifilename.length, global.inifilename.ptr);
718     }
719     else
720     {
721         global.inifilename = findConfFile(params.argv0, iniName);
722     }
723     // Read the configuration file
724     const iniReadResult = File.read(global.inifilename);
725     const inifileBuffer = iniReadResult.buffer.data;
726     /* Need path of configuration file, for use in expanding @P macro
727      */
728     const(char)[] inifilepath = FileName.path(global.inifilename);
729     Strings sections;
730     StringTable!(char*) environment;
731     environment._init(7);
732     /* Read the [Environment] section, so we can later
733      * pick up any DFLAGS settings.
734      */
735     sections.push("Environment");
736     parseConfFile(environment, global.inifilename, inifilepath, inifileBuffer, &sections);
737 
738     const(char)[] arch = target.isX86_64 ? "64" : "32"; // use default
739     arch = parse_arch_arg(&arguments, arch);
740 
741     // parse architecture from DFLAGS read from [Environment] section
742     {
743         Strings dflags;
744         getenv_setargv(readFromEnv(environment, "DFLAGS"), &dflags);
745         environment.reset(7); // erase cached environment updates
746         arch = parse_arch_arg(&dflags, arch);
747     }
748 
749     bool isX86_64 = arch[0] == '6';
750 
751     version(Windows) // delete LIB entry in [Environment] (necessary for optlink) to allow inheriting environment for MS-COFF
752     if (arch != "32omf")
753         environment.update("LIB", 3).value = null;
754 
755     // read from DFLAGS in [Environment{arch}] section
756     char[80] envsection = void;
757     snprintf(envsection.ptr, envsection.length, "Environment%.*s", cast(int) arch.length, arch.ptr);
758     sections.push(envsection.ptr);
759     parseConfFile(environment, global.inifilename, inifilepath, inifileBuffer, &sections);
760     getenv_setargv(readFromEnv(environment, "DFLAGS"), &arguments);
761     updateRealEnvironment(environment);
762     environment.reset(1); // don't need environment cache any more
763 
764     if (parseCommandLine(arguments, argc, params, files, target))
765     {
766         Loc loc;
767         errorSupplemental(loc, "run `dmd` to print the compiler manual");
768         errorSupplemental(loc, "run `dmd -man` to open browser on manual");
769         return true;
770     }
771 
772     // DDOCFILE specified in the sc.ini file comes first and gets overridden by user specified files
773     if (char* p = getenv("DDOCFILE"))
774         global.params.ddoc.files.shift(p);
775 
776     if (target.isX86_64 != isX86_64)
777         error(Loc.initial, "the architecture must not be changed in the %s section of %.*s",
778               envsection.ptr, cast(int)global.inifilename.length, global.inifilename.ptr);
779 
780     global.preprocess = &preprocess;
781     return false;
782 }
783 
784 /// Emit the makefile dependencies for the -makedeps switch
785 void emitMakeDeps(ref Param params)
786 {
787     assert(params.makeDeps.doOutput);
788 
789     OutBuffer buf;
790 
791     // start by resolving and writing the target (which is sometimes resolved during link phase)
792     if (driverParams.link && params.exefile)
793     {
794         buf.writeEscapedMakePath(&params.exefile[0]);
795     }
796     else if (driverParams.lib)
797     {
798         const(char)[] libname = params.libname ? params.libname : FileName.name(params.objfiles[0].toDString);
799         libname = FileName.forceExt(libname,target.lib_ext);
800 
801         buf.writeEscapedMakePath(&libname[0]);
802     }
803     else if (params.objname)
804     {
805         buf.writeEscapedMakePath(&params.objname[0]);
806     }
807     else if (params.objfiles.length)
808     {
809         buf.writeEscapedMakePath(params.objfiles[0]);
810         foreach (of; params.objfiles[1 .. $])
811         {
812             buf.writestring(" ");
813             buf.writeEscapedMakePath(of);
814         }
815     }
816     else
817     {
818         assert(false, "cannot resolve makedeps target");
819     }
820 
821     buf.writestring(":");
822 
823     // then output every dependency
824     foreach (dep; params.makeDeps.files)
825     {
826         buf.writestringln(" \\");
827         buf.writestring("  ");
828         buf.writeEscapedMakePath(dep);
829     }
830     buf.writenl();
831 
832     const data = buf[];
833     if (params.makeDeps.name)
834     {
835         if (!writeFile(Loc.initial, params.makeDeps.name, data))
836             fatal();
837     }
838     else
839         printf("%.*s", cast(int) data.length, data.ptr);
840 }
841 
842 // in druntime:
843 alias MainFunc = extern(C) int function(char[][] args);
844 extern (C) int _d_run_main(int argc, char** argv, MainFunc dMain);
845 
846 
847 // When using a C main, host DMD may not link against host druntime by default.
848 version (DigitalMars)
849 {
850     version (Win64)
851         pragma(lib, "phobos64");
852     else version (Win32)
853     {
854         version (CRuntime_Microsoft)
855             pragma(lib, "phobos32mscoff");
856         else
857             pragma(lib, "phobos");
858     }
859 }
860 
861 extern extern(C) __gshared string[] rt_options;
862 
863 /***********************************************
864  * Adjust gathered command line switches and reconcile them.
865  * Params:
866  *      params = switches gathered from command line,
867  *               and update in place
868  *      target = more switches from the command line,
869  *               update in place
870  *      numSrcFiles = number of source files
871  */
872 void reconcileCommands(ref Param params, ref Target target)
873 {
874     if (target.os == Target.OS.OSX)
875     {
876         driverParams.pic = PIC.pic;
877     }
878     else if (target.os == Target.OS.Windows)
879     {
880         if (driverParams.pic)
881             error(Loc.initial, "`-fPIC` and `-fPIE` cannot be used when targetting windows");
882         if (driverParams.dwarf)
883             error(Loc.initial, "`-gdwarf` cannot be used when targetting windows");
884     }
885     else if (target.os == Target.OS.DragonFlyBSD)
886     {
887         if (!target.isX86_64)
888             error(Loc.initial, "`-m32` is not supported on DragonFlyBSD, it is 64-bit only");
889     }
890 
891     if (target.os & (Target.OS.linux | Target.OS.FreeBSD | Target.OS.OpenBSD | Target.OS.Solaris | Target.OS.DragonFlyBSD))
892     {
893         if (driverParams.lib && driverParams.dll)
894             error(Loc.initial, "cannot mix `-lib` and `-shared`");
895     }
896     if (target.os == Target.OS.Windows)
897     {
898         foreach(b; params.linkswitchIsForCC[])
899         {
900             if (b)
901             {
902                 // Linking code is guarded by version (Posix):
903                 error(Loc.initial, "`Xcc=` link switches not available for this operating system");
904                 break;
905             }
906         }
907     }
908     else
909     {
910         if (target.omfobj)
911             error(Loc.initial, "`-m32omf` can only be used when targetting windows");
912         if (driverParams.mscrtlib)
913             error(Loc.initial, "`-mscrtlib` can only be used when targetting windows");
914     }
915 
916     if (params.boundscheck != CHECKENABLE._default)
917     {
918         if (params.useArrayBounds == CHECKENABLE._default)
919             params.useArrayBounds = params.boundscheck;
920     }
921 
922     if (params.useUnitTests)
923     {
924         if (params.useAssert == CHECKENABLE._default)
925             params.useAssert = CHECKENABLE.on;
926     }
927 
928     if (params.release)
929     {
930         if (params.useInvariants == CHECKENABLE._default)
931             params.useInvariants = CHECKENABLE.off;
932 
933         if (params.useIn == CHECKENABLE._default)
934             params.useIn = CHECKENABLE.off;
935 
936         if (params.useOut == CHECKENABLE._default)
937             params.useOut = CHECKENABLE.off;
938 
939         if (params.useArrayBounds == CHECKENABLE._default)
940             params.useArrayBounds = CHECKENABLE.safeonly;
941 
942         if (params.useAssert == CHECKENABLE._default)
943             params.useAssert = CHECKENABLE.off;
944 
945         if (params.useSwitchError == CHECKENABLE._default)
946             params.useSwitchError = CHECKENABLE.off;
947     }
948     else
949     {
950         if (params.useInvariants == CHECKENABLE._default)
951             params.useInvariants = CHECKENABLE.on;
952 
953         if (params.useIn == CHECKENABLE._default)
954             params.useIn = CHECKENABLE.on;
955 
956         if (params.useOut == CHECKENABLE._default)
957             params.useOut = CHECKENABLE.on;
958 
959         if (params.useArrayBounds == CHECKENABLE._default)
960             params.useArrayBounds = CHECKENABLE.on;
961 
962         if (params.useAssert == CHECKENABLE._default)
963             params.useAssert = CHECKENABLE.on;
964 
965         if (params.useSwitchError == CHECKENABLE._default)
966             params.useSwitchError = CHECKENABLE.on;
967     }
968 
969     if (params.betterC)
970     {
971         if (params.checkAction != CHECKACTION.halt)
972             params.checkAction = CHECKACTION.C;
973 
974         params.useModuleInfo = false;
975         params.useTypeInfo = false;
976         params.useExceptions = false;
977         params.useGC = false;
978     }
979 }
980 
981 /***********************************************
982  * Adjust link, run and lib line switches and reconcile them.
983  * Params:
984  *      params = switches gathered from command line,
985  *               and update in place
986  *      numSrcFiles = number of source files
987  *      obj_ext = object file extension
988  */
989 void reconcileLinkRunLib(ref Param params, size_t numSrcFiles, const char[] obj_ext)
990 {
991     if (!params.obj || driverParams.lib)
992         driverParams.link = false;
993 
994     if (target.os == Target.OS.Windows)
995     {
996         if (!driverParams.mscrtlib)
997         {
998             version (Windows)
999             {
1000                 VSOptions vsopt;
1001                 vsopt.initialize();
1002                 driverParams.mscrtlib = vsopt.defaultRuntimeLibrary(target.isX86_64).toDString;
1003             }
1004             else
1005             {
1006                 if (driverParams.link)
1007                     error(Loc.initial, "must supply `-mscrtlib` manually when cross compiling to windows");
1008             }
1009         }
1010     }
1011 
1012     if (driverParams.link)
1013     {
1014         params.exefile = params.objname;
1015         driverParams.oneobj = true;
1016         if (params.objname)
1017         {
1018             /* Use this to name the one object file with the same
1019              * name as the exe file.
1020              */
1021             params.objname = FileName.forceExt(params.objname, obj_ext);
1022             /* If output directory is given, use that path rather than
1023              * the exe file path.
1024              */
1025             if (params.objdir)
1026             {
1027                 const(char)[] name = FileName.name(params.objname);
1028                 params.objname = FileName.combine(params.objdir, name);
1029             }
1030         }
1031     }
1032     else if (params.run)
1033     {
1034         error(Loc.initial, "flags conflict with -run");
1035         fatal();
1036     }
1037     else if (driverParams.lib)
1038     {
1039         params.libname = params.objname;
1040         params.objname = null;
1041         // Haven't investigated handling these options with multiobj
1042         if (!params.cov && !params.trace)
1043             params.multiobj = true;
1044     }
1045     else
1046     {
1047         if (params.objname && numSrcFiles)
1048         {
1049             driverParams.oneobj = true;
1050             //error("multiple source files, but only one .obj name");
1051             //fatal();
1052         }
1053     }
1054 }
1055 
1056 }