ldc/dmd/mars.c
2008-10-02 03:38:29 +02:00

1284 lines
30 KiB
C

// Compiler implementation of the D programming language
// Copyright (c) 1999-2008 by Digital Mars
// All Rights Reserved
// written by Walter Bright
// http://www.digitalmars.com
// 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 __DMC__
#include <dos.h>
#endif
#if POSIX
#include <errno.h>
#elif _WIN32
#include <windows.h>
#endif
#include "mem.h"
#include "root.h"
#include "mars.h"
#include "module.h"
#include "mtype.h"
#include "id.h"
#include "cond.h"
#include "expression.h"
#include "lexer.h"
#include "gen/logger.h"
#include "gen/linker.h"
void getenv_setargv(const char *envvar, int *pargc, char** *pargv);
Global global;
Global::Global()
{
mars_ext = "d";
sym_ext = "d";
hdr_ext = "di";
doc_ext = "html";
ddoc_ext = "ddoc";
// LLVMDC
obj_ext = "bc";
ll_ext = "ll";
bc_ext = "bc";
#if _WIN32
nativeobj_ext = "obj";
#elif POSIX
nativeobj_ext = "o";
#else
#error "fix this"
#endif
copyright = "Copyright (c) 1999-2008 by Digital Mars and Tomas Lindquist Olsen";
written = "written by Walter Bright and Tomas Lindquist Olsen";
version = "v1.034";
llvmdc_version = "0.1";
global.structalign = 8;
memset(&params, 0, sizeof(Param));
}
char *Loc::toChars() const
{
OutBuffer buf;
char *p;
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;
}
/**************************************
* Print error message and exit.
*/
void error(Loc loc, const char *format, ...)
{
va_list ap;
va_start(ap, format);
verror(loc, format, ap);
va_end( ap );
}
void verror(Loc loc, const char *format, va_list ap)
{
if (!global.gag)
{
char *p = loc.toChars();
if (*p)
fprintf(stdmsg, "%s: ", p);
mem.free(p);
fprintf(stdmsg, "Error: ");
vfprintf(stdmsg, format, ap);
fprintf(stdmsg, "\n");
fflush(stdmsg);
}
global.errors++;
}
/***************************************
* 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
*(char*)0=0;
#endif
}
extern void backend_init();
extern void backend_term();
void usage()
{
printf("LLVM D Compiler %s (based on DMD %s and LLVM 2.4svn)\n%s\n%s\n",
global.llvmdc_version, global.version, global.copyright, global.written);
printf("\
D Language Documentation: http://www.digitalmars.com/d/1.0/index.html\n\
LLVMDC Homepage: http://www.dsource.org/projects/llvmdc\n\
Usage:\n\
llvmdc files.d ... { -switch }\n\
\n\
files.d D source files\n%s\
-of<filename> name output file to <filename>\n\
-o- do not write object file\n\
-od<objdir> write object files to directory <objdir>\n\
-op do not strip paths from source file\n\
-oq write object files with fully qualified names\n\
\n\
-c do not link\n\
-L<linkerflag> pass <linkerflag> to llvm-ld\n\
\n\
-g add symbolic debug info\n\
-gc add symbolic debug info, pretend to be C\n\
\n\
-w enable warnings\n\
\n\
-H generate 'header' file\n\
-Hd<hdrdir> write 'header' file to <hdrdir> directory\n\
-Hf<filename> write 'header' file to <filename>\n\
\n\
-D generate documentation\n\
-Dd<docdir> write documentation file to <docdir> directory\n\
-Df<filename> write documentation file to <filename>\n\
\n\
Codegen control:\n\
-m<arch> emit code specific to <arch> being one of:\n\
x86 x86-64 ppc32 ppc64\n\
-t<os> emit code specific to <os> being one of:\n\
Linux, Windows, MacOSX, FreeBSD\n\
\n\
-O optimize, same as -O2\n\
-O<n> optimize at level <n> (0-5)\n\
-inline do function inlining\n\
\n\
-debug enables asserts, invariants, contracts, boundscheck\n\
and sets debug=1\n\
-release disables asserts, invariants, contracts boundscheck\n\
\n\
-enable-<feature> and\n\
-disable-<feature> where <feature> is one of\n\
asserts assert statements (default: on)\n\
invariants class and struct invariants (default: on)\n\
contracts function contracts (default: on)\n\
boundscheck array bounds checking (default: on)\n\
-debug=level compile in debug stmts <= level (default: 0)\n\
-debug=ident compile in debug stmts identified by ident\n\
-version=level compile in version code >= level\n\
-version=ident compile in version code identified by ident\n\
\n\
-noasm do not allow use of inline asm\n\
-noruntime do not allow code that generates implicit runtime calls\n\
-noverify do not run the validation pass before writing bitcode\n\
-unittest compile in unit tests\n\
-d allow deprecated features\n\
\n\
-annotate annotate the bitcode with human readable source code\n\
-dis disassemble module after compiling\n\
-ignore ignore unsupported pragmas\n\
\n\
Path options:\n\
-I<path> where to look for imports\n\
-J<path> where to look for string imports\n\
-defaultlib=name set default library for non-debug build\n\
-debuglib=name set default library for debug build\n\
\n\
Misc options:\n\
-v verbose\n\
-vv very verbose (does not include -v)\n\
-quiet suppress unnecessary messages\n\
-run srcfile args... run resulting program, passing args\n\
--help print help\n\
",
#if WIN32
" @cmdfile read arguments from cmdfile\n"
#else
""
#endif
);
}
int main(int argc, char *argv[])
{
int i;
Array files;
char *p;
Module *m;
int status = EXIT_SUCCESS;
int argcstart = argc;
bool very_verbose = false;
// Check for malformed input
if (argc < 1 || !argv)
{
Largs:
error("missing or null command line arguments");
fatal();
}
for (i = 0; i < argc; i++)
{
if (!argv[i])
goto Largs;
}
#if __DMC__ // DMC unique support for response files
if (response_expand(&argc,&argv)) // expand response files
error("can't open response file");
#endif
files.reserve(argc - 1);
// Set default values
#if _WIN32
char buf[MAX_PATH];
GetModuleFileName(NULL, buf, MAX_PATH);
global.params.argv0 = buf;
#else
global.params.argv0 = argv[0];
#endif
global.params.link = 1;
global.params.useAssert = 1;
global.params.useInvariants = 1;
global.params.useIn = 1;
global.params.useOut = 1;
global.params.useArrayBounds = 1;
global.params.useSwitchError = 1;
global.params.useInline = 0; // this one messes things up to a point where codegen breaks
global.params.llvmInline = 0; // use this one instead to know if inline passes should be run
global.params.obj = 1;
global.params.Dversion = 2;
global.params.quiet = 1;
global.params.linkswitches = new Array();
global.params.libfiles = new Array();
global.params.objfiles = new Array();
global.params.ddocfiles = new Array();
global.params.is64bit = sizeof(void*) == 8 ? 1 : 0;
uint16_t endiantest = 0xFF00;
uint8_t endianres = ((uint8_t*)&endiantest)[0];
if (endianres == 0x00)
global.params.isLE = true;
else if (endianres == 0xFF)
global.params.isLE = false;
else {
error("Endian test is broken");
fatal();
}
global.params.llvmArch = 0;
global.params.forceBE = 0;
global.params.noruntime = 0;
global.params.novalidate = 0;
global.params.optimizeLevel = -1;
global.params.runtimeImppath = 0;
global.params.useInlineAsm = 1;
// Predefine version identifiers
#if IN_LLVM
VersionCondition::addPredefinedGlobalIdent("LLVM");
VersionCondition::addPredefinedGlobalIdent("LLVMDC");
#endif
// setup default target os to be build os
#if _WIN32
global.params.os = OSWindows;
#elif linux
global.params.os = OSLinux;
#elif __APPLE__
global.params.os = OSMacOSX;
#elif __FreeBSD__
global.params.os = OSFreeBSD;
#else
#error Unsupported OS
#endif /* linux */
assert(global.params.os != OSinvalid);
//VersionCondition::addPredefinedGlobalIdent("D_Bits");
VersionCondition::addPredefinedGlobalIdent("all");
#if _WIN32
inifile(global.params.argv0, "llvmdc.ini");
#elif POSIX
inifile(global.params.argv0, "llvmdc.conf");
#else
#error
#endif
getenv_setargv("DFLAGS", &argc, &argv);
#if 0
for (i = 0; i < argc; i++)
{
printf("argv[%d] = '%s'\n", i, argv[i]);
}
#endif
for (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, "fPIC") == 0)
global.params.pic = 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, "v") == 0)
global.params.verbose = 1;
else if (strcmp(p + 1, "vv") == 0) {
Logger::enable();
very_verbose = true;
}
else if (strcmp(p + 1, "v1") == 0)
global.params.Dversion = 1;
else if (strcmp(p + 1, "w") == 0)
global.params.warnings = 1;
else if (p[1] == 'O')
{
global.params.optimize = 1;
global.params.optimizeLevel = 2;
if (p[2] != 0) {
int optlevel = atoi(p+2);
if (optlevel < 0 || optlevel > 5) {
error("Optimization level must be between 0 and 5. Using default (%d)",
global.params.optimizeLevel);
}
else {
global.params.optimizeLevel = optlevel;
}
}
}
else if (strcmp(p + 1, "forcebe") == 0)
global.params.forceBE = 1;
else if (strcmp(p + 1, "noruntime") == 0)
global.params.noruntime = 1;
else if (strcmp(p + 1, "noverify") == 0)
global.params.novalidate = 1;
else if (strcmp(p + 1, "dis") == 0)
global.params.disassemble = 1;
else if (strcmp(p + 1, "annotate") == 0)
global.params.llvmAnnotate = 1;
else if (strncmp(p + 1, "enable-", 7) == 0 ||
strncmp(p + 1, "disable-", 8) == 0)
{
bool enable = (p[1] == 'e');
char* feature = p + 1 + (enable ? 7 : 8);
if (strcmp(feature, "asserts") == 0)
global.params.useAssert = enable;
else if (strcmp(feature, "boundscheck") == 0)
global.params.useArrayBounds = enable;
else if (strcmp(feature, "contracts") == 0)
{
global.params.useIn = enable;
global.params.useOut = enable;
}
else if (strcmp(feature, "invariants") == 0)
global.params.useInvariants = enable;
else
error("unrecognized feature '%s'", feature);
}
else if (strcmp(p + 1, "noasm") == 0)
global.params.useInlineAsm = 0;
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 'q':
if (p[3])
goto Lerror;
global.params.fqnNames = 1;
break;
case 0:
error("-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;
}
}
#ifdef _DH
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;
}
}
#endif
else if (strcmp(p + 1, "ignore") == 0)
global.params.ignoreUnsupportedPragmas = 1;
else if (strcmp(p + 1, "inline") == 0) {
// TODO
// the ast rewrites dmd does for inlining messes up the ast.
// someday maybe we can support it, for now llvm does an excellent job at inlining
global.params.useInline = 0; //1
global.params.llvmInline = 1;
}
else if (strcmp(p + 1, "quiet") == 0)
global.params.quiet = 1;
else if (strcmp(p + 1, "release") == 0)
{
global.params.useInvariants = 0;
global.params.useIn = 0;
global.params.useOut = 0;
global.params.useAssert = 0;
global.params.useArrayBounds = 0;
}
else if (strcmp(p + 1, "unittest") == 0)
global.params.useUnitTests = 1;
else if (p[1] == 'I')
{
if (!global.params.imppath)
global.params.imppath = new Array();
global.params.imppath->push(p + 2);
}
else if (p[1] == 'J')
{
if (!global.params.fileImppath)
global.params.fileImppath = new Array();
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(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.useInvariants = 1;
global.params.useIn = 1;
global.params.useOut = 1;
global.params.useAssert = 1;
global.params.useArrayBounds = 1;
global.params.debuglevel = 1;
}
}
else if (memcmp(p + 1, "version", 5) == 0)
{
// Parse:
// -version=number
// -version=identifier
if (p[8] == '=')
{
if (isdigit(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)
{
if(!global.params.defaultlibnames)
global.params.defaultlibnames = new Array();
global.params.defaultlibnames->push(p + 1 + 11);
}
else if (memcmp(p + 1, "debuglib=", 9) == 0)
{
if(!global.params.debuglibnames)
global.params.debuglibnames = new Array();
global.params.debuglibnames->push(p + 1 + 9);
}
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 if (p[1] == 'm')
{
global.params.llvmArch = p+2;
}
else if (p[1] == 't')
{
if(strcmp(p + 2, "Linux") == 0)
global.params.os = OSLinux;
else if(strcmp(p + 2, "Windows") == 0)
global.params.os = OSWindows;
else if(strcmp(p + 2, "MacOSX") == 0)
global.params.os = OSMacOSX;
else if(strcmp(p + 2, "FreeBSD") == 0)
global.params.os = OSFreeBSD;
else
error("unrecognized target os '%s'", p + 2);
}
else
{
Lerror:
error("unrecognized switch '%s'", argv[i]);
continue;
Lnoarg:
error("argument expected for switch '%s'", argv[i]);
continue;
}
}
else
files.push(p);
}
if (global.errors)
{
fatal();
}
if (files.dim == 0)
{ usage();
return EXIT_FAILURE;
}
Array* libs;
if (global.params.symdebug)
libs = global.params.debuglibnames;
else
libs = global.params.defaultlibnames;
if (libs)
{
for (int i = 0; i < libs->dim; i++)
{
char *arg = (char *)mem.malloc(64);
strcpy(arg, "-l");
strncat(arg, (char *)libs->data[i], 64);
global.params.linkswitches->push(arg);
}
}
else
{
char *arg;
arg = (char *)mem.malloc(64);
strcpy(arg, "-lllvmdc-runtime");
global.params.linkswitches->push(arg);
arg = (char *)mem.malloc(64);
strcpy(arg, "-ltango-cc-tango");
global.params.linkswitches->push(arg);
arg = (char *)mem.malloc(64);
strcpy(arg, "-ltango-gc-basic");
global.params.linkswitches->push(arg);
}
if (global.params.run)
global.params.quiet = 1;
if (global.params.useUnitTests)
global.params.useAssert = 1;
if (!global.params.obj)
global.params.link = 0;
if (global.params.link)
{
global.params.exefile = global.params.objname;
global.params.objname = NULL;
}
else if (global.params.run)
{
error("flags conflict with -run");
fatal();
}
else
{
if (global.params.objname && files.dim > 1)
{
error("multiple source files, but only one .obj name");
fatal();
}
}
bool allowForceEndianness = false;
if (global.params.llvmArch == 0) {
#if defined(__x86_64__) || defined(_M_X64)
global.params.llvmArch = "x86-64";
#elif defined(__i386__) || defined(_M_IX86)
global.params.llvmArch = "x86";
#elif defined(__ppc__) || defined(_M_PPC)
if (global.params.is64bit)
global.params.llvmArch = "ppc64";
else
global.params.llvmArch = "ppc32";
#else
#error
#endif
}
if (strcmp(global.params.llvmArch,"x86")==0) {
VersionCondition::addPredefinedGlobalIdent("X86");
global.params.isLE = true;
global.params.is64bit = false;
global.params.cpu = ARCHx86;
global.params.tt_arch = "i686";
global.params.data_layout = "e-p:32:32:32-i1:8:8-i8:8:8-i16:16:16-i32:32:32-i64:32:64-f32:32:32-f64:32:64-f80:32:32-v64:64:64-v128:128:128-a0:0:64";
if (global.params.useInlineAsm) {
VersionCondition::addPredefinedGlobalIdent("LLVM_InlineAsm_X86");
}
}
else if (strcmp(global.params.llvmArch,"x86-64")==0) {
VersionCondition::addPredefinedGlobalIdent("X86_64");
global.params.isLE = true;
global.params.is64bit = true;
global.params.cpu = ARCHx86_64;
global.params.tt_arch = "x86_64";
global.params.data_layout = "e-p:64:64:64-i1:8:8-i8:8:8-i16:16:16-i32:32:32-i64:64:64-f32:32:32-f64:64:64-v64:64:64-v128:128:128-a0:0:64";
}
else if (strcmp(global.params.llvmArch,"ppc32")==0) {
VersionCondition::addPredefinedGlobalIdent("PPC");
global.params.isLE = false;
global.params.is64bit = false;
global.params.cpu = ARCHppc;
global.params.tt_arch = "powerpc";
global.params.data_layout = "E-p:32:32:32-i1:8:8-i8:8:8-i16:16:16-i32:32:32-i64:32:64-f32:32:32-f64:32:64-v64:64:64-v128:128:128-a0:0:64";
}
else if (strcmp(global.params.llvmArch,"ppc64")==0) {
VersionCondition::addPredefinedGlobalIdent("PPC64");
global.params.isLE = false;
global.params.is64bit = true;
global.params.cpu = ARCHppc_64;
global.params.tt_arch = "powerpc64";
global.params.data_layout = "E-p:64:64:64-i1:8:8-i8:8:8-i16:16:16-i32:32:32-i64:64:64-f32:32:32-f64:64:64-v64:64:64-v128:128:128-a0:0:64";
}
else {
assert(0 && "Invalid arch");
}
assert(global.params.cpu != ARCHinvalid);
if (allowForceEndianness && global.params.forceBE) {
VersionCondition::addPredefinedGlobalIdent("BigEndian");
global.params.isLE = false;
}
else if (global.params.isLE) {
VersionCondition::addPredefinedGlobalIdent("LittleEndian");
}
else {
VersionCondition::addPredefinedGlobalIdent("BigEndian");
}
if (global.params.is64bit) {
VersionCondition::addPredefinedGlobalIdent("LLVM64");
}
// setup version idents and tt_os for chosen target os
switch(global.params.os)
{
case OSWindows:
VersionCondition::addPredefinedGlobalIdent("Windows");
VersionCondition::addPredefinedGlobalIdent("Win32");
VersionCondition::addPredefinedGlobalIdent("mingw32");
global.params.tt_os = "-pc-mingw32";
break;
case OSLinux:
VersionCondition::addPredefinedGlobalIdent("linux");
VersionCondition::addPredefinedGlobalIdent("Posix");
global.params.tt_os = "-pc-linux-gnu";
break;
case OSMacOSX:
VersionCondition::addPredefinedGlobalIdent("darwin");
VersionCondition::addPredefinedGlobalIdent("Posix");
global.params.tt_os = "-pc-darwin-gnu";
break;
case OSFreeBSD:
VersionCondition::addPredefinedGlobalIdent("freebsd");
VersionCondition::addPredefinedGlobalIdent("Posix");
break;
default:
assert(false && "Target OS not supported");
}
// Initialization
Type::init();
Id::initialize();
Module::init();
initPrecedence();
backend_init();
//printf("%d source files\n",files.dim);
// Build import search path
if (global.params.imppath)
{
for (i = 0; i < global.params.imppath->dim; i++)
{
char *path = (char *)global.params.imppath->data[i];
Array *a = FileName::splitPath(path);
if (a)
{
if (!global.path)
global.path = new Array();
global.path->append(a);
}
}
}
// Build string import search path
if (global.params.fileImppath)
{
for (i = 0; i < global.params.fileImppath->dim; i++)
{
char *path = (char *)global.params.fileImppath->data[i];
Array *a = FileName::splitPath(path);
if (a)
{
if (!global.filePath)
global.filePath = new Array();
global.filePath->append(a);
}
}
}
// Create Modules
Array modules;
modules.reserve(files.dim);
for (i = 0; i < files.dim; i++)
{ Identifier *id;
char *ext;
char *name;
p = (char *) files.data[i];
p = FileName::name(p); // strip path
ext = FileName::ext(p);
if (ext)
{
#if IN_LLVM
if (strcmp(ext, global.nativeobj_ext) == 0 ||
strcmp(ext, global.obj_ext) == 0)
#elif TARGET_LINUX
if (strcmp(ext, global.obj_ext) == 0)
#else
if (stricmp(ext, global.obj_ext) == 0)
#endif
{
global.params.objfiles->push(files.data[i]);
continue;
}
#if TARGET_LINUX || __MINGW32__
if (strcmp(ext, "a") == 0)
#else
if (stricmp(ext, "lib") == 0)
#endif
{
global.params.libfiles->push(files.data[i]);
continue;
}
if (strcmp(ext, global.ddoc_ext) == 0)
{
global.params.ddocfiles->push(files.data[i]);
continue;
}
#if !TARGET_LINUX
if (stricmp(ext, "res") == 0)
{
global.params.resfile = (char *)files.data[i];
continue;
}
if (stricmp(ext, "def") == 0)
{
global.params.deffile = (char *)files.data[i];
continue;
}
if (stricmp(ext, "exe") == 0)
{
global.params.exefile = (char *)files.data[i];
continue;
}
#endif
if (stricmp(ext, global.mars_ext) == 0 ||
stricmp(ext, global.hdr_ext) == 0 ||
stricmp(ext, "htm") == 0 ||
stricmp(ext, "html") == 0 ||
stricmp(ext, "xhtml") == 0)
{
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("invalid file name '%s'", (char *)files.data[i]);
fatal();
}
}
else
{ error("unrecognized file extension %s\n", ext);
fatal();
}
}
else
{ name = p;
if (!*name)
goto Linvalid;
}
id = new Identifier(name, 0);
m = new Module((char *) files.data[i], id, global.params.doDocComments, global.params.doHdrGeneration);
modules.push(m);
}
// Read files, parse them
for (i = 0; i < modules.dim; i++)
{
m = (Module *)modules.data[i];
if (global.params.verbose)
printf("parse %s\n", m->toChars());
if (!Module::rootModule)
Module::rootModule = m;
m->importedFrom = m;
m->read(0);
m->parse();
m->buildTargetFiles();
m->deleteObjFile();
if (m->isDocFile)
{
m->gendocfile();
// Remove m from list of modules
modules.remove(i);
i--;
}
}
if (global.errors)
fatal();
#ifdef _DH
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 (i = 0; i < modules.dim; i++)
{
m = (Module *)modules.data[i];
if (global.params.verbose)
printf("import %s\n", m->toChars());
m->genhdrfile();
}
}
if (global.errors)
fatal();
#endif
// Do semantic analysis
for (i = 0; i < modules.dim; i++)
{
m = (Module *)modules.data[i];
if (global.params.verbose)
printf("semantic %s\n", m->toChars());
m->semantic();
}
if (global.errors)
fatal();
// Do pass 2 semantic analysis
for (i = 0; i < modules.dim; i++)
{
m = (Module *)modules.data[i];
if (global.params.verbose)
printf("semantic2 %s\n", m->toChars());
m->semantic2();
}
if (global.errors)
fatal();
// Do pass 3 semantic analysis
for (i = 0; i < modules.dim; i++)
{
m = (Module *)modules.data[i];
if (global.params.verbose)
printf("semantic3 %s\n", m->toChars());
m->semantic3();
}
if (global.errors)
fatal();
#if !IN_LLVM
// Scan for functions to inline
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.
*/
if (!global.params.useArrayBounds && !global.params.useAssert)
{
#endif
// Do pass 3 semantic analysis on all imported modules,
// since otherwise functions in them cannot be inlined
for (i = 0; i < Module::amodules.dim; i++)
{
m = (Module *)Module::amodules.data[i];
if (global.params.verbose)
printf("semantic3 %s\n", m->toChars());
m->semantic3();
}
if (global.errors)
fatal();
#if !IN_LLVM
}
for (i = 0; i < modules.dim; i++)
{
m = (Module *)modules.data[i];
if (global.params.verbose)
printf("inline scan %s\n", m->toChars());
m->inlineScan();
}
}
if (global.errors)
fatal();
#endif
// Generate output files
for (i = 0; i < modules.dim; i++)
{
m = (Module *)modules.data[i];
if (global.params.verbose)
printf("code %s\n", m->toChars());
if (global.params.obj)
{
m->genobjfile(0);
global.params.objfiles->push(m->objfile->name->str);
}
if (global.errors)
m->deleteObjFile();
else
{
if (global.params.doDocComments)
m->gendocfile();
}
}
backend_term();
if (global.errors)
fatal();
if (!global.params.objfiles->dim)
{
if (global.params.link)
error("no object files to link");
}
else
{
if (global.params.link)
//status = runLINK();
linkExecutable(global.params.argv0);
if (global.params.run)
{
if (!status)
{
status = runExectuable();
/* Delete .obj files and .exe file
*/
for (i = 0; i < modules.dim; i++)
{
m = (Module *)modules.data[i];
m->deleteObjFile();
}
deleteExecutable();
}
}
}
return status;
}
/***********************************
* 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 *env;
char *p;
Array *argv;
int argc;
int wildcard; // do wildcard expansion
int instring;
int slash;
char c;
int j;
env = getenv(envvar);
if (!env)
return;
env = mem.strdup(env); // create our own writable copy
argc = *pargc;
argv = new Array();
argv->setDim(argc);
for (int i = 0; i < argc; i++)
argv->data[i] = (void *)(*pargv)[i];
j = 1; // leave argv[0] alone
while (1)
{
wildcard = 1;
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:
*pargc = argc;
*pargv = (char **)argv->data;
}