Merge remote-tracking branch 'origin/master' into markdown

This commit is contained in:
Eelco Dolstra 2020-07-31 16:07:04 +02:00
commit bf290c2306
No known key found for this signature in database
GPG key ID: 8170B4726D7198DE
38 changed files with 475 additions and 197 deletions

View file

@ -206,26 +206,6 @@ The following settings are currently available:
robustness in case of system crashes, but reduces performance. The robustness in case of system crashes, but reduces performance. The
default is `true`. default is `true`.
- `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
```nix
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` - `http-connections`
The maximum number of parallel TCP connections used to fetch files The maximum number of parallel TCP connections used to fetch files
from binary caches and by other downloads. It defaults to 25. 0 from binary caches and by other downloads. It defaults to 25. 0

View file

@ -33,7 +33,7 @@ std::string escapeUri(std::string uri)
static string currentLoad; 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); 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; bool rightType = false;
Machine * bestMachine = nullptr; Machine * bestMachine = nullptr;
unsigned long long bestLoad = 0; uint64_t bestLoad = 0;
for (auto & m : machines) { for (auto & m : machines) {
debug("considering building on remote machine '%s'", m.storeUri); debug("considering building on remote machine '%s'", m.storeUri);
@ -130,8 +130,8 @@ static int _main(int argc, char * * argv)
m.mandatoryMet(requiredFeatures)) { m.mandatoryMet(requiredFeatures)) {
rightType = true; rightType = true;
AutoCloseFD free; AutoCloseFD free;
unsigned long long load = 0; uint64_t load = 0;
for (unsigned long long slot = 0; slot < m.maxJobs; ++slot) { for (uint64_t slot = 0; slot < m.maxJobs; ++slot) {
auto slotLock = openSlotLock(m, slot); auto slotLock = openSlotLock(m, slot);
if (lockFile(slotLock.get(), ltWrite, false)) { if (lockFile(slotLock.get(), ltWrite, false)) {
if (!free) { if (!free) {

View file

@ -65,7 +65,7 @@ void EvalState::realiseContext(const PathSet & context)
/* For performance, prefetch all substitute info. */ /* For performance, prefetch all substitute info. */
StorePathSet willBuild, willSubstitute, unknown; StorePathSet willBuild, willSubstitute, unknown;
unsigned long long downloadSize, narSize; uint64_t downloadSize, narSize;
store->queryMissing(drvs, willBuild, willSubstitute, unknown, downloadSize, narSize); store->queryMissing(drvs, willBuild, willSubstitute, unknown, downloadSize, narSize);
store->buildPaths(drvs); store->buildPaths(drvs);
@ -1199,7 +1199,7 @@ static void prim_path(EvalState & state, const Pos & pos, Value * * args, Value
string name; string name;
Value * filterFun = nullptr; Value * filterFun = nullptr;
auto method = FileIngestionMethod::Recursive; auto method = FileIngestionMethod::Recursive;
Hash expectedHash(htSHA256); std::optional<Hash> expectedHash;
for (auto & attr : *args[0]->attrs) { for (auto & attr : *args[0]->attrs) {
const string & n(attr.name); const string & n(attr.name);

View file

@ -36,7 +36,7 @@ void printGCWarning()
void printMissing(ref<Store> store, const std::vector<StorePathWithOutputs> & paths, Verbosity lvl) void printMissing(ref<Store> store, const std::vector<StorePathWithOutputs> & paths, Verbosity lvl)
{ {
unsigned long long downloadSize, narSize; uint64_t downloadSize, narSize;
StorePathSet willBuild, willSubstitute, unknown; StorePathSet willBuild, willSubstitute, unknown;
store->queryMissing(paths, willBuild, willSubstitute, unknown, downloadSize, narSize); store->queryMissing(paths, willBuild, willSubstitute, unknown, downloadSize, narSize);
printMissing(store, willBuild, willSubstitute, unknown, downloadSize, narSize, lvl); printMissing(store, willBuild, willSubstitute, unknown, downloadSize, narSize, lvl);
@ -45,7 +45,7 @@ void printMissing(ref<Store> store, const std::vector<StorePathWithOutputs> & pa
void printMissing(ref<Store> store, const StorePathSet & willBuild, void printMissing(ref<Store> store, const StorePathSet & willBuild,
const StorePathSet & willSubstitute, const StorePathSet & unknown, 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.empty()) {
if (willBuild.size() == 1) if (willBuild.size() == 1)
@ -386,7 +386,7 @@ RunPager::~RunPager()
} }
string showBytes(unsigned long long bytes) string showBytes(uint64_t bytes)
{ {
return (format("%.2f MiB") % (bytes / (1024.0 * 1024.0))).str(); return (format("%.2f MiB") % (bytes / (1024.0 * 1024.0))).str();
} }

View file

@ -47,7 +47,7 @@ void printMissing(
void printMissing(ref<Store> store, const StorePathSet & willBuild, void printMissing(ref<Store> store, const StorePathSet & willBuild,
const StorePathSet & willSubstitute, const StorePathSet & unknown, 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, string getArg(const string & opt,
Strings::iterator & i, const Strings::iterator & end); Strings::iterator & i, const Strings::iterator & end);
@ -110,7 +110,7 @@ extern volatile ::sig_atomic_t blockInt;
/* GC helpers. */ /* GC helpers. */
string showBytes(unsigned long long bytes); string showBytes(uint64_t bytes);
struct GCResults; struct GCResults;

View file

@ -297,7 +297,7 @@ public:
GoalPtr makeDerivationGoal(const StorePath & drvPath, const StringSet & wantedOutputs, BuildMode buildMode = bmNormal); GoalPtr makeDerivationGoal(const StorePath & drvPath, const StringSet & wantedOutputs, BuildMode buildMode = bmNormal);
std::shared_ptr<DerivationGoal> makeBasicDerivationGoal(const StorePath & drvPath, std::shared_ptr<DerivationGoal> makeBasicDerivationGoal(const StorePath & drvPath,
const BasicDerivation & drv, BuildMode buildMode = bmNormal); const BasicDerivation & drv, BuildMode buildMode = bmNormal);
GoalPtr makeSubstitutionGoal(const StorePath & storePath, RepairFlag repair = NoRepair); GoalPtr makeSubstitutionGoal(const StorePath & storePath, RepairFlag repair = NoRepair, std::optional<ContentAddress> ca = std::nullopt);
/* Remove a dead goal. */ /* Remove a dead goal. */
void removeGoal(GoalPtr goal); void removeGoal(GoalPtr goal);
@ -1206,7 +1206,7 @@ void DerivationGoal::haveDerivation()
them. */ them. */
if (settings.useSubstitutes && parsedDrv->substitutesAllowed()) if (settings.useSubstitutes && parsedDrv->substitutesAllowed())
for (auto & i : invalidOutputs) 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) */ if (waitees.empty()) /* to prevent hang (no wake-up event) */
outputsSubstituted(); outputsSubstituted();
@ -1646,13 +1646,13 @@ void DerivationGoal::buildDone()
So instead, check if the disk is (nearly) full now. If So instead, check if the disk is (nearly) full now. If
so, we don't mark this build as a permanent failure. */ so, we don't mark this build as a permanent failure. */
#if HAVE_STATVFS #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; struct statvfs st;
if (statvfs(worker.store.realStoreDir.c_str(), &st) == 0 && 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; diskFull = true;
if (statvfs(tmpDir.c_str(), &st) == 0 && 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; diskFull = true;
#endif #endif
@ -2851,7 +2851,7 @@ struct RestrictedStore : public LocalFSStore
void queryMissing(const std::vector<StorePathWithOutputs> & targets, void queryMissing(const std::vector<StorePathWithOutputs> & targets,
StorePathSet & willBuild, StorePathSet & willSubstitute, StorePathSet & unknown, 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 /* This is slightly impure since it leaks information to the
client about what paths will be built/substituted or are client about what paths will be built/substituted or are
@ -4276,6 +4276,10 @@ private:
/* The store path that should be realised through a substitute. */ /* The store path that should be realised through a substitute. */
StorePath storePath; StorePath storePath;
/* The path the substituter refers to the path as. This will be
* different when the stores have different names. */
std::optional<StorePath> subPath;
/* The remaining substituters. */ /* The remaining substituters. */
std::list<ref<Store>> subs; std::list<ref<Store>> subs;
@ -4309,8 +4313,11 @@ private:
typedef void (SubstitutionGoal::*GoalState)(); typedef void (SubstitutionGoal::*GoalState)();
GoalState state; GoalState state;
/* Content address for recomputing store path */
std::optional<ContentAddress> ca;
public: public:
SubstitutionGoal(const StorePath & storePath, Worker & worker, RepairFlag repair = NoRepair); SubstitutionGoal(const StorePath & storePath, Worker & worker, RepairFlag repair = NoRepair, std::optional<ContentAddress> ca = std::nullopt);
~SubstitutionGoal(); ~SubstitutionGoal();
void timedOut(Error && ex) override { abort(); }; void timedOut(Error && ex) override { abort(); };
@ -4340,10 +4347,11 @@ public:
}; };
SubstitutionGoal::SubstitutionGoal(const StorePath & storePath, Worker & worker, RepairFlag repair) SubstitutionGoal::SubstitutionGoal(const StorePath & storePath, Worker & worker, RepairFlag repair, std::optional<ContentAddress> ca)
: Goal(worker) : Goal(worker)
, storePath(storePath) , storePath(storePath)
, repair(repair) , repair(repair)
, ca(ca)
{ {
state = &SubstitutionGoal::init; state = &SubstitutionGoal::init;
name = fmt("substitution of '%s'", worker.store.printStorePath(this->storePath)); name = fmt("substitution of '%s'", worker.store.printStorePath(this->storePath));
@ -4418,14 +4426,18 @@ void SubstitutionGoal::tryNext()
sub = subs.front(); sub = subs.front();
subs.pop_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(); tryNext();
return; return;
} }
try { try {
// FIXME: make async // FIXME: make async
info = sub->queryPathInfo(storePath); info = sub->queryPathInfo(subPath ? *subPath : storePath);
} catch (InvalidPath &) { } catch (InvalidPath &) {
tryNext(); tryNext();
return; return;
@ -4444,6 +4456,19 @@ void SubstitutionGoal::tryNext()
throw; throw;
} }
if (info->path != storePath) {
if (info->isContentAddressed(*sub) && info->references.empty()) {
auto info2 = std::make_shared<ValidPathInfo>(*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. */ /* Update the total expected download size. */
auto narInfo = std::dynamic_pointer_cast<const NarInfo>(info); auto narInfo = std::dynamic_pointer_cast<const NarInfo>(info);
@ -4533,7 +4558,7 @@ void SubstitutionGoal::tryToRun()
PushActivity pact(act.id); PushActivity pact(act.id);
copyStorePath(ref<Store>(sub), ref<Store>(worker.store.shared_from_this()), copyStorePath(ref<Store>(sub), ref<Store>(worker.store.shared_from_this()),
storePath, repair, sub->isTrusted ? NoCheckSigs : CheckSigs); subPath ? *subPath : storePath, repair, sub->isTrusted ? NoCheckSigs : CheckSigs);
promise.set_value(); promise.set_value();
} catch (...) { } catch (...) {
@ -4666,11 +4691,11 @@ std::shared_ptr<DerivationGoal> Worker::makeBasicDerivationGoal(const StorePath
} }
GoalPtr Worker::makeSubstitutionGoal(const StorePath & path, RepairFlag repair) GoalPtr Worker::makeSubstitutionGoal(const StorePath & path, RepairFlag repair, std::optional<ContentAddress> ca)
{ {
GoalPtr goal = substitutionGoals[path].lock(); // FIXME GoalPtr goal = substitutionGoals[path].lock(); // FIXME
if (!goal) { if (!goal) {
goal = std::make_shared<SubstitutionGoal>(path, *this, repair); goal = std::make_shared<SubstitutionGoal>(path, *this, repair, ca);
substitutionGoals.insert_or_assign(path, goal); substitutionGoals.insert_or_assign(path, goal);
wakeUp(goal); wakeUp(goal);
} }
@ -5038,7 +5063,7 @@ void Worker::markContentsGood(const StorePath & path)
static void primeCache(Store & store, const std::vector<StorePathWithOutputs> & paths) static void primeCache(Store & store, const std::vector<StorePathWithOutputs> & paths)
{ {
StorePathSet willBuild, willSubstitute, unknown; StorePathSet willBuild, willSubstitute, unknown;
unsigned long long downloadSize, narSize; uint64_t downloadSize, narSize;
store.queryMissing(paths, willBuild, willSubstitute, unknown, downloadSize, narSize); store.queryMissing(paths, willBuild, willSubstitute, unknown, downloadSize, narSize);
if (!willBuild.empty() && 0 == settings.maxBuildJobs && getMachines().empty()) if (!willBuild.empty() && 0 == settings.maxBuildJobs && getMachines().empty())

View file

@ -58,26 +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 (auto hash = std::get_if<DerivationOutputFixed>(&output.output)) {
if (hash->hash.method == FileIngestionMethod::Flat) {
for (auto hashedMirror : settings.hashedMirrors.get()) {
try {
if (!hasSuffix(hashedMirror, "/")) hashedMirror += '/';
fetch(hashedMirror + printHashType(hash->hash.hash.type) + "/" + hash->hash.hash.to_string(Base16, false));
return;
} catch (Error & e) {
debug(e.what());
}
}
}
}
/* Otherwise try the specified URL. */
fetch(mainUrl); fetch(mainUrl);
} }

View file

@ -579,7 +579,7 @@ static void performOp(TunnelLogger * logger, ref<Store> store,
auto path = store->parseStorePath(readString(from)); auto path = store->parseStorePath(readString(from));
logger->startWork(); logger->startWork();
SubstitutablePathInfos infos; SubstitutablePathInfos infos;
store->querySubstitutablePathInfos({path}, infos); store->querySubstitutablePathInfos({{path, std::nullopt}}, infos);
logger->stopWork(); logger->stopWork();
auto i = infos.find(path); auto i = infos.find(path);
if (i == infos.end()) if (i == infos.end())
@ -595,10 +595,16 @@ static void performOp(TunnelLogger * logger, ref<Store> store,
} }
case wopQuerySubstitutablePathInfos: { case wopQuerySubstitutablePathInfos: {
auto paths = readStorePaths<StorePathSet>(*store, from);
logger->startWork();
SubstitutablePathInfos infos; SubstitutablePathInfos infos;
store->querySubstitutablePathInfos(paths, infos); StorePathCAMap pathsMap = {};
if (GET_PROTOCOL_MINOR(clientVersion) < 22) {
auto paths = readStorePaths<StorePathSet>(*store, from);
for (auto & path : paths)
pathsMap.emplace(path, std::nullopt);
} else
pathsMap = readStorePathCAMap(*store, from);
logger->startWork();
store->querySubstitutablePathInfos(pathsMap, infos);
logger->stopWork(); logger->stopWork();
to << infos.size(); to << infos.size();
for (auto & i : infos) { for (auto & i : infos) {
@ -790,7 +796,7 @@ static void performOp(TunnelLogger * logger, ref<Store> store,
targets.push_back(store->parsePathWithOutputs(s)); targets.push_back(store->parsePathWithOutputs(s));
logger->startWork(); logger->startWork();
StorePathSet willBuild, willSubstitute, unknown; StorePathSet willBuild, willSubstitute, unknown;
unsigned long long downloadSize, narSize; uint64_t downloadSize, narSize;
store->queryMissing(targets, willBuild, willSubstitute, unknown, downloadSize, narSize); store->queryMissing(targets, willBuild, willSubstitute, unknown, downloadSize, narSize);
logger->stopWork(); logger->stopWork();
writeStorePaths(*store, to, willBuild); writeStorePaths(*store, to, willBuild);

View file

@ -500,7 +500,7 @@ struct LocalStore::GCState
StorePathSet alive; StorePathSet alive;
bool gcKeepOutputs; bool gcKeepOutputs;
bool gcKeepDerivations; bool gcKeepDerivations;
unsigned long long bytesInvalidated; uint64_t bytesInvalidated;
bool moveToTrash = true; bool moveToTrash = true;
bool shouldDelete; bool shouldDelete;
GCState(const GCOptions & options, GCResults & results) GCState(const GCOptions & options, GCResults & results)
@ -518,7 +518,7 @@ bool LocalStore::isActiveTempFile(const GCState & state,
void LocalStore::deleteGarbage(GCState & state, const Path & path) void LocalStore::deleteGarbage(GCState & state, const Path & path)
{ {
unsigned long long bytesFreed; uint64_t bytesFreed;
deletePath(path, bytesFreed); deletePath(path, bytesFreed);
state.results.bytesFreed += bytesFreed; state.results.bytesFreed += bytesFreed;
} }
@ -528,7 +528,7 @@ void LocalStore::deletePathRecursive(GCState & state, const Path & path)
{ {
checkInterrupt(); checkInterrupt();
unsigned long long size = 0; uint64_t size = 0;
auto storePath = maybeParseStorePath(path); auto storePath = maybeParseStorePath(path);
if (storePath && isValidPath(*storePath)) { if (storePath && isValidPath(*storePath)) {
@ -687,7 +687,7 @@ void LocalStore::removeUnusedLinks(const GCState & state)
AutoCloseDir dir(opendir(linksDir.c_str())); AutoCloseDir dir(opendir(linksDir.c_str()));
if (!dir) throw SysError("opening directory '%1%'", linksDir); if (!dir) throw SysError("opening directory '%1%'", linksDir);
long long actualSize = 0, unsharedSize = 0; int64_t actualSize = 0, unsharedSize = 0;
struct dirent * dirent; struct dirent * dirent;
while (errno = 0, dirent = readdir(dir.get())) { while (errno = 0, dirent = readdir(dir.get())) {
@ -717,10 +717,10 @@ void LocalStore::removeUnusedLinks(const GCState & state)
struct stat st; struct stat st;
if (stat(linksDir.c_str(), &st) == -1) if (stat(linksDir.c_str(), &st) == -1)
throw SysError("statting '%1%'", linksDir); throw SysError("statting '%1%'", linksDir);
long long overhead = st.st_blocks * 512ULL; auto overhead = st.st_blocks * 512ULL;
printInfo(format("note: currently hard linking saves %.2f MiB") printInfo("note: currently hard linking saves %.2f MiB",
% ((unsharedSize - actualSize - overhead) / (1024.0 * 1024.0))); ((unsharedSize - actualSize - overhead) / (1024.0 * 1024.0)));
} }

View file

@ -335,9 +335,6 @@ public:
"setuid/setgid bits or with file capabilities."}; "setuid/setgid bits or with file capabilities."};
#endif #endif
Setting<Strings> hashedMirrors{this, {"http://tarballs.nixos.org/"}, "hashed-mirrors",
"A list of servers used by builtins.fetchurl to fetch files by hash."};
Setting<uint64_t> minFree{this, 0, "min-free", Setting<uint64_t> minFree{this, 0, "min-free",
"Automatically run the garbage collector when free disk space drops below the specified amount."}; "Automatically run the garbage collector when free disk space drops below the specified amount."};

View file

@ -839,20 +839,32 @@ StorePathSet LocalStore::querySubstitutablePaths(const StorePathSet & paths)
} }
void LocalStore::querySubstitutablePathInfos(const StorePathSet & paths, void LocalStore::querySubstitutablePathInfos(const StorePathCAMap & paths, SubstitutablePathInfos & infos)
SubstitutablePathInfos & infos)
{ {
if (!settings.useSubstitutes) return; if (!settings.useSubstitutes) return;
for (auto & sub : getDefaultSubstituters()) { for (auto & sub : getDefaultSubstituters()) {
if (sub->storeDir != storeDir) continue;
for (auto & path : paths) { for (auto & path : paths) {
if (infos.count(path)) continue; auto subPath(path.first);
debug("checking substituter '%s' for path '%s'", sub->getUri(), printStorePath(path));
// recompute store path so that we can use a different store root
if (path.second) {
subPath = makeFixedOutputPathFromCA(path.first.name(), *path.second);
if (sub->storeDir == storeDir)
assert(subPath == path.first);
if (subPath != path.first)
debug("replaced path '%s' with '%s' for substituter '%s'", printStorePath(path.first), sub->printStorePath(subPath), sub->getUri());
} else if (sub->storeDir != storeDir) continue;
debug("checking substituter '%s' for path '%s'", sub->getUri(), sub->printStorePath(subPath));
try { try {
auto info = sub->queryPathInfo(path); auto info = sub->queryPathInfo(subPath);
if (sub->storeDir != storeDir && !(info->isContentAddressed(*sub) && info->references.empty()))
continue;
auto narInfo = std::dynamic_pointer_cast<const NarInfo>( auto narInfo = std::dynamic_pointer_cast<const NarInfo>(
std::shared_ptr<const ValidPathInfo>(info)); std::shared_ptr<const ValidPathInfo>(info));
infos.insert_or_assign(path, SubstitutablePathInfo{ infos.insert_or_assign(path.first, SubstitutablePathInfo{
info->deriver, info->deriver,
info->references, info->references,
narInfo ? narInfo->fileSize : 0, narInfo ? narInfo->fileSize : 0,

View file

@ -29,8 +29,8 @@ struct Derivation;
struct OptimiseStats struct OptimiseStats
{ {
unsigned long filesLinked = 0; unsigned long filesLinked = 0;
unsigned long long bytesFreed = 0; uint64_t bytesFreed = 0;
unsigned long long blocksFreed = 0; uint64_t blocksFreed = 0;
}; };
@ -139,7 +139,7 @@ public:
StorePathSet querySubstitutablePaths(const StorePathSet & paths) override; StorePathSet querySubstitutablePaths(const StorePathSet & paths) override;
void querySubstitutablePathInfos(const StorePathSet & paths, void querySubstitutablePathInfos(const StorePathCAMap & paths,
SubstitutablePathInfos & infos) override; SubstitutablePathInfos & infos) override;
void addToStore(const ValidPathInfo & info, Source & source, void addToStore(const ValidPathInfo & info, Source & source,

View file

@ -108,9 +108,19 @@ void Store::computeFSClosure(const StorePath & startPath,
} }
std::optional<ContentAddress> getDerivationCA(const BasicDerivation & drv)
{
auto out = drv.outputs.find("out");
if (out != drv.outputs.end()) {
if (auto v = std::get_if<DerivationOutputFixed>(&out->second.output))
return v->hash;
}
return std::nullopt;
}
void Store::queryMissing(const std::vector<StorePathWithOutputs> & targets, void Store::queryMissing(const std::vector<StorePathWithOutputs> & targets,
StorePathSet & willBuild_, StorePathSet & willSubstitute_, StorePathSet & unknown_, StorePathSet & willBuild_, StorePathSet & willSubstitute_, StorePathSet & unknown_,
unsigned long long & downloadSize_, unsigned long long & narSize_) uint64_t & downloadSize_, uint64_t & narSize_)
{ {
Activity act(*logger, lvlDebug, actUnknown, "querying info about missing paths"); Activity act(*logger, lvlDebug, actUnknown, "querying info about missing paths");
@ -122,8 +132,8 @@ void Store::queryMissing(const std::vector<StorePathWithOutputs> & targets,
{ {
std::unordered_set<std::string> done; std::unordered_set<std::string> done;
StorePathSet & unknown, & willSubstitute, & willBuild; StorePathSet & unknown, & willSubstitute, & willBuild;
unsigned long long & downloadSize; uint64_t & downloadSize;
unsigned long long & narSize; uint64_t & narSize;
}; };
struct DrvState struct DrvState
@ -157,7 +167,7 @@ void Store::queryMissing(const std::vector<StorePathWithOutputs> & targets,
auto outPath = parseStorePath(outPathS); auto outPath = parseStorePath(outPathS);
SubstitutablePathInfos infos; SubstitutablePathInfos infos;
querySubstitutablePathInfos({outPath}, infos); querySubstitutablePathInfos({{outPath, getDerivationCA(*drv)}}, infos);
if (infos.empty()) { if (infos.empty()) {
drvState_->lock()->done = true; drvState_->lock()->done = true;
@ -214,7 +224,7 @@ void Store::queryMissing(const std::vector<StorePathWithOutputs> & targets,
if (isValidPath(path.path)) return; if (isValidPath(path.path)) return;
SubstitutablePathInfos infos; SubstitutablePathInfos infos;
querySubstitutablePathInfos({path.path}, infos); querySubstitutablePathInfos({{path.path, std::nullopt}}, infos);
if (infos.empty()) { if (infos.empty()) {
auto state(state_.lock()); auto state(state_.lock());

View file

@ -80,14 +80,14 @@ struct NarAccessor : public FSAccessor
parents.top()->isExecutable = true; parents.top()->isExecutable = true;
} }
void preallocateContents(unsigned long long size) override void preallocateContents(uint64_t size) override
{ {
assert(size <= std::numeric_limits<uint64_t>::max()); assert(size <= std::numeric_limits<uint64_t>::max());
parents.top()->size = (uint64_t) size; parents.top()->size = (uint64_t) size;
parents.top()->start = pos; parents.top()->start = pos;
} }
void receiveContents(unsigned char * data, unsigned int len) override void receiveContents(unsigned char * data, size_t len) override
{ } { }
void createSymlink(const Path & path, const string & target) override void createSymlink(const Path & path, const string & target) override

View file

@ -282,7 +282,7 @@ void LocalStore::optimiseStore(OptimiseStats & stats)
} }
} }
static string showBytes(unsigned long long bytes) static string showBytes(uint64_t bytes)
{ {
return (format("%.2f MiB") % (bytes / (1024.0 * 1024.0))).str(); return (format("%.2f MiB") % (bytes / (1024.0 * 1024.0))).str();
} }

View file

@ -64,6 +64,8 @@ typedef std::set<StorePath> StorePathSet;
typedef std::vector<StorePath> StorePaths; typedef std::vector<StorePath> StorePaths;
typedef std::map<string, StorePath> OutputPathMap; typedef std::map<string, StorePath> OutputPathMap;
typedef std::map<StorePath, std::optional<ContentAddress>> StorePathCAMap;
/* Extension of derivations in the Nix store. */ /* Extension of derivations in the Nix store. */
const std::string drvExtension = ".drv"; const std::string drvExtension = ".drv";

View file

@ -39,6 +39,24 @@ void writeStorePaths(const Store & store, Sink & out, const StorePathSet & paths
out << store.printStorePath(i); out << store.printStorePath(i);
} }
StorePathCAMap readStorePathCAMap(const Store & store, Source & from)
{
StorePathCAMap paths;
auto count = readNum<size_t>(from);
while (count--)
paths.insert_or_assign(store.parseStorePath(readString(from)), parseContentAddressOpt(readString(from)));
return paths;
}
void writeStorePathCAMap(const Store & store, Sink & out, const StorePathCAMap & paths)
{
out << paths.size();
for (auto & i : paths) {
out << store.printStorePath(i.first);
out << renderContentAddress(i.second);
}
}
std::map<string, StorePath> readOutputPathMap(const Store & store, Source & from) std::map<string, StorePath> readOutputPathMap(const Store & store, Source & from)
{ {
std::map<string, StorePath> pathMap; std::map<string, StorePath> pathMap;
@ -332,18 +350,17 @@ StorePathSet RemoteStore::querySubstitutablePaths(const StorePathSet & paths)
} }
void RemoteStore::querySubstitutablePathInfos(const StorePathSet & paths, void RemoteStore::querySubstitutablePathInfos(const StorePathCAMap & pathsMap, SubstitutablePathInfos & infos)
SubstitutablePathInfos & infos)
{ {
if (paths.empty()) return; if (pathsMap.empty()) return;
auto conn(getConnection()); auto conn(getConnection());
if (GET_PROTOCOL_MINOR(conn->daemonVersion) < 12) { if (GET_PROTOCOL_MINOR(conn->daemonVersion) < 12) {
for (auto & i : paths) { for (auto & i : pathsMap) {
SubstitutablePathInfo info; SubstitutablePathInfo info;
conn->to << wopQuerySubstitutablePathInfo << printStorePath(i); conn->to << wopQuerySubstitutablePathInfo << printStorePath(i.first);
conn.processStderr(); conn.processStderr();
unsigned int reply = readInt(conn->from); unsigned int reply = readInt(conn->from);
if (reply == 0) continue; if (reply == 0) continue;
@ -353,13 +370,19 @@ void RemoteStore::querySubstitutablePathInfos(const StorePathSet & paths,
info.references = readStorePaths<StorePathSet>(*this, conn->from); info.references = readStorePaths<StorePathSet>(*this, conn->from);
info.downloadSize = readLongLong(conn->from); info.downloadSize = readLongLong(conn->from);
info.narSize = readLongLong(conn->from); info.narSize = readLongLong(conn->from);
infos.insert_or_assign(i, std::move(info)); infos.insert_or_assign(i.first, std::move(info));
} }
} else { } else {
conn->to << wopQuerySubstitutablePathInfos; conn->to << wopQuerySubstitutablePathInfos;
if (GET_PROTOCOL_MINOR(conn->daemonVersion) < 22) {
StorePathSet paths;
for (auto & path : pathsMap)
paths.insert(path.first);
writeStorePaths(*this, conn->to, paths); writeStorePaths(*this, conn->to, paths);
} else
writeStorePathCAMap(*this, conn->to, pathsMap);
conn.processStderr(); conn.processStderr();
size_t count = readNum<size_t>(conn->from); size_t count = readNum<size_t>(conn->from);
for (size_t n = 0; n < count; n++) { for (size_t n = 0; n < count; n++) {
@ -782,7 +805,7 @@ void RemoteStore::addSignatures(const StorePath & storePath, const StringSet & s
void RemoteStore::queryMissing(const std::vector<StorePathWithOutputs> & targets, void RemoteStore::queryMissing(const std::vector<StorePathWithOutputs> & targets,
StorePathSet & willBuild, StorePathSet & willSubstitute, StorePathSet & unknown, StorePathSet & willBuild, StorePathSet & willSubstitute, StorePathSet & unknown,
unsigned long long & downloadSize, unsigned long long & narSize) uint64_t & downloadSize, uint64_t & narSize)
{ {
{ {
auto conn(getConnection()); auto conn(getConnection());

View file

@ -56,7 +56,7 @@ public:
StorePathSet querySubstitutablePaths(const StorePathSet & paths) override; StorePathSet querySubstitutablePaths(const StorePathSet & paths) override;
void querySubstitutablePathInfos(const StorePathSet & paths, void querySubstitutablePathInfos(const StorePathCAMap & paths,
SubstitutablePathInfos & infos) override; SubstitutablePathInfos & infos) override;
void addToStore(const ValidPathInfo & info, Source & nar, void addToStore(const ValidPathInfo & info, Source & nar,
@ -94,7 +94,7 @@ public:
void queryMissing(const std::vector<StorePathWithOutputs> & targets, void queryMissing(const std::vector<StorePathWithOutputs> & targets,
StorePathSet & willBuild, StorePathSet & willSubstitute, StorePathSet & unknown, StorePathSet & willBuild, StorePathSet & willSubstitute, StorePathSet & unknown,
unsigned long long & downloadSize, unsigned long long & narSize) override; uint64_t & downloadSize, uint64_t & narSize) override;
void connect() override; void connect() override;

View file

@ -193,6 +193,23 @@ StorePath Store::makeFixedOutputPath(
} }
} }
// FIXME Put this somewhere?
template<class... Ts> struct overloaded : Ts... { using Ts::operator()...; };
template<class... Ts> overloaded(Ts...) -> overloaded<Ts...>;
StorePath Store::makeFixedOutputPathFromCA(std::string_view name, ContentAddress ca,
const StorePathSet & references, bool hasSelfReference) const
{
// New template
return std::visit(overloaded {
[&](TextHash th) {
return makeTextPath(name, th.hash, references);
},
[&](FixedOutputHash fsh) {
return makeFixedOutputPath(fsh.method, fsh.hash, name, references, hasSelfReference);
}
}, ca);
}
StorePath Store::makeTextPath(std::string_view name, const Hash & hash, StorePath Store::makeTextPath(std::string_view name, const Hash & hash,
const StorePathSet & references) const const StorePathSet & references) const
@ -689,6 +706,15 @@ void copyStorePath(ref<Store> srcStore, ref<Store> dstStore,
uint64_t total = 0; uint64_t total = 0;
// recompute store path on the chance dstStore does it differently
if (info->ca && info->references.empty()) {
auto info2 = make_ref<ValidPathInfo>(*info);
info2->path = dstStore->makeFixedOutputPathFromCA(info->path.name(), *info->ca);
if (dstStore->storeDir == srcStore->storeDir)
assert(info->path == info2->path);
info = info2;
}
if (!info->narHash) { if (!info->narHash) {
StringSink sink; StringSink sink;
srcStore->narFromPath({storePath}, sink); srcStore->narFromPath({storePath}, sink);
@ -724,7 +750,7 @@ void copyStorePath(ref<Store> srcStore, ref<Store> dstStore,
} }
void copyPaths(ref<Store> srcStore, ref<Store> dstStore, const StorePathSet & storePaths, std::map<StorePath, StorePath> copyPaths(ref<Store> srcStore, ref<Store> dstStore, const StorePathSet & storePaths,
RepairFlag repair, CheckSigsFlag checkSigs, SubstituteFlag substitute) RepairFlag repair, CheckSigsFlag checkSigs, SubstituteFlag substitute)
{ {
auto valid = dstStore->queryValidPaths(storePaths, substitute); auto valid = dstStore->queryValidPaths(storePaths, substitute);
@ -733,7 +759,11 @@ void copyPaths(ref<Store> srcStore, ref<Store> dstStore, const StorePathSet & st
for (auto & path : storePaths) for (auto & path : storePaths)
if (!valid.count(path)) missing.insert(path); if (!valid.count(path)) missing.insert(path);
if (missing.empty()) return; std::map<StorePath, StorePath> pathsMap;
for (auto & path : storePaths)
pathsMap.insert_or_assign(path, path);
if (missing.empty()) return pathsMap;
Activity act(*logger, lvlInfo, actCopyPaths, fmt("copying %d paths", missing.size())); Activity act(*logger, lvlInfo, actCopyPaths, fmt("copying %d paths", missing.size()));
@ -752,14 +782,23 @@ void copyPaths(ref<Store> srcStore, ref<Store> dstStore, const StorePathSet & st
StorePathSet(missing.begin(), missing.end()), StorePathSet(missing.begin(), missing.end()),
[&](const StorePath & storePath) { [&](const StorePath & storePath) {
auto info = srcStore->queryPathInfo(storePath);
auto storePathForDst = storePath;
if (info->ca && info->references.empty()) {
storePathForDst = dstStore->makeFixedOutputPathFromCA(storePath.name(), *info->ca);
if (dstStore->storeDir == srcStore->storeDir)
assert(storePathForDst == storePath);
if (storePathForDst != storePath)
debug("replaced path '%s' to '%s' for substituter '%s'", srcStore->printStorePath(storePath), dstStore->printStorePath(storePathForDst), dstStore->getUri());
}
pathsMap.insert_or_assign(storePath, storePathForDst);
if (dstStore->isValidPath(storePath)) { if (dstStore->isValidPath(storePath)) {
nrDone++; nrDone++;
showProgress(); showProgress();
return StorePathSet(); return StorePathSet();
} }
auto info = srcStore->queryPathInfo(storePath);
bytesExpected += info->narSize; bytesExpected += info->narSize;
act.setExpected(actCopyPath, bytesExpected); act.setExpected(actCopyPath, bytesExpected);
@ -769,7 +808,19 @@ void copyPaths(ref<Store> srcStore, ref<Store> dstStore, const StorePathSet & st
[&](const StorePath & storePath) { [&](const StorePath & storePath) {
checkInterrupt(); checkInterrupt();
if (!dstStore->isValidPath(storePath)) { auto info = srcStore->queryPathInfo(storePath);
auto storePathForDst = storePath;
if (info->ca && info->references.empty()) {
storePathForDst = dstStore->makeFixedOutputPathFromCA(storePath.name(), *info->ca);
if (dstStore->storeDir == srcStore->storeDir)
assert(storePathForDst == storePath);
if (storePathForDst != storePath)
debug("replaced path '%s' to '%s' for substituter '%s'", srcStore->printStorePath(storePath), dstStore->printStorePath(storePathForDst), dstStore->getUri());
}
pathsMap.insert_or_assign(storePath, storePathForDst);
if (!dstStore->isValidPath(storePathForDst)) {
MaintainCount<decltype(nrRunning)> mc(nrRunning); MaintainCount<decltype(nrRunning)> mc(nrRunning);
showProgress(); showProgress();
try { try {
@ -787,6 +838,8 @@ void copyPaths(ref<Store> srcStore, ref<Store> dstStore, const StorePathSet & st
nrDone++; nrDone++;
showProgress(); showProgress();
}); });
return pathsMap;
} }
@ -863,10 +916,6 @@ void ValidPathInfo::sign(const Store & store, const SecretKey & secretKey)
sigs.insert(secretKey.signDetached(fingerprint(store))); sigs.insert(secretKey.signDetached(fingerprint(store)));
} }
// FIXME Put this somewhere?
template<class... Ts> struct overloaded : Ts... { using Ts::operator()...; };
template<class... Ts> overloaded(Ts...) -> overloaded<Ts...>;
bool ValidPathInfo::isContentAddressed(const Store & store) const bool ValidPathInfo::isContentAddressed(const Store & store) const
{ {
if (! ca) return false; if (! ca) return false;

View file

@ -85,7 +85,7 @@ struct GCOptions
StorePathSet pathsToDelete; StorePathSet pathsToDelete;
/* Stop after at least `maxFreed' bytes have been freed. */ /* Stop after at least `maxFreed' bytes have been freed. */
unsigned long long maxFreed{std::numeric_limits<unsigned long long>::max()}; uint64_t maxFreed{std::numeric_limits<uint64_t>::max()};
}; };
@ -97,7 +97,7 @@ struct GCResults
/* For `gcReturnDead', `gcDeleteDead' and `gcDeleteSpecific', the /* For `gcReturnDead', `gcDeleteDead' and `gcDeleteSpecific', the
number of bytes that would be or was freed. */ number of bytes that would be or was freed. */
unsigned long long bytesFreed = 0; uint64_t bytesFreed = 0;
}; };
@ -105,8 +105,8 @@ struct SubstitutablePathInfo
{ {
std::optional<StorePath> deriver; std::optional<StorePath> deriver;
StorePathSet references; StorePathSet references;
unsigned long long downloadSize; /* 0 = unknown or inapplicable */ uint64_t downloadSize; /* 0 = unknown or inapplicable */
unsigned long long narSize; /* 0 = unknown */ uint64_t narSize; /* 0 = unknown */
}; };
typedef std::map<StorePath, SubstitutablePathInfo> SubstitutablePathInfos; typedef std::map<StorePath, SubstitutablePathInfo> SubstitutablePathInfos;
@ -344,7 +344,11 @@ public:
bool hasSelfReference = false) const; bool hasSelfReference = false) const;
StorePath makeTextPath(std::string_view name, const Hash & hash, StorePath makeTextPath(std::string_view name, const Hash & hash,
const StorePathSet & references) const; const StorePathSet & references = {}) const;
StorePath makeFixedOutputPathFromCA(std::string_view name, ContentAddress ca,
const StorePathSet & references = {},
bool hasSelfReference = false) const;
/* This is the preparatory part of addToStore(); it computes the /* This is the preparatory part of addToStore(); it computes the
store path to which srcPath is to be copied. Returns the store store path to which srcPath is to be copied. Returns the store
@ -436,9 +440,10 @@ public:
virtual StorePathSet querySubstitutablePaths(const StorePathSet & paths) { return {}; }; virtual StorePathSet querySubstitutablePaths(const StorePathSet & paths) { return {}; };
/* Query substitute info (i.e. references, derivers and download /* Query substitute info (i.e. references, derivers and download
sizes) of a set of paths. If a path does not have substitute sizes) of a map of paths to their optional ca values. If a path
info, it's omitted from the resulting infos map. */ does not have substitute info, it's omitted from the resulting
virtual void querySubstitutablePathInfos(const StorePathSet & paths, infos map. */
virtual void querySubstitutablePathInfos(const StorePathCAMap & paths,
SubstitutablePathInfos & infos) { return; }; SubstitutablePathInfos & infos) { return; };
/* Import a path into the store. */ /* Import a path into the store. */
@ -610,7 +615,7 @@ public:
that will be substituted. */ that will be substituted. */
virtual void queryMissing(const std::vector<StorePathWithOutputs> & targets, virtual void queryMissing(const std::vector<StorePathWithOutputs> & targets,
StorePathSet & willBuild, StorePathSet & willSubstitute, StorePathSet & unknown, StorePathSet & willBuild, StorePathSet & willSubstitute, StorePathSet & unknown,
unsigned long long & downloadSize, unsigned long long & narSize); uint64_t & downloadSize, uint64_t & narSize);
/* Sort a set of paths topologically under the references /* Sort a set of paths topologically under the references
relation. If p refers to q, then p precedes q in this list. */ relation. If p refers to q, then p precedes q in this list. */
@ -740,11 +745,13 @@ void copyStorePath(ref<Store> srcStore, ref<Store> dstStore,
/* Copy store paths from one store to another. The paths may be copied /* Copy store paths from one store to another. The paths may be copied
in parallel. They are copied in a topologically sorted order in parallel. They are copied in a topologically sorted order (i.e.
(i.e. if A is a reference of B, then A is copied before B), but if A is a reference of B, then A is copied before B), but the set
the set of store paths is not automatically closed; use of store paths is not automatically closed; use copyClosure() for
copyClosure() for that. */ that. Returns a map of what each path was copied to the dstStore
void copyPaths(ref<Store> srcStore, ref<Store> dstStore, const StorePathSet & storePaths, as. */
std::map<StorePath, StorePath> copyPaths(ref<Store> srcStore, ref<Store> dstStore,
const StorePathSet & storePaths,
RepairFlag repair = NoRepair, RepairFlag repair = NoRepair,
CheckSigsFlag checkSigs = CheckSigs, CheckSigsFlag checkSigs = CheckSigs,
SubstituteFlag substitute = NoSubstitute); SubstituteFlag substitute = NoSubstitute);
@ -843,4 +850,6 @@ std::optional<ValidPathInfo> decodeValidPathInfo(
/* Split URI into protocol+hierarchy part and its parameter set. */ /* Split URI into protocol+hierarchy part and its parameter set. */
std::pair<std::string, Store::Params> splitUriAndParams(const std::string & uri); std::pair<std::string, Store::Params> splitUriAndParams(const std::string & uri);
std::optional<ContentAddress> getDerivationCA(const BasicDerivation & drv);
} }

