Variants
In this tutorial, we will show you how to use variants in order to build a Pixi package against different versions of a dependency. Some might call this functionality, build matrix, build configurations or parameterized builds, in the conda ecosystem this is referred to as a variant.
Warning
pixi-build is a preview feature, and will change until it is stabilized.
Please keep that in mind when you use it for your projects.
Why is This Useful?#
When we depend on a Pixi package, the dependency versions of the package itself are already set.
For example, in the C++ tutorial the cpp_math package we built depended on Python 3.12.
Pixi would report a version conflict, if we'd add use both Python 3.11 and cpp_math to our workspace.
By using variants, we can add a set of allowed versions for a specific dependency.
Pixi will then resolve the package with all the different variants.
Let's Get Started#
In this tutorial we will continue with the result of the workspace tutorial so we can test it against multiple Python versions.
As a reminder, we ended up with a top-level pixi.toml containing the workspace and the Python package python_rich.
Our workspace then depended on python_rich and cpp_math.
The file tree looks like this:
.
├── packages
│ └── cpp_math
│ ├── CMakeLists.txt
│ ├── pixi.toml
│ └── src
│ └── math.cpp
├── pixi.lock
├── pixi.toml
├── pyproject.toml
└── src
└── python_rich
└── __init__.py
In order to allow multiple Python versions we first have to change the Python version requirement of cpp_math from 3.12.* to *.
[package.host-dependencies]
cmake = ">=3.20, <3.27"
nanobind = ">=2.4.0, <2.5.0"
python = "*" # (1)!
- Used to be "3.12.*"
Now, we have to specify the Python versions we want to allow.
We do that in workspace.build-variants:
If we'd run pixi install now, we'd leave it up to Pixi whether to use Python 3.11 or 3.12.
In practice, you'll want to create multiple environments specifying a different dependency version.
In our case this allows us to test our setup against both Python 3.11 and 3.12.
[feature.py311.dependencies]
python = "3.11.*"
[feature.py312.dependencies]
python = "3.12.*"
[environments]
py311 = ["py311"]
py312 = ["py312"]
By running pixi list we can see the Python version used in each environment.
You can also see that the Build string of cpp_math differ between py311 and py312.
That means that a different package has been built for each variant.
Since python_rich only contains Python source code, a single build can be used for multiple Python versions.
The package is noarch.
Therefore, the build string is the same.
$ pixi list --environment py311
Package Version Build Size Kind Source
python 3.11.11 h9e4cc4f_1_cpython 29.2 MiB conda python
cpp_math 0.1.0 py311h43a39b2_0 conda cpp_math
python_rich 0.1.0 pyhbf21a9e_0 conda python_rich
$ pixi list --environment py312
Package Version Build Size Kind Source
python 3.12.8 h9e4cc4f_1_cpython 30.1 MiB conda python
cpp_math 0.1.0 py312h2078e5b_0 conda cpp_math
python_rich 0.1.0 pyhbf21a9e_0 conda python_rich
Conclusion#
In this tutorial, we showed how to use variants to build multiple versions of a single package.
We built cpp_math for Python 3.12 and 3.13, which allows us to test whether it works properly on both Python versions.
Variants are not limited to a single dependency, you could for example try to test multiple versions of nanobind.
On top of adding variants inline, they can also be included as files. Check out the reference to learn more!
Thanks for reading! Happy Coding 🚀
Any questions? Feel free to reach out or share this tutorial on X, join our Discord, send us an e-mail or follow our GitHub.