200 lines
		
	
	
	
		
			5.7 KiB
		
	
	
	
		
			Text
		
	
	
		
			Executable file
		
	
	
	
	
			
		
		
	
	
			200 lines
		
	
	
	
		
			5.7 KiB
		
	
	
	
		
			Text
		
	
	
		
			Executable file
		
	
	
	
	
| #! @perl@ -w @perlFlags@
 | |
| 
 | |
| use strict;
 | |
| use File::Basename;
 | |
| use File::Path qw(mkpath);
 | |
| use Nix::Config;
 | |
| 
 | |
| my $manifestDir = $Nix::Config::manifestDir;
 | |
| 
 | |
| 
 | |
| # Turn on caching in nix-prefetch-url.
 | |
| my $channelCache = "$Nix::Config::stateDir/channel-cache";
 | |
| mkdir $channelCache, 0755 unless -e $channelCache;
 | |
| $ENV{'NIX_DOWNLOAD_CACHE'} = $channelCache if -W $channelCache;
 | |
| 
 | |
| # Figure out the name of the `.nix-channels' file to use.
 | |
| my $home = $ENV{"HOME"} or die '$HOME not set\n';
 | |
| my $channelsList = "$home/.nix-channels";
 | |
| my $nixDefExpr = "$home/.nix-defexpr";
 | |
| 
 | |
| # Figure out the name of the channels profile.
 | |
| my $userName = getpwuid($<) or die "cannot figure out user name";
 | |
| my $profile = "$Nix::Config::stateDir/profiles/per-user/$userName/channels";
 | |
| mkpath(dirname $profile, 0, 0755);
 | |
|     
 | |
| my %channels;
 | |
| 
 | |
| 
 | |
| # Reads the list of channels.
 | |
| sub readChannels {
 | |
|     return if (!-f $channelsList);
 | |
|     open CHANNELS, "<$channelsList" or die "cannot open `$channelsList': $!";
 | |
|     while (<CHANNELS>) {
 | |
|         chomp;
 | |
|         next if /^\s*\#/;
 | |
|         my ($url, $name) = split ' ', $_;
 | |
|         $url =~ s/\/*$//; # remove trailing slashes
 | |
|         $name = basename $url unless defined $name;
 | |
|         $channels{$name} = $url;
 | |
|     }
 | |
|     close CHANNELS;
 | |
| }
 | |
| 
 | |
| 
 | |
| # Writes the list of channels.
 | |
| sub writeChannels {
 | |
|     open CHANNELS, ">$channelsList" or die "cannot open `$channelsList': $!";
 | |
|     foreach my $name (keys %channels) {
 | |
|         print CHANNELS "$channels{$name} $name\n";
 | |
|     }
 | |
|     close CHANNELS;
 | |
| }
 | |
| 
 | |
| 
 | |
| # Adds a channel.
 | |
| sub addChannel {
 | |
|     my ($url, $name) = @_;
 | |
|     readChannels;
 | |
|     $channels{$name} = $url;
 | |
|     writeChannels;
 | |
| }
 | |
| 
 | |
| 
 | |
| # Remove a channel.
 | |
| sub removeChannel {
 | |
|     my ($name) = @_;
 | |
|     readChannels;
 | |
|     delete $channels{$name};
 | |
|     writeChannels;
 | |
| 
 | |
|     system("$Nix::Config::binDir/nix-env --profile '$profile' -e '$name'") == 0
 | |
|         or die "cannot remove channel `$name'";
 | |
| }
 | |
| 
 | |
| 
 | |
| # Fetch Nix expressions and pull manifests from the subscribed
 | |
| # channels.
 | |
| sub update {
 | |
|     my @channelNames = @_;
 | |
|         
 | |
|     readChannels;
 | |
| 
 | |
|     # Create the manifests directory if it doesn't exist.
 | |
|     mkdir $manifestDir, 0755 unless -e $manifestDir;
 | |
| 
 | |
|     # Do we have write permission to the manifests directory?
 | |
|     die "$0: you do not have write permission to `$manifestDir'!\n" unless -W $manifestDir;
 | |
| 
 | |
|     # Download each channel.
 | |
|     my $exprs = "";
 | |
|     foreach my $name (keys %channels) {
 | |
|         next if scalar @channelNames > 0 && ! grep { $_ eq $name } @{channelNames};
 | |
|         
 | |
|         my $url = $channels{$name};
 | |
|         my $origUrl = "$url/MANIFEST";
 | |
| 
 | |
|         # Check if $url is a redirect.  If so, follow it now to ensure
 | |
|         # consistency if the redirection is changed between
 | |
|         # downloading the manifest and the tarball.
 | |
|         my $headers = `$Nix::Config::curl --silent --head '$url'`;
 | |
|         die "$0: unable to check `$url'\n" if $? != 0;
 | |
|         $headers =~ s/\r//g;
 | |
|         $url = $1 if $headers =~ /^Location:\s*(.*)\s*$/m;
 | |
|         
 | |
|         # Pull the channel manifest.
 | |
|         $ENV{'NIX_ORIG_URL'} = $origUrl;
 | |
|         system("$Nix::Config::binDir/nix-pull", "--skip-wrong-store", "$url/MANIFEST") == 0
 | |
|             or die "cannot pull manifest from `$url'\n";
 | |
| 
 | |
|         # Download the channel tarball.
 | |
|         my $fullURL = "$url/nixexprs.tar.bz2";
 | |
|         print STDERR "downloading Nix expressions from `$fullURL'...\n";
 | |
|         my ($hash, $path) = `PRINT_PATH=1 QUIET=1 $Nix::Config::binDir/nix-prefetch-url '$fullURL'`;
 | |
|         die "cannot fetch `$fullURL'" if $? != 0;
 | |
|         chomp $path;
 | |
| 
 | |
|         # If the URL contains a version number, append it to the name
 | |
|         # attribute (so that "nix-env -q" on the channels profile
 | |
|         # shows something useful).
 | |
|         my $cname = $name;
 | |
|         $cname .= $1 if basename($url) =~ /(-\d.*)$/;
 | |
| 
 | |
|         $exprs .= "'f: f { name = \"$cname\"; channelName = \"$name\"; src = builtins.storePath \"$path\"; }' ";
 | |
|     }
 | |
| 
 | |
|     # Unpack the channel tarballs into the Nix store and install them
 | |
|     # into the channels profile.
 | |
|     print STDERR "unpacking channels...\n";
 | |
|     system("$Nix::Config::binDir/nix-env --profile '$profile' " .
 | |
|            "-f '<nix/unpack-channel.nix>' -i -E $exprs --quiet") == 0
 | |
|            or die "cannot unpack the channels";
 | |
| 
 | |
|     # Make the channels appear in nix-env.
 | |
|     unlink $nixDefExpr if -l $nixDefExpr; # old-skool ~/.nix-defexpr
 | |
|     mkdir $nixDefExpr or die "cannot create directory `$nixDefExpr'" if !-e $nixDefExpr;
 | |
|     my $channelLink = "$nixDefExpr/channels";
 | |
|     unlink $channelLink; # !!! not atomic
 | |
|     symlink($profile, $channelLink) or die "cannot symlink `$channelLink' to `$profile'";
 | |
| }
 | |
| 
 | |
| 
 | |
| sub usageError {
 | |
|     print STDERR <<EOF;
 | |
| Usage:
 | |
|   nix-channel --add URL [CHANNEL-NAME]
 | |
|   nix-channel --remove CHANNEL-NAME
 | |
|   nix-channel --list
 | |
|   nix-channel --update [CHANNEL-NAME...]
 | |
| EOF
 | |
|     exit 1;
 | |
| }
 | |
| 
 | |
| 
 | |
| usageError if scalar @ARGV == 0;
 | |
| 
 | |
| 
 | |
| while (scalar @ARGV) {
 | |
|     my $arg = shift @ARGV;
 | |
| 
 | |
|     if ($arg eq "--add") {
 | |
|         usageError if scalar @ARGV < 1 || scalar @ARGV > 2;
 | |
|         my $url = shift @ARGV;
 | |
|         my $name = shift @ARGV;
 | |
|         unless (defined $name) {
 | |
|             $name = basename $url;
 | |
|             $name =~ s/-unstable//;
 | |
|             $name =~ s/-stable//;
 | |
|         }
 | |
|         addChannel($url, $name);
 | |
|         last;
 | |
|     }
 | |
| 
 | |
|     if ($arg eq "--remove") {
 | |
|         usageError if scalar @ARGV != 1;
 | |
|         removeChannel(shift @ARGV);
 | |
|         last;
 | |
|     }
 | |
| 
 | |
|     if ($arg eq "--list") {
 | |
|         usageError if scalar @ARGV != 0;
 | |
|         readChannels;
 | |
|         foreach my $name (keys %channels) {
 | |
|             print "$name $channels{$name}\n";
 | |
|         }
 | |
|         last;
 | |
|     }
 | |
| 
 | |
|     elsif ($arg eq "--update") {
 | |
|         update(@ARGV);
 | |
|         last;
 | |
|     }
 | |
|     
 | |
|     elsif ($arg eq "--help") {
 | |
|         usageError;
 | |
|     }
 | |
| 
 | |
|     else {
 | |
|         die "unknown argument `$arg'; try `--help'";
 | |
|     }
 | |
| }
 |