View file

@ -70,6 +70,10 @@ template<class T> T readStorePaths(const Store & store, Source & from);
void writeStorePaths(const Store & store, Sink & out, const StorePathSet & paths); void writeStorePaths(const Store & store, Sink & out, const StorePathSet & paths);
StorePathCAMap readStorePathCAMap(const Store & store, Source & from);
void writeStorePathCAMap(const Store & store, Sink & out, const StorePathCAMap & paths);
void writeOutputPathMap(const Store & store, Sink & out, const OutputPathMap & paths); void writeOutputPathMap(const Store & store, Sink & out, const OutputPathMap & paths);
} }

View file

@ -150,17 +150,17 @@ static void skipGeneric(Source & source)
static void parseContents(ParseSink & sink, Source & source, const Path & path) static void parseContents(ParseSink & sink, Source & source, const Path & path)
{ {
unsigned long long size = readLongLong(source); uint64_t size = readLongLong(source);
sink.preallocateContents(size); sink.preallocateContents(size);
unsigned long long left = size; uint64_t left = size;
std::vector<unsigned char> buf(65536); std::vector<unsigned char> buf(65536);
while (left) { while (left) {
checkInterrupt(); checkInterrupt();
auto n = buf.size(); auto n = buf.size();
if ((unsigned long long)n > left) n = left; if ((uint64_t)n > left) n = left;
source(buf.data(), n); source(buf.data(), n);
sink.receiveContents(buf.data(), n); sink.receiveContents(buf.data(), n);
left -= n; left -= n;
@ -323,7 +323,7 @@ struct RestoreSink : ParseSink
throw SysError("fchmod"); throw SysError("fchmod");
} }
void preallocateContents(unsigned long long len) void preallocateContents(uint64_t len)
{ {
#if HAVE_POSIX_FALLOCATE #if HAVE_POSIX_FALLOCATE
if (len) { if (len) {
@ -338,7 +338,7 @@ struct RestoreSink : ParseSink
#endif #endif
} }
void receiveContents(unsigned char * data, unsigned int len) void receiveContents(unsigned char * data, size_t len)
{ {
writeFull(fd.get(), data, len); writeFull(fd.get(), data, len);
} }

View file

@ -57,8 +57,8 @@ struct ParseSink
virtual void createRegularFile(const Path & path) { }; virtual void createRegularFile(const Path & path) { };
virtual void isExecutable() { }; virtual void isExecutable() { };
virtual void preallocateContents(unsigned long long size) { }; virtual void preallocateContents(uint64_t size) { };
virtual void receiveContents(unsigned char * data, unsigned int len) { }; virtual void receiveContents(unsigned char * data, size_t len) { };
virtual void createSymlink(const Path & path, const string & target) { }; virtual void createSymlink(const Path & path, const string & target) { };
}; };
@ -77,7 +77,7 @@ struct RetrieveRegularNARSink : ParseSink
regular = false; regular = false;
} }
void receiveContents(unsigned char * data, unsigned int len) void receiveContents(unsigned char * data, size_t len)
{ {
sink(data, len); sink(data, len);
} }

View file

@ -111,7 +111,7 @@ Hash hashFile(HashType ht, const Path & path);
/* Compute the hash of the given path. The hash is defined as /* Compute the hash of the given path. The hash is defined as
(essentially) hashString(ht, dumpPath(path)). */ (essentially) hashString(ht, dumpPath(path)). */
typedef std::pair<Hash, unsigned long long> HashResult; typedef std::pair<Hash, uint64_t> HashResult;
HashResult hashPath(HashType ht, const Path & path, HashResult hashPath(HashType ht, const Path & path,
PathFilter & filter = defaultPathFilter); PathFilter & filter = defaultPathFilter);
@ -141,7 +141,7 @@ class HashSink : public BufferedSink, public AbstractHashSink
private: private:
HashType ht; HashType ht;
Ctx * ctx; Ctx * ctx;
unsigned long long bytes; uint64_t bytes;
public: public:
HashSink(HashType ht); HashSink(HashType ht);

View file

@ -312,14 +312,14 @@ T readNum(Source & source)
source(buf, sizeof(buf)); source(buf, sizeof(buf));
uint64_t n = uint64_t n =
((unsigned long long) buf[0]) | ((uint64_t) buf[0]) |
((unsigned long long) buf[1] << 8) | ((uint64_t) buf[1] << 8) |
((unsigned long long) buf[2] << 16) | ((uint64_t) buf[2] << 16) |
((unsigned long long) buf[3] << 24) | ((uint64_t) buf[3] << 24) |
((unsigned long long) buf[4] << 32) | ((uint64_t) buf[4] << 32) |
((unsigned long long) buf[5] << 40) | ((uint64_t) buf[5] << 40) |
((unsigned long long) buf[6] << 48) | ((uint64_t) buf[6] << 48) |
((unsigned long long) buf[7] << 56); ((uint64_t) buf[7] << 56);
if (n > std::numeric_limits<T>::max()) if (n > std::numeric_limits<T>::max())
throw SerialisationError("serialised integer %d is too large for type '%s'", n, typeid(T).name()); throw SerialisationError("serialised integer %d is too large for type '%s'", n, typeid(T).name());

View file

@ -374,7 +374,7 @@ void writeLine(int fd, string s)
} }
static void _deletePath(int parentfd, const Path & path, unsigned long long & bytesFreed) static void _deletePath(int parentfd, const Path & path, uint64_t & bytesFreed)
{ {
checkInterrupt(); checkInterrupt();
@ -414,7 +414,7 @@ static void _deletePath(int parentfd, const Path & path, unsigned long long & by
} }
} }
static void _deletePath(const Path & path, unsigned long long & bytesFreed) static void _deletePath(const Path & path, uint64_t & bytesFreed)
{ {
Path dir = dirOf(path); Path dir = dirOf(path);
if (dir == "") if (dir == "")
@ -435,12 +435,12 @@ static void _deletePath(const Path & path, unsigned long long & bytesFreed)
void deletePath(const Path & path) void deletePath(const Path & path)
{ {
unsigned long long dummy; uint64_t dummy;
deletePath(path, dummy); deletePath(path, dummy);
} }
void deletePath(const Path & path, unsigned long long & bytesFreed) void deletePath(const Path & path, uint64_t & bytesFreed)
{ {
//Activity act(*logger, lvlDebug, format("recursively deleting path '%1%'") % path); //Activity act(*logger, lvlDebug, format("recursively deleting path '%1%'") % path);
bytesFreed = 0; bytesFreed = 0;

View file

@ -125,7 +125,7 @@ void writeLine(int fd, string s);
second variant returns the number of bytes and blocks freed. */ second variant returns the number of bytes and blocks freed. */
void deletePath(const Path & path); void deletePath(const Path & path);
void deletePath(const Path & path, unsigned long long & bytesFreed); void deletePath(const Path & path, uint64_t & bytesFreed);
std::string getUserName(); std::string getUserName();

View file

@ -174,7 +174,7 @@ static void _main(int argc, char * * argv)
else if (*arg == "--run-env") // obsolete else if (*arg == "--run-env") // obsolete
runEnv = true; runEnv = true;
else if (*arg == "--command" || *arg == "--run") { else if (runEnv && (*arg == "--command" || *arg == "--run")) {
if (*arg == "--run") if (*arg == "--run")
interactive = false; interactive = false;
envCommand = getArg(*arg, arg, end) + "\nexit"; envCommand = getArg(*arg, arg, end) + "\nexit";
@ -192,7 +192,7 @@ static void _main(int argc, char * * argv)
else if (*arg == "--pure") pure = true; else if (*arg == "--pure") pure = true;
else if (*arg == "--impure") pure = false; else if (*arg == "--impure") pure = false;
else if (*arg == "--packages" || *arg == "-p") else if (runEnv && (*arg == "--packages" || *arg == "-p"))
packages = true; packages = true;
else if (inShebang && *arg == "-i") { else if (inShebang && *arg == "-i") {
@ -325,7 +325,7 @@ static void _main(int argc, char * * argv)
auto buildPaths = [&](const std::vector<StorePathWithOutputs> & paths) { auto buildPaths = [&](const std::vector<StorePathWithOutputs> & paths) {
/* Note: we do this even when !printMissing to efficiently /* Note: we do this even when !printMissing to efficiently
fetch binary cache data. */ fetch binary cache data. */
unsigned long long downloadSize, narSize; uint64_t downloadSize, narSize;
StorePathSet willBuild, willSubstitute, unknown; StorePathSet willBuild, willSubstitute, unknown;
store->queryMissing(paths, store->queryMissing(paths,
willBuild, willSubstitute, unknown, downloadSize, narSize); willBuild, willSubstitute, unknown, downloadSize, narSize);

View file

@ -67,10 +67,8 @@ static int _main(int argc, char * * argv)
deleteOlderThan = getArg(*arg, arg, end); deleteOlderThan = getArg(*arg, arg, end);
} }
else if (*arg == "--dry-run") dryRun = true; else if (*arg == "--dry-run") dryRun = true;
else if (*arg == "--max-freed") { else if (*arg == "--max-freed")
long long maxFreed = getIntArg<long long>(*arg, arg, end, true); options.maxFreed = std::max(getIntArg<int64_t>(*arg, arg, end, true), (int64_t) 0);
options.maxFreed = maxFreed >= 0 ? maxFreed : 0;
}
else else
return false; return false;
return true; return true;

View file

@ -130,7 +130,7 @@ static void opRealise(Strings opFlags, Strings opArgs)
for (auto & i : opArgs) for (auto & i : opArgs)
paths.push_back(store->followLinksToStorePathWithOutputs(i)); paths.push_back(store->followLinksToStorePathWithOutputs(i));
unsigned long long downloadSize, narSize; uint64_t downloadSize, narSize;
StorePathSet willBuild, willSubstitute, unknown; StorePathSet willBuild, willSubstitute, unknown;
store->queryMissing(paths, willBuild, willSubstitute, unknown, downloadSize, narSize); store->queryMissing(paths, willBuild, willSubstitute, unknown, downloadSize, narSize);
@ -572,10 +572,8 @@ static void opGC(Strings opFlags, Strings opArgs)
if (*i == "--print-roots") printRoots = true; if (*i == "--print-roots") printRoots = true;
else if (*i == "--print-live") options.action = GCOptions::gcReturnLive; else if (*i == "--print-live") options.action = GCOptions::gcReturnLive;
else if (*i == "--print-dead") options.action = GCOptions::gcReturnDead; else if (*i == "--print-dead") options.action = GCOptions::gcReturnDead;
else if (*i == "--max-freed") { else if (*i == "--max-freed")
long long maxFreed = getIntArg<long long>(*i, i, opFlags.end(), true); options.maxFreed = std::max(getIntArg<int64_t>(*i, i, opFlags.end(), true), (int64_t) 0);
options.maxFreed = maxFreed >= 0 ? maxFreed : 0;
}
else throw UsageError("bad sub-operation '%1%' in GC", *i); else throw UsageError("bad sub-operation '%1%' in GC", *i);
if (!opArgs.empty()) throw UsageError("no arguments expected"); if (!opArgs.empty()) throw UsageError("no arguments expected");
@ -831,7 +829,7 @@ static void opServe(Strings opFlags, Strings opArgs)
for (auto & path : paths) for (auto & path : paths)
if (!path.isDerivation()) if (!path.isDerivation())
paths2.push_back({path}); paths2.push_back({path});
unsigned long long downloadSize, narSize; uint64_t downloadSize, narSize;
StorePathSet willBuild, willSubstitute, unknown; StorePathSet willBuild, willSubstitute, unknown;
store->queryMissing(paths2, store->queryMissing(paths2,
willBuild, willSubstitute, unknown, downloadSize, narSize); willBuild, willSubstitute, unknown, downloadSize, narSize);

View file

@ -9,6 +9,7 @@ struct CmdAddToStore : MixDryRun, StoreCommand
{ {
Path path; Path path;
std::optional<std::string> namePart; std::optional<std::string> namePart;
FileIngestionMethod ingestionMethod = FileIngestionMethod::Recursive;
CmdAddToStore() CmdAddToStore()
{ {
@ -21,6 +22,13 @@ struct CmdAddToStore : MixDryRun, StoreCommand
.labels = {"name"}, .labels = {"name"},
.handler = {&namePart}, .handler = {&namePart},
}); });
addFlag({
.longName = "flat",
.shortName = 0,
.description = "add flat file to the Nix store",
.handler = {&ingestionMethod, FileIngestionMethod::Flat},
});
} }
std::string description() override std::string description() override
@ -45,12 +53,19 @@ struct CmdAddToStore : MixDryRun, StoreCommand
auto narHash = hashString(htSHA256, *sink.s); auto narHash = hashString(htSHA256, *sink.s);
ValidPathInfo info(store->makeFixedOutputPath(FileIngestionMethod::Recursive, narHash, *namePart)); Hash hash = narHash;
if (ingestionMethod == FileIngestionMethod::Flat) {
HashSink hsink(htSHA256);
readFile(path, hsink);
hash = hsink.finish().first;
}
ValidPathInfo info(store->makeFixedOutputPath(ingestionMethod, hash, *namePart));
info.narHash = narHash; info.narHash = narHash;
info.narSize = sink.s->size(); info.narSize = sink.s->size();
info.ca = std::optional { FixedOutputHash { info.ca = std::optional { FixedOutputHash {
.method = FileIngestionMethod::Recursive, .method = ingestionMethod,
.hash = *info.narHash, .hash = hash,
} }; } };
if (!dryRun) { if (!dryRun) {

127
src/nix/bundle.cc Normal file
View file

@ -0,0 +1,127 @@
#include "command.hh"
#include "common-args.hh"
#include "shared.hh"
#include "store-api.hh"
#include "fs-accessor.hh"
using namespace nix;
struct CmdBundle : InstallableCommand
{
std::string bundler = "github:matthewbauer/nix-bundle";
std::optional<Path> outLink;
CmdBundle()
{
addFlag({
.longName = "bundler",
.description = "use custom bundler",
.labels = {"flake-url"},
.handler = {&bundler},
.completer = {[&](size_t, std::string_view prefix) {
completeFlakeRef(getStore(), prefix);
}}
});
addFlag({
.longName = "out-link",
.shortName = 'o',
.description = "path of the symlink to the build result",
.labels = {"path"},
.handler = {&outLink},
.completer = completePath
});
}
std::string description() override
{
return "bundle an application so that it works outside of the Nix store";
}
Examples examples() override
{
return {
Example{
"To bundle Hello:",
"nix bundle hello"
},
};
}
Strings getDefaultFlakeAttrPaths() override
{
Strings res{"defaultApp." + settings.thisSystem.get()};
for (auto & s : SourceExprCommand::getDefaultFlakeAttrPaths())
res.push_back(s);
return res;
}
Strings getDefaultFlakeAttrPathPrefixes() override
{
Strings res{"apps." + settings.thisSystem.get() + ".", "packages"};
for (auto & s : SourceExprCommand::getDefaultFlakeAttrPathPrefixes())
res.push_back(s);
return res;
}
void run(ref<Store> store) override
{
auto evalState = getEvalState();
auto app = installable->toApp(*evalState);
store->buildPaths(app.context);
auto [bundlerFlakeRef, bundlerName] = parseFlakeRefWithFragment(bundler, absPath("."));
const flake::LockFlags lockFlags{ .writeLockFile = false };
auto bundler = InstallableFlake(
evalState, std::move(bundlerFlakeRef),
Strings{bundlerName == "" ? "defaultBundler" : bundlerName},
Strings({"bundlers."}), lockFlags);
Value * arg = evalState->allocValue();
evalState->mkAttrs(*arg, 2);
PathSet context;
for (auto & i : app.context)
context.insert("=" + store->printStorePath(i.path));
mkString(*evalState->allocAttr(*arg, evalState->symbols.create("program")), app.program, context);
mkString(*evalState->allocAttr(*arg, evalState->symbols.create("system")), settings.thisSystem.get());
arg->attrs->sort();
auto vRes = evalState->allocValue();
evalState->callFunction(*bundler.toValue(*evalState).first, *arg, *vRes, noPos);
if (!evalState->isDerivation(*vRes))
throw Error("the bundler '%s' does not produce a derivation", bundler.what());
auto attr1 = vRes->attrs->find(evalState->sDrvPath);
if (!attr1)
throw Error("the bundler '%s' does not produce a derivation", bundler.what());
PathSet context2;
StorePath drvPath = store->parseStorePath(evalState->coerceToPath(*attr1->pos, *attr1->value, context2));
auto attr2 = vRes->attrs->find(evalState->sOutPath);
if (!attr2)
throw Error("the bundler '%s' does not produce a derivation", bundler.what());
StorePath outPath = store->parseStorePath(evalState->coerceToPath(*attr2->pos, *attr2->value, context2));
store->buildPaths({{drvPath}});
auto outPathS = store->printStorePath(outPath);
auto info = store->queryPathInfo(outPath);
if (!info->references.empty())
throw Error("'%s' has references; a bundler must not leave any references", outPathS);
if (!outLink)
outLink = baseNameOf(app.program);
store.dynamic_pointer_cast<LocalFSStore>()->addPermRoot(outPath, absPath(*outLink), true);
}
};
static auto r2 = registerCommand<CmdBundle>("bundle");

View file

@ -368,6 +368,21 @@ struct CmdFlakeCheck : FlakeCommand
} }
}; };
auto checkBundler = [&](const std::string & attrPath, Value & v, const Pos & pos) {
try {
state->forceValue(v, pos);
if (v.type != tLambda)
throw Error("bundler must be a function");
if (!v.lambda.fun->formals ||
v.lambda.fun->formals->argNames.find(state->symbols.create("program")) == v.lambda.fun->formals->argNames.end() ||
v.lambda.fun->formals->argNames.find(state->symbols.create("system")) == v.lambda.fun->formals->argNames.end())
throw Error("bundler must take formal arguments 'program' and 'system'");
} catch (Error & e) {
e.addTrace(pos, hintfmt("while checking the template '%s'", attrPath));
throw;
}
};
{ {
Activity act(*logger, lvlInfo, actUnknown, "evaluating flake"); Activity act(*logger, lvlInfo, actUnknown, "evaluating flake");
@ -490,6 +505,16 @@ struct CmdFlakeCheck : FlakeCommand
*attr.value, *attr.pos); *attr.value, *attr.pos);
} }
else if (name == "defaultBundler")
checkBundler(name, vOutput, pos);
else if (name == "bundlers") {
state->forceAttrs(vOutput, pos);
for (auto & attr : *vOutput.attrs)
checkBundler(fmt("%s.%s", name, attr.name),
*attr.value, *attr.pos);
}
else else
warn("unknown flake output '%s'", name); warn("unknown flake output '%s'", name);

View file

@ -61,7 +61,7 @@ struct CmdPathInfo : StorePathsCommand, MixJSON
}; };
} }
void printSize(unsigned long long value) void printSize(uint64_t value)
{ {
if (!humanReadable) { if (!humanReadable) {
std::cout << fmt("\t%11d", value); std::cout << fmt("\t%11d", value);

View file

@ -61,30 +61,30 @@ nix-build check.nix -A nondeterministic --no-out-link --repeat 1 2> $TEST_ROOT/l
[ "$status" = "1" ] [ "$status" = "1" ]
grep 'differs from previous round' $TEST_ROOT/log grep 'differs from previous round' $TEST_ROOT/log
path=$(nix-build check.nix -A fetchurl --no-out-link --hashed-mirrors '') path=$(nix-build check.nix -A fetchurl --no-out-link)
chmod +w $path chmod +w $path
echo foo > $path echo foo > $path
chmod -w $path chmod -w $path
nix-build check.nix -A fetchurl --no-out-link --check --hashed-mirrors '' nix-build check.nix -A fetchurl --no-out-link --check
# Note: "check" doesn't repair anything, it just compares to the hash stored in the database. # Note: "check" doesn't repair anything, it just compares to the hash stored in the database.
[[ $(cat $path) = foo ]] [[ $(cat $path) = foo ]]
nix-build check.nix -A fetchurl --no-out-link --repair --hashed-mirrors '' nix-build check.nix -A fetchurl --no-out-link --repair
[[ $(cat $path) != foo ]] [[ $(cat $path) != foo ]]
nix-build check.nix -A hashmismatch --no-out-link --hashed-mirrors '' || status=$? nix-build check.nix -A hashmismatch --no-out-link || status=$?
[ "$status" = "102" ] [ "$status" = "102" ]
echo -n > ./dummy echo -n > ./dummy
nix-build check.nix -A hashmismatch --no-out-link --hashed-mirrors '' nix-build check.nix -A hashmismatch --no-out-link
echo 'Hello World' > ./dummy echo 'Hello World' > ./dummy
nix-build check.nix -A hashmismatch --no-out-link --check --hashed-mirrors '' || status=$? nix-build check.nix -A hashmismatch --no-out-link --check || status=$?
[ "$status" = "102" ] [ "$status" = "102" ]
# Multiple failures with --keep-going # Multiple failures with --keep-going
nix-build check.nix -A nondeterministic --no-out-link nix-build check.nix -A nondeterministic --no-out-link
nix-build check.nix -A nondeterministic -A hashmismatch --no-out-link --check --keep-going --hashed-mirrors '' || status=$? nix-build check.nix -A nondeterministic -A hashmismatch --no-out-link --check --keep-going || status=$?
[ "$status" = "110" ] [ "$status" = "110" ]

View file

@ -5,7 +5,7 @@ clearStore
# Test fetching a flat file. # Test fetching a flat file.
hash=$(nix-hash --flat --type sha256 ./fetchurl.sh) hash=$(nix-hash --flat --type sha256 ./fetchurl.sh)
outPath=$(nix-build '<nix/fetchurl.nix>' --argstr url file://$(pwd)/fetchurl.sh --argstr sha256 $hash --no-out-link --hashed-mirrors '') outPath=$(nix-build '<nix/fetchurl.nix>' --argstr url file://$(pwd)/fetchurl.sh --argstr sha256 $hash --no-out-link)
cmp $outPath fetchurl.sh cmp $outPath fetchurl.sh
@ -14,7 +14,7 @@ clearStore
hash=$(nix hash-file --type sha512 --base64 ./fetchurl.sh) hash=$(nix hash-file --type sha512 --base64 ./fetchurl.sh)
outPath=$(nix-build '<nix/fetchurl.nix>' --argstr url file://$(pwd)/fetchurl.sh --argstr sha512 $hash --no-out-link --hashed-mirrors '') outPath=$(nix-build '<nix/fetchurl.nix>' --argstr url file://$(pwd)/fetchurl.sh --argstr sha512 $hash --no-out-link)
cmp $outPath fetchurl.sh cmp $outPath fetchurl.sh
@ -25,26 +25,24 @@ hash=$(nix hash-file ./fetchurl.sh)
[[ $hash =~ ^sha256- ]] [[ $hash =~ ^sha256- ]]
outPath=$(nix-build '<nix/fetchurl.nix>' --argstr url file://$(pwd)/fetchurl.sh --argstr hash $hash --no-out-link --hashed-mirrors '') outPath=$(nix-build '<nix/fetchurl.nix>' --argstr url file://$(pwd)/fetchurl.sh --argstr hash $hash --no-out-link)
cmp $outPath fetchurl.sh cmp $outPath fetchurl.sh
# Test the hashed mirror feature. # Test that we can substitute from a different store dir.
clearStore clearStore
hash=$(nix hash-file --type sha512 --base64 ./fetchurl.sh) other_store=file://$TEST_ROOT/other_store?store=/fnord/store
hash32=$(nix hash-file --type sha512 --base16 ./fetchurl.sh)
mirror=$TEST_ROOT/hashed-mirror hash=$(nix hash-file --type sha256 --base16 ./fetchurl.sh)
rm -rf $mirror
mkdir -p $mirror/sha512
ln -s $(pwd)/fetchurl.sh $mirror/sha512/$hash32
outPath=$(nix-build '<nix/fetchurl.nix>' --argstr url file:///no-such-dir/fetchurl.sh --argstr sha512 $hash --no-out-link --hashed-mirrors "file://$mirror") storePath=$(nix --store $other_store add-to-store --flat ./fetchurl.sh)
outPath=$(nix-build '<nix/fetchurl.nix>' --argstr url file:///no-such-dir/fetchurl.sh --argstr sha256 $hash --no-out-link --substituters $other_store)
# Test hashed mirrors with an SRI hash. # Test hashed mirrors with an SRI hash.
nix-build '<nix/fetchurl.nix>' --argstr url file:///no-such-dir/fetchurl.sh --argstr hash $(nix to-sri --type sha512 $hash) \ nix-build '<nix/fetchurl.nix>' --argstr url file:///no-such-dir/fetchurl.sh --argstr hash $(nix to-sri --type sha256 $hash) \
--argstr name bla --no-out-link --hashed-mirrors "file://$mirror" --no-out-link --substituters $other_store
# Test unpacking a NAR. # Test unpacking a NAR.
rm -rf $TEST_ROOT/archive rm -rf $TEST_ROOT/archive

View file

@ -10,10 +10,16 @@ touch $TEST_ROOT/filterin/bak
touch $TEST_ROOT/filterin/bla.c.bak touch $TEST_ROOT/filterin/bla.c.bak
ln -s xyzzy $TEST_ROOT/filterin/link ln -s xyzzy $TEST_ROOT/filterin/link
nix-build ./filter-source.nix -o $TEST_ROOT/filterout checkFilter() {
test ! -e $1/foo/bar
test -e $1/xyzzy
test -e $1/bak
test ! -e $1/bla.c.bak
test ! -L $1/link
}
test ! -e $TEST_ROOT/filterout/foo/bar nix-build ./filter-source.nix -o $TEST_ROOT/filterout1
test -e $TEST_ROOT/filterout/xyzzy checkFilter $TEST_ROOT/filterout1
test -e $TEST_ROOT/filterout/bak
test ! -e $TEST_ROOT/filterout/bla.c.bak nix-build ./path.nix -o $TEST_ROOT/filterout2
test ! -L $TEST_ROOT/filterout/link checkFilter $TEST_ROOT/filterout2

14
tests/path.nix Normal file
View file

@ -0,0 +1,14 @@
with import ./config.nix;
mkDerivation {
name = "filter";
builder = builtins.toFile "builder" "ln -s $input $out";
input =
builtins.path {
path = ((builtins.getEnv "TEST_ROOT") + "/filterin");
filter = path: type:
type != "symlink"
&& baseNameOf path != "foo"
&& !((import ./lang/lib.nix).hasSuffix ".bak" (baseNameOf path));
};
}