#! @perl@ -w use strict; use Cwd; use IO::Handle; use Fcntl; STDOUT->autoflush(1); my $out = $ENV{"out"}; mkdir "$out", 0755 || die "error creating $out"; sub readlink_or_StateWrapper; my $symlinks = 0; my %path_state_identifier = (); my %priorities; my $nixBinDir = $ENV{"nixBinDir"}; my $nixStore = $ENV{"nixStore"}; # For each activated package, create symlinks. sub createLinks { my $srcDir = shift; #Lookup each $stateIdentifiers in $path_state_identifier #we strip $srcDir to its rootdir e.g. /nix/store/......./ my @srcDirParts = split /\// , substr($srcDir, length ($nixStore), length ($srcDir)); my $srcDirRoot = $nixStore . "/" . $srcDirParts[1]; # print "srcDirRoot $srcDirRoot \n"; my $dstDir = shift; my $priority = shift; my $pkgStateIdentifier = $path_state_identifier{$srcDirRoot}; # We have to look it up each time since recursion can change the $srcDir, but not the identifier #print "createLinks $srcDir to $dstDir with iden $pkgStateIdentifier \n"; my @srcFiles = glob("$srcDir/*"); foreach my $srcFile (@srcFiles) { my $baseName = $srcFile; $baseName =~ s/^.*\///g; # strip directory my $dstFile = "$dstDir/$baseName"; # Urgh, hacky... if ($srcFile =~ /\/propagated-build-inputs$/ || $srcFile =~ /\/nix-support$/ || $srcFile =~ /\/perllocal.pod$/ || $srcFile =~ /\/info\/dir$/ || $srcFile =~ /\/log$/) { # Do nothing. } elsif (-d $srcFile) { lstat $dstFile; #go recursive on directorys if (-d _) { createLinks($srcFile, $dstFile, $priority); } elsif (-l _) { my $target = readlink $dstFile or die; if (!-d $target) { die "collission between directory `$srcFile' and non-directory `$target'"; } unlink $dstFile or die "error unlinking `$dstFile': $!"; mkdir $dstFile, 0755 || die "error creating directory `$dstFile': $!"; createLinks($target, $dstFile, $priorities{$dstFile}); createLinks($srcFile, $dstFile, $priority); } else { #print "1ST DIR LINK $srcFile to $dstFile with iden $pkgStateIdentifier \n"; symlink($srcFile, $dstFile) || die "error creating link `$dstFile': $!"; $priorities{$dstFile} = $priority; $symlinks++; } } else { # print "ELSE LINK $srcFile to $dstFile with iden $pkgStateIdentifier \n"; # if we have a state component with a identifier different then "" if($pkgStateIdentifier ne "__NOSTATE__" && $pkgStateIdentifier ne ""){ my @pathparts = split /\// , $srcFile; my $parentDir = $pathparts[scalar(@pathparts) - 2]; if( $parentDir eq "bin" || $parentDir eq "sbin"){ #hacky.... print "STATELINK $srcFile to $dstFile - $pkgStateIdentifier \n"; my $new_dstFile; my $new_stateIdentifier; if($pkgStateIdentifier eq "__EMTPY__"){ $new_dstFile = $dstFile; $new_stateIdentifier = ""; } else{ $new_dstFile = "$dstFile-$pkgStateIdentifier"; $new_stateIdentifier = $pkgStateIdentifier; } # We also check with -e if the wrapperscript-file exists, and if is it a symlink (with -l) if (-l $new_dstFile || -e $new_dstFile) { my $target = readlink_or_StateWrapper $new_dstFile; die "(state) collission between `$srcFile' and `$target' (over $new_dstFile)"; } sysopen (DSTFILEHANDLE, $new_dstFile, O_RDWR|O_EXCL|O_CREAT, 0755); printf DSTFILEHANDLE "#! @shell@ \n"; printf DSTFILEHANDLE "$nixBinDir/nix-state --run --identifier=$new_stateIdentifier $srcFile \"\$@\" \n"; close (DSTFILEHANDLE); } } elsif($pkgStateIdentifier ne "__NOSTATE__" && $pkgStateIdentifier eq ""){ #TODO we now dont create symlinks for state packages with a empty identifier #TODO but we must do it if there is no normal non-state pacakge } else { if (-l $dstFile || -e $dstFile) { my $target = readlink_or_StateWrapper $dstFile; my $prevPriority = $priorities{$dstFile}; die ( "Collission between `$srcFile' and `$target'. " . "Suggested solution: use `nix-env --set-flag " . "priority NUMBER PKGNAME' to change the priority of " . "one of the conflicting packages.\n" ) if $prevPriority == $priority; next if $prevPriority < $priority; unlink $dstFile or die; } # print "2ND LINK $srcFile to $dstFile with iden $pkgStateIdentifier \n"; symlink($srcFile, $dstFile) || die "error creating link `$dstFile': $!"; $priorities{$dstFile} = $priority; $symlinks++; } } } } my %done; my %postponed; sub addPkg; sub addPkg { my $pkgDir = shift; my $priority = shift; return if (defined $done{$pkgDir}); $done{$pkgDir} = 1; # print "symlinking $pkgDir\n"; createLinks("$pkgDir", "$out", $priority); my $propagatedFN = "$pkgDir/nix-support/propagated-user-env-packages"; if (-e $propagatedFN) { open PROP, "<$propagatedFN" or die; my $propagated = ; close PROP; my @propagated = split ' ', $propagated; foreach my $p (@propagated) { $postponed{$p} = 1 unless defined $done{$p}; } } } sub readlink_or_StateWrapper { my $src = shift; my $target; if (-l $src) { $target = readlink $src; } else{ open(DAT, $src) || die("Could not open file!"); my @raw_data=; close(DAT); $target = $raw_data[1]; } return $target } my @stateIdentifiers = split ' ', $ENV{"stateIdentifiers"}; my $si_counter = 0; # Convert the stuff we get from the environment back into a coherent # data type. my @paths = split ' ', $ENV{"paths"}; my @active = split ' ', $ENV{"active"}; my @priority = split ' ', $ENV{"priority"}; die if scalar @paths != scalar @active; die if scalar @paths != scalar @priority; my %pkgs; for (my $n = 0; $n < scalar @paths; $n++) { $pkgs{$paths[$n]} = { active => $active[$n] , priority => $priority[$n] , stateidentifier => $stateIdentifiers[$n] }; $path_state_identifier{$paths[$n]} = $stateIdentifiers[$n]; } # Symlink to the packages that have been installed explicitly by the # user. foreach my $pkg (sort (keys %pkgs)) { #print "SP: $pkg \n"; #print "SI: $pkgs{$pkg}->{stateidentifier} \n"; #print "PR: $pkgs{$pkg}->{priority} \n"; addPkg($pkg, $pkgs{$pkg}->{priority}) if $pkgs{$pkg}->{active} ne "false"; $si_counter++; } # Symlink to the packages that have been "propagated" by packages # installed by the user (i.e., package X declares that it want Y # installed as well). We do these later because they have a lower # priority in case of collisions. my $priorityCounter = 1000; # don't care about collisions while (scalar(keys %postponed) > 0) { my @pkgDirs = keys %postponed; %postponed = (); foreach my $pkgDir (sort @pkgDirs) { addPkg($pkgDir, $priorityCounter++); } } print STDERR "created $symlinks symlinks in user environment\n"; symlink($ENV{"manifest"}, "$out/manifest") or die "cannot create manifest";