11 Development
dxtr edited this page 2026-10-18 16:20:21 +02:00

Development

Layout

cl-harfbuzz.asd          three systems: cl-harfbuzz, cl-harfbuzz/ft2, cl-harfbuzz/test
src/
  package.lisp           cl-harfbuzz/ffi and cl-harfbuzz packages
  ffi-library.lisp       locating and loading libharfbuzz (bundled DLL on Windows)
  ffi-types.lisp         scalar types, enums, bitfields
  ffi-structs.lisp       hand-written struct layouts
  ffi-functions.lisp     defcfun for every bound C function
  conditions.lisp        harfbuzz-error
  common.lisp            tags, languages, scripts, directions, colors, version
  face.lisp font.lisp buffer.lisp shape.lisp
  draw.lisp              glyph-outline (draw callbacks)
  paint.lisp             glyph-paint (paint callbacks)
  ot.lisp                names, metrics, layout tags, variations, color tables
ft2/                     the cl-harfbuzz/ft2 bridge
native/
  build-windows.sh       reproducible cross-build of harfbuzz.dll
  windows-x64/           harfbuzz.dll and HarfBuzz's license
tests/                   parachute tests and test fonts

The two layers follow cl-ft2 and cl-net: a raw */ffi package that transcribes C, and an idiomatic package on top. Struct layouts use cl-ffi's C-sized types, written by hand, so nothing is compiled.

Dependencies

Pinned in the stiftfile with stift: cl-ffi and cl-ft2, each at a commit of its main branch, and Parachute, with the four libraries it needs, for the tests.

After a change to cl-ffi or cl-ft2 is pushed, move the pin with stift work sync at the workspace's root, or stift update https://fakehacker.cc/dxtr/cl-ft2.git here, and commit the stiftfile.

Running the tests

stift test

asdf:test-system signals an error if any test fails, so the command exits unsuccessfully.

The suite (cl-harfbuzz/test, parachute) covers faces, fonts, shaping, outlines, paint, OpenType data, argument checking, the exported types, loading the library and the cl-ft2 bridge: 51 tests, 402 checks. shapes-without-cl-ft2 loads cl-harfbuzz alone in a fresh SBCL, which it gives the suite's own source registry and output translations, so the child loads the fasls stift built for this build list and never ones another run built against other versions of the dependencies.

The tests need:

What Debian / Ubuntu Windows
HarfBuzz libharfbuzz0b bundled
FreeType (for the bridge tests) libfreetype6 bundled with cl-ft2
DejaVu Sans fonts-dejavu-core install DejaVu Sans

The exact expectations are DejaVu Sans's. To use it from another location, set CL_HARFBUZZ_TEST_FONT to its path.

Test fonts

  • tests/fonts/cl-harfbuzz-test-color.ttf is built by tests/fonts/make-test-color-font.py (needs fontTools). Its docstring specifies the font: 1000 units per EM; glyphs box and bar; A with COLRv0 layers, B with a COLRv1 linear gradient, C plain; two CPAL palettes. The color tests are written against that description.
  • The variable-font tests use cl-ft2's tests/fonts/cl-ft2-test-variable.ttf (axes wght and wdth, instances Thin, Regular, Black), found through ASDF.

The Windows DLL

native/windows-x64/harfbuzz.dll is HarfBuzz 14.6.0, cross-compiled on Linux with MinGW-w64 by native/build-windows.sh:

apt install g++-mingw-w64-x86-64 curl xz-utils
native/build-windows.sh            # rebuild windows-x64/harfbuzz.dll
native/build-windows.sh --check    # rebuild and fail unless identical to the committed DLL
  • Sources (HarfBuzz and FreeType's headers) are downloaded and checked against pinned SHA-256 sums.
  • It is HarfBuzz's single-file build (harfbuzz.cc), with hb-ft and without glib, ICU, Graphite or the Windows shapers. Only HarfBuzz's public API is exported.
  • FreeType is delay-loaded: the script generates a delay-load import library for exactly the FT_ functions hb-ft calls, so harfbuzz.dll loads without freetype.dll. Keep FREETYPE_VERSION equal to the version cl-ft2 builds.
  • The build is reproducible (no paths or timestamps in the binary), which is what --check relies on.

To update HarfBuzz: change HARFBUZZ_VERSION and HARFBUZZ_SHA256, run the script, run the tests on Windows, and commit the DLL and HARFBUZZ-COPYING.

CI

Forgejo Actions, .forgejo/workflows/ci.yml, on pushes to main and pull requests, in a debian:forky container:

  • test: fakehacker/actions/stift-ci@v1, the action's major tag, which follows its compatible releases, on SBCL 2.6.9, installing libharfbuzz0b libfreetype6 fonts-dejavu-core, then running stift test, which fails unless the suite passes. No C toolchain or headers are needed.
  • windows-dll: installs MinGW-w64 and runs native/build-windows.sh --check. A MinGW-w64 update can change the bytes; then rebuild the DLL and commit it.

Conventions

  • Every failure is a harfbuzz-error whose message says what went wrong in terms of the Lisp call and whose hint says what to do; argument type problems are type-errors. See Errors.
  • Every exported function declaims its ftype directly above its defun and checks its arguments with check-type; a list of allowed keywords is a deftype of (member …). Every definition, internal ones included, has a docstring.
  • Structs and classes document their fields in their own docstring, not per accessor.
  • New C bindings go in ffi-functions.lisp and the cl-harfbuzz/ffi export list, named after the C function.