Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

File substitutes #52

Closed
wants to merge 24 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
24 commits
Select commit Hold shift + click to select a range
0bbd8c7
prim_import: Don't build derivations in read-only mode
shlevy May 16, 2012
9fc4cca
prim_import: Distinguish between failures due to read-only mode and o…
shlevy Aug 29, 2012
9763370
DrvInfo: Put the 'name' member behind a query/set interface
shlevy Jun 23, 2012
b83eab5
DrvInfo: Put the 'system' member behind a query/set interface
shlevy Jun 23, 2012
9ec740e
DrvInfo::queryMetaInfo(state, name): Don't evaluate all of meta just …
shlevy Jun 23, 2012
6f958bc
nix-env -q --print-description: Don't try to evaluate all of meta jus…
shlevy Jun 23, 2012
928f87f
getDerivations: add parameter to skip values that throw ImportReadOnl…
shlevy Jun 23, 2012
6f6f25f
isPrebuilt: Derivations whose outPaths are blocked by a read-only err…
shlevy Aug 29, 2012
36d0d9c
createMetaValue: Handle individual meta values blocked by ImportReadO…
shlevy Jun 23, 2012
efc58a3
nix-env -q: Gracefully bypass ImportReadOnlyErrors in outPath, drvPat…
shlevy Aug 29, 2012
f6db8fe
Make all handling of ImportReadOnlyErrors unconditional
shlevy Jun 23, 2012
a233da3
nix-env opQuery: If we can't get outPath due to an import from deriva…
shlevy Aug 29, 2012
acec363
Add test to exercise all the paths that can break in read-only mode w…
shlevy Sep 1, 2012
e377dd8
Only run the expression-substitutes test for now
shlevy Sep 1, 2012
8e98954
Use structured data for string contexts.
shlevy Sep 2, 2012
fccae2a
Merge branch 'structured-context' into expression-substitutes
shlevy Sep 2, 2012
09bea6e
expression-substitute->file-substitute
shlevy Sep 3, 2012
0decb92
Add the querySubstitutableFiles and querySubstitutableFileInfos inter…
shlevy Sep 3, 2012
90121cf
prim_import: Read the imported expression from a file substituter if …
shlevy Sep 15, 2012
75849f7
Simplify the file substitute interface: haveFile now means 'You can s…
shlevy Sep 16, 2012
f60f286
Include the recursive hash of the file in substitutableFileInfos
shlevy Sep 16, 2012
e1c7af0
coerceToString: Use file substituters to compute the dstPath for a co…
shlevy Sep 16, 2012
86a6a10
Remove probably useless file substitute test cases until/unless they'…
shlevy Sep 16, 2012
f00d0a6
Reenable tests
shlevy Sep 16, 2012
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -124,6 +124,7 @@ Makefile.in
/tests/common.sh
/tests/dummy
/tests/result*
/tests/file-substituter.sh

