buildenv: perl -> c++
This commit is contained in:
		
							parent
							
								
									eef754813f
								
							
						
					
					
						commit
						91978e3b9a
					
				
					 8 changed files with 202 additions and 172 deletions
				
			
		
							
								
								
									
										3
									
								
								.gitignore
									
										
									
									
										vendored
									
									
								
							
							
						
						
									
										3
									
								
								.gitignore
									
										
									
									
										vendored
									
									
								
							|  | @ -77,6 +77,9 @@ Makefile.config | |||
| # /src/download-via-ssh/ | ||||
| /src/download-via-ssh/download-via-ssh | ||||
| 
 | ||||
| # /src/buildenv/ | ||||
| /src/buildenv/buildenv | ||||
| 
 | ||||
| # /tests/ | ||||
| /tests/test-tmp | ||||
| /tests/common.sh | ||||
|  |  | |||
							
								
								
									
										1
									
								
								Makefile
									
										
									
									
									
								
							
							
						
						
									
										1
									
								
								Makefile
									
										
									
									
									
								
							|  | @ -12,6 +12,7 @@ makefiles = \ | |||
|   src/nix-daemon/local.mk \
 | ||||
|   src/nix-collect-garbage/local.mk \
 | ||||
|   src/nix-prefetch-url/local.mk \
 | ||||
|   src/buildenv/local.mk \
 | ||||
|   perl/local.mk \
 | ||||
|   scripts/local.mk \
 | ||||
|   corepkgs/local.mk \
 | ||||
