Mercurial > projects > ldc
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 +}