Description
So, as most people know by now, abseil's ABI (by default) depends on the C++ version used to compile it1.
This is problematic because we can only have one ABI per shared library (trying to load two different ones would lead to lots of ODR violations at best, segfaults at worst).
This left us with the choice of:
- use C++17 for everyone (though some packages downstream are not ready yet, especially on win)
- use C++11 for everyone (but some packages already require C++17, so no-go)
- try to support several ABIs at once using build strings
I tried 3. in #35, because it seemed like it would have the least painful contraints (and was nicely explicit in making people aware of the ABI issue in the build string as well as having to match using the C++ version of consuming packages). The idea was that shared builds stay on C++17 for those who can, and those who're stuck with C++11/14 can use a static lib, with the intention to encapsulate that ABI dependence at the package using the static lib (because a run-export for the same C++ version would then conflict with the C++17 shared builds).
This worked for grpc (or seemed to at least) and some other packages, but the encapsulation is leakier than I had hoped. For example, for building a static library (which we have a few of, especially on windows), even if specifying target_link_libraries(<target> PRIVATE absl::something)
, this effectively gets turned into public linkage, which makes libabseil-static
a runtime requirement, and runs into the same virality issue where stuff that's run-depending on libabseil-static=*=cxx14*
cannot be co-installed with libabseil[=*=cxx17*]
. There are also packages which need public linkage anyway (and thus a run-dependence), because abseil types are part of their public API.
In practice it's been messy to try to work through this, but so far this was the only solution (given that e.g. gRPC bumped its required C++ version to 14, and moving to C++17 seemed too aggressive on windows given that some consumers weren't yet ready to change). I've tried as best as I could so far, but I'm getting to the point where I don't think it's really feasible as an overall approach. The remaining options are:
- use C++17
use C++11 ABItry to support several ABIs at once using build strings- keep using the compat-types no matter which C++ version (see Customize options.h for ABI consistency #43)
The last option is something I hadn't been aware of. It works by forcing abseil to never replace its own types with the ones from the stdlib, which means the ABI stays the same regardless of the C++ version that a consuming package uses, but also that now there's a lot more symbols and that the compiler will have a harder time optimizing things because those symbols live behind a DLL boundary (and aren't as transparent to the compiler as stdlib types).
[update] option 5 appeared:
5. support C++17 & compat ABI by changing the inline namespace for symbols used by the latter. This should mean that two shared builds with different ABIs could co-exist at runtime without stuff blowing up.
Some advantages & disadvantages as I see them:
option | work necessary |
fragility / complexity |
performance / footprint |
comment |
---|---|---|---|---|
1. | ❌ | ✔️ | ✔️ | needs to move all feedstocks to C++17; some code bases might not be ready yet |
3. | ❌ | ❌ | ✔️ | needs to juggle several incompatible ABIs that conflict |
4. | ✔️ | ❔/ ➖ | ➖ | would break packages relying on interop between stdlib & abseil for newer standards; potential performance impact |
5. | ✔️ | ✔️ | ✔️ | best of all worlds? |
I don't think 3. is really salvageable (except through moving stuff that breaks the encapsulation to C++17), and I don't think 1. is reasonable from the work involved for a migration (and already, the migrators for abseil, grpc & protobuf are piled on top of each other).
obsoleted section about going with 4.
So I'm planning to move forward with 4., unless there are other opinions. In keeping with the spirit of marking the ABI in the build-string, I'm planning to do:libabseil=*=compat_abi*
libabseil-static=*=compat_abi*
This intentionally doesn't refer to a standard version (which would IMO be confusing vis-à-vis abseil default behaviour), and AFAICT, there's no guarantee that this has to match the ABI of one of C++11/14/17, because abseil might have more compat types than are necessary even in the oldest C++ version they support (in particular it'll likely change once abseil drops C++11 in the next release).
I think 4 sounds doable, but might still run into problems as pointed out by Isuru, and 5. just sounds way better overall. More detailed exposition on option 5 here.
Thankfully, the universe has kindly provided us with a new minor version (#44), so that the transition from the current setup can happen sanely (rerun another migration against 20220623.1).
Please comment if you have thoughts
@conda-forge/abseil-cpp @conda-forge/core
Footnotes
-
basically because it depends whether ABI will use the types from the C++ standard library (if the targetted standard version is high enough), or it's own internal emulations of those types (which aren't ABI-compatible with the stdlib-types). ↩
Activity