As someone on the Internet said not so far ago. Building its own static building system is a rite of passage for many developers. It has a lot of nice features. It gives a goal with a feeling of accomplishment. It is simple enough so most developers could build their own system. But it could also become very complex when you go down the rabbit hole.
Along the years I used different tools and used and wrote of few static website systems:
So if you look at the progression, I first used nanoc because I used ruby and it was a very new solution, the website looked really great. Also the main developer Denis Defreyne was really helpful. Ruby was really great at dealing with regular expressions for hacking my documents.
Then I was interested in Haskell, and I switched to a Haskell-made solution. I used hakyll, and I wrote a bit about it in Hakyll Setup. As a side note, the author of Hakyll Jasper Van der Jeugt is apparently a friend of the author of nanoc. They both wrote a static site generators with their preferred programming language. I added a lot of personal features to my own site builder. It was a nice toy project.
Then, due to a major disruption in my professional and private life I stopped to take care of my website.
And a few years ago, I wanted to start a new website from scratch. In the meantime I switched my editor of choice from vim to Emacs. I started to work in Clojure and emacs is generally a natural choice because you can configure it with LISP. I discovered org-mode (I don't think the homepage of org mode makes justice to how incredible it is). So org-mode comes with an export system. Thus I switched to org-publish. Again I wrote a bit about it.
It was nice, but very slow. I improved a few things like writing a short script to Generate RSS from a tree of html files. But I still had the feeling it was too slow.
Static site building is a specific usage of a build system. And as I knew I could use
pandoc to build HTML out of org-mode files and still versed in the Haskell culture I decided to try shake. You can learn more by reading this excellent paper about it, I think all developer should read it: Build System à la carte.
As a bonus, pandoc is written in Haskell. I could then directly use the pandoc library in my build program. It worked like a charm and it was very fast as compared to other solutions I tried. So really let me tell you shake is a great build system.
But it was not perfect. While it was very fast, and I was able to use pandoc API directly. It made me dependent on Haskell. The best way I found to have Haskell reproducible build environment is to use nix. This was great until the Big Sur update. To keep it short, nix stopped working on my computers after I upgraded my to Big Sur. Gosh, it was painful to fix.
Concurrently I discovered gemini and wanted to duplicate my website into gemini sphere. So I tried to update my build system but my code was to oriented to use pandoc and it was painful to have gemini in the middle of it. Particularly, generating a gemini index file. My main goal was to have gemini file that could only be linked from withing gemini sphere. Because gemini is a lot smaller web where you could feel a bit more protected from what the Web has become along the years. Whatever, in the end, I just had two problems to tackles.
So a very stable tool that I am pretty sure will still work almost exactly as today in 10 years is
make (more precisely gnumake). I expected a lot of people had already come to the same conclusion and wrote about it. To my great surprise, I found very few article about generating static website with make. I only found solutions a bit too specific for my need. This is why I would like to give you a more generic starting point solution.
Instead of copy/pasting my current
Makefile entirely let me give you a more generic one. It should be a great start.
The first part will be used to simply copy the files from
all: website # directory containing my org files as well as my assets files SRC_DIR ?= src # directory where I will but the files for my website (HTML + assets) DST_DIR ?= _site # list all files in src # if you want to exclude .org files use the exclude from the find command SRC_RAW_FILES := $(shell find $(SRC_DIR) -type f) # generate all file that should be copied in the site # For my site, I want to publish my source files along the HTML files DST_RAW_FILES := $(patsubst $(SRC_DIR)/%,$(DST_DIR)/%,$(SRC_RAW_FILES)) ALL += $(DST_RAW_FILES) # COPY EVERYTHING (.org file included) $(DST_DIR)/% : $(SRC_DIR)/% "$(dir [email protected])" mkdir -p "$<" "[email protected]" cp
This part is about running the
pandoc command for all
org files in
src/ so they generate a html file in
# ORG -> HTML, If you prefer markdown replace .org by .md EXT := .org # all source file we'll pass to pandoc SRC_PANDOC_FILES ?= $(shell find $(SRC_DIR) -type f -name "*$(EXT)") # all destination files we expect (replace the extension by .html) DST_PANDOC_FILES ?= $(subst $(EXT),.html, \ $(subst $(SRC_DIR),$(DST_DIR), \ $(SRC_PANDOC_FILES))) ALL += $(DST_PANDOC_FILES) # use a template (you should use one) TEMPLATE ?= templates/post.html # URL of the CSS put yours CSS = /css/y.css # The pandoc command to run to generate an html out of a source file PANDOC := pandoc \ -c $(CSS) \ --template=$(TEMPLATE) \ --from org \ --to html5 \ --standalone # Generate all html if the org file change or the template change $(DST_DIR)/%.html: $(SRC_DIR)/%.org $(TEMPLATE) $(dir [email protected]) mkdir -p $(PANDOC) $< \ [email protected] --output
A missing part is often the part where you would like to generate an index page to list the latest posts. Here you are a bit alone, you need to make one yourself. There is not generic way to do this one.
# Generating an index page is not difficult but not trivial either HTML_INDEX := $(DST_DIR)/index.html MKINDEX := engine/mk-index.sh $(HTML_INDEX): $(DST_PANDOC_FILES) $(MKINDEX) $(DST_DIR) mkdir -p $(MKINDEX) ALL += $(HTML_INDEX)
Finally, a few useful make commands.
make clean and
# make deploy will deploy the files to my website write your own script deploy: $(ALL) engine/deploy.sh website: $(ALL) .PHONY: clean clean: -rm -rf $(DST_DIR)/*
make is old. So it really does not support spaces in filenames. Take care of that.
But let me tell you. While this is quite a minimalist approach (<100 lines) it is nevertheless very fast. It will only generate the minimal amount of work to generate your website. I have a nice watcher script that update the website every time I save a file. It is almost instantaneous.
The only risky dependencies for my website now is
pandoc. Perhaps, they will change how they generate an HTML from the same org file in the future. I still use
nix to pin my pandoc version. But the static site builder itself is very simple, very stable and still very efficient.
As a conclusion, if you want to write your own static site builder that's great. There are plenty of things to learn along the way. Still if you want something stable for a long time, with a minimal amount of dependencies, I think this Makefile is really a great start.