Skip to content

Commit

Permalink
lib: add trivial.nixpkgsStorePathString providing a string with conte…
Browse files Browse the repository at this point in the history
…xt for self

This is an extremely subtle feature which is necessary to be able to
incur dependencies on this nixpkgs's source code without copying it
twice (NixOS/nix#9428,
NixOS/nix#5868).

pkgs.path is not sufficient, because actually using it incurs a copy,
which winds up looking like /nix/store/HASH-HASH-source in flakes.
Similarly, `toString pkgs.path` is not sufficient because that does not
have any string context, so it will not incur a dependency if it's used.
It's exceptionally subtle.

There are four cases:
- non flakes, pure mode: can't do anything about this, we must copy to
  the store.
- non flakes, not already in the store: can't do anything about this, we
  are copying to the store.
- non flakes, already in the store: storePath gives us a string with
  context for free.
- flakes: overlay makes it a stringification of self.outPath.

In all cases, this is a string with appropriate context to transfer this
nixpkgs to another system.
  • Loading branch information
lf- committed Jan 20, 2024
1 parent a9da154 commit 328d457
Show file tree
Hide file tree
Showing 4 changed files with 57 additions and 4 deletions.
6 changes: 3 additions & 3 deletions flake.nix
Original file line number Diff line number Diff line change
Expand Up @@ -9,8 +9,8 @@
nixpkgs = self;
};

libVersionInfoOverlay = import ./lib/flake-version-info.nix self;
lib = (import ./lib).extend libVersionInfoOverlay;
libFlakeInfoOverlay = import ./lib/flake-info.nix self;
lib = (import ./lib).extend libFlakeInfoOverlay;

forAllSystems = lib.genAttrs lib.systems.flakeExposed;
in
Expand Down Expand Up @@ -72,7 +72,7 @@
# information rich.
legacyPackages = forAllSystems (system:
(import ./. { inherit system; }).extend (final: prev: {
lib = prev.lib.extend libVersionInfoOverlay;
lib = prev.lib.extend libFlakeInfoOverlay;
})
);

Expand Down
11 changes: 11 additions & 0 deletions lib/flake-version-info.nix → lib/flake-info.nix
Original file line number Diff line number Diff line change
Expand Up @@ -16,5 +16,16 @@ finalLib: prevLib: # lib overlay
versionSuffix =
".${finalLib.substring 0 8 (self.lastModifiedDate or "19700101")}.${self.shortRev or "dirty"}";
revisionWithDefault = default: self.rev or default;

# This overrides the nixpkgsStorePathString logic in lib/trivial.nix for
# the special case of flakes, where we are in pure eval mode and thus
# would not be able to use builtins.storePath to establish a dependency on
# this nixpkgs' sources (https://github.com/NixOS/nix/issues/5868).
#
# However, in this particular case, due to being used from a flake, we *do*
# have a string with context for our own sources in the store in the
# `outPath` of the `self` flake input, which we obtain by calling toString
# on it.
nixpkgsStorePathString = toString self;
};
}
2 changes: 1 addition & 1 deletion lib/flake.nix
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,6 @@
let
lib0 = import ./.;
in {
lib = lib0.extend (import ./flake-version-info.nix self);
lib = lib0.extend (import ./flake-info.nix self);
};
}
42 changes: 42 additions & 0 deletions lib/trivial.nix
Original file line number Diff line number Diff line change
Expand Up @@ -232,6 +232,48 @@ in {

nixpkgsVersion = builtins.trace "`lib.nixpkgsVersion` is deprecated, use `lib.version` instead!" version;

/* The store path to this nixpkgs as a string with context. This can be used
to incur a dependency on this nixpkgs, for example, to symlink it into a
NixOS closure.
If this nixpkgs is used as a flake directly (via `nixpkgs.lib.trivial`
rather than via `import nixpkgs`), or it is already in the Nix store in
non-flake usage such as being fetched via `builtins.fetchTarball`,
evaluating this attribute is quick and does not copy anything new to the
store.
This is different than pkgs.path if nixpkgs is already in the Nix store,
since pkgs.path is a path to nixpkgs rather than a string-with-context,
and thus its usage typically, undesirably, results in copying this
nixpkgs.
See: https://github.com/NixOS/nix/issues/5868
https://github.com/NixOS/nix/issues/9428
Type: String
*/

nixpkgsStorePathString =
# Note: this is overlayed by lib/flake-info.nix if nixpkgs is used directly
# as a flake.
if lib.strings.isStorePath (toString ../.) then
# While `toString ../.` is already a store path here, it doesn't contain
# a dependency on that store path in its string context.
# We can use `builtins.storePath` to establishes a dependency on an
# existing store path that nixpkgs was imported with.
if lib.trivial.inPureEvalMode then
# However, in pure eval mode, that builtin is not currently supported, see https://github.com/NixOS/nix/issues/5868.
# So we fall back to string interpolation of a path, which copies the path that's already in the store to the store once more,
# which is slow and gives it a doubly-hashed base name, but there's not much we can do
# See also https://github.com/NixOS/nix/issues/9428
"${../.}"
else
# Only outside pure eval we can actually use builtins.storePath, which can re-use the existing store path
builtins.storePath ../.
else
# Here ../. is not in the store already, so we need to use string interpolation to copy the path to the store.
"${../.}";

/* Determine whether the function is being called from inside a Nix
shell.
Expand Down

0 comments on commit 328d457

Please sign in to comment.