diff dmd/Util.d @ 0:10317f0c89a5

Initial commit
author korDen
date Sat, 24 Oct 2009 08:42:06 +0400
parents
children 7427ded8caf7
line wrap: on
line diff
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/dmd/Util.d	Sat Oct 24 08:42:06 2009 +0400
@@ -0,0 +1,1089 @@
+module dmd.Util;
+
+import dmd.Loc;
+import dmd.Library;
+import dmd.File;
+import dmd.String;
+import dmd.OutBuffer;
+import dmd.FileName;
+import dmd.Global;
+import dmd.PREC;
+import dmd.TOK;
+
+import std.process : getenv;
+import std.c.string;
+import std.stdio : writef, writefln, write;
+import std.c.process : spawnl, spawnlp;
+import core.stdc.stdlib;
+import core.stdc.ctype;
+import core.stdc.stdarg;
+import core.stdc.stdio;
+
+extern(C) int putenv(char*);
+
+//version = LOG;
+
+version (Windows) {
+} else {
+	import core.sys.posix.stdlib : putenv;
+}
+
+enum MAX_PATH = 256; ///
+
+version (Windows) {
+	import core.sys.windows.windows : GetModuleFileNameA;
+}
+
+string fromStringz(const(char)* s)
+{
+	return s[0..strlen(s)].idup;
+}
+
+void warning(T...)(string format, T t)
+{
+	assert(false);
+}
+
+void warning(T...)(Loc loc, string format, T t)
+{
+	assert(false);
+}
+
+void error(T...)(Loc loc, string format, T t)
+{
+	if (!global.gag)
+    {
+		string p = loc.toChars();
+
+		if (p.length != 0)
+			writef("%s: ", p);
+
+		write("Error: ");
+		writefln(format, t);
+
+		//halt();
+    }
+    global.errors++;
+}
+
+char* strupr(char* s)
+{
+    char* t = s;
+    
+    while (*s)
+    {
+		*s = cast(char)toupper(*s);
+		s++;
+    }
+
+    return t;
+}
+
+char[] skipspace(char[] p)
+{
+	foreach (i, c; p) {
+		if (!isspace(c)) {
+			return p[i..$];
+		}
+	}
+	
+	return null;
+}
+
+char* skipspace(char* p)
+{
+    while (isspace(*p))
+		p++;
+		
+	return p;
+}
+
+void inifile(string argv0, string inifile)
+{
+    char *path;		// need path for @P macro
+    string filename;
+    int i;
+    int k;
+    int envsection = 0;
+
+version (LOG) {
+    writef("inifile(argv0 = '%s', inifile = '%s')\n", argv0, inifile);
+}
+    if (FileName.absolute(inifile)) {
+		filename = inifile;
+    } else {
+		/* Look for inifile in the following sequence of places:
+		 *	o current directory
+		 *	o home directory
+		 *	o directory off of argv0
+		 *	o /etc/
+		 */
+		if (FileName.exists(inifile)) {
+			filename = inifile;
+		} else {
+			filename = FileName.combine(getenv("HOME"), inifile);
+			if (!FileName.exists(filename)) {
+version (_WIN32) { // This fix by Tim Matthews
+				char resolved_name_b[MAX_PATH + 1];
+				auto resolved_name = resolved_name_b[].idup;
+				if (GetModuleFileNameA(null, resolved_name_b.ptr, MAX_PATH + 1) && FileName.exists(resolved_name))
+				{
+					filename = FileName.replaceName(resolved_name, inifile);
+					if (FileName.exists(filename)) {
+						goto Ldone;
+					}
+				}
+}
+				filename = FileName.replaceName(argv0, inifile);
+				if (!FileName.exists(filename)) {
+version (XXX) { /// linux || __APPLE__ || __FreeBSD__ || __sun&&__SVR4
+	version (XXX) { /// __GLIBC__ || __APPLE__ || __FreeBSD__ || __sun&&__SVR4   // This fix by Thomas Kuehne
+					/* argv0 might be a symbolic link,
+					* so try again looking past it to the real path
+					*/
+		version (XXX) {/// #if __APPLE__ || __FreeBSD__ || __sun&&__SVR4
+					char resolved_name[PATH_MAX + 1];
+					char* real_argv0 = realpath(argv0, resolved_name);
+		} else {
+					char* real_argv0 = realpath(argv0, null);
+		}
+					//printf("argv0 = %s, real_argv0 = %p\n", argv0, real_argv0);
+					if (real_argv0) {
+						filename = FileName.replaceName(real_argv0, inifile);
+		version (linux) {
+						free(real_argv0);
+		}
+						if (FileName.exists(filename)) {
+							goto Ldone;
+						}
+					}
+	} else {
+					static assert (false, "use of glibc non-standard extension realpath(char*, null)");
+	}
+					if (true) {
+						// Search PATH for argv0
+						const(char)* p = getenv("PATH");
+	version (LOG) {
+						writef("\tPATH='%s'\n", p);
+	}
+						Array paths = FileName.splitPath(p);
+						filename = FileName.searchPath(paths, argv0, 0);
+						if (!filename) {
+							goto Letc;		// argv0 not found on path
+						}
+						
+						filename = FileName.replaceName(filename, inifile);
+						if (FileName.exists(filename)) {
+							goto Ldone;
+						}
+					}
+}
+					// Search /etc/ for inifile
+		Letc:
+					filename = FileName.combine("/etc/", inifile);
+
+		Ldone:
+		    ;
+				}
+			}
+		}
+    }
+	
+    path = cast(char*)toStringz(FileName.path(filename));
+
+version (LOG) {
+    writef("\tpath = '%s', filename = '%s'\n", fromStringz(path), filename);
+}
+
+    scope File file = new File(filename);
+
+    if (file.read()) {
+		return;			// error reading file
+	}
+	
+	scope OutBuffer buf = new OutBuffer();
+
+    // Parse into lines
+    int eof = 0;
+    for (i = 0; i < file.len && !eof; i++)
+    {
+		int linestart = i;
+
+		for (; i < file.len; i++)
+		{
+			switch (file.buffer[i])
+			{
+				case '\r':
+					break;
+
+				case '\n':
+					// Skip if it was preceded by '\r'
+					if (i && file.buffer[i - 1] == '\r')
+						goto Lskip;
+					break;
+
+				case 0:
+				case 0x1A:
+					eof = 1;
+					break;
+
+				default:
+					continue;
+				}
+				break;
+		}
+
+		// The line is file.buffer[linestart..i]
+		char *line;
+		int len;
+		char *p;
+		char* pn;
+
+		line = cast(char*)&file.buffer[linestart];
+		len = i - linestart;
+
+		buf.reset();
+
+		// First, expand the macros.
+		// Macros are bracketed by % characters.
+
+		for (k = 0; k < len; k++)
+		{
+			if (line[k] == '%')
+			{
+				int j;
+
+				for (j = k + 1; j < len; j++)
+				{
+					if (line[j] == '%')
+					{
+						if (j - k == 3 && memicmp(&line[k + 1], "@P", 2) == 0)
+						{
+							// %@P% is special meaning the path to the .ini file
+							p = path;
+							if (!*p)
+								p = cast(char*)".";
+						}
+						else
+						{
+							int l = j - k;
+							char tmp[10];	// big enough most of the time
+
+							if (l <= tmp.sizeof)
+								p = tmp.ptr;
+							else
+								p = cast(char*)alloca(l);
+							l--;
+							memcpy(p, &line[k + 1], l);
+							p[l] = 0;
+							strupr(p);
+							p = core.stdc.stdlib.getenv(p);
+							if (!p)
+								p = cast(char*)"";
+						}
+						buf.writestring(p[0..strlen(p)]);	///
+						k = j;
+						goto L1;
+					}
+				}
+			}
+			buf.writeByte(line[k]);
+		 L1:
+			;
+		}
+
+		// Remove trailing spaces
+		while (buf.offset && isspace(buf.data[buf.offset - 1]))
+			buf.offset--;
+
+		char[] pp = buf.getString();
+
+		// The expanded line is in p.
+		// Now parse it for meaning.
+
+		pp = skipspace(pp);
+		if (pp.length != 0) {
+			switch (pp[0])
+			{
+				case ';':		// comment
+					break;
+
+				case '[':		// look for [Environment]
+					pp = skipspace(pp[1..$]);
+					for (pn = pp.ptr; isalnum(*pn); pn++) {
+						;
+					}
+					
+					if (pn - pp.ptr == 11 &&
+						memicmp(pp.ptr, "Environment", 11) == 0 &&
+						*skipspace(pn) == ']'
+					   )
+						envsection = 1;
+					else
+						envsection = 0;
+					break;
+
+				default:
+					if (envsection)
+					{
+						pn = pp.ptr;
+
+						// Convert name to upper case;
+						// remove spaces bracketing =
+						auto p2 = pn;
+						for ( ; *p2; p2++)
+						{   if (islower(*p2))
+								*p2 &= ~0x20;
+							else if (isspace(*p))
+								memmove(p2, p2 + 1, strlen(p2));
+							else if (*p2 == '=')
+							{
+								p2++;
+								while (isspace(*p2))
+									memmove(p2, p2 + 1, strlen(p2));
+								break;
+							}
+						}
+
+						//putenv(pn);
+						putenv(cast(char*)toStringz(pp));
+
+version (LOG) {
+						writef("\tputenv('%s')\n", pn[0..strlen(pn)]);
+						//printf("getenv(\"TEST\") = '%s'\n",getenv("TEST"));
+}
+					}
+					break;
+			}
+		}
+
+	Lskip:
+		;
+    }
+}
+
+///int response_expand(int *pargc, char ***pargv);
+void browse(const(char)* url)
+{
+	assert(false);
+}
+
+string[] getenv_setargv(string envvar, string[] args)
+{
+    char *p;
+
+    string[] argv = args.dup;
+    int argc = args.length;
+
+    int wildcard;		// do wildcard expansion
+    int instring;
+    int slash;
+    char c;
+    int j;
+
+    string ienv = getenv(envvar);
+    if (ienv is null)
+		return args;
+
+    char[] env = ienv.dup;	// create our own writable copy
+
+    j = 1;			// leave argv[0] alone
+	char* e = env.ptr;
+    while (1)
+    {
+		wildcard = 1;
+		switch (*e)
+		{
+			case ' ':
+			case '\t':
+			e++;
+			break;
+
+			case 0:
+			goto Ldone;
+
+			case '"':
+			wildcard = 0;
+			default:
+			argv ~= assumeUnique(e[0..strlen(e)]);		// append
+			//argv.insert(j, env);		// insert at position j
+			j++;
+			argc++;
+			p = e;
+			slash = 0;
+			instring = 0;
+			c = 0;
+			
+			char* ecopy = e;
+
+			while (1)
+			{
+				c = *e++;
+				switch (c)
+				{
+				case '"':
+					p -= (slash >> 1);
+					if (slash & 1)
+					{
+						p--;
+						goto Laddc;
+					}
+					instring ^= 1;
+					slash = 0;
+					continue;
+
+				case '\\':
+					slash++;
+					*p++ = c;
+					continue;
+
+				case ' ':
+				case '\t':
+					if (instring)
+						goto Laddc;
+				case 0:
+					*p = 0;
+					if (argv.length != 0) {
+						argv[$-1].length = p - argv[$-1].ptr;
+					}
+					//if (wildcard)
+					//wildcardexpand();	// not implemented
+					if (c == 0) goto Ldone;
+					break;
+
+				default:
+				Laddc:
+					slash = 0;
+					*p++ = c;
+					continue;
+				}
+				break;
+			}
+		}
+    }
+
+Ldone:
+    return argv;
+}
+
+void error(T...)(string format, T t)
+{
+	writefln(format, t);
+    exit(EXIT_FAILURE);
+}
+
+void usage()
+{
+	writef("Digital Mars D Compiler %s\n%s %s\n", global.version_, global.copyright, global.written);
+    writef(
+"Documentation: http://www.digitalmars.com/d/2.0/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"
+"  -g             add symbolic debug info\n"
+"  -gc            add symbolic debug info, pretend to be C\n"
+"  -H             generate 'header' file\n"
+"  -Hdhdrdir      write 'header' file to hdrdir 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"
+"  -man           open web browser on manual page\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"
+"  -quiet         suppress unnecessary messages\n"
+"  -release	 compile release version\n"
+"  -run srcfile args...   run resulting program, passing args\n"
+"  -safe          safe memory model\n"
+"  -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             enable warnings\n"
+);
+}
+
+void fatal()
+{
+static if (false) {
+    halt();
+} else {
+    exit(EXIT_FAILURE);
+}
+}
+
+void halt()
+{
+	assert(false);
+}
+
+void initPrecedence()
+{
+	precedence[TOK.TOKdotvar] = PREC.PREC_primary;
+    precedence[TOK.TOKimport] = PREC.PREC_primary;
+    precedence[TOK.TOKidentifier] = PREC.PREC_primary;
+    precedence[TOK.TOKthis] = PREC.PREC_primary;
+    precedence[TOK.TOKsuper] = PREC.PREC_primary;
+    precedence[TOK.TOKint64] = PREC.PREC_primary;
+    precedence[TOK.TOKfloat64] = PREC.PREC_primary;
+    precedence[TOK.TOKnull] = PREC.PREC_primary;
+    precedence[TOK.TOKstring] = PREC.PREC_primary;
+    precedence[TOK.TOKarrayliteral] = PREC.PREC_primary;
+    precedence[TOK.TOKtypeid] = PREC.PREC_primary;
+    precedence[TOK.TOKis] = PREC.PREC_primary;
+    precedence[TOK.TOKassert] = PREC.PREC_primary;
+    precedence[TOK.TOKfunction] = PREC.PREC_primary;
+    precedence[TOK.TOKvar] = PREC.PREC_primary;
+version (DMDV2) {
+    precedence[TOK.TOKdefault] = PREC.PREC_primary;
+}
+
+    // post
+    precedence[TOK.TOKdotti] = PREC.PREC_primary;
+    precedence[TOK.TOKdot] = PREC.PREC_primary;
+//  precedence[TOK.TOKarrow] = PREC.PREC_primary;
+    precedence[TOK.TOKplusplus] = PREC.PREC_primary;
+    precedence[TOK.TOKminusminus] = PREC.PREC_primary;
+    precedence[TOK.TOKcall] = PREC.PREC_primary;
+    precedence[TOK.TOKslice] = PREC.PREC_primary;
+    precedence[TOK.TOKarray] = PREC.PREC_primary;
+
+    precedence[TOK.TOKaddress] = PREC.PREC_unary;
+    precedence[TOK.TOKstar] = PREC.PREC_unary;
+    precedence[TOK.TOKneg] = PREC.PREC_unary;
+    precedence[TOK.TOKuadd] = PREC.PREC_unary;
+    precedence[TOK.TOKnot] = PREC.PREC_unary;
+    precedence[TOK.TOKtobool] = PREC.PREC_add;
+    precedence[TOK.TOKtilde] = PREC.PREC_unary;
+    precedence[TOK.TOKdelete] = PREC.PREC_unary;
+    precedence[TOK.TOKnew] = PREC.PREC_unary;
+    precedence[TOK.TOKcast] = PREC.PREC_unary;
+
+    precedence[TOK.TOKmul] = PREC.PREC_mul;
+    precedence[TOK.TOKdiv] = PREC.PREC_mul;
+    precedence[TOK.TOKmod] = PREC.PREC_mul;
+
+    precedence[TOK.TOKadd] = PREC.PREC_add;
+    precedence[TOK.TOKmin] = PREC.PREC_add;
+    precedence[TOK.TOKcat] = PREC.PREC_add;
+
+    precedence[TOK.TOKshl] = PREC.PREC_shift;
+    precedence[TOK.TOKshr] = PREC.PREC_shift;
+    precedence[TOK.TOKushr] = PREC.PREC_shift;
+
+    precedence[TOK.TOKlt] = PREC.PREC_rel;
+    precedence[TOK.TOKle] = PREC.PREC_rel;
+    precedence[TOK.TOKgt] = PREC.PREC_rel;
+    precedence[TOK.TOKge] = PREC.PREC_rel;
+    precedence[TOK.TOKunord] = PREC.PREC_rel;
+    precedence[TOK.TOKlg] = PREC.PREC_rel;
+    precedence[TOK.TOKleg] = PREC.PREC_rel;
+    precedence[TOK.TOKule] = PREC.PREC_rel;
+    precedence[TOK.TOKul] = PREC.PREC_rel;
+    precedence[TOK.TOKuge] = PREC.PREC_rel;
+    precedence[TOK.TOKug] = PREC.PREC_rel;
+    precedence[TOK.TOKue] = PREC.PREC_rel;
+    precedence[TOK.TOKin] = PREC.PREC_rel;
+
+static if (false) {
+    precedence[TOK.TOKequal] = PREC.PREC_equal;
+    precedence[TOK.TOKnotequal] = PREC.PREC_equal;
+    precedence[TOK.TOKidentity] = PREC.PREC_equal;
+    precedence[TOK.TOKnotidentity] = PREC.PREC_equal;
+} else {
+    /* Note that we changed precedence, so that < and != have the same
+     * precedence. This change is in the parser, too.
+     */
+    precedence[TOK.TOKequal] = PREC.PREC_rel;
+    precedence[TOK.TOKnotequal] = PREC.PREC_rel;
+    precedence[TOK.TOKidentity] = PREC.PREC_rel;
+    precedence[TOK.TOKnotidentity] = PREC.PREC_rel;
+}
+
+    precedence[TOK.TOKand] = PREC.PREC_and;
+
+    precedence[TOK.TOKxor] = PREC.PREC_xor;
+
+    precedence[TOK.TOKor] = PREC.PREC_or;
+
+    precedence[TOK.TOKandand] = PREC.PREC_andand;
+
+    precedence[TOK.TOKoror] = PREC.PREC_oror;
+
+    precedence[TOK.TOKquestion] = PREC.PREC_cond;
+
+    precedence[TOK.TOKassign] = PREC.PREC_assign;
+    precedence[TOK.TOKconstruct] = PREC.PREC_assign;
+    precedence[TOK.TOKblit] = PREC.PREC_assign;
+    precedence[TOK.TOKaddass] = PREC.PREC_assign;
+    precedence[TOK.TOKminass] = PREC.PREC_assign;
+    precedence[TOK.TOKcatass] = PREC.PREC_assign;
+    precedence[TOK.TOKmulass] = PREC.PREC_assign;
+    precedence[TOK.TOKdivass] = PREC.PREC_assign;
+    precedence[TOK.TOKmodass] = PREC.PREC_assign;
+    precedence[TOK.TOKshlass] = PREC.PREC_assign;
+    precedence[TOK.TOKshrass] = PREC.PREC_assign;
+    precedence[TOK.TOKushrass] = PREC.PREC_assign;
+    precedence[TOK.TOKandass] = PREC.PREC_assign;
+    precedence[TOK.TOKorass] = PREC.PREC_assign;
+    precedence[TOK.TOKxorass] = PREC.PREC_assign;
+
+    precedence[TOK.TOKcomma] = PREC.PREC_expr;
+}
+
+int runLINK()
+{
+version (_WIN32) {
+    string p;
+    int i;
+    int status;
+    scope OutBuffer cmdbuf = new OutBuffer();
+
+    global.params.libfiles.push(cast(void*)new String("user32"));
+    global.params.libfiles.push(cast(void*)new String("kernel32"));
+
+    for (i = 0; i < global.params.objfiles.dim; i++)
+    {
+		if (i)
+			cmdbuf.writeByte('+');
+		p = (cast(String)global.params.objfiles.data[i]).str;
+		string ext = FileName.ext(p);
+		if (ext)
+			// Write name sans extension
+			writeFilename(cmdbuf, p[0..p.length - ext.length - 1]);
+		else
+			writeFilename(cmdbuf, p);
+    }
+    cmdbuf.writeByte(',');
+    if (global.params.exefile)
+		writeFilename(cmdbuf, global.params.exefile);
+    else
+    {	
+		/* Generate exe file name from first obj name.
+		 * No need to add it to cmdbuf because the linker will default to it.
+		 */
+		string n = (cast(String)global.params.objfiles.data[0]).str;
+		n = FileName.name(n);
+		FileName fn = FileName.forceExt(n, "exe");
+		global.params.exefile = fn.toChars();
+    }
+
+    // Make sure path to exe file exists
+    {	
+		string pp = FileName.path(global.params.exefile);
+		FileName.ensurePathExists(pp);
+    }
+
+    cmdbuf.writeByte(',');
+    if (global.params.run)
+		cmdbuf.writestring("nul");
+
+//    if (mapfile)
+//	cmdbuf.writestring(output);
+    cmdbuf.writeByte(',');
+
+    for (i = 0; i < global.params.libfiles.dim; i++)
+    {
+		if (i)
+			cmdbuf.writeByte('+');
+		writeFilename(cmdbuf, (cast(String)global.params.libfiles.data[i]).str);
+    }
+
+    if (global.params.deffile)
+    {
+		cmdbuf.writeByte(',');
+		writeFilename(cmdbuf, global.params.deffile);
+    }
+
+    /* Eliminate unnecessary trailing commas	*/
+    while (1)
+    {   
+		i = cmdbuf.offset;
+		if (!i || cmdbuf.data[i - 1] != ',')
+			break;
+		cmdbuf.offset--;
+    }
+
+    if (global.params.resfile)
+    {
+		cmdbuf.writestring("/RC:");
+		writeFilename(cmdbuf, global.params.resfile);
+    }
+
+static if (false) {
+    if (mapfile)
+		cmdbuf.writestring("/m");
+    if (debuginfo)
+		cmdbuf.writestring("/li");
+    if (codeview)
+    {
+		cmdbuf.writestring("/co");
+		if (codeview3)
+			cmdbuf.writestring(":3");
+    }
+} else {
+    if (global.params.symdebug)
+		cmdbuf.writestring("/co");
+}
+
+    cmdbuf.writestring("/noi");
+    for (i = 0; i < global.params.linkswitches.dim; i++)
+    {
+		cmdbuf.writestring((cast(String)global.params.linkswitches.data[i]).str);
+    }
+    cmdbuf.writeByte(';');
+
+    p = cmdbuf.toChars();
+
+    FileName lnkfilename = null;
+    size_t plen = p.length;
+    if (plen > 7000)
+    {
+		lnkfilename = FileName.forceExt(global.params.exefile, "lnk");
+		scope File flnk = new File(lnkfilename);
+		flnk.setbuffer(cast(void*)p.ptr, plen);
+		flnk.ref_ = 1;
+		if (flnk.write())
+			error("error writing file %s", lnkfilename);
+		if (lnkfilename.len() < plen)
+			p = std.string.format("@%s", lnkfilename.toChars());
+    }
+
+    string linkcmd = getenv("LINKCMD");
+    if (!linkcmd)
+		linkcmd = "link";
+
+    status = executecmd(linkcmd, p, 1);
+    if (lnkfilename)
+    {
+		remove(toStringz(lnkfilename.toChars()));
+		///delete lnkfilename;
+    }
+    return status;
+} else if (XXX) {/// linux || __APPLE__ || __FreeBSD__ || __sun&&__SVR4
+	assert(false);
+	/+
+    pid_t childpid;
+    int i;
+    int status;
+
+    // Build argv[]
+    Array argv;
+
+    const char *cc = getenv("CC");
+    if (!cc)
+	cc = "gcc";
+    argv.push((void *)cc);
+    argv.insert(1, global.params.objfiles);
+
+    // None of that a.out stuff. Use explicit exe file name, or
+    // generate one from name of first source file.
+    argv.push((void *)"-o");
+    if (global.params.exefile)
+    {
+	argv.push(global.params.exefile);
+    }
+    else
+    {	// Generate exe file name from first obj name
+	char *n = (char *)global.params.objfiles.data[0];
+	char *e;
+	char *ex;
+
+	n = FileName.name(n);
+	e = FileName.ext(n);
+	if (e)
+	{
+	    e--;			// back up over '.'
+	    ex = (char *)mem.malloc(e - n + 1);
+	    memcpy(ex, n, e - n);
+	    ex[e - n] = 0;
+	}
+	else
+	    ex = (char *)"a.out";	// no extension, so give up
+	argv.push(ex);
+	global.params.exefile = ex;
+    }
+
+    // Make sure path to exe file exists
+    {	char *p = FileName.path(global.params.exefile);
+	FileName.ensurePathExists(p);
+	mem.free(p);
+    }
+
+    if (global.params.symdebug)
+	argv.push((void *)"-g");
+
+    if (global.params.isX86_64)
+	argv.push((void *)"-m64");
+    else
+	argv.push((void *)"-m32");
+
+    if (0 && global.params.exefile)
+    {
+	/* This switch enables what is known as 'smart linking'
+	 * in the Windows world, where unreferenced sections
+	 * are removed from the executable. It eliminates unreferenced
+	 * functions, essentially making a 'library' out of a module.
+	 * Although it is documented to work with ld version 2.13,
+	 * in practice it does not, but just seems to be ignored.
+	 * Thomas Kuehne has verified that it works with ld 2.16.1.
+	 * BUG: disabled because it causes exception handling to fail
+	 */
+	argv.push((void *)"-Xlinker");
+	argv.push((void *)"--gc-sections");
+    }
+
+    for (i = 0; i < global.params.linkswitches.dim; i++)
+    {	char *p = (char *)global.params.linkswitches.data[i];
+	if (!p || !p[0] || !(p[0] == '-' && p[1] == 'l'))
+	    // Don't need -Xlinker if switch starts with -l
+	    argv.push((void *)"-Xlinker");
+	argv.push((void *) p);
+    }
+
+    /* Add each library, prefixing it with "-l".
+     * The order of libraries passed is:
+     *  1. any libraries passed with -L command line switch
+     *  2. libraries specified on the command line
+     *  3. libraries specified by pragma(lib), which were appended
+     *     to global.params.libfiles.
+     *  4. standard libraries.
+     */
+    for (i = 0; i < global.params.libfiles.dim; i++)
+    {	char *p = (char *)global.params.libfiles.data[i];
+	size_t plen = strlen(p);
+	if (plen > 2 && p[plen - 2] == '.' && p[plen -1] == 'a')
+	    argv.push((void *)p);
+	else
+	{
+	    char *s = (char *)mem.malloc(plen + 3);
+	    s[0] = '-';
+	    s[1] = 'l';
+	    memcpy(s + 2, p, plen + 1);
+	    argv.push((void *)s);
+	}
+    }
+
+    /* Standard libraries must go after user specified libraries
+     * passed with -l.
+     */
+    const char *libname = (global.params.symdebug)
+				? global.params.debuglibname
+				: global.params.defaultlibname;
+    char *buf = (char *)malloc(2 + strlen(libname) + 1);
+    strcpy(buf, "-l");
+    strcpy(buf + 2, libname);
+    argv.push((void *)buf);		// turns into /usr/lib/libphobos2.a
+
+//    argv.push((void *)"-ldruntime");
+    argv.push((void *)"-lpthread");
+    argv.push((void *)"-lm");
+
+    if (!global.params.quiet || global.params.verbose)
+    {
+	// Print it
+	for (i = 0; i < argv.dim; i++)
+	    printf("%s ", (char *)argv.data[i]);
+	printf("\n");
+	fflush(stdout);
+    }
+
+    argv.push(null);
+    childpid = fork();
+    if (childpid == 0)
+    {
+	execvp((char *)argv.data[0], (char **)argv.data);
+	perror((char *)argv.data[0]);		// failed to execute
+	return -1;
+    }
+
+    waitpid(childpid, &status, 0);
+
+    status=WEXITSTATUS(status);
+    if (status)
+	printf("--- errorlevel %d\n", status);
+    return status;
+	+/
+} else {
+    writef ("Linking is not yet supported for this version of DMD.\n");
+    return -1;
+}
+}
+
+int runProgram()
+{
+	assert(false);
+}
+
+void deleteExeFile()
+{
+	assert(false);
+}
+
+/****************************************
+ * Write filename to cmdbuf, quoting if necessary.
+ */
+
+void writeFilename(OutBuffer buf, string filename)
+{
+	auto len = filename.length;
+    /* Loop and see if we need to quote
+     */
+    for (size_t i = 0; i < len; i++)
+    {	
+		char c = filename[i];
+
+		if (isalnum(c) || c == '_')
+			continue;
+
+		/* Need to quote
+		 */
+		buf.writeByte('"');
+		buf.writestring(filename);
+		buf.writeByte('"');
+		return;
+    }
+
+    /* No quoting necessary
+     */
+    buf.writestring(filename);
+}
+
+/******************************
+ * Execute a rule.  Return the status.
+ *	cmd	program to run
+ *	args	arguments to cmd, as a string
+ *	useenv	if cmd knows about _CMDLINE environment variable
+ */
+
+version (_WIN32) {
+int executecmd(string cmd, string args, int useenv)
+{
+    int status;
+    size_t len = args.length;
+
+    if (!global.params.quiet || global.params.verbose)
+    {
+		printf("%s %s\n", cmd, args);
+		fflush(stdout);
+    }
+
+    if (len > 255)
+    {   
+		char* q;
+		static char[9] envname = "@_CMDLINE";
+
+		envname[0] = '@';
+		switch (useenv)
+		{   
+			case 0:	goto L1;
+			case 2: envname[0] = '%';	break;
+			default: break;	///
+		}
+		q = cast(char*) alloca(envname.sizeof + len + 1);
+		sprintf(q, "%s=%s", envname.ptr + 1, args);
+		status = putenv(q);
+		if (status == 0)
+			args = envname[].idup;
+		else
+		{
+		L1:
+			error("command line length of %d is too long",len);
+		}
+    }
+
+    status = executearg0(cmd, args);
+version (_WIN32) {
+    if (status == -1) {
+		auto cmdZ = toStringz(cmd);
+		auto argsZ = toStringz(args);
+		status = spawnlp(0, cmdZ, cmdZ, argsZ, null);
+	}
+}
+//    if (global.params.verbose)
+//	printf("\n");
+    if (status)
+    {
+	if (status == -1)
+	    printf("Can't run '%s', check PATH\n", cmd);
+	else
+	    printf("--- errorlevel %d\n", status);
+    }
+    return status;
+}
+}
+
+/**************************************
+ * Attempt to find command to execute by first looking in the directory
+ * where DMD was run from.
+ * Returns:
+ *	-1	did not find command there
+ *	!=-1	exit status from command
+ */
+
+version (_WIN32) {
+int executearg0(string cmd, string args)
+{
+    string file;
+    string argv0 = global.params.argv0;
+
+    //printf("argv0='%s', cmd='%s', args='%s'\n",argv0,cmd,args);
+
+    // If cmd is fully qualified, we don't do this
+    if (FileName.absolute(cmd))
+		return -1;
+
+    file = FileName.replaceName(argv0, cmd);
+
+    //printf("spawning '%s'\n",file);
+version (_WIN32) {
+	auto fileZ = toStringz(file);
+	auto argsZ = toStringz(args);
+    return spawnl(0, fileZ, fileZ, argsZ, null);
+} else version (XXX) { ///#elif linux || __APPLE__ || __FreeBSD__ || __sun&&__SVR4
+	assert(false);
+	/+
+    char *full;
+    int cmdl = strlen(cmd);
+
+    full = (char*) mem.malloc(cmdl + strlen(args) + 2);
+    if (full == null)
+	return 1;
+    strcpy(full, cmd);
+    full [cmdl] = ' ';
+    strcpy(full + cmdl + 1, args);
+
+    int result = system(full);
+
+    mem.free(full);
+    return result;
+	+/
+} else {
+    static assert(false);
+}
+}
+}
\ No newline at end of file