* Operation `--delete-generations' to delete generations of a
profile. Arguments are either generation number, or `old' to delete all non-current generations. Typical use: $ nix-env --delete-generations old $ nix-collect-garbage * istringstream -> string2Int.
This commit is contained in:
		
							parent
							
								
									c16be6ac92
								
							
						
					
					
						commit
						dcc433de47
					
				
					 8 changed files with 100 additions and 33 deletions
				
			
		|  | @ -155,10 +155,8 @@ static void initAndRun(int argc, char * * argv) | ||||||
|         else if (arg == "--max-jobs" || arg == "-j") { |         else if (arg == "--max-jobs" || arg == "-j") { | ||||||
|             ++i; |             ++i; | ||||||
|             if (i == args.end()) throw UsageError("`--max-jobs' requires an argument"); |             if (i == args.end()) throw UsageError("`--max-jobs' requires an argument"); | ||||||
|             istringstream str(*i); |  | ||||||
|             int n; |             int n; | ||||||
|             str >> n; |             if (!string2Int(*i, n) || n < 0) | ||||||
|             if (!str || !str.eof() || n < 0) |  | ||||||
|                 throw UsageError(format("`--max-jobs' requires a non-negative integer")); |                 throw UsageError(format("`--max-jobs' requires a non-negative integer")); | ||||||
|             maxBuildJobs = n; |             maxBuildJobs = n; | ||||||
|         } |         } | ||||||
|  |  | ||||||
|  | @ -616,3 +616,11 @@ bool statusOk(int status) | ||||||
| { | { | ||||||
|     return WIFEXITED(status) && WEXITSTATUS(status) == 0; |     return WIFEXITED(status) && WEXITSTATUS(status) == 0; | ||||||
| } | } | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | bool string2Int(const string & s, int & n) | ||||||
|  | { | ||||||
|  |     istringstream str(s); | ||||||
|  |     str >> n; | ||||||
|  |     return str && str.eof(); | ||||||
|  | } | ||||||
|  |  | ||||||
|  | @ -246,6 +246,10 @@ string statusToString(int status); | ||||||
| bool statusOk(int status); | bool statusOk(int status); | ||||||
| 
 | 
 | ||||||
| 
 | 
 | ||||||
