bash: Make interactive the default

The status quo of `bash` not being interactive is frustrating for many users,
because trying to use it interactively is just messed up, and
`bashInteractive` is not intuitive and barely discoverable.

This was brought to my (and many others) attention by @stahnma in his
[talk at CfgMgmtCamp 2025](https://cfp.cfgmgmtcamp.org/ghent2025/talk/YUVUTN/),
where he highlighted this as one of the frustrations he ran into when
learning Nix.

Why this is fine:
- No reason for not making interactive the default was given in the original commit (6c6ff6f36f), but probably it was due to the increase in closure size
- The closure size only increases by 6.9MiB (19.5%) today, with the
  added dependency on the store paths for readline and ncurses, which
  are needed on systems in almost all cases anyways
- If somebody really needs to get a more minimal system, they can use
  the newly-introduced `bashNonInteractive` instead now
- Though to apply it consistently, they'll need to do that in an
  overlay like
  ```
  final: prev: {
    bash = self.bashNonInteractive;
  }
  ```

  Or alternatively using the `system.replaceDependencies.replacements`
  NixOS option approach.

While there's also other such `*Interactive` packages that could use the
same treatment, `bash` is a great start.

This was already attempted before in
https://github.com/NixOS/nixpkgs/pull/151227, but was not continued for
unknown reason.

To avoid stdenv becoming bigger, all uses of bash in the (working)
stdenv's are changed to the explicitly non-interactive version here.

This commit will however still cause a mass rebuild for all packages (and reverse deps)
making use of the default bash.
This commit is contained in:
Silvan Mosberger 2025-02-04 13:04:31 +01:00
parent 10053c5dfd
commit e3491c9e40
13 changed files with 44 additions and 44 deletions

View File

@ -22,7 +22,7 @@
pkg-config,
texinfo,
bison,
bash,
bashNonInteractive,
}:
stdenv.mkDerivation rec {
@ -79,7 +79,7 @@ stdenv.mkDerivation rec {
buildInputs =
[
perl
bash
bashNonInteractive
]
++ lib.optionals enableGhostscript [
ghostscript

View File

@ -1,7 +1,7 @@
{
lib,
autoreconfHook,
bash,
bashNonInteractive,
libtool,
fetchFromGitHub,
nix-update-script,
@ -48,7 +48,7 @@ stdenv.mkDerivation (finalAttrs: {
buildInputs = [
autoreconfHook
# For patchShebangs in postInstall
bash
bashNonInteractive
perl
];

View File

@ -24,7 +24,7 @@
, zlib
# platform-specific dependencies
, bash
, bashNonInteractive
, darwin
, windows
@ -235,7 +235,7 @@ in with passthru; stdenv.mkDerivation (finalAttrs: {
inherit nativeBuildInputs;
buildInputs = lib.optionals (!stdenv.hostPlatform.isWindows) [
bash # only required for patchShebangs
bashNonInteractive # only required for patchShebangs
] ++ buildInputs;
prePatch = optionalString stdenv.hostPlatform.isDarwin ''
@ -329,7 +329,7 @@ in with passthru; stdenv.mkDerivation (finalAttrs: {
postPatch = optionalString (!stdenv.hostPlatform.isWindows) ''
substituteInPlace Lib/subprocess.py \
--replace-fail "'/bin/sh'" "'${bash}/bin/sh'"
--replace-fail "'/bin/sh'" "'${bashNonInteractive}/bin/sh'"
'' + optionalString mimetypesSupport ''
substituteInPlace Lib/mimetypes.py \
--replace-fail "@mime-types@" "${mailcap}"
@ -611,7 +611,7 @@ in with passthru; stdenv.mkDerivation (finalAttrs: {
] ++ lib.optionals (stdenv.hostPlatform != stdenv.buildPlatform) [
# Ensure we don't have references to build-time packages.
# These typically end up in shebangs.
pythonOnBuildForHost buildPackages.bash
pythonOnBuildForHost buildPackages.bashNonInteractive
];
separateDebugInfo = true;

View File

@ -3,7 +3,7 @@
lib,
fetchurl,
libiconv,
bash,
bashNonInteractive,
updateAutotoolsGnuConfigScriptsHook,
}:
@ -84,7 +84,7 @@ stdenv.mkDerivation rec {
];
buildInputs =
lib.optionals (!stdenv.hostPlatform.isMinGW) [
bash
bashNonInteractive
]
++ lib.optionals (!stdenv.hostPlatform.isLinux && !stdenv.hostPlatform.isCygwin) [
# HACK, see #10874 (and 14664)

View File

@ -5,7 +5,7 @@
fetchurl,
perl,
libintl,
bash,
bashNonInteractive,
updateAutotoolsGnuConfigScriptsHook,
gnulib,
gawk,
@ -83,7 +83,7 @@ stdenv.mkDerivation {
nativeBuildInputs = [ updateAutotoolsGnuConfigScriptsHook ];
buildInputs =
[
bash
bashNonInteractive
libintl
]
++ optionals stdenv.hostPlatform.isSunOS [

View File

@ -6,7 +6,7 @@
fetchurl,
perl,
libintl,
bash,
bashNonInteractive,
updateAutotoolsGnuConfigScriptsHook,
gnulib,
gawk,
@ -57,7 +57,7 @@ let
xz
libintl
libiconv
bash
bashNonInteractive
gnulib
gawk
freebsd

View File

@ -6,8 +6,7 @@
, bison
, util-linux
# patch for cygwin requires readline support
, interactive ? stdenv.hostPlatform.isCygwin
, interactive ? true
, readline
, withDocs ? null
, forFHSEnv ? false

View File

@ -153,7 +153,7 @@ let
runtimeShell = prevStage.ccWrapperStdenv.shell;
};
bash = prevStage.bash or bootstrapTools;
bashNonInteractive = prevStage.bashNonInteractive or bootstrapTools;
thisStdenv = import ../generic {
name = "${name}-stdenv-darwin";
@ -168,7 +168,7 @@ let
inherit extraNativeBuildInputs;
preHook =
lib.optionalString (!isBuiltByNixpkgsCompiler bash) ''
lib.optionalString (!isBuiltByNixpkgsCompiler bashNonInteractive) ''
# Don't patch #!/interpreter because it leads to retained
# dependencies on the bootstrapTools in the final stdenv.
dontPatchShebangs=1
@ -181,9 +181,9 @@ let
export PATH_LOCALE=${prevStage.darwin.locale}/share/locale
'';
shell = bash + "/bin/bash";
shell = bashNonInteractive + "/bin/bash";
initialPath = [
bash
bashNonInteractive
prevStage.file
bootstrapTools
];
@ -328,7 +328,7 @@ let
# SDK packages include propagated packages and source release packages built during the bootstrap.
sdkPackages = prevStage: {
inherit (prevStage)
bash
bashNonInteractive
libpng
libxml2
libxo
@ -408,7 +408,7 @@ assert bootstrapTools.passthru.isFromBootstrapFiles or false; # sanity check
# stage should only access the stage that came before it.
ccWrapperStdenv = self.stdenv;
bash = bootstrapTools // {
bashNonInteractive = bootstrapTools // {
shellPath = "/bin/bash";
};
@ -600,7 +600,7 @@ assert bootstrapTools.passthru.isFromBootstrapFiles or false; # sanity check
(llvmLibrariesPackages prevStage)
{
inherit (prevStage)
bash
bashNonInteractive
cctools
coreutils
cpio
@ -847,14 +847,14 @@ assert bootstrapTools.passthru.isFromBootstrapFiles or false; # sanity check
inherit (prevStage) ccWrapperStdenv;
# Avoid an infinite recursion due to the SDKs including ncurses, which depends on bash in its `dev` output.
bash = super.bash.override { stdenv = self.darwin.bootstrapStdenv; };
bashNonInteractive = super.bashNonInteractive.override { stdenv = self.darwin.bootstrapStdenv; };
# Avoid pulling in a full python and its extra dependencies for the llvm/clang builds.
libxml2 = super.libxml2.override { pythonSupport = false; };
# Use Bash from this stage to avoid propagating Bash from a previous stage to the final stdenv.
ncurses = super.ncurses.override {
stdenv = self.darwin.bootstrapStdenv.override { shell = lib.getExe self.bash; };
stdenv = self.darwin.bootstrapStdenv.override { shell = lib.getExe self.bashNonInteractive; };
};
darwin = super.darwin.overrideScope (
@ -1162,7 +1162,7 @@ assert bootstrapTools.passthru.isFromBootstrapFiles or false; # sanity check
extraAttrs = {
inherit bootstrapTools;
libc = prevStage.darwin.libSystem;
shellPackage = prevStage.bash;
shellPackage = prevStage.bashNonInteractive;
};
disallowedRequisites = [ bootstrapTools.out ];
@ -1172,7 +1172,7 @@ assert bootstrapTools.passthru.isFromBootstrapFiles or false; # sanity check
with prevStage;
[
apple-sdk
bash
bashNonInteractive
bzip2.bin
bzip2.out
cc.expand-response-params

View File

@ -74,7 +74,7 @@ let
expand-response-params = "";
bsdcp = linkBootstrap { paths = [ "bin/bsdcp" ]; };
patchelf = linkBootstrap { paths = [ "bin/patchelf" ]; };
bash = linkBootstrap {
bashNonInteractive = linkBootstrap {
paths = [
"bin/bash"
"bin/sh"
@ -376,13 +376,13 @@ let
gawk
diffutils
patch
bash
bashNonInteractive
xz
gzip
bzip2
bsdcp
];
shell = "${prevStage.bash}/bin/bash";
shell = "${prevStage.bashNonInteractive}/bin/bash";
stdenvNoCC = import ../generic {
inherit
config
@ -471,7 +471,7 @@ in
# we CAN'T import LLVM because the compiler built here is used to build the final compiler and the final compiler must not be built by the bootstrap compiler
inherit (bootstrapTools)
patchelf
bash
bashNonInteractive
curl
coreutils
diffutils

View File

@ -10,7 +10,7 @@
pkgs.gzip
pkgs.bzip2.bin
pkgs.gnumake
pkgs.bash
pkgs.bashNonInteractive
pkgs.patch
pkgs.xz.bin

View File

@ -770,7 +770,7 @@ assert bootstrapTools.passthru.isFromBootstrapFiles or false; # sanity check
bzip2
xz
zlib
bash
bashNonInteractive
binutils
coreutils
diffutils
@ -806,7 +806,7 @@ assert bootstrapTools.passthru.isFromBootstrapFiles or false; # sanity check
gzip
bzip2
xz
bash
bashNonInteractive
binutils.bintools
coreutils
diffutils
@ -866,7 +866,7 @@ assert bootstrapTools.passthru.isFromBootstrapFiles or false; # sanity check
gzip
bzip2
xz
bash
bashNonInteractive
coreutils
diffutils
findutils

View File

@ -3,7 +3,7 @@
stdenv,
fetchFromGitHub,
cmake,
bash,
bashNonInteractive,
gnugrep,
fixDarwinDylibNames,
file,
@ -37,7 +37,7 @@ stdenv.mkDerivation rec {
};
nativeBuildInputs = [ cmake ] ++ lib.optional stdenv.hostPlatform.isDarwin fixDarwinDylibNames;
buildInputs = lib.optional stdenv.hostPlatform.isUnix bash;
buildInputs = lib.optional stdenv.hostPlatform.isUnix bashNonInteractive;
patches = [
# This patches makes sure we do not attempt to use the MD5 implementation

View File

@ -5586,17 +5586,18 @@ with pkgs;
### SHELLS
runtimeShell = "${runtimeShellPackage}${runtimeShellPackage.shellPath}";
runtimeShellPackage = bash;
runtimeShellPackage = bashNonInteractive;
bash = lowPrio (callPackage ../shells/bash/5.nix { });
bash = callPackage ../shells/bash/5.nix { };
bashNonInteractive = lowPrio (callPackage ../shells/bash/5.nix {
interactive = false;
});
# WARNING: this attribute is used by nix-shell so it shouldn't be removed/renamed
bashInteractive = callPackage ../shells/bash/5.nix {
interactive = true;
};
bashInteractiveFHS = callPackage ../shells/bash/5.nix {
interactive = true;
bashInteractive = bash;
bashFHS = callPackage ../shells/bash/5.nix {
forFHSEnv = true;
};
bashInteractiveFHS = bashFHS;
carapace = callPackage ../shells/carapace {
buildGoModule = buildGo123Module;