Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

best practice for including compiler version in abi for dependencies (pybind11/nanobind) #2326

Open
minrk opened this issue Oct 4, 2024 · 5 comments
Labels

Comments

@minrk
Copy link
Member

minrk commented Oct 4, 2024

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:

  1. restrict the C++ compiler version at build time when in host requirements, and
  2. 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 is cxx_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.

@minrk minrk added the question label Oct 4, 2024
@minrk
Copy link
Member Author

minrk commented Oct 4, 2024

It looks like most of the discussion is around "try to get these packages to not include the compiler version in the abi" rather than accepting the current behavior and expressing the dependencies that they have.

@traversaro
Copy link
Contributor

It looks like most of the discussion is around "try to get these packages to not include the compiler version in the abi" rather than accepting the current behavior and expressing the dependencies that they have.

Indeed, I cross-linked exactly to make sure that people looking to that discussions could find also this related discussion.

@carterbox
Copy link
Member

pybind11 and nanobind are both header-only packages, right? As header-only packages, "host" doesn't really have any significance for them since the same artifacts are used regardless of the host/target platform.

They could be used as build dependencies instead of host dependencies so that they can directly interact with the compiler packages. If the solution requires moving these packages to the build section, we could add a linting rule / migration to the channel to have new builds move these packages there.

@beckermr
Copy link
Member

beckermr commented Oct 7, 2024

The issue here is pass items at runtime between two different libraries compiled with these packages. The layout of objects in memory can differ and cause ABI issues.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Development

No branches or pull requests

4 participants