/** * Compiler implementation of the * $(LINK2 http://www.dlang.org, D programming language). * * Copyright: Copyright (c) 1999-2016 by Digital Mars, All Rights Reserved * Authors: $(LINK2 http://www.digitalmars.com, Walter Bright) * License: $(LINK2 http://www.boost.org/LICENSE_1_0.txt, Boost License 1.0) * Source: $(DMDSRC _link.d) */ module ddmd.link; import core.stdc.ctype; import core.stdc.stdio; import core.stdc.string; import core.sys.posix.stdio; import core.sys.posix.stdlib; import core.sys.posix.unistd; import core.sys.windows.windows; import ddmd.errors; import ddmd.globals; import ddmd.root.file; import ddmd.root.filename; import ddmd.root.outbuffer; import ddmd.root.rmem; import ddmd.utils; version (Posix) extern (C) int pipe(int*); version (Windows) extern (C) int putenv(const char*); version (Windows) extern (C) int spawnlp(int, const char*, const char*, const char*, const char*); version (Windows) extern (C) int spawnl(int, const char*, const char*, const char*, const char*); version (Windows) extern (C) int spawnv(int, const char*, const char**); version (CRuntime_Microsoft) { // until the new windows bindings are available when building dmd. static if(!is(STARTUPINFOA)) { alias STARTUPINFOA = STARTUPINFO; // dwCreationFlags for CreateProcess() and CreateProcessAsUser() enum : DWORD { DEBUG_PROCESS = 0x00000001, DEBUG_ONLY_THIS_PROCESS = 0x00000002, CREATE_SUSPENDED = 0x00000004, DETACHED_PROCESS = 0x00000008, CREATE_NEW_CONSOLE = 0x00000010, NORMAL_PRIORITY_CLASS = 0x00000020, IDLE_PRIORITY_CLASS = 0x00000040, HIGH_PRIORITY_CLASS = 0x00000080, REALTIME_PRIORITY_CLASS = 0x00000100, CREATE_NEW_PROCESS_GROUP = 0x00000200, CREATE_UNICODE_ENVIRONMENT = 0x00000400, CREATE_SEPARATE_WOW_VDM = 0x00000800, CREATE_SHARED_WOW_VDM = 0x00001000, CREATE_FORCEDOS = 0x00002000, BELOW_NORMAL_PRIORITY_CLASS = 0x00004000, ABOVE_NORMAL_PRIORITY_CLASS = 0x00008000, CREATE_BREAKAWAY_FROM_JOB = 0x01000000, CREATE_WITH_USERPROFILE = 0x02000000, CREATE_DEFAULT_ERROR_MODE = 0x04000000, CREATE_NO_WINDOW = 0x08000000, PROFILE_USER = 0x10000000, PROFILE_KERNEL = 0x20000000, PROFILE_SERVER = 0x40000000 } } } /**************************************** * Write filename to cmdbuf, quoting if necessary. */ private void writeFilename(OutBuffer* buf, const(char)* filename, size_t len) { /* 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.write(filename, len); buf.writeByte('"'); return; } /* No quoting necessary */ buf.write(filename, len); } private void writeFilename(OutBuffer* buf, const(char)* filename) { writeFilename(buf, filename, strlen(filename)); } version (Posix) { /***************************** * As it forwards the linker error message to stderr, checks for the presence * of an error indicating lack of a main function (NME_ERR_MSG). * * Returns: * 1 if there is a no main error * -1 if there is an IO error * 0 otherwise */ private int findNoMainError(int fd) { version (OSX) { static __gshared const(char)* nmeErrorMessage = "\"__Dmain\", referenced from:"; } else { static __gshared const(char)* nmeErrorMessage = "undefined reference to `_Dmain'"; } FILE* stream = fdopen(fd, "r"); if (stream is null) return -1; const(size_t) len = 64 * 1024 - 1; char[len + 1] buffer; // + '\0' size_t beg = 0, end = len; bool nmeFound = false; for (;;) { // read linker output const(size_t) n = fread(&buffer[beg], 1, len - beg, stream); if (beg + n < len && ferror(stream)) return -1; buffer[(end = beg + n)] = '\0'; // search error message, stop at last complete line const(char)* lastSep = strrchr(buffer.ptr, '\n'); if (lastSep) buffer[(end = lastSep - &buffer[0])] = '\0'; if (strstr(&buffer[0], nmeErrorMessage)) nmeFound = true; if (lastSep) buffer[end++] = '\n'; if (fwrite(&buffer[0], 1, end, stderr) < end) return -1; if (beg + n < len && feof(stream)) break; // copy over truncated last line memcpy(&buffer[0], &buffer[end], (beg = len - end)); } return nmeFound ? 1 : 0; } } /***************************** * Run the linker. Return status of execution. */ public int runLINK() { version (Windows) { if (global.params.mscoff) { OutBuffer cmdbuf; cmdbuf.writestring("/NOLOGO "); for (size_t i = 0; i < global.params.objfiles.dim; i++) { if (i) cmdbuf.writeByte(' '); const(char)* p = (*global.params.objfiles)[i]; const(char)* basename = FileName.removeExt(FileName.name(p)); const(char)* ext = FileName.ext(p); if (ext && !strchr(basename, '.')) { // Write name sans extension (but not if a double extension) writeFilename(&cmdbuf, p, ext - p - 1); } else writeFilename(&cmdbuf, p); FileName.free(basename); } if (global.params.resfile) { cmdbuf.writeByte(' '); writeFilename(&cmdbuf, global.params.resfile); } cmdbuf.writeByte(' '); if (global.params.exefile) { cmdbuf.writestring("/OUT:"); 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. */ const(char)* n = (*global.params.objfiles)[0]; n = FileName.name(n); global.params.exefile = cast(char*)FileName.forceExt(n, "exe"); } // Make sure path to exe file exists ensurePathToNameExists(Loc(), global.params.exefile); cmdbuf.writeByte(' '); if (global.params.mapfile) { cmdbuf.writestring("/MAP:"); writeFilename(&cmdbuf, global.params.mapfile); } else if (global.params.map) { const(char)* fn = FileName.forceExt(global.params.exefile, "map"); const(char)* path = FileName.path(global.params.exefile); const(char)* p; if (path[0] == '\0') p = FileName.combine(global.params.objdir, fn); else p = fn; cmdbuf.writestring("/MAP:"); writeFilename(&cmdbuf, p); } for (size_t i = 0; i < global.params.libfiles.dim; i++) { cmdbuf.writeByte(' '); cmdbuf.writestring("/DEFAULTLIB:"); writeFilename(&cmdbuf, (*global.params.libfiles)[i]); } if (global.params.deffile) { cmdbuf.writeByte(' '); cmdbuf.writestring("/DEF:"); writeFilename(&cmdbuf, global.params.deffile); } if (global.params.symdebug) { cmdbuf.writeByte(' '); cmdbuf.writestring("/DEBUG"); // in release mode we need to reactivate /OPT:REF after /DEBUG if (global.params.release) cmdbuf.writestring(" /OPT:REF"); } if (global.params.dll) { cmdbuf.writeByte(' '); cmdbuf.writestring("/DLL"); } for (size_t i = 0; i < global.params.linkswitches.dim; i++) { cmdbuf.writeByte(' '); cmdbuf.writestring((*global.params.linkswitches)[i]); } /* Append the path to the VC lib files, and then the SDK lib files */ const(char)* vcinstalldir = getenv("VCINSTALLDIR"); if (vcinstalldir) { cmdbuf.writestring(" /LIBPATH:\""); cmdbuf.writestring(vcinstalldir); if (global.params.is64bit) cmdbuf.writestring("\\lib\\amd64\""); else cmdbuf.writestring("\\lib\""); } const(char)* windowssdkdir = getenv("WindowsSdkDir"); if (windowssdkdir) { cmdbuf.writestring(" /LIBPATH:\""); cmdbuf.writestring(windowssdkdir); if (global.params.is64bit) cmdbuf.writestring("\\lib\\x64\""); else cmdbuf.writestring("\\lib\""); } cmdbuf.writeByte(' '); const(char)* lflags; if (detectVS14(cmdbuf.peekString())) { lflags = getenv("LFLAGS_VS14"); if (!lflags) lflags = "legacy_stdio_definitions.lib"; // environment variables UniversalCRTSdkDir and UCRTVersion set // when running vcvarsall.bat x64 if (const(char)* UniversalCRTSdkDir = getenv("UniversalCRTSdkDir")) if (const(char)* UCRTVersion = getenv("UCRTVersion")) { cmdbuf.writestring(" /LIBPATH:\""); cmdbuf.writestring(UniversalCRTSdkDir); cmdbuf.writestring("\\lib\\"); cmdbuf.writestring(UCRTVersion); if (global.params.is64bit) cmdbuf.writestring("\\ucrt\\x64\""); else cmdbuf.writestring("\\ucrt\\x86\""); } } else { lflags = getenv("LFLAGS_VS12"); } if (lflags) { cmdbuf.writeByte(' '); cmdbuf.writestring(lflags); } char* p = cmdbuf.peekString(); const(char)* lnkfilename = null; size_t plen = strlen(p); if (plen > 7000) { lnkfilename = FileName.forceExt(global.params.exefile, "lnk"); auto flnk = File(lnkfilename); flnk.setbuffer(p, plen); flnk._ref = 1; if (flnk.write()) error(Loc(), "error writing file %s", lnkfilename); if (strlen(lnkfilename) < plen) sprintf(p, "@%s", lnkfilename); } const(char)* linkcmd = getenv(global.params.is64bit ? "LINKCMD64" : "LINKCMD"); if (!linkcmd) linkcmd = getenv("LINKCMD"); // backward compatible if (!linkcmd) { if (vcinstalldir) { OutBuffer linkcmdbuf; linkcmdbuf.writestring(vcinstalldir); if (global.params.is64bit) linkcmdbuf.writestring("\\bin\\amd64\\link"); else linkcmdbuf.writestring("\\bin\\link"); linkcmd = linkcmdbuf.extractString(); } else linkcmd = "optlink"; } int status = executecmd(linkcmd, p); if (lnkfilename) { remove(lnkfilename); FileName.free(lnkfilename); } return status; } else { OutBuffer cmdbuf; global.params.libfiles.push("user32"); global.params.libfiles.push("kernel32"); for (size_t i = 0; i < global.params.objfiles.dim; i++) { if (i) cmdbuf.writeByte('+'); const(char)* p = (*global.params.objfiles)[i]; const(char)* basename = FileName.removeExt(FileName.name(p)); const(char)* ext = FileName.ext(p); if (ext && !strchr(basename, '.')) { // Write name sans extension (but not if a double extension) writeFilename(&cmdbuf, p, ext - p - 1); } else writeFilename(&cmdbuf, p); FileName.free(basename); } 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. */ const(char)* n = (*global.params.objfiles)[0]; n = FileName.name(n); global.params.exefile = cast(char*)FileName.forceExt(n, "exe"); } // Make sure path to exe file exists ensurePathToNameExists(Loc(), global.params.exefile); cmdbuf.writeByte(','); if (global.params.mapfile) writeFilename(&cmdbuf, global.params.mapfile); else if (global.params.map) { const(char)* fn = FileName.forceExt(global.params.exefile, "map"); const(char)* path = FileName.path(global.params.exefile); const(char)* p; if (path[0] == '\0') p = FileName.combine(global.params.objdir, fn); else p = fn; writeFilename(&cmdbuf, p); } else cmdbuf.writestring("nul"); cmdbuf.writeByte(','); for (size_t i = 0; i < global.params.libfiles.dim; i++) { if (i) cmdbuf.writeByte('+'); writeFilename(&cmdbuf, (*global.params.libfiles)[i]); } if (global.params.deffile) { cmdbuf.writeByte(','); writeFilename(&cmdbuf, global.params.deffile); } /* Eliminate unnecessary trailing commas */ while (1) { size_t i = cmdbuf.offset; if (!i || cmdbuf.data[i - 1] != ',') break; cmdbuf.offset--; } if (global.params.resfile) { cmdbuf.writestring("/RC:"); writeFilename(&cmdbuf, global.params.resfile); } if (global.params.map || global.params.mapfile) cmdbuf.writestring("/m"); version (none) { 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 (size_t i = 0; i < global.params.linkswitches.dim; i++) { cmdbuf.writestring((*global.params.linkswitches)[i]); } cmdbuf.writeByte(';'); char* p = cmdbuf.peekString(); const(char)* lnkfilename = null; size_t plen = strlen(p); if (plen > 7000) { lnkfilename = FileName.forceExt(global.params.exefile, "lnk"); auto flnk = File(lnkfilename); flnk.setbuffer(p, plen); flnk._ref = 1; if (flnk.write()) error(Loc(), "error writing file %s", lnkfilename); if (strlen(lnkfilename) < plen) sprintf(p, "@%s", lnkfilename); } const(char)* linkcmd = getenv("LINKCMD"); if (!linkcmd) linkcmd = "link"; int status = executecmd(linkcmd, p); if (lnkfilename) { remove(lnkfilename); FileName.free(lnkfilename); } return status; } } else version (Posix) { pid_t childpid; int status; // Build argv[] Strings argv; const(char)* cc = getenv("CC"); if (!cc) cc = "cc"; argv.push(cc); argv.insert(1, global.params.objfiles); version (OSX) { // If we are on Mac OS X and linking a dynamic library, // add the "-dynamiclib" flag if (global.params.dll) argv.push("-dynamiclib"); } else version (Posix) { if (global.params.dll) argv.push("-shared"); } // None of that a.out stuff. Use explicit exe file name, or // generate one from name of first source file. argv.push("-o"); if (global.params.exefile) { argv.push(global.params.exefile); } else if (global.params.run) { version (all) { char[L_tmpnam + 14 + 1] name; strcpy(name.ptr, P_tmpdir); strcat(name.ptr, "/dmd_runXXXXXX"); int fd = mkstemp(name.ptr); if (fd == -1) { error(Loc(), "error creating temporary file"); return 1; } else close(fd); global.params.exefile = mem.xstrdup(name.ptr); argv.push(global.params.exefile); } else { /* The use of tmpnam raises the issue of "is this a security hole"? * The hole is that after tmpnam and before the file is opened, * the attacker modifies the file system to get control of the * file with that name. I do not know if this is an issue in * this context. * We cannot just replace it with mkstemp, because this name is * passed to the linker that actually opens the file and writes to it. */ char[L_tmpnam + 1] s; char* n = tmpnam(s.ptr); global.params.exefile = mem.xstrdup(n); argv.push(global.params.exefile); } } else { // Generate exe file name from first obj name const(char)* n = (*global.params.objfiles)[0]; char* ex; n = FileName.name(n); const(char)* e = FileName.ext(n); if (e) { e--; // back up over '.' ex = cast(char*)mem.xmalloc(e - n + 1); memcpy(ex, n, e - n); ex[e - n] = 0; // If generating dll then force dll extension if (global.params.dll) ex = cast(char*)FileName.forceExt(ex, global.dll_ext); } else ex = cast(char*)"a.out"; // no extension, so give up argv.push(ex); global.params.exefile = ex; } // Make sure path to exe file exists ensurePathToNameExists(Loc(), global.params.exefile); if (global.params.symdebug) argv.push("-g"); if (global.params.is64bit) argv.push("-m64"); else argv.push("-m32"); version (OSX) { /* Without this switch, ld generates messages of the form: * ld: warning: could not create compact unwind for __Dmain: offset of saved registers too far to encode * meaning they are further than 255 bytes from the frame register. * ld reverts to the old method instead. * See: https://ghc.haskell.org/trac/ghc/ticket/5019 * which gives this tidbit: * "When a C++ (or x86_64 Objective-C) exception is thrown, the runtime must unwind the * stack looking for some function to catch the exception. Traditionally, the unwind * information is stored in the __TEXT/__eh_frame section of each executable as Dwarf * CFI (call frame information). Beginning in Mac OS X 10.6, the unwind information is * also encoded in the __TEXT/__unwind_info section using a two-level lookup table of * compact unwind encodings. * The unwinddump tool displays the content of the __TEXT/__unwind_info section." * * A better fix would be to save the registers next to the frame pointer. */ argv.push("-Xlinker"); argv.push("-no_compact_unwind"); } if (global.params.map || global.params.mapfile) { argv.push("-Xlinker"); version (OSX) { argv.push("-map"); } else { argv.push("-Map"); } if (!global.params.mapfile) { const(char)* fn = FileName.forceExt(global.params.exefile, "map"); const(char)* path = FileName.path(global.params.exefile); const(char)* p; if (path[0] == '\0') p = FileName.combine(global.params.objdir, fn); else p = fn; global.params.mapfile = cast(char*)p; } argv.push("-Xlinker"); argv.push(global.params.mapfile); } 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 * because EH sections are "unreferenced" and elided */ argv.push("-Xlinker"); argv.push("--gc-sections"); } for (size_t i = 0; i < global.params.linkswitches.dim; i++) { const(char)* p = (*global.params.linkswitches)[i]; if (!p || !p[0] || !(p[0] == '-' && (p[1] == 'l' || p[1] == 'L'))) { // Don't need -Xlinker if switch starts with -l or -L. // Eliding -Xlinker is significant for -L since it allows our paths // to take precedence over gcc defaults. argv.push("-Xlinker"); } argv.push(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 (size_t i = 0; i < global.params.libfiles.dim; i++) { const(char)* p = (*global.params.libfiles)[i]; size_t plen = strlen(p); if (plen > 2 && p[plen - 2] == '.' && p[plen - 1] == 'a') argv.push(p); else { char* s = cast(char*)mem.xmalloc(plen + 3); s[0] = '-'; s[1] = 'l'; memcpy(s + 2, p, plen + 1); argv.push(s); } } for (size_t i = 0; i < global.params.dllfiles.dim; i++) { const(char)* p = (*global.params.dllfiles)[i]; argv.push(p); } /* Standard libraries must go after user specified libraries * passed with -l. */ const(char)* libname = global.params.symdebug ? global.params.debuglibname : global.params.defaultlibname; size_t slen = strlen(libname); if (slen) { char* buf = cast(char*)malloc(3 + slen + 1); strcpy(buf, "-l"); if (slen > 3 + 2 && memcmp(libname, "lib".ptr, 3) == 0) { if (memcmp(libname + slen - 2, ".a".ptr, 2) == 0) { argv.push("-Xlinker"); argv.push("-Bstatic"); strncat(buf, libname + 3, slen - 3 - 2); argv.push(buf); argv.push("-Xlinker"); argv.push("-Bdynamic"); } else if (memcmp(libname + slen - 3, ".so".ptr, 3) == 0) { strncat(buf, libname + 3, slen - 3 - 3); argv.push(buf); } else { strcat(buf, libname); argv.push(buf); } } else { strcat(buf, libname); argv.push(buf); } } //argv.push("-ldruntime"); argv.push("-lpthread"); argv.push("-lm"); version (linux) { // Changes in ld for Ubuntu 11.10 require this to appear after phobos2 argv.push("-lrt"); // Link against libdl for phobos usage of dlopen argv.push("-ldl"); } if (global.params.verbose) { // Print it for (size_t i = 0; i < argv.dim; i++) fprintf(global.stdmsg, "%s ", argv[i]); fprintf(global.stdmsg, "\n"); } argv.push(null); // set up pipes int[2] fds; if (pipe(fds.ptr) == -1) { perror("unable to create pipe to linker"); return -1; } childpid = fork(); if (childpid == 0) { // pipe linker stderr to fds[0] dup2(fds[1], STDERR_FILENO); close(fds[0]); execvp(argv[0], cast(char**)argv.tdata()); perror(argv[0]); // failed to execute return -1; } else if (childpid == -1) { perror("unable to fork"); return -1; } close(fds[1]); const(int) nme = findNoMainError(fds[0]); waitpid(childpid, &status, 0); if (WIFEXITED(status)) { status = WEXITSTATUS(status); if (status) { if (nme == -1) { perror("error with the linker pipe"); return -1; } else { error(Loc(), "linker exited with status %d", status); if (nme == 1) error(Loc(), "no main function specified"); } } } else if (WIFSIGNALED(status)) { error(Loc(), "linker killed by signal %d", WTERMSIG(status)); status = 1; } return status; } else { error(Loc(), "linking is not yet supported for this version of DMD."); return -1; } } /****************************** * Execute a rule. Return the status. * cmd program to run * args arguments to cmd, as a string */ version (Windows) { private int executecmd(const(char)* cmd, const(char)* args) { int status; size_t len; if (global.params.verbose) fprintf(global.stdmsg, "%s %s\n", cmd, args); if (!global.params.mscoff) { if ((len = strlen(args)) > 255) { char* q = cast(char*)alloca(8 + len + 1); sprintf(q, "_CMDLINE=%s", args); status = putenv(q); if (status == 0) { args = "@_CMDLINE"; } else { error(Loc(), "command line length of %d is too long", len); } } } // Normalize executable path separators, see Bugzilla 9330 cmd = toWinPath(cmd); version (CRuntime_Microsoft) { // Open scope so dmd doesn't complain about alloca + exception handling { // Use process spawning through the WinAPI to avoid issues with executearg0 and spawnlp OutBuffer cmdbuf; cmdbuf.writestring("\""); cmdbuf.writestring(cmd); cmdbuf.writestring("\" "); cmdbuf.writestring(args); STARTUPINFOA startInf; startInf.dwFlags = STARTF_USESTDHANDLES; startInf.hStdInput = GetStdHandle(STD_INPUT_HANDLE); startInf.hStdOutput = GetStdHandle(STD_OUTPUT_HANDLE); startInf.hStdError = GetStdHandle(STD_ERROR_HANDLE); PROCESS_INFORMATION procInf; BOOL b = CreateProcessA(null, cmdbuf.peekString(), null, null, 1, NORMAL_PRIORITY_CLASS, null, null, &startInf, &procInf); if (b) { WaitForSingleObject(procInf.hProcess, INFINITE); DWORD returnCode; GetExitCodeProcess(procInf.hProcess, &returnCode); status = returnCode; CloseHandle(procInf.hProcess); } else { status = -1; } } } else { status = executearg0(cmd, args); if (status == -1) { status = spawnlp(0, cmd, cmd, args, null); } } //if (global.params.verbose) // fprintf(global.stdmsg, "\n"); if (status) { if (status == -1) error(Loc(), "can't run '%s', check PATH", cmd); else error(Loc(), "linker exited with status %d", 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 (Windows) { private int executearg0(const(char)* cmd, const(char)* args) { const(char)* file; const(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); // spawnlp returns intptr_t in some systems, not int return spawnl(0, file, file, args, null); } } /*************************************** * Run the compiled program. * Return exit status. */ public int runProgram() { //printf("runProgram()\n"); if (global.params.verbose) { fprintf(global.stdmsg, "%s", global.params.exefile); for (size_t i = 0; i < global.params.runargs.dim; ++i) fprintf(global.stdmsg, " %s", global.params.runargs[i]); fprintf(global.stdmsg, "\n"); } // Build argv[] Strings argv; argv.push(global.params.exefile); for (size_t i = 0; i < global.params.runargs.dim; ++i) { const(char)* a = global.params.runargs[i]; version (Windows) { // BUG: what about " appearing in the string? if (strchr(a, ' ')) { char* b = cast(char*)mem.xmalloc(3 + strlen(a)); sprintf(b, "\"%s\"", a); a = b; } } argv.push(a); } argv.push(null); version (Windows) { const(char)* ex = FileName.name(global.params.exefile); if (ex == global.params.exefile) ex = FileName.combine(".", ex); else ex = global.params.exefile; // spawnlp returns intptr_t in some systems, not int return spawnv(0, ex, argv.tdata()); } else version (Posix) { pid_t childpid; int status; childpid = fork(); if (childpid == 0) { const(char)* fn = argv[0]; if (!FileName.absolute(fn)) { // Make it "./fn" fn = FileName.combine(".", fn); } execv(fn, cast(char**)argv.tdata()); perror(fn); // failed to execute return -1; } waitpid(childpid, &status, 0); if (WIFEXITED(status)) { status = WEXITSTATUS(status); //printf("--- errorlevel %d\n", status); } else if (WIFSIGNALED(status)) { error(Loc(), "program killed by signal %d", WTERMSIG(status)); status = 1; } return status; } else { assert(0); } } version (Windows) { /***************************** * Detect whether the link will grab libraries from VS 2015 or later */ private bool detectVS14(const(char)* cmdline) { auto libpaths = new Strings(); // grab library folders passed on the command line for (const(char)* p = cmdline; *p;) { while (isspace(*p)) p++; const(char)* arg = p; const(char)* end = arg; while (*end && !isspace(*end)) { end++; if (end[-1] == '"') { while (*end && *end != '"') { if (*end == '\\' && end[1]) end++; end++; } if (*end) end++; // skip closing quote } } p = end; // remove quotes if spanning complete argument if (end > arg + 1 && arg[0] == '"' && end[-1] == '"') { arg++; end--; } if (arg[0] == '-' || arg[0] == '/') { if (end - arg > 8 && memicmp(arg + 1, "LIBPATH:", 8) == 0) { arg += 9; char* q = cast(char*)memcpy((new char[](end - arg + 1)).ptr, arg, end - arg); q[end - arg] = 0; Strings* paths = FileName.splitPath(q); libpaths.append(paths); } } } // append library paths from environment if (const(char)* lib = getenv("LIB")) libpaths.append(FileName.splitPath(lib)); // if legacy_stdio_definitions.lib can be found in the same folder as // libcmt.lib, libcmt.lib is assumed to be from VS2015 or later const(char)* libcmt = FileName.searchPath(libpaths, "libcmt.lib", true); if (!libcmt) return false; const(char)* liblegacy = FileName.replaceName(libcmt, "legacy_stdio_definitions.lib"); return FileName.exists(liblegacy) == 1; } }