13 my $out = $ENV{"out"};
15 my @pathsToLink = split ' ', $ENV{"pathsToLink"};
19 $path = "/" if $path eq "";
20 foreach my $elem (@pathsToLink) {
23 (substr($path, 0, length($elem)) eq $elem
24 && (($path eq $elem) || (substr($path, length($elem), 1) eq "/")));
29 # Returns whether a path in one of the linked packages may contain
30 # files in one of the elements of pathsToLink.
33 foreach my $elem (@pathsToLink) {
36 (substr($elem, 0, length($path)) eq $path
37 && (($path eq $elem) || (substr($elem, length($path), 1) eq "/")));
42 # Similar to `lib.isStorePath`
45 my $storePath = "@storeDir@";
47 return substr($path, 0, 1) eq "/" && dirname
($path) eq $storePath;
50 # For each activated package, determine what symlinks to create.
54 # Add all pathsToLink and all parent directories.
56 # For "/a/b/c" that will include
57 # [ "", "/a", "/a/b", "/a/b/c" ]
59 # That ensures the whole directory tree needed by pathsToLink is
60 # created as directories and not symlinks.
61 $symlinks{""} = ["", 0];
62 for my $p (@pathsToLink) {
63 my @parts = split '/', $p;
68 $cur = "" if $cur eq "/";
69 $symlinks{$cur} = ["", 0];
76 my ($relName, $target, $ignoreCollisions, $checkCollisionContents, $priority) = @_;
78 opendir DIR
, "$target" or die "cannot open `$target': $!";
79 my @names = readdir DIR
or die;
82 foreach my $name (@names) {
83 next if $name eq "." || $name eq "..";
84 findFiles
("$relName/$name", "$target/$name", $name, $ignoreCollisions, $checkCollisionContents, $priority);
89 my ($path1, $path2) = @_;
91 my $stat1 = (stat($path1))[2];
92 my $stat2 = (stat($path2))[2];
94 if ($stat1 != $stat2) {
95 warn "different permissions in `$path1' and `$path2': "
96 . sprintf("%04o", $stat1 & 07777) . " <-> "
97 . sprintf("%04o", $stat2 & 07777);
101 return compare
($path1, $path2) == 0;
105 my ($relName, $target, $baseName, $ignoreCollisions, $checkCollisionContents, $priority) = @_;
107 # The store path must not be a file
108 if (-f
$target && isStorePath
$target) {
109 die "The store path $target is a file and can't be merged into an environment using pkgs.buildEnv!";
114 $relName eq "/propagated-build-inputs" ||
115 $relName eq "/nix-support" ||
116 $relName =~ /info\/dir
/ ||
117 ( $relName =~ /^\/share\
/mime\// && !( $relName =~ /^\
/share\/mime\
/packages/ ) ) ||
118 $baseName eq "perllocal.pod" ||
119 $baseName eq "log" ||
120 ! (hasPathsToLink
($relName) || isInPathsToLink
($relName));
122 my ($oldTarget, $oldPriority) = @
{$symlinks{$relName} // [undef, undef]};
124 # If target doesn't exist, create it. If it already exists as a
125 # symlink to a file (not a directory) in a lower-priority package,
127 if (!defined $oldTarget || ($priority < $oldPriority && ($oldTarget ne "" && ! -d
$oldTarget))) {
128 $symlinks{$relName} = [$target, $priority];
132 # If target already exists and both targets resolves to the same path, skip
133 if (defined $oldTarget && $oldTarget ne "" && abs_path
($target) eq abs_path
($oldTarget)) {
134 # Prefer the target that is not a symlink, if any
135 if (-l
$oldTarget && ! -l
$target) {
136 $symlinks{$relName} = [$target, $priority];
141 # If target already exists as a symlink to a file (not a
142 # directory) in a higher-priority package, skip.
143 if (defined $oldTarget && $priority > $oldPriority && $oldTarget ne "" && ! -d
$oldTarget) {
147 unless (-d
$target && ($oldTarget eq "" || -d
$oldTarget)) {
148 if ($ignoreCollisions) {
149 warn "collision between `$target' and `$oldTarget'\n" if $ignoreCollisions == 1;
151 } elsif ($checkCollisionContents && checkCollision
($oldTarget, $target)) {
154 die "collision between `$target' and `$oldTarget'\n";
158 findFilesInDir
($relName, $oldTarget, $ignoreCollisions, $checkCollisionContents, $oldPriority) unless $oldTarget eq "";
159 findFilesInDir
($relName, $target, $ignoreCollisions, $checkCollisionContents, $priority);
161 $symlinks{$relName} = ["", $priority]; # denotes directory
169 my ($pkgDir, $ignoreCollisions, $checkCollisionContents, $priority) = @_;
171 return if (defined $done{$pkgDir});
174 findFiles
("", $pkgDir, "", $ignoreCollisions, $checkCollisionContents, $priority);
176 my $propagatedFN = "$pkgDir/nix-support/propagated-user-env-packages";
177 if (-e
$propagatedFN) {
178 open PROP
, "<$propagatedFN" or die;
179 my $propagated = <PROP
>;
181 my @propagated = split ' ', $propagated;
182 foreach my $p (@propagated) {
183 $postponed{$p} = 1 unless defined $done{$p};
188 # Read packages list.
191 if (exists $ENV{"pkgsPath"}) {
192 open FILE
, $ENV{"pkgsPath"};
199 # Symlink to the packages that have been installed explicitly by the
201 for my $pkg (@
{decode_json
$pkgs}) {
202 for my $path (@
{$pkg->{paths
}}) {
204 $ENV{"ignoreCollisions"} eq "1",
205 $ENV{"checkCollisionContents"} eq "1",
212 # Symlink to the packages that have been "propagated" by packages
213 # installed by the user (i.e., package X declares that it wants Y
214 # installed as well). We do these later because they have a lower
215 # priority in case of collisions.
216 my $priorityCounter = 1000; # don't care about collisions
217 while (scalar(keys %postponed) > 0) {
218 my @pkgDirs = keys %postponed;
220 foreach my $pkgDir (sort @pkgDirs) {
221 addPkg
($pkgDir, 2, $ENV{"checkCollisionContents"} eq "1", $priorityCounter++);
226 # Create the symlinks.
227 my $extraPrefix = $ENV{"extraPrefix"};
229 foreach my $relName (sort keys %symlinks) {
230 my ($target, $priority) = @
{$symlinks{$relName}};
231 my $abs = "$out" . "$extraPrefix" . "/$relName";
232 next unless isInPathsToLink
$relName;
234 #print "creating directory $relName\n";
235 mkpath
$abs or die "cannot create directory `$abs': $!";
237 #print "creating symlink $relName to $target\n";
238 symlink $target, $abs ||
239 die "error creating link `$abs': $!";
245 print STDERR
"created $nrLinks symlinks in user environment\n";
248 my $manifest = $ENV{"manifest"};
250 symlink($manifest, "$out/manifest") or die "cannot create manifest";