Packaging Python extensions that contain native C or C++ code has come a long way. PEP 517 defined a contract between Python build frontends (pip, build, uv) and the build backend that produces the wheel. That standard is what makes it possible today to connect a CMakeLists.txt to a pyproject.toml, declare a backend, and let pip wheel . drive the build.

The C/C++ dependency layer is a different story. Somewhere between pyproject.toml and CMakeLists.txt, a find_package(OpenSSL) has to resolve. In practice, most projects solve that outside the wheel build: through system packages, vendored source trees, FetchContent or a separate native package manager install step. That means a separate step to manage before the Python build, often duplicated across CI configurations and developer setups.

Today, we are happy to introduce conan-py-build, a PEP 517 build backend that brings Conan’s C/C++ dependency management directly into the Python wheel build.

The project is currently in beta and under active development. We are releasing it now to gather early feedback, and we would love for you to try it and tell us what you think.

What is conan-py-build?

conan-py-build is a build backend for Python packages that contain native C/C++ extensions. You declare it in pyproject.toml, provide a conanfile.py that describes the C/C++ build and its dependencies, and build wheels through standard Python packaging commands such as pip wheel ..

When a build runs, conan-py-build:

  1. Resolves the C/C++ dependency graph through Conan, downloading precompiled binaries where available and building the rest from source
  2. Prepares the build toolchain through the corresponding Conan generators
  3. Builds the extension using your project’s build system
  4. When the extension links against shared libraries, copies those runtime dependencies next to the extension module and patches RPATH on Linux and macOS where applicable
  5. Packages the result into a standard Python wheel

Because it is a PEP 517 backend, it plugs into pip, build, and uv directly, and fits into cibuildwheel-based CI workflows for multi-platform builds.

A minimal example

Let’s build a tiny Python package that exposes a single function, greet(name), which prints a colored greeting to the terminal. We’ll use CMake for the native build, pybind11 for the Python bindings, and {fmt} as a dependency pulled in through Conan. The same setup extends to other build systems like Meson or Autotools.

The project layout:

mypackage/
├── pyproject.toml
├── conanfile.py
├── CMakeLists.txt
└── src/
    ├── mypackage/
    │   └── __init__.py
    └── mypackage.cpp

pyproject.toml declares the build backend and the project metadata:

[build-system]
requires = ["conan-py-build"]
build-backend = "conan_py_build.build"

[project]
name = "mypackage"
version = "0.1.0"

conanfile.py describes the C/C++ side: its dependencies (pybind11 and fmt) and how they are built and packaged.

from conan import ConanFile
from conan.tools.cmake import CMake, cmake_layout

class MyPackageConan(ConanFile):
    settings = "os", "compiler", "build_type", "arch"
    generators = "CMakeToolchain", "CMakeDeps"

    def layout(self):
        cmake_layout(self)

    def requirements(self):
        self.requires("pybind11/3.0.1")
        self.requires("fmt/12.1.0")

    def build(self):
        cmake = CMake(self)
        cmake.configure()
        cmake.build()

    def package(self):
        cmake = CMake(self)
        cmake.install()

CMakeLists.txt builds the extension against pybind11 and fmt and installs the resulting module into the Python package directory so the backend picks it up when assembling the wheel:

cmake_minimum_required(VERSION 3.15)
project(mypackage LANGUAGES CXX)

set(PYBIND11_FINDPYTHON ON)
find_package(pybind11 CONFIG REQUIRED)
find_package(fmt CONFIG REQUIRED)

pybind11_add_module(_core src/mypackage.cpp)
target_link_libraries(_core PRIVATE fmt::fmt)

install(TARGETS _core DESTINATION mypackage)

The C++ source defines greet(name) using fmt’s color support and exposes it as a compiled _core module:

#include <string>
#include <pybind11/pybind11.h>
#include <fmt/color.h>

void greet(const std::string& name) {
    fmt::print(fmt::fg(fmt::color::green), "Hello, {}!\n", name);
}

PYBIND11_MODULE(_core, m) {
    m.def("greet", &greet);
}

And src/mypackage/__init__.py re-exports it so callers see mypackage.greet:

from mypackage._core import greet

__all__ = ["greet"]

With that in place, building the wheel is the standard Python packaging command:

$ pip wheel . -w dist/

Conan resolves pybind11 and fmt from Conan Center Index, CMake compiles the extension against them, and you get a platform-specific wheel in dist/. Install it and try it:

$ pip install dist/mypackage-*.whl
$ python -c "import mypackage; mypackage.greet('world')"
Hello, world!

You should see Hello, world! printed in green.

More examples: the repo has nanobind bindings, shared library dependencies, C++ sources fetched at build time, and a full multi-platform cibuildwheel setup for Linux, macOS, and Windows.

What conan-py-build brings

Some of the advantages of bringing Conan into the wheel build:

  • One build entry point. The usual pip wheel . command can drive both the Python packaging step and the native C/C++ dependency/build step.
  • Conan Center. A large catalog of C/C++ libraries with recipes tested across a broad compiler and OS matrix.
  • Binary caching. Compiled dependencies are reused across builds and CI runs via the Conan cache or a shared remote, rebuilt only when settings change.
  • Profiles and lockfiles. Profiles define the native build configuration of each wheel (compiler, architecture, C++ standard, dependency options), and lockfiles pin the graph for reproducible builds.
  • Shared library handling. Conan-managed runtime libraries are deployed next to the extension module, and RPATH is adjusted on Linux and macOS where applicable.

Conclusions

conan-py-build pulls the C/C++ dependency layer inside the PEP 517 build so the Python build and the C/C++ build are one problem, not two. If you have been maintaining a separate dependency step alongside your Python packaging, it is worth a look.

Check out the documentation and browse the examples. conan-py-build is still in beta and available on PyPI. If something does not work, or there is a workflow you want supported, please open an issue on GitHub and let us know where it fits, or where it does not yet.

Looking forward to your feedback.