diff --git a/.version b/.version
index 7208c2182..f398a2061 100644
--- a/.version
+++ b/.version
@@ -1 +1 @@
-2.4
\ No newline at end of file
+3.0
\ No newline at end of file
diff --git a/doc/manual/command-ref/conf-file.xml b/doc/manual/command-ref/conf-file.xml
index 1fa74a143..9c55526a3 100644
--- a/doc/manual/command-ref/conf-file.xml
+++ b/doc/manual/command-ref/conf-file.xml
@@ -370,34 +370,6 @@ false.
- hashed-mirrors
-
- A list of web servers used by
- builtins.fetchurl to obtain files by
- hash. The default is
- http://tarballs.nixos.org/. Given a hash type
- ht and a base-16 hash
- h, Nix will try to download the file
- from
- hashed-mirror/ht/h.
- This allows files to be downloaded even if they have disappeared
- from their original URI. For example, given the default mirror
- http://tarballs.nixos.org/, when building the derivation
-
-
-builtins.fetchurl {
- url = "https://example.org/foo-1.2.3.tar.xz";
- sha256 = "2c26b46b68ffc68ff99b453c1d30413413422d706483bfa0f98a5e886266e7ae";
-}
-
-
- Nix will attempt to download this file from
- http://tarballs.nixos.org/sha256/2c26b46b68ffc68ff99b453c1d30413413422d706483bfa0f98a5e886266e7ae
- first. If it is not available there, if will try the original URI.
-
-
-
-
http-connections
The maximum number of parallel TCP connections
diff --git a/doc/manual/expressions/builder-syntax.xml b/doc/manual/expressions/builder-syntax.xml
deleted file mode 100644
index e51bade44..000000000
--- a/doc/manual/expressions/builder-syntax.xml
+++ /dev/null
@@ -1,119 +0,0 @@
-
-
-Builder Syntax
-
-Build script for GNU Hello
-(builder.sh)
-
-source $stdenv/setup
-
-PATH=$perl/bin:$PATH
-
-tar xvfz $src
-cd hello-*
-./configure --prefix=$out
-make
-make install
-
-
- shows the builder referenced
-from Hello's Nix expression (stored in
-pkgs/applications/misc/hello/ex-1/builder.sh).
-The builder can actually be made a lot shorter by using the
-generic builder functions provided by
-stdenv, but here we write out the build steps to
-elucidate what a builder does. It performs the following
-steps:
-
-
-
-
-
- When Nix runs a builder, it initially completely clears the
- environment (except for the attributes declared in the
- derivation). For instance, the PATH variable is
- emptyActually, it's initialised to
- /path-not-set to prevent Bash from setting it
- to a default value.. This is done to prevent
- undeclared inputs from being used in the build process. If for
- example the PATH contained
- /usr/bin, then you might accidentally use
- /usr/bin/gcc.
-
- So the first step is to set up the environment. This is
- done by calling the setup script of the
- standard environment. The environment variable
- stdenv points to the location of the standard
- environment being used. (It wasn't specified explicitly as an
- attribute in , but
- mkDerivation adds it automatically.)
-
-
-
-
-
- Since Hello needs Perl, we have to make sure that Perl is in
- the PATH. The perl environment
- variable points to the location of the Perl package (since it
- was passed in as an attribute to the derivation), so
- $perl/bin is the
- directory containing the Perl interpreter.
-
-
-
-
-
- Now we have to unpack the sources. The
- src attribute was bound to the result of
- fetching the Hello source tarball from the network, so the
- src environment variable points to the location in
- the Nix store to which the tarball was downloaded. After
- unpacking, we cd to the resulting source
- directory.
-
- The whole build is performed in a temporary directory
- created in /tmp, by the way. This directory is
- removed after the builder finishes, so there is no need to clean
- up the sources afterwards. Also, the temporary directory is
- always newly created, so you don't have to worry about files from
- previous builds interfering with the current build.
-
-
-
-
-
- GNU Hello is a typical Autoconf-based package, so we first
- have to run its configure script. In Nix
- every package is stored in a separate location in the Nix store,
- for instance
- /nix/store/9a54ba97fb71b65fda531012d0443ce2-hello-2.1.1.
- Nix computes this path by cryptographically hashing all attributes
- of the derivation. The path is passed to the builder through the
- out environment variable. So here we give
- configure the parameter
- --prefix=$out to cause Hello to be installed in
- the expected location.
-
-
-
-
-
- Finally we build Hello (make) and install
- it into the location specified by out
- (make install).
-
-
-
-
-
-If you are wondering about the absence of error checking on the
-result of various commands called in the builder: this is because the
-shell script is evaluated with Bash's option,
-which causes the script to be aborted if any command fails without an
-error check.
-
-
\ No newline at end of file
diff --git a/mk/precompiled-headers.mk b/mk/precompiled-headers.mk
index 1c0452dc2..500c99e4a 100644
--- a/mk/precompiled-headers.mk
+++ b/mk/precompiled-headers.mk
@@ -21,13 +21,13 @@ clean-files += $(GCH) $(PCH)
ifeq ($(PRECOMPILE_HEADERS), 1)
- ifeq ($(CXX), g++)
+ ifeq ($(findstring g++,$(CXX)), g++)
GLOBAL_CXXFLAGS_PCH += -include $(buildprefix)precompiled-headers.h -Winvalid-pch
GLOBAL_ORDER_AFTER += $(GCH)
- else ifeq ($(CXX), clang++)
+ else ifeq ($(findstring clang++,$(CXX)), clang++)
GLOBAL_CXXFLAGS_PCH += -include-pch $(PCH) -Winvalid-pch
diff --git a/perl/lib/Nix/Store.xs b/perl/lib/Nix/Store.xs
index f14c3f73f..de60fb939 100644
--- a/perl/lib/Nix/Store.xs
+++ b/perl/lib/Nix/Store.xs
@@ -80,7 +80,7 @@ SV * queryReferences(char * path)
SV * queryPathHash(char * path)
PPCODE:
try {
- auto s = store()->queryPathInfo(store()->parseStorePath(path))->narHash.to_string(Base32, true);
+ auto s = store()->queryPathInfo(store()->parseStorePath(path))->narHash->to_string(Base32, true);
XPUSHs(sv_2mortal(newSVpv(s.c_str(), 0)));
} catch (Error & e) {
croak("%s", e.what());
@@ -106,7 +106,7 @@ SV * queryPathInfo(char * path, int base32)
XPUSHs(&PL_sv_undef);
else
XPUSHs(sv_2mortal(newSVpv(store()->printStorePath(*info->deriver).c_str(), 0)));
- auto s = info->narHash.to_string(base32 ? Base32 : Base16, true);
+ auto s = info->narHash->to_string(base32 ? Base32 : Base16, true);
XPUSHs(sv_2mortal(newSVpv(s.c_str(), 0)));
mXPUSHi(info->registrationTime);
mXPUSHi(info->narSize);
@@ -224,7 +224,7 @@ SV * hashString(char * algo, int base32, char * s)
SV * convertHash(char * algo, char * s, int toBase32)
PPCODE:
try {
- Hash h(s, parseHashType(algo));
+ auto h = Hash::parseAny(s, parseHashType(algo));
string s = h.to_string(toBase32 ? Base32 : Base16, false);
XPUSHs(sv_2mortal(newSVpv(s.c_str(), 0)));
} catch (Error & e) {
@@ -285,7 +285,7 @@ SV * addToStore(char * srcPath, int recursive, char * algo)
SV * makeFixedOutputPath(int recursive, char * algo, char * hash, char * name)
PPCODE:
try {
- Hash h(hash, parseHashType(algo));
+ auto h = Hash::parseAny(hash, parseHashType(algo));
auto method = recursive ? FileIngestionMethod::Recursive : FileIngestionMethod::Flat;
auto path = store()->makeFixedOutputPath(method, h, name);
XPUSHs(sv_2mortal(newSVpv(store()->printStorePath(path).c_str(), 0)));
@@ -304,7 +304,10 @@ SV * derivationFromPath(char * drvPath)
HV * outputs = newHV();
for (auto & i : drv.outputs)
- hv_store(outputs, i.first.c_str(), i.first.size(), newSVpv(store()->printStorePath(i.second.path).c_str(), 0), 0);
+ hv_store(
+ outputs, i.first.c_str(), i.first.size(),
+ newSVpv(store()->printStorePath(i.second.path(*store(), drv.name)).c_str(), 0),
+ 0);
hv_stores(hash, "outputs", newRV((SV *) outputs));
AV * inputDrvs = newAV();
diff --git a/scripts/install-multi-user.sh b/scripts/install-multi-user.sh
index 00c9d540b..e5cc4d7ed 100644
--- a/scripts/install-multi-user.sh
+++ b/scripts/install-multi-user.sh
@@ -37,6 +37,8 @@ readonly PROFILE_NIX_FILE="$NIX_ROOT/var/nix/profiles/default/etc/profile.d/nix-
readonly NIX_INSTALLED_NIX="@nix@"
readonly NIX_INSTALLED_CACERT="@cacert@"
+#readonly NIX_INSTALLED_NIX="/nix/store/j8dbv5w6jl34caywh2ygdy88knx1mdf7-nix-2.3.6"
+#readonly NIX_INSTALLED_CACERT="/nix/store/7dxhzymvy330i28ii676fl1pqwcahv2f-nss-cacert-3.49.2"
readonly EXTRACTED_NIX_PATH="$(dirname "$0")"
readonly ROOT_HOME=$(echo ~root)
@@ -69,9 +71,11 @@ uninstall_directions() {
subheader "Uninstalling nix:"
local step=0
- if poly_service_installed_check; then
+ if [ -e /run/systemd/system ] && poly_service_installed_check; then
step=$((step + 1))
poly_service_uninstall_directions "$step"
+ else
+ step=$((step + 1))
fi
for profile_target in "${PROFILE_TARGETS[@]}"; do
@@ -250,7 +254,9 @@ function finish_success {
echo "But fetching the nixpkgs channel failed. (Are you offline?)"
echo "To try again later, run \"sudo -i nix-channel --update nixpkgs\"."
fi
- cat <&2
-elif [ "$(uname -s)" = "Linux" ] && [ -e /run/systemd/system ]; then
+elif [ "$(uname -s)" = "Linux" ]; then
echo "Note: a multi-user installation is possible. See https://nixos.org/nix/manual/#sect-multi-user-installation" >&2
fi
@@ -122,7 +122,7 @@ if [ "$(uname -s)" = "Darwin" ]; then
fi
if [ "$INSTALL_MODE" = "daemon" ]; then
- printf '\e[1;31mSwitching to the Daemon-based Installer\e[0m\n'
+ printf '\e[1;31mSwitching to the Multi-user Installer\e[0m\n'
exec "$self/install-multi-user"
exit 0
fi
diff --git a/src/build-remote/build-remote.cc b/src/build-remote/build-remote.cc
index e07117496..3579d8fff 100644
--- a/src/build-remote/build-remote.cc
+++ b/src/build-remote/build-remote.cc
@@ -33,7 +33,7 @@ std::string escapeUri(std::string uri)
static string currentLoad;
-static AutoCloseFD openSlotLock(const Machine & m, unsigned long long slot)
+static AutoCloseFD openSlotLock(const Machine & m, uint64_t slot)
{
return openLockFile(fmt("%s/%s-%d", currentLoad, escapeUri(m.storeUri), slot), true);
}
@@ -119,7 +119,7 @@ static int _main(int argc, char * * argv)
bool rightType = false;
Machine * bestMachine = nullptr;
- unsigned long long bestLoad = 0;
+ uint64_t bestLoad = 0;
for (auto & m : machines) {
debug("considering building on remote machine '%s'", m.storeUri);
@@ -130,8 +130,8 @@ static int _main(int argc, char * * argv)
m.mandatoryMet(requiredFeatures)) {
rightType = true;
AutoCloseFD free;
- unsigned long long load = 0;
- for (unsigned long long slot = 0; slot < m.maxJobs; ++slot) {
+ uint64_t load = 0;
+ for (uint64_t slot = 0; slot < m.maxJobs; ++slot) {
auto slotLock = openSlotLock(m, slot);
if (lockFile(slotLock.get(), ltWrite, false)) {
if (!free) {
diff --git a/src/libexpr/eval-cache.cc b/src/libexpr/eval-cache.cc
index 919de8a4e..deb32484f 100644
--- a/src/libexpr/eval-cache.cc
+++ b/src/libexpr/eval-cache.cc
@@ -285,11 +285,10 @@ static std::shared_ptr makeAttrDb(const Hash & fingerprint)
}
EvalCache::EvalCache(
- bool useCache,
- const Hash & fingerprint,
+ std::optional> useCache,
EvalState & state,
RootLoader rootLoader)
- : db(useCache ? makeAttrDb(fingerprint) : nullptr)
+ : db(useCache ? makeAttrDb(*useCache) : nullptr)
, state(state)
, rootLoader(rootLoader)
{
diff --git a/src/libexpr/eval-cache.hh b/src/libexpr/eval-cache.hh
index 674bb03c1..afee85fa9 100644
--- a/src/libexpr/eval-cache.hh
+++ b/src/libexpr/eval-cache.hh
@@ -4,6 +4,7 @@
#include "hash.hh"
#include "eval.hh"
+#include
#include
namespace nix::eval_cache {
@@ -26,8 +27,7 @@ class EvalCache : public std::enable_shared_from_this
public:
EvalCache(
- bool useCache,
- const Hash & fingerprint,
+ std::optional> useCache,
EvalState & state,
RootLoader rootLoader);
diff --git a/src/libexpr/eval.cc b/src/libexpr/eval.cc
index 7a2f55504..0123070d1 100644
--- a/src/libexpr/eval.cc
+++ b/src/libexpr/eval.cc
@@ -345,6 +345,7 @@ EvalState::EvalState(const Strings & _searchPath, ref store)
, sStructuredAttrs(symbols.create("__structuredAttrs"))
, sBuilder(symbols.create("builder"))
, sArgs(symbols.create("args"))
+ , sContentAddressed(symbols.create("__contentAddressed"))
, sOutputHash(symbols.create("outputHash"))
, sOutputHashAlgo(symbols.create("outputHashAlgo"))
, sOutputHashMode(symbols.create("outputHashMode"))
@@ -1256,10 +1257,10 @@ void EvalState::callFunction(Value & fun, Value & arg, Value & v, const Pos & po
try {
lambda.body->eval(*this, env2, v);
} catch (Error & e) {
- addErrorTrace(e, lambda.pos, "while evaluating %s",
- (lambda.name.set()
- ? "'" + (string) lambda.name + "'"
- : "anonymous lambdaction"));
+ addErrorTrace(e, lambda.pos, "while evaluating %s",
+ (lambda.name.set()
+ ? "'" + (string) lambda.name + "'"
+ : "anonymous lambda"));
addErrorTrace(e, pos, "from call site%s", "");
throw;
}
diff --git a/src/libexpr/eval.hh b/src/libexpr/eval.hh
index 8986952e3..0382298b3 100644
--- a/src/libexpr/eval.hh
+++ b/src/libexpr/eval.hh
@@ -74,6 +74,7 @@ public:
sSystem, sOverrides, sOutputs, sOutputName, sIgnoreNulls,
sFile, sLine, sColumn, sFunctor, sToString,
sRight, sWrong, sStructuredAttrs, sBuilder, sArgs,
+ sContentAddressed,
sOutputHash, sOutputHashAlgo, sOutputHashMode,
sRecurseForDerivations,
sDescription, sSelf, sEpsilon;
diff --git a/src/libexpr/flake/flake.hh b/src/libexpr/flake/flake.hh
index 77f3abdeb..c2bb2888b 100644
--- a/src/libexpr/flake/flake.hh
+++ b/src/libexpr/flake/flake.hh
@@ -106,6 +106,6 @@ void emitTreeAttrs(
EvalState & state,
const fetchers::Tree & tree,
const fetchers::Input & input,
- Value & v);
+ Value & v, bool emptyRevFallback = false);
}
diff --git a/src/libexpr/get-drvs.cc b/src/libexpr/get-drvs.cc
index 9055f59a1..5d6e39aa0 100644
--- a/src/libexpr/get-drvs.cc
+++ b/src/libexpr/get-drvs.cc
@@ -39,7 +39,7 @@ DrvInfo::DrvInfo(EvalState & state, ref store, const std::string & drvPat
if (i == drv.outputs.end())
throw Error("derivation '%s' does not have output '%s'", store->printStorePath(drvPath), outputName);
- outPath = store->printStorePath(i->second.path);
+ outPath = store->printStorePath(i->second.path(*store, drv.name));
}
diff --git a/src/libexpr/primops.cc b/src/libexpr/primops.cc
index 9f877f765..65d36ca0e 100644
--- a/src/libexpr/primops.cc
+++ b/src/libexpr/primops.cc
@@ -52,7 +52,7 @@ void EvalState::realiseContext(const PathSet & context)
DerivationOutputs::iterator i = drv.outputs.find(outputName);
if (i == drv.outputs.end())
throw Error("derivation '%s' does not have an output named '%s'", ctxS, outputName);
- allowedPaths->insert(store->printStorePath(i->second.path));
+ allowedPaths->insert(store->printStorePath(i->second.path(*store, drv.name)));
}
}
}
@@ -65,7 +65,7 @@ void EvalState::realiseContext(const PathSet & context)
/* For performance, prefetch all substitute info. */
StorePathSet willBuild, willSubstitute, unknown;
- unsigned long long downloadSize, narSize;
+ uint64_t downloadSize, narSize;
store->queryMissing(drvs, willBuild, willSubstitute, unknown, downloadSize, narSize);
store->buildPaths(drvs);
@@ -91,8 +91,17 @@ static void prim_scopedImport(EvalState & state, const Pos & pos, Value * * args
Path realPath = state.checkSourcePath(state.toRealPath(path, context));
// FIXME
- if (state.store->isStorePath(path) && state.store->isValidPath(state.store->parseStorePath(path)) && isDerivation(path)) {
- Derivation drv = readDerivation(*state.store, realPath);
+ auto isValidDerivationInStore = [&]() -> std::optional {
+ if (!state.store->isStorePath(path))
+ return std::nullopt;
+ auto storePath = state.store->parseStorePath(path);
+ if (!(state.store->isValidPath(storePath) && isDerivation(path)))
+ return std::nullopt;
+ return storePath;
+ };
+ if (auto optStorePath = isValidDerivationInStore()) {
+ auto storePath = *optStorePath;
+ Derivation drv = readDerivation(*state.store, realPath, Derivation::nameFromPath(storePath));
Value & w = *state.allocValue();
state.mkAttrs(w, 3 + drv.outputs.size());
Value * v2 = state.allocAttr(w, state.sDrvPath);
@@ -106,7 +115,7 @@ static void prim_scopedImport(EvalState & state, const Pos & pos, Value * * args
for (const auto & o : drv.outputs) {
v2 = state.allocAttr(w, state.symbols.create(o.first));
- mkString(*v2, state.store->printStorePath(o.second.path), {"!" + o.first + "!" + path});
+ mkString(*v2, state.store->printStorePath(o.second.path(*state.store, drv.name)), {"!" + o.first + "!" + path});
outputsVal->listElems()[outputs_index] = state.allocValue();
mkString(*(outputsVal->listElems()[outputs_index++]), o.first);
}
@@ -570,9 +579,11 @@ static void prim_derivationStrict(EvalState & state, const Pos & pos, Value * *
/* Build the derivation expression by processing the attributes. */
Derivation drv;
+ drv.name = drvName;
PathSet context;
+ bool contentAddressed = false;
std::optional outputHash;
std::string outputHashAlgo;
auto ingestionMethod = FileIngestionMethod::Flat;
@@ -629,9 +640,14 @@ static void prim_derivationStrict(EvalState & state, const Pos & pos, Value * *
if (i->value->type == tNull) continue;
}
+ if (i->name == state.sContentAddressed) {
+ settings.requireExperimentalFeature("ca-derivations");
+ contentAddressed = state.forceBool(*i->value, pos);
+ }
+
/* The `args' attribute is special: it supplies the
command-line arguments to the builder. */
- if (i->name == state.sArgs) {
+ else if (i->name == state.sArgs) {
state.forceList(*i->value, pos);
for (unsigned int n = 0; n < i->value->listSize(); ++n) {
string s = state.coerceToString(posDrvName, *i->value->listElems()[n], context, true);
@@ -684,7 +700,7 @@ static void prim_derivationStrict(EvalState & state, const Pos & pos, Value * *
}
} catch (Error & e) {
- e.addTrace(posDrvName,
+ e.addTrace(posDrvName,
"while evaluating the attribute '%1%' of the derivation '%2%'",
key, drvName);
throw;
@@ -751,7 +767,10 @@ static void prim_derivationStrict(EvalState & state, const Pos & pos, Value * *
});
if (outputHash) {
- /* Handle fixed-output derivations. */
+ /* Handle fixed-output derivations.
+
+ Ignore `__contentAddressed` because fixed output derivations are
+ already content addressed. */
if (outputs.size() != 1 || *(outputs.begin()) != "out")
throw Error({
.hint = hintfmt("multiple outputs are not supported in fixed-output derivations"),
@@ -764,14 +783,28 @@ static void prim_derivationStrict(EvalState & state, const Pos & pos, Value * *
auto outPath = state.store->makeFixedOutputPath(ingestionMethod, h, drvName);
if (!jsonObject) drv.env["out"] = state.store->printStorePath(outPath);
drv.outputs.insert_or_assign("out", DerivationOutput {
- .path = std::move(outPath),
- .hash = FixedOutputHash {
- .method = ingestionMethod,
- .hash = std::move(h),
- },
+ .output = DerivationOutputCAFixed {
+ .hash = FixedOutputHash {
+ .method = ingestionMethod,
+ .hash = std::move(h),
+ },
+ },
});
}
+ else if (contentAddressed) {
+ HashType ht = parseHashType(outputHashAlgo);
+ for (auto & i : outputs) {
+ if (!jsonObject) drv.env[i] = hashPlaceholder(i);
+ drv.outputs.insert_or_assign(i, DerivationOutput {
+ .output = DerivationOutputCAFloating {
+ .method = ingestionMethod,
+ .hashType = std::move(ht),
+ },
+ });
+ }
+ }
+
else {
/* Compute a hash over the "masked" store derivation, which is
the final one except that in the list of outputs, the
@@ -783,20 +816,24 @@ static void prim_derivationStrict(EvalState & state, const Pos & pos, Value * *
if (!jsonObject) drv.env[i] = "";
drv.outputs.insert_or_assign(i,
DerivationOutput {
- .path = StorePath::dummy,
- .hash = std::optional {},
+ .output = DerivationOutputInputAddressed {
+ .path = StorePath::dummy,
+ },
});
}
- Hash h = hashDerivationModulo(*state.store, Derivation(drv), true);
+ // Regular, non-CA derivation should always return a single hash and not
+ // hash per output.
+ Hash h = std::get<0>(hashDerivationModulo(*state.store, Derivation(drv), true));
for (auto & i : outputs) {
auto outPath = state.store->makeOutputPath(i, h, drvName);
if (!jsonObject) drv.env[i] = state.store->printStorePath(outPath);
drv.outputs.insert_or_assign(i,
DerivationOutput {
- .path = std::move(outPath),
- .hash = std::optional(),
+ .output = DerivationOutputInputAddressed {
+ .path = std::move(outPath),
+ },
});
}
}
@@ -817,7 +854,7 @@ static void prim_derivationStrict(EvalState & state, const Pos & pos, Value * *
mkString(*state.allocAttr(v, state.sDrvPath), drvPathS, {"=" + drvPathS});
for (auto & i : drv.outputs) {
mkString(*state.allocAttr(v, state.symbols.create(i.first)),
- state.store->printStorePath(i.second.path), {"!" + i.first + "!" + drvPathS});
+ state.store->printStorePath(i.second.path(*state.store, drv.name)), {"!" + i.first + "!" + drvPathS});
}
v.attrs->sort();
}
@@ -1111,7 +1148,7 @@ static void prim_toFile(EvalState & state, const Pos & pos, Value * * args, Valu
static void addPath(EvalState & state, const Pos & pos, const string & name, const Path & path_,
- Value * filterFun, FileIngestionMethod method, const Hash & expectedHash, Value & v)
+ Value * filterFun, FileIngestionMethod method, const std::optional expectedHash, Value & v)
{
const auto path = evalSettings.pureEval && expectedHash ?
path_ :
@@ -1142,7 +1179,7 @@ static void addPath(EvalState & state, const Pos & pos, const string & name, con
std::optional expectedStorePath;
if (expectedHash)
- expectedStorePath = state.store->makeFixedOutputPath(method, expectedHash, name);
+ expectedStorePath = state.store->makeFixedOutputPath(method, *expectedHash, name);
Path dstPath;
if (!expectedHash || !state.store->isValidPath(*expectedStorePath)) {
dstPath = state.store->printStorePath(settings.readOnlyMode
@@ -1176,7 +1213,7 @@ static void prim_filterSource(EvalState & state, const Pos & pos, Value * * args
.errPos = pos
});
- addPath(state, pos, std::string(baseNameOf(path)), path, args[0], FileIngestionMethod::Recursive, Hash(), v);
+ addPath(state, pos, std::string(baseNameOf(path)), path, args[0], FileIngestionMethod::Recursive, std::nullopt, v);
}
static void prim_path(EvalState & state, const Pos & pos, Value * * args, Value & v)
@@ -1186,7 +1223,7 @@ static void prim_path(EvalState & state, const Pos & pos, Value * * args, Value
string name;
Value * filterFun = nullptr;
auto method = FileIngestionMethod::Recursive;
- Hash expectedHash;
+ std::optional expectedHash;
for (auto & attr : *args[0]->attrs) {
const string & n(attr.name);
diff --git a/src/libexpr/primops/fetchGit.cc b/src/libexpr/primops/fetchGit.cc
deleted file mode 100644
index 5013e74f0..000000000
--- a/src/libexpr/primops/fetchGit.cc
+++ /dev/null
@@ -1,91 +0,0 @@
-#include "primops.hh"
-#include "eval-inline.hh"
-#include "store-api.hh"
-#include "hash.hh"
-#include "fetchers.hh"
-#include "url.hh"
-
-namespace nix {
-
-static void prim_fetchGit(EvalState & state, const Pos & pos, Value * * args, Value & v)
-{
- std::string url;
- std::optional ref;
- std::optional rev;
- std::string name = "source";
- bool fetchSubmodules = false;
- PathSet context;
-
- state.forceValue(*args[0]);
-
- if (args[0]->type == tAttrs) {
-
- state.forceAttrs(*args[0], pos);
-
- for (auto & attr : *args[0]->attrs) {
- string n(attr.name);
- if (n == "url")
- url = state.coerceToString(*attr.pos, *attr.value, context, false, false);
- else if (n == "ref")
- ref = state.forceStringNoCtx(*attr.value, *attr.pos);
- else if (n == "rev")
- rev = Hash(state.forceStringNoCtx(*attr.value, *attr.pos), htSHA1);
- else if (n == "name")
- name = state.forceStringNoCtx(*attr.value, *attr.pos);
- else if (n == "submodules")
- fetchSubmodules = state.forceBool(*attr.value, *attr.pos);
- else
- throw EvalError({
- .hint = hintfmt("unsupported argument '%s' to 'fetchGit'", attr.name),
- .errPos = *attr.pos
- });
- }
-
- if (url.empty())
- throw EvalError({
- .hint = hintfmt("'url' argument required"),
- .errPos = pos
- });
-
- } else
- url = state.coerceToString(pos, *args[0], context, false, false);
-
- // FIXME: git externals probably can be used to bypass the URI
- // whitelist. Ah well.
- state.checkURI(url);
-
- if (evalSettings.pureEval && !rev)
- throw Error("in pure evaluation mode, 'fetchGit' requires a Git revision");
-
- fetchers::Attrs attrs;
- attrs.insert_or_assign("type", "git");
- attrs.insert_or_assign("url", url.find("://") != std::string::npos ? url : "file://" + url);
- if (ref) attrs.insert_or_assign("ref", *ref);
- if (rev) attrs.insert_or_assign("rev", rev->gitRev());
- if (fetchSubmodules) attrs.insert_or_assign("submodules", fetchers::Explicit{true});
- auto input = fetchers::Input::fromAttrs(std::move(attrs));
-
- // FIXME: use name?
- auto [tree, input2] = input.fetch(state.store);
-
- state.mkAttrs(v, 8);
- auto storePath = state.store->printStorePath(tree.storePath);
- mkString(*state.allocAttr(v, state.sOutPath), storePath, PathSet({storePath}));
- // Backward compatibility: set 'rev' to
- // 0000000000000000000000000000000000000000 for a dirty tree.
- auto rev2 = input2.getRev().value_or(Hash(htSHA1));
- mkString(*state.allocAttr(v, state.symbols.create("rev")), rev2.gitRev());
- mkString(*state.allocAttr(v, state.symbols.create("shortRev")), rev2.gitShortRev());
- // Backward compatibility: set 'revCount' to 0 for a dirty tree.
- mkInt(*state.allocAttr(v, state.symbols.create("revCount")),
- input2.getRevCount().value_or(0));
- mkBool(*state.allocAttr(v, state.symbols.create("submodules")), fetchSubmodules);
- v.attrs->sort();
-
- if (state.allowedPaths)
- state.allowedPaths->insert(tree.actualPath);
-}
-
-static RegisterPrimOp r("fetchGit", 1, prim_fetchGit);
-
-}
diff --git a/src/libexpr/primops/fetchMercurial.cc b/src/libexpr/primops/fetchMercurial.cc
index fc2a6a1c2..cef85cfef 100644
--- a/src/libexpr/primops/fetchMercurial.cc
+++ b/src/libexpr/primops/fetchMercurial.cc
@@ -31,7 +31,7 @@ static void prim_fetchMercurial(EvalState & state, const Pos & pos, Value * * ar
// be both a revision or a branch/tag name.
auto value = state.forceStringNoCtx(*attr.value, *attr.pos);
if (std::regex_match(value, revRegex))
- rev = Hash(value, htSHA1);
+ rev = Hash::parseAny(value, htSHA1);
else
ref = value;
}
diff --git a/src/libexpr/primops/fetchTree.cc b/src/libexpr/primops/fetchTree.cc
index 6a796f3d3..cddcf0e59 100644
--- a/src/libexpr/primops/fetchTree.cc
+++ b/src/libexpr/primops/fetchTree.cc
@@ -14,7 +14,8 @@ void emitTreeAttrs(
EvalState & state,
const fetchers::Tree & tree,
const fetchers::Input & input,
- Value & v)
+ Value & v,
+ bool emptyRevFallback)
{
assert(input.isImmutable());
@@ -34,10 +35,20 @@ void emitTreeAttrs(
if (auto rev = input.getRev()) {
mkString(*state.allocAttr(v, state.symbols.create("rev")), rev->gitRev());
mkString(*state.allocAttr(v, state.symbols.create("shortRev")), rev->gitShortRev());
+ } else if (emptyRevFallback) {
+ // Backwards compat for `builtins.fetchGit`: dirty repos return an empty sha1 as rev
+ auto emptyHash = Hash(htSHA1);
+ mkString(*state.allocAttr(v, state.symbols.create("rev")), emptyHash.gitRev());
+ mkString(*state.allocAttr(v, state.symbols.create("shortRev")), emptyHash.gitRev());
}
+ if (input.getType() == "git")
+ mkBool(*state.allocAttr(v, state.symbols.create("submodules")), maybeGetBoolAttr(input.attrs, "submodules").value_or(false));
+
if (auto revCount = input.getRevCount())
mkInt(*state.allocAttr(v, state.symbols.create("revCount")), *revCount);
+ else if (emptyRevFallback)
+ mkInt(*state.allocAttr(v, state.symbols.create("revCount")), 0);
if (auto lastModified = input.getLastModified()) {
mkInt(*state.allocAttr(v, state.symbols.create("lastModified")), *lastModified);
@@ -48,10 +59,26 @@ void emitTreeAttrs(
v.attrs->sort();
}
-static void prim_fetchTree(EvalState & state, const Pos & pos, Value * * args, Value & v)
+std::string fixURI(std::string uri, EvalState &state)
{
- settings.requireExperimentalFeature("flakes");
+ state.checkURI(uri);
+ return uri.find("://") != std::string::npos ? uri : "file://" + uri;
+}
+void addURI(EvalState &state, fetchers::Attrs &attrs, Symbol name, std::string v)
+{
+ string n(name);
+ attrs.emplace(name, n == "url" ? fixURI(v, state) : v);
+}
+
+static void fetchTree(
+ EvalState &state,
+ const Pos &pos,
+ Value **args,
+ Value &v,
+ const std::optional type,
+ bool emptyRevFallback = false
+) {
fetchers::Input input;
PathSet context;
@@ -64,8 +91,15 @@ static void prim_fetchTree(EvalState & state, const Pos & pos, Value * * args, V
for (auto & attr : *args[0]->attrs) {
state.forceValue(*attr.value);
- if (attr.value->type == tString)
- attrs.emplace(attr.name, attr.value->string.s);
+ if (attr.value->type == tPath || attr.value->type == tString)
+ addURI(
+ state,
+ attrs,
+ attr.name,
+ state.coerceToString(*attr.pos, *attr.value, context, false, false)
+ );
+ else if (attr.value->type == tString)
+ addURI(state, attrs, attr.name, attr.value->string.s);
else if (attr.value->type == tBool)
attrs.emplace(attr.name, fetchers::Explicit{attr.value->boolean});
else if (attr.value->type == tInt)
@@ -75,6 +109,9 @@ static void prim_fetchTree(EvalState & state, const Pos & pos, Value * * args, V
attr.name, showType(*attr.value));
}
+ if (type)
+ attrs.emplace("type", type.value());
+
if (!attrs.count("type"))
throw Error({
.hint = hintfmt("attribute 'type' is missing in call to 'fetchTree'"),
@@ -82,8 +119,18 @@ static void prim_fetchTree(EvalState & state, const Pos & pos, Value * * args, V
});
input = fetchers::Input::fromAttrs(std::move(attrs));
- } else
- input = fetchers::Input::fromURL(state.coerceToString(pos, *args[0], context, false, false));
+ } else {
+ auto url = fixURI(state.coerceToString(pos, *args[0], context, false, false), state);
+
+ if (type == "git") {
+ fetchers::Attrs attrs;
+ attrs.emplace("type", "git");
+ attrs.emplace("url", url);
+ input = fetchers::Input::fromAttrs(std::move(attrs));
+ } else {
+ input = fetchers::Input::fromURL(url);
+ }
+ }
if (!evalSettings.pureEval && !input.isDirect())
input = lookupInRegistries(state.store, input).first;
@@ -96,7 +143,13 @@ static void prim_fetchTree(EvalState & state, const Pos & pos, Value * * args, V
if (state.allowedPaths)
state.allowedPaths->insert(tree.actualPath);
- emitTreeAttrs(state, tree, input2, v);
+ emitTreeAttrs(state, tree, input2, v, emptyRevFallback);
+}
+
+static void prim_fetchTree(EvalState & state, const Pos & pos, Value * * args, Value & v)
+{
+ settings.requireExperimentalFeature("flakes");
+ fetchTree(state, pos, args, v, std::nullopt);
}
static RegisterPrimOp r("fetchTree", 1, prim_fetchTree);
@@ -159,7 +212,7 @@ static void fetch(EvalState & state, const Pos & pos, Value * * args, Value & v,
: hashFile(htSHA256, path);
if (hash != *expectedHash)
throw Error((unsigned int) 102, "hash mismatch in file downloaded from '%s':\n wanted: %s\n got: %s",
- *url, expectedHash->to_string(Base32, true), hash.to_string(Base32, true));
+ *url, expectedHash->to_string(Base32, true), hash->to_string(Base32, true));
}
if (state.allowedPaths)
@@ -178,7 +231,13 @@ static void prim_fetchTarball(EvalState & state, const Pos & pos, Value * * args
fetch(state, pos, args, v, "fetchTarball", true, "source");
}
+static void prim_fetchGit(EvalState &state, const Pos &pos, Value **args, Value &v)
+{
+ fetchTree(state, pos, args, v, "git", true);
+}
+
static RegisterPrimOp r2("__fetchurl", 1, prim_fetchurl);
static RegisterPrimOp r3("fetchTarball", 1, prim_fetchTarball);
+static RegisterPrimOp r4("fetchGit", 1, prim_fetchGit);
}
diff --git a/src/libfetchers/fetchers.cc b/src/libfetchers/fetchers.cc
index 2b6173df9..9c69fc564 100644
--- a/src/libfetchers/fetchers.cc
+++ b/src/libfetchers/fetchers.cc
@@ -130,12 +130,12 @@ std::pair Input::fetch(ref store) const
tree.actualPath = store->toRealPath(tree.storePath);
auto narHash = store->queryPathInfo(tree.storePath)->narHash;
- input.attrs.insert_or_assign("narHash", narHash.to_string(SRI, true));
+ input.attrs.insert_or_assign("narHash", narHash->to_string(SRI, true));
if (auto prevNarHash = getNarHash()) {
if (narHash != *prevNarHash)
- throw Error("NAR hash mismatch in input '%s' (%s), expected '%s', got '%s'",
- to_string(), tree.actualPath, prevNarHash->to_string(SRI, true), narHash.to_string(SRI, true));
+ throw Error((unsigned int) 102, "NAR hash mismatch in input '%s' (%s), expected '%s', got '%s'",
+ to_string(), tree.actualPath, prevNarHash->to_string(SRI, true), narHash->to_string(SRI, true));
}
if (auto prevLastModified = getLastModified()) {
@@ -200,9 +200,12 @@ std::string Input::getType() const
std::optional Input::getNarHash() const
{
- if (auto s = maybeGetStrAttr(attrs, "narHash"))
- // FIXME: require SRI hash.
- return newHashAllowEmpty(*s, htSHA256);
+ if (auto s = maybeGetStrAttr(attrs, "narHash")) {
+ auto hash = s->empty() ? Hash(htSHA256) : Hash::parseSRI(*s);
+ if (hash.type != htSHA256)
+ throw UsageError("narHash must use SHA-256");
+ return hash;
+ }
return {};
}
@@ -216,7 +219,7 @@ std::optional Input::getRef() const
std::optional Input::getRev() const
{
if (auto s = maybeGetStrAttr(attrs, "rev"))
- return Hash(*s, htSHA1);
+ return Hash::parseAny(*s, htSHA1);
return {};
}
diff --git a/src/libfetchers/git.cc b/src/libfetchers/git.cc
index 5d38e0c2b..8b6e047f1 100644
--- a/src/libfetchers/git.cc
+++ b/src/libfetchers/git.cc
@@ -121,7 +121,7 @@ struct GitInputScheme : InputScheme
args.push_back(*ref);
}
- if (input.getRev()) throw Error("cloning a specific revision is not implemented");
+ if (input.getRev()) throw UnimplementedError("cloning a specific revision is not implemented");
args.push_back(destDir);
@@ -293,14 +293,14 @@ struct GitInputScheme : InputScheme
if (!input.getRev())
input.attrs.insert_or_assign("rev",
- Hash(chomp(runProgram("git", true, { "-C", actualUrl, "rev-parse", *input.getRef() })), htSHA1).gitRev());
+ Hash::parseAny(chomp(runProgram("git", true, { "-C", actualUrl, "rev-parse", *input.getRef() })), htSHA1).gitRev());
repoDir = actualUrl;
} else {
if (auto res = getCache()->lookup(store, mutableAttrs)) {
- auto rev2 = Hash(getStrAttr(res->first, "rev"), htSHA1);
+ auto rev2 = Hash::parseAny(getStrAttr(res->first, "rev"), htSHA1);
if (!input.getRev() || input.getRev() == rev2) {
input.attrs.insert_or_assign("rev", rev2.gitRev());
return makeResult(res->first, std::move(res->second));
@@ -370,7 +370,7 @@ struct GitInputScheme : InputScheme
}
if (!input.getRev())
- input.attrs.insert_or_assign("rev", Hash(chomp(readFile(localRefFile)), htSHA1).gitRev());
+ input.attrs.insert_or_assign("rev", Hash::parseAny(chomp(readFile(localRefFile)), htSHA1).gitRev());
}
bool isShallow = chomp(runProgram("git", true, { "-C", repoDir, "rev-parse", "--is-shallow-repository" })) == "true";
diff --git a/src/libfetchers/github.cc b/src/libfetchers/github.cc
index 8bb7c2c1d..9f84ffb68 100644
--- a/src/libfetchers/github.cc
+++ b/src/libfetchers/github.cc
@@ -29,7 +29,7 @@ struct GitArchiveInputScheme : InputScheme
if (path.size() == 2) {
} else if (path.size() == 3) {
if (std::regex_match(path[2], revRegex))
- rev = Hash(path[2], htSHA1);
+ rev = Hash::parseAny(path[2], htSHA1);
else if (std::regex_match(path[2], refRegex))
ref = path[2];
else
@@ -41,7 +41,7 @@ struct GitArchiveInputScheme : InputScheme
if (name == "rev") {
if (rev)
throw BadURL("URL '%s' contains multiple commit hashes", url.url);
- rev = Hash(value, htSHA1);
+ rev = Hash::parseAny(value, htSHA1);
}
else if (name == "ref") {
if (!std::regex_match(value, refRegex))
@@ -191,7 +191,7 @@ struct GitHubInputScheme : GitArchiveInputScheme
readFile(
store->toRealPath(
downloadFile(store, url, "source", false).storePath)));
- auto rev = Hash(std::string { json["sha"] }, htSHA1);
+ auto rev = Hash::parseAny(std::string { json["sha"] }, htSHA1);
debug("HEAD revision for '%s' is %s", url, rev.gitRev());
return rev;
}
@@ -235,7 +235,7 @@ struct GitLabInputScheme : GitArchiveInputScheme
readFile(
store->toRealPath(
downloadFile(store, url, "source", false).storePath)));
- auto rev = Hash(std::string(json[0]["id"]), htSHA1);
+ auto rev = Hash::parseAny(std::string(json[0]["id"]), htSHA1);
debug("HEAD revision for '%s' is %s", url, rev.gitRev());
return rev;
}
diff --git a/src/libfetchers/indirect.cc b/src/libfetchers/indirect.cc
index 91dc83740..b981d4d8e 100644
--- a/src/libfetchers/indirect.cc
+++ b/src/libfetchers/indirect.cc
@@ -18,7 +18,7 @@ struct IndirectInputScheme : InputScheme
if (path.size() == 1) {
} else if (path.size() == 2) {
if (std::regex_match(path[1], revRegex))
- rev = Hash(path[1], htSHA1);
+ rev = Hash::parseAny(path[1], htSHA1);
else if (std::regex_match(path[1], refRegex))
ref = path[1];
else
@@ -29,7 +29,7 @@ struct IndirectInputScheme : InputScheme
ref = path[1];
if (!std::regex_match(path[2], revRegex))
throw BadURL("in flake URL '%s', '%s' is not a commit hash", url.url, path[2]);
- rev = Hash(path[2], htSHA1);
+ rev = Hash::parseAny(path[2], htSHA1);
} else
throw BadURL("GitHub URL '%s' is invalid", url.url);
diff --git a/src/libfetchers/mercurial.cc b/src/libfetchers/mercurial.cc
index c48cb6fd1..3e76ffc4d 100644
--- a/src/libfetchers/mercurial.cc
+++ b/src/libfetchers/mercurial.cc
@@ -209,7 +209,7 @@ struct MercurialInputScheme : InputScheme
});
if (auto res = getCache()->lookup(store, mutableAttrs)) {
- auto rev2 = Hash(getStrAttr(res->first, "rev"), htSHA1);
+ auto rev2 = Hash::parseAny(getStrAttr(res->first, "rev"), htSHA1);
if (!input.getRev() || input.getRev() == rev2) {
input.attrs.insert_or_assign("rev", rev2.gitRev());
return makeResult(res->first, std::move(res->second));
@@ -252,7 +252,7 @@ struct MercurialInputScheme : InputScheme
runProgram("hg", true, { "log", "-R", cacheDir, "-r", revOrRef, "--template", "{node} {rev} {branch}" }));
assert(tokens.size() == 3);
- input.attrs.insert_or_assign("rev", Hash(tokens[0], htSHA1).gitRev());
+ input.attrs.insert_or_assign("rev", Hash::parseAny(tokens[0], htSHA1).gitRev());
auto revCount = std::stoull(tokens[1]);
input.attrs.insert_or_assign("ref", tokens[2]);
diff --git a/src/libmain/shared.cc b/src/libmain/shared.cc
index 52718c231..2b1f25ca3 100644
--- a/src/libmain/shared.cc
+++ b/src/libmain/shared.cc
@@ -36,7 +36,7 @@ void printGCWarning()
void printMissing(ref store, const std::vector & paths, Verbosity lvl)
{
- unsigned long long downloadSize, narSize;
+ uint64_t downloadSize, narSize;
StorePathSet willBuild, willSubstitute, unknown;
store->queryMissing(paths, willBuild, willSubstitute, unknown, downloadSize, narSize);
printMissing(store, willBuild, willSubstitute, unknown, downloadSize, narSize, lvl);
@@ -45,7 +45,7 @@ void printMissing(ref store, const std::vector & pa
void printMissing(ref store, const StorePathSet & willBuild,
const StorePathSet & willSubstitute, const StorePathSet & unknown,
- unsigned long long downloadSize, unsigned long long narSize, Verbosity lvl)
+ uint64_t downloadSize, uint64_t narSize, Verbosity lvl)
{
if (!willBuild.empty()) {
if (willBuild.size() == 1)
@@ -384,7 +384,7 @@ RunPager::~RunPager()
}
-string showBytes(unsigned long long bytes)
+string showBytes(uint64_t bytes)
{
return (format("%.2f MiB") % (bytes / (1024.0 * 1024.0))).str();
}
diff --git a/src/libmain/shared.hh b/src/libmain/shared.hh
index f558247c0..ffae5d796 100644
--- a/src/libmain/shared.hh
+++ b/src/libmain/shared.hh
@@ -47,7 +47,7 @@ void printMissing(
void printMissing(ref store, const StorePathSet & willBuild,
const StorePathSet & willSubstitute, const StorePathSet & unknown,
- unsigned long long downloadSize, unsigned long long narSize, Verbosity lvl = lvlInfo);
+ uint64_t downloadSize, uint64_t narSize, Verbosity lvl = lvlInfo);
string getArg(const string & opt,
Strings::iterator & i, const Strings::iterator & end);
@@ -110,7 +110,7 @@ extern volatile ::sig_atomic_t blockInt;
/* GC helpers. */
-string showBytes(unsigned long long bytes);
+string showBytes(uint64_t bytes);
struct GCResults;
diff --git a/src/libstore/binary-cache-store.cc b/src/libstore/binary-cache-store.cc
index b791c125b..9682db730 100644
--- a/src/libstore/binary-cache-store.cc
+++ b/src/libstore/binary-cache-store.cc
@@ -153,6 +153,8 @@ void BinaryCacheStore::addToStore(const ValidPathInfo & info, Source & narSource
auto [fdTemp, fnTemp] = createTempFile();
+ AutoDelete autoDelete(fnTemp);
+
auto now1 = std::chrono::steady_clock::now();
/* Read the NAR simultaneously into a CompressionSink+FileSink (to
@@ -167,6 +169,7 @@ void BinaryCacheStore::addToStore(const ValidPathInfo & info, Source & narSource
TeeSource teeSource(narSource, *compressionSink);
narAccessor = makeNarAccessor(teeSource);
compressionSink->finish();
+ fileSink.flush();
}
auto now2 = std::chrono::steady_clock::now();
@@ -178,7 +181,7 @@ void BinaryCacheStore::addToStore(const ValidPathInfo & info, Source & narSource
auto [fileHash, fileSize] = fileHashSink.finish();
narInfo->fileHash = fileHash;
narInfo->fileSize = fileSize;
- narInfo->url = "nar/" + narInfo->fileHash.to_string(Base32, false) + ".nar"
+ narInfo->url = "nar/" + narInfo->fileHash->to_string(Base32, false) + ".nar"
+ (compression == "xz" ? ".xz" :
compression == "bzip2" ? ".bz2" :
compression == "br" ? ".br" :
@@ -280,7 +283,7 @@ void BinaryCacheStore::addToStore(const ValidPathInfo & info, Source & narSource
if (repair || !fileExists(narInfo->url)) {
stats.narWrite++;
upsertFile(narInfo->url,
- std::make_shared(fnTemp, std::ios_base::in),
+ std::make_shared(fnTemp, std::ios_base::in | std::ios_base::binary),
"application/x-nix-nar");
} else
stats.narWriteAverted++;
@@ -372,7 +375,7 @@ StorePath BinaryCacheStore::addToStore(const string & name, const Path & srcPath
method for very large paths, but `copyPath' is mainly used for
small files. */
StringSink sink;
- Hash h;
+ std::optional h;
if (method == FileIngestionMethod::Recursive) {
dumpPath(srcPath, sink, filter);
h = hashString(hashAlgo, *sink.s);
@@ -382,7 +385,7 @@ StorePath BinaryCacheStore::addToStore(const string & name, const Path & srcPath
h = hashString(hashAlgo, s);
}
- ValidPathInfo info(makeFixedOutputPath(method, h, name));
+ ValidPathInfo info(makeFixedOutputPath(method, *h, name));
auto source = StringSource { *sink.s };
addToStore(info, source, repair, CheckSigs);
diff --git a/src/libstore/build.cc b/src/libstore/build.cc
index 62294a08c..94c398b2f 100644
--- a/src/libstore/build.cc
+++ b/src/libstore/build.cc
@@ -297,7 +297,7 @@ public:
GoalPtr makeDerivationGoal(const StorePath & drvPath, const StringSet & wantedOutputs, BuildMode buildMode = bmNormal);
std::shared_ptr makeBasicDerivationGoal(const StorePath & drvPath,
const BasicDerivation & drv, BuildMode buildMode = bmNormal);
- GoalPtr makeSubstitutionGoal(const StorePath & storePath, RepairFlag repair = NoRepair);
+ GoalPtr makeSubstitutionGoal(const StorePath & storePath, RepairFlag repair = NoRepair, std::optional ca = std::nullopt);
/* Remove a dead goal. */
void removeGoal(GoalPtr goal);
@@ -806,8 +806,8 @@ private:
/* RAII object to delete the chroot directory. */
std::shared_ptr autoDelChroot;
- /* Whether this is a fixed-output derivation. */
- bool fixedOutput;
+ /* The sort of derivation we are building. */
+ DerivationType derivationType;
/* Whether to run the build in a private network namespace. */
bool privateNetwork = false;
@@ -1047,7 +1047,7 @@ DerivationGoal::DerivationGoal(const StorePath & drvPath, const BasicDerivation
{
this->drv = std::make_unique(BasicDerivation(drv));
state = &DerivationGoal::haveDerivation;
- name = fmt("building of %s", worker.store.showPaths(drv.outputPaths()));
+ name = fmt("building of %s", worker.store.showPaths(drv.outputPaths(worker.store)));
trace("created");
mcExpectedBuilds = std::make_unique>(worker.expectedBuilds);
@@ -1182,7 +1182,7 @@ void DerivationGoal::haveDerivation()
retrySubstitution = false;
for (auto & i : drv->outputs)
- worker.store.addTempRoot(i.second.path);
+ worker.store.addTempRoot(i.second.path(worker.store, drv->name));
/* Check what outputs paths are not already valid. */
auto invalidOutputs = checkPathValidity(false, buildMode == bmRepair);
@@ -1195,9 +1195,9 @@ void DerivationGoal::haveDerivation()
parsedDrv = std::make_unique(drvPath, *drv);
- if (parsedDrv->contentAddressed()) {
+ if (drv->type() == DerivationType::CAFloating) {
settings.requireExperimentalFeature("ca-derivations");
- throw Error("ca-derivations isn't implemented yet");
+ throw UnimplementedError("ca-derivations isn't implemented yet");
}
@@ -1206,7 +1206,7 @@ void DerivationGoal::haveDerivation()
them. */
if (settings.useSubstitutes && parsedDrv->substitutesAllowed())
for (auto & i : invalidOutputs)
- addWaitee(worker.makeSubstitutionGoal(i, buildMode == bmRepair ? Repair : NoRepair));
+ addWaitee(worker.makeSubstitutionGoal(i, buildMode == bmRepair ? Repair : NoRepair, getDerivationCA(*drv)));
if (waitees.empty()) /* to prevent hang (no wake-up event) */
outputsSubstituted();
@@ -1290,12 +1290,12 @@ void DerivationGoal::repairClosure()
StorePathSet outputClosure;
for (auto & i : drv->outputs) {
if (!wantOutput(i.first, wantedOutputs)) continue;
- worker.store.computeFSClosure(i.second.path, outputClosure);
+ worker.store.computeFSClosure(i.second.path(worker.store, drv->name), outputClosure);
}
/* Filter out our own outputs (which we have already checked). */
for (auto & i : drv->outputs)
- outputClosure.erase(i.second.path);
+ outputClosure.erase(i.second.path(worker.store, drv->name));
/* Get all dependencies of this derivation so that we know which
derivation is responsible for which path in the output
@@ -1307,7 +1307,7 @@ void DerivationGoal::repairClosure()
if (i.isDerivation()) {
Derivation drv = worker.store.derivationFromPath(i);
for (auto & j : drv.outputs)
- outputsToDrv.insert_or_assign(j.second.path, i);
+ outputsToDrv.insert_or_assign(j.second.path(worker.store, drv.name), i);
}
/* Check each path (slow!). */
@@ -1379,7 +1379,7 @@ void DerivationGoal::inputsRealised()
for (auto & j : i.second) {
auto k = inDrv.outputs.find(j);
if (k != inDrv.outputs.end())
- worker.store.computeFSClosure(k->second.path, inputPaths);
+ worker.store.computeFSClosure(k->second.path(worker.store, inDrv.name), inputPaths);
else
throw Error(
"derivation '%s' requires non-existent output '%s' from input derivation '%s'",
@@ -1392,12 +1392,12 @@ void DerivationGoal::inputsRealised()
debug("added input paths %s", worker.store.showPaths(inputPaths));
- /* Is this a fixed-output derivation? */
- fixedOutput = drv->isFixedOutput();
+ /* What type of derivation are we building? */
+ derivationType = drv->type();
/* Don't repeat fixed-output derivations since they're already
verified by their output hash.*/
- nrRounds = fixedOutput ? 1 : settings.buildRepeat + 1;
+ nrRounds = derivationIsFixed(derivationType) ? 1 : settings.buildRepeat + 1;
/* Okay, try to build. Note that here we don't wait for a build
slot to become available, since we don't need one if there is a
@@ -1432,7 +1432,7 @@ void DerivationGoal::tryToBuild()
goal can start a build, and if not, the main loop will sleep a
few seconds and then retry this goal. */
PathSet lockFiles;
- for (auto & outPath : drv->outputPaths())
+ for (auto & outPath : drv->outputPaths(worker.store))
lockFiles.insert(worker.store.Store::toRealPath(outPath));
if (!outputLocks.lockPaths(lockFiles, "", false)) {
@@ -1460,16 +1460,16 @@ void DerivationGoal::tryToBuild()
return;
}
- missingPaths = drv->outputPaths();
+ missingPaths = drv->outputPaths(worker.store);
if (buildMode != bmCheck)
for (auto & i : validPaths) missingPaths.erase(i);
/* If any of the outputs already exist but are not valid, delete
them. */
for (auto & i : drv->outputs) {
- if (worker.store.isValidPath(i.second.path)) continue;
- debug("removing invalid path '%s'", worker.store.printStorePath(i.second.path));
- deletePath(worker.store.Store::toRealPath(i.second.path));
+ if (worker.store.isValidPath(i.second.path(worker.store, drv->name))) continue;
+ debug("removing invalid path '%s'", worker.store.printStorePath(i.second.path(worker.store, drv->name)));
+ deletePath(worker.store.Store::toRealPath(i.second.path(worker.store, drv->name)));
}
/* Don't do a remote build if the derivation has the attribute
@@ -1646,13 +1646,13 @@ void DerivationGoal::buildDone()
So instead, check if the disk is (nearly) full now. If
so, we don't mark this build as a permanent failure. */
#if HAVE_STATVFS
- unsigned long long required = 8ULL * 1024 * 1024; // FIXME: make configurable
+ uint64_t required = 8ULL * 1024 * 1024; // FIXME: make configurable
struct statvfs st;
if (statvfs(worker.store.realStoreDir.c_str(), &st) == 0 &&
- (unsigned long long) st.f_bavail * st.f_bsize < required)
+ (uint64_t) st.f_bavail * st.f_bsize < required)
diskFull = true;
if (statvfs(tmpDir.c_str(), &st) == 0 &&
- (unsigned long long) st.f_bavail * st.f_bsize < required)
+ (uint64_t) st.f_bavail * st.f_bsize < required)
diskFull = true;
#endif
@@ -1692,7 +1692,7 @@ void DerivationGoal::buildDone()
fmt("running post-build-hook '%s'", settings.postBuildHook),
Logger::Fields{worker.store.printStorePath(drvPath)});
PushActivity pact(act.id);
- auto outputPaths = drv->outputPaths();
+ auto outputPaths = drv->outputPaths(worker.store);
std::map hookEnvironment = getEnv();
hookEnvironment.emplace("DRV_PATH", worker.store.printStorePath(drvPath));
@@ -1783,7 +1783,7 @@ void DerivationGoal::buildDone()
st =
dynamic_cast(&e) ? BuildResult::NotDeterministic :
statusOk(status) ? BuildResult::OutputRejected :
- fixedOutput || diskFull ? BuildResult::TransientFailure :
+ derivationIsImpure(derivationType) || diskFull ? BuildResult::TransientFailure :
BuildResult::PermanentFailure;
}
@@ -1920,7 +1920,7 @@ StorePathSet DerivationGoal::exportReferences(const StorePathSet & storePaths)
if (j.isDerivation()) {
Derivation drv = worker.store.derivationFromPath(j);
for (auto & k : drv.outputs)
- worker.store.computeFSClosure(k.second.path, paths);
+ worker.store.computeFSClosure(k.second.path(worker.store, drv.name), paths);
}
}
@@ -1996,7 +1996,7 @@ void DerivationGoal::startBuilder()
else if (settings.sandboxMode == smDisabled)
useChroot = false;
else if (settings.sandboxMode == smRelaxed)
- useChroot = !fixedOutput && !noChroot;
+ useChroot = !(derivationIsImpure(derivationType)) && !noChroot;
}
if (worker.store.storeDir != worker.store.realStoreDir) {
@@ -2015,7 +2015,7 @@ void DerivationGoal::startBuilder()
/* Substitute output placeholders with the actual output paths. */
for (auto & output : drv->outputs)
- inputRewrites[hashPlaceholder(output.first)] = worker.store.printStorePath(output.second.path);
+ inputRewrites[hashPlaceholder(output.first)] = worker.store.printStorePath(output.second.path(worker.store, drv->name));
/* Construct the environment passed to the builder. */
initEnv();
@@ -2165,7 +2165,7 @@ void DerivationGoal::startBuilder()
"nogroup:x:65534:\n") % sandboxGid).str());
/* Create /etc/hosts with localhost entry. */
- if (!fixedOutput)
+ if (!(derivationIsImpure(derivationType)))
writeFile(chrootRootDir + "/etc/hosts", "127.0.0.1 localhost\n::1 localhost\n");
/* Make the closure of the inputs available in the chroot,
@@ -2200,7 +2200,7 @@ void DerivationGoal::startBuilder()
(typically the dependencies of /bin/sh). Throw them
out. */
for (auto & i : drv->outputs)
- dirsInChroot.erase(worker.store.printStorePath(i.second.path));
+ dirsInChroot.erase(worker.store.printStorePath(i.second.path(worker.store, drv->name)));
#elif __APPLE__
/* We don't really have any parent prep work to do (yet?)
@@ -2373,7 +2373,7 @@ void DerivationGoal::startBuilder()
us.
*/
- if (!fixedOutput)
+ if (!(derivationIsImpure(derivationType)))
privateNetwork = true;
userNamespaceSync.create();
@@ -2574,7 +2574,7 @@ void DerivationGoal::initEnv()
derivation, tell the builder, so that for instance `fetchurl'
can skip checking the output. On older Nixes, this environment
variable won't be set, so `fetchurl' will do the check. */
- if (fixedOutput) env["NIX_OUTPUT_CHECKED"] = "1";
+ if (derivationIsFixed(derivationType)) env["NIX_OUTPUT_CHECKED"] = "1";
/* *Only* if this is a fixed-output derivation, propagate the
values of the environment variables specified in the
@@ -2585,7 +2585,7 @@ void DerivationGoal::initEnv()
to the builder is generally impure, but the output of
fixed-output derivations is by definition pure (since we
already know the cryptographic hash of the output). */
- if (fixedOutput) {
+ if (derivationIsImpure(derivationType)) {
for (auto & i : parsedDrv->getStringsAttr("impureEnvVars").value_or(Strings()))
env[i] = getEnv(i).value_or("");
}
@@ -2613,7 +2613,7 @@ void DerivationGoal::writeStructuredAttrs()
/* Add an "outputs" object containing the output paths. */
nlohmann::json outputs;
for (auto & i : drv->outputs)
- outputs[i.first] = rewriteStrings(worker.store.printStorePath(i.second.path), inputRewrites);
+ outputs[i.first] = rewriteStrings(worker.store.printStorePath(i.second.path(worker.store, drv->name)), inputRewrites);
json["outputs"] = outputs;
/* Handle exportReferencesGraph. */
@@ -2817,7 +2817,7 @@ struct RestrictedStore : public LocalFSStore
auto drv = derivationFromPath(path.path);
for (auto & output : drv.outputs)
if (wantOutput(output.first, path.outputs))
- newPaths.insert(output.second.path);
+ newPaths.insert(output.second.path(*this, drv.name));
} else if (!goal.isAllowed(path.path))
throw InvalidPath("cannot build unknown path '%s' in recursive Nix", printStorePath(path.path));
}
@@ -2851,7 +2851,7 @@ struct RestrictedStore : public LocalFSStore
void queryMissing(const std::vector & targets,
StorePathSet & willBuild, StorePathSet & willSubstitute, StorePathSet & unknown,
- unsigned long long & downloadSize, unsigned long long & narSize) override
+ uint64_t & downloadSize, uint64_t & narSize) override
{
/* This is slightly impure since it leaks information to the
client about what paths will be built/substituted or are
@@ -3195,7 +3195,7 @@ void DerivationGoal::runChild()
/* Fixed-output derivations typically need to access the
network, so give them access to /etc/resolv.conf and so
on. */
- if (fixedOutput) {
+ if (derivationIsImpure(derivationType)) {
ss.push_back("/etc/resolv.conf");
// Only use nss functions to resolve hosts and
@@ -3436,7 +3436,7 @@ void DerivationGoal::runChild()
sandboxProfile += "(import \"sandbox-defaults.sb\")\n";
- if (fixedOutput)
+ if (derivationIsImpure(derivationType))
sandboxProfile += "(import \"sandbox-network.sb\")\n";
/* Our rwx outputs */
@@ -3579,7 +3579,7 @@ StorePathSet parseReferenceSpecifiers(Store & store, const BasicDerivation & drv
if (store.isStorePath(i))
result.insert(store.parseStorePath(i));
else if (drv.outputs.count(i))
- result.insert(drv.outputs.find(i)->second.path);
+ result.insert(drv.outputs.find(i)->second.path(store, drv.name));
else throw BuildError("derivation contains an illegal reference specifier '%s'", i);
}
return result;
@@ -3617,7 +3617,7 @@ void DerivationGoal::registerOutputs()
if (hook) {
bool allValid = true;
for (auto & i : drv->outputs)
- if (!worker.store.isValidPath(i.second.path)) allValid = false;
+ if (!worker.store.isValidPath(i.second.path(worker.store, drv->name))) allValid = false;
if (allValid) return;
}
@@ -3638,23 +3638,23 @@ void DerivationGoal::registerOutputs()
Nix calls. */
StorePathSet referenceablePaths;
for (auto & p : inputPaths) referenceablePaths.insert(p);
- for (auto & i : drv->outputs) referenceablePaths.insert(i.second.path);
+ for (auto & i : drv->outputs) referenceablePaths.insert(i.second.path(worker.store, drv->name));
for (auto & p : addedPaths) referenceablePaths.insert(p);
/* Check whether the output paths were created, and grep each
output path to determine what other paths it references. Also make all
output paths read-only. */
for (auto & i : drv->outputs) {
- auto path = worker.store.printStorePath(i.second.path);
- if (!missingPaths.count(i.second.path)) continue;
+ auto path = worker.store.printStorePath(i.second.path(worker.store, drv->name));
+ if (!missingPaths.count(i.second.path(worker.store, drv->name))) continue;
Path actualPath = path;
if (needsHashRewrite()) {
- auto r = redirectedOutputs.find(i.second.path);
+ auto r = redirectedOutputs.find(i.second.path(worker.store, drv->name));
if (r != redirectedOutputs.end()) {
auto redirected = worker.store.Store::toRealPath(r->second);
if (buildMode == bmRepair
- && redirectedBadOutputs.count(i.second.path)
+ && redirectedBadOutputs.count(i.second.path(worker.store, drv->name))
&& pathExists(redirected))
replaceValidPath(path, redirected);
if (buildMode == bmCheck)
@@ -3721,9 +3721,24 @@ void DerivationGoal::registerOutputs()
hash). */
std::optional ca;
- if (fixedOutput) {
+ if (! std::holds_alternative(i.second.output)) {
+ DerivationOutputCAFloating outputHash;
+ std::visit(overloaded {
+ [&](DerivationOutputInputAddressed doi) {
+ assert(false); // Enclosing `if` handles this case in other branch
+ },
+ [&](DerivationOutputCAFixed dof) {
+ outputHash = DerivationOutputCAFloating {
+ .method = dof.hash.method,
+ .hashType = dof.hash.hash.type,
+ };
+ },
+ [&](DerivationOutputCAFloating dof) {
+ outputHash = dof;
+ },
+ }, i.second.output);
- if (i.second.hash->method == FileIngestionMethod::Flat) {
+ if (outputHash.method == FileIngestionMethod::Flat) {
/* The output path should be a regular file without execute permission. */
if (!S_ISREG(st.st_mode) || (st.st_mode & S_IXUSR) != 0)
throw BuildError(
@@ -3734,13 +3749,18 @@ void DerivationGoal::registerOutputs()
/* Check the hash. In hash mode, move the path produced by
the derivation to its content-addressed location. */
- Hash h2 = i.second.hash->method == FileIngestionMethod::Recursive
- ? hashPath(*i.second.hash->hash.type, actualPath).first
- : hashFile(*i.second.hash->hash.type, actualPath);
+ Hash h2 = outputHash.method == FileIngestionMethod::Recursive
+ ? hashPath(outputHash.hashType, actualPath).first
+ : hashFile(outputHash.hashType, actualPath);
- auto dest = worker.store.makeFixedOutputPath(i.second.hash->method, h2, i.second.path.name());
+ auto dest = worker.store.makeFixedOutputPath(outputHash.method, h2, i.second.path(worker.store, drv->name).name());
- if (i.second.hash->hash != h2) {
+ // true if either floating CA, or incorrect fixed hash.
+ bool needsMove = true;
+
+ if (auto p = std::get_if(& i.second.output)) {
+ Hash & h = p->hash.hash;
+ if (h != h2) {
/* Throw an error after registering the path as
valid. */
@@ -3748,9 +3768,15 @@ void DerivationGoal::registerOutputs()
delayedException = std::make_exception_ptr(
BuildError("hash mismatch in fixed-output derivation '%s':\n wanted: %s\n got: %s",
worker.store.printStorePath(dest),
- i.second.hash->hash.to_string(SRI, true),
+ h.to_string(SRI, true),
h2.to_string(SRI, true)));
+ } else {
+ // matched the fixed hash, so no move needed.
+ needsMove = false;
+ }
+ }
+ if (needsMove) {
Path actualDest = worker.store.Store::toRealPath(dest);
if (worker.store.isValidPath(dest))
@@ -3770,7 +3796,7 @@ void DerivationGoal::registerOutputs()
assert(worker.store.parseStorePath(path) == dest);
ca = FixedOutputHash {
- .method = i.second.hash->method,
+ .method = outputHash.method,
.hash = h2,
};
}
@@ -3785,8 +3811,10 @@ void DerivationGoal::registerOutputs()
time. The hash is stored in the database so that we can
verify later on whether nobody has messed with the store. */
debug("scanning for references inside '%1%'", path);
- HashResult hash;
- auto references = worker.store.parseStorePathSet(scanForReferences(actualPath, worker.store.printStorePathSet(referenceablePaths), hash));
+ // HashResult hash;
+ auto pathSetAndHash = scanForReferences(actualPath, worker.store.printStorePathSet(referenceablePaths));
+ auto references = worker.store.parseStorePathSet(pathSetAndHash.first);
+ HashResult hash = pathSetAndHash.second;
if (buildMode == bmCheck) {
if (!worker.store.isValidPath(worker.store.parseStorePath(path))) continue;
@@ -3894,7 +3922,7 @@ void DerivationGoal::registerOutputs()
/* If this is the first round of several, then move the output out of the way. */
if (nrRounds > 1 && curRound == 1 && curRound < nrRounds && keepPreviousRound) {
for (auto & i : drv->outputs) {
- auto path = worker.store.printStorePath(i.second.path);
+ auto path = worker.store.printStorePath(i.second.path(worker.store, drv->name));
Path prev = path + checkSuffix;
deletePath(prev);
Path dst = path + checkSuffix;
@@ -3912,7 +3940,7 @@ void DerivationGoal::registerOutputs()
if the result was not determistic? */
if (curRound == nrRounds) {
for (auto & i : drv->outputs) {
- Path prev = worker.store.printStorePath(i.second.path) + checkSuffix;
+ Path prev = worker.store.printStorePath(i.second.path(worker.store, drv->name)) + checkSuffix;
deletePath(prev);
}
}
@@ -4213,9 +4241,9 @@ StorePathSet DerivationGoal::checkPathValidity(bool returnValid, bool checkHash)
for (auto & i : drv->outputs) {
if (!wantOutput(i.first, wantedOutputs)) continue;
bool good =
- worker.store.isValidPath(i.second.path) &&
- (!checkHash || worker.pathContentsGood(i.second.path));
- if (good == returnValid) result.insert(i.second.path);
+ worker.store.isValidPath(i.second.path(worker.store, drv->name)) &&
+ (!checkHash || worker.pathContentsGood(i.second.path(worker.store, drv->name)));
+ if (good == returnValid) result.insert(i.second.path(worker.store, drv->name));
}
return result;
}
@@ -4272,6 +4300,10 @@ private:
/* The store path that should be realised through a substitute. */
StorePath storePath;
+ /* The path the substituter refers to the path as. This will be
+ * different when the stores have different names. */
+ std::optional subPath;
+
/* The remaining substituters. */
std::list[> subs;
@@ -4305,8 +4337,11 @@ private:
typedef void (SubstitutionGoal::*GoalState)();
GoalState state;
+ /* Content address for recomputing store path */
+ std::optional ca;
+
public:
- SubstitutionGoal(const StorePath & storePath, Worker & worker, RepairFlag repair = NoRepair);
+ SubstitutionGoal(const StorePath & storePath, Worker & worker, RepairFlag repair = NoRepair, std::optional ca = std::nullopt);
~SubstitutionGoal();
void timedOut(Error && ex) override { abort(); };
@@ -4336,10 +4371,11 @@ public:
};
-SubstitutionGoal::SubstitutionGoal(const StorePath & storePath, Worker & worker, RepairFlag repair)
+SubstitutionGoal::SubstitutionGoal(const StorePath & storePath, Worker & worker, RepairFlag repair, std::optional ca)
: Goal(worker)
, storePath(storePath)
, repair(repair)
+ , ca(ca)
{
state = &SubstitutionGoal::init;
name = fmt("substitution of '%s'", worker.store.printStorePath(this->storePath));
@@ -4414,14 +4450,18 @@ void SubstitutionGoal::tryNext()
sub = subs.front();
subs.pop_front();
- if (sub->storeDir != worker.store.storeDir) {
+ if (ca) {
+ subPath = sub->makeFixedOutputPathFromCA(storePath.name(), *ca);
+ if (sub->storeDir == worker.store.storeDir)
+ assert(subPath == storePath);
+ } else if (sub->storeDir != worker.store.storeDir) {
tryNext();
return;
}
try {
// FIXME: make async
- info = sub->queryPathInfo(storePath);
+ info = sub->queryPathInfo(subPath ? *subPath : storePath);
} catch (InvalidPath &) {
tryNext();
return;
@@ -4440,6 +4480,19 @@ void SubstitutionGoal::tryNext()
throw;
}
+ if (info->path != storePath) {
+ if (info->isContentAddressed(*sub) && info->references.empty()) {
+ auto info2 = std::make_shared(*info);
+ info2->path = storePath;
+ info = info2;
+ } else {
+ printError("asked '%s' for '%s' but got '%s'",
+ sub->getUri(), worker.store.printStorePath(storePath), sub->printStorePath(info->path));
+ tryNext();
+ return;
+ }
+ }
+
/* Update the total expected download size. */
auto narInfo = std::dynamic_pointer_cast(info);
@@ -4529,7 +4582,7 @@ void SubstitutionGoal::tryToRun()
PushActivity pact(act.id);
copyStorePath(ref(sub), ref(worker.store.shared_from_this()),
- storePath, repair, sub->isTrusted ? NoCheckSigs : CheckSigs);
+ subPath ? *subPath : storePath, repair, sub->isTrusted ? NoCheckSigs : CheckSigs);
promise.set_value();
} catch (...) {
@@ -4662,11 +4715,11 @@ std::shared_ptr Worker::makeBasicDerivationGoal(const StorePath
}
-GoalPtr Worker::makeSubstitutionGoal(const StorePath & path, RepairFlag repair)
+GoalPtr Worker::makeSubstitutionGoal(const StorePath & path, RepairFlag repair, std::optional ca)
{
GoalPtr goal = substitutionGoals[path].lock(); // FIXME
if (!goal) {
- goal = std::make_shared(path, *this, repair);
+ goal = std::make_shared(path, *this, repair, ca);
substitutionGoals.insert_or_assign(path, goal);
wakeUp(goal);
}
@@ -5008,7 +5061,7 @@ bool Worker::pathContentsGood(const StorePath & path)
if (!pathExists(store.printStorePath(path)))
res = false;
else {
- HashResult current = hashPath(*info->narHash.type, store.printStorePath(path));
+ HashResult current = hashPath(info->narHash->type, store.printStorePath(path));
Hash nullHash(htSHA256);
res = info->narHash == nullHash || info->narHash == current.first;
}
@@ -5034,7 +5087,7 @@ void Worker::markContentsGood(const StorePath & path)
static void primeCache(Store & store, const std::vector & paths)
{
StorePathSet willBuild, willSubstitute, unknown;
- unsigned long long downloadSize, narSize;
+ uint64_t downloadSize, narSize;
store.queryMissing(paths, willBuild, willSubstitute, unknown, downloadSize, narSize);
if (!willBuild.empty() && 0 == settings.maxBuildJobs && getMachines().empty())
diff --git a/src/libstore/builtins/fetchurl.cc b/src/libstore/builtins/fetchurl.cc
index e630cf6f1..6585a480d 100644
--- a/src/libstore/builtins/fetchurl.cc
+++ b/src/libstore/builtins/fetchurl.cc
@@ -58,23 +58,6 @@ void builtinFetchurl(const BasicDerivation & drv, const std::string & netrcData)
}
};
- /* We always have one output, and if it's a fixed-output derivation (as
- checked below) it must be the only output */
- auto & output = drv.outputs.begin()->second;
-
- /* Try the hashed mirrors first. */
- if (output.hash && output.hash->method == FileIngestionMethod::Flat)
- for (auto hashedMirror : settings.hashedMirrors.get())
- try {
- if (!hasSuffix(hashedMirror, "/")) hashedMirror += '/';
- auto & h = output.hash->hash;
- fetch(hashedMirror + printHashType(*h.type) + "/" + h.to_string(Base16, false));
- return;
- } catch (Error & e) {
- debug(e.what());
- }
-
- /* Otherwise try the specified URL. */
fetch(mainUrl);
}
diff --git a/src/libstore/content-address.cc b/src/libstore/content-address.cc
index f45f77d5c..0885c3d0e 100644
--- a/src/libstore/content-address.cc
+++ b/src/libstore/content-address.cc
@@ -1,9 +1,11 @@
+#include "args.hh"
#include "content-address.hh"
+#include "split.hh"
namespace nix {
std::string FixedOutputHash::printMethodAlgo() const {
- return makeFileIngestionPrefix(method) + printHashType(*hash.type);
+ return makeFileIngestionPrefix(method) + printHashType(hash.type);
}
std::string makeFileIngestionPrefix(const FileIngestionMethod m) {
@@ -36,38 +38,46 @@ std::string renderContentAddress(ContentAddress ca) {
}
ContentAddress parseContentAddress(std::string_view rawCa) {
- auto prefixSeparator = rawCa.find(':');
- if (prefixSeparator != string::npos) {
- auto prefix = string(rawCa, 0, prefixSeparator);
- if (prefix == "text") {
- auto hashTypeAndHash = rawCa.substr(prefixSeparator+1, string::npos);
- Hash hash = Hash(string(hashTypeAndHash));
- if (*hash.type != htSHA256) {
- throw Error("parseContentAddress: the text hash should have type SHA256");
- }
- return TextHash { hash };
- } else if (prefix == "fixed") {
- // This has to be an inverse of makeFixedOutputCA
- auto methodAndHash = rawCa.substr(prefixSeparator+1, string::npos);
- if (methodAndHash.substr(0,2) == "r:") {
- std::string_view hashRaw = methodAndHash.substr(2,string::npos);
- return FixedOutputHash {
- .method = FileIngestionMethod::Recursive,
- .hash = Hash(string(hashRaw)),
- };
- } else {
- std::string_view hashRaw = methodAndHash;
- return FixedOutputHash {
- .method = FileIngestionMethod::Flat,
- .hash = Hash(string(hashRaw)),
- };
- }
- } else {
- throw Error("parseContentAddress: format not recognized; has to be text or fixed");
- }
- } else {
- throw Error("Not a content address because it lacks an appropriate prefix");
+ auto rest = rawCa;
+
+ std::string_view prefix;
+ {
+ auto optPrefix = splitPrefixTo(rest, ':');
+ if (!optPrefix)
+ throw UsageError("not a content address because it is not in the form ':': %s", rawCa);
+ prefix = *optPrefix;
}
+
+ auto parseHashType_ = [&](){
+ auto hashTypeRaw = splitPrefixTo(rest, ':');
+ if (!hashTypeRaw)
+ throw UsageError("content address hash must be in form ':', but found: %s", rawCa);
+ HashType hashType = parseHashType(*hashTypeRaw);
+ return std::move(hashType);
+ };
+
+ // Switch on prefix
+ if (prefix == "text") {
+ // No parsing of the method, "text" only support flat.
+ HashType hashType = parseHashType_();
+ if (hashType != htSHA256)
+ throw Error("text content address hash should use %s, but instead uses %s",
+ printHashType(htSHA256), printHashType(hashType));
+ return TextHash {
+ .hash = Hash::parseNonSRIUnprefixed(rest, std::move(hashType)),
+ };
+ } else if (prefix == "fixed") {
+ // Parse method
+ auto method = FileIngestionMethod::Flat;
+ if (splitPrefix(rest, "r:"))
+ method = FileIngestionMethod::Recursive;
+ HashType hashType = parseHashType_();
+ return FixedOutputHash {
+ .method = method,
+ .hash = Hash::parseNonSRIUnprefixed(rest, std::move(hashType)),
+ };
+ } else
+ throw UsageError("content address prefix '%s' is unrecognized. Recogonized prefixes are 'text' or 'fixed'", prefix);
};
std::optional parseContentAddressOpt(std::string_view rawCaOpt) {
diff --git a/src/libstore/daemon.cc b/src/libstore/daemon.cc
index 7e16529a5..5e568fc94 100644
--- a/src/libstore/daemon.cc
+++ b/src/libstore/daemon.cc
@@ -86,7 +86,7 @@ struct TunnelLogger : public Logger
}
/* startWork() means that we're starting an operation for which we
- want to send out stderr to the client. */
+ want to send out stderr to the client. */
void startWork()
{
auto state(state_.lock());
@@ -289,7 +289,7 @@ static void performOp(TunnelLogger * logger, ref store,
logger->startWork();
auto hash = store->queryPathInfo(path)->narHash;
logger->stopWork();
- to << hash.to_string(Base16, false);
+ to << hash->to_string(Base16, false);
break;
}
@@ -451,7 +451,7 @@ static void performOp(TunnelLogger * logger, ref store,
case wopBuildDerivation: {
auto drvPath = store->parseStorePath(readString(from));
BasicDerivation drv;
- readDerivation(from, *store, drv);
+ readDerivation(from, *store, drv, Derivation::nameFromPath(drvPath));
BuildMode buildMode = (BuildMode) readInt(from);
logger->startWork();
if (!trusted)
@@ -579,7 +579,7 @@ static void performOp(TunnelLogger * logger, ref store,
auto path = store->parseStorePath(readString(from));
logger->startWork();
SubstitutablePathInfos infos;
- store->querySubstitutablePathInfos({path}, infos);
+ store->querySubstitutablePathInfos({{path, std::nullopt}}, infos);
logger->stopWork();
auto i = infos.find(path);
if (i == infos.end())
@@ -595,10 +595,16 @@ static void performOp(TunnelLogger * logger, ref store,
}
case wopQuerySubstitutablePathInfos: {
- auto paths = readStorePaths(*store, from);
- logger->startWork();
SubstitutablePathInfos infos;
- store->querySubstitutablePathInfos(paths, infos);
+ StorePathCAMap pathsMap = {};
+ if (GET_PROTOCOL_MINOR(clientVersion) < 22) {
+ auto paths = readStorePaths(*store, from);
+ for (auto & path : paths)
+ pathsMap.emplace(path, std::nullopt);
+ } else
+ pathsMap = readStorePathCAMap(*store, from);
+ logger->startWork();
+ store->querySubstitutablePathInfos(pathsMap, infos);
logger->stopWork();
to << infos.size();
for (auto & i : infos) {
@@ -632,7 +638,7 @@ static void performOp(TunnelLogger * logger, ref store,
if (GET_PROTOCOL_MINOR(clientVersion) >= 17)
to << 1;
to << (info->deriver ? store->printStorePath(*info->deriver) : "")
- << info->narHash.to_string(Base16, false);
+ << info->narHash->to_string(Base16, false);
writeStorePaths(*store, to, info->references);
to << info->registrationTime << info->narSize;
if (GET_PROTOCOL_MINOR(clientVersion) >= 16) {
@@ -692,7 +698,7 @@ static void performOp(TunnelLogger * logger, ref store,
auto deriver = readString(from);
if (deriver != "")
info.deriver = store->parseStorePath(deriver);
- info.narHash = Hash(readString(from), htSHA256);
+ info.narHash = Hash::parseAny(readString(from), htSHA256);
info.references = readStorePaths(*store, from);
from >> info.registrationTime >> info.narSize >> info.ultimate;
info.sigs = readStrings(from);
@@ -703,24 +709,84 @@ static void performOp(TunnelLogger * logger, ref store,
if (!trusted)
info.ultimate = false;
- std::unique_ptr]