forked from OSchip/llvm-project
[ELF] Add --dependency-file option
Clang and GCC have a feature (-MD flag) to create a dependency file in a format that build systems such as Make or Ninja can read, which specifies all the additional inputs such .h files. This change introduces the same functionality to lld bringing it to feature parity with ld and gold which gained this feature recently. See https://sourceware.org/bugzilla/show_bug.cgi?id=22843 for more details and discussion. The implementation corresponds to -MD -MP compiler flag where the generated dependency file also includes phony targets which works around the errors where the dependency is removed. This matches the format used by ld and gold. Fixes PR42806 Differential Revision: https://reviews.llvm.org/D82437
This commit is contained in:
parent
4c16eafe12
commit
b4c7657ba6
|
@ -11,6 +11,7 @@
|
|||
|
||||
#include "lld/Common/ErrorHandler.h"
|
||||
#include "llvm/ADT/MapVector.h"
|
||||
#include "llvm/ADT/SetVector.h"
|
||||
#include "llvm/ADT/StringRef.h"
|
||||
#include "llvm/ADT/StringSet.h"
|
||||
#include "llvm/BinaryFormat/ELF.h"
|
||||
|
@ -90,11 +91,13 @@ struct Configuration {
|
|||
uint8_t osabi = 0;
|
||||
uint32_t andFeatures = 0;
|
||||
llvm::CachePruningPolicy thinLTOCachePolicy;
|
||||
llvm::SetVector<StringRef> dependencyFiles; // for --dependency-file
|
||||
llvm::StringMap<uint64_t> sectionStartMap;
|
||||
llvm::StringRef bfdname;
|
||||
llvm::StringRef chroot;
|
||||
llvm::StringRef dynamicLinker;
|
||||
llvm::StringRef dependencyFile;
|
||||
llvm::StringRef dwoDir;
|
||||
llvm::StringRef dynamicLinker;
|
||||
llvm::StringRef entry;
|
||||
llvm::StringRef emulation;
|
||||
llvm::StringRef fini;
|
||||
|
|
|
@ -918,6 +918,7 @@ static void readConfigs(opt::InputArgList &args) {
|
|||
config->optimizeBBJumps =
|
||||
args.hasFlag(OPT_optimize_bb_jumps, OPT_no_optimize_bb_jumps, false);
|
||||
config->demangle = args.hasFlag(OPT_demangle, OPT_no_demangle, true);
|
||||
config->dependencyFile = args.getLastArgValue(OPT_dependency_file);
|
||||
config->dependentLibraries = args.hasFlag(OPT_dependent_libraries, OPT_no_dependent_libraries, true);
|
||||
config->disableVerify = args.hasArg(OPT_disable_verify);
|
||||
config->discard = getDiscard(args);
|
||||
|
@ -1564,6 +1565,75 @@ static void handleLibcall(StringRef name) {
|
|||
sym->fetch();
|
||||
}
|
||||
|
||||
// Handle --dependency-file=<path>. If that option is given, lld creates a
|
||||
// file at a given path with the following contents:
|
||||
//
|
||||
// <output-file>: <input-file> ...
|
||||
//
|
||||
// <input-file>:
|
||||
//
|
||||
// where <output-file> is a pathname of an output file and <input-file>
|
||||
// ... is a list of pathnames of all input files. `make` command can read a
|
||||
// file in the above format and interpret it as a dependency info. We write
|
||||
// phony targets for every <input-file> to avoid an error when that file is
|
||||
// removed.
|
||||
//
|
||||
// This option is useful if you want to make your final executable to depend
|
||||
// on all input files including system libraries. Here is why.
|
||||
//
|
||||
// When you write a Makefile, you usually write it so that the final
|
||||
// executable depends on all user-generated object files. Normally, you
|
||||
// don't make your executable to depend on system libraries (such as libc)
|
||||
// because you don't know the exact paths of libraries, even though system
|
||||
// libraries that are linked to your executable statically are technically a
|
||||
// part of your program. By using --dependency-file option, you can make
|
||||
// lld to dump dependency info so that you can maintain exact dependencies
|
||||
// easily.
|
||||
static void writeDependencyFile() {
|
||||
std::error_code ec;
|
||||
raw_fd_ostream os(config->dependencyFile, ec, sys::fs::F_None);
|
||||
if (ec) {
|
||||
error("cannot open " + config->dependencyFile + ": " + ec.message());
|
||||
return;
|
||||
}
|
||||
|
||||
// We use the same escape rules as Clang/GCC which are accepted by Make/Ninja:
|
||||
// * A space is escaped by a backslash which itself must be escaped.
|
||||
// * A hash sign is escaped by a single backslash.
|
||||
// * $ is escapes as $$.
|
||||
auto printFilename = [](raw_fd_ostream &os, StringRef filename) {
|
||||
llvm::SmallString<256> nativePath;
|
||||
llvm::sys::path::native(filename.str(), nativePath);
|
||||
llvm::sys::path::remove_dots(nativePath, /*remove_dot_dot=*/true);
|
||||
for (unsigned i = 0, e = nativePath.size(); i != e; ++i) {
|
||||
if (nativePath[i] == '#') {
|
||||
os << '\\';
|
||||
} else if (nativePath[i] == ' ') {
|
||||
os << '\\';
|
||||
unsigned j = i;
|
||||
while (j > 0 && nativePath[--j] == '\\')
|
||||
os << '\\';
|
||||
} else if (nativePath[i] == '$') {
|
||||
os << '$';
|
||||
}
|
||||
os << nativePath[i];
|
||||
}
|
||||
};
|
||||
|
||||
os << config->outputFile << ":";
|
||||
for (StringRef path : config->dependencyFiles) {
|
||||
os << " \\\n ";
|
||||
printFilename(os, path);
|
||||
}
|
||||
os << "\n";
|
||||
|
||||
for (StringRef path : config->dependencyFiles) {
|
||||
os << "\n";
|
||||
printFilename(os, path);
|
||||
os << ":\n";
|
||||
}
|
||||
}
|
||||
|
||||
// Replaces common symbols with defined symbols reside in .bss sections.
|
||||
// This function is called after all symbol names are resolved. As a
|
||||
// result, the passes after the symbol resolution won't see any
|
||||
|
@ -2064,6 +2134,11 @@ template <class ELFT> void LinkerDriver::link(opt::InputArgList &args) {
|
|||
return false;
|
||||
});
|
||||
|
||||
// Since we now have a complete set of input files, we can create
|
||||
// a .d file to record build dependencies.
|
||||
if (!config->dependencyFile.empty())
|
||||
writeDependencyFile();
|
||||
|
||||
// Now that the number of partitions is fixed, save a pointer to the main
|
||||
// partition.
|
||||
mainPart = &partitions[0];
|
||||
|
|
|
@ -110,6 +110,7 @@ Optional<MemoryBufferRef> elf::readFile(StringRef path) {
|
|||
path = saver.save(config->chroot + path);
|
||||
|
||||
log(path);
|
||||
config->dependencyFiles.insert(path);
|
||||
|
||||
auto mbOrErr = MemoryBuffer::getFile(path, -1, false);
|
||||
if (auto ec = mbOrErr.getError()) {
|
||||
|
|
|
@ -132,6 +132,9 @@ defm demangle: B<"demangle",
|
|||
"Demangle symbol names (default)",
|
||||
"Do not demangle symbol names">;
|
||||
|
||||
defm dependency_file: EEq<"dependency-file", "Write a dependency file">,
|
||||
MetaVarName<"<path>">;
|
||||
|
||||
def disable_new_dtags: F<"disable-new-dtags">,
|
||||
HelpText<"Disable new dynamic tags">;
|
||||
|
||||
|
|
|
@ -0,0 +1,21 @@
|
|||
# REQUIRES: x86
|
||||
# RUN: mkdir -p %t
|
||||
# RUN: llvm-mc -filetype=obj -triple=x86_64 %s -o %t/foo.o
|
||||
# RUN: llvm-mc -filetype=obj -triple=x86_64 /dev/null -o "%t/bar baz.o"
|
||||
# RUN: llvm-mc -filetype=obj -triple=x86_64 /dev/null -o "%t/#quux$.o"
|
||||
# RUN: ld.lld -o %t/foo.exe %t/foo.o %t/"bar baz.o" "%t/#quux$.o" --dependency-file=%t/foo.d
|
||||
# RUN: FileCheck --match-full-lines -DFILE=%t %s < %t/foo.d
|
||||
|
||||
# CHECK: [[FILE]]{{/|\\\\}}foo.exe: \
|
||||
# CHECK-NEXT: [[FILE]]{{/|\\\\}}foo.o \
|
||||
# CHECK-NEXT: [[FILE]]{{/|\\\\}}bar\ baz.o \
|
||||
# CHECK-NEXT: [[FILE]]{{/|\\\\}}\#quux$$.o
|
||||
# CHECK-EMPTY:
|
||||
# CHECK-NEXT: [[FILE]]{{/|\\\\}}foo.o:
|
||||
# CHECK-EMPTY:
|
||||
# CHECK-NEXT: [[FILE]]{{/|\\\\}}bar\ baz.o:
|
||||
# CHECK-EMPTY:
|
||||
# CHECK-NEXT: [[FILE]]{{/|\\\\}}\#quux$$.o:
|
||||
|
||||
.global _start
|
||||
_start:
|
Loading…
Reference in New Issue