Alien::Libduckdb
Builds the DuckDB C library (libduckdb) from source
and tells Raku where the result lives, so that a NativeCall binding can just
depend on it instead of hunting for a pre-built binary.
Consumed by DB::DuckDB as its
depends entry.
Why build it ourselves
The DuckDB release assets are convenient but not portable enough for a Raku
distribution that has to work on both Linux and Windows:
- Linux tarballs ship
libduckdb.so with no RUNPATH, so a binding either
needs LD_LIBRARY_PATH or a system-wide install; - Windows MinGW builds emit
libduckdb.dll, MSVC builds emit duckdb.dll,
and both need the import library next to them for downstream linking; - the headers must match the shared object exactly - mismatched
duckdb.h
is the classic source of silent memory corruption in NativeCall bindings.
Building from a pinned upstream tag once, into a private prefix, removes all
three problems. DuckDB 2.x vendors every third-party dependency in-tree, so a
plain git clone + CMake is enough - no submodules, no vcpkg.
Install
zef install Alien-Libduckdb
This compiles DuckDB and therefore takes a few minutes (~5 min on 10 cores).
The result is cached per upstream version; reinstalling the module does not
rebuild unless the version changed.
To install the module but build later:
zef install Alien-Libduckdb --/build # or ZEF_NO_BUILD=1
and then, whenever you want the library:
use Alien::Libduckdb;
ensure-installed(); # idempotent, builds only when missing
Usage
use Alien::Libduckdb;
say duckdb-version(); # 2.0.0 - upstream version we build
say prefix(); # .../Alien-Libduckdb/2.0.0
say installed(); # True once the build succeeded
say shared-lib().absolute; # .../bin/duckdb.dll or .../lib/libduckdb.so
# what a NativeCall binding should do
sub duckdb_library_version(--> Str) is native(Alien::Libduckdb::lib()) {*}
lib() returns the absolute path when the library exists and falls back to the
bare name (duckdb) so the OS loader can still find a system install.
Exported API
| sub | meaning |
|---|
duckdb-version() | upstream DuckDB version this module builds |
prefix() | private install prefix (per version) |
lib-dir() | directory holding the shared library |
include-dir() | directory holding duckdb.h, duckdb_v2.h, ... |
shared-lib() | absolute path of the shared library, or an undefined IO::Path |
lib() | value to pass to is native(...) |
installed() | is a build matching version and platform present? |
config() | Hash with all of the above |
cflags() / ldflags() | flags for building against this prefix |
ensure-installed() | build on demand |
build-from-source(:force, :clean, :jobs) | explicit control |
How it works
- Fetch -
git clone --depth 1 --branch v<version> from the DuckDB repo
(falls back to the release tarball when git is unavailable). Submodules
are initialised when the tag still has .gitmodules (1.x). - Configure - CMake out-of-tree,
Release, shell/unittests/benchmarks
off, PIC on. Ninja is preferred, Makefiles are the fallback. - Compile -
<cores> parallel jobs (capped to keep memory sane). - Harvest - copy the shared object into
<prefix>/bin (Windows) or
<prefix>/lib (POSIX), normalising libduckdb.dll -> duckdb.dll so
consumers always load one canonical name; copy the headers and, on Windows,
the import library. - Verify - load the library and call
duckdb_library_version(); write a
.alientag marker with version/platform/build-id. installed() compares
the marker against the current request, so a library built for another
DuckDB version or platform is never silently reused.
The source tree is deleted after a successful build (about 400 MB). Set
ALIEN_LIBDUCKDB_KEEP_SOURCE=1 to keep it, and the next build becomes a
near-instant incremental rebuild.
Linux / WSL
- Needs
cmake, ninja (or make), g++ (GCC 11+ works, Clang too),
git, tar, curl. - The library is installed to
~/.local/share/Alien-Libduckdb/<version>/lib,
honouring XDG_DATA_HOME. ldflags() adds -Wl,-rpath,<lib-dir> so binaries linked against the
prefix do not need LD_LIBRARY_PATH.
Windows
- Needs CMake, Ninja, and a compiler. Both toolchains work:
- MinGW-w64 (e.g.
D:\llvm-mingw-...\bin\gcc.exe) - emits libduckdb.dll,
which this module renames to duckdb.dll on install; - MSVC - emits
duckdb.dll and duckdb.lib, both installed.
- Default prefix:
%LOCALAPPDATA%\Alien-Libduckdb\<version>. Path is looked up case-insensitively on Windows (%*ENV<PATH> is not
always populated by Rakudo there), so cmake/ninja from PATH are found
even when the environment block uses mixed case.
macOS
The layout follows ~/Library/Application Support and ldflags() omits
-rpath specifics beyond the standard flag; untested but no code path is
Windows- or Linux-only.
Environment variables
| variable | effect |
|---|
ALIEN_LIBDUCKDB_VERSION | build a different upstream tag (e.g. 1.4.1) |
ALIEN_LIBDUCKDB_ROOT | override the root that holds src/, build/, <version>/ |
ALIEN_LIBDUCKDB_PREFIX | override the install prefix entirely |
ALIEN_LIBDUCKDB_SRC | use an existing DuckDB checkout instead of cloning |
ALIEN_LIBDUCKDB_JOBS | parallel compile jobs (default: CPU cores) |
ALIEN_LIBDUCKDB_CC / _CXX | force a specific compiler |
ALIEN_LIBDUCKDB_KEEP_SOURCE | keep the source tree after building |
Layout
<root>/ %LOCALAPPDATA%\Alien-Libduckdb or ~/.local/share/Alien-Libduckdb
├── src/duckdb-<version>/ source checkout (removed after success)
├── build/duckdb-<version>/ CMake build tree
└── <version>/ install prefix
├── bin/ duckdb.dll (Windows)
├── lib/ libduckdb.so (POSIX) + import libs
├── include/ duckdb.h, duckdb_v2.h, ...
├── logs/ clone/configure/build logs of the last attempt
└── .alientag marker: version, platform, build id
License
Artistic-2.0. DuckDB itself is MIT-licensed.