The nice thing about dates is that they can be checked instantaneously, and independently of file size. If a file has changed, its date has, so it's all the information you need.
time find ./vendor -type f -print0 | sort -z | xargs -0 sha256sum | sort -k2 | sha256sum
Note if using this code for practical purposes, you should be sure your collation is set appropriately. That's taken with 1280 files that are in total 19MB.
For raw throughput, you can use BLAKE2 for even more speed https://www.blake2.net/
This might be approaching niche but I’ve seen a few mega-projects with 100 times more than that (also mostly due to vendored deps). You really don’t want to read 10G from disk for every incremental build. The actual sha computation isn’t the bottleneck, disk is. Ninja on the other hand, using mtime, handles this without a sweat.
The best solution would probably be to integrate the change detection with git, it is already content addressable and has lots of other smart things built in to make it fast.
Unless the date hasn't changed -- there is no hard guarantee that mtime will be accurate.
Yes, file hashes might be another way. On the other hand, if I modify a comment, the file hash will change, but re-compilation is not necessary. In this case, you might need yet another way...
You can always write your Makefiles to compute and depend on hashes or whatever else you might consider appropriate for your exact use case.
I mean those things work, but they're by no means nice. Parallelization works, but output of different tasks (and even the log that certain commands have been run) are interleaved, making it impossible to view progress. Language agnosticism works by operating on the level of shell commands, but that's really about it. If shell commands need to be customized on a per-system or per-OS level... well it's possible to hack around that with enough external scripts and even esoteric if statements inside the Makefile, but it's not nice.
Documentation for make feels like it's from the 80s. Which it is.
All these things (with the exception of having to rely on arcane sets of external scripts and if-statements-in-Makefile) are perfectly fixable. But they don't get fixed.