From a46db5d013a5c3ab5b041824bfb935e5c042886c Mon Sep 17 00:00:00 2001 From: Eelco Dolstra Date: Fri, 27 Apr 2007 23:28:44 +0000 Subject: [PATCH] * Package conflict resolution through priority levels. If there is a user environment collission between two packages due to overlapping file names, then a package with a higher priority will overwrite the symlinks of a package with a lower priority. E.g., $ nix-env --set-flag priority 5 gcc $ nix-env --set-flag priority 10 binutils gives gcc a higher priority than binutils (higher number = lower priority). --- corepkgs/buildenv/builder.pl.in | 62 +++++++++++++++++++++++---------- corepkgs/buildenv/default.nix | 4 ++- doc/manual/release-notes.xml | 4 ++- 3 files changed, 49 insertions(+), 21 deletions(-) diff --git a/corepkgs/buildenv/builder.pl.in b/corepkgs/buildenv/builder.pl.in index faebe27cc..04c895554 100755 --- a/corepkgs/buildenv/builder.pl.in +++ b/corepkgs/buildenv/builder.pl.in @@ -12,13 +12,15 @@ mkdir "$out", 0755 || die "error creating $out"; my $symlinks = 0; +my %priorities; + # For each activated package, create symlinks. sub createLinks { my $srcDir = shift; my $dstDir = shift; - my $ignoreCollisions = shift; + my $priority = shift; my @srcFiles = glob("$srcDir/*"); @@ -42,7 +44,7 @@ sub createLinks { lstat $dstFile; if (-d _) { - createLinks($srcFile, $dstFile, $ignoreCollisions); + createLinks($srcFile, $dstFile, $priority); } elsif (-l _) { @@ -53,27 +55,35 @@ sub createLinks { unlink $dstFile or die "error unlinking `$dstFile': $!"; mkdir $dstFile, 0755 || die "error creating directory `$dstFile': $!"; - createLinks($target, $dstFile, $ignoreCollisions); - createLinks($srcFile, $dstFile, $ignoreCollisions); + createLinks($target, $dstFile, $priority); # !!! <- priority isn't right + createLinks($srcFile, $dstFile, $priority); } else { symlink($srcFile, $dstFile) || die "error creating link `$dstFile': $!"; + $priorities{$dstFile} = $priority; $symlinks++; } } - elsif (-l $dstFile) { - if (!$ignoreCollisions) { - my $target = readlink $dstFile; - die "collission between `$srcFile' and `$target'"; - } - } - else { + + if (-l $dstFile) { + my $target = readlink $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; + } + symlink($srcFile, $dstFile) || die "error creating link `$dstFile': $!"; + $priorities{$dstFile} = $priority; $symlinks++; } } @@ -86,13 +96,13 @@ my %postponed; sub addPkg; sub addPkg { my $pkgDir = shift; - my $ignoreCollisions = shift; + my $priority = shift; return if (defined $done{$pkgDir}); $done{$pkgDir} = 1; # print "symlinking $pkgDir\n"; - createLinks("$pkgDir", "$out", $ignoreCollisions); + createLinks("$pkgDir", "$out", $priority); my $propagatedFN = "$pkgDir/nix-support/propagated-user-env-packages"; if (-e $propagatedFN) { @@ -107,16 +117,29 @@ sub addPkg { } -# Symlink to the packages that have been installed explicitly by the user. -my @paths = split ' ', $ENV{"derivations"}; +# 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++) { - my $pkgDir = $paths[$n]; - my $isActive = $active[$n]; - addPkg($pkgDir, 0) if $isActive ne "false"; + $pkgs{$paths[$n]} = + { active => $active[$n] + , priority => $priority[$n] }; +} + + +# Symlink to the packages that have been installed explicitly by the +# user. +foreach my $pkg (sort (keys %pkgs)) { + #print $pkg, " ", $pkgs{$pkg}->{priority}, "\n"; + addPkg($pkg, $pkgs{$pkg}->{priority}) if $pkgs{$pkg}->{active} ne "false"; } @@ -124,11 +147,12 @@ for (my $n = 0; $n < scalar @paths; $n++) { # 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, 1); + addPkg($pkgDir, $priorityCounter++); } } diff --git a/corepkgs/buildenv/default.nix b/corepkgs/buildenv/default.nix index a5452db5e..36dd9d0c6 100644 --- a/corepkgs/buildenv/default.nix +++ b/corepkgs/buildenv/default.nix @@ -4,9 +4,11 @@ derivation { name = "user-environment"; system = system; builder = ./builder.pl; - derivations = derivations; + manifest = manifest; # !!! grmbl, need structured data for passing this in a clean way. + paths = derivations; active = map (x: if x ? meta && x.meta ? active then x.meta.active else "true") derivations; + priority = map (x: if x ? meta && x.meta ? priority then x.meta.priority else "5") derivations; } diff --git a/doc/manual/release-notes.xml b/doc/manual/release-notes.xml index e5d0adf74..161643e52 100644 --- a/doc/manual/release-notes.xml +++ b/doc/manual/release-notes.xml @@ -71,7 +71,9 @@ environments. TODO: nix-env - . + . Specific flags: + active, + priority. nix-env -q now has a flag