6 ICU Versions and Saved Images
dxtr edited this page 2026-10-18 15:24:07 +02:00

ICU versions and saved images

The problem: versioned symbols

ICU4C is normally built with renaming: every exported C function has the library's major version appended. ICU 78 exports ubidi_open_78, ICU 74 exports ubidi_open_74. The shared library's file name carries the same version (libicuuc.so.78). Bindings that hard-code the suffix work with one ICU version only, and an image saved on one machine breaks on a machine with another ICU.

Some builds are configured without renaming and export plain names (ubidi_open). Windows' own icu.dll is one of them.

What cl-icu does

Nothing about the ICU version is fixed when cl-icu is compiled. Instead:

  1. Each raw function in cl-icu/ffi is defined with deficu, which records the C name without the suffix and calls through a function pointer.
  2. load-icu loads libicuuc, finds the suffix the loaded library uses (probe-symbol-suffix), and looks up every recorded function under that suffix.
  3. load-icu runs when cl-icu is loaded, and again when an image saved with cl-icu starts, through an image start hook.
cl-icu/ffi:*symbol-suffix*          ; => "_78"   ("" for an unrenamed build or Windows)
(cl-icu/ffi:probe-symbol-suffix)    ; => "_78"
(icu:icu-version)                   ; => "78.3"

So:

  • An ICU upgrade needs no recompile. The next time cl-icu is loaded, it finds the new suffix.
  • A saved image uses the ICU of the machine it runs on, which may differ from the one it was built on. The test suite checks this by saving an image and starting it.
  • Any ICU from 52 on works. Every function cl-icu binds exists in ICU 52.

Which library is loaded

Platform Tried in order
Linux and other Unix libicuuc.so (the development symlink), then libicuuc.so.99 down to libicuuc.so.50
macOS libicuuc.dylib, then libicuuc.99.dylib down to libicuuc.50.dylib
Windows icu.dll (Windows 10 version 1903 and later)

With several ICU versions installed and no development symlink, the newest is used. (cl-ffi:foreign-library-pathname 'cl-icu/ffi:libicuuc) says which file was loaded.

Saved images

When a saved image starts, cl-ffi loads libicuuc again, by the same search as at load time, and then runs the hooks added with cl-ffi:add-image-start-hook, of which load-icu is one. So cl-icu has no implementation-specific code.

Looking the functions up again

cl-ffi never closes a library, so the libicuuc cl-icu found stays loaded for the life of the process, and the function pointers stay good. (cl-icu/ffi:load-icu) looks them up again; it is safe to call at any time, but rewrites the function table, so call it while no other thread is using cl-icu.

Errors at load time

Message Hint Cause
cl-icu could not load ICU: … (each file tried, and why it failed) Install ICU's common library: on Debian or Ubuntu, libicu-dev or a versioned runtime package such as libicu76. no libicuuc found. See Installation. On Windows the hint is to update Windows.
cl-icu loaded ICU's common library, but cannot find u_getVersion in it, either without a version suffix or with one from _50 to _99. cl-icu works with ICU 52 to 99: install one of those. a library outside the range cl-icu looks for, or not ICU
The loaded ICU has no function …_51. cl-icu's own functions need ICU 52 or newer. For a function you bound with DEFICU, check its name and the ICU version that introduced it. an ICU older than 52, which lacks a function cl-icu binds, or a deficu of a function this ICU does not have

Each is an icu-error (see Errors), with no icu-error-code. They are signalled while loading cl-icu, when a saved image starts, by load-icu, and, for the last, when a deficu definition is loaded. A deficu that fails registers nothing: the functions bound before keep working, and load-icu does not look for the missing one again.

Versions and results

The Unicode version comes with ICU, so results can change with an ICU upgrade: new characters get properties, new emoji become single graphemes, line-break and segmentation rules are refined. icu-version and unicode-version report what is in use:

(icu:icu-version)       ; => "78.3"
(icu:unicode-version)   ; => "17.0"

On Windows these are whatever ICU the installed Windows ships.