* 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/
|
||||||
/externals/Makefile
|
/externals/Makefile
|
||||||
/externals/Makefile.in
|
/externals/Makefile.in
|
||||||
/externals/aterm-*
|
|
||||||
/externals/have-aterm
|
|
||||||
/externals/build-aterm
|
|
||||||
/externals/inst-aterm
|
|
||||||
/externals/bzip2-*
|
/externals/bzip2-*
|
||||||
/externals/have-bzip2
|
|
||||||
/externals/build-bzip2
|
/externals/build-bzip2
|
||||||
/externals/inst-bzip2
|
/externals/inst-bzip2
|
||||||
|
/externals/sqlite-*
|
||||||
# /make/examples/aterm/
|
/externals/build-sqlite
|
||||||
/make/examples/aterm/result*
|
|
||||||
|
|
||||||
# /make/examples/aterm/aterm/
|
|
||||||
/make/examples/aterm/aterm/*
|
|
||||||
|
|
||||||
# /make/examples/aterm/test/
|
|
||||||
/make/examples/aterm/test/*
|
|
||||||
|
|
||||||
# /misc/
|
# /misc/
|
||||||
/misc/Makefile.in
|
/misc/Makefile.in
|
||||||
|
@ -100,13 +88,16 @@
|
||||||
/scripts/nix-channel
|
/scripts/nix-channel
|
||||||
/scripts/nix-build
|
/scripts/nix-build
|
||||||
/scripts/nix-copy-closure
|
/scripts/nix-copy-closure
|
||||||
/scripts/readmanifest.pm
|
/scripts/nix-generate-patches
|
||||||
/scripts/readconfig.pm
|
/scripts/NixConfig.pm
|
||||||
|
/scripts/NixManifest.pm
|
||||||
|
/scripts/GeneratePatches.pm
|
||||||
/scripts/download-using-manifests.pl
|
/scripts/download-using-manifests.pl
|
||||||
/scripts/copy-from-other-stores.pl
|
/scripts/copy-from-other-stores.pl
|
||||||
/scripts/generate-patches.pl
|
|
||||||
/scripts/find-runtime-roots.pl
|
/scripts/find-runtime-roots.pl
|
||||||
/scripts/build-remote.pl
|
/scripts/build-remote.pl
|
||||||
|
/scripts/nix-reduce-build
|
||||||
|
/scripts/nix-http-export.cgi
|
||||||
|
|
||||||
# /src/
|
# /src/
|
||||||
/src/Makefile
|
/src/Makefile
|
||||||
|
@ -168,6 +159,7 @@
|
||||||
/src/libstore/derivations-ast.cc
|
/src/libstore/derivations-ast.cc
|
||||||
/src/libstore/derivations-ast.hh
|
/src/libstore/derivations-ast.hh
|
||||||
/src/libstore/.libs
|
/src/libstore/.libs
|
||||||
|
/src/libstore/schema.sql.hh
|
||||||
|
|
||||||
# /src/libutil/
|
# /src/libutil/
|
||||||
/src/libutil/Makefile
|
/src/libutil/Makefile
|
||||||
|
@ -242,6 +234,7 @@
|
||||||
/tests/config.nix
|
/tests/config.nix
|
||||||
/tests/common.sh
|
/tests/common.sh
|
||||||
/tests/dummy
|
/tests/dummy
|
||||||
|
/tests/result*
|
||||||
|
|
||||||
# /tests/lang/
|
# /tests/lang/
|
||||||
/tests/lang/*.out
|
/tests/lang/*.out
|
||||||
|
|
|
@ -274,8 +274,8 @@ AC_SUBST(sqlite_bin)
|
||||||
# Whether to use the Boehm garbage collector.
|
# Whether to use the Boehm garbage collector.
|
||||||
AC_ARG_ENABLE(gc, AC_HELP_STRING([--enable-gc],
|
AC_ARG_ENABLE(gc, AC_HELP_STRING([--enable-gc],
|
||||||
[enable garbage collection in the Nix expression evaluator (requires Boehm GC)]),
|
[enable garbage collection in the Nix expression evaluator (requires Boehm GC)]),
|
||||||
gc=$enableval, gc=)
|
gc=$enableval, gc=no)
|
||||||
if test -n "$gc"; then
|
if test "$gc" = yes; then
|
||||||
PKG_CHECK_MODULES([BDW_GC], [bdw-gc])
|
PKG_CHECK_MODULES([BDW_GC], [bdw-gc])
|
||||||
CXXFLAGS="$BDW_GC_CFLAGS $CXXFLAGS"
|
CXXFLAGS="$BDW_GC_CFLAGS $CXXFLAGS"
|
||||||
AC_DEFINE(HAVE_BOEHMGC, 1, [Whether to use the Boehm garbage collector.])
|
AC_DEFINE(HAVE_BOEHMGC, 1, [Whether to use the Boehm garbage collector.])
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
all-local: config.nix
|
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-exec-local:
|
||||||
$(INSTALL) -d $(DESTDIR)$(datadir)/nix/corepkgs
|
$(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") {
|
for my $manifestLink (glob "$manifestDir/*.nixmanifest") {
|
||||||
my $manifest = Cwd::abs_path($manifestLink);
|
my $manifest = Cwd::abs_path($manifestLink);
|
||||||
|
next unless -f $manifest;
|
||||||
my $timestamp = lstat($manifest)->mtime;
|
my $timestamp = lstat($manifest)->mtime;
|
||||||
$seen{$manifest} = 1;
|
$seen{$manifest} = 1;
|
||||||
|
|
||||||
|
|
|
@ -148,8 +148,6 @@ EvalState::EvalState()
|
||||||
nrAttrsets = nrOpUpdates = nrOpUpdateValuesCopied = 0;
|
nrAttrsets = nrOpUpdates = nrOpUpdateValuesCopied = 0;
|
||||||
deepestStack = (char *) -1;
|
deepestStack = (char *) -1;
|
||||||
|
|
||||||
createBaseEnv();
|
|
||||||
|
|
||||||
allowUnsafeEquality = getEnv("NIX_NO_UNSAFE_EQ", "") == "";
|
allowUnsafeEquality = getEnv("NIX_NO_UNSAFE_EQ", "") == "";
|
||||||
|
|
||||||
#if HAVE_BOEHMGC
|
#if HAVE_BOEHMGC
|
||||||
|
@ -188,6 +186,8 @@ EvalState::EvalState()
|
||||||
foreach (Strings::iterator, i, paths) addToSearchPath(*i);
|
foreach (Strings::iterator, i, paths) addToSearchPath(*i);
|
||||||
addToSearchPath("nix=" + nixDataDir + "/nix/corepkgs");
|
addToSearchPath("nix=" + nixDataDir + "/nix/corepkgs");
|
||||||
searchPathInsertionPoint = searchPath.begin();
|
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
|
inputs to ensure that they are available when the builder
|
||||||
runs. */
|
runs. */
|
||||||
if (path.at(0) == '=') {
|
if (path.at(0) == '=') {
|
||||||
path = string(path, 1);
|
/* !!! This doesn't work if readOnlyMode is set. */
|
||||||
PathSet refs; computeFSClosure(*store, path, refs);
|
PathSet refs; computeFSClosure(*store, string(path, 1), refs);
|
||||||
foreach (PathSet::iterator, j, refs) {
|
foreach (PathSet::iterator, j, refs) {
|
||||||
drv.inputSrcs.insert(*j);
|
drv.inputSrcs.insert(*j);
|
||||||
if (isDerivation(*j))
|
if (isDerivation(*j))
|
||||||
drv.inputDrvs[*j] = singleton<StringSet>("out");
|
drv.inputDrvs[*j] = store->queryDerivationOutputNames(*j);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/* See prim_unsafeDiscardOutputDependency. */
|
/* See prim_unsafeDiscardOutputDependency. */
|
||||||
bool useDrvAsSrc = false;
|
else if (path.at(0) == '~')
|
||||||
if (path.at(0) == '~') {
|
drv.inputSrcs.insert(string(path, 1));
|
||||||
path = string(path, 1);
|
|
||||||
useDrvAsSrc = true;
|
/* 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);
|
/* Otherwise it's a source file. */
|
||||||
if (!useDrvAsSrc && isDerivation(path))
|
|
||||||
drv.inputDrvs[path] = singleton<StringSet>("out");
|
|
||||||
else
|
else
|
||||||
drv.inputSrcs.insert(path);
|
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());
|
state.mkAttrs(v, 1 + drv.outputs.size());
|
||||||
mkString(*state.allocAttr(v, state.sDrvPath), drvPath, singleton<PathSet>("=" + drvPath));
|
mkString(*state.allocAttr(v, state.sDrvPath), drvPath, singleton<PathSet>("=" + drvPath));
|
||||||
foreach (DerivationOutputs::iterator, i, drv.outputs) {
|
foreach (DerivationOutputs::iterator, i, drv.outputs) {
|
||||||
/* The output path of an output X is ‘<X>Path’,
|
mkString(*state.allocAttr(v, state.symbols.create(i->first)),
|
||||||
e.g. ‘outPath’. */
|
i->second.path, singleton<PathSet>("!" + i->first + "!" + drvPath));
|
||||||
mkString(*state.allocAttr(v, state.symbols.create(i->first + "Path")),
|
|
||||||
i->second.path, singleton<PathSet>(drvPath));
|
|
||||||
}
|
}
|
||||||
v.attrs->sort();
|
v.attrs->sort();
|
||||||
}
|
}
|
||||||
|
@ -1042,15 +1044,6 @@ void EvalState::createBaseEnv()
|
||||||
addPrimOp("__getEnv", 1, prim_getEnv);
|
addPrimOp("__getEnv", 1, prim_getEnv);
|
||||||
addPrimOp("__trace", 2, prim_trace);
|
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
|
// Paths
|
||||||
addPrimOp("__toPath", 1, prim_toPath);
|
addPrimOp("__toPath", 1, prim_toPath);
|
||||||
addPrimOp("__storePath", 1, prim_storePath);
|
addPrimOp("__storePath", 1, prim_storePath);
|
||||||
|
@ -1099,6 +1092,14 @@ void EvalState::createBaseEnv()
|
||||||
addPrimOp("__parseDrvName", 1, prim_parseDrvName);
|
addPrimOp("__parseDrvName", 1, prim_parseDrvName);
|
||||||
addPrimOp("__compareVersions", 2, prim_compareVersions);
|
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
|
/* Now that we've added all primops, sort the `builtins' attribute
|
||||||
set, because attribute lookups expect it to be sorted. */
|
set, because attribute lookups expect it to be sorted. */
|
||||||
baseEnv.values[0]->attrs->sort();
|
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
|
/* 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;
|
ValidPathInfos infos;
|
||||||
foreach (DerivationOutputs::iterator, i, drv.outputs) {
|
foreach (DerivationOutputs::iterator, i, drv.outputs) {
|
||||||
ValidPathInfo info;
|
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 { };
|
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)
|
void LocalStore::startSubstituter(const Path & substituter, RunningSubstituter & run)
|
||||||
{
|
{
|
||||||
if (run.pid != -1) return;
|
if (run.pid != -1) return;
|
||||||
|
@ -944,12 +966,14 @@ void LocalStore::registerValidPaths(const ValidPathInfos & infos)
|
||||||
while (1) {
|
while (1) {
|
||||||
try {
|
try {
|
||||||
SQLiteTxn txn(db);
|
SQLiteTxn txn(db);
|
||||||
|
PathSet paths;
|
||||||
|
|
||||||
foreach (ValidPathInfos::const_iterator, i, infos) {
|
foreach (ValidPathInfos::const_iterator, i, infos) {
|
||||||
assert(i->hash.type == htSHA256);
|
assert(i->hash.type == htSHA256);
|
||||||
/* !!! Maybe the registration info should be updated if the
|
/* !!! Maybe the registration info should be updated if the
|
||||||
path is already valid. */
|
path is already valid. */
|
||||||
if (!isValidPath(i->path)) addValidPath(*i);
|
if (!isValidPath(i->path)) addValidPath(*i);
|
||||||
|
paths.insert(i->path);
|
||||||
}
|
}
|
||||||
|
|
||||||
foreach (ValidPathInfos::const_iterator, i, infos) {
|
foreach (ValidPathInfos::const_iterator, i, infos) {
|
||||||
|
@ -958,6 +982,12 @@ void LocalStore::registerValidPaths(const ValidPathInfos & infos)
|
||||||
addReference(referrer, queryValidPathId(*j));
|
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();
|
txn.commit();
|
||||||
break;
|
break;
|
||||||
} catch (SQLiteBusy & e) {
|
} catch (SQLiteBusy & e) {
|
||||||
|
|
|
@ -119,6 +119,8 @@ public:
|
||||||
|
|
||||||
PathSet queryDerivationOutputs(const Path & path);
|
PathSet queryDerivationOutputs(const Path & path);
|
||||||
|
|
||||||
|
StringSet queryDerivationOutputNames(const Path & path);
|
||||||
|
|
||||||
PathSet querySubstitutablePaths();
|
PathSet querySubstitutablePaths();
|
||||||
|
|
||||||
bool hasSubstitutes(const Path & path);
|
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,
|
Path RemoteStore::addToStore(const Path & _srcPath,
|
||||||
bool recursive, HashType hashAlgo, PathFilter & filter)
|
bool recursive, HashType hashAlgo, PathFilter & filter)
|
||||||
{
|
{
|
||||||
|
|
|
@ -41,6 +41,8 @@ public:
|
||||||
|
|
||||||
PathSet queryDerivationOutputs(const Path & path);
|
PathSet queryDerivationOutputs(const Path & path);
|
||||||
|
|
||||||
|
StringSet queryDerivationOutputNames(const Path & path);
|
||||||
|
|
||||||
bool hasSubstitutes(const Path & path);
|
bool hasSubstitutes(const Path & path);
|
||||||
|
|
||||||
bool querySubstitutablePathInfo(const Path & path,
|
bool querySubstitutablePathInfo(const Path & path,
|
||||||
|
|
|
@ -141,6 +141,9 @@ public:
|
||||||
/* Query the outputs of the derivation denoted by `path'. */
|
/* Query the outputs of the derivation denoted by `path'. */
|
||||||
virtual PathSet queryDerivationOutputs(const Path & path) = 0;
|
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. */
|
/* Query whether a path has substitutes. */
|
||||||
virtual bool hasSubstitutes(const Path & path) = 0;
|
virtual bool hasSubstitutes(const Path & path) = 0;
|
||||||
|
|
||||||
|
@ -346,6 +349,10 @@ void exportPaths(StoreAPI & store, const Paths & paths,
|
||||||
bool sign, Sink & sink);
|
bool sign, Sink & sink);
|
||||||
|
|
||||||
|
|
||||||
|
MakeError(SubstError, Error)
|
||||||
|
MakeError(BuildError, Error) /* denotes a permanent build failure */
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -39,6 +39,7 @@ typedef enum {
|
||||||
wopClearFailedPaths = 25,
|
wopClearFailedPaths = 25,
|
||||||
wopQueryPathInfo = 26,
|
wopQueryPathInfo = 26,
|
||||||
wopImportPaths = 27,
|
wopImportPaths = 27,
|
||||||
|
wopQueryDerivationOutputNames = 28,
|
||||||
} WorkerOp;
|
} WorkerOp;
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -50,26 +50,30 @@ static Path useDeriver(Path path)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/* Realisation the given path. For a derivation that means build it;
|
/* Realise the given path. For a derivation that means build it; for
|
||||||
for other paths it means ensure their validity. */
|
other paths it means ensure their validity. */
|
||||||
static Path realisePath(const Path & path)
|
static PathSet realisePath(const Path & path)
|
||||||
{
|
{
|
||||||
if (isDerivation(path)) {
|
if (isDerivation(path)) {
|
||||||
PathSet paths;
|
store->buildDerivations(singleton<PathSet>(path));
|
||||||
paths.insert(path);
|
Derivation drv = derivationFromPath(*store, path);
|
||||||
store->buildDerivations(paths);
|
|
||||||
Path outPath = findOutput(derivationFromPath(*store, path), "out");
|
|
||||||
|
|
||||||
if (gcRoot == "")
|
PathSet outputs;
|
||||||
printGCWarning();
|
foreach (DerivationOutputs::iterator, i, drv.outputs) {
|
||||||
else
|
Path outPath = i->second.path;
|
||||||
outPath = addPermRoot(*store, outPath,
|
if (gcRoot == "")
|
||||||
makeRootName(gcRoot, rootNr), indirectRoot);
|
printGCWarning();
|
||||||
|
else
|
||||||
|
outPath = addPermRoot(*store, outPath,
|
||||||
|
makeRootName(gcRoot, rootNr), indirectRoot);
|
||||||
|
outputs.insert(outPath);
|
||||||
|
}
|
||||||
|
return outputs;
|
||||||
|
}
|
||||||
|
|
||||||
return outPath;
|
else {
|
||||||
} else {
|
|
||||||
store->ensurePath(path);
|
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);
|
if (isDerivation(*i)) drvPaths.insert(*i);
|
||||||
store->buildDerivations(drvPaths);
|
store->buildDerivations(drvPaths);
|
||||||
|
|
||||||
foreach (Strings::iterator, i, opArgs)
|
foreach (Strings::iterator, i, opArgs) {
|
||||||
cout << format("%1%\n") % realisePath(*i);
|
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 (forceRealise) realisePath(storePath);
|
||||||
if (useOutput && isDerivation(storePath)) {
|
if (useOutput && isDerivation(storePath)) {
|
||||||
Derivation drv = derivationFromPath(*store, 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);
|
*i = followLinksToStorePath(*i);
|
||||||
if (forceRealise) realisePath(*i);
|
if (forceRealise) realisePath(*i);
|
||||||
Derivation drv = derivationFromPath(*store, *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;
|
break;
|
||||||
}
|
}
|
||||||
|
@ -268,11 +279,13 @@ static void opQuery(Strings opFlags, Strings opArgs)
|
||||||
case qReferrersClosure: {
|
case qReferrersClosure: {
|
||||||
PathSet paths;
|
PathSet paths;
|
||||||
foreach (Strings::iterator, i, opArgs) {
|
foreach (Strings::iterator, i, opArgs) {
|
||||||
Path path = maybeUseOutput(followLinksToStorePath(*i), useOutput, forceRealise);
|
PathSet ps = maybeUseOutputs(followLinksToStorePath(*i), useOutput, forceRealise);
|
||||||
if (query == qRequisites) computeFSClosure(*store, path, paths, false, includeOutputs);
|
foreach (PathSet::iterator, j, ps) {
|
||||||
else if (query == qReferences) store->queryReferences(path, paths);
|
if (query == qRequisites) computeFSClosure(*store, *j, paths, false, includeOutputs);
|
||||||
else if (query == qReferrers) store->queryReferrers(path, paths);
|
else if (query == qReferences) store->queryReferences(*j, paths);
|
||||||
else if (query == qReferrersClosure) computeFSClosure(*store, path, paths, true);
|
else if (query == qReferrers) store->queryReferrers(*j, paths);
|
||||||
|
else if (query == qReferrersClosure) computeFSClosure(*store, *j, paths, true);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
Paths sorted = topoSortPaths(*store, paths);
|
Paths sorted = topoSortPaths(*store, paths);
|
||||||
for (Paths::reverse_iterator i = sorted.rbegin();
|
for (Paths::reverse_iterator i = sorted.rbegin();
|
||||||
|
@ -304,13 +317,15 @@ static void opQuery(Strings opFlags, Strings opArgs)
|
||||||
case qHash:
|
case qHash:
|
||||||
case qSize:
|
case qSize:
|
||||||
foreach (Strings::iterator, i, opArgs) {
|
foreach (Strings::iterator, i, opArgs) {
|
||||||
Path path = maybeUseOutput(followLinksToStorePath(*i), useOutput, forceRealise);
|
PathSet paths = maybeUseOutputs(followLinksToStorePath(*i), useOutput, forceRealise);
|
||||||
ValidPathInfo info = store->queryPathInfo(path);
|
foreach (PathSet::iterator, j, paths) {
|
||||||
if (query == qHash) {
|
ValidPathInfo info = store->queryPathInfo(*j);
|
||||||
assert(info.hash.type == htSHA256);
|
if (query == qHash) {
|
||||||
cout << format("sha256:%1%\n") % printHash32(info.hash);
|
assert(info.hash.type == htSHA256);
|
||||||
} else if (query == qSize)
|
cout << format("sha256:%1%\n") % printHash32(info.hash);
|
||||||
cout << format("%1%\n") % info.narSize;
|
} else if (query == qSize)
|
||||||
|
cout << format("%1%\n") % info.narSize;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
|
|
||||||
|
@ -323,16 +338,20 @@ static void opQuery(Strings opFlags, Strings opArgs)
|
||||||
|
|
||||||
case qGraph: {
|
case qGraph: {
|
||||||
PathSet roots;
|
PathSet roots;
|
||||||
foreach (Strings::iterator, i, opArgs)
|
foreach (Strings::iterator, i, opArgs) {
|
||||||
roots.insert(maybeUseOutput(followLinksToStorePath(*i), useOutput, forceRealise));
|
PathSet paths = maybeUseOutputs(followLinksToStorePath(*i), useOutput, forceRealise);
|
||||||
|
roots.insert(paths.begin(), paths.end());
|
||||||
|
}
|
||||||
printDotGraph(roots);
|
printDotGraph(roots);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
case qXml: {
|
case qXml: {
|
||||||
PathSet roots;
|
PathSet roots;
|
||||||
foreach (Strings::iterator, i, opArgs)
|
foreach (Strings::iterator, i, opArgs) {
|
||||||
roots.insert(maybeUseOutput(followLinksToStorePath(*i), useOutput, forceRealise));
|
PathSet paths = maybeUseOutputs(followLinksToStorePath(*i), useOutput, forceRealise);
|
||||||
|
roots.insert(paths.begin(), paths.end());
|
||||||
|
}
|
||||||
printXmlGraph(roots);
|
printXmlGraph(roots);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
@ -345,10 +364,11 @@ static void opQuery(Strings opFlags, Strings opArgs)
|
||||||
|
|
||||||
case qRoots: {
|
case qRoots: {
|
||||||
PathSet referrers;
|
PathSet referrers;
|
||||||
foreach (Strings::iterator, i, opArgs)
|
foreach (Strings::iterator, i, opArgs) {
|
||||||
computeFSClosure(*store,
|
PathSet paths = maybeUseOutputs(followLinksToStorePath(*i), useOutput, forceRealise);
|
||||||
maybeUseOutput(followLinksToStorePath(*i), useOutput, forceRealise),
|
foreach (PathSet::iterator, j, paths)
|
||||||
referrers, true);
|
computeFSClosure(*store, *j, referrers, true);
|
||||||
|
}
|
||||||
Roots roots = store->findRoots();
|
Roots roots = store->findRoots();
|
||||||
foreach (Roots::iterator, i, roots)
|
foreach (Roots::iterator, i, roots)
|
||||||
if (referrers.find(i->second) != referrers.end())
|
if (referrers.find(i->second) != referrers.end())
|
||||||
|
|
|
@ -331,6 +331,16 @@ static void performOp(unsigned int clientVersion,
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
case wopQueryDerivationOutputNames: {
|
||||||
|
Path path = readStorePath(from);
|
||||||
|
startWork();
|
||||||
|
StringSet names;
|
||||||
|
names = store->queryDerivationOutputNames(path);
|
||||||
|
stopWork();
|
||||||
|
writeStrings(names, to);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
case wopQueryDeriver: {
|
case wopQueryDeriver: {
|
||||||
Path path = readStorePath(from);
|
Path path = readStorePath(from);
|
||||||
startWork();
|
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 \
|
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 \
|
gc-runtime.sh install-package.sh check-refs.sh filter-source.sh \
|
||||||
remote-store.sh export.sh export-graph.sh negative-caching.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 =
|
XFAIL_TESTS =
|
||||||
|
|
||||||
|
@ -35,5 +36,6 @@ EXTRA_DIST = $(TESTS) \
|
||||||
binary-patching.nix \
|
binary-patching.nix \
|
||||||
timeout.nix timeout.builder.sh \
|
timeout.nix timeout.builder.sh \
|
||||||
secure-drv-outputs.nix \
|
secure-drv-outputs.nix \
|
||||||
|
multiple-outputs.nix \
|
||||||
$(wildcard lang/*.nix) $(wildcard lang/*.exp) $(wildcard lang/*.exp.xml) $(wildcard lang/*.flags) $(wildcard lang/dir*/*.nix) \
|
$(wildcard lang/*.nix) $(wildcard lang/*.exp) $(wildcard lang/*.exp.xml) $(wildcard lang/*.flags) $(wildcard lang/dir*/*.nix) \
|
||||||
common.sh.in
|
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