nix/scripts/nix-build.in

353 lines
11 KiB
Plaintext
Executable File
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

#! @perl@ -w @perlFlags@
use utf8;
use strict;
use Nix::Config;
use Nix::Store;
use Nix::Utils;
use File::Basename;
use Text::ParseWords;
use Cwd;
binmode STDERR, ":encoding(utf8)";
my $dryRun = 0;
my $verbose = 0;
my $runEnv = $0 =~ /nix-shell$/;
my $pure = 0;
my $fromArgs = 0;
my $packages = 0;
my $interactive = 1;
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 $inShebang = 0;
my $script;
my @savedArgs;
my $tmpDir = mkTempDir($myName);
my $outLink = "./result";
my $drvLink = "$tmpDir/derivation";
# Ensure that the $tmpDir is deleted.
$SIG{'INT'} = sub { exit 1 };
# Heuristic to see if we're invoked as a shebang script, namely, if we
# have a single argument, it's the name of an executable file, and it
# starts with "#!".
if ($runEnv && defined $ARGV[0] && $ARGV[0] !~ /nix-shell/) {
$script = $ARGV[0];
if (-f $script && -x $script) {
open SCRIPT, "<$script" or die "$0: cannot open $script: $!\n";
my $first = <SCRIPT>;
if ($first =~ /^\#\!/) {
$inShebang = 1;
@savedArgs = @ARGV; shift @savedArgs;
@ARGV = ();
while (<SCRIPT>) {
chomp;
if (/^\#\!\s*nix-shell (.*)$/) {
push @ARGV, shellwords(/ /, $1);
}
}
}
close SCRIPT;
}
}
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" || $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" || $arg eq "-o") {
$n++;
die "$0: $arg requires an argument\n" unless $n < scalar @ARGV;
$outLink = $ARGV[$n];
}
elsif ($arg eq "--attr" || $arg eq "-A" || $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" || $arg eq "-j" || $arg eq "--max-silent-time" || $arg eq "--log-type" || $arg eq "--cores" || $arg eq "--timeout" || $arg eq '--add-root') {
$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" || 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 "--check") {
push @buildArgs, $arg;
}
elsif ($arg eq "--run-env") { # obsolete
$runEnv = 1;
}
elsif ($arg eq "--command" || $arg eq "--run") {
$n++;
die "$0: $arg requires an argument\n" unless $n < scalar @ARGV;
$envCommand = "$ARGV[$n]\nexit";
$interactive = 0 if $arg eq "--run";
}
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 ($arg eq "--impure") { $pure = 0; }
elsif ($arg eq "--expr" || $arg eq "-E") {
$fromArgs = 1;
push @instArgs, "--expr";
}
elsif ($arg eq "--packages" || $arg eq "-p") {
$packages = 1;
}
elsif ($inShebang && $arg eq "-i") {
$n++;
die "$0: $arg requires an argument\n" unless $n < scalar @ARGV;
my $interpreter = $ARGV[$n];
# Überhack to support Perl. Perl examines the shebang and
# executes it unless it contains the string "perl" or "indir",
# or (undocumented) argv[0] does not contain "perl". Exploit
# the latter by doing "exec -a".
my $execArgs = $interpreter =~ /perl/ ? "-a PERL" : "";
sub shellEscape {
my $s = $_;
$s =~ s/'/'\\''/g;
return "'" . $s . "'";
}
$envCommand = "exec $execArgs $interpreter $script ${\(join ' ', (map shellEscape, @savedArgs))}";
}
elsif (substr($arg, 0, 1) eq "-") {
push @buildArgs, $arg;
}
elsif ($arg eq "-Q" || $arg eq "--no-build-output") {
push @buildArgs, $arg;
push @instArgs, $arg;
}
else {
push @exprs, $arg;
}
}
die "$0: -p and -E are mutually exclusive\n" if $packages && $fromArgs;
if ($packages) {
push @instArgs, "--expr";
@exprs = (
'with import <nixpkgs> { }; runCommand "shell" { buildInputs = [ '
. (join " ", map { "($_)" } @exprs) . ']; } ""');
} elsif (!$fromArgs) {
@exprs = ("shell.nix") if scalar @exprs == 0 && $runEnv && -e "shell.nix";
@exprs = ("default.nix") if scalar @exprs == 0;
}
$ENV{'IN_NIX_SHELL'} = 1 if $runEnv;
foreach my $expr (@exprs) {
# Instantiate.
my @drvPaths;
if ($expr !~ /^\/.*\.drv$/) {
# If we're in a #! script, interpret filenames relative to the
# script.
$expr = dirname(Cwd::abs_path($script)) . "/" . $expr
if $inShebang && !$packages && $expr !~ /^\//;
# !!! 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 = (split '!',$drvPath)[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", "--no-output", "--no-gc-warning", @buildArgs, @inputDrvs, @{$drv->{inputSrcs}}) == 0
or die "$0: failed to build all dependencies\n";
# Set the environment.
my $tmp = $ENV{"TMPDIR"} // $ENV{"XDG_RUNTIME_DIR"} // "/tmp";
if ($pure) {
foreach my $name (keys %ENV) {
next if grep { $_ eq $name } ("HOME", "USER", "LOGNAME", "DISPLAY", "PATH", "TERM", "IN_NIX_SHELL", "TZ", "PAGER", "NIX_BUILD_SHELL");
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'} = $tmp;
$ENV{'NIX_STORE'} = $Nix::Config::storeDir;
$ENV{$_} = $drv->{env}->{$_} foreach keys %{$drv->{env}};
# 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,
"rm -rf '$tmpDir'; " .
'unset BASH_ENV; ' .
'[ -n "$PS1" ] && [ -e ~/.bashrc ] && source ~/.bashrc; ' .
($pure ? '' : 'p=$PATH; ' ) .
'dontAddDisableDepTrack=1; ' .
'[ -e $stdenv/setup ] && source $stdenv/setup; ' .
($pure ? '' : 'PATH=$PATH:$p; unset p; ') .
'set +e; ' .
'[ -n "$PS1" ] && PS1="\n\[\033[1;32m\][nix-shell:\w]$\[\033[0m\] "; ' .
'if [ "$(type -t runHook)" = function ]; then runHook shellHook; fi; ' .
'unset NIX_ENFORCE_PURITY; ' .
'unset NIX_INDENT_MAKE; ' .
'shopt -u nullglob; ' .
'unset TZ; ' . (defined $ENV{'TZ'} ? "export TZ='${ENV{'TZ'}}'; " : '') .
$envCommand);
$ENV{BASH_ENV} = $rcfile;
my @args = ($ENV{NIX_BUILD_SHELL} // "bash");
push @args, "--rcfile" if $interactive;
push @args, $rcfile;
exec @args;
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 ($? >> 8 || 1);
}
next if $dryRun;
foreach my $outPath (@outPaths) {
my $target = readlink $outPath or die "cannot read symlink $outPath";
print "$target\n";
}
}