|  | /* Parse a string into an integer. */ | ||||||
|  | bool string2Int(const string & s, int & n); | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
| /* !!! HACK HACK HACK - this should be in shared.hh, but it's to
 | /* !!! HACK HACK HACK - this should be in shared.hh, but it's to
 | ||||||
|    facilitate a quick hack - will remove this eventually (famous last |    facilitate a quick hack - will remove this eventually (famous last | ||||||
|    words). */ |    words). */ | ||||||
|  |  | ||||||
|  | @ -643,10 +643,8 @@ static void opSwitchGeneration(Globals & globals, | ||||||
|     if (opArgs.size() != 1) |     if (opArgs.size() != 1) | ||||||
|         throw UsageError(format("exactly one argument expected")); |         throw UsageError(format("exactly one argument expected")); | ||||||
| 
 | 
 | ||||||
|     istringstream str(opArgs.front()); |  | ||||||
|     int dstGen; |     int dstGen; | ||||||
|     str >> dstGen; |     if (!string2Int(opArgs.front(), dstGen)) | ||||||
|     if (!str || !str.eof()) |  | ||||||
|         throw UsageError(format("expected a generation number")); |         throw UsageError(format("expected a generation number")); | ||||||
| 
 | 
 | ||||||
|     switchGeneration(globals, dstGen); |     switchGeneration(globals, dstGen); | ||||||
|  | @ -688,6 +686,49 @@ static void opListGenerations(Globals & globals, | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| 
 | 
 | ||||||
|  | static void deleteGeneration2(const Path & profile, unsigned int gen) | ||||||
|  | { | ||||||
|  |     printMsg(lvlInfo, format("removing generation %1%") % gen); | ||||||
|  |     deleteGeneration(profile, gen); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | static void opDeleteGenerations(Globals & globals, | ||||||
|  |     Strings opFlags, Strings opArgs) | ||||||
|  | { | ||||||
|  |     if (opFlags.size() > 0) | ||||||
|  |         throw UsageError(format("unknown flag `%1%'") % opFlags.front()); | ||||||
|  | 
 | ||||||
|  |     int curGen; | ||||||
|  |     Generations gens = findGenerations(globals.profile, curGen); | ||||||
|  | 
 | ||||||
|  |     for (Strings::iterator i = opArgs.begin(); i != opArgs.end(); ++i) { | ||||||
|  | 
 | ||||||
|  |         if (*i == "old") { | ||||||
|  |             for (Generations::iterator j = gens.begin(); j != gens.end(); ++j) | ||||||
|  |                 if (j->number != curGen) | ||||||
|  |                     deleteGeneration2(globals.profile, j->number); | ||||||
|  |         } | ||||||
|  | 
 | ||||||
|  |         else { | ||||||
|  |             int n; | ||||||
|  |             if (!string2Int(*i, n) || n < 0) | ||||||
|  |                 throw UsageError(format("invalid generation specifier `%1%'")  % *i); | ||||||
|  |             bool found = false; | ||||||
|  |             for (Generations::iterator j = gens.begin(); j != gens.end(); ++j) { | ||||||
|  |                 if (j->number == n) { | ||||||
|  |                     deleteGeneration2(globals.profile, j->number); | ||||||
|  |                     found = true; | ||||||
|  |                     break; | ||||||
|  |                 } | ||||||
|  |             } | ||||||
|  |             if (!found) | ||||||
|  |                 printMsg(lvlError, format("generation %1% does not exist") % n); | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
| static void opDefaultExpr(Globals & globals, | static void opDefaultExpr(Globals & globals, | ||||||
|     Strings opFlags, Strings opArgs) |     Strings opFlags, Strings opArgs) | ||||||
| { | { | ||||||
|  | @ -750,6 +791,8 @@ void run(Strings args) | ||||||
|             op = opRollback; |             op = opRollback; | ||||||
|         else if (arg == "--list-generations") |         else if (arg == "--list-generations") | ||||||
|             op = opListGenerations; |             op = opListGenerations; | ||||||
|  |         else if (arg == "--delete-generations") | ||||||
|  |             op = opDeleteGenerations; | ||||||
|         else if (arg == "--dry-run") { |         else if (arg == "--dry-run") { | ||||||
|             printMsg(lvlInfo, "(dry run; not doing anything)"); |             printMsg(lvlInfo, "(dry run; not doing anything)"); | ||||||
|             globals.dryRun = true; |             globals.dryRun = true; | ||||||
|  |  | ||||||
|  | @ -56,20 +56,10 @@ static string nextComponent(string::const_iterator & p, | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| 
 | 
 | ||||||
| #include <fstream> |  | ||||||
| 
 |  | ||||||
| static bool parseInt(const string & s, int & n) |  | ||||||
| { |  | ||||||
|     istringstream st(s); |  | ||||||
|     st >> n; |  | ||||||
|     return !st.fail(); |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| 
 |  | ||||||
| static bool componentsLT(const string & c1, const string & c2) | static bool componentsLT(const string & c1, const string & c2) | ||||||
| { | { | ||||||
|     int n1, n2; |     int n1, n2; | ||||||
|     bool c1Num = parseInt(c1, n1), c2Num = parseInt(c2, n2); |     bool c1Num = string2Int(c1, n1), c2Num = string2Int(c2, n2); | ||||||
| 
 | 
 | ||||||
|     if (c1Num && c2Num) return n1 < n2; |     if (c1Num && c2Num) return n1 < n2; | ||||||
|     else if (c1 == "" && c2Num) return true; |     else if (c1 == "" && c2Num) return true; | ||||||
|  |  | ||||||
|  | @ -20,9 +20,11 @@ static int parseName(const string & profileName, const string & name) | ||||||
|     string s = string(name, profileName.size() + 1); |     string s = string(name, profileName.size() + 1); | ||||||
|     int p = s.find("-link"); |     int p = s.find("-link"); | ||||||
|     if (p == string::npos) return -1; |     if (p == string::npos) return -1; | ||||||
|     istringstream str(string(s, 0, p)); |     int n; | ||||||
|     unsigned int n; |     if (string2Int(string(s, 0, p), n) && n >= 0) | ||||||
|     if (str >> n && str.eof()) return n; else return -1; |         return n; | ||||||
|  |     else | ||||||
|  |         return -1; | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| 
 | 
 | ||||||
|  | @ -59,6 +61,16 @@ Generations findGenerations(Path profile, int & curGen) | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| 
 | 
 | ||||||
|  | static void makeNames(const Path & profile, unsigned int num, | ||||||
|  |     Path & generation, Path & gcrootDrv, Path & gcrootClr) | ||||||
|  | { | ||||||
|  |     Path prefix = (format("%1%-%2%") % profile % num).str(); | ||||||
|  |     generation = prefix + "-link"; | ||||||
|  |     gcrootDrv = prefix + "-drv.gcroot"; | ||||||
|  |     gcrootClr = prefix + "-clr.gcroot"; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
| Path createGeneration(Path profile, Path outPath, | Path createGeneration(Path profile, Path outPath, | ||||||
|     Path drvPath, Path clrPath) |     Path drvPath, Path clrPath) | ||||||
| { | { | ||||||
|  | @ -72,10 +84,7 @@ Path createGeneration(Path profile, Path outPath, | ||||||
|     Path generation, gcrootDrv, gcrootClr; |     Path generation, gcrootDrv, gcrootClr; | ||||||
| 
 | 
 | ||||||
|     while (1) { |     while (1) { | ||||||
|         Path prefix = (format("%1%-%2%") % profile % num).str(); |         makeNames(profile, num, generation, gcrootDrv, gcrootClr); | ||||||
|         generation = prefix + "-link"; |  | ||||||
|         gcrootDrv = prefix + "-drv.gcroot"; |  | ||||||
|         gcrootClr = prefix + "-clr.gcroot"; |  | ||||||
|         if (symlink(outPath.c_str(), generation.c_str()) == 0) break; |         if (symlink(outPath.c_str(), generation.c_str()) == 0) break; | ||||||
|         if (errno != EEXIST) |         if (errno != EEXIST) | ||||||
|             throw SysError(format("creating symlink `%1%'") % generation); |             throw SysError(format("creating symlink `%1%'") % generation); | ||||||
|  | @ -90,6 +99,23 @@ Path createGeneration(Path profile, Path outPath, | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| 
 | 
 | ||||||
|  | static void removeFile(const Path & path) | ||||||
|  | { | ||||||
|  |     if (remove(path.c_str()) == -1) | ||||||
|  |         throw SysError(format("cannot unlink `%1%'") % path); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | void deleteGeneration(const Path & profile, unsigned int gen) | ||||||
|  | { | ||||||
|  |     Path generation, gcrootDrv, gcrootClr; | ||||||
|  |     makeNames(profile, gen, generation, gcrootDrv, gcrootClr); | ||||||
|  |     removeFile(generation); | ||||||
|  |     if (pathExists(gcrootClr)) removeFile(gcrootClr); | ||||||
|  |     if (pathExists(gcrootDrv)) removeFile(gcrootDrv); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
| void switchLink(Path link, Path target) | void switchLink(Path link, Path target) | ||||||
| { | { | ||||||
|     /* Hacky. */ |     /* Hacky. */ | ||||||
|  |  | ||||||
|  | @ -31,6 +31,8 @@ Generations findGenerations(Path profile, int & curGen); | ||||||
| Path createGeneration(Path profile, Path outPath, | Path createGeneration(Path profile, Path outPath, | ||||||
|     Path drvPath, Path clrPath); |     Path drvPath, Path clrPath); | ||||||
| 
 | 
 | ||||||
|  | void deleteGeneration(const Path & profile, unsigned int gen); | ||||||
|  | 
 | ||||||
| void switchLink(Path link, Path target); | void switchLink(Path link, Path target); | ||||||
| 
 | 
 | ||||||
| 
 | 
 | ||||||
|  |  | ||||||
|  | @ -1,5 +1,4 @@ | ||||||
| #include <iostream> | #include <iostream> | ||||||
| #include <sstream> |  | ||||||
| 
 | 
 | ||||||
| #include "globals.hh" | #include "globals.hh" | ||||||
| #include "normalise.hh" | #include "normalise.hh" | ||||||
|  | @ -171,10 +170,8 @@ static void opSubstitute(Strings opFlags, Strings opArgs) | ||||||
|         getline(cin, sub.program); |         getline(cin, sub.program); | ||||||
|         string s; |         string s; | ||||||
|         getline(cin, s); |         getline(cin, s); | ||||||
|         istringstream st(s); |  | ||||||
|         int n; |         int n; | ||||||
|         st >> n; |         if (!string2Int(s, n)) throw Error("number expected"); | ||||||
|         if (!st) throw Error("number expected"); |  | ||||||
|         while (n--) { |         while (n--) { | ||||||
|             getline(cin, s); |             getline(cin, s); | ||||||
|             sub.args.push_back(s); |             sub.args.push_back(s); | ||||||
|  | @ -224,11 +221,10 @@ static void opGC(Strings opFlags, Strings opArgs) | ||||||
|         else if (*i == "--print-dead") subOp = soPrintDead; |         else if (*i == "--print-dead") subOp = soPrintDead; | ||||||
|         else if (*i == "--delete") subOp = soDelete; |         else if (*i == "--delete") subOp = soDelete; | ||||||
|         else if (*i == "--min-age") { |         else if (*i == "--min-age") { | ||||||
|             if (opArgs.size() == 0) |             int n; | ||||||
|                 throw UsageError("`--min-age' requires an argument"); |             if (opArgs.size() == 0 || !string2Int(opArgs.front(), n)) | ||||||
|             istringstream st(opArgs.front()); |                 throw UsageError("`--min-age' requires an integer argument"); | ||||||
|             st >> minAge; |             minAge = n; | ||||||
|             if (!st) throw Error("number expected"); |  | ||||||
|         } |         } | ||||||
|         else throw UsageError(format("bad sub-operation `%1%' in GC") % *i); |         else throw UsageError(format("bad sub-operation `%1%' in GC") % *i); | ||||||
|          |          | ||||||
|  |  | ||||||
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue