view dmd/link.c @ 270:d9d5d59873d8 trunk

[svn r291] Fixed a bunch of the old Phobos tests to work with Tango. Branch statements now emit a new block after it. Fixed the _adSort runtime function had a bad signature. Added a missing dot prefix on compiler generated string tables for string switch. Fixed, PTRSIZE seems like it was wrong on 64bit, now it definitely gets set properly.
author lindquist
date Mon, 16 Jun 2008 16:01:19 +0200
parents b77664331d06
children 21f85bac0b1a
line wrap: on
line source



// 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"

#include "gen/logger.h"

int executecmd(char *cmd, char *args, int useenv);
int executearg0(char *cmd, char *args);

/*****************************
 * Run the linker.  Return status of execution.
 */

int runLINK()
{
    Logger::println("*** Linking executable ***");

#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 LLVMDC.\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
}