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