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