[PATCH] D65430: Add `--dependency-files` option, which is equivalent to compiler option -MD.

Fangrui Song via Phabricator via llvm-commits llvm-commits at lists.llvm.org
Mon Aug 5 22:43:47 PDT 2019


MaskRay added a subscriber: bd1976llvm.
MaskRay added a comment.

In D65430#1615429 <https://reviews.llvm.org/D65430#1615429>, @phosek wrote:

> One use case for `ld.lld --dependency-file=` is auto-linking which is supported by lld for both ELF and COFF. In case of auto-linking, you don't know which additional dependencies are going to be included until you process all input files. Another case that's specific to ELF are linker scripts which allows specifying additional inputs using the `INPUT` statement e.g. libc++ installs the following linker script `INPUT(libc++.so.1 -lunwind -lc++abi)` as `libc++.so`.
>
> Regarding the depfile format, the simplified Makefile format is the de-facto standard for depfiles and is emitted and supported by build systems like Make or Ninja (which is generated by number of other high-level build systems like CMake, Meson, GN, etc.). Using this format would make this functionality immediately usable.


I carefully think about this. deplibs is probably the only reasonable use case. Let me give an example to ensure I understand it correctly:

  touch d.h
  
  cat > a.c <<e
  #include "d.h"
  #pragma comment(lib, "b.a")
  #pragma comment(lib, "c.a")
  extern int b, c;
  int main() {}
  e
  
  cat > b.c <<e
  int b;
  e
  
  cat > c.c <<e
  int c;
  e

build.ninja:

  rule cc
    depfile = $out.d
    command = clang -MD -MT $out -MF $out.d -c $in -o $out
  
  rule link
    depfile = $out.link.d
    command = clang $in -fuse-ld=lld -Wl,--dependency-file=$out.link.d -o $out
  
  build a: link a.o
  build a.o: cc a.c

Alternatively, Makefile:

  CFLAGS = -MD -MP -MF $@.d  # note -MP is not required in build.ninja
  CC = clang
  LDFLAGS = -fuse-ld=lld -Wl,--dependency-file=$@.link.d
  a_deps = a.o  # explicit deps (.deplibs are excluded)
  
  # implicit rule of a cannot be used because it would link all files specified in a.link.d together
  a: $(a_deps)
  	$(LINK.c) $(a_deps) $(LDLIBS) -o $@
  
  -include a.o.d
  -include a.link.d

This dependency file approach has an assumption: `a.o.d` is not older than `a.o`, `a.link.d` is not older than `a`.

1. if `a.o.d` does not exist, according to the assumption, `a.o` does not exist as well. `ninja` or `make` generates `a.o` and `a.o.d`. `a` and `a.link.d` are similar.
2. if `a.c` or `d.h` is updated, `a` get generated due to the dependency described in `a.o.d`.
3. if `d.h` is deleted and `#include "d.h"` is deleted from `a.c`. Note there is no rule to generate `d.h`. ninja seems to ignore its nonexistence, while Makefile needs a force target. That is why in the Makefile version we use `-MP`.
4. if `b.a` is deleted and `#pragma comment(lib, "b.a")` is deleted from `a.c`. ninja ignores b.a and generates `a`. However, GNU make will error: `make: *** No rule to make target 'b.a', needed by 'a'.  Stop.`

I think `ld.lld --write-dependencies=` can be plugged into a ninja based build system (including `cmake -G Ninja`) without change, but not into a Makefile based build system.
To use this feature in meson/bazel/buck/..., a post-processing tool is definitely needed.
However, if a post-processing tool is ever needed, `ld.lld -t` will be just as simple (or difficult) to use.

Let's revisit this ninja fragment:

  rule link
    depfile = $out.link.d
    command = clang $in -fuse-ld=lld -Wl,--dependency-file=$out.link.d -o $out

`#pragma comment(lib, "b.a")` is not common. If the build system goes through the trouble to use deplibs,
there may likely be other post processing steps. I wonder if it can just point `-fuse-ld=` to a custom wrapper that processes `ld.lld -t` output and generates `$out.link.d`.

So the question is whether we consider this feature too application-specific.
For simple dependency information, we can already use `-t`.
For richer information, there is a proposal for dependency analysis (https://lists.llvm.org/pipermail/llvm-dev/2019-February/130565.html)

I'd also like to hear from @bd1976llvm, who implemented the deplibs feature in the first place.



================
Comment at: lld/ELF/Config.h:87
   llvm::CachePruningPolicy thinLTOCachePolicy;
+  llvm::SetVector<StringRef> dependencyFiles; // for --dependency-file
   llvm::StringMap<uint64_t> sectionStartMap;
----------------
This probably should be

  llvm::SetVector<std::string, std::vector<std::string>,
                  llvm::StringSet<llvm::MallocAllocator>>
      dependencyFiles; // for --dependency-file


Some `StringRef` elements are not internalized.


================
Comment at: lld/ELF/Driver.cpp:1377
 
+// Handle --write-dependencies=<path>. If that option is given, lld creates a
+// file at a given path with the following contents:
----------------
`--dependency-file`


Repository:
  rG LLVM Github Monorepo

CHANGES SINCE LAST ACTION
  https://reviews.llvm.org/D65430/new/

https://reviews.llvm.org/D65430





More information about the llvm-commits mailing list