-
Notifications
You must be signed in to change notification settings - Fork 0
/
3.html
176 lines (170 loc) · 11.5 KB
/
3.html
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
<html lang="en">
<head>
<meta content="text/html;charset=utf-8" http-equiv="Content-Type">
<meta charset="utf-8">
<title>
Markdown and makefiles
</title>
<meta name="author" content="Chrisman Brown">
<meta name="viewport" content="width=device-width, initial-scale=1">
<script type="module" src="scripts/themes.js"></script>
<link rel="alternate" type="application/rss+xml" href="/rss.xml" title="chrisman"> <link rel="stylesheet" href="styles/reset.css"> <link rel="stylesheet" href="styles/main.css"> <link rel="stylesheet" href="styles/code.css"> <link rel="index" href="/list" /> <link rel="prev" href="/2"> <link rel="next" href="/4">
</head>
<body>
<header>
👩💻 chrismanbrown.gitlab.io
<nav>
<a href="list.html">Blog</a> | <a href="about.html">About</a> | <a href="contact.html">Contact</a> | <a href="feeds.html">Feeds</a> | <a href="hire.html">Work with me</a>
</nav>
</header>
<main>
<h1 class="title">
Markdown and makefiles
</h1>
<p>
using Make to make websites
</p>
<p>
2020-01-01
</p>
<p>I wrote a makefile!</p>
<h2 id="there-has-always-been-a-make">There has always been a make</h2>
<p>I’ve always known that Make is a thing.</p>
<p>Rather, I know that Make has always been a thing.</p>
<p>It was written in 1976 which, at the time of this writing, makes it a 43 year old tool, an older tool than me. At the time of this writing.</p>
<p>Make is a relic from the olden times, from the days of yore. A standby of C/C++ hackers. Something that greybeard wizards mutter and chatter and argue about as they craft and maintain their own special makefiles by hand.</p>
<p>But my interaction with <code>make</code> has always been limited to merely reciting that age old incantation: <code>./configure && make && make install</code> without ever delving too much into what it is and what it does. I knew that it’s something you use when building code from source, but that was the extent of my knowledge.</p>
<p>I’ve always appreciated the elegance of the basic formula:</p>
<pre class="make"><code>target: prerequisite
recipe</code></pre>
<p>Or, to use a slightly different vocabulary:</p>
<pre class="make"><code>task: dependency
code</code></pre>
<p>Whichever you prefer, really:</p>
<pre class="make"><code>target: source
command</code></pre>
<p>Very plain, very organized. Simple. I’ve heard people say that a well written Makefile is like documentation for a project, and I can understand a little bit why they would say that. It’s like reading a cookbook: a list of dishes, ingredients, and how to build those dishes from those ingredients:</p>
<pre class="make"><code>dish: ingredient
recipe</code></pre>
<h2 id="make-is-a-build-tool">Make is a build tool</h2>
<p>Make is a build tool, and a task runner.</p>
<p>You’ve encountered such things in your travels:</p>
<ul>
<li><p><code>ant</code>, <code>maven</code>, and/or <code>gradle</code> if you’re a Java kind of person.</p></li>
<li><p>If you’re a java <em>script</em> kind of person, then you’ve written npm scripts in your <code>package.json</code> to run your tests or start your server; and you’ve used grunt or gulp, or webpack.</p></li>
<li><p><code>rake</code>, if you’re a ruby/rails kind of person. (Which was my first attempt at using a Make-like kind of build tool.)</p></li>
</ul>
<p>It builds stuff and automates tasks.</p>
<p>Advantages that <code>make</code> has over other solutions like npm scripts is that:</p>
<ol type="1">
<li><p>It only builds a target if it needs to. (If the sources have been updated.)</p></li>
<li><p>No extra dependencies: it’s pre-installed on all *nix platforms, including macos.</p></li>
<li><p>It can wrap existing build tools! You can have <code>make</code> install your node_modules if they’re missing (as long as there is a package.json), run a bash script, start your server, etc.</p></li>
</ol>
<h2 id="i-wrote-a-makefile">I wrote a makefile</h2>
<p>So I’ve always wanted an excuse to learn <code>make</code>.</p>
<p>And when starting this blog, I needed a way to create HTML files from markdown source files. I certainly didn’t want to install webpack, or grunt or gulp, or anything like that. Or even npm. Minimalism is the name of our game. I don’t want any other dependencies.</p>
<p>And so, excuse in hand, I got to cracking away at it.</p>
<p>It took me a minute to learn <del>all</del> enough of Make’s magic variables, macros, and built-in functions. What <em>really</em> took me a minute to wrap my head around was how I had to adhere to, and work around, the basic <code>task > dependency > recipe</code> formula. There’s no flexibility here. Declarative or bust.</p>
<p>For example: I was trying to write a recipe that targets a bunch of HTML files. The problem is that I didn’t <em>have</em> those HTML files. Nor did I necessarily know what they <em>are</em>. I didn’t have a list of my targets, and that was a problem.</p>
<p>What I did have is a list of prerequisites: a bunch of Markdown files.</p>
<p>That is, given Make’s strict syntax requirements, you can’t just start with a bunch of ingredients and then simply build something from them. No, you must have the target <em>first</em>. Something to depend on those prerequisites. That’s your entry point.</p>
<p>The trick was to do this:</p>
<ol type="1">
<li><p>Get a list of all the prerequisites. Easy enough. E.g. <code>find src/posts -name "*.md"</code>. And then,</p></li>
<li><p>Use make’s super weird pattern substitution function to map that list of markdown files to a list of the HTML files I <em>wish I had</em> (my list of targets).</p></li>
<li><p>And finally, assign that list to a variable, which I can then use as a list of targets.</p></li>
</ol>
<p>Being declarative is hard work some times.</p>
<p>That whole part looks like this:</p>
<pre class="make"><code>markdowns := $(shell find src/posts -type f)
htmls := $(patsubst src/posts/%.md, posts/%.html, $(markdowns))</code></pre>
<p>Here’s another way to do it using the built-in <code>wildcard</code> macro and a different, built-in substitution short hand:</p>
<pre class="make"><code>markdowns=$(wildcard src/posts/*.md)
htmls=$(markdowns:src/posts/%.md=posts/%.html)</code></pre>
<p>Those targets (<code>htmls</code>) are strings that have the format of <code>posts/somefilename.html</code>.</p>
<p>So now, targets in hand, we get to do this:</p>
<ol type="1">
<li><p>Create some new task that has dependencies equal to the list of htmls. We’ll call it “posts.”</p></li>
<li><p>Use pattern matching to create a task for any file matching <code>posts/*.html</code>, which just happens to be the format of all htmls! This target has a dependency of its corresponding markdown file, and its recipe is the pandoc conversion.</p></li>
</ol>
<p>That part looks like this:</p>
<pre class="make"><code>posts: $(htmls)
posts/%.html: src/posts/%.md
pandoc --options --more-options -o $@ $<</code></pre>
<p>Now I can call <code>make posts</code> from the command line, and every markdown file in <code>/src/posts</code> will generate a new HTML file in <code>/posts</code>! Assuming the following, of course:</p>
<ol type="1">
<li><p>There’s not already an existing HTML file for that markdown file, AND</p></li>
<li><p>the HTML file is newer than the markdown file.</p></li>
</ol>
<p>That is, it will only create the HTML file if it needs to: if it is missing, or if it is out of date.</p>
<p>The makefile in its entirety currently looks like this:</p>
<pre class="make"><code>markdowns := $(shell find src/posts -type f)
htmls := $(patsubst src/posts/%.md, posts/%.html, $(markdowns))
all: index posts
index: index.html
index.html: src/index.md
pandoc -s -c styles/reset.css -c styles/main.css -c styles/index.css -o $@ $<
posts: $(htmls)
posts/%.html: src/posts/%.md
pandoc -s --toc -c ../styles/reset.css -c ../styles/main.css -o $@ $<</code></pre>
<p>(The above snippet may be out of date even as soon as the publication of this post, but the <a href="https://github.com/chrisman/chrismanbrown.gitlab.io/blob/master/Makefile">current Makefile I’m using for this site can always be found on github</a>.)</p>
<p>Other parts I didn’t go over are these:</p>
<ul>
<li>magic variables / “macros”
<ul>
<li><code>$@</code>: the target</li>
<li><code>@<</code>: the first dependency</li>
<li><code>@^</code>: (not shown here) ALL dependencies</li>
</ul></li>
<li>assignment
<ul>
<li><code>=</code>: lazy assignment</li>
<li><code>:=</code>: immediate assignment</li>
</ul></li>
<li>pattern matching and substitution
<ul>
<li><code>%</code>: wildcard. as opposed to <code>*</code>, like the rest of the civilized world uses</li>
<li><code>patsubst</code>: a function that takes a from pattern, a to pattern, and things to do that pattern substitution on.</li>
<li>other macros like <code>shell</code> and <code>wildcard</code></li>
</ul></li>
</ul>
<p>Anyway, that’s my first experience making any kind of a makefile. It’s not fantastic. It’s not easy, but it is simple. And it’s ubiquitous and transferable.</p>
<p>And now in the meantime I can <code>make index</code> to create the index page, or <code>make posts</code> to create posts, or just <code>make</code> to make anything and everything that needs making.</p>
<h2 id="conclusion">Conclusion</h2>
<p>So there. I wrote a makefile.</p>
<p>I still have some TODOs and some nitpicks. For example:</p>
<ol type="1">
<li><p>I don’t have a great way at the moment, because of my github workflow, to incorporate deployment into my makefile the way I could if I was just <code>scp</code>ing these files to a remote server.</p></li>
<li><p>I’d also like to figure out a way to not have to have <code>index</code> and <code>posts</code> be separate targets. That’d mean either learning how to manage subdirectories better in make (complete with relative paths for things that need them, like the stylesheets), or basically removing all directory structure and just dumping everything into one root folder. That’d make building files super simple, but it would also result in a messy, untidy pile of files, which is not something I find delightful.</p></li>
</ol>
<h2 id="resources">Resources</h2>
<ol type="1">
<li>Links</li>
<li>Glossary</li>
</ol>
<h3 id="links">Links</h3>
<ul>
<li><a href="https://bost.ocks.org/mike/make/" class="uri">https://bost.ocks.org/mike/make/</a></li>
<li><a href="https://www.olioapps.com/blog/the-lost-art-of-the-makefile/" class="uri">https://www.olioapps.com/blog/the-lost-art-of-the-makefile/</a></li>
<li><a href="https://blog.mindlessness.life/2019/11/17/the-language-agnostic-all-purpose-incredible-makefile.html" class="uri">https://blog.mindlessness.life/2019/11/17/the-language-agnostic-all-purpose-incredible-makefile.html</a></li>
<li><a href="https://jblevins.org/log/markdown-makefiles" class="uri">https://jblevins.org/log/markdown-makefiles</a></li>
</ul>
<h3 id="glossary">Glossary</h3>
<dl>
<dt>Make</dt>
<dd>Often refers to GNU Make. An ancient, language agnostic build tool which defines tasks, dependencies, and recipes. Used by C/C++ hackers of old. Not a <em>great</em> tool, but an extremely useful one, and installed everywhere, much like vim. An example of the worse-is-better?
</dd>
<dt>Pandoc</dt>
<dd>Converts documents from one format/markup to another. Supports a large variety of formats: <a href="https://pandoc.org/">pandoc.org</a>
</dd>
</dl>
</main>
<footer>
<a href="2.html">« older</a> | <a href="3.html">2020-01-01</a> | <a href="4.html">newer »</a>
<nav>
<a href="list.html">Blog</a> | <a href="about.html">About</a> | <a href="contact.html">Contact</a> | <a href="feeds.html">Feeds</a> | <a href="hire.html">Work with me</a>
</nav>
</footer>
</body>
</html>