// Compiler implementation of the D programming language // Copyright (c) 1999-2012 by Digital Mars // All Rights Reserved // written by Walter Bright // http://www.digitalmars.com // https://github.com/D-Programming-Language/dmd/blob/master/src/mars.c // License for redistribution is by either the Artistic License // in artistic.txt, or the GNU General Public License in gnu.txt. // See the included readme.txt for details. #include #include #include #include #include #include #include #if linux || __APPLE__ || __FreeBSD__ || __OpenBSD__ || __sun #include #endif #include "rmem.h" #include "root.h" #if !IN_LLVM #include "async.h" #endif #include "mars.h" #include "module.h" #include "mtype.h" #include "id.h" #include "cond.h" #include "expression.h" #include "lexer.h" #if !IN_LLVM #include "lib.h" #include "json.h" #endif #if WINDOWS_SEH #include long __cdecl __ehfilter(LPEXCEPTION_POINTERS ep); #endif #if !IN_LLVM int response_expand(size_t *pargc, char ***pargv); void browse(const char *url); void getenv_setargv(const char *envvar, size_t *pargc, char** *pargv); void obj_start(char *srcfile); void obj_end(Library *library, File *objfile); #endif void printCtfePerformanceStats(); static bool parse_arch(size_t argc, char** argv, bool is64bit); Global global; Global::Global() { mars_ext = "d"; sym_ext = "d"; hdr_ext = "di"; doc_ext = "html"; ddoc_ext = "ddoc"; json_ext = "json"; map_ext = "map"; #if IN_LLVM ll_ext = "ll"; bc_ext = "bc"; s_ext = "s"; obj_ext = "o"; obj_ext_alt = "obj"; #else #if TARGET_WINDOS obj_ext = "obj"; #elif TARGET_LINUX || TARGET_OSX || TARGET_FREEBSD || TARGET_OPENBSD || TARGET_SOLARIS obj_ext = "o"; #elif TARGET_NET #else #error "fix this" #endif #if TARGET_WINDOS lib_ext = "lib"; #elif TARGET_LINUX || TARGET_OSX || TARGET_FREEBSD || TARGET_OPENBSD || TARGET_SOLARIS lib_ext = "a"; #elif TARGET_NET #else #error "fix this" #endif #endif copyright = "Copyright (c) 1999-2012 by Digital Mars"; written = "written by Walter Bright" #if TARGET_NET "\nMSIL back-end (alpha release) by Cristian L. Vlasceanu and associates."; #endif ; version = "v2.061"; #if IN_LLVM ldc_version = "trunk"; llvm_version = "LLVM "LDC_LLVM_VERSION_STRING; #endif global.structalign = STRUCTALIGN_DEFAULT; // This should only be used as a global, so the other fields are // automatically initialized to zero when the program is loaded. // In particular, DO NOT zero-initialize .params here (like DMD // does) because command-line options initialize some of those // fields to non-zero defaults, and do so from constructors that // may run before this one. } unsigned Global::startGagging() { ++gag; return gaggedErrors; } bool Global::endGagging(unsigned oldGagged) { bool anyErrs = (gaggedErrors != oldGagged); --gag; // Restore the original state of gagged errors; set total errors // to be original errors + new ungagged errors. errors -= (gaggedErrors - oldGagged); gaggedErrors = oldGagged; return anyErrs; } bool Global::isSpeculativeGagging() { return gag && gag == speculativeGag; } char *Loc::toChars() { OutBuffer buf; if (filename) { buf.printf("%s", filename); } if (linnum) buf.printf("(%d)", linnum); buf.writeByte(0); return (char *)buf.extractData(); } Loc::Loc(Module *mod, unsigned linnum) { this->linnum = linnum; this->filename = mod ? mod->srcfile->toChars() : NULL; } bool Loc::equals(const Loc& loc) { return linnum == loc.linnum && FileName::equals(filename, loc.filename); } /************************************** * Print error message */ void error(Loc loc, const char *format, ...) { va_list ap; va_start(ap, format); verror(loc, format, ap); va_end( ap ); } void warning(Loc loc, const char *format, ...) { va_list ap; va_start(ap, format); vwarning(loc, format, ap); va_end( ap ); } /************************************** * Print supplementary message about the last error * Used for backtraces, etc */ void errorSupplemental(Loc loc, const char *format, ...) { va_list ap; va_start(ap, format); verrorSupplemental(loc, format, ap); va_end( ap ); } void deprecation(Loc loc, const char *format, ...) { va_list ap; va_start(ap, format); vdeprecation(loc, format, ap); va_end( ap ); } // Just print, doesn't care about gagging void verrorPrint(Loc loc, const char *header, const char *format, va_list ap, const char *p1, const char *p2) { char *p = loc.toChars(); if (*p) fprintf(stdmsg, "%s: ", p); mem.free(p); fputs(header, stdmsg); if (p1) fprintf(stdmsg, "%s ", p1); if (p2) fprintf(stdmsg, "%s ", p2); #if _MSC_VER // MS doesn't recognize %zu format OutBuffer tmp; tmp.vprintf(format, ap); fprintf(stdmsg, "%s", tmp.toChars()); #else vfprintf(stdmsg, format, ap); #endif fprintf(stdmsg, "\n"); fflush(stdmsg); } // header is "Error: " by default (see mars.h) void verror(Loc loc, const char *format, va_list ap, const char *p1, const char *p2, const char *header) { if (!global.gag) { verrorPrint(loc, header, format, ap, p1, p2); if (global.errors >= 20) // moderate blizzard of cascading messages fatal(); //halt(); } else { global.gaggedErrors++; } global.errors++; } // Doesn't increase error count, doesn't print "Error:". void verrorSupplemental(Loc loc, const char *format, va_list ap) { if (!global.gag) verrorPrint(loc, " ", format, ap); } void vwarning(Loc loc, const char *format, va_list ap) { if (global.params.warnings && !global.gag) { verrorPrint(loc, "Warning: ", format, ap); //halt(); if (global.params.warnings == 1) global.warnings++; // warnings don't count if gagged } } void vdeprecation(Loc loc, const char *format, va_list ap, const char *p1, const char *p2) { static const char *header = "Deprecation: "; if (global.params.useDeprecated == 0) verror(loc, format, ap, p1, p2, header); else if (global.params.useDeprecated == 2 && !global.gag) verrorPrint(loc, header, format, ap, p1, p2); } /*************************************** * Call this after printing out fatal error messages to clean up and exit * the compiler. */ void fatal() { #if 0 halt(); #endif exit(EXIT_FAILURE); } /************************************** * Try to stop forgetting to remove the breakpoints from * release builds. */ void halt() { #ifdef DEBUG *(volatile char*)0=0; #endif } #if !IN_LLVM extern void backend_init(); extern void backend_term(); void usage() { #if TARGET_LINUX const char fpic[] ="\ -fPIC generate position independent code\n\ "; #else const char fpic[] = ""; #endif printf("DMD%d D Compiler %s\n%s %s\n", sizeof(size_t) * 8, global.version, global.copyright, global.written); printf("\ Documentation: http://www.dlang.org/index.html\n\ Usage:\n\ dmd files.d ... { -switch }\n\ \n\ files.d D source files\n\ @cmdfile read arguments from cmdfile\n\ -c do not link\n\ -cov do code coverage analysis\n\ -D generate documentation\n\ -Dddocdir write documentation file to docdir directory\n\ -Dffilename write documentation file to filename\n\ -d silently allow deprecated features\n\ -dw show use of deprecated features as warnings (default)\n\ -de show use of deprecated features as errors (halt compilation)\n\ -debug compile in debug code\n\ -debug=level compile in debug code <= level\n\ -debug=ident compile in debug code identified by ident\n\ -debuglib=name set symbolic debug library to name\n\ -defaultlib=name set default library to name\n\ -deps=filename write module dependencies to filename\n%s" " -g add symbolic debug info\n\ -gc add symbolic debug info, pretend to be C\n\ -gs always emit stack frame\n\ -H generate 'header' file\n\ -Hddirectory write 'header' file to directory\n\ -Hffilename write 'header' file to filename\n\ --help print help\n\ -Ipath where to look for imports\n\ -ignore ignore unsupported pragmas\n\ -inline do function inlining\n\ -Jpath where to look for string imports\n\ -Llinkerflag pass linkerflag to link\n\ -lib generate library rather than object files\n" #if TARGET_LINUX || TARGET_OSX || TARGET_FREEBSD || TARGET_OPENBSD || TARGET_SOLARIS " -m32 generate 32 bit code\n\ -m64 generate 64 bit code\n" #endif " -man open web browser on manual page\n\ -map generate linker .map file\n\ -noboundscheck turns off array bounds checking for all functions\n\ -O optimize\n\ -o- do not write object file\n\ -odobjdir write object & library files to directory objdir\n\ -offilename name output file to filename\n\ -op do not strip paths from source file\n\ -profile profile runtime performance of generated code\n\ -property enforce property syntax\n\ -quiet suppress unnecessary messages\n\ -release compile release version\n\ -run srcfile args... run resulting program, passing args\n" #if TARGET_LINUX || TARGET_OSX || TARGET_FREEBSD || TARGET_OPENBSD || TARGET_SOLARIS " -shared generate shared library\n" #endif " -unittest compile in unit tests\n\ -v verbose\n\ -version=level compile in version code >= level\n\ -version=ident compile in version code identified by ident\n\ -vtls list all variables going into thread local storage\n\ -w warnings as errors (compilation will halt)\n\ -wi warnings as messages (compilation will continue)\n\ -X generate JSON file\n\ -Xffilename write JSON file to filename\n\ ", fpic); } extern signed char tyalignsize[]; #if _WIN32 && __DMC__ extern "C" { extern int _xi_a; extern int _end; } #endif int tryMain(size_t argc, char *argv[]) { mem.init(); // initialize storage allocator mem.setStackBottom(&argv); #if _WIN32 && __DMC__ mem.addroots((char *)&_xi_a, (char *)&_end); #endif Strings files; Strings libmodules; char *p; Module *m; size_t argcstart = argc; int setdebuglib = 0; char noboundscheck = 0; int setdefaultlib = 0; const char *inifilename = NULL; #ifdef DEBUG printf("DMD %s DEBUG\n", global.version); #endif unittests(); // Check for malformed input if (argc < 1 || !argv) { Largs: error(0, "missing or null command line arguments"); fatal(); } for (size_t i = 0; i < argc; i++) { if (!argv[i]) goto Largs; } if (response_expand(&argc,&argv)) // expand response files error(0, "can't open response file"); files.reserve(argc - 1); // Set default values global.params.argv0 = argv[0]; global.params.link = 1; global.params.useAssert = 1; global.params.useInvariants = 1; global.params.useIn = 1; global.params.useOut = 1; global.params.useArrayBounds = 2; // default to all functions global.params.useSwitchError = 1; global.params.useInline = 0; global.params.obj = 1; global.params.Dversion = 2; global.params.quiet = 1; global.params.useDeprecated = 2; global.params.linkswitches = new Strings(); global.params.libfiles = new Strings(); global.params.objfiles = new Strings(); global.params.ddocfiles = new Strings(); // Default to -m32 for 32 bit dmd, -m64 for 64 bit dmd global.params.is64bit = (sizeof(size_t) == 8); #if TARGET_WINDOS global.params.is64bit = 0; global.params.defaultlibname = "phobos"; #elif TARGET_LINUX || TARGET_OSX || TARGET_FREEBSD || TARGET_OPENBSD || TARGET_SOLARIS global.params.defaultlibname = "phobos2"; #elif TARGET_NET #else #error "fix this" #endif // Predefine version identifiers VersionCondition::addPredefinedGlobalIdent("DigitalMars"); #if TARGET_WINDOS VersionCondition::addPredefinedGlobalIdent("Windows"); global.params.isWindows = 1; #if TARGET_NET // TARGET_NET macro is NOT mutually-exclusive with TARGET_WINDOS VersionCondition::addPredefinedGlobalIdent("D_NET"); #endif #elif TARGET_LINUX VersionCondition::addPredefinedGlobalIdent("Posix"); VersionCondition::addPredefinedGlobalIdent("linux"); global.params.isLinux = 1; #elif TARGET_OSX VersionCondition::addPredefinedGlobalIdent("Posix"); VersionCondition::addPredefinedGlobalIdent("OSX"); global.params.isOSX = 1; // For legacy compatibility VersionCondition::addPredefinedGlobalIdent("darwin"); #elif TARGET_FREEBSD VersionCondition::addPredefinedGlobalIdent("Posix"); VersionCondition::addPredefinedGlobalIdent("FreeBSD"); global.params.isFreeBSD = 1; #elif TARGET_OPENBSD VersionCondition::addPredefinedGlobalIdent("Posix"); VersionCondition::addPredefinedGlobalIdent("OpenBSD"); global.params.isFreeBSD = 1; #elif TARGET_SOLARIS VersionCondition::addPredefinedGlobalIdent("Posix"); VersionCondition::addPredefinedGlobalIdent("Solaris"); global.params.isSolaris = 1; #else #error "fix this" #endif VersionCondition::addPredefinedGlobalIdent("LittleEndian"); //VersionCondition::addPredefinedGlobalIdent("D_Bits"); #if DMDV2 VersionCondition::addPredefinedGlobalIdent("D_Version2"); #endif VersionCondition::addPredefinedGlobalIdent("all"); #if _WIN32 inifilename = inifile(argv[0], "sc.ini", "Environment"); #elif linux || __APPLE__ || __FreeBSD__ || __OpenBSD__ || __sun inifilename = inifile(argv[0], "dmd.conf", "Environment"); #else #error "fix this" #endif size_t dflags_argc = 0; char** dflags_argv = NULL; getenv_setargv("DFLAGS", &dflags_argc, &dflags_argv); bool is64bit = global.params.is64bit; // use default is64bit = parse_arch(argc, argv, is64bit); is64bit = parse_arch(dflags_argc, dflags_argv, is64bit); global.params.is64bit = is64bit; inifile(argv[0], inifilename, is64bit ? "Environment64" : "Environment32"); getenv_setargv("DFLAGS", &argc, &argv); #if 0 for (size_t i = 0; i < argc; i++) { printf("argv[%d] = '%s'\n", i, argv[i]); } #endif for (size_t i = 1; i < argc; i++) { p = argv[i]; if (*p == '-') { if (strcmp(p + 1, "de") == 0) global.params.useDeprecated = 0; else if (strcmp(p + 1, "d") == 0) global.params.useDeprecated = 1; else if (strcmp(p + 1, "dw") == 0) global.params.useDeprecated = 2; else if (strcmp(p + 1, "c") == 0) global.params.link = 0; else if (strcmp(p + 1, "cov") == 0) global.params.cov = 1; #if TARGET_LINUX || TARGET_OSX || TARGET_FREEBSD || TARGET_OPENBSD || TARGET_SOLARIS else if (strcmp(p + 1, "shared") == 0 #if TARGET_OSX // backwards compatibility with old switch || strcmp(p + 1, "dylib") == 0 #endif ) global.params.dll = 1; else if (strcmp(p + 1, "fPIC") == 0) global.params.pic = 1; #endif else if (strcmp(p + 1, "map") == 0) global.params.map = 1; else if (strcmp(p + 1, "multiobj") == 0) global.params.multiobj = 1; else if (strcmp(p + 1, "g") == 0) global.params.symdebug = 1; else if (strcmp(p + 1, "gc") == 0) global.params.symdebug = 2; else if (strcmp(p + 1, "gs") == 0) global.params.alwaysframe = 1; else if (strcmp(p + 1, "gt") == 0) { error(0, "use -profile instead of -gt"); global.params.trace = 1; } else if (strcmp(p + 1, "m32") == 0) global.params.is64bit = 0; else if (strcmp(p + 1, "m64") == 0) global.params.is64bit = 1; else if (strcmp(p + 1, "profile") == 0) global.params.trace = 1; else if (strcmp(p + 1, "v") == 0) global.params.verbose = 1; #if DMDV2 else if (strcmp(p + 1, "vtls") == 0) global.params.vtls = 1; #endif else if (strcmp(p + 1, "v1") == 0) { #if DMDV1 global.params.Dversion = 1; #else error(0, "use DMD 1.0 series compilers for -v1 switch"); break; #endif } else if (strcmp(p + 1, "w") == 0) global.params.warnings = 1; else if (strcmp(p + 1, "wi") == 0) global.params.warnings = 2; else if (strcmp(p + 1, "O") == 0) global.params.optimize = 1; else if (p[1] == 'o') { switch (p[2]) { case '-': global.params.obj = 0; break; case 'd': if (!p[3]) goto Lnoarg; global.params.objdir = p + 3; break; case 'f': if (!p[3]) goto Lnoarg; global.params.objname = p + 3; break; case 'p': if (p[3]) goto Lerror; global.params.preservePaths = 1; break; case 0: error(0, "-o no longer supported, use -of or -od"); break; default: goto Lerror; } } else if (p[1] == 'D') { global.params.doDocComments = 1; switch (p[2]) { case 'd': if (!p[3]) goto Lnoarg; global.params.docdir = p + 3; break; case 'f': if (!p[3]) goto Lnoarg; global.params.docname = p + 3; break; case 0: break; default: goto Lerror; } } else if (p[1] == 'H') { global.params.doHdrGeneration = 1; switch (p[2]) { case 'd': if (!p[3]) goto Lnoarg; global.params.hdrdir = p + 3; break; case 'f': if (!p[3]) goto Lnoarg; global.params.hdrname = p + 3; break; case 0: break; default: goto Lerror; } } else if (p[1] == 'X') { global.params.doXGeneration = 1; switch (p[2]) { case 'f': if (!p[3]) goto Lnoarg; global.params.xfilename = p + 3; break; case 0: break; default: goto Lerror; } } else if (strcmp(p + 1, "ignore") == 0) global.params.ignoreUnsupportedPragmas = 1; else if (strcmp(p + 1, "property") == 0) global.params.enforcePropertySyntax = 1; else if (strcmp(p + 1, "inline") == 0) global.params.useInline = 1; else if (strcmp(p + 1, "lib") == 0) global.params.lib = 1; else if (strcmp(p + 1, "nofloat") == 0) global.params.nofloat = 1; else if (strcmp(p + 1, "quiet") == 0) global.params.quiet = 1; else if (strcmp(p + 1, "release") == 0) global.params.release = 1; else if (strcmp(p + 1, "betterC") == 0) global.params.betterC = 1; #if DMDV2 else if (strcmp(p + 1, "noboundscheck") == 0) noboundscheck = 1; #endif else if (strcmp(p + 1, "unittest") == 0) global.params.useUnitTests = 1; else if (p[1] == 'I') { if (!global.params.imppath) global.params.imppath = new Strings(); global.params.imppath->push(p + 2); } else if (p[1] == 'J') { if (!global.params.fileImppath) global.params.fileImppath = new Strings(); global.params.fileImppath->push(p + 2); } else if (memcmp(p + 1, "debug", 5) == 0 && p[6] != 'l') { // Parse: // -debug // -debug=number // -debug=identifier if (p[6] == '=') { if (isdigit((unsigned char)p[7])) { long level; errno = 0; level = strtol(p + 7, &p, 10); if (*p || errno || level > INT_MAX) goto Lerror; DebugCondition::setGlobalLevel((int)level); } else if (Lexer::isValidIdentifier(p + 7)) DebugCondition::addGlobalIdent(p + 7); else goto Lerror; } else if (p[6]) goto Lerror; else global.params.debuglevel = 1; } else if (memcmp(p + 1, "version", 7) == 0) { // Parse: // -version=number // -version=identifier if (p[8] == '=') { if (isdigit((unsigned char)p[9])) { long level; errno = 0; level = strtol(p + 9, &p, 10); if (*p || errno || level > INT_MAX) goto Lerror; VersionCondition::setGlobalLevel((int)level); } else if (Lexer::isValidIdentifier(p + 9)) VersionCondition::addGlobalIdent(p + 9); else goto Lerror; } else goto Lerror; } else if (strcmp(p + 1, "-b") == 0) global.params.debugb = 1; else if (strcmp(p + 1, "-c") == 0) global.params.debugc = 1; else if (strcmp(p + 1, "-f") == 0) global.params.debugf = 1; else if (strcmp(p + 1, "-help") == 0) { usage(); exit(EXIT_SUCCESS); } else if (strcmp(p + 1, "-r") == 0) global.params.debugr = 1; else if (strcmp(p + 1, "-x") == 0) global.params.debugx = 1; else if (strcmp(p + 1, "-y") == 0) global.params.debugy = 1; else if (p[1] == 'L') { global.params.linkswitches->push(p + 2); } else if (memcmp(p + 1, "defaultlib=", 11) == 0) { setdefaultlib = 1; global.params.defaultlibname = p + 1 + 11; } else if (memcmp(p + 1, "debuglib=", 9) == 0) { setdebuglib = 1; global.params.debuglibname = p + 1 + 9; } else if (memcmp(p + 1, "deps=", 5) == 0) { global.params.moduleDepsFile = p + 1 + 5; if (!global.params.moduleDepsFile[0]) goto Lnoarg; global.params.moduleDeps = new OutBuffer; } else if (memcmp(p + 1, "man", 3) == 0) { #if _WIN32 #if DMDV1 browse("http://www.digitalmars.com/d/1.0/dmd-windows.html"); #else browse("http://www.dlang.org/dmd-windows.html"); #endif #endif #if linux #if DMDV1 browse("http://www.digitalmars.com/d/1.0/dmd-linux.html"); #else browse("http://www.dlang.org/dmd-linux.html"); #endif #endif #if __APPLE__ #if DMDV1 browse("http://www.digitalmars.com/d/1.0/dmd-osx.html"); #else browse("http://www.dlang.org/dmd-osx.html"); #endif #endif #if __FreeBSD__ #if DMDV1 browse("http://www.digitalmars.com/d/1.0/dmd-freebsd.html"); #else browse("http://www.dlang.org/dmd-freebsd.html"); #endif #endif #if __OpenBSD__ #if DMDV1 browse("http://www.digitalmars.com/d/1.0/dmd-openbsd.html"); #else browse("http://www.dlang.org/dmd-openbsd.html"); #endif #endif exit(EXIT_SUCCESS); } else if (strcmp(p + 1, "run") == 0) { global.params.run = 1; global.params.runargs_length = ((i >= argcstart) ? argc : argcstart) - i - 1; if (global.params.runargs_length) { files.push(argv[i + 1]); global.params.runargs = &argv[i + 2]; i += global.params.runargs_length; global.params.runargs_length--; } else { global.params.run = 0; goto Lnoarg; } } else { Lerror: error(0, "unrecognized switch '%s'", argv[i]); continue; Lnoarg: error(0, "argument expected for switch '%s'", argv[i]); continue; } } else { #if TARGET_WINDOS char *ext = FileName::ext(p); if (ext && FileName::compare(ext, "exe") == 0) { global.params.objname = p; continue; } #endif files.push(p); } } if(global.params.is64bit != is64bit) error(0, "the architecture must not be changed in the %s section of %s", is64bit ? "Environment64" : "Environment32", inifilename); if (global.errors) { fatal(); } if (files.dim == 0) { usage(); return EXIT_FAILURE; } if (!setdebuglib) global.params.debuglibname = global.params.defaultlibname; #if TARGET_OSX global.params.pic = 1; #endif #if TARGET_LINUX || TARGET_OSX || TARGET_FREEBSD || TARGET_OPENBSD || TARGET_SOLARIS if (global.params.lib && global.params.dll) error(0, "cannot mix -lib and -shared"); #endif if (global.params.release) { global.params.useInvariants = 0; global.params.useIn = 0; global.params.useOut = 0; global.params.useAssert = 0; global.params.useArrayBounds = 1; global.params.useSwitchError = 0; } if (noboundscheck) global.params.useArrayBounds = 0; if (global.params.run) global.params.quiet = 1; if (global.params.useUnitTests) global.params.useAssert = 1; if (!global.params.obj || global.params.lib) global.params.link = 0; if (global.params.link) { global.params.exefile = global.params.objname; global.params.oneobj = 1; if (global.params.objname) { /* Use this to name the one object file with the same * name as the exe file. */ global.params.objname = FileName::forceExt(global.params.objname, global.obj_ext)->toChars(); /* If output directory is given, use that path rather than * the exe file path. */ if (global.params.objdir) { char *name = FileName::name(global.params.objname); global.params.objname = FileName::combine(global.params.objdir, name); } } } else if (global.params.lib) { global.params.libname = global.params.objname; global.params.objname = NULL; // Haven't investigated handling these options with multiobj if (!global.params.cov && !global.params.trace #if 0 && TARGET_WINDOS /* multiobj causes class/struct debug info to be attached to init-data, * but this will not be linked into the executable, so this info is lost. * Bugzilla 4014 */ && !global.params.symdebug #endif ) global.params.multiobj = 1; } else if (global.params.run) { error(0, "flags conflict with -run"); fatal(); } else { if (global.params.objname && files.dim > 1) { global.params.oneobj = 1; //error("multiple source files, but only one .obj name"); //fatal(); } } if (global.params.is64bit) { VersionCondition::addPredefinedGlobalIdent("D_InlineAsm_X86_64"); VersionCondition::addPredefinedGlobalIdent("X86_64"); VersionCondition::addPredefinedGlobalIdent("D_LP64"); VersionCondition::addPredefinedGlobalIdent("D_SIMD"); #if TARGET_WINDOS VersionCondition::addPredefinedGlobalIdent("Win64"); if (!setdefaultlib) { global.params.defaultlibname = "phobos64"; if (!setdebuglib) global.params.debuglibname = global.params.defaultlibname; } #endif } else { VersionCondition::addPredefinedGlobalIdent("D_InlineAsm"); VersionCondition::addPredefinedGlobalIdent("D_InlineAsm_X86"); VersionCondition::addPredefinedGlobalIdent("X86"); #if TARGET_OSX VersionCondition::addPredefinedGlobalIdent("D_SIMD"); #endif #if TARGET_WINDOS VersionCondition::addPredefinedGlobalIdent("Win32"); #endif } if (global.params.doDocComments) VersionCondition::addPredefinedGlobalIdent("D_Ddoc"); if (global.params.cov) VersionCondition::addPredefinedGlobalIdent("D_Coverage"); if (global.params.pic) VersionCondition::addPredefinedGlobalIdent("D_PIC"); #if DMDV2 if (global.params.useUnitTests) VersionCondition::addPredefinedGlobalIdent("unittest"); if (global.params.useAssert) VersionCondition::addPredefinedGlobalIdent("assert"); if (noboundscheck) VersionCondition::addPredefinedGlobalIdent("D_NoBoundsChecks"); #endif VersionCondition::addPredefinedGlobalIdent("D_HardFloat"); // Initialization Type::init(); Id::initialize(); Module::init(); initPrecedence(); if (global.params.verbose) { printf("binary %s\n", argv[0]); printf("version %s\n", global.version); printf("config %s\n", inifilename ? inifilename : "(none)"); } //printf("%d source files\n",files.dim); // Build import search path if (global.params.imppath) { for (size_t i = 0; i < global.params.imppath->dim; i++) { char *path = (*global.params.imppath)[i]; Strings *a = FileName::splitPath(path); if (a) { if (!global.path) global.path = new Strings(); global.path->append(a); } } } // Build string import search path if (global.params.fileImppath) { for (size_t i = 0; i < global.params.fileImppath->dim; i++) { char *path = (*global.params.fileImppath)[i]; Strings *a = FileName::splitPath(path); if (a) { if (!global.filePath) global.filePath = new Strings(); global.filePath->append(a); } } } // Create Modules Modules modules; modules.reserve(files.dim); int firstmodule = 1; for (size_t i = 0; i < files.dim; i++) { char *ext; char *name; p = files[i]; #if _WIN32 // Convert / to \ so linker will work for (size_t j = 0; p[j]; j++) { if (p[j] == '/') p[j] = '\\'; } #endif p = FileName::name(p); // strip path ext = FileName::ext(p); if (ext) { /* Deduce what to do with a file based on its extension */ if (FileName::equals(ext, global.obj_ext)) { global.params.objfiles->push(files[i]); libmodules.push(files[i]); continue; } if (FileName::equals(ext, global.lib_ext)) { global.params.libfiles->push(files[i]); libmodules.push(files[i]); continue; } if (strcmp(ext, global.ddoc_ext) == 0) { global.params.ddocfiles->push(files[i]); continue; } if (FileName::equals(ext, global.json_ext)) { global.params.doXGeneration = 1; global.params.xfilename = files[i]; continue; } if (FileName::equals(ext, global.map_ext)) { global.params.mapfile = files[i]; continue; } #if TARGET_WINDOS if (FileName::equals(ext, "res")) { global.params.resfile = files[i]; continue; } if (FileName::equals(ext, "def")) { global.params.deffile = files[i]; continue; } if (FileName::equals(ext, "exe")) { assert(0); // should have already been handled } #endif /* Examine extension to see if it is a valid * D source file extension */ if (FileName::equals(ext, global.mars_ext) || FileName::equals(ext, global.hdr_ext) || FileName::equals(ext, "dd")) { ext--; // skip onto '.' assert(*ext == '.'); name = (char *)mem.malloc((ext - p) + 1); memcpy(name, p, ext - p); name[ext - p] = 0; // strip extension if (name[0] == 0 || strcmp(name, "..") == 0 || strcmp(name, ".") == 0) { Linvalid: error(0, "invalid file name '%s'", files[i]); fatal(); } } else { error(0, "unrecognized file extension %s", ext); fatal(); } } else { name = p; if (!*name) goto Linvalid; } /* At this point, name is the D source file name stripped of * its path and extension. */ Identifier *id = Lexer::idPool(name); m = new Module(files[i], id, global.params.doDocComments, global.params.doHdrGeneration); modules.push(m); if (firstmodule) { global.params.objfiles->push(m->objfile->name->str); firstmodule = 0; } } // Read files #define ASYNCREAD 1 #if ASYNCREAD // Multi threaded AsyncRead *aw = AsyncRead::create(modules.dim); for (size_t i = 0; i < modules.dim; i++) { m = modules[i]; aw->addFile(m->srcfile); } aw->start(); #else // Single threaded for (size_t i = 0; i < modules.dim; i++) { m = modules[i]; m->read(0); } #endif // Parse files bool anydocfiles = false; size_t filecount = modules.dim; for (size_t filei = 0, modi = 0; filei < filecount; filei++, modi++) { m = modules[modi]; if (global.params.verbose) printf("parse %s\n", m->toChars()); if (!Module::rootModule) Module::rootModule = m; m->importedFrom = m; if (!global.params.oneobj || modi == 0 || m->isDocFile) m->deleteObjFile(); #if ASYNCREAD if (aw->read(filei)) { error(0, "cannot read file %s", m->srcfile->name->toChars()); fatal(); } #endif m->parse(); if (m->isDocFile) { anydocfiles = true; m->gendocfile(); // Remove m from list of modules modules.remove(modi); modi--; // Remove m's object file from list of object files for (size_t j = 0; j < global.params.objfiles->dim; j++) { if (m->objfile->name->str == (*global.params.objfiles)[j]) { global.params.objfiles->remove(j); break; } } if (global.params.objfiles->dim == 0) global.params.link = 0; } } #if ASYNCREAD AsyncRead::dispose(aw); #endif if (anydocfiles && modules.dim && (global.params.oneobj || global.params.objname)) { error(0, "conflicting Ddoc and obj generation options"); fatal(); } if (global.errors) fatal(); if (global.params.doHdrGeneration) { /* Generate 'header' import files. * Since 'header' import files must be independent of command * line switches and what else is imported, they are generated * before any semantic analysis. */ for (size_t i = 0; i < modules.dim; i++) { m = modules[i]; if (global.params.verbose) printf("import %s\n", m->toChars()); m->genhdrfile(); } } if (global.errors) fatal(); // load all unconditional imports for better symbol resolving for (size_t i = 0; i < modules.dim; i++) { m = modules[i]; if (global.params.verbose) printf("importall %s\n", m->toChars()); m->importAll(0); } if (global.errors) fatal(); backend_init(); // Do semantic analysis for (size_t i = 0; i < modules.dim; i++) { m = modules[i]; if (global.params.verbose) printf("semantic %s\n", m->toChars()); m->semantic(); } if (global.errors) fatal(); Module::dprogress = 1; Module::runDeferredSemantic(); // Do pass 2 semantic analysis for (size_t i = 0; i < modules.dim; i++) { m = modules[i]; if (global.params.verbose) printf("semantic2 %s\n", m->toChars()); m->semantic2(); } if (global.errors) fatal(); // Do pass 3 semantic analysis for (size_t i = 0; i < modules.dim; i++) { m = modules[i]; if (global.params.verbose) printf("semantic3 %s\n", m->toChars()); m->semantic3(); } if (global.errors) fatal(); if (global.params.useInline) { /* The problem with useArrayBounds and useAssert is that the * module being linked to may not have generated them, so if * we inline functions from those modules, the symbols for them will * not be found at link time. * We must do this BEFORE generating the .deps file! */ if (!global.params.useArrayBounds && !global.params.useAssert) { // Do pass 3 semantic analysis on all imported modules, // since otherwise functions in them cannot be inlined for (size_t i = 0; i < Module::amodules.dim; i++) { m = Module::amodules[i]; if (global.params.verbose) printf("semantic3 %s\n", m->toChars()); m->semantic3(); } if (global.errors) fatal(); } } if (global.params.moduleDeps != NULL) { assert(global.params.moduleDepsFile != NULL); File deps(global.params.moduleDepsFile); OutBuffer* ob = global.params.moduleDeps; deps.setbuffer((void*)ob->data, ob->offset); deps.writev(); } // Scan for functions to inline if (global.params.useInline) { for (size_t i = 0; i < modules.dim; i++) { m = modules[i]; if (global.params.verbose) printf("inline scan %s\n", m->toChars()); m->inlineScan(); } } // Do not attempt to generate output files if errors or warnings occurred if (global.errors || global.warnings) fatal(); printCtfePerformanceStats(); Library *library = NULL; if (global.params.lib) { library = Library::factory(); library->setFilename(global.params.objdir, global.params.libname); // Add input object and input library files to output library for (size_t i = 0; i < libmodules.dim; i++) { char *p = libmodules[i]; library->addObject(p, NULL, 0); } } // Generate output files if (global.params.doXGeneration) json_generate(&modules); if (global.params.oneobj) { for (size_t i = 0; i < modules.dim; i++) { m = modules[i]; if (global.params.verbose) printf("code %s\n", m->toChars()); if (i == 0) obj_start(m->srcfile->toChars()); m->genobjfile(0); if (!global.errors && global.params.doDocComments) m->gendocfile(); } if (!global.errors && modules.dim) { obj_end(library, modules[0]->objfile); } } else { for (size_t i = 0; i < modules.dim; i++) { m = modules[i]; if (global.params.verbose) printf("code %s\n", m->toChars()); if (global.params.obj) { obj_start(m->srcfile->toChars()); m->genobjfile(global.params.multiobj); obj_end(library, m->objfile); obj_write_deferred(library); } if (global.errors) { if (!global.params.lib) m->deleteObjFile(); } else { if (global.params.doDocComments) m->gendocfile(); } } } if (global.params.lib && !global.errors) library->write(); backend_term(); if (global.errors) fatal(); int status = EXIT_SUCCESS; if (!global.params.objfiles->dim) { if (global.params.link) error(0, "no object files to link"); } else { if (global.params.link) status = runLINK(); if (global.params.run) { if (!status) { status = runProgram(); /* Delete .obj files and .exe file */ for (size_t i = 0; i < modules.dim; i++) { Module *m = modules[i]; m->deleteObjFile(); if (global.params.oneobj) break; } deleteExeFile(); } } } return status; } int main(int argc, char *argv[]) { int status = -1; #if WINDOWS_SEH __try { #endif status = tryMain(argc, argv); #if WINDOWS_SEH } __except (__ehfilter(GetExceptionInformation())) { printf("Stack overflow\n"); fatal(); } #endif return status; } #endif // !IN_LLVM /*********************************** * Parse and append contents of environment variable envvar * to argc and argv[]. * The string is separated into arguments, processing \ and ". */ void getenv_setargv(const char *envvar, size_t *pargc, char** *pargv) { char *p; int instring; int slash; char c; char *env = getenv(envvar); if (!env) return; env = mem.strdup(env); // create our own writable copy size_t argc = *pargc; Strings *argv = new Strings(); argv->setDim(argc); int argc_left = 0; for (int i = 0; i < argc; i++) { if (!strcmp((*pargv)[i], "-run") || !strcmp((*pargv)[i], "--run")) { // HACK: set flag to indicate we saw '-run' here global.params.run = true; // Don't eat -run yet so the program arguments don't get changed argc_left = argc - i; argc = i; *pargv = &(*pargv)[i]; argv->setDim(i); break; } else { } } // HACK to stop required values from command line being drawn from DFLAGS argv->push((char*)""); argc++; size_t j = 1; // leave argv[0] alone while (1) { int wildcard = 1; // do wildcard expansion switch (*env) { case ' ': case '\t': env++; break; case 0: goto Ldone; case '"': wildcard = 0; default: argv->push(env); // append //argv->insert(j, env); // insert at position j j++; argc++; p = env; slash = 0; instring = 0; c = 0; while (1) { c = *env++; switch (c) { case '"': p -= (slash >> 1); if (slash & 1) { p--; goto Laddc; } instring ^= 1; slash = 0; continue; case ' ': case '\t': if (instring) goto Laddc; *p = 0; //if (wildcard) //wildcardexpand(); // not implemented break; case '\\': slash++; *p++ = c; continue; case 0: *p = 0; //if (wildcard) //wildcardexpand(); // not implemented goto Ldone; default: Laddc: slash = 0; *p++ = c; continue; } break; } } } Ldone: assert(argc == argv->dim); argv->reserve(argc_left); for (int i = 0; i < argc_left; i++) argv->data[argc++] = (void *)(*pargv)[i]; *pargc = argc; *pargv = argv->tdata(); } /*********************************** * Parse command line arguments for -m32 or -m64 * to detect the desired architecture. */ static bool parse_arch(size_t argc, char** argv, bool is64bit) { for (size_t i = 0; i < argc; ++i) { char* p = argv[i]; if (p[0] == '-') { if (strcmp(p + 1, "m32") == 0) is64bit = 0; else if (strcmp(p + 1, "m64") == 0) is64bit = 1; else if (strcmp(p + 1, "run") == 0) break; } } return is64bit; } #if WINDOWS_SEH long __cdecl __ehfilter(LPEXCEPTION_POINTERS ep) { //printf("%x\n", ep->ExceptionRecord->ExceptionCode); if (ep->ExceptionRecord->ExceptionCode == STATUS_STACK_OVERFLOW) { #if 1 //ndef DEBUG return EXCEPTION_EXECUTE_HANDLER; #endif } return EXCEPTION_CONTINUE_SEARCH; } #endif