diff --git a/lld/ELF/Config.h b/lld/ELF/Config.h --- a/lld/ELF/Config.h +++ b/lld/ELF/Config.h @@ -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" @@ -83,10 +84,12 @@ uint8_t osabi = 0; uint32_t andFeatures = 0; llvm::CachePruningPolicy thinLTOCachePolicy; + llvm::SetVector dependencyFiles; // for --dependency-file llvm::StringMap sectionStartMap; llvm::StringRef chroot; - llvm::StringRef dynamicLinker; + llvm::StringRef dependencyFile; llvm::StringRef dwoDir; + llvm::StringRef dynamicLinker; llvm::StringRef entry; llvm::StringRef emulation; llvm::StringRef fini; diff --git a/lld/ELF/Driver.cpp b/lld/ELF/Driver.cpp --- a/lld/ELF/Driver.cpp +++ b/lld/ELF/Driver.cpp @@ -808,6 +808,7 @@ config->defineCommon = args.hasFlag(OPT_define_common, OPT_no_define_common, !args.hasArg(OPT_relocatable)); 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); @@ -1373,6 +1374,46 @@ sym->fetch(); } +// Handle --write-dependencies=. If that option is given, lld creates a +// file at a given path with the following contents: +// +// : ... +// +// where is a pathname of an output file and +// ... 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. +// +// 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 --write-dependencies 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; + } + + os << config->outputFile << ":"; + for (StringRef path : config->dependencyFiles) { + SmallString<128> s = path; + sys::path::remove_dots(s, /*remove_dot_dot=*/true); + if (s.str().contains(' ')) + os << " \\\n \"" << s << '"'; + else + os << " \\\n " << s; + } + 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 @@ -1842,6 +1883,11 @@ (s->name.startswith(".debug") || s->name.startswith(".zdebug")); }); + // 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]; diff --git a/lld/ELF/InputFiles.cpp b/lld/ELF/InputFiles.cpp --- a/lld/ELF/InputFiles.cpp +++ b/lld/ELF/InputFiles.cpp @@ -95,6 +95,7 @@ path = saver.save(config->chroot + path); log(path); + config->dependencyFiles.insert(path); auto mbOrErr = MemoryBuffer::getFile(path, -1, false); if (auto ec = mbOrErr.getError()) { diff --git a/lld/ELF/Options.td b/lld/ELF/Options.td --- a/lld/ELF/Options.td +++ b/lld/ELF/Options.td @@ -108,6 +108,9 @@ "Demangle symbol names (default)", "Do not demangle symbol names">; +defm dependency_file: Eq<"dependency-file", "Write a dependency file">, + MetaVarName<"">; + def disable_new_dtags: F<"disable-new-dtags">, HelpText<"Disable new dynamic tags">; diff --git a/lld/test/ELF/dependency-file.s b/lld/test/ELF/dependency-file.s new file mode 100644 --- /dev/null +++ b/lld/test/ELF/dependency-file.s @@ -0,0 +1,13 @@ +// REQUIRES: x86 +// RUN: mkdir -p %t +// RUN: llvm-mc -filetype=obj -triple=x86_64-unknown-linux %s -o %t/foo.o +// RUN: llvm-mc -filetype=obj -triple=x86_64-unknown-linux /dev/null -o "%t/bar baz.o" +// RUN: ld.lld -o %t/foo.exe %t/foo.o %t/"bar baz.o" --dependency-file=%t/foo.d +// RUN: FileCheck --match-full-lines %s < %t/foo.d + +// CHECK: {{.*}}foo.exe: \ +// CHECK-NEXT: {{.*}}foo.o \ +// CHECK-NEXT: "{{.*}}bar baz.o" + +.global _start +_start: