Skip to content
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

mkShellMinimal: Create an ultra minimal nix-shell #132617

Draft
wants to merge 2 commits into
base: master
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions doc/builders/special.xml
Original file line number Diff line number Diff line change
Expand Up @@ -7,4 +7,5 @@
</para>
<xi:include href="special/fhs-environments.section.xml" />
<xi:include href="special/mkshell.section.xml" />
<xi:include href="special/mkshellMinimal.section.xml" />
</chapter>
38 changes: 38 additions & 0 deletions doc/builders/special/mkshellMinimal.section.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
# pkgs.mkShellMinimal {#sec-pkgs-mkShellMinimal}

`pkgs.mkShellMinimal` is similar to `mkShell` but does not rely on the
`stdenv` or even `bash`. It has a much smaller footprint and feature-set than
`mkShell` and is useful if you care about your closure size or being very explicit
about the dependencies (i.e. coreutils vs. busybox).

## Usage {#sec-pkgs-mkShellMinimal-usage}

```nix
let
nixpkgs = import <nixpkgs> { };
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
nixpkgs = import <nixpkgs> { };
pkgs = import <nixpkgs> { };

Since you use pkgs.mkShellMinimal above (and is more idiomatic IMO)

in
with nixpkgs;
mkShellMinimal {
Comment on lines +14 to +15
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can we remove this super-broad use of with from the doc? It doesn't seem to add anything over

Suggested change
with nixpkgs;
mkShellMinimal {
pkgs.mkShellMinimal {

name = "my-minimal-shell";

# Place your dependencies here
packages = [ ];

# You can do typical environment variable setting
FOO = "bar";
}
```

This shell can be started with `nix-shell` and has zero dependencies.

```bash
❯ nix path-info -rSsh $(nix-build shell.nix)
This derivation is not meant to be built, unless you want to capture the dependency closure.

/nix/store/8ka1hnlf06z3h2rpd00b4d9w5yxh0n39-setup 376.0 376.0
/nix/store/nprykggfqhdkn4r5lxxknjvlqc4qm1yl-builder.sh 280.0 280.0
/nix/store/xd8d72ccrxhaz3sxlmiqjnn1z0zwfhm8-my-minimal-shell 744.0 1.4K
```

`mkShellMinimal` is buildable as opposed to `mkShell`. This is useful if you want to upload the
transitive closure of the shell to a remote nix-store.
59 changes: 59 additions & 0 deletions pkgs/build-support/mkshell/minimal.nix
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
{ writeTextFile, writeScript, system }:

# A special kind of derivation that is only meant to be consumed by the
# nix-shell. This differs from the traditional `mkShell` in that:
# It does not come with traditional stdenv (i.e. coreutils).
# Its only dependency is essentially bash.
{
# a list of packages to add to the shell environment
# we simplify here rather than `native` or `propagated` since
# this is only ever used to be in a nix-shell; which is by default
# the current system
packages ? [ ], ... }@attrs:
derivation ({
inherit system;

name = "minimal-nix-shell";

# Nix checks if the stdenv attribute exists and sources that
# We use it here to setup the pure enviroment and do to clear envs like PATH
# reference: https://github.com/NixOS/nix/blob/94ec9e47030c2a7280503d338f0dca7ad92811f5/src/nix-build/nix-build.cc#L494
"stdenv" = writeTextFile rec {
name = "setup";
executable = true;
destination = "/${name}";
text = ''
set -e

# This is needed for `--pure` to work as expected.
# https://github.com/NixOS/nix/issues/5092
PATH=

for p in $packages; do
export PATH=$p/bin:$PATH
done
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You'll also want to set XDG_DATA_DIRS, because that's what bash uses to find completion scripts on demand. Stdenv sets it generically since #103501, which inspired devShell to do the same.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It comes at a very very little cost, but does enable shell completions.

If that's not the design choice you want to make, I wonder if it should even set PATH. Maybe someone only wants to set another environment variable, in which case PATH is redundant.

I guess it boils down to the question "when is it not a shell anymore?" which is obviously not clearly delineated, but needs an answer for this to be actually minimal.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Also, in what regard is it minimal? I would define it as the combination of the number of store paths and the total byte size. The latter is generally so disproportionally determined by dependencies that the size of the shellHook (or similar) does not matter, as long as it doesn't increase store path count.
I do consider store path count separately because it introduces latency with nix-copy-closure and most binary caches.

Copy link
Member

@Mic92 Mic92 Sep 18, 2021

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I would say minimal with regards to closure size but from that we could support features to make the whole thing practical usable i.e. set MANPATH.

'';
};

# Typically `mkShell` is not buildable. This has made it in practice, difficult to upload
# the dependency closure to a binary cache. Rather than add a confusing attribute to capture this
# let's just make the nix-shell buildable but message to the user that it doesn't make much sense.
#
#
# The builtin `export` dumps all current environment variables,
# which is where all build input references end up (e.g. $PATH for
# binaries). By writing this to $out, Nix can find and register
# them as runtime dependencies (since Nix greps for store paths
# through $out to find them)
# https://github.com/NixOS/nixpkgs/pull/95536/files#diff-282a02cc3871874f16401347d8fadc90d59d7ab11f6a99eaa5173c3867e1a160R358
#
# For purity you generally don't want to rely on /bin/sh but since this derivation is *strictly* for a nix-shell
# we can rely on the fact that the underlying system is POSIX compliant.
builder = writeScript "builder.sh" ''
#!/bin/sh
echo
echo "This derivation is not meant to be built, unless you want to capture the dependency closure.";
echo
export > $out
'';
} // attrs)
roberth marked this conversation as resolved.
Show resolved Hide resolved
1 change: 1 addition & 0 deletions pkgs/top-level/all-packages.nix
Original file line number Diff line number Diff line change
Expand Up @@ -623,6 +623,7 @@ in

mkShell = callPackage ../build-support/mkshell { };
mkShellNoCC = mkShell.override { stdenv = stdenvNoCC; };
mkShellMinimal = callPackage ../build-support/mkshell/minimal.nix { };
fzakaria marked this conversation as resolved.
Show resolved Hide resolved

nixBufferBuilders = import ../build-support/emacs/buffer.nix { inherit (pkgs) lib writeText; inherit (emacs.pkgs) inherit-local; };

Expand Down