From e4610f2965c2ab60132344defa055792a658ea45 Mon Sep 17 00:00:00 2001 From: Eelco Dolstra Date: Tue, 25 Aug 2015 00:37:54 +0200 Subject: [PATCH] buildEnv: Support package priorities like nix-env This gets rid of a bunch of collision warnings. --- nixos/modules/system/boot/modprobe.nix | 1 + pkgs/build-support/buildenv/builder.pl | 70 +++++++++++-------- pkgs/build-support/buildenv/default.nix | 10 ++- .../firmware-linux-nonfree/default.nix | 1 + pkgs/os-specific/linux/nvidia-x11/default.nix | 1 + pkgs/os-specific/linux/util-linux/default.nix | 1 + pkgs/tools/archivers/cpio/default.nix | 1 + 7 files changed, 53 insertions(+), 32 deletions(-) diff --git a/nixos/modules/system/boot/modprobe.nix b/nixos/modules/system/boot/modprobe.nix index a3b616ff3eff..c49380899664 100644 --- a/nixos/modules/system/boot/modprobe.nix +++ b/nixos/modules/system/boot/modprobe.nix @@ -35,6 +35,7 @@ with lib; fi ''; + meta.priority = 4; }; description = '' Wrapper around modprobe that sets the path to the modules diff --git a/pkgs/build-support/buildenv/builder.pl b/pkgs/build-support/buildenv/builder.pl index 08331b178f4f..798fca6572ea 100755 --- a/pkgs/build-support/buildenv/builder.pl +++ b/pkgs/build-support/buildenv/builder.pl @@ -5,6 +5,7 @@ use Cwd 'abs_path'; use IO::Handle; use File::Path; use File::Basename; +use JSON::PP; STDOUT->autoflush(1); @@ -17,7 +18,7 @@ sub isInPathsToLink { $path = "/" if $path eq ""; foreach my $elem (@pathsToLink) { return 1 if - $elem eq "/" || + $elem eq "/" || (substr($path, 0, length($elem)) eq $elem && (($path eq $elem) || (substr($path, length($elem), 1) eq "/"))); } @@ -28,25 +29,27 @@ sub isInPathsToLink { # For each activated package, determine what symlinks to create. my %symlinks; -$symlinks{""} = ""; # create root directory +$symlinks{""} = ["", 0]; # create root directory + +my %priorities; sub findFiles; sub findFilesInDir { - my ($relName, $target, $ignoreCollisions) = @_; + my ($relName, $target, $ignoreCollisions, $priority) = @_; opendir DIR, "$target" or die "cannot open `$target': $!"; my @names = readdir DIR or die; closedir DIR; - + foreach my $name (@names) { next if $name eq "." || $name eq ".."; - findFiles("$relName/$name", "$target/$name", $name, $ignoreCollisions); + findFiles("$relName/$name", "$target/$name", $name, $ignoreCollisions, $priority); } } - + sub findFiles { - my ($relName, $target, $baseName, $ignoreCollisions) = @_; + my ($relName, $target, $baseName, $ignoreCollisions, $priority) = @_; # Urgh, hacky... return if @@ -57,41 +60,48 @@ sub findFiles { $baseName eq "perllocal.pod" || $baseName eq "log"; - my $oldTarget = $symlinks{$relName}; + my ($oldTarget, $oldPriority) = @{$symlinks{$relName} // [undef, undef]}; - if (!defined $oldTarget) { - $symlinks{$relName} = $target; + # If target doesn't exist, create it. If it already exists as a + # symlink to a file (not a directory) in a lower-priority package, + # overwrite it. + if (!defined $oldTarget || ($priority < $oldPriority && ($oldTarget ne "" && ! -d $oldTarget))) { + $symlinks{$relName} = [$target, $priority]; + return; + } + + # If target already exists as a symlink to a file (not a + # directory) in a higher-priority package, skip. + if (defined $oldTarget && $priority > $oldPriority && $oldTarget ne "" && ! -d $oldTarget) { return; } unless (-d $target && ($oldTarget eq "" || -d $oldTarget)) { if ($ignoreCollisions) { - warn "collision between `$target' and `$oldTarget'" if $ignoreCollisions == 1; + warn "collision between `$target' and `$oldTarget'\n" if $ignoreCollisions == 1; return; } else { - die "collision between `$target' and `$oldTarget'"; + die "collision between `$target' and `$oldTarget'\n"; } } - findFilesInDir($relName, $oldTarget, $ignoreCollisions) unless $oldTarget eq ""; - findFilesInDir($relName, $target, $ignoreCollisions); - - $symlinks{$relName} = ""; # denotes directory + findFilesInDir($relName, $oldTarget, $ignoreCollisions, $oldPriority) unless $oldTarget eq ""; + findFilesInDir($relName, $target, $ignoreCollisions, $priority); + + $symlinks{$relName} = ["", $priority]; # denotes directory } my %done; my %postponed; -sub addPkg; -sub addPkg($;$) { - my $pkgDir = shift; - my $ignoreCollisions = shift; +sub addPkg { + my ($pkgDir, $ignoreCollisions, $priority) = @_; return if (defined $done{$pkgDir}); $done{$pkgDir} = 1; - findFiles("", "$pkgDir", "", $ignoreCollisions); + findFiles("", $pkgDir, "", $ignoreCollisions, $priority); my $propagatedFN = "$pkgDir/nix-support/propagated-user-env-packages"; if (-e $propagatedFN) { @@ -106,23 +116,25 @@ sub addPkg($;$) { } -# Symlink to the packages that have been installed explicitly by the user. -my @args = split ' ', $ENV{"paths"}; - -foreach my $pkgDir (@args) { - addPkg($pkgDir, $ENV{"ignoreCollisions"} eq "1") if -e $pkgDir; +# Symlink to the packages that have been installed explicitly by the +# user. +for my $pkg (@{decode_json $ENV{"pkgs"}}) { + for my $path (@{$pkg->{paths}}) { + addPkg($path, $ENV{"ignoreCollisions"} eq "1", $pkg->{priority}) if -e $path; + } } # Symlink to the packages that have been "propagated" by packages -# installed by the user (i.e., package X declares that it want Y +# installed by the user (i.e., package X declares that it wants 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, 2); + addPkg($pkgDir, 2, $priorityCounter++); } } @@ -130,7 +142,7 @@ while (scalar(keys %postponed) > 0) { # Create the symlinks. my $nrLinks = 0; foreach my $relName (sort keys %symlinks) { - my $target = $symlinks{$relName}; + my ($target, $priority) = @{$symlinks{$relName}}; my $abs = "$out/$relName"; next unless isInPathsToLink $relName; if ($target eq "") { diff --git a/pkgs/build-support/buildenv/default.nix b/pkgs/build-support/buildenv/default.nix index 293291dc1dad..2ae8123faca4 100644 --- a/pkgs/build-support/buildenv/default.nix +++ b/pkgs/build-support/buildenv/default.nix @@ -9,10 +9,10 @@ , # The manifest file (if any). A symlink $out/manifest will be # created to it. manifest ? "" - + , # The paths to symlink. paths - + , # Whether to ignore collisions or abort. ignoreCollisions ? false @@ -28,7 +28,11 @@ }: runCommand name - { inherit manifest paths ignoreCollisions passthru pathsToLink postBuild; + { inherit manifest ignoreCollisions passthru pathsToLink postBuild; + pkgs = builtins.toJSON (map (drv: { + paths = [ drv ]; # FIXME: handle multiple outputs + priority = drv.meta.priority or 5; + }) paths); preferLocalBuild = true; } '' diff --git a/pkgs/os-specific/linux/firmware/firmware-linux-nonfree/default.nix b/pkgs/os-specific/linux/firmware/firmware-linux-nonfree/default.nix index 259c5acdf873..a995b193a845 100644 --- a/pkgs/os-specific/linux/firmware/firmware-linux-nonfree/default.nix +++ b/pkgs/os-specific/linux/firmware/firmware-linux-nonfree/default.nix @@ -30,6 +30,7 @@ stdenv.mkDerivation rec { license = licenses.unfreeRedistributableFirmware; platforms = platforms.linux; maintainers = with maintainers; [ wkennington ]; + priority = 6; # give precedence to kernel firmware }; passthru = { inherit version; }; diff --git a/pkgs/os-specific/linux/nvidia-x11/default.nix b/pkgs/os-specific/linux/nvidia-x11/default.nix index ab564c10e2e5..cbd9a19777e5 100644 --- a/pkgs/os-specific/linux/nvidia-x11/default.nix +++ b/pkgs/os-specific/linux/nvidia-x11/default.nix @@ -63,5 +63,6 @@ stdenv.mkDerivation { license = licenses.unfreeRedistributable; platforms = platforms.linux; maintainers = [ maintainers.vcunat ]; + priority = 4; # resolves collision with xorg-server's "lib/xorg/modules/extensions/libglx.so" }; } diff --git a/pkgs/os-specific/linux/util-linux/default.nix b/pkgs/os-specific/linux/util-linux/default.nix index 7f2aeca255c8..7768875ca57f 100644 --- a/pkgs/os-specific/linux/util-linux/default.nix +++ b/pkgs/os-specific/linux/util-linux/default.nix @@ -53,5 +53,6 @@ stdenv.mkDerivation rec { homepage = http://www.kernel.org/pub/linux/utils/util-linux/; description = "A set of system utilities for Linux"; platforms = stdenv.lib.platforms.linux; + priority = 6; # lower priority than coreutils ("kill") and shadow ("login" etc.) packages }; } diff --git a/pkgs/tools/archivers/cpio/default.nix b/pkgs/tools/archivers/cpio/default.nix index 2cd65391216d..f2207b588499 100644 --- a/pkgs/tools/archivers/cpio/default.nix +++ b/pkgs/tools/archivers/cpio/default.nix @@ -35,5 +35,6 @@ stdenv.mkDerivation { homepage = http://www.gnu.org/software/cpio/; description = "A program to create or extract from cpio archives"; platforms = stdenv.lib.platforms.all; + priority = 6; # resolves collision with gnutar's "libexec/rmt" }; }