1 : #include <iostream>
2 : #include <algorithm>
3 :
4 : #include "globals.hh"
5 : #include "build.hh"
6 : #include "misc.hh"
7 : #include "gc.hh"
8 : #include "archive.hh"
9 : #include "shared.hh"
10 : #include "dotgraph.hh"
11 : #include "store.hh"
12 : #include "db.hh"
13 : #include "util.hh"
14 : #include "help.txt.hh"
15 :
16 :
17 : using namespace nix;
18 : using std::cin;
19 : using std::cout;
20 :
21 :
22 : typedef void (* Operation) (Strings opFlags, Strings opArgs);
23 :
24 :
25 : void printHelp()
26 1 : {
27 1 : cout << string((char *) helpText, sizeof helpText);
28 : }
29 :
30 :
31 336 : static Path gcRoot;
32 : static int rootNr = 0;
33 : static bool indirectRoot = false;
34 :
35 :
36 : static Path fixPath(Path path)
37 78 : {
38 78 : SwitchToOriginalUser sw;
39 78 : path = absPath(path);
40 79 : while (!isInStore(path)) {
41 1 : if (!isLink(path)) break;
42 1 : string target = readLink(path);
43 1 : path = absPath(target, dirOf(path));
44 : }
45 78 : return toStorePath(path);
46 : }
47 :
48 :
49 : /* Realisation the given path. For a derivation that means build it;
50 : for other paths it means ensure their validity. */
51 : static Path realisePath(const Path & path)
52 38 : {
53 38 : if (isDerivation(path)) {
54 42337 : PathSet paths;
55 37 : paths.insert(path);
56 37 : buildDerivations(paths);
57 62266 : Path outPath = findOutput(derivationFromPath(path), "out");
58 :
59 36 : if (gcRoot == "")
60 35 : printGCWarning();
61 : else
62 1 : outPath = addPermRoot(outPath,
63 : makeRootName(gcRoot, rootNr),
64 : indirectRoot);
65 :
66 36 : return outPath;
67 : } else {
68 1 : ensurePath(path);
69 1 : return path;
70 : }
71 : }
72 :
73 :
74 : /* Realise the given paths. */
75 : static void opRealise(Strings opFlags, Strings opArgs)
76 22 : {
77 18366 : if (!opFlags.empty()) throw UsageError("unknown flag");
78 :
79 56 : for (Strings::iterator i = opArgs.begin();
80 : i != opArgs.end(); ++i)
81 34 : *i = fixPath(*i);
82 :
83 22 : if (opArgs.size() > 1) {
84 2 : PathSet drvPaths;
85 16 : for (Strings::iterator i = opArgs.begin();
86 : i != opArgs.end(); ++i)
87 14 : if (isDerivation(*i))
88 14 : drvPaths.insert(*i);
89 2 : buildDerivations(drvPaths);
90 : }
91 :
92 55 : for (Strings::iterator i = opArgs.begin();
93 : i != opArgs.end(); ++i)
94 34 : cout << format("%1%\n") % realisePath(*i);
95 : }
96 :
97 :
98 : /* Add files to the Nix store and print the resulting paths. */
99 : static void opAdd(Strings opFlags, Strings opArgs)
100 1 : {
101 1 : if (!opFlags.empty()) throw UsageError("unknown flag");
102 :
103 2 : for (Strings::iterator i = opArgs.begin(); i != opArgs.end(); ++i)
104 1 : cout << format("%1%\n") % addToStore(*i);
105 : }
106 :
107 :
108 : /* Preload the output of a fixed-output derivation into the Nix
109 : store. */
110 : static void opAddFixed(Strings opFlags, Strings opArgs)
111 12 : {
112 12 : bool recursive = false;
113 :
114 12 : for (Strings::iterator i = opFlags.begin();
115 : i != opFlags.end(); ++i)
116 0 : if (*i == "--recursive") recursive = true;
117 0 : else throw UsageError(format("unknown flag `%1%'") % *i);
118 :
119 12 : if (opArgs.empty())
120 0 : throw UsageError("first argument must be hash algorithm");
121 :
122 12 : string hashAlgo = opArgs.front();
123 12 : opArgs.pop_front();
124 :
125 24 : for (Strings::iterator i = opArgs.begin(); i != opArgs.end(); ++i)
126 12 : cout << format("%1%\n") % addToStoreFixed(recursive, hashAlgo, *i);
127 : }
128 :
129 :
130 : /* Hack to support caching in `nix-prefetch-url'. */
131 : static void opPrintFixedPath(Strings opFlags, Strings opArgs)
132 12 : {
133 12 : bool recursive = false;
134 :
135 12 : for (Strings::iterator i = opFlags.begin();
136 : i != opFlags.end(); ++i)
137 0 : if (*i == "--recursive") recursive = true;
138 0 : else throw UsageError(format("unknown flag `%1%'") % *i);
139 :
140 12 : Strings::iterator i = opArgs.begin();
141 12 : string hashAlgo = *i++;
142 12 : string hash = *i++;
143 12 : string name = *i++;
144 :
145 12 : HashType ht(parseHashType(hashAlgo));
146 : Hash h = hash.size() == Hash(ht).hashSize * 2
147 : ? parseHash(ht, hash)
148 12 : : parseHash32(ht, hash);
149 :
150 12 : cout << format("%1%\n") %
151 : makeFixedOutputPath(recursive, hashAlgo, h, name);
152 : }
153 :
154 :
155 : /* Place in `paths' the set of paths that are required to `realise'
156 : the given store path, i.e., all paths necessary for valid
157 : deployment of the path. For a derivation, this is the union of
158 : requisites of the inputs, plus the derivation; for other store
159 : paths, it is the set of paths in the FS closure of the path. If
160 : `includeOutputs' is true, include the requisites of the output
161 : paths of derivations as well.
162 :
163 : Note that this function can be used to implement three different
164 : deployment policies:
165 :
166 : - Source deployment (when called on a derivation).
167 : - Binary deployment (when called on an output path).
168 : - Source/binary deployment (when called on a derivation with
169 : `includeOutputs' set to true).
170 : */
171 : static void storePathRequisites(const Path & storePath,
172 : bool includeOutputs, PathSet & paths)
173 2 : {
174 2 : computeFSClosure(storePath, paths);
175 :
176 2 : if (includeOutputs) {
177 9 : for (PathSet::iterator i = paths.begin();
178 : i != paths.end(); ++i)
179 8 : if (isDerivation(*i)) {
180 3 : Derivation drv = derivationFromPath(*i);
181 6 : for (DerivationOutputs::iterator j = drv.outputs.begin();
182 : j != drv.outputs.end(); ++j)
183 3 : if (isValidPath(j->second.path))
184 3 : computeFSClosure(j->second.path, paths);
185 : }
186 : }
187 : }
188 :
189 :
190 : static Path maybeUseOutput(const Path & storePath, bool useOutput, bool forceRealise)
191 15 : {
192 15 : if (forceRealise) realisePath(storePath);
193 15 : if (useOutput && isDerivation(storePath)) {
194 1 : Derivation drv = derivationFromPath(storePath);
195 1 : return findOutput(drv, "out");
196 : }
197 14 : else return storePath;
198 : }
199 :
200 :
201 : static void printPathSet(const PathSet & paths)
202 12 : {
203 32 : for (PathSet::iterator i = paths.begin();
204 : i != paths.end(); ++i)
205 20 : cout << format("%s\n") % *i;
206 : }
207 :
208 :
209 : /* Some code to print a tree representation of a derivation dependency
210 : graph. Topological sorting is used to keep the tree relatively
211 : flat. */
212 :
213 336 : const string treeConn = "+---";
214 336 : const string treeLine = "| ";
215 336 : const string treeNull = " ";
216 :
217 :
218 : static void dfsVisit(const PathSet & paths, const Path & path,
219 : PathSet & visited, Paths & sorted)
220 8 : {
221 8 : if (visited.find(path) != visited.end()) return;
222 7 : visited.insert(path);
223 :
224 7 : PathSet closure;
225 7 : computeFSClosure(path, closure);
226 :
227 17 : for (PathSet::iterator i = closure.begin();
228 : i != closure.end(); ++i)
229 10 : if (*i != path && paths.find(*i) != paths.end())
230 1 : dfsVisit(paths, *i, visited, sorted);
231 :
232 7 : sorted.push_front(path);
233 : }
234 :
235 :
236 : static Paths topoSort(const PathSet & paths)
237 8 : {
238 8 : Paths sorted;
239 8 : PathSet visited;
240 15 : for (PathSet::const_iterator i = paths.begin(); i != paths.end(); ++i)
241 7 : dfsVisit(paths, *i, visited, sorted);
242 8 : return sorted;
243 : }
244 :
245 :
246 : static void printTree(const Path & path,
247 : const string & firstPad, const string & tailPad, PathSet & done)
248 9 : {
249 9 : if (done.find(path) != done.end()) {
250 1 : cout << format("%1%%2% [...]\n") % firstPad % path;
251 1 : return;
252 : }
253 8 : done.insert(path);
254 :
255 8 : cout << format("%1%%2%\n") % firstPad % path;
256 :
257 8 : PathSet references;
258 8 : queryReferences(noTxn, path, references);
259 :
260 : #if 0
261 : for (PathSet::iterator i = drv.inputSrcs.begin();
262 : i != drv.inputSrcs.end(); ++i)
263 : cout << format("%1%%2%\n") % (tailPad + treeConn) % *i;
264 : #endif
265 :
266 : /* Topologically sort under the relation A < B iff A \in
267 : closure(B). That is, if derivation A is an (possibly indirect)
268 : input of B, then A is printed first. This has the effect of
269 : flattening the tree, preventing deeply nested structures. */
270 8 : Paths sorted = topoSort(references);
271 8 : reverse(sorted.begin(), sorted.end());
272 :
273 15 : for (Paths::iterator i = sorted.begin(); i != sorted.end(); ++i) {
274 7 : Paths::iterator j = i; ++j;
275 7 : printTree(*i, tailPad + treeConn,
276 : j == sorted.end() ? tailPad + treeNull : tailPad + treeLine,
277 : done);
278 : }
279 : }
280 :
281 :
282 : /* Perform various sorts of queries. */
283 : static void opQuery(Strings opFlags, Strings opArgs)
284 41 : {
285 : enum { qOutputs, qRequisites, qReferences, qReferrers
286 : , qReferrersClosure, qDeriver, qBinding, qHash
287 41 : , qTree, qGraph } query = qOutputs;
288 41 : bool useOutput = false;
289 41 : bool includeOutputs = false;
290 41 : bool forceRealise = false;
291 41 : string bindingName;
292 :
293 79 : for (Strings::iterator i = opFlags.begin();
294 : i != opFlags.end(); ++i)
295 38 : if (*i == "--outputs") query = qOutputs;
296 38 : else if (*i == "--requisites" || *i == "-R") query = qRequisites;
297 36 : else if (*i == "--references") query = qReferences;
298 27 : else if (*i == "--referrers" || *i == "--referers") query = qReferrers;
299 27 : else if (*i == "--referrers-closure" || *i == "--referers-closure") query = qReferrersClosure;
300 26 : else if (*i == "--deriver" || *i == "-d") query = qDeriver;
301 13 : else if (*i == "--binding" || *i == "-b") {
302 1 : if (opArgs.size() == 0)
303 0 : throw UsageError("expected binding name");
304 1 : bindingName = opArgs.front();
305 1 : opArgs.pop_front();
306 1 : query = qBinding;
307 : }
308 12 : else if (*i == "--hash") query = qHash;
309 11 : else if (*i == "--tree") query = qTree;
310 9 : else if (*i == "--graph") query = qGraph;
311 7 : else if (*i == "--use-output" || *i == "-u") useOutput = true;
312 5 : else if (*i == "--force-realise" || *i == "-f") forceRealise = true;
313 1 : else if (*i == "--include-outputs") includeOutputs = true;
314 0 : else throw UsageError(format("unknown flag `%1%'") % *i);
315 :
316 41 : switch (query) {
317 :
318 : case qOutputs: {
319 20 : for (Strings::iterator i = opArgs.begin();
320 : i != opArgs.end(); ++i)
321 : {
322 10 : *i = fixPath(*i);
323 10 : if (forceRealise) realisePath(*i);
324 10 : Derivation drv = derivationFromPath(*i);
325 10 : cout << format("%1%\n") % findOutput(drv, "out");
326 : }
327 12 : break;
328 : }
329 :
330 : case qRequisites:
331 : case qReferences:
332 : case qReferrers:
333 : case qReferrersClosure: {
334 12 : PathSet paths;
335 24 : for (Strings::iterator i = opArgs.begin();
336 : i != opArgs.end(); ++i)
337 : {
338 12 : Path path = maybeUseOutput(fixPath(*i), useOutput, forceRealise);
339 12 : if (query == qRequisites)
340 2 : storePathRequisites(path, includeOutputs, paths);
341 10 : else if (query == qReferences) queryReferences(noTxn, path, paths);
342 1 : else if (query == qReferrers) queryReferrers(noTxn, path, paths);
343 1 : else if (query == qReferrersClosure) computeFSClosure(path, paths, true);
344 : }
345 12 : printPathSet(paths);
346 12 : break;
347 : }
348 :
349 : case qDeriver:
350 26 : for (Strings::iterator i = opArgs.begin();
351 : i != opArgs.end(); ++i)
352 : {
353 13 : Path deriver = queryDeriver(noTxn, fixPath(*i));
354 13 : cout << format("%1%\n") %
355 : (deriver == "" ? "unknown-deriver" : deriver);
356 : }
357 1 : break;
358 :
359 : case qBinding:
360 2 : for (Strings::iterator i = opArgs.begin();
361 : i != opArgs.end(); ++i)
362 : {
363 1 : *i = fixPath(*i);
364 1 : Derivation drv = derivationFromPath(*i);
365 1 : StringPairs::iterator j = drv.env.find(bindingName);
366 1 : if (j == drv.env.end())
367 0 : throw Error(format("derivation `%1%' has no environment binding named `%2%'")
368 : % *i % bindingName);
369 1 : cout << format("%1%\n") % j->second;
370 : }
371 1 : break;
372 :
373 : case qHash:
374 2 : for (Strings::iterator i = opArgs.begin();
375 : i != opArgs.end(); ++i)
376 : {
377 1 : Path path = maybeUseOutput(fixPath(*i), useOutput, forceRealise);
378 1 : Hash hash = queryPathHash(path);
379 1 : assert(hash.type == htSHA256);
380 1 : cout << format("sha256:%1%\n") % printHash32(hash);
381 : }
382 2 : break;
383 :
384 : case qTree: {
385 2 : PathSet done;
386 4 : for (Strings::iterator i = opArgs.begin();
387 : i != opArgs.end(); ++i)
388 2 : printTree(fixPath(*i), "", "", done);
389 2 : break;
390 : }
391 :
392 : case qGraph: {
393 2 : PathSet roots;
394 4 : for (Strings::iterator i = opArgs.begin();
395 : i != opArgs.end(); ++i)
396 2 : roots.insert(maybeUseOutput(fixPath(*i), useOutput, forceRealise));
397 2 : printDotGraph(roots);
398 2 : break;
399 : }
400 :
401 : default:
402 0 : abort();
403 : }
404 : }
405 :
406 :
407 : static void opRegisterSubstitutes(Strings opFlags, Strings opArgs)
408 8 : {
409 8 : if (!opFlags.empty()) throw UsageError("unknown flag");
410 8 : if (!opArgs.empty()) throw UsageError("no arguments expected");
411 :
412 8 : Transaction txn;
413 8 : createStoreTransaction(txn);
414 :
415 48 : while (1) {
416 48 : Path srcPath;
417 1356 : Substitute sub;
418 48 : PathSet references;
419 48 : getline(cin, srcPath);
420 48 : if (cin.eof()) break;
421 40 : getline(cin, sub.deriver);
422 40 : getline(cin, sub.program);
423 40 : string s; int n;
424 40 : getline(cin, s);
425 40 : if (!string2Int(s, n)) throw Error("number expected");
426 49 : while (n--) {
427 9 : getline(cin, s);
428 9 : sub.args.push_back(s);
429 : }
430 40 : getline(cin, s);
431 40 : if (!string2Int(s, n)) throw Error("number expected");
432 68 : while (n--) {
433 28 : getline(cin, s);
434 28 : references.insert(s);
435 : }
436 40 : if (!cin || cin.eof()) throw Error("missing input");
437 40 : registerSubstitute(txn, srcPath, sub);
438 40 : setReferences(txn, srcPath, references);
439 : }
440 :
441 8 : txn.commit();
442 : }
443 :
444 :
445 : static void opClearSubstitutes(Strings opFlags, Strings opArgs)
446 1 : {
447 1 : if (!opFlags.empty()) throw UsageError("unknown flag");
448 1 : if (!opArgs.empty())
449 0 : throw UsageError("no arguments expected");
450 :
451 1 : clearSubstitutes();
452 : }
453 :
454 :
455 : static void opRegisterValidity(Strings opFlags, Strings opArgs)
456 2 : {
457 2 : if (!opFlags.empty()) throw UsageError("unknown flag");
458 2 : if (!opArgs.empty()) throw UsageError("no arguments expected");
459 :
460 140 : ValidPathInfos infos;
461 :
462 2503 : while (1) {
463 17919 : ValidPathInfo info;
464 2503 : getline(cin, info.path);
465 2503 : if (cin.eof()) break;
466 2501 : getline(cin, info.deriver);
467 2501 : string s; int n;
468 2501 : getline(cin, s);
469 2501 : if (!string2Int(s, n)) throw Error("number expected");
470 5001 : while (n--) {
471 2500 : getline(cin, s);
472 2500 : info.references.insert(s);
473 : }
474 2501 : if (!cin || cin.eof()) throw Error("missing input");
475 2501 : if (!isValidPath(info.path)) {
476 : /* !!! races */
477 2501 : canonicalisePathMetaData(info.path);
478 2501 : info.hash = hashPath(htSHA256, info.path);
479 2501 : infos.push_back(info);
480 : }
481 : }
482 :
483 2 : Transaction txn;
484 2 : createStoreTransaction(txn);
485 2 : registerValidPaths(txn, infos);
486 2 : txn.commit();
487 : }
488 :
489 :
490 : static void opCheckValidity(Strings opFlags, Strings opArgs)
491 24 : {
492 24 : if (!opFlags.empty()) throw UsageError("unknown flag");
493 :
494 24 : for (Strings::iterator i = opArgs.begin();
495 : i != opArgs.end(); ++i)
496 24 : if (!isValidPath(*i))
497 24 : throw Error(format("path `%1%' is not valid") % *i);
498 : }
499 :
500 :
501 : struct PrintFreed
502 : {
503 : bool show, dryRun;
504 : unsigned long long bytesFreed;
505 : PrintFreed(bool show, bool dryRun)
506 36 : : show(show), dryRun(dryRun), bytesFreed(0) { }
507 : ~PrintFreed()
508 36 : {
509 18 : if (show)
510 16 : cout << format(
511 : (dryRun
512 : ? "%d bytes would be freed (%.2f MiB)\n"
513 : : "%d bytes freed (%.2f MiB)\n"))
514 : % bytesFreed % (bytesFreed / (1024.0 * 1024.0));
515 : }
516 : };
517 :
518 :
519 : static void opGC(Strings opFlags, Strings opArgs)
520 15 : {
521 15 : GCAction action = gcDeleteDead;
522 :
523 : /* Do what? */
524 20 : for (Strings::iterator i = opFlags.begin();
525 : i != opFlags.end(); ++i)
526 5 : if (*i == "--print-roots") action = gcReturnRoots;
527 4 : else if (*i == "--print-live") action = gcReturnLive;
528 3 : else if (*i == "--print-dead") action = gcReturnDead;
529 0 : else if (*i == "--delete") action = gcDeleteDead;
530 0 : else throw UsageError(format("bad sub-operation `%1%' in GC") % *i);
531 :
532 15 : PathSet result;
533 : PrintFreed freed(action == gcDeleteDead || action == gcReturnDead,
534 15 : action == gcReturnDead);
535 15 : collectGarbage(action, PathSet(), false, result, freed.bytesFreed);
536 :
537 15 : if (action != gcDeleteDead) {
538 197 : for (PathSet::iterator i = result.begin(); i != result.end(); ++i)
539 192 : cout << *i << std::endl;
540 : }
541 : }
542 :
543 :
544 : /* Remove paths from the Nix store if possible (i.e., if they do not
545 : have any remaining referrers and are not reachable from any GC
546 : roots). */
547 : static void opDelete(Strings opFlags, Strings opArgs)
548 3 : {
549 3 : bool ignoreLiveness = false;
550 :
551 3 : for (Strings::iterator i = opFlags.begin();
552 : i != opFlags.end(); ++i)
553 0 : if (*i == "--ignore-liveness") ignoreLiveness = true;
554 0 : else throw UsageError(format("unknown flag `%1%'") % *i);
555 :
556 3 : PathSet pathsToDelete;
557 6 : for (Strings::iterator i = opArgs.begin();
558 : i != opArgs.end(); ++i)
559 3 : pathsToDelete.insert(fixPath(*i));
560 :
561 3 : PathSet dummy;
562 3 : PrintFreed freed(true, false);
563 3 : collectGarbage(gcDeleteSpecific, pathsToDelete, ignoreLiveness,
564 : dummy, freed.bytesFreed);
565 : }
566 :
567 :
568 : /* A sink that writes dump output to stdout. */
569 : struct StdoutSink : DumpSink
570 : {
571 : virtual void operator ()
572 : (const unsigned char * data, unsigned int len)
573 342 : {
574 342 : writeFull(STDOUT_FILENO, data, len);
575 : }
576 : };
577 :
578 :
579 : /* Dump a path as a Nix archive. The archive is written to standard
580 : output. */
581 : static void opDump(Strings opFlags, Strings opArgs)
582 9 : {
583 9 : if (!opFlags.empty()) throw UsageError("unknown flag");
584 9 : if (opArgs.size() != 1) throw UsageError("only one argument allowed");
585 :
586 5257 : StdoutSink sink;
587 9 : string path = *opArgs.begin();
588 9 : dumpPath(path, sink);
589 : }
590 :
591 :
592 : /* A source that read restore intput to stdin. */
593 : struct StdinSource : RestoreSource
594 : {
595 : virtual void operator () (unsigned char * data, unsigned int len)
596 636 : {
597 636 : readFull(STDIN_FILENO, data, len);
598 : }
599 : };
600 :
601 :
602 : /* Restore a value from a Nix archive. The archive is read from
603 : standard input. */
604 : static void opRestore(Strings opFlags, Strings opArgs)
605 12 : {
606 12 : if (!opFlags.empty()) throw UsageError("unknown flag");
607 12 : if (opArgs.size() != 1) throw UsageError("only one argument allowed");
608 :
609 110 : StdinSource source;
610 12 : restorePath(*opArgs.begin(), source);
611 : }
612 :
613 :
614 : /* Initialise the Nix databases. */
615 : static void opInit(Strings opFlags, Strings opArgs)
616 4 : {
617 4 : if (!opFlags.empty()) throw UsageError("unknown flag");
618 4 : if (!opArgs.empty())
619 0 : throw UsageError("no arguments expected");
620 4 : initDB();
621 : }
622 :
623 :
624 : /* Verify the consistency of the Nix environment. */
625 : static void opVerify(Strings opFlags, Strings opArgs)
626 1 : {
627 1 : if (!opArgs.empty())
628 0 : throw UsageError("no arguments expected");
629 :
630 1 : bool checkContents = false;
631 :
632 1 : for (Strings::iterator i = opFlags.begin();
633 : i != opFlags.end(); ++i)
634 0 : if (*i == "--check-contents") checkContents = true;
635 0 : else throw UsageError(format("unknown flag `%1%'") % *i);
636 :
637 1 : verifyStore(checkContents);
638 : }
639 :
640 :
641 : /* Scan the arguments; find the operation, set global flags, put all
642 : other flags in a list, and put all other arguments in another
643 : list. */
644 : void run(Strings args)
645 167 : {
646 167 : Strings opFlags, opArgs;
647 167 : Operation op = 0;
648 :
649 564 : for (Strings::iterator i = args.begin(); i != args.end(); ) {
650 397 : string arg = *i++;
651 :
652 397 : Operation oldOp = op;
653 :
654 397 : if (arg == "--realise" || arg == "-r")
655 22 : op = opRealise;
656 375 : else if (arg == "--add" || arg == "-A")
657 1 : op = opAdd;
658 374 : else if (arg == "--add-fixed")
659 12 : op = opAddFixed;
660 362 : else if (arg == "--print-fixed-path")
661 12 : op = opPrintFixedPath;
662 350 : else if (arg == "--delete")
663 3 : op = opDelete;
664 347 : else if (arg == "--query" || arg == "-q")
665 41 : op = opQuery;
666 306 : else if (arg == "--register-substitutes")
667 8 : op = opRegisterSubstitutes;
668 298 : else if (arg == "--clear-substitutes")
669 1 : op = opClearSubstitutes;
670 297 : else if (arg == "--register-validity")
671 2 : op = opRegisterValidity;
672 295 : else if (arg == "--check-validity")
673 24 : op = opCheckValidity;
674 271 : else if (arg == "--gc")
675 15 : op = opGC;
676 256 : else if (arg == "--dump")
677 9 : op = opDump;
678 247 : else if (arg == "--restore")
679 12 : op = opRestore;
680 235 : else if (arg == "--init")
681 4 : op = opInit;
682 231 : else if (arg == "--verify")
683 1 : op = opVerify;
684 230 : else if (arg == "--add-root") {
685 1 : if (i == args.end())
686 0 : throw UsageError("`--add-root requires an argument");
687 1 : gcRoot = absPath(*i++);
688 : }
689 229 : else if (arg == "--indirect")
690 1 : indirectRoot = true;
691 228 : else if (arg[0] == '-')
692 43 : opFlags.push_back(arg);
693 : else
694 185 : opArgs.push_back(arg);
695 :
696 397 : if (oldOp && oldOp != op)
697 0 : throw UsageError("only one operation may be specified");
698 : }
699 :
700 167 : if (!op) throw UsageError("no operation specified");
701 :
702 167 : if (op != opDump && op != opRestore) /* !!! hack */
703 146 : openDB(op != opGC);
704 :
705 167 : op(opFlags, opArgs);
706 : }
707 :
708 :
709 336 : string programId = "nix-store";
|