Home Explore Blog Models CI



nixpkgs

7th chunk of `doc/stdenv/stdenv.chapter.md`
9811686eded25475dd6017b97b3b8e65516528f723b85e6200000001000010f0
# Truth table of `mapOffset(h, t, i)`

`x` means that the dependency was discarded because `h + i ∉ {-1, 0, 1}`.

<!-- This is written as an ascii art table because the CSS was introducing so much space it was unreadable and doesn't support double lines -->

```
  h |   t  || i=-1 |  i=0 |  i=1
----|------||------|------|-----
 -1 |  -1  ||   x  |  -1  |  -1
 -1 |   0  ||   x  |  -1  |   0
 -1 |   1  ||   x  |  -1  |   1
  0 |   0  ||  -1  |   0  |   0
  0 |   1  ||  -1  |   0  |   1
  1 |   1  ||   0  |   1  |   x
```

:::

```
let mapOffset(h, t, i) = i + (if i <= 0 then h else t - 1)

propagated-dep(h0, t0, A, B)
propagated-dep(h1, t1, B, C)
h0 + h1 in {-1, 0, 1}
h0 + t1 in {-1, 0, 1}
-------------------------------------- Transitive property
propagated-dep(mapOffset(h0, t0, h1),
               mapOffset(h0, t0, t1),
               A, C)
```

```
let mapOffset(h, t, i) = i + (if i <= 0 then h else t - 1)

dep(h0, t0, A, B)
propagated-dep(h1, t1, B, C)
h0 + h1 in {-1, 0, 1}
h0 + t1 in {-1, 0, 1}
----------------------------- Take immediate dependencies' propagated dependencies
propagated-dep(mapOffset(h0, t0, h1),
               mapOffset(h0, t0, t1),
               A, C)
```

```
propagated-dep(h, t, A, B)
----------------------------- Propagated dependencies count as dependencies
dep(h, t, A, B)
```

Some explanation of this monstrosity is in order. In the common case of `nativeBuildInputs` or `buildInputs`, the target offset of a dependency is one greater than the host offset: `t = h + 1`. That means that:

```
let f(h, t, i) = i + (if i <= 0 then h else t - 1)
let f(h, h + 1, i) = i + (if i <= 0 then h else (h + 1) - 1)
let f(h, h + 1, i) = i + (if i <= 0 then h else h)
let f(h, h + 1, i) = i + h
```

This is where “sum-like” comes in from above: We can just sum all of the host offsets to get the host offset of the transitive dependency. The target offset is the transitive dependency is the host offset + 1, just as it was with the dependencies composed to make this transitive one; it can be ignored as it doesn’t add any new information.

Because of the bounds checks, the uncommon cases are `h = t` (`depsBuildBuild`, etc) and `h + 2 = t` (`depsBuildTarget`).

In the former case, the motivation for `mapOffset` is that since its host and target platforms are the same, no transitive dependency of it should be able to “discover” an offset greater than its reduced target offsets. `mapOffset` effectively “squashes” all its transitive dependencies’ offsets so that none will ever be greater than the target offset of the original `h = t` package.

In the other case, `h + 1` (0) is skipped over between the host (-1) and target (1) offsets. Instead of squashing the offsets, we need to “rip” them apart so no transitive dependency’s offset is 0.

Overall, the unifying theme here is that propagation shouldn’t be introducing transitive dependencies involving platforms the depending package is unaware of. \[One can imagine the depending package asking for dependencies with the platforms it knows about; other platforms it doesn’t know how to ask for. The platform description in that scenario is a kind of unforgeable capability.\] The offset bounds checking and definition of `mapOffset` together ensure that this is the case. Discovering a new offset is discovering a new platform, and since those platforms weren’t in the derivation “spec” of the needing package, they cannot be relevant. From a capability perspective, we can imagine that the host and target platforms of a package are the capabilities a package requires, and the depending package must provide the capability to the dependency.

#### Variables specifying dependencies {#variables-specifying-dependencies}

##### `depsBuildBuild` {#var-stdenv-depsBuildBuild}

A list of dependencies whose host and target platforms are the new derivation’s build platform. These are programs and libraries used at build time that produce programs and libraries also used at build time. If the dependency doesn’t care about the target platform (i.e. isn’t a compiler or similar tool), put it in `nativeBuildInputs` instead. The most common use of this `buildPackages.stdenv.cc`, the default C compiler for this role. That example crops up more than one might think in old commonly used C libraries.

Title: Nix Dependency Propagation: `mapOffset` Explanations and `depsBuildBuild`
Summary
This chunk elaborates on the `mapOffset` function and its role in Nix dependency propagation. It reiterates the `mapOffset` truth table and formal inference rules for combining propagated dependencies and converting them into direct dependencies. The `mapOffset` function's behavior is explained for common cases (where target offset `t` is `h + 1`, simplifying to `i + h`), and for uncommon cases like `h = t` (which 'squashes' offsets) and `h + 2 = t` (which 'rips' them apart to avoid intermediate values). The underlying principle is to prevent the introduction of transitive dependencies on platforms unknown to the depending package. The chunk then begins to define specific dependency variables, starting with `depsBuildBuild`, which are dependencies whose host and target platforms match the new derivation's build platform, exemplified by `buildPackages.stdenv.cc`.