Repository navigation
Expand file tree
/
Copy pathMakefile
More file actions
290 lines (258 loc) · 13.4 KB
/
Copy pathMakefile
File metadata and controls
290 lines (258 loc) · 13.4 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
# Release builds, and the check that says whether one is shippable.
#
# Everyday work is plain `cargo build` / `cargo test` — this file is only for
# producing a binary that runs on someone else's machine. See `Containerfile`
# for why that needs a container at all.
CONTAINER ?= podman
RUST_IMAGE ?= docker.io/library/rust:bookworm
IMAGE ?= lightforge-build
OUT ?= target/container/release
DIST ?= target/dist
# For `make install` only; the packages set their own roots.
PREFIX ?= /usr/local
DESTDIR ?=
# Read from the workspace manifest rather than written twice. `PKGVER` is the
# same version in the form `dpkg` and `rpm` both order correctly: a `~` sorts
# *before* the release it precedes, where `-` does not, so `1.0.0-beta.1`
# would upgrade to nothing and `1.0.0~beta.1` upgrades to `1.0.0`.
VERSION := $(shell sed -n 's/^version = "\(.*\)"/\1/p' Cargo.toml | head -1)
PKGVER := $(subst -,~,$(VERSION))
# Every release is built for both: aarch64 is cross-compiled in the same
# container. The deb, the rpm and the PKGBUILDs stay x86_64 only.
ARCHES := x86_64 aarch64
OUT_x86_64 := $(OUT)
OUT_aarch64 := target/container/aarch64-unknown-linux-gnu/release
IN_IMAGE := $(CONTAINER) run --rm --volume "$(CURDIR):/src:Z" --workdir /src $(IMAGE)
# How each build is inspected and run from this machine.
OBJDUMP_x86_64 := objdump
OBJDUMP_aarch64 := $(IN_IMAGE) aarch64-linux-gnu-objdump
RUN_x86_64 :=
RUN_aarch64 := $(IN_IMAGE) qemu-aarch64
# The AppImage's static type-2 runtime, pinned: it needs no libfuse2 on the
# host, which Ubuntu 22.04+ and Fedora Atomic do not ship. The AppImage has no
# version in its name: the self-update replaces the file in place, and
# `releases/latest/download/` then always has it.
APPIMAGE_RUNTIME_URL := https://github.com/AppImage/type2-runtime/releases/download/20251108
APPIMAGE_RUNTIME_SHA256_x86_64 := 2fca8b443c92510f1483a883f60061ad09b46b978b2631c807cd873a47ec260d
APPIMAGE_RUNTIME_SHA256_aarch64 := 00cbdfcf917cc6c0ff6d3347d59e0ca1f7f45a6df1a428a0d6d8a78664d87444
# The highest glibc a release may ask for, and what the deb and rpm declare.
# Ubuntu 22.04 is 2.35; the Debian 12 container is 2.36, so this also catches
# a dependency that starts using a 2.36 symbol, not only a build that escaped
# the container and picked up the host's libc.
GLIBC_MAX ?= 2.35
BINARIES := lightforge lightforge-gui
.PHONY: release image check-glibc clean-release cli-doc check-cli-doc \
packages deb rpm tarball appimage installer checksums github-names check-binaries \
check-spec clean-packages install uninstall
## Build release binaries against an old glibc, and verify they are portable.
release: image
$(IN_IMAGE) sh -c 'cargo build --release --locked \
&& cargo build --release --locked --target aarch64-unknown-linux-gnu \
&& aarch64-linux-gnu-strip $(addprefix $(OUT_aarch64)/,$(BINARIES))'
strip $(addprefix $(OUT)/,$(BINARIES))
@$(MAKE) --no-print-directory check-glibc
@echo
@echo "Release binaries:"
@ls -lh $(foreach arch,$(ARCHES),$(addprefix $(OUT_$(arch))/,$(BINARIES))) | awk '{print " " $$9 " " $$5}'
image:
$(CONTAINER) build --build-arg RUST_IMAGE=$(RUST_IMAGE) --tag $(IMAGE) --file Containerfile .
## Fail if a binary demands a glibc newer than the floor.
##
## The whole point of the container, asserted rather than assumed — a release
## that quietly regains the host's glibc looks fine here and fails on a Deck.
check-glibc: $(addprefix check-glibc-,$(ARCHES))
check-glibc-%:
@fail=0; \
for bin in $(addprefix $(OUT_$*)/,$(BINARIES)); do \
if [ ! -f "$$bin" ]; then echo "missing: $$bin"; fail=1; continue; fi; \
needs=$$($(OBJDUMP_$*) -T "$$bin" | grep -o 'GLIBC_[0-9.]*' | sort -uV | tail -1); \
highest=$${needs#GLIBC_}; \
top=$$(printf '%s\n%s\n' "$(GLIBC_MAX)" "$$highest" | sort -V | tail -1); \
if [ "$$top" = "$(GLIBC_MAX)" ]; then \
echo "ok $$bin needs glibc $$highest (floor $(GLIBC_MAX))"; \
else \
echo "FAIL $$bin needs glibc $$highest, above the $(GLIBC_MAX) floor"; \
fail=1; \
fi; \
done; \
exit $$fail
## Regenerate docs/CLI.md from the binary's own --help.
##
## The page is forty-four commands long; typing it by hand guarantees it is
## wrong the first time a flag changes, and nobody finds out until a reader
## follows it.
cli-doc:
cargo build --release --bin lightforge
sh docs/gen-cli.sh
## Fail if docs/CLI.md is not what the current binary would produce.
##
## Belongs in whatever runs before a release: the generator is only useful if
## somebody notices it was not run.
check-cli-doc: cli-doc
@git diff --quiet -- docs/CLI.md \
|| { echo "docs/CLI.md is out of date — commit the regenerated page"; exit 1; }
@echo "ok docs/CLI.md matches the binary"
## Install a source build the way a package would.
##
## For someone building from the tree: `cargo build --release` leaves two
## binaries and nothing else, so without this there is no menu entry and no
## icon. `PREFIX=$$HOME/.local make install` needs no root.
##
## The three packages do **not** call this — each of them lists the same files
## in its own idiom, because that is what their tooling reads.
install:
install -Dm755 target/release/lightforge-gui $(DESTDIR)$(PREFIX)/bin/lightforge-gui
install -Dm755 target/release/lightforge $(DESTDIR)$(PREFIX)/bin/lightforge
install -Dm644 packaging/pro.tuxcore.Lightforge.desktop \
$(DESTDIR)$(PREFIX)/share/applications/pro.tuxcore.Lightforge.desktop
install -Dm644 packaging/pro.tuxcore.Lightforge.metainfo.xml \
$(DESTDIR)$(PREFIX)/share/metainfo/pro.tuxcore.Lightforge.metainfo.xml
@for size in 16 22 24 32 48 64 128 256 512; do \
install -Dm644 "packaging/icons/hicolor/$${size}x$${size}/apps/pro.tuxcore.Lightforge.png" \
"$(DESTDIR)$(PREFIX)/share/icons/hicolor/$${size}x$${size}/apps/pro.tuxcore.Lightforge.png"; \
done
install -Dm644 packaging/icons/hicolor/scalable/apps/pro.tuxcore.Lightforge.svg \
$(DESTDIR)$(PREFIX)/share/icons/hicolor/scalable/apps/pro.tuxcore.Lightforge.svg
install -Dm644 LICENSE $(DESTDIR)$(PREFIX)/share/licenses/lightforge/LICENSE
@echo "installed to $(DESTDIR)$(PREFIX)"
uninstall:
rm -f $(DESTDIR)$(PREFIX)/bin/lightforge-gui \
$(DESTDIR)$(PREFIX)/bin/lightforge \
$(DESTDIR)$(PREFIX)/share/applications/pro.tuxcore.Lightforge.desktop \
$(DESTDIR)$(PREFIX)/share/metainfo/pro.tuxcore.Lightforge.metainfo.xml \
$(DESTDIR)$(PREFIX)/share/icons/hicolor/*/apps/pro.tuxcore.Lightforge.png \
$(DESTDIR)$(PREFIX)/share/icons/hicolor/scalable/apps/pro.tuxcore.Lightforge.svg
rm -rf $(DESTDIR)$(PREFIX)/share/licenses/lightforge
@echo "removed from $(DESTDIR)$(PREFIX)"
## Everything a release page carries, from one `make release`.
##
## The three package targets deliberately do **not** depend on `release`:
## rebuilding 27 MB of GUI three times to produce three files from the same
## two binaries is a minute nobody needs to spend. Run `make release` first —
## each target checks the binaries are there.
packages: check-spec tarball appimage deb rpm installer checksums
@echo
@echo "Release artifacts in $(DIST):"
@ls -lh $(DIST) | awk 'NR>1 {print " " $$9 " " $$5}'
## The portable tarball: what the `lightforge-bin` PKGBUILD installs, and what
## anyone without a package manager we support downloads.
##
## It carries the desktop entry, the icons and the AppStream file beside the
## binaries, so the PKGBUILD installs from this one source and the Arch
## package has the same contents as the deb and the rpm.
tarball: $(addprefix tarball-,$(ARCHES))
tarball-%: check-binaries-%
@mkdir -p $(DIST)
@rm -rf target/tarball/$*/lightforge-$(VERSION)
@mkdir -p target/tarball/$*/lightforge-$(VERSION)
@cp $(addprefix $(OUT_$*)/,$(BINARIES)) target/tarball/$*/lightforge-$(VERSION)/
@cp -r packaging/icons packaging/pro.tuxcore.Lightforge.desktop \
packaging/pro.tuxcore.Lightforge.metainfo.xml \
LICENSE README.md RELEASES.md TROUBLESHOOTING.md docs/CLI.md \
target/tarball/$*/lightforge-$(VERSION)/
@tar -czf $(DIST)/lightforge-$(VERSION)-$*-linux.tar.gz -C target/tarball/$* lightforge-$(VERSION)
@echo "ok $(DIST)/lightforge-$(VERSION)-$*-linux.tar.gz"
## The AppImage: the runtime with a squashfs of the AppDir appended, which is
## all appimagetool does too. Packed in the build container, so the host needs
## no squashfs-tools. Nothing is bundled besides the two binaries; see AppRun.
appimage: $(addprefix appimage-,$(ARCHES))
appimage-%: check-binaries-% image target/appimage/runtime-20251108-%
@mkdir -p $(DIST)
@rm -rf target/appimage/$*
@install -Dm755 packaging/appimage/AppRun target/appimage/$*/AppDir/AppRun
@install -Dm755 -t target/appimage/$*/AppDir/usr/bin $(addprefix $(OUT_$*)/,$(BINARIES))
@mkdir -p target/appimage/$*/AppDir/usr/share
@cp -r packaging/icons target/appimage/$*/AppDir/usr/share/
@install -Dm644 packaging/pro.tuxcore.Lightforge.metainfo.xml \
target/appimage/$*/AppDir/usr/share/metainfo/pro.tuxcore.Lightforge.metainfo.xml
@# TryExec would hide an entry Gear Lever or AppImageLauncher integrates.
@sed '/^TryExec=/d' packaging/pro.tuxcore.Lightforge.desktop \
> target/appimage/$*/AppDir/pro.tuxcore.Lightforge.desktop
@install -Dm644 target/appimage/$*/AppDir/pro.tuxcore.Lightforge.desktop \
target/appimage/$*/AppDir/usr/share/applications/pro.tuxcore.Lightforge.desktop
@cp packaging/icons/hicolor/256x256/apps/pro.tuxcore.Lightforge.png target/appimage/$*/AppDir/
@ln -s pro.tuxcore.Lightforge.png target/appimage/$*/AppDir/.DirIcon
$(IN_IMAGE) mksquashfs target/appimage/$*/AppDir target/appimage/$*/squashfs \
-root-owned -noappend -comp zstd -quiet
@cat target/appimage/runtime-20251108-$* target/appimage/$*/squashfs > $(DIST)/Lightforge-$*.AppImage
@chmod 755 $(DIST)/Lightforge-$*.AppImage
@echo "ok $(DIST)/Lightforge-$*.AppImage"
# Kept between runs: pinned, so it never changes.
.PRECIOUS: target/appimage/runtime-20251108-%
target/appimage/runtime-20251108-%:
@mkdir -p $(@D)
curl -fsSL -o $@.part $(APPIMAGE_RUNTIME_URL)/runtime-$*
@echo "$(APPIMAGE_RUNTIME_SHA256_$*) $@.part" | sha256sum -c --quiet
@mv $@.part $@
## The double-click installer, published beside the packages so the release
## page has one file a Steam Deck user can take. It carries no version: it
## fetches install.sh from main, which picks the newest release itself.
installer:
@mkdir -p $(DIST)
@cp packaging/installer/lightforge-installer.desktop $(DIST)/
@echo "ok $(DIST)/lightforge-installer.desktop"
## A .deb, written natively — no `dpkg` on this machine.
##
## `CARGO_TARGET_DIR` is what points the asset paths in the manifest at the
## container build; `--no-build` and `--no-strip` because `release` already
## did both.
deb: check-binaries-x86_64
@mkdir -p $(DIST)
CARGO_TARGET_DIR=target/container cargo deb \
--package lightforge-gui \
--no-build --no-strip \
--deb-version '$(PKGVER)-1' \
--output $(DIST)/
## An .rpm, written natively — no `rpmbuild` on this machine either.
rpm: check-binaries-x86_64
@mkdir -p $(DIST)
cargo generate-rpm \
--package crates/launcher-gui \
--target-dir target/container \
--set-metadata 'version = "$(PKGVER)"' \
--set-metadata 'release = "1"' \
--output $(DIST)/
## Rename the packages to the names GitHub will actually serve them under.
##
## A release asset may not have a `~` in its name: GitHub rewrites it to `.`,
## so `lightforge_1.0.0~beta.1-1_amd64.deb` is downloaded as
## `lightforge_1.0.0.beta.1-1_amd64.deb` — and a `SHA256SUMS` listing the name
## we built fails `sha256sum -c` for everyone. Renaming here means what is
## built is exactly what is published. Only the filename changes; the version
## *inside* the package is still `1.0.0~beta.1`, which is what `apt` and `dnf`
## read and order by.
github-names:
@for f in $(DIST)/*~*; do \
[ -e "$$f" ] || continue; \
mv "$$f" "$$(echo "$$f" | tr '~' '.')"; \
done
## The sums the AUR PKGBUILDs and the release page quote.
checksums: github-names
@cd $(DIST) && rm -f SHA256SUMS && sha256sum lightforge* Lightforge-*.AppImage > SHA256SUMS
@cat $(DIST)/SHA256SUMS
## Fail with a useful sentence rather than a missing-file error from tar,
## and refuse binaries built from another version: packages are named from
## Cargo.toml, so stale binaries would ship under the new name (beta.2 nearly
## did). The timeout is for a GUI built before it answered `--version`,
## which would open a window instead and hold the build.
check-binaries: $(addprefix check-binaries-,$(ARCHES))
## The aarch64 build answers through qemu in the container: the same question,
## and a first sign it starts at all.
check-binaries-%:
@for bin in $(addprefix $(OUT_$*)/,$(BINARIES)); do \
[ -f "$$bin" ] || { echo "missing $$bin — run \`make release\` first"; exit 1; }; \
got=$$(timeout 60 $(RUN_$*) "$$bin" --version 2>/dev/null); \
want="$$(basename "$$bin") $(VERSION)"; \
[ "$$got" = "$$want" ] || { echo "$$bin reports '$$got', expected '$$want' — run \`make release\`"; exit 1; }; \
done
@echo "ok $* binaries are $(VERSION)"
## The COPR spec carries its version by hand, because COPR builds it from git
## and never sees this Makefile. Caught here so a release cannot forget it.
check-spec:
@got=$$(sed -n 's/^%global upstream_version //p' packaging/rpm/lightforge.spec); \
[ "$$got" = "$(VERSION)" ] || { echo "packaging/rpm/lightforge.spec is $$got, Cargo.toml is $(VERSION)"; exit 1; }
@echo "ok spec is $(VERSION)"
clean-packages:
rm -rf $(DIST) target/tarball target/debian target/container/generate-rpm $(addprefix target/appimage/,$(ARCHES))
clean-release:
rm -rf target/container