Files
ldc/dmd2/mars.c
David Nadlinger b713afe9c4 Cleaned up version output.
I removed the copyright notice. I don't think we are obliged to
display it for DMD; on the other hand, if we do, we might also
have to display the libconfig one (LGPL). Thus, this seemed like
the better choice.

The indentation is to align with the registered target output,
which we cannot control.
2012-12-16 18:08:19 +01:00

1641 lines
46 KiB
C

// 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 <stdio.h>
#include <stdlib.h>
#include <ctype.h>
#include <assert.h>
#include <limits.h>
#include <string>
#include <cstdarg>
#if POSIX
#include <errno.h>
#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 <windows.h>
long __cdecl __ehfilter(LPEXCEPTION_POINTERS ep);
#endif
#if !IN_LLVM
int response_expand(int *pargc, char ***pargv);
void browse(const char *url);
void getenv_setargv(const char *envvar, int *pargc, char** *pargv);
void obj_start(char *srcfile);
void obj_end(Library *library, File *objfile);
#endif
void printCtfePerformanceStats();
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.060";
#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 verror(Loc loc, const char *format, va_list ap, const char *p1, const char *p2)
{
if (!global.gag)
{
char *p = loc.toChars();
if (*p)
fprintf(stdmsg, "%s: ", p);
mem.free(p);
fprintf(stdmsg, "Error: ");
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);
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)
{
fprintf(stdmsg, "%s: ", loc.toChars());
#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);
}
}
void vwarning(Loc loc, const char *format, va_list ap)
{
if (global.params.warnings && !global.gag)
{
char *p = loc.toChars();
if (*p)
fprintf(stdmsg, "%s: ", p);
mem.free(p);
fprintf(stdmsg, "Warning: ");
#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);
//halt();
if (global.params.warnings == 1)
global.warnings++; // warnings don't count if gagged
}
}
/***************************************
* 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%s D Compiler %s\n%s %s\n",
sizeof(size_t) == 4 ? "32" : "64",
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 allow deprecated features\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\
-nofloat do not emit reference to floating point\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(int 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;
int status = EXIT_SUCCESS;
int argcstart = argc;
int setdebuglib = 0;
char noboundscheck = 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.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");
#elif linux || __APPLE__ || __FreeBSD__ || __OpenBSD__ || __sun&&__SVR4
inifilename = inifile(argv[0], "dmd.conf");
#else
#error "fix this"
#endif
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, "d") == 0)
global.params.useDeprecated = 1;
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\n");
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;
#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", 5) == 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)
{
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.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\n");
#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");
#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");
#endif
// 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\n", 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();
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, int *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
int 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();
}
#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