blob: 630ebeb8d5697763fd5b4f046d9e57dcd6d1bfa3 [file] [log] [blame] [view]
---
layout: default
title: Plug-ins
nav_order: 4
parent: ICU4C
---
<!--
© 2020 and later: Unicode, Inc. and others.
License & terms of use: http://www.unicode.org/copyright.html
-->
# Plug-ins
{: .no_toc }
## Contents
{: .no_toc .text-delta }
1. TOC
{:toc}
---
## Overview
This page documents the ICU4C DLL Plug-in capability.
This feature is a Technology Preview which first appeared in ICU4C version
4.3.4. It may be altered or removed in subsequent releases, and feedback is
appreciated.
## Off by default
As per ticket [ICU-11763](https://unicode-org.atlassian.net/browse/ICU-11763), the plugin
mechanism discussed here is disabled by default as of ICU 56. Use
**--enable-plugins** and/or define **UCONFIG_ENABLE_PLUGINS=1** to enable the
mechanism.
## Background
ICU4C has functionality for registering services, setting
mutex/allocation handlers, etc. But they must be installed 'before any
ICU services are used'
The ICU plugin mechanism allows small code modules, called plugins, to be loaded
automatically when ICU starts.
## How it works
At u_init time, ICU will read from a list of DLLs and entrypoints, and
attempt to load plugins found in the list. plugins are called and can
perform any ICU related function, such as registering or unregistering
service objects. At u_cleanup time, plugins have the opportunity to
uninstall themselves before they are removed from memory and unloaded.
## Plugin API
The current plugin API is documented as
[icuplug.h](https://unicode-org.github.io/icu-docs/apidoc/released/icu4c/icuplug_8h.html)
Some sample plugins are available at:
[testplug.c](https://github.com/unicode-org/icu/blob/main/icu4c/source/tools/icuinfo/testplug.c)
Here is a simple, trivial plugin:
```c
U_CAPI
UPlugTokenReturn U_EXPORT2 myPlugin (UPlugData *data, UPlugReason reason, UErrorCode *status) {
if(reason==UPLUG_REASON_QUERY) {
uplug_setPlugName(data, "Simple Plugin"); /* optional */
uplug_setPlugLevel(data, UPLUG_LEVEL_HIGH); /* Mandatory */
} else if(reason==UPLUG_REASON_LOAD) {
/* ... load ... */
/* Set up some ICU things here. */
} else if(reason==UPLUG_REASON_UNLOAD) {
/* ... unload ... */
}
return UPLUG_TOKEN; /* Mandatory. */
}
```
The `UPlugData*` is an opaque pointer to the plugin-specific data,
and is used in all other API calls.
The API contract is:
1. the plugin MUST always return UPLUG_TOKEN as a return value- to
indicate that it is a valid plugin.
2. when the 'reason' parameter is set to UPLUG_REASON_QUERY, the
plugin MUST call `uplug_setPlugLevel()` to indicate whether it is a high
level or low level plugin.
3. when the 'reason' parameter is UPLUG_REASON_QUERY, the plugin
SHOULD call `uplug_setPlugName` to indicate a human readable plugin name.
## Configuration
You can see a sample configuration file here:
[icuplugins_windows_sample.txt](https://github.com/unicode-org/icu/blob/main/icu4c/source/tools/icuinfo/icuplugins_windows_sample.txt)
At ICU startup time, the environment variable "ICU_PLUGINS" will be
queried for a directory name. If it is not set, the #define
`DEFAULT_ICU_PLUGINS` will be checked for a default value.
`DEFAULT_ICU_PLUGINS` will be set, on autoconf'ed and installed ICU
versions, to "$(prefix)/lib/icu" if not set otherwise by the build
environment.
Within the above-named directory, the file "icuplugins##.txt" will be
opened, if present, where _##_ is the major+minor number of the currently
running ICU (such as, 44 for ICU 4.4).
So, for example, by default, ICU 4.4 would attempt to open
`$(prefix)/lib/icu/icuplugins44.txt`
The configuration file has the following format:
1. Hash (#) begins a comment line
2. Non-comment lines have two or three components:
> `LIBRARYNAME ENTRYPOINT [ CONFIGURATION .. ]`
3. Tabs or spaces separate the three items.
4. _LIBRARYNAME_ is the name of a shared library, either a short name if
it is on the loader path, or a full pathname.
5. _ENTRYPOINT_ is the short (undecorated) symbol name of the plugin's
entrypoint, as above.
6. _CONFIGURATION_ is the entire rest of the line. It's passed as-is to
the plugin.
An example configuration file is, in its entirety:
```
# this is icuplugins44.txt
testplug.dll myPlugin hello=world
```
The DLL testplug.dll is opened, and searched for the entrypoint
"myPlugin", which must meet the API contract above.
The string "hello=world" is passed to the plugin verbatim.
## Load Order
Plugins are categorized as "high" or "low" level. Low level are those
which must be run BEFORE high level plugins, and before any operations
which cause ICU to be 'initialized'. If a plugin is low level but
causes ICU to allocate memory or become initialized, that plugin is said
to cause a 'level change'.
At load time, ICU first queries all plugins to determine their level,
then loads all 'low' plugins first, and then loads all 'high' plugins.
Plugins are otherwise loaded in the order listed in the configuration file.
## User interface and troubleshooting
The new command line utility, `icuinfo`, will not only print out ICU
version information, but will also give information on the load status
of plugins, with the "-L" option. It will list all loaded or
possibly-loaded plugins, give their level, and list any errors
encountered which prevented them from loading. Thus, the end user can
validate their plugin configuration file to determine if plugins are
missing, unloadable, or loaded in the wrong order.
For example the following run shows that the plugin named
"myPluginFailQuery" did not call `uplug_setPlugLevel()` and thus failed to
load.
```
$ icuinfo -v -L
Compiled against ICU 4.3.4, currently running ICU 4.3.4
ICUDATA is icudt43l
plugin file is: /lib/plugins/icuplugins43.txt
Plugins:
# Level Name
Library:Symbol
config| (configuration string)
>>> Error | Explanation
-----------------------------------
#1 HIGH Just a Test High-Level Plugin
plugin| /lib/plugins/libplugin.dylib:myPlugin
config| x=4
#2 HIGH High Plugin
plugin| /lib/plugins/libplugin.dylib:myPluginHigh
config| x=4
#3 INVALID this plugin did not call uplug_setPlugName()
plugin| /lib/plugins/libplugin.dylib:myPluginFailQuery
config| uery
\\\ status| U_PLUGIN_DIDNT_SET_LEVEL
/// Error: This plugin did not call uplug_setPlugLevel during QUERY.
#4 LOW Low Plugin
plugin| /lib/plugins/libplugin.dylib:myPluginLow
config| x=4
Default locale is en_US
Default converter is UTF-8.
```