conan provider
A conan stage provisions native, non-Python tools (compilers, cmake,
ninja, …) via Conan and puts them on PATH.
[my-conan-stage]
provider = "conan"
conanfile = "conanfile.py"
[[my-conan-stage.recipes]]
dirs = ["path/to/recipes"]
catalog = "catalog.yml" # optional
(provider:/description:/disabled:/depends-on:/scripts:/env:/env-prepend:/env-append: are generic keys every stage has —
see “Generic stage keys” in Configuration. Everything else is specific to conan.)
Requires
conan must be available wherever this stage runs — denver never
installs it. In practice an earlier uv stage does, by listing conan in
its requirements:, which puts it on PATH via that stage’s venv; a
host-wide or in-image install works just as well, and exe: can point at a
specific one. A stage with no conan available fails with conan provider needs 'conan' on PATH.
Key reference
exe(default:conanonPATH) — the conan executable.deployers(default: just denver’s own bundled symlink deployer) — a list of scripts that deploy installed packages ontoPATH, each run as its ownconan install --deployer=<script>. The env-wide recipes-exporter (the script that generates/exports recipe references) is not user-configurable at this level — only arecipes:entry’s ownexport-tool:(below) can override it, for that entry only.base-classes— optional; a list of directories of shared conanfile base classes recipes can inherit from. Each is put on the recipes-exporter’sPYTHONPATH, in list order (earlier entries win), and may itself live in a base env, resolved the normal way (falling back to an imported base env’s own directory). A listed dir must exist (it’s an error if it doesn’t). Appends acrossimport:layers like any other list (see Configuration’s “Merge rules”), so a derived env only needs to list the base-classes dirs it adds itself.conanfile— optional; the single conanfile to install viaconan install(a project only ever has one dependency graph, so this is a single path, not a list). Left unset, config/export/prepare still run as normal — this is the only toggle for whetherconan install(and theconanbuildenv.shactivation after it) runs at all; there is no separateinstall:flag to keep in sync with it. Independent ofrecipes:below — which recipes get exported has nothing to do with what the conanfile itself requires; it installs whatever its ownrequirements()/build_requirements()reference, whether or not this env exports it itself.recipes— a list of entries, each exported into the local conan cache beforeconanfile:(if any) is installed. Never guessed from the directory layout (see “Explicit over implicit” in../concepts/philosophy.md). Appends acrossimport:layers like any other list. Each entry is a mapping with these keys:dirs— optional; directories containing recipes to export. Each must be listed explicitly and must exist. An entry may be a whole recipe tree or a single recipe directory; recipes are found by theirconandata.yml, wherever it sits.catalog— optional; a path to write this entry’s catalog to (every one of its recipes pinned asname/version@user/channel#rrev). Unset, the catalog is built in memory, handed straight to the export step and never written — so a run leaves no generated file behind. Set it when the pins should be reviewable/committed, or when the conanfile installed viaconanfile:reads them back. Requiresdirs:— a catalog with nothing to build it from is rejected.export-tool— optional; overrides the env-wide recipes-exporter for this entry only.
An entry’s
dirs:are resolved as one catalog, so recipes in one dir may require recipes in another dir of the same entry — and an entry’s catalog content follows from that entry’s membership. Put recipes in their own entry to keep their catalog independent of what another entry does.build(default"missing") — passed as--build=<value>(a string or a list) toconan install.install-args— extra literalconan installarguments.authentication(defaulttrue) — whenfalse,conan installruns with--no-remote.profiles—host/build, each a list; every entry becomes its own-pr:h=<value>/-pr:b=<value>flag, in list order. Empty by default (no explicit profile flags).config— optional; a list of directories, each installed viaconan config install <dir>, in order, before profile detection. Whatever conan’s ownconfig installunderstands inside them (profiles,remotes.json,credentials.json,source_credentials.json,settings.yml, …) is installed into the conan cache by conan itself — denver never opens or interprets any file inside aconfig:directory, it only invokes the command.remotes— optional; a project-owned, exhaustive list of the conan remotes this env wants. Each entry:url,verify_ssl(defaulttrue),enabled(defaulttrue). When the prepare stage runs, the recipes-exporter adds/renames/enables exactly these remotes and disables every other remote already present in the conan home — soremotes:should list every remote the env needs, not just ones being newly added. A remote’s own login (if reachable) runs as part of the same stage.CONAN_REMOTE_ENABLE_<NAME>(an env var,ON/OFF) overrides a given remote’senabled:at run time.keep-remotes(defaultfalse) — set totrueto opt out of makingremotes:exhaustive: with noremotes:of its own, the env then leaves the conan home’s existing remote configuration alone entirely. Left at the defaultfalse, the prepare stage disables every remote already present in the conan home whenremotes:is left unset/empty, so each env’s remote configuration is fully self-contained regardless of what an earlier run of a different env left behind. This cleanup is automatically skipped (regardless ofkeep-remotes:) wheneverremotes:is left unset/empty and the env also has aconfig:— itsconan config install <dir>may itself have installed aremotes.json, and reconciling an emptyremotes:to “exhaustive” would otherwise silently disable everythingconfig:just set up. An explicit (non-empty)remotes:still reconciles as normal regardless ofconfig:.user(default"denver") /channel(default"snapshot") — become the user/channel half of every reference the recipes-exporter generates while exporting recipes (name/version@user/channel).
Design notes
When a
customstage is enough. For a single prebuilt archive with no dependencies,curl+sha256sum -c+tarin acustomstage is an honest twenty lines and needs no conan onPATH— see “Bringing a prebuilt binary in by hand” incustom.md. What conan adds is the per-tool cost: a url and a checksum instead of a script, plus a cache shared across envs and checkouts, and dependencies between tools.Monorepo pattern. Recipes commonly live in the same repository as the project using them, not a separate recipes repo. A
recipes:entry’sdirs:just points at wherever they actually are; there’s no requirement they live in any particular place relative to thedenver.yml. This simplifies the integration as there is no deployment necessary.A base env with recipes but no conanfile. Since
conanfile:andrecipes:are independent, a shared base env can shiprecipes:with noconanfile:of its own; each derived env that wants to actually install something sets its ownconanfile:, and either inherits the base’srecipes:or adds more of its own.Works with or without remotes. An env with no
remotes:and noconfig:at all is a fully offline/local-cache setup — nothing gets reconciled, conan just uses whatever’s already in its local cache/home. Addremotes:(or aconfig:that installs its own remote config) only once real remote access is actually needed.Skips
conan installwhen the dependency graph is unchanged. Before wiping and reinstalling, the install step runs a fastconan graph infoquery (no downloads/builds) and hashes its output; if that hash matches the one stored by the last successful install here, the existing install tree is left alone entirely. A failed query (conan not onPATHyet under--dry-run, a broken recipe, …) is treated as “changed”, soconan installruns and reports the real problem itself. Only the install step is gated this way —config/prepare/exportabove it always run.--fastactivates the already-generatedconanbuildenv.shinstead of re-runningconan install(or theconan graph infocheck); dies with a clear message if it doesn’t exist yet.--forcerecreates the conan/workspace setup steps’ own on-disk state unconditionally, including a freshconan installregardless of the graph-info check above.--dry-runprints theconan/recipes-exporter commands instead of running them, and leaves the install tree (which a real run wipes first) untouched.conan config homestill runs — it is a read-only query whose answer decides whetherconan profile detectwould be shown; when conan isn’t installed yet (an earlieruvstage would have), that query simply fails, and denver warns and previews the detection anyway rather than stopping there.