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/ | ||||||
| /src/download-via-ssh/download-via-ssh | /src/download-via-ssh/download-via-ssh | ||||||
| 
 | 
 | ||||||
|  | # /src/buildenv/ | ||||||
|  | /src/buildenv/buildenv | ||||||
|  | 
 | ||||||
| # /tests/ | # /tests/ | ||||||
| /tests/test-tmp | /tests/test-tmp | ||||||
| /tests/common.sh | /tests/common.sh | ||||||
|  |  | ||||||
							
								
								
									
										1
									
								
								Makefile
									
										
									
									
									
								
							
							
						
						
									
										1
									
								
								Makefile
									
										
									
									
									
								
							|  | @ -12,6 +12,7 @@ makefiles = \ | ||||||
|   src/nix-daemon/local.mk \
 |   src/nix-daemon/local.mk \
 | ||||||
|   src/nix-collect-garbage/local.mk \
 |   src/nix-collect-garbage/local.mk \
 | ||||||
|   src/nix-prefetch-url/local.mk \
 |   src/nix-prefetch-url/local.mk \
 | ||||||
|  |   src/buildenv/local.mk \
 | ||||||
|   perl/local.mk \
 |   perl/local.mk \
 | ||||||
|   scripts/local.mk \
 |   scripts/local.mk \
 | ||||||
|   corepkgs/local.mk \
 |   corepkgs/local.mk \
 | ||||||
|  |  | ||||||
|  | @ -5,8 +5,7 @@ with import <nix/config.nix>; | ||||||
| derivation { | derivation { | ||||||
|   name = "user-environment"; |   name = "user-environment"; | ||||||
|   system = builtins.currentSystem; |   system = builtins.currentSystem; | ||||||
|   builder = perl; |   builder = nixLibexecDir + "/nix/buildenv"; | ||||||
|   args = [ "-w" ./buildenv.pl ]; |  | ||||||
| 
 | 
 | ||||||
|   inherit manifest; |   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 |     let val = builtins.getEnv var; in | ||||||
|     if val != "" then val else def; |     if val != "" then val else def; | ||||||
| in rec { | in rec { | ||||||
|   perl = "@perl@"; |  | ||||||
|   shell = "@bash@"; |   shell = "@bash@"; | ||||||
|   coreutils = "@coreutils@"; |   coreutils = "@coreutils@"; | ||||||
|   bzip2 = "@bzip2@"; |   bzip2 = "@bzip2@"; | ||||||
|  | @ -14,6 +13,7 @@ in rec { | ||||||
|   tr = "@tr@"; |   tr = "@tr@"; | ||||||
|   nixBinDir = fromEnv "NIX_BIN_DIR" "@bindir@"; |   nixBinDir = fromEnv "NIX_BIN_DIR" "@bindir@"; | ||||||
|   nixPrefix = "@prefix@"; |   nixPrefix = "@prefix@"; | ||||||
|  |   nixLibexecDir = fromEnv "NIX_LIBEXEC_DIR" "@libexecdir@"; | ||||||
| 
 | 
 | ||||||
|   # If Nix is installed in the Nix store, then automatically add it as |   # If Nix is installed in the Nix store, then automatically add it as | ||||||
|   # a dependency to the core packages. This ensures that they work |   # 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))) | $(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