diff options
author | Eelco Dolstra <eelco.dolstra@logicblox.com> | 2015-01-08 14:32:45 +0100 |
---|---|---|
committer | Eelco Dolstra <eelco.dolstra@logicblox.com> | 2015-01-08 14:32:45 +0100 |
commit | a957893b261a4438101c205e38fe8ce62b83a121 (patch) | |
tree | de3e3fd274ca4fa201f4fdf746929d55bfdc79f2 /scripts | |
parent | 7ba0e9cb481f00baca02f31393ad49681fc48a5d (diff) |
Allow nix-shell to be used as a #! interpreter
This allows scripts to fetch their own dependencies via nix-shell. For
instance, here is a Haskell script that, when executed, pulls in GHC
and the HTTP package:
#! /usr/bin/env nix-shell
#! nix-shell -i runghc -p haskellPackages.ghc haskellPackages.HTTP
import Network.HTTP
main = do
resp <- Network.HTTP.simpleHTTP (getRequest "http://nixos.org/")
body <- getResponseBody resp
print (take 100 body)
Or a Perl script that pulls in Perl and some CPAN packages:
#! /usr/bin/env nix-shell
#! nix-shell -i perl -p perl perlPackages.HTMLTokeParserSimple perlPackages.LWP
use HTML::TokeParser::Simple;
my $p = HTML::TokeParser::Simple->new(url => 'http://nixos.org/');
while (my $token = $p->get_tag("a")) {
my $href = $token->get_attr("href");
print "$href\n" if $href;
}
Note that the options to nix-shell must be given on a separate line
that starts with the magic string ‘#! nix-shell’. This is because
‘env’ does not allow passing arguments to an interpreter directly.
Diffstat (limited to 'scripts')
-rwxr-xr-x | scripts/nix-build.in | 37 |
1 files changed, 37 insertions, 0 deletions
diff --git a/scripts/nix-build.in b/scripts/nix-build.in index 3ac02ad35..b7c923f88 100755 --- a/scripts/nix-build.in +++ b/scripts/nix-build.in @@ -25,6 +25,8 @@ my @envExclude = (); my $myName = $runEnv ? "nix-shell" : "nix-build"; +my $inShebang = 0; +my $script; my $tmpDir = mkTempDir($myName); @@ -35,6 +37,29 @@ my $drvLink = "$tmpDir/derivation"; $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 && scalar @ARGV == 1) { + $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; + @ARGV = (); + while (<SCRIPT>) { + chomp; + if (/^\#\!\s*nix-shell (.*)$/) { + @ARGV = split / /, $1; + } + } + } + close SCRIPT; + } +} + + for (my $n = 0; $n < scalar @ARGV; $n++) { my $arg = $ARGV[$n]; @@ -155,6 +180,18 @@ for (my $n = 0; $n < scalar @ARGV; $n++) { $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" : ""; + $envCommand = "exec $execArgs $interpreter $script"; + } + elsif (substr($arg, 0, 1) eq "-") { push @buildArgs, $arg; } |