# /tests/lang/
/tests/lang/*.out
Expand Down
2 changes: 1 addition & 1 deletion src/libexpr/Makefile.am
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ libexpr_la_SOURCES = \
pkginclude_HEADERS = \
nixexpr.hh eval.hh eval-inline.hh lexer-tab.hh parser-tab.hh \
get-drvs.hh attr-path.hh value-to-xml.hh common-opts.hh \
names.hh symbol-table.hh value.hh
names.hh symbol-table.hh context.hh value.hh

libexpr_la_LIBADD = ../libutil/libutil.la ../libstore/libstore.la \
../boost/format/libformat.la @BDW_GC_LIBS@
Expand Down
27 changes: 27 additions & 0 deletions src/libexpr/context.hh
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
#pragma once

#include <cstring>

namespace nix {


struct ContextEntry
{
const char *path;
const char *output;
bool discardOutputs;
};


struct CompareContextEntry
{
inline bool operator() (const ContextEntry* const & c1, const ContextEntry* const & c2) {
return (strcmp(c1->path,c2->path) < 0) || ((strcmp(c1->path,c2->path) == 0) && (strcmp(c1->output,c2->output) < 0));
}
};


typedef std::set<const ContextEntry *, CompareContextEntry> ContextEntrySet;


}
79 changes: 59 additions & 20 deletions src/libexpr/eval.cc
Original file line number Diff line number Diff line change
Expand Up @@ -142,7 +142,7 @@ EvalState::EvalState()
, baseEnvDispl(0)
, staticBaseEnv(false, 0)
{
nrEnvs = nrValuesInEnvs = nrValues = nrListElems = 0;
nrEnvs = nrValuesInEnvs = nrValues = nrListElems = nrContextEntries = nrContextEntryBytes = 0;
nrAttrsets = nrOpUpdates = nrOpUpdateValuesCopied = 0;
nrListConcats = nrPrimOpCalls = nrFunctionCalls = 0;
countCalls = getEnv("NIX_COUNT_CALLS", "0") != "0";
Expand Down Expand Up @@ -275,15 +275,15 @@ void mkString(Value & v, const char * s)
}


void mkString(Value & v, const string & s, const PathSet & context)
void mkString(Value & v, const string & s, const ContextEntrySet & context)
{
mkString(v, s.c_str());
if (!context.empty()) {
unsigned int n = 0;
v.string.context = (const char * *)
GC_MALLOC((context.size() + 1) * sizeof(char *));
foreach (PathSet::const_iterator, i, context)
v.string.context[n++] = GC_STRDUP(i->c_str());
v.string.context = (const ContextEntry * *)
GC_MALLOC((context.size() + 1) * sizeof(ContextEntry *));
foreach (ContextEntrySet::const_iterator, i, context)
v.string.context[n++] = *i;
v.string.context[n] = 0;
}
}
Expand Down Expand Up @@ -322,6 +322,20 @@ Value * EvalState::allocValue()
}


ContextEntry * EvalState::allocContextEntry(const Path & path, const string & output) {
nrContextEntries++;
nrContextEntryBytes += sizeof(ContextEntry) + (path.size() + 1) + (output.size() + 1);
ContextEntry * c = (ContextEntry *) GC_MALLOC(sizeof(ContextEntry) + (path.size() + 1) + (output.size() + 1));
c->path = ((const char *) c) + sizeof(ContextEntry);
c->output = c->path + (path.size() + 1);
// !!! Commented out because of zero-out behaviour of GC_MALLOC, can we rely on false == 0?
// c->discardOutputs = false;
memcpy((void *) c->path, path.c_str(), path.size() + 1);
memcpy((void *) c->output, output.c_str(), output.size() + 1);
return c;
}


Env & EvalState::allocEnv(unsigned int size)
{
nrEnvs++;
Expand Down Expand Up @@ -441,6 +455,24 @@ void EvalState::evalFile(const Path & path, Value & v)
}


void EvalState::evalSubstitutableFile(const Path & path, Value & v)
{
FileEvalCache::iterator i = fileEvalCache.find(path);
if (i == fileEvalCache.end()) {
startNest(nest, lvlTalkative, format("evaluating file `%1%'") % path);
Expr * e = parseExprFromSubstitutableFile(path);
try {
eval(e, v);
} catch (Error & e) {
addErrorPrefix(e, "while evaluating the file `%1%':\n", path);
throw;
}
fileEvalCache[path] = v;
} else
v = i->second;
}


void EvalState::eval(Expr * e, Value & v)
{
e->eval(*this, baseEnv, v);
Expand Down Expand Up @@ -948,7 +980,7 @@ void EvalState::concatLists(Value & v, unsigned int nrLists, Value * * lists)

void ExprConcatStrings::eval(EvalState & state, Env & env, Value & v)
{
PathSet context;
ContextEntrySet context;
std::ostringstream s;

bool first = true, isPath = false;
Expand Down Expand Up @@ -1030,15 +1062,15 @@ string EvalState::forceString(Value & v)
}


void copyContext(const Value & v, PathSet & context)
void copyContext(const Value & v, ContextEntrySet & context)
{
if (v.string.context)
for (const char * * p = v.string.context; *p; ++p)
for (const ContextEntry * * p = v.string.context; *p; ++p)
context.insert(*p);
}


string EvalState::forceString(Value & v, PathSet & context)
string EvalState::forceString(Value & v, ContextEntrySet & context)
{
string s = forceString(v);
copyContext(v, context);
Expand All @@ -1051,7 +1083,7 @@ string EvalState::forceStringNoCtx(Value & v)
string s = forceString(v);
if (v.string.context)
throwEvalError("the string `%1%' is not allowed to refer to a store path (such as `%2%')",
v.string.s, v.string.context[0]);
v.string.s, v.string.context[0]->path);
return s;
}

Expand All @@ -1067,7 +1099,7 @@ bool EvalState::isDerivation(Value & v)
}


string EvalState::coerceToString(Value & v, PathSet & context,
string EvalState::coerceToString(Value & v, ContextEntrySet & context,
bool coerceMore, bool copyToStore)
{
forceValue(v);
Expand All @@ -1091,15 +1123,18 @@ string EvalState::coerceToString(Value & v, PathSet & context,
if (srcToStore[path] != "")
dstPath = srcToStore[path];
else {
dstPath = settings.readOnlyMode
? computeStorePathForPath(path).first
: store->addToStore(path);
if (settings.readOnlyMode && isInStore(path) && !store->isValidPath(toStorePath(path)))
dstPath = computeStorePathForSubstitutablePath(path).first;
else
dstPath = settings.readOnlyMode
? computeStorePathForPath(path).first
: store->addToStore(path);
srcToStore[path] = dstPath;
printMsg(lvlChatty, format("copied source `%1%' -> `%2%'")
% path % dstPath);
}

context.insert(dstPath);
context.insert(allocContextEntry(dstPath));
return dstPath;
}

Expand Down Expand Up @@ -1137,7 +1172,7 @@ string EvalState::coerceToString(Value & v, PathSet & context,
}


Path EvalState::coerceToPath(Value & v, PathSet & context)
Path EvalState::coerceToPath(Value & v, ContextEntrySet & context)
{
string path = coerceToString(v, context, false, false);
if (path == "" || path[0] != '/')
Expand Down Expand Up @@ -1170,11 +1205,13 @@ bool EvalState::eqValues(Value & v1, Value & v2)
case tString: {
/* Compare both the string and its context. */
if (strcmp(v1.string.s, v2.string.s) != 0) return false;
const char * * p = v1.string.context, * * q = v2.string.context;
const ContextEntry * * p = v1.string.context, * * q = v2.string.context;
if (!p && !q) return true;
if (!p || !q) return false;
for ( ; *p && *q; ++p, ++q)
if (strcmp(*p, *q) != 0) return false;
for ( ; *p && *q; ++p, ++q) {
if (strcmp((*p)->path, (*q)->path) != 0) return false;
if (strcmp((*p)->output, (*q)->output) != 0) return false;
}
if (*p || *q) return false;
return true;
}
Expand Down Expand Up @@ -1243,6 +1280,8 @@ void EvalState::printStats()
printMsg(v, format(" list concatenations: %1%") % nrListConcats);
printMsg(v, format(" values allocated: %1% (%2% bytes)")
% nrValues % (nrValues * sizeof(Value)));
printMsg(v, format(" string context entries allocated: %1% (%2% bytes)")
% nrContextEntries % nrContextEntryBytes);
printMsg(v, format(" attribute sets allocated: %1%") % nrAttrsets);
printMsg(v, format(" right-biased unions: %1%") % nrOpUpdates);
printMsg(v, format(" values copied in right-biased unions: %1%") % nrOpUpdateValuesCopied);
Expand Down
24 changes: 18 additions & 6 deletions src/libexpr/eval.hh
Original file line number Diff line number Diff line change
Expand Up @@ -71,10 +71,9 @@ struct Attr
}
};

