-
Notifications
You must be signed in to change notification settings - Fork 0
/
Makefile
383 lines (326 loc) · 13.3 KB
/
Makefile
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
# SPDX-FileCopyrightText: 2022 Mischback
# SPDX-License-Identifier: MIT
# SPDX-FileType: SOURCE
# ### INTERNAL SETTINGS
# The absolute path to the repository.
#
# This assumes that this ``Makefile`` is placed in the root of the repository.
# REPO_ROOT does not contain a trailing ``/``
#
# Ref: https://stackoverflow.com/a/324782
# Ref: https://stackoverflow.com/a/2547973
# Ref: https://stackoverflow.com/a/73450593
REPO_ROOT := $(patsubst %/, %, $(dir $(abspath $(lastword $(MAKEFILE_LIST)))))
# Content source file directory
BUILD_DIR := $(REPO_ROOT)/.build
CONTENT_DIR := $(REPO_ROOT)/content
THEME_DIR := $(REPO_ROOT)/theme/mischback
FONT_SRC_DIR := $(THEME_DIR)/_src/fonts
STYLE_DIR := $(THEME_DIR)/_src/style
# The source files for the actual content
SRC_CONTENT := $(shell find $(CONTENT_DIR) -type f)
# Ref: https://stackoverflow.com/a/69830768
#
# FIXME: $(SRC_THEME) should not include the source files that are meant to be
# compiled to theme assets (e.g. the stylesheet)!
SRC_THEME := $(shell find $(THEME_DIR) -type f -not \( -name "_src" -prune \))
SRC_STYLE := $(shell find $(STYLE_DIR) -type f)
# Internal Settings
DEV_FLAG := dev
BUILD_MODE ?=
# Stamps
#
# Track certain things with artificial *stamps*.
STAMP_DIR := $(REPO_ROOT)/.make-stamps
# This stamp is **not** placed in the (internal) stamp directory. Instead, it
# is used to *tag* the build, providing the source commit SHA1 and the time of
# the build.
STAMP_BUILD_COMPLETED := $(BUILD_DIR)/build-source.txt
STAMP_CACHE_BUSTED := $(STAMP_DIR)/cache-busted
STAMP_HTML_PRETTIFIED := $(STAMP_DIR)/html-prettified
STAMP_NODE_READY := $(STAMP_DIR)/node-ready
STAMP_SPHINX_COMPLETED := $(STAMP_DIR)/sphinx-completed
STAMP_STYLESHEET_MINIFIED := $(STAMP_DIR)/stylesheet-minified
STAMP_THEME_READY := $(STAMP_DIR)/theme-ready
STAMP_THEME_STYLES_READY := $(STAMP_DIR)/theme-styles-ready
# Internal Python environments
#
# Actually this does only handle the setup of ``tox``, while the actual build
# scripts are executed through ``tox``'s environments.
TOX_VENV_DIR := $(REPO_ROOT)/.tox-venv
TOX_VENV_CREATED := $(TOX_VENV_DIR)/pyvenv.cfg
TOX_VENV_INSTALLED := $(TOX_VENV_DIR)/packages.txt
TOX_CMD := $(TOX_VENV_DIR)/bin/tox
# ``pre-commit`` is used to run several code-quality tools automatically.
#
# ``pre-commit`` is run through ``tox`` aswell, see ``tox``'s ``util``
# environment.
PRE_COMMIT_READY := .git/hooks/pre-commit
# ``make``-specific settings
.SILENT :
.DELETE_ON_ERROR :
MAKEFLAGS += --no-print-directory
MAKEFLAGS += --warn-undefined-variables
MAKEFLAGS += --no-builtin-rules
# ### RECIPES
# Create the actual build artifact.
build : $(STAMP_BUILD_COMPLETED)
.PHONY : build
# Build and serve in production mode.
srv/prod : | $(TOX_VENV_INSTALLED)
$(MAKE) build && \
$(TOX_CMD) -q -e sphinx -- python -m http.server 8082 --directory $(BUILD_DIR)
.PHONY : srv/prod
# Build and serve in development mode.
#
# In development mode some build steps are skipped, e.g. the minification of
# assets like the stylesheet. This speeds up the build process, but foremost
# it enables better developing/debugging.
srv/dev : | $(TOX_VENV_INSTALLED)
BUILD_MODE=$(DEV_FLAG) \
$(MAKE) build && \
$(TOX_CMD) -q -e sphinx -- python -m http.server 8082 --directory $(BUILD_DIR)
.PHONY : srv/dev
# Finish the build process by adding the current source commit SHA1 and the
# current timestamp to a dedicated file in the $(BUILD_DIR).
$(STAMP_BUILD_COMPLETED) : $(STAMP_HTML_PRETTIFIED)
$(create_dir)
echo "Commit: $(shell git rev-parse HEAD); Timestamp: $(shell date --iso=seconds)" > $@
# Prettify the (HTML) build artifacts
#
# See ``util/prettify-html.py`` for implementation details. As of now this is a
# wrapper around ``tidylib``.
$(STAMP_HTML_PRETTIFIED) : $(STAMP_CACHE_BUSTED)
$(create_dir)
$(MAKE) util/post-processing post-processing_cmd="{toxinidir}/util/prettify-html.py $(BUILD_DIR)"
touch $@
# Perform the Cache Busting.
#
# Cache Busting relies on the fact, that modified assets like the stylesheets
# will have a unique name by including a hash of the file's content in the
# filename.
$(STAMP_CACHE_BUSTED) : $(STAMP_SPHINX_COMPLETED) $(STAMP_STYLESHEET_MINIFIED) $(BUILD_DIR)/_static/sprite.svg
$(create_dir)
ifeq ($(BUILD_MODE), $(DEV_FLAG))
echo "[SKIPPED] Cache Busting is skipped in development mode"
else
$(MAKE) util/post-processing post-processing_cmd="{toxinidir}/util/cache-busting.py --source $(BUILD_DIR) --asset _static/style.css --asset _static/sprite.svg"
endif
touch $@
$(STAMP_STYLESHEET_MINIFIED) : $(BUILD_DIR)/_static/style.css $(STAMP_SPHINX_COMPLETED) | $(STAMP_NODE_READY)
$(create_dir)
ifeq ($(BUILD_MODE), $(DEV_FLAG))
DEV_FLAG=$(DEV_FLAG) npx postcss -o $< $<
else
npx postcss -o $< $<
endif
touch $@
# Run ``Sphinx`` to build HTML output from reST sources
#
# This is the primary build recipe, as it will generate the HTML output by
# running ``Sphinx``. It is (obviously) dependent on a plethora of things,
# including the actual content source files and the theme files.
#
# Please note that some of the automatically generated documents are
# **removed** here, as they are not used/desired for the final build artifact:
# - genindex/*.*
# - objects.inv
# - search/*.*
# - searchindex.js
# - _static/pygments.css
# - .buildinfo
# - .doctrees/*.*
# - output.[json|txt] -> this is from the linkcheck builder and only present
# during CI runs
$(STAMP_SPHINX_COMPLETED) : $(SRC_CONTENT) $(STAMP_THEME_READY)
$(create_dir)
$(MAKE) util/sphinx/build sphinx-build_options="-W --keep-going" && \
rm -rf $(BUILD_DIR)/genindex && \
rm -rf $(BUILD_DIR)/objects.inv && \
rm -rf $(BUILD_DIR)/search && \
rm -rf $(BUILD_DIR)/searchindex.js && \
rm -rf $(BUILD_DIR)/_static/pygments.css && \
rm -rf $(BUILD_DIR)/.buildinfo && \
rm -rf $(BUILD_DIR)/.doctrees && \
rm -rf $(BUILD_DIR)/output.json && \
rm -rf $(BUILD_DIR)/output.txt
touch $@
# Track and create the additional assets of the theme.
#
# This is meant to trigger the creation of stylesheets and script files from
# source code
$(STAMP_THEME_READY) : $(STAMP_THEME_STYLES_READY) $(SRC_THEME)
$(create_dir)
touch $@
# Track and create the theme's stylesheets
#
# This is only a meta-target to collect the stylesheets. In fact it is desired
# to have exactly **one** stylesheet.
$(STAMP_THEME_STYLES_READY) : $(THEME_DIR)/static/style.css
$(create_dir)
touch $@
# Compile SASS sources to an actual stylesheet
#
# During development, the sources are embedded into the stylesheet. For
# production a raw stylesheet is generated.
$(THEME_DIR)/static/%.css : $(STYLE_DIR)/%.scss $(SRC_STYLE) | $(STAMP_NODE_READY)
$(create_dir)
ifeq ($(BUILD_MODE), $(DEV_FLAG))
npx sass --embed-sources --embed-source-map --stop-on-error --verbose $< $@
else
npx sass --no-source-map --stop-on-error --verbose $< $@
endif
# Remove build artifacts
clean :
rm -rf $(BUILD_DIR)
rm -rf $(STAMP_CACHE_BUSTED)
rm -rf $(STAMP_HTML_PRETTIFIED)
rm -rf $(STAMP_SPHINX_COMPLETED)
rm -rf $(STAMP_THEME_READY)
rm -rf $(STAMP_THEME_STYLES_READY)
rm -rf $(THEME_DIR)/static/style.css
rm -rf $(THEME_DIR)/static/style.css.map
.PHONY : clean
# Remove build environments
full-clean : clean
rm -rf $(STAMP_DIR)
rm -rf $(REPO_ROOT)/node_modules
rm -rf $(REPO_ROOT)/.npm
rm -rf $(REPO_ROOT)/.tox
rm -rf $(REPO_ROOT)/.tox-venv
.PHONY : full-clean
# ##### Utility Stuff
#
# The following recipes are mainly used as shortcuts to run several tools.
# They are not directly related to the actual build process.
# Run a prepared ``tree`` command
tree :
tree --dirsfirst -I "node_modules|requirements|LICENSE|package-lock.json|README.md"
.PHONY : tree
responsive-image_src ?= ""
util/responsive-image :
$(MAKE) util/image-processing image-processing_cmd="{toxinidir}/util/process-image.py responsive --source $(responsive-image_src) --destination ./content/img --required-ssim 0.97 --format jpg --jpeg-compression 50 --format webp --webp-compression 45 --format avif --avif-compression 40 --size 320 320 --size 480 480 --size 640 640 --size 960 960 --size 1280 1280 --size 1600 1600 --size 1920 1920"
.PHONY : util/responsive-image
# Run all linters through ``pre-commit``
util/lint/all : | $(STAMP_NODE_READY)
$(MAKE) util/pre-commit pre-commit_files="--all-files"
.PHONY : util/lint/all
# Run ``black``
util/lint/black :
$(MAKE) util/pre-commit pre-commit_id="black" pre-commit_files="--all-files"
.PHONY : util/lint/black
# Verify that all articles have the "keywords" meta value
util/lint/content/keywords :
$(MAKE) util/pre-commit pre-commit_id="content_keywords" pre-commit_files="--all-files"
.PHONY : util/lint/content/keywords
# Verify that all articles have the "summary" meta value
util/lint/content/summary :
$(MAKE) util/pre-commit pre-commit_id="content_summary" pre-commit_files="--all-files"
.PHONY : util/lint/content/summary
# Run ``curlylint``
util/lint/curlylint :
$(MAKE) util/pre-commit pre-commit_id="curlylint" pre-commit_files="--all-files"
.PHONY : util/lint/curlylint
# Run ``djlint``
util/lint/djlint :
$(MAKE) util/pre-commit pre-commit_id="djlint-jinja" pre-commit_files="--all-files"
.PHONY : util/lint/djlint
# Run ``doc8``
util/lint/doc8 :
$(MAKE) util/pre-commit pre-commit_id="doc8" pre-commit_files="--all-files"
.PHONY : util/lint/doc8
# Run ``flake8``
util/lint/flake8 :
$(MAKE) util/pre-commit pre-commit_id="flake8" pre-commit_files="--all-files"
.PHONY : util/lint/flake8
# Run ``isort``
util/lint/isort :
$(MAKE) util/pre-commit pre-commit_id="isort" pre-commit_files="--all-files"
.PHONY : util/lint/isort
# Run ``prettier``
util/lint/prettier : | $(STAMP_NODE_READY)
$(MAKE) util/pre-commit pre-commit_id="prettier" pre-commit_files="--all-files"
.PHONY : util/lint/prettier
# Run ``Sphinx``'s linkcheck builder
util/lint/sphinx-linkcheck :
$(MAKE) util/sphinx/build sphinx_builder="linkcheck"
.PHONY : util/lint/sphinx-linkcheck
# Run ``sphinx-lint``
util/lint/sphinx-lint :
$(MAKE) util/pre-commit pre-commit_id="sphinx-lint" pre-commit_files="--all-files"
.PHONY : util/lint/sphinx-lint
# Run ``stylelint``
util/lint/stylelint : | $(STAMP_NODE_READY)
$(MAKE) util/pre-commit pre-commit_id="stylelint" pre-commit_files="--all-files"
.PHONY : util/lint/stylelint
# Run ``pre-commit``
#
# This is the actual recipe that runs ``pre-commit``. It is used by other
# recipes, that will set the required ``pre-commit_id`` and
# ``pre-commit_files`` variables.
pre-commit_id ?= ""
pre-commit_files ?= ""
util/pre-commit : | $(PRE_COMMIT_READY)
$(TOX_CMD) -q -e pre-commit -- pre-commit run $(pre-commit_files) $(pre-commit_id)
.PHONY : util/pre-commit
# Run ``sphinx-build``
#
# This is the recipe that runs ``Sphinx``'s builders. It does provide mandatory
# settings / configuration values but does accept additional flags aswell.
# The actual *builder* to run is configured by ``sphinx_builder``.
#
# As of now, ``dirhtml`` builder is the default, as this builder is used to
# generate the HTML output. The ``linkcheck`` builder is used as an additional
# linter.
sphinx_builder ?= "dirhtml"
sphinx_config-dir ?= "./"
sphinx-build_options ?= ""
util/sphinx/build : conf.py requirements/sphinx.txt pyproject.toml | $(TOX_VENV_INSTALLED)
$(TOX_CMD) -q -e sphinx -- sphinx-build $(sphinx-build_options) -b $(sphinx_builder) -c $(sphinx_config-dir) $(CONTENT_DIR) $(BUILD_DIR)
.PHONY : util/sphinx/build
# Run commands in the ``image-processing`` environment.
image-processing_cmd ?= ""
util/image-processing : requirements/image-processing.txt pyproject.toml | $(TOX_VENV_INSTALLED)
$(TOX_CMD) -q -e image-processing -- $(image-processing_cmd)
.PHONY : util/image-processing
# Run commands in the ``pre-processing`` environment.
pre-processing_cmd ?= ""
util/pre-processing : requirements/pre-processing.txt pyproject.toml | $(TOX_VENV_INSTALLED)
$(TOX_CMD) -q -e pre-processing -- $(pre-processing_cmd)
.PHONY : util/pre-processing
# Run commands in the ``post-processing`` environment.
post-processing_cmd ?= ""
util/post-processing : requirements/post-processing.txt pyproject.toml | $(TOX_VENV_INSTALLED)
$(TOX_CMD) -q -e post-processing -- $(post-processing_cmd)
.PHONY : util/post-processing
# (Re-) Generate the requirements files using pip-tools (``pip-compile``)
#
# ``pip-compile`` is run through a ``tox`` environment. The actual command is
# included in ``tox``'s configuration in ``pyproject.toml``. That's why that
# file is an additional prerequisite. This may lead to additional
# regenerations, but these will most likely not affect the generated files.
requirements/%.txt : requirements/%.in pyproject.toml | $(TOX_VENV_INSTALLED)
$(TOX_CMD) -q -e pip-tools -- $<
# ##### Internal utility stuff
# Create the virtual environment for running tox
$(TOX_VENV_CREATED) :
/usr/bin/env python3 -m venv $(TOX_VENV_DIR)
# Install the required packages in tox's virtual environment
$(TOX_VENV_INSTALLED) : $(TOX_VENV_CREATED)
$(TOX_VENV_DIR)/bin/pip install -r requirements/tox.txt
$(TOX_VENV_DIR)/bin/pip freeze > $@
# Install the pre-commit hooks
$(PRE_COMMIT_READY) : | $(TOX_VENV_INSTALLED)
$(TOX_CMD) -e pre-commit -- pre-commit install
# Install the required NodeJS packages
#
# Uses npm's ``ci`` to create the required NodeJS environment. It (re-) uses
# a local cache for npm in order to speed up builds during CI.
#
# https://stackoverflow.com/a/58187176
$(STAMP_NODE_READY) : package.json package-lock.json
$(create_dir)
npm ci --cache .npm --prefer-offline
touch $@
# Create a directory as required by other recipes
create_dir = @mkdir -p $(@D)