Description
Your question:
The problem
For packages built with pybind11 and nanobind, the major version of the C++ compiler is part of the ABI. That means any package built with nanobind that links to another package via its nanobind API has a runtime requirement that both were built with the same major version of the compiler. This is not currently handled by the pybind11-abi
metapackage or other compiler run_exports.
I don't think it comes up very often, but it does in the fenics stack. For fenics, I've added a fenics-basix-nanobind-abi
package with a custom version that includes the nanobind and cxx_compiler versions. In the past, I had the same that bundled the pybind11-abi version and compiler version. I've only made one of these because everything in fenics depends on basix, but technically every package build with nanobind (that might be linked against in host
) should have one. But this is a general problem, if not a common one. pybind11 has a pybind11-abi
package, and nanobind will soon as well. But this is only half a solution, because it doesn't take the build-time compiler version into account, which is also part of the ABI in packages built with either pybind11 or nanobind.
My fenics-basix-nanobind-abi
approach also doesn't solve the problem as well as I'd like because downstream packages build just fine since the compiler version is not actually restricted in the build env, but they won't import. that means e.g. cross-compiled outputs pass all CI when they are completely broken, when really the build should have been prevented in the first place due to the compiler version conflict.
Also, I've only dealt with this on linux/mac and don't actually know what needs to be restricted on Windows.
Question
I've struggled to come up with a general solution, but it seems like having nanobind
or pybind11
in host dependencies should:
- restrict the C++ compiler version at build time when in host requirements, and
- restrict install-time packages to those built with the same C++ compiler
One hard part is that pybind11/nanobind packages are host dependencies, so can't easily restrict the cxx compiler version in the build environment. Is there a package in the host env that could be used as a proxy? Or should we be adding a metapackage to the build env to keep things in sync?
Does anyone have a recommendation/experience for a best practice for ensuring ABI compatibility that is sensitive to the compiler version? Is defining one of these abi metapackage outputs for every nanobind-built package really the way to go?
Possible solution
My only idea so far for a general solution is to update the pybind11/nanobind-abi
packages (or separately add nanobind-cxx-abi
) to generate a version for each compiler version with run_constrains on the compiler version and nanobind and strong run_exports on itself.
so e.g. nanobind-cxx-abi 0.13.12 gxx_*
would have:
where:
- 0 is the versioning scheme (increment when we change how it works)
- 13 is the nanobind internals version
- 12 is
cxx_compiler_version
gxx
iscxx_compiler
and would look something like:
run_exports:
strong:
- {{ pin_subpackage('nanobind-cxx-abi', max_pin='x.x.x.x') }} {{ cxx_compiler }}_*
run_constrained:
# constrain compiler version (only works if it's in the build env)
- {{ cxx_compiler }}_impl_{{ target_platform }} {{ cxx_compiler_version }}.*
# constrain nanobind abi (host env)
- nanobind-abi {{ nanobind_internals_version }}
# another compiler constraint that would work on the host env? libstdcxx_devel?
and then packages make sure this is the same version in both the build and host requirements. But as convoluted as that is, I'm not even sure it would work.