about summary refs log tree commit diff
diff options
context:
space:
mode:
-rw-r--r--doc/manual/release-notes.xml14
-rw-r--r--doc/manual/writing-nix-expressions.xml19
-rw-r--r--src/libstore/build.cc19
-rw-r--r--tests/check-reqs.nix43
-rw-r--r--tests/check-reqs.sh12
-rw-r--r--tests/local.mk3
6 files changed, 108 insertions, 2 deletions
diff --git a/doc/manual/release-notes.xml b/doc/manual/release-notes.xml
index 426078b829de..36e345ecf273 100644
--- a/doc/manual/release-notes.xml
+++ b/doc/manual/release-notes.xml
@@ -11,6 +11,20 @@
 
 <para>TODO</para>
 
+<itemizedlist>
+
+  <listitem><para>Derivations can specify the new special attribute
+  <varname>allowedRequisites</varname>, which has a similar meaning to
+  <varname>allowedReferences</varname>. But instead of only enforcing
+  to explicitly specify the immediate references, it requires the
+  derivation to specify all the dependencies recursively (hence the
+  name, requisites) that are used by the resulting output. This is
+  used in NixOS when rebuilding the stdenv on Linux to ensure that the
+  resulting stdenv doesn't have any surprising dependency, e.g. on
+  bootstrapTools.</para></listitem>
+
+</itemizedlist>
+
 </section>
 
 
diff --git a/doc/manual/writing-nix-expressions.xml b/doc/manual/writing-nix-expressions.xml
index 0470625ff6d3..2c9b4a633271 100644
--- a/doc/manual/writing-nix-expressions.xml
+++ b/doc/manual/writing-nix-expressions.xml
@@ -1569,6 +1569,25 @@ allowedReferences = [];
 
   </varlistentry>
 
+  <varlistentry><term><varname>allowedRequisites</varname></term>
+
+    <listitem><para>This attribute is similar to
+    <varname>allowedReferences</varname>, but it specifies the legal
+    requisites of the whole closure, so all the dependencies
+    recursively.  For example,
+
+<programlisting>
+allowedReferences = [ foobar ];
+</programlisting>
+
+    enforces that the output of a derivation cannot have any other
+    runtime dependency than <varname>foobar</varname>, and in addition
+    it enforces that <varname>foobar</varname> itself doesn't
+    introduce any other dependency itself. This is used in NixOS when
+    rebuilding the stdenv on Linux to ensure that the resulting stdenv
+    doesn't have any surprising dependency, e.g. on bootstrapTools.
+
+  </varlistentry>
 
   <varlistentry><term><varname>exportReferencesGraph</varname></term>
 
diff --git a/src/libstore/build.cc b/src/libstore/build.cc
index c547a5cbfecf..6390a7480e99 100644
--- a/src/libstore/build.cc
+++ b/src/libstore/build.cc
@@ -2326,7 +2326,24 @@ void DerivationGoal::registerOutputs()
             PathSet allowed = parseReferenceSpecifiers(drv, get(drv.env, "allowedReferences"));
             foreach (PathSet::iterator, i, references)
                 if (allowed.find(*i) == allowed.end())
-                    throw BuildError(format("output is not allowed to refer to path ‘%1%’") % *i);
+                    throw BuildError(format("output (‘%1%’) is not allowed to refer to path ‘%2%’") % actualPath % *i);
+        }
+
+        /* If the derivation specifies an `allowedRequisites'
+           attribute (containing a list of paths that the output may
+           refer to), check that all requisites are in that list.  !!!
+           allowedRequisites should really be per-output. */
+        if (drv.env.find("allowedRequisites") != drv.env.end()) {
+            PathSet allowed = parseReferenceSpecifiers(drv, get(drv.env, "allowedRequisites"));
+            PathSet requisites;
+            /* Our requisites are the union of the closures of our references. */
+            foreach (PathSet::iterator, i, references)
+                /* Don't call computeFSClosure on ourselves. */
+                if (actualPath != *i)
+                    computeFSClosure(worker.store, *i, requisites);
+            foreach (PathSet::iterator, i, requisites)
+                if (allowed.find(*i) == allowed.end())
+                    throw BuildError(format("output (‘%1%’) is not allowed to refer to requisite path ‘%2%’") % actualPath % *i);
         }
 
         worker.store.optimisePath(path); // FIXME: combine with scanForReferences()
diff --git a/tests/check-reqs.nix b/tests/check-reqs.nix
new file mode 100644
index 000000000000..5af8ea6fa185
--- /dev/null
+++ b/tests/check-reqs.nix
@@ -0,0 +1,43 @@
+with import ./config.nix;
+
+rec {
+  dep1 = mkDerivation {
+    name = "check-reqs-dep1";
+    builder = builtins.toFile "builder.sh" "mkdir $out; touch $out/file1";
+  };
+
+  dep2 = mkDerivation {
+    name = "check-reqs-dep2";
+    builder = builtins.toFile "builder.sh" "mkdir $out; touch $out/file2";
+  };
+
+  deps = mkDerivation {
+    name = "check-reqs-deps";
+    dep1 = dep1;
+    dep2 = dep2;
+    builder = builtins.toFile "builder.sh" ''
+      mkdir $out
+      ln -s $dep1/file1 $out/file1
+      ln -s $dep2/file2 $out/file2
+    '';
+  };
+
+  makeTest = nr: allowreqs: mkDerivation {
+    name = "check-reqs-" + toString nr;
+    inherit deps;
+    builder = builtins.toFile "builder.sh" ''
+      mkdir $out
+      ln -s $deps $out/depdir1
+    '';
+    allowedRequisites = allowreqs;
+  };
+
+  # When specifying all the requisites, the build succeeds.
+  test1 = makeTest 1 [ dep1 dep2 deps ];
+
+  # But missing anything it fails.
+  test2 = makeTest 2 [ dep2 deps ];
+  test3 = makeTest 3 [ dep1 deps ];
+  test4 = makeTest 4 [ deps ];
+  test5 = makeTest 5 [];
+}
diff --git a/tests/check-reqs.sh b/tests/check-reqs.sh
new file mode 100644
index 000000000000..643c2d0cdc0d
--- /dev/null
+++ b/tests/check-reqs.sh
@@ -0,0 +1,12 @@
+source common.sh
+
+RESULT=$TEST_ROOT/result
+
+# test1 should succeed.
+nix-build -o $RESULT check-reqs.nix -A test1
+
+# test{2,3,4,5} should fail.
+(! nix-build -o $RESULT check-reqs.nix -A test2)
+(! nix-build -o $RESULT check-reqs.nix -A test3)
+(! nix-build -o $RESULT check-reqs.nix -A test4)
+(! nix-build -o $RESULT check-reqs.nix -A test5)
diff --git a/tests/local.mk b/tests/local.mk
index 65aa12637055..69a227495d94 100644
--- a/tests/local.mk
+++ b/tests/local.mk
@@ -10,7 +10,8 @@ nix_tests = \
   remote-store.sh export.sh export-graph.sh negative-caching.sh \
   binary-patching.sh timeout.sh secure-drv-outputs.sh nix-channel.sh \
   multiple-outputs.sh import-derivation.sh fetchurl.sh optimise-store.sh \
-  binary-cache.sh nix-profile.sh repair.sh dump-db.sh case-hack.sh
+  binary-cache.sh nix-profile.sh repair.sh dump-db.sh case-hack.sh \
+  check-reqs.sh
   # parallel.sh
 
 install-tests += $(foreach x, $(nix_tests), tests/$(x))