Repository navigation
Expand file tree
/
Copy pathMakefile
More file actions
314 lines (262 loc) · 13.6 KB
/
Copy pathMakefile
File metadata and controls
314 lines (262 loc) · 13.6 KB
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
EMACS ?= emacs
PYTHON ?= python3
# Extra flags injected before every Emacs invocation (e.g. `-L /tmp/compat'
# in CI so older Emacs versions can find the compat library).
EMACSFLAGS ?=
export EMACSFLAGS
XDG_CACHE_HOME ?= $(HOME)/.cache
MELPAZOID_DIR ?= $(XDG_CACHE_HOME)/melpazoid
EVIL_DIR ?= $(XDG_CACHE_HOME)/evil
CONSULT_DIR ?= $(XDG_CACHE_HOME)/consult
MARGINALIA_DIR ?= $(XDG_CACHE_HOME)/marginalia
LINT_ELPA_DIR ?= $(XDG_CACHE_HOME)/ghostel-lint-elpa
LINT_DEPS_STAMP := $(LINT_ELPA_DIR)/.deps-installed
DOC_ELPA_DIR ?= $(XDG_CACHE_HOME)/ghostel-doc-elpa
DOC_DEPS_STAMP := $(DOC_ELPA_DIR)/.deps-installed
ELISP_FILES := $(filter-out %-autoloads.el,$(wildcard lisp/ghostel*.el) \
$(wildcard extensions/evil-ghostel/*.el) \
$(wildcard extensions/consult-ghostel/*.el))
PACKAGE_FILES := $(shell grep -l '^;; Package-Requires:' $(ELISP_FILES) 2>/dev/null)
CORE_PACKAGE_FILE := $(firstword $(filter lisp/%,$(PACKAGE_FILES)))
ELISP := $(CORE_PACKAGE_FILE) $(filter-out $(CORE_PACKAGE_FILE),$(ELISP_FILES))
ELC := $(patsubst %.el,%.elc,$(ELISP))
LINT_HELPERS := tools/ghostel-lint.el
TOOLS_ELISP := $(sort $(wildcard tools/*.el))
CHECKDOC_FILES = $(ELISP) $(TOOLS_ELISP) $(sort $(wildcard test/*-test-helpers.el)) $(TEST_FILES)
DOCQUOTE_FILES = $(ELISP) $(TOOLS_ELISP)
# Native module artifact (kept in sync with `clean'). Listed as a real
# file so the per-test stamp rules depend on its mtime instead of on the
# phony `build' target — that way the Zig sources, not the act of asking
# for `build', decide whether tests need to re-run.
UNAME := $(shell uname 2>/dev/null)
ifeq ($(OS),Windows_NT)
MODULE := ghostel-module.dll
# Use MinGW rather than Zig's MSVC-flavoured native Windows target so local
# builds match release artifacts and do not require a Windows SDK. The DLL
# architecture must match Emacs, not necessarily the OS (e.g. x64 Emacs under
# ARM64 Windows emulation).
ifndef ZIG_WINDOWS_TARGET
WINDOWS_EMACS_ARCH := $(shell $(EMACS) --batch -Q --eval "(princ (car (split-string system-configuration \"-\")))" 2>/dev/null)
WINDOWS_ZIG_ARCH := x86_64
ifneq ($(filter arm64 aarch64,$(WINDOWS_EMACS_ARCH)),)
WINDOWS_ZIG_ARCH := aarch64
endif
ZIG_WINDOWS_TARGET := $(WINDOWS_ZIG_ARCH)-windows-gnu
endif
ZIG_TARGET_FLAG ?= -Dtarget=$(ZIG_WINDOWS_TARGET)
else ifeq ($(UNAME),Darwin)
MODULE := ghostel-module.dylib
else
MODULE := ghostel-module.so
endif
ZIG_BUILD_FLAGS := --prefix . -Doptimize=ReleaseFast -Dcpu=baseline $(ZIG_TARGET_FLAG)
ZIG_SOURCES := $(wildcard src/*.zig src/*.c build.zig build.zig.zon symbols.map) \
$(wildcard vendor/*.h)
.PHONY: all build test test-native test-zig test-hypothesis test-hypothesis-cases test-all test-evil test-consult lint melpazoid melpazoid-ghostel melpazoid-evil-ghostel melpazoid-consult-ghostel byte-compile checkdoc docquotes package-lint bench bench-quick bench-e2e bench-tui-partial html clean regen-terminfo
# Recommended invocation: `make -j$(nproc) all' on Linux,
# `make -j$(sysctl -n hw.ncpu) all' on macOS. GNU make 4+ also accepts
# bare `-j' (unlimited); pair with `-l$(nproc)' to cap by load.
all: build test-all test-evil test-consult lint
build: $(MODULE)
$(MODULE): $(ZIG_SOURCES)
zig build $(ZIG_BUILD_FLAGS)
test-zig:
zig build $(ZIG_TARGET_FLAG) test
test-hypothesis: build
$(PYTHON) -m unittest test/hypothesis/test_render.py
test-hypothesis-cases: build
cd test/hypothesis && $(PYTHON) -m unittest test_render.RenderSavedCaseRegressionTest
# Pattern rule: rebuild .elc whenever its .el source is newer.
# Make's timestamp tracking keeps the byte-compiled files in sync, so
# test targets never load stale .elc (Emacs prefers .elc over .el
# even when the source is newer, which silently masks edits).
lisp/%.elc: lisp/%.el
$(EMACS) --batch $(EMACSFLAGS) -Q -L lisp --eval "(setq byte-compile-error-on-warn t load-prefer-newer t)" -f batch-byte-compile $<
# Extension packages depend on third-party libraries; reuse the
# checkouts that `test-evil' / `test-consult' manage.
$(EVIL_DIR):
git clone --depth 1 https://github.com/emacs-evil/evil.git "$@"
$(CONSULT_DIR):
git clone --depth 1 https://github.com/minad/consult.git "$@"
$(MARGINALIA_DIR):
git clone --depth 1 https://github.com/minad/marginalia.git "$@"
# Depend on the core .elc files: `require' prefers a stale core .elc over
# the fresh .el, so a parallel build could otherwise compile the extension
# before a core function it uses exists in the loaded bytecode.
extensions/evil-ghostel/%.elc: extensions/evil-ghostel/%.el $(filter lisp/%.elc,$(ELC)) | $(EVIL_DIR)
$(EMACS) --batch $(EMACSFLAGS) -Q -L "$(EVIL_DIR)" -L lisp -L extensions/evil-ghostel \
--eval "(setq byte-compile-error-on-warn t load-prefer-newer t)" -f batch-byte-compile $<
extensions/consult-ghostel/%.elc: extensions/consult-ghostel/%.el $(filter lisp/%.elc,$(ELC)) | $(CONSULT_DIR) $(MARGINALIA_DIR)
$(EMACS) --batch $(EMACSFLAGS) -Q -L "$(CONSULT_DIR)" -L "$(MARGINALIA_DIR)" -L lisp -L extensions/consult-ghostel \
--eval "(setq byte-compile-error-on-warn t load-prefer-newer t)" -f batch-byte-compile $<
# Per-topic test files. Each file becomes its own Make target with a
# per-file stamp under .build/tests/, so `make -jN' parallelises test
# execution across cores. The slowest single file sets the wall floor,
# not the sum of all files.
TEST_FILES := $(sort $(wildcard test/ghostel-*-test.el))
TEST_BASES := $(notdir $(basename $(TEST_FILES)))
TEST_STAMPS_DIR := .build/tests
TEST_ELISP_STAMPS := $(patsubst %,$(TEST_STAMPS_DIR)/elisp-%.ok,$(TEST_BASES))
TEST_NATIVE_STAMPS := $(patsubst %,$(TEST_STAMPS_DIR)/native-%.ok,$(TEST_BASES))
TEST_FIXTURES := $(wildcard test/fixtures/*.py)
test: $(TEST_ELISP_STAMPS)
test-native: $(TEST_NATIVE_STAMPS)
# Pass `-O target' (output-sync, GNU make 4+) for clean interleaving:
# make -j$(nproc) -O target test
$(TEST_STAMPS_DIR):
@mkdir -p $@
$(TEST_STAMPS_DIR)/elisp-%.ok: test/%.el test/ghostel-test-helpers.el $(ELC) | $(TEST_STAMPS_DIR)
@printf ' ELISP %s\n' $*
@$(EMACS) --batch $(EMACSFLAGS) -Q -L lisp -L test \
-l ert -l test/ghostel-test-helpers.el -l $< \
-f ghostel-test-run-elisp
@touch $@
$(TEST_STAMPS_DIR)/native-%.ok: test/%.el test/ghostel-test-helpers.el $(TEST_FIXTURES) $(ELC) $(MODULE) | $(TEST_STAMPS_DIR)
@printf ' NATIVE %s\n' $*
@$(EMACS) --batch $(EMACSFLAGS) -Q -L lisp -L test \
-l ert -l test/ghostel-test-helpers.el -l $< \
-f ghostel-test-run-native
@touch $@
test-all: test test-zig test-native
test-evil: build $(ELC) | $(EVIL_DIR)
$(EMACS) --batch $(EMACSFLAGS) -Q -L "$(EVIL_DIR)" -L lisp -L extensions/evil-ghostel \
-l ert -l test/evil-ghostel-test.el -f evil-ghostel-test-run
test-consult: $(ELC) | $(CONSULT_DIR) $(MARGINALIA_DIR)
$(EMACS) --batch $(EMACSFLAGS) -Q -L "$(CONSULT_DIR)" -L "$(MARGINALIA_DIR)" -L lisp -L extensions/consult-ghostel \
-l ert -l test/consult-ghostel-test.el -f consult-ghostel-test-run
byte-compile: $(ELC)
lint: byte-compile package-lint checkdoc docquotes
# Two things the default archives don't provide: the linter (NonGNU ELPA
# has only 0.26, predating the `eshell/' prefix allowance) and a ghostel
# package for evil-ghostel's dependency check -- MELPA's ghostel cannot
# serve, as the lint runs below leave MELPA out of `package-archives'.
# An isolated `package-user-dir' keeps `make package-lint' standalone.
$(LINT_DEPS_STAMP): $(CORE_PACKAGE_FILE) Makefile
$(EMACS) --batch $(EMACSFLAGS) -Q \
--eval "(require 'package)" \
--eval "(setq package-user-dir \"$(LINT_ELPA_DIR)\")" \
--eval "(add-to-list 'package-archives '(\"melpa\" . \"https://melpa.org/packages/\") t)" \
--eval "(package-initialize)" \
--eval "(package-refresh-contents)" \
--eval "(package-install 'package-lint)" \
--eval "(package-install-file (expand-file-name \"$(CORE_PACKAGE_FILE)\"))"
@touch $@
# All package files are linted, not just the main ones; sub-files need
# `package-lint-main-file' so per-package header checks stay on the main
# file.
# $(1): the package's main file; $(2): all of the package's files.
define run-package-lint
$(EMACS) --batch $(EMACSFLAGS) -Q -L lisp \
--eval "(setq package-user-dir \"$(LINT_ELPA_DIR)\")" \
--eval "(package-initialize)" \
--eval "(require 'package-lint)" \
--eval "(setq package-lint-main-file \"$(1)\")" \
-f package-lint-batch-and-exit $(2)
endef
# One stamp per package (`ghostel' + each extensions/<pkg>), so repeat
# lints with nothing changed are free.
LINT_STAMPS_DIR := .build/lint
LINT_PACKAGES := ghostel $(notdir $(wildcard extensions/*))
LINT_STAMPS := $(patsubst %,$(LINT_STAMPS_DIR)/%.ok,$(LINT_PACKAGES))
package-lint: $(LINT_STAMPS)
$(LINT_STAMPS_DIR):
@mkdir -p $@
$(LINT_STAMPS_DIR)/ghostel.ok: $(filter lisp/%,$(ELISP)) $(LINT_DEPS_STAMP) Makefile | $(LINT_STAMPS_DIR)
@printf ' PKGLINT %s\n' ghostel
@$(call run-package-lint,$(CORE_PACKAGE_FILE),$(filter lisp/%,$(ELISP)))
@touch $@
# Each extensions/<pkg>/ is its own package, main file <pkg>/<pkg>.el.
$(LINT_STAMPS_DIR)/%.ok: $(ELISP) $(LINT_DEPS_STAMP) Makefile | $(LINT_STAMPS_DIR)
@printf ' PKGLINT %s\n' $*
@$(call run-package-lint,extensions/$*/$*.el,$(filter extensions/$*/%,$(ELISP)))
@touch $@
checkdoc: $(CHECKDOC_FILES)
$(EMACS) --batch $(EMACSFLAGS) -Q -l $(LINT_HELPERS) \
-f ghostel-lint-checkdoc $(CHECKDOC_FILES)
# Mirrors melpazoid's "Only use back/front quotes to link to top-level
# elisp symbols" check, widened to also catch identifiers with
# underscores like INSIDE_EMACS — env-var and macro-style names that
# melpazoid's stricter [A-Z]+ regex skips.
docquotes: $(DOCQUOTE_FILES)
$(EMACS) --batch $(EMACSFLAGS) -Q -l $(LINT_HELPERS) \
-f ghostel-lint-docquotes $(DOCQUOTE_FILES)
melpazoid: melpazoid-ghostel melpazoid-evil-ghostel melpazoid-consult-ghostel
$(MELPAZOID_DIR):
git clone https://github.com/riscy/melpazoid.git "$@"
melpazoid-ghostel: | $(MELPAZOID_DIR)
RECIPE='(ghostel :fetcher github :repo "dakra/ghostel" :files (:defaults "etc" "src" "vendor" "build.zig" "build.zig.zon" "symbols.map"))' \
LOCAL_REPO=$(CURDIR) \
make -C "$(MELPAZOID_DIR)"
melpazoid-evil-ghostel: | $(MELPAZOID_DIR)
RECIPE='(evil-ghostel :fetcher github :repo "dakra/ghostel" :files ("extensions/evil-ghostel/evil-ghostel.el"))' \
LOCAL_REPO=$(CURDIR) \
make -C "$(MELPAZOID_DIR)"
melpazoid-consult-ghostel: | $(MELPAZOID_DIR)
RECIPE='(consult-ghostel :fetcher github :repo "dakra/ghostel" :files ("extensions/consult-ghostel/consult-ghostel.el"))' \
LOCAL_REPO=$(CURDIR) \
make -C "$(MELPAZOID_DIR)"
bench:
bash bench/run-bench.sh
bench-quick:
bash bench/run-bench.sh --quick
bench-e2e:
bash bench/run-bench.sh --e2e
bench-tui-partial:
$(EMACS) --batch $(EMACSFLAGS) -Q -L lisp -l bench/ghostel-bench.el \
--eval '(progn (setq ghostel-bench-include-vterm nil ghostel-bench-include-eat nil ghostel-bench-include-term nil) (ghostel-bench--load-backends) (ghostel-bench--run-tui-partial-scenarios))'
# htmlize provides source-block syntax highlighting for the HTML export.
# Provision it into an isolated `package-user-dir' (mirrors the
# package-lint setup above) so `make html' is standalone; CI picks it up
# automatically via the `public/index.html' prerequisite.
$(DOC_DEPS_STAMP):
$(EMACS) --batch $(EMACSFLAGS) -Q \
--eval "(require 'package)" \
--eval "(setq package-user-dir \"$(DOC_ELPA_DIR)\")" \
--eval "(add-to-list 'package-archives '(\"melpa\" . \"https://melpa.org/packages/\") t)" \
--eval "(package-initialize)" \
--eval "(package-refresh-contents)" \
--eval "(package-install 'htmlize)"
@touch $@
# Export README.org to a themed single-page site (ReadTheOrg, vendored under
# docs/org-html-themes/) for GitHub Pages. The explicit output filename
# sidesteps `#+export_file_name: ghostel.texi' (which would otherwise make
# ox-html write ghostel.html). The theme's src/ tree goes into public/ so its
# relative HTML_HEAD links resolve.
DOC_THEME_FILES := $(shell find docs/org-html-themes -type f)
html: public/index.html
public/index.html: README.org $(DOC_DEPS_STAMP) $(DOC_THEME_FILES)
@mkdir -p public
$(EMACS) --batch $(EMACSFLAGS) -Q \
--eval "(setq package-user-dir \"$(DOC_ELPA_DIR)\")" \
--eval "(package-initialize)" \
--eval "(require 'htmlize)" \
--eval "(require 'ox-html)" \
--eval "(setq make-backup-files nil \
org-html-validation-link nil \
org-export-with-broken-links 'mark \
org-html-htmlize-output-type 'css)" \
--eval "(with-current-buffer (find-file-noselect \"README.org\") \
(org-export-to-file 'html \"public/index.html\"))"
cp -R docs/org-html-themes/src public/
clean:
rm -f ghostel-module.dylib ghostel-module.so ghostel-module.dll ghostel-module.version
rm -f $(ELC)
rm -rf zig-out .zig-cache .build public
# Maintainer-only: regenerate the bundled compiled terminfo from
# `etc/terminfo/xterm-ghostty.terminfo'. Run after bumping libghostty
# (the source file should be re-extracted from a fresh Ghostty install
# via `infocmp -x xterm-ghostty') and commit the resulting binaries.
# `tic' on macOS emits the BSD hashed-dir layout (78/, 67/); the
# binary file format is identical to Linux ncurses, so we mirror the
# compiled entries into the Linux layout (x/, g/) by copying.
regen-terminfo:
rm -rf etc/terminfo/x etc/terminfo/g etc/terminfo/78 etc/terminfo/67
tic -x -o etc/terminfo/ etc/terminfo/xterm-ghostty.terminfo
@if [ -d etc/terminfo/78 ]; then \
mkdir -p etc/terminfo/x etc/terminfo/g; \
cp etc/terminfo/78/xterm-ghostty etc/terminfo/x/xterm-ghostty; \
cp etc/terminfo/67/ghostty etc/terminfo/g/ghostty; \
fi
@TERMINFO=$(CURDIR)/etc/terminfo infocmp xterm-ghostty >/dev/null \
|| (echo "ERROR: regenerated terminfo failed to round-trip"; exit 1)
@find etc/terminfo -type f | sort