Generate headers for a Cargo workspace
cheadergen was designed for Cargo workspaces with multiple FFI-facing
crates that share types. In partitioned mode (the default) the right shape
falls out automatically: each crate that contributes types gets its own
header, and consuming crates #include it.
This guide shows the common patterns. For the mental model of which crates participate in a run and why, see Target packages.
Select multiple target packages
Pass --package multiple times to generate headers for several workspace members
in one invocation:
cheadergen generate \
--output-dir include \
--package alpha \
--package beta
Each --package argument names a workspace member that should produce a “target”
header (one with extern "C" function declarations, not just types).
Alternatively, omit --package and use --input-dir to target every library
member under one or more workspace directories:
cheadergen generate \
--output-dir include \
--input-dir crates/alpha \
--input-dir crates/beta
Pass --input-dir multiple times to combine several directories. Each path
must live inside the workspace rooted at the current directory.
Omitting both --package and --input-dir falls back to a Cargo-style
default: if the current directory sits inside a workspace member
it targets just that member, otherwise it targets the workspace’s
default-members (every member, for a virtual workspace). See
Target packages for the full
rule.
What gets generated
Given two targets alpha and beta that both use a Config type from a
workspace dependency core:
include/
├── alpha.h # target — extern "C" functions and types unique to alpha
├── beta.h # target — extern "C" functions and types unique to beta
└── core.h # types-only — Config and other shared types
- Both
alpha.handbeta.hstart with#include "core.h". Configis defined exactly once, incore.h.core.hhas noextern "C"block —coreisn’t a target, just a type contributor.
A non-target dependency only gets a header if at least one target actually uses its types. External crates (from crates.io) follow the same rules.
Customising header filenames
By default a crate named my-crate produces my_crate.h (hyphens become
underscores). Override the
base name per package with header_name:
# cheadergen.toml
[package."core"]
header_name = "myproject_core"
Now alpha.h and beta.h will #include "myproject_core.h" and the file
on disk is renamed to match.
If a dependency name is ambiguous (multiple versions present), disambiguate
with name@version:
[package."internal-state@1"]
header_name = "state_v1"
Pruning stale output
Add --prune-orphans to clean up any *.h files in the output directory
that this run didn’t write. Useful when you remove types or packages:
cheadergen generate \
--output-dir include \
--prune-orphans \
-p alpha \
-p beta
Without --prune-orphans, removing a crate from the workspace leaves its
header behind as silent stale output.
--prune-orphans only acts on top-level files matching the language
extension (*.h for C); it never touches subdirectories
or unrelated files.
Skipping empty headers
Some non-target crates contribute no concrete C-visible types, only
generic definitions that get monomorphized into their consumers’ headers.
By default cheadergen still writes an (empty) header file for those crates.
Pass --skip-empty to suppress the empty files:
cheadergen generate \
--output-dir include \
--skip-empty \
--prune-orphans
--skip-empty is partitioned-only (it’s rejected with --bundle).
Combining it with --prune-orphans ensures empty-by-deletion headers also
get cleaned up.
See also
- Bundled vs partitioned output — the conceptual model behind the per-crate header layout.
- Generics and monomorphization —
where
Wrapper<i32>actually ends up. - The
cheadergen.tomlreference for the full[package.<name>]schema.