Skip to content

Commit 627ec59

Browse files
committed
new concept page on package priority levels
1 parent 8c825e1 commit 627ec59

File tree

1 file changed

+230
-0
lines changed

1 file changed

+230
-0
lines changed

docs/concepts/priority.md

Lines changed: 230 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,230 @@
1+
---
2+
title: Package Priority
3+
description: How priority controls file conflict resolution in Flox environments
4+
---
5+
6+
When multiple packages install a file to the same path,
7+
Flox needs a rule to decide which one wins.
8+
That rule is **priority**.
9+
10+
## How priority works
11+
12+
A Flox environment is a directory of symlinks
13+
pointing into the Nix store.
14+
When the environment is built,
15+
every installed package's files are merged into this directory.
16+
If two packages both provide `bin/python` or `share/licenses/LICENSE`,
17+
their files collide.
18+
19+
Priority resolves these collisions:
20+
21+
- Every package has a `priority` value (default: **5**)
22+
- **Lower numbers win**.
23+
A package with `priority = 1` takes precedence
24+
over a package with `priority = 5`
25+
- If two packages collide at the **same priority**,
26+
Flox reports an error and the environment fails to build
27+
28+
Priority only affects files that actually collide.
29+
Packages that install to non-overlapping paths coexist regardless
30+
of their priority values.
31+
32+
## Setting priority
33+
34+
Set priority in the `[install]` section of your manifest:
35+
36+
```toml
37+
[install]
38+
gcc.pkg-path = "gcc"
39+
40+
gcc-unwrapped.pkg-path = "gcc-unwrapped"
41+
gcc-unwrapped.priority = 6
42+
```
43+
44+
Here `gcc` keeps the default priority of 5
45+
and takes precedence over `gcc-unwrapped` (priority 6)
46+
for any overlapping files.
47+
Both packages are fully installed —
48+
only the conflicting files are resolved in favor of `gcc`.
49+
50+
## When you need to set priority
51+
52+
Most packages don't conflict,
53+
so you rarely need to think about priority.
54+
The situations where it matters are:
55+
56+
### Overlapping packages
57+
58+
Some packages provide subsets or supersets of each other.
59+
For example,
60+
`gcc` and `gcc-unwrapped` both install some of the same files.
61+
You may need both —
62+
`gcc` for the compiler and `gcc-unwrapped.lib` for `libstdc++`
63+
but their overlapping files need a tiebreaker:
64+
65+
```toml
66+
[install]
67+
gcc.pkg-path = "gcc"
68+
69+
gcc-unwrapped.pkg-path = "gcc-unwrapped"
70+
gcc-unwrapped.priority = 6
71+
gcc-unwrapped.pkg-group = "libraries"
72+
```
73+
74+
### CUDA packages
75+
76+
CUDA packages are a common source of collisions
77+
because multiple packages install `LICENSE` files
78+
to the same path.
79+
When installing several CUDA packages,
80+
assign incremental priorities:
81+
82+
```toml
83+
[install]
84+
cuda_nvcc.pkg-path = "flox-cuda/cudaPackages_12_8.cuda_nvcc"
85+
cuda_nvcc.systems = ["aarch64-linux", "x86_64-linux"]
86+
cuda_nvcc.priority = 1
87+
88+
cuda_cudart.pkg-path = "flox-cuda/cudaPackages.cuda_cudart"
89+
cuda_cudart.systems = ["aarch64-linux", "x86_64-linux"]
90+
cuda_cudart.priority = 2
91+
92+
cudatoolkit.pkg-path = "flox-cuda/cudaPackages_12_8.cudatoolkit"
93+
cudatoolkit.systems = ["aarch64-linux", "x86_64-linux"]
94+
cudatoolkit.priority = 3
95+
```
96+
97+
The specific priority values don't matter
98+
as long as each package gets a distinct number.
99+
This tells Flox which `LICENSE` file to keep
100+
when they collide.
101+
102+
### Cross-platform fallbacks
103+
104+
When providing platform-specific alternatives
105+
(e.g. CUDA on Linux, CPU on macOS),
106+
priority can indicate which variant is preferred
107+
if both happen to be available:
108+
109+
```toml
110+
[install]
111+
cuda-torch.pkg-path = "flox-cuda/python3Packages.torch"
112+
cuda-torch.systems = ["x86_64-linux", "aarch64-linux"]
113+
cuda-torch.priority = 1
114+
115+
torch-cpu.pkg-path = "python311Packages.torch-bin"
116+
torch-cpu.systems = ["x86_64-darwin", "aarch64-darwin"]
117+
torch-cpu.priority = 6
118+
```
119+
120+
In practice the `systems` filter already prevents collisions here,
121+
but setting priority makes the intent explicit
122+
and protects against future changes.
123+
124+
## Diagnosing collisions
125+
126+
When two packages collide at the same priority,
127+
you'll see an error like:
128+
129+
```text
130+
> ❌ ERROR: 'cuda13.0-cuda_nvcc' conflicts with 'cuda13.0-libcublas'. Both packages provide the file 'LICENSE'
131+
> Resolve by uninstalling one of the conflicting packages or setting the priority of the preferred package to a value lower than '5'
132+
```
133+
134+
To fix this,
135+
identify which package should win for the conflicting path
136+
and give it a lower priority number:
137+
138+
```toml
139+
[install]
140+
packageA.pkg-path = "packageA"
141+
packageA.priority = 4
142+
143+
packageB.pkg-path = "packageB"
144+
packageB.priority = 6
145+
```
146+
147+
## Priority and package groups
148+
149+
Priority and [package groups][package-groups-concept]
150+
solve different problems:
151+
152+
- **Package groups** control which `nixpkgs` revision
153+
each set of packages resolves against.
154+
They address version and ABI compatibility.
155+
- **Priority** controls which package's file wins
156+
when two packages install to the same path.
157+
It addresses file-level collisions
158+
in the merged environment.
159+
160+
You can use both together.
161+
For example,
162+
`gcc-unwrapped` might need its own package group
163+
(to resolve against a different `nixpkgs` revision)
164+
_and_ a higher priority number
165+
(to let `gcc` win file conflicts):
166+
167+
```toml
168+
[install]
169+
gcc.pkg-path = "gcc"
170+
171+
gcc-unwrapped.pkg-path = "gcc-unwrapped"
172+
gcc-unwrapped.priority = 6
173+
gcc-unwrapped.pkg-group = "libraries"
174+
```
175+
176+
## Priority and builds
177+
178+
Priority has no effect on what is available
179+
during [manifest builds][manifest-builds-concept].
180+
Only packages in the `toplevel`
181+
[package group][package-groups-concept]
182+
are available during builds,
183+
regardless of their priority.
184+
185+
If you use `runtime-packages` to trim your build's closure,
186+
all listed packages are included at their assigned priorities.
187+
188+
## Priority and composition
189+
190+
When [composing environments][composition-concept],
191+
the included manifests are merged before the environment is built.
192+
If two environments define the same install ID
193+
(e.g. both provide `gcc.pkg-path`),
194+
the later environment's package descriptor overrides the earlier one
195+
during the manifest merge —
196+
this is a manifest-level override,
197+
not a file-level priority collision.
198+
199+
After merging,
200+
the resulting environment is built
201+
and any file-level collisions between *different* packages
202+
are resolved by priority
203+
the same way they are in a non-composed environment.
204+
205+
## Reference
206+
207+
| Aspect | Detail |
208+
| ------ | ------ |
209+
| Default value | `5` |
210+
| Direction | Lower number = higher precedence |
211+
| Valid values | Integers (no fixed bounds) |
212+
| Common range | `1` through `10` |
213+
| Same-priority collision | Error — environment fails to build |
214+
| Scope | File paths only — doesn't affect resolution |
215+
216+
### Nix equivalent
217+
218+
Flox's `priority` maps directly to `meta.priority` in nixpkgs.
219+
The semantics are identical:
220+
default value of 5, lower wins,
221+
same-priority collisions produce errors.
222+
223+
Nixpkgs provides convenience wrappers —
224+
`lib.meta.hiPrio` (sets priority to -10) and
225+
`lib.meta.lowPrio` (sets priority to 10) —
226+
but in Flox you set the integer directly in the manifest.
227+
228+
[package-groups-concept]: ./package-groups.md
229+
[manifest-builds-concept]: ./manifest-builds.md
230+
[composition-concept]: ./composition.md

0 commit comments

Comments
 (0)