cargo-duckdb-ext-tools
July 21, 2026 · View on GitHub
A Cargo-native workspace for developing DuckDB extensions in Rust. It brings together the cargo-duckdb-ext CLI and the companion duckdb-ext-macros procedural macro so project scaffolding, extension entrypoints, builds, and packaging can evolve in one repository.
Overview
DuckDB extensions are dynamic libraries (.dylib/.so/.dll) with a 534-byte metadata footer appended to the file. The official DuckDB Rust extension template relies on Python scripts for metadata appending, requiring developers to maintain both Rust and Python environments.
This workspace keeps the Rust extension workflow in the Rust ecosystem:
| Package | Path | Role |
|---|---|---|
cargo-duckdb-ext-tools | . | Cargo plugin that installs as cargo-duckdb-ext and provides new, build, package, and meta subcommands |
duckdb-ext-macros | crates/duckdb-ext-macros | Procedural macro crate that provides #[duckdb_extension] for generating DuckDB C API entrypoints |
Key Features
- Zero Non-Rust Dependencies: Pure Rust implementation, no Python or external runtime tools required
- Complete Development Workflow: Scaffold, build, package, and inspect extension metadata in one cohesive tool
- Workspace-Owned Entrypoint Macro: Generated projects use
duckdb-ext-macros, maintained in the same repository as the CLI templates - Intelligent Defaults: Automatic parameter inference from Cargo metadata and build artifacts
- Cross-Platform Support: Native builds and cross-compilation for all major platforms
- Professional Logging: Cargo-compatible output format with color support
- Metadata Inspection and Editing: Read or update DuckDB extension metadata footers without external scripts
- ABI Version Support: Both stable (
C_STRUCT) and unstable (C_STRUCT_UNSTABLE) ABI types
Installation
Install the CLI:
cargo install cargo-duckdb-ext-tools
The tool installs as cargo-duckdb-ext and provides four subcommands: new, build, package, and meta.
The companion macro crate is normally added automatically when you run cargo duckdb-ext new. For custom extension projects that do not use the generator, add it alongside DuckDB's loadable extension dependencies:
[dependencies]
duckdb = { version = "1.10504.0", features = ["vtab-loadable"] }
duckdb-ext-macros = "0.3.0"
libduckdb-sys = { version = "1.10504.0", features = ["loadable-extension"] }
Complete Workflow Example
Here's a complete example from project creation to running the extension in DuckDB:
# 1. Create a new extension project
cargo duckdb-ext new quack
# 2. Enter the project directory
cd quack
# 3. Build and package the extension
cargo duckdb-ext build -- --release
# 4. The extension is now ready at:
# target/release/quack.duckdb_extension
# 5. Inspect the extension metadata
cargo duckdb-ext meta target/release/quack.duckdb_extension
# 6. Load and test the extension in DuckDB
duckdb -unsigned -c "load 'target/release/quack.duckdb_extension'; from quack('Joe')"
Expected output:
┌───────────────┐
│ 🐥 │
│ varchar │
├───────────────┤
│ Hello Joe │
└───────────────┘
This creates a table function extension that returns Hello {name} for any input name.
Subcommands
1. cargo duckdb-ext new - Create New Extension Projects
Creates a complete DuckDB extension project with proper configuration and template code.
cargo duckdb-ext new [OPTIONS] <PATH>
Basic Usage
# Create a table function extension (default)
cargo duckdb-ext new my-extension
# Create a scalar function extension
cargo duckdb-ext new --scalar my-scalar-extension
Key Options
--table/--scalar: Choose function type (table function is default)--name <NAME>: Set package name (defaults to directory name)--edition <YEAR>: Rust edition (2015, 2018, 2021, 2024)--vcs <VCS>: Version control system (git, hg, pijul, fossil, none)
What It Creates
- Complete Cargo project with
cdylibconfiguration - DuckDB dependencies (
duckdb,libduckdb-sys,duckdb-ext-macros) - Template code using
#[duckdb_extension]for the DuckDB entrypoint - Table function or scalar function sample implementation
- Release profile optimizations (LTO, strip)
2. cargo duckdb-ext build - Build and Package Extensions
The high-level command that combines compilation and packaging with intelligent defaults.
cargo duckdb-ext build [OPTIONS] [-- <CARGO_BUILD_ARGS>...]
Basic Usage
# Build with release optimizations
cargo duckdb-ext build -- --release
# Cross-compile for Linux from macOS
cargo duckdb-ext build -- --release --target x86_64-unknown-linux-gnu
Intelligent Defaults
The tool automatically detects:
- Library path: From
cdylibartifacts in build output - Extension path:
<package-name>.duckdb_extensionnext to the library - Extension version: From
Cargo.tomlversion field (prefixed withv) - Platform: From target triple or host system
- DuckDB version: From
duckdborlibduckdb-sysdependency
Key Options
-m, --manifest-path: Path toCargo.toml-o, --extension-path: Override output extension path-v, --extension-version: Override extension version-p, --duckdb-platform: Override target platform-d, --duckdb-version: Specify DuckDB version-a, --duckdb-capi-version: Specify DuckDB C API version (enables stable ABI)
3. cargo duckdb-ext package - Package Existing Libraries
Low-level command to append DuckDB metadata to an existing dynamic library.
cargo duckdb-ext package --library-path <LIB> --extension-path <OUTPUT> \
--extension-version <VERSION> --duckdb-platform <PLATFORM> \
(--duckdb-version <VERSION> | --duckdb-capi-version <VERSION>)
Example
cargo duckdb-ext package \
-i target/release/libmy_extension.dylib \
-o my_extension.duckdb_extension \
-v v1.0.0 \
-p osx_arm64 \
-d v1.4.2
4. cargo duckdb-ext meta - Inspect and Update Extension Metadata
Reads the 534-byte DuckDB metadata footer from a .duckdb_extension file, or updates selected fields in place. If the target file does not already contain a metadata footer and update options are provided, the command appends a new footer.
cargo duckdb-ext meta [OPTIONS] <EXTENSION-FILE>
Basic Usage
# Display metadata without modifying the extension
cargo duckdb-ext meta target/release/quack.duckdb_extension
# Update selected metadata fields
cargo duckdb-ext meta \
-v v1.0.1 \
-p osx_arm64 \
-a v1.2.0 \
target/release/quack.duckdb_extension
Key Options
--field-1,--field-2,--field-3: Update reserved 32-byte fields as lowercase hexadecimal bytes-v, --extension-version: Update the extension version-d, --duckdb-version: Update the DuckDB version and set ABI type toC_STRUCT_UNSTABLE-a, --duckdb-capi-version: Update the DuckDB C API version and set ABI type toC_STRUCT-p, --duckdb-platform: Update the DuckDB platform identifier-m, --magic-version: Update the metadata magic version-s, --signature: Update the 256-byte signature area as lowercase hexadecimal bytes
When no options are provided, meta is read-only. Hexadecimal fields must use lowercase characters and fit in their fixed field sizes.
Companion Macro Crate
duckdb-ext-macros provides the #[duckdb_extension] attribute macro used by generated extension projects:
use duckdb::Connection;
use duckdb_ext_macros::duckdb_extension;
use std::error::Error;
#[duckdb_extension(name = "my_extension", api_version = "v1.2.0")]
pub fn extension_entrypoint(connection: Connection) -> Result<(), Box<dyn Error>> {
// Register functions, types, or perform setup.
Ok(())
}
The macro generates a C-compatible function named {extension_name}_init_c_api, initializes DuckDB's Rust extension API, opens a duckdb::Connection, calls your initialization function, and forwards initialization errors to DuckDB. See crates/duckdb-ext-macros/README.md for the macro-specific guide.
Platform Support
Supported Platforms
- macOS: Apple Silicon (arm64) and Intel (x86_64)
- Linux: x86_64, aarch64, x86, arm
- Windows: x86_64, x86 (via cross-compilation)
Platform Mapping
The tool automatically maps Rust target triples to DuckDB platform identifiers:
| Rust Target Triple | DuckDB Platform |
|---|---|
x86_64-apple-darwin | osx_amd64 |
aarch64-apple-darwin | osx_arm64 |
x86_64-unknown-linux-gnu | linux_amd64 |
aarch64-unknown-linux-gnu | linux_arm64 |
x86_64-pc-windows-msvc | windows_amd64 |
i686-pc-windows-msvc | windows_amd |
Technical Details
ABI Types
C_STRUCT_UNSTABLE: Default for extensions built against a DuckDB versionC_STRUCT: Used when--duckdb-capi-versionis specified (stable ABI)
Metadata Structure
The 534-byte footer includes:
- Start signature (22 bytes)
- 3 reserved fields (96 bytes)
- ABI type (32 bytes)
- Extension version (32 bytes)
- DuckDB/C API version (32 bytes)
- Platform identifier (32 bytes)
- Magic version
4(32 bytes) - Signature area (256 bytes; all zeros means unsigned)
The package and build subcommands write this footer when creating .duckdb_extension files. The meta subcommand can display the same fields, update selected values, or append a footer to an existing file that does not have one yet.
Project Structure
.
├── Cargo.toml # Workspace manifest and CLI package manifest
├── src/ # cargo-duckdb-ext-tools CLI/library code
│ ├── main.rs # CLI entry point
│ ├── lib.rs # Library exports
│ ├── error.rs # Error types
│ ├── logging.rs # Cargo-compatible logging
│ ├── helpers.rs # File system utilities
│ ├── commands/ # Subcommand implementations
│ └── options/ # CLI option parsing
└── crates/
└── duckdb-ext-macros/ # #[duckdb_extension] procedural macro crate
├── Cargo.toml
└── src/lib.rs
Testing
# Build the default CLI package in development mode
cargo build
# Run tests for the default CLI package
cargo test
# Check the companion macro crate
cargo check -p duckdb-ext-macros
# Build or test every workspace member
cargo build --workspace
cargo test --workspace
# Install the CLI locally for testing
cargo install --path .
Contributing
Contributions are welcome. Please feel free to submit a Pull Request.
- Fork the repository
- Create your feature branch (
git checkout -b feature/amazing-feature) - Commit your changes (
git commit -m 'Add some amazing feature') - Push to the branch (
git push origin feature/amazing-feature) - Open a Pull Request
Support
- GitHub Issues: https://github.com/redraiment/cargo-duckdb-ext-tools/issues
- Email: Zhang, Zepeng redraiment@gmail.com
License
This project is licensed under the MIT License - see the LICENSE file for details.