forked from lix-project/lix
![Eelco Dolstra](/assets/img/avatar_default.png)
Nixpkgs's stdenv setup script sets the "nullglob" option, but doing so breaks Bash completion on NixOS (when ‘programs.bash.enableCompletion’ is set) and on Ubuntu. So clear that flag afterwards. Of course, this may break stdenv functions in subtle ways...
259 lines
7.7 KiB
Plaintext
Executable file
259 lines
7.7 KiB
Plaintext
Executable file
#! @perl@ -w @perlFlags@
|
|
|
|
use strict;
|
|
use Nix::Config;
|
|
use Nix::Store;
|
|
use Nix::Utils;
|
|
use File::Temp qw(tempdir);
|
|
|
|
|
|
my $dryRun = 0;
|
|
my $verbose = 0;
|
|
my $runEnv = $0 =~ /nix-shell$/;
|
|
my $pure = 0;
|
|
|
|
my @instArgs = ();
|
|
my @buildArgs = ();
|
|
my @exprs = ();
|
|
|
|
my $shell = $ENV{SHELL} || "/bin/sh";
|
|
my $envCommand = ""; # interactive shell
|
|
my @envExclude = ();
|
|
|
|
my $myName = $runEnv ? "nix-shell" : "nix-build";
|
|
|
|
|
|
my $tmpDir = tempdir("$myName.XXXXXX", CLEANUP => 1, TMPDIR => 1)
|
|
or die "cannot create a temporary directory";
|
|
|
|
my $outLink = "./result";
|
|
my $drvLink = "$tmpDir/derivation";
|
|
|
|
# Ensure that the $tmpDir is deleted.
|
|
$SIG{'INT'} = sub { exit 1 };
|
|
|
|
|
|
for (my $n = 0; $n < scalar @ARGV; $n++) {
|
|
my $arg = $ARGV[$n];
|
|
|
|
if ($arg eq "--help") {
|
|
exec "man $myName" or die;
|
|
}
|
|
|
|
elsif ($arg eq "--version") {
|
|
print "$myName (Nix) $Nix::Config::version\n";
|
|
exit 0;
|
|
}
|
|
|
|
elsif ($arg eq "--add-drv-link") {
|
|
$drvLink = "./derivation";
|
|
}
|
|
|
|
elsif ($arg eq "--no-out-link" or $arg eq "--no-link") {
|
|
$outLink = "$tmpDir/result";
|
|
}
|
|
|
|
elsif ($arg eq "--drv-link") {
|
|
$n++;
|
|
die "$0: `$arg' requires an argument\n" unless $n < scalar @ARGV;
|
|
$drvLink = $ARGV[$n];
|
|
}
|
|
|
|
elsif ($arg eq "--out-link" or $arg eq "-o") {
|
|
$n++;
|
|
die "$0: `$arg' requires an argument\n" unless $n < scalar @ARGV;
|
|
$outLink = $ARGV[$n];
|
|
}
|
|
|
|
elsif ($arg eq "--attr" or $arg eq "-A" or $arg eq "-I") {
|
|
$n++;
|
|
die "$0: `$arg' requires an argument\n" unless $n < scalar @ARGV;
|
|
push @instArgs, ($arg, $ARGV[$n]);
|
|
}
|
|
|
|
elsif ($arg eq "--arg" || $arg eq "--argstr") {
|
|
die "$0: `$arg' requires two arguments\n" unless $n + 2 < scalar @ARGV;
|
|
push @instArgs, ($arg, $ARGV[$n + 1], $ARGV[$n + 2]);
|
|
$n += 2;
|
|
}
|
|
|
|
elsif ($arg eq "--log-type") {
|
|
$n++;
|
|
die "$0: `$arg' requires an argument\n" unless $n < scalar @ARGV;
|
|
push @instArgs, ($arg, $ARGV[$n]);
|
|
push @buildArgs, ($arg, $ARGV[$n]);
|
|
}
|
|
|
|
elsif ($arg eq "--option") {
|
|
die "$0: `$arg' requires two arguments\n" unless $n + 2 < scalar @ARGV;
|
|
push @instArgs, ($arg, $ARGV[$n + 1], $ARGV[$n + 2]);
|
|
push @buildArgs, ($arg, $ARGV[$n + 1], $ARGV[$n + 2]);
|
|
$n += 2;
|
|
}
|
|
|
|
elsif ($arg eq "--max-jobs" or $arg eq "-j" or $arg eq "--max-silent-time" or $arg eq "--log-type" or $arg eq "--cores" or $arg eq "--timeout") {
|
|
$n++;
|
|
die "$0: `$arg' requires an argument\n" unless $n < scalar @ARGV;
|
|
push @buildArgs, ($arg, $ARGV[$n]);
|
|
}
|
|
|
|
elsif ($arg eq "--dry-run") {
|
|
push @buildArgs, "--dry-run";
|
|
$dryRun = 1;
|
|
}
|
|
|
|
elsif ($arg eq "--show-trace") {
|
|
push @instArgs, $arg;
|
|
}
|
|
|
|
elsif ($arg eq "-") {
|
|
@exprs = ("-");
|
|
}
|
|
|
|
elsif ($arg eq "--verbose" or substr($arg, 0, 2) eq "-v") {
|
|
push @buildArgs, $arg;
|
|
push @instArgs, $arg;
|
|
$verbose = 1;
|
|
}
|
|
|
|
elsif ($arg eq "--quiet" || $arg eq "--repair") {
|
|
push @buildArgs, $arg;
|
|
push @instArgs, $arg;
|
|
}
|
|
|
|
elsif ($arg eq "--run-env") { # obsolete
|
|
$runEnv = 1;
|
|
}
|
|
|
|
elsif ($arg eq "--command") {
|
|
$n++;
|
|
die "$0: `$arg' requires an argument\n" unless $n < scalar @ARGV;
|
|
$envCommand = "$ARGV[$n]\nexit $!";
|
|
}
|
|
|
|
elsif ($arg eq "--exclude") {
|
|
$n++;
|
|
die "$0: `$arg' requires an argument\n" unless $n < scalar @ARGV;
|
|
push @envExclude, $ARGV[$n];
|
|
}
|
|
|
|
elsif ($arg eq "--pure") {
|
|
$pure = 1;
|
|
}
|
|
|
|
elsif (substr($arg, 0, 1) eq "-") {
|
|
push @buildArgs, $arg;
|
|
}
|
|
|
|
else {
|
|
push @exprs, $arg;
|
|
}
|
|
}
|
|
|
|
@exprs = ("./default.nix") if scalar @exprs == 0;
|
|
|
|
|
|
foreach my $expr (@exprs) {
|
|
|
|
# Instantiate.
|
|
my @drvPaths;
|
|
if ($expr !~ /^\/.*\.drv$/) {
|
|
# !!! would prefer the perl 5.8.0 pipe open feature here.
|
|
my $pid = open(DRVPATHS, "-|") || exec "$Nix::Config::binDir/nix-instantiate", "--add-root", $drvLink, "--indirect", @instArgs, $expr;
|
|
while (<DRVPATHS>) {chomp; push @drvPaths, $_;}
|
|
if (!close DRVPATHS) {
|
|
die "nix-instantiate killed by signal " . ($? & 127) . "\n" if ($? & 127);
|
|
exit 1;
|
|
}
|
|
} else {
|
|
push @drvPaths, $expr;
|
|
}
|
|
|
|
if ($runEnv) {
|
|
die "$0: a single derivation is required\n" if scalar @drvPaths != 1;
|
|
my $drvPath = $drvPaths[0];
|
|
$drvPath = readlink $drvPath or die "cannot read symlink `$drvPath'" if -l $drvPath;
|
|
my $drv = derivationFromPath($drvPath);
|
|
|
|
# Build or fetch all dependencies of the derivation.
|
|
my @inputDrvs = grep { my $x = $_; (grep { $x =~ $_ } @envExclude) == 0 } @{$drv->{inputDrvs}};
|
|
system("$Nix::Config::binDir/nix-store -r @buildArgs @inputDrvs @{$drv->{inputSrcs}} > /dev/null") == 0
|
|
or die "$0: failed to build all dependencies\n";
|
|
|
|
# Set the environment.
|
|
if ($pure) {
|
|
foreach my $name (keys %ENV) {
|
|
next if $name eq "HOME" || $name eq "USER" || $name eq "LOGNAME" || $name eq "DISPLAY" || $name eq "PATH";
|
|
delete $ENV{$name};
|
|
}
|
|
# NixOS hack: prevent /etc/bashrc from sourcing /etc/profile.
|
|
$ENV{'__ETC_PROFILE_SOURCED'} = 1;
|
|
}
|
|
$ENV{'NIX_BUILD_TOP'} = $ENV{'TMPDIR'} = $ENV{'TEMPDIR'} = $ENV{'TMP'} = $ENV{'TEMP'} = $ENV{'TMPDIR'} // "/tmp";
|
|
$ENV{'NIX_STORE'} = $Nix::Config::storeDir;
|
|
$ENV{$_} = $drv->{env}->{$_} foreach keys %{$drv->{env}};
|
|
|
|
$ENV{'IN_NIX_SHELL'} = 1;
|
|
|
|
# Run a shell using the derivation's environment. For
|
|
# convenience, source $stdenv/setup to setup additional
|
|
# environment variables and shell functions. Also don't lose
|
|
# the current $PATH directories.
|
|
my $rcfile = "$tmpDir/rc";
|
|
writeFile(
|
|
$rcfile,
|
|
'[ -e ~/.bashrc ] && source ~/.bashrc; ' .
|
|
($pure ? '' : 'p=$PATH; ' ) .
|
|
'dontAddDisableDepTrack=1; ' .
|
|
'[ -e $stdenv/setup ] && source $stdenv/setup; ' .
|
|
($pure ? '' : 'PATH=$PATH:$p; ') .
|
|
'set +e; ' .
|
|
'PS1="\n\[\033[1;32m\][nix-shell:\w]$\[\033[0m\] "; ' .
|
|
'unset NIX_ENFORCE_PURITY; ' .
|
|
'shopt -u nullglob; ' .
|
|
$envCommand);
|
|
exec($ENV{NIX_BUILD_SHELL} // "bash", "--rcfile", $rcfile);
|
|
die;
|
|
}
|
|
|
|
# Ugly hackery to make "nix-build -A foo.all" produce symlinks
|
|
# ./result, ./result-dev, and so on, rather than ./result,
|
|
# ./result-2-dev, and so on. This combines multiple derivation
|
|
# paths into one "/nix/store/drv-path!out1,out2,..." argument.
|
|
my $prevDrvPath = "";
|
|
my @drvPaths2;
|
|
foreach my $drvPath (@drvPaths) {
|
|
my $p = $drvPath; my $output = "out";
|
|
if ($drvPath =~ /(.*)!(.*)/) {
|
|
$p = $1; $output = $2;
|
|
} else {
|
|
$p = $drvPath;
|
|
}
|
|
my $target = readlink $p or die "cannot read symlink `$p'";
|
|
print STDERR "derivation is $target\n" if $verbose;
|
|
if ($target eq $prevDrvPath) {
|
|
push @drvPaths2, (pop @drvPaths2) . "," . $output;
|
|
} else {
|
|
push @drvPaths2, $target . "!" . $output;
|
|
$prevDrvPath = $target;
|
|
}
|
|
}
|
|
|
|
# Build.
|
|
my @outPaths;
|
|
my $pid = open(OUTPATHS, "-|") || exec "$Nix::Config::binDir/nix-store", "--add-root", $outLink, "--indirect", "-r",
|
|
@buildArgs, @drvPaths2;
|
|
while (<OUTPATHS>) {chomp; push @outPaths, $_;}
|
|
if (!close OUTPATHS) {
|
|
die "nix-store killed by signal " . ($? & 127) . "\n" if ($? & 127);
|
|
exit 1;
|
|
}
|
|
|
|
next if $dryRun;
|
|
|
|
foreach my $outPath (@outPaths) {
|
|
my $target = readlink $outPath or die "cannot read symlink `$outPath'";
|
|
print "$target\n";
|
|
}
|
|
}
|