ソースを参照

A bit of re-reading my article.

webp
コミット
2acd6effce
署名者: yogsototh GPGキーID: 7B19A4C650D59646
  1. 107
      src/posts/0018-makefile-as-static-site-builder-follow-up/index.org

107
src/posts/0018-makefile-as-static-site-builder-follow-up/index.org

@ -8,17 +8,17 @@
#+OPTIONS: auto-id:t
#+STARTUP: showeverything
This article will dig a bit deeper about how I generate my static website.
This article will dig a bit deeper about my =Makefile= based static website generator.
In a [[https://her.esy.fun/posts/0017-static-blog-builder/index.html][previous article]] I just gave the rationale and an overview to do it
yourself.
Mainly it is very fast and portable.
A few goal reached by my current build system are:
A few goals reached by my current build system are:
1. Be fast, try to make as few work as possible.
1. Be fast and make the minimal amount of work as possible.
I don't want to rebuild all the html pages if I only change one file.
2. Source file format agnostic. You can use markdown, org-mode or even
directly writing html.
directly write html.
3. Support gemini
4. Optimize size: minify HTML, CSS, images
5. Generate an index page listing the posts
@ -38,7 +38,7 @@ To make those transformations I use very short a shell scripts.
:CUSTOM_ID: -makefile--overview
:END:
A Makefile is constitued of rules.
A Makefile is made out of rules.
The first rule of your Makefile will be the default rule.
The first rule of my Makefile is called =all=.
@ -50,15 +50,14 @@ target: file1 file2
--output target
#+end_src
if =target= does not exists, then =make= will look at its
dependencies.
If any of its dependency need to be updated, it will run all the rules in
the correct order to rebuild them, and finally run the script to build
if =target= does not exists, then =make= will look at its dependencies.
If any of its dependencies need to be updated, it will run all the rules in
the correct order to rebuild them and finally run the script to build
=target=.
A file need to be updated if one of its dependency need to be updated or is
A file needs to be updated if one of its dependency needs to be updated or is
newer.
The ususal case of =make= is about building a single binary out of many
The usual use case of =make= is about building a single binary out of many
source files.
But for a static website, we need to generate a lot of files from a lot of
files.
@ -105,7 +104,7 @@ I have a block for:
The rules to copy assets will be a good first example.
1. find all assets in =src/= directory
2. generate all assets from these file in =_site/= directory
2. generate all assets from these files in =_site/= directory
3. make this rule a dependency on the =all= rule.
@ -123,44 +122,44 @@ ALL += assets
OK, this looks terrible.
But mainly:
- ~SRC_ASSETS~ will contains the result of the command ~find~.
- ~DST_ASSETS~ will contains the files of ~SRC_ASSETS~ but we replace the
- ~SRC_ASSETS~ will contain the result of the command ~find~.
- ~DST_ASSETS~ will contain the files of ~SRC_ASSETS~ but we replace the
=src/= by =_site/=.
- We create a generic rule; for all files matching the following pattern
=_site/%=, look for the file =src/%= and if it is newer (in our case)
then execute the following commmands:
then execute the following commands:
- create the directory to put =_site/%= in
- copy the file
About the line ~@mkdir -p "$(dir $@)"~:
- the =@= at the start of the command simply means that we make this execution silent.
- The =$@= is replaced by the target string.
- And =$(dir $@)= will generate the dirname of =$@=.
- And =$(dir $@)= will generate the folder name of =$@=.
For the line with ~cp~ you just need to know that =$<= will represent the
For the line with ~cp~, you just need to know that =~$<~= will represent the
first dependency.
So my Makefile is composed of similar blocks, where I replace the first
find command to match specific files and where I use different building rule.
An important point, is that the rule must be the most specific possible
because make will use the most specific rule in case of ambiguity.
So for example, the matching rule =_site/%: src/%= will match all files in
My Makefile is composed of similar blocks, where I replace the first
find command to match specific files and where I use different building rules.
An important point is that the rules must be the most specific possible.
This is because =make= will use the most specific rule in case of ambiguity.
For example, the matching rule =_site/%: src/%= will match all files in
the =src/= dir.
But if we want to treat css file with another rule we could write:
But if we want to treat =CSS= files with another rule we could write:
#+begin_src makefile
_site/%.css: src/%.css
minify "$<" "$@"
#+end_src
And if the selected file is a css file, this rule will be selected.
And if the selected file is a =CSS= file, this rule will be selected.
** Prelude
:PROPERTIES:
:CUSTOM_ID: prelude
:END:
So to start I have a few predefined useful variables.
I start with variables declarations:
#+begin_src makefile
all: site
@ -183,7 +182,7 @@ NO_SRC_FILE := ! -name '*.org'
:CUSTOM_ID: css
:END:
So here we go, the same simple pattern for CSS files.
Here we go; the same simple pattern for CSS files.
#+begin_src makefile
# CSS
@ -280,16 +279,15 @@ or the file, it will generate the dependencies.
:CUSTOM_ID: indexes
:END:
One of the goal I have is to be as agnostic as possible regarding format.
I know that the main destination format will be html.
So as much as possible, I would like to use this format.
So for every generated html file I will generate a clean XML file (via
hxclean) so I will be able to get specific node of my HTML files.
These XML files will constitute my "index".
Of course this is not the most optimized index (I could have used sqlite
for example) but it will already be quite helpful as the same index files
will be used to build the homepage with the list of articles, and the RSS
file.
We often need indexes to build a website.
Typically to list the latest articles, build the RSS file.
So for sake of simplicity, I decided to build my index as a set of XML files.
Of course, this could be optimizide, by using SQLite for example.
But this will already be really fast.
For every generated html file I will generate a clean XML file with
=hxclean=.
Once cleaned, it will be easy to access a specific node of in these XML files.
#+begin_src makefile
# INDEXES
@ -308,24 +306,23 @@ indexcache: $(DST_XML_FILES)
ALL += indexcache
#+end_src
So to resume this rule will generate for every file in =site/posts/*.html=
a corresponding =xml= file (=hxclean= takes an HTML an try its best to make
an XML out of it).
This rule will generate for every file in =site/posts/*.html= a corresponding
=xml= file (=hxclean= takes an HTML an try its best to make an XML out of it).
** HTML Index
:PROPERTIES:
:CUSTOM_ID: html-index
:END:
So now we just want to generate the main =index.html= page at the root of
Now we just want to generate the main =index.html= page at the root of
the site.
This page should list all articles by date in reverse order.
So the first step is to take advantage of the cache index.
The first step is to take advantage of the cache index.
For every XML file I generated before I should generate the small HTML
block I want for every entry.
For this I use a script =mk-index-entry.sh=.
He will use =hxclean= to retrieve the date and the title from the cached
He will use =hxselect= to retrieve the date and the title from the cached
XML files.
Then generate a small file just containing the date and the link.
@ -340,7 +337,7 @@ $(INDEX_CACHE_DIR)/%.index: $(INDEX_CACHE_DIR)/%.xml $(MK_INDEX_ENTRY)
$(MK_INDEX_ENTRY) "$<" "$@"
#+end_src
which reads, for every =.xml= file generate a =.index= file with
It means: for every =.xml= file generate a =.index= file with
=mk-index-entry.sh=.
#+begin_src sh
@ -378,7 +375,7 @@ printf ": %-55s" "$title ($keywords)"
echo " [${fg[green]}OK${reset_color}]"
#+end_src
Then I use these intermediate file to generate a single bigger index file.
Then I use these intermediate files to generate a single bigger index file.
#+begin_src makefile
HTML_INDEX := $(DST_DIR)/index.html
@ -393,13 +390,16 @@ ALL += index
#+end_src
This script is a big one, but it is not that complex.
For every file, I generate a new file =DATE-dirname=, I sort them in
reverse order and put their content in the middle of an HTML file.
For every file, I generate a new file =DATE-dirname=.
I sort them in reverse order and put their content in the middle of an HTML
file.
The important part is that it is only generated if the index change.
So first part of the script handle the creation of file using the date in
their file name which will help us sort them later.
Important note: this file updates only if the index change.
The first part of the script creates files with the creation date in their
metadatas.
The created file name will contain the creation date, this will be helpful
later.
#+begin_src sh
#!/usr/bin/env zsh
@ -480,11 +480,11 @@ echo "* HTML INDEX [done]"
:CUSTOM_ID: rss
:END:
So for my RSS generation this is quite similar to the system I use to
generate my index file.
My RSS generation is similar to the system I used to generate the index
file.
I just slightly improved the rules.
The makefile blocks look like:
The =Makefile= blocks look like:
#+begin_src makefile
# RSS
@ -553,7 +553,7 @@ gemini: $(DST_GMI_FILES) $(GMI_INDEX) $(GEM_ATOM)
:CUSTOM_ID: images
:END:
For images, I try to convert all of them with imagemagick to compress them.
For images, I try to compress them all with imagemagick.
#+begin_src makefile
# Images
@ -586,7 +586,6 @@ ALL += $(DST_IMG_FILES)
:END:
A nice bonus is that I also deploy my website using make.
And note I protect myself from Makefile temporary bugs for the =clean= rule.
#+begin_src makefile
# DEPLOY

読み込み中…
キャンセル
保存