by dsego on 6/20/25, 8:05 AM with 118 comments
by bsenftner on 6/20/25, 11:07 AM
by stabbles on 6/20/25, 9:24 AM
Output synchronization which makes `make` print stdout/stderr only once a target finishes. Otherwise it's typically interleaved and hard to follow:
make --output-sync=recurse -j10
On busy / multi-user systems, the `-j` flag for jobs may not be best. Instead you can also limit parallelism based on load average: make -j10 --load-average=10
Randomizing the order in which targets are scheduled. This is useful for your CI to harden your Makefiles and see if you're missing dependencies between targets: make --shuffle # or --shuffle=seed/reverse
by leetrout on 6/20/25, 12:13 PM
My teammates gave me a hard time for adding and maintaining .PHONY on all our recipes since we use make as a task runner.
Clark Grubb has a great page explaining a style guide for make files:
https://clarkgrubb.com/makefile-style-guide
Does anyone else use this style guide? Or for phony recipes marking phony at the recipe declaration vs a giant list at the top of the file?
I would love to have a linter that enforced this…
by danw1979 on 6/20/25, 11:01 AM
People sometimes treat it as a generic “project specific job runner”, which it’s not a good fit for. Even simple conditionals are difficult.
I’ve seen several well-intentioned attempts at wrapping Terraform with it, for example, which have ended terribly.
by llukas on 6/20/25, 10:36 AM
by amelius on 6/20/25, 12:21 PM
https://gittup.org/tup/ex_dependencies.html
It is a build system that automatically determines dependencies based on file system access, so it can work with any kind of compiler/tool.
by stabbles on 6/20/25, 9:36 AM
Basically it's considered too hard if not impossible to statically define the target's dependencies. This is now done dynamically with tools like `clang-scan-deps` [2]
[1] https://cmake.org/cmake/help/latest/manual/cmake-cxxmodules....
[2] https://llvm.org/devmtg/2019-04/slides/TechTalk-Lorenz-clang...
by o11c on 6/20/25, 4:02 PM
In order to handle long options and empty short options, when searching MAKEFLAGS you really need to do:
ifneq (,$(findstring t,$(firstword -$(MAKEFLAGS))))
If you need compatibility with the ancient version of `make` shipped by default on OS X, not that it is missing a lot of functions and features, some subtly.Most other problems are either obvious typos or due to violating best-practice style, so I won't go into them.
Note also that `load` is actually more portable than `guile`, but make sure you use the correct compiler in case you're cross-compiling the main project.
***
Really you should just read Paul's Rules of Makefiles [1], then the GNU make manual [2]. Some of the other manuals related to GNU build tools are also relevant even if you aren't using the particular tool. I also have a demo project [3] if you just want something that works and isn't complicated.
[1]: https://make.mad-scientist.net/papers/rules-of-makefiles/
by andreynering on 6/20/25, 12:03 PM
It has existed for 8+ years and still evolving. Give it a try if you're looking for something fresh, and don't hesitate to ask any questions.
by articsputnik on 6/20/25, 12:13 PM
by pards on 6/20/25, 10:50 AM
Oh no. I have never worked with Makefiles but I bet that causes pain and suffering.
I've lost so many hours to missing/extraneous spaces in YAML files that my team recently agreed to get rid of YAML from our Spring Boot codebase.
by jekwoooooe on 6/20/25, 1:28 PM
by codelikeawolf on 6/20/25, 2:57 PM
> The build system does not need to be highly portable.
I know "highly" is a vague qualifier here, but I pretty much always default to a Makefile in Go projects and have used it to build Electron apps on Linux, macOS, and Windows (without WSL, just Make for Windows). You have to do a little extra finagling to get the executable paths right, but it works well enough for my purposes.
To some extent, I get why Make gets a lot of hate. But if you keep them simple, they provide a great way to get around some of the limitations of package.json scripts (e.g., adding comments).
by Joker_vD on 6/20/25, 11:57 AM
SOURCE_FILES := $(wildcard $(SRC_DIR)/*.c)
HEADER_FILES := $(wildcard $(SRC_DIR)/*.h)
OBJ_FILES := $(patsubst $(SRC_DIR)/%.c,$(BUILD_DIR)/%.o,$(SOURCE_FILES))
.PHONY: build clean
build: $(BUILD_DIR)/$(TARGET)
clean:
rm -rf $(BUILD_DIR)
$(BUILD_DIR):
mkdir $(BUILD_DIR)
$(BUILD_DIR)/$(TARGET): $(OBJ_FILES) | $(BUILD_DIR)
$(LINK.o) $^ $(LDLIBS) -o $@
$(BUILD_DIR)/%.o: $(SRC_DIR)/%.c $(HEADER_FILES) | $(BUILD_DIR)
$(COMPILE.c) $< -o $@
So when you don't fiddle with inter-file/shared interfaces, you get an incremental rebuild. When you do — you get a full rebuild. Not ideal, but mostly fine, in my experience.P.S. I just love the way that Make names its built-in variables. The output is obviously $@, but can you quickly tell which of $^ and $< give you only the first of the inputs? What about $> and $∨, do you remember what they do?
by circadian on 6/20/25, 12:40 PM
by buserror on 6/20/25, 9:37 AM
The interesting bits are for example the -MMD flag to gcc, which outputs a .d file you can -include ${wildcard *.d} and you get free, up to date dependencies for your headers etc.
That and 'vpath' to tell it where to find the source files for % rules, and really, all the hard work is done and your 1/2 page Makefile will stay the same 'forever' and wills still work in 20 years...
by donatj on 6/20/25, 10:46 AM
$ make foo
Hello foo
This ran too!
That's a contrived example, but some of these take a bit too much thought parsing the example Makefile alone to understand the execution order and rule selection.It would just be very helpful to have clear examples of when I run this, I get this.
by globular-toast on 6/20/25, 10:59 AM
I also realised at one point how naturally the idea extends to other tasks that I do. Going by the picture at the top of this site, it seems the author realised a similar thing to me: you can understand food recipes better if you think about them declaratively like makefiles, rather than imperatively like scripts, which is how recipes are traditionally written down.
I wrote about it here: https://blog.gpkb.org/posts/cooking-with-make/
I always scribble down recipes in a way that I can read like a Makefile and take that into the kitchen with me. I'm curious if anyone has tried typesetting or displaying recipes in this way as I feel like it would save a lot of time when reading new recipes as I wouldn't have to convert from a script to a makefile myself.
by torcete on 6/20/25, 1:03 PM
by uncircle on 6/20/25, 11:13 AM
Is this true? Doesn't macOS ship with BSD make?
by donatj on 6/20/25, 10:37 AM
I knew there was a lot of weirdness and baggage but I am frankly kind of horrified to learn about these "implicit rules" that seemingly automatically activate the C compiler due to the mere presence of a rule that ends in ".c" or ".o"
by matheusmoreira on 6/20/25, 9:29 AM
Makefiles are eerily lisplike turing tarpits. GNU Make even has metaprogramming capabilities. Resisting the urge to metaprogram some unholy system inside the makefile can be difficult. The ubiquitousness of GNU Make makes it quite tempting.
by signa11 on 6/20/25, 9:19 AM
by kjgkjhfkjf on 6/20/25, 10:33 AM
by claytonaalves on 6/20/25, 11:09 AM
by akoboldfrying on 6/20/25, 12:10 PM
Make cares only about how to generate files from other files.
I point this out because this is one of the classic misunderstandings about dependencies that beginners (and sometimes old hands) make. The code inside main.cpp might well depend on code in one.cpp to work, but you don't generate main.cpp from one.cpp et al. You generate the final binary from those other files, and that's what make cares about.
One right way to show it would be to have one.o depend on both one.cpp and on one.h (yes, this is confusing at first), likewise for two.o, and main.exe depend on one.o, two.o, libc and libm. Another way would be to omit the object files completely (as now), and just have main.exe depend directly on all other targets, but this makes for a less helpful example.
ETA: I'd also appreciate it if you would mention in the "Recursive use of make" section that calling make recursively is a Bad Idea [0]. (Why? In short, because no dependency information can cross that process barrier, so there's always a risk that you don't build things in the right order, forget to build something you should, etc. If you have a hierarchy of projects in a subdir hierarchy, it's much better to use a separate "Makefile fragment" file in each subdir, and then "include" them all into a single top-level Makefile, so that make has a chance to ensure that, ya know, things get built before other things that need them.) I realise the GNU docs themselves don't say so, and GNU make has a ton of hacks to accommodate recursive make (suggesting that this is a blessed way), but that is simply unfortunate.
[0]: "Recursive Make Considered Harmful", https://accu.org/journals/overload/14/71/miller_2004/
by 90s_dev on 6/20/25, 1:02 PM
by revskill on 6/20/25, 11:58 AM