ldc/driver/linker.cpp
Martin cab165f3d8 Set up the MSVC environment in a more flexible way
For invoking MS tools such as link.exe and lib.exe, which require
compatible PATH, LIB, LIBPATH etc.

We previously wrapped the invokation of the tool with a batch file taking
care of setting up the environment and then calling the actual tool.

I changed this to first invoke another batch file setting up its
environment and then dumping all environment variables to a file. LDC then
parses this file and updates its own environment variables. Later spawned
child processes then inherit the updated environment, and the tools will
be located by LLVM in updated PATH.

This allows LDC to keep track of the used Visual C++ installation (and
could so set up additional runtime libs required since VS 2015, something
we would have needed some time back when we still supported VS 2013). It's
also required as a prerequisite for integrating LLD as in-process linker
on Windows.

I quickly looked into the option of setting up the environment manually,
but the MS batch files are full of quirks, mixtures of registry and
file-system lookups, checking for existence of misc. headers to filter out
bogus installations etc. With VS 2017, there's nothing in the registry at
all anymore (at least for the Build Tools); MS expose a COM interface for
querying the VS installer... But at least they still provide the batch
files.
2017-03-13 00:36:45 +01:00

957 lines
27 KiB
C++
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

//===-- linker.cpp --------------------------------------------------------===//
//
// LDC the LLVM D compiler
//
// This file is distributed under the BSD-style LDC license. See the LICENSE
// file for details.
//
//===----------------------------------------------------------------------===//
#include "driver/linker.h"
#include "mars.h"
#include "module.h"
#include "root.h"
#include "driver/archiver.h"
#include "driver/cl_options.h"
#include "driver/exe_path.h"
#include "driver/tool.h"
#include "gen/irstate.h"
#include "gen/llvm.h"
#include "gen/logger.h"
#include "gen/optimizer.h"
#include "gen/programs.h"
#include "llvm/ADT/Triple.h"
#include "llvm/IRReader/IRReader.h"
#include "llvm/Linker/Linker.h"
#include "llvm/ProfileData/InstrProf.h"
#include "llvm/Support/FileSystem.h"
#include "llvm/Support/Program.h"
#include "llvm/Support/Path.h"
#include "llvm/Support/SourceMgr.h"
#include "llvm/Target/TargetMachine.h"
#include "llvm/Target/TargetOptions.h"
#if _WIN32
#include "llvm/Support/SystemUtils.h"
#include "llvm/Support/ConvertUTF.h"
#include <Windows.h>
#endif
#include <algorithm>
//////////////////////////////////////////////////////////////////////////////
static llvm::cl::opt<bool> staticFlag(
"static",
llvm::cl::desc(
"Create a statically linked binary, including all system dependencies"),
llvm::cl::ZeroOrMore);
static llvm::cl::opt<std::string>
ltoLibrary("flto-binary",
llvm::cl::desc("Set the linker LTO plugin library file (e.g. "
"LLVMgold.so (Unixes) or libLTO.dylib (Darwin))"),
llvm::cl::value_desc("file"), llvm::cl::ZeroOrMore);
static llvm::cl::opt<std::string>
externalArchiver("archiver",
llvm::cl::desc("External static library archiver"),
llvm::cl::value_desc("file"), llvm::cl::ZeroOrMore);
//////////////////////////////////////////////////////////////////////////////
static void CreateDirectoryOnDisk(llvm::StringRef fileName) {
auto dir = llvm::sys::path::parent_path(fileName);
if (!dir.empty() && !llvm::sys::fs::exists(dir)) {
if (auto ec = llvm::sys::fs::create_directories(dir)) {
error(Loc(), "failed to create path to file: %s\n%s", dir.data(),
ec.message().c_str());
fatal();
}
}
}
//////////////////////////////////////////////////////////////////////////////
static std::string getOutputName(bool const sharedLib) {
const auto &triple = *global.params.targetTriple;
const char *extension = nullptr;
if (sharedLib) {
extension = global.dll_ext;
} else if (triple.isOSWindows()) {
extension = "exe";
}
if (global.params.exefile) {
// DMD adds the default extension if there is none
return opts::invokedByLDMD && extension
? FileName::defaultExt(global.params.exefile, extension)
: global.params.exefile;
}
// Infer output name from first object file.
std::string result = global.params.objfiles->dim
? FileName::removeExt((*global.params.objfiles)[0])
: "a.out";
if (sharedLib && !triple.isWindowsMSVCEnvironment())
result = "lib" + result;
if (global.params.run) {
// If `-run` is passed, the executable is temporary and is removed
// after execution. Make sure the name does not collide with other files
// from other processes by creating a unique filename.
llvm::SmallString<128> tempFilename;
auto EC = llvm::sys::fs::createTemporaryFile(FileName::name(result.c_str()),
extension ? extension : "",
tempFilename);
if (!EC)
result = tempFilename.str();
} else if (extension) {
result += '.';
result += extension;
}
return result;
}
//////////////////////////////////////////////////////////////////////////////
// LTO functionality
namespace {
void addLinkerFlag(std::vector<std::string> &args, const llvm::Twine &flag) {
args.push_back("-Xlinker");
args.push_back(flag.str());
}
std::string getLTOGoldPluginPath() {
if (!ltoLibrary.empty()) {
if (llvm::sys::fs::exists(ltoLibrary))
return ltoLibrary;
error(Loc(), "-flto-binary: file '%s' not found", ltoLibrary.c_str());
fatal();
} else {
std::string searchPaths[] = {
// The plugin packaged with LDC has a "-ldc" suffix.
exe_path::prependLibDir("LLVMgold-ldc.so"),
// Perhaps the user copied the plugin to LDC's lib dir.
exe_path::prependLibDir("LLVMgold.so"),
#if __LP64__
"/usr/local/lib64/LLVMgold.so",
#endif
"/usr/local/lib/LLVMgold.so",
#if __LP64__
"/usr/lib64/LLVMgold.so",
#endif
"/usr/lib/LLVMgold.so",
"/usr/lib/bfd-plugins/LLVMgold.so",
};
// Try all searchPaths and early return upon the first path found.
for (const auto &p : searchPaths) {
if (llvm::sys::fs::exists(p))
return p;
}
error(Loc(), "The LLVMgold.so plugin (needed for LTO) was not found. You "
"can specify its path with -flto-binary=<file>.");
fatal();
}
}
void addLTOGoldPluginFlags(std::vector<std::string> &args) {
addLinkerFlag(args, "-plugin");
addLinkerFlag(args, getLTOGoldPluginPath());
if (opts::isUsingThinLTO())
addLinkerFlag(args, "-plugin-opt=thinlto");
if (!opts::mCPU.empty())
addLinkerFlag(args, llvm::Twine("-plugin-opt=mcpu=") + opts::mCPU);
// Use the O-level passed to LDC as the O-level for LTO, but restrict it to
// the [0, 3] range that can be passed to the linker plugin.
static char optChars[15] = "-plugin-opt=O0";
optChars[13] = '0' + std::min<char>(optLevel(), 3);
addLinkerFlag(args, optChars);
#if LDC_LLVM_VER >= 400
const llvm::TargetOptions &TO = gTargetMachine->Options;
if (TO.FunctionSections)
addLinkerFlag(args, "-plugin-opt=-function-sections");
if (TO.DataSections)
addLinkerFlag(args, "-plugin-opt=-data-sections");
#endif
}
// Returns an empty string when libLTO.dylib was not specified nor found.
std::string getLTOdylibPath() {
if (!ltoLibrary.empty()) {
if (llvm::sys::fs::exists(ltoLibrary))
return ltoLibrary;
error(Loc(), "-flto-binary: '%s' not found", ltoLibrary.c_str());
fatal();
} else {
// The plugin packaged with LDC has a "-ldc" suffix.
std::string searchPath = exe_path::prependLibDir("libLTO-ldc.dylib");
if (llvm::sys::fs::exists(searchPath))
return searchPath;
return "";
}
}
void addDarwinLTOFlags(std::vector<std::string> &args) {
std::string dylibPath = getLTOdylibPath();
if (!dylibPath.empty()) {
args.push_back("-lto_library");
args.push_back(std::move(dylibPath));
}
}
/// Adds the required linker flags for LTO builds to args.
void addLTOLinkFlags(std::vector<std::string> &args) {
#if LDC_LLVM_VER >= 309
if (global.params.targetTriple->isOSLinux() ||
global.params.targetTriple->isOSFreeBSD() ||
global.params.targetTriple->isOSNetBSD() ||
global.params.targetTriple->isOSOpenBSD() ||
global.params.targetTriple->isOSDragonFly()) {
// Assume that ld.gold or ld.bfd is used with plugin support.
addLTOGoldPluginFlags(args);
} else if (global.params.targetTriple->isOSDarwin()) {
addDarwinLTOFlags(args);
}
#endif
}
} // anonymous namespace
//////////////////////////////////////////////////////////////////////////////
namespace {
#if LDC_LLVM_VER >= 306
/// Insert an LLVM bitcode file into the module
void insertBitcodeIntoModule(const char *bcFile, llvm::Module &M,
llvm::LLVMContext &Context) {
Logger::println("*** Linking-in bitcode file %s ***", bcFile);
llvm::SMDiagnostic Err;
std::unique_ptr<llvm::Module> loadedModule(
getLazyIRFileModule(bcFile, Err, Context));
if (!loadedModule) {
error(Loc(), "Error when loading LLVM bitcode file: %s", bcFile);
fatal();
}
#if LDC_LLVM_VER >= 308
llvm::Linker(M).linkInModule(std::move(loadedModule));
#else
llvm::Linker(&M).linkInModule(loadedModule.release());
#endif
}
#endif
}
/// Insert LLVM bitcode files into the module
void insertBitcodeFiles(llvm::Module &M, llvm::LLVMContext &Ctx,
Array<const char *> &bitcodeFiles) {
#if LDC_LLVM_VER >= 306
for (const char *fname : bitcodeFiles) {
insertBitcodeIntoModule(fname, M, Ctx);
}
#else
if (!bitcodeFiles.empty()) {
error(Loc(),
"Passing LLVM bitcode files to LDC is not supported for LLVM < 3.6");
fatal();
}
#endif
}
//////////////////////////////////////////////////////////////////////////////
static void appendObjectFiles(std::vector<std::string> &args) {
for (unsigned i = 0; i < global.params.objfiles->dim; i++)
args.push_back((*global.params.objfiles)[i]);
if (global.params.targetTriple->isWindowsMSVCEnvironment()) {
if (global.params.resfile)
args.push_back(global.params.resfile);
if (global.params.deffile)
args.push_back(std::string("/DEF:") + global.params.deffile);
}
}
//////////////////////////////////////////////////////////////////////////////
static std::string gExePath;
static int linkObjToBinaryGcc(bool sharedLib, bool fullyStatic) {
Logger::println("*** Linking executable ***");
// find gcc for linking
std::string gcc(getGcc());
// build arguments
std::vector<std::string> args;
appendObjectFiles(args);
// Link with profile-rt library when generating an instrumented binary.
// profile-rt uses Phobos (MD5 hashing) and therefore must be passed on the
// commandline before Phobos.
if (global.params.genInstrProf) {
#if LDC_LLVM_VER >= 308
if (global.params.targetTriple->isOSLinux()) {
// For Linux, explicitly define __llvm_profile_runtime as undefined
// symbol, so that the initialization part of profile-rt is linked in.
args.push_back(
("-Wl,-u," + llvm::getInstrProfRuntimeHookVarName()).str());
}
#endif
args.push_back("-lldc-profile-rt");
}
// user libs
for (unsigned i = 0; i < global.params.libfiles->dim; i++)
args.push_back((*global.params.libfiles)[i]);
// output filename
std::string output = getOutputName(sharedLib);
if (sharedLib) {
args.push_back("-shared");
}
if (fullyStatic) {
args.push_back("-static");
}
args.push_back("-o");
args.push_back(output);
// set the global gExePath
gExePath = output;
// assert(gExePath.isValid());
// create path to exe
CreateDirectoryOnDisk(gExePath);
// Pass sanitizer arguments to linker. Requires clang.
if (opts::sanitize == opts::AddressSanitizer) {
args.push_back("-fsanitize=address");
}
if (opts::sanitize == opts::MemorySanitizer) {
args.push_back("-fsanitize=memory");
}
if (opts::sanitize == opts::ThreadSanitizer) {
args.push_back("-fsanitize=thread");
}
// Add LTO link flags before adding the user link switches, such that the user
// can pass additional options to the LTO plugin.
if (opts::isUsingLTO())
addLTOLinkFlags(args);
// additional linker switches
for (unsigned i = 0; i < global.params.linkswitches->dim; i++) {
const char *p = (*global.params.linkswitches)[i];
// Don't push -l and -L switches using -Xlinker, but pass them indirectly
// via GCC. This makes sure user-defined paths take precedence over
// GCC's builtin LIBRARY_PATHs.
// Options starting with `-Wl,`, -shared or -static are not handled by
// the linker and must be passed to the driver.
auto str = llvm::StringRef(p);
if (!(str.startswith("-l") || str.startswith("-L") ||
str.startswith("-Wl,") ||
str.startswith("-shared") || str.startswith("-static"))) {
args.push_back("-Xlinker");
}
args.push_back(p);
}
// default libs
bool addSoname = false;
switch (global.params.targetTriple->getOS()) {
case llvm::Triple::Linux:
addSoname = true;
// Make sure we don't do --gc-sections when generating a profile-
// instrumented binary. The runtime relies on magic sections, which
// would be stripped by gc-section on older version of ld, see bug:
// https://sourceware.org/bugzilla/show_bug.cgi?id=19161
if (!opts::disableLinkerStripDead && !global.params.genInstrProf) {
args.push_back("-Wl,--gc-sections");
}
if (global.params.targetTriple->getEnvironment() == llvm::Triple::Android) {
args.push_back("-ldl");
args.push_back("-lm");
break;
}
args.push_back("-lrt");
// fallthrough
case llvm::Triple::Darwin:
case llvm::Triple::MacOSX:
args.push_back("-ldl");
// fallthrough
case llvm::Triple::FreeBSD:
case llvm::Triple::NetBSD:
case llvm::Triple::OpenBSD:
case llvm::Triple::DragonFly:
addSoname = true;
args.push_back("-lpthread");
args.push_back("-lm");
break;
case llvm::Triple::Solaris:
args.push_back("-lm");
args.push_back("-lumem");
args.push_back("-lsocket");
args.push_back("-lnsl");
break;
default:
// OS not yet handled, will probably lead to linker errors.
// FIXME: Win32.
break;
}
if (global.params.targetTriple->isWindowsGNUEnvironment()) {
// This is really more of a kludge, as linking in the Winsock functions
// should be handled by the pragma(lib, ...) in std.socket, but it
// makes LDC behave as expected for now.
args.push_back("-lws2_32");
}
// Only specify -m32/-m64 for architectures where the two variants actually
// exist (as e.g. the GCC ARM toolchain doesn't recognize the switches).
// MIPS does not have -m32/-m64 but requires -mabi=.
if (global.params.targetTriple->get64BitArchVariant().getArch() !=
llvm::Triple::UnknownArch &&
global.params.targetTriple->get32BitArchVariant().getArch() !=
llvm::Triple::UnknownArch) {
if (global.params.targetTriple->get64BitArchVariant().getArch() ==
llvm::Triple::mips64 ||
global.params.targetTriple->get64BitArchVariant().getArch() ==
llvm::Triple::mips64el) {
switch (getMipsABI()) {
case MipsABI::EABI:
args.push_back("-mabi=eabi");
break;
case MipsABI::O32:
args.push_back("-mabi=32");
break;
case MipsABI::N32:
args.push_back("-mabi=n32");
break;
case MipsABI::N64:
args.push_back("-mabi=64");
break;
case MipsABI::Unknown:
break;
}
} else {
switch (global.params.targetTriple->getArch()) {
case llvm::Triple::arm:
case llvm::Triple::armeb:
case llvm::Triple::aarch64:
case llvm::Triple::aarch64_be:
#if LDC_LLVM_VER == 305
case llvm::Triple::arm64:
case llvm::Triple::arm64_be:
#endif
break;
default:
if (global.params.is64bit) {
args.push_back("-m64");
} else {
args.push_back("-m32");
}
}
}
}
if (global.params.dll && addSoname) {
std::string soname = opts::soname;
if (!soname.empty()) {
args.push_back("-Wl,-soname," + soname);
}
}
Logger::println("Linking with: ");
Stream logstr = Logger::cout();
for (const auto &arg : args) {
if (!arg.empty()) {
logstr << "'" << arg << "'"
<< " ";
}
}
logstr << "\n"; // FIXME where's flush ?
// try to call linker
return executeToolAndWait(gcc, args, global.params.verbose);
}
//////////////////////////////////////////////////////////////////////////////
#ifdef _WIN32
namespace windows {
bool needsQuotes(const llvm::StringRef &arg) {
return // not already quoted
!(arg.size() > 1 && arg[0] == '"' &&
arg.back() == '"') && // empty or min 1 space or min 1 double quote
(arg.empty() || arg.find(' ') != arg.npos || arg.find('"') != arg.npos);
}
size_t countPrecedingBackslashes(llvm::StringRef arg, size_t index) {
size_t count = 0;
for (size_t i = index - 1; i >= 0; --i) {
if (arg[i] != '\\')
break;
++count;
}
return count;
}
std::string quoteArg(llvm::StringRef arg) {
if (!needsQuotes(arg))
return arg;
std::string quotedArg;
quotedArg.reserve(3 + 2 * arg.size()); // worst case
quotedArg.push_back('"');
const size_t argLength = arg.size();
for (size_t i = 0; i < argLength; ++i) {
if (arg[i] == '"') {
// Escape all preceding backslashes (if any).
// Note that we *don't* need to escape runs of backslashes that don't
// precede a double quote! See MSDN:
// http://msdn.microsoft.com/en-us/library/17w5ykft%28v=vs.85%29.aspx
quotedArg.append(countPrecedingBackslashes(arg, i), '\\');
// Escape the double quote.
quotedArg.push_back('\\');
}
quotedArg.push_back(arg[i]);
}
// Make sure our final double quote doesn't get escaped by a trailing
// backslash.
quotedArg.append(countPrecedingBackslashes(arg, argLength), '\\');
quotedArg.push_back('"');
return quotedArg;
}
int executeAndWait(const char *commandLine) {
STARTUPINFO si;
ZeroMemory(&si, sizeof(si));
si.cb = sizeof(si);
PROCESS_INFORMATION pi;
ZeroMemory(&pi, sizeof(pi));
DWORD exitCode;
#if UNICODE
std::wstring wcommandLine;
if (!llvm::ConvertUTF8toWide(commandLine, wcommandLine))
return -3;
auto cmdline = const_cast<wchar_t *>(wcommandLine.data());
#else
auto cmdline = const_cast<char *>(commandLine);
#endif
// according to MSDN, only CreateProcessW (unicode) may modify the passed
// command line
if (!CreateProcess(NULL, cmdline, NULL, NULL, TRUE, 0,
NULL, NULL, &si, &pi)) {
exitCode = -1;
} else {
if (WaitForSingleObject(pi.hProcess, INFINITE) != 0 ||
!GetExitCodeProcess(pi.hProcess, &exitCode))
exitCode = -2;
CloseHandle(pi.hProcess);
CloseHandle(pi.hThread);
}
return exitCode;
}
bool setupMsvcEnvironment() {
if (getenv("VSINSTALLDIR"))
return true;
llvm::SmallString<128> tmpFilePath;
if (llvm::sys::fs::createTemporaryFile("ldc_dumpEnv", "", tmpFilePath))
return false;
/* Run `%ComSpec% /s /c "...\dumpEnv.bat <x86|amd64> > <tmpFilePath>"` to dump
* the MSVC environment to the temporary file.
*
* cmd.exe /c treats the following string argument (the command)
* in a very peculiar way if it starts with a double-quote.
* By adding /s and enclosing the command in extra double-quotes
* (WITHOUT additionally escaping the command), the command will
* be parsed properly.
*/
auto comspecEnv = getenv("ComSpec");
if (!comspecEnv) {
warning(Loc(),
"'ComSpec' environment variable is not set, assuming 'cmd.exe'.");
comspecEnv = "cmd.exe";
}
std::string cmdExecutable = comspecEnv;
std::string batchFile = exe_path::prependBinDir("dumpEnv.bat");
std::string arch =
global.params.targetTriple->isArch64Bit() ? "amd64" : "x86";
llvm::SmallString<512> commandLine;
commandLine += quoteArg(cmdExecutable);
commandLine += " /s /c \"";
commandLine += quoteArg(batchFile);
commandLine += ' ';
commandLine += arch;
commandLine += " > ";
commandLine += quoteArg(tmpFilePath);
commandLine += '"';
const int exitCode = executeAndWait(commandLine.c_str());
if (exitCode != 0) {
error(Loc(), "`%s` failed with status: %d", commandLine.c_str(), exitCode);
llvm::sys::fs::remove(tmpFilePath);
return false;
}
auto fileBuffer = llvm::MemoryBuffer::getFile(tmpFilePath);
llvm::sys::fs::remove(tmpFilePath);
if (fileBuffer.getError())
return false;
const auto contents = (*fileBuffer)->getBuffer();
const auto size = contents.size();
// Parse the file.
std::vector<std::pair<llvm::StringRef, llvm::StringRef>> env;
size_t i = 0;
// for each line
while (i < size) {
llvm::StringRef key, value;
for (size_t j = i; j < size; ++j) {
const char c = contents[j];
if (c == '=' && key.empty()) {
key = contents.slice(i, j);
i = j + 1;
} else if (c == '\n' || c == '\r' || c == '\0') {
if (!key.empty()) {
value = contents.slice(i, j);
}
// break and continue with next line
i = j + 1;
break;
}
}
if (!key.empty() && !value.empty())
env.emplace_back(key, value);
}
if (global.params.verbose)
fprintf(global.stdmsg, "Applying environment variables:\n");
bool haveVsInstallDir = false;
for (const auto &pair : env) {
const std::string key = pair.first.str();
const std::string value = pair.second.str();
if (global.params.verbose)
fprintf(global.stdmsg, " %s=%s\n", key.c_str(), value.c_str());
SetEnvironmentVariableA(key.c_str(), value.c_str());
if (key == "VSINSTALLDIR")
haveVsInstallDir = true;
}
return haveVsInstallDir;
}
} // namespace windows
#endif
//////////////////////////////////////////////////////////////////////////////
static int linkObjToBinaryMSVC(bool sharedLib) {
Logger::println("*** Linking executable ***");
const std::string tool = "link.exe";
// build arguments
std::vector<std::string> args;
args.push_back("/NOLOGO");
// specify that the image will contain a table of safe exception handlers
// and can handle addresses >2GB (32bit only)
if (!global.params.is64bit) {
args.push_back("/SAFESEH");
args.push_back("/LARGEADDRESSAWARE");
}
// output debug information
if (global.params.symdebug) {
args.push_back("/DEBUG");
}
// enable Link-time Code Generation (aka. whole program optimization)
if (global.params.optimize) {
args.push_back("/LTCG");
}
// remove dead code and fold identical COMDATs
if (opts::disableLinkerStripDead) {
args.push_back("/OPT:NOREF");
} else {
args.push_back("/OPT:REF");
args.push_back("/OPT:ICF");
}
// specify creation of DLL
if (sharedLib) {
args.push_back("/DLL");
}
// output filename
std::string output = getOutputName(sharedLib);
args.push_back("/OUT:" + output);
appendObjectFiles(args);
// Link with profile-rt library when generating an instrumented binary
// profile-rt depends on Phobos (MD5 hashing).
if (global.params.genInstrProf) {
args.push_back("ldc-profile-rt.lib");
// profile-rt depends on ws2_32 for symbol `gethostname`
args.push_back("ws2_32.lib");
}
// user libs
for (unsigned i = 0; i < global.params.libfiles->dim; i++)
args.push_back((*global.params.libfiles)[i]);
// set the global gExePath
gExePath = output;
// assert(gExePath.isValid());
// create path to exe
CreateDirectoryOnDisk(gExePath);
// additional linker switches
for (unsigned i = 0; i < global.params.linkswitches->dim; i++) {
std::string str = global.params.linkswitches->data[i];
if (str.length() > 2) {
// rewrite common -L and -l switches
if (str[0] == '-' && str[1] == 'L') {
str = "/LIBPATH:" + str.substr(2);
} else if (str[0] == '-' && str[1] == 'l') {
str = str.substr(2) + ".lib";
}
}
args.push_back(str);
}
// default libs
// TODO check which libaries are necessary
args.push_back("kernel32.lib");
args.push_back("user32.lib");
args.push_back("gdi32.lib");
args.push_back("winspool.lib");
args.push_back("shell32.lib"); // required for dmain2.d
args.push_back("ole32.lib");
args.push_back("oleaut32.lib");
args.push_back("uuid.lib");
args.push_back("comdlg32.lib");
args.push_back("advapi32.lib");
Logger::println("Linking with: ");
Stream logstr = Logger::cout();
for (const auto &arg : args) {
if (!arg.empty()) {
logstr << "'" << arg << "'"
<< " ";
}
}
logstr << "\n"; // FIXME where's flush ?
#ifdef _WIN32
windows::setupMsvcEnvironment();
#endif
// try to call linker
return executeToolAndWait(tool, args, global.params.verbose);
}
//////////////////////////////////////////////////////////////////////////////
int linkObjToBinary() {
if (global.params.targetTriple->isWindowsMSVCEnvironment()) {
// TODO: Choose dynamic/static MSVCRT version based on staticFlag?
return linkObjToBinaryMSVC(global.params.dll);
}
return linkObjToBinaryGcc(global.params.dll, staticFlag);
}
//////////////////////////////////////////////////////////////////////////////
int createStaticLibrary() {
Logger::println("*** Creating static library ***");
const bool isTargetMSVC =
global.params.targetTriple->isWindowsMSVCEnvironment();
#if LDC_LLVM_VER >= 309
const bool useInternalArchiver = externalArchiver.empty();
#else
const bool useInternalArchiver = false;
#endif
// find archiver
std::string tool;
if (useInternalArchiver) {
tool = isTargetMSVC ? "llvm-lib.exe" : "llvm-ar";
} else if (!externalArchiver.empty()) {
tool = externalArchiver;
} else {
tool = isTargetMSVC ? "lib.exe" : getArchiver();
}
// build arguments
std::vector<std::string> args;
// ask ar to create a new library
if (!isTargetMSVC) {
args.push_back("rcs");
}
// ask lib to be quiet
if (isTargetMSVC) {
args.push_back("/NOLOGO");
}
// enable Link-time Code Generation (aka. whole program optimization)
if (isTargetMSVC && global.params.optimize) {
args.push_back("/LTCG");
}
// output filename
std::string libName;
if (global.params.libname) { // explicit
// DMD adds the default extension if there is none
libName = opts::invokedByLDMD
? FileName::defaultExt(global.params.libname, global.lib_ext)
: global.params.libname;
} else { // infer from first object file
libName = global.params.objfiles->dim
? FileName::removeExt((*global.params.objfiles)[0])
: "a.out";
libName += '.';
libName += global.lib_ext;
}
// DMD creates static libraries in the objects directory (unless using an
// absolute output path via `-of`).
if (opts::invokedByLDMD && global.params.objdir &&
!FileName::absolute(libName.c_str())) {
libName = FileName::combine(global.params.objdir, libName.c_str());
}
if (isTargetMSVC) {
args.push_back("/OUT:" + libName);
} else {
args.push_back(libName);
}
appendObjectFiles(args);
// create path to the library
CreateDirectoryOnDisk(libName);
#if LDC_LLVM_VER >= 309
if (useInternalArchiver) {
std::vector<const char *> fullArgs;
fullArgs.reserve(1 + args.size());
fullArgs.push_back(tool.c_str());
for (const auto &arg : args)
fullArgs.push_back(arg.c_str());
if (global.params.verbose) {
for (auto arg : fullArgs) {
fprintf(global.stdmsg, "%s ", arg);
}
fprintf(global.stdmsg, "\n");
fflush(global.stdmsg);
}
const int exitCode = isTargetMSVC ? ldc::lib(fullArgs) : ldc::ar(fullArgs);
if (exitCode)
error(Loc(), "%s failed with status: %d", tool.c_str(), exitCode);
return exitCode;
}
#endif
#ifdef _WIN32
if (isTargetMSVC)
windows::setupMsvcEnvironment();
#endif
// try to call archiver
return executeToolAndWait(tool, args, global.params.verbose);
}
//////////////////////////////////////////////////////////////////////////////
void deleteExeFile() {
if (!gExePath.empty() && !llvm::sys::fs::is_directory(gExePath)) {
llvm::sys::fs::remove(gExePath);
}
}
//////////////////////////////////////////////////////////////////////////////
int runProgram() {
assert(!gExePath.empty());
// assert(gExePath.isValid());
// Run executable
int status =
executeToolAndWait(gExePath, opts::runargs, global.params.verbose);
if (status < 0) {
#if defined(_MSC_VER) || defined(__MINGW32__)
error(Loc(), "program received signal %d", -status);
#else
error(Loc(), "program received signal %d (%s)", -status,
strsignal(-status));
#endif
return -status;
}
return status;
}