* Merge the multiple-outputs-sandbox branch (svn merge --reintegrate
^/nix/branches/multiple-outputs-sandbox). Multiple output support still isn't complete, but it wasn't complete in the trunk either, so it doesn't hurt.
This commit is contained in:
commit
adaf64a99b
21 changed files with 343 additions and 125 deletions
27
.gitignore
vendored
27
.gitignore
vendored
|
@ -61,23 +61,11 @@
|
|||
# /externals/
|
||||
/externals/Makefile
|
||||
/externals/Makefile.in
|
||||
/externals/aterm-*
|
||||
/externals/have-aterm
|
||||
/externals/build-aterm
|
||||
/externals/inst-aterm
|
||||
/externals/bzip2-*
|
||||
/externals/have-bzip2
|
||||
/externals/build-bzip2
|
||||
/externals/inst-bzip2
|
||||
|
||||
# /make/examples/aterm/
|
||||
/make/examples/aterm/result*
|
||||
|
||||
# /make/examples/aterm/aterm/
|
||||
/make/examples/aterm/aterm/*
|
||||
|
||||
# /make/examples/aterm/test/
|
||||
/make/examples/aterm/test/*
|
||||
/externals/sqlite-*
|
||||
/externals/build-sqlite
|
||||
|
||||
# /misc/
|
||||
/misc/Makefile.in
|
||||
|
@ -100,13 +88,16 @@
|
|||
/scripts/nix-channel
|
||||
/scripts/nix-build
|
||||
/scripts/nix-copy-closure
|
||||
/scripts/readmanifest.pm
|
||||
/scripts/readconfig.pm
|
||||
/scripts/nix-generate-patches
|
||||
/scripts/NixConfig.pm
|
||||
/scripts/NixManifest.pm
|
||||
/scripts/GeneratePatches.pm
|
||||
/scripts/download-using-manifests.pl
|
||||
/scripts/copy-from-other-stores.pl
|
||||
/scripts/generate-patches.pl
|
||||
/scripts/find-runtime-roots.pl
|
||||
/scripts/build-remote.pl
|
||||
/scripts/nix-reduce-build
|
||||
/scripts/nix-http-export.cgi
|
||||
|
||||
# /src/
|
||||
/src/Makefile
|
||||
|
@ -168,6 +159,7 @@
|
|||
/src/libstore/derivations-ast.cc
|
||||
/src/libstore/derivations-ast.hh
|
||||
/src/libstore/.libs
|
||||
/src/libstore/schema.sql.hh
|
||||
|
||||
# /src/libutil/
|
||||
/src/libutil/Makefile
|
||||
|
@ -242,6 +234,7 @@
|
|||
/tests/config.nix
|
||||
/tests/common.sh
|
||||
/tests/dummy
|
||||
/tests/result*
|
||||
|
||||
# /tests/lang/
|
||||
/tests/lang/*.out
|
||||
|
|
|
@ -274,8 +274,8 @@ AC_SUBST(sqlite_bin)
|
|||
# Whether to use the Boehm garbage collector.
|
||||
AC_ARG_ENABLE(gc, AC_HELP_STRING([--enable-gc],
|
||||
[enable garbage collection in the Nix expression evaluator (requires Boehm GC)]),
|
||||
gc=$enableval, gc=)
|
||||
if test -n "$gc"; then
|
||||
gc=$enableval, gc=no)
|
||||
if test "$gc" = yes; then
|
||||
PKG_CHECK_MODULES([BDW_GC], [bdw-gc])
|
||||
CXXFLAGS="$BDW_GC_CFLAGS $CXXFLAGS"
|
||||
AC_DEFINE(HAVE_BOEHMGC, 1, [Whether to use the Boehm garbage collector.])
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
all-local: config.nix
|
||||
|
||||
files = nar.nix buildenv.nix buildenv.pl unpack-channel.nix unpack-channel.sh
|
||||
files = nar.nix buildenv.nix buildenv.pl unpack-channel.nix unpack-channel.sh derivation.nix
|
||||
|
||||
install-exec-local:
|
||||
$(INSTALL) -d $(DESTDIR)$(datadir)/nix/corepkgs
|
||||
|
|
27
corepkgs/derivation.nix
Normal file
27
corepkgs/derivation.nix
Normal file
|
@ -0,0 +1,27 @@
|
|||
/* This is the implementation of the ‘derivation’ builtin function.
|
||||
It's actually a wrapper around the ‘derivationStrict’ primop. */
|
||||
|
||||
drvAttrs @ { outputs ? [ "out" ], ... }:
|
||||
|
||||
let
|
||||
|
||||
strict = derivationStrict drvAttrs;
|
||||
|
||||
commonAttrs = drvAttrs // (builtins.listToAttrs outputsList) //
|
||||
{ all = map (x: x.value) outputsList;
|
||||
inherit drvAttrs;
|
||||
};
|
||||
|
||||
outputToAttrListElement = outputName:
|
||||
{ name = outputName;
|
||||
value = commonAttrs // {
|
||||
outPath = builtins.getAttr outputName strict;
|
||||
drvPath = strict.drvPath;
|
||||
type = "derivation";
|
||||
inherit outputName;
|
||||
};
|
||||
};
|
||||
|
||||
outputsList = map outputToAttrListElement outputs;
|
||||
|
||||
in (builtins.head outputsList).value
|
|
@ -307,6 +307,7 @@ EOF
|
|||
|
||||
for my $manifestLink (glob "$manifestDir/*.nixmanifest") {
|
||||
my $manifest = Cwd::abs_path($manifestLink);
|
||||
next unless -f $manifest;
|
||||
my $timestamp = lstat($manifest)->mtime;
|
||||
$seen{$manifest} = 1;
|
||||
|
||||
|
|
|
@ -148,8 +148,6 @@ EvalState::EvalState()
|
|||
nrAttrsets = nrOpUpdates = nrOpUpdateValuesCopied = 0;
|
||||
deepestStack = (char *) -1;
|
||||
|
||||
createBaseEnv();
|
||||
|
||||
allowUnsafeEquality = getEnv("NIX_NO_UNSAFE_EQ", "") == "";
|
||||
|
||||
#if HAVE_BOEHMGC
|
||||
|
@ -188,6 +186,8 @@ EvalState::EvalState()
|
|||
foreach (Strings::iterator, i, paths) addToSearchPath(*i);
|
||||
addToSearchPath("nix=" + nixDataDir + "/nix/corepkgs");
|
||||
searchPathInsertionPoint = searchPath.begin();
|
||||
|
||||
createBaseEnv();
|
||||
}
|
||||
|
||||
|
||||
|
|
|
@ -356,27 +356,31 @@ static void prim_derivationStrict(EvalState & state, Value * * args, Value & v)
|
|||
inputs to ensure that they are available when the builder
|
||||
runs. */
|
||||
if (path.at(0) == '=') {
|
||||
path = string(path, 1);
|
||||
PathSet refs; computeFSClosure(*store, path, refs);
|
||||
/* !!! This doesn't work if readOnlyMode is set. */
|
||||
PathSet refs; computeFSClosure(*store, string(path, 1), refs);
|
||||
foreach (PathSet::iterator, j, refs) {
|
||||
drv.inputSrcs.insert(*j);
|
||||
if (isDerivation(*j))
|
||||
drv.inputDrvs[*j] = singleton<StringSet>("out");
|
||||
drv.inputDrvs[*j] = store->queryDerivationOutputNames(*j);
|
||||
}
|
||||
}
|
||||
|
||||
/* See prim_unsafeDiscardOutputDependency. */
|
||||
bool useDrvAsSrc = false;
|
||||
if (path.at(0) == '~') {
|
||||
path = string(path, 1);
|
||||
useDrvAsSrc = true;
|
||||
else if (path.at(0) == '~')
|
||||
drv.inputSrcs.insert(string(path, 1));
|
||||
|
||||
/* Handle derivation outputs of the form ‘!<name>!<path>’. */
|
||||
else if (path.at(0) == '!') {
|
||||
size_t index = path.find("!", 1);
|
||||
drv.inputDrvs[string(path, index + 1)].insert(string(path, 1, index - 1));
|
||||
}
|
||||
|
||||
assert(isStorePath(path));
|
||||
/* Handle derivation contexts returned by
|
||||
‘builtins.storePath’. */
|
||||
else if (isDerivation(path))
|
||||
drv.inputDrvs[path] = store->queryDerivationOutputNames(path);
|
||||
|
||||
debug(format("derivation uses `%1%'") % path);
|
||||
if (!useDrvAsSrc && isDerivation(path))
|
||||
drv.inputDrvs[path] = singleton<StringSet>("out");
|
||||
/* Otherwise it's a source file. */
|
||||
else
|
||||
drv.inputSrcs.insert(path);
|
||||
}
|
||||
|
@ -447,10 +451,8 @@ static void prim_derivationStrict(EvalState & state, Value * * args, Value & v)
|
|||
state.mkAttrs(v, 1 + drv.outputs.size());
|
||||
mkString(*state.allocAttr(v, state.sDrvPath), drvPath, singleton<PathSet>("=" + drvPath));
|
||||
foreach (DerivationOutputs::iterator, i, drv.outputs) {
|
||||
/* The output path of an output X is ‘<X>Path’,
|
||||
e.g. ‘outPath’. */
|
||||
mkString(*state.allocAttr(v, state.symbols.create(i->first + "Path")),
|
||||
i->second.path, singleton<PathSet>(drvPath));
|
||||
mkString(*state.allocAttr(v, state.symbols.create(i->first)),
|
||||
i->second.path, singleton<PathSet>("!" + i->first + "!" + drvPath));
|
||||
}
|
||||
v.attrs->sort();
|
||||
}
|
||||
|
@ -1042,15 +1044,6 @@ void EvalState::createBaseEnv()
|
|||
addPrimOp("__getEnv", 1, prim_getEnv);
|
||||
addPrimOp("__trace", 2, prim_trace);
|
||||
|
||||
// Derivations
|
||||
addPrimOp("derivationStrict", 1, prim_derivationStrict);
|
||||
|
||||
/* Add a wrapper around the derivation primop that computes the
|
||||
`drvPath' and `outPath' attributes lazily. */
|
||||
string s = "attrs: let res = derivationStrict attrs; in attrs // { drvPath = res.drvPath; outPath = res.outPath; type = \"derivation\"; }";
|
||||
mkThunk_(v, parseExprFromString(s, "/"));
|
||||
addConstant("derivation", v);
|
||||
|
||||
// Paths
|
||||
addPrimOp("__toPath", 1, prim_toPath);
|
||||
addPrimOp("__storePath", 1, prim_storePath);
|
||||
|
@ -1099,6 +1092,14 @@ void EvalState::createBaseEnv()
|
|||
addPrimOp("__parseDrvName", 1, prim_parseDrvName);
|
||||
addPrimOp("__compareVersions", 2, prim_compareVersions);
|
||||
|
||||
// Derivations
|
||||
addPrimOp("derivationStrict", 1, prim_derivationStrict);
|
||||
|
||||
/* Add a wrapper around the derivation primop that computes the
|
||||
`drvPath' and `outPath' attributes lazily. */
|
||||
mkThunk_(v, parseExprFromFile(findFile("nix/derivation.nix")));
|
||||
addConstant("derivation", v);
|
||||
|
||||
/* Now that we've added all primops, sort the `builtins' attribute
|
||||
set, because attribute lookups expect it to be sorted. */
|
||||
baseEnv.values[0]->attrs->sort();
|
||||
|
|
|
@ -278,10 +278,6 @@ public:
|
|||
};
|
||||
|
||||
|
||||
MakeError(SubstError, Error)
|
||||
MakeError(BuildError, Error) /* denotes a permanent build failure */
|
||||
|
||||
|
||||
//////////////////////////////////////////////////////////////////////
|
||||
|
||||
|
||||
|
@ -1982,7 +1978,8 @@ void DerivationGoal::computeClosure()
|
|||
}
|
||||
|
||||
/* Register each output path as valid, and register the sets of
|
||||
paths referenced by each of them. */
|
||||
paths referenced by each of them. If there are cycles in the
|
||||
outputs, this will fail. */
|
||||
ValidPathInfos infos;
|
||||
foreach (DerivationOutputs::iterator, i, drv.outputs) {
|
||||
ValidPathInfo info;
|
||||
|
|
|
@ -371,36 +371,6 @@ static void addAdditionalRoots(StoreAPI & store, PathSet & roots)
|
|||
}
|
||||
|
||||
|
||||
static void dfsVisit(StoreAPI & store, const PathSet & paths,
|
||||
const Path & path, PathSet & visited, Paths & sorted)
|
||||
{
|
||||
if (visited.find(path) != visited.end()) return;
|
||||
visited.insert(path);
|
||||
|
||||
PathSet references;
|
||||
if (store.isValidPath(path))
|
||||
store.queryReferences(path, references);
|
||||
|
||||
foreach (PathSet::iterator, i, references)
|
||||
/* Don't traverse into paths that don't exist. That can
|
||||
happen due to substitutes for non-existent paths. */
|
||||
if (*i != path && paths.find(*i) != paths.end())
|
||||
dfsVisit(store, paths, *i, visited, sorted);
|
||||
|
||||
sorted.push_front(path);
|
||||
}
|
||||
|
||||
|
||||
Paths topoSortPaths(StoreAPI & store, const PathSet & paths)
|
||||
{
|
||||
Paths sorted;
|
||||
PathSet visited;
|
||||
foreach (PathSet::const_iterator, i, paths)
|
||||
dfsVisit(store, paths, *i, visited, sorted);
|
||||
return sorted;
|
||||
}
|
||||
|
||||
|
||||
struct GCLimitReached { };
|
||||
|
||||
|
||||
|
|
|
@ -820,6 +820,28 @@ PathSet LocalStore::queryDerivationOutputs(const Path & path)
|
|||
}
|
||||
|
||||
|
||||
StringSet LocalStore::queryDerivationOutputNames(const Path & path)
|
||||
{
|
||||
SQLiteTxn txn(db);
|
||||
|
||||
SQLiteStmtUse use(stmtQueryDerivationOutputs);
|
||||
stmtQueryDerivationOutputs.bind(queryValidPathId(path));
|
||||
|
||||
StringSet outputNames;
|
||||
int r;
|
||||
while ((r = sqlite3_step(stmtQueryDerivationOutputs)) == SQLITE_ROW) {
|
||||
const char * s = (const char *) sqlite3_column_text(stmtQueryDerivationOutputs, 0);
|
||||
assert(s);
|
||||
outputNames.insert(s);
|
||||
}
|
||||
|
||||
if (r != SQLITE_DONE)
|
||||
throwSQLiteError(db, format("error getting output names of `%1%'") % path);
|
||||
|
||||
return outputNames;
|
||||
}
|
||||
|
||||
|
||||
void LocalStore::startSubstituter(const Path & substituter, RunningSubstituter & run)
|
||||
{
|
||||
if (run.pid != -1) return;
|
||||
|
@ -944,12 +966,14 @@ void LocalStore::registerValidPaths(const ValidPathInfos & infos)
|
|||
while (1) {
|
||||
try {
|
||||
SQLiteTxn txn(db);
|
||||
PathSet paths;
|
||||
|
||||
foreach (ValidPathInfos::const_iterator, i, infos) {
|
||||
assert(i->hash.type == htSHA256);
|
||||
/* !!! Maybe the registration info should be updated if the
|
||||
path is already valid. */
|
||||
if (!isValidPath(i->path)) addValidPath(*i);
|
||||
paths.insert(i->path);
|
||||
}
|
||||
|
||||
foreach (ValidPathInfos::const_iterator, i, infos) {
|
||||
|
@ -958,6 +982,12 @@ void LocalStore::registerValidPaths(const ValidPathInfos & infos)
|
|||
addReference(referrer, queryValidPathId(*j));
|
||||
}
|
||||
|
||||
/* Do a topological sort of the paths. This will throw an
|
||||
error if a cycle is detected and roll back the
|
||||
transaction. Cycles can only occur when a derivation
|
||||
has multiple outputs. */
|
||||
topoSortPaths(*this, paths);
|
||||
|
||||
txn.commit();
|
||||
break;
|
||||
} catch (SQLiteBusy & e) {
|
||||
|
|
|
@ -119,6 +119,8 @@ public:
|
|||
|
||||
PathSet queryDerivationOutputs(const Path & path);
|
||||
|
||||
StringSet queryDerivationOutputNames(const Path & path);
|
||||
|
||||
PathSet querySubstitutablePaths();
|
||||
|
||||
bool hasSubstitutes(const Path & path);
|
||||
|
|
|
@ -97,4 +97,40 @@ void queryMissing(StoreAPI & store, const PathSet & targets,
|
|||
}
|
||||
|
||||
|
||||
static void dfsVisit(StoreAPI & store, const PathSet & paths,
|
||||
const Path & path, PathSet & visited, Paths & sorted,
|
||||
PathSet & parents)
|
||||
{
|
||||
if (parents.find(path) != parents.end())
|
||||
throw BuildError(format("cycle detected in the references of `%1%'") % path);
|
||||
|
||||
if (visited.find(path) != visited.end()) return;
|
||||
visited.insert(path);
|
||||
parents.insert(path);
|
||||
|
||||
PathSet references;
|
||||
if (store.isValidPath(path))
|
||||
store.queryReferences(path, references);
|
||||
|
||||
foreach (PathSet::iterator, i, references)
|
||||
/* Don't traverse into paths that don't exist. That can
|
||||
happen due to substitutes for non-existent paths. */
|
||||
if (*i != path && paths.find(*i) != paths.end())
|
||||
dfsVisit(store, paths, *i, visited, sorted, parents);
|
||||
|
||||
sorted.push_front(path);
|
||||
parents.erase(path);
|
||||
}
|
||||
|
||||
|
||||
Paths topoSortPaths(StoreAPI & store, const PathSet & paths)
|
||||
{
|
||||
Paths sorted;
|
||||
PathSet visited, parents;
|
||||
foreach (PathSet::const_iterator, i, paths)
|
||||
dfsVisit(store, paths, *i, visited, sorted, parents);
|
||||
return sorted;
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
|
|
@ -326,6 +326,16 @@ PathSet RemoteStore::queryDerivationOutputs(const Path & path)
|
|||
}
|
||||
|
||||
|
||||
PathSet RemoteStore::queryDerivationOutputNames(const Path & path)
|
||||
{
|
||||
openConnection();
|
||||
writeInt(wopQueryDerivationOutputNames, to);
|
||||
writeString(path, to);
|
||||
processStderr();
|
||||
return readStrings<PathSet>(from);
|
||||
}
|
||||
|
||||
|
||||
Path RemoteStore::addToStore(const Path & _srcPath,
|
||||
bool recursive, HashType hashAlgo, PathFilter & filter)
|
||||
{
|
||||
|
|
|
@ -41,6 +41,8 @@ public:
|
|||
|
||||
PathSet queryDerivationOutputs(const Path & path);
|
||||
|
||||
StringSet queryDerivationOutputNames(const Path & path);
|
||||
|
||||
bool hasSubstitutes(const Path & path);
|
||||
|
||||
bool querySubstitutablePathInfo(const Path & path,
|
||||
|
|
|
@ -141,6 +141,9 @@ public:
|
|||
/* Query the outputs of the derivation denoted by `path'. */
|
||||
virtual PathSet queryDerivationOutputs(const Path & path) = 0;
|
||||
|
||||
/* Query the output names of the derivation denoted by `path'. */
|
||||
virtual StringSet queryDerivationOutputNames(const Path & path) = 0;
|
||||
|
||||
/* Query whether a path has substitutes. */
|
||||
virtual bool hasSubstitutes(const Path & path) = 0;
|
||||
|
||||
|
@ -346,6 +349,10 @@ void exportPaths(StoreAPI & store, const Paths & paths,
|
|||
bool sign, Sink & sink);
|
||||
|
||||
|
||||
MakeError(SubstError, Error)
|
||||
MakeError(BuildError, Error) /* denotes a permanent build failure */
|
||||
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
|
|
@ -39,6 +39,7 @@ typedef enum {
|
|||
wopClearFailedPaths = 25,
|
||||
wopQueryPathInfo = 26,
|
||||
wopImportPaths = 27,
|
||||
wopQueryDerivationOutputNames = 28,
|
||||
} WorkerOp;
|
||||
|
||||
|
||||
|
|
|
@ -50,26 +50,30 @@ static Path useDeriver(Path path)
|
|||
}
|
||||
|
||||
|
||||
/* Realisation the given path. For a derivation that means build it;
|
||||
for other paths it means ensure their validity. */
|
||||
static Path realisePath(const Path & path)
|
||||
/* Realise the given path. For a derivation that means build it; for
|
||||
other paths it means ensure their validity. */
|
||||
static PathSet realisePath(const Path & path)
|
||||
{
|
||||
if (isDerivation(path)) {
|
||||
PathSet paths;
|
||||
paths.insert(path);
|
||||
store->buildDerivations(paths);
|
||||
Path outPath = findOutput(derivationFromPath(*store, path), "out");
|
||||
store->buildDerivations(singleton<PathSet>(path));
|
||||
Derivation drv = derivationFromPath(*store, path);
|
||||
|
||||
if (gcRoot == "")
|
||||
printGCWarning();
|
||||
else
|
||||
outPath = addPermRoot(*store, outPath,
|
||||
makeRootName(gcRoot, rootNr), indirectRoot);
|
||||
PathSet outputs;
|
||||
foreach (DerivationOutputs::iterator, i, drv.outputs) {
|
||||
Path outPath = i->second.path;
|
||||
if (gcRoot == "")
|
||||
printGCWarning();
|
||||
else
|
||||
outPath = addPermRoot(*store, outPath,
|
||||
makeRootName(gcRoot, rootNr), indirectRoot);
|
||||
outputs.insert(outPath);
|
||||
}
|
||||
return outputs;
|
||||
}
|
||||
|
||||
return outPath;
|
||||
} else {
|
||||
else {
|
||||
store->ensurePath(path);
|
||||
return path;
|
||||
return singleton<PathSet>(path);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -96,8 +100,11 @@ static void opRealise(Strings opFlags, Strings opArgs)
|
|||
if (isDerivation(*i)) drvPaths.insert(*i);
|
||||
store->buildDerivations(drvPaths);
|
||||
|
||||
foreach (Strings::iterator, i, opArgs)
|
||||
cout << format("%1%\n") % realisePath(*i);
|
||||
foreach (Strings::iterator, i, opArgs) {
|
||||
PathSet paths = realisePath(*i);
|
||||
foreach (PathSet::iterator, j, paths)
|
||||
cout << format("%1%\n") % *j;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
@ -157,14 +164,17 @@ static void opPrintFixedPath(Strings opFlags, Strings opArgs)
|
|||
}
|
||||
|
||||
|
||||
static Path maybeUseOutput(const Path & storePath, bool useOutput, bool forceRealise)
|
||||
static PathSet maybeUseOutputs(const Path & storePath, bool useOutput, bool forceRealise)
|
||||
{
|
||||
if (forceRealise) realisePath(storePath);
|
||||
if (useOutput && isDerivation(storePath)) {
|
||||
Derivation drv = derivationFromPath(*store, storePath);
|
||||
return findOutput(drv, "out");
|
||||
PathSet outputs;
|
||||
foreach (DerivationOutputs::iterator, i, drv.outputs)
|
||||
outputs.insert(i->second.path);
|
||||
return outputs;
|
||||
}
|
||||
else return storePath;
|
||||
else return singleton<PathSet>(storePath);
|
||||
}
|
||||
|
||||
|
||||
|
@ -257,7 +267,8 @@ static void opQuery(Strings opFlags, Strings opArgs)
|
|||
*i = followLinksToStorePath(*i);
|
||||
if (forceRealise) realisePath(*i);
|
||||
Derivation drv = derivationFromPath(*store, *i);
|
||||
cout << format("%1%\n") % findOutput(drv, "out");
|
||||
foreach (DerivationOutputs::iterator, j, drv.outputs)
|
||||
cout << format("%1%\n") % j->second.path;
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
@ -268,11 +279,13 @@ static void opQuery(Strings opFlags, Strings opArgs)
|
|||
case qReferrersClosure: {
|
||||
PathSet paths;
|
||||
foreach (Strings::iterator, i, opArgs) {
|
||||
Path path = maybeUseOutput(followLinksToStorePath(*i), useOutput, forceRealise);
|
||||
if (query == qRequisites) computeFSClosure(*store, path, paths, false, includeOutputs);
|
||||
else if (query == qReferences) store->queryReferences(path, paths);
|
||||
else if (query == qReferrers) store->queryReferrers(path, paths);
|
||||
else if (query == qReferrersClosure) computeFSClosure(*store, path, paths, true);
|
||||
PathSet ps = maybeUseOutputs(followLinksToStorePath(*i), useOutput, forceRealise);
|
||||
foreach (PathSet::iterator, j, ps) {
|
||||
if (query == qRequisites) computeFSClosure(*store, *j, paths, false, includeOutputs);
|
||||
else if (query == qReferences) store->queryReferences(*j, paths);
|
||||
else if (query == qReferrers) store->queryReferrers(*j, paths);
|
||||
else if (query == qReferrersClosure) computeFSClosure(*store, *j, paths, true);
|
||||
}
|
||||
}
|
||||
Paths sorted = topoSortPaths(*store, paths);
|
||||
for (Paths::reverse_iterator i = sorted.rbegin();
|
||||
|
@ -304,13 +317,15 @@ static void opQuery(Strings opFlags, Strings opArgs)
|
|||
case qHash:
|
||||
case qSize:
|
||||
foreach (Strings::iterator, i, opArgs) {
|
||||
Path path = maybeUseOutput(followLinksToStorePath(*i), useOutput, forceRealise);
|
||||
ValidPathInfo info = store->queryPathInfo(path);
|
||||
if (query == qHash) {
|
||||
assert(info.hash.type == htSHA256);
|
||||
cout << format("sha256:%1%\n") % printHash32(info.hash);
|
||||
} else if (query == qSize)
|
||||
cout << format("%1%\n") % info.narSize;
|
||||
PathSet paths = maybeUseOutputs(followLinksToStorePath(*i), useOutput, forceRealise);
|
||||
foreach (PathSet::iterator, j, paths) {
|
||||
ValidPathInfo info = store->queryPathInfo(*j);
|
||||
if (query == qHash) {
|
||||
assert(info.hash.type == htSHA256);
|
||||
cout << format("sha256:%1%\n") % printHash32(info.hash);
|
||||
} else if (query == qSize)
|
||||
cout << format("%1%\n") % info.narSize;
|
||||
}
|
||||
}
|
||||
break;
|
||||
|
||||
|
@ -323,16 +338,20 @@ static void opQuery(Strings opFlags, Strings opArgs)
|
|||
|
||||
case qGraph: {
|
||||
PathSet roots;
|
||||
foreach (Strings::iterator, i, opArgs)
|
||||
roots.insert(maybeUseOutput(followLinksToStorePath(*i), useOutput, forceRealise));
|
||||
foreach (Strings::iterator, i, opArgs) {
|
||||
PathSet paths = maybeUseOutputs(followLinksToStorePath(*i), useOutput, forceRealise);
|
||||
roots.insert(paths.begin(), paths.end());
|
||||
}
|
||||
printDotGraph(roots);
|
||||
break;
|
||||
}
|
||||
|
||||
case qXml: {
|
||||
PathSet roots;
|
||||
foreach (Strings::iterator, i, opArgs)
|
||||
roots.insert(maybeUseOutput(followLinksToStorePath(*i), useOutput, forceRealise));
|
||||
foreach (Strings::iterator, i, opArgs) {
|
||||
PathSet paths = maybeUseOutputs(followLinksToStorePath(*i), useOutput, forceRealise);
|
||||
roots.insert(paths.begin(), paths.end());
|
||||
}
|
||||
printXmlGraph(roots);
|
||||
break;
|
||||
}
|
||||
|
@ -345,10 +364,11 @@ static void opQuery(Strings opFlags, Strings opArgs)
|
|||
|
||||
case qRoots: {
|
||||
PathSet referrers;
|
||||
foreach (Strings::iterator, i, opArgs)
|
||||
computeFSClosure(*store,
|
||||
maybeUseOutput(followLinksToStorePath(*i), useOutput, forceRealise),
|
||||
referrers, true);
|
||||
foreach (Strings::iterator, i, opArgs) {
|
||||
PathSet paths = maybeUseOutputs(followLinksToStorePath(*i), useOutput, forceRealise);
|
||||
foreach (PathSet::iterator, j, paths)
|
||||
computeFSClosure(*store, *j, referrers, true);
|
||||
}
|
||||
Roots roots = store->findRoots();
|
||||
foreach (Roots::iterator, i, roots)
|
||||
if (referrers.find(i->second) != referrers.end())
|
||||
|
|
|
@ -331,6 +331,16 @@ static void performOp(unsigned int clientVersion,
|
|||
break;
|
||||
}
|
||||
|
||||
case wopQueryDerivationOutputNames: {
|
||||
Path path = readStorePath(from);
|
||||
startWork();
|
||||
StringSet names;
|
||||
names = store->queryDerivationOutputNames(path);
|
||||
stopWork();
|
||||
writeStrings(names, to);
|
||||
break;
|
||||
}
|
||||
|
||||
case wopQueryDeriver: {
|
||||
Path path = readStorePath(from);
|
||||
startWork();
|
||||
|
|
|
@ -8,7 +8,8 @@ TESTS = init.sh hash.sh lang.sh add.sh simple.sh dependencies.sh \
|
|||
referrers.sh user-envs.sh logging.sh nix-build.sh misc.sh fixed.sh \
|
||||
gc-runtime.sh install-package.sh check-refs.sh filter-source.sh \
|
||||
remote-store.sh export.sh export-graph.sh negative-caching.sh \
|
||||
binary-patching.sh timeout.sh secure-drv-outputs.sh nix-channel.sh
|
||||
binary-patching.sh timeout.sh secure-drv-outputs.sh nix-channel.sh \
|
||||
multiple-outputs.sh
|
||||
|
||||
XFAIL_TESTS =
|
||||
|
||||
|
@ -35,5 +36,6 @@ EXTRA_DIST = $(TESTS) \
|
|||
binary-patching.nix \
|
||||
timeout.nix timeout.builder.sh \
|
||||
secure-drv-outputs.nix \
|
||||
multiple-outputs.nix \
|
||||
$(wildcard lang/*.nix) $(wildcard lang/*.exp) $(wildcard lang/*.exp.xml) $(wildcard lang/*.flags) $(wildcard lang/dir*/*.nix) \
|
||||
common.sh.in
|
||||
|
|
67
tests/multiple-outputs.nix
Normal file
67
tests/multiple-outputs.nix
Normal file
|
@ -0,0 +1,67 @@
|
|||
with import ./config.nix;
|
||||
|
||||
rec {
|
||||
|
||||
a = mkDerivation {
|
||||
name = "multiple-outputs-a";
|
||||
outputs = [ "first" "second" ];
|
||||
builder = builtins.toFile "builder.sh"
|
||||
''
|
||||
mkdir $first $second
|
||||
test -z $all
|
||||
echo "second" > $first/file
|
||||
echo "first" > $second/file
|
||||
'';
|
||||
helloString = "Hello, world!";
|
||||
};
|
||||
|
||||
b = mkDerivation {
|
||||
defaultOutput = assert a.second.helloString == "Hello, world!"; a;
|
||||
firstOutput = assert a.outputName == "first"; a.first.first;
|
||||
secondOutput = assert a.second.outputName == "second"; a.second.first.first.second.second.first.second;
|
||||
allOutputs = a.all;
|
||||
name = "multiple-outputs-b";
|
||||
builder = builtins.toFile "builder.sh"
|
||||
''
|
||||
mkdir $out
|
||||
test "$firstOutput $secondOutput" = "$allOutputs"
|
||||
test "$defaultOutput" = "$firstOutput"
|
||||
test "$(cat $firstOutput/file)" = "second"
|
||||
test "$(cat $secondOutput/file)" = "first"
|
||||
echo "success" > $out/file
|
||||
'';
|
||||
};
|
||||
|
||||
c = mkDerivation {
|
||||
name = "multiple-outputs-c";
|
||||
drv = b.drvPath;
|
||||
builder = builtins.toFile "builder.sh"
|
||||
''
|
||||
mkdir $out
|
||||
ln -s $drv $out/drv
|
||||
'';
|
||||
};
|
||||
|
||||
d = mkDerivation {
|
||||
name = "multiple-outputs-d";
|
||||
drv = builtins.unsafeDiscardOutputDependency b.drvPath;
|
||||
builder = builtins.toFile "builder.sh"
|
||||
''
|
||||
mkdir $out
|
||||
echo $drv > $out/drv
|
||||
'';
|
||||
};
|
||||
|
||||
cyclic = (mkDerivation {
|
||||
name = "cyclic-outputs";
|
||||
outputs = [ "a" "b" "c" ];
|
||||
builder = builtins.toFile "builder.sh"
|
||||
''
|
||||
mkdir $a $b $c
|
||||
echo $a > $b/foo
|
||||
echo $b > $c/bar
|
||||
echo $c > $a/baz
|
||||
'';
|
||||
}).a;
|
||||
|
||||
}
|
42
tests/multiple-outputs.sh
Normal file
42
tests/multiple-outputs.sh
Normal file
|
@ -0,0 +1,42 @@
|
|||
source common.sh
|
||||
|
||||
clearStore
|
||||
|
||||
# Test whether read-only evaluation works when referring to the
|
||||
# ‘drvPath’ attribute.
|
||||
echo "evaluating c..."
|
||||
#drvPath=$(nix-instantiate multiple-outputs.nix -A c --readonly-mode)
|
||||
|
||||
# And check whether the resulting derivation explicitly depends on all
|
||||
# outputs.
|
||||
drvPath=$(nix-instantiate multiple-outputs.nix -A c)
|
||||
#[ "$drvPath" = "$drvPath2" ]
|
||||
grep -q 'multiple-outputs-a.drv",\["first","second"\]' $drvPath
|
||||
grep -q 'multiple-outputs-b.drv",\["out"\]' $drvPath
|
||||
|
||||
# While we're at it, test the ‘unsafeDiscardOutputDependency’ primop.
|
||||
outPath=$(nix-build multiple-outputs.nix -A d)
|
||||
drvPath=$(cat $outPath/drv)
|
||||
outPath=$(nix-store -q $drvPath)
|
||||
! [ -e "$outPath" ]
|
||||
|
||||
# Do a build of something that depends on a derivation with multiple
|
||||
# outputs.
|
||||
echo "building b..."
|
||||
outPath=$(nix-build multiple-outputs.nix -A b)
|
||||
echo "output path is $outPath"
|
||||
[ "$(cat "$outPath"/file)" = "success" ]
|
||||
|
||||
# Make sure that nix-build works on derivations with multiple outputs.
|
||||
echo "building a.first..."
|
||||
nix-build multiple-outputs.nix -A a.first
|
||||
|
||||
# Cyclic outputs should be rejected.
|
||||
echo "building cyclic..."
|
||||
if nix-build multiple-outputs.nix -A cyclic; then
|
||||
echo "Cyclic outputs incorrectly accepted!"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
echo "collecting garbage..."
|
||||
nix-store --gc
|
Loading…
Reference in a new issue