|  |  | |||
|  | @ -5,8 +5,7 @@ with import <nix/config.nix>; | |||
| derivation { | ||||
|   name = "user-environment"; | ||||
|   system = builtins.currentSystem; | ||||
|   builder = perl; | ||||
|   args = [ "-w" ./buildenv.pl ]; | ||||
|   builder = nixLibexecDir + "/nix/buildenv"; | ||||
| 
 | ||||
|   inherit manifest; | ||||
| 
 | ||||
|  |  | |||
|  | @ -1,168 +0,0 @@ | |||
| use strict; | ||||
| use Cwd; | ||||
| use IO::Handle; | ||||
| use utf8; | ||||
| 
 | ||||
| STDOUT->autoflush(1); | ||||
| 
 | ||||
| my $out = $ENV{"out"}; | ||||
| 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 $priority = shift; | ||||
| 
 | ||||
|     my @srcFiles = glob("$srcDir/*"); | ||||
| 
 | ||||
|     foreach my $srcFile (@srcFiles) { | ||||
|         my $baseName = $srcFile; | ||||
|         $baseName =~ s/^.*\///g; # strip directory | ||||
|         my $dstFile = "$dstDir/$baseName"; | ||||
| 
 | ||||
|         # The files below are special-cased so that they don't show up | ||||
|         # in user profiles, either because they are useless, or | ||||
|         # because they would cause pointless collisions (e.g., each | ||||
|         # Python package brings its own | ||||
|         # `$out/lib/pythonX.Y/site-packages/easy-install.pth'.) | ||||
|         # 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; | ||||
| 
 | ||||
|             if (-d _) { | ||||
|                 createLinks($srcFile, $dstFile, $priority); | ||||
|             } | ||||
| 
 | ||||
|             elsif (-l _) { | ||||
|                 my $target = readlink $dstFile or die; | ||||
|                 if (!-d $target) { | ||||
|                     die "collision 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 { | ||||
|                 symlink($srcFile, $dstFile) || | ||||
|                     die "error creating link ‘$dstFile’: $!"; | ||||
|                 $priorities{$dstFile} = $priority; | ||||
|                 $symlinks++; | ||||
|             } | ||||
|         } | ||||
| 
 | ||||
|         else { | ||||
| 
 | ||||
|             if (-l $dstFile) { | ||||
|                 my $target = readlink $dstFile; | ||||
|                 my $prevPriority = $priorities{$dstFile}; | ||||
|                 die("collision between ‘$srcFile’ and ‘$target’; " . | ||||
|                     "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++; | ||||
|         } | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| 
 | ||||
| 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 = <PROP>; | ||||
|         close PROP; | ||||
|         my @propagated = split ' ', $propagated; | ||||
|         foreach my $p (@propagated) { | ||||
|             $postponed{$p} = 1 unless defined $done{$p}; | ||||
|         } | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| 
 | ||||
| # Convert the stuff we get from the environment back into a coherent | ||||
| # data type. | ||||
| my @pkgs; | ||||
| my @derivations = split ' ', $ENV{"derivations"}; | ||||
| while (scalar @derivations) { | ||||
|     my $active = shift @derivations; | ||||
|     my $priority = shift @derivations; | ||||
|     my $outputs = shift @derivations; | ||||
|     for (my $n = 0; $n < $outputs; $n++) { | ||||
|         my $path = shift @derivations; | ||||
|         push @pkgs, | ||||
|             { path => $path | ||||
|             , active => $active ne "false" | ||||
|             , priority => int($priority) }; | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| 
 | ||||
| # Symlink to the packages that have been installed explicitly by the | ||||
| # user.  Process in priority order to reduce unnecessary | ||||
| # symlink/unlink steps. | ||||
| @pkgs = sort { $a->{priority} <=> $b->{priority} || $a->{path} cmp $b->{path} } @pkgs; | ||||
| foreach my $pkg (@pkgs) { | ||||
|     #print $pkg, " ", $pkgs{$pkg}->{priority}, "\n"; | ||||
|     addPkg($pkg->{path}, $pkg->{priority}) if $pkg->{active}; | ||||
| } | ||||
| 
 | ||||
| 
 | ||||
| # Symlink to the packages that have been "propagated" by packages | ||||
| # 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, $priorityCounter++); | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| 
 | ||||
| print STDERR "created $symlinks symlinks in user environment\n"; | ||||
| 
 | ||||
| 
 | ||||
| symlink($ENV{"manifest"}, "$out/manifest.nix") or die "cannot create manifest"; | ||||
|  | @ -3,7 +3,6 @@ let | |||
|     let val = builtins.getEnv var; in | ||||
|     if val != "" then val else def; | ||||
| in rec { | ||||
|   perl = "@perl@"; | ||||
|   shell = "@bash@"; | ||||
|   coreutils = "@coreutils@"; | ||||
|   bzip2 = "@bzip2@"; | ||||
|  | @ -14,6 +13,7 @@ in rec { | |||
|   tr = "@tr@"; | ||||
|   nixBinDir = fromEnv "NIX_BIN_DIR" "@bindir@"; | ||||
|   nixPrefix = "@prefix@"; | ||||
|   nixLibexecDir = fromEnv "NIX_LIBEXEC_DIR" "@libexecdir@"; | ||||
| 
 | ||||
|   # If Nix is installed in the Nix store, then automatically add it as | ||||
|   # a dependency to the core packages. This ensures that they work | ||||
|  |  | |||
|  | @ -1,4 +1,4 @@ | |||
| corepkgs_FILES = nar.nix buildenv.nix buildenv.pl unpack-channel.nix derivation.nix fetchurl.nix imported-drv-to-derivation.nix | ||||
| corepkgs_FILES = nar.nix buildenv.nix unpack-channel.nix derivation.nix fetchurl.nix imported-drv-to-derivation.nix | ||||
| 
 | ||||
| $(foreach file,config.nix $(corepkgs_FILES),$(eval $(call install-data-in,$(d)/$(file),$(datadir)/nix/corepkgs))) | ||||
| 
 | ||||
|  |  | |||
							
								
								
									
										186
									
								
								src/buildenv/buildenv.cc
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										186
									
								
								src/buildenv/buildenv.cc
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,186 @@ | |||
| #include "shared.hh" | ||||
| #include <sys/stat.h> | ||||
| #include <sys/types.h> | ||||
| #include <fcntl.h> | ||||
| #include <algorithm> | ||||
| 
 | ||||
| using namespace nix; | ||||
| 
 | ||||
| typedef std::map<Path,int> Priorities; | ||||
| 
 | ||||
| static bool isDirectory (const Path & path) | ||||
| { | ||||
|     struct stat st; | ||||
|     if (stat(path.c_str(), &st) == -1) | ||||
|         throw SysError(format("getting status of ‘%1%’") % path); | ||||
|     return S_ISDIR(st.st_mode); | ||||
| } | ||||
| 
 | ||||
| static auto priorities = Priorities{}; | ||||
| 
 | ||||
| static auto symlinks = 0; | ||||
| 
 | ||||
| /* For each activated package, create symlinks */ | ||||
| static void createLinks(const Path & srcDir, const Path & dstDir, int priority) | ||||
| { | ||||
|     auto srcFiles = readDirectory(srcDir); | ||||
|     for (const auto & ent : srcFiles) { | ||||
|         if (ent.name[0] == '.') | ||||
|             /* not matched by glob */ | ||||
|             continue; | ||||
|         const auto & srcFile = srcDir + "/" + ent.name; | ||||
|         auto dstFile = dstDir + "/" + ent.name; | ||||
| 
 | ||||
|         /* The files below are special-cased to that they don't show up
 | ||||
|          * in user profiles, either because they are useless, or | ||||
|          * because they would cauase pointless collisions (e.g., each | ||||
|          * Python package brings its own | ||||
|          * `$out/lib/pythonX.Y/site-packages/easy-install.pth'.) | ||||
|          */ | ||||
|         if (hasSuffix(srcFile, "/propagated-build-inputs") || | ||||
|             hasSuffix(srcFile, "/nix-support") || | ||||
|             hasSuffix(srcFile, "/perllocal.pod") || | ||||
|             hasSuffix(srcFile, "/info/dir") || | ||||
|             hasSuffix(srcFile, "/log")) { | ||||
|             continue; | ||||
|         } else if (isDirectory(srcFile)) { | ||||
|             struct stat dstSt; | ||||
|             auto res = lstat(dstFile.c_str(), &dstSt); | ||||
|             if (res == 0) { | ||||
|                 if (S_ISDIR(dstSt.st_mode)) { | ||||
|                     createLinks(srcFile, dstFile, priority); | ||||
|                     continue; | ||||
|                 } else if (S_ISLNK(dstSt.st_mode)) { | ||||
|                     auto target = readLink(dstFile); | ||||
|                     if (!isDirectory(target)) | ||||
|                         throw Error(format("collision between ‘%1%’ and non-directory ‘%2%’") | ||||
|                             % srcFile % target); | ||||
|                     if (unlink(dstFile.c_str()) == -1) | ||||
|                         throw SysError(format("unlinking ‘%1%’") % dstFile); | ||||
|                     if (mkdir(dstFile.c_str(), 0755) == -1) | ||||
|                         throw SysError(format("creating directory ‘%1%’")); | ||||
|                     createLinks(target, dstFile, priorities[dstFile]); | ||||
|                     createLinks(srcFile, dstFile, priority); | ||||
|                     continue; | ||||
|                 } | ||||
|             } else if (errno != ENOENT) | ||||
|                 throw SysError(format("getting status of ‘%1%’") % dstFile); | ||||
|         } else { | ||||
|             struct stat dstSt; | ||||
|             auto res = lstat(dstFile.c_str(), &dstSt); | ||||
|             if (res == 0) { | ||||
|                 if (S_ISLNK(dstSt.st_mode)) { | ||||
|                     auto target = readLink(dstFile); | ||||
|                     auto prevPriority = priorities[dstFile]; | ||||
|                     if (prevPriority == priority) | ||||
|                         throw Error(format( | ||||
|                                 "collision between ‘%1%’ and ‘%2%’; " | ||||
|                                 "use ‘nix-env --set-flag priority NUMBER PKGNAME’ " | ||||
|                                 "to change the priority of one of the conflicting packages" | ||||
|                                 ) % srcFile % target); | ||||
|                     if (prevPriority < priority) | ||||
|                         continue; | ||||
|                     if (unlink(dstFile.c_str()) == -1) | ||||
|                         throw SysError(format("unlinking ‘%1%’") % dstFile); | ||||
|                 } | ||||
|             } else if (errno != ENOENT) | ||||
|                 throw SysError(format("getting status of ‘%1%’") % dstFile); | ||||
|         } | ||||
|         createSymlink(srcFile, dstFile); | ||||
|         priorities[dstFile] = priority; | ||||
|         symlinks++; | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| typedef std::set<Path> FileProp; | ||||
| 
 | ||||
| static auto done = FileProp{}; | ||||
| static auto postponed = FileProp{}; | ||||
| 
 | ||||
| static auto out = string{}; | ||||
| 
 | ||||
| static void addPkg(const Path & pkgDir, int priority) | ||||
| { | ||||
|     if (done.find(pkgDir) != done.end()) | ||||
|         return; | ||||
|     done.insert(pkgDir); | ||||
|     createLinks(pkgDir, out, priority); | ||||
|     auto propagatedFN = pkgDir + "/nix-support/propagated-user-env-packages"; | ||||
|     auto propagated = string{}; | ||||
|     { | ||||
|         AutoCloseFD fd = open(propagatedFN.c_str(), O_RDONLY | O_CLOEXEC); | ||||
|         if (!fd) { | ||||
|             if (errno == ENOENT) | ||||
|                 return; | ||||
|             throw SysError(format("opening ‘%1%’") % propagatedFN); | ||||
|         } | ||||
|         propagated = readLine(fd.get()); | ||||
|     } | ||||
|     for (const auto & p : tokenizeString<std::vector<string>>(propagated, " ")) | ||||
|         if (done.find(p) == done.end()) | ||||
|             postponed.insert(p); | ||||
| } | ||||
| 
 | ||||
| struct Package { | ||||
|     Path path; | ||||
|     bool active; | ||||
|     int priority; | ||||
|     Package(Path path, bool active, int priority) : path{std::move(path)}, active{active}, priority{priority} {} | ||||
| }; | ||||
| 
 | ||||
| typedef std::vector<Package> Packages; | ||||
| 
 | ||||
| int main(int argc, char ** argv) | ||||
| { | ||||
|     return handleExceptions(argv[0], [&]() { | ||||
|         initNix(); | ||||
|         out = getEnv("out"); | ||||
|         if (mkdir(out.c_str(), 0755) == -1) | ||||
|             throw SysError(format("creating %1%") % out); | ||||
| 
 | ||||
|         /* Convert the stuff we get from the environment back into a coherent
 | ||||
|          * data type. | ||||
|          */ | ||||
|         auto pkgs = Packages{}; | ||||
|         auto derivations = tokenizeString<Strings>(getEnv("derivations")); | ||||
|         while (!derivations.empty()) { | ||||
|             /* !!! We're trusting the caller to structure derivations env var correctly */ | ||||
|             auto active = derivations.front(); derivations.pop_front(); | ||||
|             auto priority = stoi(derivations.front()); derivations.pop_front(); | ||||
|             auto outputs = stoi(derivations.front()); derivations.pop_front(); | ||||
|             for (auto n = 0; n < outputs; n++) { | ||||
|                 auto path = derivations.front(); derivations.pop_front(); | ||||
|                 pkgs.emplace_back(path, active != "false", priority); | ||||
|             } | ||||
|         } | ||||
| 
 | ||||
|         /* Symlink to the packages that have been installed explicitly by the
 | ||||
|          * user. Process in priority order to reduce unnecessary | ||||
|          * symlink/unlink steps. | ||||
|          */ | ||||
|         std::sort(pkgs.begin(), pkgs.end(), [](const Package & a, const Package & b) { | ||||
|             return a.priority < b.priority || (a.priority == b.priority && a.path < b.path); | ||||
|         }); | ||||
|         for (const auto & pkg : pkgs) | ||||
|             if (pkg.active) | ||||
|                 addPkg(pkg.path, pkg.priority); | ||||
| 
 | ||||
|         /* Symlink to the packages that have been "propagated" by packages
 | ||||
|          * 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. | ||||
|          */ | ||||
|         auto priorityCounter = 1000; | ||||
|         while (!postponed.empty()) { | ||||
|             auto pkgDirs = postponed; | ||||
|             postponed = FileProp{}; | ||||
|             for (const auto & pkgDir : pkgDirs) | ||||
|                 addPkg(pkgDir, priorityCounter++); | ||||
|         } | ||||
| 
 | ||||
|         std::cerr << "created " << symlinks << " symlinks in user environment\n"; | ||||
| 
 | ||||
|         createSymlink(getEnv("manifest"), out + "/manifest.nix"); | ||||
|     }); | ||||
| } | ||||
| 
 | ||||
							
								
								
									
										9
									
								
								src/buildenv/local.mk
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										9
									
								
								src/buildenv/local.mk
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,9 @@ | |||
| programs += buildenv | ||||
| 
 | ||||
| buildenv_DIR := $(d) | ||||
| 
 | ||||
| buildenv_INSTALL_DIR := $(libexecdir)/nix | ||||
| 
 | ||||
| buildenv_LIBS = libmain libutil libformat | ||||
| 
 | ||||
| buildenv_SOURCES := $(d)/buildenv.cc | ||||
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue