Makefile.md - Possibly Use(ful|less) Polyglot Synthesis of Makefile and Markdown
This is a short story of how my coworker Jace sniped me into creating a polyglot Makefile-Markdown file.
Never have I seen an article step through the process of creating a polyglot file. Most are small trivial examples, or glance over the difficulties you may run into. While there are plenty noteworthy ployglot projects1 in the wild, this Makefile.md experiment appears to be a public first. The idea of making Markdown executable or adding human-readable elements to Makefiles appears to be questionably useful, as there are a few projects that have tried to achieve this merger in more "full solution" ways already234!
I'm a simple person though, who desires simple, beautiful, synergistic solutions.
On Friday (this article is posted on a Sunday), trudging through the Playwright mines (a test framework typically for web applications), my coworker Jace said something that immediately triggered me to do what this article details:
As soon as I opened our project's Makefile, it became crystal clear just how compatible Markdown could be with Makefiles, partially thanks to how Jace wrote it:
Oh my god, a Makefile comment looks just like a Markdown header!
To take advantage of GitHub rendering README.md
by default, I deleted our
original one and symlinked the Makefile
as the README.md
to see how it'd
render with zero changes.
rm README.md
ln -s Makefile README.md
git add README.md
git commit -m "let's go"
git push
The result is interesting! But not digestible. The universe. demands. better.
We need to now take in and reflect why this works in the first place. What is making this successfully work? What are the fundamentals at play?
Let's start with the statements:
# INSTALL
is a comment in Makefile.# INSTALL
is a header in Markdown.- A comment in a no-op in Makefile.
- A header is styling in Markdown.
- Thus
#
is essentially a "stylistic no-op", doing no meaningful computation in Makefile but styling the document as Markdown.
This idea is pivotal in discovering new "styled-execution syntaxes". Because Makefiles do essential computational work, we must find other "no-op" operations that allow us to "pull through" Markdown syntax elements without affecting the intended Makefile instructions!
From here on, I would have Makefile and Markdown documentation open to look for everything and anything that helped move us toward the goal of rendering more Markdown styles without affecting the Makefile scripts.
The next styling I decided to do was, unintuitively, code-blocks, because
we had ifdef
s in our Makefile that I couldn't imagine looking any better
than as regular code. Using the reasoning above I was quickly able to find a
working combination:
(Not looking too bad already!)
```sh :
echo "hello world"
#\
```
There is one issue with this: it always leaves a trailing #\
. I was
happy though and left it at that. Code-blocks were particularly hard because
Markdown demanded that the 3 backticks stood alone on their own line, but
Makefile was not happy, always complaining about a missing separator, but we
cannot use a separator due to what we just said.
To get around this, we are lucky Makefile supports what I'm going to call
"line-folding", where \
is used to collapse two lines. See where we're going
with this? We collapse a comment #
and the 3 backticks with \
to keep
Makefile happy while rendering the code-block. Phew!
Another noteworthy, interesting addition, is the abuse of Makefile's separator :
to start the code-block. In Makefile's eyes, we are defining a no-op recipe,
which reads "for the recipe \
``sh`, do nothing". Perfect for us! In the
the Markdown rendering, it's completely hidden.
Hold on minute... Whaaa, thinking more about :
leads us to discover another amazing
"stylistic no-op"! It is the dual of #
!
#
ignores everything after the symbol in Makefile.:
ignores everything before the symbol in Makefile (if no dependencies).
Ho boy. Now we're really cooking.
With this knowledge I began finding other "stylistic no-ops" much more quickly.
The mental model was proving itself effective. Then I tackled plain text
paragraphs finally, and I discovered what would be the final "stylistic no-op"
of the evening: [](:)
.
It's kinda cute.
Without this combination, all my plain-text would have a colon :
on the end.
Using Markdown links with no text, we could effectively hide this colon!
Running the Makefile though, it complains about )
being a missing dependency.
No problem, we will make this a no-op recipe! ):
, done!
With that, we could define any number of useful "no-op" recipes at the bottom of the Makefile that supports our Markdown styling.
The final working Makefile.md
after 2 hours, rendered in both code and
visual to see their simulatenous beauty:
Will I seriously use this? Maybe not, but it serves as some really good inspiration for building something very close to this, or trying it in other languages. Maybe there's a combination of languages out there with better synergy?
If you've got any ideas on how to improve the current Makefile-Markdown polyglot vocabulary presented here, please let me know! lee@zoo.dev or just comment on whatever platform this gets posted to. I'll find you.
Footnotes
-
https://codegolf.stackexchange.com/questions/102370/add-a-language-to-a-polyglot?answertab=createdasc#tab-top ↩
-
https://medium.com/@hirokistring/integrating-readme-md-and-makefile-with-makedown-10995152f069 ↩
-
https://web.archive.org/web/20200515171555/https://agdr.org/2020/05/14/Polyglot-Makefiles.html ↩