--- layout: docu redirect_from: - /docs/current/dev/building/linking_extensions - /docs/lts/dev/building/linking_extensions - /docs/preview/dev/building/linking_extensions title: Statically Linking Extensions --- This page is for client and binding authors, and for anyone who embeds `libduckdb` in their own application. It describes how statically linking extensions changed in DuckDB 2.0 and what builders on top of DuckDB need to do. > This change is independent of the new C API. It applies even if you continue to build against the existing (v1) C API: the difference is in how extension archives are linked and registered, not in which API your bindings call. ## What Changed in DuckDB 2.0 > New Link-time static linking of extensions was introduced in DuckDB 2.0. Before v2.0, the set of statically linked extensions was a _compile-time_ decision. The sources were compiled with knowledge of which extensions were included, so a build with `CORE_EXTENSIONS='json;parquet;icu'` could not reuse the object files of a build with `CORE_EXTENSIONS='json;parquet;icu;tpch'`, even though one is a subset of the other. DuckDB 2.0 modularized this into a _link-time_ decision ([`duckdb#25814`](https://github.com/duckdb/duckdb/pull/25814)). Each extension is compiled once into its own static archive, for example: ```text libduckdb_static.a libcore_functions_extension.a libparquet_extension.a libjson_extension.a libicu_extension.a ``` The list of extensions to link in is now chosen when linking, not when compiling. As a follow-up, the extension loader and the built-in `httplib` also became opt-in at link time ([`duckdb#26188`](https://github.com/duckdb/duckdb/pull/26188)). Two aspects that earlier builds relied on are gone: * The `bundle-library` Makefile target that merged everything into a single `libduckdb_bundle.a`. Extensions, `httplib`, and loadable extensions are now separate archives. * The generated loader that `libduckdb_static.a` used to register extensions automatically. `libduckdb_static.a` no longer registers extensions on its own. ## Registering Statically Linked Extensions Because extension selection moved to link time, a client that statically links extensions must now _register_ them with the engine before any database is opened. Each extension archive carries a generated describe function, `duckdb_extension_⟨name⟩_describe`, that fills a `duckdb_extension_descriptor` declared in the new public header `duckdb_static_extension.h`. The descriptor carries a layout version, so the engine refuses an archive whose descriptor layout it does not support, with a clear message instead of misbehaving. The engine exposes a C API call, `duckdb_register_static_extension(describe)`, which validates the descriptor and registers the extension for every database opened afterwards. A generated loader (`static_extension_loader.c`) defines `duckdb_register_static_extensions()`, which passes every linked extension's describe function to `duckdb_register_static_extension` in turn. Call it once at startup, before any database is opened, from any language that can call C: ```cpp // Register the statically linked extensions before any database can be opened if (duckdb_register_static_extensions() != 0) { throw std::runtime_error("Failed to register the statically linked DuckDB extensions"); } ``` If registration is skipped, the linked extensions (such as `core_functions`, `icu`, `json`, and `parquet`) are not loaded. Queries that depend on them then fail at runtime. For example, `SELECT to_minutes(5)` reports that the function exists in the `core_functions` extension, even though that extension was linked in. A program can also skip the generated loader and call `duckdb_register_static_extension` directly on the describe functions it declares itself. An archive whose describe function is never registered contributes nothing, and a registered extension whose archive is missing fails the link. ### Calling the Loader Before `main` A C++ program that wants registration to happen automatically before `main` compiles `extension/loader/static_extension_autoregister.cpp` alongside the loader. Compile it into the program itself, not into an archive, or the linker drops it. If you do package the loader and the auto-register object into an archive, keep the registration symbol from being discarded by forcing it onto the link line: ```bash -Wl,-u,duckdb_register_static_extensions ``` ## Build Options When building DuckDB or `libduckdb`, select the extensions to compile with `BUILD_EXTENSIONS` (or `CORE_EXTENSIONS`): ```bash BUILD_EXTENSIONS='core_functions;json;parquet;icu' GEN=ninja make ``` Which of the built extensions are statically linked is a separate decision. By default the DuckDB targets link the extensions that the build configs mark for static linking, plus the ones named in `BUILD_EXTENSIONS` when you build with `LINK_CORE_EXTENSIONS=1`. To link an explicit set instead, set `STATICALLY_LINK_EXTENSIONS` (the older name for this flag is `LINK_EXTENSIONS`): ```bash STATICALLY_LINK_EXTENSIONS='core_functions;parquet;json;icu;tpch' make ``` Changing the linked set only re-generates the loader, compiles it, and re-links; it does not recompile the extensions themselves. An extension that is built but not statically linked is still available: it is installed from the build's own extension repository and loaded the first time a query needs it. ### Linking the Archives by Hand Outside CMake, link one archive per extension, compile the generated loader, and put the engine archive last: ```bash LINK_EXTENSIONS='parquet;json' make static_extension_loader cc -I src/include main.c build/release/static_extension_loader.c libparquet_extension.a libjson_extension.a libduckdb_static.a -lstdc++ ``` The order matters: each extension archive must come before `libduckdb_static.a` on the link line. See [Build Configuration]({% link docs/2.0/dev/building/build_configuration.md %}) and [Building Extensions]({% link docs/2.0/dev/building/building_extensions.md %}) for the full set of flags. ## Obtaining Prebuilt Archives Rather than building `libduckdb` yourself, you can download the static and shared archives for DuckDB 2.0 and later using the [`duckdb-install-scripts`](https://github.com/duckdb/duckdb-install-scripts) tooling ([`duckdb-install-scripts#5`](https://github.com/duckdb/duckdb-install-scripts/pull/5)). Setting `DUCKDB_INSTALL=static` installs the archives and headers under `~/.duckdb/lib/⟨version⟩/⟨build_type⟩_⟨platform⟩` (e.g., `~/.duckdb/lib/2.0.0/release_osx_arm64`): ```bash curl https://install.duckdb.org | DUCKDB_INSTALL=static bash ``` Use `DUCKDB_INSTALL=shared` for the shared library, or combine components, e.g., `DUCKDB_INSTALL=static,shared,cli`. See the [install script page]({% link docs/2.0/operations_manual/installing_duckdb/install_script.md %}) for details. This installs, for example: ```text duckdb.h duckdb_v2.h duckdb_extension.h duckdb_extension_v2.h duckdb_static_extension.h libduckdb_static.a libcore_functions_extension.a libparquet_extension.a libjson_extension.a libicu_extension.a libautocomplete_extension.a libtpch_extension.a libtpcds_extension.a ``` A specific release can be installed by setting `DUCKDB_VERSION` (e.g., `DUCKDB_VERSION=2.0.0`). Static and shared libraries are only available for DuckDB 2.0 and later. > For DuckDB 2.0 and later, most artifacts are distributed as `.tar.gz` files instead of `.zip` files ([`duckdb#24773`](https://github.com/duckdb/duckdb/pull/24773)). ## Reference Implementations for Clients The following pull requests adapt the official clients to the v2.0 linking model. They provide a path forward for anyone maintaining a binding or an application that links `libduckdb`.
| Client | Status | Reference pull requests | |---|---|---| | Python | Migrated | [`duckdb-python#634`](https://github.com/duckdb/duckdb-python/pull/634) | | Rust | Migrated | [`duckdb-rs#834`](https://github.com/duckdb/duckdb-rs/pull/834), [`duckdb-rs#839`](https://github.com/duckdb/duckdb-rs/pull/839), [`duckdb-rs#883`](https://github.com/duckdb/duckdb-rs/pull/883), [`duckdb-rs#835`](https://github.com/duckdb/duckdb-rs/pull/835) | | Node.js | In progress | [`duckdb-node-neo#476`](https://github.com/duckdb/duckdb-node-neo/pull/476), [`duckdb-node-neo#508`](https://github.com/duckdb/duckdb-node-neo/pull/508), [`duckdb-node-neo#512`](https://github.com/duckdb/duckdb-node-neo/pull/512) | | Go | In progress | [`duckdb-go-bindings#112`](https://github.com/duckdb/duckdb-go-bindings/issues/112), [`duckdb-go-bindings#116`](https://github.com/duckdb/duckdb-go-bindings/pull/116) | | Wasm | In progress | [`duckdb-wasm#2281`](https://github.com/duckdb/duckdb-wasm/pull/2281) | > The Python pull request is the clearest end-to-end example: it restores static linking of the default `BUILD_EXTENSIONS` set by calling `duckdb_register_static_extensions()` and shows how to override the linked set. ## See Also * [Building Extensions]({% link docs/2.0/dev/building/building_extensions.md %}) * [Build Configuration]({% link docs/2.0/dev/building/build_configuration.md %}) * [C API Overview]({% link docs/2.0/clients/c/overview.md %}) * [Extensions Overview]({% link docs/2.0/extensions/overview.md %})