mirror of
https://github.com/ldc-developers/ldc.git
synced 2025-05-13 22:48:43 +03:00

This is what DMD does and crucial for `ldmd2 -od=objects foo.d` with relative -od path (and no -of). The object file path will be `objects/foo.o[bj]`. As it's relative, LDC used to prepend the objects dir (again) in LDMD mode, resulting in the inferred executable file name `objects/objects/foo[.exe]`. So while this is a breaking change, it fixes DMD compatibility of LDMD and makes a lot of sense for 'pure' LDC too IMO (use the first object's file name, replace the extension and save it in the working dir, not in the directory containing the first object file). This fixes the dmd-testsuite regressions with relative RESULTS_DIR and a few long-standing non-fatal dmd-testsuite errors (failing file removals).
165 lines
5.1 KiB
C++
165 lines
5.1 KiB
C++
//===-- linker.cpp --------------------------------------------------------===//
|
||
//
|
||
// LDC – the LLVM D compiler
|
||
//
|
||
// This file is distributed under the BSD-style LDC license. See the LICENSE
|
||
// file for details.
|
||
//
|
||
//===----------------------------------------------------------------------===//
|
||
|
||
#include "errors.h"
|
||
#include "driver/cl_options.h"
|
||
#include "driver/linker.h"
|
||
#include "driver/tool.h"
|
||
#include "gen/llvm.h"
|
||
#include "gen/logger.h"
|
||
#include "llvm/IRReader/IRReader.h"
|
||
#include "llvm/Linker/Linker.h"
|
||
#include "llvm/Support/FileSystem.h"
|
||
#include "llvm/Support/SourceMgr.h"
|
||
|
||
//////////////////////////////////////////////////////////////////////////////
|
||
|
||
#if LDC_WITH_LLD
|
||
static llvm::cl::opt<bool>
|
||
useInternalLinker("link-internally", llvm::cl::ZeroOrMore, llvm::cl::Hidden,
|
||
llvm::cl::desc("Use internal LLD for linking"),
|
||
llvm::cl::cat(opts::linkingCategory));
|
||
#else
|
||
constexpr bool useInternalLinker = false;
|
||
#endif
|
||
|
||
//////////////////////////////////////////////////////////////////////////////
|
||
|
||
// linker-gcc.cpp
|
||
int linkObjToBinaryGcc(llvm::StringRef outputPath, bool useInternalLinker,
|
||
llvm::cl::boolOrDefault fullyStaticFlag);
|
||
|
||
// linker-msvc.cpp
|
||
int linkObjToBinaryMSVC(llvm::StringRef outputPath, bool useInternalLinker,
|
||
llvm::cl::boolOrDefault fullyStaticFlag);
|
||
|
||
//////////////////////////////////////////////////////////////////////////////
|
||
|
||
static std::string getOutputName() {
|
||
const auto &triple = *global.params.targetTriple;
|
||
const bool sharedLib = global.params.dll;
|
||
|
||
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(FileName::name(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(
|
||
result, extension ? extension : "", tempFilename);
|
||
if (!EC)
|
||
result = tempFilename.str();
|
||
} else if (extension) {
|
||
result += '.';
|
||
result += extension;
|
||
}
|
||
|
||
return result;
|
||
}
|
||
|
||
//////////////////////////////////////////////////////////////////////////////
|
||
|
||
/// Insert an LLVM bitcode file into the module
|
||
static 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
|
||
}
|
||
|
||
/// Insert LLVM bitcode files into the module
|
||
void insertBitcodeFiles(llvm::Module &M, llvm::LLVMContext &Ctx,
|
||
Array<const char *> &bitcodeFiles) {
|
||
for (const char *fname : bitcodeFiles) {
|
||
insertBitcodeIntoModule(fname, M, Ctx);
|
||
}
|
||
}
|
||
|
||
//////////////////////////////////////////////////////////////////////////////
|
||
|
||
// path to the produced executable/shared library
|
||
static std::string gExePath;
|
||
|
||
//////////////////////////////////////////////////////////////////////////////
|
||
|
||
int linkObjToBinary() {
|
||
Logger::println("*** Linking executable ***");
|
||
|
||
// remember output path for later
|
||
gExePath = getOutputName();
|
||
|
||
createDirectoryForFileOrFail(gExePath);
|
||
|
||
if (global.params.targetTriple->isWindowsMSVCEnvironment()) {
|
||
return linkObjToBinaryMSVC(gExePath, useInternalLinker, opts::staticFlag);
|
||
}
|
||
|
||
return linkObjToBinaryGcc(gExePath, useInternalLinker, opts::staticFlag);
|
||
}
|
||
|
||
//////////////////////////////////////////////////////////////////////////////
|
||
|
||
void deleteExeFile() {
|
||
if (!gExePath.empty() && !llvm::sys::fs::is_directory(gExePath)) {
|
||
llvm::sys::fs::remove(gExePath);
|
||
}
|
||
}
|
||
|
||
//////////////////////////////////////////////////////////////////////////////
|
||
|
||
int runProgram() {
|
||
assert(!gExePath.empty());
|
||
|
||
// 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;
|
||
}
|