TE
TechEcho
Home24h TopNewestBestAskShowJobs
GitHubTwitter
Home

TechEcho

A tech news platform built with Next.js, providing global tech news and discussions.

GitHubTwitter

Home

HomeNewestBestAskShowJobs

Resources

HackerNews APIOriginal HackerNewsNext.js

© 2025 TechEcho. All rights reserved.

An opinionated approach to GNU Make

414 pointsby DarkCrusader2over 5 years ago

28 comments

jgrahamcover 5 years ago
For one-line recipes you can use semicolon<p><pre><code> foo.o: foo.c ; $(CC) $&lt; </code></pre> If you find $(@D) hard to remember then do $(dir $@) instead.<p>I wrote a book about GNU Make stuff (<a href="https:&#x2F;&#x2F;nostarch.com&#x2F;gnumake" rel="nofollow">https:&#x2F;&#x2F;nostarch.com&#x2F;gnumake</a>). If (and only if) you&#x27;ve read the GNU Make Manual from the FSF my book may help you. It&#x27;s not for newbies. Don&#x27;t want the book? All the articles are here: <a href="https:&#x2F;&#x2F;blog.jgc.org&#x2F;2013&#x2F;02&#x2F;updated-list-of-my-gnu-make-articles.html" rel="nofollow">https:&#x2F;&#x2F;blog.jgc.org&#x2F;2013&#x2F;02&#x2F;updated-list-of-my-gnu-make-art...</a>
评论 #21814572 未加载
评论 #21813065 未加载
ciesover 5 years ago
Learned some good tricks here. There&#x27;s one that I use but did not find in the article. This one:<p><pre><code> # Borrowed from https:&#x2F;&#x2F;marmelab.com&#x2F;blog&#x2F;2016&#x2F;02&#x2F;29&#x2F;auto-documented-makefile.html .PHONY: help help: ## Display this help section @awk &#x27;BEGIN {FS = &quot;:.*?## &quot;} &#x2F;^[a-zA-Z0-9_-]+:.*?## &#x2F; {printf &quot;\033[36m%-38s\033[0m %s\n&quot;, $$1, $$2}&#x27; $(MAKEFILE_LIST) .DEFAULT_GOAL := help .PHONY: load-terraform-output load-terraform-output: ## Request the output JSON from Terraform some commands some more </code></pre> It makes Makefiles a little more self documenting.<p>Now I issue a `make` (or `make help`) to get a listing of the documented tasks. Very helpful.
评论 #21817505 未加载
评论 #21819894 未加载
评论 #21814002 未加载
评论 #21820658 未加载
评论 #21836440 未加载
评论 #21813675 未加载
sirnover 5 years ago
If you&#x27;re using GNU-specific Make features, please, please consider naming the file GNUmakefile instead of Makefile and use $(MAKE) for recursion. GNU make will happily pick up GNUmakefile before Makefile, and using $(MAKE) will remove a lot of headaches when people try to build your project outside of author&#x27;s $PREFERRED_PLATFORM.
评论 #21813499 未加载
评论 #21813509 未加载
calpatersonover 5 years ago
Terrible clickbaity title that he walks back throughout the entire post - &quot;this is not dogma&quot;. There are a couple of valid remedial notes in here (like using files as targets) but the part about changing the default shell and inserting hacks that allow you to use spaces will just baffle anyone else and probably make your Makefiles non-portable (eg macs don&#x27;t have a recent bash).
评论 #21813743 未加载
评论 #21812828 未加载
评论 #21815374 未加载
评论 #21813392 未加载
评论 #21817828 未加载
majewskyover 5 years ago
&gt; And you will never again pull your hair out because some editor swapped a tab for four spaces and made Make do insane things.<p><i>steps up to you and whispers in your ear</i><p>Or you could, you know... use a real text editor.
评论 #21812890 未加载
评论 #21815269 未加载
评论 #21819829 未加载
clarryover 5 years ago
Ok, since we&#x27;re sharing controversial opinions...<p>If you&#x27;re going to rely on a fancy shell anyway, why not just throw make out of the loop altogether? That is, unless you&#x27;re working on a big project where incremental builds really make a difference. (But IME, with a few exceptions, these are the projects that usually outgrow and abandon make anyway)<p>You can run cc via shell via make, or you can just run cc via shell. In the latter case, there&#x27;s one less program (with quirks) to fight with, and more flexibility to do stuff that you can&#x27;t easily bend make to do.
评论 #21812889 未加载
评论 #21814023 未加载
评论 #21813821 未加载
评论 #21813541 未加载
评论 #21822524 未加载
bogwogover 5 years ago
This is a good list, but I have to disagree on the tab thing.<p>&gt; And you will never again pull your hair out because some editor swapped a tab for four spaces<p>How many editors out there in 2019 will automatically replace tabs with spaces by default? Unless you&#x27;re editing makefiles in Microsoft Word or something, I don&#x27;t see why a <i>working</i> code editor will do something you didn&#x27;t tell it to do.<p>Vim, Emacs, Nano, Sublime Text, Kate&#x2F;Kdevelop, Visual Studio&#x2F;Code, Atom, Brackets, Text Mate, Scintilla&#x2F;SciTE&#x2F;Geany&#x2F;etc, Programmers Notepad, CodeBlocks, Eclipse, and JetBrains all know not to mess with your tabs.<p>Rather than switch away from tabs so you can keep using a broken editor, the correct solution is to switch an editor that works. And if you configured your editor to replace tabs with spaces, and it doesn&#x27;t give you an option to handle Makefiles differently, then that&#x27;s a broken editor.
评论 #21815741 未加载
wyldfireover 5 years ago
&gt; MAKEFLAGS += --no-builtin-rules<p>&gt; This disables the bewildering array of built in rules ...<p>Oh, wow, I totally disagree. It&#x27;s good to be opinionated -- that is, unless you&#x27;re wrong. ;)<p>In all seriousness I find it terribly useful to quickly create simple Makefiles and follow idioms like CFLAGS&#x2F;CXXFLAGS&#x2F;LDFLAGS&#x2F;LDLIBS&#x2F;etc.
评论 #21818561 未加载
评论 #21815198 未加载
rwmjover 5 years ago
I wonder if anyone has ever generalized &quot;make&quot; so that it can operate on general dependencies and tactics rather than always on files? The sort of thing I mean is that you&#x27;d be able to write:<p><pre><code> url_exists(http:&#x2F;&#x2F;example.com&#x2F;file): file rsync file example.com:&#x2F;html </code></pre> I&#x27;ve had a few attempts at this (most recently in 2013: <a href="https:&#x2F;&#x2F;people.redhat.com&#x2F;~rjones&#x2F;goaljobs&#x2F;" rel="nofollow">https:&#x2F;&#x2F;people.redhat.com&#x2F;~rjones&#x2F;goaljobs&#x2F;</a>) but never really arrived at anything satisfactory.
评论 #21813299 未加载
评论 #21813171 未加载
评论 #21813157 未加载
评论 #21814085 未加载
bilekasover 5 years ago
$(CXX) -MM $(CPPFLAGS) $&lt; | sed &#x27;s,\($<i>\)\.o[ :]</i>,\1.o $@ : ,g&#x27; &gt; $@<p>My makefiles are beautiful... To me<p>And they&#x27;re sure as sh!t not wrong, or they wouldn&#x27;t work! :)
评论 #21812749 未加载
评论 #21813457 未加载
评论 #21812748 未加载
mvaliente2001over 5 years ago
Another trick, create a `help` target:<p><pre><code> help: ## Show this help @fgrep -h &quot;##&quot; $(MAKEFILE_LIST) | fgrep -v fgrep | sed -e &#x27;s&#x2F;\\$$&#x2F;&#x2F;&#x27; | sed -e &#x27;s&#x2F;##&#x2F;&#x2F;&#x27; foo: ## foo help # make help will print the names of the targets with # the help message following ## </code></pre> I don&#x27;t remember the source, I think I saw it in a stackoverflow question.
anon9001over 5 years ago
Experimental, but this exists: <a href="https:&#x2F;&#x2F;github.com&#x2F;mrtazz&#x2F;checkmake" rel="nofollow">https:&#x2F;&#x2F;github.com&#x2F;mrtazz&#x2F;checkmake</a>
KerrickStaleyover 5 years ago
My 2c on GNU Make is that you should not use it for new projects. There are better build systems, like Bazel, that are faster and produce consistent, reproducible results.<p>Make&#x27;s lack of hermeticity means that it&#x27;s easy to accidentally craft a Makefile where there are dependencies between targets that are not explicitly listed in the Makefile. When this happens, you can&#x27;t be sure that when you change an input file and run `make`, all the dependent targets will be rebuilt. This means you often have to `make clean; make` to make sure that things get correctly rebuilt.<p>Bazel also supports things like caching of artifacts so that when you build, switch branches, build, switch back, and build, it can re-use built artifacts from build #1 so that build #3 becomes a no-op. With remote caching, this can happen across computers and users; when user 1 builds at SHA 123, and then user 2 later checks out SHA 123 and does a build, user 2&#x27;s build will simply copy the cached artifacts over the network and do no local work. (There is some operational overhead to maintaining this shared cache however).<p>That said, guides like this are really helpful for maintaining and extending existing make-based build systems!
评论 #21819877 未加载
评论 #21821242 未加载
morelispover 5 years ago
&quot;Opinionated&quot; sure is right.<p>I love make, but a lot of this advice is targeted at being able to write &quot;better&quot; shell scripts in make. I don&#x27;t recommend it. If you find yourself writing a shell for loop, you probably want instead to build a list of targets. If you find yourself wanting complex shell variable preparation, you probably instead want target-specific variables. ONESHELL is a good way to accidentally build some invisible dependencies into a recipe, and make it difficult to use custom functions or canned recipes.<p>If you do find yourself really wanting a shell, you&#x27;ll probably also want &quot;advanced&quot; features like error traps, and you&#x27;ll probably want to work with tools like shellcheck (imo critical for any shell script longer than one pipeline). Both are thwarted by baking the invocations into your make recipe. And the recipe still looks great - probably better! - if you extract any logic into a separate script (which then also opens up further possibilities, like including that tool itself in a shell pipeline in a make recipe).
schnableover 5 years ago
I liked Make due to my perceived simplicity of it. Thanks to this article, I think I&#x27;ll just use Rake next time.
评论 #21815490 未加载
评论 #21813565 未加载
anticristiover 5 years ago
I hope I&#x27;m not carried away by the example, but I would definitely not build Docker images using make. A multi-stage Dockerfile can take care of building, testing, packing, etc. source code a lot more predictably than any amount of make, by using known images of the tools involved (e.g., node, webpack, etc.).<p>Moreover, Docker&#x27;s caching implicitly allows to declare dependencies with a lot fewer headaches than make. On the downside, it is all to easy to write something that busts Docker&#x27;s cache early in the build process, rendering `docker build` super-expensive.<p>So my opinionated tooling: docker, git (to extract project versions and inject them into the image), bash (to glue everything together). Everything else belongs <i>inside</i> the Docker build.
评论 #21817528 未加载
bobbyi_settvover 5 years ago
The problem with this:<p><pre><code> out&#x2F;image-id: $(shell find src -type f) </code></pre> is that if you delete a file from src (without also modifying or adding a file), make won&#x27;t consider image-id to need rebuilding
评论 #21819705 未加载
chrismorganover 5 years ago
Concerning creating directories:<p><pre><code> out&#x2F;foo: &gt; mkdir -p $(@D) &gt; … </code></pre> I prefer to use this pattern, which lets you have many fewer `mkdir -p …` lines in the make output when you use it a bunch of times:<p><pre><code> out&#x2F;foo: | out &gt; … out: &gt; mkdir &quot;$@&quot; </code></pre> (Here using the &gt; recipe prefix from the article, though you won’t find me doing that in real life—I like my tabs.)
评论 #21813233 未加载
knorkerover 5 years ago
So force GNU make and bash as shell?<p>No thanks.
Tyr42over 5 years ago
I 120% recommend MAKEFLAGS += --no-builtin-rules<p>If I could rewrite history, I would make those builtin rules nicely, explicitly, importable. No magic when I&#x27;m trying to teach make, please.
Porthos9Kover 5 years ago
I&#x27;m glad the author specified that this is mainly for GNU make, because I suspect that if I tried most of these using OpenBSD make I would have a bad time.
einpoklumover 5 years ago
To be honest - I don&#x27;t believe I&#x27;ve had to write a single Makefile since I started using CMake.<p>At most I&#x27;ve had to tinker with existing ones - in which case I wasn&#x27;t motivated to make them more elegant, as opposed to replacing them with CMake generation.
dima55over 5 years ago
FYI, there&#x27;s a patched Make providing (among other things) an interactive debugger:<p><a href="https:&#x2F;&#x2F;github.com&#x2F;rocky&#x2F;remake" rel="nofollow">https:&#x2F;&#x2F;github.com&#x2F;rocky&#x2F;remake</a>
评论 #21820786 未加载
donpdonpover 5 years ago
.ONESHELL is a huge help, and replacing the recipe tab is very interesting. Great post!
jupedover 5 years ago
More derangements to undo when you want it to build on a different system.
jrockwayover 5 years ago
I think I have decided that I&#x27;m done with Makefiles. They are very tempting, because they follow naturally from interactive exploration. You see yourself writing a command a lot, and think &quot;I&#x27;ll just paste that into a Makefile&quot;. Now you don&#x27;t have to remember the command anymore.<p>But the problem is that building software is a lot more than just running a bunch of commands. The commands represent solutions to problems, but if the solutions aren&#x27;t good enough, you just make more problems for yourself.<p>The biggest problems I&#x27;ve had with Makefile-based builds are getting everyone using the repository the right version of dependencies, and incrementality. A project I did at work involved protos, and it was great when I was the only person working on it. I had a Makefile that generated them for Go and Typescript (gRPC-Web) and usually an incremental edit to a proto file and a re-run of the Makefile resulted in an incremental update to the generated protos. Perfect. Then other people started working on the project, and sometimes a simple proto change would regenerate the entire proto. Sometimes the protos would compile, but not actually work. The problem was that there is a hidden dependency of the proto compiler, protoc-gen-(go|ts), and the language-specific proto API version that controls the output of the proto compilation process. Make has no real way to say &quot;when I say protoc, I mean protoc from this .tar.gz file with SHA256:abc123def456...&quot; You just kind of yolo it. Yolo-ing it works fine for one person; even if your dev machine gets destroyed, you&#x27;ll probably get it working again in a day or two. As soon as you have four people working on it, every hidden dependency destroys a day of productivity. I just don&#x27;t think it&#x27;s a good idea.<p>Meanwhile, you can see how well automated dependency management systems work. Things like npm and go modules pretty much always deliver the right version of dependencies to you. With go, the compiler even updates the project definition for you, so you don&#x27;t even have to manage files. It just works. This is what we should be aiming for for everything.<p>I have also not had much luck with incremental builds in make. Some projects have a really good set of Makefiles that usually results in an edit resulting in a changed binary. Some don&#x27;t! You make a change, try out your binary, and see that it decided to cache something that isn&#x27;t cacheable. How do you debug it? Blow away the cache and wait 20 minutes for a full build. Correctness or speed, choose any 1. I had this problem all the time when I worked on a buildroot project, probably because I never understood what the build system was doing. &quot;Oh yeah, just clean out those dep files.&quot; What even are the dep files? I never understood how to make it work for me, even after asking questions and getting little pieces of wisdom that seemed a lot like cargo-culting or religion. Nobody could ever point to &quot;here&#x27;s the function that computes the dependency graph&quot; and &quot;here&#x27;s the function that schedules commands to use all your CPUs&quot;. The reason is... because it lives in many different modules that don&#x27;t know about each other. (Some in make itself, some in makefiles, some in the jobs make runs... it&#x27;s a mess.)<p>Meanwhile, I&#x27;ve also worked on projects that use a full build system that tracks every dependency required to build every input. You start it up, and it uses 300M of RAM to build a full graph. When it&#x27;s done it maxes out all your CPUs until you have a binary. You change one file, and 100% of the time, it just builds what depended on that file. You run it in your CI environment and it builds and the tests pass, the first time.<p>I am really tired of not having that. I started using Bazel for all my personal projects that involve protocol buffers or have files in more than one language. The setup is intense, watching your CPU stress the neighborhood power grid as it builds the proto compiler from scratch is surprising, but once it starts working, it keeps working. There are no magic incantations. The SHA256 of everything you depend on is versioned in the repository. It works with traditional go tools like goimports and gopls. Someone can join your project and contribute code by only installing one piece of software and cloning your repository. It&#x27;s the way of the future. Makefiles got us far, but I&#x27;m done. I am tired of debugging builds. I am tired of helping people install software. &quot;bazel build ...&quot; and get your work done.
Annatarover 5 years ago
This is chalk full of bad advice like &quot;use GNU make and bash&quot; that it&#x27;s obvious to me it was written by someone who only knows GNU&#x2F;Linux and doesn&#x27;t know UNIX (or much else). Blind leading the blind again. Don&#x27;t write GNU-specific constructs unless you have no other choice; use ksh instead of bash, it&#x27;s portable and a standard and has been long before the GNU&#x2F;Linux abomination. What nonsense I get to read...
评论 #21830097 未加载
floor_over 5 years ago
If you&#x27;re doing anything other than a unity build (single compilation unit) you&#x27;re doing it wrong.