-
-
Notifications
You must be signed in to change notification settings - Fork 24
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
ABI pinning via SONAME in build string #48
Draft
carterbox
wants to merge
9
commits into
conda-forge:main
Choose a base branch
from
carterbox:track-abi
base: main
Could not load branches
Branch not found: {{ refName }}
Loading
Could not load tags
Nothing to show
Loading
Are you sure you want to change the base?
Some commits from the old base branch may be removed from the timeline,
and old review comments may become outdated.
Draft
Changes from 5 commits
Commits
Show all changes
9 commits
Select commit
Hold shift + click to select a range
bf33e73
Create cfep-22.md
carterbox 520c8cb
Add links to related discussions
carterbox 6f42e94
Add more details to the proposal
carterbox ea8176b
Add note about multiple libraries
carterbox cc507f8
Fix spelling mistake
carterbox 5c30cb9
Add upside to ABI in package name
carterbox 547c4d4
NEW: Add more detailed explanation/example of ABI in name alternate
carterbox a239b0d
STY: Remove stray parenthesis
carterbox 7a5b36f
Add more detailed example named-package recipe
carterbox File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,183 @@ | ||
|
||
<table> | ||
<tr><td> Title </td><td> ABI pinning via SONAME in build string </td> | ||
<tr><td> Status </td><td> Draft </td></tr> | ||
<tr><td> Author(s) </td><td> Daniel Ching <[email protected]></td></tr> | ||
<tr><td> Created </td><td> Jul 22, 2022</td></tr> | ||
<tr><td> Updated </td><td> Jul 26, 2022</td></tr> | ||
<tr><td> Discussion </td><td> | ||
https://github.com/conda-forge/conda-forge.github.io/issues/610 | ||
https://github.com/conda-forge/conda-forge.github.io/issues/157 | ||
https://github.com/conda-forge/conda-forge.github.io/issues/150 | ||
</td></tr> | ||
<tr><td> Implementation </td><td> NA </td></tr> | ||
</table> | ||
|
||
## Abstract | ||
|
||
Conda lacks a built-in method of tracking ABI separately from API. This is fine | ||
for python, whose ABI/API are effectively the same, but for compiled libraries | ||
it is not. Trying to figure out what ABI compatability guarantees that a | ||
project offers between API releases is annoying because you have to contact | ||
upstream maintainers and manually monitor for changes. However, projects which | ||
care about ABI stability already offer this information in the form of the | ||
SONAME which is the version of the ABI and is commonly added to the name of the | ||
shared library. This proposal is a standard procedure for adding the SONAME | ||
into the build string, so that the exisiting conda solver can choose a | ||
compatible ABI automatically. | ||
|
||
## Motivation | ||
|
||
Conda doesn't have a built in way for tracking ABI separately from API which | ||
causes hardship for recipe maintainers in an ecosystem where packages are | ||
required to link dynamically. In order to prevent ABI breaks from a future API | ||
release, maintainers need to know ahead of time at which API release, the ABI | ||
will change. This means either asking upstream package maintainers about | ||
compatability guarantees or exporting a pin to the minor version. | ||
|
||
One of the approaches is imperfect because maintainers could change their | ||
policy or not have a policy. The other is inflexible because always pinning to | ||
the minor API version may not be necessary. One approach may lead to broken | ||
packages, and the other causes extra downstream builds and may occasionally | ||
cause unsolvable environments. | ||
|
||
## Specification | ||
|
||
Recipes whose libraries include a separate ABI version should add the major ABI | ||
version to the build string between `v` and `so`. For example, `v2so` for | ||
version `2`. This substring in the build string should be used in the | ||
run_export to constrain the pinning to the same ABI major version in addition | ||
to cosntraints on the API version. | ||
|
||
Recipes whose libraries have an API version only should export a pin to the | ||
patch level (`x.x.x`) in order to prevent ABI breaks. | ||
|
||
Recipes whose package version is the same as the ABI version do not need to | ||
modify their build string. | ||
|
||
Recipes with multiple library artifacts whose ABIs are tracked separately | ||
should split these libraries into separate outputs of one recipe. A metapackage | ||
may be used for installation convenience, but the metapackge must also | ||
enumerate the run_exports for each subpackage. | ||
|
||
## Sample Implementation | ||
|
||
```yaml | ||
{% set name = "libavif" %} | ||
{% set build = 0 %} | ||
# NOTE: Humans must also update the library version in the tests section of this recipe | ||
{% set version = "0.10.1" %} | ||
# Look in the libavif top level CMakeLists.txt for the updated ABI version. | ||
# ABI is updated separately from API version. | ||
{% set so_version = "14.0.1" %} | ||
{% set so_major_version = so_version.split('.')[0] %} | ||
|
||
package: | ||
name: {{ name|lower }} | ||
version: {{ version }} | ||
|
||
build: | ||
number: {{ build }} | ||
string: "v{{ so_name_major }}soh{{ PKG_HASH }}_{{ build }}" | ||
run_exports: | ||
- {{ pin_subpackage(name|lower) }} *v{{ so_name_major }}so* | ||
|
||
test: | ||
commands: | ||
- test -f ${PREFIX}/lib/libavif.so.{{ so_major_version }} # [linux] | ||
- test -f ${PREFIX}/lib/libavif.so.{{ so_version }} # [linux] | ||
|
||
``` | ||
|
||
## Rationale | ||
|
||
Adding the ABI version to the build string allows the run_export to handle both | ||
API and ABI using existing conda capabilities. Not needing new conda features | ||
saves engineering effort and is implementable today. Build strings are already | ||
used to track features such as the CUDA version and MPI variants. | ||
|
||
The ABI version is sandwiched between `v` and `so` because these letters are | ||
not part of hexadecimal and to prevent matching collisions with other build | ||
string tags or higher numbers. i.e. `6*` matches with `60`, but `6so*` does | ||
not. | ||
|
||
Using a combination of build string and run_exports to add ABI information | ||
requires no changes from downstream packages except using the latest build. | ||
They will be guarded from ABI breakages as soon as they rebuild with the | ||
updated package. | ||
|
||
## Backward Compatability | ||
|
||
This proposal does not break exisiting packages. It is a feature that only | ||
helps future package builds. | ||
|
||
|
||
## Alternatives | ||
|
||
### API Pinning Only | ||
|
||
In theory, breaking ABI changes can be introduced without changing the API | ||
(reordering struct elements for example). In practice, project managers would | ||
always make a new API release for this change, so ABI breaks can be avoided by | ||
pinning down to the patch version. This is the most conservative approach, but | ||
is the least flexible. Recipe maintainers can pin to minor API versions if the | ||
upstream package makes any such promises about not breaking the ABI. | ||
|
||
### Adding ABI name to package name | ||
|
||
This alternatives renames outputs to `{{ name|lower }}{{ so_major_version }}`. | ||
This is not backward compatible and would require migrating all downstream | ||
packages to the new output names. | ||
|
||
### Prepending ABI to package version | ||
|
||
This would mean versioning packages to `{{so_major_version}}.{{version}}`. This | ||
approach may not be backward compatible with already published package versions | ||
if the ABI version is lower than the API version and would require migrating | ||
all downstream feedstocks. | ||
|
||
### Exporting a separate ABI package | ||
|
||
The conda-forge pybind11 package does this currently. An empty package called | ||
pybind11_abi tracks the ABI version and is used as a run_constraint and | ||
run_export. This approach is backward compatible, but requires additions to the | ||
recipe (additional outputs, run_constraints, and exports). | ||
|
||
### Modifying conda to automatically track ABI via SONAME | ||
|
||
This approach requires new software features on conda instead of relying on | ||
existing features. | ||
|
||
# Reference | ||
|
||
https://github.com/conda-forge/conda-forge.github.io/issues/610 | ||
|
||
https://github.com/conda-forge/conda-forge.github.io/issues/157 | ||
|
||
https://github.com/conda-forge/conda-forge.github.io/issues/150 | ||
|
||
https://github.com/conda-forge/libavif-feedstock/pull/1#issuecomment-986310764 | ||
|
||
https://github.com/conda-forge/dav1d-feedstock/pull/1/files#r927851556 | ||
|
||
https://github.com/conda-forge/libwebp-feedstock/blob/615b5309a76ac96409394aa100ec11bb1c7ea150/recipe/meta.yaml#L14 | ||
|
||
## Other sections | ||
|
||
Other relevant sections of the proposal. Common sections include: | ||
|
||
* Specification -- The technical details of the proposed change. | ||
* Motivation -- Why the proposed change is needed. | ||
* Rationale -- Why particular decisions were made in the proposal. | ||
* Backwards Compatibility -- Will the proposed change break existing | ||
packages or workflows. | ||
* Alternatives -- Any alternatives considered during the design. | ||
* Sample Implementation -- Links to prototype or a sample implementation of | ||
the proposed change. | ||
* FAQ -- Frequently asked questions (and answers to them). | ||
* Resolution -- A short summary of the decision made by the community. | ||
* Reference -- Any references used in the design of the CFEP. | ||
|
||
## Copyright | ||
|
||
All CFEPs are explicitly [CC0 1.0 Universal](https://creativecommons.org/publicdomain/zero/1.0/). |
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I have addressed this in the Alternatives section. It is also discussed in one of the linked issues: conda-forge/conda-forge.github.io#157
An upside to this approach which I have added is that you can install multiple versions simultaneously.