aboutsummaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/libcmd/command.cc47
-rw-r--r--src/libcmd/command.hh13
-rw-r--r--src/libcmd/installables.cc39
-rw-r--r--src/libcmd/installables.hh11
-rw-r--r--src/libexpr/attr-set.hh7
-rw-r--r--src/libexpr/eval.cc20
-rw-r--r--src/libexpr/eval.hh12
-rw-r--r--src/libexpr/flake/flake.cc20
-rw-r--r--src/libexpr/flake/flakeref.cc22
-rw-r--r--src/libexpr/lexer.l41
-rw-r--r--src/libexpr/nixexpr.cc4
-rw-r--r--src/libexpr/parser.y49
-rw-r--r--src/libexpr/primops.cc207
-rw-r--r--src/libexpr/symbol-table.hh21
-rw-r--r--src/libfetchers/tarball.cc10
-rw-r--r--src/libstore/binary-cache-store.cc42
-rw-r--r--src/libstore/binary-cache-store.hh12
-rw-r--r--src/libstore/build/derivation-goal.cc2
-rw-r--r--src/libstore/build/local-derivation-goal.cc6
-rw-r--r--src/libstore/daemon.cc40
-rw-r--r--src/libstore/derivations.cc5
-rw-r--r--src/libstore/export-import.cc6
-rw-r--r--src/libstore/filetransfer.cc20
-rw-r--r--src/libstore/filetransfer.hh8
-rw-r--r--src/libstore/globals.hh7
-rw-r--r--src/libstore/http-binary-cache-store.cc8
-rw-r--r--src/libstore/legacy-ssh-store.cc2
-rw-r--r--src/libstore/local-binary-cache-store.cc1
-rw-r--r--src/libstore/local-fs-store.cc16
-rw-r--r--src/libstore/local-fs-store.hh3
-rw-r--r--src/libstore/local-store.cc25
-rw-r--r--src/libstore/local-store.hh2
-rw-r--r--src/libstore/nar-accessor.cc8
-rw-r--r--src/libstore/nar-accessor.hh2
-rw-r--r--src/libstore/realisation.cc2
-rw-r--r--src/libstore/remote-fs-accessor.cc30
-rw-r--r--src/libstore/remote-fs-accessor.hh3
-rw-r--r--src/libstore/remote-store.cc14
-rw-r--r--src/libstore/remote-store.hh2
-rw-r--r--src/libstore/s3-binary-cache-store.cc2
-rw-r--r--src/libstore/s3.hh4
-rw-r--r--src/libstore/store-api.cc15
-rw-r--r--src/libstore/store-api.hh14
-rw-r--r--src/libstore/worker-protocol.hh1
-rw-r--r--src/libutil/compression.cc8
-rw-r--r--src/libutil/compression.hh4
-rw-r--r--src/libutil/error.hh2
-rw-r--r--src/libutil/fmt.cc46
-rw-r--r--src/libutil/fmt.hh13
-rw-r--r--src/libutil/serialise.cc8
-rw-r--r--src/libutil/serialise.hh19
-rw-r--r--src/libutil/tests/compression.cc28
-rw-r--r--src/libutil/tests/fmt.cc68
-rw-r--r--src/libutil/types.hh1
-rw-r--r--src/libutil/util.cc51
-rw-r--r--src/libutil/util.hh18
-rwxr-xr-xsrc/nix-build/nix-build.cc4
-rw-r--r--src/nix/add-to-store.cc6
-rw-r--r--src/nix/app.cc2
-rw-r--r--src/nix/cat.cc2
-rw-r--r--src/nix/copy.cc37
-rw-r--r--src/nix/flake.md9
-rw-r--r--src/nix/ls.cc2
-rw-r--r--src/nix/main.cc9
-rw-r--r--src/nix/make-content-addressable.cc8
-rw-r--r--src/nix/nix.md44
-rw-r--r--src/nix/profile.cc6
-rw-r--r--src/nix/repl.cc2
-rw-r--r--src/nix/search.cc51
-rw-r--r--src/nix/store-copy-log.cc46
-rw-r--r--src/nix/store-copy-log.md33
-rw-r--r--src/nix/upgrade-nix.cc2
-rw-r--r--src/nix/why-depends.cc29
73 files changed, 928 insertions, 455 deletions
diff --git a/src/libcmd/command.cc b/src/libcmd/command.cc
index 429cd32cc..6d183dfad 100644
--- a/src/libcmd/command.cc
+++ b/src/libcmd/command.cc
@@ -54,6 +54,36 @@ void StoreCommand::run()
run(getStore());
}
+CopyCommand::CopyCommand()
+{
+ addFlag({
+ .longName = "from",
+ .description = "URL of the source Nix store.",
+ .labels = {"store-uri"},
+ .handler = {&srcUri},
+ });
+
+ addFlag({
+ .longName = "to",
+ .description = "URL of the destination Nix store.",
+ .labels = {"store-uri"},
+ .handler = {&dstUri},
+ });
+}
+
+ref<Store> CopyCommand::createStore()
+{
+ return srcUri.empty() ? StoreCommand::createStore() : openStore(srcUri);
+}
+
+ref<Store> CopyCommand::getDstStore()
+{
+ if (srcUri.empty() && dstUri.empty())
+ throw UsageError("you must pass '--from' and/or '--to'");
+
+ return dstUri.empty() ? openStore() : openStore(dstUri);
+}
+
EvalCommand::EvalCommand()
{
}
@@ -73,13 +103,16 @@ ref<Store> EvalCommand::getEvalStore()
ref<EvalState> EvalCommand::getEvalState()
{
- if (!evalState) evalState =
-#if HAVE_BOEHMGC
- std::allocate_shared<EvalState>(traceable_allocator<EvalState>(),
-#else
- std::make_shared<EvalState>(
-#endif
- searchPath, getEvalStore(), getStore());
+ if (!evalState)
+ evalState =
+ #if HAVE_BOEHMGC
+ std::allocate_shared<EvalState>(traceable_allocator<EvalState>(),
+ searchPath, getEvalStore(), getStore())
+ #else
+ std::make_shared<EvalState>(
+ searchPath, getEvalStore(), getStore())
+ #endif
+ ;
return ref<EvalState>(evalState);
}
diff --git a/src/libcmd/command.hh b/src/libcmd/command.hh
index 07f398468..bd2a0a7ee 100644
--- a/src/libcmd/command.hh
+++ b/src/libcmd/command.hh
@@ -43,6 +43,19 @@ private:
std::shared_ptr<Store> _store;
};
+/* A command that copies something between `--from` and `--to`
+ stores. */
+struct CopyCommand : virtual StoreCommand
+{
+ std::string srcUri, dstUri;
+
+ CopyCommand();
+
+ ref<Store> createStore() override;
+
+ ref<Store> getDstStore();
+};
+
struct EvalCommand : virtual StoreCommand, MixEvalArgs
{
EvalCommand();
diff --git a/src/libcmd/installables.cc b/src/libcmd/installables.cc
index 6c20bb7b1..0c2db0399 100644
--- a/src/libcmd/installables.cc
+++ b/src/libcmd/installables.cc
@@ -345,6 +345,18 @@ Installable::getCursor(EvalState & state)
return cursors[0];
}
+static StorePath getDeriver(
+ ref<Store> store,
+ const Installable & i,
+ const StorePath & drvPath)
+{
+ auto derivers = store->queryValidDerivers(drvPath);
+ if (derivers.empty())
+ throw Error("'%s' does not have a known deriver", i.what());
+ // FIXME: use all derivers?
+ return *derivers.begin();
+}
+
struct InstallableStorePath : Installable
{
ref<Store> store;
@@ -353,7 +365,7 @@ struct InstallableStorePath : Installable
InstallableStorePath(ref<Store> store, StorePath && storePath)
: store(store), storePath(std::move(storePath)) { }
- std::string what() override { return store->printStorePath(storePath); }
+ std::string what() const override { return store->printStorePath(storePath); }
DerivedPaths toDerivedPaths() override
{
@@ -374,6 +386,15 @@ struct InstallableStorePath : Installable
}
}
+ StorePathSet toDrvPaths(ref<Store> store) override
+ {
+ if (storePath.isDerivation()) {
+ return {storePath};
+ } else {
+ return {getDeriver(store, *this, storePath)};
+ }
+ }
+
std::optional<StorePath> getStorePath() override
{
return storePath;
@@ -402,6 +423,14 @@ DerivedPaths InstallableValue::toDerivedPaths()
return res;
}
+StorePathSet InstallableValue::toDrvPaths(ref<Store> store)
+{
+ StorePathSet res;
+ for (auto & drv : toDerivations())
+ res.insert(drv.drvPath);
+ return res;
+}
+
struct InstallableAttrPath : InstallableValue
{
SourceExprCommand & cmd;
@@ -412,7 +441,7 @@ struct InstallableAttrPath : InstallableValue
: InstallableValue(state), cmd(cmd), v(allocRootValue(v)), attrPath(attrPath)
{ }
- std::string what() override { return attrPath; }
+ std::string what() const override { return attrPath; }
std::pair<Value *, Pos> toValue(EvalState & state) override
{
@@ -836,11 +865,7 @@ StorePathSet toDerivations(
[&](const DerivedPath::Opaque & bo) {
if (!useDeriver)
throw Error("argument '%s' did not evaluate to a derivation", i->what());
- auto derivers = store->queryValidDerivers(bo.path);
- if (derivers.empty())
- throw Error("'%s' does not have a known deriver", i->what());
- // FIXME: use all derivers?
- drvPaths.insert(*derivers.begin());
+ drvPaths.insert(getDeriver(store, *i, bo.path));
},
[&](const DerivedPath::Built & bfd) {
drvPaths.insert(bfd.drvPath);
diff --git a/src/libcmd/installables.hh b/src/libcmd/installables.hh
index 79931ad3e..ced6b3f10 100644
--- a/src/libcmd/installables.hh
+++ b/src/libcmd/installables.hh
@@ -33,10 +33,15 @@ struct Installable
{
virtual ~Installable() { }
- virtual std::string what() = 0;
+ virtual std::string what() const = 0;
virtual DerivedPaths toDerivedPaths() = 0;
+ virtual StorePathSet toDrvPaths(ref<Store> store)
+ {
+ throw Error("'%s' cannot be converted to a derivation path", what());
+ }
+
DerivedPath toDerivedPath();
UnresolvedApp toApp(EvalState & state);
@@ -81,6 +86,8 @@ struct InstallableValue : Installable
virtual std::vector<DerivationInfo> toDerivations() = 0;
DerivedPaths toDerivedPaths() override;
+
+ StorePathSet toDrvPaths(ref<Store> store) override;
};
struct InstallableFlake : InstallableValue
@@ -99,7 +106,7 @@ struct InstallableFlake : InstallableValue
Strings && prefixes,
const flake::LockFlags & lockFlags);
- std::string what() override { return flakeRef.to_string() + "#" + *attrPaths.begin(); }
+ std::string what() const override { return flakeRef.to_string() + "#" + *attrPaths.begin(); }
std::vector<std::string> getActualAttrPaths();
diff --git a/src/libexpr/attr-set.hh b/src/libexpr/attr-set.hh
index 82c348287..3e4899efc 100644
--- a/src/libexpr/attr-set.hh
+++ b/src/libexpr/attr-set.hh
@@ -121,6 +121,8 @@ class BindingsBuilder
Bindings * bindings;
public:
+ // needed by std::back_inserter
+ using value_type = Attr;
EvalState & state;
@@ -135,6 +137,11 @@ public:
void insert(const Attr & attr)
{
+ push_back(attr);
+ }
+
+ void push_back(const Attr & attr)
+ {
bindings->push_back(attr);
}
diff --git a/src/libexpr/eval.cc b/src/libexpr/eval.cc
index 61bccd6e2..b884b4001 100644
--- a/src/libexpr/eval.cc
+++ b/src/libexpr/eval.cc
@@ -425,11 +425,21 @@ EvalState::EvalState(
, sDescription(symbols.create("description"))
, sSelf(symbols.create("self"))
, sEpsilon(symbols.create(""))
+ , sStartSet(symbols.create("startSet"))
+ , sOperator(symbols.create("operator"))
+ , sKey(symbols.create("key"))
+ , sPath(symbols.create("path"))
+ , sPrefix(symbols.create("prefix"))
, repair(NoRepair)
, emptyBindings(0)
, store(store)
, buildStore(buildStore ? buildStore : store)
, regexCache(makeRegexCache())
+#if HAVE_BOEHMGC
+ , valueAllocCache(std::allocate_shared<void *>(traceable_allocator<void *>(), nullptr))
+#else
+ , valueAllocCache(std::make_shared<void *>(nullptr))
+#endif
, baseEnv(allocEnv(128))
, staticBaseEnv(false, 0)
{
@@ -847,15 +857,15 @@ Value * EvalState::allocValue()
GC_malloc_many returns a linked list of objects of the given size, where the first word
of each object is also the pointer to the next object in the list. This also means that we
have to explicitly clear the first word of every object we take. */
- if (!valueAllocCache) {
- valueAllocCache = GC_malloc_many(sizeof(Value));
- if (!valueAllocCache) throw std::bad_alloc();
+ if (!*valueAllocCache) {
+ *valueAllocCache = GC_malloc_many(sizeof(Value));
+ if (!*valueAllocCache) throw std::bad_alloc();
}
/* GC_NEXT is a convenience macro for accessing the first word of an object.
Take the first list item, advance the list to the next item, and clear the next pointer. */
- void * p = valueAllocCache;
- GC_PTR_STORE_AND_DIRTY(&valueAllocCache, GC_NEXT(p));
+ void * p = *valueAllocCache;
+ GC_PTR_STORE_AND_DIRTY(&*valueAllocCache, GC_NEXT(p));
GC_NEXT(p) = nullptr;
nrValues++;
diff --git a/src/libexpr/eval.hh b/src/libexpr/eval.hh
index 89814785e..c59203aa5 100644
--- a/src/libexpr/eval.hh
+++ b/src/libexpr/eval.hh
@@ -80,7 +80,8 @@ public:
sContentAddressed,
sOutputHash, sOutputHashAlgo, sOutputHashMode,
sRecurseForDerivations,
- sDescription, sSelf, sEpsilon;
+ sDescription, sSelf, sEpsilon, sStartSet, sOperator, sKey, sPath,
+ sPrefix;
Symbol sDerivationNix;
/* If set, force copying files to the Nix store even if they
@@ -132,7 +133,7 @@ private:
std::shared_ptr<RegexCache> regexCache;
/* Allocation cache for GC'd Value objects. */
- void * valueAllocCache = nullptr;
+ std::shared_ptr<void *> valueAllocCache;
public:
@@ -179,8 +180,8 @@ public:
Expr * parseExprFromFile(const Path & path, StaticEnv & staticEnv);
/* Parse a Nix expression from the specified string. */
- Expr * parseExprFromString(std::string_view s, const Path & basePath, StaticEnv & staticEnv);
- Expr * parseExprFromString(std::string_view s, const Path & basePath);
+ Expr * parseExprFromString(std::string s, const Path & basePath, StaticEnv & staticEnv);
+ Expr * parseExprFromString(std::string s, const Path & basePath);
Expr * parseStdin();
@@ -308,7 +309,7 @@ private:
friend struct ExprAttrs;
friend struct ExprLet;
- Expr * parse(const char * text, FileOrigin origin, const Path & path,
+ Expr * parse(char * text, size_t length, FileOrigin origin, const Path & path,
const Path & basePath, StaticEnv & staticEnv);
public:
@@ -399,6 +400,7 @@ private:
friend struct ExprSelect;
friend void prim_getAttr(EvalState & state, const Pos & pos, Value * * args, Value & v);
friend void prim_match(EvalState & state, const Pos & pos, Value * * args, Value & v);
+ friend void prim_split(EvalState & state, const Pos & pos, Value * * args, Value & v);
friend struct Value;
};
diff --git a/src/libexpr/flake/flake.cc b/src/libexpr/flake/flake.cc
index 190a128d7..0fbe9b960 100644
--- a/src/libexpr/flake/flake.cc
+++ b/src/libexpr/flake/flake.cc
@@ -633,12 +633,24 @@ LockedFlake lockFlake(
newLockFile.write(path);
+ std::optional<std::string> commitMessage = std::nullopt;
+ if (lockFlags.commitLockFile) {
+ std::string cm;
+
+ cm = settings.commitLockFileSummary.get();
+
+ if (cm == "") {
+ cm = fmt("%s: %s", relPath, lockFileExists ? "Update" : "Add");
+ }
+
+ cm += "\n\nFlake lock file updates:\n\n";
+ cm += filterANSIEscapes(diff, true);
+ commitMessage = cm;
+ }
+
topRef.input.markChangedFile(
(topRef.subdir == "" ? "" : topRef.subdir + "/") + "flake.lock",
- lockFlags.commitLockFile
- ? std::optional<std::string>(fmt("%s: %s\n\nFlake lock file changes:\n\n%s",
- relPath, lockFileExists ? "Update" : "Add", filterANSIEscapes(diff, true)))
- : std::nullopt);
+ commitMessage);
/* Rewriting the lockfile changed the top-level
repo, so we should re-read it. FIXME: we could
diff --git a/src/libexpr/flake/flakeref.cc b/src/libexpr/flake/flakeref.cc
index c3b74e0fe..930ed9ccd 100644
--- a/src/libexpr/flake/flakeref.cc
+++ b/src/libexpr/flake/flakeref.cc
@@ -122,6 +122,28 @@ std::pair<FlakeRef, std::string> parseFlakeRefWithFragment(
if (isFlake) {
+ if (!allowMissing && !pathExists(path + "/flake.nix")){
+ notice("path '%s' does not contain a 'flake.nix', searching up",path);
+
+ // Save device to detect filesystem boundary
+ dev_t device = lstat(path).st_dev;
+ bool found = false;
+ while (path != "/") {
+ if (pathExists(path + "/flake.nix")) {
+ found = true;
+ break;
+ } else if (pathExists(path + "/.git"))
+ throw Error("path '%s' is not part of a flake (neither it nor its parent directories contain a 'flake.nix' file)", path);
+ else {
+ if (lstat(path).st_dev != device)
+ throw Error("unable to find a flake before encountering filesystem boundary at '%s'", path);
+ }
+ path = dirOf(path);
+ }
+ if (!found)
+ throw BadURL("could not find a flake.nix file");
+ }
+
if (!S_ISDIR(lstat(path).st_mode))
throw BadURL("path '%s' is not a flake (because it's not a directory)", path);
diff --git a/src/libexpr/lexer.l b/src/libexpr/lexer.l
index c18877e29..a0e7a1877 100644
--- a/src/libexpr/lexer.l
+++ b/src/libexpr/lexer.l
@@ -64,29 +64,32 @@ static void adjustLoc(YYLTYPE * loc, const char * s, size_t len)
}
-// FIXME: optimize
-static Expr * unescapeStr(SymbolTable & symbols, const char * s, size_t length)
+// we make use of the fact that the parser receives a private copy of the input
+// string and can munge around in it.
+static Expr * unescapeStr(SymbolTable & symbols, char * s, size_t length)
{
- string t;
- t.reserve(length);
+ char * result = s;
+ char * t = s;
char c;
+ // the input string is terminated with *two* NULs, so we can safely take
+ // *one* character after the one being checked against.
while ((c = *s++)) {
if (c == '\\') {
- assert(*s);
c = *s++;
- if (c == 'n') t += '\n';
- else if (c == 'r') t += '\r';
- else if (c == 't') t += '\t';
- else t += c;
+ if (c == 'n') *t = '\n';
+ else if (c == 'r') *t = '\r';
+ else if (c == 't') *t = '\t';
+ else *t = c;
}
else if (c == '\r') {
/* Normalise CR and CR/LF into LF. */
- t += '\n';
+ *t = '\n';
if (*s == '\n') s++; /* cr/lf */
}
- else t += c;
+ else *t = c;
+ t++;
}
- return new ExprString(symbols.create(t));
+ return new ExprString(symbols.create({result, size_t(t - result)}));
}
@@ -139,7 +142,7 @@ or { return OR_KW; }
\/\/ { return UPDATE; }
\+\+ { return CONCAT; }
-{ID} { yylval->id = strdup(yytext); return ID; }
+{ID} { yylval->id = {yytext, (size_t) yyleng}; return ID; }
{INT} { errno = 0;
try {
yylval->n = boost::lexical_cast<int64_t>(yytext);
@@ -221,14 +224,14 @@ or { return OR_KW; }
<PATH_START>{PATH_SEG} {
POP_STATE();
PUSH_STATE(INPATH_SLASH);
- yylval->path = strdup(yytext);
+ yylval->path = {yytext, (size_t) yyleng};
return PATH;
}
<PATH_START>{HPATH_START} {
POP_STATE();
PUSH_STATE(INPATH_SLASH);
- yylval->path = strdup(yytext);
+ yylval->path = {yytext, (size_t) yyleng};
return HPATH;
}
@@ -237,7 +240,7 @@ or { return OR_KW; }
PUSH_STATE(INPATH_SLASH);
else
PUSH_STATE(INPATH);
- yylval->path = strdup(yytext);
+ yylval->path = {yytext, (size_t) yyleng};
return PATH;
}
{HPATH} {
@@ -245,7 +248,7 @@ or { return OR_KW; }
PUSH_STATE(INPATH_SLASH);
else
PUSH_STATE(INPATH);
- yylval->path = strdup(yytext);
+ yylval->path = {yytext, (size_t) yyleng};
return HPATH;
}
@@ -280,8 +283,8 @@ or { return OR_KW; }
throw ParseError("path has a trailing slash");
}
-{SPATH} { yylval->path = strdup(yytext); return SPATH; }
-{URI} { yylval->uri = strdup(yytext); return URI; }
+{SPATH} { yylval->path = {yytext, (size_t) yyleng}; return SPATH; }
+{URI} { yylval->uri = {yytext, (size_t) yyleng}; return URI; }
[ \t\r\n]+ /* eat up whitespace */
\#[^\r\n]* /* single-line comments */
diff --git a/src/libexpr/nixexpr.cc b/src/libexpr/nixexpr.cc
index a75357871..2d2cd96cd 100644
--- a/src/libexpr/nixexpr.cc
+++ b/src/libexpr/nixexpr.cc
@@ -191,7 +191,7 @@ void ExprConcatStrings::show(std::ostream & str) const
str << "(";
for (auto & i : *es) {
if (first) first = false; else str << " + ";
- str << i.second;
+ str << *i.second;
}
str << ")";
}
@@ -473,7 +473,7 @@ string ExprLambda::showNamePos() const
size_t SymbolTable::totalSize() const
{
size_t n = 0;
- for (auto & i : symbols)
+ for (auto & i : store)
n += i.size();
return n;
}
diff --git a/src/libexpr/parser.y b/src/libexpr/parser.y
index f8aaea582..7a8e93c12 100644
--- a/src/libexpr/parser.y
+++ b/src/libexpr/parser.y
@@ -273,9 +273,16 @@ void yyerror(YYLTYPE * loc, yyscan_t scanner, ParseData * data, const char * err
nix::Formal * formal;
nix::NixInt n;
nix::NixFloat nf;
- const char * id; // !!! -> Symbol
- char * path;
- char * uri;
+ // using C a struct allows us to avoid having to define the special
+ // members that using string_view here would implicitly delete.
+ struct StringToken {
+ const char * p;
+ size_t l;
+ operator std::string_view() const { return {p, l}; }
+ };
+ StringToken id; // !!! -> Symbol
+ StringToken path;
+ StringToken uri;
std::vector<nix::AttrName> * attrNames;
std::vector<std::pair<nix::Pos, nix::Expr *> > * string_parts;
}
@@ -397,7 +404,8 @@ expr_select
expr_simple
: ID {
- if (strcmp($1, "__curPos") == 0)
+ std::string_view s = "__curPos";
+ if ($1.l == s.size() && strncmp($1.p, s.data(), s.size()) == 0)
$$ = new ExprPos(CUR_POS);
else
$$ = new ExprVar(CUR_POS, data->symbols.create($1));
@@ -414,7 +422,7 @@ expr_simple
$$ = new ExprConcatStrings(CUR_POS, false, $2);
}
| SPATH {
- string path($1 + 1, strlen($1) - 2);
+ string path($1.p + 1, $1.l - 2);
$$ = new ExprCall(CUR_POS,
new ExprVar(data->symbols.create("__findFile")),
{new ExprVar(data->symbols.create("__nixPath")),
@@ -460,14 +468,14 @@ string_parts_interpolated
path_start
: PATH {
- Path path(absPath($1, data->basePath));
+ Path path(absPath({$1.p, $1.l}, data->basePath));
/* add back in the trailing '/' to the first segment */
- if ($1[strlen($1)-1] == '/' && strlen($1) > 1)
+ if ($1.p[$1.l-1] == '/' && $1.l > 1)
path += "/";
$$ = new ExprPath(path);
}
| HPATH {
- Path path(getHome() + string($1 + 1));
+ Path path(getHome() + string($1.p + 1, $1.l - 1));
$$ = new ExprPath(path);
}
;
@@ -543,7 +551,7 @@ attrpath
attr
: ID { $$ = $1; }
- | OR_KW { $$ = "or"; }
+ | OR_KW { $$ = {"or", 2}; }
;
string_attr
@@ -589,7 +597,7 @@ formal
namespace nix {
-Expr * EvalState::parse(const char * text, FileOrigin origin,
+Expr * EvalState::parse(char * text, size_t length, FileOrigin origin,
const Path & path, const Path & basePath, StaticEnv & staticEnv)
{
yyscan_t scanner;
@@ -609,7 +617,7 @@ Expr * EvalState::parse(const char * text, FileOrigin origin,
data.basePath = basePath;
yylex_init(&scanner);
- yy_scan_string(text, scanner);
+ yy_scan_buffer(text, length, scanner);
int res = yyparse(scanner, &data);
yylex_destroy(scanner);
@@ -655,26 +663,33 @@ Expr * EvalState::parseExprFromFile(const Path & path)
Expr * EvalState::parseExprFromFile(const Path & path, StaticEnv & staticEnv)
{
- return parse(readFile(path).c_str(), foFile, path, dirOf(path), staticEnv);
+ auto buffer = readFile(path);
+ // readFile should have left some extra space for terminators
+ buffer.append("\0\0", 2);
+ return parse(buffer.data(), buffer.size(), foFile, path, dirOf(path), staticEnv);
}
-Expr * EvalState::parseExprFromString(std::string_view s, const Path & basePath, StaticEnv & staticEnv)
+Expr * EvalState::parseExprFromString(std::string s, const Path & basePath, StaticEnv & staticEnv)
{
- return parse(s.data(), foString, "", basePath, staticEnv);
+ s.append("\0\0", 2);
+ return parse(s.data(), s.size(), foString, "", basePath, staticEnv);
}
-Expr * EvalState::parseExprFromString(std::string_view s, const Path & basePath)
+Expr * EvalState::parseExprFromString(std::string s, const Path & basePath)
{
- return parseExprFromString(s, basePath, staticBaseEnv);
+ return parseExprFromString(std::move(s), basePath, staticBaseEnv);
}
Expr * EvalState::parseStdin()
{
//Activity act(*logger, lvlTalkative, format("parsing standard input"));
- return parse(drainFD(0).data(), foStdin, "", absPath("."), staticBaseEnv);
+ auto buffer = drainFD(0);
+ // drainFD should have left some extra space for terminators
+ buffer.append("\0\0", 2);
+ return parse(buffer.data(), buffer.size(), foStdin, "", absPath("."), staticBaseEnv);
}
diff --git a/src/libexpr/primops.cc b/src/libexpr/primops.cc
index 1d2a7d5d2..9d36f72a8 100644
--- a/src/libexpr/primops.cc
+++ b/src/libexpr/primops.cc
@@ -12,6 +12,8 @@
#include "value-to-xml.hh"
#include "primops.hh"
+#include <boost/container/small_vector.hpp>
+
#include <sys/types.h>
#include <sys/stat.h>
#include <unistd.h>
@@ -100,17 +102,30 @@ static Path realisePath(EvalState & state, const Pos & pos, Value & v, const Rea
{
PathSet context;
- Path path = flags.requireAbsolutePath
- ? state.coerceToPath(pos, v, context)
- : state.coerceToString(pos, v, context, false, false);
+ auto path = [&]()
+ {
+ try {
+ return flags.requireAbsolutePath
+ ? state.coerceToPath(pos, v, context)
+ : state.coerceToString(pos, v, context, false, false);
+ } catch (Error & e) {
+ e.addTrace(pos, "while realising the context of a path");
+ throw;
+ }
+ }();
- StringMap rewrites = state.realiseContext(context);
+ try {
+ StringMap rewrites = state.realiseContext(context);
- auto realPath = state.toRealPath(rewriteStrings(path, rewrites), context);
+ auto realPath = state.toRealPath(rewriteStrings(path, rewrites), context);
- return flags.checkForPureEval
- ? state.checkSourcePath(realPath)
- : realPath;
+ return flags.checkForPureEval
+ ? state.checkSourcePath(realPath)
+ : realPath;
+ } catch (Error & e) {
+ e.addTrace(pos, "while realising the context of path '%s'", path);
+ throw;
+ }
}
/* Add and attribute to the given attribute map from the output name to
@@ -148,18 +163,7 @@ static void mkOutputString(
argument. */
static void import(EvalState & state, const Pos & pos, Value & vPath, Value * vScope, Value & v)
{
- Path path;
- try {
- path = realisePath(state, pos, vPath);
- } catch (InvalidPathError & e) {
- throw EvalError({
- .msg = hintfmt("cannot import '%1%', since path '%2%' is not valid", path, e.path),
- .errPos = pos
- });
- } catch (Error & e) {
- e.addTrace(pos, "while importing '%s'", path);
- throw;
- }
+ auto path = realisePath(state, pos, vPath);
// FIXME
auto isValidDerivationInStore = [&]() -> std::optional<StorePath> {
@@ -312,18 +316,7 @@ extern "C" typedef void (*ValueInitializer)(EvalState & state, Value & v);
/* Load a ValueInitializer from a DSO and return whatever it initializes */
void prim_importNative(EvalState & state, const Pos & pos, Value * * args, Value & v)
{
- Path path;
- try {
- path = realisePath(state, pos, *args[0]);
- } catch (InvalidPathError & e) {
- throw EvalError({
- .msg = hintfmt("cannot import '%1%', since path '%2%' is not valid", path, e.path),
- .errPos = pos
- });
- } catch (Error & e) {
- e.addTrace(pos, "while importing '%s'", path);
- throw;
- }
+ auto path = realisePath(state, pos, *args[0]);
string sym = state.forceStringNoCtx(*args[1], pos);
@@ -378,7 +371,7 @@ void prim_exec(EvalState & state, const Pos & pos, Value * * args, Value & v)
auto output = runProgram(program, true, commandArgs);
Expr * parsed;
try {
- parsed = state.parseExprFromString(output, pos.file);
+ parsed = state.parseExprFromString(std::move(output), pos.file);
} catch (Error & e) {
e.addTrace(pos, "While parsing the output from '%1%'", program);
throw;
@@ -592,16 +585,16 @@ typedef list<Value *> ValueList;
static Bindings::iterator getAttr(
EvalState & state,
- string funcName,
- string attrName,
+ std::string_view funcName,
+ Symbol attrSym,
Bindings * attrSet,
const Pos & pos)
{
- Bindings::iterator value = attrSet->find(state.symbols.create(attrName));
+ Bindings::iterator value = attrSet->find(attrSym);
if (value == attrSet->end()) {
hintformat errorMsg = hintfmt(
"attribute '%s' missing for call to '%s'",
- attrName,
+ attrSym,
funcName
);
@@ -635,7 +628,7 @@ static void prim_genericClosure(EvalState & state, const Pos & pos, Value * * ar
Bindings::iterator startSet = getAttr(
state,
"genericClosure",
- "startSet",
+ state.sStartSet,
args[0]->attrs,
pos
);
@@ -650,7 +643,7 @@ static void prim_genericClosure(EvalState & state, const Pos & pos, Value * * ar
Bindings::iterator op = getAttr(
state,
"genericClosure",
- "operator",
+ state.sOperator,
args[0]->attrs,
pos
);
@@ -672,7 +665,7 @@ static void prim_genericClosure(EvalState & state, const Pos & pos, Value * * ar
state.forceAttrs(*e, pos);
Bindings::iterator key =
- e->attrs->find(state.symbols.create("key"));
+ e->attrs->find(state.sKey);
if (key == e->attrs->end())
throw EvalError({
.msg = hintfmt("attribute 'key' required"),
@@ -1079,10 +1072,10 @@ static void prim_derivationStrict(EvalState & state, const Pos & pos, Value * *
} else {
auto s = state.coerceToString(*i->pos, *i->value, context, true);
drv.env.emplace(key, s);
- if (i->name == state.sBuilder) drv.builder = s;
- else if (i->name == state.sSystem) drv.platform = s;
- else if (i->name == state.sOutputHash) outputHash = s;
- else if (i->name == state.sOutputHashAlgo) outputHashAlgo = s;
+ if (i->name == state.sBuilder) drv.builder = std::move(s);
+ else if (i->name == state.sSystem) drv.platform = std::move(s);
+ else if (i->name == state.sOutputHash) outputHash = std::move(s);
+ else if (i->name == state.sOutputHashAlgo) outputHashAlgo = std::move(s);
else if (i->name == state.sOutputHashMode) handleHashMode(s);
else if (i->name == state.sOutputs)
handleOutputs(tokenizeString<Strings>(s));
@@ -1377,22 +1370,12 @@ static RegisterPrimOp primop_storePath({
static void prim_pathExists(EvalState & state, const Pos & pos, Value * * args, Value & v)
{
- Path path;
- try {
- // We don’t check the path right now, because we don’t want to throw if
- // the path isn’t allowed, but just return false
- // (and we can’t just catch the exception here because we still want to
- // throw if something in the evaluation of `*args[0]` tries to access an
- // unauthorized path)
- path = realisePath(state, pos, *args[0], { .checkForPureEval = false });
- } catch (InvalidPathError & e) {
- throw EvalError({
- .msg = hintfmt(
- "cannot check the existence of '%1%', since path '%2%' is not valid",
- path, e.path),
- .errPos = pos
- });
- }
+ /* We don’t check the path right now, because we don’t want to
+ throw if the path isn’t allowed, but just return false (and we
+ can’t just catch the exception here because we still want to
+ throw if something in the evaluation of `*args[0]` tries to
+ access an unauthorized path). */
+ auto path = realisePath(state, pos, *args[0], { .checkForPureEval = false });
try {
v.mkBool(pathExists(state.checkSourcePath(path)));
@@ -1458,21 +1441,17 @@ static RegisterPrimOp primop_dirOf({
/* Return the contents of a file as a string. */
static void prim_readFile(EvalState & state, const Pos & pos, Value * * args, Value & v)
{
- Path path;
- try {
- path = realisePath(state, pos, *args[0]);
- } catch (InvalidPathError & e) {
- throw EvalError({
- .msg = hintfmt("cannot read '%1%', since path '%2%' is not valid", path, e.path),
- .errPos = pos
- });
- }
+ auto path = realisePath(state, pos, *args[0]);
string s = readFile(path);
if (s.find((char) 0) != string::npos)
throw Error("the contents of the file '%1%' cannot be represented as a Nix string", path);
- auto refs = state.store->isInStore(path) ?
- state.store->queryPathInfo(state.store->toStorePath(path).first)->references :
- StorePathSet{};
+ StorePathSet refs;
+ if (state.store->isInStore(path)) {
+ try {
+ refs = state.store->queryPathInfo(state.store->toStorePath(path).first)->references;
+ } catch (Error &) { // FIXME: should be InvalidPathError
+ }
+ }
auto context = state.store->printStorePathSet(refs);
v.mkString(s, context);
}
@@ -1498,28 +1477,19 @@ static void prim_findFile(EvalState & state, const Pos & pos, Value * * args, Va
state.forceAttrs(*v2, pos);
string prefix;
- Bindings::iterator i = v2->attrs->find(state.symbols.create("prefix"));
+ Bindings::iterator i = v2->attrs->find(state.sPrefix);
if (i != v2->attrs->end())
prefix = state.forceStringNoCtx(*i->value, pos);
i = getAttr(
state,
"findFile",
- "path",
+ state.sPath,
v2->attrs,
pos
);
- Path path;
-
- try {
- path = realisePath(state, pos, *i->value, { .requireAbsolutePath = false });
- } catch (InvalidPathError & e) {
- throw EvalError({
- .msg = hintfmt("cannot find '%1%', since path '%2%' is not valid", path, e.path),
- .errPos = pos
- });
- }
+ auto path = realisePath(state, pos, *i->value, { .requireAbsolutePath = false });
searchPath.emplace_back(prefix, path);
}
@@ -1546,12 +1516,7 @@ static void prim_hashFile(EvalState & state, const Pos & pos, Value * * args, Va
.errPos = pos
});
- Path path;
- try {
- path = realisePath(state, pos, *args[1]);
- } catch (InvalidPathError & e) {
- throw EvalError("cannot read '%s' since path '%s' is not valid, at %s", path, e.path, pos);
- }
+ auto path = realisePath(state, pos, *args[1]);
v.mkString(hashFile(*ht, path).to_string(Base16, false));
}
@@ -1570,15 +1535,7 @@ static RegisterPrimOp primop_hashFile({
/* Read a directory (without . or ..) */
static void prim_readDir(EvalState & state, const Pos & pos, Value * * args, Value & v)
{
- Path path;
- try {
- path = realisePath(state, pos, *args[0]);
- } catch (InvalidPathError & e) {
- throw EvalError({
- .msg = hintfmt("cannot read '%1%', since path '%2%' is not valid", path, e.path),
- .errPos = pos
- });
- }
+ auto path = realisePath(state, pos, *args[0]);
DirEntries entries = readDirectory(path);
@@ -1913,10 +1870,13 @@ static void addPath(
StorePathSet refs;
if (state.store->isInStore(path)) {
- auto [storePath, subPath] = state.store->toStorePath(path);
- // FIXME: we should scanForReferences on the path before adding it
- refs = state.store->queryPathInfo(storePath)->references;
- path = state.store->toRealPath(storePath) + subPath;
+ try {
+ auto [storePath, subPath] = state.store->toStorePath(path);
+ // FIXME: we should scanForReferences on the path before adding it
+ refs = state.store->queryPathInfo(storePath)->references;
+ path = state.store->toRealPath(storePath) + subPath;
+ } catch (Error &) { // FIXME: should be InvalidPathError
+ }
}
path = evalSettings.pureEval && expectedHash
@@ -2163,7 +2123,10 @@ static void prim_attrValues(EvalState & state, const Pos & pos, Value * * args,
v.listElems()[n++] = (Value *) &i;
std::sort(v.listElems(), v.listElems() + n,
- [](Value * v1, Value * v2) { return (string) ((Attr *) v1)->name < (string) ((Attr *) v2)->name; });
+ [](Value * v1, Value * v2) {
+ std::string_view s1 = ((Attr *) v1)->name, s2 = ((Attr *) v2)->name;
+ return s1 < s2;
+ });
for (unsigned int i = 0; i < n; ++i)
v.listElems()[i] = ((Attr *) v.listElems()[i])->value;
@@ -2184,11 +2147,10 @@ void prim_getAttr(EvalState & state, const Pos & pos, Value * * args, Value & v)
{
string attr = state.forceStringNoCtx(*args[0], pos);
state.forceAttrs(*args[1], pos);
- // !!! Should we create a symbol here or just do a lookup?
Bindings::iterator i = getAttr(
state,
"getAttr",
- attr,
+ state.symbols.create(attr),
args[1]->attrs,
pos
);
@@ -2268,21 +2230,25 @@ static void prim_removeAttrs(EvalState & state, const Pos & pos, Value * * args,
state.forceAttrs(*args[0], pos);
state.forceList(*args[1], pos);
- /* Get the attribute names to be removed. */
- std::set<Symbol> names;
+ /* Get the attribute names to be removed.
+ We keep them as Attrs instead of Symbols so std::set_difference
+ can be used to remove them from attrs[0]. */
+ boost::container::small_vector<Attr, 64> names;
+ names.reserve(args[1]->listSize());
for (auto elem : args[1]->listItems()) {
state.forceStringNoCtx(*elem, pos);
- names.insert(state.symbols.create(elem->string.s));
+ names.emplace_back(state.symbols.create(elem->string.s), nullptr);
}
+ std::sort(names.begin(), names.end());
/* Copy all attributes not in that set. Note that we don't need
to sort v.attrs because it's a subset of an already sorted
vector. */
auto attrs = state.buildBindings(args[0]->attrs->size());
- for (auto & i : *args[0]->attrs) {
- if (!names.count(i.name))
- attrs.insert(i);
- }
+ std::set_difference(
+ args[0]->attrs->begin(), args[0]->attrs->end(),
+ names.begin(), names.end(),
+ std::back_inserter(attrs));
v.mkAttrs(attrs.alreadySorted());
}
@@ -3520,18 +3486,20 @@ static RegisterPrimOp primop_match({
/* Split a string with a regular expression, and return a list of the
non-matching parts interleaved by the lists of the matching groups. */
-static void prim_split(EvalState & state, const Pos & pos, Value * * args, Value & v)
+void prim_split(EvalState & state, const Pos & pos, Value * * args, Value & v)
{
auto re = state.forceStringNoCtx(*args[0], pos);
try {
- std::regex regex(re, std::regex::extended);
+ auto regex = state.regexCache->cache.find(re);
+ if (regex == state.regexCache->cache.end())
+ regex = state.regexCache->cache.emplace(re, std::regex(re, std::regex::extended)).first;
PathSet context;
const std::string str = state.forceString(*args[1], context, pos);
- auto begin = std::sregex_iterator(str.begin(), str.end(), regex);
+ auto begin = std::sregex_iterator(str.begin(), str.end(), regex->second);
auto end = std::sregex_iterator();
// Any matches results are surrounded by non-matching results.
@@ -3915,9 +3883,12 @@ void EvalState::createBaseEnv()
/* Note: we have to initialize the 'derivation' constant *after*
building baseEnv/staticBaseEnv because it uses 'builtins'. */
- eval(parse(
+ char code[] =
#include "primops/derivation.nix.gen.hh"
- , foFile, sDerivationNix, "/", staticBaseEnv), *vDerivation);
+ // the parser needs two NUL bytes as terminators; one of them
+ // is implied by being a C string.
+ "\0";
+ eval(parse(code, sizeof(code), foFile, sDerivationNix, "/", staticBaseEnv), *vDerivation);
}
diff --git a/src/libexpr/symbol-table.hh b/src/libexpr/symbol-table.hh
index 4eb6dac81..a090ebae5 100644
--- a/src/libexpr/symbol-table.hh
+++ b/src/libexpr/symbol-table.hh
@@ -1,7 +1,8 @@
#pragma once
+#include <list>
#include <map>
-#include <unordered_set>
+#include <unordered_map>
#include "types.hh"
@@ -70,15 +71,21 @@ public:
class SymbolTable
{
private:
- typedef std::unordered_set<string> Symbols;
- Symbols symbols;
+ std::unordered_map<std::string_view, Symbol> symbols;
+ std::list<string> store;
public:
Symbol create(std::string_view s)
{
- // FIXME: avoid allocation if 's' already exists in the symbol table.
- std::pair<Symbols::iterator, bool> res = symbols.emplace(std::string(s));
- return Symbol(&*res.first);
+ // Most symbols are looked up more than once, so we trade off insertion performance
+ // for lookup performance.
+ // TODO: could probably be done more efficiently with transparent Hash and Equals
+ // on the original implementation using unordered_set
+ auto it = symbols.find(s);
+ if (it != symbols.end()) return it->second;
+
+ const string & rawSym = store.emplace_back(s);
+ return symbols.emplace(rawSym, Symbol(&rawSym)).first->second;
}
size_t size() const
@@ -91,7 +98,7 @@ public:
template<typename T>
void dump(T callback)
{
- for (auto & s : symbols)
+ for (auto & s : store)
callback(s);
}
};
diff --git a/src/libfetchers/tarball.cc b/src/libfetchers/tarball.cc
index 69d020748..c933475ca 100644
--- a/src/libfetchers/tarball.cc
+++ b/src/libfetchers/tarball.cc
@@ -67,18 +67,18 @@ DownloadFileResult downloadFile(
storePath = std::move(cached->storePath);
} else {
StringSink sink;
- dumpString(*res.data, sink);
- auto hash = hashString(htSHA256, *res.data);
+ dumpString(res.data, sink);
+ auto hash = hashString(htSHA256, res.data);
ValidPathInfo info {
store->makeFixedOutputPath(FileIngestionMethod::Flat, hash, name),
- hashString(htSHA256, *sink.s),
+ hashString(htSHA256, sink.s),
};
- info.narSize = sink.s->size();
+ info.narSize = sink.s.size();
info.ca = FixedOutputHash {
.method = FileIngestionMethod::Flat,
.hash = hash,
};
- auto source = StringSource { *sink.s };
+ auto source = StringSource(sink.s);
store->addToStore(info, source, NoRepair, NoCheckSigs);
storePath = std::move(info.path);
}
diff --git a/src/libstore/binary-cache-store.cc b/src/libstore/binary-cache-store.cc
index 13c086a46..6e4458f7a 100644
--- a/src/libstore/binary-cache-store.cc
+++ b/src/libstore/binary-cache-store.cc
@@ -31,7 +31,7 @@ BinaryCacheStore::BinaryCacheStore(const Params & params)
StringSink sink;
sink << narVersionMagic1;
- narMagic = *sink.s;
+ narMagic = sink.s;
}
void BinaryCacheStore::init()
@@ -68,7 +68,7 @@ void BinaryCacheStore::upsertFile(const std::string & path,
}
void BinaryCacheStore::getFile(const std::string & path,
- Callback<std::shared_ptr<std::string>> callback) noexcept
+ Callback<std::optional<std::string>> callback) noexcept
{
try {
callback(getFile(path));
@@ -77,9 +77,9 @@ void BinaryCacheStore::getFile(const std::string & path,
void BinaryCacheStore::getFile(const std::string & path, Sink & sink)
{
- std::promise<std::shared_ptr<std::string>> promise;
+ std::promise<std::optional<std::string>> promise;
getFile(path,
- {[&](std::future<std::shared_ptr<std::string>> result) {
+ {[&](std::future<std::optional<std::string>> result) {
try {
promise.set_value(result.get());
} catch (...) {
@@ -89,15 +89,15 @@ void BinaryCacheStore::getFile(const std::string & path, Sink & sink)
sink(*promise.get_future().get());
}
-std::shared_ptr<std::string> BinaryCacheStore::getFile(const std::string & path)
+std::optional<std::string> BinaryCacheStore::getFile(const std::string & path)
{
StringSink sink;
try {
getFile(path, sink);
} catch (NoSuchBinaryCacheFile &) {
- return nullptr;
+ return std::nullopt;
}
- return sink.s;
+ return std::move(sink.s);
}
std::string BinaryCacheStore::narInfoFileFor(const StorePath & storePath)
@@ -367,11 +367,11 @@ void BinaryCacheStore::queryPathInfoUncached(const StorePath & storePath,
auto callbackPtr = std::make_shared<decltype(callback)>(std::move(callback));
getFile(narInfoFile,
- {[=](std::future<std::shared_ptr<std::string>> fut) {
+ {[=](std::future<std::optional<std::string>> fut) {
try {
auto data = fut.get();
- if (!data) return (*callbackPtr)(nullptr);
+ if (!data) return (*callbackPtr)({});
stats.narInfoRead++;
@@ -429,7 +429,7 @@ StorePath BinaryCacheStore::addTextToStore(const string & name, const string & s
StringSink sink;
dumpString(s, sink);
- auto source = StringSource { *sink.s };
+ StringSource source(sink.s);
return addToStoreCommon(source, repair, CheckSigs, [&](HashResult nar) {
ValidPathInfo info { path, nar.first };
info.narSize = nar.second;
@@ -446,11 +446,11 @@ void BinaryCacheStore::queryRealisationUncached(const DrvOutput & id,
auto callbackPtr = std::make_shared<decltype(callback)>(std::move(callback));
- Callback<std::shared_ptr<std::string>> newCallback = {
- [=](std::future<std::shared_ptr<std::string>> fut) {
+ Callback<std::optional<std::string>> newCallback = {
+ [=](std::future<std::optional<std::string>> fut) {
try {
auto data = fut.get();
- if (!data) return (*callbackPtr)(nullptr);
+ if (!data) return (*callbackPtr)({});
auto realisation = Realisation::fromJSON(
nlohmann::json::parse(*data), outputInfoFilePath);
@@ -490,7 +490,7 @@ void BinaryCacheStore::addSignatures(const StorePath & storePath, const StringSe
writeNarInfo(narInfo);
}
-std::shared_ptr<std::string> BinaryCacheStore::getBuildLog(const StorePath & path)
+std::optional<std::string> BinaryCacheStore::getBuildLog(const StorePath & path)
{
auto drvPath = path;
@@ -498,10 +498,10 @@ std::shared_ptr<std::string> BinaryCacheStore::getBuildLog(const StorePath & pat
try {
auto info = queryPathInfo(path);
// FIXME: add a "Log" field to .narinfo
- if (!info->deriver) return nullptr;
+ if (!info->deriver) return std::nullopt;
drvPath = *info->deriver;
} catch (InvalidPath &) {
- return nullptr;
+ return std::nullopt;
}
}
@@ -512,4 +512,14 @@ std::shared_ptr<std::string> BinaryCacheStore::getBuildLog(const StorePath & pat
return getFile(logPath);
}
+void BinaryCacheStore::addBuildLog(const StorePath & drvPath, std::string_view log)
+{
+ assert(drvPath.isDerivation());
+
+ upsertFile(
+ "log/" + std::string(drvPath.to_string()),
+ (std::string) log, // FIXME: don't copy
+ "text/plain; charset=utf-8");
+}
+
}
diff --git a/src/libstore/binary-cache-store.hh b/src/libstore/binary-cache-store.hh
index 9815af591..7599230d9 100644
--- a/src/libstore/binary-cache-store.hh
+++ b/src/libstore/binary-cache-store.hh
@@ -51,6 +51,7 @@ public:
const std::string & mimeType) = 0;
void upsertFile(const std::string & path,
+ // FIXME: use std::string_view
std::string && data,
const std::string & mimeType);
@@ -62,10 +63,11 @@ public:
/* Fetch the specified file and call the specified callback with
the result. A subclass may implement this asynchronously. */
- virtual void getFile(const std::string & path,
- Callback<std::shared_ptr<std::string>> callback) noexcept;
+ virtual void getFile(
+ const std::string & path,
+ Callback<std::optional<std::string>> callback) noexcept;
- std::shared_ptr<std::string> getFile(const std::string & path);
+ std::optional<std::string> getFile(const std::string & path);
public:
@@ -117,7 +119,9 @@ public:
void addSignatures(const StorePath & storePath, const StringSet & sigs) override;
- std::shared_ptr<std::string> getBuildLog(const StorePath & path) override;
+ std::optional<std::string> getBuildLog(const StorePath & path) override;
+
+ void addBuildLog(const StorePath & drvPath, std::string_view log) override;
};
diff --git a/src/libstore/build/derivation-goal.cc b/src/libstore/build/derivation-goal.cc
index d34e53fe8..151217b8b 100644
--- a/src/libstore/build/derivation-goal.cc
+++ b/src/libstore/build/derivation-goal.cc
@@ -278,7 +278,7 @@ void DerivationGoal::outputsSubstitutionTried()
if (nrFailed > 0 && nrFailed > nrNoSubstituters + nrIncompleteClosure && !settings.tryFallback) {
done(BuildResult::TransientFailure,
- fmt("some substitutes for the outputs of derivation '%s' failed (usually happens due to networking issues); try '--fallback' to build derivation from source ",
+ Error("some substitutes for the outputs of derivation '%s' failed (usually happens due to networking issues); try '--fallback' to build derivation from source ",
worker.store.printStorePath(drvPath)));
return;
}
diff --git a/src/libstore/build/local-derivation-goal.cc b/src/libstore/build/local-derivation-goal.cc
index e4d2add73..0d0afea2d 100644
--- a/src/libstore/build/local-derivation-goal.cc
+++ b/src/libstore/build/local-derivation-goal.cc
@@ -2226,8 +2226,8 @@ void LocalDerivationGoal::registerOutputs()
StringSink sink;
dumpPath(actualPath, sink);
deletePath(actualPath);
- sink.s = make_ref<std::string>(rewriteStrings(*sink.s, outputRewrites));
- StringSource source(*sink.s);
+ sink.s = rewriteStrings(sink.s, outputRewrites);
+ StringSource source(sink.s);
restorePath(actualPath, source);
}
};
@@ -2295,7 +2295,7 @@ void LocalDerivationGoal::registerOutputs()
StringSink sink;
dumpPath(actualPath, sink);
RewritingSink rsink2(oldHashPart, std::string(finalPath.hashPart()), nextSink);
- rsink2(*sink.s);
+ rsink2(sink.s);
rsink2.flush();
});
Path tmpPath = actualPath + ".tmp";
diff --git a/src/libstore/daemon.cc b/src/libstore/daemon.cc
index 59325da79..101aa13a5 100644
--- a/src/libstore/daemon.cc
+++ b/src/libstore/daemon.cc
@@ -69,7 +69,7 @@ struct TunnelLogger : public Logger
StringSink buf;
buf << STDERR_NEXT << (fs.s + "\n");
- enqueueMsg(*buf.s);
+ enqueueMsg(buf.s);
}
void logEI(const ErrorInfo & ei) override
@@ -81,7 +81,7 @@ struct TunnelLogger : public Logger
StringSink buf;
buf << STDERR_NEXT << oss.str();
- enqueueMsg(*buf.s);
+ enqueueMsg(buf.s);
}
/* startWork() means that we're starting an operation for which we
@@ -129,7 +129,7 @@ struct TunnelLogger : public Logger
StringSink buf;
buf << STDERR_START_ACTIVITY << act << lvl << type << s << fields << parent;
- enqueueMsg(*buf.s);
+ enqueueMsg(buf.s);
}
void stopActivity(ActivityId act) override
@@ -137,7 +137,7 @@ struct TunnelLogger : public Logger
if (GET_PROTOCOL_MINOR(clientVersion) < 20) return;
StringSink buf;
buf << STDERR_STOP_ACTIVITY << act;
- enqueueMsg(*buf.s);
+ enqueueMsg(buf.s);
}
void result(ActivityId act, ResultType type, const Fields & fields) override
@@ -145,7 +145,7 @@ struct TunnelLogger : public Logger
if (GET_PROTOCOL_MINOR(clientVersion) < 20) return;
StringSink buf;
buf << STDERR_RESULT << act << type << fields;
- enqueueMsg(*buf.s);
+ enqueueMsg(buf.s);
}
};
@@ -468,10 +468,12 @@ static void performOp(TunnelLogger * logger, ref<Store> store,
dontCheckSigs = false;
logger->startWork();
- FramedSource source(from);
- store->addMultipleToStore(source,
- RepairFlag{repair},
- dontCheckSigs ? NoCheckSigs : CheckSigs);
+ {
+ FramedSource source(from);
+ store->addMultipleToStore(source,
+ RepairFlag{repair},
+ dontCheckSigs ? NoCheckSigs : CheckSigs);
+ }
logger->stopWork();
break;
}
@@ -852,14 +854,14 @@ static void performOp(TunnelLogger * logger, ref<Store> store,
else {
std::unique_ptr<Source> source;
+ StringSink saved;
if (GET_PROTOCOL_MINOR(clientVersion) >= 21)
source = std::make_unique<TunnelSource>(from, to);
else {
- StringSink saved;
TeeSource tee { from, saved };
ParseSink ether;
parseDump(ether, tee);
- source = std::make_unique<StringSource>(std::move(*saved.s));
+ source = std::make_unique<StringSource>(saved.s);
}
logger->startWork();
@@ -920,6 +922,22 @@ static void performOp(TunnelLogger * logger, ref<Store> store,
break;
}
+ case wopAddBuildLog: {
+ StorePath path{readString(from)};
+ logger->startWork();
+ if (!trusted)
+ throw Error("you are not privileged to add logs");
+ {
+ FramedSource source(from);
+ StringSink sink;
+ source.drainInto(sink);
+ store->addBuildLog(path, sink.s);
+ }
+ logger->stopWork();
+ to << 1;
+ break;
+ }
+
default:
throw Error("invalid operation %1%", op);
}
diff --git a/src/libstore/derivations.cc b/src/libstore/derivations.cc
index b926bb711..3e3d50144 100644
--- a/src/libstore/derivations.cc
+++ b/src/libstore/derivations.cc
@@ -4,6 +4,7 @@
#include "util.hh"
#include "worker-protocol.hh"
#include "fs-accessor.hh"
+#include <boost/container/small_vector.hpp>
namespace nix {
@@ -272,7 +273,9 @@ Derivation parseDerivation(const Store & store, std::string && s, std::string_vi
static void printString(string & res, std::string_view s)
{
- char buf[s.size() * 2 + 2];
+ boost::container::small_vector<char, 64 * 1024> buffer;
+ buffer.reserve(s.size() * 2 + 2);
+ char * buf = buffer.data();
char * p = buf;
*p++ = '"';
for (auto c : s)
diff --git a/src/libstore/export-import.cc b/src/libstore/export-import.cc
index 02c839520..9875da909 100644
--- a/src/libstore/export-import.cc
+++ b/src/libstore/export-import.cc
@@ -75,20 +75,20 @@ StorePaths Store::importPaths(Source & source, CheckSigsFlag checkSigs)
auto references = worker_proto::read(*this, source, Phantom<StorePathSet> {});
auto deriver = readString(source);
- auto narHash = hashString(htSHA256, *saved.s);
+ auto narHash = hashString(htSHA256, saved.s);
ValidPathInfo info { path, narHash };
if (deriver != "")
info.deriver = parseStorePath(deriver);
info.references = references;
- info.narSize = saved.s->size();
+ info.narSize = saved.s.size();
// Ignore optional legacy signature.
if (readInt(source) == 1)
readString(source);
// Can't use underlying source, which would have been exhausted
- auto source = StringSource { *saved.s };
+ auto source = StringSource(saved.s);
addToStore(info, source, NoRepair, checkSigs);
res.push_back(info.path);
diff --git a/src/libstore/filetransfer.cc b/src/libstore/filetransfer.cc
index 17980ab22..6b62311cf 100644
--- a/src/libstore/filetransfer.cc
+++ b/src/libstore/filetransfer.cc
@@ -106,7 +106,7 @@ struct curlFileTransfer : public FileTransfer
this->request.dataCallback(data);
}
} else
- this->result.data->append(data);
+ this->result.data.append(data);
})
{
if (!request.expectedETag.empty())
@@ -195,7 +195,7 @@ struct curlFileTransfer : public FileTransfer
std::smatch match;
if (std::regex_match(line, match, statusLine)) {
result.etag = "";
- result.data = std::make_shared<std::string>();
+ result.data.clear();
result.bodySize = 0;
statusMsg = trim(match[1]);
acceptRanges = false;
@@ -340,7 +340,7 @@ struct curlFileTransfer : public FileTransfer
if (writtenToSink)
curl_easy_setopt(req, CURLOPT_RESUME_FROM_LARGE, writtenToSink);
- result.data = std::make_shared<std::string>();
+ result.data.clear();
result.bodySize = 0;
}
@@ -434,21 +434,21 @@ struct curlFileTransfer : public FileTransfer
attempt++;
- std::shared_ptr<std::string> response;
+ std::optional<std::string> response;
if (errorSink)
- response = errorSink->s;
+ response = std::move(errorSink->s);
auto exc =
code == CURLE_ABORTED_BY_CALLBACK && _isInterrupted
- ? FileTransferError(Interrupted, response, "%s of '%s' was interrupted", request.verb(), request.uri)
+ ? FileTransferError(Interrupted, std::move(response), "%s of '%s' was interrupted", request.verb(), request.uri)
: httpStatus != 0
? FileTransferError(err,
- response,
+ std::move(response),
fmt("unable to %s '%s': HTTP error %d ('%s')",
request.verb(), request.uri, httpStatus, statusMsg)
+ (code == CURLE_OK ? "" : fmt(" (curl error: %s)", curl_easy_strerror(code)))
)
: FileTransferError(err,
- response,
+ std::move(response),
fmt("unable to %s '%s': %s (%d)",
request.verb(), request.uri, curl_easy_strerror(code), code));
@@ -705,7 +705,7 @@ struct curlFileTransfer : public FileTransfer
FileTransferResult res;
if (!s3Res.data)
throw FileTransferError(NotFound, nullptr, "S3 object '%s' does not exist", request.uri);
- res.data = s3Res.data;
+ res.data = std::move(*s3Res.data);
callback(std::move(res));
#else
throw nix::Error("cannot download '%s' because Nix is not built with S3 support", request.uri);
@@ -859,7 +859,7 @@ void FileTransfer::download(FileTransferRequest && request, Sink & sink)
}
template<typename... Args>
-FileTransferError::FileTransferError(FileTransfer::Error error, std::shared_ptr<string> response, const Args & ... args)
+FileTransferError::FileTransferError(FileTransfer::Error error, std::optional<std::string> response, const Args & ... args)
: Error(args...), error(error), response(response)
{
const auto hf = hintfmt(args...);
diff --git a/src/libstore/filetransfer.hh b/src/libstore/filetransfer.hh
index 45d9ccf89..3e61b23b1 100644
--- a/src/libstore/filetransfer.hh
+++ b/src/libstore/filetransfer.hh
@@ -59,7 +59,7 @@ struct FileTransferRequest
unsigned int baseRetryTimeMs = 250;
ActivityId parentAct;
bool decompress = true;
- std::shared_ptr<std::string> data;
+ std::optional<std::string> data;
std::string mimeType;
std::function<void(std::string_view data)> dataCallback;
@@ -77,7 +77,7 @@ struct FileTransferResult
bool cached = false;
std::string etag;
std::string effectiveUri;
- std::shared_ptr<std::string> data;
+ std::string data;
uint64_t bodySize = 0;
};
@@ -119,10 +119,10 @@ class FileTransferError : public Error
{
public:
FileTransfer::Error error;
- std::shared_ptr<string> response; // intentionally optional
+ std::optional<string> response; // intentionally optional
template<typename... Args>
- FileTransferError(FileTransfer::Error error, std::shared_ptr<string> response, const Args & ... args);
+ FileTransferError(FileTransfer::Error error, std::optional<string> response, const Args & ... args);
virtual const char* sname() const override { return "FileTransferError"; }
};
diff --git a/src/libstore/globals.hh b/src/libstore/globals.hh
index 1911ec855..f65893b10 100644
--- a/src/libstore/globals.hh
+++ b/src/libstore/globals.hh
@@ -966,6 +966,13 @@ public:
Setting<bool> acceptFlakeConfig{this, false, "accept-flake-config",
"Whether to accept nix configuration from a flake without prompting."};
+
+ Setting<std::string> commitLockFileSummary{
+ this, "", "commit-lockfile-summary",
+ R"(
+ The commit summary to use when commiting changed flake lock files. If
+ empty, the summary is generated based on the action performed.
+ )"};
};
diff --git a/src/libstore/http-binary-cache-store.cc b/src/libstore/http-binary-cache-store.cc
index 605ec4b28..3cb5efdbf 100644
--- a/src/libstore/http-binary-cache-store.cc
+++ b/src/libstore/http-binary-cache-store.cc
@@ -126,7 +126,7 @@ protected:
const std::string & mimeType) override
{
auto req = makeRequest(path);
- req.data = std::make_shared<string>(StreamToSourceAdapter(istream).drain());
+ req.data = StreamToSourceAdapter(istream).drain();
req.mimeType = mimeType;
try {
getFileTransfer()->upload(req);
@@ -159,7 +159,7 @@ protected:
}
void getFile(const std::string & path,
- Callback<std::shared_ptr<std::string>> callback) noexcept override
+ Callback<std::optional<std::string>> callback) noexcept override
{
checkEnabled();
@@ -170,10 +170,10 @@ protected:
getFileTransfer()->enqueueFileTransfer(request,
{[callbackPtr, this](std::future<FileTransferResult> result) {
try {
- (*callbackPtr)(result.get().data);
+ (*callbackPtr)(std::move(result.get().data));
} catch (FileTransferError & e) {
if (e.error == FileTransfer::NotFound || e.error == FileTransfer::Forbidden)
- return (*callbackPtr)(std::shared_ptr<std::string>());
+ return (*callbackPtr)({});
maybeDisable();
callbackPtr->rethrow();
} catch (...) {
diff --git a/src/libstore/legacy-ssh-store.cc b/src/libstore/legacy-ssh-store.cc
index 4861d185e..f8b2662af 100644
--- a/src/libstore/legacy-ssh-store.cc
+++ b/src/libstore/legacy-ssh-store.cc
@@ -94,7 +94,7 @@ struct LegacySSHStore : public virtual LegacySSHStoreConfig, public virtual Stor
conn->sshConn->in.close();
auto msg = conn->from.drain();
throw Error("'nix-store --serve' protocol mismatch from '%s', got '%s'",
- host, chomp(*saved.s + msg));
+ host, chomp(saved.s + msg));
}
conn->remoteVersion = readInt(conn->from);
if (GET_PROTOCOL_MAJOR(conn->remoteVersion) != 0x200)
diff --git a/src/libstore/local-binary-cache-store.cc b/src/libstore/local-binary-cache-store.cc
index f93111fce..f754770f9 100644
--- a/src/libstore/local-binary-cache-store.cc
+++ b/src/libstore/local-binary-cache-store.cc
@@ -96,6 +96,7 @@ void LocalBinaryCacheStore::init()
createDirs(binaryCacheDir + "/" + realisationsPrefix);
if (writeDebugInfo)
createDirs(binaryCacheDir + "/debuginfo");
+ createDirs(binaryCacheDir + "/log");
BinaryCacheStore::init();
}
diff --git a/src/libstore/local-fs-store.cc b/src/libstore/local-fs-store.cc
index 6de13c73a..c933251db 100644
--- a/src/libstore/local-fs-store.cc
+++ b/src/libstore/local-fs-store.cc
@@ -87,34 +87,32 @@ void LocalFSStore::narFromPath(const StorePath & path, Sink & sink)
const string LocalFSStore::drvsLogDir = "drvs";
-
-
-std::shared_ptr<std::string> LocalFSStore::getBuildLog(const StorePath & path_)
+std::optional<std::string> LocalFSStore::getBuildLog(const StorePath & path_)
{
auto path = path_;
if (!path.isDerivation()) {
try {
auto info = queryPathInfo(path);
- if (!info->deriver) return nullptr;
+ if (!info->deriver) return std::nullopt;
path = *info->deriver;
} catch (InvalidPath &) {
- return nullptr;
+ return std::nullopt;
}
}
- auto baseName = std::string(baseNameOf(printStorePath(path)));
+ auto baseName = path.to_string();
for (int j = 0; j < 2; j++) {
Path logPath =
j == 0
- ? fmt("%s/%s/%s/%s", logDir, drvsLogDir, string(baseName, 0, 2), string(baseName, 2))
+ ? fmt("%s/%s/%s/%s", logDir, drvsLogDir, baseName.substr(0, 2), baseName.substr(2))
: fmt("%s/%s/%s", logDir, drvsLogDir, baseName);
Path logBz2Path = logPath + ".bz2";
if (pathExists(logPath))
- return std::make_shared<std::string>(readFile(logPath));
+ return readFile(logPath);
else if (pathExists(logBz2Path)) {
try {
@@ -124,7 +122,7 @@ std::shared_ptr<std::string> LocalFSStore::getBuildLog(const StorePath & path_)
}
- return nullptr;
+ return std::nullopt;
}
}
diff --git a/src/libstore/local-fs-store.hh b/src/libstore/local-fs-store.hh
index f8b19d00d..e44b27cc2 100644
--- a/src/libstore/local-fs-store.hh
+++ b/src/libstore/local-fs-store.hh
@@ -45,7 +45,8 @@ public:
return getRealStoreDir() + "/" + std::string(storePath, storeDir.size() + 1);
}
- std::shared_ptr<std::string> getBuildLog(const StorePath & path) override;
+ std::optional<std::string> getBuildLog(const StorePath & path) override;
+
};
}
diff --git a/src/libstore/local-store.cc b/src/libstore/local-store.cc
index 9ebdfd6ed..1807940d8 100644
--- a/src/libstore/local-store.cc
+++ b/src/libstore/local-store.cc
@@ -9,6 +9,7 @@
#include "callback.hh"
#include "topo-sort.hh"
#include "finally.hh"
+#include "compression.hh"
#include <iostream>
#include <algorithm>
@@ -1461,12 +1462,12 @@ StorePath LocalStore::addTextToStore(const string & name, const string & s,
StringSink sink;
dumpString(s, sink);
- auto narHash = hashString(htSHA256, *sink.s);
+ auto narHash = hashString(htSHA256, sink.s);
optimisePath(realPath, repair);
ValidPathInfo info { dstPath, narHash };
- info.narSize = sink.s->size();
+ info.narSize = sink.s.size();
info.references = references;
info.ca = TextHash { .hash = hash };
registerValidPath(info);
@@ -1898,4 +1899,24 @@ FixedOutputHash LocalStore::hashCAPath(
};
}
+void LocalStore::addBuildLog(const StorePath & drvPath, std::string_view log)
+{
+ assert(drvPath.isDerivation());
+
+ auto baseName = drvPath.to_string();
+
+ auto logPath = fmt("%s/%s/%s/%s.bz2", logDir, drvsLogDir, baseName.substr(0, 2), baseName.substr(2));
+
+ if (pathExists(logPath)) return;
+
+ createDirs(dirOf(logPath));
+
+ auto tmpFile = fmt("%s.tmp.%d", logPath, getpid());
+
+ writeFile(tmpFile, compress("bzip2", log));
+
+ if (rename(tmpFile.c_str(), logPath.c_str()) != 0)
+ throw SysError("renaming '%1%' to '%2%'", tmpFile, logPath);
+}
+
} // namespace nix
diff --git a/src/libstore/local-store.hh b/src/libstore/local-store.hh
index c4d7b80bd..6d867d778 100644
--- a/src/libstore/local-store.hh
+++ b/src/libstore/local-store.hh
@@ -280,6 +280,8 @@ private:
const std::string_view pathHash
);
+ void addBuildLog(const StorePath & drvPath, std::string_view log) override;
+
friend struct LocalDerivationGoal;
friend struct PathSubstitutionGoal;
friend struct SubstitutionGoal;
diff --git a/src/libstore/nar-accessor.cc b/src/libstore/nar-accessor.cc
index 784ebb719..7d27d7667 100644
--- a/src/libstore/nar-accessor.cc
+++ b/src/libstore/nar-accessor.cc
@@ -28,7 +28,7 @@ struct NarMember
struct NarAccessor : public FSAccessor
{
- std::shared_ptr<const std::string> nar;
+ std::optional<const std::string> nar;
GetNarBytes getNarBytes;
@@ -104,7 +104,7 @@ struct NarAccessor : public FSAccessor
}
};
- NarAccessor(ref<const std::string> nar) : nar(nar)
+ NarAccessor(std::string && _nar) : nar(_nar)
{
StringSource source(*nar);
NarIndexer indexer(*this, source);
@@ -224,9 +224,9 @@ struct NarAccessor : public FSAccessor
}
};
-ref<FSAccessor> makeNarAccessor(ref<const std::string> nar)
+ref<FSAccessor> makeNarAccessor(std::string && nar)
{
- return make_ref<NarAccessor>(nar);
+ return make_ref<NarAccessor>(std::move(nar));
}
ref<FSAccessor> makeNarAccessor(Source & source)
diff --git a/src/libstore/nar-accessor.hh b/src/libstore/nar-accessor.hh
index 8af1272f6..c2241a04c 100644
--- a/src/libstore/nar-accessor.hh
+++ b/src/libstore/nar-accessor.hh
@@ -10,7 +10,7 @@ struct Source;
/* Return an object that provides access to the contents of a NAR
file. */
-ref<FSAccessor> makeNarAccessor(ref<const std::string> nar);
+ref<FSAccessor> makeNarAccessor(std::string && nar);
ref<FSAccessor> makeNarAccessor(Source & source);
diff --git a/src/libstore/realisation.cc b/src/libstore/realisation.cc
index f871e6437..d63ec5ea2 100644
--- a/src/libstore/realisation.cc
+++ b/src/libstore/realisation.cc
@@ -78,7 +78,7 @@ Realisation Realisation::fromJSON(
auto fieldIterator = json.find(fieldName);
if (fieldIterator == json.end())
return std::nullopt;
- return *fieldIterator;
+ return {*fieldIterator};
};
auto getField = [&](std::string fieldName) -> std::string {
if (auto field = getOptionalField(fieldName))
diff --git a/src/libstore/remote-fs-accessor.cc b/src/libstore/remote-fs-accessor.cc
index f43456f0b..0ce335646 100644
--- a/src/libstore/remote-fs-accessor.cc
+++ b/src/libstore/remote-fs-accessor.cc
@@ -22,9 +22,18 @@ Path RemoteFSAccessor::makeCacheFile(std::string_view hashPart, const std::strin
return fmt("%s/%s.%s", cacheDir, hashPart, ext);
}
-void RemoteFSAccessor::addToCache(std::string_view hashPart, const std::string & nar,
- ref<FSAccessor> narAccessor)
+ref<FSAccessor> RemoteFSAccessor::addToCache(std::string_view hashPart, std::string && nar)
{
+ if (cacheDir != "") {
+ try {
+ /* FIXME: do this asynchronously. */
+ writeFile(makeCacheFile(hashPart, "nar"), nar);
+ } catch (...) {
+ ignoreException();
+ }
+ }
+
+ auto narAccessor = makeNarAccessor(std::move(nar));
nars.emplace(hashPart, narAccessor);
if (cacheDir != "") {
@@ -33,14 +42,12 @@ void RemoteFSAccessor::addToCache(std::string_view hashPart, const std::string &
JSONPlaceholder jsonRoot(str);
listNar(jsonRoot, narAccessor, "", true);
writeFile(makeCacheFile(hashPart, "ls"), str.str());
-
- /* FIXME: do this asynchronously. */
- writeFile(makeCacheFile(hashPart, "nar"), nar);
-
} catch (...) {
ignoreException();
}
}
+
+ return narAccessor;
}
std::pair<ref<FSAccessor>, Path> RemoteFSAccessor::fetch(const Path & path_, bool requireValidPath)
@@ -55,7 +62,6 @@ std::pair<ref<FSAccessor>, Path> RemoteFSAccessor::fetch(const Path & path_, boo
auto i = nars.find(std::string(storePath.hashPart()));
if (i != nars.end()) return {i->second, restPath};
- StringSink sink;
std::string listing;
Path cacheFile;
@@ -86,19 +92,15 @@ std::pair<ref<FSAccessor>, Path> RemoteFSAccessor::fetch(const Path & path_, boo
} catch (SysError &) { }
try {
- *sink.s = nix::readFile(cacheFile);
-
- auto narAccessor = makeNarAccessor(sink.s);
+ auto narAccessor = makeNarAccessor(nix::readFile(cacheFile));
nars.emplace(storePath.hashPart(), narAccessor);
return {narAccessor, restPath};
-
} catch (SysError &) { }
}
+ StringSink sink;
store->narFromPath(storePath, sink);
- auto narAccessor = makeNarAccessor(sink.s);
- addToCache(storePath.hashPart(), *sink.s, narAccessor);
- return {narAccessor, restPath};
+ return {addToCache(storePath.hashPart(), std::move(sink.s)), restPath};
}
FSAccessor::Stat RemoteFSAccessor::stat(const Path & path)
diff --git a/src/libstore/remote-fs-accessor.hh b/src/libstore/remote-fs-accessor.hh
index 594852d0e..99f5544ef 100644
--- a/src/libstore/remote-fs-accessor.hh
+++ b/src/libstore/remote-fs-accessor.hh
@@ -20,8 +20,7 @@ class RemoteFSAccessor : public FSAccessor
Path makeCacheFile(std::string_view hashPart, const std::string & ext);
- void addToCache(std::string_view hashPart, const std::string & nar,
- ref<FSAccessor> narAccessor);
+ ref<FSAccessor> addToCache(std::string_view hashPart, std::string && nar);
public:
diff --git a/src/libstore/remote-store.cc b/src/libstore/remote-store.cc
index 57cc260b0..aac2965e0 100644
--- a/src/libstore/remote-store.cc
+++ b/src/libstore/remote-store.cc
@@ -172,7 +172,7 @@ void RemoteStore::initConnection(Connection & conn)
it. */
conn.closeWrite();
auto msg = conn.from.drain();
- throw Error("protocol mismatch, got '%s'", chomp(*saved.s + msg));
+ throw Error("protocol mismatch, got '%s'", chomp(saved.s + msg));
}
conn.from >> conn.daemonVersion;
@@ -908,6 +908,18 @@ void RemoteStore::queryMissing(const std::vector<DerivedPath> & targets,
}
+void RemoteStore::addBuildLog(const StorePath & drvPath, std::string_view log)
+{
+ auto conn(getConnection());
+ conn->to << wopAddBuildLog << drvPath.to_string();
+ StringSource source(log);
+ conn.withFramedSink([&](Sink & sink) {
+ source.drainInto(sink);
+ });
+ readInt(conn->from);
+}
+
+
void RemoteStore::connect()
{
auto conn(getConnection());
diff --git a/src/libstore/remote-store.hh b/src/libstore/remote-store.hh
index 0fd67f371..4754ff45a 100644
--- a/src/libstore/remote-store.hh
+++ b/src/libstore/remote-store.hh
@@ -116,6 +116,8 @@ public:
StorePathSet & willBuild, StorePathSet & willSubstitute, StorePathSet & unknown,
uint64_t & downloadSize, uint64_t & narSize) override;
+ void addBuildLog(const StorePath & drvPath, std::string_view log) override;
+
void connect() override;
unsigned int getProtocol() override;
diff --git a/src/libstore/s3-binary-cache-store.cc b/src/libstore/s3-binary-cache-store.cc
index 7accad7f4..a024e971d 100644
--- a/src/libstore/s3-binary-cache-store.cc
+++ b/src/libstore/s3-binary-cache-store.cc
@@ -385,7 +385,7 @@ struct S3BinaryCacheStoreImpl : virtual S3BinaryCacheStoreConfig, public virtual
auto compress = [&](std::string compression)
{
auto compressed = nix::compress(compression, StreamToSourceAdapter(istream).drain());
- return std::make_shared<std::stringstream>(std::move(*compressed));
+ return std::make_shared<std::stringstream>(std::move(compressed));
};
if (narinfoCompression != "" && hasSuffix(path, ".narinfo"))
diff --git a/src/libstore/s3.hh b/src/libstore/s3.hh
index 2042bffcf..3f55c74db 100644
--- a/src/libstore/s3.hh
+++ b/src/libstore/s3.hh
@@ -4,6 +4,8 @@
#include "ref.hh"
+#include <optional>
+
namespace Aws { namespace Client { class ClientConfiguration; } }
namespace Aws { namespace S3 { class S3Client; } }
@@ -20,7 +22,7 @@ struct S3Helper
struct FileTransferResult
{
- std::shared_ptr<std::string> data;
+ std::optional<std::string> data;
unsigned int durationMs;
};
diff --git a/src/libstore/store-api.cc b/src/libstore/store-api.cc
index aab4ce94c..84767e917 100644
--- a/src/libstore/store-api.cc
+++ b/src/libstore/store-api.cc
@@ -1109,6 +1109,21 @@ void copyClosure(
copyPaths(srcStore, dstStore, closure, repair, checkSigs, substitute);
}
+void copyClosure(
+ Store & srcStore,
+ Store & dstStore,
+ const StorePathSet & storePaths,
+ RepairFlag repair,
+ CheckSigsFlag checkSigs,
+ SubstituteFlag substitute)
+{
+ if (&srcStore == &dstStore) return;
+
+ StorePathSet closure;
+ srcStore.computeFSClosure(storePaths, closure);
+ copyPaths(srcStore, dstStore, closure, repair, checkSigs, substitute);
+}
+
std::optional<ValidPathInfo> decodeValidPathInfo(const Store & store, std::istream & str, std::optional<HashResult> hashGiven)
{
std::string path;
diff --git a/src/libstore/store-api.hh b/src/libstore/store-api.hh
index 3dd446f23..8306509f3 100644
--- a/src/libstore/store-api.hh
+++ b/src/libstore/store-api.hh
@@ -724,8 +724,11 @@ public:
/* Return the build log of the specified store path, if available,
or null otherwise. */
- virtual std::shared_ptr<std::string> getBuildLog(const StorePath & path)
- { return nullptr; }
+ virtual std::optional<std::string> getBuildLog(const StorePath & path)
+ { return std::nullopt; }
+
+ virtual void addBuildLog(const StorePath & path, std::string_view log)
+ { unsupported("addBuildLog"); }
/* Hack to allow long-running processes like hydra-queue-runner to
occasionally flush their path info cache. */
@@ -812,6 +815,13 @@ void copyClosure(
CheckSigsFlag checkSigs = CheckSigs,
SubstituteFlag substitute = NoSubstitute);
+void copyClosure(
+ Store & srcStore, Store & dstStore,
+ const StorePathSet & paths,
+ RepairFlag repair = NoRepair,
+ CheckSigsFlag checkSigs = CheckSigs,
+ SubstituteFlag substitute = NoSubstitute);
+
/* Remove the temporary roots file for this process. Any temporary
root becomes garbage after this point unless it has been registered
as a (permanent) root. */
diff --git a/src/libstore/worker-protocol.hh b/src/libstore/worker-protocol.hh
index 93cf546d2..ecf42a5d0 100644
--- a/src/libstore/worker-protocol.hh
+++ b/src/libstore/worker-protocol.hh
@@ -56,6 +56,7 @@ typedef enum {
wopRegisterDrvOutput = 42,
wopQueryRealisation = 43,
wopAddMultipleToStore = 44,
+ wopAddBuildLog = 45,
} WorkerOp;
diff --git a/src/libutil/compression.cc b/src/libutil/compression.cc
index f80ca664c..89180e7a7 100644
--- a/src/libutil/compression.cc
+++ b/src/libutil/compression.cc
@@ -190,13 +190,13 @@ struct BrotliDecompressionSink : ChunkedCompressionSink
}
};
-ref<std::string> decompress(const std::string & method, const std::string & in)
+std::string decompress(const std::string & method, std::string_view in)
{
StringSink ssink;
auto sink = makeDecompressionSink(method, ssink);
(*sink)(in);
sink->finish();
- return ssink.s;
+ return std::move(ssink.s);
}
std::unique_ptr<FinishSink> makeDecompressionSink(const std::string & method, Sink & nextSink)
@@ -281,13 +281,13 @@ ref<CompressionSink> makeCompressionSink(const std::string & method, Sink & next
throw UnknownCompressionMethod("unknown compression method '%s'", method);
}
-ref<std::string> compress(const std::string & method, const std::string & in, const bool parallel, int level)
+std::string compress(const std::string & method, std::string_view in, const bool parallel, int level)
{
StringSink ssink;
auto sink = makeCompressionSink(method, ssink, parallel, level);
(*sink)(in);
sink->finish();
- return ssink.s;
+ return std::move(ssink.s);
}
}
diff --git a/src/libutil/compression.hh b/src/libutil/compression.hh
index 9b1e4a9d4..c470b82a5 100644
--- a/src/libutil/compression.hh
+++ b/src/libutil/compression.hh
@@ -15,11 +15,11 @@ struct CompressionSink : BufferedSink, FinishSink
using FinishSink::finish;
};
-ref<std::string> decompress(const std::string & method, const std::string & in);
+std::string decompress(const std::string & method, std::string_view in);
std::unique_ptr<FinishSink> makeDecompressionSink(const std::string & method, Sink & nextSink);
-ref<std::string> compress(const std::string & method, const std::string & in, const bool parallel = false, int level = -1);
+std::string compress(const std::string & method, std::string_view in, const bool parallel = false, int level = -1);
ref<CompressionSink> makeCompressionSink(const std::string & method, Sink & nextSink, const bool parallel = false, int level = -1);
diff --git a/src/libutil/error.hh b/src/libutil/error.hh
index ff58d3e00..6fe5e4857 100644
--- a/src/libutil/error.hh
+++ b/src/libutil/error.hh
@@ -137,7 +137,7 @@ public:
{ }
template<typename... Args>
- BaseError(const std::string & fs, const Args & ... args)
+ explicit BaseError(const std::string & fs, const Args & ... args)
: err { .level = lvlError, .msg = hintfmt(fs, args...) }
{ }
diff --git a/src/libutil/fmt.cc b/src/libutil/fmt.cc
new file mode 100644
index 000000000..3dd93d73e
--- /dev/null
+++ b/src/libutil/fmt.cc
@@ -0,0 +1,46 @@
+#include "fmt.hh"
+
+#include <regex>
+
+namespace nix {
+
+std::string hiliteMatches(
+ std::string_view s,
+ std::vector<std::smatch> matches,
+ std::string_view prefix,
+ std::string_view postfix)
+{
+ // Avoid copy on zero matches
+ if (matches.size() == 0)
+ return (std::string) s;
+
+ std::sort(matches.begin(), matches.end(), [](const auto & a, const auto & b) {
+ return a.position() < b.position();
+ });
+
+ std::string out;
+ ssize_t last_end = 0;
+
+ for (auto it = matches.begin(); it != matches.end();) {
+ auto m = *it;
+ size_t start = m.position();
+ out.append(s.substr(last_end, m.position() - last_end));
+ // Merge continous matches
+ ssize_t end = start + m.length();
+ while (++it != matches.end() && (*it).position() <= end) {
+ auto n = *it;
+ ssize_t nend = start + (n.position() - start + n.length());
+ if (nend > end)
+ end = nend;
+ }
+ out.append(prefix);
+ out.append(s.substr(start, end - start));
+ out.append(postfix);
+ last_end = end;
+ }
+
+ out.append(s.substr(last_end));
+ return out;
+}
+
+}
diff --git a/src/libutil/fmt.hh b/src/libutil/fmt.hh
index fd335b811..a7126fb65 100644
--- a/src/libutil/fmt.hh
+++ b/src/libutil/fmt.hh
@@ -2,6 +2,7 @@
#include <boost/format.hpp>
#include <string>
+#include <regex>
#include "ansicolor.hh"
@@ -154,4 +155,16 @@ inline hintformat hintfmt(std::string plain_string)
// we won't be receiving any args in this case, so just print the original string
return hintfmt("%s", normaltxt(plain_string));
}
+
+/* Highlight all the given matches in the given string `s` by wrapping
+ them between `prefix` and `postfix`.
+
+ If some matches overlap, then their union will be wrapped rather
+ than the individual matches. */
+std::string hiliteMatches(
+ std::string_view s,
+ std::vector<std::smatch> matches,
+ std::string_view prefix,
+ std::string_view postfix);
+
}
diff --git a/src/libutil/serialise.cc b/src/libutil/serialise.cc
index 16f3476c2..fe703de06 100644
--- a/src/libutil/serialise.cc
+++ b/src/libutil/serialise.cc
@@ -110,7 +110,7 @@ std::string Source::drain()
{
StringSink s;
drainInto(s);
- return *s.s;
+ return std::move(s.s);
}
@@ -325,7 +325,7 @@ void writeString(std::string_view data, Sink & sink)
}
-Sink & operator << (Sink & sink, const string & s)
+Sink & operator << (Sink & sink, std::string_view s)
{
writeString(s, sink);
return sink;
@@ -450,11 +450,11 @@ Error readError(Source & source)
void StringSink::operator () (std::string_view data)
{
static bool warned = false;
- if (!warned && s->size() > threshold) {
+ if (!warned && s.size() > threshold) {
warnLargeDump();
warned = true;
}
- s->append(data);
+ s.append(data);
}
size_t ChainSource::read(char * data, size_t len)
diff --git a/src/libutil/serialise.hh b/src/libutil/serialise.hh
index 0fe6e8332..fdd35aa00 100644
--- a/src/libutil/serialise.hh
+++ b/src/libutil/serialise.hh
@@ -154,12 +154,13 @@ private:
/* A sink that writes data to a string. */
struct StringSink : Sink
{
- ref<std::string> s;
- StringSink() : s(make_ref<std::string>()) { };
- explicit StringSink(const size_t reservedSize) : s(make_ref<std::string>()) {
- s->reserve(reservedSize);
+ std::string s;
+ StringSink() { }
+ explicit StringSink(const size_t reservedSize)
+ {
+ s.reserve(reservedSize);
};
- StringSink(ref<std::string> s) : s(s) { };
+ StringSink(std::string && s) : s(std::move(s)) { };
void operator () (std::string_view data) override;
};
@@ -167,9 +168,9 @@ struct StringSink : Sink
/* A source that reads data from a string. */
struct StringSource : Source
{
- const string & s;
+ std::string_view s;
size_t pos;
- StringSource(const string & _s) : s(_s), pos(0) { }
+ StringSource(std::string_view s) : s(s), pos(0) { }
size_t read(char * data, size_t len) override;
};
@@ -317,10 +318,10 @@ inline Sink & operator << (Sink & sink, uint64_t n)
return sink;
}
-Sink & operator << (Sink & sink, const string & s);
+Sink & operator << (Sink & in, const Error & ex);
+Sink & operator << (Sink & sink, std::string_view s);
Sink & operator << (Sink & sink, const Strings & s);
Sink & operator << (Sink & sink, const StringSet & s);
-Sink & operator << (Sink & in, const Error & ex);
MakeError(SerialisationError, Error);
diff --git a/src/libutil/tests/compression.cc b/src/libutil/tests/compression.cc
index 2efa3266b..bbbf3500f 100644
--- a/src/libutil/tests/compression.cc
+++ b/src/libutil/tests/compression.cc
@@ -12,17 +12,17 @@ namespace nix {
}
TEST(compress, noneMethodDoesNothingToTheInput) {
- ref<std::string> o = compress("none", "this-is-a-test");
+ auto o = compress("none", "this-is-a-test");
- ASSERT_EQ(*o, "this-is-a-test");
+ ASSERT_EQ(o, "this-is-a-test");
}
TEST(decompress, decompressNoneCompressed) {
auto method = "none";
auto str = "slfja;sljfklsa;jfklsjfkl;sdjfkl;sadjfkl;sdjf;lsdfjsadlf";
- ref<std::string> o = decompress(method, str);
+ auto o = decompress(method, str);
- ASSERT_EQ(*o, str);
+ ASSERT_EQ(o, str);
}
TEST(decompress, decompressEmptyCompressed) {
@@ -30,33 +30,33 @@ namespace nix {
// (Content-Encoding == "").
auto method = "";
auto str = "slfja;sljfklsa;jfklsjfkl;sdjfkl;sadjfkl;sdjf;lsdfjsadlf";
- ref<std::string> o = decompress(method, str);
+ auto o = decompress(method, str);
- ASSERT_EQ(*o, str);
+ ASSERT_EQ(o, str);
}
TEST(decompress, decompressXzCompressed) {
auto method = "xz";
auto str = "slfja;sljfklsa;jfklsjfkl;sdjfkl;sadjfkl;sdjf;lsdfjsadlf";
- ref<std::string> o = decompress(method, *compress(method, str));
+ auto o = decompress(method, compress(method, str));
- ASSERT_EQ(*o, str);
+ ASSERT_EQ(o, str);
}
TEST(decompress, decompressBzip2Compressed) {
auto method = "bzip2";
auto str = "slfja;sljfklsa;jfklsjfkl;sdjfkl;sadjfkl;sdjf;lsdfjsadlf";
- ref<std::string> o = decompress(method, *compress(method, str));
+ auto o = decompress(method, compress(method, str));
- ASSERT_EQ(*o, str);
+ ASSERT_EQ(o, str);
}
TEST(decompress, decompressBrCompressed) {
auto method = "br";
auto str = "slfja;sljfklsa;jfklsjfkl;sdjfkl;sadjfkl;sdjf;lsdfjsadlf";
- ref<std::string> o = decompress(method, *compress(method, str));
+ auto o = decompress(method, compress(method, str));
- ASSERT_EQ(*o, str);
+ ASSERT_EQ(o, str);
}
TEST(decompress, decompressInvalidInputThrowsCompressionError) {
@@ -77,7 +77,7 @@ namespace nix {
(*sink)(inputString);
sink->finish();
- ASSERT_STREQ((*strSink.s).c_str(), inputString);
+ ASSERT_STREQ(strSink.s.c_str(), inputString);
}
TEST(makeCompressionSink, compressAndDecompress) {
@@ -90,7 +90,7 @@ namespace nix {
sink->finish();
decompressionSink->finish();
- ASSERT_STREQ((*strSink.s).c_str(), inputString);
+ ASSERT_STREQ(strSink.s.c_str(), inputString);
}
}
diff --git a/src/libutil/tests/fmt.cc b/src/libutil/tests/fmt.cc
new file mode 100644
index 000000000..33772162c
--- /dev/null
+++ b/src/libutil/tests/fmt.cc
@@ -0,0 +1,68 @@
+#include "fmt.hh"
+
+#include <gtest/gtest.h>
+
+#include <regex>
+
+namespace nix {
+/* ----------- tests for fmt.hh -------------------------------------------------*/
+
+ TEST(hiliteMatches, noHighlight) {
+ ASSERT_STREQ(hiliteMatches("Hello, world!", std::vector<std::smatch>(), "(", ")").c_str(), "Hello, world!");
+ }
+
+ TEST(hiliteMatches, simpleHighlight) {
+ std::string str = "Hello, world!";
+ std::regex re = std::regex("world");
+ auto matches = std::vector(std::sregex_iterator(str.begin(), str.end(), re), std::sregex_iterator());
+ ASSERT_STREQ(
+ hiliteMatches(str, matches, "(", ")").c_str(),
+ "Hello, (world)!"
+ );
+ }
+
+ TEST(hiliteMatches, multipleMatches) {
+ std::string str = "Hello, world, world, world, world, world, world, Hello!";
+ std::regex re = std::regex("world");
+ auto matches = std::vector(std::sregex_iterator(str.begin(), str.end(), re), std::sregex_iterator());
+ ASSERT_STREQ(
+ hiliteMatches(str, matches, "(", ")").c_str(),
+ "Hello, (world), (world), (world), (world), (world), (world), Hello!"
+ );
+ }
+
+ TEST(hiliteMatches, overlappingMatches) {
+ std::string str = "world, Hello, world, Hello, world, Hello, world, Hello, world!";
+ std::regex re = std::regex("Hello, world");
+ std::regex re2 = std::regex("world, Hello");
+ auto v = std::vector(std::sregex_iterator(str.begin(), str.end(), re), std::sregex_iterator());
+ for(auto it = std::sregex_iterator(str.begin(), str.end(), re2); it != std::sregex_iterator(); ++it) {
+ v.push_back(*it);
+ }
+ ASSERT_STREQ(
+ hiliteMatches(str, v, "(", ")").c_str(),
+ "(world, Hello, world, Hello, world, Hello, world, Hello, world)!"
+ );
+ }
+
+ TEST(hiliteMatches, complexOverlappingMatches) {
+ std::string str = "legacyPackages.x86_64-linux.git-crypt";
+ std::vector regexes = {
+ std::regex("t-cry"),
+ std::regex("ux\\.git-cry"),
+ std::regex("git-c"),
+ std::regex("pt"),
+ };
+ std::vector<std::smatch> matches;
+ for(auto regex : regexes)
+ {
+ for(auto it = std::sregex_iterator(str.begin(), str.end(), regex); it != std::sregex_iterator(); ++it) {
+ matches.push_back(*it);
+ }
+ }
+ ASSERT_STREQ(
+ hiliteMatches(str, matches, "(", ")").c_str(),
+ "legacyPackages.x86_64-lin(ux.git-crypt)"
+ );
+ }
+}
diff --git a/src/libutil/types.hh b/src/libutil/types.hh
index 9c85fef62..8f72c926f 100644
--- a/src/libutil/types.hh
+++ b/src/libutil/types.hh
@@ -22,6 +22,7 @@ typedef std::map<string, string> StringMap;
/* Paths are just strings. */
typedef string Path;
+typedef std::string_view PathView;
typedef list<Path> Paths;
typedef set<Path> PathSet;
diff --git a/src/libutil/util.cc b/src/libutil/util.cc
index 9edd69c64..1f1f2c861 100644
--- a/src/libutil/util.cc
+++ b/src/libutil/util.cc
@@ -106,16 +106,16 @@ Path absPath(Path path, std::optional<Path> dir, bool resolveSymlinks)
}
-Path canonPath(const Path & path, bool resolveSymlinks)
+Path canonPath(PathView path, bool resolveSymlinks)
{
assert(path != "");
string s;
+ s.reserve(256);
if (path[0] != '/')
throw Error("not an absolute path: '%1%'", path);
- string::const_iterator i = path.begin(), end = path.end();
string temp;
/* Count the number of times we follow a symlink and stop at some
@@ -125,33 +125,37 @@ Path canonPath(const Path & path, bool resolveSymlinks)
while (1) {
/* Skip slashes. */
- while (i != end && *i == '/') i++;
- if (i == end) break;
+ while (!path.empty() && path[0] == '/') path.remove_prefix(1);
+ if (path.empty()) break;
/* Ignore `.'. */
- if (*i == '.' && (i + 1 == end || i[1] == '/'))
- i++;
+ if (path == "." || path.substr(0, 2) == "./")
+ path.remove_prefix(1);
/* If `..', delete the last component. */
- else if (*i == '.' && i + 1 < end && i[1] == '.' &&
- (i + 2 == end || i[2] == '/'))
+ else if (path == ".." || path.substr(0, 3) == "../")
{
if (!s.empty()) s.erase(s.rfind('/'));
- i += 2;
+ path.remove_prefix(2);
}
/* Normal component; copy it. */
else {
s += '/';
- while (i != end && *i != '/') s += *i++;
+ if (const auto slash = path.find('/'); slash == string::npos) {
+ s += path;
+ path = {};
+ } else {
+ s += path.substr(0, slash);
+ path = path.substr(slash + 1);
+ }
/* If s points to a symlink, resolve it and continue from there */
if (resolveSymlinks && isLink(s)) {
if (++followCount >= maxFollow)
throw Error("infinite symlink recursion in path '%1%'", path);
- temp = readLink(s) + string(i, end);
- i = temp.begin();
- end = temp.end();
+ temp = concatStrings(readLink(s), path);
+ path = temp;
if (!temp.empty() && temp[0] == '/') {
s.clear(); /* restart for symlinks pointing to absolute path */
} else {
@@ -164,7 +168,7 @@ Path canonPath(const Path & path, bool resolveSymlinks)
}
}
- return s.empty() ? "/" : s;
+ return s.empty() ? "/" : std::move(s);
}
@@ -668,9 +672,11 @@ void writeFull(int fd, std::string_view s, bool allowInterrupts)
string drainFD(int fd, bool block, const size_t reserveSize)
{
- StringSink sink(reserveSize);
+ // the parser needs two extra bytes to append terminating characters, other users will
+ // not care very much about the extra memory.
+ StringSink sink(reserveSize + 2);
drainFD(fd, sink, block);
- return std::move(*sink.s);
+ return std::move(sink.s);
}
@@ -1055,7 +1061,7 @@ std::pair<int, std::string> runProgram(RunOptions && options)
status = e.status;
}
- return {status, std::move(*sink.s)};
+ return {status, std::move(sink.s)};
}
void runProgram2(const RunOptions & options)
@@ -1229,23 +1235,22 @@ void _interrupted()
//////////////////////////////////////////////////////////////////////
-template<class C> C tokenizeString(std::string_view s, const string & separators)
+template<class C> C tokenizeString(std::string_view s, std::string_view separators)
{
C result;
string::size_type pos = s.find_first_not_of(separators, 0);
while (pos != string::npos) {
string::size_type end = s.find_first_of(separators, pos + 1);
if (end == string::npos) end = s.size();
- string token(s, pos, end - pos);
- result.insert(result.end(), token);
+ result.insert(result.end(), string(s, pos, end - pos));
pos = s.find_first_not_of(separators, end);
}
return result;
}
-template Strings tokenizeString(std::string_view s, const string & separators);
-template StringSet tokenizeString(std::string_view s, const string & separators);
-template vector<string> tokenizeString(std::string_view s, const string & separators);
+template Strings tokenizeString(std::string_view s, std::string_view separators);
+template StringSet tokenizeString(std::string_view s, std::string_view separators);
+template vector<string> tokenizeString(std::string_view s, std::string_view separators);
string chomp(std::string_view s)
diff --git a/src/libutil/util.hh b/src/libutil/util.hh
index c900033f8..369c44f78 100644
--- a/src/libutil/util.hh
+++ b/src/libutil/util.hh
@@ -56,7 +56,7 @@ Path absPath(Path path,
double or trailing slashes. Optionally resolves all symlink
components such that each component of the resulting path is *not*
a symbolic link. */
-Path canonPath(const Path & path, bool resolveSymlinks = false);
+Path canonPath(PathView path, bool resolveSymlinks = false);
/* Return the directory part of the given canonical path, i.e.,
everything before the final `/'. If the path is the root or an
@@ -368,15 +368,19 @@ MakeError(FormatError, Error);
/* String tokenizer. */
-template<class C> C tokenizeString(std::string_view s, const string & separators = " \t\n\r");
+template<class C> C tokenizeString(std::string_view s, std::string_view separators = " \t\n\r");
/* Concatenate the given strings with a separator between the
elements. */
template<class C>
-string concatStringsSep(const string & sep, const C & ss)
+string concatStringsSep(const std::string_view sep, const C & ss)
{
+ size_t size = 0;
+ // need a cast to string_view since this is also called with Symbols
+ for (const auto & s : ss) size += sep.size() + std::string_view(s).size();
string s;
+ s.reserve(size);
for (auto & i : ss) {
if (s.size() != 0) s += sep;
s += i;
@@ -384,6 +388,14 @@ string concatStringsSep(const string & sep, const C & ss)
return s;
}
+template<class ... Parts>
+auto concatStrings(Parts && ... parts)
+ -> std::enable_if_t<(... && std::is_convertible_v<Parts, std::string_view>), string>
+{
+ std::string_view views[sizeof...(parts)] = { parts... };
+ return concatStringsSep({}, views);
+}
+
/* Add quotes around a collection of strings. */
template<class C> Strings quoteStrings(const C & c)
diff --git a/src/nix-build/nix-build.cc b/src/nix-build/nix-build.cc
index b73eb0795..5cdf5c717 100755
--- a/src/nix-build/nix-build.cc
+++ b/src/nix-build/nix-build.cc
@@ -292,7 +292,7 @@ static void main_nix_build(int argc, char * * argv)
else
for (auto i : left) {
if (fromArgs)
- exprs.push_back(state->parseExprFromString(i, absPath(".")));
+ exprs.push_back(state->parseExprFromString(std::move(i), absPath(".")));
else {
auto absolute = i;
try {
@@ -483,7 +483,7 @@ static void main_nix_build(int argc, char * * argv)
lose the current $PATH directories. */
auto rcfile = (Path) tmpDir + "/rc";
std::string rc = fmt(
- R"(_nix_shell_clean_tmpdir() { rm -rf %1%; }; )"s +
+ R"(_nix_shell_clean_tmpdir() { command rm -rf %1%; }; )"s +
(keepTmp ?
"trap _nix_shell_clean_tmpdir EXIT; "
"exitHooks+=(_nix_shell_clean_tmpdir); "
diff --git a/src/nix/add-to-store.cc b/src/nix/add-to-store.cc
index 2ae042789..5168413d2 100644
--- a/src/nix/add-to-store.cc
+++ b/src/nix/add-to-store.cc
@@ -32,7 +32,7 @@ struct CmdAddToStore : MixDryRun, StoreCommand
StringSink sink;
dumpPath(path, sink);
- auto narHash = hashString(htSHA256, *sink.s);
+ auto narHash = hashString(htSHA256, sink.s);
Hash hash = narHash;
if (ingestionMethod == FileIngestionMethod::Flat) {
@@ -45,14 +45,14 @@ struct CmdAddToStore : MixDryRun, StoreCommand
store->makeFixedOutputPath(ingestionMethod, hash, *namePart),
narHash,
};
- info.narSize = sink.s->size();
+ info.narSize = sink.s.size();
info.ca = std::optional { FixedOutputHash {
.method = ingestionMethod,
.hash = hash,
} };
if (!dryRun) {
- auto source = StringSource { *sink.s };
+ auto source = StringSource(sink.s);
store->addToStore(info, source);
}
diff --git a/src/nix/app.cc b/src/nix/app.cc
index 2fcf4752c..e104cc9c1 100644
--- a/src/nix/app.cc
+++ b/src/nix/app.cc
@@ -19,7 +19,7 @@ struct InstallableDerivedPath : Installable
}
- std::string what() override { return derivedPath.to_string(*store); }
+ std::string what() const override { return derivedPath.to_string(*store); }
DerivedPaths toDerivedPaths() override
{
diff --git a/src/nix/cat.cc b/src/nix/cat.cc
index e28ee3c50..6420a0f79 100644
--- a/src/nix/cat.cc
+++ b/src/nix/cat.cc
@@ -78,7 +78,7 @@ struct CmdCatNar : StoreCommand, MixCat
void run(ref<Store> store) override
{
- cat(makeNarAccessor(make_ref<std::string>(readFile(narPath))));
+ cat(makeNarAccessor(readFile(narPath)));
}
};
diff --git a/src/nix/copy.cc b/src/nix/copy.cc
index 197c85316..8730a9a5c 100644
--- a/src/nix/copy.cc
+++ b/src/nix/copy.cc
@@ -1,17 +1,11 @@
#include "command.hh"
#include "shared.hh"
#include "store-api.hh"
-#include "sync.hh"
-#include "thread-pool.hh"
-
-#include <atomic>
using namespace nix;
-struct CmdCopy : BuiltPathsCommand
+struct CmdCopy : virtual CopyCommand, virtual BuiltPathsCommand
{
- std::string srcUri, dstUri;
-
CheckSigsFlag checkSigs = CheckSigs;
SubstituteFlag substitute = NoSubstitute;
@@ -22,20 +16,6 @@ struct CmdCopy : BuiltPathsCommand
: BuiltPathsCommand(true)
{
addFlag({
- .longName = "from",
- .description = "URL of the source Nix store.",
- .labels = {"store-uri"},
- .handler = {&srcUri},
- });
-
- addFlag({
- .longName = "to",
- .description = "URL of the destination Nix store.",
- .labels = {"store-uri"},
- .handler = {&dstUri},
- });
-
- addFlag({
.longName = "no-check-sigs",
.description = "Do not require that paths are signed by trusted keys.",
.handler = {&checkSigs, NoCheckSigs},
@@ -65,22 +45,9 @@ struct CmdCopy : BuiltPathsCommand
Category category() override { return catSecondary; }
- ref<Store> createStore() override
- {
- return srcUri.empty() ? StoreCommand::createStore() : openStore(srcUri);
- }
-
- void run(ref<Store> store) override
- {
- if (srcUri.empty() && dstUri.empty())
- throw UsageError("you must pass '--from' and/or '--to'");
-
- BuiltPathsCommand::run(store);
- }
-
void run(ref<Store> srcStore, BuiltPaths && paths) override
{
- ref<Store> dstStore = dstUri.empty() ? openStore() : openStore(dstUri);
+ auto dstStore = getDstStore();
RealisedPath::Set stuffToCopy;
diff --git a/src/nix/flake.md b/src/nix/flake.md
index 3b5812a0a..a8436bcaa 100644
--- a/src/nix/flake.md
+++ b/src/nix/flake.md
@@ -137,15 +137,6 @@ Currently the `type` attribute can be one of the following:
*path* must be a directory in the file system containing a file
named `flake.nix`.
- If the directory or any of its parents is a Git repository, then
- this is essentially equivalent to `git+file://<path>` (see below),
- except that the `dir` parameter is derived automatically. For
- example, if `/foo/bar` is a Git repository, then the flake reference
- `/foo/bar/flake` is equivalent to `/foo/bar?dir=flake`.
-
- If the directory is not inside a Git repository, then the flake
- contents is the entire contents of *path*.
-
*path* generally must be an absolute path. However, on the command
line, it can be a relative path (e.g. `.` or `./foo`) which is
interpreted as relative to the current directory. In this case, it
diff --git a/src/nix/ls.cc b/src/nix/ls.cc
index c1dc9a95b..07554994b 100644
--- a/src/nix/ls.cc
+++ b/src/nix/ls.cc
@@ -157,7 +157,7 @@ struct CmdLsNar : Command, MixLs
void run() override
{
- list(makeNarAccessor(make_ref<std::string>(readFile(narPath))));
+ list(makeNarAccessor(readFile(narPath)));
}
};
diff --git a/src/nix/main.cc b/src/nix/main.cc
index fe7469be4..b923f2535 100644
--- a/src/nix/main.cc
+++ b/src/nix/main.cc
@@ -59,7 +59,6 @@ struct HelpRequested { };
struct NixArgs : virtual MultiCommand, virtual MixCommonArgs
{
- bool printBuildLogs = false;
bool useNet = true;
bool refresh = false;
bool showVersion = false;
@@ -270,11 +269,15 @@ void mainWrapped(int argc, char * * argv)
if (legacy) return legacy(argc, argv);
}
- verbosity = lvlNotice;
- settings.verboseBuild = false;
evalSettings.pureEval = true;
setLogFormat("bar");
+ settings.verboseBuild = false;
+ if (isatty(STDERR_FILENO)) {
+ verbosity = lvlNotice;
+ } else {
+ verbosity = lvlInfo;
+ }
Finally f([] { logger->stop(); });
diff --git a/src/nix/make-content-addressable.cc b/src/nix/make-content-addressable.cc
index 12f303a10..2e75a3b61 100644
--- a/src/nix/make-content-addressable.cc
+++ b/src/nix/make-content-addressable.cc
@@ -61,10 +61,10 @@ struct CmdMakeContentAddressable : StorePathsCommand, MixJSON
}
}
- *sink.s = rewriteStrings(*sink.s, rewrites);
+ sink.s = rewriteStrings(sink.s, rewrites);
HashModuloSink hashModuloSink(htSHA256, oldHashPart);
- hashModuloSink(*sink.s);
+ hashModuloSink(sink.s);
auto narHash = hashModuloSink.finish().first;
@@ -74,7 +74,7 @@ struct CmdMakeContentAddressable : StorePathsCommand, MixJSON
};
info.references = std::move(references);
if (hasSelfReference) info.references.insert(info.path);
- info.narSize = sink.s->size();
+ info.narSize = sink.s.size();
info.ca = FixedOutputHash {
.method = FileIngestionMethod::Recursive,
.hash = info.narHash,
@@ -85,7 +85,7 @@ struct CmdMakeContentAddressable : StorePathsCommand, MixJSON
auto source = sinkToSource([&](Sink & nextSink) {
RewritingSink rsink2(oldHashPart, std::string(info.path.hashPart()), nextSink);
- rsink2(*sink.s);
+ rsink2(sink.s);
rsink2.flush();
});
diff --git a/src/nix/nix.md b/src/nix/nix.md
index d10de7c01..2f54c5e2c 100644
--- a/src/nix/nix.md
+++ b/src/nix/nix.md
@@ -57,9 +57,47 @@ the Nix store. Here are the recognised types of installables:
These have the form *flakeref*[`#`*attrpath*], where *flakeref* is a
flake reference and *attrpath* is an optional attribute path. For
more information on flakes, see [the `nix flake` manual
- page](./nix3-flake.md). Flake references are most commonly a flake
- identifier in the flake registry (e.g. `nixpkgs`) or a path
- (e.g. `/path/to/my-flake` or `.`).
+ page](./nix3-flake.md). Flake references are most commonly a flake
+ identifier in the flake registry (e.g. `nixpkgs`), or a raw path
+ (e.g. `/path/to/my-flake` or `.` or `../foo`), or a full URL
+ (e.g. `github:nixos/nixpkgs` or `path:.`)
+
+ When the flake reference is a raw path (a path without any URL
+ scheme), it is interpreted as a `path:` or `git+file:` url in the following
+ way:
+
+ - If the path is within a Git repository, then the url will be of the form
+ `git+file://[GIT_REPO_ROOT]?dir=[RELATIVE_FLAKE_DIR_PATH]`
+ where `GIT_REPO_ROOT` is the path to the root of the git repository,
+ and `RELATIVE_FLAKE_DIR_PATH` is the path (relative to the directory
+ root) of the closest parent of the given path that contains a `flake.nix` within
+ the git repository.
+ If no such directory exists, then Nix will error-out.
+
+ Note that the search will only include files indexed by git. In particular, files
+ which are matched by `.gitignore` or have never been `git add`-ed will not be
+ available in the flake. If this is undesireable, specify `path:<directory>` explicitly;
+
+ For example, if `/foo/bar` is a git repository with the following structure:
+ ```
+ .
+ └── baz
+ ├── blah
+ │  └── file.txt
+ └── flake.nix
+ ```
+
+ Then `/foo/bar/baz/blah` will resolve to `git+file:///foo/bar?dir=baz`
+
+ - If the supplied path is not a git repository, then the url will have the form
+ `path:FLAKE_DIR_PATH` where `FLAKE_DIR_PATH` is the closest parent
+ of the supplied path that contains a `flake.nix` file (within the same file-system).
+ If no such directory exists, then Nix will error-out.
+
+ For example, if `/foo/bar/flake.nix` exists, then `/foo/bar/baz/` will resolve to
+ `path:/foo/bar`
+
+
If *attrpath* is omitted, Nix tries some default values; for most
subcommands, the default is `defaultPackage.`*system*
diff --git a/src/nix/profile.cc b/src/nix/profile.cc
index 96a20f673..9b7c999af 100644
--- a/src/nix/profile.cc
+++ b/src/nix/profile.cc
@@ -157,17 +157,17 @@ struct ProfileManifest
StringSink sink;
dumpPath(tempDir, sink);
- auto narHash = hashString(htSHA256, *sink.s);
+ auto narHash = hashString(htSHA256, sink.s);
ValidPathInfo info {
store->makeFixedOutputPath(FileIngestionMethod::Recursive, narHash, "profile", references),
narHash,
};
info.references = std::move(references);
- info.narSize = sink.s->size();
+ info.narSize = sink.s.size();
info.ca = FixedOutputHash { .method = FileIngestionMethod::Recursive, .hash = info.narHash };
- auto source = StringSource { *sink.s };
+ StringSource source(sink.s);
store->addToStore(info, source);
return std::move(info.path);
diff --git a/src/nix/repl.cc b/src/nix/repl.cc
index a62ad66c2..be9f96836 100644
--- a/src/nix/repl.cc
+++ b/src/nix/repl.cc
@@ -703,7 +703,7 @@ void NixRepl::addVarToScope(const Symbol & name, Value & v)
Expr * NixRepl::parseString(string s)
{
- Expr * e = state->parseExprFromString(s, curDir, staticEnv);
+ Expr * e = state->parseExprFromString(std::move(s), curDir, staticEnv);
return e;
}
diff --git a/src/nix/search.cc b/src/nix/search.cc
index 0d8fdd5c2..e9307342c 100644
--- a/src/nix/search.cc
+++ b/src/nix/search.cc
@@ -9,6 +9,7 @@
#include "shared.hh"
#include "eval-cache.hh"
#include "attr-path.hh"
+#include "fmt.hh"
#include <regex>
#include <fstream>
@@ -20,16 +21,6 @@ std::string wrap(std::string prefix, std::string s)
return prefix + s + ANSI_NORMAL;
}
-std::string hilite(const std::string & s, const std::smatch & m, std::string postfix)
-{
- return
- m.empty()
- ? s
- : std::string(m.prefix())
- + ANSI_GREEN + std::string(m.str()) + postfix
- + std::string(m.suffix());
-}
-
struct CmdSearch : InstallableCommand, MixJSON
{
std::vector<std::string> res;
@@ -100,8 +91,6 @@ struct CmdSearch : InstallableCommand, MixJSON
};
if (cursor.isDerivation()) {
- size_t found = 0;
-
DrvName name(cursor.getAttr("name")->getString());
auto aMeta = cursor.maybeGetAttr("meta");
@@ -110,21 +99,31 @@ struct CmdSearch : InstallableCommand, MixJSON
std::replace(description.begin(), description.end(), '\n', ' ');
auto attrPath2 = concatStringsSep(".", attrPath);
- std::smatch attrPathMatch;
- std::smatch descriptionMatch;
- std::smatch nameMatch;
+ std::vector<std::smatch> attrPathMatches;
+ std::vector<std::smatch> descriptionMatches;
+ std::vector<std::smatch> nameMatches;
+ bool found = false;
for (auto & regex : regexes) {
- std::regex_search(attrPath2, attrPathMatch, regex);
- std::regex_search(name.name, nameMatch, regex);
- std::regex_search(description, descriptionMatch, regex);
- if (!attrPathMatch.empty()
- || !nameMatch.empty()
- || !descriptionMatch.empty())
- found++;
+ found = false;
+ auto addAll = [&found](std::sregex_iterator it, std::vector<std::smatch> & vec) {
+ const auto end = std::sregex_iterator();
+ while (it != end) {
+ vec.push_back(*it++);
+ found = true;
+ }
+ };
+
+ addAll(std::sregex_iterator(attrPath2.begin(), attrPath2.end(), regex), attrPathMatches);
+ addAll(std::sregex_iterator(name.name.begin(), name.name.end(), regex), nameMatches);
+ addAll(std::sregex_iterator(description.begin(), description.end(), regex), descriptionMatches);
+
+ if (!found)
+ break;
}
- if (found == res.size()) {
+ if (found)
+ {
results++;
if (json) {
auto jsonElem = jsonOut->object(attrPath2);
@@ -132,15 +131,15 @@ struct CmdSearch : InstallableCommand, MixJSON
jsonElem.attr("version", name.version);
jsonElem.attr("description", description);
} else {
- auto name2 = hilite(name.name, nameMatch, "\e[0;2m");
+ auto name2 = hiliteMatches(name.name, std::move(nameMatches), ANSI_GREEN, "\e[0;2m");
if (results > 1) logger->cout("");
logger->cout(
"* %s%s",
- wrap("\e[0;1m", hilite(attrPath2, attrPathMatch, "\e[0;1m")),
+ wrap("\e[0;1m", hiliteMatches(attrPath2, std::move(attrPathMatches), ANSI_GREEN, "\e[0;1m")),
name.version != "" ? " (" + name.version + ")" : "");
if (description != "")
logger->cout(
- " %s", hilite(description, descriptionMatch, ANSI_NORMAL));
+ " %s", hiliteMatches(description, std::move(descriptionMatches), ANSI_GREEN, ANSI_NORMAL));
}
}
}
diff --git a/src/nix/store-copy-log.cc b/src/nix/store-copy-log.cc
new file mode 100644
index 000000000..079cd6b3e
--- /dev/null
+++ b/src/nix/store-copy-log.cc
@@ -0,0 +1,46 @@
+#include "command.hh"
+#include "shared.hh"
+#include "store-api.hh"
+#include "sync.hh"
+#include "thread-pool.hh"
+
+#include <atomic>
+
+using namespace nix;
+
+struct CmdCopyLog : virtual CopyCommand, virtual InstallablesCommand
+{
+ std::string description() override
+ {
+ return "copy build logs between Nix stores";
+ }
+
+ std::string doc() override
+ {
+ return
+ #include "store-copy-log.md"
+ ;
+ }
+
+ Category category() override { return catUtility; }
+
+ void run(ref<Store> srcStore) override
+ {
+ auto dstStore = getDstStore();
+
+ StorePathSet drvPaths;
+
+ for (auto & i : installables)
+ for (auto & drvPath : i->toDrvPaths(getEvalStore()))
+ drvPaths.insert(drvPath);
+
+ for (auto & drvPath : drvPaths) {
+ if (auto log = srcStore->getBuildLog(drvPath))
+ dstStore->addBuildLog(drvPath, *log);
+ else
+ throw Error("build log for '%s' is not available", srcStore->printStorePath(drvPath));
+ }
+ }
+};
+
+static auto rCmdCopyLog = registerCommand2<CmdCopyLog>({"store", "copy-log"});
diff --git a/src/nix/store-copy-log.md b/src/nix/store-copy-log.md
new file mode 100644
index 000000000..19ae57079
--- /dev/null
+++ b/src/nix/store-copy-log.md
@@ -0,0 +1,33 @@
+R""(
+
+# Examples
+
+* To copy the build log of the `hello` package from
+ https://cache.nixos.org to the local store:
+
+ ```console
+ # nix store copy-log --from https://cache.nixos.org --eval-store auto nixpkgs#hello
+ ```
+
+ You can verify that the log is available locally:
+
+ ```console
+ # nix log --substituters '' nixpkgs#hello
+ ```
+
+ (The flag `--substituters ''` avoids querying
+ `https://cache.nixos.org` for the log.)
+
+* To copy the log for a specific store derivation via SSH:
+
+ ```console
+ # nix store copy-log --to ssh-ng://machine /nix/store/ilgm50plpmcgjhcp33z6n4qbnpqfhxym-glibc-2.33-59.drv
+ ```
+
+# Description
+
+`nix store copy-log` copies build logs between two Nix stores. The
+source store is specified using `--from` and the destination using
+`--to`. If one of these is omitted, it defaults to the local store.
+
+)""
diff --git a/src/nix/upgrade-nix.cc b/src/nix/upgrade-nix.cc
index 9cd567896..17a5a77ee 100644
--- a/src/nix/upgrade-nix.cc
+++ b/src/nix/upgrade-nix.cc
@@ -140,7 +140,7 @@ struct CmdUpgradeNix : MixDryRun, StoreCommand
auto state = std::make_unique<EvalState>(Strings(), store);
auto v = state->allocValue();
- state->eval(state->parseExprFromString(*res.data, "/no-such-path"), *v);
+ state->eval(state->parseExprFromString(res.data, "/no-such-path"), *v);
Bindings & bindings(*state->allocBindings(0));
auto v2 = findAlongAttrPath(*state, settings.thisSystem, bindings, *v).first;
diff --git a/src/nix/why-depends.cc b/src/nix/why-depends.cc
index e27f56ef5..657df30d7 100644
--- a/src/nix/why-depends.cc
+++ b/src/nix/why-depends.cc
@@ -31,6 +31,7 @@ struct CmdWhyDepends : SourceExprCommand
{
std::string _package, _dependency;
bool all = false;
+ bool precise = false;
CmdWhyDepends()
{
@@ -56,6 +57,12 @@ struct CmdWhyDepends : SourceExprCommand
.description = "Show all edges in the dependency graph leading from *package* to *dependency*, rather than just a shortest path.",
.handler = {&all, true},
});
+
+ addFlag({
+ .longName = "precise",
+ .description = "For each edge in the dependency graph, show the files in the parent that cause the dependency.",
+ .handler = {&precise, true},
+ });
}
std::string description() override
@@ -158,11 +165,19 @@ struct CmdWhyDepends : SourceExprCommand
auto pathS = store->printStorePath(node.path);
assert(node.dist != inf);
- logger->cout("%s%s%s%s" ANSI_NORMAL,
- firstPad,
- node.visited ? "\e[38;5;244m" : "",
- firstPad != "" ? "→ " : "",
- pathS);
+ if (precise) {
+ logger->cout("%s%s%s%s" ANSI_NORMAL,
+ firstPad,
+ node.visited ? "\e[38;5;244m" : "",
+ firstPad != "" ? "→ " : "",
+ pathS);
+ } else {
+ logger->cout("%s%s%s%s" ANSI_NORMAL,
+ firstPad,
+ node.visited ? "\e[38;5;244m" : "",
+ firstPad != "" ? treeLast : "",
+ pathS);
+ }
if (node.path == dependencyPath && !all
&& packagePath != dependencyPath)
@@ -237,9 +252,8 @@ struct CmdWhyDepends : SourceExprCommand
// FIXME: should use scanForReferences().
- visitPath(pathS);
+ if (precise) visitPath(pathS);
- RunPager pager;
for (auto & ref : refs) {
std::string hash(ref.second->path.hashPart());
@@ -259,6 +273,7 @@ struct CmdWhyDepends : SourceExprCommand
}
};
+ RunPager pager;
try {
printNode(graph.at(packagePath), "", "");
} catch (BailOut & ) { }