5 ms·
Make has some serious shortcomings though. Some that I can think of: it relies on timestamps instead of content hashes, it doesn't rebuild things when e.g. comp
by pg314 9y ago
Make has some serious shortcomings though. Some that I can think of: it relies on timestamps instead of content hashes, it doesn't rebuild things when e.g. compiler flags change, its approach to header dependencies in C/C++ is clumsy, it needs to parse the Makefile each time (which for large projects takes non-negligible time), it doesn't have an integration with inotify (which means it needs to stat() every file to check its timestamp).
- LukeShu 9y ago> it relies on timestamps instead of content hashes Which means it can be faster. If you need to be able to write a file, but have the build system recognize that the new one is the same as the old; this can be accomplished with a simple `sponge`-like script that only writes the file if the new version is different than the old. (I like to call the script write-ifchanged) > it doesn't rebuild things when e.g. compiler flags change, If you don't tell it to, no it doesn't. You can easily combine the above write-ifchanged with the technique described in the article to declare dependencies on variable values. > its approach to header dependencies in C/C++ is clumsy Sure, but not clumsier than other build systems. > it needs to parse the Makefile each time (which for large projects takes non-negligible time) Truth. > it doesn't have an integration with inotify (which means it needs to stat() every file to check its timestamp). Which means that it works with NFS, doesn't have race conditions involving renaming directories, ...
- pg314 9y ago>Which means it can be faster. If you need to be able to write a file, but have the build system recognize that the new one is the same as the old; this can be accomplished with a simple `sponge`-like script that only writes the file if the new version is different than the old. (I like to call the script write-ifchanged) > If you don't tell it to, no it doesn't. You can easily combine the above write-ifchanged with the technique described in the article to declare dependencies on variable values. You can make Make do a lot of things with enough hacking, but the defaults matter. The majority of projects that use make don't bother. Meaning you need a 'make clean; make' if you change compiler flags. > Which means that it works with NFS, doesn't have race conditions involving renaming directories, ... It has plenty of race conditions without that: if you change a header file in the middle of a make run, it will happily continue without warning. A lot of Make's design decisions make sense given its history. Given its ubiquity it's also the best choice for a lot of projects. That doesn't mean it's perfect.
- spc476 9y ago> it relies on timestamps instead of content hashes If it relied upon content hashes, then make would have to hash the files each time it ran (taking time---checking the timestamp is faster). For a large project, this might be too excessive. > it doesn't rebuild things when e.g. compiler flags change Just make the Makefile a dependency on each target. That problem solved. > it needs to parse the Makefile each time Um ... of course. The only way around this is for make to compile into some easier format to read or some database type thing (perhaps a .makefile?) and it would need to track changes to Makefile to update the database type thing (well, make does dependency tracking so that shouldn't be too hard). Just a question---do you have a project where reading the Makefile takes a non-negligible time? > it doesn't have an integration with inotify inotify is a) Linux only (make, and specifically GNU Make, runs on nearly everything) and b) it's an API, not a service that can be queried. Doing this implies that make will have to always be running checking all files for a project. Which project? All projects that have a makefile on my system? After a certain number of files being tracked, I'm certain that checking "inotify" (a daemon perhaps?) is the same as checking "stat()" for file metadata.
- paulddraper 9y agoThese are solvable approaches, though it's easy to see why make did what it did. > hash the files each time it ran Bazel & co relies on file hashes, but only checks then when the timestamp changes. Downside: now you have to keep hashes as metadata somewhere. > it needs to parse the Makefile each time Many build tools run a long running process either in the foreground (SBT, new Maven) or in the background (Gradle). Downside: Now you have to run a long running process... > it doesn't have an integration with inotify It could. Downside: Not universal, uses memory, more complex.
- pg314 9y ago> If it relied upon content hashes, then make would have to hash the files each time it ran (taking time---checking the timestamp is faster). For a large project, this might be too excessive. You can combine timestamps with content hashes. Or if you have inotify integration, you only need to process a file when it changes. > Just make the Makefile a dependency on each target. That problem solved. You can override variables when invoking make without changing the Makefile. Problem not solved. Of course there are some hacks to add dependencies on variable values. The majority of Makefiles in the wild don't do this, requiring you to do 'make clean; make' if you change flags. Defaults matter. > Just a question---do you have a project where reading the Makefile takes a non-negligible time? At the moment, no. But just to give you an idea of how it could become a problem: a simple C++ file including <iostream> leads to a 25KB .d file with 200 dependencies (generated by clang++ -MD). For a project with 1000 files, I made a 25MB file with one target and 20000 dependencies on the same file. Make took one second to process that. That is about the simplest Makefile to read. Throw in some includes, multiple Makefiles, variable expansions, and external shell invocations and that time will go up. > inotify is a) Linux only (make, and specifically GNU Make, runs on nearly everything) I should have said inotify or similar system (bsd has kqueue, I imagine Windows has something similar). > b) it's an API, not a service that can be queried. Doing this implies that make will have to always be running checking all files for a project. Which project? The one I'm working on. I'll start a daemon when I'm working on a project. As long as we're dreaming, it would be nice to have a filesystem that integrates Merkle trees and exposes an API you could query to see if anything has changed in a subdirectory. Apple kludged something together with their file systems events API [1] that provides similar functionality. It provides an API to query what has changed in a directory subtree since a previous invocation. No need for a persistent process. They use it for their Time Machine backup software. > After a certain number of files being tracked, I'm certain that checking "inotify" (a daemon perhaps?) is the same as checking "stat()" for file metadata. No. With inotify the work is proportional to the number of changed files. With stat() you need to check every file, so the work is proportional to the total number of files in your project. As I said elsewhere in this thread, most of the design decisions of Make make sense given its history. That doesn't mean they are still optimal today. [1] https://developer.apple.com/library/content/documentation/Darwin/Conceptual/FSEvents_ProgGuide/UsingtheFSEventsFramework/UsingtheFSEventsFramework.html https://developer.apple.com/library/content/documentation/Da...
- uvatbc 9y agoI hesitate to divert the topic, however: I'm working in a startup that has a solution to three of these shortcomings. We're not publicly available yet, but we are interested in anyone who'd want to try us out.
- emelski 9y agoThat's true if you limit yourself to, say, GNU make -- but there are GNU-make-compatible alternatives like Electric Make, part of ElectricAccelerator (http://electric-cloud.com/products/electricaccelerator http://electric-cloud.com/products/electricaccelerator), that add features like ledger, to trigger rebuilds when compiler flags change; filesystem monitoring for truly accurate dependency detection; and parse avoidance, to avoid reparsing the makefile on every run; as well as a long list of other enhancements. Disclaimer: I'm the author of TFA and Chief Architect for Electric Make.
- evmar 9y agohttps://ninja-build.org/ https://ninja-build.org/ addresses many of these issues while staying mostly compatible with the spirit of make (simple file-based dependency rules).