Anyone who has ever dabbled in HTML is probably aware that to make even a basic page you need a lot of boilerplate: The tags are lengthy and getting even just the header set up requires multiple lines of code. Engineers, however, are extremely lazy: If you present them with a problem that is mere busy work, they will rather invest multiple hours into an optimized and streamlined process instead of taking slightly longer to write the boilerplate. All this was a roundabout way saying that I wanted to find a nice and easy way to concentrate on actually writing for my blog, and not having to create so much boilerplate.
If you've ever been to a GitHub or GitLab repository, or managed a few
yourself, you might have noticed those mysterious README.md
files. the md
stands for Markdown, a markup language designed to quickly format texts without
all the boilerplate from HTML. Its extreme simplicity allows for fast typing,
however, you are quite restricted in styling your files. For example, you can't
make index tabs, put background images into your pages or stylize the text by
exchanging fonts mid text or coloring it. It is good enough for READMEs or
other texts that don't require a lot of stylization.
Markdown is in fact the main language in which these pages are written, however, it is not HTML and thus (most likely) not readable by your web browser, and I also lose a lot of the amenities aforementioned. If I wanted to create a good looking blog though, I needed to find some way to turn my Markdown into HTML.
As standards go, Markdown is extremely small, featuring only a few formatting
options. This includes #
, ##
and ###
for headers, **bold**
,
*italics*
and ~~strike through~~
among other things. I could have written
my own script turning Markdown into HTML, but again, engineers are lazy. Lucky
for me, there was a tool: It's called markdown
. It takes as an input a
Markdown text and prints out the equivalent HTML code. That's the main body
taken care of, but it doesn't generate the rest of the boilerplate html code
like the header, or the <body>
and <html>
tags. Sadly, neither Markdown the
language nor markdown
the tool had any options for including one file in
another. In the spirit of eliminating boilerplate though, I was determined.
Fellow C programmers might know about the glorious C Preprocessor. Its central
role is in eliminating a lot of boilerplate by declaring functions that another
file might want to use. The best thing about it is that it doesn't really care
what the actual contents of the source files are. It only cares about
preprocessor directives placed inside that file. The plan was to include
templates by using the appropriate directive and passing it through the
preprocessor, however, there were always some preceding lines of the form
# n "some text"...
. Aside from just being weird to see, in Markdown, a
# text
is a Level 1 heading, not good.
So I was faced with a choice: Sanitize the output, meaning I get rid of any erroneous lines, or I could write my own preprocessor. While the latter would saved me from this awful rabbit hole, the former one sounded more fun, and I plain liked the idea of using the C preprocessor in cursed ways nobody intended to. It would also allow me to do more advanced things, like replacing any occurrence of a word with a Wikipedia link to that word, or a glossary link. Sadly, using the C preprocessor had proven to be overkill. The only use I could find was using it in conjunction with a macro to get the title of the page into the header of the HTML code.
Finally, to put all the pieces together, I use the tool Make from the Free Software Foundation, Inc. to splice together the head, body and foot, construct each webpage from a few boilerplate files and the Markdown file that contains the actual article, and then output a single HTML file with everything configured correctly. Best of all, if anything changes about my setup it automatically rebuilds any pages that need to be built.
Make was originally designed to have easily expandable code projects, by defining so called Targets that you could tell Make to run, which means it looks at the source code of the target and all its dependencies, and if any changes were made since the last time that target was built, it builds the file again. Long story short: It lets software developers focus on just creating their software and not have to worry about how to compile it into a product. And if you think about it, this website here is the same right? All I want to worry about is just making a website, but there is so much boilerplate to deal with, so I just use Make to deal with that for me. But that comfort came with a price...
By far the most cursed line of code is in this Makefile for the project. That
line creates the navigation bar at the top of the website. it looks something
like this:
1. echo
2. $(foreach navgroup,$(NAV_FOLDERS),
3. "<div class="dropdown">
4. <button class="dropbtn">$(shell head pages/$(navgroup)/.page-info -n
1)</button>
5. <div class="dropdown-content">$(foreach file,$(shell find pages/$(navgroup)
-name "*.md"),
6. <a href=\"https://keheck.github.io/$(navgroup)/$(notdir
$(basename $(file)))\">$(shell head -n 1 $(file))</a>)
7. </div></div>")
8. >> $(BOILERPLATE_DIR)/navbar.html
The line has been broken up into several for your convenience, and line numbers
have been added to the front to reference down below in the explanation. In
reality, all this is one line. Don't worry, I will go over what each line does
one by one:
echo
: All it does is output the result of whatever commands follow it, and
using a structure seen in 8.$(foreach navgroup,$(NAV_FOLDERS),<something>)
: Here, we iterate, or loop,
over every entry navgroup
in a list called NAV_FOLDERS
, and do
<something>
with them (that something
is what happens between 3. and 7.).
Each navgroup here represents a category of articles, like "This Page", which
the article you're reading right now is a part of.<div class="dropdown">
: Here we create a container that will hold all the
relevant elements for the category. This is a so called "opening tag", which is
part of HTML and represents a single element. Elements can contain other
elements, so we also need to mark the end of this container. That we do with
the second </div>
in 7.<button class="dropbtn">$(shell head pages/$(navgroup)/.page-info -n
1)</button>
: This part creates a button element that will be used by the user
to open the menu by overing over it with their cursor. The $(shell head
pages/$(navgroup)/.page-info -n 1)
command tells Make (The utility used to
build the webpages) to interpret the rest of the parentheses as a shell command
(aka a terminal command), and replace it with whatever got reported back by
that command. Here, the head
command, with this specific configuration,
returns the first line of the file it's given, here it's a .page-info
file (a
file format I made up) located in the navgroup
directory under pages
. It
simply contains the name of the current category.<div class="dropdown-content">$(foreach file,$(shell find pages/$(navgroup)
-name "*.md"),<something>)
: Here we open another group of elements by looping
over this time the files contained in the pages/\<navgroup\>
subdirectory,
and doing <something>
with them. That something is located in 6. The end of
this container is the first </div>
in 7. You'll notice that we're using
shell
again, this time to find every file in the current directory we're
looking at ending in .md
, which are the Markdown files we want to turn into
our webpages.<a href=\"https://keheck.github.io/$(navgroup)/$(notdir
$(basename $(file)))\">$(shell head -n 1 $(file))</a>)
: Finally, we create a
hyperlink for each file we're looking at, by taking its navgroup
and sticking
the name of the file after that. Since the file
variable we're using to loop
over the files still has the extension and the path to that file, we use
basename
and notdir
, which removes the file extension and the directory
path respectively, leaving only the pure file name. Finally, we get the first
line of the file, which is just the title of the article, and use that as the
text to display to the user, because it just makes sense, unlike anything else
outlined here.</div></div>
: This just closes all the open containers (the one holding
the) links and the one containing the entire navbar category>> $(BOILERPLATE_DIR)/navbar.html
: Finally, after all this madness, we
tell our echo command to append everything that resulted from this insanity
into the navbar file, so that we may include it into every article for us.To those asking the question: Yes, this is absolutely horrendous. Yes, I could have made it easier and no, this is not what you're dealing with when you write Makefiles. As I said, GNU Make was built to make software projects easier. That means its only job is to call other tools that actually build the files. But here I'm using Make as the tool that builds the pages, so it's no wonder that it ends up cursed.
And that is the way I created this website. To the layperson, it may seem quite complex, and yeah I suppose it kind of is, especially when reading it from someone like me. Who knows, maybe some day I'll create some more pages that go more in depth about a specific topic mentioned here. But you can expect some other, more serious topics as well, there are some ideas I would like to play around with. See this as a kind of experiment to my... carreer? as a software blogger. Until next time...