void mkString(Value & v, const string & s, const ContextEntrySet & context = ContextEntrySet());

void mkString(Value & v, const string & s, const PathSet & context = PathSet());

void copyContext(const Value & v, PathSet & context);
void copyContext(const Value & v, ContextEntrySet & context);


/* Cache for calls to addToStore(); maps source paths to the store
Expand Down Expand Up @@ -124,13 +123,21 @@ public:
refers to a directory, then "/default.nix" is appended. */
Expr * parseExprFromFile(Path path);

/* Parse a Nix expression from the substitute for the specified file.
If `path' refers to a directory, then "/default.nix" is appended. */
Expr * parseExprFromSubstitutableFile(Path path);

/* Parse a Nix expression from the specified string. */
Expr * parseExprFromString(const string & s, const Path & basePath);

/* Evaluate an expression read from the given file to normal
form. */
void evalFile(const Path & path, Value & v);

/* Evaluate an expression read from the substutite for the
given file to normal form. */
void evalSubstitutableFile(const Path & path, Value & v);

/* Look up a file in the search path. */
Path findFile(const string & path);

Expand Down Expand Up @@ -160,7 +167,7 @@ public:
inline void forceList(Value & v);
void forceFunction(Value & v); // either lambda or primop
string forceString(Value & v);
string forceString(Value & v, PathSet & context);
string forceString(Value & v, ContextEntrySet & context);
string forceStringNoCtx(Value & v);

/* Return true iff the value `v' denotes a derivation (i.e. a
Expand All @@ -171,13 +178,13 @@ public:
string. If `coerceMore' is set, also converts nulls, integers,
booleans and lists to a string. If `copyToStore' is set,
referenced paths are copied to the Nix store as a side effect.q */
string coerceToString(Value & v, PathSet & context,
string coerceToString(Value & v, ContextEntrySet & context,
bool coerceMore = false, bool copyToStore = true);

/* Path coercion. Converts strings, paths and derivations to a
path. The result is guaranteed to be a canonicalised, absolute
path. Nothing is copied to the store. */
Path coerceToPath(Value & v, PathSet & context);
Path coerceToPath(Value & v, ContextEntrySet & context);

private:

Expand Down Expand Up @@ -224,6 +231,9 @@ public:

/* Allocation primitives. */
Value * allocValue();

ContextEntry * allocContextEntry(const Path & path, const string & output = string());

Env & allocEnv(unsigned int size);

Value * allocAttr(Value & vAttrs, const Symbol & name);
Expand All @@ -242,6 +252,8 @@ private:
unsigned long nrEnvs;
unsigned long nrValuesInEnvs;
unsigned long nrValues;
unsigned long nrContextEntries;
unsigned long nrContextEntryBytes;
unsigned long nrListElems;
unsigned long nrAttrsets;
unsigned long nrOpUpdates;
Expand Down
Loading