linux_xanmod: 5.11.14 -> 5.11.15
[NixPkgs.git] / pkgs / build-support / buildenv / builder.pl
blob411b147cc58ef7e96e0ea4a20c83e81d2fc179ee
1 #! @perl@ -w
3 use strict;
4 use Cwd 'abs_path';
5 use IO::Handle;
6 use File::Path;
7 use File::Basename;
8 use File::Compare;
9 use JSON::PP;
11 STDOUT->autoflush(1);
13 my $out = $ENV{"out"};
15 my @pathsToLink = split ' ', $ENV{"pathsToLink"};
17 sub isInPathsToLink {
18 my $path = shift;
19 $path = "/" if $path eq "";
20 foreach my $elem (@pathsToLink) {
21 return 1 if
22 $elem eq "/" ||
23 (substr($path, 0, length($elem)) eq $elem
24 && (($path eq $elem) || (substr($path, length($elem), 1) eq "/")));
26 return 0;
29 # Returns whether a path in one of the linked packages may contain
30 # files in one of the elements of pathsToLink.
31 sub hasPathsToLink {
32 my $path = shift;
33 foreach my $elem (@pathsToLink) {
34 return 1 if
35 $path eq "" ||
36 (substr($elem, 0, length($path)) eq $path
37 && (($path eq $elem) || (substr($elem, length($path), 1) eq "/")));
39 return 0;
42 # Similar to `lib.isStorePath`
43 sub isStorePath {
44 my $path = shift;
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.
52 my %symlinks;
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;
65 my $cur = "";
66 for my $x (@parts) {
67 $cur = $cur . "/$x";
68 $cur = "" if $cur eq "/";
69 $symlinks{$cur} = ["", 0];
73 sub findFiles;
75 sub findFilesInDir {
76 my ($relName, $target, $ignoreCollisions, $checkCollisionContents, $priority) = @_;
78 opendir DIR, "$target" or die "cannot open `$target': $!";
79 my @names = readdir DIR or die;
80 closedir DIR;
82 foreach my $name (@names) {
83 next if $name eq "." || $name eq "..";
84 findFiles("$relName/$name", "$target/$name", $name, $ignoreCollisions, $checkCollisionContents, $priority);
88 sub checkCollision {
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);
98 return 0;
101 return compare($path1, $path2) == 0;
104 sub findFiles {
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!";
112 # Urgh, hacky...
113 return if
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,
126 # overwrite it.
127 if (!defined $oldTarget || ($priority < $oldPriority && ($oldTarget ne "" && ! -d $oldTarget))) {
128 $symlinks{$relName} = [$target, $priority];
129 return;
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];
138 return;
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) {
144 return;
147 unless (-d $target && ($oldTarget eq "" || -d $oldTarget)) {
148 if ($ignoreCollisions) {
149 warn "collision between `$target' and `$oldTarget'\n" if $ignoreCollisions == 1;
150 return;
151 } elsif ($checkCollisionContents && checkCollision($oldTarget, $target)) {
152 return;
153 } else {
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
165 my %done;
166 my %postponed;
168 sub addPkg {
169 my ($pkgDir, $ignoreCollisions, $checkCollisionContents, $priority) = @_;
171 return if (defined $done{$pkgDir});
172 $done{$pkgDir} = 1;
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>;
180 close PROP;
181 my @propagated = split ' ', $propagated;
182 foreach my $p (@propagated) {
183 $postponed{$p} = 1 unless defined $done{$p};
188 # Read packages list.
189 my $pkgs;
191 if (exists $ENV{"pkgsPath"}) {
192 open FILE, $ENV{"pkgsPath"};
193 $pkgs = <FILE>;
194 close FILE;
195 } else {
196 $pkgs = $ENV{"pkgs"}
199 # Symlink to the packages that have been installed explicitly by the
200 # user.
201 for my $pkg (@{decode_json $pkgs}) {
202 for my $path (@{$pkg->{paths}}) {
203 addPkg($path,
204 $ENV{"ignoreCollisions"} eq "1",
205 $ENV{"checkCollisionContents"} eq "1",
206 $pkg->{priority})
207 if -e $path;
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;
219 %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"};
228 my $nrLinks = 0;
229 foreach my $relName (sort keys %symlinks) {
230 my ($target, $priority) = @{$symlinks{$relName}};
231 my $abs = "$out" . "$extraPrefix" . "/$relName";
232 next unless isInPathsToLink $relName;
233 if ($target eq "") {
234 #print "creating directory $relName\n";
235 mkpath $abs or die "cannot create directory `$abs': $!";
236 } else {
237 #print "creating symlink $relName to $target\n";
238 symlink $target, $abs ||
239 die "error creating link `$abs': $!";
240 $nrLinks++;
245 print STDERR "created $nrLinks symlinks in user environment\n";
248 my $manifest = $ENV{"manifest"};
249 if ($manifest) {
250 symlink($manifest, "$out/manifest") or die "cannot create manifest";