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:

    jace and me talking

    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:

    design studio's Makefile

    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 immediate result with no changes

    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 ifdefs 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:

    working code-blocks

    (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:

    final file markdown

    final file makefile

    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

    1. https://codegolf.stackexchange.com/questions/102370/add-a-language-to-a-polyglot?answertab=createdasc#tab-top

    2. https://medium.com/@hirokistring/integrating-readme-md-and-makefile-with-makedown-10995152f069

    3. https://github.com/OmenApps/PolyglotMarkdown

    4. https://web.archive.org/web/20200515171555/https://agdr.org/2020/05/14/Polyglot-Makefiles.html