<?xml version="1.0" encoding="utf-8"?>
<?xml-stylesheet type="text/xsl" href="../assets/xml/rss.xsl" media="all"?><rss version="2.0" xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:atom="http://www.w3.org/2005/Atom"><channel><title>chris' random ramblings (Posts about make)</title><link>https://atlee.ca/</link><description></description><atom:link href="https://atlee.ca/categories/make.xml" rel="self" type="application/rss+xml"></atom:link><language>en</language><lastBuildDate>Sat, 26 Sep 2026 23:30:13 GMT</lastBuildDate><generator>Nikola (getnikola.com)</generator><docs>http://blogs.law.harvard.edu/tech/rss</docs><item><title>need: for when you just need simple build dependencies</title><link>https://atlee.ca/posts/need-build-dependencies/</link><dc:creator>chris</dc:creator><description>&lt;p&gt;Sometimes I need to express a simple dependency between files. For example, if I have a directory full of images, I want to generate a thumbnail for each image, and only regenerate the thumbnails that are out of date.&lt;/p&gt;
&lt;p&gt;&lt;a href="https://github.com/casey/just"&gt;&lt;code&gt;just&lt;/code&gt;&lt;/a&gt; is great as a generic command runner. I frequently set up a &lt;code&gt;justfile&lt;/code&gt; with common targets like &lt;code&gt;test&lt;/code&gt;, &lt;code&gt;format&lt;/code&gt;, and &lt;code&gt;clean&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;But &lt;code&gt;just&lt;/code&gt; doesn't track file dependencies. I could write a recipe that loops over all the images:&lt;/p&gt;
&lt;div class="code"&gt;&lt;pre class="code literal-block"&gt;&lt;span class="nf"&gt;thumbs&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="k"&gt;for&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;f&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;in&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;images/*.jpg&lt;span class="p"&gt;;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;do&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;magick&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$f&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;-thumbnail&lt;span class="w"&gt; &lt;/span&gt;200x200&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"thumbs/&lt;/span&gt;&lt;span class="si"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;f&lt;/span&gt;&lt;span class="p"&gt;#images/&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;done&lt;/span&gt;

&lt;span class="nf"&gt;clean&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;rm&lt;span class="w"&gt; &lt;/span&gt;-rf&lt;span class="w"&gt; &lt;/span&gt;thumbs
&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;This regenerates every thumbnail each time I run &lt;code&gt;just thumbs&lt;/code&gt;, even if most of them haven't changed.&lt;/p&gt;
&lt;h3 id="why-not-make"&gt;Why not Make?&lt;/h3&gt;
&lt;p&gt;Make can track those file dependencies:&lt;/p&gt;
&lt;div class="code"&gt;&lt;pre class="code literal-block"&gt;&lt;span class="nf"&gt;.PHONY&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;all&lt;/span&gt;-&lt;span class="n"&gt;thumbs&lt;/span&gt; &lt;span class="n"&gt;clean&lt;/span&gt;
&lt;span class="nf"&gt;all-thumbs&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;$(&lt;/span&gt;&lt;span class="nv"&gt;patsubst&lt;/span&gt; &lt;span class="nv"&gt;images&lt;/span&gt;/%&lt;span class="nv"&gt;.jpg&lt;/span&gt;,&lt;span class="nv"&gt;thumbs&lt;/span&gt;/%&lt;span class="nv"&gt;.jpg&lt;/span&gt;,&lt;span class="k"&gt;$(&lt;/span&gt;&lt;span class="nv"&gt;wildcard&lt;/span&gt; &lt;span class="nv"&gt;images&lt;/span&gt;/*&lt;span class="nv"&gt;.jpg&lt;/span&gt;&lt;span class="k"&gt;))&lt;/span&gt;

&lt;span class="nf"&gt;thumbs/%.jpg&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;images&lt;/span&gt;/%.&lt;span class="n"&gt;jpg&lt;/span&gt; &lt;span class="p"&gt;|&lt;/span&gt; &lt;span class="n"&gt;thumbs&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;magick&lt;span class="w"&gt; &lt;/span&gt;$&amp;lt;&lt;span class="w"&gt; &lt;/span&gt;-thumbnail&lt;span class="w"&gt; &lt;/span&gt;200x200&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;$@&lt;/span&gt;

&lt;span class="nf"&gt;thumbs&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;mkdir&lt;span class="w"&gt; &lt;/span&gt;-p&lt;span class="w"&gt; &lt;/span&gt;thumbs

&lt;span class="nf"&gt;clean&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;rm&lt;span class="w"&gt; &lt;/span&gt;-rf&lt;span class="w"&gt; &lt;/span&gt;thumbs
&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;I've always found Make annoying for this kind of thing.&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;You have to mark command-like targets such as &lt;code&gt;clean&lt;/code&gt; and &lt;code&gt;all-thumbs&lt;/code&gt; as phony, or Make may mistake a file with that name for an up-to-date target.&lt;/li&gt;
&lt;li&gt;You have to create the output directory yourself.&lt;/li&gt;
&lt;li&gt;Recipes need literal tabs. I've lost far too much time because a Makefile was missing one.&lt;/li&gt;
&lt;/ol&gt;
&lt;h3 id="why-you-need-need"&gt;Why you need &lt;code&gt;need&lt;/code&gt;&lt;/h3&gt;
&lt;p&gt;I wrote &lt;a href="https://github.com/catlee/need"&gt;&lt;code&gt;need&lt;/code&gt;&lt;/a&gt; to address these annoyances. Need only handles file dependencies. It doesn't try to be a command runner.&lt;/p&gt;
&lt;p&gt;In &lt;code&gt;need&lt;/code&gt;, I express dependencies between files and give it a recipe for building the outputs from the inputs. &lt;code&gt;need&lt;/code&gt; compares file contents, so changing an input makes its outputs stale even if the timestamps are misleading. It also creates output directories automatically.&lt;/p&gt;
&lt;p&gt;So I keep my &lt;code&gt;justfile&lt;/code&gt; for running commands and add a &lt;code&gt;needfile&lt;/code&gt; to express file dependencies. The thumbnail example becomes:&lt;/p&gt;
&lt;div class="code"&gt;&lt;pre class="code literal-block"&gt;&lt;span class="c"&gt;# justfile&lt;/span&gt;
&lt;span class="nf"&gt;thumbs&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;need&lt;span class="w"&gt; &lt;/span&gt;get&lt;span class="w"&gt; &lt;/span&gt;-j&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s1"&gt;'thumbs/%.jpg: images/%.jpg'&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;--&lt;span class="w"&gt; &lt;/span&gt;images/*.jpg

&lt;span class="nf"&gt;clean&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;need&lt;span class="w"&gt; &lt;/span&gt;clean
&lt;/pre&gt;&lt;/div&gt;

&lt;div class="code"&gt;&lt;pre class="code literal-block"&gt;&lt;span class="c"&gt;# needfile&lt;/span&gt;
&lt;span class="nf"&gt;thumbs/%.jpg&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;images&lt;/span&gt;/%.&lt;span class="n"&gt;jpg&lt;/span&gt;
&lt;span class="w"&gt;  &lt;/span&gt;magick&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;{{&lt;/span&gt;&lt;span class="k"&gt;in&lt;/span&gt;&lt;span class="o"&gt;}}&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;-thumbnail&lt;span class="w"&gt; &lt;/span&gt;200x200&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;{{&lt;/span&gt;out&lt;span class="o"&gt;}}&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;Here, the shell expands &lt;code&gt;images/*.jpg&lt;/code&gt; into the input filenames. &lt;code&gt;need get&lt;/code&gt; maps each &lt;code&gt;images/name.jpg&lt;/code&gt; to &lt;code&gt;thumbs/name.jpg&lt;/code&gt;, then builds those targets. The &lt;code&gt;-j&lt;/code&gt; option lets independent thumbnails build in parallel. If nothing changed, Need has nothing to do.&lt;/p&gt;
&lt;p&gt;Need can also publish outputs atomically, so a failed recipe doesn't leave a partially written thumbnail in place. Its dependency signatures use file contents, not timestamps alone. For a bigger example, it can use compiler depfiles to track headers discovered while compiling.&lt;/p&gt;
&lt;p&gt;I still use &lt;code&gt;just&lt;/code&gt; for commands like &lt;code&gt;test&lt;/code&gt; and &lt;code&gt;format&lt;/code&gt;. &lt;code&gt;need&lt;/code&gt; handles the file artifacts those commands depend on.&lt;/p&gt;
&lt;p&gt;&lt;a href="https://github.com/catlee/need"&gt;need on GitHub&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;&lt;a href="https://crates.io/crates/need-tool"&gt;need-tool on crates.io&lt;/a&gt;&lt;/p&gt;</description><category>build tools</category><category>make</category><category>need</category><guid>https://atlee.ca/posts/need-build-dependencies/</guid><pubDate>Sat, 26 Sep 2026 21:57:14 GMT</pubDate></item></channel></rss>