AtlatestRepositorycore-channel
core-channel / tree / src / core / packagesfonts.sgl
1
;;; Font rasterisation and font configuration.2
;;;3
;;; This is the half of foot's closure that has to reach OUTSIDE the store to4
;;; be useful, and it is the one place in this chain where "hermetic" and5
;;; "works on the user's desktop" pull against each other. Wayland does not:6
;;; a client and a compositor exchange a wire protocol over a socket and never7
;;; share a library, so version skew there is a non-issue. Fonts are8
;;; different -- a fontconfig built into the store looks for font FILES, and9
;;; the font files a user has are wherever their system put them.10
;;;11
;;; Nothing here solves that; it is recorded so the next reader does not12
;;; mistake a font-path gap for a foot defect. See the task note for the13
;;; measured runtime behaviour.14
(define-library (core packages fonts)15
(import (sigil core)16
(core build-environments)17
(core packages compression)18
(core packages xml)19
(core packages graphics)20
(core packages datastructures)21
(core packages build-tools)22
(core packages python)23
(sigil env package)24
(sigil build external))25
(export freetype fontconfig fcft)26
(begin27
;; freetype, with every optional backend OFF.28
;;29
;; The harfbuzz one is not a size decision, it is a CYCLE: harfbuzz builds30
;; against freetype and freetype can build against harfbuzz for31
;; auto-hinting. Distributions resolve it by building freetype twice.32
;; This channel does not need to, because nothing here does complex text33
;; shaping -- foot is configured without grapheme clustering, which is what34
;; would have pulled harfbuzz in through fcft.35
(define freetype36
(package37
name: "freetype"38
version: "2.13.3"39
source: (external-source40
location: "https://download.savannah.gnu.org/releases/freetype/freetype-2.13.3.tar.xz"41
sha256: "0550350666d427c74daeb85d5ac7bb353acba5f76956395995311a9c6f063289")42
buildenv: core-buildenv43
dependencies: (list zlib)44
phases: (list45
(external-phase46
kind: 'configure47
args: (list "--prefix=${out}"48
"--disable-shared"49
"--enable-static"50
"--with-zlib=yes"51
"--with-bzip2=no"52
"--with-png=no"53
"--with-brotli=no"54
"--with-harfbuzz=no"))55
(external-phase kind: 'make)56
(external-phase kind: 'install)57
;; fontconfig asks for `freetype2 >= 21.0.15` through58
;; PKG_CHECK_MODULES and fcft asks for it too, so the `.pc`59
;; is load-bearing rather than decorative. Note the version60
;; in that constraint is freetype's LIBTOOL version, not its61
;; release version -- 2.13.3 publishes 26.x -- which is why62
;; the check below asserts the file exists and does not try63
;; to be clever about its contents.64
(external-phase65
kind: 'run66
args: (list "sh" "-c"67
(string-append68
"set -eu\n"69
"d=\"$(dirname \"$PWD\")/install-root\"\n"70
"test -f \"$d${out}/lib/pkgconfig/freetype2.pc\"\n"71
"echo FREETYPE2-PC-PRESENT\n"))))))73
;; VERSION AND SOURCE, because both were forced and the reasoning matters.74
;;75
;; `www.freedesktop.org/software/fontconfig/release/` -- the URL every76
;; older recipe uses -- was unreachable from this host across three77
;; attempts spanning ten minutes. Upstream has moved distribution to78
;; the freedesktop GitLab generic package registry, which is where79
;; Alpine now points too, and that registry does NOT carry 2.15.0.80
;;81
;; So the choice was between 2.15.0 from a DISTRIBUTION MIRROR and82
;; 2.18.1 from the CANONICAL upstream host. Taking the canonical host83
;; keeps a real upstream release artifact on the critical path, which84
;; is the rule this channel is built on, and it is a current release85
;; rather than a two-year-old one.86
;;87
;; 2.18.1 SHIPS BOTH BUILD SYSTEMS, and this build deliberately uses88
;; the autotools one. Its meson.build pulls in Rust for the optional89
;; `fc-fontations` backend (meson.build:92, add_languages([rust]));90
;; configure.ac mentions Rust nowhere. Autotools therefore avoids91
;; introducing a whole second language toolchain to this channel for a92
;; feature foot does not use.93
;;94
;; fontconfig. Three build-time tools and two libraries, and the tool list95
;; is the interesting part:96
;;97
;; gperf -- configure.ac:216 RUNS it: `echo 'foo' | gperf -L ANSI-C`.98
;; Absent, the substitution is empty, the two compile probes99
;; that follow test an empty snippet, the first succeeds, and100
;; configure concludes FC_GPERF_SIZE_T=size_t from a probe101
;; that measured nothing.102
;; python -- configure.ac:56 is AM_PATH_PYTHON([3]).103
;;104
;; Both are `build-inputs:` and not `dependencies:`, because neither is105
;; linked into the result and neither may contribute a store path to it.106
(define fontconfig107
(package108
name: "fontconfig"109
version: "2.18.1"110
source: (external-source111
location: "https://gitlab.freedesktop.org/api/v4/projects/890/packages/generic/fontconfig/2.18.1/fontconfig-2.18.1.tar.xz"112
sha256: "2300f3dbfa7253b3a44f4feecdbc8dfa45dde5dc2cfb71fceaf31f394cb41031")113
buildenv: core-buildenv114
;; ZLIB IS DECLARED HERE ALTHOUGH FONTCONFIG NEVER MENTIONS IT, AND THE115
;; REASON IS A PROPERTY OF THIS CHANNEL RATHER THAN OF FONTCONFIG.116
;;117
;; `dependency-pkgconfig` in (sigil build external) composes118
;; PKG_CONFIG_PATH from DIRECT dependencies only -- one119
;; `<dep>/lib/pkgconfig` entry each, with no transitive walk. freetype's120
;; generated `freetype2.pc` carries `Requires: zlib`, so pkg-config121
;; resolving `freetype2` must also resolve `zlib`, and without this line122
;; it cannot:123
;;124
;; checking for FREETYPE... no125
;; configure: error: Package requirements (freetype2 >= 21.0.15) were126
;; not met: Package 'zlib', required by 'freetype2', not found127
;;128
;; THE DIAGNOSTIC NAMES FREETYPE AND THE MISSING PACKAGE IS ZLIB, which129
;; is the part that costs the time.130
;;131
;; This is not a workaround. It is the same rule the builder applies to132
;; `${name}` interpolation -- a build that wants to reach a133
;; grand-dependency has to declare it -- and it is TRUE here: everything134
;; in this channel links statically, so zlib really is on fontconfig's135
;; link line and really is in its closure. Every consumer of a `.pc`136
;; with a `Requires:` chain inherits this.137
dependencies: (list freetype expat zlib)138
build-inputs: (list gperf python)139
phases: (list140
(external-phase141
kind: 'configure142
args: (list "--prefix=${out}"143
"--disable-shared"144
"--enable-static"145
;; The docs want docbook and would otherwise be146
;; a probe on what is installed.147
"--disable-docs"148
;; Nothing in this channel reads a cache149
;; fontconfig would have to build at install150
;; time, and running fc-cache during a build151
;; would write outside the staged prefix.152
"--disable-cache-build"153
;; NLS wants gettext, which is not in the seed154
;; and which nothing here reads.155
"--disable-nls"))156
(external-phase kind: 'make)157
(external-phase kind: 'install)158
(external-phase159
kind: 'run160
args: (list "sh" "-c"161
(string-append162
"set -eu\n"163
"d=\"$(dirname \"$PWD\")/install-root\"\n"164
"test -f \"$d${out}/lib/pkgconfig/fontconfig.pc\"\n"165
;; The config file is what a runtime consumer166
;; needs and what the font-path problem above is167
;; about; assert it landed so a missing one is168
;; found here rather than at first launch.169
"test -f \"$d${out}/etc/fonts/fonts.conf\"\n"170
"echo FONTCONFIG-PC-AND-CONF-PRESENT\n"))))))172
;; fcft: the glyph rasterisation and caching layer between foot and173
;; freetype/fontconfig. By the same author as foot, and foot's meson.build174
;; carries a `[wrap-git]` fallback for it -- which cannot resolve inside a175
;; sandbox, so a real package is required rather than optional.176
;;177
;; TEXT SHAPING IS OFF, AND THAT IS THE LARGEST SCOPE DECISION IN THIS178
;; CHAIN. `grapheme-shaping` and `run-shaping` each pull in harfbuzz, and179
;; run-shaping additionally pulls utf8proc. Turning both off removes two180
;; packages, and harfbuzz would additionally have re-opened the181
;; freetype/harfbuzz build cycle that `freetype` above is configured to182
;; avoid.183
;;184
;; WHAT IS LOST, said plainly rather than buried: without shaping, foot185
;; renders each grapheme cluster from a single font glyph. Combining186
;; sequences -- emoji built from ZWJ sequences, some scripts with complex187
;; joining behaviour -- will not compose. Latin, Cyrillic, Greek, CJK and188
;; box drawing are unaffected. Turn it back on by packaging harfbuzz and189
;; utf8proc and flipping these two options here and in `foot`.190
;;191
;; The SVG backend is left at its default, which uses the bundled nanosvg192
;; and therefore costs no package.193
(define fcft194
(package195
name: "fcft"196
version: "3.3.3"197
source: (external-source198
location: "https://codeberg.org/dnkl/fcft/releases/download/3.3.3/fcft-3.3.3.tar.gz"199
sha256: "bb298772d625e7d917373d541456f34f1957a9e22b16f17f64158b2c3816563c")200
buildenv: core-buildenv201
;; zlib is here for the reason spelled out on fontconfig above: it is202
;; reached through freetype2.pc's `Requires:` line, and this channel203
;; composes PKG_CONFIG_PATH from direct dependencies only.204
dependencies: (list fontconfig freetype pixman tllist expat zlib)205
build-inputs: (list meson samurai python)206
phases: (list207
(external-phase208
kind: 'run209
args: (list "sh" "-c"210
(string-append211
"set -eu\n"212
"meson setup build"213
" --prefix=${out}"214
" --buildtype=plain"215
" --default-library=static"216
;; `-Dprefer_static=true` MAKES MESON ASK217
;; PKG-CONFIG THE STATIC QUESTION, and without it218
;; a fully static link fails at the last step.219
;;220
;; A `.pc` file splits its link line: `Libs:` is221
;; what a SHARED consumer needs, `Libs.private:`222
;; and `Requires.private:` are the extra223
;; transitive libraries a STATIC consumer needs.224
;; fontconfig lists expat under225
;; `Requires.private:`, so a default226
;; `dependency('fontconfig')` emits no `-lexpat`227
;; -- correct for a shared link, fatal here:228
;;229
;; ld.lld: error: undefined symbol: XML_ErrorString230
;; >>> referenced by fcxml.c231
;; >>> fcxml.o in archive libfontconfig.a232
;;233
;; The symbol is missing from the LINK, not from234
;; the closure: expat is declared, built, and235
;; present. Only the query was the wrong one.236
;; This is the same `Requires.private:` line that237
;; had to be satisfied at CONFIGURE time; it bites238
;; twice, in two different ways, and fixing the239
;; first does not fix the second.240
" -Dprefer_static=true"241
" -Dgrapheme-shaping=disabled"242
" -Drun-shaping=disabled"243
" -Ddocs=disabled"244
" -Dexamples=false"245
" -Dtest-text-shaping=false\n"246
"echo FCFT-CONFIGURED\n")))247
(external-phase248
kind: 'run249
args: (list "sh" "-c"250
(string-append251
"set -eu\n"252
"meson compile -C build\n"253
"echo FCFT-COMPILED\n")))254
(external-phase255
kind: 'run256
args: (list "sh" "-c"257
(string-append258
"set -eu\n"259
"d=\"$(dirname \"$PWD\")/install-root\"\n"260
"DESTDIR=\"$d\" meson install -C build --no-rebuild\n"261
"test -f \"$d${out}/lib/pkgconfig/fcft.pc\"\n"262
;; foot pins fcft to >=3.3.1 <4.0.0, so the263
;; version pkg-config reports is load-bearing and264
;; is asserted rather than assumed.265
;; `--modversion` PRINTS a version; it does not266
;; CHECK one, and it exits 0 for any value. foot267
;; pins fcft to `>=3.3.1 <4.0.0`, so assert that268
;; range with the options that actually compare.269
;; Printing it as well keeps the value in the log.270
"v=$(PKG_CONFIG_PATH=\"$d${out}/lib/pkgconfig\" pkg-config --modversion fcft)\n"271
"echo \"FCFT-VERSION=[$v]\"\n"272
"PKG_CONFIG_PATH=\"$d${out}/lib/pkgconfig\" pkg-config --atleast-version=3.3.1 fcft\n"273
"PKG_CONFIG_PATH=\"$d${out}/lib/pkgconfig\" pkg-config --max-version=3.99.99 fcft\n"274
"echo FCFT-INSTALLED\n"))))))))