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:
- Each raw function in
cl-icu/ffiis defined withdeficu, which records the C name without the suffix and calls through a function pointer. load-iculoadslibicuuc, finds the suffix the loaded library uses (probe-symbol-suffix), and looks up every recorded function under that suffix.load-icuruns 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.