511455965e
Nixpkgs' stdenv disables dependency tracking by default. That makes sense for one-time builds, but in an interactive environment we expect repeated "make" invocations to do the right thing.
252 lines
7.5 KiB
Text
Executable file
252 lines
7.5 KiB
Text
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;
|
|
# !!! 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;
|
|
}
|
|
|
|
if ($runEnv) {
|
|
die "$0: a single derivation is required\n" if scalar @drvPaths != 1;
|
|
my $drvPath = readlink $drvPaths[0] or die "cannot read symlink `$drvPaths[0]'";
|
|
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; ' .
|
|
$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;
|
|
$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";
|
|
}
|
|
}
|