diff dmd/link.c @ 159:5acec6b2eef8 trunk

[svn r175] merged dmd 1.029
author ChristianK
date Thu, 01 May 2008 15:15:28 +0200
parents 44a95ac7368a
children b77664331d06
line wrap: on
line diff
--- a/dmd/link.c	Thu May 01 13:33:02 2008 +0200
+++ b/dmd/link.c	Thu May 01 15:15:28 2008 +0200
@@ -1,513 +1,513 @@
-
-
-// Copyright (c) 1999-2007 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	<ctype.h>
-#include	<assert.h>
-#include	<stdarg.h>
-#include	<string.h>
-#include	<stdlib.h>
-
-#if _WIN32
-#include	<process.h>
-#endif
-
-#if linux
-#include	<sys/types.h>
-#include	<sys/wait.h>
-#include	<unistd.h>
-#endif
-
-#include	"root.h"
-
-#include	"mars.h"
-
-#include	"mem.h"
-
-int executecmd(char *cmd, char *args, int useenv);
-int executearg0(char *cmd, char *args);
-
-/*****************************
- * Run the linker.  Return status of execution.
- */
-
-int runLINK()
-{
-#if _WIN32
-    assert(0 && "linking not done for win32");
-
-    char *p;
-    int i;
-    int status;
-    OutBuffer cmdbuf;
-
-    global.params.libfiles->push((void *) "user32");
-    global.params.libfiles->push((void *) "kernel32");
-
-    for (i = 0; i < global.params.objfiles->dim; i++)
-    {
-	if (i)
-	    cmdbuf.writeByte('+');
-	p = (char *)global.params.objfiles->data[i];
-	char *ext = FileName::ext(p);
-	if (ext)
-	    cmdbuf.write(p, ext - p - 1);
-	else
-	    cmdbuf.writestring(p);
-    }
-    cmdbuf.writeByte(',');
-    if (global.params.exefile)
-	cmdbuf.writestring(global.params.exefile);
-    else
-    {	// Generate exe file name from first obj name
-	char *n = (char *)global.params.objfiles->data[0];
-	char *ex;
-
-	n = FileName::name(n);
-	FileName *fn = FileName::forceExt(n, "exe");
-	global.params.exefile = fn->toChars();
-    }
-
-    // Make sure path to exe file exists
-    {	char *p = FileName::path(global.params.exefile);
-	FileName::ensurePathExists(p);
-	mem.free(p);
-    }
-
-    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('+');
-	cmdbuf.writestring((char *) global.params.libfiles->data[i]);
-    }
-
-    if (global.params.deffile)
-    {
-	cmdbuf.writeByte(',');
-	cmdbuf.writestring(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:");
-	cmdbuf.writestring(global.params.resfile);
-    }
-
-#if 0
-    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");
-#endif
-
-    cmdbuf.writestring("/noi");
-    for (i = 0; i < global.params.linkswitches->dim; i++)
-    {
-	cmdbuf.writestring((char *) global.params.linkswitches->data[i]);
-    }
-    cmdbuf.writeByte(';');
-
-    p = cmdbuf.toChars();
-
-    FileName *lnkfilename = NULL;
-    size_t plen = strlen(p);
-    if (plen > 7000)
-    {
-	lnkfilename = FileName::forceExt(global.params.exefile, "lnk");
-	File flnk(lnkfilename);
-	flnk.setbuffer(p, plen);
-	flnk.ref = 1;
-	if (flnk.write())
-	    error("error writing file %s", lnkfilename);
-	if (lnkfilename->len() < plen)
-	    sprintf(p, "@%s", lnkfilename->toChars());
-    }
-
-    char *linkcmd = getenv("LINKCMD");
-    if (!linkcmd)
-	linkcmd = "link";
-    status = executecmd(linkcmd, p, 1);
-    if (lnkfilename)
-    {
-	remove(lnkfilename->toChars());
-	delete lnkfilename;
-    }
-    return status;
-#elif linux
-    pid_t childpid;
-    int i;
-    int status;
-
-    // Build argv[]
-    Array argv;
-
-    //char *cc = getenv("CC");
-    //if (!cc)
-	//cc = "gcc";
-    char *cc = "llvm-ld";
-    argv.push((void *)cc);
-
-    // None of that a.out stuff. Use explicit exe file name, or
-    // generate one from name of first source file.
-    OutBuffer* exestr = new OutBuffer;
-    if (global.params.exefile)
-    {
-        exestr->printf("-o=%s", global.params.exefile);
-        argv.push(exestr->toChars());
-    }
-    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
-    exestr->printf("-o=%s", ex);
-    ex = exestr->toChars();
-	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);
-    }
-
-    argv.insert(argv.dim, global.params.libfiles);
-
-    if (!global.params.symdebug)
-        argv.push((void *)"-strip-debug");
-
-    //argv.push((void *)"-m32");
-
-
-    if (!global.params.optimize)
-    argv.push((void *)"-disable-opt");
-    else {
-        const char* s = 0;
-        switch(global.params.optimizeLevel) {
-        case 0:
-        s = "-O0"; break;
-        case 1:
-        s = "-O1"; break;
-        case 2:
-        s = "-O2"; break;
-        case 3:
-        s = "-O3"; break;
-        case 4:
-        s = "-O4"; break;
-        case 5:
-        s = "-O5"; break;
-        default:
-        assert(0);
-        }
-        argv.push((void*)s);
-    }
-
-    if (!(global.params.useInline || global.params.llvmInline)) {
-        argv.push((void *)"-disable-inlining");
-    }
-
-#if 0
-    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");
-    }
-#endif
-
-    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);
-    }
-
-    argv.push((void*)"-native");
-    
-    /* Standard libraries must go after user specified libraries
-     * passed with -l.
-     */
-
-    argv.push((void*)"-ltango-base-c-llvmdc");
-    argv.push((void*)"-lpthread");
-    argv.push((void*)"-ldl");
-    argv.push((void*)"-lm");
-
-    argv.append(global.params.objfiles);
-
-    std::string runtime_path(global.params.runtimePath);
-    if (*runtime_path.rbegin() != '/')
-        runtime_path.append("/");
-    runtime_path.append("libtango-base-llvmdc.a");
-    argv.push((void*)runtime_path.c_str());
-
-    if (!global.params.quiet)
-    {
-	// 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
-    printf ("Linking is not yet supported for this version of LLVMDMD.\n");
-    return -1;
-#endif
-}
-
-/**********************************
- * Delete generated EXE file.
- */
-
-void deleteExeFile()
-{
-    if (global.params.exefile)
-    {
-	//printf("deleteExeFile() %s\n", global.params.exefile);
-	remove(global.params.exefile);
-    }
-}
-
-/******************************
- * 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
- */
-
-#if _WIN32
-int executecmd(char *cmd, char *args, int useenv)
-{
-    int status;
-    char *buff;
-    size_t len;
-
-    if (!global.params.quiet || global.params.verbose)
-    {
-	printf("%s %s\n", cmd, args);
-	fflush(stdout);
-    }
-
-    if ((len = strlen(args)) > 255)
-    {   char *q;
-	static char envname[] = "@_CMDLINE";
-
-	envname[0] = '@';
-	switch (useenv)
-	{   case 0:	goto L1;
-	    case 2: envname[0] = '%';	break;
-	}
-	q = (char *) alloca(sizeof(envname) + len + 1);
-	sprintf(q,"%s=%s", envname + 1, args);
-	status = putenv(q);
-	if (status == 0)
-	    args = envname;
-	else
-	{
-	L1:
-	    error("command line length of %d is too long",len);
-	}
-    }
-
-    status = executearg0(cmd,args);
-#if _WIN32
-    if (status == -1)
-	status = spawnlp(0,cmd,cmd,args,NULL);
-#endif
-//    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;
-}
-#endif
-
-/**************************************
- * 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
- */
-
-#if _WIN32
-int executearg0(char *cmd, char *args)
-{
-    char *file;
-    char *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);
-#if _WIN32
-    return spawnl(0,file,file,args,NULL);
-#elif linux
-    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
-    assert(0);
-#endif
-}
-#endif
-
-/***************************************
- * Run the compiled program.
- * Return exit status.
- */
-
-int runProgram()
-{
-    //printf("runProgram()\n");
-    if (global.params.verbose)
-    {
-	printf("%s", global.params.exefile);
-	for (size_t i = 0; i < global.params.runargs_length; i++)
-	    printf(" %s", (char *)global.params.runargs[i]);
-	printf("\n");
-    }
-
-    // Build argv[]
-    Array argv;
-
-    argv.push((void *)global.params.exefile);
-    for (size_t i = 0; i < global.params.runargs_length; i++)
-    {	char *a = global.params.runargs[i];
-
-#if _WIN32
-	// BUG: what about " appearing in the string?
-	if (strchr(a, ' '))
-	{   char *b = (char *)mem.malloc(3 + strlen(a));
-	    sprintf(b, "\"%s\"", a);
-	    a = b;
-	}
-#endif
-	argv.push((void *)a);
-    }
-    argv.push(NULL);
-
-#if _WIN32
-    char *ex = FileName::name(global.params.exefile);
-    if (ex == global.params.exefile)
-	ex = FileName::combine(".", ex);
-    else
-	ex = global.params.exefile;
-    return spawnv(0,ex,(char **)argv.data);
-#elif linux
-    pid_t childpid;
-    int status;
-
-    childpid = fork();
-    if (childpid == 0)
-    {
-	char *fn = (char *)argv.data[0];
-	if (!FileName::absolute(fn))
-	{   // Make it "./fn"
-	    fn = FileName::combine(".", fn);
-	}
-	execv(fn, (char **)argv.data);
-	perror(fn);		// failed to execute
-	return -1;
-    }
-
-    waitpid(childpid, &status, 0);
-
-    status = WEXITSTATUS(status);
-    return status;
-#else
-    assert(0);
-#endif
-}
+
+
+// Copyright (c) 1999-2007 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	<ctype.h>
+#include	<assert.h>
+#include	<stdarg.h>
+#include	<string.h>
+#include	<stdlib.h>
+
+#if _WIN32
+#include	<process.h>
+#endif
+
+#if linux
+#include	<sys/types.h>
+#include	<sys/wait.h>
+#include	<unistd.h>
+#endif
+
+#include	"root.h"
+
+#include	"mars.h"
+
+#include	"mem.h"
+
+int executecmd(char *cmd, char *args, int useenv);
+int executearg0(char *cmd, char *args);
+
+/*****************************
+ * Run the linker.  Return status of execution.
+ */
+
+int runLINK()
+{
+#if _WIN32
+    assert(0 && "linking not done for win32");
+
+    char *p;
+    int i;
+    int status;
+    OutBuffer cmdbuf;
+
+    global.params.libfiles->push((void *) "user32");
+    global.params.libfiles->push((void *) "kernel32");
+
+    for (i = 0; i < global.params.objfiles->dim; i++)
+    {
+	if (i)
+	    cmdbuf.writeByte('+');
+	p = (char *)global.params.objfiles->data[i];
+	char *ext = FileName::ext(p);
+	if (ext)
+	    cmdbuf.write(p, ext - p - 1);
+	else
+	    cmdbuf.writestring(p);
+    }
+    cmdbuf.writeByte(',');
+    if (global.params.exefile)
+	cmdbuf.writestring(global.params.exefile);
+    else
+    {	// Generate exe file name from first obj name
+	char *n = (char *)global.params.objfiles->data[0];
+	char *ex;
+
+	n = FileName::name(n);
+	FileName *fn = FileName::forceExt(n, "exe");
+	global.params.exefile = fn->toChars();
+    }
+
+    // Make sure path to exe file exists
+    {	char *p = FileName::path(global.params.exefile);
+	FileName::ensurePathExists(p);
+	mem.free(p);
+    }
+
+    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('+');
+	cmdbuf.writestring((char *) global.params.libfiles->data[i]);
+    }
+
+    if (global.params.deffile)
+    {
+	cmdbuf.writeByte(',');
+	cmdbuf.writestring(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:");
+	cmdbuf.writestring(global.params.resfile);
+    }
+
+#if 0
+    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");
+#endif
+
+    cmdbuf.writestring("/noi");
+    for (i = 0; i < global.params.linkswitches->dim; i++)
+    {
+	cmdbuf.writestring((char *) global.params.linkswitches->data[i]);
+    }
+    cmdbuf.writeByte(';');
+
+    p = cmdbuf.toChars();
+
+    FileName *lnkfilename = NULL;
+    size_t plen = strlen(p);
+    if (plen > 7000)
+    {
+	lnkfilename = FileName::forceExt(global.params.exefile, "lnk");
+	File flnk(lnkfilename);
+	flnk.setbuffer(p, plen);
+	flnk.ref = 1;
+	if (flnk.write())
+	    error("error writing file %s", lnkfilename);
+	if (lnkfilename->len() < plen)
+	    sprintf(p, "@%s", lnkfilename->toChars());
+    }
+
+    char *linkcmd = getenv("LINKCMD");
+    if (!linkcmd)
+	linkcmd = "link";
+    status = executecmd(linkcmd, p, 1);
+    if (lnkfilename)
+    {
+	remove(lnkfilename->toChars());
+	delete lnkfilename;
+    }
+    return status;
+#elif linux
+    pid_t childpid;
+    int i;
+    int status;
+
+    // Build argv[]
+    Array argv;
+
+    //char *cc = getenv("CC");
+    //if (!cc)
+	//cc = "gcc";
+    char *cc = "llvm-ld";
+    argv.push((void *)cc);
+
+    // None of that a.out stuff. Use explicit exe file name, or
+    // generate one from name of first source file.
+    OutBuffer* exestr = new OutBuffer;
+    if (global.params.exefile)
+    {
+        exestr->printf("-o=%s", global.params.exefile);
+        argv.push(exestr->toChars());
+    }
+    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
+    exestr->printf("-o=%s", ex);
+    ex = exestr->toChars();
+	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);
+    }
+
+    argv.insert(argv.dim, global.params.libfiles);
+
+    if (!global.params.symdebug)
+        argv.push((void *)"-strip-debug");
+
+    //argv.push((void *)"-m32");
+
+
+    if (!global.params.optimize)
+    argv.push((void *)"-disable-opt");
+    else {
+        const char* s = 0;
+        switch(global.params.optimizeLevel) {
+        case 0:
+        s = "-O0"; break;
+        case 1:
+        s = "-O1"; break;
+        case 2:
+        s = "-O2"; break;
+        case 3:
+        s = "-O3"; break;
+        case 4:
+        s = "-O4"; break;
+        case 5:
+        s = "-O5"; break;
+        default:
+        assert(0);
+        }
+        argv.push((void*)s);
+    }
+
+    if (!(global.params.useInline || global.params.llvmInline)) {
+        argv.push((void *)"-disable-inlining");
+    }
+
+#if 0
+    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");
+    }
+#endif
+
+    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);
+    }
+
+    argv.push((void*)"-native");
+    
+    /* Standard libraries must go after user specified libraries
+     * passed with -l.
+     */
+
+    argv.push((void*)"-ltango-base-c-llvmdc");
+    argv.push((void*)"-lpthread");
+    argv.push((void*)"-ldl");
+    argv.push((void*)"-lm");
+
+    argv.append(global.params.objfiles);
+
+    std::string runtime_path(global.params.runtimePath);
+    if (*runtime_path.rbegin() != '/')
+        runtime_path.append("/");
+    runtime_path.append("libtango-base-llvmdc.a");
+    argv.push((void*)runtime_path.c_str());
+
+    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
+    printf ("Linking is not yet supported for this version of LLVMDMD.\n");
+    return -1;
+#endif
+}
+
+/**********************************
+ * Delete generated EXE file.
+ */
+
+void deleteExeFile()
+{
+    if (global.params.exefile)
+    {
+	//printf("deleteExeFile() %s\n", global.params.exefile);
+	remove(global.params.exefile);
+    }
+}
+
+/******************************
+ * 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
+ */
+
+#if _WIN32
+int executecmd(char *cmd, char *args, int useenv)
+{
+    int status;
+    char *buff;
+    size_t len;
+
+    if (!global.params.quiet || global.params.verbose)
+    {
+	printf("%s %s\n", cmd, args);
+	fflush(stdout);
+    }
+
+    if ((len = strlen(args)) > 255)
+    {   char *q;
+	static char envname[] = "@_CMDLINE";
+
+	envname[0] = '@';
+	switch (useenv)
+	{   case 0:	goto L1;
+	    case 2: envname[0] = '%';	break;
+	}
+	q = (char *) alloca(sizeof(envname) + len + 1);
+	sprintf(q,"%s=%s", envname + 1, args);
+	status = putenv(q);
+	if (status == 0)
+	    args = envname;
+	else
+	{
+	L1:
+	    error("command line length of %d is too long",len);
+	}
+    }
+
+    status = executearg0(cmd,args);
+#if _WIN32
+    if (status == -1)
+	status = spawnlp(0,cmd,cmd,args,NULL);
+#endif
+//    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;
+}
+#endif
+
+/**************************************
+ * 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
+ */
+
+#if _WIN32
+int executearg0(char *cmd, char *args)
+{
+    char *file;
+    char *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);
+#if _WIN32
+    return spawnl(0,file,file,args,NULL);
+#elif linux
+    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
+    assert(0);
+#endif
+}
+#endif
+
+/***************************************
+ * Run the compiled program.
+ * Return exit status.
+ */
+
+int runProgram()
+{
+    //printf("runProgram()\n");
+    if (global.params.verbose)
+    {
+	printf("%s", global.params.exefile);
+	for (size_t i = 0; i < global.params.runargs_length; i++)
+	    printf(" %s", (char *)global.params.runargs[i]);
+	printf("\n");
+    }
+
+    // Build argv[]
+    Array argv;
+
+    argv.push((void *)global.params.exefile);
+    for (size_t i = 0; i < global.params.runargs_length; i++)
+    {	char *a = global.params.runargs[i];
+
+#if _WIN32
+	// BUG: what about " appearing in the string?
+	if (strchr(a, ' '))
+	{   char *b = (char *)mem.malloc(3 + strlen(a));
+	    sprintf(b, "\"%s\"", a);
+	    a = b;
+	}
+#endif
+	argv.push((void *)a);
+    }
+    argv.push(NULL);
+
+#if _WIN32
+    char *ex = FileName::name(global.params.exefile);
+    if (ex == global.params.exefile)
+	ex = FileName::combine(".", ex);
+    else
+	ex = global.params.exefile;
+    return spawnv(0,ex,(char **)argv.data);
+#elif linux
+    pid_t childpid;
+    int status;
+
+    childpid = fork();
+    if (childpid == 0)
+    {
+	char *fn = (char *)argv.data[0];
+	if (!FileName::absolute(fn))
+	{   // Make it "./fn"
+	    fn = FileName::combine(".", fn);
+	}
+	execv(fn, (char **)argv.data);
+	perror(fn);		// failed to execute
+	return -1;
+    }
+
+    waitpid(childpid, &status, 0);
+
+    status = WEXITSTATUS(status);
+    return status;
+#else
+    assert(0);
+#endif
+}