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, §ions); 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, §ions); 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(¶ms.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(¶ms.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 }