diff --git a/src/build-remote/build-remote.cc b/src/build-remote/build-remote.cc index ff8ba2724147..372b622037d6 100644 --- a/src/build-remote/build-remote.cc +++ b/src/build-remote/build-remote.cc @@ -24,8 +24,7 @@ using namespace nix; using std::cin; -static void handleAlarm(int sig) { -} +static void handleAlarm(int sig) {} std::string escapeUri(std::string uri) { @@ -37,16 +36,20 @@ static std::string currentLoad; 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); } -static bool allSupportedLocally(Store & store, const std::set& requiredFeatures) { +static bool allSupportedLocally(Store & store, + const std::set & requiredFeatures) +{ for (auto & feature : requiredFeatures) - if (!store.systemFeatures.get().count(feature)) return false; + if (!store.systemFeatures.get().count(feature)) + return false; return true; } -static int main_build_remote(int argc, char * * argv) +static int main_build_remote(int argc, char ** argv) { { logger = makeJSONLogger(*logger); @@ -72,7 +75,8 @@ static int main_build_remote(int argc, char * * argv) settings.set(name, value); } - settings.maxBuildJobs.set("1"); // hack to make tests with local?root= work + settings.maxBuildJobs.set( + "1"); // hack to make tests with local?root= work initPlugins(); @@ -82,7 +86,7 @@ static int main_build_remote(int argc, char * * argv) that gets cleared on reboot, but it wouldn't work on macOS. */ auto currentLoadName = "/current-load"; if (auto localStore = store.dynamic_pointer_cast()) - currentLoad = std::string { localStore->stateDir } + currentLoadName; + currentLoad = std::string{localStore->stateDir} + currentLoadName; else currentLoad = settings.nixStateDir + currentLoadName; @@ -104,25 +108,30 @@ static int main_build_remote(int argc, char * * argv) try { auto s = readString(source); - if (s != "try") return 0; - } catch (EndOfFile &) { return 0; } + if (s != "try") + return 0; + } catch (EndOfFile &) { + return 0; + } auto amWilling = readInt(source); auto neededSystem = readString(source); drvPath = store->parseStorePath(readString(source)); auto requiredFeatures = readStrings>(source); - auto canBuildLocally = amWilling - && ( neededSystem == settings.thisSystem - || settings.extraPlatforms.get().count(neededSystem) > 0) - && allSupportedLocally(*store, requiredFeatures); + auto canBuildLocally = + amWilling && + (neededSystem == settings.thisSystem || + settings.extraPlatforms.get().count(neededSystem) > 0) && + allSupportedLocally(*store, requiredFeatures); /* Error ignored here, will be caught later */ mkdir(currentLoad.c_str(), 0777); while (true) { bestSlotLock = -1; - AutoCloseFD lock = openLockFile(currentLoad + "/main-lock", true); + AutoCloseFD lock = + openLockFile(currentLoad + "/main-lock", true); lockFile(lock.get(), ltWrite, true); bool rightType = false; @@ -130,16 +139,15 @@ static int main_build_remote(int argc, char * * argv) Machine * bestMachine = nullptr; uint64_t bestLoad = 0; for (auto & m : machines) { - debug("considering building on remote machine '%s'", m.storeUri); + debug("considering building on remote machine '%s'", + m.storeUri); - if (m.enabled - && (neededSystem == "builtin" - || std::find(m.systemTypes.begin(), - m.systemTypes.end(), - neededSystem) != m.systemTypes.end()) && + if (m.enabled && + (neededSystem == "builtin" || + std::find(m.systemTypes.begin(), m.systemTypes.end(), + neededSystem) != m.systemTypes.end()) && m.allSupported(requiredFeatures) && - m.mandatoryMet(requiredFeatures)) - { + m.mandatoryMet(requiredFeatures)) { rightType = true; AutoCloseFD free; uint64_t load = 0; @@ -159,12 +167,15 @@ static int main_build_remote(int argc, char * * argv) bool best = false; if (!bestSlotLock) { best = true; - } else if (load / m.speedFactor < bestLoad / bestMachine->speedFactor) { + } else if (load / m.speedFactor < + bestLoad / bestMachine->speedFactor) { best = true; - } else if (load / m.speedFactor == bestLoad / bestMachine->speedFactor) { + } else if (load / m.speedFactor == + bestLoad / bestMachine->speedFactor) { if (m.speedFactor > bestMachine->speedFactor) { best = true; - } else if (m.speedFactor == bestMachine->speedFactor) { + } else if (m.speedFactor == + bestMachine->speedFactor) { if (load < bestLoad) { best = true; } @@ -181,14 +192,15 @@ static int main_build_remote(int argc, char * * argv) if (!bestSlotLock) { if (rightType && !canBuildLocally) std::cerr << "# postpone\n"; - else - { + else { // build the hint template. std::string errorText = "Failed to find a machine for remote build!\n" - "derivation: %s\nrequired (system, features): (%s, %s)"; + "derivation: %s\nrequired (system, features): (%s, " + "%s)"; errorText += "\n%s available machines:"; - errorText += "\n(systems, maxjobs, supportedFeatures, mandatoryFeatures)"; + errorText += "\n(systems, maxjobs, supportedFeatures, " + "mandatoryFeatures)"; for (unsigned int i = 0; i < machines.size(); ++i) errorText += "\n(%s, %s, %s, %s)"; @@ -201,18 +213,20 @@ static int main_build_remote(int argc, char * * argv) drvstr = ""; auto error = hintformat(errorText); - error - % drvstr - % neededSystem - % concatStringsSep(", ", requiredFeatures) - % machines.size(); + error % drvstr % neededSystem % + concatStringsSep(", ", + requiredFeatures) % + machines.size(); for (auto & m : machines) - error - % concatStringsSep>(", ", m.systemTypes) - % m.maxJobs - % concatStringsSep(", ", m.supportedFeatures) - % concatStringsSep(", ", m.mandatoryFeatures); + error % + concatStringsSep>( + ", ", m.systemTypes) % + m.maxJobs % + concatStringsSep( + ", ", m.supportedFeatures) % + concatStringsSep( + ", ", m.mandatoryFeatures); printMsg(canBuildLocally ? lvlChatty : lvlWarn, error); @@ -231,7 +245,9 @@ static int main_build_remote(int argc, char * * argv) try { - Activity act(*logger, lvlTalkative, actUnknown, fmt("connecting to '%s'", bestMachine->storeUri)); + Activity act( + *logger, lvlTalkative, actUnknown, + fmt("connecting to '%s'", bestMachine->storeUri)); sshStore = bestMachine->openStore(); sshStore->connect(); @@ -240,8 +256,8 @@ static int main_build_remote(int argc, char * * argv) } catch (std::exception & e) { auto msg = chomp(drainFD(5, false)); printError("cannot build on '%s': %s%s", - bestMachine->storeUri, e.what(), - msg.empty() ? "" : ": " + msg); + bestMachine->storeUri, e.what(), + msg.empty() ? "" : ": " + msg); bestMachine->enabled = false; continue; } @@ -250,7 +266,7 @@ static int main_build_remote(int argc, char * * argv) } } -connected: + connected: close(5); std::cerr << "# accept\n" << storeUri << "\n"; @@ -258,24 +274,30 @@ static int main_build_remote(int argc, char * * argv) auto inputs = readStrings(source); auto wantedOutputs = readStrings(source); - AutoCloseFD uploadLock = openLockFile(currentLoad + "/" + escapeUri(storeUri) + ".upload-lock", true); + AutoCloseFD uploadLock = openLockFile( + currentLoad + "/" + escapeUri(storeUri) + ".upload-lock", true); { - Activity act(*logger, lvlTalkative, actUnknown, fmt("waiting for the upload lock to '%s'", storeUri)); + Activity act(*logger, lvlTalkative, actUnknown, + fmt("waiting for the upload lock to '%s'", storeUri)); auto old = signal(SIGALRM, handleAlarm); alarm(15 * 60); if (!lockFile(uploadLock.get(), ltWrite, true)) - printError("somebody is hogging the upload lock for '%s', continuing..."); + printError("somebody is hogging the upload lock for '%s', " + "continuing..."); alarm(0); signal(SIGALRM, old); } - auto substitute = settings.buildersUseSubstitutes ? Substitute : NoSubstitute; + auto substitute = + settings.buildersUseSubstitutes ? Substitute : NoSubstitute; { - Activity act(*logger, lvlTalkative, actUnknown, fmt("copying dependencies to '%s'", storeUri)); - copyPaths(*store, *sshStore, store->parseStorePathSet(inputs), NoRepair, NoCheckSigs, substitute); + Activity act(*logger, lvlTalkative, actUnknown, + fmt("copying dependencies to '%s'", storeUri)); + copyPaths(*store, *sshStore, store->parseStorePathSet(inputs), + NoRepair, NoCheckSigs, substitute); } uploadLock = -1; @@ -296,14 +318,17 @@ static int main_build_remote(int argc, char * * argv) auto result = sshStore->buildDerivation(*drvPath, drv); if (!result.success()) - throw Error("build of '%s' on '%s' failed: %s", store->printStorePath(*drvPath), storeUri, result.errorMsg); + throw Error("build of '%s' on '%s' failed: %s", + store->printStorePath(*drvPath), storeUri, + result.errorMsg); std::set missingRealisations; StorePathSet missingPaths; - if (settings.isExperimentalFeatureEnabled(Xp::CaDerivations) && !drv.type().hasKnownOutputPaths()) { + if (settings.isExperimentalFeatureEnabled(Xp::CaDerivations) && + !drv.type().hasKnownOutputPaths()) { for (auto & outputName : wantedOutputs) { auto thisOutputHash = outputHashes.at(outputName); - auto thisOutputId = DrvOutput{ thisOutputHash, outputName }; + auto thisOutputId = DrvOutput{thisOutputHash, outputName}; if (!store->queryRealisation(thisOutputId)) { debug("missing output %s", outputName); assert(result.builtOutputs.count(thisOutputId)); @@ -322,11 +347,14 @@ static int main_build_remote(int argc, char * * argv) } if (!missingPaths.empty()) { - Activity act(*logger, lvlTalkative, actUnknown, fmt("copying outputs from '%s'", storeUri)); + Activity act(*logger, lvlTalkative, actUnknown, + fmt("copying outputs from '%s'", storeUri)); if (auto localStore = store.dynamic_pointer_cast()) for (auto & path : missingPaths) - localStore->locksHeld.insert(store->printStorePath(path)); /* FIXME: ugly */ - copyPaths(*sshStore, *store, missingPaths, NoRepair, NoCheckSigs, NoSubstitute); + localStore->locksHeld.insert( + store->printStorePath(path)); /* FIXME: ugly */ + copyPaths(*sshStore, *store, missingPaths, NoRepair, NoCheckSigs, + NoSubstitute); } // XXX: Should be done as part of `copyPaths` for (auto & realisation : missingRealisations) { diff --git a/src/libcmd/command.cc b/src/libcmd/command.cc index 7f8072d75fcb..63390fd0de98 100644 --- a/src/libcmd/command.cc +++ b/src/libcmd/command.cc @@ -7,20 +7,22 @@ #include -extern char * * environ __attribute__((weak)); +extern char ** environ __attribute__((weak)); namespace nix { RegisterCommand::Commands * RegisterCommand::commands = nullptr; -nix::Commands RegisterCommand::getCommandsFor(const std::vector & prefix) +nix::Commands +RegisterCommand::getCommandsFor(const std::vector & prefix) { nix::Commands res; for (auto & [name, command] : *RegisterCommand::commands) if (name.size() == prefix.size() + 1) { bool equal = true; for (size_t i = 0; i < prefix.size(); ++i) - if (name[i] != prefix[i]) equal = false; + if (name[i] != prefix[i]) + equal = false; if (equal) res.insert_or_assign(name[prefix.size()], command); } @@ -33,9 +35,7 @@ nlohmann::json NixMultiCommand::toJSON() return MultiCommand::toJSON(); } -StoreCommand::StoreCommand() -{ -} +StoreCommand::StoreCommand() {} ref StoreCommand::getStore() { @@ -44,15 +44,9 @@ ref StoreCommand::getStore() return ref(_store); } -ref StoreCommand::createStore() -{ - return openStore(); -} +ref StoreCommand::createStore() { return openStore(); } -void StoreCommand::run() -{ - run(getStore()); -} +void StoreCommand::run() { run(getStore()); } CopyCommand::CopyCommand() { @@ -110,24 +104,23 @@ ref EvalCommand::getEvalState() { if (!evalState) { evalState = - #if HAVE_BOEHMGC +#if HAVE_BOEHMGC std::allocate_shared(traceable_allocator(), - searchPath, getEvalStore(), getStore()) - #else - std::make_shared( - searchPath, getEvalStore(), getStore()) - #endif + searchPath, getEvalStore(), + getStore()) +#else + std::make_shared(searchPath, getEvalStore(), getStore()) +#endif ; if (startReplOnEvalErrors) { - evalState->debugRepl = &runRepl; + evalState->debugRepl = &runRepl; }; } return ref(evalState); } -BuiltPathsCommand::BuiltPathsCommand(bool recursive) - : recursive(recursive) +BuiltPathsCommand::BuiltPathsCommand(bool recursive) : recursive(recursive) { if (recursive) addFlag({ @@ -163,7 +156,8 @@ void BuiltPathsCommand::run(ref store) for (auto & p : store->queryAllValidPaths()) paths.push_back(BuiltPath::Opaque{p}); } else { - paths = Installable::toBuiltPaths(getEvalStore(), store, realiseMode, operateOn, installables); + paths = Installable::toBuiltPaths(getEvalStore(), store, realiseMode, + operateOn, installables); if (recursive) { // XXX: This only computes the store path closure, ignoring // intermediate realisations @@ -199,7 +193,8 @@ void StorePathsCommand::run(ref store, BuiltPaths && paths) run(store, std::move(sorted)); } -void StorePathCommand::run(ref store, std::vector && storePaths) +void StorePathCommand::run(ref store, + std::vector && storePaths) { if (storePaths.size() != 1) throw UsageError("this command requires exactly one store path"); @@ -211,11 +206,10 @@ Strings editorFor(const Path & file, uint32_t line) { auto editor = getEnv("EDITOR").value_or("cat"); auto args = tokenizeString(editor); - if (line > 0 && ( - editor.find("emacs") != std::string::npos || - editor.find("nano") != std::string::npos || - editor.find("vim") != std::string::npos || - editor.find("kak") != std::string::npos)) + if (line > 0 && (editor.find("emacs") != std::string::npos || + editor.find("nano") != std::string::npos || + editor.find("vim") != std::string::npos || + editor.find("kak") != std::string::npos)) args.push_back(fmt("+%d", line)); args.push_back(file); return args; @@ -223,63 +217,63 @@ Strings editorFor(const Path & file, uint32_t line) MixProfile::MixProfile() { - addFlag({ - .longName = "profile", - .description = "The profile to update.", - .labels = {"path"}, - .handler = {&profile}, - .completer = completePath - }); + addFlag({.longName = "profile", + .description = "The profile to update.", + .labels = {"path"}, + .handler = {&profile}, + .completer = completePath}); } void MixProfile::updateProfile(const StorePath & storePath) { - if (!profile) return; + if (!profile) + return; auto store = getStore().dynamic_pointer_cast(); - if (!store) throw Error("'--profile' is not supported for this Nix store"); + if (!store) + throw Error("'--profile' is not supported for this Nix store"); auto profile2 = absPath(*profile); switchLink(profile2, - createGeneration( - ref(store), - profile2, storePath)); + createGeneration(ref(store), profile2, storePath)); } void MixProfile::updateProfile(const BuiltPaths & buildables) { - if (!profile) return; + if (!profile) + return; std::vector result; for (auto & buildable : buildables) { - std::visit(overloaded { - [&](const BuiltPath::Opaque & bo) { - result.push_back(bo.path); - }, - [&](const BuiltPath::Built & bfd) { - for (auto & output : bfd.outputs) { - result.push_back(output.second); - } - }, - }, buildable.raw()); + std::visit(overloaded{ + [&](const BuiltPath::Opaque & bo) { + result.push_back(bo.path); + }, + [&](const BuiltPath::Built & bfd) { + for (auto & output : bfd.outputs) { + result.push_back(output.second); + } + }, + }, + buildable.raw()); } if (result.size() != 1) - throw UsageError("'--profile' requires that the arguments produce a single store path, but there are %d", result.size()); + throw UsageError("'--profile' requires that the arguments produce a " + "single store path, but there are %d", + result.size()); updateProfile(result[0]); } -MixDefaultProfile::MixDefaultProfile() -{ - profile = getDefaultProfile(); -} +MixDefaultProfile::MixDefaultProfile() { profile = getDefaultProfile(); } MixEnvironment::MixEnvironment() : ignoreEnvironment(false) { addFlag({ .longName = "ignore-environment", .shortName = 'i', - .description = "Clear the entire environment (except those specified with `--keep`).", + .description = "Clear the entire environment (except those specified " + "with `--keep`).", .handler = {&ignoreEnvironment, true}, }); @@ -300,21 +294,25 @@ MixEnvironment::MixEnvironment() : ignoreEnvironment(false) }); } -void MixEnvironment::setEnviron() { +void MixEnvironment::setEnviron() +{ if (ignoreEnvironment) { if (!unset.empty()) - throw UsageError("--unset does not make sense with --ignore-environment"); + throw UsageError( + "--unset does not make sense with --ignore-environment"); for (const auto & var : keep) { auto val = getenv(var.c_str()); - if (val) stringsEnv.emplace_back(fmt("%s=%s", var.c_str(), val)); + if (val) + stringsEnv.emplace_back(fmt("%s=%s", var.c_str(), val)); } vectorEnv = stringsToCharPtrs(stringsEnv); environ = vectorEnv.data(); } else { if (!keep.empty()) - throw UsageError("--keep does not make sense without --ignore-environment"); + throw UsageError( + "--keep does not make sense without --ignore-environment"); for (const auto & var : unset) unsetenv(var.c_str()); diff --git a/src/libcmd/command.hh b/src/libcmd/command.hh index 8982f21d0864..31079592b989 100644 --- a/src/libcmd/command.hh +++ b/src/libcmd/command.hh @@ -12,7 +12,7 @@ namespace nix { extern std::string programPath; -extern char * * savedArgv; +extern char ** savedArgv; class EvalState; struct Pos; @@ -22,30 +22,28 @@ static constexpr Command::Category catSecondary = 100; static constexpr Command::Category catUtility = 101; static constexpr Command::Category catNixInstallation = 102; -static constexpr auto installablesCategory = "Options that change the interpretation of installables"; +static constexpr auto installablesCategory = + "Options that change the interpretation of installables"; -struct NixMultiCommand : virtual MultiCommand, virtual Command -{ +struct NixMultiCommand : virtual MultiCommand, virtual Command { nlohmann::json toJSON() override; }; /* A command that requires a Nix store. */ -struct StoreCommand : virtual Command -{ +struct StoreCommand : virtual Command { StoreCommand(); void run() override; ref getStore(); virtual ref createStore(); virtual void run(ref) = 0; -private: + private: std::shared_ptr _store; }; /* A command that copies something between `--from` and `--to` stores. */ -struct CopyCommand : virtual StoreCommand -{ +struct CopyCommand : virtual StoreCommand { std::string srcUri, dstUri; CopyCommand(); @@ -55,8 +53,7 @@ struct CopyCommand : virtual StoreCommand ref getDstStore(); }; -struct EvalCommand : virtual StoreCommand, MixEvalArgs -{ +struct EvalCommand : virtual StoreCommand, MixEvalArgs { bool startReplOnEvalErrors = false; EvalCommand(); @@ -67,24 +64,21 @@ struct EvalCommand : virtual StoreCommand, MixEvalArgs ref getEvalState(); -private: + private: std::shared_ptr evalStore; std::shared_ptr evalState; }; -struct MixFlakeOptions : virtual Args, EvalCommand -{ +struct MixFlakeOptions : virtual Args, EvalCommand { flake::LockFlags lockFlags; MixFlakeOptions(); - virtual std::optional getFlakeRefForCompletion() - { return {}; } + virtual std::optional getFlakeRefForCompletion() { return {}; } }; -struct SourceExprCommand : virtual Args, MixFlakeOptions -{ +struct SourceExprCommand : virtual Args, MixFlakeOptions { std::optional file; std::optional expr; bool readOnlyMode = false; @@ -94,11 +88,11 @@ struct SourceExprCommand : virtual Args, MixFlakeOptions SourceExprCommand(bool supportReadOnlyMode = false); - std::vector> parseInstallables( - ref store, std::vector ss); + std::vector> + parseInstallables(ref store, std::vector ss); - std::shared_ptr parseInstallable( - ref store, const std::string & installable); + std::shared_ptr + parseInstallable(ref store, const std::string & installable); virtual Strings getDefaultFlakeAttrPaths(); @@ -109,8 +103,7 @@ struct SourceExprCommand : virtual Args, MixFlakeOptions /* A command that operates on a list of "installables", which can be store paths, attribute paths, Nix expressions, etc. */ -struct InstallablesCommand : virtual Args, SourceExprCommand -{ +struct InstallablesCommand : virtual Args, SourceExprCommand { std::vector> installables; InstallablesCommand(); @@ -121,14 +114,12 @@ struct InstallablesCommand : virtual Args, SourceExprCommand std::optional getFlakeRefForCompletion() override; -private: - + private: std::vector _installables; }; /* A command that operates on exactly one "installable" */ -struct InstallableCommand : virtual Args, SourceExprCommand -{ +struct InstallableCommand : virtual Args, SourceExprCommand { std::shared_ptr installable; InstallableCommand(bool supportReadOnlyMode = false); @@ -140,25 +131,20 @@ struct InstallableCommand : virtual Args, SourceExprCommand return parseFlakeRefWithFragment(_installable, absPath(".")).first; } -private: - + private: std::string _installable{"."}; }; /* A command that operates on zero or more store paths. */ -struct BuiltPathsCommand : public InstallablesCommand -{ -private: - +struct BuiltPathsCommand : public InstallablesCommand { + private: bool recursive = false; bool all = false; -protected: - + protected: Realise realiseMode = Realise::Derivation; -public: - + public: BuiltPathsCommand(bool recursive = false); using StoreCommand::run; @@ -170,20 +156,19 @@ public: bool useDefaultInstallables() override { return !all; } }; -struct StorePathsCommand : public BuiltPathsCommand -{ +struct StorePathsCommand : public BuiltPathsCommand { StorePathsCommand(bool recursive = false); using BuiltPathsCommand::run; - virtual void run(ref store, std::vector && storePaths) = 0; + virtual void run(ref store, + std::vector && storePaths) = 0; void run(ref store, BuiltPaths && paths) override; }; /* A command that operates on exactly one store path. */ -struct StorePathCommand : public StorePathsCommand -{ +struct StorePathCommand : public StorePathsCommand { using StorePathsCommand::run; virtual void run(ref store, const StorePath & storePath) = 0; @@ -192,39 +177,40 @@ struct StorePathCommand : public StorePathsCommand }; /* A helper class for registering commands globally. */ -struct RegisterCommand -{ - typedef std::map, std::function()>> Commands; +struct RegisterCommand { + typedef std::map, std::function()>> + Commands; static Commands * commands; RegisterCommand(std::vector && name, - std::function()> command) + std::function()> command) { - if (!commands) commands = new Commands; + if (!commands) + commands = new Commands; commands->emplace(name, command); } - static nix::Commands getCommandsFor(const std::vector & prefix); + static nix::Commands + getCommandsFor(const std::vector & prefix); }; template static RegisterCommand registerCommand(const std::string & name) { - return RegisterCommand({name}, [](){ return make_ref(); }); + return RegisterCommand({name}, []() { return make_ref(); }); } template static RegisterCommand registerCommand2(std::vector && name) { - return RegisterCommand(std::move(name), [](){ return make_ref(); }); + return RegisterCommand(std::move(name), []() { return make_ref(); }); } /* Helper function to generate args that invoke $EDITOR on filename:lineno. */ Strings editorFor(const Path & file, uint32_t line); -struct MixProfile : virtual StoreCommand -{ +struct MixProfile : virtual StoreCommand { std::optional profile; MixProfile(); @@ -237,8 +223,7 @@ struct MixProfile : virtual StoreCommand void updateProfile(const BuiltPaths & buildables); }; -struct MixDefaultProfile : MixProfile -{ +struct MixDefaultProfile : MixProfile { MixDefaultProfile(); }; @@ -246,34 +231,29 @@ struct MixEnvironment : virtual Args { StringSet keep, unset; Strings stringsEnv; - std::vector vectorEnv; + std::vector vectorEnv; bool ignoreEnvironment; MixEnvironment(); - /* Modify global environ based on ignoreEnvironment, keep, and unset. It's expected that exec will be called before this class goes out of scope, otherwise environ will become invalid. */ + /* Modify global environ based on ignoreEnvironment, keep, and unset. It's + * expected that exec will be called before this class goes out of scope, + * otherwise environ will become invalid. */ void setEnviron(); }; void completeFlakeRef(ref store, std::string_view prefix); -void completeFlakeRefWithFragment( - ref evalState, - flake::LockFlags lockFlags, - Strings attrPathPrefixes, - const Strings & defaultFlakeAttrPaths, - std::string_view prefix); +void completeFlakeRefWithFragment(ref evalState, + flake::LockFlags lockFlags, + Strings attrPathPrefixes, + const Strings & defaultFlakeAttrPaths, + std::string_view prefix); std::string showVersions(const std::set & versions); -void printClosureDiff( - ref store, - const StorePath & beforePath, - const StorePath & afterPath, - std::string_view indent); - +void printClosureDiff(ref store, const StorePath & beforePath, + const StorePath & afterPath, std::string_view indent); -void runRepl( - ref evalState, - const ValMap & extraEnv); +void runRepl(ref evalState, const ValMap & extraEnv); } diff --git a/src/libcmd/common-eval-args.cc b/src/libcmd/common-eval-args.cc index 5b6e82388218..39a432e4002d 100644 --- a/src/libcmd/common-eval-args.cc +++ b/src/libcmd/common-eval-args.cc @@ -15,56 +15,58 @@ MixEvalArgs::MixEvalArgs() { auto category = "Common evaluation options"; - addFlag({ - .longName = "arg", - .description = "Pass the value *expr* as the argument *name* to Nix functions.", - .category = category, - .labels = {"name", "expr"}, - .handler = {[&](std::string name, std::string expr) { autoArgs[name] = 'E' + expr; }} - }); + addFlag( + {.longName = "arg", + .description = + "Pass the value *expr* as the argument *name* to Nix functions.", + .category = category, + .labels = {"name", "expr"}, + .handler = {[&](std::string name, std::string expr) { + autoArgs[name] = 'E' + expr; + }}}); addFlag({ .longName = "argstr", - .description = "Pass the string *string* as the argument *name* to Nix functions.", + .description = + "Pass the string *string* as the argument *name* to Nix functions.", .category = category, .labels = {"name", "string"}, - .handler = {[&](std::string name, std::string s) { autoArgs[name] = 'S' + s; }}, + .handler = {[&](std::string name, std::string s) { + autoArgs[name] = 'S' + s; + }}, }); - addFlag({ - .longName = "include", - .shortName = 'I', - .description = "Add *path* to the list of locations used to look up `<...>` file names.", - .category = category, - .labels = {"path"}, - .handler = {[&](std::string s) { searchPath.push_back(s); }} - }); + addFlag({.longName = "include", + .shortName = 'I', + .description = "Add *path* to the list of locations used to look " + "up `<...>` file names.", + .category = category, + .labels = {"path"}, + .handler = {[&](std::string s) { searchPath.push_back(s); }}}); addFlag({ .longName = "impure", .description = "Allow access to mutable paths and repositories.", .category = category, - .handler = {[&]() { - evalSettings.pureEval = false; - }}, + .handler = {[&]() { evalSettings.pureEval = false; }}, }); - addFlag({ - .longName = "override-flake", - .description = "Override the flake registries, redirecting *original-ref* to *resolved-ref*.", - .category = category, - .labels = {"original-ref", "resolved-ref"}, - .handler = {[&](std::string _from, std::string _to) { - auto from = parseFlakeRef(_from, absPath(".")); - auto to = parseFlakeRef(_to, absPath(".")); - fetchers::Attrs extraAttrs; - if (to.subdir != "") extraAttrs["dir"] = to.subdir; - fetchers::overrideRegistry(from.input, to.input, extraAttrs); - }}, - .completer = {[&](size_t, std::string_view prefix) { - completeFlakeRef(openStore(), prefix); - }} - }); + addFlag({.longName = "override-flake", + .description = "Override the flake registries, redirecting " + "*original-ref* to *resolved-ref*.", + .category = category, + .labels = {"original-ref", "resolved-ref"}, + .handler = {[&](std::string _from, std::string _to) { + auto from = parseFlakeRef(_from, absPath(".")); + auto to = parseFlakeRef(_to, absPath(".")); + fetchers::Attrs extraAttrs; + if (to.subdir != "") + extraAttrs["dir"] = to.subdir; + fetchers::overrideRegistry(from.input, to.input, extraAttrs); + }}, + .completer = {[&](size_t, std::string_view prefix) { + completeFlakeRef(openStore(), prefix); + }}}); addFlag({ .longName = "eval-store", @@ -81,7 +83,8 @@ Bindings * MixEvalArgs::getAutoArgs(EvalState & state) for (auto & i : autoArgs) { auto v = state.allocValue(); if (i.second[0] == 'E') - state.mkThunk_(*v, state.parseExprFromString(i.second.substr(1), absPath("."))); + state.mkThunk_(*v, state.parseExprFromString(i.second.substr(1), + absPath("."))); else v->mkString(((std::string_view) i.second).substr(1)); res.insert(state.symbols.create(i.first), v); @@ -93,8 +96,9 @@ Path lookupFileArg(EvalState & state, std::string_view s) { if (isUri(s)) { return state.store->toRealPath( - fetchers::downloadTarball( - state.store, resolveUri(s), "source", false).first.storePath); + fetchers::downloadTarball(state.store, resolveUri(s), "source", + false) + .first.storePath); } else if (s.size() > 2 && s.at(0) == '<' && s.at(s.size() - 1) == '>') { Path p(s.substr(1, s.size() - 2)); return state.findFile(p); diff --git a/src/libcmd/common-eval-args.hh b/src/libcmd/common-eval-args.hh index 03fa226aaece..0ffde347df53 100644 --- a/src/libcmd/common-eval-args.hh +++ b/src/libcmd/common-eval-args.hh @@ -8,8 +8,7 @@ class Store; class EvalState; class Bindings; -struct MixEvalArgs : virtual Args -{ +struct MixEvalArgs : virtual Args { MixEvalArgs(); Bindings * getAutoArgs(EvalState & state); @@ -18,7 +17,7 @@ struct MixEvalArgs : virtual Args std::optional evalStoreUrl; -private: + private: std::map autoArgs; }; diff --git a/src/libcmd/installables.cc b/src/libcmd/installables.cc index ffc25135eb61..7279766b9ee2 100644 --- a/src/libcmd/installables.cc +++ b/src/libcmd/installables.cc @@ -23,10 +23,8 @@ namespace nix { -void completeFlakeInputPath( - ref evalState, - const FlakeRef & flakeRef, - std::string_view prefix) +void completeFlakeInputPath(ref evalState, const FlakeRef & flakeRef, + std::string_view prefix) { auto flake = flake::getFlake(*evalState, flakeRef, true); for (auto & input : flake.inputs) @@ -38,133 +36,126 @@ MixFlakeOptions::MixFlakeOptions() { auto category = "Common flake-related options"; - addFlag({ - .longName = "recreate-lock-file", - .description = "Recreate the flake's lock file from scratch.", - .category = category, - .handler = {&lockFlags.recreateLockFile, true} - }); - - addFlag({ - .longName = "no-update-lock-file", - .description = "Do not allow any updates to the flake's lock file.", - .category = category, - .handler = {&lockFlags.updateLockFile, false} - }); - - addFlag({ - .longName = "no-write-lock-file", - .description = "Do not write the flake's newly generated lock file.", - .category = category, - .handler = {&lockFlags.writeLockFile, false} - }); - - addFlag({ - .longName = "no-registries", - .description = - "Don't allow lookups in the flake registries. This option is deprecated; use `--no-use-registries`.", - .category = category, - .handler = {[&]() { - lockFlags.useRegistries = false; - warn("'--no-registries' is deprecated; use '--no-use-registries'"); - }} - }); - - addFlag({ - .longName = "commit-lock-file", - .description = "Commit changes to the flake's lock file.", - .category = category, - .handler = {&lockFlags.commitLockFile, true} - }); - - addFlag({ - .longName = "update-input", - .description = "Update a specific flake input (ignoring its previous entry in the lock file).", - .category = category, - .labels = {"input-path"}, - .handler = {[&](std::string s) { - lockFlags.inputUpdates.insert(flake::parseInputPath(s)); - }}, - .completer = {[&](size_t, std::string_view prefix) { - if (auto flakeRef = getFlakeRefForCompletion()) - completeFlakeInputPath(getEvalState(), *flakeRef, prefix); - }} - }); - - addFlag({ - .longName = "override-input", - .description = "Override a specific flake input (e.g. `dwarffs/nixpkgs`). This implies `--no-write-lock-file`.", - .category = category, - .labels = {"input-path", "flake-url"}, - .handler = {[&](std::string inputPath, std::string flakeRef) { - lockFlags.writeLockFile = false; - lockFlags.inputOverrides.insert_or_assign( - flake::parseInputPath(inputPath), - parseFlakeRef(flakeRef, absPath("."), true)); - }}, - .completer = {[&](size_t n, std::string_view prefix) { - if (n == 0) { - if (auto flakeRef = getFlakeRefForCompletion()) - completeFlakeInputPath(getEvalState(), *flakeRef, prefix); - } else if (n == 1) { - completeFlakeRef(getEvalState()->store, prefix); - } - }} - }); - - addFlag({ - .longName = "inputs-from", - .description = "Use the inputs of the specified flake as registry entries.", - .category = category, - .labels = {"flake-url"}, - .handler = {[&](std::string flakeRef) { - auto evalState = getEvalState(); - auto flake = flake::lockFlake( - *evalState, - parseFlakeRef(flakeRef, absPath(".")), - { .writeLockFile = false }); - for (auto & [inputName, input] : flake.lockFile.root->inputs) { - auto input2 = flake.lockFile.findInput({inputName}); // resolve 'follows' nodes - if (auto input3 = std::dynamic_pointer_cast(input2)) { - overrideRegistry( - fetchers::Input::fromAttrs({{"type","indirect"}, {"id", inputName}}), - input3->lockedRef.input, - {}); - } - } - }}, - .completer = {[&](size_t, std::string_view prefix) { - completeFlakeRef(getEvalState()->store, prefix); - }} - }); + addFlag({.longName = "recreate-lock-file", + .description = "Recreate the flake's lock file from scratch.", + .category = category, + .handler = {&lockFlags.recreateLockFile, true}}); + + addFlag( + {.longName = "no-update-lock-file", + .description = "Do not allow any updates to the flake's lock file.", + .category = category, + .handler = {&lockFlags.updateLockFile, false}}); + + addFlag( + {.longName = "no-write-lock-file", + .description = "Do not write the flake's newly generated lock file.", + .category = category, + .handler = {&lockFlags.writeLockFile, false}}); + + addFlag( + {.longName = "no-registries", + .description = "Don't allow lookups in the flake registries. This " + "option is deprecated; use `--no-use-registries`.", + .category = category, + .handler = {[&]() { + lockFlags.useRegistries = false; + warn("'--no-registries' is deprecated; use '--no-use-registries'"); + }}}); + + addFlag({.longName = "commit-lock-file", + .description = "Commit changes to the flake's lock file.", + .category = category, + .handler = {&lockFlags.commitLockFile, true}}); + + addFlag({.longName = "update-input", + .description = "Update a specific flake input (ignoring its " + "previous entry in the lock file).", + .category = category, + .labels = {"input-path"}, + .handler = {[&](std::string s) { + lockFlags.inputUpdates.insert(flake::parseInputPath(s)); + }}, + .completer = {[&](size_t, std::string_view prefix) { + if (auto flakeRef = getFlakeRefForCompletion()) + completeFlakeInputPath(getEvalState(), *flakeRef, prefix); + }}}); + + addFlag({.longName = "override-input", + .description = + "Override a specific flake input (e.g. `dwarffs/nixpkgs`). " + "This implies `--no-write-lock-file`.", + .category = category, + .labels = {"input-path", "flake-url"}, + .handler = {[&](std::string inputPath, std::string flakeRef) { + lockFlags.writeLockFile = false; + lockFlags.inputOverrides.insert_or_assign( + flake::parseInputPath(inputPath), + parseFlakeRef(flakeRef, absPath("."), true)); + }}, + .completer = {[&](size_t n, std::string_view prefix) { + if (n == 0) { + if (auto flakeRef = getFlakeRefForCompletion()) + completeFlakeInputPath(getEvalState(), *flakeRef, + prefix); + } else if (n == 1) { + completeFlakeRef(getEvalState()->store, prefix); + } + }}}); + + addFlag({.longName = "inputs-from", + .description = + "Use the inputs of the specified flake as registry entries.", + .category = category, + .labels = {"flake-url"}, + .handler = {[&](std::string flakeRef) { + auto evalState = getEvalState(); + auto flake = flake::lockFlake( + *evalState, parseFlakeRef(flakeRef, absPath(".")), + {.writeLockFile = false}); + for (auto & [inputName, input] : flake.lockFile.root->inputs) { + auto input2 = flake.lockFile.findInput( + {inputName}); // resolve 'follows' nodes + if (auto input3 = + std::dynamic_pointer_cast( + input2)) { + overrideRegistry( + fetchers::Input::fromAttrs( + {{"type", "indirect"}, {"id", inputName}}), + input3->lockedRef.input, {}); + } + } + }}, + .completer = {[&](size_t, std::string_view prefix) { + completeFlakeRef(getEvalState()->store, prefix); + }}}); } SourceExprCommand::SourceExprCommand(bool supportReadOnlyMode) { - addFlag({ - .longName = "file", - .shortName = 'f', - .description = - "Interpret installables as attribute paths relative to the Nix expression stored in *file*. " - "If *file* is the character -, then a Nix expression will be read from standard input. " - "Implies `--impure`.", - .category = installablesCategory, - .labels = {"file"}, - .handler = {&file}, - .completer = completePath - }); - - addFlag({ - .longName = "expr", - .description = "Interpret installables as attribute paths relative to the Nix expression *expr*.", - .category = installablesCategory, - .labels = {"expr"}, - .handler = {&expr} - }); + addFlag({.longName = "file", + .shortName = 'f', + .description = "Interpret installables as attribute paths " + "relative to the Nix expression stored in *file*. " + "If *file* is the character -, then a Nix " + "expression will be read from standard input. " + "Implies `--impure`.", + .category = installablesCategory, + .labels = {"file"}, + .handler = {&file}, + .completer = completePath}); + + addFlag({.longName = "expr", + .description = "Interpret installables as attribute paths " + "relative to the Nix expression *expr*.", + .category = installablesCategory, + .labels = {"expr"}, + .handler = {&expr}}); addFlag({ .longName = "derivation", - .description = "Operate on the store derivation rather than its outputs.", + .description = + "Operate on the store derivation rather than its outputs.", .category = installablesCategory, .handler = {&operateOn, OperateOn::Derivation}, }); @@ -172,10 +163,10 @@ SourceExprCommand::SourceExprCommand(bool supportReadOnlyMode) if (supportReadOnlyMode) { addFlag({ .longName = "read-only", - .description = - "Do not instantiate each evaluated derivation. " - "This improves performance, but can cause errors when accessing " - "store paths of derivations during evaluation.", + .description = "Do not instantiate each evaluated derivation. " + "This improves performance, but can cause errors " + "when accessing " + "store paths of derivations during evaluation.", .handler = {&readOnlyMode, true}, }); } @@ -183,22 +174,18 @@ SourceExprCommand::SourceExprCommand(bool supportReadOnlyMode) Strings SourceExprCommand::getDefaultFlakeAttrPaths() { - return { - "packages." + settings.thisSystem.get() + ".default", - "defaultPackage." + settings.thisSystem.get() - }; + return {"packages." + settings.thisSystem.get() + ".default", + "defaultPackage." + settings.thisSystem.get()}; } Strings SourceExprCommand::getDefaultFlakeAttrPathPrefixes() { - return { - // As a convenience, look for the attribute in - // 'outputs.packages'. - "packages." + settings.thisSystem.get() + ".", - // As a temporary hack until Nixpkgs is properly converted - // to provide a clean 'packages' set, look in 'legacyPackages'. - "legacyPackages." + settings.thisSystem.get() + "." - }; + return {// As a convenience, look for the attribute in + // 'outputs.packages'. + "packages." + settings.thisSystem.get() + ".", + // As a temporary hack until Nixpkgs is properly converted + // to provide a clean 'packages' set, look in 'legacyPackages'. + "legacyPackages." + settings.thisSystem.get() + "."}; } void SourceExprCommand::completeInstallable(std::string_view prefix) @@ -208,9 +195,8 @@ void SourceExprCommand::completeInstallable(std::string_view prefix) evalSettings.pureEval = false; auto state = getEvalState(); - Expr *e = state->parseExprFromFile( - resolveExprPath(state->checkSourcePath(lookupFileArg(*state, *file))) - ); + Expr * e = state->parseExprFromFile(resolveExprPath( + state->checkSourcePath(lookupFileArg(*state, *file)))); Value root; state->eval(e, root); @@ -229,7 +215,7 @@ void SourceExprCommand::completeInstallable(std::string_view prefix) } auto [v, pos] = findAlongAttrPath(*state, prefix_, *autoArgs, root); - Value &v1(*v); + Value & v1(*v); state->forceValue(v1, pos); Value v2; state->autoCallFunction(*autoArgs, v1, v2); @@ -246,21 +232,17 @@ void SourceExprCommand::completeInstallable(std::string_view prefix) } } } else { - completeFlakeRefWithFragment( - getEvalState(), - lockFlags, - getDefaultFlakeAttrPathPrefixes(), - getDefaultFlakeAttrPaths(), - prefix); + completeFlakeRefWithFragment(getEvalState(), lockFlags, + getDefaultFlakeAttrPathPrefixes(), + getDefaultFlakeAttrPaths(), prefix); } } -void completeFlakeRefWithFragment( - ref evalState, - flake::LockFlags lockFlags, - Strings attrPathPrefixes, - const Strings & defaultFlakeAttrPaths, - std::string_view prefix) +void completeFlakeRefWithFragment(ref evalState, + flake::LockFlags lockFlags, + Strings attrPathPrefixes, + const Strings & defaultFlakeAttrPaths, + std::string_view prefix) { /* Look for flake output attributes that match the prefix. */ @@ -275,8 +257,9 @@ void completeFlakeRefWithFragment( auto flakeRefS = std::string(prefix.substr(0, hash)); auto flakeRef = parseFlakeRef(expandTilde(flakeRefS), absPath(".")); - auto evalCache = openEvalCache(*evalState, - std::make_shared(lockFlake(*evalState, flakeRef, lockFlags))); + auto evalCache = openEvalCache( + *evalState, std::make_shared( + lockFlake(*evalState, flakeRef, lockFlags))); auto root = evalCache->getRoot(); @@ -286,7 +269,8 @@ void completeFlakeRefWithFragment( attrPathPrefixes.push_back(""); for (auto & attrPathPrefixS : attrPathPrefixes) { - auto attrPathPrefix = parseAttrPath(*evalState, attrPathPrefixS); + auto attrPathPrefix = + parseAttrPath(*evalState, attrPathPrefixS); auto attrPathS = attrPathPrefixS + std::string(fragment); auto attrPath = parseAttrPath(*evalState, attrPathS); @@ -297,14 +281,20 @@ void completeFlakeRefWithFragment( } auto attr = root->findAlongAttrPath(attrPath); - if (!attr) continue; + if (!attr) + continue; for (auto & attr2 : (*attr)->getAttrs()) { if (hasPrefix(evalState->symbols[attr2], lastAttr)) { auto attrPath2 = (*attr)->getAttrPath(attr2); /* Strip the attrpath prefix. */ - attrPath2.erase(attrPath2.begin(), attrPath2.begin() + attrPathPrefix.size()); - completions->add(flakeRefS + "#" + concatStringsSep(".", evalState->symbols.resolve(attrPath2))); + attrPath2.erase(attrPath2.begin(), + attrPath2.begin() + + attrPathPrefix.size()); + completions->add( + flakeRefS + "#" + + concatStringsSep( + ".", evalState->symbols.resolve(attrPath2))); } } } @@ -313,8 +303,10 @@ void completeFlakeRefWithFragment( attrpaths. */ if (fragment.empty()) { for (auto & attrPath : defaultFlakeAttrPaths) { - auto attr = root->findAlongAttrPath(parseAttrPath(*evalState, attrPath)); - if (!attr) continue; + auto attr = root->findAlongAttrPath( + parseAttrPath(*evalState, attrPath)); + if (!attr) + continue; completions->add(flakeRefS + "#"); } } @@ -354,21 +346,21 @@ DerivedPath Installable::toDerivedPath() { auto buildables = toDerivedPaths(); if (buildables.size() != 1) - throw Error("installable '%s' evaluates to %d derivations, where only one is expected", what(), buildables.size()); + throw Error("installable '%s' evaluates to %d derivations, where only " + "one is expected", + what(), buildables.size()); return std::move(buildables[0]); } std::vector> Installable::getCursors(EvalState & state) { - auto evalCache = - std::make_shared(std::nullopt, state, - [&]() { return toValue(state).first; }); + auto evalCache = std::make_shared( + std::nullopt, state, [&]() { return toValue(state).first; }); return {evalCache->getRoot()}; } -ref -Installable::getCursor(EvalState & state) +ref Installable::getCursor(EvalState & state) { auto cursors = getCursors(state); if (cursors.empty()) @@ -376,10 +368,8 @@ Installable::getCursor(EvalState & state) return cursors[0]; } -static StorePath getDeriver( - ref store, - const Installable & i, - const StorePath & drvPath) +static StorePath getDeriver(ref store, const Installable & i, + const StorePath & drvPath) { auto derivers = store->queryValidDerivers(drvPath); if (derivers.empty()) @@ -388,32 +378,32 @@ static StorePath getDeriver( return *derivers.begin(); } -struct InstallableStorePath : Installable -{ +struct InstallableStorePath : Installable { ref store; StorePath storePath; InstallableStorePath(ref store, StorePath && storePath) - : store(store), storePath(std::move(storePath)) { } + : store(store), storePath(std::move(storePath)) + { + } - std::string what() const override { return store->printStorePath(storePath); } + std::string what() const override + { + return store->printStorePath(storePath); + } DerivedPaths toDerivedPaths() override { if (storePath.isDerivation()) { auto drv = store->readDerivation(storePath); - return { - DerivedPath::Built { - .drvPath = storePath, - .outputs = drv.outputNames(), - } - }; + return {DerivedPath::Built{ + .drvPath = storePath, + .outputs = drv.outputNames(), + }}; } else { - return { - DerivedPath::Opaque { - .path = storePath, - } - }; + return {DerivedPath::Opaque{ + .path = storePath, + }}; } } @@ -426,10 +416,7 @@ struct InstallableStorePath : Installable } } - std::optional getStorePath() override - { - return storePath; - } + std::optional getStorePath() override { return storePath; } }; DerivedPaths InstallableValue::toDerivedPaths() @@ -447,7 +434,7 @@ DerivedPaths InstallableValue::toDerivedPaths() } for (auto & i : drvsToOutputs) - res.push_back(DerivedPath::Built { i.first, i.second }); + res.push_back(DerivedPath::Built{i.first, i.second}); return res; } @@ -460,39 +447,36 @@ StorePathSet InstallableValue::toDrvPaths(ref store) return res; } -struct InstallableAttrPath : InstallableValue -{ +struct InstallableAttrPath : InstallableValue { SourceExprCommand & cmd; RootValue v; std::string attrPath; OutputsSpec outputsSpec; - InstallableAttrPath( - ref state, - SourceExprCommand & cmd, - Value * v, - const std::string & attrPath, - OutputsSpec outputsSpec) - : InstallableValue(state) - , cmd(cmd) - , v(allocRootValue(v)) - , attrPath(attrPath) - , outputsSpec(std::move(outputsSpec)) - { } + InstallableAttrPath(ref state, SourceExprCommand & cmd, + Value * v, const std::string & attrPath, + OutputsSpec outputsSpec) + : InstallableValue(state), cmd(cmd), v(allocRootValue(v)), + attrPath(attrPath), outputsSpec(std::move(outputsSpec)) + { + } std::string what() const override { return attrPath; } std::pair toValue(EvalState & state) override { - auto [vRes, pos] = findAlongAttrPath(state, attrPath, *cmd.getAutoArgs(state), **v); + auto [vRes, pos] = + findAlongAttrPath(state, attrPath, *cmd.getAutoArgs(state), **v); state.forceValue(*vRes, pos); return {vRes, pos}; } - virtual std::vector toDerivations() override; + virtual std::vector + toDerivations() override; }; -std::vector InstallableAttrPath::toDerivations() +std::vector +InstallableAttrPath::toDerivations() { auto v = toValue(*state).first; @@ -512,13 +496,13 @@ std::vector InstallableAttrPath::toDerivations if (auto outputNames = std::get_if(&outputsSpec)) outputsToInstall = *outputNames; else - for (auto & output : drvInfo.queryOutputs(false, std::get_if(&outputsSpec))) + for (auto & output : drvInfo.queryOutputs( + false, std::get_if(&outputsSpec))) outputsToInstall.insert(output.first); - res.push_back(DerivationInfo { - .drvPath = *drvPath, - .outputsToInstall = std::move(outputsToInstall) - }); + res.push_back( + DerivationInfo{.drvPath = *drvPath, + .outputsToInstall = std::move(outputsToInstall)}); } return res; @@ -537,7 +521,9 @@ std::vector InstallableFlake::getActualAttrPaths() return res; } -Value * InstallableFlake::getFlakeOutputs(EvalState & state, const flake::LockedFlake & lockedFlake) +Value * +InstallableFlake::getFlakeOutputs(EvalState & state, + const flake::LockedFlake & lockedFlake) { auto vFlake = state.allocValue(); @@ -546,27 +532,27 @@ Value * InstallableFlake::getFlakeOutputs(EvalState & state, const flake::Locked auto aOutputs = vFlake->attrs->get(state.symbols.create("outputs")); assert(aOutputs); - state.forceValue(*aOutputs->value, [&]() { return aOutputs->value->determinePos(noPos); }); + state.forceValue(*aOutputs->value, + [&]() { return aOutputs->value->determinePos(noPos); }); return aOutputs->value; } -ref openEvalCache( - EvalState & state, - std::shared_ptr lockedFlake) +ref +openEvalCache(EvalState & state, + std::shared_ptr lockedFlake) { auto fingerprint = lockedFlake->getFingerprint(); return make_ref( evalSettings.useEvalCache && evalSettings.pureEval - ? std::optional { std::cref(fingerprint) } + ? std::optional{std::cref(fingerprint)} : std::nullopt, - state, - [&state, lockedFlake]() - { + state, [&state, lockedFlake]() { /* For testing whether the evaluation cache is complete. */ if (getEnv("NIX_ALLOW_EVAL").value_or("1") == "0") - throw Error("not everything is cached, but evaluation is not allowed"); + throw Error( + "not everything is cached, but evaluation is not allowed"); auto vFlake = state.allocValue(); flake::callFlake(state, *lockedFlake, *vFlake); @@ -584,40 +570,40 @@ static std::string showAttrPaths(const std::vector & paths) { std::string s; for (const auto & [n, i] : enumerate(paths)) { - if (n > 0) s += n + 1 == paths.size() ? " or " : ", "; - s += '\''; s += i; s += '\''; + if (n > 0) + s += n + 1 == paths.size() ? " or " : ", "; + s += '\''; + s += i; + s += '\''; } return s; } -InstallableFlake::InstallableFlake( - SourceExprCommand * cmd, - ref state, - FlakeRef && flakeRef, - std::string_view fragment, - OutputsSpec outputsSpec, - Strings attrPaths, - Strings prefixes, - const flake::LockFlags & lockFlags) - : InstallableValue(state), - flakeRef(flakeRef), +InstallableFlake::InstallableFlake(SourceExprCommand * cmd, + ref state, FlakeRef && flakeRef, + std::string_view fragment, + OutputsSpec outputsSpec, Strings attrPaths, + Strings prefixes, + const flake::LockFlags & lockFlags) + : InstallableValue(state), flakeRef(flakeRef), attrPaths(fragment == "" ? attrPaths : Strings{(std::string) fragment}), prefixes(fragment == "" ? Strings{} : prefixes), - outputsSpec(std::move(outputsSpec)), - lockFlags(lockFlags) + outputsSpec(std::move(outputsSpec)), lockFlags(lockFlags) { if (cmd && cmd->getAutoArgs(*state)->size()) throw UsageError("'--arg' and '--argstr' are incompatible with flakes"); } -std::tuple InstallableFlake::toDerivation() +std::tuple +InstallableFlake::toDerivation() { auto attr = getCursor(*state); auto attrPath = attr->getAttrPathStr(); if (!attr->isDerivation()) - throw Error("flake output attribute '%s' is not a derivation", attrPath); + throw Error("flake output attribute '%s' is not a derivation", + attrPath); auto drvPath = attr->forceDerivation(); @@ -627,7 +613,7 @@ std::tuple InstallableF if (auto aOutputSpecified = attr->maybeGetAttr(state->sOutputSpecified)) { if (aOutputSpecified->getBool()) { if (auto aOutputName = attr->maybeGetAttr("outputName")) - outputsToInstall = { aOutputName->getString() }; + outputsToInstall = {aOutputName->getString()}; } } @@ -652,7 +638,7 @@ std::tuple InstallableF if (auto outputNames = std::get_if(&outputsSpec)) outputsToInstall = *outputNames; - auto drvInfo = DerivationInfo { + auto drvInfo = DerivationInfo{ .drvPath = std::move(drvPath), .outputsToInstall = std::move(outputsToInstall), .priority = priority, @@ -676,8 +662,9 @@ std::pair InstallableFlake::toValue(EvalState & state) std::vector> InstallableFlake::getCursors(EvalState & state) { - auto evalCache = openEvalCache(state, - std::make_shared(lockFlake(state, flakeRef, lockFlags))); + auto evalCache = + openEvalCache(state, std::make_shared( + lockFlake(state, flakeRef, lockFlags))); auto root = evalCache->getRoot(); @@ -685,7 +672,8 @@ InstallableFlake::getCursors(EvalState & state) for (auto & attrPath : getActualAttrPaths()) { auto attr = root->findAlongAttrPath(parseAttrPath(state, attrPath)); - if (attr) res.push_back(ref(*attr)); + if (attr) + res.push_back(ref(*attr)); } return res; @@ -705,10 +693,8 @@ ref InstallableFlake::getCursor(EvalState & state) for (auto & attrPath : attrPaths) { debug("trying flake output attribute '%s'", attrPath); - auto attrOrSuggestions = root->findAlongAttrPath( - parseAttrPath(state, attrPath), - true - ); + auto attrOrSuggestions = + root->findAlongAttrPath(parseAttrPath(state, attrPath), true); if (!attrOrSuggestions) { suggestions += attrOrSuggestions.getSuggestions(); @@ -718,11 +704,8 @@ ref InstallableFlake::getCursor(EvalState & state) return *attrOrSuggestions; } - throw Error( - suggestions, - "flake '%s' does not provide attribute %s", - flakeRef, - showAttrPaths(attrPaths)); + throw Error(suggestions, "flake '%s' does not provide attribute %s", + flakeRef, showAttrPaths(attrPaths)); } std::shared_ptr InstallableFlake::getLockedFlake() const @@ -730,7 +713,8 @@ std::shared_ptr InstallableFlake::getLockedFlake() const if (!_lockedFlake) { flake::LockFlags lockFlagsApplyConfig = lockFlags; lockFlagsApplyConfig.applyNixConfig = true; - _lockedFlake = std::make_shared(lockFlake(*state, flakeRef, lockFlagsApplyConfig)); + _lockedFlake = std::make_shared( + lockFlake(*state, flakeRef, lockFlagsApplyConfig)); } return _lockedFlake; } @@ -740,7 +724,9 @@ FlakeRef InstallableFlake::nixpkgsFlakeRef() const auto lockedFlake = getLockedFlake(); if (auto nixpkgsInput = lockedFlake->lockFile.findInput({"nixpkgs"})) { - if (auto lockedNode = std::dynamic_pointer_cast(nixpkgsInput)) { + if (auto lockedNode = + std::dynamic_pointer_cast( + nixpkgsInput)) { debug("using nixpkgs flake '%s'", lockedNode->lockedRef); return std::move(lockedNode->lockedRef); } @@ -749,8 +735,9 @@ FlakeRef InstallableFlake::nixpkgsFlakeRef() const return Installable::nixpkgsFlakeRef(); } -std::vector> SourceExprCommand::parseInstallables( - ref store, std::vector ss) +std::vector> +SourceExprCommand::parseInstallables(ref store, + std::vector ss) { std::vector> result; @@ -763,7 +750,8 @@ std::vector> SourceExprCommand::parseInstallables( throw UsageError("'--file' and '--expr' are exclusive"); // FIXME: backward compatibility hack - if (file) evalSettings.pureEval = false; + if (file) + evalSettings.pureEval = false; auto state = getEvalState(); auto vFile = state->allocValue(); @@ -780,11 +768,8 @@ std::vector> SourceExprCommand::parseInstallables( for (auto & s : ss) { auto [prefix, outputsSpec] = parseOutputsSpec(s); - result.push_back( - std::make_shared( - state, *this, vFile, - prefix == "." ? "" : prefix, - outputsSpec)); + result.push_back(std::make_shared( + state, *this, vFile, prefix == "." ? "" : prefix, outputsSpec)); } } else { @@ -794,7 +779,8 @@ std::vector> SourceExprCommand::parseInstallables( if (s.find('/') != std::string::npos) { try { - result.push_back(std::make_shared(store, store->followLinksToStorePath(s))); + result.push_back(std::make_shared( + store, store->followLinksToStorePath(s))); continue; } catch (BadStorePath &) { } catch (...) { @@ -804,16 +790,12 @@ std::vector> SourceExprCommand::parseInstallables( } try { - auto [flakeRef, fragment, outputsSpec] = parseFlakeRefWithFragmentAndOutputsSpec(s, absPath(".")); + auto [flakeRef, fragment, outputsSpec] = + parseFlakeRefWithFragmentAndOutputsSpec(s, absPath(".")); result.push_back(std::make_shared( - this, - getEvalState(), - std::move(flakeRef), - fragment, - outputsSpec, - getDefaultFlakeAttrPaths(), - getDefaultFlakeAttrPathPrefixes(), - lockFlags)); + this, getEvalState(), std::move(flakeRef), fragment, + outputsSpec, getDefaultFlakeAttrPaths(), + getDefaultFlakeAttrPathPrefixes(), lockFlags)); continue; } catch (...) { ex = std::current_exception(); @@ -826,8 +808,9 @@ std::vector> SourceExprCommand::parseInstallables( return result; } -std::shared_ptr SourceExprCommand::parseInstallable( - ref store, const std::string & installable) +std::shared_ptr +SourceExprCommand::parseInstallable(ref store, + const std::string & installable) { auto installables = parseInstallables(store, {installable}); assert(installables.size() == 1); @@ -835,22 +818,20 @@ std::shared_ptr SourceExprCommand::parseInstallable( } BuiltPaths Installable::build( - ref evalStore, - ref store, - Realise mode, + ref evalStore, ref store, Realise mode, const std::vector> & installables, BuildMode bMode) { BuiltPaths res; - for (auto & [_, builtPath] : build2(evalStore, store, mode, installables, bMode)) + for (auto & [_, builtPath] : + build2(evalStore, store, mode, installables, bMode)) res.push_back(builtPath); return res; } -std::vector, BuiltPath>> Installable::build2( - ref evalStore, - ref store, - Realise mode, +std::vector, BuiltPath>> +Installable::build2( + ref evalStore, ref store, Realise mode, const std::vector> & installables, BuildMode bMode) { @@ -877,43 +858,54 @@ std::vector, BuiltPath>> Installable::bui for (auto & path : pathsToBuild) { for (auto & installable : backmap[path]) { - std::visit(overloaded { - [&](const DerivedPath::Built & bfd) { - OutputPathMap outputs; - auto drv = evalStore->readDerivation(bfd.drvPath); - auto outputHashes = staticOutputHashes(*evalStore, drv); // FIXME: expensive - auto drvOutputs = drv.outputsAndOptPaths(*store); - for (auto & output : bfd.outputs) { - auto outputHash = get(outputHashes, output); - if (!outputHash) - throw Error( - "the derivation '%s' doesn't have an output named '%s'", - store->printStorePath(bfd.drvPath), output); - if (settings.isExperimentalFeatureEnabled(Xp::CaDerivations)) { - DrvOutput outputId { *outputHash, output }; - auto realisation = store->queryRealisation(outputId); - if (!realisation) + std::visit( + overloaded{ + [&](const DerivedPath::Built & bfd) { + OutputPathMap outputs; + auto drv = evalStore->readDerivation(bfd.drvPath); + auto outputHashes = staticOutputHashes( + *evalStore, drv); // FIXME: expensive + auto drvOutputs = drv.outputsAndOptPaths(*store); + for (auto & output : bfd.outputs) { + auto outputHash = get(outputHashes, output); + if (!outputHash) throw Error( - "cannot operate on an output of the " - "unbuilt derivation '%s'", - outputId.to_string()); - outputs.insert_or_assign(output, realisation->outPath); - } else { - // If ca-derivations isn't enabled, assume that - // the output path is statically known. - auto drvOutput = get(drvOutputs, output); - assert(drvOutput); - assert(drvOutput->second); - outputs.insert_or_assign( - output, *drvOutput->second); + "the derivation '%s' doesn't have an " + "output named '%s'", + store->printStorePath(bfd.drvPath), + output); + if (settings.isExperimentalFeatureEnabled( + Xp::CaDerivations)) { + DrvOutput outputId{*outputHash, output}; + auto realisation = + store->queryRealisation(outputId); + if (!realisation) + throw Error("cannot operate on an " + "output of the " + "unbuilt derivation '%s'", + outputId.to_string()); + outputs.insert_or_assign( + output, realisation->outPath); + } else { + // If ca-derivations isn't enabled, assume + // that the output path is statically known. + auto drvOutput = get(drvOutputs, output); + assert(drvOutput); + assert(drvOutput->second); + outputs.insert_or_assign( + output, *drvOutput->second); + } } - } - res.push_back({installable, BuiltPath::Built { bfd.drvPath, outputs }}); + res.push_back( + {installable, + BuiltPath::Built{bfd.drvPath, outputs}}); + }, + [&](const DerivedPath::Opaque & bo) { + res.push_back( + {installable, BuiltPath::Opaque{bo.path}}); + }, }, - [&](const DerivedPath::Opaque & bo) { - res.push_back({installable, BuiltPath::Opaque { bo.path }}); - }, - }, path.raw()); + path.raw()); } } @@ -921,24 +913,30 @@ std::vector, BuiltPath>> Installable::bui case Realise::Outputs: { if (settings.printMissing) - printMissing(store, pathsToBuild, lvlInfo); + printMissing(store, pathsToBuild, lvlInfo); - for (auto & buildResult : store->buildPathsWithResults(pathsToBuild, bMode, evalStore)) { + for (auto & buildResult : + store->buildPathsWithResults(pathsToBuild, bMode, evalStore)) { if (!buildResult.success()) buildResult.rethrow(); for (auto & installable : backmap[buildResult.path]) { - std::visit(overloaded { - [&](const DerivedPath::Built & bfd) { - std::map outputs; - for (auto & path : buildResult.builtOutputs) - outputs.emplace(path.first.outputName, path.second.outPath); - res.push_back({installable, BuiltPath::Built { bfd.drvPath, outputs }}); - }, - [&](const DerivedPath::Opaque & bo) { - res.push_back({installable, BuiltPath::Opaque { bo.path }}); - }, - }, buildResult.path.raw()); + std::visit(overloaded{ + [&](const DerivedPath::Built & bfd) { + std::map outputs; + for (auto & path : buildResult.builtOutputs) + outputs.emplace(path.first.outputName, + path.second.outPath); + res.push_back({installable, + BuiltPath::Built{bfd.drvPath, + outputs}}); + }, + [&](const DerivedPath::Opaque & bo) { + res.push_back({installable, + BuiltPath::Opaque{bo.path}}); + }, + }, + buildResult.path.raw()); } } @@ -953,10 +951,7 @@ std::vector, BuiltPath>> Installable::bui } BuiltPaths Installable::toBuiltPaths( - ref evalStore, - ref store, - Realise mode, - OperateOn operateOn, + ref evalStore, ref store, Realise mode, OperateOn operateOn, const std::vector> & installables) { if (operateOn == OperateOn::Output) @@ -966,36 +961,35 @@ BuiltPaths Installable::toBuiltPaths( settings.readOnlyMode = true; BuiltPaths res; - for (auto & drvPath : Installable::toDerivations(store, installables, true)) + for (auto & drvPath : + Installable::toDerivations(store, installables, true)) res.push_back(BuiltPath::Opaque{drvPath}); return res; } } StorePathSet Installable::toStorePaths( - ref evalStore, - ref store, - Realise mode, OperateOn operateOn, + ref evalStore, ref store, Realise mode, OperateOn operateOn, const std::vector> & installables) { StorePathSet outPaths; - for (auto & path : toBuiltPaths(evalStore, store, mode, operateOn, installables)) { + for (auto & path : + toBuiltPaths(evalStore, store, mode, operateOn, installables)) { auto thisOutPaths = path.outPaths(); outPaths.insert(thisOutPaths.begin(), thisOutPaths.end()); } return outPaths; } -StorePath Installable::toStorePath( - ref evalStore, - ref store, - Realise mode, OperateOn operateOn, - std::shared_ptr installable) +StorePath Installable::toStorePath(ref evalStore, ref store, + Realise mode, OperateOn operateOn, + std::shared_ptr installable) { auto paths = toStorePaths(evalStore, store, mode, operateOn, {installable}); if (paths.size() != 1) - throw Error("argument '%s' should evaluate to one store path", installable->what()); + throw Error("argument '%s' should evaluate to one store path", + installable->what()); return *paths.begin(); } @@ -1009,29 +1003,30 @@ StorePathSet Installable::toDerivations( for (const auto & i : installables) for (const auto & b : i->toDerivedPaths()) - std::visit(overloaded { - [&](const DerivedPath::Opaque & bo) { - if (!useDeriver) - throw Error("argument '%s' did not evaluate to a derivation", i->what()); - drvPaths.insert(getDeriver(store, *i, bo.path)); - }, - [&](const DerivedPath::Built & bfd) { - drvPaths.insert(bfd.drvPath); - }, - }, b.raw()); + std::visit(overloaded{ + [&](const DerivedPath::Opaque & bo) { + if (!useDeriver) + throw Error("argument '%s' did not evaluate " + "to a derivation", + i->what()); + drvPaths.insert(getDeriver(store, *i, bo.path)); + }, + [&](const DerivedPath::Built & bfd) { + drvPaths.insert(bfd.drvPath); + }, + }, + b.raw()); return drvPaths; } InstallablesCommand::InstallablesCommand() { - expectArgs({ - .label = "installables", - .handler = {&_installables}, - .completer = {[&](size_t, std::string_view prefix) { - completeInstallable(prefix); - }} - }); + expectArgs({.label = "installables", + .handler = {&_installables}, + .completer = {[&](size_t, std::string_view prefix) { + completeInstallable(prefix); + }}}); } void InstallablesCommand::prepare() @@ -1056,14 +1051,12 @@ std::optional InstallablesCommand::getFlakeRefForCompletion() InstallableCommand::InstallableCommand(bool supportReadOnlyMode) : SourceExprCommand(supportReadOnlyMode) { - expectArgs({ - .label = "installable", - .optional = true, - .handler = {&_installable}, - .completer = {[&](size_t, std::string_view prefix) { - completeInstallable(prefix); - }} - }); + expectArgs({.label = "installable", + .optional = true, + .handler = {&_installable}, + .completer = {[&](size_t, std::string_view prefix) { + completeInstallable(prefix); + }}}); } void InstallableCommand::prepare() diff --git a/src/libcmd/installables.hh b/src/libcmd/installables.hh index 5d715210e92f..9bee5b853477 100644 --- a/src/libcmd/installables.hh +++ b/src/libcmd/installables.hh @@ -15,17 +15,18 @@ namespace nix { struct DrvInfo; struct SourceExprCommand; -namespace eval_cache { class EvalCache; class AttrCursor; } +namespace eval_cache { +class EvalCache; +class AttrCursor; +} -struct App -{ +struct App { std::vector context; Path program; // FIXME: add args, sandbox settings, metadata, ... }; -struct UnresolvedApp -{ +struct UnresolvedApp { App unresolved; App resolve(ref evalStore, ref store); }; @@ -51,9 +52,8 @@ enum class OperateOn { Derivation }; -struct Installable -{ - virtual ~Installable() { } +struct Installable { + virtual ~Installable() {} virtual std::string what() const = 0; @@ -75,49 +75,36 @@ struct Installable /* Return a value only if this installable is a store path or a symlink to it. */ - virtual std::optional getStorePath() - { - return {}; - } + virtual std::optional getStorePath() { return {}; } virtual std::vector> getCursors(EvalState & state); - virtual ref - getCursor(EvalState & state); + virtual ref getCursor(EvalState & state); virtual FlakeRef nixpkgsFlakeRef() const { - return FlakeRef::fromAttrs({{"type","indirect"}, {"id", "nixpkgs"}}); + return FlakeRef::fromAttrs({{"type", "indirect"}, {"id", "nixpkgs"}}); } - static BuiltPaths build( - ref evalStore, - ref store, - Realise mode, - const std::vector> & installables, - BuildMode bMode = bmNormal); + static BuiltPaths + build(ref evalStore, ref store, Realise mode, + const std::vector> & installables, + BuildMode bMode = bmNormal); - static std::vector, BuiltPath>> build2( - ref evalStore, - ref store, - Realise mode, - const std::vector> & installables, - BuildMode bMode = bmNormal); + static std::vector, BuiltPath>> + build2(ref evalStore, ref store, Realise mode, + const std::vector> & installables, + BuildMode bMode = bmNormal); static std::set toStorePaths( - ref evalStore, - ref store, - Realise mode, + ref evalStore, ref store, Realise mode, OperateOn operateOn, const std::vector> & installables); - static StorePath toStorePath( - ref evalStore, - ref store, - Realise mode, - OperateOn operateOn, - std::shared_ptr installable); + static StorePath toStorePath(ref evalStore, ref store, + Realise mode, OperateOn operateOn, + std::shared_ptr installable); static std::set toDerivations( ref store, @@ -125,21 +112,17 @@ struct Installable bool useDeriver = false); static BuiltPaths toBuiltPaths( - ref evalStore, - ref store, - Realise mode, + ref evalStore, ref store, Realise mode, OperateOn operateOn, const std::vector> & installables); }; -struct InstallableValue : Installable -{ +struct InstallableValue : Installable { ref state; InstallableValue(ref state) : state(state) {} - struct DerivationInfo - { + struct DerivationInfo { StorePath drvPath; std::set outputsToInstall; std::optional priority; @@ -152,8 +135,7 @@ struct InstallableValue : Installable StorePathSet toDrvPaths(ref store) override; }; -struct InstallableFlake : InstallableValue -{ +struct InstallableFlake : InstallableValue { FlakeRef flakeRef; Strings attrPaths; Strings prefixes; @@ -161,21 +143,20 @@ struct InstallableFlake : InstallableValue const flake::LockFlags & lockFlags; mutable std::shared_ptr _lockedFlake; - InstallableFlake( - SourceExprCommand * cmd, - ref state, - FlakeRef && flakeRef, - std::string_view fragment, - OutputsSpec outputsSpec, - Strings attrPaths, - Strings prefixes, - const flake::LockFlags & lockFlags); + InstallableFlake(SourceExprCommand * cmd, ref state, + FlakeRef && flakeRef, std::string_view fragment, + OutputsSpec outputsSpec, Strings attrPaths, + Strings prefixes, const flake::LockFlags & lockFlags); - std::string what() const override { return flakeRef.to_string() + "#" + *attrPaths.begin(); } + std::string what() const override + { + return flakeRef.to_string() + "#" + *attrPaths.begin(); + } std::vector getActualAttrPaths(); - Value * getFlakeOutputs(EvalState & state, const flake::LockedFlake & lockedFlake); + Value * getFlakeOutputs(EvalState & state, + const flake::LockedFlake & lockedFlake); std::tuple toDerivation(); @@ -197,8 +178,8 @@ struct InstallableFlake : InstallableValue FlakeRef nixpkgsFlakeRef() const override; }; -ref openEvalCache( - EvalState & state, - std::shared_ptr lockedFlake); +ref +openEvalCache(EvalState & state, + std::shared_ptr lockedFlake); } diff --git a/src/libcmd/legacy.hh b/src/libcmd/legacy.hh index f503b0da3e1a..e353f3a7f786 100644 --- a/src/libcmd/legacy.hh +++ b/src/libcmd/legacy.hh @@ -6,16 +6,16 @@ namespace nix { -typedef std::function MainFunction; +typedef std::function MainFunction; -struct RegisterLegacyCommand -{ +struct RegisterLegacyCommand { typedef std::map Commands; static Commands * commands; RegisterLegacyCommand(const std::string & name, MainFunction fun) { - if (!commands) commands = new Commands; + if (!commands) + commands = new Commands; (*commands)[name] = fun; } }; diff --git a/src/libcmd/markdown.cc b/src/libcmd/markdown.cc index 71f9c8dff7f2..5c842c33ef30 100644 --- a/src/libcmd/markdown.cc +++ b/src/libcmd/markdown.cc @@ -12,12 +12,11 @@ std::string renderMarkdownToTerminal(std::string_view markdown) int windowWidth = getWindowSize().second; struct lowdown_opts opts { - .type = LOWDOWN_TERM, - .maxdepth = 20, - .cols = (size_t) std::max(windowWidth - 5, 60), - .hmargin = 0, + .type = LOWDOWN_TERM, .maxdepth = 20, + .cols = (size_t) std::max(windowWidth - 5, 60), .hmargin = 0, .vmargin = 0, - .feat = LOWDOWN_COMMONMARK | LOWDOWN_FENCED | LOWDOWN_DEFLIST | LOWDOWN_TABLES, + .feat = LOWDOWN_COMMONMARK | LOWDOWN_FENCED | LOWDOWN_DEFLIST | + LOWDOWN_TABLES, .oflags = 0, }; @@ -27,7 +26,8 @@ std::string renderMarkdownToTerminal(std::string_view markdown) Finally freeDoc([&]() { lowdown_doc_free(doc); }); size_t maxn = 0; - auto node = lowdown_doc_parse(doc, &maxn, markdown.data(), markdown.size(), nullptr); + auto node = lowdown_doc_parse(doc, &maxn, markdown.data(), markdown.size(), + nullptr); if (!node) throw Error("cannot parse Markdown document"); Finally freeNode([&]() { lowdown_node_free(node); }); diff --git a/src/libcmd/repl.cc b/src/libcmd/repl.cc index 3c89a8ea36cc..0870e7a233e0 100644 --- a/src/libcmd/repl.cc +++ b/src/libcmd/repl.cc @@ -10,7 +10,8 @@ #include #else // editline < 1.15.2 don't wrap their API for C++ usage -// (added in https://github.com/troglobit/editline/commit/91398ceb3427b730995357e9d120539fb9bb7461). +// (added in +// https://github.com/troglobit/editline/commit/91398ceb3427b730995357e9d120539fb9bb7461). // This results in linker errors due to to name-mangling of editline C symbols. // For compatibility with these versions, we wrap the API here // (wrapping multiple times on newer versions is no problem). @@ -43,9 +44,9 @@ extern "C" { namespace nix { struct NixRepl - #if HAVE_BOEHMGC +#if HAVE_BOEHMGC : gc - #endif +#endif { std::string curDir; ref state; @@ -82,137 +83,140 @@ struct NixRepl void loadDebugTraceEnv(DebugTrace & dt); typedef std::set ValuesSeen; - std::ostream & printValue(std::ostream & str, Value & v, unsigned int maxDepth); - std::ostream & printValue(std::ostream & str, Value & v, unsigned int maxDepth, ValuesSeen & seen); + std::ostream & printValue(std::ostream & str, Value & v, + unsigned int maxDepth); + std::ostream & printValue(std::ostream & str, Value & v, + unsigned int maxDepth, ValuesSeen & seen); }; - std::string removeWhitespace(std::string s) { s = chomp(s); size_t n = s.find_first_not_of(" \n\r\t"); - if (n != std::string::npos) s = std::string(s, n); + if (n != std::string::npos) + s = std::string(s, n); return s; } - NixRepl::NixRepl(ref state) - : state(state) - , debugTraceIndex(0) - , staticEnv(new StaticEnv(false, state->staticBaseEnv.get())) - , historyFile(getDataDir() + "/nix/repl-history") + : state(state), debugTraceIndex(0), + staticEnv(new StaticEnv(false, state->staticBaseEnv.get())), + historyFile(getDataDir() + "/nix/repl-history") { curDir = absPath("."); } - -NixRepl::~NixRepl() -{ - write_history(historyFile.c_str()); -} +NixRepl::~NixRepl() { write_history(historyFile.c_str()); } std::string runNix(Path program, const Strings & args, - const std::optional & input = {}) + const std::optional & input = {}) { auto subprocessEnv = getEnv(); subprocessEnv["NIX_CONFIG"] = globalConfig.toKeyValue(); - auto res = runProgram(RunOptions { - .program = settings.nixBinDir+ "/" + program, + auto res = runProgram(RunOptions{ + .program = settings.nixBinDir + "/" + program, .args = args, .environment = subprocessEnv, .input = input, }); if (!statusOk(res.first)) - throw ExecError(res.first, "program '%1%' %2%", program, statusToString(res.first)); + throw ExecError(res.first, "program '%1%' %2%", program, + statusToString(res.first)); return res.second; } static NixRepl * curRepl; // ugly -static char * completionCallback(char * s, int *match) { - auto possible = curRepl->completePrefix(s); - if (possible.size() == 1) { - *match = 1; - auto *res = strdup(possible.begin()->c_str() + strlen(s)); - if (!res) throw Error("allocation failure"); - return res; - } else if (possible.size() > 1) { - auto checkAllHaveSameAt = [&](size_t pos) { - auto &first = *possible.begin(); - for (auto &p : possible) { - if (p.size() <= pos || p[pos] != first[pos]) - return false; - } - return true; - }; - size_t start = strlen(s); - size_t len = 0; - while (checkAllHaveSameAt(start + len)) ++len; - if (len > 0) { - *match = 1; - auto *res = strdup(std::string(*possible.begin(), start, len).c_str()); - if (!res) throw Error("allocation failure"); - return res; +static char * completionCallback(char * s, int * match) +{ + auto possible = curRepl->completePrefix(s); + if (possible.size() == 1) { + *match = 1; + auto * res = strdup(possible.begin()->c_str() + strlen(s)); + if (!res) + throw Error("allocation failure"); + return res; + } else if (possible.size() > 1) { + auto checkAllHaveSameAt = [&](size_t pos) { + auto & first = *possible.begin(); + for (auto & p : possible) { + if (p.size() <= pos || p[pos] != first[pos]) + return false; + } + return true; + }; + size_t start = strlen(s); + size_t len = 0; + while (checkAllHaveSameAt(start + len)) + ++len; + if (len > 0) { + *match = 1; + auto * res = + strdup(std::string(*possible.begin(), start, len).c_str()); + if (!res) + throw Error("allocation failure"); + return res; + } } - } - *match = 0; - return nullptr; + *match = 0; + return nullptr; } -static int listPossibleCallback(char *s, char ***avp) { - auto possible = curRepl->completePrefix(s); +static int listPossibleCallback(char * s, char *** avp) +{ + auto possible = curRepl->completePrefix(s); - if (possible.size() > (INT_MAX / sizeof(char*))) - throw Error("too many completions"); + if (possible.size() > (INT_MAX / sizeof(char *))) + throw Error("too many completions"); - int ac = 0; - char **vp = nullptr; + int ac = 0; + char ** vp = nullptr; - auto check = [&](auto *p) { - if (!p) { - if (vp) { - while (--ac >= 0) - free(vp[ac]); - free(vp); - } - throw Error("allocation failure"); - } - return p; - }; + auto check = [&](auto * p) { + if (!p) { + if (vp) { + while (--ac >= 0) + free(vp[ac]); + free(vp); + } + throw Error("allocation failure"); + } + return p; + }; - vp = check((char **)malloc(possible.size() * sizeof(char*))); + vp = check((char **) malloc(possible.size() * sizeof(char *))); - for (auto & p : possible) - vp[ac++] = check(strdup(p.c_str())); + for (auto & p : possible) + vp[ac++] = check(strdup(p.c_str())); - *avp = vp; + *avp = vp; - return ac; + return ac; } namespace { - // Used to communicate to NixRepl::getLine whether a signal occurred in ::readline. - volatile sig_atomic_t g_signal_received = 0; +// Used to communicate to NixRepl::getLine whether a signal occurred in +// ::readline. +volatile sig_atomic_t g_signal_received = 0; - void sigintHandler(int signo) { - g_signal_received = signo; - } +void sigintHandler(int signo) { g_signal_received = signo; } } -static std::ostream & showDebugTrace(std::ostream & out, const PosTable & positions, const DebugTrace & dt) +static std::ostream & showDebugTrace(std::ostream & out, + const PosTable & positions, + const DebugTrace & dt) { if (dt.isError) out << ANSI_RED "error: " << ANSI_NORMAL; out << dt.hint.str() << "\n"; // prefer direct pos, but if noPos then try the expr. - auto pos = *dt.pos - ? *dt.pos - : positions[dt.expr.getPos() ? dt.expr.getPos() : noPos]; + auto pos = *dt.pos ? *dt.pos + : positions[dt.expr.getPos() ? dt.expr.getPos() : noPos]; if (pos) { printAtPos(pos, out); @@ -239,7 +243,8 @@ void NixRepl::mainLoop(const std::vector & files) } loadFiles(); - if (!loadedFiles.empty()) notice(""); + if (!loadedFiles.empty()) + notice(""); // Allow nix-repl specific settings in .inputrc rl_readline_name = "nix-repl"; @@ -257,8 +262,8 @@ void NixRepl::mainLoop(const std::vector & files) std::string input; while (true) { - // When continuing input from previous lines, don't print a prompt, just align to the same - // number of chars as the prompt. + // When continuing input from previous lines, don't print a prompt, just + // align to the same number of chars as the prompt. if (!getLine(input, input.empty() ? "nix-repl> " : " ")) { // ctrl-D should exit the debugger. state->debugStop = false; @@ -266,21 +271,24 @@ void NixRepl::mainLoop(const std::vector & files) break; } try { - if (!removeWhitespace(input).empty() && !processLine(input)) return; + if (!removeWhitespace(input).empty() && !processLine(input)) + return; } catch (ParseError & e) { if (e.msg().find("unexpected end of file") != std::string::npos) { - // For parse errors on incomplete input, we continue waiting for the next line of - // input without clearing the input so far. + // For parse errors on incomplete input, we continue waiting for + // the next line of input without clearing the input so far. continue; } else { - printMsg(lvlError, e.msg()); + printMsg(lvlError, e.msg()); } } catch (EvalError & e) { - // in debugger mode, an EvalError should trigger another repl session. - // when that session returns the exception will land here. No need to show it again; - // show the error for this repl session instead. + // in debugger mode, an EvalError should trigger another repl + // session. when that session returns the exception will land here. + // No need to show it again; show the error for this repl session + // instead. if (state->debugRepl && !state->debugTraces.empty()) - showDebugTrace(std::cout, state->positions, state->debugTraces.front()); + showDebugTrace(std::cout, state->positions, + state->debugTraces.front()); else printMsg(lvlError, e.msg()); } catch (Error & e) { @@ -296,7 +304,6 @@ void NixRepl::mainLoop(const std::vector & files) } } - bool NixRepl::getLine(std::string & input, const std::string & prompt) { struct sigaction act, old; @@ -335,13 +342,12 @@ bool NixRepl::getLine(std::string & input, const std::string & prompt) } if (!s) - return false; + return false; input += s; input += '\n'; return true; } - StringSet NixRepl::completePrefix(const std::string & prefix) { StringSet completions; @@ -372,7 +378,8 @@ StringSet NixRepl::completePrefix(const std::string & prefix) /* This is a variable name; look it up in the current scope. */ StringSet::iterator i = varNames.lower_bound(cur); while (i != varNames.end()) { - if (i->substr(0, cur.size()) != cur) break; + if (i->substr(0, cur.size()) != cur) + break; completions.insert(prev + *i); i++; } @@ -391,7 +398,8 @@ StringSet NixRepl::completePrefix(const std::string & prefix) for (auto & i : *v.attrs) { std::string_view name = state->symbols[i.name]; - if (name.substr(0, cur2.size()) != cur2) continue; + if (name.substr(0, cur2.size()) != cur2) + continue; completions.insert(concatStrings(prev, expr, ".", name)); } @@ -409,31 +417,33 @@ StringSet NixRepl::completePrefix(const std::string & prefix) return completions; } - static bool isVarName(std::string_view s) { - if (s.size() == 0) return false; + if (s.size() == 0) + return false; char c = s[0]; - if ((c >= '0' && c <= '9') || c == '-' || c == '\'') return false; + if ((c >= '0' && c <= '9') || c == '-' || c == '\'') + return false; for (auto & i : s) - if (!((i >= 'a' && i <= 'z') || - (i >= 'A' && i <= 'Z') || - (i >= '0' && i <= '9') || - i == '_' || i == '-' || i == '\'')) + if (!((i >= 'a' && i <= 'z') || (i >= 'A' && i <= 'Z') || + (i >= '0' && i <= '9') || i == '_' || i == '-' || i == '\'')) return false; return true; } - -StorePath NixRepl::getDerivationPath(Value & v) { +StorePath NixRepl::getDerivationPath(Value & v) +{ auto drvInfo = getDerivation(*state, v, false); if (!drvInfo) - throw Error("expression does not evaluate to a derivation, so I can't build it"); + throw Error("expression does not evaluate to a derivation, so I can't " + "build it"); auto drvPath = drvInfo->queryDrvPath(); if (!drvPath) - throw Error("expression did not evaluate to a valid derivation (no 'drvPath' attribute)"); + throw Error("expression did not evaluate to a valid derivation (no " + "'drvPath' attribute)"); if (!state->store->isValidPath(*drvPath)) - throw Error("expression evaluated to invalid derivation '%s'", state->store->printStorePath(*drvPath)); + throw Error("expression evaluated to invalid derivation '%s'", + state->store->printStorePath(*drvPath)); return *drvPath; } @@ -454,7 +464,8 @@ void NixRepl::loadDebugTraceEnv(DebugTrace & dt) bool NixRepl::processLine(std::string line) { line = trim(line); - if (line == "") return true; + if (line == "") + return true; _isInterrupted = false; @@ -463,7 +474,8 @@ bool NixRepl::processLine(std::string line) if (line[0] == ':') { size_t p = line.find_first_of(" \n\r\t"); command = line.substr(0, p); - if (p != std::string::npos) arg = removeWhitespace(line.substr(p)); + if (p != std::string::npos) + arg = removeWhitespace(line.substr(p)); } else { arg = line; } @@ -471,43 +483,47 @@ bool NixRepl::processLine(std::string line) if (command == ":?" || command == ":help") { // FIXME: convert to Markdown, include in the 'nix repl' manpage. std::cout - << "The following commands are available:\n" - << "\n" - << " Evaluate and print expression\n" - << " = Bind expression to variable\n" - << " :a Add attributes from resulting set to scope\n" - << " :b Build a derivation\n" - << " :bl Build a derivation, creating GC roots in the working directory\n" - << " :e Open package or function in $EDITOR\n" - << " :i Build derivation, then install result into current profile\n" - << " :l Load Nix expression and add it to scope\n" - << " :lf Load Nix flake and add it to scope\n" - << " :p Evaluate and print expression recursively\n" - << " :q Exit nix-repl\n" - << " :r Reload all files\n" - << " :sh Build dependencies of derivation, then start nix-shell\n" - << " :t Describe result of evaluation\n" - << " :u Build derivation, then start nix-shell\n" - << " :doc Show documentation of a builtin function\n" - << " :log Show logs for a derivation\n" - << " :te [bool] Enable, disable or toggle showing traces for errors\n" - ; + << "The following commands are available:\n" + << "\n" + << " Evaluate and print expression\n" + << " = Bind expression to variable\n" + << " :a Add attributes from resulting set to scope\n" + << " :b Build a derivation\n" + << " :bl Build a derivation, creating GC roots in the " + "working directory\n" + << " :e Open package or function in $EDITOR\n" + << " :i Build derivation, then install result into " + "current profile\n" + << " :l Load Nix expression and add it to scope\n" + << " :lf Load Nix flake and add it to scope\n" + << " :p Evaluate and print expression recursively\n" + << " :q Exit nix-repl\n" + << " :r Reload all files\n" + << " :sh Build dependencies of derivation, then start " + "nix-shell\n" + << " :t Describe result of evaluation\n" + << " :u Build derivation, then start nix-shell\n" + << " :doc Show documentation of a builtin function\n" + << " :log Show logs for a derivation\n" + << " :te [bool] Enable, disable or toggle showing traces for " + "errors\n"; if (state->debugRepl) { - std::cout - << "\n" - << " Debug mode commands\n" - << " :env Show env stack\n" - << " :bt Show trace stack\n" - << " :st Show current trace\n" - << " :st Change to another trace in the stack\n" - << " :c Go until end of program, exception, or builtins.break\n" - << " :s Go one step\n" - ; + std::cout + << "\n" + << " Debug mode commands\n" + << " :env Show env stack\n" + << " :bt Show trace stack\n" + << " :st Show current trace\n" + << " :st Change to another trace in the stack\n" + << " :c Go until end of program, exception, or " + "builtins.break\n" + << " :s Go one step\n"; } } - else if (state->debugRepl && (command == ":bt" || command == ":backtrace")) { + else if (state->debugRepl && + (command == ":bt" || command == ":backtrace")) { for (const auto & [idx, i] : enumerate(state->debugTraces)) { std::cout << "\n" << ANSI_BLUE << idx << ANSI_NORMAL << ": "; showDebugTrace(std::cout, state->positions, i); @@ -527,17 +543,18 @@ bool NixRepl::processLine(std::string line) try { // change the DebugTrace index. debugTraceIndex = stoi(arg); - } catch (...) { } + } catch (...) { + } for (const auto & [idx, i] : enumerate(state->debugTraces)) { - if (idx == debugTraceIndex) { - std::cout << "\n" << ANSI_BLUE << idx << ANSI_NORMAL << ": "; - showDebugTrace(std::cout, state->positions, i); - std::cout << std::endl; - printEnvBindings(*state, i.expr, i.env); - loadDebugTraceEnv(i); - break; - } + if (idx == debugTraceIndex) { + std::cout << "\n" << ANSI_BLUE << idx << ANSI_NORMAL << ": "; + showDebugTrace(std::cout, state->positions, i); + std::cout << std::endl; + printEnvBindings(*state, i.expr, i.env); + loadDebugTraceEnv(i); + break; + } } } @@ -577,10 +594,11 @@ bool NixRepl::processLine(std::string line) Value v; evalString(arg, v); - const auto [file, line] = [&] () -> std::pair { + const auto [file, line] = [&]() -> std::pair { if (v.type() == nPath || v.type() == nString) { PathSet context; - auto filename = state->coerceToString(noPos, v, context).toOwned(); + auto filename = + state->coerceToString(noPos, v, context).toOwned(); state->symbols.create(filename); return {filename, 0}; } else if (v.isLambda()) { @@ -599,7 +617,8 @@ bool NixRepl::processLine(std::string line) // runProgram redirects stdout to a StringSink, // using runProgram2 to allow editors to display their UI - runProgram2(RunOptions { .program = editor, .searchPath = true, .args = args }); + runProgram2( + RunOptions{.program = editor, .searchPath = true, .args = args}); // Reload right after exiting the editor state->resetFileCache(); @@ -615,14 +634,17 @@ bool NixRepl::processLine(std::string line) else if (command == ":u") { Value v, f, result; evalString(arg, v); - evalString("drv: (import {}).runCommand \"shell\" { buildInputs = [ drv ]; } \"\"", f); + evalString("drv: (import {}).runCommand \"shell\" { " + "buildInputs = [ drv ]; } \"\"", + f); state->callFunction(f, v, result, PosIdx()); StorePath drvPath = getDerivationPath(result); runNix("nix-shell", {state->store->printStorePath(drvPath)}); } - else if (command == ":b" || command == ":bl" || command == ":i" || command == ":sh" || command == ":log") { + else if (command == ":b" || command == ":bl" || command == ":i" || + command == ":sh" || command == ":log") { Value v; evalString(arg, v); StorePath drvPath = getDerivationPath(v); @@ -632,23 +654,25 @@ bool NixRepl::processLine(std::string line) state->store->buildPaths({DerivedPath::Built{drvPath}}); auto drv = state->store->readDerivation(drvPath); logger->cout("\nThis derivation produced the following outputs:"); - for (auto & [outputName, outputPath] : state->store->queryDerivationOutputMap(drvPath)) { - auto localStore = state->store.dynamic_pointer_cast(); + for (auto & [outputName, outputPath] : + state->store->queryDerivationOutputMap(drvPath)) { + auto localStore = + state->store.dynamic_pointer_cast(); if (localStore && command == ":bl") { std::string symlink = "repl-result-" + outputName; localStore->addPermRoot(outputPath, absPath(symlink)); - logger->cout(" ./%s -> %s", symlink, state->store->printStorePath(outputPath)); + logger->cout(" ./%s -> %s", symlink, + state->store->printStorePath(outputPath)); } else { - logger->cout(" %s -> %s", outputName, state->store->printStorePath(outputPath)); + logger->cout(" %s -> %s", outputName, + state->store->printStorePath(outputPath)); } } } else if (command == ":i") { runNix("nix-env", {"-i", drvPathRaw}); } else if (command == ":log") { settings.readOnlyMode = true; - Finally roModeReset([&]() { - settings.readOnlyMode = false; - }); + Finally roModeReset([&]() { settings.readOnlyMode = false; }); auto subs = getDefaultSubstituters(); subs.push_front(state->store); @@ -658,20 +682,24 @@ bool NixRepl::processLine(std::string line) for (auto & sub : subs) { auto * logSubP = dynamic_cast(&*sub); if (!logSubP) { - printInfo("Skipped '%s' which does not support retrieving build logs", sub->getUri()); + printInfo("Skipped '%s' which does not support retrieving " + "build logs", + sub->getUri()); continue; } auto & logSub = *logSubP; auto log = logSub.getBuildLog(drvPath); if (log) { - printInfo("got build log for '%s' from '%s'", drvPathRaw, logSub.getUri()); + printInfo("got build log for '%s' from '%s'", drvPathRaw, + logSub.getUri()); logger->writeToStdout(*log); foundLog = true; break; } } - if (!foundLog) throw Error("build log of '%s' is not available", drvPathRaw); + if (!foundLog) + throw Error("build log of '%s' is not available", drvPathRaw); } else { runNix("nix-shell", {drvPathRaw}); } @@ -700,9 +728,9 @@ bool NixRepl::processLine(std::string line) for (auto & arg : args) arg = "*" + arg + "*"; - markdown += - "**Synopsis:** `builtins." + (std::string) (*doc->name) + "` " - + concatStringsSep(" ", args) + "\n\n"; + markdown += "**Synopsis:** `builtins." + + (std::string)(*doc->name) + "` " + + concatStringsSep(" ", args) + "\n\n"; } markdown += stripIndentation(doc->doc); @@ -730,11 +758,8 @@ bool NixRepl::processLine(std::string line) else { size_t p = line.find('='); std::string name; - if (p != std::string::npos && - p < line.size() && - line[p + 1] != '=' && - isVarName(name = removeWhitespace(line.substr(0, p)))) - { + if (p != std::string::npos && p < line.size() && line[p + 1] != '=' && + isVarName(name = removeWhitespace(line.substr(0, p)))) { Expr * e = parseString(line.substr(p + 1)); Value & v(*state->allocValue()); v.mkThunk(env, e); @@ -749,7 +774,6 @@ bool NixRepl::processLine(std::string line) return true; } - void NixRepl::loadFile(const Path & path) { loadedFiles.remove(path); @@ -763,26 +787,29 @@ void NixRepl::loadFile(const Path & path) void NixRepl::loadFlake(const std::string & flakeRefS) { if (flakeRefS.empty()) - throw Error("cannot use ':load-flake' without a path specified. (Use '.' for the current working directory.)"); + throw Error("cannot use ':load-flake' without a path specified. (Use " + "'.' for the current working directory.)"); auto flakeRef = parseFlakeRef(flakeRefS, absPath("."), true); if (evalSettings.pureEval && !flakeRef.input.isLocked()) - throw Error("cannot use ':load-flake' on locked flake reference '%s' (use --impure to override)", flakeRefS); + throw Error("cannot use ':load-flake' on locked flake reference '%s' " + "(use --impure to override)", + flakeRefS); Value v; - flake::callFlake(*state, + flake::callFlake( + *state, flake::lockFlake(*state, flakeRef, - flake::LockFlags { - .updateLockFile = false, - .useRegistries = !evalSettings.pureEval, - .allowMutable = !evalSettings.pureEval, - }), + flake::LockFlags{ + .updateLockFile = false, + .useRegistries = !evalSettings.pureEval, + .allowMutable = !evalSettings.pureEval, + }), v); addAttrsToScope(v); } - void NixRepl::initEnv() { env = &state->allocEnv(envSize); @@ -795,7 +822,6 @@ void NixRepl::initEnv() varNames.emplace(state->symbols[i.first]); } - void NixRepl::reloadFiles() { initEnv(); @@ -803,7 +829,6 @@ void NixRepl::reloadFiles() loadFiles(); } - void NixRepl::loadFiles() { Strings old = loadedFiles; @@ -811,14 +836,14 @@ void NixRepl::loadFiles() bool first = true; for (auto & i : old) { - if (!first) notice(""); + if (!first) + notice(""); first = false; notice("Loading '%1%'...", i); loadFile(i); } } - void NixRepl::addAttrsToScope(Value & attrs) { state->forceAttrs(attrs, [&]() { return attrs.determinePos(noPos); }); @@ -835,7 +860,6 @@ void NixRepl::addAttrsToScope(Value & attrs) notice("Added %1% variables.", attrs.attrs->size()); } - void NixRepl::addVarToScope(const Symbol name, Value & v) { if (displ >= envSize) @@ -848,14 +872,12 @@ void NixRepl::addVarToScope(const Symbol name, Value & v) varNames.emplace(state->symbols[name]); } - Expr * NixRepl::parseString(std::string s) { Expr * e = state->parseExprFromString(std::move(s), curDir, staticEnv); return e; } - void NixRepl::evalString(std::string s, Value & v) { Expr * e = parseString(s); @@ -863,29 +885,34 @@ void NixRepl::evalString(std::string s, Value & v) state->forceValue(v, [&]() { return v.determinePos(noPos); }); } - -std::ostream & NixRepl::printValue(std::ostream & str, Value & v, unsigned int maxDepth) +std::ostream & NixRepl::printValue(std::ostream & str, Value & v, + unsigned int maxDepth) { ValuesSeen seen; return printValue(str, v, maxDepth, seen); } - -std::ostream & printStringValue(std::ostream & str, const char * string) { +std::ostream & printStringValue(std::ostream & str, const char * string) +{ str << "\""; for (const char * i = string; *i; i++) - if (*i == '\"' || *i == '\\') str << "\\" << *i; - else if (*i == '\n') str << "\\n"; - else if (*i == '\r') str << "\\r"; - else if (*i == '\t') str << "\\t"; - else str << *i; + if (*i == '\"' || *i == '\\') + str << "\\" << *i; + else if (*i == '\n') + str << "\\n"; + else if (*i == '\r') + str << "\\r"; + else if (*i == '\t') + str << "\\t"; + else + str << *i; str << "\""; return str; } - // FIXME: lot of cut&paste from Nix's eval.cc. -std::ostream & NixRepl::printValue(std::ostream & str, Value & v, unsigned int maxDepth, ValuesSeen & seen) +std::ostream & NixRepl::printValue(std::ostream & str, Value & v, + unsigned int maxDepth, ValuesSeen & seen) { str.flush(); checkInterrupt(); @@ -926,7 +953,8 @@ std::ostream & NixRepl::printValue(std::ostream & str, Value & v, unsigned int m Bindings::iterator i = v.attrs->find(state->sDrvPath); PathSet context; if (i != v.attrs->end()) - str << state->store->printStorePath(state->coerceToStorePath(i->pos, *i->value, context)); + str << state->store->printStorePath( + state->coerceToStorePath(i->pos, *i->value, context)); else str << "???"; str << "»"; @@ -952,7 +980,8 @@ std::ostream & NixRepl::printValue(std::ostream & str, Value & v, unsigned int m try { printValue(str, *i.second, maxDepth - 1, seen); } catch (AssertionError & e) { - str << ANSI_RED "«error: " << e.msg() << "»" ANSI_NORMAL; + str << ANSI_RED "«error: " << e.msg() + << "»" ANSI_NORMAL; } str << "; "; } @@ -976,7 +1005,8 @@ std::ostream & NixRepl::printValue(std::ostream & str, Value & v, unsigned int m try { printValue(str, *elem, maxDepth - 1, seen); } catch (AssertionError & e) { - str << ANSI_RED "«error: " << e.msg() << "»" ANSI_NORMAL; + str << ANSI_RED "«error: " << e.msg() + << "»" ANSI_NORMAL; } str << " "; } @@ -989,7 +1019,8 @@ std::ostream & NixRepl::printValue(std::ostream & str, Value & v, unsigned int m if (v.isLambda()) { std::ostringstream s; s << state->positions[v.lambda.fun->pos]; - str << ANSI_BLUE "«lambda @ " << filterANSIEscapes(s.str()) << "»" ANSI_NORMAL; + str << ANSI_BLUE "«lambda @ " << filterANSIEscapes(s.str()) + << "»" ANSI_NORMAL; } else if (v.isPrimOp()) { str << ANSI_MAGENTA "«primop»" ANSI_NORMAL; } else if (v.isPrimOpApp()) { @@ -1011,9 +1042,7 @@ std::ostream & NixRepl::printValue(std::ostream & str, Value & v, unsigned int m return str; } -void runRepl( - refevalState, - const ValMap & extraEnv) +void runRepl(ref evalState, const ValMap & extraEnv) { auto repl = std::make_unique(evalState); @@ -1026,34 +1055,28 @@ void runRepl( repl->mainLoop({}); } -struct CmdRepl : StoreCommand, MixEvalArgs -{ +struct CmdRepl : StoreCommand, MixEvalArgs { std::vector files; CmdRepl() { - expectArgs({ - .label = "files", - .handler = {&files}, - .completer = completePath - }); + expectArgs( + {.label = "files", .handler = {&files}, .completer = completePath}); } - bool forceImpureByDefault() override - { - return true; - } + bool forceImpureByDefault() override { return true; } std::string description() override { - return "start an interactive environment for evaluating Nix expressions"; + return "start an interactive environment for evaluating Nix " + "expressions"; } std::string doc() override { return - #include "repl.md" - ; +#include "repl.md" + ; } void run(ref store) override diff --git a/src/libexpr/attr-path.cc b/src/libexpr/attr-path.cc index 94ab60f9ac82..762958b5acd0 100644 --- a/src/libexpr/attr-path.cc +++ b/src/libexpr/attr-path.cc @@ -2,10 +2,8 @@ #include "eval-inline.hh" #include "util.hh" - namespace nix { - static Strings parseAttrPath(std::string_view s) { Strings res; @@ -19,19 +17,21 @@ static Strings parseAttrPath(std::string_view s) ++i; while (1) { if (i == s.end()) - throw ParseError("missing closing quote in selection path '%1%'", s); - if (*i == '"') break; + throw ParseError( + "missing closing quote in selection path '%1%'", s); + if (*i == '"') + break; cur.push_back(*i++); } } else cur.push_back(*i); ++i; } - if (!cur.empty()) res.push_back(cur); + if (!cur.empty()) + res.push_back(cur); return res; } - std::vector parseAttrPath(EvalState & state, std::string_view s) { std::vector res; @@ -40,9 +40,9 @@ std::vector parseAttrPath(EvalState & state, std::string_view s) return res; } - -std::pair findAlongAttrPath(EvalState & state, const std::string & attrPath, - Bindings & autoArgs, Value & vIn) +std::pair findAlongAttrPath(EvalState & state, + const std::string & attrPath, + Bindings & autoArgs, Value & vIn) { Strings tokens = parseAttrPath(attrPath); @@ -66,12 +66,12 @@ std::pair findAlongAttrPath(EvalState & state, const std::strin if (!attrIndex) { if (v->type() != nAttrs) - throw TypeError( - "the expression selected by the selection path '%1%' should be a set but is %2%", - attrPath, - showType(*v)); + throw TypeError("the expression selected by the selection path " + "'%1%' should be a set but is %2%", + attrPath, showType(*v)); if (attr.empty()) - throw Error("empty attribute name in selection path '%1%'", attrPath); + throw Error("empty attribute name in selection path '%1%'", + attrPath); Bindings::iterator a = v->attrs->find(state.symbols.create(attr)); if (a == v->attrs->end()) { @@ -80,7 +80,10 @@ std::pair findAlongAttrPath(EvalState & state, const std::strin attrNames.insert(state.symbols[attr.name]); auto suggestions = Suggestions::bestMatches(attrNames, attr); - throw AttrPathNotFound(suggestions, "attribute '%1%' in selection path '%2%' not found", attr, attrPath); + throw AttrPathNotFound( + suggestions, + "attribute '%1%' in selection path '%2%' not found", attr, + attrPath); } v = &*a->value; pos = a->pos; @@ -89,31 +92,32 @@ std::pair findAlongAttrPath(EvalState & state, const std::strin else { if (!v->isList()) - throw TypeError( - "the expression selected by the selection path '%1%' should be a list but is %2%", - attrPath, - showType(*v)); + throw TypeError("the expression selected by the selection path " + "'%1%' should be a list but is %2%", + attrPath, showType(*v)); if (*attrIndex >= v->listSize()) - throw AttrPathNotFound("list index %1% in selection path '%2%' is out of range", *attrIndex, attrPath); + throw AttrPathNotFound( + "list index %1% in selection path '%2%' is out of range", + *attrIndex, attrPath); v = v->listElems()[*attrIndex]; pos = noPos; } - } return {v, pos}; } - -std::pair findPackageFilename(EvalState & state, Value & v, std::string what) +std::pair +findPackageFilename(EvalState & state, Value & v, std::string what) { Value * v2; try { auto dummyArgs = state.allocBindings(0); v2 = findAlongAttrPath(state, "meta.position", *dummyArgs, v).first; } catch (Error &) { - throw NoPositionInfo("package '%s' has no source location information", what); + throw NoPositionInfo("package '%s' has no source location information", + what); } // FIXME: is it possible to extract the Pos object instead of doing this @@ -132,8 +136,7 @@ std::pair findPackageFilename(EvalState & state, Value & throw ParseError("cannot parse line number '%s'", pos); } - return { std::move(filename), lineno }; + return {std::move(filename), lineno}; } - } diff --git a/src/libexpr/attr-path.hh b/src/libexpr/attr-path.hh index 117e0051bb8e..4e40cbac871e 100644 --- a/src/libexpr/attr-path.hh +++ b/src/libexpr/attr-path.hh @@ -10,14 +10,13 @@ namespace nix { MakeError(AttrPathNotFound, Error); MakeError(NoPositionInfo, Error); -std::pair findAlongAttrPath( - EvalState & state, - const std::string & attrPath, - Bindings & autoArgs, - Value & vIn); +std::pair findAlongAttrPath(EvalState & state, + const std::string & attrPath, + Bindings & autoArgs, Value & vIn); /* Heuristic to find the filename and lineno or a nix value. */ -std::pair findPackageFilename(EvalState & state, Value & v, std::string what); +std::pair +findPackageFilename(EvalState & state, Value & v, std::string what); std::vector parseAttrPath(EvalState & state, std::string_view s); diff --git a/src/libexpr/attr-set.cc b/src/libexpr/attr-set.cc index 877116f1f9c5..fc333a842649 100644 --- a/src/libexpr/attr-set.cc +++ b/src/libexpr/attr-set.cc @@ -3,11 +3,8 @@ #include - namespace nix { - - /* Allocate a new array of attributes for an attribute set with a specific capacity. The space is implicitly reserved after the Bindings structure. */ @@ -19,10 +16,10 @@ Bindings * EvalState::allocBindings(size_t capacity) throw Error("attribute set of size %d is too big", capacity); nrAttrsets++; nrAttrsInAttrsets += capacity; - return new (allocBytes(sizeof(Bindings) + sizeof(Attr) * capacity)) Bindings((Bindings::size_t) capacity); + return new (allocBytes(sizeof(Bindings) + sizeof(Attr) * capacity)) + Bindings((Bindings::size_t) capacity); } - /* Create a new attribute named 'name' on an existing attribute set stored in 'vAttrs' and return the newly allocated Value which is associated with this attribute. */ @@ -33,13 +30,11 @@ Value * EvalState::allocAttr(Value & vAttrs, Symbol name) return v; } - Value * EvalState::allocAttr(Value & vAttrs, std::string_view name) { return allocAttr(vAttrs, symbols.create(name)); } - Value & BindingsBuilder::alloc(Symbol name, PosIdx pos) { auto value = state.allocValue(); @@ -47,24 +42,21 @@ Value & BindingsBuilder::alloc(Symbol name, PosIdx pos) return *value; } - Value & BindingsBuilder::alloc(std::string_view name, PosIdx pos) { return alloc(state.symbols.create(name), pos); } - void Bindings::sort() { - if (size_) std::sort(begin(), end()); + if (size_) + std::sort(begin(), end()); } - Value & Value::mkAttrs(BindingsBuilder & bindings) { mkAttrs(bindings.finish()); return *this; } - } diff --git a/src/libexpr/attr-set.hh b/src/libexpr/attr-set.hh index dcc73b506917..8db817aecbe4 100644 --- a/src/libexpr/attr-set.hh +++ b/src/libexpr/attr-set.hh @@ -8,13 +8,11 @@ namespace nix { - class EvalState; struct Value; /* Map one attribute name to its value. */ -struct Attr -{ +struct Attr { /* the placement of `name` and `pos` in this struct is important. both of them are uint32 wrappers, they are next to each other to make sure that Attr has no padding on 64 bit machines. that @@ -23,15 +21,13 @@ struct Attr PosIdx pos; Value * value; Attr(Symbol name, Value * value, PosIdx pos = noPos) - : name(name), pos(pos), value(value) { }; - Attr() { }; - bool operator < (const Attr & a) const - { - return name < a.name; - } + : name(name), pos(pos), value(value){}; + Attr(){}; + bool operator<(const Attr & a) const { return name < a.name; } }; -static_assert(sizeof(Attr) == 2 * sizeof(uint32_t) + sizeof(Value *), +static_assert( + sizeof(Attr) == 2 * sizeof(uint32_t) + sizeof(Value *), "performance of the evaluator is highly sensitive to the size of Attr. " "avoid introducing any padding into Attr if at all possible, and do not " "introduce new fields that need not be present for almost every instance."); @@ -40,20 +36,19 @@ static_assert(sizeof(Attr) == 2 * sizeof(uint32_t) + sizeof(Value *), by its size and its capacity, the capacity being the number of Attr elements allocated after this structure, while the size corresponds to the number of elements already inserted in this structure. */ -class Bindings -{ -public: +class Bindings { + public: typedef uint32_t size_t; PosIdx pos; -private: + private: size_t size_, capacity_; Attr attrs[0]; - Bindings(size_t capacity) : size_(0), capacity_(capacity) { } + Bindings(size_t capacity) : size_(0), capacity_(capacity) {} Bindings(const Bindings & bindings) = delete; -public: + public: size_t size() const { return size_; } bool empty() const { return !size_; } @@ -70,7 +65,8 @@ public: { Attr key(name, 0); iterator i = std::lower_bound(begin(), end(), key); - if (i != end() && i->name == name) return i; + if (i != end() && i->name == name) + return i; return end(); } @@ -78,24 +74,23 @@ public: { Attr key(name, 0); iterator i = std::lower_bound(begin(), end(), key); - if (i != end() && i->name == name) return &*i; + if (i != end() && i->name == name) + return &*i; return nullptr; } iterator begin() { return &attrs[0]; } iterator end() { return &attrs[size_]; } - Attr & operator[](size_t pos) - { - return attrs[pos]; - } + Attr & operator[](size_t pos) { return attrs[pos]; } void sort(); size_t capacity() { return capacity_; } /* Returns the attributes in lexicographically sorted order. */ - std::vector lexicographicOrder(const SymbolTable & symbols) const + std::vector + lexicographicOrder(const SymbolTable & symbols) const { std::vector res; res.reserve(size_); @@ -114,11 +109,10 @@ public: /* A wrapper around Bindings that ensures that its always in sorted order at the end. The only way to consume a BindingsBuilder is to call finish(), which sorts the bindings. */ -class BindingsBuilder -{ +class BindingsBuilder { Bindings * bindings; -public: + public: // needed by std::back_inserter using value_type = Attr; @@ -126,22 +120,17 @@ public: BindingsBuilder(EvalState & state, Bindings * bindings) : bindings(bindings), state(state) - { } + { + } void insert(Symbol name, Value * value, PosIdx pos = noPos) { insert(Attr(name, value, pos)); } - void insert(const Attr & attr) - { - push_back(attr); - } + void insert(const Attr & attr) { push_back(attr); } - void push_back(const Attr & attr) - { - bindings->push_back(attr); - } + void push_back(const Attr & attr) { bindings->push_back(attr); } Value & alloc(Symbol name, PosIdx pos = noPos); @@ -153,10 +142,7 @@ public: return bindings; } - Bindings * alreadySorted() - { - return bindings; - } + Bindings * alreadySorted() { return bindings; } }; } diff --git a/src/libexpr/eval-cache.cc b/src/libexpr/eval-cache.cc index dbfd8e70be59..841b8154549e 100644 --- a/src/libexpr/eval-cache.cc +++ b/src/libexpr/eval-cache.cc @@ -17,14 +17,12 @@ create table if not exists Attributes ( ); )sql"; -struct AttrDb -{ +struct AttrDb { std::atomic_bool failed{false}; const Store & cfg; - struct State - { + struct State { SQLite db; SQLiteStmt insertAttribute; SQLiteStmt insertAttributeWithContext; @@ -37,36 +35,35 @@ struct AttrDb SymbolTable & symbols; - AttrDb( - const Store & cfg, - const Hash & fingerprint, - SymbolTable & symbols) - : cfg(cfg) - , _state(std::make_unique>()) - , symbols(symbols) + AttrDb(const Store & cfg, const Hash & fingerprint, SymbolTable & symbols) + : cfg(cfg), _state(std::make_unique>()), symbols(symbols) { auto state(_state->lock()); Path cacheDir = getCacheDir() + "/nix/eval-cache-v4"; createDirs(cacheDir); - Path dbPath = cacheDir + "/" + fingerprint.to_string(Base16, false) + ".sqlite"; + Path dbPath = + cacheDir + "/" + fingerprint.to_string(Base16, false) + ".sqlite"; state->db = SQLite(dbPath); state->db.isCache(); state->db.exec(schema); - state->insertAttribute.create(state->db, - "insert or replace into Attributes(parent, name, type, value) values (?, ?, ?, ?)"); + state->insertAttribute.create( + state->db, "insert or replace into Attributes(parent, name, type, " + "value) values (?, ?, ?, ?)"); - state->insertAttributeWithContext.create(state->db, - "insert or replace into Attributes(parent, name, type, value, context) values (?, ?, ?, ?, ?)"); + state->insertAttributeWithContext.create( + state->db, "insert or replace into Attributes(parent, name, type, " + "value, context) values (?, ?, ?, ?, ?)"); - state->queryAttribute.create(state->db, - "select rowid, type, value, context from Attributes where parent = ? and name = ?"); + state->queryAttribute.create( + state->db, "select rowid, type, value, context from Attributes " + "where parent = ? and name = ?"); - state->queryAttributes.create(state->db, - "select name from Attributes where parent = ?"); + state->queryAttributes.create( + state->db, "select name from Attributes where parent = ?"); state->txn = std::make_unique(state->db); } @@ -83,10 +80,10 @@ struct AttrDb } } - template - AttrId doSQLite(F && fun) + template AttrId doSQLite(F && fun) { - if (failed) return 0; + if (failed) + return 0; try { return fun(); } catch (SQLiteError &) { @@ -96,116 +93,92 @@ struct AttrDb } } - AttrId setAttrs( - AttrKey key, - const std::vector & attrs) + AttrId setAttrs(AttrKey key, const std::vector & attrs) { - return doSQLite([&]() - { + return doSQLite([&]() { auto state(_state->lock()); - state->insertAttribute.use() - (key.first) - (symbols[key.second]) - (AttrType::FullAttrs) - (0, false).exec(); + state->insertAttribute + .use()(key.first)(symbols[key.second])(AttrType::FullAttrs)( + 0, false) + .exec(); AttrId rowId = state->db.getLastInsertedRowId(); assert(rowId); for (auto & attr : attrs) - state->insertAttribute.use() - (rowId) - (symbols[attr]) - (AttrType::Placeholder) - (0, false).exec(); + state->insertAttribute + .use()(rowId)(symbols[attr])(AttrType::Placeholder)(0, + false) + .exec(); return rowId; }); } - AttrId setString( - AttrKey key, - std::string_view s, - const char * * context = nullptr) + AttrId setString(AttrKey key, std::string_view s, + const char ** context = nullptr) { - return doSQLite([&]() - { + return doSQLite([&]() { auto state(_state->lock()); if (context) { std::string ctx; - for (const char * * p = context; *p; ++p) { - if (p != context) ctx.push_back(' '); + for (const char ** p = context; *p; ++p) { + if (p != context) + ctx.push_back(' '); ctx.append(*p); } - state->insertAttributeWithContext.use() - (key.first) - (symbols[key.second]) - (AttrType::String) - (s) - (ctx).exec(); + state->insertAttributeWithContext + .use()(key.first)(symbols[key.second])(AttrType::String)( + s) (ctx) + .exec(); } else { - state->insertAttribute.use() - (key.first) - (symbols[key.second]) - (AttrType::String) - (s).exec(); + state->insertAttribute + .use()(key.first)(symbols[key.second])(AttrType::String)(s) + .exec(); } return state->db.getLastInsertedRowId(); }); } - AttrId setBool( - AttrKey key, - bool b) + AttrId setBool(AttrKey key, bool b) { - return doSQLite([&]() - { + return doSQLite([&]() { auto state(_state->lock()); - state->insertAttribute.use() - (key.first) - (symbols[key.second]) - (AttrType::Bool) - (b ? 1 : 0).exec(); + state->insertAttribute + .use()(key.first)(symbols[key.second])(AttrType::Bool)(b ? 1 + : 0) + .exec(); return state->db.getLastInsertedRowId(); }); } - AttrId setInt( - AttrKey key, - int n) + AttrId setInt(AttrKey key, int n) { - return doSQLite([&]() - { + return doSQLite([&]() { auto state(_state->lock()); - state->insertAttribute.use() - (key.first) - (symbols[key.second]) - (AttrType::Int) - (n).exec(); + state->insertAttribute + .use()(key.first)(symbols[key.second])(AttrType::Int)(n) + .exec(); return state->db.getLastInsertedRowId(); }); } - AttrId setListOfStrings( - AttrKey key, - const std::vector & l) + AttrId setListOfStrings(AttrKey key, const std::vector & l) { - return doSQLite([&]() - { + return doSQLite([&]() { auto state(_state->lock()); - state->insertAttribute.use() - (key.first) - (symbols[key.second]) - (AttrType::ListOfStrings) - (concatStringsSep("\t", l)).exec(); + state->insertAttribute + .use()(key.first)(symbols[key.second])(AttrType::ListOfStrings)( + concatStringsSep("\t", l)) + .exec(); return state->db.getLastInsertedRowId(); }); @@ -213,15 +186,13 @@ struct AttrDb AttrId setPlaceholder(AttrKey key) { - return doSQLite([&]() - { + return doSQLite([&]() { auto state(_state->lock()); - state->insertAttribute.use() - (key.first) - (symbols[key.second]) - (AttrType::Placeholder) - (0, false).exec(); + state->insertAttribute + .use()(key.first)(symbols[key.second])(AttrType::Placeholder)( + 0, false) + .exec(); return state->db.getLastInsertedRowId(); }); @@ -229,15 +200,13 @@ struct AttrDb AttrId setMissing(AttrKey key) { - return doSQLite([&]() - { + return doSQLite([&]() { auto state(_state->lock()); - state->insertAttribute.use() - (key.first) - (symbols[key.second]) - (AttrType::Missing) - (0, false).exec(); + state->insertAttribute + .use()(key.first)(symbols[key.second])(AttrType::Missing)(0, + false) + .exec(); return state->db.getLastInsertedRowId(); }); @@ -245,15 +214,12 @@ struct AttrDb AttrId setMisc(AttrKey key) { - return doSQLite([&]() - { + return doSQLite([&]() { auto state(_state->lock()); - state->insertAttribute.use() - (key.first) - (symbols[key.second]) - (AttrType::Misc) - (0, false).exec(); + state->insertAttribute + .use()(key.first)(symbols[key.second])(AttrType::Misc)(0, false) + .exec(); return state->db.getLastInsertedRowId(); }); @@ -261,15 +227,13 @@ struct AttrDb AttrId setFailed(AttrKey key) { - return doSQLite([&]() - { + return doSQLite([&]() { auto state(_state->lock()); - state->insertAttribute.use() - (key.first) - (symbols[key.second]) - (AttrType::Failed) - (0, false).exec(); + state->insertAttribute + .use()(key.first)(symbols[key.second])(AttrType::Failed)(0, + false) + .exec(); return state->db.getLastInsertedRowId(); }); @@ -279,52 +243,54 @@ struct AttrDb { auto state(_state->lock()); - auto queryAttribute(state->queryAttribute.use()(key.first)(symbols[key.second])); - if (!queryAttribute.next()) return {}; + auto queryAttribute( + state->queryAttribute.use()(key.first)(symbols[key.second])); + if (!queryAttribute.next()) + return {}; auto rowId = (AttrId) queryAttribute.getInt(0); auto type = (AttrType) queryAttribute.getInt(1); switch (type) { - case AttrType::Placeholder: - return {{rowId, placeholder_t()}}; - case AttrType::FullAttrs: { - // FIXME: expensive, should separate this out. - std::vector attrs; - auto queryAttributes(state->queryAttributes.use()(rowId)); - while (queryAttributes.next()) - attrs.emplace_back(symbols.create(queryAttributes.getStr(0))); - return {{rowId, attrs}}; - } - case AttrType::String: { - NixStringContext context; - if (!queryAttribute.isNull(3)) - for (auto & s : tokenizeString>(queryAttribute.getStr(3), ";")) - context.push_back(decodeContext(cfg, s)); - return {{rowId, string_t{queryAttribute.getStr(2), context}}}; - } - case AttrType::Bool: - return {{rowId, queryAttribute.getInt(2) != 0}}; - case AttrType::Int: - return {{rowId, int_t{queryAttribute.getInt(2)}}}; - case AttrType::ListOfStrings: - return {{rowId, tokenizeString>(queryAttribute.getStr(2), "\t")}}; - case AttrType::Missing: - return {{rowId, missing_t()}}; - case AttrType::Misc: - return {{rowId, misc_t()}}; - case AttrType::Failed: - return {{rowId, failed_t()}}; - default: - throw Error("unexpected type in evaluation cache"); + case AttrType::Placeholder: + return {{rowId, placeholder_t()}}; + case AttrType::FullAttrs: { + // FIXME: expensive, should separate this out. + std::vector attrs; + auto queryAttributes(state->queryAttributes.use()(rowId)); + while (queryAttributes.next()) + attrs.emplace_back(symbols.create(queryAttributes.getStr(0))); + return {{rowId, attrs}}; + } + case AttrType::String: { + NixStringContext context; + if (!queryAttribute.isNull(3)) + for (auto & s : tokenizeString>( + queryAttribute.getStr(3), ";")) + context.push_back(decodeContext(cfg, s)); + return {{rowId, string_t{queryAttribute.getStr(2), context}}}; + } + case AttrType::Bool: + return {{rowId, queryAttribute.getInt(2) != 0}}; + case AttrType::Int: + return {{rowId, int_t{queryAttribute.getInt(2)}}}; + case AttrType::ListOfStrings: + return {{rowId, tokenizeString>( + queryAttribute.getStr(2), "\t")}}; + case AttrType::Missing: + return {{rowId, missing_t()}}; + case AttrType::Misc: + return {{rowId, misc_t()}}; + case AttrType::Failed: + return {{rowId, failed_t()}}; + default: + throw Error("unexpected type in evaluation cache"); } } }; -static std::shared_ptr makeAttrDb( - const Store & cfg, - const Hash & fingerprint, - SymbolTable & symbols) +static std::shared_ptr +makeAttrDb(const Store & cfg, const Hash & fingerprint, SymbolTable & symbols) { try { return std::make_shared(cfg, fingerprint, symbols); @@ -334,13 +300,11 @@ static std::shared_ptr makeAttrDb( } } -EvalCache::EvalCache( - std::optional> useCache, - EvalState & state, - RootLoader rootLoader) - : db(useCache ? makeAttrDb(*state.store, *useCache, state.symbols) : nullptr) - , state(state) - , rootLoader(rootLoader) +EvalCache::EvalCache(std::optional> useCache, + EvalState & state, RootLoader rootLoader) + : db(useCache ? makeAttrDb(*state.store, *useCache, state.symbols) + : nullptr), + state(state), rootLoader(rootLoader) { } @@ -359,9 +323,7 @@ ref EvalCache::getRoot() } AttrCursor::AttrCursor( - ref root, - Parent parent, - Value * value, + ref root, Parent parent, Value * value, std::optional> && cachedValue) : root(root), parent(parent), cachedValue(std::move(cachedValue)) { @@ -388,7 +350,8 @@ Value & AttrCursor::getValue() root->state.forceAttrs(vParent, noPos); auto attr = vParent.attrs->get(parent->second); if (!attr) - throw Error("attribute '%s' is unexpectedly missing", getAttrPathStr()); + throw Error("attribute '%s' is unexpectedly missing", + getAttrPathStr()); _value = allocRootValue(attr->value); } else _value = allocRootValue(root->getRootValue()); @@ -420,7 +383,8 @@ std::string AttrCursor::getAttrPathStr() const std::string AttrCursor::getAttrPathStr(Symbol name) const { - return concatStringsSep(".", root->state.symbols.resolve(getAttrPath(name))); + return concatStringsSep(".", + root->state.symbols.resolve(getAttrPath(name))); } Value & AttrCursor::forceValue() @@ -438,16 +402,20 @@ Value & AttrCursor::forceValue() throw; } - if (root->db && (!cachedValue || std::get_if(&cachedValue->second))) { + if (root->db && + (!cachedValue || std::get_if(&cachedValue->second))) { if (v.type() == nString) - cachedValue = {root->db->setString(getKey(), v.string.s, v.string.context), - string_t{v.string.s, {}}}; + cachedValue = { + root->db->setString(getKey(), v.string.s, v.string.context), + string_t{v.string.s, {}}}; else if (v.type() == nPath) - cachedValue = {root->db->setString(getKey(), v.path), string_t{v.path, {}}}; + cachedValue = {root->db->setString(getKey(), v.path), + string_t{v.path, {}}}; else if (v.type() == nBool) cachedValue = {root->db->setBool(getKey(), v.boolean), v.boolean}; else if (v.type() == nInt) - cachedValue = {root->db->setInt(getKey(), v.integer), int_t{v.integer}}; + cachedValue = {root->db->setInt(getKey(), v.integer), + int_t{v.integer}}; else if (v.type() == nAttrs) ; // FIXME: do something? else @@ -467,17 +435,20 @@ Suggestions AttrCursor::getSuggestionsForAttr(Symbol name) return Suggestions::bestMatches(strAttrNames, root->state.symbols[name]); } -std::shared_ptr AttrCursor::maybeGetAttr(Symbol name, bool forceErrors) +std::shared_ptr AttrCursor::maybeGetAttr(Symbol name, + bool forceErrors) { if (root->db) { if (!cachedValue) cachedValue = root->db->getAttr(getKey()); if (cachedValue) { - if (auto attrs = std::get_if>(&cachedValue->second)) { + if (auto attrs = + std::get_if>(&cachedValue->second)) { for (auto & attr : *attrs) if (attr == name) - return std::make_shared(root, std::make_pair(shared_from_this(), attr)); + return std::make_shared( + root, std::make_pair(shared_from_this(), attr)); return nullptr; } else if (std::get_if(&cachedValue->second)) { auto attr = root->db->getAttr({cachedValue->first, name}); @@ -488,16 +459,20 @@ std::shared_ptr AttrCursor::maybeGetAttr(Symbol name, bool forceErro if (forceErrors) debug("reevaluating failed cached attribute '%s'"); else - throw CachedEvalError("cached failure of attribute '%s'", getAttrPathStr(name)); + throw CachedEvalError( + "cached failure of attribute '%s'", + getAttrPathStr(name)); } else - return std::make_shared(root, - std::make_pair(shared_from_this(), name), nullptr, std::move(attr)); + return std::make_shared( + root, std::make_pair(shared_from_this(), name), + nullptr, std::move(attr)); } // Incomplete attrset, so need to fall thru and // evaluate to see whether 'name' exists } else return nullptr; - //throw TypeError("'%s' is not an attribute set", getAttrPathStr()); + // throw TypeError("'%s' is not an attribute set", + // getAttrPathStr()); } } @@ -505,7 +480,7 @@ std::shared_ptr AttrCursor::maybeGetAttr(Symbol name, bool forceErro if (v.type() != nAttrs) return nullptr; - //throw TypeError("'%s' is not an attribute set", getAttrPathStr()); + // throw TypeError("'%s' is not an attribute set", getAttrPathStr()); for (auto & attr : *v.attrs) { if (root->db) @@ -517,7 +492,8 @@ std::shared_ptr AttrCursor::maybeGetAttr(Symbol name, bool forceErro if (!attr) { if (root->db) { if (!cachedValue) - cachedValue = {root->db->setPlaceholder(getKey()), placeholder_t()}; + cachedValue = {root->db->setPlaceholder(getKey()), + placeholder_t()}; root->db->setMissing({cachedValue->first, name}); } return nullptr; @@ -527,11 +503,12 @@ std::shared_ptr AttrCursor::maybeGetAttr(Symbol name, bool forceErro if (root->db) { if (!cachedValue) cachedValue = {root->db->setPlaceholder(getKey()), placeholder_t()}; - cachedValue2 = {root->db->setPlaceholder({cachedValue->first, name}), placeholder_t()}; + cachedValue2 = {root->db->setPlaceholder({cachedValue->first, name}), + placeholder_t()}; } - return make_ref( - root, std::make_pair(shared_from_this(), name), attr->value, std::move(cachedValue2)); + return make_ref(root, std::make_pair(shared_from_this(), name), + attr->value, std::move(cachedValue2)); } std::shared_ptr AttrCursor::maybeGetAttr(std::string_view name) @@ -552,7 +529,8 @@ ref AttrCursor::getAttr(std::string_view name) return getAttr(root->state.symbols.create(name)); } -OrSuggestions> AttrCursor::findAlongAttrPath(const std::vector & attrPath, bool force) +OrSuggestions> +AttrCursor::findAlongAttrPath(const std::vector & attrPath, bool force) { auto res = shared_from_this(); for (auto & attr : attrPath) { @@ -576,14 +554,17 @@ std::string AttrCursor::getString() debug("using cached string attribute '%s'", getAttrPathStr()); return s->first; } else - root->state.debugThrowLastTrace(TypeError("'%s' is not a string", getAttrPathStr())); + root->state.debugThrowLastTrace( + TypeError("'%s' is not a string", getAttrPathStr())); } } auto & v = forceValue(); if (v.type() != nString && v.type() != nPath) - root->state.debugThrowLastTrace(TypeError("'%s' is not a string but %s", getAttrPathStr(), showType(v.type()))); + root->state.debugThrowLastTrace(TypeError("'%s' is not a string but %s", + getAttrPathStr(), + showType(v.type()))); return v.type() == nString ? v.string.s : v.path; } @@ -603,11 +584,13 @@ string_t AttrCursor::getStringWithContext() } } if (valid) { - debug("using cached string attribute '%s'", getAttrPathStr()); + debug("using cached string attribute '%s'", + getAttrPathStr()); return *s; } } else - root->state.debugThrowLastTrace(TypeError("'%s' is not a string", getAttrPathStr())); + root->state.debugThrowLastTrace( + TypeError("'%s' is not a string", getAttrPathStr())); } } @@ -618,7 +601,9 @@ string_t AttrCursor::getStringWithContext() else if (v.type() == nPath) return {v.path, {}}; else - root->state.debugThrowLastTrace(TypeError("'%s' is not a string but %s", getAttrPathStr(), showType(v.type()))); + root->state.debugThrowLastTrace(TypeError("'%s' is not a string but %s", + getAttrPathStr(), + showType(v.type()))); } bool AttrCursor::getBool() @@ -631,14 +616,16 @@ bool AttrCursor::getBool() debug("using cached Boolean attribute '%s'", getAttrPathStr()); return *b; } else - root->state.debugThrowLastTrace(TypeError("'%s' is not a Boolean", getAttrPathStr())); + root->state.debugThrowLastTrace( + TypeError("'%s' is not a Boolean", getAttrPathStr())); } } auto & v = forceValue(); if (v.type() != nBool) - root->state.debugThrowLastTrace(TypeError("'%s' is not a Boolean", getAttrPathStr())); + root->state.debugThrowLastTrace( + TypeError("'%s' is not a Boolean", getAttrPathStr())); return v.boolean; } @@ -671,11 +658,14 @@ std::vector AttrCursor::getListOfStrings() if (!cachedValue) cachedValue = root->db->getAttr(getKey()); if (cachedValue && !std::get_if(&cachedValue->second)) { - if (auto l = std::get_if>(&cachedValue->second)) { - debug("using cached list of strings attribute '%s'", getAttrPathStr()); + if (auto l = std::get_if>( + &cachedValue->second)) { + debug("using cached list of strings attribute '%s'", + getAttrPathStr()); return *l; } else - throw TypeError("'%s' is not a list of strings", getAttrPathStr()); + throw TypeError("'%s' is not a list of strings", + getAttrPathStr()); } } @@ -704,24 +694,28 @@ std::vector AttrCursor::getAttrs() if (!cachedValue) cachedValue = root->db->getAttr(getKey()); if (cachedValue && !std::get_if(&cachedValue->second)) { - if (auto attrs = std::get_if>(&cachedValue->second)) { + if (auto attrs = + std::get_if>(&cachedValue->second)) { debug("using cached attrset attribute '%s'", getAttrPathStr()); return *attrs; } else - root->state.debugThrowLastTrace(TypeError("'%s' is not an attribute set", getAttrPathStr())); + root->state.debugThrowLastTrace(TypeError( + "'%s' is not an attribute set", getAttrPathStr())); } } auto & v = forceValue(); if (v.type() != nAttrs) - root->state.debugThrowLastTrace(TypeError("'%s' is not an attribute set", getAttrPathStr())); + root->state.debugThrowLastTrace( + TypeError("'%s' is not an attribute set", getAttrPathStr())); std::vector attrs; for (auto & attr : *getValue().attrs) attrs.push_back(attr.name); std::sort(attrs.begin(), attrs.end(), [&](Symbol a, Symbol b) { - std::string_view sa = root->state.symbols[a], sb = root->state.symbols[b]; + std::string_view sa = root->state.symbols[a], + sb = root->state.symbols[b]; return sa < sb; }); @@ -747,7 +741,7 @@ StorePath AttrCursor::forceDerivation() aDrvPath->forceValue(); if (!root->state.store->isValidPath(drvPath)) throw Error("don't know how to recreate store derivation '%s'!", - root->state.store->printStorePath(drvPath)); + root->state.store->printStorePath(drvPath)); } return drvPath; } diff --git a/src/libexpr/eval-cache.hh b/src/libexpr/eval-cache.hh index c93e55b93d01..ee433722a1b1 100644 --- a/src/libexpr/eval-cache.hh +++ b/src/libexpr/eval-cache.hh @@ -14,8 +14,7 @@ MakeError(CachedEvalError, EvalError); struct AttrDb; class AttrCursor; -class EvalCache : public std::enable_shared_from_this -{ +class EvalCache : public std::enable_shared_from_this { friend class AttrCursor; std::shared_ptr db; @@ -26,12 +25,9 @@ class EvalCache : public std::enable_shared_from_this Value * getRootValue(); -public: - - EvalCache( - std::optional> useCache, - EvalState & state, - RootLoader rootLoader); + public: + EvalCache(std::optional> useCache, + EvalState & state, RootLoader rootLoader); ref getRoot(); }; @@ -48,33 +44,31 @@ enum AttrType { Int = 8, }; -struct placeholder_t {}; -struct missing_t {}; -struct misc_t {}; -struct failed_t {}; -struct int_t { NixInt x; }; +struct placeholder_t { +}; +struct missing_t { +}; +struct misc_t { +}; +struct failed_t { +}; +struct int_t { + NixInt x; +}; typedef uint64_t AttrId; typedef std::pair AttrKey; typedef std::pair string_t; -typedef std::variant< - std::vector, - string_t, - placeholder_t, - missing_t, - misc_t, - failed_t, - bool, - int_t, - std::vector - > AttrValue; - -class AttrCursor : public std::enable_shared_from_this -{ +typedef std::variant, string_t, placeholder_t, missing_t, + misc_t, failed_t, bool, int_t, std::vector> + AttrValue; + +class AttrCursor : public std::enable_shared_from_this { friend class EvalCache; ref root; - typedef std::optional, Symbol>> Parent; + typedef std::optional, Symbol>> + Parent; Parent parent; RootValue _value; std::optional> cachedValue; @@ -83,13 +77,9 @@ class AttrCursor : public std::enable_shared_from_this Value & getValue(); -public: - - AttrCursor( - ref root, - Parent parent, - Value * value = nullptr, - std::optional> && cachedValue = {}); + public: + AttrCursor(ref root, Parent parent, Value * value = nullptr, + std::optional> && cachedValue = {}); std::vector getAttrPath() const; @@ -101,7 +91,8 @@ public: Suggestions getSuggestionsForAttr(Symbol name); - std::shared_ptr maybeGetAttr(Symbol name, bool forceErrors = false); + std::shared_ptr maybeGetAttr(Symbol name, + bool forceErrors = false); std::shared_ptr maybeGetAttr(std::string_view name); @@ -111,7 +102,8 @@ public: /* Get an attribute along a chain of attrsets. Note that this does not auto-call functors or functions. */ - OrSuggestions> findAlongAttrPath(const std::vector & attrPath, bool force = false); + OrSuggestions> + findAlongAttrPath(const std::vector & attrPath, bool force = false); std::string getString(); diff --git a/src/libexpr/eval-inline.hh b/src/libexpr/eval-inline.hh index f2f4ba725b61..28b28bc46fb6 100644 --- a/src/libexpr/eval-inline.hh +++ b/src/libexpr/eval-inline.hh @@ -5,8 +5,7 @@ namespace nix { /* Note: Various places expect the allocated memory to be zeroed. */ -[[gnu::always_inline]] -inline void * allocBytes(size_t n) +[[gnu::always_inline]] inline void * allocBytes(size_t n) { void * p; #if HAVE_BOEHMGC @@ -14,26 +13,28 @@ inline void * allocBytes(size_t n) #else p = calloc(n, 1); #endif - if (!p) throw std::bad_alloc(); + if (!p) + throw std::bad_alloc(); return p; } - -[[gnu::always_inline]] -Value * EvalState::allocValue() +[[gnu::always_inline]] Value * EvalState::allocValue() { #if HAVE_BOEHMGC - /* We use the boehm batch allocator to speed up allocations of Values (of which there are many). - GC_malloc_many returns a linked list of objects of the given size, where the first word - of each object is also the pointer to the next object in the list. This also means that we - have to explicitly clear the first word of every object we take. */ + /* We use the boehm batch allocator to speed up allocations of Values (of + which there are many). GC_malloc_many returns a linked list of objects of + the given size, where the first word of each object is also the pointer + to the next object in the list. This also means that we have to + explicitly clear the first word of every object we take. */ if (!*valueAllocCache) { *valueAllocCache = GC_malloc_many(sizeof(Value)); - if (!*valueAllocCache) throw std::bad_alloc(); + if (!*valueAllocCache) + throw std::bad_alloc(); } /* GC_NEXT is a convenience macro for accessing the first word of an object. - Take the first list item, advance the list to the next item, and clear the next pointer. */ + Take the first list item, advance the list to the next item, and clear + the next pointer. */ void * p = *valueAllocCache; *valueAllocCache = GC_NEXT(p); GC_NEXT(p) = nullptr; @@ -45,9 +46,7 @@ Value * EvalState::allocValue() return (Value *) p; } - -[[gnu::always_inline]] -Env & EvalState::allocEnv(size_t size) +[[gnu::always_inline]] Env & EvalState::allocEnv(size_t size) { nrEnvs++; nrValuesInEnvs += size; @@ -59,7 +58,8 @@ Env & EvalState::allocEnv(size_t size) /* see allocValue for explanations. */ if (!*env1AllocCache) { *env1AllocCache = GC_malloc_many(sizeof(Env) + sizeof(Value *)); - if (!*env1AllocCache) throw std::bad_alloc(); + if (!*env1AllocCache) + throw std::bad_alloc(); } void * p = *env1AllocCache; @@ -72,19 +72,17 @@ Env & EvalState::allocEnv(size_t size) env->type = Env::Plain; - /* We assume that env->values has been cleared by the allocator; maybeThunk() and lookupVar fromWith expect this. */ + /* We assume that env->values has been cleared by the allocator; + * maybeThunk() and lookupVar fromWith expect this. */ return *env; } - -[[gnu::always_inline]] -void EvalState::forceValue(Value & v, const PosIdx pos) +[[gnu::always_inline]] void EvalState::forceValue(Value & v, const PosIdx pos) { forceValue(v, [&]() { return pos; }); } - template void EvalState::forceValue(Value & v, Callable getPos) { @@ -93,44 +91,39 @@ void EvalState::forceValue(Value & v, Callable getPos) Expr * expr = v.thunk.expr; try { v.mkBlackhole(); - //checkInterrupt(); + // checkInterrupt(); expr->eval(*this, *env, v); } catch (...) { v.mkThunk(env, expr); throw; } - } - else if (v.isApp()) + } else if (v.isApp()) callFunction(*v.app.left, *v.app.right, v, noPos); else if (v.isBlackhole()) throwEvalError(getPos(), "infinite recursion encountered"); } - -[[gnu::always_inline]] -inline void EvalState::forceAttrs(Value & v, const PosIdx pos) +[[gnu::always_inline]] inline void EvalState::forceAttrs(Value & v, + const PosIdx pos) { forceAttrs(v, [&]() { return pos; }); } - -template -[[gnu::always_inline]] -inline void EvalState::forceAttrs(Value & v, Callable getPos) +template +[[gnu::always_inline]] inline void EvalState::forceAttrs(Value & v, + Callable getPos) { forceValue(v, getPos); if (v.type() != nAttrs) throwTypeError(getPos(), "value is %1% while a set was expected", v); } - -[[gnu::always_inline]] -inline void EvalState::forceList(Value & v, const PosIdx pos) +[[gnu::always_inline]] inline void EvalState::forceList(Value & v, + const PosIdx pos) { forceValue(v, pos); if (!v.isList()) throwTypeError(pos, "value is %1% while a list was expected", v); } - } diff --git a/src/libexpr/eval.cc b/src/libexpr/eval.cc index 40462afdfc68..b209e7a173ad 100644 --- a/src/libexpr/eval.cc +++ b/src/libexpr/eval.cc @@ -45,11 +45,11 @@ static char * allocString(size_t size) #else t = malloc(size); #endif - if (!t) throw std::bad_alloc(); + if (!t) + throw std::bad_alloc(); return t; } - static char * dupString(const char * s) { char * t; @@ -58,11 +58,11 @@ static char * dupString(const char * s) #else t = strdup(s); #endif - if (!t) throw std::bad_alloc(); + if (!t) + throw std::bad_alloc(); return t; } - // When there's no need to write to the string, we can optimize away empty // string allocations. // This function handles makeImmutableStringWithLen(null, 0) by returning the @@ -77,15 +77,16 @@ static const char * makeImmutableStringWithLen(const char * s, size_t size) #else t = strndup(s, size); #endif - if (!t) throw std::bad_alloc(); + if (!t) + throw std::bad_alloc(); return t; } -static inline const char * makeImmutableString(std::string_view s) { +static inline const char * makeImmutableString(std::string_view s) +{ return makeImmutableStringWithLen(s.data(), s.size()); } - RootValue allocRootValue(Value * v) { #if HAVE_BOEHMGC @@ -95,9 +96,8 @@ RootValue allocRootValue(Value * v) #endif } - void Value::print(const SymbolTable & symbols, std::ostream & str, - std::set * seen) const + std::set * seen) const { checkInterrupt(); @@ -111,12 +111,18 @@ void Value::print(const SymbolTable & symbols, std::ostream & str, case tString: str << "\""; for (const char * i = string.s; *i; i++) - if (*i == '\"' || *i == '\\') str << "\\" << *i; - else if (*i == '\n') str << "\\n"; - else if (*i == '\r') str << "\\r"; - else if (*i == '\t') str << "\\t"; - else if (*i == '$' && *(i+1) == '{') str << "\\" << *i; - else str << *i; + if (*i == '\"' || *i == '\\') + str << "\\" << *i; + else if (*i == '\n') + str << "\\n"; + else if (*i == '\r') + str << "\\r"; + else if (*i == '\t') + str << "\\t"; + else if (*i == '$' && *(i + 1) == '{') + str << "\\" << *i; + else + str << *i; str << "\""; break; case tPath: @@ -180,15 +186,16 @@ void Value::print(const SymbolTable & symbols, std::ostream & str, } } - -void Value::print(const SymbolTable & symbols, std::ostream & str, bool showRepeated) const +void Value::print(const SymbolTable & symbols, std::ostream & str, + bool showRepeated) const { std::set seen; print(symbols, str, showRepeated ? nullptr : &seen); } // Pretty print types for assertion errors -std::ostream & operator << (std::ostream & os, const ValueType t) { +std::ostream & operator<<(std::ostream & os, const ValueType t) +{ os << showType(t); return os; } @@ -200,8 +207,8 @@ std::string printValue(const EvalState & state, const Value & v) return out.str(); } - -const Value * getPrimOp(const Value &v) { +const Value * getPrimOp(const Value & v) +{ const Value * primOp = &v; while (primOp->isPrimOpApp()) { primOp = primOp->primOpApp.left; @@ -213,34 +220,50 @@ const Value * getPrimOp(const Value &v) { std::string_view showType(ValueType type) { switch (type) { - case nInt: return "an integer"; - case nBool: return "a Boolean"; - case nString: return "a string"; - case nPath: return "a path"; - case nNull: return "null"; - case nAttrs: return "a set"; - case nList: return "a list"; - case nFunction: return "a function"; - case nExternal: return "an external value"; - case nFloat: return "a float"; - case nThunk: return "a thunk"; + case nInt: + return "an integer"; + case nBool: + return "a Boolean"; + case nString: + return "a string"; + case nPath: + return "a path"; + case nNull: + return "null"; + case nAttrs: + return "a set"; + case nList: + return "a list"; + case nFunction: + return "a function"; + case nExternal: + return "an external value"; + case nFloat: + return "a float"; + case nThunk: + return "a thunk"; } abort(); } - std::string showType(const Value & v) { switch (v.internalType) { - case tString: return v.string.context ? "a string with context" : "a string"; - case tPrimOp: - return fmt("the built-in function '%s'", std::string(v.primOp->name)); - case tPrimOpApp: - return fmt("the partially applied built-in function '%s'", std::string(getPrimOp(v)->primOp->name)); - case tExternal: return v.external->showType(); - case tThunk: return "a thunk"; - case tApp: return "a function application"; - case tBlackhole: return "a black hole"; + case tString: + return v.string.context ? "a string with context" : "a string"; + case tPrimOp: + return fmt("the built-in function '%s'", std::string(v.primOp->name)); + case tPrimOpApp: + return fmt("the partially applied built-in function '%s'", + std::string(getPrimOp(v)->primOp->name)); + case tExternal: + return v.external->showType(); + case tThunk: + return "a thunk"; + case tApp: + return "a function application"; + case tBlackhole: + return "a black hole"; default: return std::string(showType(v.type())); } @@ -249,26 +272,27 @@ std::string showType(const Value & v) PosIdx Value::determinePos(const PosIdx pos) const { switch (internalType) { - case tAttrs: return attrs->pos; - case tLambda: return lambda.fun->pos; - case tApp: return app.left->determinePos(pos); - default: return pos; + case tAttrs: + return attrs->pos; + case tLambda: + return lambda.fun->pos; + case tApp: + return app.left->determinePos(pos); + default: + return pos; } } bool Value::isTrivial() const { - return - internalType != tApp - && internalType != tPrimOpApp - && (internalType != tThunk - || (dynamic_cast(thunk.expr) - && ((ExprAttrs *) thunk.expr)->dynamicAttrs.empty()) - || dynamic_cast(thunk.expr) - || dynamic_cast(thunk.expr)); + return internalType != tApp && internalType != tPrimOpApp && + (internalType != tThunk || + (dynamic_cast(thunk.expr) && + ((ExprAttrs *) thunk.expr)->dynamicAttrs.empty()) || + dynamic_cast(thunk.expr) || + dynamic_cast(thunk.expr)); } - #if HAVE_BOEHMGC /* Called when the Boehm GC runs out of memory. */ static void * oomHandler(size_t requested) @@ -278,44 +302,49 @@ static void * oomHandler(size_t requested) } class BoehmGCStackAllocator : public StackAllocator { - boost::coroutines2::protected_fixedsize_stack stack { + boost::coroutines2::protected_fixedsize_stack stack{ // We allocate 8 MB, the default max stack size on NixOS. // A smaller stack might be quicker to allocate but reduces the stack // depth available for source filter expressions etc. - std::max(boost::context::stack_traits::default_size(), static_cast(8 * 1024 * 1024)) - }; + std::max(boost::context::stack_traits::default_size(), + static_cast(8 * 1024 * 1024))}; // This is specific to boost::coroutines2::protected_fixedsize_stack. // The stack protection page is included in sctx.size, so we have to // subtract one page size from the stack size. - std::size_t pfss_usable_stack_size(boost::context::stack_context &sctx) { + std::size_t pfss_usable_stack_size(boost::context::stack_context & sctx) + { return sctx.size - boost::context::stack_traits::page_size(); } public: - boost::context::stack_context allocate() override { + boost::context::stack_context allocate() override + { auto sctx = stack.allocate(); // Stacks generally start at a high address and grow to lower addresses. // Architectures that do the opposite are rare; in fact so rare that // boost_routine does not implement it. // So we subtract the stack size. - GC_add_roots(static_cast(sctx.sp) - pfss_usable_stack_size(sctx), sctx.sp); + GC_add_roots(static_cast(sctx.sp) - + pfss_usable_stack_size(sctx), + sctx.sp); return sctx; } - void deallocate(boost::context::stack_context sctx) override { - GC_remove_roots(static_cast(sctx.sp) - pfss_usable_stack_size(sctx), sctx.sp); + void deallocate(boost::context::stack_context sctx) override + { + GC_remove_roots(static_cast(sctx.sp) - + pfss_usable_stack_size(sctx), + sctx.sp); stack.deallocate(sctx); } - }; static BoehmGCStackAllocator boehmGCStackAllocator; #endif - static Symbol getName(const AttrName & name, EvalState & state, Env & env) { if (name.symbol) { @@ -328,12 +357,12 @@ static Symbol getName(const AttrName & name, EvalState & state, Env & env) } } - static bool gcInitialised = false; void initGC() { - if (gcInitialised) return; + if (gcInitialised) + return; #if HAVE_BOEHMGC /* Initialise the Boehm garbage collector. */ @@ -369,7 +398,8 @@ void initGC() long pages = sysconf(_SC_PHYS_PAGES); if (pageSize != -1) size = (pageSize * pages) / 4; // 25% of RAM - if (size > maxSize) size = maxSize; + if (size > maxSize) + size = maxSize; #endif debug(format("setting initial heap size to %1% bytes") % size); GC_expand_hp(size); @@ -380,7 +410,6 @@ void initGC() gcInitialised = true; } - /* Very hacky way to parse $NIX_PATH, which is colon-separated, but can contain URLs (e.g. "nixpkgs=https://bla...:foo=https://"). */ static Strings parseNixPath(const std::string & s) @@ -394,22 +423,26 @@ static Strings parseNixPath(const std::string & s) auto start2 = p; while (p != s.end() && *p != ':') { - if (*p == '=') start2 = p + 1; + if (*p == '=') + start2 = p + 1; ++p; } if (p == s.end()) { - if (p != start) res.push_back(std::string(start, p)); + if (p != start) + res.push_back(std::string(start, p)); break; } if (*p == ':') { if (isUri(std::string(start2, s.end()))) { ++p; - while (p != s.end() && *p != ':') ++p; + while (p != s.end() && *p != ':') + ++p; } res.push_back(std::string(start, p)); - if (p == s.end()) break; + if (p == s.end()) + break; } ++p; @@ -418,65 +451,51 @@ static Strings parseNixPath(const std::string & s) return res; } - -EvalState::EvalState( - const Strings & _searchPath, - ref store, - std::shared_ptr buildStore) - : sWith(symbols.create("")) - , sOutPath(symbols.create("outPath")) - , sDrvPath(symbols.create("drvPath")) - , sType(symbols.create("type")) - , sMeta(symbols.create("meta")) - , sName(symbols.create("name")) - , sValue(symbols.create("value")) - , sSystem(symbols.create("system")) - , sOverrides(symbols.create("__overrides")) - , sOutputs(symbols.create("outputs")) - , sOutputName(symbols.create("outputName")) - , sIgnoreNulls(symbols.create("__ignoreNulls")) - , sFile(symbols.create("file")) - , sLine(symbols.create("line")) - , sColumn(symbols.create("column")) - , sFunctor(symbols.create("__functor")) - , sToString(symbols.create("__toString")) - , sRight(symbols.create("right")) - , sWrong(symbols.create("wrong")) - , sStructuredAttrs(symbols.create("__structuredAttrs")) - , sBuilder(symbols.create("builder")) - , sArgs(symbols.create("args")) - , sContentAddressed(symbols.create("__contentAddressed")) - , sImpure(symbols.create("__impure")) - , sOutputHash(symbols.create("outputHash")) - , sOutputHashAlgo(symbols.create("outputHashAlgo")) - , sOutputHashMode(symbols.create("outputHashMode")) - , sRecurseForDerivations(symbols.create("recurseForDerivations")) - , sDescription(symbols.create("description")) - , sSelf(symbols.create("self")) - , sEpsilon(symbols.create("")) - , sStartSet(symbols.create("startSet")) - , sOperator(symbols.create("operator")) - , sKey(symbols.create("key")) - , sPath(symbols.create("path")) - , sPrefix(symbols.create("prefix")) - , sOutputSpecified(symbols.create("outputSpecified")) - , repair(NoRepair) - , emptyBindings(0) - , store(store) - , buildStore(buildStore ? buildStore : store) - , debugRepl(0) - , debugStop(false) - , debugQuit(false) - , regexCache(makeRegexCache()) +EvalState::EvalState(const Strings & _searchPath, ref store, + std::shared_ptr buildStore) + : sWith(symbols.create("")), sOutPath(symbols.create("outPath")), + sDrvPath(symbols.create("drvPath")), sType(symbols.create("type")), + sMeta(symbols.create("meta")), sName(symbols.create("name")), + sValue(symbols.create("value")), sSystem(symbols.create("system")), + sOverrides(symbols.create("__overrides")), + sOutputs(symbols.create("outputs")), + sOutputName(symbols.create("outputName")), + sIgnoreNulls(symbols.create("__ignoreNulls")), + sFile(symbols.create("file")), sLine(symbols.create("line")), + sColumn(symbols.create("column")), sFunctor(symbols.create("__functor")), + sToString(symbols.create("__toString")), sRight(symbols.create("right")), + sWrong(symbols.create("wrong")), + sStructuredAttrs(symbols.create("__structuredAttrs")), + sBuilder(symbols.create("builder")), sArgs(symbols.create("args")), + sContentAddressed(symbols.create("__contentAddressed")), + sImpure(symbols.create("__impure")), + sOutputHash(symbols.create("outputHash")), + sOutputHashAlgo(symbols.create("outputHashAlgo")), + sOutputHashMode(symbols.create("outputHashMode")), + sRecurseForDerivations(symbols.create("recurseForDerivations")), + sDescription(symbols.create("description")), + sSelf(symbols.create("self")), sEpsilon(symbols.create("")), + sStartSet(symbols.create("startSet")), + sOperator(symbols.create("operator")), sKey(symbols.create("key")), + sPath(symbols.create("path")), sPrefix(symbols.create("prefix")), + sOutputSpecified(symbols.create("outputSpecified")), repair(NoRepair), + emptyBindings(0), store(store), + buildStore(buildStore ? buildStore : store), debugRepl(0), + debugStop(false), debugQuit(false), regexCache(makeRegexCache()) #if HAVE_BOEHMGC - , valueAllocCache(std::allocate_shared(traceable_allocator(), nullptr)) - , env1AllocCache(std::allocate_shared(traceable_allocator(), nullptr)) + , + valueAllocCache( + std::allocate_shared(traceable_allocator(), nullptr)), + env1AllocCache( + std::allocate_shared(traceable_allocator(), nullptr)) #else - , valueAllocCache(std::make_shared(nullptr)) - , env1AllocCache(std::make_shared(nullptr)) + , + valueAllocCache(std::make_shared(nullptr)), + env1AllocCache(std::make_shared(nullptr)) #endif - , baseEnv(allocEnv(128)) - , staticBaseEnv{std::make_shared(false, nullptr)} + , + baseEnv(allocEnv(128)), staticBaseEnv{ + std::make_shared(false, nullptr)} { countCalls = getEnv("NIX_COUNT_CALLS").value_or("0") != "0"; @@ -486,8 +505,10 @@ EvalState::EvalState( /* Initialise the Nix expression search path. */ if (!evalSettings.pureEval) { - for (auto & i : _searchPath) addToSearchPath(i); - for (auto & i : evalSettings.nixPath.get()) addToSearchPath(i); + for (auto & i : _searchPath) + addToSearchPath(i); + for (auto & i : evalSettings.nixPath.get()) + addToSearchPath(i); } if (evalSettings.restrictEval || evalSettings.pureEval) { @@ -495,14 +516,16 @@ EvalState::EvalState( for (auto & i : searchPath) { auto r = resolveSearchPathElem(i); - if (!r.first) continue; + if (!r.first) + continue; auto path = r.second; if (store->isInStore(r.second)) { try { StorePathSet closure; - store->computeFSClosure(store->toStorePath(r.second).first, closure); + store->computeFSClosure(store->toStorePath(r.second).first, + closure); for (auto & path : closure) allowPath(path); } catch (InvalidPath &) { @@ -516,11 +539,7 @@ EvalState::EvalState( createBaseEnv(); } - -EvalState::~EvalState() -{ -} - +EvalState::~EvalState() {} void EvalState::allowPath(const Path & path) { @@ -534,7 +553,8 @@ void EvalState::allowPath(const StorePath & storePath) allowedPaths->insert(store->toRealPath(storePath)); } -void EvalState::allowAndSetStorePathString(const StorePath & storePath, Value & v) +void EvalState::allowAndSetStorePathString(const StorePath & storePath, + Value & v) { allowPath(storePath); @@ -544,7 +564,8 @@ void EvalState::allowAndSetStorePathString(const StorePath & storePath, Value & Path EvalState::checkSourcePath(const Path & path_) { - if (!allowedPaths) return path_; + if (!allowedPaths) + return path_; auto i = resolvedPaths.find(path_); if (i != resolvedPaths.end()) @@ -558,7 +579,8 @@ Path EvalState::checkSourcePath(const Path & path_) */ Path abspath = canonPath(path_); - if (hasPrefix(abspath, corepkgsPrefix)) return abspath; + if (hasPrefix(abspath, corepkgsPrefix)) + return abspath; for (auto & i : *allowedPaths) { if (isDirOrInDir(abspath, i)) { @@ -568,10 +590,13 @@ Path EvalState::checkSourcePath(const Path & path_) } if (!found) { - auto modeInformation = evalSettings.pureEval - ? "in pure eval mode (use '--impure' to override)" - : "in restricted mode"; - throw RestrictedPathError("access to absolute path '%1%' is forbidden %2%", abspath, modeInformation); + auto modeInformation = + evalSettings.pureEval + ? "in pure eval mode (use '--impure' to override)" + : "in restricted mode"; + throw RestrictedPathError( + "access to absolute path '%1%' is forbidden %2%", abspath, + modeInformation); } /* Resolve symlinks. */ @@ -585,13 +610,14 @@ Path EvalState::checkSourcePath(const Path & path_) } } - throw RestrictedPathError("access to canonical path '%1%' is forbidden in restricted mode", path); + throw RestrictedPathError( + "access to canonical path '%1%' is forbidden in restricted mode", path); } - void EvalState::checkURI(const std::string & uri) { - if (!evalSettings.restrictEval) return; + if (!evalSettings.restrictEval) + return; /* 'uri' should be equal to a prefix, or in a subdirectory of a prefix. Thus, the prefix https://github.co does not permit @@ -599,10 +625,9 @@ void EvalState::checkURI(const std::string & uri) 'https://' as prefixes for any http/https URI. */ for (auto & prefix : evalSettings.allowedUris.get()) if (uri == prefix || - (uri.size() > prefix.size() - && prefix.size() > 0 - && hasPrefix(uri, prefix) - && (prefix[prefix.size() - 1] == '/' || uri[prefix.size()] == '/'))) + (uri.size() > prefix.size() && prefix.size() > 0 && + hasPrefix(uri, prefix) && + (prefix[prefix.size() - 1] == '/' || uri[prefix.size()] == '/'))) return; /* If the URI is a path, then check it against allowedPaths as @@ -617,20 +642,17 @@ void EvalState::checkURI(const std::string & uri) return; } - throw RestrictedPathError("access to URI '%s' is forbidden in restricted mode", uri); + throw RestrictedPathError( + "access to URI '%s' is forbidden in restricted mode", uri); } - Path EvalState::toRealPath(const Path & path, const PathSet & context) { // FIXME: check whether 'path' is in 'context'. - return - !context.empty() && store->isInStore(path) - ? store->toRealPath(path) - : path; + return !context.empty() && store->isInStore(path) ? store->toRealPath(path) + : path; } - Value * EvalState::addConstant(const std::string & name, Value & v) { Value * v2 = allocValue(); @@ -639,7 +661,6 @@ Value * EvalState::addConstant(const std::string & name, Value & v) return v2; } - void EvalState::addConstant(const std::string & name, Value * v) { staticBaseEnv->vars.emplace_back(symbols.create(name), baseEnvDispl); @@ -648,9 +669,8 @@ void EvalState::addConstant(const std::string & name, Value * v) baseEnv.values[0]->attrs->push_back(Attr(symbols.create(name2), v)); } - -Value * EvalState::addPrimOp(const std::string & name, - size_t arity, PrimOpFun primOp) +Value * EvalState::addPrimOp(const std::string & name, size_t arity, + PrimOpFun primOp) { auto name2 = name.substr(0, 2) == "__" ? name.substr(2) : name; auto sym = symbols.create(name2); @@ -659,21 +679,20 @@ Value * EvalState::addPrimOp(const std::string & name, the primop to a dummy value. */ if (arity == 0) { auto vPrimOp = allocValue(); - vPrimOp->mkPrimOp(new PrimOp { .fun = primOp, .arity = 1, .name = name2 }); + vPrimOp->mkPrimOp(new PrimOp{.fun = primOp, .arity = 1, .name = name2}); Value v; v.mkApp(vPrimOp, vPrimOp); return addConstant(name, v); } Value * v = allocValue(); - v->mkPrimOp(new PrimOp { .fun = primOp, .arity = arity, .name = name2 }); + v->mkPrimOp(new PrimOp{.fun = primOp, .arity = arity, .name = name2}); staticBaseEnv->vars.emplace_back(symbols.create(name), baseEnvDispl); baseEnv.values[baseEnvDispl++] = v; baseEnv.values[0]->attrs->push_back(Attr(sym, v)); return v; } - Value * EvalState::addPrimOp(PrimOp && primOp) { /* Hack to make constants lazy: turn them into a application of @@ -699,19 +718,17 @@ Value * EvalState::addPrimOp(PrimOp && primOp) return v; } - Value & EvalState::getBuiltin(const std::string & name) { return *baseEnv.values[0]->attrs->find(symbols.create(name))->value; } - std::optional EvalState::getDoc(Value & v) { if (v.isPrimOp()) { auto v2 = &v; if (v2->primOp->doc) - return Doc { + return Doc{ .pos = {}, .name = v2->primOp->name, .arity = v2->primOp->arity, @@ -722,7 +739,6 @@ std::optional EvalState::getDoc(Value & v) return {}; } - // just for the current level of StaticEnv, not the whole chain. void printStaticEnvBindings(const SymbolTable & st, const StaticEnv & se) { @@ -749,7 +765,8 @@ void printWithBindings(const SymbolTable & st, const Env & env) } } -void printEnvBindings(const SymbolTable & st, const StaticEnv & se, const Env & env, int lvl) +void printEnvBindings(const SymbolTable & st, const StaticEnv & se, + const Env & env, int lvl) { std::cout << "Env level " << lvl << std::endl; @@ -768,13 +785,12 @@ void printEnvBindings(const SymbolTable & st, const StaticEnv & se, const Env & std::cout << st[i.first] << " "; std::cout << ANSI_NORMAL; std::cout << std::endl; - printWithBindings(st, env); // probably nothing there for the top level. + printWithBindings(st, env); // probably nothing there for the top level. std::cout << std::endl; - } } -void printEnvBindings(const EvalState &es, const Expr & expr, const Env & env) +void printEnvBindings(const EvalState & es, const Expr & expr, const Env & env) { // just print the names for now auto se = es.getStaticEnv(expr); @@ -782,11 +798,12 @@ void printEnvBindings(const EvalState &es, const Expr & expr, const Env & env) printEnvBindings(es.symbols, *se, env, 0); } -void mapStaticEnvBindings(const SymbolTable & st, const StaticEnv & se, const Env & env, ValMap & vm) +void mapStaticEnvBindings(const SymbolTable & st, const StaticEnv & se, + const Env & env, ValMap & vm) { - // add bindings for the next level up first, so that the bindings for this level - // override the higher levels. - // The top level bindings (builtins) are skipped since they are added for us by initEnv() + // add bindings for the next level up first, so that the bindings for this + // level override the higher levels. The top level bindings (builtins) are + // skipped since they are added for us by initEnv() if (env.up && se.up) { mapStaticEnvBindings(st, *se.up, *env.up, vm); @@ -805,14 +822,17 @@ void mapStaticEnvBindings(const SymbolTable & st, const StaticEnv & se, const En } } -std::unique_ptr mapStaticEnvBindings(const SymbolTable & st, const StaticEnv & se, const Env & env) +std::unique_ptr mapStaticEnvBindings(const SymbolTable & st, + const StaticEnv & se, + const Env & env) { auto vm = std::make_unique(); mapStaticEnvBindings(st, se, env, *vm); return vm; } -void EvalState::runDebugRepl(const Error * error, const Env & env, const Expr & expr) +void EvalState::runDebugRepl(const Error * error, const Env & env, + const Expr & expr) { // double check we've got the debugRepl function pointer. if (!debugRepl) @@ -820,19 +840,21 @@ void EvalState::runDebugRepl(const Error * error, const Env & env, const Expr & auto dts = error && expr.getPos() - ? std::make_unique( - *this, - DebugTrace { - .pos = error->info().errPos ? *error->info().errPos : positions[expr.getPos()], - .expr = expr, - .env = env, - .hint = error->info().msg, - .isError = true - }) - : nullptr; + ? std::make_unique( + *this, DebugTrace{.pos = error->info().errPos + ? *error->info().errPos + : positions[expr.getPos()], + .expr = expr, + .env = env, + .hint = error->info().msg, + .isError = true}) + : nullptr; if (error) - printError("%s\n\n" ANSI_BOLD "Starting REPL to allow you to inspect the current state of the evaluator.\n" ANSI_NORMAL, error->what()); + printError("%s\n\n" ANSI_BOLD + "Starting REPL to allow you to inspect the current state of " + "the evaluator.\n" ANSI_NORMAL, + error->what()); auto se = getStaticEnv(expr); if (se) { @@ -845,20 +867,17 @@ void EvalState::runDebugRepl(const Error * error, const Env & env, const Expr & of stack space, which is a real killer in the recursive evaluator. So here are some helper functions for throwing exceptions. */ -void EvalState::throwEvalError(const PosIdx pos, const char * s, Env & env, Expr & expr) +void EvalState::throwEvalError(const PosIdx pos, const char * s, Env & env, + Expr & expr) { - debugThrow(EvalError({ - .msg = hintfmt(s), - .errPos = positions[pos] - }), env, expr); + debugThrow(EvalError({.msg = hintfmt(s), .errPos = positions[pos]}), env, + expr); } void EvalState::throwEvalError(const PosIdx pos, const char * s) { - debugThrowLastTrace(EvalError({ - .msg = hintfmt(s), - .errPos = positions[pos] - })); + debugThrowLastTrace( + EvalError({.msg = hintfmt(s), .errPos = positions[pos]})); } void EvalState::throwEvalError(const char * s, const std::string & s2) @@ -866,192 +885,187 @@ void EvalState::throwEvalError(const char * s, const std::string & s2) debugThrowLastTrace(EvalError(s, s2)); } -void EvalState::throwEvalError(const PosIdx pos, const Suggestions & suggestions, const char * s, - const std::string & s2, Env & env, Expr & expr) +void EvalState::throwEvalError(const PosIdx pos, + const Suggestions & suggestions, const char * s, + const std::string & s2, Env & env, Expr & expr) { debugThrow(EvalError(ErrorInfo{ - .msg = hintfmt(s, s2), - .errPos = positions[pos], - .suggestions = suggestions, - }), env, expr); + .msg = hintfmt(s, s2), + .errPos = positions[pos], + .suggestions = suggestions, + }), + env, expr); } -void EvalState::throwEvalError(const PosIdx pos, const char * s, const std::string & s2) +void EvalState::throwEvalError(const PosIdx pos, const char * s, + const std::string & s2) { - debugThrowLastTrace(EvalError({ - .msg = hintfmt(s, s2), - .errPos = positions[pos] - })); + debugThrowLastTrace( + EvalError({.msg = hintfmt(s, s2), .errPos = positions[pos]})); } -void EvalState::throwEvalError(const PosIdx pos, const char * s, const std::string & s2, Env & env, Expr & expr) +void EvalState::throwEvalError(const PosIdx pos, const char * s, + const std::string & s2, Env & env, Expr & expr) { - debugThrow(EvalError({ - .msg = hintfmt(s, s2), - .errPos = positions[pos] - }), env, expr); + debugThrow(EvalError({.msg = hintfmt(s, s2), .errPos = positions[pos]}), + env, expr); } void EvalState::throwEvalError(const char * s, const std::string & s2, - const std::string & s3) + const std::string & s3) { - debugThrowLastTrace(EvalError({ - .msg = hintfmt(s, s2), - .errPos = positions[noPos] - })); + debugThrowLastTrace( + EvalError({.msg = hintfmt(s, s2), .errPos = positions[noPos]})); } -void EvalState::throwEvalError(const PosIdx pos, const char * s, const std::string & s2, - const std::string & s3) +void EvalState::throwEvalError(const PosIdx pos, const char * s, + const std::string & s2, const std::string & s3) { - debugThrowLastTrace(EvalError({ - .msg = hintfmt(s, s2), - .errPos = positions[pos] - })); + debugThrowLastTrace( + EvalError({.msg = hintfmt(s, s2), .errPos = positions[pos]})); } -void EvalState::throwEvalError(const PosIdx pos, const char * s, const std::string & s2, - const std::string & s3, Env & env, Expr & expr) +void EvalState::throwEvalError(const PosIdx pos, const char * s, + const std::string & s2, const std::string & s3, + Env & env, Expr & expr) { - debugThrow(EvalError({ - .msg = hintfmt(s, s2), - .errPos = positions[pos] - }), env, expr); + debugThrow(EvalError({.msg = hintfmt(s, s2), .errPos = positions[pos]}), + env, expr); } -void EvalState::throwEvalError(const PosIdx p1, const char * s, const Symbol sym, const PosIdx p2, Env & env, Expr & expr) +void EvalState::throwEvalError(const PosIdx p1, const char * s, + const Symbol sym, const PosIdx p2, Env & env, + Expr & expr) { - // p1 is where the error occurred; p2 is a position mentioned in the message. - debugThrow(EvalError({ - .msg = hintfmt(s, symbols[sym], positions[p2]), - .errPos = positions[p1] - }), env, expr); + // p1 is where the error occurred; p2 is a position mentioned in the + // message. + debugThrow(EvalError({.msg = hintfmt(s, symbols[sym], positions[p2]), + .errPos = positions[p1]}), + env, expr); } -void EvalState::throwTypeError(const PosIdx pos, const char * s, const Value & v) +void EvalState::throwTypeError(const PosIdx pos, const char * s, + const Value & v) { - debugThrowLastTrace(TypeError({ - .msg = hintfmt(s, showType(v)), - .errPos = positions[pos] - })); + debugThrowLastTrace( + TypeError({.msg = hintfmt(s, showType(v)), .errPos = positions[pos]})); } -void EvalState::throwTypeError(const PosIdx pos, const char * s, const Value & v, Env & env, Expr & expr) +void EvalState::throwTypeError(const PosIdx pos, const char * s, + const Value & v, Env & env, Expr & expr) { - debugThrow(TypeError({ - .msg = hintfmt(s, showType(v)), - .errPos = positions[pos] - }), env, expr); + debugThrow( + TypeError({.msg = hintfmt(s, showType(v)), .errPos = positions[pos]}), + env, expr); } void EvalState::throwTypeError(const PosIdx pos, const char * s) { - debugThrowLastTrace(TypeError({ - .msg = hintfmt(s), - .errPos = positions[pos] - })); + debugThrowLastTrace( + TypeError({.msg = hintfmt(s), .errPos = positions[pos]})); } -void EvalState::throwTypeError(const PosIdx pos, const char * s, const ExprLambda & fun, - const Symbol s2, Env & env, Expr &expr) +void EvalState::throwTypeError(const PosIdx pos, const char * s, + const ExprLambda & fun, const Symbol s2, + Env & env, Expr & expr) { - debugThrow(TypeError({ - .msg = hintfmt(s, fun.showNamePos(*this), symbols[s2]), - .errPos = positions[pos] - }), env, expr); + debugThrow( + TypeError({.msg = hintfmt(s, fun.showNamePos(*this), symbols[s2]), + .errPos = positions[pos]}), + env, expr); } -void EvalState::throwTypeError(const PosIdx pos, const Suggestions & suggestions, const char * s, - const ExprLambda & fun, const Symbol s2, Env & env, Expr &expr) +void EvalState::throwTypeError(const PosIdx pos, + const Suggestions & suggestions, const char * s, + const ExprLambda & fun, const Symbol s2, + Env & env, Expr & expr) { - debugThrow(TypeError(ErrorInfo { - .msg = hintfmt(s, fun.showNamePos(*this), symbols[s2]), - .errPos = positions[pos], - .suggestions = suggestions, - }), env, expr); + debugThrow(TypeError(ErrorInfo{ + .msg = hintfmt(s, fun.showNamePos(*this), symbols[s2]), + .errPos = positions[pos], + .suggestions = suggestions, + }), + env, expr); } -void EvalState::throwTypeError(const char * s, const Value & v, Env & env, Expr &expr) +void EvalState::throwTypeError(const char * s, const Value & v, Env & env, + Expr & expr) { debugThrow(TypeError({ - .msg = hintfmt(s, showType(v)), - .errPos = positions[expr.getPos()], - }), env, expr); + .msg = hintfmt(s, showType(v)), + .errPos = positions[expr.getPos()], + }), + env, expr); } -void EvalState::throwAssertionError(const PosIdx pos, const char * s, const std::string & s1, Env & env, Expr &expr) +void EvalState::throwAssertionError(const PosIdx pos, const char * s, + const std::string & s1, Env & env, + Expr & expr) { - debugThrow(AssertionError({ - .msg = hintfmt(s, s1), - .errPos = positions[pos] - }), env, expr); + debugThrow( + AssertionError({.msg = hintfmt(s, s1), .errPos = positions[pos]}), env, + expr); } -void EvalState::throwUndefinedVarError(const PosIdx pos, const char * s, const std::string & s1, Env & env, Expr &expr) +void EvalState::throwUndefinedVarError(const PosIdx pos, const char * s, + const std::string & s1, Env & env, + Expr & expr) { - debugThrow(UndefinedVarError({ - .msg = hintfmt(s, s1), - .errPos = positions[pos] - }), env, expr); + debugThrow( + UndefinedVarError({.msg = hintfmt(s, s1), .errPos = positions[pos]}), + env, expr); } -void EvalState::throwMissingArgumentError(const PosIdx pos, const char * s, const std::string & s1, Env & env, Expr &expr) +void EvalState::throwMissingArgumentError(const PosIdx pos, const char * s, + const std::string & s1, Env & env, + Expr & expr) { - debugThrow(MissingArgumentError({ - .msg = hintfmt(s, s1), - .errPos = positions[pos] - }), env, expr); + debugThrow( + MissingArgumentError({.msg = hintfmt(s, s1), .errPos = positions[pos]}), + env, expr); } -void EvalState::addErrorTrace(Error & e, const char * s, const std::string & s2) const +void EvalState::addErrorTrace(Error & e, const char * s, + const std::string & s2) const { e.addTrace(std::nullopt, s, s2); } -void EvalState::addErrorTrace(Error & e, const PosIdx pos, const char * s, const std::string & s2) const +void EvalState::addErrorTrace(Error & e, const PosIdx pos, const char * s, + const std::string & s2) const { e.addTrace(positions[pos], s, s2); } -static std::unique_ptr makeDebugTraceStacker( - EvalState & state, - Expr & expr, - Env & env, - std::optional pos, - const char * s, - const std::string & s2) +static std::unique_ptr +makeDebugTraceStacker(EvalState & state, Expr & expr, Env & env, + std::optional pos, const char * s, + const std::string & s2) { - return std::make_unique(state, - DebugTrace { - .pos = pos, - .expr = expr, - .env = env, - .hint = hintfmt(s, s2), - .isError = false - }); + return std::make_unique( + state, DebugTrace{.pos = pos, + .expr = expr, + .env = env, + .hint = hintfmt(s, s2), + .isError = false}); } DebugTraceStacker::DebugTraceStacker(EvalState & evalState, DebugTrace t) - : evalState(evalState) - , trace(std::move(t)) + : evalState(evalState), trace(std::move(t)) { evalState.debugTraces.push_front(trace); if (evalState.debugStop && evalState.debugRepl) evalState.runDebugRepl(nullptr, trace.env, trace.expr); } -void Value::mkString(std::string_view s) -{ - mkString(makeImmutableString(s)); -} - +void Value::mkString(std::string_view s) { mkString(makeImmutableString(s)); } static void copyContextToValue(Value & v, const PathSet & context) { if (!context.empty()) { size_t n = 0; - v.string.context = (const char * *) - allocBytes((context.size() + 1) * sizeof(char *)); + v.string.context = + (const char **) allocBytes((context.size() + 1) * sizeof(char *)); for (auto & i : context) v.string.context[n++] = dupString(i.c_str()); v.string.context[n] = 0; @@ -1070,22 +1084,20 @@ void Value::mkStringMove(const char * s, const PathSet & context) copyContextToValue(*this, context); } - -void Value::mkPath(std::string_view s) -{ - mkPath(makeImmutableString(s)); -} - +void Value::mkPath(std::string_view s) { mkPath(makeImmutableString(s)); } inline Value * EvalState::lookupVar(Env * env, const ExprVar & var, bool noEval) { - for (auto l = var.level; l; --l, env = env->up) ; + for (auto l = var.level; l; --l, env = env->up) + ; - if (!var.fromWith) return env->values[var.displ]; + if (!var.fromWith) + return env->values[var.displ]; while (1) { if (env->type == Env::HasWithExpr) { - if (noEval) return 0; + if (noEval) + return 0; Value * v = allocValue(); evalAttrs(*env->up, (Expr *) env->values[0], *v); env->values[0] = v; @@ -1093,12 +1105,16 @@ inline Value * EvalState::lookupVar(Env * env, const ExprVar & var, bool noEval) } Bindings::iterator j = env->values[0]->attrs->find(var.name); if (j != env->values[0]->attrs->end()) { - if (countCalls) attrSelects[j->pos]++; + if (countCalls) + attrSelects[j->pos]++; return j->value; } if (!env->prevWith) - throwUndefinedVarError(var.pos, "undefined variable '%1%'", symbols[var.name], *env, const_cast(var)); - for (size_t l = env->prevWith; l; --l, env = env->up) ; + throwUndefinedVarError(var.pos, "undefined variable '%1%'", + symbols[var.name], *env, + const_cast(var)); + for (size_t l = env->prevWith; l; --l, env = env->up) + ; } } @@ -1106,11 +1122,10 @@ void EvalState::mkList(Value & v, size_t size) { v.mkList(size); if (size > 2) - v.bigList.elems = (Value * *) allocBytes(size * sizeof(Value *)); + v.bigList.elems = (Value **) allocBytes(size * sizeof(Value *)); nrListElems += size; } - unsigned long nrThunks = 0; static inline void mkThunk(Value & v, Env & env, Expr * expr) @@ -1119,12 +1134,7 @@ static inline void mkThunk(Value & v, Env & env, Expr * expr) nrThunks++; } - -void EvalState::mkThunk_(Value & v, Expr * expr) -{ - mkThunk(v, baseEnv, expr); -} - +void EvalState::mkThunk_(Value & v, Expr * expr) { mkThunk(v, baseEnv, expr); } void EvalState::mkPos(Value & v, PosIdx p) { @@ -1139,7 +1149,6 @@ void EvalState::mkPos(Value & v, PosIdx p) v.mkNull(); } - /* Create a thunk for the delayed computation of the given expression in the given environment. But if the expression is a variable, then look it up right away. This significantly reduces the number @@ -1151,17 +1160,18 @@ Value * Expr::maybeThunk(EvalState & state, Env & env) return v; } - Value * ExprVar::maybeThunk(EvalState & state, Env & env) { Value * v = state.lookupVar(&env, *this, true); /* The value might not be initialised in the environment yet. In that case, ignore it. */ - if (v) { state.nrAvoided++; return v; } + if (v) { + state.nrAvoided++; + return v; + } return Expr::maybeThunk(state, env); } - Value * ExprString::maybeThunk(EvalState & state, Env & env) { state.nrAvoided++; @@ -1186,7 +1196,6 @@ Value * ExprPath::maybeThunk(EvalState & state, Env & env) return &v; } - void EvalState::evalFile(const Path & path_, Value & v, bool mustBeTrivial) { auto path = checkSourcePath(path_); @@ -1216,37 +1225,30 @@ void EvalState::evalFile(const Path & path_, Value & v, bool mustBeTrivial) cacheFile(path, resolvedPath, e, v, mustBeTrivial); } - void EvalState::resetFileCache() { fileEvalCache.clear(); fileParseCache.clear(); } - -void EvalState::cacheFile( - const Path & path, - const Path & resolvedPath, - Expr * e, - Value & v, - bool mustBeTrivial) +void EvalState::cacheFile(const Path & path, const Path & resolvedPath, + Expr * e, Value & v, bool mustBeTrivial) { fileParseCache[resolvedPath] = e; try { auto dts = debugRepl - ? makeDebugTraceStacker( - *this, - *e, - this->baseEnv, - e->getPos() ? std::optional(ErrPos(positions[e->getPos()])) : std::nullopt, - "while evaluating the file '%1%':", resolvedPath) - : nullptr; + ? makeDebugTraceStacker( + *this, *e, this->baseEnv, + e->getPos() + ? std::optional(ErrPos(positions[e->getPos()])) + : std::nullopt, + "while evaluating the file '%1%':", resolvedPath) + : nullptr; // Enforce that 'flake.nix' is a direct attrset, not a // computation. - if (mustBeTrivial && - !(dynamic_cast(e))) + if (mustBeTrivial && !(dynamic_cast(e))) throw EvalError("file '%s' must be an attribute set", path); eval(e, v); } catch (Error & e) { @@ -1255,72 +1257,49 @@ void EvalState::cacheFile( } fileEvalCache[resolvedPath] = v; - if (path != resolvedPath) fileEvalCache[path] = v; -} - - -void EvalState::eval(Expr * e, Value & v) -{ - e->eval(*this, baseEnv, v); + if (path != resolvedPath) + fileEvalCache[path] = v; } +void EvalState::eval(Expr * e, Value & v) { e->eval(*this, baseEnv, v); } inline bool EvalState::evalBool(Env & env, Expr * e) { Value v; e->eval(*this, env, v); if (v.type() != nBool) - throwTypeError(noPos, "value is %1% while a Boolean was expected", v, env, *e); + throwTypeError(noPos, "value is %1% while a Boolean was expected", v, + env, *e); return v.boolean; } - inline bool EvalState::evalBool(Env & env, Expr * e, const PosIdx pos) { Value v; e->eval(*this, env, v); if (v.type() != nBool) - throwTypeError(pos, "value is %1% while a Boolean was expected", v, env, *e); + throwTypeError(pos, "value is %1% while a Boolean was expected", v, env, + *e); return v.boolean; } - inline void EvalState::evalAttrs(Env & env, Expr * e, Value & v) { e->eval(*this, env, v); if (v.type() != nAttrs) - throwTypeError(noPos, "value is %1% while a set was expected", v, env, *e); + throwTypeError(noPos, "value is %1% while a set was expected", v, env, + *e); } +void Expr::eval(EvalState & state, Env & env, Value & v) { abort(); } -void Expr::eval(EvalState & state, Env & env, Value & v) -{ - abort(); -} +void ExprInt::eval(EvalState & state, Env & env, Value & v) { v = this->v; } +void ExprFloat::eval(EvalState & state, Env & env, Value & v) { v = this->v; } -void ExprInt::eval(EvalState & state, Env & env, Value & v) -{ - v = this->v; -} - - -void ExprFloat::eval(EvalState & state, Env & env, Value & v) -{ - v = this->v; -} - -void ExprString::eval(EvalState & state, Env & env, Value & v) -{ - v = this->v; -} - - -void ExprPath::eval(EvalState & state, Env & env, Value & v) -{ - v = this->v; -} +void ExprString::eval(EvalState & state, Env & env, Value & v) { v = this->v; } +void ExprPath::eval(EvalState & state, Env & env, Value & v) { v = this->v; } void ExprAttrs::eval(EvalState & state, Env & env, Value & v) { @@ -1347,7 +1326,8 @@ void ExprAttrs::eval(EvalState & state, Env & env, Value & v) vAttr = state.allocValue(); mkThunk(*vAttr, env2, i.second.e); } else - vAttr = i.second.e->maybeThunk(state, i.second.inherited ? env : env2); + vAttr = i.second.e->maybeThunk(state, + i.second.inherited ? env : env2); env2.values[displ++] = vAttr; v.attrs->push_back(Attr(i.first, vAttr, i.second.pos)); } @@ -1362,8 +1342,10 @@ void ExprAttrs::eval(EvalState & state, Env & env, Value & v) Hence we need __overrides.) */ if (hasOverrides) { Value * vOverrides = (*v.attrs)[overrides->second.displ].value; - state.forceAttrs(*vOverrides, [&]() { return vOverrides->determinePos(noPos); }); - Bindings * newBnds = state.allocBindings(v.attrs->capacity() + vOverrides->attrs->size()); + state.forceAttrs(*vOverrides, + [&]() { return vOverrides->determinePos(noPos); }); + Bindings * newBnds = state.allocBindings(v.attrs->capacity() + + vOverrides->attrs->size()); for (auto & i : *v.attrs) newBnds->push_back(i); for (auto & i : *vOverrides->attrs) { @@ -1381,7 +1363,8 @@ void ExprAttrs::eval(EvalState & state, Env & env, Value & v) else for (auto & i : attrs) - v.attrs->push_back(Attr(i.first, i.second.e->maybeThunk(state, env), i.second.pos)); + v.attrs->push_back(Attr(i.first, i.second.e->maybeThunk(state, env), + i.second.pos)); /* Dynamic attrs apply *after* rec and __overrides. */ for (auto & i : dynamicAttrs) { @@ -1394,18 +1377,20 @@ void ExprAttrs::eval(EvalState & state, Env & env, Value & v) auto nameSym = state.symbols.create(nameVal.string.s); Bindings::iterator j = v.attrs->find(nameSym); if (j != v.attrs->end()) - state.throwEvalError(i.pos, "dynamic attribute '%1%' already defined at %2%", nameSym, j->pos, env, *this); + state.throwEvalError( + i.pos, "dynamic attribute '%1%' already defined at %2%", + nameSym, j->pos, env, *this); i.valueExpr->setName(nameSym); /* Keep sorted order so find can catch duplicates */ - v.attrs->push_back(Attr(nameSym, i.valueExpr->maybeThunk(state, *dynamicEnv), i.pos)); + v.attrs->push_back( + Attr(nameSym, i.valueExpr->maybeThunk(state, *dynamicEnv), i.pos)); v.attrs->sort(); // FIXME: inefficient } v.attrs->pos = pos; } - void ExprLet::eval(EvalState & state, Env & env, Value & v) { /* Create a new environment that contains the attributes in this @@ -1418,20 +1403,19 @@ void ExprLet::eval(EvalState & state, Env & env, Value & v) environment. */ Displacement displ = 0; for (auto & i : attrs->attrs) - env2.values[displ++] = i.second.e->maybeThunk(state, i.second.inherited ? env : env2); + env2.values[displ++] = + i.second.e->maybeThunk(state, i.second.inherited ? env : env2); body->eval(state, env2, v); } - void ExprList::eval(EvalState & state, Env & env, Value & v) { state.mkList(v, elems.size()); for (auto [n, v2] : enumerate(v.listItems())) - const_cast(v2) = elems[n]->maybeThunk(state, env); + const_cast(v2) = elems[n]->maybeThunk(state, env); } - void ExprVar::eval(EvalState & state, Env & env, Value & v) { Value * v2 = state.lookupVar(&env, *this, false); @@ -1439,13 +1423,16 @@ void ExprVar::eval(EvalState & state, Env & env, Value & v) v = *v2; } - -static std::string showAttrPath(EvalState & state, Env & env, const AttrPath & attrPath) +static std::string showAttrPath(EvalState & state, Env & env, + const AttrPath & attrPath) { std::ostringstream out; bool first = true; for (auto & i : attrPath) { - if (!first) out << '.'; else first = false; + if (!first) + out << '.'; + else + first = false; try { out << state.symbols[getName(i, state, env)]; } catch (Error & e) { @@ -1458,7 +1445,6 @@ static std::string showAttrPath(EvalState & state, Env & env, const AttrPath & a return out.str(); } - void ExprSelect::eval(EvalState & state, Env & env, Value & v) { Value vTmp; @@ -1469,14 +1455,11 @@ void ExprSelect::eval(EvalState & state, Env & env, Value & v) try { auto dts = state.debugRepl - ? makeDebugTraceStacker( - state, - *this, - env, - state.positions[pos2], - "while evaluating the attribute '%1%'", - showAttrPath(state, env, attrPath)) - : nullptr; + ? makeDebugTraceStacker( + state, *this, env, state.positions[pos2], + "while evaluating the attribute '%1%'", + showAttrPath(state, env, attrPath)) + : nullptr; for (auto & i : attrPath) { state.nrLookups++; @@ -1485,8 +1468,7 @@ void ExprSelect::eval(EvalState & state, Env & env, Value & v) if (def) { state.forceValue(*vAttrs, pos); if (vAttrs->type() != nAttrs || - (j = vAttrs->attrs->find(name)) == vAttrs->attrs->end()) - { + (j = vAttrs->attrs->find(name)) == vAttrs->attrs->end()) { def->eval(state, env, v); return; } @@ -1496,31 +1478,32 @@ void ExprSelect::eval(EvalState & state, Env & env, Value & v) std::set allAttrNames; for (auto & attr : *vAttrs->attrs) allAttrNames.insert(state.symbols[attr.name]); - state.throwEvalError( - pos, - Suggestions::bestMatches(allAttrNames, state.symbols[name]), - "attribute '%1%' missing", state.symbols[name], env, *this); + state.throwEvalError(pos, + Suggestions::bestMatches( + allAttrNames, state.symbols[name]), + "attribute '%1%' missing", + state.symbols[name], env, *this); } } vAttrs = j->value; pos2 = j->pos; - if (state.countCalls) state.attrSelects[pos2]++; + if (state.countCalls) + state.attrSelects[pos2]++; } - state.forceValue(*vAttrs, (pos2 ? pos2 : this->pos ) ); + state.forceValue(*vAttrs, (pos2 ? pos2 : this->pos)); } catch (Error & e) { auto pos2r = state.positions[pos2]; if (pos2 && pos2r.file != state.derivationNixPath) state.addErrorTrace(e, pos2, "while evaluating the attribute '%1%'", - showAttrPath(state, env, attrPath)); + showAttrPath(state, env, attrPath)); throw; } v = *vAttrs; } - void ExprOpHasAttr::eval(EvalState & state, Env & env, Value & v) { Value vTmp; @@ -1533,8 +1516,7 @@ void ExprOpHasAttr::eval(EvalState & state, Env & env, Value & v) Bindings::iterator j; auto name = getName(i, state, env); if (vAttrs->type() != nAttrs || - (j = vAttrs->attrs->find(name)) == vAttrs->attrs->end()) - { + (j = vAttrs->attrs->find(name)) == vAttrs->attrs->end()) { v.mkBool(false); return; } else { @@ -1545,25 +1527,23 @@ void ExprOpHasAttr::eval(EvalState & state, Env & env, Value & v) v.mkBool(true); } - void ExprLambda::eval(EvalState & state, Env & env, Value & v) { v.mkLambda(&env, this); } - -void EvalState::callFunction(Value & fun, size_t nrArgs, Value * * args, Value & vRes, const PosIdx pos) +void EvalState::callFunction(Value & fun, size_t nrArgs, Value ** args, + Value & vRes, const PosIdx pos) { auto trace = evalSettings.traceFunctionCalls - ? std::make_unique(positions[pos]) - : nullptr; + ? std::make_unique(positions[pos]) + : nullptr; forceValue(fun, pos); Value vCur(fun); - auto makeAppChain = [&]() - { + auto makeAppChain = [&]() { vRes = vCur; for (size_t i = 0; i < nrArgs; ++i) { auto fun2 = allocValue(); @@ -1603,8 +1583,11 @@ void EvalState::callFunction(Value & fun, size_t nrArgs, Value * * args, Value & for (auto & i : lambda.formals->formals) { auto j = args[0]->attrs->get(i.name); if (!j) { - if (!i.def) throwTypeError(pos, "%1% called without required argument '%2%'", - lambda, i.name, *fun.lambda.env, lambda); + if (!i.def) + throwTypeError( + pos, + "%1% called without required argument '%2%'", + lambda, i.name, *fun.lambda.env, lambda); env2.values[displ++] = i.def->maybeThunk(*this, env2); } else { attrsUsed++; @@ -1614,7 +1597,8 @@ void EvalState::callFunction(Value & fun, size_t nrArgs, Value * * args, Value & /* Check that each actual argument is listed as a formal argument (unless the attribute match specifies a `...'). */ - if (!lambda.formals->ellipsis && attrsUsed != args[0]->attrs->size()) { + if (!lambda.formals->ellipsis && + attrsUsed != args[0]->attrs->size()) { /* Nope, so show the first unexpected argument to the user. */ for (auto & i : *args[0]->attrs) @@ -1624,7 +1608,8 @@ void EvalState::callFunction(Value & fun, size_t nrArgs, Value * * args, Value & formalNames.insert(symbols[formal.name]); throwTypeError( pos, - Suggestions::bestMatches(formalNames, symbols[i.name]), + Suggestions::bestMatches(formalNames, + symbols[i.name]), "%1% called with unexpected argument '%2%'", lambda, i.name, *fun.lambda.env, lambda); } @@ -1633,26 +1618,29 @@ void EvalState::callFunction(Value & fun, size_t nrArgs, Value * * args, Value & } nrFunctionCalls++; - if (countCalls) incrFunctionCall(&lambda); + if (countCalls) + incrFunctionCall(&lambda); /* Evaluate the body. */ try { - auto dts = debugRepl - ? makeDebugTraceStacker( - *this, *lambda.body, env2, positions[lambda.pos], - "while evaluating %s", - lambda.name - ? concatStrings("'", symbols[lambda.name], "'") - : "anonymous lambda") - : nullptr; + auto dts = + debugRepl + ? makeDebugTraceStacker( + *this, *lambda.body, env2, positions[lambda.pos], + "while evaluating %s", + lambda.name ? concatStrings( + "'", symbols[lambda.name], "'") + : "anonymous lambda") + : nullptr; lambda.body->eval(*this, env2, vCur); } catch (Error & e) { if (loggerSettings.showTrace.get()) { - addErrorTrace(e, lambda.pos, "while evaluating %s", + addErrorTrace( + e, lambda.pos, "while evaluating %s", (lambda.name - ? concatStrings("'", symbols[lambda.name], "'") - : "anonymous lambda")); + ? concatStrings("'", symbols[lambda.name], "'") + : "anonymous lambda")); addErrorTrace(e, pos, "from call site%s", ""); } throw; @@ -1667,13 +1655,15 @@ void EvalState::callFunction(Value & fun, size_t nrArgs, Value * * args, Value & size_t argsLeft = vCur.primOp->arity; if (nrArgs < argsLeft) { - /* We don't have enough arguments, so create a tPrimOpApp chain. */ + /* We don't have enough arguments, so create a tPrimOpApp chain. + */ makeAppChain(); return; } else { /* We have all the arguments, so call the primop. */ nrPrimOpCalls++; - if (countCalls) primOpCalls[vCur.primOp->name]++; + if (countCalls) + primOpCalls[vCur.primOp->name]++; vCur.primOp->fun(*this, pos, args, vCur); nrArgs -= argsLeft; @@ -1694,7 +1684,8 @@ void EvalState::callFunction(Value & fun, size_t nrArgs, Value * * args, Value & auto argsLeft = arity - argsDone; if (nrArgs < argsLeft) { - /* We still don't have enough arguments, so extend the tPrimOpApp chain. */ + /* We still don't have enough arguments, so extend the + * tPrimOpApp chain. */ makeAppChain(); return; } else { @@ -1703,14 +1694,16 @@ void EvalState::callFunction(Value & fun, size_t nrArgs, Value * * args, Value & Value * vArgs[arity]; auto n = argsDone; - for (Value * arg = &vCur; arg->isPrimOpApp(); arg = arg->primOpApp.left) + for (Value * arg = &vCur; arg->isPrimOpApp(); + arg = arg->primOpApp.left) vArgs[--n] = arg->primOpApp.right; for (size_t i = 0; i < argsLeft; ++i) vArgs[argsDone + i] = args[i]; nrPrimOpCalls++; - if (countCalls) primOpCalls[primOp->primOp->name]++; + if (countCalls) + primOpCalls[primOp->primOp->name]++; primOp->primOp->fun(*this, pos, vArgs, vCur); nrArgs -= argsLeft; @@ -1718,7 +1711,8 @@ void EvalState::callFunction(Value & fun, size_t nrArgs, Value * * args, Value & } } - else if (vCur.type() == nAttrs && (functor = vCur.attrs->get(sFunctor))) { + else if (vCur.type() == nAttrs && + (functor = vCur.attrs->get(sFunctor))) { /* 'vCur' may be allocated on the stack of the calling function, but for functors we may keep a reference, so heap-allocate a copy and use that instead. */ @@ -1731,13 +1725,15 @@ void EvalState::callFunction(Value & fun, size_t nrArgs, Value * * args, Value & } else - throwTypeError(pos, "attempt to call something which is not a function but %1%", vCur); + throwTypeError( + pos, + "attempt to call something which is not a function but %1%", + vCur); } vRes = vCur; } - void ExprCall::eval(EvalState & state, Env & env, Value & v) { Value vFun; @@ -1750,14 +1746,9 @@ void ExprCall::eval(EvalState & state, Env & env, Value & v) state.callFunction(vFun, args.size(), vArgs, v, pos); } - // Lifted out of callFunction() because it creates a temporary that // prevents tail-call optimisation. -void EvalState::incrFunctionCall(ExprLambda * fun) -{ - functionCalls[fun]++; -} - +void EvalState::incrFunctionCall(ExprLambda * fun) { functionCalls[fun]++; } void EvalState::autoCallFunction(Bindings & args, Value & fun, Value & res) { @@ -1780,12 +1771,14 @@ void EvalState::autoCallFunction(Bindings & args, Value & fun, Value & res) return; } - auto attrs = buildBindings(std::max(static_cast(fun.lambda.fun->formals->formals.size()), args.size())); + auto attrs = buildBindings( + std::max(static_cast(fun.lambda.fun->formals->formals.size()), + args.size())); if (fun.lambda.fun->formals->ellipsis) { - // If the formals have an ellipsis (eg the function accepts extra args) pass - // all available automatic arguments (which includes arguments specified on - // the command line via --arg/--argstr) + // If the formals have an ellipsis (eg the function accepts extra args) + // pass all available automatic arguments (which includes arguments + // specified on the command line via --arg/--argstr) for (auto & v : args) attrs.insert(v); } else { @@ -1795,13 +1788,15 @@ void EvalState::autoCallFunction(Bindings & args, Value & fun, Value & res) if (j != args.end()) { attrs.insert(*j); } else if (!i.def) { - throwMissingArgumentError(i.pos, R"(cannot evaluate a function that has an argument without a value ('%1%') + throwMissingArgumentError( + i.pos, + R"(cannot evaluate a function that has an argument without a value ('%1%') Nix attempted to evaluate a function as a top level expression; in this case it must have its arguments supplied either by default values, or passed explicitly with '--arg' or '--argstr'. See -https://nixos.org/manual/nix/stable/expressions/language-constructs.html#functions.)", symbols[i.name], - *fun.lambda.env, *fun.lambda.fun); +https://nixos.org/manual/nix/stable/expressions/language-constructs.html#functions.)", + symbols[i.name], *fun.lambda.env, *fun.lambda.fun); } } } @@ -1809,7 +1804,6 @@ values, or passed explicitly with '--arg' or '--argstr'. See callFunction(fun, allocValue()->mkAttrs(attrs), res, noPos); } - void ExprWith::eval(EvalState & state, Env & env, Value & v) { Env & env2(state.allocEnv(1)); @@ -1821,64 +1815,60 @@ void ExprWith::eval(EvalState & state, Env & env, Value & v) body->eval(state, env2, v); } - void ExprIf::eval(EvalState & state, Env & env, Value & v) { (state.evalBool(env, cond, pos) ? then : else_)->eval(state, env, v); } - void ExprAssert::eval(EvalState & state, Env & env, Value & v) { if (!state.evalBool(env, cond, pos)) { std::ostringstream out; cond->show(state.symbols, out); - state.throwAssertionError(pos, "assertion '%1%' failed", out.str(), env, *this); + state.throwAssertionError(pos, "assertion '%1%' failed", out.str(), env, + *this); } body->eval(state, env, v); } - void ExprOpNot::eval(EvalState & state, Env & env, Value & v) { v.mkBool(!state.evalBool(env, e)); } - void ExprOpEq::eval(EvalState & state, Env & env, Value & v) { - Value v1; e1->eval(state, env, v1); - Value v2; e2->eval(state, env, v2); + Value v1; + e1->eval(state, env, v1); + Value v2; + e2->eval(state, env, v2); v.mkBool(state.eqValues(v1, v2)); } - void ExprOpNEq::eval(EvalState & state, Env & env, Value & v) { - Value v1; e1->eval(state, env, v1); - Value v2; e2->eval(state, env, v2); + Value v1; + e1->eval(state, env, v1); + Value v2; + e2->eval(state, env, v2); v.mkBool(!state.eqValues(v1, v2)); } - void ExprOpAnd::eval(EvalState & state, Env & env, Value & v) { v.mkBool(state.evalBool(env, e1, pos) && state.evalBool(env, e2, pos)); } - void ExprOpOr::eval(EvalState & state, Env & env, Value & v) { v.mkBool(state.evalBool(env, e1, pos) || state.evalBool(env, e2, pos)); } - void ExprOpImpl::eval(EvalState & state, Env & env, Value & v) { v.mkBool(!state.evalBool(env, e1, pos) || state.evalBool(env, e2, pos)); } - void ExprOpUpdate::eval(EvalState & state, Env & env, Value & v) { Value v1, v2; @@ -1887,8 +1877,14 @@ void ExprOpUpdate::eval(EvalState & state, Env & env, Value & v) state.nrOpUpdates++; - if (v1.attrs->size() == 0) { v = v2; return; } - if (v2.attrs->size() == 0) { v = v1; return; } + if (v1.attrs->size() == 0) { + v = v2; + return; + } + if (v2.attrs->size() == 0) { + v = v1; + return; + } auto attrs = state.buildBindings(v1.attrs->size() + v2.attrs->size()); @@ -1900,33 +1896,36 @@ void ExprOpUpdate::eval(EvalState & state, Env & env, Value & v) while (i != v1.attrs->end() && j != v2.attrs->end()) { if (i->name == j->name) { attrs.insert(*j); - ++i; ++j; - } - else if (i->name < j->name) + ++i; + ++j; + } else if (i->name < j->name) attrs.insert(*i++); else attrs.insert(*j++); } - while (i != v1.attrs->end()) attrs.insert(*i++); - while (j != v2.attrs->end()) attrs.insert(*j++); + while (i != v1.attrs->end()) + attrs.insert(*i++); + while (j != v2.attrs->end()) + attrs.insert(*j++); v.mkAttrs(attrs.alreadySorted()); state.nrOpUpdateValuesCopied += v.attrs->size(); } - void ExprOpConcatLists::eval(EvalState & state, Env & env, Value & v) { - Value v1; e1->eval(state, env, v1); - Value v2; e2->eval(state, env, v2); - Value * lists[2] = { &v1, &v2 }; + Value v1; + e1->eval(state, env, v1); + Value v2; + e2->eval(state, env, v2); + Value * lists[2] = {&v1, &v2}; state.concatLists(v, 2, lists, pos); } - -void EvalState::concatLists(Value & v, size_t nrLists, Value * * lists, const PosIdx pos) +void EvalState::concatLists(Value & v, size_t nrLists, Value ** lists, + const PosIdx pos) { nrListConcats++; @@ -1936,7 +1935,8 @@ void EvalState::concatLists(Value & v, size_t nrLists, Value * * lists, const Po forceList(*lists[n], pos); auto l = lists[n]->listSize(); len += l; - if (l) nonEmpty = lists[n]; + if (l) + nonEmpty = lists[n]; } if (nonEmpty && len == nonEmpty->listSize()) { @@ -1954,7 +1954,6 @@ void EvalState::concatLists(Value & v, size_t nrLists, Value * * lists, const Po } } - void ExprConcatStrings::eval(EvalState & state, Env & env, Value & v) { PathSet context; @@ -1969,7 +1968,8 @@ void ExprConcatStrings::eval(EvalState & state, Env & env, Value & v) const auto str = [&] { std::string result; result.reserve(sSize); - for (const auto & part : s) result += *part; + for (const auto & part : s) + result += *part; return result; }; /* c_str() is not str().c_str() because we want to create a string @@ -2010,20 +2010,24 @@ void ExprConcatStrings::eval(EvalState & state, Env & env, Value & v) nf = n; nf += vTmp.fpoint; } else - state.throwEvalError(i_pos, "cannot add %1% to an integer", showType(vTmp), env, *this); + state.throwEvalError(i_pos, "cannot add %1% to an integer", + showType(vTmp), env, *this); } else if (firstType == nFloat) { if (vTmp.type() == nInt) { nf += vTmp.integer; } else if (vTmp.type() == nFloat) { nf += vTmp.fpoint; } else - state.throwEvalError(i_pos, "cannot add %1% to a float", showType(vTmp), env, *this); + state.throwEvalError(i_pos, "cannot add %1% to a float", + showType(vTmp), env, *this); } else { - if (s.empty()) s.reserve(es->size()); + if (s.empty()) + s.reserve(es->size()); /* skip canonization of first path, which would only be not canonized in the first place if it's coming from a ./${foo} type path */ - auto part = state.coerceToString(i_pos, vTmp, context, false, firstType == nString, !first); + auto part = state.coerceToString(i_pos, vTmp, context, false, + firstType == nString, !first); sSize += part->size(); s.emplace_back(std::move(part)); } @@ -2037,19 +2041,20 @@ void ExprConcatStrings::eval(EvalState & state, Env & env, Value & v) v.mkFloat(nf); else if (firstType == nPath) { if (!context.empty()) - state.throwEvalError(pos, "a string that refers to a store path cannot be appended to a path", env, *this); + state.throwEvalError(pos, + "a string that refers to a store path cannot " + "be appended to a path", + env, *this); v.mkPath(canonPath(str())); } else v.mkStringMove(c_str(), context); } - void ExprPos::eval(EvalState & state, Env & env, Value & v) { state.mkPos(v, pos); } - void EvalState::forceValueDeep(Value & v) { std::set seen; @@ -2057,22 +2062,29 @@ void EvalState::forceValueDeep(Value & v) std::function recurse; recurse = [&](Value & v) { - if (!seen.insert(&v).second) return; + if (!seen.insert(&v).second) + return; forceValue(v, [&]() { return v.determinePos(noPos); }); if (v.type() == nAttrs) { for (auto & i : *v.attrs) try { - // If the value is a thunk, we're evaling. Otherwise no trace necessary. + // If the value is a thunk, we're evaling. Otherwise no + // trace necessary. auto dts = debugRepl && i.value->isThunk() - ? makeDebugTraceStacker(*this, *i.value->thunk.expr, *i.value->thunk.env, positions[i.pos], - "while evaluating the attribute '%1%'", symbols[i.name]) - : nullptr; + ? makeDebugTraceStacker( + *this, *i.value->thunk.expr, + *i.value->thunk.env, positions[i.pos], + "while evaluating the attribute '%1%'", + symbols[i.name]) + : nullptr; recurse(*i.value); } catch (Error & e) { - addErrorTrace(e, i.pos, "while evaluating the attribute '%1%'", symbols[i.name]); + addErrorTrace(e, i.pos, + "while evaluating the attribute '%1%'", + symbols[i.name]); throw; } } @@ -2086,7 +2098,6 @@ void EvalState::forceValueDeep(Value & v) recurse(v); } - NixInt EvalState::forceInt(Value & v, const PosIdx pos) { forceValue(v, pos); @@ -2096,7 +2107,6 @@ NixInt EvalState::forceInt(Value & v, const PosIdx pos) return v.integer; } - NixFloat EvalState::forceFloat(Value & v, const PosIdx pos) { forceValue(v, pos); @@ -2107,7 +2117,6 @@ NixFloat EvalState::forceFloat(Value & v, const PosIdx pos) return v.fpoint; } - bool EvalState::forceBool(Value & v, const PosIdx pos) { forceValue(v, pos); @@ -2116,13 +2125,12 @@ bool EvalState::forceBool(Value & v, const PosIdx pos) return v.boolean; } - bool EvalState::isFunctor(Value & fun) { - return fun.type() == nAttrs && fun.attrs->find(sFunctor) != fun.attrs->end(); + return fun.type() == nAttrs && + fun.attrs->find(sFunctor) != fun.attrs->end(); } - void EvalState::forceFunction(Value & v, const PosIdx pos) { forceValue(v, pos); @@ -2130,7 +2138,6 @@ void EvalState::forceFunction(Value & v, const PosIdx pos) throwTypeError(pos, "value is %1% while a function was expected", v); } - std::string_view EvalState::forceString(Value & v, const PosIdx pos) { forceValue(v, pos); @@ -2140,7 +2147,6 @@ std::string_view EvalState::forceString(Value & v, const PosIdx pos) return v.string.s; } - /* Decode a context string ‘!!’ into a pair . */ NixStringContextElem decodeContext(const Store & store, std::string_view s) @@ -2153,83 +2159,85 @@ NixStringContextElem decodeContext(const Store & store, std::string_view s) }; } else return { - store.parseStorePath( - s.at(0) == '/' - ? s - : s.substr(1)), + store.parseStorePath(s.at(0) == '/' ? s : s.substr(1)), "", }; } - void copyContext(const Value & v, PathSet & context) { if (v.string.context) - for (const char * * p = v.string.context; *p; ++p) + for (const char ** p = v.string.context; *p; ++p) context.insert(*p); } - NixStringContext Value::getContext(const Store & store) { NixStringContext res; assert(internalType == tString); if (string.context) - for (const char * * p = string.context; *p; ++p) + for (const char ** p = string.context; *p; ++p) res.push_back(decodeContext(store, *p)); return res; } - -std::string_view EvalState::forceString(Value & v, PathSet & context, const PosIdx pos) +std::string_view EvalState::forceString(Value & v, PathSet & context, + const PosIdx pos) { auto s = forceString(v, pos); copyContext(v, context); return s; } - std::string_view EvalState::forceStringNoCtx(Value & v, const PosIdx pos) { auto s = forceString(v, pos); if (v.string.context) { if (pos) - throwEvalError(pos, "the string '%1%' is not allowed to refer to a store path (such as '%2%')", - v.string.s, v.string.context[0]); + throwEvalError(pos, + "the string '%1%' is not allowed to refer to a " + "store path (such as '%2%')", + v.string.s, v.string.context[0]); else - throwEvalError("the string '%1%' is not allowed to refer to a store path (such as '%2%')", - v.string.s, v.string.context[0]); + throwEvalError("the string '%1%' is not allowed to refer to a " + "store path (such as '%2%')", + v.string.s, v.string.context[0]); } return s; } - bool EvalState::isDerivation(Value & v) { - if (v.type() != nAttrs) return false; + if (v.type() != nAttrs) + return false; Bindings::iterator i = v.attrs->find(sType); - if (i == v.attrs->end()) return false; + if (i == v.attrs->end()) + return false; forceValue(*i->value, i->pos); - if (i->value->type() != nString) return false; + if (i->value->type() != nString) + return false; return strcmp(i->value->string.s, "derivation") == 0; } - -std::optional EvalState::tryAttrsToString(const PosIdx pos, Value & v, - PathSet & context, bool coerceMore, bool copyToStore) +std::optional +EvalState::tryAttrsToString(const PosIdx pos, Value & v, PathSet & context, + bool coerceMore, bool copyToStore) { auto i = v.attrs->find(sToString); if (i != v.attrs->end()) { Value v1; callFunction(*i->value, v, v1, pos); - return coerceToString(pos, v1, context, coerceMore, copyToStore).toOwned(); + return coerceToString(pos, v1, context, coerceMore, copyToStore) + .toOwned(); } return {}; } -BackedStringView EvalState::coerceToString(const PosIdx pos, Value & v, PathSet & context, - bool coerceMore, bool copyToStore, bool canonicalizePath) +BackedStringView EvalState::coerceToString(const PosIdx pos, Value & v, + PathSet & context, bool coerceMore, + bool copyToStore, + bool canonicalizePath) { forceValue(v, pos); @@ -2248,7 +2256,8 @@ BackedStringView EvalState::coerceToString(const PosIdx pos, Value & v, PathSet } if (v.type() == nAttrs) { - auto maybeString = tryAttrsToString(pos, v, context, coerceMore, copyToStore); + auto maybeString = + tryAttrsToString(pos, v, context, coerceMore, copyToStore); if (maybeString) return std::move(*maybeString); auto i = v.attrs->find(sOutPath); @@ -2258,21 +2267,28 @@ BackedStringView EvalState::coerceToString(const PosIdx pos, Value & v, PathSet } if (v.type() == nExternal) - return v.external->coerceToString(positions[pos], context, coerceMore, copyToStore); + return v.external->coerceToString(positions[pos], context, coerceMore, + copyToStore); if (coerceMore) { /* Note that `false' is represented as an empty string for shell scripting convenience, just like `null'. */ - if (v.type() == nBool && v.boolean) return "1"; - if (v.type() == nBool && !v.boolean) return ""; - if (v.type() == nInt) return std::to_string(v.integer); - if (v.type() == nFloat) return std::to_string(v.fpoint); - if (v.type() == nNull) return ""; + if (v.type() == nBool && v.boolean) + return "1"; + if (v.type() == nBool && !v.boolean) + return ""; + if (v.type() == nInt) + return std::to_string(v.integer); + if (v.type() == nFloat) + return std::to_string(v.fpoint); + if (v.type() == nNull) + return ""; if (v.isList()) { std::string result; for (auto [n, v2] : enumerate(v.listItems())) { - result += *coerceToString(pos, *v2, context, coerceMore, copyToStore); + result += + *coerceToString(pos, *v2, context, coerceMore, copyToStore); if (n < v.listSize() - 1 /* !!! not quite correct */ && (!v2->isList() || v2->listSize() != 0)) @@ -2285,20 +2301,27 @@ BackedStringView EvalState::coerceToString(const PosIdx pos, Value & v, PathSet throwTypeError(pos, "cannot coerce %1% to a string", v); } - std::string EvalState::copyPathToStore(PathSet & context, const Path & path) { if (nix::isDerivation(path)) - throwEvalError("file names are not allowed to end in '%1%'", drvExtension); + throwEvalError("file names are not allowed to end in '%1%'", + drvExtension); Path dstPath; auto i = srcToStore.find(path); if (i != srcToStore.end()) dstPath = store->printStorePath(i->second); else { - auto p = settings.readOnlyMode - ? store->computeStorePathForPath(std::string(baseNameOf(path)), checkSourcePath(path)).first - : store->addToStore(std::string(baseNameOf(path)), checkSourcePath(path), FileIngestionMethod::Recursive, htSHA256, defaultPathFilter, repair); + auto p = + settings.readOnlyMode + ? store + ->computeStorePathForPath(std::string(baseNameOf(path)), + checkSourcePath(path)) + .first + : store->addToStore(std::string(baseNameOf(path)), + checkSourcePath(path), + FileIngestionMethod::Recursive, htSHA256, + defaultPathFilter, repair); dstPath = store->printStorePath(p); allowPath(p); srcToStore.insert_or_assign(path, std::move(p)); @@ -2309,28 +2332,25 @@ std::string EvalState::copyPathToStore(PathSet & context, const Path & path) return dstPath; } - Path EvalState::coerceToPath(const PosIdx pos, Value & v, PathSet & context) { auto path = coerceToString(pos, v, context, false, false).toOwned(); if (path == "" || path[0] != '/') - throwEvalError(pos, "string '%1%' doesn't represent an absolute path", path); + throwEvalError(pos, "string '%1%' doesn't represent an absolute path", + path); return path; } - -StorePath EvalState::coerceToStorePath(const PosIdx pos, Value & v, PathSet & context) +StorePath EvalState::coerceToStorePath(const PosIdx pos, Value & v, + PathSet & context) { auto path = coerceToString(pos, v, context, false, false).toOwned(); if (auto storePath = store->maybeParseStorePath(path)) return *storePath; - throw EvalError({ - .msg = hintfmt("path '%1%' is not in the Nix store", path), - .errPos = positions[pos] - }); + throw EvalError({.msg = hintfmt("path '%1%' is not in the Nix store", path), + .errPos = positions[pos]}); } - bool EvalState::eqValues(Value & v1, Value & v2) { forceValue(v1, noPos); @@ -2339,7 +2359,8 @@ bool EvalState::eqValues(Value & v1, Value & v2) /* !!! Hack to support some old broken code that relies on pointer equality tests between sets. (Specifically, builderDefs calls uniqList on a list of sets.) Will remove this eventually. */ - if (&v1 == &v2) return true; + if (&v1 == &v2) + return true; // Special case type-compatibility between float and int if (v1.type() == nInt && v2.type() == nFloat) @@ -2348,66 +2369,70 @@ bool EvalState::eqValues(Value & v1, Value & v2) return v1.fpoint == v2.integer; // All other types are not compatible with each other. - if (v1.type() != v2.type()) return false; + if (v1.type() != v2.type()) + return false; switch (v1.type()) { - case nInt: - return v1.integer == v2.integer; + case nInt: + return v1.integer == v2.integer; - case nBool: - return v1.boolean == v2.boolean; + case nBool: + return v1.boolean == v2.boolean; - case nString: - return strcmp(v1.string.s, v2.string.s) == 0; + case nString: + return strcmp(v1.string.s, v2.string.s) == 0; - case nPath: - return strcmp(v1.path, v2.path) == 0; + case nPath: + return strcmp(v1.path, v2.path) == 0; - case nNull: - return true; + case nNull: + return true; - case nList: - if (v1.listSize() != v2.listSize()) return false; - for (size_t n = 0; n < v1.listSize(); ++n) - if (!eqValues(*v1.listElems()[n], *v2.listElems()[n])) return false; - return true; - - case nAttrs: { - /* If both sets denote a derivation (type = "derivation"), - then compare their outPaths. */ - if (isDerivation(v1) && isDerivation(v2)) { - Bindings::iterator i = v1.attrs->find(sOutPath); - Bindings::iterator j = v2.attrs->find(sOutPath); - if (i != v1.attrs->end() && j != v2.attrs->end()) - return eqValues(*i->value, *j->value); - } + case nList: + if (v1.listSize() != v2.listSize()) + return false; + for (size_t n = 0; n < v1.listSize(); ++n) + if (!eqValues(*v1.listElems()[n], *v2.listElems()[n])) + return false; + return true; + + case nAttrs: { + /* If both sets denote a derivation (type = "derivation"), + then compare their outPaths. */ + if (isDerivation(v1) && isDerivation(v2)) { + Bindings::iterator i = v1.attrs->find(sOutPath); + Bindings::iterator j = v2.attrs->find(sOutPath); + if (i != v1.attrs->end() && j != v2.attrs->end()) + return eqValues(*i->value, *j->value); + } - if (v1.attrs->size() != v2.attrs->size()) return false; + if (v1.attrs->size() != v2.attrs->size()) + return false; - /* Otherwise, compare the attributes one by one. */ - Bindings::iterator i, j; - for (i = v1.attrs->begin(), j = v2.attrs->begin(); i != v1.attrs->end(); ++i, ++j) - if (i->name != j->name || !eqValues(*i->value, *j->value)) - return false; + /* Otherwise, compare the attributes one by one. */ + Bindings::iterator i, j; + for (i = v1.attrs->begin(), j = v2.attrs->begin(); i != v1.attrs->end(); + ++i, ++j) + if (i->name != j->name || !eqValues(*i->value, *j->value)) + return false; - return true; - } + return true; + } - /* Functions are incomparable. */ - case nFunction: - return false; + /* Functions are incomparable. */ + case nFunction: + return false; - case nExternal: - return *v1.external == *v2.external; + case nExternal: + return *v1.external == *v2.external; - case nFloat: - return v1.fpoint == v2.fpoint; + case nFloat: + return v1.fpoint == v2.fpoint; - default: - throwEvalError("cannot compare %1% with %2%", - showType(v1), - showType(v2)); + default: + throwEvalError("cannot compare %1% with %2%", showType(v1), + showType(v2)); } } @@ -2417,12 +2442,14 @@ void EvalState::printStats() struct rusage buf; getrusage(RUSAGE_SELF, &buf); - float cpuTime = buf.ru_utime.tv_sec + ((float) buf.ru_utime.tv_usec / 1000000); + float cpuTime = + buf.ru_utime.tv_sec + ((float) buf.ru_utime.tv_usec / 1000000); uint64_t bEnvs = nrEnvs * sizeof(Env) + nrValuesInEnvs * sizeof(Value *); uint64_t bLists = nrListElems * sizeof(Value *); uint64_t bValues = nrValues * sizeof(Value); - uint64_t bAttrsets = nrAttrsets * sizeof(Bindings) + nrAttrsInAttrsets * sizeof(Attr); + uint64_t bAttrsets = + nrAttrsets * sizeof(Bindings) + nrAttrsInAttrsets * sizeof(Attr); #if HAVE_BOEHMGC GC_word heapSize, totalBytes; @@ -2434,7 +2461,7 @@ void EvalState::printStats() if (outPath != "-") fs.open(outPath, std::fstream::out); JSONObject topObj(outPath == "-" ? std::cerr : fs, true); - topObj.attr("cpuTime",cpuTime); + topObj.attr("cpuTime", cpuTime); { auto envs = topObj.object("envs"); envs.attr("number", nrEnvs); @@ -2528,31 +2555,30 @@ void EvalState::printStats() } } - -std::string ExternalValueBase::coerceToString(const Pos & pos, PathSet & context, bool copyMore, bool copyToStore) const +std::string ExternalValueBase::coerceToString(const Pos & pos, + PathSet & context, bool copyMore, + bool copyToStore) const { - throw TypeError({ - .msg = hintfmt("cannot coerce %1% to a string", showType()), - .errPos = pos - }); + throw TypeError( + {.msg = hintfmt("cannot coerce %1% to a string", showType()), + .errPos = pos}); } - bool ExternalValueBase::operator==(const ExternalValueBase & b) const { return false; } - -std::ostream & operator << (std::ostream & str, const ExternalValueBase & v) { +std::ostream & operator<<(std::ostream & str, const ExternalValueBase & v) +{ return v.print(str); } - EvalSettings::EvalSettings() { auto var = getEnv("NIX_PATH"); - if (var) nixPath = parseNixPath(*var); + if (var) + nixPath = parseNixPath(*var); } Strings EvalSettings::getDefaultNixPath() @@ -2570,7 +2596,8 @@ Strings EvalSettings::getDefaultNixPath() if (!evalSettings.restrictEval && !evalSettings.pureEval) { add(getHome() + "/.nix-defexpr/channels"); - add(settings.nixStateDir + "/profiles/per-user/root/channels/nixpkgs", "nixpkgs"); + add(settings.nixStateDir + "/profiles/per-user/root/channels/nixpkgs", + "nixpkgs"); add(settings.nixStateDir + "/profiles/per-user/root/channels"); } @@ -2581,5 +2608,4 @@ EvalSettings evalSettings; static GlobalConfig::Register rEvalSettings(&evalSettings); - } diff --git a/src/libexpr/eval.hh b/src/libexpr/eval.hh index 4eaa3c9b03e6..672b0dfb6209 100644 --- a/src/libexpr/eval.hh +++ b/src/libexpr/eval.hh @@ -15,17 +15,15 @@ namespace nix { - class Store; class EvalState; class StorePath; enum RepairFlag : bool; +typedef void (*PrimOpFun)(EvalState & state, const PosIdx pos, Value ** args, + Value & v); -typedef void (* PrimOpFun) (EvalState & state, const PosIdx pos, Value * * args, Value & v); - -struct PrimOp -{ +struct PrimOp { PrimOpFun fun; size_t arity; std::string name; @@ -34,45 +32,45 @@ struct PrimOp }; #if HAVE_BOEHMGC - typedef std::map, traceable_allocator > > ValMap; +typedef std::map, + traceable_allocator>> + ValMap; #else - typedef std::map ValMap; +typedef std::map ValMap; #endif -struct Env -{ +struct Env { Env * up; - unsigned short prevWith:14; // nr of levels up to next `with' environment - enum { Plain = 0, HasWithExpr, HasWithAttrs } type:2; + unsigned short prevWith : 14; // nr of levels up to next `with' environment + enum { Plain = 0, HasWithExpr, HasWithAttrs } type : 2; Value * values[0]; }; -void printEnvBindings(const EvalState &es, const Expr & expr, const Env & env); -void printEnvBindings(const SymbolTable & st, const StaticEnv & se, const Env & env, int lvl = 0); +void printEnvBindings(const EvalState & es, const Expr & expr, const Env & env); +void printEnvBindings(const SymbolTable & st, const StaticEnv & se, + const Env & env, int lvl = 0); -std::unique_ptr mapStaticEnvBindings(const SymbolTable & st, const StaticEnv & se, const Env & env); +std::unique_ptr mapStaticEnvBindings(const SymbolTable & st, + const StaticEnv & se, + const Env & env); void copyContext(const Value & v, PathSet & context); - /* Cache for calls to addToStore(); maps source paths to the store paths. */ typedef std::map SrcToStore; - -std::ostream & printValue(const EvalState & state, std::ostream & str, const Value & v); +std::ostream & printValue(const EvalState & state, std::ostream & str, + const Value & v); std::string printValue(const EvalState & state, const Value & v); -std::ostream & operator << (std::ostream & os, const ValueType t); - +std::ostream & operator<<(std::ostream & os, const ValueType t); typedef std::pair SearchPathElem; typedef std::list SearchPath; - /* Initialise the Boehm GC, if applicable. */ void initGC(); - struct RegexCache; std::shared_ptr makeRegexCache(); @@ -87,23 +85,19 @@ struct DebugTrace { void debugError(Error * e, Env & env, Expr & expr); -class EvalState : public std::enable_shared_from_this -{ -public: +class EvalState : public std::enable_shared_from_this { + public: SymbolTable symbols; PosTable positions; static inline std::string derivationNixPath = "//builtin/derivation.nix"; const Symbol sWith, sOutPath, sDrvPath, sType, sMeta, sName, sValue, - sSystem, sOverrides, sOutputs, sOutputName, sIgnoreNulls, - sFile, sLine, sColumn, sFunctor, sToString, - sRight, sWrong, sStructuredAttrs, sBuilder, sArgs, - sContentAddressed, sImpure, - sOutputHash, sOutputHashAlgo, sOutputHashMode, - sRecurseForDerivations, - sDescription, sSelf, sEpsilon, sStartSet, sOperator, sKey, sPath, - sPrefix, + sSystem, sOverrides, sOutputs, sOutputName, sIgnoreNulls, sFile, sLine, + sColumn, sFunctor, sToString, sRight, sWrong, sStructuredAttrs, + sBuilder, sArgs, sContentAddressed, sImpure, sOutputHash, + sOutputHashAlgo, sOutputHashMode, sRecurseForDerivations, sDescription, + sSelf, sEpsilon, sStartSet, sOperator, sKey, sPath, sPrefix, sOutputSpecified; Symbol sDerivationNix; @@ -127,25 +121,26 @@ public: RootValue vImportedDrvToDerivation = nullptr; /* Debugger */ - void (* debugRepl)(ref es, const ValMap & extraEnv); + void (*debugRepl)(ref es, const ValMap & extraEnv); bool debugStop; bool debugQuit; std::list debugTraces; - std::map> exprEnvs; + std::map> exprEnvs; const std::shared_ptr getStaticEnv(const Expr & expr) const { auto i = exprEnvs.find(&expr); if (i != exprEnvs.end()) return i->second; else - return std::shared_ptr();; + return std::shared_ptr(); + ; } void runDebugRepl(const Error * error, const Env & env, const Expr & expr); template - [[gnu::noinline, gnu::noreturn]] - void debugThrow(E && error, const Env & env, const Expr & expr) + [[gnu::noinline, gnu::noreturn]] void + debugThrow(E && error, const Env & env, const Expr & expr) { if (debugRepl) runDebugRepl(&error, env, expr); @@ -154,8 +149,7 @@ public: } template - [[gnu::noinline, gnu::noreturn]] - void debugThrowLastTrace(E && e) + [[gnu::noinline, gnu::noreturn]] void debugThrowLastTrace(E && e) { // Call this in the situation where Expr and Env are inaccessible. // The debugger will start in the last context that's in the @@ -168,13 +162,14 @@ public: throw std::move(e); } - -private: + private: SrcToStore srcToStore; /* A cache from path names to parse trees. */ #if HAVE_BOEHMGC - typedef std::map, traceable_allocator>> FileParseCache; + typedef std::map, + traceable_allocator>> + FileParseCache; #else typedef std::map FileParseCache; #endif @@ -182,7 +177,9 @@ private: /* A cache from path names to values. */ #if HAVE_BOEHMGC - typedef std::map, traceable_allocator>> FileEvalCache; + typedef std::map, + traceable_allocator>> + FileEvalCache; #else typedef std::map FileEvalCache; #endif @@ -206,12 +203,9 @@ private: std::shared_ptr env1AllocCache; #endif -public: - - EvalState( - const Strings & _searchPath, - ref store, - std::shared_ptr buildStore = nullptr); + public: + EvalState(const Strings & _searchPath, ref store, + std::shared_ptr buildStore = nullptr); ~EvalState(); void addToSearchPath(const std::string & s); @@ -245,10 +239,12 @@ public: /* Parse a Nix expression from the specified file. */ Expr * parseExprFromFile(const Path & path); - Expr * parseExprFromFile(const Path & path, std::shared_ptr & staticEnv); + Expr * parseExprFromFile(const Path & path, + std::shared_ptr & staticEnv); /* Parse a Nix expression from the specified string. */ - Expr * parseExprFromString(std::string s, const Path & basePath, std::shared_ptr & staticEnv); + Expr * parseExprFromString(std::string s, const Path & basePath, + std::shared_ptr & staticEnv); Expr * parseExprFromString(std::string s, const Path & basePath); Expr * parseStdin(); @@ -259,21 +255,19 @@ public: void evalFile(const Path & path, Value & v, bool mustBeTrivial = false); /* Like `evalFile`, but with an already parsed expression. */ - void cacheFile( - const Path & path, - const Path & resolvedPath, - Expr * e, - Value & v, - bool mustBeTrivial = false); + void cacheFile(const Path & path, const Path & resolvedPath, Expr * e, + Value & v, bool mustBeTrivial = false); void resetFileCache(); /* Look up a file in the search path. */ Path findFile(const std::string_view path); - Path findFile(SearchPath & searchPath, const std::string_view path, const PosIdx pos = noPos); + Path findFile(SearchPath & searchPath, const std::string_view path, + const PosIdx pos = noPos); /* If the specified search path element is a URI, download it. */ - std::pair resolveSearchPathElem(const SearchPathElem & elem); + std::pair + resolveSearchPathElem(const SearchPathElem & elem); /* Evaluate an expression to normal form, storing the result in value `v'. */ @@ -291,7 +285,7 @@ public: result. Otherwise, this is a no-op. */ inline void forceValue(Value & v, const PosIdx pos); - template + template inline void forceValue(Value & v, Callable getPos); /* Force a value, then recursively force list elements and @@ -305,99 +299,106 @@ public: void forceAttrs(Value & v, const PosIdx pos); - template + template inline void forceAttrs(Value & v, Callable getPos); inline void forceList(Value & v, const PosIdx pos); void forceFunction(Value & v, const PosIdx pos); // either lambda or primop std::string_view forceString(Value & v, const PosIdx pos = noPos); - std::string_view forceString(Value & v, PathSet & context, const PosIdx pos = noPos); + std::string_view forceString(Value & v, PathSet & context, + const PosIdx pos = noPos); std::string_view forceStringNoCtx(Value & v, const PosIdx pos = noPos); - [[gnu::noinline, gnu::noreturn]] - void throwEvalError(const PosIdx pos, const char * s); - [[gnu::noinline, gnu::noreturn]] - void throwEvalError(const PosIdx pos, const char * s, - Env & env, Expr & expr); - [[gnu::noinline, gnu::noreturn]] - void throwEvalError(const char * s, const std::string & s2); - [[gnu::noinline, gnu::noreturn]] - void throwEvalError(const PosIdx pos, const char * s, const std::string & s2); - [[gnu::noinline, gnu::noreturn]] - void throwEvalError(const char * s, const std::string & s2, - Env & env, Expr & expr); - [[gnu::noinline, gnu::noreturn]] - void throwEvalError(const PosIdx pos, const char * s, const std::string & s2, - Env & env, Expr & expr); - [[gnu::noinline, gnu::noreturn]] - void throwEvalError(const char * s, const std::string & s2, const std::string & s3, - Env & env, Expr & expr); - [[gnu::noinline, gnu::noreturn]] - void throwEvalError(const PosIdx pos, const char * s, const std::string & s2, const std::string & s3, - Env & env, Expr & expr); - [[gnu::noinline, gnu::noreturn]] - void throwEvalError(const PosIdx pos, const char * s, const std::string & s2, const std::string & s3); - [[gnu::noinline, gnu::noreturn]] - void throwEvalError(const char * s, const std::string & s2, const std::string & s3); - [[gnu::noinline, gnu::noreturn]] - void throwEvalError(const PosIdx pos, const Suggestions & suggestions, const char * s, const std::string & s2, - Env & env, Expr & expr); - [[gnu::noinline, gnu::noreturn]] - void throwEvalError(const PosIdx p1, const char * s, const Symbol sym, const PosIdx p2, - Env & env, Expr & expr); - - [[gnu::noinline, gnu::noreturn]] - void throwTypeError(const PosIdx pos, const char * s, const Value & v); - [[gnu::noinline, gnu::noreturn]] - void throwTypeError(const PosIdx pos, const char * s, const Value & v, - Env & env, Expr & expr); - [[gnu::noinline, gnu::noreturn]] - void throwTypeError(const PosIdx pos, const char * s); - [[gnu::noinline, gnu::noreturn]] - void throwTypeError(const PosIdx pos, const char * s, - Env & env, Expr & expr); - [[gnu::noinline, gnu::noreturn]] - void throwTypeError(const PosIdx pos, const char * s, const ExprLambda & fun, const Symbol s2, - Env & env, Expr & expr); - [[gnu::noinline, gnu::noreturn]] - void throwTypeError(const PosIdx pos, const Suggestions & suggestions, const char * s, const ExprLambda & fun, const Symbol s2, - Env & env, Expr & expr); - [[gnu::noinline, gnu::noreturn]] - void throwTypeError(const char * s, const Value & v, - Env & env, Expr & expr); - - [[gnu::noinline, gnu::noreturn]] - void throwAssertionError(const PosIdx pos, const char * s, const std::string & s1, - Env & env, Expr & expr); - - [[gnu::noinline, gnu::noreturn]] - void throwUndefinedVarError(const PosIdx pos, const char * s, const std::string & s1, - Env & env, Expr & expr); - - [[gnu::noinline, gnu::noreturn]] - void throwMissingArgumentError(const PosIdx pos, const char * s, const std::string & s1, - Env & env, Expr & expr); - - [[gnu::noinline]] - void addErrorTrace(Error & e, const char * s, const std::string & s2) const; - [[gnu::noinline]] - void addErrorTrace(Error & e, const PosIdx pos, const char * s, const std::string & s2) const; - -public: + [[gnu::noinline, gnu::noreturn]] void throwEvalError(const PosIdx pos, + const char * s); + [[gnu::noinline, gnu::noreturn]] void + throwEvalError(const PosIdx pos, const char * s, Env & env, Expr & expr); + [[gnu::noinline, gnu::noreturn]] void + throwEvalError(const char * s, const std::string & s2); + [[gnu::noinline, gnu::noreturn]] void + throwEvalError(const PosIdx pos, const char * s, const std::string & s2); + [[gnu::noinline, gnu::noreturn]] void throwEvalError(const char * s, + const std::string & s2, + Env & env, + Expr & expr); + [[gnu::noinline, gnu::noreturn]] void + throwEvalError(const PosIdx pos, const char * s, const std::string & s2, + Env & env, Expr & expr); + [[gnu::noinline, gnu::noreturn]] void + throwEvalError(const char * s, const std::string & s2, + const std::string & s3, Env & env, Expr & expr); + [[gnu::noinline, gnu::noreturn]] void + throwEvalError(const PosIdx pos, const char * s, const std::string & s2, + const std::string & s3, Env & env, Expr & expr); + [[gnu::noinline, gnu::noreturn]] void + throwEvalError(const PosIdx pos, const char * s, const std::string & s2, + const std::string & s3); + [[gnu::noinline, gnu::noreturn]] void + throwEvalError(const char * s, const std::string & s2, + const std::string & s3); + [[gnu::noinline, gnu::noreturn]] void + throwEvalError(const PosIdx pos, const Suggestions & suggestions, + const char * s, const std::string & s2, Env & env, + Expr & expr); + [[gnu::noinline, gnu::noreturn]] void + throwEvalError(const PosIdx p1, const char * s, const Symbol sym, + const PosIdx p2, Env & env, Expr & expr); + + [[gnu::noinline, gnu::noreturn]] void + throwTypeError(const PosIdx pos, const char * s, const Value & v); + [[gnu::noinline, gnu::noreturn]] void + throwTypeError(const PosIdx pos, const char * s, const Value & v, Env & env, + Expr & expr); + [[gnu::noinline, gnu::noreturn]] void throwTypeError(const PosIdx pos, + const char * s); + [[gnu::noinline, gnu::noreturn]] void + throwTypeError(const PosIdx pos, const char * s, Env & env, Expr & expr); + [[gnu::noinline, gnu::noreturn]] void + throwTypeError(const PosIdx pos, const char * s, const ExprLambda & fun, + const Symbol s2, Env & env, Expr & expr); + [[gnu::noinline, gnu::noreturn]] void + throwTypeError(const PosIdx pos, const Suggestions & suggestions, + const char * s, const ExprLambda & fun, const Symbol s2, + Env & env, Expr & expr); + [[gnu::noinline, gnu::noreturn]] void + throwTypeError(const char * s, const Value & v, Env & env, Expr & expr); + + [[gnu::noinline, gnu::noreturn]] void + throwAssertionError(const PosIdx pos, const char * s, + const std::string & s1, Env & env, Expr & expr); + + [[gnu::noinline, gnu::noreturn]] void + throwUndefinedVarError(const PosIdx pos, const char * s, + const std::string & s1, Env & env, Expr & expr); + + [[gnu::noinline, gnu::noreturn]] void + throwMissingArgumentError(const PosIdx pos, const char * s, + const std::string & s1, Env & env, Expr & expr); + + [[gnu::noinline]] void addErrorTrace(Error & e, const char * s, + const std::string & s2) const; + [[gnu::noinline]] void addErrorTrace(Error & e, const PosIdx pos, + const char * s, + const std::string & s2) const; + + public: /* Return true iff the value `v' denotes a derivation (i.e. a set with attribute `type = "derivation"'). */ bool isDerivation(Value & v); std::optional tryAttrsToString(const PosIdx pos, Value & v, - PathSet & context, bool coerceMore = false, bool copyToStore = true); + PathSet & context, + bool coerceMore = false, + bool copyToStore = true); /* String coercion. Converts strings, paths and derivations to a string. If `coerceMore' is set, also converts nulls, integers, booleans and lists to a string. If `copyToStore' is set, referenced paths are copied to the Nix store as a side effect. */ - BackedStringView coerceToString(const PosIdx pos, Value & v, PathSet & context, - bool coerceMore = false, bool copyToStore = true, - bool canonicalizePath = true); + BackedStringView coerceToString(const PosIdx pos, Value & v, + PathSet & context, bool coerceMore = false, + bool copyToStore = true, + bool canonicalizePath = true); std::string copyPathToStore(PathSet & context, const Path & path); @@ -409,8 +410,7 @@ public: /* Like coerceToPath, but the result must be a store path. */ StorePath coerceToStorePath(const PosIdx pos, Value & v, PathSet & context); -public: - + public: /* The base environment, containing the builtin functions and values. */ Env & baseEnv; @@ -418,8 +418,7 @@ public: /* The same, but used during parsing to resolve variables. */ std::shared_ptr staticBaseEnv; // !!! should be private -private: - + private: unsigned int baseEnvDispl = 0; void createBaseEnv(); @@ -428,17 +427,14 @@ private: void addConstant(const std::string & name, Value * v); - Value * addPrimOp(const std::string & name, - size_t arity, PrimOpFun primOp); + Value * addPrimOp(const std::string & name, size_t arity, PrimOpFun primOp); Value * addPrimOp(PrimOp && primOp); -public: - + public: Value & getBuiltin(const std::string & name); - struct Doc - { + struct Doc { Pos pos; std::optional name; size_t arity; @@ -448,19 +444,18 @@ public: std::optional getDoc(Value & v); -private: - + private: inline Value * lookupVar(Env * env, const ExprVar & var, bool noEval); friend struct ExprVar; friend struct ExprAttrs; friend struct ExprLet; - Expr * parse(char * text, size_t length, FileOrigin origin, const PathView path, - const PathView basePath, std::shared_ptr & staticEnv); - -public: + Expr * parse(char * text, size_t length, FileOrigin origin, + const PathView path, const PathView basePath, + std::shared_ptr & staticEnv); + public: /* Do a deep equality test between two values. That is, list elements and attributes are compared recursively. */ bool eqValues(Value & v1, Value & v2); @@ -468,7 +463,8 @@ public: bool isFunctor(Value & fun); // FIXME: use std::span - void callFunction(Value & fun, size_t nrArgs, Value * * args, Value & vRes, const PosIdx pos); + void callFunction(Value & fun, size_t nrArgs, Value ** args, Value & vRes, + const PosIdx pos); void callFunction(Value & fun, Value & arg, Value & vRes, const PosIdx pos) { @@ -498,7 +494,8 @@ public: void mkThunk_(Value & v, Expr * expr); void mkPos(Value & v, PosIdx pos); - void concatLists(Value & v, size_t nrLists, Value * * lists, const PosIdx pos); + void concatLists(Value & v, size_t nrLists, Value ** lists, + const PosIdx pos); /* Print statistics. */ void printStats(); @@ -508,8 +505,7 @@ public: */ [[nodiscard]] StringMap realiseContext(const PathSet & context); -private: - + private: unsigned long nrEnvs = 0; unsigned long nrValuesInEnvs = 0; unsigned long nrValues = 0; @@ -545,9 +541,12 @@ private: friend struct ExprFloat; friend struct ExprPath; friend struct ExprSelect; - friend void prim_getAttr(EvalState & state, const PosIdx pos, Value * * args, Value & v); - friend void prim_match(EvalState & state, const PosIdx pos, Value * * args, Value & v); - friend void prim_split(EvalState & state, const PosIdx pos, Value * * args, Value & v); + friend void prim_getAttr(EvalState & state, const PosIdx pos, Value ** args, + Value & v); + friend void prim_match(EvalState & state, const PosIdx pos, Value ** args, + Value & v); + friend void prim_split(EvalState & state, const PosIdx pos, Value ** args, + Value & v); friend struct Value; }; @@ -574,31 +573,30 @@ NixStringContextElem decodeContext(const Store & store, std::string_view s); /* If `path' refers to a directory, then append "/default.nix". */ Path resolveExprPath(Path path); -struct InvalidPathError : EvalError -{ +struct InvalidPathError : EvalError { Path path; InvalidPathError(const Path & path); #ifdef EXCEPTION_NEEDS_THROW_SPEC - ~InvalidPathError() throw () { }; + ~InvalidPathError() throw(){}; #endif }; -struct EvalSettings : Config -{ +struct EvalSettings : Config { EvalSettings(); static Strings getDefaultNixPath(); - Setting enableNativeCode{this, false, "allow-unsafe-native-code-during-evaluation", - "Whether builtin functions that allow executing native code should be enabled."}; + Setting enableNativeCode{this, false, + "allow-unsafe-native-code-during-evaluation", + "Whether builtin functions that allow " + "executing native code should be enabled."}; Setting nixPath{ this, getDefaultNixPath(), "nix-path", "List of directories to be searched for `<...>` file references."}; - Setting restrictEval{ - this, false, "restrict-eval", - R"( + Setting restrictEval{this, false, "restrict-eval", + R"( If set to `true`, the Nix evaluator will not allow access to any files outside of the Nix search path (as set via the `NIX_PATH` environment variable or the `-I` option), or to URIs outside of @@ -606,11 +604,12 @@ struct EvalSettings : Config )"}; Setting pureEval{this, false, "pure-eval", - "Whether to restrict file system and network access to files specified by cryptographic hash."}; + "Whether to restrict file system and network access " + "to files specified by cryptographic hash."}; - Setting enableImportFromDerivation{ - this, true, "allow-import-from-derivation", - R"( + Setting enableImportFromDerivation{this, true, + "allow-import-from-derivation", + R"( By default, Nix allows you to `import` from a derivation, allowing building at evaluation time. With this option set to false, Nix will throw an error when evaluating an expression that uses this feature, @@ -618,8 +617,10 @@ struct EvalSettings : Config builds to take place. )"}; - Setting allowedUris{this, {}, "allowed-uris", - R"( + Setting allowedUris{this, + {}, + "allowed-uris", + R"( A list of URI prefixes to which access is allowed in restricted evaluation mode. For example, when set to `https://github.com/NixOS`, builtin functions such as `fetchGit` are @@ -627,7 +628,7 @@ struct EvalSettings : Config )"}; Setting traceFunctionCalls{this, false, "trace-function-calls", - R"( + R"( If set to `true`, the Nix evaluator will trace every function call. Nix will print a log message at the "vomit" level for every function entrance and function exit. @@ -645,7 +646,7 @@ struct EvalSettings : Config )"}; Setting useEvalCache{this, true, "eval-cache", - "Whether to use the flake evaluation cache."}; + "Whether to use the flake evaluation cache."}; }; extern EvalSettings evalSettings; diff --git a/src/libexpr/flake/config.cc b/src/libexpr/flake/config.cc index 3e9d264b43ea..b9770b8b2249 100644 --- a/src/libexpr/flake/config.cc +++ b/src/libexpr/flake/config.cc @@ -9,15 +9,13 @@ namespace nix::flake { // setting name -> setting value -> allow or ignore. typedef std::map> TrustedList; -Path trustedListPath() -{ - return getDataDir() + "/nix/trusted-settings.json"; -} +Path trustedListPath() { return getDataDir() + "/nix/trusted-settings.json"; } static TrustedList readTrustedList() { auto path = trustedListPath(); - if (!pathExists(path)) return {}; + if (!pathExists(path)) + return {}; auto json = nlohmann::json::parse(readFile(path)); return json; } @@ -31,7 +29,8 @@ static void writeTrustedList(const TrustedList & trustedList) void ConfigFile::apply() { - std::set whitelist{"bash-prompt", "bash-prompt-prefix", "bash-prompt-suffix", "flake-registry"}; + std::set whitelist{"bash-prompt", "bash-prompt-prefix", + "bash-prompt-suffix", "flake-registry"}; for (auto & [name, value] : settings) { @@ -39,36 +38,51 @@ void ConfigFile::apply() // FIXME: Move into libutil/config.cc. std::string valueS; - if (auto* s = std::get_if(&value)) + if (auto * s = std::get_if(&value)) valueS = *s; - else if (auto* n = std::get_if(&value)) + else if (auto * n = std::get_if(&value)) valueS = fmt("%d", *n); - else if (auto* b = std::get_if>(&value)) + else if (auto * b = std::get_if>(&value)) valueS = b->t ? "true" : "false"; else if (auto ss = std::get_if>(&value)) valueS = concatStringsSep(" ", *ss); // FIXME: evil else assert(false); - if (!whitelist.count(baseName) && !nix::fetchSettings.acceptFlakeConfig) { + if (!whitelist.count(baseName) && + !nix::fetchSettings.acceptFlakeConfig) { bool trusted = false; auto trustedList = readTrustedList(); auto tlname = get(trustedList, name); if (auto saved = tlname ? get(*tlname, valueS) : nullptr) { trusted = *saved; - warn("Using saved setting for '%s = %s' from ~/.local/share/nix/trusted-settings.json.", name,valueS); + warn("Using saved setting for '%s = %s' from " + "~/.local/share/nix/trusted-settings.json.", + name, valueS); } else { // FIXME: filter ANSI escapes, newlines, \r, etc. - if (std::tolower(logger->ask(fmt("do you want to allow configuration setting '%s' to be set to '" ANSI_RED "%s" ANSI_NORMAL "' (y/N)?", name, valueS)).value_or('n')) == 'y') { + if (std::tolower( + logger + ->ask(fmt("do you want to allow configuration " + "setting '%s' to be set to '" ANSI_RED + "%s" ANSI_NORMAL "' (y/N)?", + name, valueS)) + .value_or('n')) == 'y') { trusted = true; } - if (std::tolower(logger->ask(fmt("do you want to permanently mark this value as %s (y/N)?", trusted ? "trusted": "untrusted" )).value_or('n')) == 'y') { + if (std::tolower( + logger + ->ask(fmt("do you want to permanently mark this " + "value as %s (y/N)?", + trusted ? "trusted" : "untrusted")) + .value_or('n')) == 'y') { trustedList[name][valueS] = trusted; writeTrustedList(trustedList); } } if (!trusted) { - warn("ignoring untrusted flake configuration setting '%s'", name); + warn("ignoring untrusted flake configuration setting '%s'", + name); continue; } } diff --git a/src/libexpr/flake/flake.cc b/src/libexpr/flake/flake.cc index 920726b73b00..984b3161c6cc 100644 --- a/src/libexpr/flake/flake.cc +++ b/src/libexpr/flake/flake.cc @@ -17,15 +17,14 @@ namespace flake { typedef std::pair FetchedFlake; typedef std::vector> FlakeCache; -static std::optional lookupInFlakeCache( - const FlakeCache & flakeCache, - const FlakeRef & flakeRef) +static std::optional +lookupInFlakeCache(const FlakeCache & flakeCache, const FlakeRef & flakeRef) { // FIXME: inefficient. for (auto & i : flakeCache) { if (flakeRef == i.first) { - debug("mapping '%s' to previously seen input '%s' -> '%s", - flakeRef, i.first, i.second.second); + debug("mapping '%s' to previously seen input '%s' -> '%s", flakeRef, + i.first, i.second.second); return i.second; } } @@ -33,11 +32,9 @@ static std::optional lookupInFlakeCache( return std::nullopt; } -static std::tuple fetchOrSubstituteTree( - EvalState & state, - const FlakeRef & originalRef, - bool allowLookup, - FlakeCache & flakeCache) +static std::tuple +fetchOrSubstituteTree(EvalState & state, const FlakeRef & originalRef, + bool allowLookup, FlakeCache & flakeCache) { auto fetched = lookupInFlakeCache(flakeCache, originalRef); FlakeRef resolvedRef = originalRef; @@ -48,13 +45,16 @@ static std::tuple fetchOrSubstituteTree( } else { if (allowLookup) { resolvedRef = originalRef.resolve(state.store); - auto fetchedResolved = lookupInFlakeCache(flakeCache, originalRef); - if (!fetchedResolved) fetchedResolved.emplace(resolvedRef.fetchTree(state.store)); + auto fetchedResolved = + lookupInFlakeCache(flakeCache, originalRef); + if (!fetchedResolved) + fetchedResolved.emplace(resolvedRef.fetchTree(state.store)); flakeCache.push_back({resolvedRef, *fetchedResolved}); fetched.emplace(*fetchedResolved); - } - else { - throw Error("'%s' is an indirect flake reference, but registry lookups are not allowed", originalRef); + } else { + throw Error("'%s' is an indirect flake reference, but registry " + "lookups are not allowed", + originalRef); } } flakeCache.push_back({originalRef, *fetched}); @@ -63,38 +63,41 @@ static std::tuple fetchOrSubstituteTree( auto [tree, lockedRef] = *fetched; debug("got tree '%s' from '%s'", - state.store->printStorePath(tree.storePath), lockedRef); + state.store->printStorePath(tree.storePath), lockedRef); state.allowPath(tree.storePath); - assert(!originalRef.input.getNarHash() || tree.storePath == originalRef.input.computeStorePath(*state.store)); + assert(!originalRef.input.getNarHash() || + tree.storePath == originalRef.input.computeStorePath(*state.store)); return {std::move(tree), resolvedRef, lockedRef}; } -static void forceTrivialValue(EvalState & state, Value & value, const PosIdx pos) +static void forceTrivialValue(EvalState & state, Value & value, + const PosIdx pos) { if (value.isThunk() && value.isTrivial()) state.forceValue(value, pos); } - -static void expectType(EvalState & state, ValueType type, - Value & value, const PosIdx pos) +static void expectType(EvalState & state, ValueType type, Value & value, + const PosIdx pos) { forceTrivialValue(state, value, pos); if (value.type() != type) - throw Error("expected %s but got %s at %s", - showType(type), showType(value.type()), state.positions[pos]); + throw Error("expected %s but got %s at %s", showType(type), + showType(value.type()), state.positions[pos]); } -static std::map parseFlakeInputs( - EvalState & state, Value * value, const PosIdx pos, - const std::optional & baseDir, InputPath lockRootPath); +static std::map +parseFlakeInputs(EvalState & state, Value * value, const PosIdx pos, + const std::optional & baseDir, InputPath lockRootPath); static FlakeInput parseFlakeInput(EvalState & state, - const std::string & inputName, Value * value, const PosIdx pos, - const std::optional & baseDir, InputPath lockRootPath) + const std::string & inputName, Value * value, + const PosIdx pos, + const std::optional & baseDir, + InputPath lockRootPath) { expectType(state, nAttrs, *value, pos); @@ -118,26 +121,33 @@ static FlakeInput parseFlakeInput(EvalState & state, expectType(state, nBool, *attr.value, attr.pos); input.isFlake = attr.value->boolean; } else if (attr.name == sInputs) { - input.overrides = parseFlakeInputs(state, attr.value, attr.pos, baseDir, lockRootPath); + input.overrides = parseFlakeInputs(state, attr.value, attr.pos, + baseDir, lockRootPath); } else if (attr.name == sFollows) { expectType(state, nString, *attr.value, attr.pos); auto follows(parseInputPath(attr.value->string.s)); - follows.insert(follows.begin(), lockRootPath.begin(), lockRootPath.end()); + follows.insert(follows.begin(), lockRootPath.begin(), + lockRootPath.end()); input.follows = follows; } else { switch (attr.value->type()) { - case nString: - attrs.emplace(state.symbols[attr.name], attr.value->string.s); - break; - case nBool: - attrs.emplace(state.symbols[attr.name], Explicit { attr.value->boolean }); - break; - case nInt: - attrs.emplace(state.symbols[attr.name], (long unsigned int)attr.value->integer); - break; - default: - throw TypeError("flake input attribute '%s' is %s while a string, Boolean, or integer is expected", - state.symbols[attr.name], showType(*attr.value)); + case nString: + attrs.emplace(state.symbols[attr.name], + attr.value->string.s); + break; + case nBool: + attrs.emplace(state.symbols[attr.name], + Explicit{attr.value->boolean}); + break; + case nInt: + attrs.emplace(state.symbols[attr.name], + (long unsigned int) attr.value->integer); + break; + default: + throw TypeError("flake input attribute '%s' is %s while a " + "string, Boolean, or integer is expected", + state.symbols[attr.name], + showType(*attr.value)); } } } catch (Error & e) { @@ -158,20 +168,22 @@ static FlakeInput parseFlakeInput(EvalState & state, else { attrs.erase("url"); if (!attrs.empty()) - throw Error("unexpected flake input attribute '%s', at %s", attrs.begin()->first, state.positions[pos]); + throw Error("unexpected flake input attribute '%s', at %s", + attrs.begin()->first, state.positions[pos]); if (url) input.ref = parseFlakeRef(*url, baseDir, true, input.isFlake); } if (!input.follows && !input.ref) - input.ref = FlakeRef::fromAttrs({{"type", "indirect"}, {"id", inputName}}); + input.ref = + FlakeRef::fromAttrs({{"type", "indirect"}, {"id", inputName}}); return input; } -static std::map parseFlakeInputs( - EvalState & state, Value * value, const PosIdx pos, - const std::optional & baseDir, InputPath lockRootPath) +static std::map +parseFlakeInputs(EvalState & state, Value * value, const PosIdx pos, + const std::optional & baseDir, InputPath lockRootPath) { std::map inputs; @@ -179,48 +191,46 @@ static std::map parseFlakeInputs( for (nix::Attr & inputAttr : *(*value).attrs) { inputs.emplace(state.symbols[inputAttr.name], - parseFlakeInput(state, - state.symbols[inputAttr.name], - inputAttr.value, - inputAttr.pos, - baseDir, - lockRootPath)); + parseFlakeInput(state, state.symbols[inputAttr.name], + inputAttr.value, inputAttr.pos, baseDir, + lockRootPath)); } return inputs; } -static Flake getFlake( - EvalState & state, - const FlakeRef & originalRef, - bool allowLookup, - FlakeCache & flakeCache, - InputPath lockRootPath) +static Flake getFlake(EvalState & state, const FlakeRef & originalRef, + bool allowLookup, FlakeCache & flakeCache, + InputPath lockRootPath) { - auto [sourceInfo, resolvedRef, lockedRef] = fetchOrSubstituteTree( - state, originalRef, allowLookup, flakeCache); + auto [sourceInfo, resolvedRef, lockedRef] = + fetchOrSubstituteTree(state, originalRef, allowLookup, flakeCache); // Guard against symlink attacks. - auto flakeDir = canonPath(sourceInfo.actualPath + "/" + lockedRef.subdir, true); + auto flakeDir = + canonPath(sourceInfo.actualPath + "/" + lockedRef.subdir, true); auto flakeFile = canonPath(flakeDir + "/flake.nix", true); if (!isInDir(flakeFile, sourceInfo.actualPath)) throw Error("'flake.nix' file of flake '%s' escapes from '%s'", - lockedRef, state.store->printStorePath(sourceInfo.storePath)); + lockedRef, + state.store->printStorePath(sourceInfo.storePath)); - Flake flake { - .originalRef = originalRef, - .resolvedRef = resolvedRef, - .lockedRef = lockedRef, - .sourceInfo = std::make_shared(std::move(sourceInfo)) - }; + Flake flake{.originalRef = originalRef, + .resolvedRef = resolvedRef, + .lockedRef = lockedRef, + .sourceInfo = + std::make_shared(std::move(sourceInfo))}; if (!pathExists(flakeFile)) - throw Error("source tree referenced by '%s' does not contain a '%s/flake.nix' file", lockedRef, lockedRef.subdir); + throw Error("source tree referenced by '%s' does not contain a " + "'%s/flake.nix' file", + lockedRef, lockedRef.subdir); Value vInfo; state.evalFile(flakeFile, vInfo, true); // FIXME: symlink attack - expectType(state, nAttrs, vInfo, state.positions.add({flakeFile, foFile}, 0, 0)); + expectType(state, nAttrs, vInfo, + state.positions.add({flakeFile, foFile}, 0, 0)); if (auto description = vInfo.attrs->get(state.sDescription)) { expectType(state, nString, *description->value, description->pos); @@ -230,19 +240,22 @@ static Flake getFlake( auto sInputs = state.symbols.create("inputs"); if (auto inputs = vInfo.attrs->get(sInputs)) - flake.inputs = parseFlakeInputs(state, inputs->value, inputs->pos, flakeDir, lockRootPath); + flake.inputs = parseFlakeInputs(state, inputs->value, inputs->pos, + flakeDir, lockRootPath); auto sOutputs = state.symbols.create("outputs"); if (auto outputs = vInfo.attrs->get(sOutputs)) { expectType(state, nFunction, *outputs->value, outputs->pos); - if (outputs->value->isLambda() && outputs->value->lambda.fun->hasFormals()) { + if (outputs->value->isLambda() && + outputs->value->lambda.fun->hasFormals()) { for (auto & formal : outputs->value->lambda.fun->formals->formals) { if (formal.name != state.sSelf) - flake.inputs.emplace(state.symbols[formal.name], FlakeInput { - .ref = parseFlakeRef(state.symbols[formal.name]) - }); + flake.inputs.emplace( + state.symbols[formal.name], + FlakeInput{ + .ref = parseFlakeRef(state.symbols[formal.name])}); } } @@ -259,55 +272,63 @@ static Flake getFlake( if (setting.value->type() == nString) flake.config.settings.emplace( state.symbols[setting.name], - std::string(state.forceStringNoCtx(*setting.value, setting.pos))); + std::string( + state.forceStringNoCtx(*setting.value, setting.pos))); else if (setting.value->type() == nPath) { PathSet emptyContext = {}; flake.config.settings.emplace( state.symbols[setting.name], - state.coerceToString(setting.pos, *setting.value, emptyContext, false, true, true) .toOwned()); - } - else if (setting.value->type() == nInt) + state + .coerceToString(setting.pos, *setting.value, + emptyContext, false, true, true) + .toOwned()); + } else if (setting.value->type() == nInt) flake.config.settings.emplace( state.symbols[setting.name], state.forceInt(*setting.value, setting.pos)); else if (setting.value->type() == nBool) flake.config.settings.emplace( state.symbols[setting.name], - Explicit { state.forceBool(*setting.value, setting.pos) }); + Explicit{ + state.forceBool(*setting.value, setting.pos)}); else if (setting.value->type() == nList) { std::vector ss; for (auto elem : setting.value->listItems()) { if (elem->type() != nString) - throw TypeError("list element in flake configuration setting '%s' is %s while a string is expected", - state.symbols[setting.name], showType(*setting.value)); + throw TypeError( + "list element in flake configuration setting '%s' " + "is %s while a string is expected", + state.symbols[setting.name], + showType(*setting.value)); ss.emplace_back(state.forceStringNoCtx(*elem, setting.pos)); } flake.config.settings.emplace(state.symbols[setting.name], ss); - } - else + } else throw TypeError("flake configuration setting '%s' is %s", - state.symbols[setting.name], showType(*setting.value)); + state.symbols[setting.name], + showType(*setting.value)); } } for (auto & attr : *vInfo.attrs) { - if (attr.name != state.sDescription && - attr.name != sInputs && - attr.name != sOutputs && - attr.name != sNixConfig) + if (attr.name != state.sDescription && attr.name != sInputs && + attr.name != sOutputs && attr.name != sNixConfig) throw Error("flake '%s' has an unsupported attribute '%s', at %s", - lockedRef, state.symbols[attr.name], state.positions[attr.pos]); + lockedRef, state.symbols[attr.name], + state.positions[attr.pos]); } return flake; } -Flake getFlake(EvalState & state, const FlakeRef & originalRef, bool allowLookup, FlakeCache & flakeCache) +Flake getFlake(EvalState & state, const FlakeRef & originalRef, + bool allowLookup, FlakeCache & flakeCache) { return getFlake(state, originalRef, allowLookup, flakeCache, {}); } -Flake getFlake(EvalState & state, const FlakeRef & originalRef, bool allowLookup) +Flake getFlake(EvalState & state, const FlakeRef & originalRef, + bool allowLookup) { FlakeCache flakeCache; return getFlake(state, originalRef, allowLookup, flakeCache); @@ -315,16 +336,15 @@ Flake getFlake(EvalState & state, const FlakeRef & originalRef, bool allowLookup /* Compute an in-memory lock file for the specified top-level flake, and optionally write it to file, if the flake is writable. */ -LockedFlake lockFlake( - EvalState & state, - const FlakeRef & topRef, - const LockFlags & lockFlags) +LockedFlake lockFlake(EvalState & state, const FlakeRef & topRef, + const LockFlags & lockFlags) { settings.requireExperimentalFeature(Xp::Flakes); FlakeCache flakeCache; - auto useRegistries = lockFlags.useRegistries.value_or(fetchSettings.useRegistries); + auto useRegistries = + lockFlags.useRegistries.value_or(fetchSettings.useRegistries); auto flake = getFlake(state, topRef, useRegistries, flakeCache); @@ -336,8 +356,9 @@ LockedFlake lockFlake( try { // FIXME: symlink attack - auto oldLockFile = LockFile::read( - flake.sourceInfo->actualPath + "/" + flake.lockedRef.subdir + "/flake.lock"); + auto oldLockFile = + LockFile::read(flake.sourceInfo->actualPath + "/" + + flake.lockedRef.subdir + "/flake.lock"); debug("old lock file: %s", oldLockFile); @@ -346,32 +367,27 @@ LockedFlake lockFlake( std::set overridesUsed, updatesUsed; for (auto & i : lockFlags.inputOverrides) - overrides.insert_or_assign(i.first, FlakeInput { .ref = i.second }); + overrides.insert_or_assign(i.first, FlakeInput{.ref = i.second}); LockFile newLockFile; std::vector parents; std::function node, + const FlakeInputs & flakeInputs, std::shared_ptr node, const InputPath & inputPathPrefix, - std::shared_ptr oldNode, - const InputPath & lockRootPath, - const Path & parentPath, - bool trustLock)> + std::shared_ptr oldNode, const InputPath & lockRootPath, + const Path & parentPath, bool trustLock)> computeLocks; - computeLocks = [&]( - const FlakeInputs & flakeInputs, - std::shared_ptr node, - const InputPath & inputPathPrefix, - std::shared_ptr oldNode, - const InputPath & lockRootPath, - const Path & parentPath, - bool trustLock) - { - debug("computing lock file node '%s'", printInputPath(inputPathPrefix)); + computeLocks = [&](const FlakeInputs & flakeInputs, + std::shared_ptr node, + const InputPath & inputPathPrefix, + std::shared_ptr oldNode, + const InputPath & lockRootPath, + const Path & parentPath, bool trustLock) { + debug("computing lock file node '%s'", + printInputPath(inputPathPrefix)); /* Get the overrides (i.e. attributes of the form 'inputs.nixops.inputs.nixpkgs.url = ...'). */ @@ -412,9 +428,11 @@ LockedFlake lockFlake( if (input.follows) { InputPath target; - target.insert(target.end(), input.follows->begin(), input.follows->end()); + target.insert(target.end(), input.follows->begin(), + input.follows->end()); - debug("input '%s' follows '%s'", inputPathS, printInputPath(target)); + debug("input '%s' follows '%s'", inputPathS, + printInputPath(target)); node->inputs.insert_or_assign(id, target); continue; } @@ -432,17 +450,16 @@ LockedFlake lockFlake( if (auto oldLock3 = std::get_if<0>(&*oldLock2)) oldLock = *oldLock3; - if (oldLock - && oldLock->originalRef == *input.ref - && !hasOverride) - { + if (oldLock && oldLock->originalRef == *input.ref && + !hasOverride) { debug("keeping existing input '%s'", inputPathS); /* Copy the input from the old lock since its flakeref didn't change and there is no override from a higher level flake. */ auto childNode = std::make_shared( - oldLock->lockedRef, oldLock->originalRef, oldLock->isFlake); + oldLock->lockedRef, oldLock->originalRef, + oldLock->isFlake); node->inputs.insert_or_assign(id, childNode); @@ -452,9 +469,10 @@ LockedFlake lockFlake( auto lb = lockFlags.inputUpdates.lower_bound(inputPath); auto mustRefetch = - lb != lockFlags.inputUpdates.end() - && lb->size() > inputPath.size() - && std::equal(inputPath.begin(), inputPath.end(), lb->begin()); + lb != lockFlags.inputUpdates.end() && + lb->size() > inputPath.size() && + std::equal(inputPath.begin(), inputPath.end(), + lb->begin()); FlakeInputs fakeInputs; @@ -464,54 +482,73 @@ LockedFlake lockFlake( inputs of this flake, so we need to check those. */ for (auto & i : oldLock->inputs) { - if (auto lockedNode = std::get_if<0>(&i.second)) { - fakeInputs.emplace(i.first, FlakeInput { - .ref = (*lockedNode)->originalRef, - .isFlake = (*lockedNode)->isFlake, - }); - } else if (auto follows = std::get_if<1>(&i.second)) { - if (! trustLock) { - // It is possible that the flake has changed, - // so we must confirm all the follows that are in the lockfile are also in the flake. + if (auto lockedNode = + std::get_if<0>(&i.second)) { + fakeInputs.emplace( + i.first, + FlakeInput{ + .ref = (*lockedNode)->originalRef, + .isFlake = (*lockedNode)->isFlake, + }); + } else if (auto follows = + std::get_if<1>(&i.second)) { + if (!trustLock) { + // It is possible that the flake has + // changed, so we must confirm all the + // follows that are in the lockfile are + // also in the flake. auto overridePath(inputPath); overridePath.push_back(i.first); auto o = overrides.find(overridePath); - // If the override disappeared, we have to refetch the flake, - // since some of the inputs may not be present in the lockfile. + // If the override disappeared, we have + // to refetch the flake, since some of + // the inputs may not be present in the + // lockfile. if (o == overrides.end()) { mustRefetch = true; - // There's no point populating the rest of the fake inputs, - // since we'll refetch the flake anyways. + // There's no point populating the + // rest of the fake inputs, since + // we'll refetch the flake anyways. break; } } auto absoluteFollows(lockRootPath); - absoluteFollows.insert(absoluteFollows.end(), follows->begin(), follows->end()); - fakeInputs.emplace(i.first, FlakeInput { - .follows = absoluteFollows, - }); + absoluteFollows.insert( + absoluteFollows.end(), follows->begin(), + follows->end()); + fakeInputs.emplace( + i.first, FlakeInput{ + .follows = absoluteFollows, + }); } } } auto localPath(parentPath); // If this input is a path, recurse it down. - // This allows us to resolve path inputs relative to the current flake. + // This allows us to resolve path inputs relative to the + // current flake. if ((*input.ref).input.getType() == "path") - localPath = absPath(*input.ref->input.getSourcePath(), parentPath); + localPath = absPath( + *input.ref->input.getSourcePath(), parentPath); computeLocks( - mustRefetch - ? getFlake(state, oldLock->lockedRef, false, flakeCache, inputPath).inputs - : fakeInputs, - childNode, inputPath, oldLock, lockRootPath, parentPath, !mustRefetch); + mustRefetch ? getFlake(state, oldLock->lockedRef, + false, flakeCache, inputPath) + .inputs + : fakeInputs, + childNode, inputPath, oldLock, lockRootPath, + parentPath, !mustRefetch); } else { /* We need to create a new lock file entry. So fetch this input. */ debug("creating new input '%s'", inputPathS); - if (!lockFlags.allowMutable && !input.ref->input.isLocked()) - throw Error("cannot update flake input '%s' in pure mode", inputPathS); + if (!lockFlags.allowMutable && + !input.ref->input.isLocked()) + throw Error( + "cannot update flake input '%s' in pure mode", + inputPathS); /* Note: in case of an --override-input, we use the *original* ref (input2.ref) for the @@ -527,20 +564,28 @@ LockedFlake lockFlake( FlakeRef localRef = *input.ref; // If this input is a path, recurse it down. - // This allows us to resolve path inputs relative to the current flake. + // This allows us to resolve path inputs relative to + // the current flake. if (localRef.input.getType() == "path") - localPath = absPath(*input.ref->input.getSourcePath(), parentPath); + localPath = + absPath(*input.ref->input.getSourcePath(), + parentPath); - auto inputFlake = getFlake(state, localRef, useRegistries, flakeCache, inputPath); + auto inputFlake = + getFlake(state, localRef, useRegistries, + flakeCache, inputPath); - auto childNode = std::make_shared(inputFlake.lockedRef, ref); + auto childNode = std::make_shared( + inputFlake.lockedRef, ref); node->inputs.insert_or_assign(id, childNode); /* Guard against circular flake imports. */ for (auto & parent : parents) if (parent == *input.ref) - throw Error("found circular import of flake '%s'", parent); + throw Error( + "found circular import of flake '%s'", + parent); parents.push_back(*input.ref); Finally cleanup([&]() { parents.pop_back(); }); @@ -551,42 +596,54 @@ LockedFlake lockFlake( computeLocks( inputFlake.inputs, childNode, inputPath, oldLock - ? std::dynamic_pointer_cast(oldLock) - : LockFile::read( - inputFlake.sourceInfo->actualPath + "/" + inputFlake.lockedRef.subdir + "/flake.lock").root, - oldLock ? lockRootPath : inputPath, localPath, false); + ? std::dynamic_pointer_cast( + oldLock) + : LockFile::read( + inputFlake.sourceInfo->actualPath + + "/" + inputFlake.lockedRef.subdir + + "/flake.lock") + .root, + oldLock ? lockRootPath : inputPath, localPath, + false); } else { - auto [sourceInfo, resolvedRef, lockedRef] = fetchOrSubstituteTree( - state, *input.ref, useRegistries, flakeCache); - node->inputs.insert_or_assign(id, - std::make_shared(lockedRef, ref, false)); + auto [sourceInfo, resolvedRef, lockedRef] = + fetchOrSubstituteTree(state, *input.ref, + useRegistries, + flakeCache); + node->inputs.insert_or_assign( + id, std::make_shared(lockedRef, ref, + false)); } } } catch (Error & e) { - e.addTrace({}, "while updating the flake input '%s'", inputPathS); + e.addTrace({}, "while updating the flake input '%s'", + inputPathS); throw; } } }; // Bring in the current ref for relative path resolution if we have it - auto parentPath = canonPath(flake.sourceInfo->actualPath + "/" + flake.lockedRef.subdir, true); + auto parentPath = canonPath( + flake.sourceInfo->actualPath + "/" + flake.lockedRef.subdir, true); - computeLocks( - flake.inputs, newLockFile.root, {}, - lockFlags.recreateLockFile ? nullptr : oldLockFile.root, {}, parentPath, false); + computeLocks(flake.inputs, newLockFile.root, {}, + lockFlags.recreateLockFile ? nullptr : oldLockFile.root, + {}, parentPath, false); for (auto & i : lockFlags.inputOverrides) if (!overridesUsed.count(i.first)) - warn("the flag '--override-input %s %s' does not match any input", - printInputPath(i.first), i.second); + warn("the flag '--override-input %s %s' does not match any " + "input", + printInputPath(i.first), i.second); for (auto & i : lockFlags.inputUpdates) if (!updatesUsed.count(i)) - warn("the flag '--update-input %s' does not match any input", printInputPath(i)); + warn("the flag '--update-input %s' does not match any input", + printInputPath(i)); /* Check 'follows' inputs. */ newLockFile.check(); @@ -602,12 +659,19 @@ LockedFlake lockFlake( if (auto sourcePath = topRef.input.getSourcePath()) { if (!newLockFile.isImmutable()) { if (fetchSettings.warnDirty) - warn("will not write lock file of flake '%s' because it has a mutable input", topRef); + warn("will not write lock file of flake '%s' " + "because it has a mutable input", + topRef); } else { if (!lockFlags.updateLockFile) - throw Error("flake '%s' requires lock file changes but they're not allowed due to '--no-update-lock-file'", topRef); + throw Error("flake '%s' requires lock file changes " + "but they're not allowed due to " + "'--no-update-lock-file'", + topRef); - auto relPath = (topRef.subdir == "" ? "" : topRef.subdir + "/") + "flake.lock"; + auto relPath = + (topRef.subdir == "" ? "" : topRef.subdir + "/") + + "flake.lock"; auto path = *sourcePath + "/" + relPath; @@ -631,7 +695,8 @@ LockedFlake lockFlake( cm = fetchSettings.commitLockFileSummary.get(); if (cm == "") { - cm = fmt("%s: %s", relPath, lockFileExists ? "Update" : "Add"); + cm = fmt("%s: %s", relPath, + lockFileExists ? "Update" : "Add"); } cm += "\n\nFlake lock file updates:\n\n"; @@ -640,7 +705,8 @@ LockedFlake lockFlake( } topRef.input.markChangedFile( - (topRef.subdir == "" ? "" : topRef.subdir + "/") + "flake.lock", + (topRef.subdir == "" ? "" : topRef.subdir + "/") + + "flake.lock", commitMessage); /* Rewriting the lockfile changed the top-level @@ -648,40 +714,49 @@ LockedFlake lockFlake( also just clear the 'rev' field... */ auto prevLockedRef = flake.lockedRef; FlakeCache dummyCache; - flake = getFlake(state, topRef, useRegistries, dummyCache); + flake = + getFlake(state, topRef, useRegistries, dummyCache); if (lockFlags.commitLockFile && flake.lockedRef.input.getRev() && - prevLockedRef.input.getRev() != flake.lockedRef.input.getRev()) - warn("committed new revision '%s'", flake.lockedRef.input.getRev()->gitRev()); + prevLockedRef.input.getRev() != + flake.lockedRef.input.getRev()) + warn("committed new revision '%s'", + flake.lockedRef.input.getRev()->gitRev()); /* Make sure that we picked up the change, i.e. the tree should usually be dirty now. Corner case: we could have reverted from a dirty to a clean tree! */ - if (flake.lockedRef.input == prevLockedRef.input - && !flake.lockedRef.input.isLocked()) - throw Error("'%s' did not change after I updated its 'flake.lock' file; is 'flake.lock' under version control?", flake.originalRef); + if (flake.lockedRef.input == prevLockedRef.input && + !flake.lockedRef.input.isLocked()) + throw Error("'%s' did not change after I updated " + "its 'flake.lock' file; is " + "'flake.lock' under version control?", + flake.originalRef); } } else - throw Error("cannot write modified lock file of flake '%s' (use '--no-write-lock-file' to ignore)", topRef); + throw Error("cannot write modified lock file of flake '%s' " + "(use '--no-write-lock-file' to ignore)", + topRef); } else { - warn("not writing modified lock file of flake '%s':\n%s", topRef, chomp(diff)); + warn("not writing modified lock file of flake '%s':\n%s", + topRef, chomp(diff)); flake.forceDirty = true; } } - return LockedFlake { .flake = std::move(flake), .lockFile = std::move(newLockFile) }; + return LockedFlake{.flake = std::move(flake), + .lockFile = std::move(newLockFile)}; } catch (Error & e) { - e.addTrace({}, "while updating the lock file of flake '%s'", flake.lockedRef.to_string()); + e.addTrace({}, "while updating the lock file of flake '%s'", + flake.lockedRef.to_string()); throw; } } -void callFlake(EvalState & state, - const LockedFlake & lockedFlake, - Value & vRes) +void callFlake(EvalState & state, const LockedFlake & lockedFlake, Value & vRes) { auto vLocks = state.allocValue(); auto vRootSrc = state.allocValue(); @@ -691,21 +766,18 @@ void callFlake(EvalState & state, vLocks->mkString(lockedFlake.lockFile.to_string()); - emitTreeAttrs( - state, - *lockedFlake.flake.sourceInfo, - lockedFlake.flake.lockedRef.input, - *vRootSrc, - false, - lockedFlake.flake.forceDirty); + emitTreeAttrs(state, *lockedFlake.flake.sourceInfo, + lockedFlake.flake.lockedRef.input, *vRootSrc, false, + lockedFlake.flake.forceDirty); vRootSubdir->mkString(lockedFlake.flake.lockedRef.subdir); if (!state.vCallFlake) { state.vCallFlake = allocRootValue(state.allocValue()); state.eval(state.parseExprFromString( - #include "call-flake.nix.gen.hh" - , "/"), **state.vCallFlake); +#include "call-flake.nix.gen.hh" + , "/"), + **state.vCallFlake); } state.callFunction(**state.vCallFlake, *vLocks, *vTmp1, noPos); @@ -713,26 +785,30 @@ void callFlake(EvalState & state, state.callFunction(*vTmp2, *vRootSubdir, vRes, noPos); } -static void prim_getFlake(EvalState & state, const PosIdx pos, Value * * args, Value & v) +static void prim_getFlake(EvalState & state, const PosIdx pos, Value ** args, + Value & v) { std::string flakeRefS(state.forceStringNoCtx(*args[0], pos)); auto flakeRef = parseFlakeRef(flakeRefS, {}, true); if (evalSettings.pureEval && !flakeRef.input.isLocked()) - throw Error("cannot call 'getFlake' on unlocked flake reference '%s', at %s (use --impure to override)", flakeRefS, state.positions[pos]); + throw Error("cannot call 'getFlake' on unlocked flake reference '%s', " + "at %s (use --impure to override)", + flakeRefS, state.positions[pos]); callFlake(state, - lockFlake(state, flakeRef, - LockFlags { - .updateLockFile = false, - .writeLockFile = false, - .useRegistries = !evalSettings.pureEval && fetchSettings.useRegistries, - .allowMutable = !evalSettings.pureEval, - }), - v); + lockFlake(state, flakeRef, + LockFlags{ + .updateLockFile = false, + .writeLockFile = false, + .useRegistries = !evalSettings.pureEval && + fetchSettings.useRegistries, + .allowMutable = !evalSettings.pureEval, + }), + v); } static RegisterPrimOp r2({ - .name = "__getFlake", + .name = "__getFlake", .args = {"args"}, .doc = R"( Fetch a flake from a flake reference, and return its output attributes and some metadata. For example: @@ -763,15 +839,14 @@ Fingerprint LockedFlake::getFingerprint() const // FIXME: as an optimization, if the flake contains a lock file // and we haven't changed it, then it's sufficient to use // flake.sourceInfo.storePath for the fingerprint. - return hashString(htSHA256, - fmt("%s;%s;%d;%d;%s", - flake.sourceInfo->storePath.to_string(), + return hashString( + htSHA256, + fmt("%s;%s;%d;%d;%s", flake.sourceInfo->storePath.to_string(), flake.lockedRef.subdir, flake.lockedRef.input.getRevCount().value_or(0), - flake.lockedRef.input.getLastModified().value_or(0), - lockFile)); + flake.lockedRef.input.getLastModified().value_or(0), lockFile)); } -Flake::~Flake() { } +Flake::~Flake() {} } diff --git a/src/libexpr/flake/flake.hh b/src/libexpr/flake/flake.hh index 524b18af1756..482da592a0e2 100644 --- a/src/libexpr/flake/flake.hh +++ b/src/libexpr/flake/flake.hh @@ -9,7 +9,9 @@ namespace nix { class EvalState; -namespace fetchers { struct Tree; } +namespace fetchers { +struct Tree; +} namespace flake { @@ -38,17 +40,17 @@ typedef std::map FlakeInputs; * flake to be resolved in the registry. */ -struct FlakeInput -{ +struct FlakeInput { std::optional ref; - bool isFlake = true; // true = process flake to get outputs, false = (fetched) static source path + bool isFlake = true; // true = process flake to get outputs, false = + // (fetched) static source path std::optional follows; FlakeInputs overrides; }; -struct ConfigFile -{ - using ConfigValue = std::variant, std::vector>; +struct ConfigFile { + using ConfigValue = std::variant, + std::vector>; std::map settings; @@ -56,11 +58,12 @@ struct ConfigFile }; /* The contents of a flake.nix file. */ -struct Flake -{ +struct Flake { FlakeRef originalRef; // the original flake specification (by the user) - FlakeRef resolvedRef; // registry references and caching resolved to the specific underlying flake - FlakeRef lockedRef; // the specific local store result of invoking the fetcher + FlakeRef resolvedRef; // registry references and caching resolved to the + // specific underlying flake + FlakeRef + lockedRef; // the specific local store result of invoking the fetcher bool forceDirty = false; // pretend that 'lockedRef' is dirty std::optional description; std::shared_ptr sourceInfo; @@ -74,16 +77,14 @@ Flake getFlake(EvalState & state, const FlakeRef & flakeRef, bool allowLookup); /* Fingerprint of a locked flake; used as a cache key. */ typedef Hash Fingerprint; -struct LockedFlake -{ +struct LockedFlake { Flake flake; LockFile lockFile; Fingerprint getFingerprint() const; }; -struct LockFlags -{ +struct LockFlags { /* Whether to ignore the existing lock file, creating a new one from scratch. */ bool recreateLockFile = false; @@ -125,24 +126,15 @@ struct LockFlags std::set inputUpdates; }; -LockedFlake lockFlake( - EvalState & state, - const FlakeRef & flakeRef, - const LockFlags & lockFlags); +LockedFlake lockFlake(EvalState & state, const FlakeRef & flakeRef, + const LockFlags & lockFlags); -void callFlake( - EvalState & state, - const LockedFlake & lockedFlake, - Value & v); +void callFlake(EvalState & state, const LockedFlake & lockedFlake, Value & v); } -void emitTreeAttrs( - EvalState & state, - const fetchers::Tree & tree, - const fetchers::Input & input, - Value & v, - bool emptyRevFallback = false, - bool forceDirty = false); +void emitTreeAttrs(EvalState & state, const fetchers::Tree & tree, + const fetchers::Input & input, Value & v, + bool emptyRevFallback = false, bool forceDirty = false); } diff --git a/src/libexpr/flake/flakeref.cc b/src/libexpr/flake/flakeref.cc index eede493f8dd1..4c0a996f41c1 100644 --- a/src/libexpr/flake/flakeref.cc +++ b/src/libexpr/flake/flakeref.cc @@ -30,13 +30,13 @@ fetchers::Attrs FlakeRef::toAttrs() const return attrs; } -std::ostream & operator << (std::ostream & str, const FlakeRef & flakeRef) +std::ostream & operator<<(std::ostream & str, const FlakeRef & flakeRef) { str << flakeRef.to_string(); return str; } -bool FlakeRef::operator ==(const FlakeRef & other) const +bool FlakeRef::operator==(const FlakeRef & other) const { return input == other.input && subdir == other.subdir; } @@ -44,23 +44,25 @@ bool FlakeRef::operator ==(const FlakeRef & other) const FlakeRef FlakeRef::resolve(ref store) const { auto [input2, extraAttrs] = lookupInRegistries(store, input); - return FlakeRef(std::move(input2), fetchers::maybeGetStrAttr(extraAttrs, "dir").value_or(subdir)); + return FlakeRef( + std::move(input2), + fetchers::maybeGetStrAttr(extraAttrs, "dir").value_or(subdir)); } -FlakeRef parseFlakeRef( - const std::string & url, - const std::optional & baseDir, - bool allowMissing, - bool isFlake) +FlakeRef parseFlakeRef(const std::string & url, + const std::optional & baseDir, bool allowMissing, + bool isFlake) { - auto [flakeRef, fragment] = parseFlakeRefWithFragment(url, baseDir, allowMissing, isFlake); + auto [flakeRef, fragment] = + parseFlakeRefWithFragment(url, baseDir, allowMissing, isFlake); if (fragment != "") - throw Error("unexpected fragment '%s' in flake reference '%s'", fragment, url); + throw Error("unexpected fragment '%s' in flake reference '%s'", + fragment, url); return flakeRef; } -std::optional maybeParseFlakeRef( - const std::string & url, const std::optional & baseDir) +std::optional maybeParseFlakeRef(const std::string & url, + const std::optional & baseDir) { try { return parseFlakeRef(url, baseDir); @@ -69,26 +71,24 @@ std::optional maybeParseFlakeRef( } } -std::pair parseFlakeRefWithFragment( - const std::string & url, - const std::optional & baseDir, - bool allowMissing, - bool isFlake) +std::pair +parseFlakeRefWithFragment(const std::string & url, + const std::optional & baseDir, + bool allowMissing, bool isFlake) { using namespace fetchers; static std::string fnRegex = "[0-9a-zA-Z-._~!$&'\"()*+,;=]+"; - static std::regex pathUrlRegex( - "(/?" + fnRegex + "(?:/" + fnRegex + ")*/?)" - + "(?:\\?(" + queryRegex + "))?" - + "(?:#(" + queryRegex + "))?", - std::regex::ECMAScript); + static std::regex pathUrlRegex("(/?" + fnRegex + "(?:/" + fnRegex + + ")*/?)" + "(?:\\?(" + queryRegex + + "))?" + "(?:#(" + queryRegex + "))?", + std::regex::ECMAScript); - static std::regex flakeRegex( - "((" + flakeIdRegexS + ")(?:/(?:" + refAndOrRevRegex + "))?)" - + "(?:#(" + queryRegex + "))?", - std::regex::ECMAScript); + static std::regex flakeRegex("((" + flakeIdRegexS + + ")(?:/(?:" + refAndOrRevRegex + "))?)" + + "(?:#(" + queryRegex + "))?", + std::regex::ECMAScript); std::smatch match; @@ -104,9 +104,8 @@ std::pair parseFlakeRefWithFragment( .path = match[1], }; - return std::make_pair( - FlakeRef(Input::fromURL(parsedURL), ""), - percentDecode(match.str(6))); + return std::make_pair(FlakeRef(Input::fromURL(parsedURL), ""), + percentDecode(match.str(6))); } else if (std::regex_match(url, match, pathUrlRegex)) { @@ -122,8 +121,10 @@ std::pair parseFlakeRefWithFragment( if (isFlake) { - if (!allowMissing && !pathExists(path + "/flake.nix")){ - notice("path '%s' does not contain a 'flake.nix', searching up",path); + if (!allowMissing && !pathExists(path + "/flake.nix")) { + notice("path '%s' does not contain a 'flake.nix', " + "searching up", + path); // Save device to detect filesystem boundary dev_t device = lstat(path).st_dev; @@ -133,10 +134,17 @@ std::pair parseFlakeRefWithFragment( found = true; break; } else if (pathExists(path + "/.git")) - throw Error("path '%s' is not part of a flake (neither it nor its parent directories contain a 'flake.nix' file)", path); + throw Error( + "path '%s' is not part of a flake (neither it " + "nor its parent directories contain a " + "'flake.nix' file)", + path); else { if (lstat(path).st_dev != device) - throw Error("unable to find a flake before encountering filesystem boundary at '%s'", path); + throw Error( + "unable to find a flake before " + "encountering filesystem boundary at '%s'", + path); } path = dirOf(path); } @@ -145,10 +153,14 @@ std::pair parseFlakeRefWithFragment( } if (!S_ISDIR(lstat(path).st_mode)) - throw BadURL("path '%s' is not a flake (because it's not a directory)", path); + throw BadURL("path '%s' is not a flake (because it's not a " + "directory)", + path); if (!allowMissing && !pathExists(path + "/flake.nix")) - throw BadURL("path '%s' is not a flake (because it doesn't contain a 'flake.nix' file)", path); + throw BadURL("path '%s' is not a flake (because it doesn't " + "contain a 'flake.nix' file)", + path); auto flakeRoot = path; std::string subdir; @@ -168,7 +180,9 @@ std::pair parseFlakeRefWithFragment( if (subdir != "") { if (parsedURL.query.count("dir")) - throw Error("flake URL '%s' has an inconsistent 'dir' parameter", url); + throw Error("flake URL '%s' has an " + "inconsistent 'dir' parameter", + url); parsedURL.query.insert_or_assign("dir", subdir); } @@ -176,18 +190,21 @@ std::pair parseFlakeRefWithFragment( parsedURL.query.insert_or_assign("shallow", "1"); return std::make_pair( - FlakeRef(Input::fromURL(parsedURL), getOr(parsedURL.query, "dir", "")), + FlakeRef(Input::fromURL(parsedURL), + getOr(parsedURL.query, "dir", "")), fragment); } - subdir = std::string(baseNameOf(flakeRoot)) + (subdir.empty() ? "" : "/" + subdir); + subdir = std::string(baseNameOf(flakeRoot)) + + (subdir.empty() ? "" : "/" + subdir); flakeRoot = dirOf(flakeRoot); } } } else { if (!hasPrefix(path, "/")) - throw BadURL("flake reference '%s' is not an absolute path", url); + throw BadURL("flake reference '%s' is not an absolute path", + url); auto query = decodeQuery(match[2]); path = canonPath(path + "/" + getOr(query, "dir", "")); } @@ -196,7 +213,8 @@ std::pair parseFlakeRefWithFragment( attrs.insert_or_assign("type", "path"); attrs.insert_or_assign("path", path); - return std::make_pair(FlakeRef(Input::fromAttrs(std::move(attrs)), ""), fragment); + return std::make_pair(FlakeRef(Input::fromAttrs(std::move(attrs)), ""), + fragment); } else { @@ -213,8 +231,9 @@ std::pair parseFlakeRefWithFragment( } } -std::optional> maybeParseFlakeRefWithFragment( - const std::string & url, const std::optional & baseDir) +std::optional> +maybeParseFlakeRefWithFragment(const std::string & url, + const std::optional & baseDir) { try { return parseFlakeRefWithFragment(url, baseDir); @@ -227,9 +246,8 @@ FlakeRef FlakeRef::fromAttrs(const fetchers::Attrs & attrs) { auto attrs2(attrs); attrs2.erase("dir"); - return FlakeRef( - fetchers::Input::fromAttrs(std::move(attrs2)), - fetchers::maybeGetStrAttr(attrs, "dir").value_or("")); + return FlakeRef(fetchers::Input::fromAttrs(std::move(attrs2)), + fetchers::maybeGetStrAttr(attrs, "dir").value_or("")); } std::pair FlakeRef::fetchTree(ref store) const @@ -238,14 +256,14 @@ std::pair FlakeRef::fetchTree(ref store) const return {std::move(tree), FlakeRef(std::move(lockedInput), subdir)}; } -std::tuple parseFlakeRefWithFragmentAndOutputsSpec( - const std::string & url, - const std::optional & baseDir, - bool allowMissing, - bool isFlake) +std::tuple +parseFlakeRefWithFragmentAndOutputsSpec(const std::string & url, + const std::optional & baseDir, + bool allowMissing, bool isFlake) { auto [prefix, outputsSpec] = parseOutputsSpec(url); - auto [flakeRef, fragment] = parseFlakeRefWithFragment(prefix, baseDir, allowMissing, isFlake); + auto [flakeRef, fragment] = + parseFlakeRefWithFragment(prefix, baseDir, allowMissing, isFlake); return {std::move(flakeRef), fragment, outputsSpec}; } diff --git a/src/libexpr/flake/flakeref.hh b/src/libexpr/flake/flakeref.hh index a9182f4bfd72..2a53862cade1 100644 --- a/src/libexpr/flake/flakeref.hh +++ b/src/libexpr/flake/flakeref.hh @@ -33,8 +33,7 @@ typedef std::string FlakeId; * FlakeRef to ensure the store is populated with this input. */ -struct FlakeRef -{ +struct FlakeRef { /* fetcher-specific representation of the input, sufficient to perform the fetch operation. */ fetchers::Input input; @@ -46,7 +45,8 @@ struct FlakeRef FlakeRef(fetchers::Input && input, const Path & subdir) : input(std::move(input)), subdir(subdir) - { } + { + } // FIXME: change to operator <<. std::string to_string() const; @@ -60,31 +60,28 @@ struct FlakeRef std::pair fetchTree(ref store) const; }; -std::ostream & operator << (std::ostream & str, const FlakeRef & flakeRef); +std::ostream & operator<<(std::ostream & str, const FlakeRef & flakeRef); -FlakeRef parseFlakeRef( - const std::string & url, - const std::optional & baseDir = {}, - bool allowMissing = false, - bool isFlake = true); +FlakeRef parseFlakeRef(const std::string & url, + const std::optional & baseDir = {}, + bool allowMissing = false, bool isFlake = true); -std::optional maybeParseFlake( - const std::string & url, const std::optional & baseDir = {}); +std::optional +maybeParseFlake(const std::string & url, + const std::optional & baseDir = {}); -std::pair parseFlakeRefWithFragment( - const std::string & url, - const std::optional & baseDir = {}, - bool allowMissing = false, - bool isFlake = true); +std::pair +parseFlakeRefWithFragment(const std::string & url, + const std::optional & baseDir = {}, + bool allowMissing = false, bool isFlake = true); -std::optional> maybeParseFlakeRefWithFragment( - const std::string & url, const std::optional & baseDir = {}); - -std::tuple parseFlakeRefWithFragmentAndOutputsSpec( - const std::string & url, - const std::optional & baseDir = {}, - bool allowMissing = false, - bool isFlake = true); +std::optional> +maybeParseFlakeRefWithFragment(const std::string & url, + const std::optional & baseDir = {}); +std::tuple +parseFlakeRefWithFragmentAndOutputsSpec( + const std::string & url, const std::optional & baseDir = {}, + bool allowMissing = false, bool isFlake = true); } diff --git a/src/libexpr/flake/lockfile.cc b/src/libexpr/flake/lockfile.cc index 60b52d578ee5..491f3c1b6d80 100644 --- a/src/libexpr/flake/lockfile.cc +++ b/src/libexpr/flake/lockfile.cc @@ -8,10 +8,8 @@ namespace nix::flake { -FlakeRef getFlakeRef( - const nlohmann::json & json, - const char * attr, - const char * info) +FlakeRef getFlakeRef(const nlohmann::json & json, const char * attr, + const char * info) { auto i = json.find(attr); if (i != json.end()) { @@ -31,13 +29,13 @@ FlakeRef getFlakeRef( } LockedNode::LockedNode(const nlohmann::json & json) - : lockedRef(getFlakeRef(json, "locked", "info")) - , originalRef(getFlakeRef(json, "original", nullptr)) - , isFlake(json.find("flake") != json.end() ? (bool) json["flake"] : true) + : lockedRef(getFlakeRef(json, "locked", "info")), + originalRef(getFlakeRef(json, "original", nullptr)), + isFlake(json.find("flake") != json.end() ? (bool) json["flake"] : true) { if (!lockedRef.input.isLocked()) throw Error("lockfile contains mutable lock '%s'", - fetchers::attrsToJSON(lockedRef.input.toAttrs())); + fetchers::attrsToJSON(lockedRef.input.toAttrs())); } StorePath LockedNode::computeStorePath(Store & store) const @@ -49,7 +47,8 @@ std::shared_ptr LockFile::findInput(const InputPath & path) { auto pos = root; - if (!pos) return {}; + if (!pos) + return {}; for (auto & elem : path) { if (auto i = get(pos->inputs, elem)) { @@ -57,7 +56,8 @@ std::shared_ptr LockFile::findInput(const InputPath & path) pos = *node; else if (auto follows = std::get_if<1>(&*i)) { pos = findInput(*follows); - if (!pos) return {}; + if (!pos) + return {}; } } else return {}; @@ -76,9 +76,9 @@ LockFile::LockFile(const nlohmann::json & json, const Path & path) std::function getInputs; - getInputs = [&](Node & node, const nlohmann::json & jsonNode) - { - if (jsonNode.find("inputs") == jsonNode.end()) return; + getInputs = [&](Node & node, const nlohmann::json & jsonNode) { + if (jsonNode.find("inputs") == jsonNode.end()) + return; for (auto & i : jsonNode["inputs"].items()) { if (i.value().is_array()) { // FIXME: remove, obsolete InputPath path; @@ -92,12 +92,14 @@ LockFile::LockFile(const nlohmann::json & json, const Path & path) auto nodes = json["nodes"]; auto jsonNode2 = nodes.find(inputKey); if (jsonNode2 == nodes.end()) - throw Error("lock file references missing node '%s'", inputKey); + throw Error("lock file references missing node '%s'", + inputKey); auto input = std::make_shared(*jsonNode2); k = nodeMap.insert_or_assign(inputKey, input).first; getInputs(*input, *jsonNode2); } - if (auto child = std::dynamic_pointer_cast(k->second)) + if (auto child = + std::dynamic_pointer_cast(k->second)) node.inputs.insert_or_assign(i.key(), child); else // FIXME: replace by follows node @@ -122,16 +124,18 @@ nlohmann::json LockFile::toJSON() const std::unordered_map, std::string> nodeKeys; std::unordered_set keys; - std::function node)> dumpNode; + std::function node)> + dumpNode; - dumpNode = [&](std::string key, std::shared_ptr node) -> std::string - { + dumpNode = [&](std::string key, + std::shared_ptr node) -> std::string { auto k = nodeKeys.find(node); if (k != nodeKeys.end()) return k->second; if (!keys.insert(key).second) { - for (int n = 2; ; ++n) { + for (int n = 2;; ++n) { auto k = fmt("%s_%d", key, n); if (keys.insert(k).second) { key = k; @@ -159,10 +163,14 @@ nlohmann::json LockFile::toJSON() const n["inputs"] = std::move(inputs); } - if (auto lockedNode = std::dynamic_pointer_cast(node)) { - n["original"] = fetchers::attrsToJSON(lockedNode->originalRef.toAttrs()); - n["locked"] = fetchers::attrsToJSON(lockedNode->lockedRef.toAttrs()); - if (!lockedNode->isFlake) n["flake"] = false; + if (auto lockedNode = + std::dynamic_pointer_cast(node)) { + n["original"] = + fetchers::attrsToJSON(lockedNode->originalRef.toAttrs()); + n["locked"] = + fetchers::attrsToJSON(lockedNode->lockedRef.toAttrs()); + if (!lockedNode->isFlake) + n["flake"] = false; } nodes[key] = std::move(n); @@ -178,18 +186,16 @@ nlohmann::json LockFile::toJSON() const return json; } -std::string LockFile::to_string() const -{ - return toJSON().dump(2); -} +std::string LockFile::to_string() const { return toJSON().dump(2); } LockFile LockFile::read(const Path & path) { - if (!pathExists(path)) return LockFile(); + if (!pathExists(path)) + return LockFile(); return LockFile(nlohmann::json::parse(readFile(path)), path); } -std::ostream & operator <<(std::ostream & stream, const LockFile & lockFile) +std::ostream & operator<<(std::ostream & stream, const LockFile & lockFile) { stream << lockFile.toJSON().dump(2); return stream; @@ -207,9 +213,9 @@ bool LockFile::isImmutable() const std::function node)> visit; - visit = [&](std::shared_ptr node) - { - if (!nodes.insert(node).second) return; + visit = [&](std::shared_ptr node) { + if (!nodes.insert(node).second) + return; for (auto & i : node->inputs) if (auto child = std::get_if<0>(&i.second)) visit(*child); @@ -218,15 +224,17 @@ bool LockFile::isImmutable() const visit(root); for (auto & i : nodes) { - if (i == root) continue; + if (i == root) + continue; auto lockedNode = std::dynamic_pointer_cast(i); - if (lockedNode && !lockedNode->lockedRef.input.isLocked()) return false; + if (lockedNode && !lockedNode->lockedRef.input.isLocked()) + return false; } return true; } -bool LockFile::operator ==(const LockFile & other) const +bool LockFile::operator==(const LockFile & other) const { // FIXME: slow return toJSON() == other.toJSON(); @@ -250,13 +258,14 @@ std::map LockFile::getAllInputs() const std::unordered_set> done; std::map res; - std::function node)> recurse; + std::function node)> + recurse; - recurse = [&](const InputPath & prefix, std::shared_ptr node) - { - if (!done.insert(node).second) return; + recurse = [&](const InputPath & prefix, std::shared_ptr node) { + if (!done.insert(node).second) + return; - for (auto &[id, input] : node->inputs) { + for (auto & [id, input] : node->inputs) { auto inputPath(prefix); inputPath.push_back(id); res.emplace(inputPath, input); @@ -275,12 +284,13 @@ static std::string describe(const FlakeRef & flakeRef) auto s = fmt("'%s'", flakeRef.to_string()); if (auto lastModified = flakeRef.input.getLastModified()) - s += fmt(" (%s)", std::put_time(std::gmtime(&*lastModified), "%Y-%m-%d")); + s += fmt(" (%s)", + std::put_time(std::gmtime(&*lastModified), "%Y-%m-%d")); return s; } -std::ostream & operator <<(std::ostream & stream, const Node::Edge & edge) +std::ostream & operator<<(std::ostream & stream, const Node::Edge & edge) { if (auto node = std::get_if<0>(&edge)) stream << describe((*node)->lockedRef); @@ -311,18 +321,20 @@ std::string LockFile::diff(const LockFile & oldLocks, const LockFile & newLocks) while (i != oldFlat.end() || j != newFlat.end()) { if (j != newFlat.end() && (i == oldFlat.end() || i->first > j->first)) { - res += fmt("• " ANSI_GREEN "Added input '%s':" ANSI_NORMAL "\n %s\n", - printInputPath(j->first), j->second); + res += fmt("• " ANSI_GREEN "Added input '%s':" ANSI_NORMAL + "\n %s\n", + printInputPath(j->first), j->second); ++j; - } else if (i != oldFlat.end() && (j == newFlat.end() || i->first < j->first)) { - res += fmt("• " ANSI_RED "Removed input '%s'" ANSI_NORMAL "\n", printInputPath(i->first)); + } else if (i != oldFlat.end() && + (j == newFlat.end() || i->first < j->first)) { + res += fmt("• " ANSI_RED "Removed input '%s'" ANSI_NORMAL "\n", + printInputPath(i->first)); ++i; } else { if (!equals(i->second, j->second)) { - res += fmt("• " ANSI_BOLD "Updated input '%s':" ANSI_NORMAL "\n %s\n → %s\n", - printInputPath(i->first), - i->second, - j->second); + res += fmt("• " ANSI_BOLD "Updated input '%s':" ANSI_NORMAL + "\n %s\n → %s\n", + printInputPath(i->first), i->second, j->second); } ++i; ++j; @@ -340,8 +352,8 @@ void LockFile::check() if (auto follows = std::get_if<1>(&input)) { if (!follows->empty() && !get(inputs, *follows)) throw Error("input '%s' follows a non-existent input '%s'", - printInputPath(inputPath), - printInputPath(*follows)); + printInputPath(inputPath), + printInputPath(*follows)); } } } diff --git a/src/libexpr/flake/lockfile.hh b/src/libexpr/flake/lockfile.hh index 96f1edc76bab..8fdc64fa1176 100644 --- a/src/libexpr/flake/lockfile.hh +++ b/src/libexpr/flake/lockfile.hh @@ -18,38 +18,34 @@ struct LockedNode; /* A node in the lock file. It has outgoing edges to other nodes (its inputs). Only the root node has this type; all other nodes have type LockedNode. */ -struct Node : std::enable_shared_from_this -{ +struct Node : std::enable_shared_from_this { typedef std::variant, InputPath> Edge; std::map inputs; - virtual ~Node() { } + virtual ~Node() {} }; /* A non-root node in the lock file. */ -struct LockedNode : Node -{ +struct LockedNode : Node { FlakeRef lockedRef, originalRef; bool isFlake = true; - LockedNode( - const FlakeRef & lockedRef, - const FlakeRef & originalRef, - bool isFlake = true) + LockedNode(const FlakeRef & lockedRef, const FlakeRef & originalRef, + bool isFlake = true) : lockedRef(lockedRef), originalRef(originalRef), isFlake(isFlake) - { } + { + } LockedNode(const nlohmann::json & json); StorePath computeStorePath(Store & store) const; }; -struct LockFile -{ +struct LockFile { std::shared_ptr root = std::make_shared(); - LockFile() {}; + LockFile(){}; LockFile(const nlohmann::json & json, const Path & path); nlohmann::json toJSON() const; @@ -62,19 +58,20 @@ struct LockFile bool isImmutable() const; - bool operator ==(const LockFile & other) const; + bool operator==(const LockFile & other) const; std::shared_ptr findInput(const InputPath & path); std::map getAllInputs() const; - static std::string diff(const LockFile & oldLocks, const LockFile & newLocks); + static std::string diff(const LockFile & oldLocks, + const LockFile & newLocks); /* Check that every 'follows' input target exists. */ void check(); }; -std::ostream & operator <<(std::ostream & stream, const LockFile & lockFile); +std::ostream & operator<<(std::ostream & stream, const LockFile & lockFile); InputPath parseInputPath(std::string_view s); diff --git a/src/libexpr/function-trace.cc b/src/libexpr/function-trace.cc index c6057b3842f1..5beefa049a49 100644 --- a/src/libexpr/function-trace.cc +++ b/src/libexpr/function-trace.cc @@ -3,14 +3,18 @@ namespace nix { -FunctionCallTrace::FunctionCallTrace(const Pos & pos) : pos(pos) { - auto duration = std::chrono::high_resolution_clock::now().time_since_epoch(); +FunctionCallTrace::FunctionCallTrace(const Pos & pos) : pos(pos) +{ + auto duration = + std::chrono::high_resolution_clock::now().time_since_epoch(); auto ns = std::chrono::duration_cast(duration); printMsg(lvlInfo, "function-trace entered %1% at %2%", pos, ns.count()); } -FunctionCallTrace::~FunctionCallTrace() { - auto duration = std::chrono::high_resolution_clock::now().time_since_epoch(); +FunctionCallTrace::~FunctionCallTrace() +{ + auto duration = + std::chrono::high_resolution_clock::now().time_since_epoch(); auto ns = std::chrono::duration_cast(duration); printMsg(lvlInfo, "function-trace exited %1% at %2%", pos, ns.count()); } diff --git a/src/libexpr/function-trace.hh b/src/libexpr/function-trace.hh index e9a2526bde82..7d11658ee977 100644 --- a/src/libexpr/function-trace.hh +++ b/src/libexpr/function-trace.hh @@ -6,8 +6,7 @@ namespace nix { -struct FunctionCallTrace -{ +struct FunctionCallTrace { const Pos pos; FunctionCallTrace(const Pos & pos); ~FunctionCallTrace(); diff --git a/src/libexpr/get-drvs.cc b/src/libexpr/get-drvs.cc index 346741dd5460..0700be503b34 100644 --- a/src/libexpr/get-drvs.cc +++ b/src/libexpr/get-drvs.cc @@ -8,20 +8,19 @@ #include #include - namespace nix { - DrvInfo::DrvInfo(EvalState & state, std::string attrPath, Bindings * attrs) : state(&state), attrs(attrs), attrPath(std::move(attrPath)) { } - -DrvInfo::DrvInfo(EvalState & state, ref store, const std::string & drvPathWithOutputs) +DrvInfo::DrvInfo(EvalState & state, ref store, + const std::string & drvPathWithOutputs) : state(&state), attrs(nullptr), attrPath("") { - auto [drvPath, selectedOutputs] = parsePathWithOutputs(*store, drvPathWithOutputs); + auto [drvPath, selectedOutputs] = + parsePathWithOutputs(*store, drvPathWithOutputs); this->drvPath = drvPath; @@ -30,43 +29,43 @@ DrvInfo::DrvInfo(EvalState & state, ref store, const std::string & drvPat name = drvPath.name(); if (selectedOutputs.size() > 1) - throw Error("building more than one derivation output is not supported, in '%s'", drvPathWithOutputs); + throw Error("building more than one derivation output is not " + "supported, in '%s'", + drvPathWithOutputs); - outputName = - selectedOutputs.empty() - ? getOr(drv.env, "outputName", "out") - : *selectedOutputs.begin(); + outputName = selectedOutputs.empty() ? getOr(drv.env, "outputName", "out") + : *selectedOutputs.begin(); auto i = drv.outputs.find(outputName); if (i == drv.outputs.end()) - throw Error("derivation '%s' does not have output '%s'", store->printStorePath(drvPath), outputName); + throw Error("derivation '%s' does not have output '%s'", + store->printStorePath(drvPath), outputName); auto & [outputName, output] = *i; outPath = {output.path(*store, drv.name, outputName)}; } - std::string DrvInfo::queryName() const { if (name == "" && attrs) { auto i = attrs->find(state->sName); - if (i == attrs->end()) throw TypeError("derivation name missing"); + if (i == attrs->end()) + throw TypeError("derivation name missing"); name = state->forceStringNoCtx(*i->value); } return name; } - std::string DrvInfo::querySystem() const { if (system == "" && attrs) { auto i = attrs->find(state->sSystem); - system = i == attrs->end() ? "unknown" : state->forceStringNoCtx(*i->value, i->pos); + system = i == attrs->end() ? "unknown" + : state->forceStringNoCtx(*i->value, i->pos); } return system; } - std::optional DrvInfo::queryDrvPath() const { if (!drvPath && attrs) { @@ -80,7 +79,6 @@ std::optional DrvInfo::queryDrvPath() const return drvPath.value_or(std::nullopt); } - StorePath DrvInfo::requireDrvPath() const { if (auto drvPath = queryDrvPath()) @@ -88,7 +86,6 @@ StorePath DrvInfo::requireDrvPath() const throw Error("derivation does not contain a 'drvPath' attribute"); } - StorePath DrvInfo::queryOutPath() const { if (!outPath && attrs) { @@ -102,8 +99,8 @@ StorePath DrvInfo::queryOutPath() const return *outPath; } - -DrvInfo::Outputs DrvInfo::queryOutputs(bool withPaths, bool onlyOutputsToInstall) +DrvInfo::Outputs DrvInfo::queryOutputs(bool withPaths, + bool onlyOutputsToInstall) { if (outputs.empty()) { /* Get the ‘outputs’ list. */ @@ -117,128 +114,148 @@ DrvInfo::Outputs DrvInfo::queryOutputs(bool withPaths, bool onlyOutputsToInstall if (withPaths) { /* Evaluate the corresponding set. */ - Bindings::iterator out = attrs->find(state->symbols.create(output)); - if (out == attrs->end()) continue; // FIXME: throw error? + Bindings::iterator out = + attrs->find(state->symbols.create(output)); + if (out == attrs->end()) + continue; // FIXME: throw error? state->forceAttrs(*out->value, i->pos); /* And evaluate its ‘outPath’ attribute. */ - Bindings::iterator outPath = out->value->attrs->find(state->sOutPath); - if (outPath == out->value->attrs->end()) continue; // FIXME: throw error? + Bindings::iterator outPath = + out->value->attrs->find(state->sOutPath); + if (outPath == out->value->attrs->end()) + continue; // FIXME: throw error? PathSet context; - outputs.emplace(output, state->coerceToStorePath(outPath->pos, *outPath->value, context)); + outputs.emplace( + output, state->coerceToStorePath( + outPath->pos, *outPath->value, context)); } else outputs.emplace(output, std::nullopt); } } else - outputs.emplace("out", withPaths ? std::optional{queryOutPath()} : std::nullopt); + outputs.emplace("out", withPaths ? std::optional{queryOutPath()} + : std::nullopt); } if (!onlyOutputsToInstall || !attrs) return outputs; Bindings::iterator i; - if (attrs && (i = attrs->find(state->sOutputSpecified)) != attrs->end() && state->forceBool(*i->value, i->pos)) { + if (attrs && (i = attrs->find(state->sOutputSpecified)) != attrs->end() && + state->forceBool(*i->value, i->pos)) { Outputs result; auto out = outputs.find(queryOutputName()); if (out == outputs.end()) - throw Error("derivation does not have output '%s'", queryOutputName()); + throw Error("derivation does not have output '%s'", + queryOutputName()); result.insert(*out); return result; } else { - /* Check for `meta.outputsToInstall` and return `outputs` reduced to that. */ + /* Check for `meta.outputsToInstall` and return `outputs` reduced to + * that. */ const Value * outTI = queryMeta("outputsToInstall"); - if (!outTI) return outputs; - const auto errMsg = Error("this derivation has bad 'meta.outputsToInstall'"); - /* ^ this shows during `nix-env -i` right under the bad derivation */ - if (!outTI->isList()) throw errMsg; + if (!outTI) + return outputs; + const auto errMsg = + Error("this derivation has bad 'meta.outputsToInstall'"); + /* ^ this shows during `nix-env -i` right under the bad derivation */ + if (!outTI->isList()) + throw errMsg; Outputs result; for (auto elem : outTI->listItems()) { - if (elem->type() != nString) throw errMsg; + if (elem->type() != nString) + throw errMsg; auto out = outputs.find(elem->string.s); - if (out == outputs.end()) throw errMsg; + if (out == outputs.end()) + throw errMsg; result.insert(*out); } return result; } } - std::string DrvInfo::queryOutputName() const { if (outputName == "" && attrs) { Bindings::iterator i = attrs->find(state->sOutputName); - outputName = i != attrs->end() ? state->forceStringNoCtx(*i->value) : ""; + outputName = + i != attrs->end() ? state->forceStringNoCtx(*i->value) : ""; } return outputName; } - Bindings * DrvInfo::getMeta() { - if (meta) return meta; - if (!attrs) return 0; + if (meta) + return meta; + if (!attrs) + return 0; Bindings::iterator a = attrs->find(state->sMeta); - if (a == attrs->end()) return 0; + if (a == attrs->end()) + return 0; state->forceAttrs(*a->value, a->pos); meta = a->value->attrs; return meta; } - StringSet DrvInfo::queryMetaNames() { StringSet res; - if (!getMeta()) return res; + if (!getMeta()) + return res; for (auto & i : *meta) res.emplace(state->symbols[i.name]); return res; } - bool DrvInfo::checkMeta(Value & v) { state->forceValue(v, [&]() { return v.determinePos(noPos); }); if (v.type() == nList) { for (auto elem : v.listItems()) - if (!checkMeta(*elem)) return false; + if (!checkMeta(*elem)) + return false; return true; - } - else if (v.type() == nAttrs) { + } else if (v.type() == nAttrs) { Bindings::iterator i = v.attrs->find(state->sOutPath); - if (i != v.attrs->end()) return false; + if (i != v.attrs->end()) + return false; for (auto & i : *v.attrs) - if (!checkMeta(*i.value)) return false; + if (!checkMeta(*i.value)) + return false; return true; - } - else return v.type() == nInt || v.type() == nBool || v.type() == nString || - v.type() == nFloat; + } else + return v.type() == nInt || v.type() == nBool || v.type() == nString || + v.type() == nFloat; } - Value * DrvInfo::queryMeta(const std::string & name) { - if (!getMeta()) return 0; + if (!getMeta()) + return 0; Bindings::iterator a = meta->find(state->symbols.create(name)); - if (a == meta->end() || !checkMeta(*a->value)) return 0; + if (a == meta->end() || !checkMeta(*a->value)) + return 0; return a->value; } - std::string DrvInfo::queryMetaString(const std::string & name) { Value * v = queryMeta(name); - if (!v || v->type() != nString) return ""; + if (!v || v->type() != nString) + return ""; return v->string.s; } - NixInt DrvInfo::queryMetaInt(const std::string & name, NixInt def) { Value * v = queryMeta(name); - if (!v) return def; - if (v->type() == nInt) return v->integer; + if (!v) + return def; + if (v->type() == nInt) + return v->integer; if (v->type() == nString) { /* Backwards compatibility with before we had support for integer meta fields. */ @@ -251,8 +268,10 @@ NixInt DrvInfo::queryMetaInt(const std::string & name, NixInt def) NixFloat DrvInfo::queryMetaFloat(const std::string & name, NixFloat def) { Value * v = queryMeta(name); - if (!v) return def; - if (v->type() == nFloat) return v->fpoint; + if (!v) + return def; + if (v->type() == nFloat) + return v->fpoint; if (v->type() == nString) { /* Backwards compatibility with before we had support for float meta fields. */ @@ -262,22 +281,24 @@ NixFloat DrvInfo::queryMetaFloat(const std::string & name, NixFloat def) return def; } - bool DrvInfo::queryMetaBool(const std::string & name, bool def) { Value * v = queryMeta(name); - if (!v) return def; - if (v->type() == nBool) return v->boolean; + if (!v) + return def; + if (v->type() == nBool) + return v->boolean; if (v->type() == nString) { /* Backwards compatibility with before we had support for Boolean meta fields. */ - if (strcmp(v->string.s, "true") == 0) return true; - if (strcmp(v->string.s, "false") == 0) return false; + if (strcmp(v->string.s, "true") == 0) + return true; + if (strcmp(v->string.s, "false") == 0) + return false; } return def; } - void DrvInfo::setMeta(const std::string & name, Value * v) { getMeta(); @@ -287,30 +308,31 @@ void DrvInfo::setMeta(const std::string & name, Value * v) for (auto i : *meta) if (i.name != sym) attrs.insert(i); - if (v) attrs.insert(sym, v); + if (v) + attrs.insert(sym, v); meta = attrs.finish(); } - /* Cache for already considered attrsets. */ typedef std::set Done; - /* Evaluate value `v'. If it evaluates to a set of type `derivation', then put information about it in `drvs' (unless it's already in `done'). The result boolean indicates whether it makes sense for the caller to recursively search for derivations in `v'. */ static bool getDerivation(EvalState & state, Value & v, - const std::string & attrPath, DrvInfos & drvs, Done & done, - bool ignoreAssertionFailures) + const std::string & attrPath, DrvInfos & drvs, + Done & done, bool ignoreAssertionFailures) { try { state.forceValue(v, [&]() { return v.determinePos(noPos); }); - if (!state.isDerivation(v)) return true; + if (!state.isDerivation(v)) + return true; /* Remove spurious duplicates (e.g., a set like `rec { x = derivation {...}; y = x;}'. */ - if (!done.insert(v.attrs).second) return false; + if (!done.insert(v.attrs).second) + return false; DrvInfo drv(state, attrPath, v.attrs); @@ -321,48 +343,50 @@ static bool getDerivation(EvalState & state, Value & v, return false; } catch (AssertionError & e) { - if (ignoreAssertionFailures) return false; + if (ignoreAssertionFailures) + return false; throw; } } - std::optional getDerivation(EvalState & state, Value & v, - bool ignoreAssertionFailures) + bool ignoreAssertionFailures) { Done done; DrvInfos drvs; getDerivation(state, v, "", drvs, done, ignoreAssertionFailures); - if (drvs.size() != 1) return {}; + if (drvs.size() != 1) + return {}; return std::move(drvs.front()); } - static std::string addToPath(const std::string & s1, const std::string & s2) { return s1.empty() ? s2 : s1 + "." + s2; } - static std::regex attrRegex("[A-Za-z_][A-Za-z0-9-_+]*"); - static void getDerivations(EvalState & state, Value & vIn, - const std::string & pathPrefix, Bindings & autoArgs, - DrvInfos & drvs, Done & done, - bool ignoreAssertionFailures) + const std::string & pathPrefix, Bindings & autoArgs, + DrvInfos & drvs, Done & done, + bool ignoreAssertionFailures) { Value v; state.autoCallFunction(autoArgs, vIn, v); /* Process the expression. */ - if (!getDerivation(state, v, pathPrefix, drvs, done, ignoreAssertionFailures)) ; + if (!getDerivation(state, v, pathPrefix, drvs, done, + ignoreAssertionFailures)) + ; else if (v.type() == nAttrs) { /* !!! undocumented hackery to support combining channels in nix-env.cc. */ - bool combineChannels = v.attrs->find(state.symbols.create("_combineChannels")) != v.attrs->end(); + bool combineChannels = + v.attrs->find(state.symbols.create("_combineChannels")) != + v.attrs->end(); /* Consider the attributes in sorted order to get more deterministic behaviour in nix-env operations (e.g. when @@ -371,19 +395,26 @@ static void getDerivations(EvalState & state, Value & vIn, precedence). */ for (auto & i : v.attrs->lexicographicOrder(state.symbols)) { debug("evaluating attribute '%1%'", state.symbols[i->name]); - if (!std::regex_match(std::string(state.symbols[i->name]), attrRegex)) + if (!std::regex_match(std::string(state.symbols[i->name]), + attrRegex)) continue; - std::string pathPrefix2 = addToPath(pathPrefix, state.symbols[i->name]); + std::string pathPrefix2 = + addToPath(pathPrefix, state.symbols[i->name]); if (combineChannels) - getDerivations(state, *i->value, pathPrefix2, autoArgs, drvs, done, ignoreAssertionFailures); - else if (getDerivation(state, *i->value, pathPrefix2, drvs, done, ignoreAssertionFailures)) { + getDerivations(state, *i->value, pathPrefix2, autoArgs, drvs, + done, ignoreAssertionFailures); + else if (getDerivation(state, *i->value, pathPrefix2, drvs, done, + ignoreAssertionFailures)) { /* If the value of this attribute is itself a set, should we recurse into it? => Only if it has a `recurseForDerivations = true' attribute. */ if (i->value->type() == nAttrs) { - Bindings::iterator j = i->value->attrs->find(state.sRecurseForDerivations); - if (j != i->value->attrs->end() && state.forceBool(*j->value, j->pos)) - getDerivations(state, *i->value, pathPrefix2, autoArgs, drvs, done, ignoreAssertionFailures); + Bindings::iterator j = + i->value->attrs->find(state.sRecurseForDerivations); + if (j != i->value->attrs->end() && + state.forceBool(*j->value, j->pos)) + getDerivations(state, *i->value, pathPrefix2, autoArgs, + drvs, done, ignoreAssertionFailures); } } } @@ -392,21 +423,25 @@ static void getDerivations(EvalState & state, Value & vIn, else if (v.type() == nList) { for (auto [n, elem] : enumerate(v.listItems())) { std::string pathPrefix2 = addToPath(pathPrefix, fmt("%d", n)); - if (getDerivation(state, *elem, pathPrefix2, drvs, done, ignoreAssertionFailures)) - getDerivations(state, *elem, pathPrefix2, autoArgs, drvs, done, ignoreAssertionFailures); + if (getDerivation(state, *elem, pathPrefix2, drvs, done, + ignoreAssertionFailures)) + getDerivations(state, *elem, pathPrefix2, autoArgs, drvs, done, + ignoreAssertionFailures); } } - else throw TypeError("expression does not evaluate to a derivation (or a set or list of those)"); + else + throw TypeError("expression does not evaluate to a derivation (or a " + "set or list of those)"); } - -void getDerivations(EvalState & state, Value & v, const std::string & pathPrefix, - Bindings & autoArgs, DrvInfos & drvs, bool ignoreAssertionFailures) +void getDerivations(EvalState & state, Value & v, + const std::string & pathPrefix, Bindings & autoArgs, + DrvInfos & drvs, bool ignoreAssertionFailures) { Done done; - getDerivations(state, v, pathPrefix, autoArgs, drvs, done, ignoreAssertionFailures); + getDerivations(state, v, pathPrefix, autoArgs, drvs, done, + ignoreAssertionFailures); } - } diff --git a/src/libexpr/get-drvs.hh b/src/libexpr/get-drvs.hh index bbd2d3c477b2..e11bf3557b55 100644 --- a/src/libexpr/get-drvs.hh +++ b/src/libexpr/get-drvs.hh @@ -6,16 +6,13 @@ #include #include - namespace nix { - -struct DrvInfo -{ -public: +struct DrvInfo { + public: typedef std::map> Outputs; -private: + private: EvalState * state; mutable std::string name; @@ -27,18 +24,19 @@ private: bool failed = false; // set if we get an AssertionError - Bindings * attrs = nullptr, * meta = nullptr; + Bindings *attrs = nullptr, *meta = nullptr; Bindings * getMeta(); bool checkMeta(Value & v); -public: + public: std::string attrPath; /* path towards the derivation */ - DrvInfo(EvalState & state) : state(&state) { }; + DrvInfo(EvalState & state) : state(&state){}; DrvInfo(EvalState & state, std::string attrPath, Bindings * attrs); - DrvInfo(EvalState & state, ref store, const std::string & drvPathWithOutputs); + DrvInfo(EvalState & state, ref store, + const std::string & drvPathWithOutputs); std::string queryName() const; std::string querySystem() const; @@ -48,7 +46,8 @@ public: std::string queryOutputName() const; /** Return the unordered map of output names to (optional) output paths. * The "outputs to install" are determined by `meta.outputsToInstall`. */ - Outputs queryOutputs(bool withPaths = true, bool onlyOutputsToInstall = false); + Outputs queryOutputs(bool withPaths = true, + bool onlyOutputsToInstall = false); StringSet queryMetaNames(); Value * queryMeta(const std::string & name); @@ -71,22 +70,19 @@ public: bool hasFailed() { return failed; }; }; - #if HAVE_BOEHMGC typedef std::list> DrvInfos; #else typedef std::list DrvInfos; #endif - /* If value `v' denotes a derivation, return a DrvInfo object describing it. Otherwise return nothing. */ -std::optional getDerivation(EvalState & state, - Value & v, bool ignoreAssertionFailures); - -void getDerivations(EvalState & state, Value & v, const std::string & pathPrefix, - Bindings & autoArgs, DrvInfos & drvs, - bool ignoreAssertionFailures); +std::optional getDerivation(EvalState & state, Value & v, + bool ignoreAssertionFailures); +void getDerivations(EvalState & state, Value & v, + const std::string & pathPrefix, Bindings & autoArgs, + DrvInfos & drvs, bool ignoreAssertionFailures); } diff --git a/src/libexpr/json-to-value.cc b/src/libexpr/json-to-value.cc index 99a475ff9871..bc0eb99cdf6f 100644 --- a/src/libexpr/json-to-value.cc +++ b/src/libexpr/json-to-value.cc @@ -11,15 +11,19 @@ namespace nix { // https://github.com/nlohmann/json/blob/master/include/nlohmann/detail/input/json_sax.hpp class JSONSax : nlohmann::json_sax { class JSONState { - protected: + protected: std::unique_ptr parent; RootValue v; - public: + + public: virtual std::unique_ptr resolve(EvalState &) { throw std::logic_error("tried to close toplevel json parser state"); } - explicit JSONState(std::unique_ptr && p) : parent(std::move(p)) {} + explicit JSONState(std::unique_ptr && p) + : parent(std::move(p)) + { + } explicit JSONState(Value * v) : v(allocRootValue(v)) {} JSONState(JSONState & p) = delete; Value & value(EvalState & state) @@ -44,7 +48,8 @@ class JSONSax : nlohmann::json_sax { return std::move(parent); } void add() override { v = nullptr; } - public: + + public: void key(string_t & name, EvalState & state) { attrs.insert_or_assign(state.symbols.create(name), &value(state)); @@ -62,12 +67,15 @@ class JSONSax : nlohmann::json_sax { } return std::move(parent); } - void add() override { + void add() override + { values.push_back(*v); v = nullptr; } - public: - JSONListState(std::unique_ptr && p, std::size_t reserve) : JSONState(std::move(p)) + + public: + JSONListState(std::unique_ptr && p, std::size_t reserve) + : JSONState(std::move(p)) { values.reserve(reserve); } @@ -76,8 +84,9 @@ class JSONSax : nlohmann::json_sax { EvalState & state; std::unique_ptr rs; -public: - JSONSax(EvalState & state, Value & v) : state(state), rs(new JSONState(&v)) {}; + public: + JSONSax(EvalState & state, Value & v) + : state(state), rs(new JSONState(&v)){}; bool null() { @@ -122,7 +131,7 @@ class JSONSax : nlohmann::json_sax { } #if NLOHMANN_JSON_VERSION_MAJOR >= 3 && NLOHMANN_JSON_VERSION_MINOR >= 8 - bool binary(binary_t&) + bool binary(binary_t &) { // This function ought to be unreachable assert(false); @@ -138,27 +147,30 @@ class JSONSax : nlohmann::json_sax { bool key(string_t & name) { - dynamic_cast(rs.get())->key(name, state); + dynamic_cast(rs.get())->key(name, state); return true; } - bool end_object() { + bool end_object() + { rs = rs->resolve(state); rs->add(); return true; } - bool end_array() { - return end_object(); - } + bool end_array() { return end_object(); } - bool start_array(size_t len) { - rs = std::make_unique(std::move(rs), + bool start_array(size_t len) + { + rs = std::make_unique( + std::move(rs), len != std::numeric_limits::max() ? len : 128); return true; } - bool parse_error(std::size_t, const std::string&, const nlohmann::detail::exception& ex) { + bool parse_error(std::size_t, const std::string &, + const nlohmann::detail::exception & ex) + { throw JSONParseError(ex.what()); } }; diff --git a/src/libexpr/nixexpr.cc b/src/libexpr/nixexpr.cc index 7c623a07deb4..6cf86f5582d0 100644 --- a/src/libexpr/nixexpr.cc +++ b/src/libexpr/nixexpr.cc @@ -14,15 +14,20 @@ static void showString(std::ostream & str, std::string_view s) { str << '"'; for (auto c : s) - if (c == '"' || c == '\\' || c == '$') str << "\\" << c; - else if (c == '\n') str << "\\n"; - else if (c == '\r') str << "\\r"; - else if (c == '\t') str << "\\t"; - else str << c; + if (c == '"' || c == '\\' || c == '$') + str << "\\" << c; + else if (c == '\n') + str << "\\n"; + else if (c == '\r') + str << "\\r"; + else if (c == '\t') + str << "\\t"; + else + str << c; str << '"'; } -std::ostream & operator <<(std::ostream & str, const SymbolStr & symbol) +std::ostream & operator<<(std::ostream & str, const SymbolStr & symbol) { std::string_view s = symbol; @@ -37,10 +42,9 @@ std::ostream & operator <<(std::ostream & str, const SymbolStr & symbol) return str; } for (auto c : s) - if (!((c >= 'a' && c <= 'z') || - (c >= 'A' && c <= 'Z') || - (c >= '0' && c <= '9') || - c == '_' || c == '\'' || c == '-')) { + if (!((c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z') || + (c >= '0' && c <= '9') || c == '_' || c == '\'' || + c == '-')) { showString(str, s); return str; } @@ -100,18 +104,21 @@ void ExprOpHasAttr::show(const SymbolTable & symbols, std::ostream & str) const void ExprAttrs::show(const SymbolTable & symbols, std::ostream & str) const { - if (recursive) str << "rec "; + if (recursive) + str << "rec "; str << "{ "; typedef const decltype(attrs)::value_type * Attr; std::vector sorted; - for (auto & i : attrs) sorted.push_back(&i); + for (auto & i : attrs) + sorted.push_back(&i); std::sort(sorted.begin(), sorted.end(), [&](Attr a, Attr b) { std::string_view sa = symbols[a->first], sb = symbols[b->first]; return sa < sb; }); for (auto & i : sorted) { if (i->second.inherited) - str << "inherit " << symbols[i->first] << " " << "; "; + str << "inherit " << symbols[i->first] << " " + << "; "; else { str << symbols[i->first] << " = "; i->second.e->show(symbols, str); @@ -146,7 +153,10 @@ void ExprLambda::show(const SymbolTable & symbols, std::ostream & str) const str << "{ "; bool first = true; for (auto & i : formals->formals) { - if (first) first = false; else str << ", "; + if (first) + first = false; + else + str << ", "; str << symbols[i.name]; if (i.def) { str << " ? "; @@ -154,13 +164,16 @@ void ExprLambda::show(const SymbolTable & symbols, std::ostream & str) const } } if (formals->ellipsis) { - if (!first) str << ", "; + if (!first) + str << ", "; str << "..."; } str << " }"; - if (arg) str << " @ "; + if (arg) + str << " @ "; } - if (arg) str << symbols[arg]; + if (arg) + str << symbols[arg]; str << ": "; body->show(symbols, str); str << ")"; @@ -171,7 +184,7 @@ void ExprCall::show(const SymbolTable & symbols, std::ostream & str) const str << '('; fun->show(symbols, str); for (auto e : args) { - str << ' '; + str << ' '; e->show(symbols, str); } str << ')'; @@ -183,8 +196,7 @@ void ExprLet::show(const SymbolTable & symbols, std::ostream & str) const for (auto & i : attrs->attrs) if (i.second.inherited) { str << "inherit " << symbols[i.first] << "; "; - } - else { + } else { str << symbols[i.first] << " = "; i.second.e->show(symbols, str); str << "; "; @@ -229,12 +241,16 @@ void ExprOpNot::show(const SymbolTable & symbols, std::ostream & str) const str << ")"; } -void ExprConcatStrings::show(const SymbolTable & symbols, std::ostream & str) const +void ExprConcatStrings::show(const SymbolTable & symbols, + std::ostream & str) const { bool first = true; str << "("; for (auto & i : *es) { - if (first) first = false; else str << " + "; + if (first) + first = false; + else + str << " + "; i.second->show(symbols, str); } str << ")"; @@ -245,24 +261,22 @@ void ExprPos::show(const SymbolTable & symbols, std::ostream & str) const str << "__curPos"; } - -std::ostream & operator << (std::ostream & str, const Pos & pos) +std::ostream & operator<<(std::ostream & str, const Pos & pos) { if (!pos) str << "undefined position"; - else - { + else { auto f = format(ANSI_BOLD "%1%" ANSI_NORMAL ":%2%:%3%"); switch (pos.origin) { - case foFile: - f % (const std::string &) pos.file; - break; - case foStdin: - case foString: - f % "(string)"; - break; - default: - throw Error("unhandled Pos origin!"); + case foFile: + f % (const std::string &) pos.file; + break; + case foStdin: + case foString: + f % "(string)"; + break; + default: + throw Error("unhandled Pos origin!"); } str << (f % pos.line % pos.column).str(); } @@ -270,13 +284,15 @@ std::ostream & operator << (std::ostream & str, const Pos & pos) return str; } - std::string showAttrPath(const SymbolTable & symbols, const AttrPath & attrPath) { std::ostringstream out; bool first = true; for (auto & i : attrPath) { - if (!first) out << '.'; else first = false; + if (!first) + out << '.'; + else + first = false; if (i.symbol) out << symbols[i.symbol]; else { @@ -288,40 +304,44 @@ std::string showAttrPath(const SymbolTable & symbols, const AttrPath & attrPath) return out.str(); } - - /* Computing levels/displacements for variables. */ -void Expr::bindVars(EvalState & es, const std::shared_ptr & env) +void Expr::bindVars(EvalState & es, + const std::shared_ptr & env) { abort(); } -void ExprInt::bindVars(EvalState & es, const std::shared_ptr & env) +void ExprInt::bindVars(EvalState & es, + const std::shared_ptr & env) { if (es.debugRepl) es.exprEnvs.insert(std::make_pair(this, env)); } -void ExprFloat::bindVars(EvalState & es, const std::shared_ptr & env) +void ExprFloat::bindVars(EvalState & es, + const std::shared_ptr & env) { if (es.debugRepl) es.exprEnvs.insert(std::make_pair(this, env)); } -void ExprString::bindVars(EvalState & es, const std::shared_ptr & env) +void ExprString::bindVars(EvalState & es, + const std::shared_ptr & env) { if (es.debugRepl) es.exprEnvs.insert(std::make_pair(this, env)); } -void ExprPath::bindVars(EvalState & es, const std::shared_ptr & env) +void ExprPath::bindVars(EvalState & es, + const std::shared_ptr & env) { if (es.debugRepl) es.exprEnvs.insert(std::make_pair(this, env)); } -void ExprVar::bindVars(EvalState & es, const std::shared_ptr & env) +void ExprVar::bindVars(EvalState & es, + const std::shared_ptr & env) { if (es.debugRepl) es.exprEnvs.insert(std::make_pair(this, env)); @@ -333,7 +353,8 @@ void ExprVar::bindVars(EvalState & es, const std::shared_ptr & int withLevel = -1; for (curEnv = env.get(), level = 0; curEnv; curEnv = curEnv->up, level++) { if (curEnv->isWith) { - if (withLevel == -1) withLevel = level; + if (withLevel == -1) + withLevel = level; } else { auto i = curEnv->find(name); if (i != curEnv->vars.end()) { @@ -349,27 +370,29 @@ void ExprVar::bindVars(EvalState & es, const std::shared_ptr & enclosing `with'. If there is no `with', then we can issue an "undefined variable" error now. */ if (withLevel == -1) - throw UndefinedVarError({ - .msg = hintfmt("undefined variable '%1%'", es.symbols[name]), - .errPos = es.positions[pos] - }); + throw UndefinedVarError( + {.msg = hintfmt("undefined variable '%1%'", es.symbols[name]), + .errPos = es.positions[pos]}); fromWith = true; this->level = withLevel; } -void ExprSelect::bindVars(EvalState & es, const std::shared_ptr & env) +void ExprSelect::bindVars(EvalState & es, + const std::shared_ptr & env) { if (es.debugRepl) es.exprEnvs.insert(std::make_pair(this, env)); e->bindVars(es, env); - if (def) def->bindVars(es, env); + if (def) + def->bindVars(es, env); for (auto & i : attrPath) if (!i.symbol) i.expr->bindVars(es, env); } -void ExprOpHasAttr::bindVars(EvalState & es, const std::shared_ptr & env) +void ExprOpHasAttr::bindVars(EvalState & es, + const std::shared_ptr & env) { if (es.debugRepl) es.exprEnvs.insert(std::make_pair(this, env)); @@ -380,13 +403,15 @@ void ExprOpHasAttr::bindVars(EvalState & es, const std::shared_ptrbindVars(es, env); } -void ExprAttrs::bindVars(EvalState & es, const std::shared_ptr & env) +void ExprAttrs::bindVars(EvalState & es, + const std::shared_ptr & env) { if (es.debugRepl) es.exprEnvs.insert(std::make_pair(this, env)); if (recursive) { - auto newEnv = std::make_shared(false, env.get(), recursive ? attrs.size() : 0); + auto newEnv = std::make_shared(false, env.get(), + recursive ? attrs.size() : 0); Displacement displ = 0; for (auto & i : attrs) @@ -401,8 +426,7 @@ void ExprAttrs::bindVars(EvalState & es, const std::shared_ptr i.nameExpr->bindVars(es, newEnv); i.valueExpr->bindVars(es, newEnv); } - } - else { + } else { for (auto & i : attrs) i.second.e->bindVars(es, env); @@ -413,7 +437,8 @@ void ExprAttrs::bindVars(EvalState & es, const std::shared_ptr } } -void ExprList::bindVars(EvalState & es, const std::shared_ptr & env) +void ExprList::bindVars(EvalState & es, + const std::shared_ptr & env) { if (es.debugRepl) es.exprEnvs.insert(std::make_pair(this, env)); @@ -422,19 +447,20 @@ void ExprList::bindVars(EvalState & es, const std::shared_ptr & i->bindVars(es, env); } -void ExprLambda::bindVars(EvalState & es, const std::shared_ptr & env) +void ExprLambda::bindVars(EvalState & es, + const std::shared_ptr & env) { if (es.debugRepl) es.exprEnvs.insert(std::make_pair(this, env)); auto newEnv = std::make_shared( false, env.get(), - (hasFormals() ? formals->formals.size() : 0) + - (!arg ? 0 : 1)); + (hasFormals() ? formals->formals.size() : 0) + (!arg ? 0 : 1)); Displacement displ = 0; - if (arg) newEnv->vars.emplace_back(arg, displ++); + if (arg) + newEnv->vars.emplace_back(arg, displ++); if (hasFormals()) { for (auto & i : formals->formals) @@ -443,13 +469,15 @@ void ExprLambda::bindVars(EvalState & es, const std::shared_ptr newEnv->sort(); for (auto & i : formals->formals) - if (i.def) i.def->bindVars(es, newEnv); + if (i.def) + i.def->bindVars(es, newEnv); } body->bindVars(es, newEnv); } -void ExprCall::bindVars(EvalState & es, const std::shared_ptr & env) +void ExprCall::bindVars(EvalState & es, + const std::shared_ptr & env) { if (es.debugRepl) es.exprEnvs.insert(std::make_pair(this, env)); @@ -459,12 +487,14 @@ void ExprCall::bindVars(EvalState & es, const std::shared_ptr & e->bindVars(es, env); } -void ExprLet::bindVars(EvalState & es, const std::shared_ptr & env) +void ExprLet::bindVars(EvalState & es, + const std::shared_ptr & env) { if (es.debugRepl) es.exprEnvs.insert(std::make_pair(this, env)); - auto newEnv = std::make_shared(false, env.get(), attrs->attrs.size()); + auto newEnv = + std::make_shared(false, env.get(), attrs->attrs.size()); Displacement displ = 0; for (auto & i : attrs->attrs) @@ -478,7 +508,8 @@ void ExprLet::bindVars(EvalState & es, const std::shared_ptr & body->bindVars(es, newEnv); } -void ExprWith::bindVars(EvalState & es, const std::shared_ptr & env) +void ExprWith::bindVars(EvalState & es, + const std::shared_ptr & env) { if (es.debugRepl) es.exprEnvs.insert(std::make_pair(this, env)); @@ -503,7 +534,8 @@ void ExprWith::bindVars(EvalState & es, const std::shared_ptr & body->bindVars(es, newEnv); } -void ExprIf::bindVars(EvalState & es, const std::shared_ptr & env) +void ExprIf::bindVars(EvalState & es, + const std::shared_ptr & env) { if (es.debugRepl) es.exprEnvs.insert(std::make_pair(this, env)); @@ -513,7 +545,8 @@ void ExprIf::bindVars(EvalState & es, const std::shared_ptr & e else_->bindVars(es, env); } -void ExprAssert::bindVars(EvalState & es, const std::shared_ptr & env) +void ExprAssert::bindVars(EvalState & es, + const std::shared_ptr & env) { if (es.debugRepl) es.exprEnvs.insert(std::make_pair(this, env)); @@ -522,7 +555,8 @@ void ExprAssert::bindVars(EvalState & es, const std::shared_ptr body->bindVars(es, env); } -void ExprOpNot::bindVars(EvalState & es, const std::shared_ptr & env) +void ExprOpNot::bindVars(EvalState & es, + const std::shared_ptr & env) { if (es.debugRepl) es.exprEnvs.insert(std::make_pair(this, env)); @@ -530,7 +564,8 @@ void ExprOpNot::bindVars(EvalState & es, const std::shared_ptr e->bindVars(es, env); } -void ExprConcatStrings::bindVars(EvalState & es, const std::shared_ptr & env) +void ExprConcatStrings::bindVars(EvalState & es, + const std::shared_ptr & env) { if (es.debugRepl) es.exprEnvs.insert(std::make_pair(this, env)); @@ -539,19 +574,16 @@ void ExprConcatStrings::bindVars(EvalState & es, const std::shared_ptrbindVars(es, env); } -void ExprPos::bindVars(EvalState & es, const std::shared_ptr & env) +void ExprPos::bindVars(EvalState & es, + const std::shared_ptr & env) { if (es.debugRepl) es.exprEnvs.insert(std::make_pair(this, env)); } - /* Storing function names. */ -void Expr::setName(Symbol name) -{ -} - +void Expr::setName(Symbol name) {} void ExprLambda::setName(Symbol name) { @@ -559,23 +591,19 @@ void ExprLambda::setName(Symbol name) body->setName(name); } - std::string ExprLambda::showNamePos(const EvalState & state) const { - std::string id(name - ? concatStrings("'", state.symbols[name], "'") - : "anonymous function"); + std::string id(name ? concatStrings("'", state.symbols[name], "'") + : "anonymous function"); return fmt("%1% at %2%", id, state.positions[pos]); } - - /* Symbol table. */ size_t SymbolTable::totalSize() const { size_t n = 0; - dump([&] (const std::string & s) { n += s.size(); }); + dump([&](const std::string & s) { n += s.size(); }); return n; } diff --git a/src/libexpr/nixexpr.hh b/src/libexpr/nixexpr.hh index 5eb0227707f3..0fba5ee6fa76 100644 --- a/src/libexpr/nixexpr.hh +++ b/src/libexpr/nixexpr.hh @@ -8,10 +8,8 @@ #include "error.hh" #include "chunked-vector.hh" - namespace nix { - MakeError(EvalError, Error); MakeError(ParseError, Error); MakeError(AssertionError, EvalError); @@ -24,8 +22,7 @@ MakeError(RestrictedPathError, Error); /* Position objects. */ -struct Pos -{ +struct Pos { std::string file; FileOrigin origin; uint32_t line; @@ -37,12 +34,12 @@ struct Pos class PosIdx { friend class PosTable; -private: + private: uint32_t id; - explicit PosIdx(uint32_t id): id(id) {} + explicit PosIdx(uint32_t id) : id(id) {} -public: + public: PosIdx() : id(0) {} explicit operator bool() const { return id > 0; } @@ -50,39 +47,40 @@ public: bool operator<(const PosIdx other) const { return id < other.id; } }; -class PosTable -{ -public: +class PosTable { + public: class Origin { friend PosTable; - private: - // must always be invalid by default, add() replaces this with the actual value. - // subsequent add() calls use this index as a token to quickly check whether the - // current origins.back() can be reused or not. + + private: + // must always be invalid by default, add() replaces this with the + // actual value. subsequent add() calls use this index as a token to + // quickly check whether the current origins.back() can be reused or + // not. mutable uint32_t idx = std::numeric_limits::max(); - explicit Origin(uint32_t idx): idx(idx), file{}, origin{} {} + explicit Origin(uint32_t idx) : idx(idx), file{}, origin{} {} - public: + public: const std::string file; const FileOrigin origin; - Origin(std::string file, FileOrigin origin): file(std::move(file)), origin(origin) {} + Origin(std::string file, FileOrigin origin) + : file(std::move(file)), origin(origin) + { + } }; struct Offset { uint32_t line, column; }; -private: + private: std::vector origins; ChunkedVector offsets; -public: - PosTable(): offsets(1024) - { - origins.reserve(1024); - } + public: + PosTable() : offsets(1024) { origins.reserve(1024); } PosIdx add(const Origin & origin, uint32_t line, uint32_t column) { @@ -104,7 +102,7 @@ public: key is always 0. */ const auto pastOrigin = std::upper_bound( origins.begin(), origins.end(), Origin(idx), - [] (const auto & a, const auto & b) { return a.idx < b.idx; }); + [](const auto & a, const auto & b) { return a.idx < b.idx; }); const auto origin = *std::prev(pastOrigin); const auto offset = offsets[idx]; return {origin.file, origin.origin, offset.line, offset.column}; @@ -113,49 +111,46 @@ public: inline PosIdx noPos = {}; -std::ostream & operator << (std::ostream & str, const Pos & pos); - +std::ostream & operator<<(std::ostream & str, const Pos & pos); struct Env; struct Value; class EvalState; struct StaticEnv; - /* An attribute path is a sequence of attribute names. */ -struct AttrName -{ +struct AttrName { Symbol symbol; Expr * expr; - AttrName(Symbol s) : symbol(s) {}; - AttrName(Expr * e) : expr(e) {}; + AttrName(Symbol s) : symbol(s){}; + AttrName(Expr * e) : expr(e){}; }; typedef std::vector AttrPath; -std::string showAttrPath(const SymbolTable & symbols, const AttrPath & attrPath); - +std::string showAttrPath(const SymbolTable & symbols, + const AttrPath & attrPath); /* Abstract syntax of Nix expressions. */ -struct Expr -{ - virtual ~Expr() { }; +struct Expr { + virtual ~Expr(){}; virtual void show(const SymbolTable & symbols, std::ostream & str) const; - virtual void bindVars(EvalState & es, const std::shared_ptr & env); + virtual void bindVars(EvalState & es, + const std::shared_ptr & env); virtual void eval(EvalState & state, Env & env, Value & v); virtual Value * maybeThunk(EvalState & state, Env & env); virtual void setName(Symbol name); virtual PosIdx getPos() const { return noPos; } }; -#define COMMON_METHODS \ +#define COMMON_METHODS \ void show(const SymbolTable & symbols, std::ostream & str) const override; \ - void eval(EvalState & state, Env & env, Value & v) override; \ - void bindVars(EvalState & es, const std::shared_ptr & env) override; + void eval(EvalState & state, Env & env, Value & v) override; \ + void bindVars(EvalState & es, \ + const std::shared_ptr & env) override; -struct ExprInt : Expr -{ +struct ExprInt : Expr { NixInt n; Value v; ExprInt(NixInt n) : n(n) { v.mkInt(n); }; @@ -163,8 +158,7 @@ struct ExprInt : Expr COMMON_METHODS }; -struct ExprFloat : Expr -{ +struct ExprFloat : Expr { NixFloat nf; Value v; ExprFloat(NixFloat nf) : nf(nf) { v.mkFloat(nf); }; @@ -172,8 +166,7 @@ struct ExprFloat : Expr COMMON_METHODS }; -struct ExprString : Expr -{ +struct ExprString : Expr { std::string s; Value v; ExprString(std::string s) : s(std::move(s)) { v.mkString(this->s.data()); }; @@ -181,8 +174,7 @@ struct ExprString : Expr COMMON_METHODS }; -struct ExprPath : Expr -{ +struct ExprPath : Expr { std::string s; Value v; ExprPath(std::string s) : s(std::move(s)) { v.mkPath(this->s.c_str()); }; @@ -193,8 +185,7 @@ struct ExprPath : Expr typedef uint32_t Level; typedef uint32_t Displacement; -struct ExprVar : Expr -{ +struct ExprVar : Expr { PosIdx pos; Symbol name; @@ -211,35 +202,39 @@ struct ExprVar : Expr Level level; Displacement displ; - ExprVar(Symbol name) : name(name) { }; - ExprVar(const PosIdx & pos, Symbol name) : pos(pos), name(name) { }; + ExprVar(Symbol name) : name(name){}; + ExprVar(const PosIdx & pos, Symbol name) : pos(pos), name(name){}; Value * maybeThunk(EvalState & state, Env & env) override; PosIdx getPos() const override { return pos; } COMMON_METHODS }; -struct ExprSelect : Expr -{ +struct ExprSelect : Expr { PosIdx pos; - Expr * e, * def; + Expr *e, *def; AttrPath attrPath; - ExprSelect(const PosIdx & pos, Expr * e, const AttrPath & attrPath, Expr * def) : pos(pos), e(e), def(def), attrPath(attrPath) { }; - ExprSelect(const PosIdx & pos, Expr * e, Symbol name) : pos(pos), e(e), def(0) { attrPath.push_back(AttrName(name)); }; + ExprSelect(const PosIdx & pos, Expr * e, const AttrPath & attrPath, + Expr * def) + : pos(pos), e(e), def(def), attrPath(attrPath){}; + ExprSelect(const PosIdx & pos, Expr * e, Symbol name) + : pos(pos), e(e), def(0) + { + attrPath.push_back(AttrName(name)); + }; PosIdx getPos() const override { return pos; } COMMON_METHODS }; -struct ExprOpHasAttr : Expr -{ +struct ExprOpHasAttr : Expr { Expr * e; AttrPath attrPath; - ExprOpHasAttr(Expr * e, const AttrPath & attrPath) : e(e), attrPath(attrPath) { }; + ExprOpHasAttr(Expr * e, const AttrPath & attrPath) + : e(e), attrPath(attrPath){}; PosIdx getPos() const override { return e->getPos(); } COMMON_METHODS }; -struct ExprAttrs : Expr -{ +struct ExprAttrs : Expr { bool recursive; PosIdx pos; struct AttrDef { @@ -247,30 +242,29 @@ struct ExprAttrs : Expr Expr * e; PosIdx pos; Displacement displ; // displacement - AttrDef(Expr * e, const PosIdx & pos, bool inherited=false) - : inherited(inherited), e(e), pos(pos) { }; - AttrDef() { }; + AttrDef(Expr * e, const PosIdx & pos, bool inherited = false) + : inherited(inherited), e(e), pos(pos){}; + AttrDef(){}; }; typedef std::map AttrDefs; AttrDefs attrs; struct DynamicAttrDef { - Expr * nameExpr, * valueExpr; + Expr *nameExpr, *valueExpr; PosIdx pos; DynamicAttrDef(Expr * nameExpr, Expr * valueExpr, const PosIdx & pos) - : nameExpr(nameExpr), valueExpr(valueExpr), pos(pos) { }; + : nameExpr(nameExpr), valueExpr(valueExpr), pos(pos){}; }; typedef std::vector DynamicAttrDefs; DynamicAttrDefs dynamicAttrs; - ExprAttrs(const PosIdx &pos) : recursive(false), pos(pos) { }; - ExprAttrs() : recursive(false) { }; + ExprAttrs(const PosIdx & pos) : recursive(false), pos(pos){}; + ExprAttrs() : recursive(false){}; PosIdx getPos() const override { return pos; } COMMON_METHODS }; -struct ExprList : Expr -{ +struct ExprList : Expr { std::vector elems; - ExprList() { }; + ExprList(){}; COMMON_METHODS PosIdx getPos() const override @@ -279,23 +273,22 @@ struct ExprList : Expr } }; -struct Formal -{ +struct Formal { PosIdx pos; Symbol name; Expr * def; }; -struct Formals -{ +struct Formals { typedef std::vector Formals_; Formals_ formals; bool ellipsis; bool has(Symbol arg) const { - auto it = std::lower_bound(formals.begin(), formals.end(), arg, - [] (const Formal & f, const Symbol & sym) { return f.name < sym; }); + auto it = std::lower_bound( + formals.begin(), formals.end(), arg, + [](const Formal & f, const Symbol & sym) { return f.name < sym; }); return it != formals.end() && it->name == arg; } @@ -303,25 +296,23 @@ struct Formals { std::vector result(formals.begin(), formals.end()); std::sort(result.begin(), result.end(), - [&] (const Formal & a, const Formal & b) { - std::string_view sa = symbols[a.name], sb = symbols[b.name]; - return sa < sb; - }); + [&](const Formal & a, const Formal & b) { + std::string_view sa = symbols[a.name], + sb = symbols[b.name]; + return sa < sb; + }); return result; } }; -struct ExprLambda : Expr -{ +struct ExprLambda : Expr { PosIdx pos; Symbol name; Symbol arg; Formals * formals; Expr * body; ExprLambda(PosIdx pos, Symbol arg, Formals * formals, Expr * body) - : pos(pos), arg(arg), formals(formals), body(body) - { - }; + : pos(pos), arg(arg), formals(formals), body(body){}; ExprLambda(PosIdx pos, Formals * formals, Expr * body) : pos(pos), formals(formals), body(body) { @@ -333,113 +324,111 @@ struct ExprLambda : Expr COMMON_METHODS }; -struct ExprCall : Expr -{ +struct ExprCall : Expr { Expr * fun; std::vector args; PosIdx pos; ExprCall(const PosIdx & pos, Expr * fun, std::vector && args) : fun(fun), args(args), pos(pos) - { } + { + } PosIdx getPos() const override { return pos; } COMMON_METHODS }; -struct ExprLet : Expr -{ +struct ExprLet : Expr { ExprAttrs * attrs; Expr * body; - ExprLet(ExprAttrs * attrs, Expr * body) : attrs(attrs), body(body) { }; + ExprLet(ExprAttrs * attrs, Expr * body) : attrs(attrs), body(body){}; COMMON_METHODS }; -struct ExprWith : Expr -{ +struct ExprWith : Expr { PosIdx pos; - Expr * attrs, * body; + Expr *attrs, *body; size_t prevWith; - ExprWith(const PosIdx & pos, Expr * attrs, Expr * body) : pos(pos), attrs(attrs), body(body) { }; + ExprWith(const PosIdx & pos, Expr * attrs, Expr * body) + : pos(pos), attrs(attrs), body(body){}; PosIdx getPos() const override { return pos; } COMMON_METHODS }; -struct ExprIf : Expr -{ +struct ExprIf : Expr { PosIdx pos; - Expr * cond, * then, * else_; - ExprIf(const PosIdx & pos, Expr * cond, Expr * then, Expr * else_) : pos(pos), cond(cond), then(then), else_(else_) { }; + Expr *cond, *then, *else_; + ExprIf(const PosIdx & pos, Expr * cond, Expr * then, Expr * else_) + : pos(pos), cond(cond), then(then), else_(else_){}; PosIdx getPos() const override { return pos; } COMMON_METHODS }; -struct ExprAssert : Expr -{ +struct ExprAssert : Expr { PosIdx pos; - Expr * cond, * body; - ExprAssert(const PosIdx & pos, Expr * cond, Expr * body) : pos(pos), cond(cond), body(body) { }; + Expr *cond, *body; + ExprAssert(const PosIdx & pos, Expr * cond, Expr * body) + : pos(pos), cond(cond), body(body){}; PosIdx getPos() const override { return pos; } COMMON_METHODS }; -struct ExprOpNot : Expr -{ +struct ExprOpNot : Expr { Expr * e; - ExprOpNot(Expr * e) : e(e) { }; + ExprOpNot(Expr * e) : e(e){}; COMMON_METHODS }; -#define MakeBinOp(name, s) \ - struct name : Expr \ - { \ - PosIdx pos; \ - Expr * e1, * e2; \ - name(Expr * e1, Expr * e2) : e1(e1), e2(e2) { }; \ - name(const PosIdx & pos, Expr * e1, Expr * e2) : pos(pos), e1(e1), e2(e2) { }; \ - void show(const SymbolTable & symbols, std::ostream & str) const override \ - { \ - str << "("; e1->show(symbols, str); str << " " s " "; e2->show(symbols, str); str << ")"; \ - } \ - void bindVars(EvalState & es, const std::shared_ptr & env) override \ - { \ - e1->bindVars(es, env); e2->bindVars(es, env); \ - } \ - void eval(EvalState & state, Env & env, Value & v) override; \ - PosIdx getPos() const override { return pos; } \ +#define MakeBinOp(name, s) \ + struct name : Expr { \ + PosIdx pos; \ + Expr *e1, *e2; \ + name(Expr * e1, Expr * e2) : e1(e1), e2(e2){}; \ + name(const PosIdx & pos, Expr * e1, Expr * e2) \ + : pos(pos), e1(e1), e2(e2){}; \ + void show(const SymbolTable & symbols, \ + std::ostream & str) const override \ + { \ + str << "("; \ + e1->show(symbols, str); \ + str << " " s " "; \ + e2->show(symbols, str); \ + str << ")"; \ + } \ + void bindVars(EvalState & es, \ + const std::shared_ptr & env) override \ + { \ + e1->bindVars(es, env); \ + e2->bindVars(es, env); \ + } \ + void eval(EvalState & state, Env & env, Value & v) override; \ + PosIdx getPos() const override { return pos; } \ }; -MakeBinOp(ExprOpEq, "==") -MakeBinOp(ExprOpNEq, "!=") -MakeBinOp(ExprOpAnd, "&&") -MakeBinOp(ExprOpOr, "||") -MakeBinOp(ExprOpImpl, "->") -MakeBinOp(ExprOpUpdate, "//") -MakeBinOp(ExprOpConcatLists, "++") +MakeBinOp(ExprOpEq, "==") MakeBinOp(ExprOpNEq, "!=") MakeBinOp(ExprOpAnd, "&&") + MakeBinOp(ExprOpOr, "||") MakeBinOp(ExprOpImpl, "->") + MakeBinOp(ExprOpUpdate, "//") MakeBinOp(ExprOpConcatLists, "++") -struct ExprConcatStrings : Expr -{ + struct ExprConcatStrings : Expr { PosIdx pos; bool forceString; std::vector> * es; - ExprConcatStrings(const PosIdx & pos, bool forceString, std::vector> * es) - : pos(pos), forceString(forceString), es(es) { }; + ExprConcatStrings(const PosIdx & pos, bool forceString, + std::vector> * es) + : pos(pos), forceString(forceString), es(es){}; PosIdx getPos() const override { return pos; } COMMON_METHODS }; -struct ExprPos : Expr -{ +struct ExprPos : Expr { PosIdx pos; - ExprPos(const PosIdx & pos) : pos(pos) { }; + ExprPos(const PosIdx & pos) : pos(pos){}; PosIdx getPos() const override { return pos; } COMMON_METHODS }; - /* Static environments are used to map variable names onto (level, displacement) pairs used to obtain the value of the variable at runtime. */ -struct StaticEnv -{ +struct StaticEnv { bool isWith; const StaticEnv * up; @@ -447,14 +436,19 @@ struct StaticEnv typedef std::vector> Vars; Vars vars; - StaticEnv(bool isWith, const StaticEnv * up, size_t expectedSize = 0) : isWith(isWith), up(up) { + StaticEnv(bool isWith, const StaticEnv * up, size_t expectedSize = 0) + : isWith(isWith), up(up) + { vars.reserve(expectedSize); }; void sort() { - std::stable_sort(vars.begin(), vars.end(), - [](const Vars::value_type & a, const Vars::value_type & b) { return a.first < b.first; }); + std::stable_sort( + vars.begin(), vars.end(), + [](const Vars::value_type & a, const Vars::value_type & b) { + return a.first < b.first; + }); } void deduplicate() @@ -462,7 +456,8 @@ struct StaticEnv auto it = vars.begin(), jt = it, end = vars.end(); while (jt != end) { *it = *jt++; - while (jt != end && it->first == jt->first) *it = *jt++; + while (jt != end && it->first == jt->first) + *it = *jt++; it++; } vars.erase(it, end); @@ -472,10 +467,10 @@ struct StaticEnv { Vars::value_type key(name, 0); auto i = std::lower_bound(vars.begin(), vars.end(), key); - if (i != vars.end() && i->first == name) return i; + if (i != vars.end() && i->first == name) + return i; return vars.end(); } }; - } diff --git a/src/libexpr/primops.cc b/src/libexpr/primops.cc index eea2743010eb..206b5051980c 100644 --- a/src/libexpr/primops.cc +++ b/src/libexpr/primops.cc @@ -25,17 +25,16 @@ #include - namespace nix { - /************************************************************* * Miscellaneous *************************************************************/ - -InvalidPathError::InvalidPathError(const Path & path) : - EvalError("path '%s' is not valid", path), path(path) {} +InvalidPathError::InvalidPathError(const Path & path) + : EvalError("path '%s' is not valid", path), path(path) +{ +} StringMap EvalState::realiseContext(const PathSet & context) { @@ -54,16 +53,19 @@ StringMap EvalState::realiseContext(const PathSet & context) } } - if (drvs.empty()) return {}; + if (drvs.empty()) + return {}; if (!evalSettings.enableImportFromDerivation) - debugThrowLastTrace(Error( - "cannot build '%1%' during evaluation because the option 'allow-import-from-derivation' is disabled", - store->printStorePath(drvs.begin()->drvPath))); + debugThrowLastTrace( + Error("cannot build '%1%' during evaluation because the option " + "'allow-import-from-derivation' is disabled", + store->printStorePath(drvs.begin()->drvPath))); /* Build/substitute the context. */ std::vector buildReqs; - for (auto & d : drvs) buildReqs.emplace_back(DerivedPath { d }); + for (auto & d : drvs) + buildReqs.emplace_back(DerivedPath{d}); store->buildPaths(buildReqs); /* Get all the output paths corresponding to the placeholders we had */ @@ -72,12 +74,12 @@ StringMap EvalState::realiseContext(const PathSet & context) for (auto & outputName : outputs) { auto outputPath = get(outputPaths, outputName); if (!outputPath) - debugThrowLastTrace(Error("derivation '%s' does not have an output named '%s'", - store->printStorePath(drvPath), outputName)); + debugThrowLastTrace( + Error("derivation '%s' does not have an output named '%s'", + store->printStorePath(drvPath), outputName)); res.insert_or_assign( downstreamPlaceholder(*store, drvPath, outputName), - store->printStorePath(*outputPath) - ); + store->printStorePath(*outputPath)); } } @@ -97,16 +99,17 @@ struct RealisePathFlags { bool checkForPureEval = true; }; -static Path realisePath(EvalState & state, const PosIdx pos, Value & v, const RealisePathFlags flags = {}) +static Path realisePath(EvalState & state, const PosIdx pos, Value & v, + const RealisePathFlags flags = {}) { PathSet context; - auto path = [&]() - { + auto path = [&]() { try { return state.coerceToPath(pos, v, context); } catch (Error & e) { - e.addTrace(state.positions[pos], "while realising the context of a path"); + e.addTrace(state.positions[pos], + "while realising the context of a path"); throw; } }(); @@ -114,13 +117,14 @@ static Path realisePath(EvalState & state, const PosIdx pos, Value & v, const Re try { StringMap rewrites = state.realiseContext(context); - auto realPath = state.toRealPath(rewriteStrings(path, rewrites), context); + auto realPath = + state.toRealPath(rewriteStrings(path, rewrites), context); - return flags.checkForPureEval - ? state.checkSourcePath(realPath) - : realPath; + return flags.checkForPureEval ? state.checkSourcePath(realPath) + : realPath; } catch (Error & e) { - e.addTrace(state.positions[pos], "while realising the context of path '%s'", path); + e.addTrace(state.positions[pos], + "while realising the context of path '%s'", path); throw; } } @@ -137,28 +141,26 @@ static Path realisePath(EvalState & state, const PosIdx pos, Value & v, const Re the actual path. The 'drv' and 'drvPath' outputs must correspond. */ -static void mkOutputString( - EvalState & state, - BindingsBuilder & attrs, - const StorePath & drvPath, - const BasicDerivation & drv, - const std::pair & o) +static void mkOutputString(EvalState & state, BindingsBuilder & attrs, + const StorePath & drvPath, + const BasicDerivation & drv, + const std::pair & o) { auto optOutputPath = o.second.path(*state.store, drv.name, o.first); attrs.alloc(o.first).mkString( - optOutputPath - ? state.store->printStorePath(*optOutputPath) - /* Downstream we would substitute this for an actual path once - we build the floating CA derivation */ - /* FIXME: we need to depend on the basic derivation, not - derivation */ - : downstreamPlaceholder(*state.store, drvPath, o.first), + optOutputPath ? state.store->printStorePath(*optOutputPath) + /* Downstream we would substitute this for an actual path + once we build the floating CA derivation */ + /* FIXME: we need to depend on the basic derivation, not + derivation */ + : downstreamPlaceholder(*state.store, drvPath, o.first), {"!" + o.first + "!" + state.store->printStorePath(drvPath)}); } /* Load and evaluate an expression from path specified by the argument. */ -static void import(EvalState & state, const PosIdx pos, Value & vPath, Value * vScope, Value & v) +static void import(EvalState & state, const PosIdx pos, Value & vPath, + Value * vScope, Value & v) { auto path = realisePath(state, pos, vPath); @@ -192,8 +194,9 @@ static void import(EvalState & state, const PosIdx pos, Value & vPath, Value * v if (!state.vImportedDrvToDerivation) { state.vImportedDrvToDerivation = allocRootValue(state.allocValue()); state.eval(state.parseExprFromString( - #include "imported-drv-to-derivation.nix.gen.hh" - , "/"), **state.vImportedDrvToDerivation); +#include "imported-drv-to-derivation.nix.gen.hh" + , "/"), + **state.vImportedDrvToDerivation); } state.forceFunction(**state.vImportedDrvToDerivation, pos); @@ -203,8 +206,9 @@ static void import(EvalState & state, const PosIdx pos, Value & vPath, Value * v else if (path == corepkgsPrefix + "fetchurl.nix") { state.eval(state.parseExprFromString( - #include "fetchurl.nix.gen.hh" - , "/"), v); +#include "fetchurl.nix.gen.hh" + , "/"), + v); } else { @@ -216,7 +220,8 @@ static void import(EvalState & state, const PosIdx pos, Value & vPath, Value * v Env * env = &state.allocEnv(vScope->attrs->size()); env->up = &state.baseEnv; - auto staticEnv = std::make_shared(false, state.staticBaseEnv.get(), vScope->attrs->size()); + auto staticEnv = std::make_shared( + false, state.staticBaseEnv.get(), vScope->attrs->size()); unsigned int displ = 0; for (auto & attr : *vScope->attrs) { @@ -228,26 +233,25 @@ static void import(EvalState & state, const PosIdx pos, Value & vPath, Value * v // args[0]->attrs is already sorted. printTalkative("evaluating file '%1%'", path); - Expr * e = state.parseExprFromFile(resolveExprPath(path), staticEnv); + Expr * e = + state.parseExprFromFile(resolveExprPath(path), staticEnv); e->eval(state, *env, v); } } } -static RegisterPrimOp primop_scopedImport(RegisterPrimOp::Info { +static RegisterPrimOp primop_scopedImport(RegisterPrimOp::Info{ .name = "scopedImport", .arity = 2, - .fun = [](EvalState & state, const PosIdx pos, Value * * args, Value & v) - { + .fun = [](EvalState & state, const PosIdx pos, Value ** args, Value & v) { import(state, pos, *args[1], args[0], v); - } -}); + }}); -static RegisterPrimOp primop_import({ - .name = "import", - .args = {"path"}, - .doc = R"( +static RegisterPrimOp primop_import( + {.name = "import", + .args = {"path"}, + .doc = R"( Load, parse and return the Nix expression in the file *path*. If *path* is a directory, the file ` default.nix ` in that directory is loaded. Evaluation aborts if the file doesn’t exist or contains @@ -300,68 +304,75 @@ static RegisterPrimOp primop_import({ (The function argument doesn’t have to be called `x` in `foo.nix`; any name would work.) )", - .fun = [](EvalState & state, const PosIdx pos, Value * * args, Value & v) - { - import(state, pos, *args[0], nullptr, v); - } -}); + .fun = [](EvalState & state, const PosIdx pos, Value ** args, Value & v) { + import(state, pos, *args[0], nullptr, v); + }}); /* Want reasonable symbol names, so extern C */ /* !!! Should we pass the Pos or the file name too? */ extern "C" typedef void (*ValueInitializer)(EvalState & state, Value & v); /* Load a ValueInitializer from a DSO and return whatever it initializes */ -void prim_importNative(EvalState & state, const PosIdx pos, Value * * args, Value & v) +void prim_importNative(EvalState & state, const PosIdx pos, Value ** args, + Value & v) { auto path = realisePath(state, pos, *args[0]); std::string sym(state.forceStringNoCtx(*args[1], pos)); - void *handle = dlopen(path.c_str(), RTLD_LAZY | RTLD_LOCAL); + void * handle = dlopen(path.c_str(), RTLD_LAZY | RTLD_LOCAL); if (!handle) - state.debugThrowLastTrace(EvalError("could not open '%1%': %2%", path, dlerror())); + state.debugThrowLastTrace( + EvalError("could not open '%1%': %2%", path, dlerror())); dlerror(); ValueInitializer func = (ValueInitializer) dlsym(handle, sym.c_str()); - if(!func) { - char *message = dlerror(); + if (!func) { + char * message = dlerror(); if (message) - state.debugThrowLastTrace(EvalError("could not load symbol '%1%' from '%2%': %3%", sym, path, message)); + state.debugThrowLastTrace( + EvalError("could not load symbol '%1%' from '%2%': %3%", sym, + path, message)); else - state.debugThrowLastTrace(EvalError("symbol '%1%' from '%2%' resolved to NULL when a function pointer was expected", sym, path)); + state.debugThrowLastTrace( + EvalError("symbol '%1%' from '%2%' resolved to NULL when a " + "function pointer was expected", + sym, path)); } (func)(state, v); - /* We don't dlclose because v may be a primop referencing a function in the shared object file */ + /* We don't dlclose because v may be a primop referencing a function in the + * shared object file */ } - /* Execute a program and parse its output */ -void prim_exec(EvalState & state, const PosIdx pos, Value * * args, Value & v) +void prim_exec(EvalState & state, const PosIdx pos, Value ** args, Value & v) { state.forceList(*args[0], pos); auto elems = args[0]->listElems(); auto count = args[0]->listSize(); if (count == 0) - state.debugThrowLastTrace(EvalError({ - .msg = hintfmt("at least one argument to 'exec' required"), - .errPos = state.positions[pos] - })); + state.debugThrowLastTrace(EvalError( + {.msg = hintfmt("at least one argument to 'exec' required"), + .errPos = state.positions[pos]})); PathSet context; - auto program = state.coerceToString(pos, *elems[0], context, false, false).toOwned(); + auto program = + state.coerceToString(pos, *elems[0], context, false, false).toOwned(); Strings commandArgs; for (unsigned int i = 1; i < args[0]->listSize(); ++i) { - commandArgs.push_back(state.coerceToString(pos, *elems[i], context, false, false).toOwned()); + commandArgs.push_back( + state.coerceToString(pos, *elems[i], context, false, false) + .toOwned()); } try { auto _ = state.realiseContext(context); // FIXME: Handle CA derivations } catch (InvalidPathError & e) { - state.debugThrowLastTrace(EvalError({ - .msg = hintfmt("cannot execute '%1%', since path '%2%' is not valid", - program, e.path), - .errPos = state.positions[pos] - })); + state.debugThrowLastTrace(EvalError( + {.msg = + hintfmt("cannot execute '%1%', since path '%2%' is not valid", + program, e.path), + .errPos = state.positions[pos]})); } auto output = runProgram(program, true, commandArgs); @@ -370,37 +381,58 @@ void prim_exec(EvalState & state, const PosIdx pos, Value * * args, Value & v) auto base = state.positions[pos]; parsed = state.parseExprFromString(std::move(output), base.file); } catch (Error & e) { - e.addTrace(state.positions[pos], "While parsing the output from '%1%'", program); + e.addTrace(state.positions[pos], "While parsing the output from '%1%'", + program); throw; } try { state.eval(parsed, v); } catch (Error & e) { - e.addTrace(state.positions[pos], "While evaluating the output from '%1%'", program); + e.addTrace(state.positions[pos], + "While evaluating the output from '%1%'", program); throw; } } - /* Return a string representing the type of the expression. */ -static void prim_typeOf(EvalState & state, const PosIdx pos, Value * * args, Value & v) +static void prim_typeOf(EvalState & state, const PosIdx pos, Value ** args, + Value & v) { state.forceValue(*args[0], pos); std::string t; switch (args[0]->type()) { - case nInt: t = "int"; break; - case nBool: t = "bool"; break; - case nString: t = "string"; break; - case nPath: t = "path"; break; - case nNull: t = "null"; break; - case nAttrs: t = "set"; break; - case nList: t = "list"; break; - case nFunction: t = "lambda"; break; - case nExternal: - t = args[0]->external->typeOf(); - break; - case nFloat: t = "float"; break; - case nThunk: abort(); + case nInt: + t = "int"; + break; + case nBool: + t = "bool"; + break; + case nString: + t = "string"; + break; + case nPath: + t = "path"; + break; + case nNull: + t = "null"; + break; + case nAttrs: + t = "set"; + break; + case nList: + t = "list"; + break; + case nFunction: + t = "lambda"; + break; + case nExternal: + t = args[0]->external->typeOf(); + break; + case nFloat: + t = "float"; + break; + case nThunk: + abort(); } v.mkString(t); } @@ -417,7 +449,8 @@ static RegisterPrimOp primop_typeOf({ }); /* Determine whether the argument is the null value. */ -static void prim_isNull(EvalState & state, const PosIdx pos, Value * * args, Value & v) +static void prim_isNull(EvalState & state, const PosIdx pos, Value ** args, + Value & v) { state.forceValue(*args[0], pos); v.mkBool(args[0]->type() == nNull); @@ -437,7 +470,8 @@ static RegisterPrimOp primop_isNull({ }); /* Determine whether the argument is a function. */ -static void prim_isFunction(EvalState & state, const PosIdx pos, Value * * args, Value & v) +static void prim_isFunction(EvalState & state, const PosIdx pos, Value ** args, + Value & v) { state.forceValue(*args[0], pos); v.mkBool(args[0]->type() == nFunction); @@ -453,7 +487,8 @@ static RegisterPrimOp primop_isFunction({ }); /* Determine whether the argument is an integer. */ -static void prim_isInt(EvalState & state, const PosIdx pos, Value * * args, Value & v) +static void prim_isInt(EvalState & state, const PosIdx pos, Value ** args, + Value & v) { state.forceValue(*args[0], pos); v.mkBool(args[0]->type() == nInt); @@ -469,7 +504,8 @@ static RegisterPrimOp primop_isInt({ }); /* Determine whether the argument is a float. */ -static void prim_isFloat(EvalState & state, const PosIdx pos, Value * * args, Value & v) +static void prim_isFloat(EvalState & state, const PosIdx pos, Value ** args, + Value & v) { state.forceValue(*args[0], pos); v.mkBool(args[0]->type() == nFloat); @@ -485,7 +521,8 @@ static RegisterPrimOp primop_isFloat({ }); /* Determine whether the argument is a string. */ -static void prim_isString(EvalState & state, const PosIdx pos, Value * * args, Value & v) +static void prim_isString(EvalState & state, const PosIdx pos, Value ** args, + Value & v) { state.forceValue(*args[0], pos); v.mkBool(args[0]->type() == nString); @@ -501,7 +538,8 @@ static RegisterPrimOp primop_isString({ }); /* Determine whether the argument is a Boolean. */ -static void prim_isBool(EvalState & state, const PosIdx pos, Value * * args, Value & v) +static void prim_isBool(EvalState & state, const PosIdx pos, Value ** args, + Value & v) { state.forceValue(*args[0], pos); v.mkBool(args[0]->type() == nBool); @@ -517,7 +555,8 @@ static RegisterPrimOp primop_isBool({ }); /* Determine whether the argument is a path. */ -static void prim_isPath(EvalState & state, const PosIdx pos, Value * * args, Value & v) +static void prim_isPath(EvalState & state, const PosIdx pos, Value ** args, + Value & v) { state.forceValue(*args[0], pos); v.mkBool(args[0]->type() == nPath); @@ -532,68 +571,62 @@ static RegisterPrimOp primop_isPath({ .fun = prim_isPath, }); -struct CompareValues -{ +struct CompareValues { EvalState & state; - CompareValues(EvalState & state) : state(state) { }; + CompareValues(EvalState & state) : state(state){}; - bool operator () (Value * v1, Value * v2) const + bool operator()(Value * v1, Value * v2) const { if (v1->type() == nFloat && v2->type() == nInt) return v1->fpoint < v2->integer; if (v1->type() == nInt && v2->type() == nFloat) return v1->integer < v2->fpoint; if (v1->type() != v2->type()) - state.debugThrowLastTrace(EvalError("cannot compare %1% with %2%", showType(*v1), showType(*v2))); + state.debugThrowLastTrace(EvalError("cannot compare %1% with %2%", + showType(*v1), showType(*v2))); switch (v1->type()) { - case nInt: - return v1->integer < v2->integer; - case nFloat: - return v1->fpoint < v2->fpoint; - case nString: - return strcmp(v1->string.s, v2->string.s) < 0; - case nPath: - return strcmp(v1->path, v2->path) < 0; - case nList: - // Lexicographic comparison - for (size_t i = 0;; i++) { - if (i == v2->listSize()) { - return false; - } else if (i == v1->listSize()) { - return true; - } else if (!state.eqValues(*v1->listElems()[i], *v2->listElems()[i])) { - return (*this)(v1->listElems()[i], v2->listElems()[i]); - } + case nInt: + return v1->integer < v2->integer; + case nFloat: + return v1->fpoint < v2->fpoint; + case nString: + return strcmp(v1->string.s, v2->string.s) < 0; + case nPath: + return strcmp(v1->path, v2->path) < 0; + case nList: + // Lexicographic comparison + for (size_t i = 0;; i++) { + if (i == v2->listSize()) { + return false; + } else if (i == v1->listSize()) { + return true; + } else if (!state.eqValues(*v1->listElems()[i], + *v2->listElems()[i])) { + return (*this)(v1->listElems()[i], v2->listElems()[i]); } - default: - state.debugThrowLastTrace(EvalError("cannot compare %1% with %2%", showType(*v1), showType(*v2))); + } + default: + state.debugThrowLastTrace(EvalError("cannot compare %1% with %2%", + showType(*v1), showType(*v2))); } } }; - #if HAVE_BOEHMGC typedef std::list> ValueList; #else typedef std::list ValueList; #endif - -static Bindings::iterator getAttr( - EvalState & state, - std::string_view funcName, - Symbol attrSym, - Bindings * attrSet, - const PosIdx pos) +static Bindings::iterator getAttr(EvalState & state, std::string_view funcName, + Symbol attrSym, Bindings * attrSet, + const PosIdx pos) { Bindings::iterator value = attrSet->find(attrSym); if (value == attrSet->end()) { - hintformat errorMsg = hintfmt( - "attribute '%s' missing for call to '%s'", - state.symbols[attrSym], - funcName - ); + hintformat errorMsg = hintfmt("attribute '%s' missing for call to '%s'", + state.symbols[attrSym], funcName); auto aPos = attrSet->pos; if (!aPos) { @@ -609,7 +642,8 @@ static Bindings::iterator getAttr( // Adding another trace for the function name to make it clear // which call received wrong arguments. - e.addTrace(state.positions[pos], hintfmt("while invoking '%s'", funcName)); + e.addTrace(state.positions[pos], + hintfmt("while invoking '%s'", funcName)); state.debugThrowLastTrace(e); } } @@ -617,18 +651,14 @@ static Bindings::iterator getAttr( return value; } -static void prim_genericClosure(EvalState & state, const PosIdx pos, Value * * args, Value & v) +static void prim_genericClosure(EvalState & state, const PosIdx pos, + Value ** args, Value & v) { state.forceAttrs(*args[0], pos); /* Get the start set. */ - Bindings::iterator startSet = getAttr( - state, - "genericClosure", - state.sStartSet, - args[0]->attrs, - pos - ); + Bindings::iterator startSet = + getAttr(state, "genericClosure", state.sStartSet, args[0]->attrs, pos); state.forceList(*startSet->value, pos); @@ -637,13 +667,8 @@ static void prim_genericClosure(EvalState & state, const PosIdx pos, Value * * a workSet.push_back(elem); /* Get the operator. */ - Bindings::iterator op = getAttr( - state, - "genericClosure", - state.sOperator, - args[0]->attrs, - pos - ); + Bindings::iterator op = + getAttr(state, "genericClosure", state.sOperator, args[0]->attrs, pos); state.forceValue(*op->value, pos); @@ -661,16 +686,15 @@ static void prim_genericClosure(EvalState & state, const PosIdx pos, Value * * a state.forceAttrs(*e, pos); - Bindings::iterator key = - e->attrs->find(state.sKey); + Bindings::iterator key = e->attrs->find(state.sKey); if (key == e->attrs->end()) - state.debugThrowLastTrace(EvalError({ - .msg = hintfmt("attribute 'key' required"), - .errPos = state.positions[pos] - })); + state.debugThrowLastTrace( + EvalError({.msg = hintfmt("attribute 'key' required"), + .errPos = state.positions[pos]})); state.forceValue(*key->value, pos); - if (!doneKeys.insert(key->value).second) continue; + if (!doneKeys.insert(key->value).second) + continue; res.push_back(e); /* Call the `operator' function with `e' as argument. */ @@ -692,7 +716,7 @@ static void prim_genericClosure(EvalState & state, const PosIdx pos, Value * * a v.listElems()[n++] = i; } -static RegisterPrimOp primop_genericClosure(RegisterPrimOp::Info { +static RegisterPrimOp primop_genericClosure(RegisterPrimOp::Info{ .name = "__genericClosure", .args = {"attrset"}, .arity = 1, @@ -723,92 +747,89 @@ static RegisterPrimOp primop_genericClosure(RegisterPrimOp::Info { .fun = prim_genericClosure, }); - -static RegisterPrimOp primop_break({ - .name = "break", - .args = {"v"}, - .doc = R"( +static RegisterPrimOp primop_break( + {.name = "break", + .args = {"v"}, + .doc = R"( In debug mode (enabled using `--debugger`), pause Nix expression evaluation and enter the REPL. Otherwise, return the argument `v`. )", - .fun = [](EvalState & state, const PosIdx pos, Value * * args, Value & v) - { - if (state.debugRepl && !state.debugTraces.empty()) { - auto error = Error(ErrorInfo { - .level = lvlInfo, - .msg = hintfmt("breakpoint reached"), - .errPos = state.positions[pos], - }); - - auto & dt = state.debugTraces.front(); - state.runDebugRepl(&error, dt.env, dt.expr); - - if (state.debugQuit) { - // If the user elects to quit the repl, throw an exception. - throw Error(ErrorInfo{ - .level = lvlInfo, - .msg = hintfmt("quit the debugger"), - .errPos = state.positions[noPos], - }); - } - } - - // Return the value we were passed. - v = *args[0]; - } -}); - -static RegisterPrimOp primop_abort({ - .name = "abort", - .args = {"s"}, - .doc = R"( + .fun = [](EvalState & state, const PosIdx pos, Value ** args, Value & v) { + if (state.debugRepl && !state.debugTraces.empty()) { + auto error = Error(ErrorInfo{ + .level = lvlInfo, + .msg = hintfmt("breakpoint reached"), + .errPos = state.positions[pos], + }); + + auto & dt = state.debugTraces.front(); + state.runDebugRepl(&error, dt.env, dt.expr); + + if (state.debugQuit) { + // If the user elects to quit the repl, throw an exception. + throw Error(ErrorInfo{ + .level = lvlInfo, + .msg = hintfmt("quit the debugger"), + .errPos = state.positions[noPos], + }); + } + } + + // Return the value we were passed. + v = *args[0]; + }}); + +static RegisterPrimOp primop_abort( + {.name = "abort", + .args = {"s"}, + .doc = R"( Abort Nix expression evaluation and print the error message *s*. )", - .fun = [](EvalState & state, const PosIdx pos, Value * * args, Value & v) - { - PathSet context; - auto s = state.coerceToString(pos, *args[0], context).toOwned(); - state.debugThrowLastTrace(Abort("evaluation aborted with the following error message: '%1%'", s)); - } -}); - -static RegisterPrimOp primop_throw({ - .name = "throw", - .args = {"s"}, - .doc = R"( + .fun = [](EvalState & state, const PosIdx pos, Value ** args, Value & v) { + PathSet context; + auto s = state.coerceToString(pos, *args[0], context).toOwned(); + state.debugThrowLastTrace(Abort( + "evaluation aborted with the following error message: '%1%'", s)); + }}); + +static RegisterPrimOp primop_throw( + {.name = "throw", + .args = {"s"}, + .doc = R"( Throw an error message *s*. This usually aborts Nix expression evaluation, but in `nix-env -qa` and other commands that try to evaluate a set of derivations to get information about those derivations, a derivation that throws an error is silently skipped (which is not the case for `abort`). )", - .fun = [](EvalState & state, const PosIdx pos, Value * * args, Value & v) - { - PathSet context; - auto s = state.coerceToString(pos, *args[0], context).toOwned(); - state.debugThrowLastTrace(ThrownError(s)); - } -}); + .fun = [](EvalState & state, const PosIdx pos, Value ** args, Value & v) { + PathSet context; + auto s = state.coerceToString(pos, *args[0], context).toOwned(); + state.debugThrowLastTrace(ThrownError(s)); + }}); -static void prim_addErrorContext(EvalState & state, const PosIdx pos, Value * * args, Value & v) +static void prim_addErrorContext(EvalState & state, const PosIdx pos, + Value ** args, Value & v) { try { state.forceValue(*args[1], pos); v = *args[1]; } catch (Error & e) { PathSet context; - e.addTrace(std::nullopt, state.coerceToString(pos, *args[0], context).toOwned()); + e.addTrace(std::nullopt, + state.coerceToString(pos, *args[0], context).toOwned()); throw; } } -static RegisterPrimOp primop_addErrorContext(RegisterPrimOp::Info { +static RegisterPrimOp primop_addErrorContext(RegisterPrimOp::Info{ .name = "__addErrorContext", .arity = 2, .fun = prim_addErrorContext, }); -static void prim_ceil(EvalState & state, const PosIdx pos, Value * * args, Value & v) +static void prim_ceil(EvalState & state, const PosIdx pos, Value ** args, + Value & v) { auto value = state.forceFloat(*args[0], args[0]->determinePos(pos)); v.mkInt(ceil(value)); @@ -827,7 +848,8 @@ static RegisterPrimOp primop_ceil({ .fun = prim_ceil, }); -static void prim_floor(EvalState & state, const PosIdx pos, Value * * args, Value & v) +static void prim_floor(EvalState & state, const PosIdx pos, Value ** args, + Value & v) { auto value = state.forceFloat(*args[0], args[0]->determinePos(pos)); v.mkInt(floor(value)); @@ -848,7 +870,8 @@ static RegisterPrimOp primop_floor({ /* Try evaluating the argument. Success => {success=true; value=something;}, * else => {success=false; value=false;} */ -static void prim_tryEval(EvalState & state, const PosIdx pos, Value * * args, Value & v) +static void prim_tryEval(EvalState & state, const PosIdx pos, Value ** args, + Value & v) { auto attrs = state.buildBindings(2); try { @@ -884,10 +907,13 @@ static RegisterPrimOp primop_tryEval({ }); /* Return an environment variable. Use with care. */ -static void prim_getEnv(EvalState & state, const PosIdx pos, Value * * args, Value & v) +static void prim_getEnv(EvalState & state, const PosIdx pos, Value ** args, + Value & v) { std::string name(state.forceStringNoCtx(*args[0], pos)); - v.mkString(evalSettings.restrictEval || evalSettings.pureEval ? "" : getEnv(name).value_or("")); + v.mkString(evalSettings.restrictEval || evalSettings.pureEval + ? "" + : getEnv(name).value_or("")); } static RegisterPrimOp primop_getEnv({ @@ -908,7 +934,8 @@ static RegisterPrimOp primop_getEnv({ }); /* Evaluate the first argument, then return the second argument. */ -static void prim_seq(EvalState & state, const PosIdx pos, Value * * args, Value & v) +static void prim_seq(EvalState & state, const PosIdx pos, Value ** args, + Value & v) { state.forceValue(*args[0], pos); state.forceValue(*args[1], pos); @@ -927,7 +954,8 @@ static RegisterPrimOp primop_seq({ /* Evaluate the first argument deeply (i.e. recursing into lists and attrsets), then return the second argument. */ -static void prim_deepSeq(EvalState & state, const PosIdx pos, Value * * args, Value & v) +static void prim_deepSeq(EvalState & state, const PosIdx pos, Value ** args, + Value & v) { state.forceValueDeep(*args[0]); state.forceValue(*args[1], pos); @@ -947,7 +975,8 @@ static RegisterPrimOp primop_deepSeq({ /* Evaluate the first expression and print it on standard error. Then return the second expression. Useful for debugging. */ -static void prim_trace(EvalState & state, const PosIdx pos, Value * * args, Value & v) +static void prim_trace(EvalState & state, const PosIdx pos, Value ** args, + Value & v) { state.forceValue(*args[0], pos); if (args[0]->type() == nString) @@ -969,12 +998,10 @@ static RegisterPrimOp primop_trace({ .fun = prim_trace, }); - /************************************************************* * Derivations *************************************************************/ - /* Construct (as a unobservable side effect) a Nix derivation expression that performs the derivation described by the argument set. Returns the original set extended with the following @@ -982,25 +1009,22 @@ static RegisterPrimOp primop_trace({ derivation; `drvPath' containing the path of the Nix expression; and `type' set to `derivation' to indicate that this is a derivation. */ -static void prim_derivationStrict(EvalState & state, const PosIdx pos, Value * * args, Value & v) +static void prim_derivationStrict(EvalState & state, const PosIdx pos, + Value ** args, Value & v) { state.forceAttrs(*args[0], pos); /* Figure out the name first (for stack backtraces). */ - Bindings::iterator attr = getAttr( - state, - "derivationStrict", - state.sName, - args[0]->attrs, - pos - ); + Bindings::iterator attr = + getAttr(state, "derivationStrict", state.sName, args[0]->attrs, pos); std::string drvName; const auto posDrvName = attr->pos; try { drvName = state.forceStringNoCtx(*attr->value, pos); } catch (Error & e) { - e.addTrace(state.positions[posDrvName], "while evaluating the derivation attribute 'name'"); + e.addTrace(state.positions[posDrvName], + "while evaluating the derivation attribute 'name'"); throw; } @@ -1033,52 +1057,55 @@ static void prim_derivationStrict(EvalState & state, const PosIdx pos, Value * * outputs.insert("out"); for (auto & i : args[0]->attrs->lexicographicOrder(state.symbols)) { - if (i->name == state.sIgnoreNulls) continue; + if (i->name == state.sIgnoreNulls) + continue; const std::string & key = state.symbols[i->name]; vomit("processing attribute '%1%'", key); auto handleHashMode = [&](const std::string_view s) { - if (s == "recursive") ingestionMethod = FileIngestionMethod::Recursive; - else if (s == "flat") ingestionMethod = FileIngestionMethod::Flat; + if (s == "recursive") + ingestionMethod = FileIngestionMethod::Recursive; + else if (s == "flat") + ingestionMethod = FileIngestionMethod::Flat; else - state.debugThrowLastTrace(EvalError({ - .msg = hintfmt("invalid value '%s' for 'outputHashMode' attribute", s), - .errPos = state.positions[posDrvName] - })); + state.debugThrowLastTrace(EvalError( + {.msg = hintfmt( + "invalid value '%s' for 'outputHashMode' attribute", + s), + .errPos = state.positions[posDrvName]})); }; auto handleOutputs = [&](const Strings & ss) { outputs.clear(); for (auto & j : ss) { if (outputs.find(j) != outputs.end()) - state.debugThrowLastTrace(EvalError({ - .msg = hintfmt("duplicate derivation output '%1%'", j), - .errPos = state.positions[posDrvName] - })); + state.debugThrowLastTrace(EvalError( + {.msg = hintfmt("duplicate derivation output '%1%'", j), + .errPos = state.positions[posDrvName]})); /* !!! Check whether j is a valid attribute name. */ /* Derivations cannot be named ‘drv’, because then we'd have an attribute ‘drvPath’ in the resulting set. */ if (j == "drv") - state.debugThrowLastTrace(EvalError({ - .msg = hintfmt("invalid derivation output name 'drv'" ), - .errPos = state.positions[posDrvName] - })); + state.debugThrowLastTrace(EvalError( + {.msg = hintfmt("invalid derivation output name 'drv'"), + .errPos = state.positions[posDrvName]})); outputs.insert(j); } if (outputs.empty()) - state.debugThrowLastTrace(EvalError({ - .msg = hintfmt("derivation cannot have an empty set of outputs"), - .errPos = state.positions[posDrvName] - })); + state.debugThrowLastTrace(EvalError( + {.msg = hintfmt( + "derivation cannot have an empty set of outputs"), + .errPos = state.positions[posDrvName]})); }; try { if (ignoreNulls) { state.forceValue(*i->value, pos); - if (i->value->type() == nNull) continue; + if (i->value->type() == nNull) + continue; } if (i->name == state.sContentAddressed) { @@ -1098,7 +1125,9 @@ static void prim_derivationStrict(EvalState & state, const PosIdx pos, Value * * else if (i->name == state.sArgs) { state.forceList(*i->value, pos); for (auto elem : i->value->listItems()) { - auto s = state.coerceToString(posDrvName, *elem, context, true).toOwned(); + auto s = + state.coerceToString(posDrvName, *elem, context, true) + .toOwned(); drv.args.push_back(s); } } @@ -1109,46 +1138,61 @@ static void prim_derivationStrict(EvalState & state, const PosIdx pos, Value * * if (jsonObject) { - if (i->name == state.sStructuredAttrs) continue; + if (i->name == state.sStructuredAttrs) + continue; auto placeholder(jsonObject->placeholder(key)); - printValueAsJSON(state, true, *i->value, pos, placeholder, context); + printValueAsJSON(state, true, *i->value, pos, placeholder, + context); if (i->name == state.sBuilder) - drv.builder = state.forceString(*i->value, context, posDrvName); + drv.builder = + state.forceString(*i->value, context, posDrvName); else if (i->name == state.sSystem) - drv.platform = state.forceStringNoCtx(*i->value, posDrvName); + drv.platform = + state.forceStringNoCtx(*i->value, posDrvName); else if (i->name == state.sOutputHash) - outputHash = state.forceStringNoCtx(*i->value, posDrvName); + outputHash = + state.forceStringNoCtx(*i->value, posDrvName); else if (i->name == state.sOutputHashAlgo) - outputHashAlgo = state.forceStringNoCtx(*i->value, posDrvName); + outputHashAlgo = + state.forceStringNoCtx(*i->value, posDrvName); else if (i->name == state.sOutputHashMode) - handleHashMode(state.forceStringNoCtx(*i->value, posDrvName)); + handleHashMode( + state.forceStringNoCtx(*i->value, posDrvName)); else if (i->name == state.sOutputs) { /* Require ‘outputs’ to be a list of strings. */ state.forceList(*i->value, posDrvName); Strings ss; for (auto elem : i->value->listItems()) - ss.emplace_back(state.forceStringNoCtx(*elem, posDrvName)); + ss.emplace_back( + state.forceStringNoCtx(*elem, posDrvName)); handleOutputs(ss); } } else { - auto s = state.coerceToString(i->pos, *i->value, context, true).toOwned(); + auto s = + state.coerceToString(i->pos, *i->value, context, true) + .toOwned(); drv.env.emplace(key, s); - if (i->name == state.sBuilder) drv.builder = std::move(s); - else if (i->name == state.sSystem) drv.platform = std::move(s); - else if (i->name == state.sOutputHash) outputHash = std::move(s); - else if (i->name == state.sOutputHashAlgo) outputHashAlgo = std::move(s); - else if (i->name == state.sOutputHashMode) handleHashMode(s); + if (i->name == state.sBuilder) + drv.builder = std::move(s); + else if (i->name == state.sSystem) + drv.platform = std::move(s); + else if (i->name == state.sOutputHash) + outputHash = std::move(s); + else if (i->name == state.sOutputHashAlgo) + outputHashAlgo = std::move(s); + else if (i->name == state.sOutputHashMode) + handleHashMode(s); else if (i->name == state.sOutputs) handleOutputs(tokenizeString(s)); } - } } catch (Error & e) { - e.addTrace(state.positions[posDrvName], + e.addTrace( + state.positions[posDrvName], "while evaluating the attribute '%1%' of the derivation '%2%'", key, drvName); throw; @@ -1175,11 +1219,14 @@ static void prim_derivationStrict(EvalState & state, const PosIdx pos, Value * * if (path.at(0) == '=') { /* !!! This doesn't work if readOnlyMode is set. */ StorePathSet refs; - state.store->computeFSClosure(state.store->parseStorePath(std::string_view(path).substr(1)), refs); + state.store->computeFSClosure( + state.store->parseStorePath(std::string_view(path).substr(1)), + refs); for (auto & j : refs) { drv.inputSrcs.insert(j); if (j.isDerivation()) - drv.inputDrvs[j] = state.store->readDerivation(j).outputNames(); + drv.inputDrvs[j] = + state.store->readDerivation(j).outputNames(); } } @@ -1196,23 +1243,21 @@ static void prim_derivationStrict(EvalState & state, const PosIdx pos, Value * * /* Do we have all required attributes? */ if (drv.builder == "") - state.debugThrowLastTrace(EvalError({ - .msg = hintfmt("required attribute 'builder' missing"), - .errPos = state.positions[posDrvName] - })); + state.debugThrowLastTrace( + EvalError({.msg = hintfmt("required attribute 'builder' missing"), + .errPos = state.positions[posDrvName]})); if (drv.platform == "") - state.debugThrowLastTrace(EvalError({ - .msg = hintfmt("required attribute 'system' missing"), - .errPos = state.positions[posDrvName] - })); + state.debugThrowLastTrace( + EvalError({.msg = hintfmt("required attribute 'system' missing"), + .errPos = state.positions[posDrvName]})); /* Check whether the derivation name is valid. */ if (isDerivation(drvName)) - state.debugThrowLastTrace(EvalError({ - .msg = hintfmt("derivation names are not allowed to end in '%s'", drvExtension), - .errPos = state.positions[posDrvName] - })); + state.debugThrowLastTrace(EvalError( + {.msg = hintfmt("derivation names are not allowed to end in '%s'", + drvExtension), + .errPos = state.positions[posDrvName]})); if (outputHash) { /* Handle fixed-output derivations. @@ -1220,31 +1265,32 @@ static void prim_derivationStrict(EvalState & state, const PosIdx pos, Value * * Ignore `__contentAddressed` because fixed output derivations are already content addressed. */ if (outputs.size() != 1 || *(outputs.begin()) != "out") - state.debugThrowLastTrace(Error({ - .msg = hintfmt("multiple outputs are not supported in fixed-output derivations"), - .errPos = state.positions[posDrvName] - })); + state.debugThrowLastTrace( + Error({.msg = hintfmt("multiple outputs are not supported in " + "fixed-output derivations"), + .errPos = state.positions[posDrvName]})); - auto h = newHashAllowEmpty(*outputHash, parseHashTypeOpt(outputHashAlgo)); + auto h = + newHashAllowEmpty(*outputHash, parseHashTypeOpt(outputHashAlgo)); auto method = ingestionMethod.value_or(FileIngestionMethod::Flat); auto outPath = state.store->makeFixedOutputPath(method, h, drvName); drv.env["out"] = state.store->printStorePath(outPath); - drv.outputs.insert_or_assign("out", - DerivationOutput::CAFixed { - .hash = FixedOutputHash { - .method = method, - .hash = std::move(h), - }, - }); + drv.outputs.insert_or_assign("out", DerivationOutput::CAFixed{ + .hash = + FixedOutputHash{ + .method = method, + .hash = std::move(h), + }, + }); } else if (contentAddressed || isImpure) { if (contentAddressed && isImpure) - throw EvalError({ - .msg = hintfmt("derivation cannot be both content-addressed and impure"), - .errPos = state.positions[posDrvName] - }); + throw EvalError( + {.msg = hintfmt( + "derivation cannot be both content-addressed and impure"), + .errPos = state.positions[posDrvName]}); auto ht = parseHashTypeOpt(outputHashAlgo).value_or(htSHA256); auto method = ingestionMethod.value_or(FileIngestionMethod::Recursive); @@ -1252,17 +1298,15 @@ static void prim_derivationStrict(EvalState & state, const PosIdx pos, Value * * for (auto & i : outputs) { drv.env[i] = hashPlaceholder(i); if (isImpure) - drv.outputs.insert_or_assign(i, - DerivationOutput::Impure { - .method = method, - .hashType = ht, - }); + drv.outputs.insert_or_assign(i, DerivationOutput::Impure{ + .method = method, + .hashType = ht, + }); else - drv.outputs.insert_or_assign(i, - DerivationOutput::CAFloating { - .method = method, - .hashType = ht, - }); + drv.outputs.insert_or_assign(i, DerivationOutput::CAFloating{ + .method = method, + .hashType = ht, + }); } } @@ -1275,33 +1319,32 @@ static void prim_derivationStrict(EvalState & state, const PosIdx pos, Value * * the hash. */ for (auto & i : outputs) { drv.env[i] = ""; - drv.outputs.insert_or_assign(i, - DerivationOutput::Deferred { }); + drv.outputs.insert_or_assign(i, DerivationOutput::Deferred{}); } - auto hashModulo = hashDerivationModulo(*state.store, Derivation(drv), true); + auto hashModulo = + hashDerivationModulo(*state.store, Derivation(drv), true); switch (hashModulo.kind) { case DrvHash::Kind::Regular: for (auto & i : outputs) { auto h = get(hashModulo.hashes, i); if (!h) throw AssertionError({ - .msg = hintfmt("derivation produced no hash for output '%s'", i), + .msg = hintfmt( + "derivation produced no hash for output '%s'", i), .errPos = state.positions[posDrvName], }); auto outPath = state.store->makeOutputPath(i, *h, drvName); drv.env[i] = state.store->printStorePath(outPath); - drv.outputs.insert_or_assign( - i, - DerivationOutputInputAddressed { - .path = std::move(outPath), - }); + drv.outputs.insert_or_assign(i, DerivationOutputInputAddressed{ + .path = std::move(outPath), + }); } break; ; case DrvHash::Kind::Deferred: for (auto & i : outputs) { - drv.outputs.insert_or_assign(i, DerivationOutputDeferred {}); + drv.outputs.insert_or_assign(i, DerivationOutputDeferred{}); } } } @@ -1327,7 +1370,7 @@ static void prim_derivationStrict(EvalState & state, const PosIdx pos, Value * * v.mkAttrs(attrs); } -static RegisterPrimOp primop_derivationStrict(RegisterPrimOp::Info { +static RegisterPrimOp primop_derivationStrict(RegisterPrimOp::Info{ .name = "derivationStrict", .arity = 1, .fun = prim_derivationStrict, @@ -1340,7 +1383,8 @@ static RegisterPrimOp primop_derivationStrict(RegisterPrimOp::Info { time, any occurrence of this string in an derivation attribute will be replaced with the concrete path in the Nix store of the output ‘out’. */ -static void prim_placeholder(EvalState & state, const PosIdx pos, Value * * args, Value & v) +static void prim_placeholder(EvalState & state, const PosIdx pos, Value ** args, + Value & v) { v.mkString(hashPlaceholder(state.forceStringNoCtx(*args[0], pos))); } @@ -1356,14 +1400,13 @@ static RegisterPrimOp primop_placeholder({ .fun = prim_placeholder, }); - /************************************************************* * Paths *************************************************************/ - /* Convert the argument to a path. !!! obsolete? */ -static void prim_toPath(EvalState & state, const PosIdx pos, Value * * args, Value & v) +static void prim_toPath(EvalState & state, const PosIdx pos, Value ** args, + Value & v) { PathSet context; Path path = state.coerceToPath(pos, *args[0], context); @@ -1388,25 +1431,27 @@ static RegisterPrimOp primop_toPath({ /nix/store/newhash-oldhash-oldname. In the past, `toPath' had special case behaviour for store paths, but that created weird corner cases. */ -static void prim_storePath(EvalState & state, const PosIdx pos, Value * * args, Value & v) +static void prim_storePath(EvalState & state, const PosIdx pos, Value ** args, + Value & v) { if (evalSettings.pureEval) - state.debugThrowLastTrace(EvalError({ - .msg = hintfmt("'%s' is not allowed in pure evaluation mode", "builtins.storePath"), - .errPos = state.positions[pos] - })); + state.debugThrowLastTrace(EvalError( + {.msg = hintfmt("'%s' is not allowed in pure evaluation mode", + "builtins.storePath"), + .errPos = state.positions[pos]})); PathSet context; - Path path = state.checkSourcePath(state.coerceToPath(pos, *args[0], context)); + Path path = + state.checkSourcePath(state.coerceToPath(pos, *args[0], context)); /* Resolve symlinks in ‘path’, unless ‘path’ itself is a symlink directly in the store. The latter condition is necessary so e.g. nix-push does the right thing. */ - if (!state.store->isStorePath(path)) path = canonPath(path, true); + if (!state.store->isStorePath(path)) + path = canonPath(path, true); if (!state.store->isInStore(path)) - state.debugThrowLastTrace(EvalError({ - .msg = hintfmt("path '%1%' is not in the Nix store", path), - .errPos = state.positions[pos] - })); + state.debugThrowLastTrace(EvalError( + {.msg = hintfmt("path '%1%' is not in the Nix store", path), + .errPos = state.positions[pos]})); auto path2 = state.store->toStorePath(path).first; if (!settings.readOnlyMode) state.store->ensurePath(path2); @@ -1432,14 +1477,15 @@ static RegisterPrimOp primop_storePath({ .fun = prim_storePath, }); -static void prim_pathExists(EvalState & state, const PosIdx pos, Value * * args, Value & v) +static void prim_pathExists(EvalState & state, const PosIdx pos, Value ** args, + Value & v) { /* We don’t check the path right now, because we don’t want to throw if the path isn’t allowed, but just return false (and we can’t just catch the exception here because we still want to throw if something in the evaluation of `*args[0]` tries to access an unauthorized path). */ - auto path = realisePath(state, pos, *args[0], { .checkForPureEval = false }); + auto path = realisePath(state, pos, *args[0], {.checkForPureEval = false}); try { v.mkBool(pathExists(state.checkSourcePath(path))); @@ -1464,10 +1510,13 @@ static RegisterPrimOp primop_pathExists({ /* Return the base name of the given string, i.e., everything following the last slash. */ -static void prim_baseNameOf(EvalState & state, const PosIdx pos, Value * * args, Value & v) +static void prim_baseNameOf(EvalState & state, const PosIdx pos, Value ** args, + Value & v) { PathSet context; - v.mkString(baseNameOf(*state.coerceToString(pos, *args[0], context, false, false)), context); + v.mkString( + baseNameOf(*state.coerceToString(pos, *args[0], context, false, false)), + context); } static RegisterPrimOp primop_baseNameOf({ @@ -1484,12 +1533,16 @@ static RegisterPrimOp primop_baseNameOf({ /* Return the directory of the given path, i.e., everything before the last slash. Return either a path or a string depending on the type of the argument. */ -static void prim_dirOf(EvalState & state, const PosIdx pos, Value * * args, Value & v) +static void prim_dirOf(EvalState & state, const PosIdx pos, Value ** args, + Value & v) { PathSet context; auto path = state.coerceToString(pos, *args[0], context, false, false); auto dir = dirOf(*path); - if (args[0]->type() == nPath) v.mkPath(dir); else v.mkString(dir, context); + if (args[0]->type() == nPath) + v.mkPath(dir); + else + v.mkString(dir, context); } static RegisterPrimOp primop_dirOf({ @@ -1504,16 +1557,21 @@ static RegisterPrimOp primop_dirOf({ }); /* Return the contents of a file as a string. */ -static void prim_readFile(EvalState & state, const PosIdx pos, Value * * args, Value & v) +static void prim_readFile(EvalState & state, const PosIdx pos, Value ** args, + Value & v) { auto path = realisePath(state, pos, *args[0]); auto s = readFile(path); if (s.find((char) 0) != std::string::npos) - state.debugThrowLastTrace(Error("the contents of the file '%1%' cannot be represented as a Nix string", path)); + state.debugThrowLastTrace(Error("the contents of the file '%1%' cannot " + "be represented as a Nix string", + path)); StorePathSet refs; if (state.store->isInStore(path)) { try { - refs = state.store->queryPathInfo(state.store->toStorePath(path).first)->references; + refs = + state.store->queryPathInfo(state.store->toStorePath(path).first) + ->references; } catch (Error &) { // FIXME: should be InvalidPathError } } @@ -1532,7 +1590,8 @@ static RegisterPrimOp primop_readFile({ /* Find a file in the Nix search path. Used to implement paths, which are desugared to 'findFile __nixPath "x"'. */ -static void prim_findFile(EvalState & state, const PosIdx pos, Value * * args, Value & v) +static void prim_findFile(EvalState & state, const PosIdx pos, Value ** args, + Value & v) { state.forceList(*args[0], pos); @@ -1546,25 +1605,21 @@ static void prim_findFile(EvalState & state, const PosIdx pos, Value * * args, V if (i != v2->attrs->end()) prefix = state.forceStringNoCtx(*i->value, pos); - i = getAttr( - state, - "findFile", - state.sPath, - v2->attrs, - pos - ); + i = getAttr(state, "findFile", state.sPath, v2->attrs, pos); PathSet context; - auto path = state.coerceToString(pos, *i->value, context, false, false).toOwned(); + auto path = state.coerceToString(pos, *i->value, context, false, false) + .toOwned(); try { auto rewrites = state.realiseContext(context); path = rewriteStrings(path, rewrites); } catch (InvalidPathError & e) { - state.debugThrowLastTrace(EvalError({ - .msg = hintfmt("cannot find '%1%', since path '%2%' is not valid", path, e.path), - .errPos = state.positions[pos] - })); + state.debugThrowLastTrace(EvalError( + {.msg = + hintfmt("cannot find '%1%', since path '%2%' is not valid", + path, e.path), + .errPos = state.positions[pos]})); } searchPath.emplace_back(prefix, path); @@ -1575,22 +1630,22 @@ static void prim_findFile(EvalState & state, const PosIdx pos, Value * * args, V v.mkPath(state.checkSourcePath(state.findFile(searchPath, path, pos))); } -static RegisterPrimOp primop_findFile(RegisterPrimOp::Info { +static RegisterPrimOp primop_findFile(RegisterPrimOp::Info{ .name = "__findFile", .arity = 2, .fun = prim_findFile, }); /* Return the cryptographic hash of a file in base-16. */ -static void prim_hashFile(EvalState & state, const PosIdx pos, Value * * args, Value & v) +static void prim_hashFile(EvalState & state, const PosIdx pos, Value ** args, + Value & v) { auto type = state.forceStringNoCtx(*args[0], pos); std::optional ht = parseHashType(type); if (!ht) - state.debugThrowLastTrace(Error({ - .msg = hintfmt("unknown hash type '%1%'", type), - .errPos = state.positions[pos] - })); + state.debugThrowLastTrace( + Error({.msg = hintfmt("unknown hash type '%1%'", type), + .errPos = state.positions[pos]})); auto path = realisePath(state, pos, *args[1]); @@ -1609,7 +1664,8 @@ static RegisterPrimOp primop_hashFile({ }); /* Read a directory (without . or ..) */ -static void prim_readDir(EvalState & state, const PosIdx pos, Value * * args, Value & v) +static void prim_readDir(EvalState & state, const PosIdx pos, Value ** args, + Value & v) { auto path = realisePath(state, pos, *args[0]); @@ -1620,11 +1676,10 @@ static void prim_readDir(EvalState & state, const PosIdx pos, Value * * args, Va for (auto & ent : entries) { if (ent.type == DT_UNKNOWN) ent.type = getFileType(path + "/" + ent.name); - attrs.alloc(ent.name).mkString( - ent.type == DT_REG ? "regular" : - ent.type == DT_DIR ? "directory" : - ent.type == DT_LNK ? "symlink" : - "unknown"); + attrs.alloc(ent.name).mkString(ent.type == DT_REG ? "regular" + : ent.type == DT_DIR ? "directory" + : ent.type == DT_LNK ? "symlink" + : "unknown"); } v.mkAttrs(attrs); @@ -1649,16 +1704,15 @@ static RegisterPrimOp primop_readDir({ .fun = prim_readDir, }); - /************************************************************* * Creating files *************************************************************/ - /* Convert the argument (which can be any Nix expression) to an XML representation returned in a string. Not all Nix expressions can be sensibly or completely represented (e.g., functions). */ -static void prim_toXML(EvalState & state, const PosIdx pos, Value * * args, Value & v) +static void prim_toXML(EvalState & state, const PosIdx pos, Value ** args, + Value & v) { std::ostringstream out; PathSet context; @@ -1766,7 +1820,8 @@ static RegisterPrimOp primop_toXML({ /* Convert the argument (which can be any Nix expression) to a JSON string. Not all Nix expressions can be sensibly or completely represented (e.g., functions). */ -static void prim_toJSON(EvalState & state, const PosIdx pos, Value * * args, Value & v) +static void prim_toJSON(EvalState & state, const PosIdx pos, Value ** args, + Value & v) { std::ostringstream out; PathSet context; @@ -1789,12 +1844,13 @@ static RegisterPrimOp primop_toJSON({ }); /* Parse a JSON string to a value. */ -static void prim_fromJSON(EvalState & state, const PosIdx pos, Value * * args, Value & v) +static void prim_fromJSON(EvalState & state, const PosIdx pos, Value ** args, + Value & v) { auto s = state.forceStringNoCtx(*args[0], pos); try { parseJSON(state, s, v); - } catch (JSONParseError &e) { + } catch (JSONParseError & e) { e.addTrace(state.positions[pos], "while decoding a JSON string"); throw; } @@ -1817,7 +1873,8 @@ static RegisterPrimOp primop_fromJSON({ /* Store a string in the Nix store as a source file that can be used as an input by derivations. */ -static void prim_toFile(EvalState & state, const PosIdx pos, Value * * args, Value & v) +static void prim_toFile(EvalState & state, const PosIdx pos, Value ** args, + Value & v) { PathSet context; std::string name(state.forceStringNoCtx(*args[0], pos)); @@ -1827,19 +1884,19 @@ static void prim_toFile(EvalState & state, const PosIdx pos, Value * * args, Val for (auto path : context) { if (path.at(0) != '/') - state.debugThrowLastTrace(EvalError({ - .msg = hintfmt( - "in 'toFile': the file named '%1%' must not contain a reference " - "to a derivation but contains (%2%)", - name, path), - .errPos = state.positions[pos] - })); + state.debugThrowLastTrace( + EvalError({.msg = hintfmt("in 'toFile': the file named '%1%' " + "must not contain a reference " + "to a derivation but contains (%2%)", + name, path), + .errPos = state.positions[pos]})); refs.insert(state.store->parseStorePath(path)); } - auto storePath = settings.readOnlyMode - ? state.store->computeStorePathForText(name, contents, refs) - : state.store->addTextToStore(name, contents, refs, state.repair); + auto storePath = + settings.readOnlyMode + ? state.store->computeStorePathForText(name, contents, refs) + : state.store->addTextToStore(name, contents, refs, state.repair); /* Note: we don't need to add `context' to the context of the result, since `storePath' itself has references to the paths @@ -1927,16 +1984,11 @@ static RegisterPrimOp primop_toFile({ .fun = prim_toFile, }); -static void addPath( - EvalState & state, - const PosIdx pos, - const std::string & name, - Path path, - Value * filterFun, - FileIngestionMethod method, - const std::optional expectedHash, - Value & v, - const PathSet & context) +static void addPath(EvalState & state, const PosIdx pos, + const std::string & name, Path path, Value * filterFun, + FileIngestionMethod method, + const std::optional expectedHash, Value & v, + const PathSet & context) { try { // FIXME: handle CA derivation outputs (where path needs to @@ -1949,7 +2001,8 @@ static void addPath( if (state.store->isInStore(path)) { try { auto [storePath, subPath] = state.store->toStorePath(path); - // FIXME: we should scanForReferences on the path before adding it + // FIXME: we should scanForReferences on the path before adding + // it refs = state.store->queryPathInfo(storePath)->references; path = state.store->toRealPath(storePath) + subPath; } catch (Error &) { // FIXME: should be InvalidPathError @@ -1957,8 +2010,8 @@ static void addPath( } path = evalSettings.pureEval && expectedHash - ? path - : state.checkSourcePath(path); + ? path + : state.checkSourcePath(path); PathFilter filter = filterFun ? ([&](const Path & path) { auto st = lstat(path); @@ -1969,29 +2022,39 @@ static void addPath( arg1.mkString(path); Value arg2; - arg2.mkString( - S_ISREG(st.st_mode) ? "regular" : - S_ISDIR(st.st_mode) ? "directory" : - S_ISLNK(st.st_mode) ? "symlink" : - "unknown" /* not supported, will fail! */); + arg2.mkString(S_ISREG(st.st_mode) ? "regular" + : S_ISDIR(st.st_mode) ? "directory" + : S_ISLNK(st.st_mode) + ? "symlink" + : "unknown" /* not supported, will fail! */); - Value * args []{&arg1, &arg2}; + Value * args[]{&arg1, &arg2}; Value res; state.callFunction(*filterFun, 2, args, res, pos); return state.forceBool(res, pos); - }) : defaultPathFilter; + }) + : defaultPathFilter; std::optional expectedStorePath; if (expectedHash) - expectedStorePath = state.store->makeFixedOutputPath(method, *expectedHash, name); + expectedStorePath = + state.store->makeFixedOutputPath(method, *expectedHash, name); if (!expectedHash || !state.store->isValidPath(*expectedStorePath)) { - StorePath dstPath = settings.readOnlyMode - ? state.store->computeStorePathForPath(name, path, method, htSHA256, filter).first - : state.store->addToStore(name, path, method, htSHA256, filter, state.repair, refs); + StorePath dstPath = + settings.readOnlyMode + ? state.store + ->computeStorePathForPath(name, path, method, + htSHA256, filter) + .first + : state.store->addToStore(name, path, method, htSHA256, + filter, state.repair, refs); if (expectedHash && expectedStorePath != dstPath) - state.debugThrowLastTrace(Error("store path mismatch in (possibly filtered) path added from '%s'", path)); + state.debugThrowLastTrace( + Error("store path mismatch in (possibly filtered) path " + "added from '%s'", + path)); state.allowAndSetStorePathString(dstPath, v); } else state.allowAndSetStorePathString(*expectedStorePath, v); @@ -2001,22 +2064,22 @@ static void addPath( } } - -static void prim_filterSource(EvalState & state, const PosIdx pos, Value * * args, Value & v) +static void prim_filterSource(EvalState & state, const PosIdx pos, + Value ** args, Value & v) { PathSet context; Path path = state.coerceToPath(pos, *args[1], context); state.forceValue(*args[0], pos); if (args[0]->type() != nFunction) - state.debugThrowLastTrace(TypeError({ - .msg = hintfmt( - "first argument in call to 'filterSource' is not a function but %1%", - showType(*args[0])), - .errPos = state.positions[pos] - })); + state.debugThrowLastTrace(TypeError( + {.msg = hintfmt("first argument in call to 'filterSource' is not a " + "function but %1%", + showType(*args[0])), + .errPos = state.positions[pos]})); - addPath(state, pos, std::string(baseNameOf(path)), path, args[0], FileIngestionMethod::Recursive, std::nullopt, v, context); + addPath(state, pos, std::string(baseNameOf(path)), path, args[0], + FileIngestionMethod::Recursive, std::nullopt, v, context); } static RegisterPrimOp primop_filterSource({ @@ -2074,7 +2137,8 @@ static RegisterPrimOp primop_filterSource({ .fun = prim_filterSource, }); -static void prim_path(EvalState & state, const PosIdx pos, Value * * args, Value & v) +static void prim_path(EvalState & state, const PosIdx pos, Value ** args, + Value & v) { state.forceAttrs(*args[0], pos); Path path; @@ -2094,24 +2158,25 @@ static void prim_path(EvalState & state, const PosIdx pos, Value * * args, Value state.forceValue(*attr.value, pos); filterFun = attr.value; } else if (n == "recursive") - method = FileIngestionMethod { state.forceBool(*attr.value, attr.pos) }; + method = + FileIngestionMethod{state.forceBool(*attr.value, attr.pos)}; else if (n == "sha256") - expectedHash = newHashAllowEmpty(state.forceStringNoCtx(*attr.value, attr.pos), htSHA256); + expectedHash = newHashAllowEmpty( + state.forceStringNoCtx(*attr.value, attr.pos), htSHA256); else - state.debugThrowLastTrace(EvalError({ - .msg = hintfmt("unsupported argument '%1%' to 'addPath'", state.symbols[attr.name]), - .errPos = state.positions[attr.pos] - })); + state.debugThrowLastTrace(EvalError( + {.msg = hintfmt("unsupported argument '%1%' to 'addPath'", + state.symbols[attr.name]), + .errPos = state.positions[attr.pos]})); } if (path.empty()) - state.debugThrowLastTrace(EvalError({ - .msg = hintfmt("'path' required"), - .errPos = state.positions[pos] - })); + state.debugThrowLastTrace(EvalError({.msg = hintfmt("'path' required"), + .errPos = state.positions[pos]})); if (name.empty()) name = baseNameOf(path); - addPath(state, pos, name, path, filterFun, method, expectedHash, v, context); + addPath(state, pos, name, path, filterFun, method, expectedHash, v, + context); } static RegisterPrimOp primop_path({ @@ -2149,15 +2214,14 @@ static RegisterPrimOp primop_path({ .fun = prim_path, }); - /************************************************************* * Sets *************************************************************/ - /* Return the names of the attributes in a set as a sorted list of strings. */ -static void prim_attrNames(EvalState & state, const PosIdx pos, Value * * args, Value & v) +static void prim_attrNames(EvalState & state, const PosIdx pos, Value ** args, + Value & v) { state.forceAttrs(*args[0], pos); @@ -2165,10 +2229,12 @@ static void prim_attrNames(EvalState & state, const PosIdx pos, Value * * args, size_t n = 0; for (auto & i : *args[0]->attrs) - (v.listElems()[n++] = state.allocValue())->mkString(state.symbols[i.name]); + (v.listElems()[n++] = state.allocValue()) + ->mkString(state.symbols[i.name]); - std::sort(v.listElems(), v.listElems() + n, - [](Value * v1, Value * v2) { return strcmp(v1->string.s, v2->string.s) < 0; }); + std::sort(v.listElems(), v.listElems() + n, [](Value * v1, Value * v2) { + return strcmp(v1->string.s, v2->string.s) < 0; + }); } static RegisterPrimOp primop_attrNames({ @@ -2184,7 +2250,8 @@ static RegisterPrimOp primop_attrNames({ /* Return the values of the attributes in a set as a list, in the same order as attrNames. */ -static void prim_attrValues(EvalState & state, const PosIdx pos, Value * * args, Value & v) +static void prim_attrValues(EvalState & state, const PosIdx pos, Value ** args, + Value & v) { state.forceAttrs(*args[0], pos); @@ -2194,12 +2261,11 @@ static void prim_attrValues(EvalState & state, const PosIdx pos, Value * * args, for (auto & i : *args[0]->attrs) v.listElems()[n++] = (Value *) &i; - std::sort(v.listElems(), v.listElems() + n, - [&](Value * v1, Value * v2) { - std::string_view s1 = state.symbols[((Attr *) v1)->name], - s2 = state.symbols[((Attr *) v2)->name]; - return s1 < s2; - }); + std::sort(v.listElems(), v.listElems() + n, [&](Value * v1, Value * v2) { + std::string_view s1 = state.symbols[((Attr *) v1)->name], + s2 = state.symbols[((Attr *) v2)->name]; + return s1 < s2; + }); for (unsigned int i = 0; i < n; ++i) v.listElems()[i] = ((Attr *) v.listElems()[i])->value; @@ -2216,19 +2282,15 @@ static RegisterPrimOp primop_attrValues({ }); /* Dynamic version of the `.' operator. */ -void prim_getAttr(EvalState & state, const PosIdx pos, Value * * args, Value & v) +void prim_getAttr(EvalState & state, const PosIdx pos, Value ** args, Value & v) { auto attr = state.forceStringNoCtx(*args[0], pos); state.forceAttrs(*args[1], pos); - Bindings::iterator i = getAttr( - state, - "getAttr", - state.symbols.create(attr), - args[1]->attrs, - pos - ); + Bindings::iterator i = getAttr(state, "getAttr", state.symbols.create(attr), + args[1]->attrs, pos); // !!! add to stack trace? - if (state.countCalls && i->pos) state.attrSelects[i->pos]++; + if (state.countCalls && i->pos) + state.attrSelects[i->pos]++; state.forceValue(*i->value, pos); v = *i->value; } @@ -2246,7 +2308,8 @@ static RegisterPrimOp primop_getAttr({ }); /* Return position information of the specified attribute. */ -static void prim_unsafeGetAttrPos(EvalState & state, const PosIdx pos, Value * * args, Value & v) +static void prim_unsafeGetAttrPos(EvalState & state, const PosIdx pos, + Value ** args, Value & v) { auto attr = state.forceStringNoCtx(*args[0], pos); state.forceAttrs(*args[1], pos); @@ -2257,18 +2320,20 @@ static void prim_unsafeGetAttrPos(EvalState & state, const PosIdx pos, Value * * state.mkPos(v, i->pos); } -static RegisterPrimOp primop_unsafeGetAttrPos(RegisterPrimOp::Info { +static RegisterPrimOp primop_unsafeGetAttrPos(RegisterPrimOp::Info{ .name = "__unsafeGetAttrPos", .arity = 2, .fun = prim_unsafeGetAttrPos, }); /* Dynamic version of the `?' operator. */ -static void prim_hasAttr(EvalState & state, const PosIdx pos, Value * * args, Value & v) +static void prim_hasAttr(EvalState & state, const PosIdx pos, Value ** args, + Value & v) { auto attr = state.forceStringNoCtx(*args[0], pos); state.forceAttrs(*args[1], pos); - v.mkBool(args[1]->attrs->find(state.symbols.create(attr)) != args[1]->attrs->end()); + v.mkBool(args[1]->attrs->find(state.symbols.create(attr)) != + args[1]->attrs->end()); } static RegisterPrimOp primop_hasAttr({ @@ -2283,7 +2348,8 @@ static RegisterPrimOp primop_hasAttr({ }); /* Determine whether the argument is a set. */ -static void prim_isAttrs(EvalState & state, const PosIdx pos, Value * * args, Value & v) +static void prim_isAttrs(EvalState & state, const PosIdx pos, Value ** args, + Value & v) { state.forceValue(*args[0], pos); v.mkBool(args[0]->type() == nAttrs); @@ -2298,7 +2364,8 @@ static RegisterPrimOp primop_isAttrs({ .fun = prim_isAttrs, }); -static void prim_removeAttrs(EvalState & state, const PosIdx pos, Value * * args, Value & v) +static void prim_removeAttrs(EvalState & state, const PosIdx pos, Value ** args, + Value & v) { state.forceAttrs(*args[0], pos); state.forceList(*args[1], pos); @@ -2318,10 +2385,8 @@ static void prim_removeAttrs(EvalState & state, const PosIdx pos, Value * * args to sort v.attrs because it's a subset of an already sorted vector. */ auto attrs = state.buildBindings(args[0]->attrs->size()); - std::set_difference( - args[0]->attrs->begin(), args[0]->attrs->end(), - names.begin(), names.end(), - std::back_inserter(attrs)); + std::set_difference(args[0]->attrs->begin(), args[0]->attrs->end(), + names.begin(), names.end(), std::back_inserter(attrs)); v.mkAttrs(attrs.alreadySorted()); } @@ -2346,7 +2411,8 @@ static RegisterPrimOp primop_removeAttrs({ "nameN"; value = valueN;}] is transformed to {name1 = value1; ... nameN = valueN;}. In case of duplicate occurrences of the same name, the first takes precedence. */ -static void prim_listToAttrs(EvalState & state, const PosIdx pos, Value * * args, Value & v) +static void prim_listToAttrs(EvalState & state, const PosIdx pos, Value ** args, + Value & v) { state.forceList(*args[0], pos); @@ -2357,25 +2423,15 @@ static void prim_listToAttrs(EvalState & state, const PosIdx pos, Value * * args for (auto v2 : args[0]->listItems()) { state.forceAttrs(*v2, pos); - Bindings::iterator j = getAttr( - state, - "listToAttrs", - state.sName, - v2->attrs, - pos - ); + Bindings::iterator j = + getAttr(state, "listToAttrs", state.sName, v2->attrs, pos); auto name = state.forceStringNoCtx(*j->value, j->pos); auto sym = state.symbols.create(name); if (seen.insert(sym).second) { - Bindings::iterator j2 = getAttr( - state, - "listToAttrs", - state.sValue, - v2->attrs, - pos - ); + Bindings::iterator j2 = + getAttr(state, "listToAttrs", state.sValue, v2->attrs, pos); attrs.insert(sym, j2->value, j2->pos); } } @@ -2408,12 +2464,14 @@ static RegisterPrimOp primop_listToAttrs({ .fun = prim_listToAttrs, }); -static void prim_intersectAttrs(EvalState & state, const PosIdx pos, Value * * args, Value & v) +static void prim_intersectAttrs(EvalState & state, const PosIdx pos, + Value ** args, Value & v) { state.forceAttrs(*args[0], pos); state.forceAttrs(*args[1], pos); - auto attrs = state.buildBindings(std::min(args[0]->attrs->size(), args[1]->attrs->size())); + auto attrs = state.buildBindings( + std::min(args[0]->attrs->size(), args[1]->attrs->size())); for (auto & i : *args[0]->attrs) { Bindings::iterator j = args[1]->attrs->find(i.name); @@ -2434,7 +2492,8 @@ static RegisterPrimOp primop_intersectAttrs({ .fun = prim_intersectAttrs, }); -static void prim_catAttrs(EvalState & state, const PosIdx pos, Value * * args, Value & v) +static void prim_catAttrs(EvalState & state, const PosIdx pos, Value ** args, + Value & v) { auto attrName = state.symbols.create(state.forceStringNoCtx(*args[0], pos)); state.forceList(*args[1], pos); @@ -2471,7 +2530,8 @@ static RegisterPrimOp primop_catAttrs({ .fun = prim_catAttrs, }); -static void prim_functionArgs(EvalState & state, const PosIdx pos, Value * * args, Value & v) +static void prim_functionArgs(EvalState & state, const PosIdx pos, + Value ** args, Value & v) { state.forceValue(*args[0], pos); if (args[0]->isPrimOpApp() || args[0]->isPrimOp()) { @@ -2479,17 +2539,17 @@ static void prim_functionArgs(EvalState & state, const PosIdx pos, Value * * arg return; } if (!args[0]->isLambda()) - state.debugThrowLastTrace(TypeError({ - .msg = hintfmt("'functionArgs' requires a function"), - .errPos = state.positions[pos] - })); + state.debugThrowLastTrace( + TypeError({.msg = hintfmt("'functionArgs' requires a function"), + .errPos = state.positions[pos]})); if (!args[0]->lambda.fun->hasFormals()) { v.mkAttrs(&state.emptyBindings); return; } - auto attrs = state.buildBindings(args[0]->lambda.fun->formals->formals.size()); + auto attrs = + state.buildBindings(args[0]->lambda.fun->formals->formals.size()); for (auto & i : args[0]->lambda.fun->formals->formals) // !!! should optimise booleans (allocate only once) attrs.alloc(i.name, i.pos).mkBool(i.def); @@ -2514,7 +2574,8 @@ static RegisterPrimOp primop_functionArgs({ }); /* */ -static void prim_mapAttrs(EvalState & state, const PosIdx pos, Value * * args, Value & v) +static void prim_mapAttrs(EvalState & state, const PosIdx pos, Value ** args, + Value & v) { state.forceAttrs(*args[1], pos); @@ -2546,7 +2607,8 @@ static RegisterPrimOp primop_mapAttrs({ .fun = prim_mapAttrs, }); -static void prim_zipAttrsWith(EvalState & state, const PosIdx pos, Value * * args, Value & v) +static void prim_zipAttrsWith(EvalState & state, const PosIdx pos, + Value ** args, Value & v) { // we will first count how many values are present for each given key. // we then allocate a single attrset and pre-populate it with lists of @@ -2555,7 +2617,7 @@ static void prim_zipAttrsWith(EvalState & state, const PosIdx pos, Value * * arg // attribute with the merge function application. this way we need not // use (slightly slower) temporary storage the GC does not know about. - std::map> attrsSeen; + std::map> attrsSeen; state.forceFunction(*args[0], pos); state.forceList(*args[1], pos); @@ -2569,7 +2631,8 @@ static void prim_zipAttrsWith(EvalState & state, const PosIdx pos, Value * * arg for (auto & attr : *vElem->attrs) attrsSeen[attr.name].first++; } catch (TypeError & e) { - e.addTrace(state.positions[pos], hintfmt("while invoking '%s'", "zipAttrsWith")); + e.addTrace(state.positions[pos], + hintfmt("while invoking '%s'", "zipAttrsWith")); state.debugThrowLastTrace(e); } } @@ -2631,14 +2694,13 @@ static RegisterPrimOp primop_zipAttrsWith({ .fun = prim_zipAttrsWith, }); - /************************************************************* * Lists *************************************************************/ - /* Determine whether the argument is a list. */ -static void prim_isList(EvalState & state, const PosIdx pos, Value * * args, Value & v) +static void prim_isList(EvalState & state, const PosIdx pos, Value ** args, + Value & v) { state.forceValue(*args[0], pos); v.mkBool(args[0]->type() == nList); @@ -2653,20 +2715,21 @@ static RegisterPrimOp primop_isList({ .fun = prim_isList, }); -static void elemAt(EvalState & state, const PosIdx pos, Value & list, int n, Value & v) +static void elemAt(EvalState & state, const PosIdx pos, Value & list, int n, + Value & v) { state.forceList(list, pos); if (n < 0 || (unsigned int) n >= list.listSize()) - state.debugThrowLastTrace(Error({ - .msg = hintfmt("list index %1% is out of bounds", n), - .errPos = state.positions[pos] - })); + state.debugThrowLastTrace( + Error({.msg = hintfmt("list index %1% is out of bounds", n), + .errPos = state.positions[pos]})); state.forceValue(*list.listElems()[n], pos); v = *list.listElems()[n]; } /* Return the n-1'th element of a list. */ -static void prim_elemAt(EvalState & state, const PosIdx pos, Value * * args, Value & v) +static void prim_elemAt(EvalState & state, const PosIdx pos, Value ** args, + Value & v) { elemAt(state, pos, *args[0], state.forceInt(*args[1], pos), v); } @@ -2682,7 +2745,8 @@ static RegisterPrimOp primop_elemAt({ }); /* Return the first element of a list. */ -static void prim_head(EvalState & state, const PosIdx pos, Value * * args, Value & v) +static void prim_head(EvalState & state, const PosIdx pos, Value ** args, + Value & v) { elemAt(state, pos, *args[0], 0, v); } @@ -2701,14 +2765,14 @@ static RegisterPrimOp primop_head({ /* Return a list consisting of everything but the first element of a list. Warning: this function takes O(n) time, so you probably don't want to use it! */ -static void prim_tail(EvalState & state, const PosIdx pos, Value * * args, Value & v) +static void prim_tail(EvalState & state, const PosIdx pos, Value ** args, + Value & v) { state.forceList(*args[0], pos); if (args[0]->listSize() == 0) - state.debugThrowLastTrace(Error({ - .msg = hintfmt("'tail' called on an empty list"), - .errPos = state.positions[pos] - })); + state.debugThrowLastTrace( + Error({.msg = hintfmt("'tail' called on an empty list"), + .errPos = state.positions[pos]})); state.mkList(v, args[0]->listSize() - 1); for (unsigned int n = 0; n < v.listSize(); ++n) @@ -2732,15 +2796,16 @@ static RegisterPrimOp primop_tail({ }); /* Apply a function to every element of a list. */ -static void prim_map(EvalState & state, const PosIdx pos, Value * * args, Value & v) +static void prim_map(EvalState & state, const PosIdx pos, Value ** args, + Value & v) { state.forceList(*args[1], pos); state.mkList(v, args[1]->listSize()); for (unsigned int n = 0; n < v.listSize(); ++n) - (v.listElems()[n] = state.allocValue())->mkApp( - args[0], args[1]->listElems()[n]); + (v.listElems()[n] = state.allocValue()) + ->mkApp(args[0], args[1]->listElems()[n]); } static RegisterPrimOp primop_map({ @@ -2762,7 +2827,8 @@ static RegisterPrimOp primop_map({ /* Filter a list using a predicate; that is, return a list containing every element from the list for which the predicate function returns true. */ -static void prim_filter(EvalState & state, const PosIdx pos, Value * * args, Value & v) +static void prim_filter(EvalState & state, const PosIdx pos, Value ** args, + Value & v) { state.forceFunction(*args[0], pos); state.forceList(*args[1], pos); @@ -2785,7 +2851,8 @@ static void prim_filter(EvalState & state, const PosIdx pos, Value * * args, Val v = *args[1]; else { state.mkList(v, k); - for (unsigned int n = 0; n < k; ++n) v.listElems()[n] = vs[n]; + for (unsigned int n = 0; n < k; ++n) + v.listElems()[n] = vs[n]; } } @@ -2800,7 +2867,8 @@ static RegisterPrimOp primop_filter({ }); /* Return true if a list contains a given element. */ -static void prim_elem(EvalState & state, const PosIdx pos, Value * * args, Value & v) +static void prim_elem(EvalState & state, const PosIdx pos, Value ** args, + Value & v) { bool res = false; state.forceList(*args[1], pos); @@ -2823,7 +2891,8 @@ static RegisterPrimOp primop_elem({ }); /* Concatenate a list of lists. */ -static void prim_concatLists(EvalState & state, const PosIdx pos, Value * * args, Value & v) +static void prim_concatLists(EvalState & state, const PosIdx pos, Value ** args, + Value & v) { state.forceList(*args[0], pos); state.concatLists(v, args[0]->listSize(), args[0]->listElems(), pos); @@ -2839,7 +2908,8 @@ static RegisterPrimOp primop_concatLists({ }); /* Return the length of a list. This is an O(1) time operation. */ -static void prim_length(EvalState & state, const PosIdx pos, Value * * args, Value & v) +static void prim_length(EvalState & state, const PosIdx pos, Value ** args, + Value & v) { state.forceList(*args[0], pos); v.mkInt(args[0]->listSize()); @@ -2856,7 +2926,8 @@ static RegisterPrimOp primop_length({ /* Reduce a list by applying a binary operator, from left to right. The operator is applied strictly. */ -static void prim_foldlStrict(EvalState & state, const PosIdx pos, Value * * args, Value & v) +static void prim_foldlStrict(EvalState & state, const PosIdx pos, Value ** args, + Value & v) { state.forceFunction(*args[0], pos); state.forceList(*args[2], pos); @@ -2865,7 +2936,7 @@ static void prim_foldlStrict(EvalState & state, const PosIdx pos, Value * * args Value * vCur = args[1]; for (auto [n, elem] : enumerate(args[2]->listItems())) { - Value * vs []{vCur, elem}; + Value * vs[]{vCur, elem}; vCur = n == args[2]->listSize() - 1 ? &v : state.allocValue(); state.callFunction(*args[0], 2, vs, *vCur, pos); } @@ -2889,7 +2960,8 @@ static RegisterPrimOp primop_foldlStrict({ .fun = prim_foldlStrict, }); -static void anyOrAll(bool any, EvalState & state, const PosIdx pos, Value * * args, Value & v) +static void anyOrAll(bool any, EvalState & state, const PosIdx pos, + Value ** args, Value & v) { state.forceFunction(*args[0], pos); state.forceList(*args[1], pos); @@ -2907,8 +2979,8 @@ static void anyOrAll(bool any, EvalState & state, const PosIdx pos, Value * * ar v.mkBool(!any); } - -static void prim_any(EvalState & state, const PosIdx pos, Value * * args, Value & v) +static void prim_any(EvalState & state, const PosIdx pos, Value ** args, + Value & v) { anyOrAll(true, state, pos, args, v); } @@ -2923,7 +2995,8 @@ static RegisterPrimOp primop_any({ .fun = prim_any, }); -static void prim_all(EvalState & state, const PosIdx pos, Value * * args, Value & v) +static void prim_all(EvalState & state, const PosIdx pos, Value ** args, + Value & v) { anyOrAll(false, state, pos, args, v); } @@ -2938,15 +3011,15 @@ static RegisterPrimOp primop_all({ .fun = prim_all, }); -static void prim_genList(EvalState & state, const PosIdx pos, Value * * args, Value & v) +static void prim_genList(EvalState & state, const PosIdx pos, Value ** args, + Value & v) { auto len = state.forceInt(*args[1], pos); if (len < 0) - state.debugThrowLastTrace(EvalError({ - .msg = hintfmt("cannot create list of size %1%", len), - .errPos = state.positions[pos] - })); + state.debugThrowLastTrace( + EvalError({.msg = hintfmt("cannot create list of size %1%", len), + .errPos = state.positions[pos]})); state.mkList(v, len); @@ -2973,10 +3046,11 @@ static RegisterPrimOp primop_genList({ .fun = prim_genList, }); -static void prim_lessThan(EvalState & state, const PosIdx pos, Value * * args, Value & v); +static void prim_lessThan(EvalState & state, const PosIdx pos, Value ** args, + Value & v); - -static void prim_sort(EvalState & state, const PosIdx pos, Value * * args, Value & v) +static void prim_sort(EvalState & state, const PosIdx pos, Value ** args, + Value & v) { state.forceFunction(*args[0], pos); state.forceList(*args[1], pos); @@ -3027,7 +3101,8 @@ static RegisterPrimOp primop_sort({ .fun = prim_sort, }); -static void prim_partition(EvalState & state, const PosIdx pos, Value * * args, Value & v) +static void prim_partition(EvalState & state, const PosIdx pos, Value ** args, + Value & v) { state.forceFunction(*args[0], pos); state.forceList(*args[1], pos); @@ -3087,7 +3162,8 @@ static RegisterPrimOp primop_partition({ .fun = prim_partition, }); -static void prim_groupBy(EvalState & state, const PosIdx pos, Value * * args, Value & v) +static void prim_groupBy(EvalState & state, const PosIdx pos, Value ** args, + Value & v) { state.forceFunction(*args[0], pos); state.forceList(*args[1], pos); @@ -3139,7 +3215,8 @@ static RegisterPrimOp primop_groupBy({ .fun = prim_groupBy, }); -static void prim_concatMap(EvalState & state, const PosIdx pos, Value * * args, Value & v) +static void prim_concatMap(EvalState & state, const PosIdx pos, Value ** args, + Value & v) { state.forceFunction(*args[0], pos); state.forceList(*args[1], pos); @@ -3152,9 +3229,11 @@ static void prim_concatMap(EvalState & state, const PosIdx pos, Value * * args, Value * vElem = args[1]->listElems()[n]; state.callFunction(*args[0], *vElem, lists[n], pos); try { - state.forceList(lists[n], lists[n].determinePos(args[0]->determinePos(pos))); - } catch (TypeError &e) { - e.addTrace(state.positions[pos], hintfmt("while invoking '%s'", "concatMap")); + state.forceList(lists[n], + lists[n].determinePos(args[0]->determinePos(pos))); + } catch (TypeError & e) { + e.addTrace(state.positions[pos], + hintfmt("while invoking '%s'", "concatMap")); state.debugThrowLastTrace(e); } len += lists[n].listSize(); @@ -3180,18 +3259,18 @@ static RegisterPrimOp primop_concatMap({ .fun = prim_concatMap, }); - /************************************************************* * Integer arithmetic *************************************************************/ - -static void prim_add(EvalState & state, const PosIdx pos, Value * * args, Value & v) +static void prim_add(EvalState & state, const PosIdx pos, Value ** args, + Value & v) { state.forceValue(*args[0], pos); state.forceValue(*args[1], pos); if (args[0]->type() == nFloat || args[1]->type() == nFloat) - v.mkFloat(state.forceFloat(*args[0], pos) + state.forceFloat(*args[1], pos)); + v.mkFloat(state.forceFloat(*args[0], pos) + + state.forceFloat(*args[1], pos)); else v.mkInt(state.forceInt(*args[0], pos) + state.forceInt(*args[1], pos)); } @@ -3205,12 +3284,14 @@ static RegisterPrimOp primop_add({ .fun = prim_add, }); -static void prim_sub(EvalState & state, const PosIdx pos, Value * * args, Value & v) +static void prim_sub(EvalState & state, const PosIdx pos, Value ** args, + Value & v) { state.forceValue(*args[0], pos); state.forceValue(*args[1], pos); if (args[0]->type() == nFloat || args[1]->type() == nFloat) - v.mkFloat(state.forceFloat(*args[0], pos) - state.forceFloat(*args[1], pos)); + v.mkFloat(state.forceFloat(*args[0], pos) - + state.forceFloat(*args[1], pos)); else v.mkInt(state.forceInt(*args[0], pos) - state.forceInt(*args[1], pos)); } @@ -3224,12 +3305,14 @@ static RegisterPrimOp primop_sub({ .fun = prim_sub, }); -static void prim_mul(EvalState & state, const PosIdx pos, Value * * args, Value & v) +static void prim_mul(EvalState & state, const PosIdx pos, Value ** args, + Value & v) { state.forceValue(*args[0], pos); state.forceValue(*args[1], pos); if (args[0]->type() == nFloat || args[1]->type() == nFloat) - v.mkFloat(state.forceFloat(*args[0], pos) * state.forceFloat(*args[1], pos)); + v.mkFloat(state.forceFloat(*args[0], pos) * + state.forceFloat(*args[1], pos)); else v.mkInt(state.forceInt(*args[0], pos) * state.forceInt(*args[1], pos)); } @@ -3243,29 +3326,28 @@ static RegisterPrimOp primop_mul({ .fun = prim_mul, }); -static void prim_div(EvalState & state, const PosIdx pos, Value * * args, Value & v) +static void prim_div(EvalState & state, const PosIdx pos, Value ** args, + Value & v) { state.forceValue(*args[0], pos); state.forceValue(*args[1], pos); NixFloat f2 = state.forceFloat(*args[1], pos); if (f2 == 0) - state.debugThrowLastTrace(EvalError({ - .msg = hintfmt("division by zero"), - .errPos = state.positions[pos] - })); + state.debugThrowLastTrace(EvalError({.msg = hintfmt("division by zero"), + .errPos = state.positions[pos]})); if (args[0]->type() == nFloat || args[1]->type() == nFloat) { - v.mkFloat(state.forceFloat(*args[0], pos) / state.forceFloat(*args[1], pos)); + v.mkFloat(state.forceFloat(*args[0], pos) / + state.forceFloat(*args[1], pos)); } else { NixInt i1 = state.forceInt(*args[0], pos); NixInt i2 = state.forceInt(*args[1], pos); /* Avoid division overflow as it might raise SIGFPE. */ if (i1 == std::numeric_limits::min() && i2 == -1) - state.debugThrowLastTrace(EvalError({ - .msg = hintfmt("overflow in integer division"), - .errPos = state.positions[pos] - })); + state.debugThrowLastTrace( + EvalError({.msg = hintfmt("overflow in integer division"), + .errPos = state.positions[pos]})); v.mkInt(i1 / i2); } @@ -3280,7 +3362,8 @@ static RegisterPrimOp primop_div({ .fun = prim_div, }); -static void prim_bitAnd(EvalState & state, const PosIdx pos, Value * * args, Value & v) +static void prim_bitAnd(EvalState & state, const PosIdx pos, Value ** args, + Value & v) { v.mkInt(state.forceInt(*args[0], pos) & state.forceInt(*args[1], pos)); } @@ -3294,7 +3377,8 @@ static RegisterPrimOp primop_bitAnd({ .fun = prim_bitAnd, }); -static void prim_bitOr(EvalState & state, const PosIdx pos, Value * * args, Value & v) +static void prim_bitOr(EvalState & state, const PosIdx pos, Value ** args, + Value & v) { v.mkInt(state.forceInt(*args[0], pos) | state.forceInt(*args[1], pos)); } @@ -3308,7 +3392,8 @@ static RegisterPrimOp primop_bitOr({ .fun = prim_bitOr, }); -static void prim_bitXor(EvalState & state, const PosIdx pos, Value * * args, Value & v) +static void prim_bitXor(EvalState & state, const PosIdx pos, Value ** args, + Value & v) { v.mkInt(state.forceInt(*args[0], pos) ^ state.forceInt(*args[1], pos)); } @@ -3322,7 +3407,8 @@ static RegisterPrimOp primop_bitXor({ .fun = prim_bitXor, }); -static void prim_lessThan(EvalState & state, const PosIdx pos, Value * * args, Value & v) +static void prim_lessThan(EvalState & state, const PosIdx pos, Value ** args, + Value & v) { state.forceValue(*args[0], pos); state.forceValue(*args[1], pos); @@ -3341,16 +3427,15 @@ static RegisterPrimOp primop_lessThan({ .fun = prim_lessThan, }); - /************************************************************* * String manipulation *************************************************************/ - /* Convert the argument to a string. Paths are *not* copied to the store, so `toString /foo/bar' yields `"/foo/bar"', not `"/nix/store/whatever..."'. */ -static void prim_toString(EvalState & state, const PosIdx pos, Value * * args, Value & v) +static void prim_toString(EvalState & state, const PosIdx pos, Value ** args, + Value & v) { PathSet context; auto s = state.coerceToString(pos, *args[0], context, true, false); @@ -3385,7 +3470,8 @@ static RegisterPrimOp primop_toString({ at character position `min(start, stringLength str)' inclusive and ending at `min(start + len, stringLength str)'. `start' must be non-negative. */ -static void prim_substring(EvalState & state, const PosIdx pos, Value * * args, Value & v) +static void prim_substring(EvalState & state, const PosIdx pos, Value ** args, + Value & v) { int start = state.forceInt(*args[0], pos); int len = state.forceInt(*args[1], pos); @@ -3393,12 +3479,12 @@ static void prim_substring(EvalState & state, const PosIdx pos, Value * * args, auto s = state.coerceToString(pos, *args[2], context); if (start < 0) - state.debugThrowLastTrace(EvalError({ - .msg = hintfmt("negative start position in 'substring'"), - .errPos = state.positions[pos] - })); + state.debugThrowLastTrace( + EvalError({.msg = hintfmt("negative start position in 'substring'"), + .errPos = state.positions[pos]})); - v.mkString((unsigned int) start >= s->size() ? "" : s->substr(start, len), context); + v.mkString((unsigned int) start >= s->size() ? "" : s->substr(start, len), + context); } static RegisterPrimOp primop_substring({ @@ -3421,7 +3507,8 @@ static RegisterPrimOp primop_substring({ .fun = prim_substring, }); -static void prim_stringLength(EvalState & state, const PosIdx pos, Value * * args, Value & v) +static void prim_stringLength(EvalState & state, const PosIdx pos, + Value ** args, Value & v) { PathSet context; auto s = state.coerceToString(pos, *args[0], context); @@ -3439,15 +3526,15 @@ static RegisterPrimOp primop_stringLength({ }); /* Return the cryptographic hash of a string in base-16. */ -static void prim_hashString(EvalState & state, const PosIdx pos, Value * * args, Value & v) +static void prim_hashString(EvalState & state, const PosIdx pos, Value ** args, + Value & v) { auto type = state.forceStringNoCtx(*args[0], pos); std::optional ht = parseHashType(type); if (!ht) - state.debugThrowLastTrace(Error({ - .msg = hintfmt("unknown hash type '%1%'", type), - .errPos = state.positions[pos] - })); + state.debugThrowLastTrace( + Error({.msg = hintfmt("unknown hash type '%1%'", type), + .errPos = state.positions[pos]})); PathSet context; // discarded auto s = state.forceString(*args[1], context, pos); @@ -3466,8 +3553,7 @@ static RegisterPrimOp primop_hashString({ .fun = prim_hashString, }); -struct RegexCache -{ +struct RegexCache { // TODO use C++20 transparent comparison when available std::unordered_map cache; std::list keys; @@ -3478,7 +3564,9 @@ struct RegexCache if (it != cache.end()) return it->second; keys.emplace_back(re); - return cache.emplace(keys.back(), std::regex(keys.back(), std::regex::extended)).first->second; + return cache + .emplace(keys.back(), std::regex(keys.back(), std::regex::extended)) + .first->second; } }; @@ -3487,7 +3575,7 @@ std::shared_ptr makeRegexCache() return std::make_shared(); } -void prim_match(EvalState & state, const PosIdx pos, Value * * args, Value & v) +void prim_match(EvalState & state, const PosIdx pos, Value ** args, Value & v) { auto re = state.forceStringNoCtx(*args[0], pos); @@ -3508,24 +3596,24 @@ void prim_match(EvalState & state, const PosIdx pos, Value * * args, Value & v) const size_t len = match.size() - 1; state.mkList(v, len); for (size_t i = 0; i < len; ++i) { - if (!match[i+1].matched) + if (!match[i + 1].matched) (v.listElems()[i] = state.allocValue())->mkNull(); else - (v.listElems()[i] = state.allocValue())->mkString(match[i + 1].str()); + (v.listElems()[i] = state.allocValue()) + ->mkString(match[i + 1].str()); } } catch (std::regex_error & e) { if (e.code() == std::regex_constants::error_space) { // limit is _GLIBCXX_REGEX_STATE_LIMIT for libstdc++ - state.debugThrowLastTrace(EvalError({ - .msg = hintfmt("memory limit exceeded by regular expression '%s'", re), - .errPos = state.positions[pos] - })); + state.debugThrowLastTrace(EvalError( + {.msg = hintfmt( + "memory limit exceeded by regular expression '%s'", re), + .errPos = state.positions[pos]})); } else - state.debugThrowLastTrace(EvalError({ - .msg = hintfmt("invalid regular expression '%s'", re), - .errPos = state.positions[pos] - })); + state.debugThrowLastTrace(EvalError( + {.msg = hintfmt("invalid regular expression '%s'", re), + .errPos = state.positions[pos]})); } } @@ -3567,7 +3655,7 @@ static RegisterPrimOp primop_match({ /* Split a string with a regular expression, and return a list of the non-matching parts interleaved by the lists of the matching groups. */ -void prim_split(EvalState & state, const PosIdx pos, Value * * args, Value & v) +void prim_split(EvalState & state, const PosIdx pos, Value ** args, Value & v) { auto re = state.forceStringNoCtx(*args[0], pos); @@ -3596,7 +3684,8 @@ void prim_split(EvalState & state, const PosIdx pos, Value * * args, Value & v) auto match = *i; // Add a string for non-matched characters. - (v.listElems()[idx++] = state.allocValue())->mkString(match.prefix().str()); + (v.listElems()[idx++] = state.allocValue()) + ->mkString(match.prefix().str()); // Add a list for matched substrings. const size_t slen = match.size() - 1; @@ -3608,12 +3697,14 @@ void prim_split(EvalState & state, const PosIdx pos, Value * * args, Value & v) if (!match[si + 1].matched) (elem->listElems()[si] = state.allocValue())->mkNull(); else - (elem->listElems()[si] = state.allocValue())->mkString(match[si + 1].str()); + (elem->listElems()[si] = state.allocValue()) + ->mkString(match[si + 1].str()); } // Add a string for non-matched suffix characters. if (idx == 2 * len) - (v.listElems()[idx++] = state.allocValue())->mkString(match.suffix().str()); + (v.listElems()[idx++] = state.allocValue()) + ->mkString(match.suffix().str()); } assert(idx == 2 * len + 1); @@ -3621,15 +3712,14 @@ void prim_split(EvalState & state, const PosIdx pos, Value * * args, Value & v) } catch (std::regex_error & e) { if (e.code() == std::regex_constants::error_space) { // limit is _GLIBCXX_REGEX_STATE_LIMIT for libstdc++ - state.debugThrowLastTrace(EvalError({ - .msg = hintfmt("memory limit exceeded by regular expression '%s'", re), - .errPos = state.positions[pos] - })); + state.debugThrowLastTrace(EvalError( + {.msg = hintfmt( + "memory limit exceeded by regular expression '%s'", re), + .errPos = state.positions[pos]})); } else - state.debugThrowLastTrace(EvalError({ - .msg = hintfmt("invalid regular expression '%s'", re), - .errPos = state.positions[pos] - })); + state.debugThrowLastTrace(EvalError( + {.msg = hintfmt("invalid regular expression '%s'", re), + .errPos = state.positions[pos]})); } } @@ -3670,7 +3760,8 @@ static RegisterPrimOp primop_split({ .fun = prim_split, }); -static void prim_concatStringsSep(EvalState & state, const PosIdx pos, Value * * args, Value & v) +static void prim_concatStringsSep(EvalState & state, const PosIdx pos, + Value ** args, Value & v) { PathSet context; @@ -3682,7 +3773,10 @@ static void prim_concatStringsSep(EvalState & state, const PosIdx pos, Value * * bool first = true; for (auto elem : args[1]->listItems()) { - if (first) first = false; else res += sep; + if (first) + first = false; + else + res += sep; res += *state.coerceToString(pos, *elem, context); } @@ -3700,15 +3794,16 @@ static RegisterPrimOp primop_concatStringsSep({ .fun = prim_concatStringsSep, }); -static void prim_replaceStrings(EvalState & state, const PosIdx pos, Value * * args, Value & v) +static void prim_replaceStrings(EvalState & state, const PosIdx pos, + Value ** args, Value & v) { state.forceList(*args[0], pos); state.forceList(*args[1], pos); if (args[0]->listSize() != args[1]->listSize()) - state.debugThrowLastTrace(EvalError({ - .msg = hintfmt("'from' and 'to' arguments to 'replaceStrings' have different lengths"), - .errPos = state.positions[pos] - })); + state.debugThrowLastTrace(EvalError( + {.msg = hintfmt("'from' and 'to' arguments to 'replaceStrings' " + "have different lengths"), + .errPos = state.positions[pos]})); std::vector from; from.reserve(args[0]->listSize()); @@ -3727,8 +3822,9 @@ static void prim_replaceStrings(EvalState & state, const PosIdx pos, Value * * a auto s = state.forceString(*args[2], context, pos); std::string res; - // Loops one past last character to handle the case where 'from' contains an empty string. - for (size_t p = 0; p <= s.size(); ) { + // Loops one past last character to handle the case where 'from' contains an + // empty string. + for (size_t p = 0; p <= s.size();) { bool found = false; auto i = from.begin(); auto j = to.begin(); @@ -3743,7 +3839,7 @@ static void prim_replaceStrings(EvalState & state, const PosIdx pos, Value * * a } else { p += i->size(); } - for (auto& path : j->second) + for (auto & path : j->second) context.insert(path); j->second.clear(); break; @@ -3774,13 +3870,12 @@ static RegisterPrimOp primop_replaceStrings({ .fun = prim_replaceStrings, }); - /************************************************************* * Versions *************************************************************/ - -static void prim_parseDrvName(EvalState & state, const PosIdx pos, Value * * args, Value & v) +static void prim_parseDrvName(EvalState & state, const PosIdx pos, + Value ** args, Value & v) { auto name = state.forceStringNoCtx(*args[0], pos); DrvName parsed(name); @@ -3804,7 +3899,8 @@ static RegisterPrimOp primop_parseDrvName({ .fun = prim_parseDrvName, }); -static void prim_compareVersions(EvalState & state, const PosIdx pos, Value * * args, Value & v) +static void prim_compareVersions(EvalState & state, const PosIdx pos, + Value ** args, Value & v) { auto version1 = state.forceStringNoCtx(*args[0], pos); auto version2 = state.forceStringNoCtx(*args[1], pos); @@ -3824,7 +3920,8 @@ static RegisterPrimOp primop_compareVersions({ .fun = prim_compareVersions, }); -static void prim_splitVersion(EvalState & state, const PosIdx pos, Value * * args, Value & v) +static void prim_splitVersion(EvalState & state, const PosIdx pos, + Value ** args, Value & v) { auto version = state.forceStringNoCtx(*args[0], pos); auto iter = version.cbegin(); @@ -3851,18 +3948,16 @@ static RegisterPrimOp primop_splitVersion({ .fun = prim_splitVersion, }); - /************************************************************* * Primop registration *************************************************************/ - RegisterPrimOp::PrimOps * RegisterPrimOp::primOps; - RegisterPrimOp::RegisterPrimOp(std::string name, size_t arity, PrimOpFun fun) { - if (!primOps) primOps = new PrimOps; + if (!primOps) + primOps = new PrimOps; primOps->push_back({ .name = name, .args = {}, @@ -3871,14 +3966,13 @@ RegisterPrimOp::RegisterPrimOp(std::string name, size_t arity, PrimOpFun fun) }); } - RegisterPrimOp::RegisterPrimOp(Info && info) { - if (!primOps) primOps = new PrimOps; + if (!primOps) + primOps = new PrimOps; primOps->push_back(std::move(info)); } - void EvalState::createBaseEnv() { baseEnv.up = 0; @@ -3939,9 +4033,9 @@ void EvalState::createBaseEnv() if (RegisterPrimOp::primOps) for (auto & primOp : *RegisterPrimOp::primOps) - if (!primOp.experimentalFeature - || settings.isExperimentalFeatureEnabled(*primOp.experimentalFeature)) - { + if (!primOp.experimentalFeature || + settings.isExperimentalFeatureEnabled( + *primOp.experimentalFeature)) { addPrimOp({ .fun = primOp.fun, .arity = std::max(primOp.args.size(), primOp.arity), @@ -3966,12 +4060,13 @@ void EvalState::createBaseEnv() /* Note: we have to initialize the 'derivation' constant *after* building baseEnv/staticBaseEnv because it uses 'builtins'. */ char code[] = - #include "primops/derivation.nix.gen.hh" +#include "primops/derivation.nix.gen.hh" // the parser needs two NUL bytes as terminators; one of them // is implied by being a C string. "\0"; - eval(parse(code, sizeof(code), foFile, derivationNixPath, "/", staticBaseEnv), *vDerivation); + eval(parse(code, sizeof(code), foFile, derivationNixPath, "/", + staticBaseEnv), + *vDerivation); } - } diff --git a/src/libexpr/primops.hh b/src/libexpr/primops.hh index 1cfb4356b27f..dcac98e0b7bc 100644 --- a/src/libexpr/primops.hh +++ b/src/libexpr/primops.hh @@ -7,10 +7,8 @@ namespace nix { -struct RegisterPrimOp -{ - struct Info - { +struct RegisterPrimOp { + struct Info { std::string name; std::vector args; size_t arity = 0; @@ -25,10 +23,7 @@ struct RegisterPrimOp /* You can register a constant by passing an arity of 0. fun will get called during EvalState initialization, so there may be primops not yet added and builtins is not yet sorted. */ - RegisterPrimOp( - std::string name, - size_t arity, - PrimOpFun fun); + RegisterPrimOp(std::string name, size_t arity, PrimOpFun fun); RegisterPrimOp(Info && info); }; @@ -38,9 +33,10 @@ struct RegisterPrimOp them. */ /* Load a ValueInitializer from a DSO and return whatever it initializes */ -void prim_importNative(EvalState & state, const PosIdx pos, Value * * args, Value & v); +void prim_importNative(EvalState & state, const PosIdx pos, Value ** args, + Value & v); /* Execute a program and parse its output */ -void prim_exec(EvalState & state, const PosIdx pos, Value * * args, Value & v); +void prim_exec(EvalState & state, const PosIdx pos, Value ** args, Value & v); } diff --git a/src/libexpr/primops/context.cc b/src/libexpr/primops/context.cc index 97913698429f..e94edf4e76ce 100644 --- a/src/libexpr/primops/context.cc +++ b/src/libexpr/primops/context.cc @@ -5,17 +5,20 @@ namespace nix { -static void prim_unsafeDiscardStringContext(EvalState & state, const PosIdx pos, Value * * args, Value & v) +static void prim_unsafeDiscardStringContext(EvalState & state, const PosIdx pos, + Value ** args, Value & v) { PathSet context; auto s = state.coerceToString(pos, *args[0], context); v.mkString(*s); } -static RegisterPrimOp primop_unsafeDiscardStringContext("__unsafeDiscardStringContext", 1, prim_unsafeDiscardStringContext); +static RegisterPrimOp + primop_unsafeDiscardStringContext("__unsafeDiscardStringContext", 1, + prim_unsafeDiscardStringContext); - -static void prim_hasContext(EvalState & state, const PosIdx pos, Value * * args, Value & v) +static void prim_hasContext(EvalState & state, const PosIdx pos, Value ** args, + Value & v) { PathSet context; state.forceString(*args[0], context, pos); @@ -24,14 +27,15 @@ static void prim_hasContext(EvalState & state, const PosIdx pos, Value * * args, static RegisterPrimOp primop_hasContext("__hasContext", 1, prim_hasContext); - /* Sometimes we want to pass a derivation path (i.e. pkg.drvPath) to a builder without causing the derivation to be built (for instance, in the derivation that builds NARs in nix-push, when doing source-only deployment). This primop marks the string context so that builtins.derivation adds the path to drv.inputSrcs rather than drv.inputDrvs. */ -static void prim_unsafeDiscardOutputDependency(EvalState & state, const PosIdx pos, Value * * args, Value & v) +static void prim_unsafeDiscardOutputDependency(EvalState & state, + const PosIdx pos, Value ** args, + Value & v) { PathSet context; auto s = state.coerceToString(pos, *args[0], context); @@ -43,8 +47,9 @@ static void prim_unsafeDiscardOutputDependency(EvalState & state, const PosIdx p v.mkString(*s, context2); } -static RegisterPrimOp primop_unsafeDiscardOutputDependency("__unsafeDiscardOutputDependency", 1, prim_unsafeDiscardOutputDependency); - +static RegisterPrimOp + primop_unsafeDiscardOutputDependency("__unsafeDiscardOutputDependency", 1, + prim_unsafeDiscardOutputDependency); /* Extract the context of a string as a structured Nix value. @@ -65,7 +70,8 @@ static RegisterPrimOp primop_unsafeDiscardOutputDependency("__unsafeDiscardOutpu Note that for a given path any combination of the above attributes may be present. */ -static void prim_getContext(EvalState & state, const PosIdx pos, Value * * args, Value & v) +static void prim_getContext(EvalState & state, const PosIdx pos, Value ** args, + Value & v) { struct ContextInfo { bool path = false; @@ -93,7 +99,11 @@ static void prim_getContext(EvalState & state, const PosIdx pos, Value * * args, auto iter = contextInfos.find(*path); if (iter == contextInfos.end()) { - contextInfos.emplace(*path, ContextInfo{isPath, isAllOutputs, output.empty() ? Strings{} : Strings{std::move(output)}}); + contextInfos.emplace(*path, + ContextInfo{isPath, isAllOutputs, + output.empty() + ? Strings{} + : Strings{std::move(output)}}); } else { if (isPath) iter->second.path = true; @@ -118,7 +128,8 @@ static void prim_getContext(EvalState & state, const PosIdx pos, Value * * args, auto & outputsVal = infoAttrs.alloc(state.sOutputs); state.mkList(outputsVal, info.second.outputs.size()); for (const auto & [i, output] : enumerate(info.second.outputs)) - (outputsVal.listElems()[i] = state.allocValue())->mkString(output); + (outputsVal.listElems()[i] = state.allocValue()) + ->mkString(output); } attrs.alloc(info.first).mkAttrs(infoAttrs); } @@ -128,13 +139,13 @@ static void prim_getContext(EvalState & state, const PosIdx pos, Value * * args, static RegisterPrimOp primop_getContext("__getContext", 1, prim_getContext); - /* Append the given context to a given string. See the commentary above unsafeGetContext for details of the context representation. */ -static void prim_appendContext(EvalState & state, const PosIdx pos, Value * * args, Value & v) +static void prim_appendContext(EvalState & state, const PosIdx pos, + Value ** args, Value & v) { PathSet context; auto orig = state.forceString(*args[0], context, pos); @@ -146,10 +157,9 @@ static void prim_appendContext(EvalState & state, const PosIdx pos, Value * * ar for (auto & i : *args[1]->attrs) { const auto & name = state.symbols[i.name]; if (!state.store->isStorePath(name)) - throw EvalError({ - .msg = hintfmt("Context key '%s' is not a store path", name), - .errPos = state.positions[i.pos] - }); + throw EvalError( + {.msg = hintfmt("Context key '%s' is not a store path", name), + .errPos = state.positions[i.pos]}); if (!settings.readOnlyMode) state.store->ensurePath(state.store->parseStorePath(name)); state.forceAttrs(*i.value, i.pos); @@ -163,10 +173,12 @@ static void prim_appendContext(EvalState & state, const PosIdx pos, Value * * ar if (iter != i.value->attrs->end()) { if (state.forceBool(*iter->value, iter->pos)) { if (!isDerivation(name)) { - throw EvalError({ - .msg = hintfmt("Tried to add all-outputs context of %s, which is not a derivation, to a string", name), - .errPos = state.positions[i.pos] - }); + throw EvalError( + {.msg = + hintfmt("Tried to add all-outputs context of %s, " + "which is not a derivation, to a string", + name), + .errPos = state.positions[i.pos]}); } context.insert(concatStrings("=", name)); } @@ -176,10 +188,12 @@ static void prim_appendContext(EvalState & state, const PosIdx pos, Value * * ar if (iter != i.value->attrs->end()) { state.forceList(*iter->value, iter->pos); if (iter->value->listSize() && !isDerivation(name)) { - throw EvalError({ - .msg = hintfmt("Tried to add derivation output context of %s, which is not a derivation, to a string", name), - .errPos = state.positions[i.pos] - }); + throw EvalError( + {.msg = + hintfmt("Tried to add derivation output context of " + "%s, which is not a derivation, to a string", + name), + .errPos = state.positions[i.pos]}); } for (auto elem : iter->value->listItems()) { auto outputName = state.forceStringNoCtx(*elem, iter->pos); @@ -191,6 +205,7 @@ static void prim_appendContext(EvalState & state, const PosIdx pos, Value * * ar v.mkString(orig, context); } -static RegisterPrimOp primop_appendContext("__appendContext", 2, prim_appendContext); +static RegisterPrimOp primop_appendContext("__appendContext", 2, + prim_appendContext); } diff --git a/src/libexpr/primops/fetchClosure.cc b/src/libexpr/primops/fetchClosure.cc index 662c9652eaa4..2b1506c9fe3b 100644 --- a/src/libexpr/primops/fetchClosure.cc +++ b/src/libexpr/primops/fetchClosure.cc @@ -5,7 +5,8 @@ namespace nix { -static void prim_fetchClosure(EvalState & state, const PosIdx pos, Value * * args, Value & v) +static void prim_fetchClosure(EvalState & state, const PosIdx pos, + Value ** args, Value & v) { state.forceAttrs(*args[0], pos); @@ -25,9 +26,11 @@ static void prim_fetchClosure(EvalState & state, const PosIdx pos, Value * * arg else if (attrName == "toPath") { state.forceValue(*attr.value, attr.pos); toCA = true; - if (attr.value->type() != nString || attr.value->string.s != std::string("")) { + if (attr.value->type() != nString || + attr.value->string.s != std::string("")) { PathSet context; - toPath = state.coerceToStorePath(attr.pos, *attr.value, context); + toPath = + state.coerceToStorePath(attr.pos, *attr.value, context); } } @@ -35,68 +38,69 @@ static void prim_fetchClosure(EvalState & state, const PosIdx pos, Value * * arg fromStoreUrl = state.forceStringNoCtx(*attr.value, attr.pos); else - throw Error({ - .msg = hintfmt("attribute '%s' isn't supported in call to 'fetchClosure'", attrName), - .errPos = state.positions[pos] - }); + throw Error( + {.msg = hintfmt( + "attribute '%s' isn't supported in call to 'fetchClosure'", + attrName), + .errPos = state.positions[pos]}); } if (!fromPath) - throw Error({ - .msg = hintfmt("attribute '%s' is missing in call to 'fetchClosure'", "fromPath"), - .errPos = state.positions[pos] - }); + throw Error({.msg = hintfmt( + "attribute '%s' is missing in call to 'fetchClosure'", + "fromPath"), + .errPos = state.positions[pos]}); if (!fromStoreUrl) - throw Error({ - .msg = hintfmt("attribute '%s' is missing in call to 'fetchClosure'", "fromStore"), - .errPos = state.positions[pos] - }); + throw Error({.msg = hintfmt( + "attribute '%s' is missing in call to 'fetchClosure'", + "fromStore"), + .errPos = state.positions[pos]}); auto parsedURL = parseURL(*fromStoreUrl); - if (parsedURL.scheme != "http" && - parsedURL.scheme != "https" && + if (parsedURL.scheme != "http" && parsedURL.scheme != "https" && !(getEnv("_NIX_IN_TEST").has_value() && parsedURL.scheme == "file")) - throw Error({ - .msg = hintfmt("'fetchClosure' only supports http:// and https:// stores"), - .errPos = state.positions[pos] - }); + throw Error( + {.msg = hintfmt( + "'fetchClosure' only supports http:// and https:// stores"), + .errPos = state.positions[pos]}); if (!parsedURL.query.empty()) - throw Error({ - .msg = hintfmt("'fetchClosure' does not support URL query parameters (in '%s')", *fromStoreUrl), - .errPos = state.positions[pos] - }); + throw Error({.msg = hintfmt("'fetchClosure' does not support URL query " + "parameters (in '%s')", + *fromStoreUrl), + .errPos = state.positions[pos]}); auto fromStore = openStore(parsedURL.to_string()); if (toCA) { if (!toPath || !state.store->isValidPath(*toPath)) { - auto remappings = makeContentAddressed(*fromStore, *state.store, { *fromPath }); + auto remappings = + makeContentAddressed(*fromStore, *state.store, {*fromPath}); auto i = remappings.find(*fromPath); assert(i != remappings.end()); if (toPath && *toPath != i->second) - throw Error({ - .msg = hintfmt("rewriting '%s' to content-addressed form yielded '%s', while '%s' was expected", - state.store->printStorePath(*fromPath), - state.store->printStorePath(i->second), - state.store->printStorePath(*toPath)), - .errPos = state.positions[pos] - }); + throw Error( + {.msg = hintfmt("rewriting '%s' to content-addressed form " + "yielded '%s', while '%s' was expected", + state.store->printStorePath(*fromPath), + state.store->printStorePath(i->second), + state.store->printStorePath(*toPath)), + .errPos = state.positions[pos]}); if (!toPath) - throw Error({ - .msg = hintfmt( - "rewriting '%s' to content-addressed form yielded '%s'; " - "please set this in the 'toPath' attribute passed to 'fetchClosure'", - state.store->printStorePath(*fromPath), - state.store->printStorePath(i->second)), - .errPos = state.positions[pos] - }); + throw Error( + {.msg = hintfmt("rewriting '%s' to content-addressed form " + "yielded '%s'; " + "please set this in the 'toPath' attribute " + "passed to 'fetchClosure'", + state.store->printStorePath(*fromPath), + state.store->printStorePath(i->second)), + .errPos = state.positions[pos]}); } } else { if (!state.store->isValidPath(*fromPath)) - copyClosure(*fromStore, *state.store, RealisedPath::Set { *fromPath }); + copyClosure(*fromStore, *state.store, RealisedPath::Set{*fromPath}); toPath = fromPath; } @@ -104,11 +108,11 @@ static void prim_fetchClosure(EvalState & state, const PosIdx pos, Value * * arg if (evalSettings.pureEval) { auto info = state.store->queryPathInfo(*toPath); if (!info->isContentAddressed(*state.store)) - throw Error({ - .msg = hintfmt("in pure mode, 'fetchClosure' requires a content-addressed path, which '%s' isn't", - state.store->printStorePath(*toPath)), - .errPos = state.positions[pos] - }); + throw Error( + {.msg = hintfmt("in pure mode, 'fetchClosure' requires a " + "content-addressed path, which '%s' isn't", + state.store->printStorePath(*toPath)), + .errPos = state.positions[pos]}); } auto toPathS = state.store->printStorePath(*toPath); diff --git a/src/libexpr/primops/fetchMercurial.cc b/src/libexpr/primops/fetchMercurial.cc index 249c0934e246..6ca76e7cb564 100644 --- a/src/libexpr/primops/fetchMercurial.cc +++ b/src/libexpr/primops/fetchMercurial.cc @@ -7,7 +7,8 @@ namespace nix { -static void prim_fetchMercurial(EvalState & state, const PosIdx pos, Value * * args, Value & v) +static void prim_fetchMercurial(EvalState & state, const PosIdx pos, + Value ** args, Value & v) { std::string url; std::optional rev; @@ -24,7 +25,10 @@ static void prim_fetchMercurial(EvalState & state, const PosIdx pos, Value * * a for (auto & attr : *args[0]->attrs) { std::string_view n(state.symbols[attr.name]); if (n == "url") - url = state.coerceToString(attr.pos, *attr.value, context, false, false).toOwned(); + url = state + .coerceToString(attr.pos, *attr.value, context, false, + false) + .toOwned(); else if (n == "rev") { // Ugly: unlike fetchGit, here the "rev" attribute can // be both a revision or a branch/tag name. @@ -33,38 +37,41 @@ static void prim_fetchMercurial(EvalState & state, const PosIdx pos, Value * * a rev = Hash::parseAny(value, htSHA1); else ref = value; - } - else if (n == "name") + } else if (n == "name") name = state.forceStringNoCtx(*attr.value, attr.pos); else - throw EvalError({ - .msg = hintfmt("unsupported argument '%s' to 'fetchMercurial'", state.symbols[attr.name]), - .errPos = state.positions[attr.pos] - }); + throw EvalError( + {.msg = hintfmt( + "unsupported argument '%s' to 'fetchMercurial'", + state.symbols[attr.name]), + .errPos = state.positions[attr.pos]}); } if (url.empty()) - throw EvalError({ - .msg = hintfmt("'url' argument required"), - .errPos = state.positions[pos] - }); + throw EvalError({.msg = hintfmt("'url' argument required"), + .errPos = state.positions[pos]}); } else - url = state.coerceToString(pos, *args[0], context, false, false).toOwned(); + url = state.coerceToString(pos, *args[0], context, false, false) + .toOwned(); // 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, 'fetchMercurial' requires a Mercurial revision"); + throw Error("in pure evaluation mode, 'fetchMercurial' requires a " + "Mercurial revision"); fetchers::Attrs attrs; attrs.insert_or_assign("type", "hg"); - attrs.insert_or_assign("url", url.find("://") != std::string::npos ? url : "file://" + url); + attrs.insert_or_assign( + "url", url.find("://") != std::string::npos ? url : "file://" + url); attrs.insert_or_assign("name", std::string(name)); - if (ref) attrs.insert_or_assign("ref", *ref); - if (rev) attrs.insert_or_assign("rev", rev->gitRev()); + if (ref) + attrs.insert_or_assign("ref", *ref); + if (rev) + attrs.insert_or_assign("rev", rev->gitRev()); auto input = fetchers::Input::fromAttrs(std::move(attrs)); // FIXME: use name @@ -87,6 +94,7 @@ static void prim_fetchMercurial(EvalState & state, const PosIdx pos, Value * * a state.allowPath(tree.storePath); } -static RegisterPrimOp r_fetchMercurial("fetchMercurial", 1, prim_fetchMercurial); +static RegisterPrimOp r_fetchMercurial("fetchMercurial", 1, + prim_fetchMercurial); } diff --git a/src/libexpr/primops/fetchTree.cc b/src/libexpr/primops/fetchTree.cc index e5eeea520dc5..e3351abe4530 100644 --- a/src/libexpr/primops/fetchTree.cc +++ b/src/libexpr/primops/fetchTree.cc @@ -11,13 +11,9 @@ namespace nix { -void emitTreeAttrs( - EvalState & state, - const fetchers::Tree & tree, - const fetchers::Input & input, - Value & v, - bool emptyRevFallback, - bool forceDirty) +void emitTreeAttrs(EvalState & state, const fetchers::Tree & tree, + const fetchers::Input & input, Value & v, + bool emptyRevFallback, bool forceDirty) { assert(input.isLocked()); @@ -34,8 +30,9 @@ void emitTreeAttrs( attrs.alloc("narHash").mkString(narHash->to_string(SRI, true)); if (input.getType() == "git") - attrs.alloc("submodules").mkBool( - fetchers::maybeGetBoolAttr(input.attrs, "submodules").value_or(false)); + attrs.alloc("submodules") + .mkBool(fetchers::maybeGetBoolAttr(input.attrs, "submodules") + .value_or(false)); if (!forceDirty) { @@ -43,7 +40,8 @@ void emitTreeAttrs( attrs.alloc("rev").mkString(rev->gitRev()); attrs.alloc("shortRev").mkString(rev->gitShortRev()); } else if (emptyRevFallback) { - // Backwards compat for `builtins.fetchGit`: dirty repos return an empty sha1 as rev + // Backwards compat for `builtins.fetchGit`: dirty repos return an + // empty sha1 as rev auto emptyHash = Hash(htSHA1); attrs.alloc("rev").mkString(emptyHash.gitRev()); attrs.alloc("shortRev").mkString(emptyHash.gitShortRev()); @@ -53,22 +51,24 @@ void emitTreeAttrs( attrs.alloc("revCount").mkInt(*revCount); else if (emptyRevFallback) attrs.alloc("revCount").mkInt(0); - } if (auto lastModified = input.getLastModified()) { attrs.alloc("lastModified").mkInt(*lastModified); - attrs.alloc("lastModifiedDate").mkString( - fmt("%s", std::put_time(std::gmtime(&*lastModified), "%Y%m%d%H%M%S"))); + attrs.alloc("lastModifiedDate") + .mkString(fmt("%s", std::put_time(std::gmtime(&*lastModified), + "%Y%m%d%H%M%S"))); } v.mkAttrs(attrs); } -std::string fixURI(std::string uri, EvalState & state, const std::string & defaultScheme = "file") +std::string fixURI(std::string uri, EvalState & state, + const std::string & defaultScheme = "file") { state.checkURI(uri); - return uri.find("://") != std::string::npos ? uri : defaultScheme + "://" + uri; + return uri.find("://") != std::string::npos ? uri + : defaultScheme + "://" + uri; } std::string fixURIForGit(std::string uri, EvalState & state) @@ -78,7 +78,8 @@ std::string fixURIForGit(std::string uri, EvalState & state) * */ static std::regex scp_uri("([^/]*)@(.*):(.*)"); if (uri[0] != '/' && std::regex_match(uri, scp_uri)) - return fixURI(std::regex_replace(uri, scp_uri, "$1@$2/$3"), state, "ssh"); + return fixURI(std::regex_replace(uri, scp_uri, "$1@$2/$3"), state, + "ssh"); else return fixURI(uri, state); } @@ -88,14 +89,10 @@ struct FetchTreeParams { bool allowNameArgument = false; }; -static void fetchTree( - EvalState & state, - const PosIdx pos, - Value * * args, - Value & v, - std::optional type, - const FetchTreeParams & params = FetchTreeParams{} -) { +static void fetchTree(EvalState & state, const PosIdx pos, Value ** args, + Value & v, std::optional type, + const FetchTreeParams & params = FetchTreeParams{}) +{ fetchers::Input input; PathSet context; @@ -108,50 +105,56 @@ static void fetchTree( if (auto aType = args[0]->attrs->get(state.sType)) { if (type) - state.debugThrowLastTrace(EvalError({ - .msg = hintfmt("unexpected attribute 'type'"), - .errPos = state.positions[pos] - })); + state.debugThrowLastTrace( + EvalError({.msg = hintfmt("unexpected attribute 'type'"), + .errPos = state.positions[pos]})); type = state.forceStringNoCtx(*aType->value, aType->pos); } else if (!type) - state.debugThrowLastTrace(EvalError({ - .msg = hintfmt("attribute 'type' is missing in call to 'fetchTree'"), - .errPos = state.positions[pos] - })); + state.debugThrowLastTrace(EvalError( + {.msg = hintfmt( + "attribute 'type' is missing in call to 'fetchTree'"), + .errPos = state.positions[pos]})); attrs.emplace("type", type.value()); for (auto & attr : *args[0]->attrs) { - if (attr.name == state.sType) continue; + if (attr.name == state.sType) + continue; state.forceValue(*attr.value, attr.pos); if (attr.value->type() == nPath || attr.value->type() == nString) { - auto s = state.coerceToString(attr.pos, *attr.value, context, false, false).toOwned(); + auto s = state + .coerceToString(attr.pos, *attr.value, context, + false, false) + .toOwned(); attrs.emplace(state.symbols[attr.name], - state.symbols[attr.name] == "url" - ? type == "git" - ? fixURIForGit(s, state) - : fixURI(s, state) - : s); - } - else if (attr.value->type() == nBool) - attrs.emplace(state.symbols[attr.name], Explicit{attr.value->boolean}); + state.symbols[attr.name] == "url" + ? type == "git" ? fixURIForGit(s, state) + : fixURI(s, state) + : s); + } else if (attr.value->type() == nBool) + attrs.emplace(state.symbols[attr.name], + Explicit{attr.value->boolean}); else if (attr.value->type() == nInt) - attrs.emplace(state.symbols[attr.name], uint64_t(attr.value->integer)); + attrs.emplace(state.symbols[attr.name], + uint64_t(attr.value->integer)); else - state.debugThrowLastTrace(TypeError("fetchTree argument '%s' is %s while a string, Boolean or integer is expected", - state.symbols[attr.name], showType(*attr.value))); + state.debugThrowLastTrace( + TypeError("fetchTree argument '%s' is %s while a string, " + "Boolean or integer is expected", + state.symbols[attr.name], showType(*attr.value))); } if (!params.allowNameArgument) if (auto nameIter = attrs.find("name"); nameIter != attrs.end()) - state.debugThrowLastTrace(EvalError({ - .msg = hintfmt("attribute 'name' isn’t supported in call to 'fetchTree'"), - .errPos = state.positions[pos] - })); + state.debugThrowLastTrace(EvalError( + {.msg = hintfmt("attribute 'name' isn’t supported in call " + "to 'fetchTree'"), + .errPos = state.positions[pos]})); input = fetchers::Input::fromAttrs(std::move(attrs)); } else { - auto url = state.coerceToString(pos, *args[0], context, false, false).toOwned(); + auto url = state.coerceToString(pos, *args[0], context, false, false) + .toOwned(); if (type == "git") { fetchers::Attrs attrs; @@ -167,7 +170,10 @@ static void fetchTree( input = lookupInRegistries(state.store, input).first; if (evalSettings.pureEval && !input.isLocked()) - state.debugThrowLastTrace(EvalError("in pure evaluation mode, 'fetchTree' requires a locked input, at %s", state.positions[pos])); + state.debugThrowLastTrace( + EvalError("in pure evaluation mode, 'fetchTree' requires a locked " + "input, at %s", + state.positions[pos])); auto [tree, input2] = input.fetch(state.store); @@ -176,17 +182,19 @@ static void fetchTree( emitTreeAttrs(state, tree, input2, v, params.emptyRevFallback, false); } -static void prim_fetchTree(EvalState & state, const PosIdx pos, Value * * args, Value & v) +static void prim_fetchTree(EvalState & state, const PosIdx pos, Value ** args, + Value & v) { settings.requireExperimentalFeature(Xp::Flakes); - fetchTree(state, pos, args, v, std::nullopt, FetchTreeParams { .allowNameArgument = false }); + fetchTree(state, pos, args, v, std::nullopt, + FetchTreeParams{.allowNameArgument = false}); } // FIXME: document static RegisterPrimOp primop_fetchTree("fetchTree", 1, prim_fetchTree); -static void fetch(EvalState & state, const PosIdx pos, Value * * args, Value & v, - const std::string & who, bool unpack, std::string name) +static void fetch(EvalState & state, const PosIdx pos, Value ** args, Value & v, + const std::string & who, bool unpack, std::string name) { std::optional url; std::optional expectedHash; @@ -202,21 +210,21 @@ static void fetch(EvalState & state, const PosIdx pos, Value * * args, Value & v if (n == "url") url = state.forceStringNoCtx(*attr.value, attr.pos); else if (n == "sha256") - expectedHash = newHashAllowEmpty(state.forceStringNoCtx(*attr.value, attr.pos), htSHA256); + expectedHash = newHashAllowEmpty( + state.forceStringNoCtx(*attr.value, attr.pos), htSHA256); else if (n == "name") name = state.forceStringNoCtx(*attr.value, attr.pos); else - state.debugThrowLastTrace(EvalError({ - .msg = hintfmt("unsupported argument '%s' to '%s'", n, who), - .errPos = state.positions[attr.pos] - })); + state.debugThrowLastTrace(EvalError( + {.msg = + hintfmt("unsupported argument '%s' to '%s'", n, who), + .errPos = state.positions[attr.pos]})); } if (!url) - state.debugThrowLastTrace(EvalError({ - .msg = hintfmt("'url' argument required"), - .errPos = state.positions[pos] - })); + state.debugThrowLastTrace( + EvalError({.msg = hintfmt("'url' argument required"), + .errPos = state.positions[pos]})); } else url = state.forceStringNoCtx(*args[0], pos); @@ -228,14 +236,17 @@ static void fetch(EvalState & state, const PosIdx pos, Value * * args, Value & v name = baseNameOf(*url); if (evalSettings.pureEval && !expectedHash) - state.debugThrowLastTrace(EvalError("in pure evaluation mode, '%s' requires a 'sha256' argument", who)); + state.debugThrowLastTrace(EvalError( + "in pure evaluation mode, '%s' requires a 'sha256' argument", who)); // early exit if pinned and already in the store if (expectedHash && expectedHash->type == htSHA256) { auto expectedPath = unpack - ? state.store->makeFixedOutputPath(FileIngestionMethod::Recursive, *expectedHash, name, {}) - : state.store->makeFixedOutputPath(FileIngestionMethod::Flat, *expectedHash, name, {}); + ? state.store->makeFixedOutputPath( + FileIngestionMethod::Recursive, *expectedHash, name, {}) + : state.store->makeFixedOutputPath(FileIngestionMethod::Flat, + *expectedHash, name, {}); if (state.store->isValidPath(expectedPath)) { state.allowAndSetStorePathString(expectedPath, v); @@ -245,24 +256,31 @@ static void fetch(EvalState & state, const PosIdx pos, Value * * args, Value & v // TODO: fetching may fail, yet the path may be substitutable. // https://github.com/NixOS/nix/issues/4313 - auto storePath = - unpack - ? fetchers::downloadTarball(state.store, *url, name, (bool) expectedHash).first.storePath - : fetchers::downloadFile(state.store, *url, name, (bool) expectedHash).storePath; + auto storePath = unpack ? fetchers::downloadTarball(state.store, *url, name, + (bool) expectedHash) + .first.storePath + : fetchers::downloadFile(state.store, *url, name, + (bool) expectedHash) + .storePath; if (expectedHash) { - auto hash = unpack - ? state.store->queryPathInfo(storePath)->narHash - : hashFile(htSHA256, state.store->toRealPath(storePath)); + auto hash = + unpack ? state.store->queryPathInfo(storePath)->narHash + : hashFile(htSHA256, state.store->toRealPath(storePath)); if (hash != *expectedHash) - state.debugThrowLastTrace(EvalError((unsigned int) 102, "hash mismatch in file downloaded from '%s':\n specified: %s\n got: %s", - *url, expectedHash->to_string(Base32, true), hash.to_string(Base32, true))); + state.debugThrowLastTrace( + EvalError((unsigned int) 102, + "hash mismatch in file downloaded from '%s':\n " + "specified: %s\n got: %s", + *url, expectedHash->to_string(Base32, true), + hash.to_string(Base32, true))); } state.allowAndSetStorePathString(storePath, v); } -static void prim_fetchurl(EvalState & state, const PosIdx pos, Value * * args, Value & v) +static void prim_fetchurl(EvalState & state, const PosIdx pos, Value ** args, + Value & v) { fetch(state, pos, args, v, "fetchurl", false, ""); } @@ -278,7 +296,8 @@ static RegisterPrimOp primop_fetchurl({ .fun = prim_fetchurl, }); -static void prim_fetchTarball(EvalState & state, const PosIdx pos, Value * * args, Value & v) +static void prim_fetchTarball(EvalState & state, const PosIdx pos, + Value ** args, Value & v) { fetch(state, pos, args, v, "fetchTarball", true, "source"); } @@ -329,9 +348,12 @@ static RegisterPrimOp primop_fetchTarball({ .fun = prim_fetchTarball, }); -static void prim_fetchGit(EvalState & state, const PosIdx pos, Value * * args, Value & v) +static void prim_fetchGit(EvalState & state, const PosIdx pos, Value ** args, + Value & v) { - fetchTree(state, pos, args, v, "git", FetchTreeParams { .emptyRevFallback = true, .allowNameArgument = true }); + fetchTree( + state, pos, args, v, "git", + FetchTreeParams{.emptyRevFallback = true, .allowNameArgument = true}); } static RegisterPrimOp primop_fetchGit({ diff --git a/src/libexpr/primops/fromTOML.cc b/src/libexpr/primops/fromTOML.cc index 9753e2ac9087..00170e91fa62 100644 --- a/src/libexpr/primops/fromTOML.cc +++ b/src/libexpr/primops/fromTOML.cc @@ -5,7 +5,8 @@ namespace nix { -static void prim_fromTOML(EvalState & state, const PosIdx pos, Value * * args, Value & val) +static void prim_fromTOML(EvalState & state, const PosIdx pos, Value ** args, + Value & val) { auto toml = state.forceStringNoCtx(*args[0], pos); @@ -14,67 +15,70 @@ static void prim_fromTOML(EvalState & state, const PosIdx pos, Value * * args, V std::function visit; visit = [&](Value & v, toml::value t) { - - switch(t.type()) - { - case toml::value_t::table: - { - auto table = toml::get(t); - - size_t size = 0; - for (auto & i : table) { (void) i; size++; } - - auto attrs = state.buildBindings(size); - - for(auto & elem : table) - visit(attrs.alloc(elem.first), elem.second); - - v.mkAttrs(attrs); - } - break;; - case toml::value_t::array: - { - auto array = toml::get>(t); - - size_t size = array.size(); - state.mkList(v, size); - for (size_t i = 0; i < size; ++i) - visit(*(v.listElems()[i] = state.allocValue()), array[i]); - } - break;; - case toml::value_t::boolean: - v.mkBool(toml::get(t)); - break;; - case toml::value_t::integer: - v.mkInt(toml::get(t)); - break;; - case toml::value_t::floating: - v.mkFloat(toml::get(t)); - break;; - case toml::value_t::string: - v.mkString(toml::get(t)); - break;; - case toml::value_t::local_datetime: - case toml::value_t::offset_datetime: - case toml::value_t::local_date: - case toml::value_t::local_time: - // We fail since Nix doesn't have date and time types - throw std::runtime_error("Dates and times are not supported"); - break;; - case toml::value_t::empty: - v.mkNull(); - break;; - + switch (t.type()) { + case toml::value_t::table: { + auto table = toml::get(t); + + size_t size = 0; + for (auto & i : table) { + (void) i; + size++; + } + + auto attrs = state.buildBindings(size); + + for (auto & elem : table) + visit(attrs.alloc(elem.first), elem.second); + + v.mkAttrs(attrs); + } break; + ; + case toml::value_t::array: { + auto array = toml::get>(t); + + size_t size = array.size(); + state.mkList(v, size); + for (size_t i = 0; i < size; ++i) + visit(*(v.listElems()[i] = state.allocValue()), array[i]); + } break; + ; + case toml::value_t::boolean: + v.mkBool(toml::get(t)); + break; + ; + case toml::value_t::integer: + v.mkInt(toml::get(t)); + break; + ; + case toml::value_t::floating: + v.mkFloat(toml::get(t)); + break; + ; + case toml::value_t::string: + v.mkString(toml::get(t)); + break; + ; + case toml::value_t::local_datetime: + case toml::value_t::offset_datetime: + case toml::value_t::local_date: + case toml::value_t::local_time: + // We fail since Nix doesn't have date and time types + throw std::runtime_error("Dates and times are not supported"); + break; + ; + case toml::value_t::empty: + v.mkNull(); + break; + ; } }; try { visit(val, toml::parse(tomlStream, "fromTOML" /* the "filename" */)); } catch (std::exception & e) { // TODO: toml::syntax_error - throw EvalError({ - .msg = hintfmt("while parsing a TOML string: %s", e.what()), - .errPos = state.positions[pos] - }); + throw EvalError( + {.msg = hintfmt("while parsing a TOML string: %s", e.what()), + .errPos = state.positions[pos]}); } } diff --git a/src/libexpr/symbol-table.hh b/src/libexpr/symbol-table.hh index 288c15602fe0..40c68eef53ee 100644 --- a/src/libexpr/symbol-table.hh +++ b/src/libexpr/symbol-table.hh @@ -17,45 +17,36 @@ namespace nix { /* This class mainly exists to give us an operator<< for ostreams. We could also return plain strings from SymbolTable, but then we'd have to wrap every - instance of a symbol that is fmt()ed, which is inconvenient and error-prone. */ -class SymbolStr -{ + instance of a symbol that is fmt()ed, which is inconvenient and error-prone. + */ +class SymbolStr { friend class SymbolTable; -private: + private: const std::string * s; - explicit SymbolStr(const std::string & symbol): s(&symbol) {} + explicit SymbolStr(const std::string & symbol) : s(&symbol) {} -public: - bool operator == (std::string_view s2) const - { - return *s == s2; - } + public: + bool operator==(std::string_view s2) const { return *s == s2; } - operator const std::string & () const - { - return *s; - } + operator const std::string &() const { return *s; } - operator const std::string_view () const - { - return *s; - } + operator const std::string_view() const { return *s; } - friend std::ostream & operator <<(std::ostream & os, const SymbolStr & symbol); + friend std::ostream & operator<<(std::ostream & os, + const SymbolStr & symbol); }; -class Symbol -{ +class Symbol { friend class SymbolTable; -private: + private: uint32_t id; - explicit Symbol(uint32_t id): id(id) {} + explicit Symbol(uint32_t id) : id(id) {} -public: + public: Symbol() : id(0) {} explicit operator bool() const { return id > 0; } @@ -65,23 +56,24 @@ public: bool operator!=(const Symbol other) const { return id != other.id; } }; -class SymbolTable -{ -private: - std::unordered_map> symbols; +class SymbolTable { + private: + std::unordered_map> + symbols; ChunkedVector store{16}; -public: - + public: Symbol create(std::string_view s) { - // Most symbols are looked up more than once, so we trade off insertion performance - // for lookup performance. - // TODO: could probably be done more efficiently with transparent Hash and Equals - // on the original implementation using unordered_set + // Most symbols are looked up more than once, so we trade off insertion + // performance for lookup performance. + // TODO: could probably be done more efficiently with transparent Hash + // and Equals on the original implementation using unordered_set // FIXME: make this thread-safe. auto it = symbols.find(s); - if (it != symbols.end()) return Symbol(it->second.second + 1); + if (it != symbols.end()) + return Symbol(it->second.second + 1); const auto & [rawSym, idx] = store.add(std::string(s)); symbols.emplace(rawSym, std::make_pair(&rawSym, idx)); @@ -104,15 +96,11 @@ public: return SymbolStr(store[s.id - 1]); } - size_t size() const - { - return store.size(); - } + size_t size() const { return store.size(); } size_t totalSize() const; - template - void dump(T callback) const + template void dump(T callback) const { store.forEach(callback); } diff --git a/src/libexpr/tests/json.cc b/src/libexpr/tests/json.cc index f1ea1b19785f..ffb67d587cdc 100644 --- a/src/libexpr/tests/json.cc +++ b/src/libexpr/tests/json.cc @@ -4,65 +4,76 @@ namespace nix { // Testing the conversion to JSON - class JSONValueTest : public LibExprTest { - protected: - std::string getJSONValue(Value& value) { - std::stringstream ss; - PathSet ps; - printValueAsJSON(state, true, value, noPos, ss, ps); - return ss.str(); - } - }; - - TEST_F(JSONValueTest, null) { - Value v; - v.mkNull(); - ASSERT_EQ(getJSONValue(v), "null"); +class JSONValueTest : public LibExprTest { + protected: + std::string getJSONValue(Value & value) + { + std::stringstream ss; + PathSet ps; + printValueAsJSON(state, true, value, noPos, ss, ps); + return ss.str(); } +}; - TEST_F(JSONValueTest, BoolFalse) { - Value v; - v.mkBool(false); - ASSERT_EQ(getJSONValue(v),"false"); - } +TEST_F(JSONValueTest, null) +{ + Value v; + v.mkNull(); + ASSERT_EQ(getJSONValue(v), "null"); +} - TEST_F(JSONValueTest, BoolTrue) { - Value v; - v.mkBool(true); - ASSERT_EQ(getJSONValue(v), "true"); - } +TEST_F(JSONValueTest, BoolFalse) +{ + Value v; + v.mkBool(false); + ASSERT_EQ(getJSONValue(v), "false"); +} - TEST_F(JSONValueTest, IntPositive) { - Value v; - v.mkInt(100); - ASSERT_EQ(getJSONValue(v), "100"); - } +TEST_F(JSONValueTest, BoolTrue) +{ + Value v; + v.mkBool(true); + ASSERT_EQ(getJSONValue(v), "true"); +} - TEST_F(JSONValueTest, IntNegative) { - Value v; - v.mkInt(-100); - ASSERT_EQ(getJSONValue(v), "-100"); - } +TEST_F(JSONValueTest, IntPositive) +{ + Value v; + v.mkInt(100); + ASSERT_EQ(getJSONValue(v), "100"); +} - TEST_F(JSONValueTest, String) { - Value v; - v.mkString("test"); - ASSERT_EQ(getJSONValue(v), "\"test\""); - } +TEST_F(JSONValueTest, IntNegative) +{ + Value v; + v.mkInt(-100); + ASSERT_EQ(getJSONValue(v), "-100"); +} - TEST_F(JSONValueTest, StringQuotes) { - Value v; +TEST_F(JSONValueTest, String) +{ + Value v; + v.mkString("test"); + ASSERT_EQ(getJSONValue(v), "\"test\""); +} - v.mkString("test\""); - ASSERT_EQ(getJSONValue(v), "\"test\\\"\""); - } +TEST_F(JSONValueTest, StringQuotes) +{ + Value v; - // The dummy store doesn't support writing files. Fails with this exception message: - // C++ exception with description "error: operation 'addToStoreFromDump' is - // not supported by store 'dummy'" thrown in the test body. - TEST_F(JSONValueTest, DISABLED_Path) { - Value v; - v.mkPath("test"); - ASSERT_EQ(getJSONValue(v), "\"/nix/store/g1w7hy3qg1w7hy3qg1w7hy3qg1w7hy3q-x\""); - } + v.mkString("test\""); + ASSERT_EQ(getJSONValue(v), "\"test\\\"\""); +} + +// The dummy store doesn't support writing files. Fails with this exception +// message: C++ exception with description "error: operation +// 'addToStoreFromDump' is not supported by store 'dummy'" thrown in the test +// body. +TEST_F(JSONValueTest, DISABLED_Path) +{ + Value v; + v.mkPath("test"); + ASSERT_EQ(getJSONValue(v), + "\"/nix/store/g1w7hy3qg1w7hy3qg1w7hy3qg1w7hy3q-x\""); +} } /* namespace nix */ diff --git a/src/libexpr/tests/libexprtests.hh b/src/libexpr/tests/libexprtests.hh index 4f6915882b93..b166161f9b65 100644 --- a/src/libexpr/tests/libexprtests.hh +++ b/src/libexpr/tests/libexprtests.hh @@ -7,130 +7,122 @@ #include "eval-inline.hh" #include "store-api.hh" - namespace nix { - class LibExprTest : public ::testing::Test { - public: - static void SetUpTestSuite() { - initGC(); - } - - protected: - LibExprTest() - : store(openStore("dummy://")) - , state({}, store) - { - } - Value eval(std::string input, bool forceValue = true) { - Value v; - Expr * e = state.parseExprFromString(input, ""); - assert(e); - state.eval(e, v); - if (forceValue) - state.forceValue(v, noPos); - return v; - } - - Symbol createSymbol(const char * value) { - return state.symbols.create(value); - } - - ref store; - EvalState state; - }; - - MATCHER(IsListType, "") { - return arg != nList; +class LibExprTest : public ::testing::Test { + public: + static void SetUpTestSuite() { initGC(); } + + protected: + LibExprTest() : store(openStore("dummy://")), state({}, store) {} + Value eval(std::string input, bool forceValue = true) + { + Value v; + Expr * e = state.parseExprFromString(input, ""); + assert(e); + state.eval(e, v); + if (forceValue) + state.forceValue(v, noPos); + return v; } - MATCHER(IsList, "") { - return arg.type() == nList; + Symbol createSymbol(const char * value) + { + return state.symbols.create(value); } - MATCHER(IsString, "") { - return arg.type() == nString; - } + ref store; + EvalState state; +}; - MATCHER(IsNull, "") { - return arg.type() == nNull; - } +MATCHER(IsListType, "") { return arg != nList; } - MATCHER(IsThunk, "") { - return arg.type() == nThunk; - } +MATCHER(IsList, "") { return arg.type() == nList; } - MATCHER(IsAttrs, "") { - return arg.type() == nAttrs; - } +MATCHER(IsString, "") { return arg.type() == nString; } - MATCHER_P(IsStringEq, s, fmt("The string is equal to \"%1%\"", s)) { - if (arg.type() != nString) { - return false; - } - return std::string_view(arg.string.s) == s; - } +MATCHER(IsNull, "") { return arg.type() == nNull; } - MATCHER_P(IsIntEq, v, fmt("The string is equal to \"%1%\"", v)) { - if (arg.type() != nInt) { - return false; - } - return arg.integer == v; - } +MATCHER(IsThunk, "") { return arg.type() == nThunk; } - MATCHER_P(IsFloatEq, v, fmt("The float is equal to \"%1%\"", v)) { - if (arg.type() != nFloat) { - return false; - } - return arg.fpoint == v; - } +MATCHER(IsAttrs, "") { return arg.type() == nAttrs; } - MATCHER(IsTrue, "") { - if (arg.type() != nBool) { - return false; - } - return arg.boolean == true; +MATCHER_P(IsStringEq, s, fmt("The string is equal to \"%1%\"", s)) +{ + if (arg.type() != nString) { + return false; } + return std::string_view(arg.string.s) == s; +} - MATCHER(IsFalse, "") { - if (arg.type() != nBool) { - return false; - } - return arg.boolean == false; +MATCHER_P(IsIntEq, v, fmt("The string is equal to \"%1%\"", v)) +{ + if (arg.type() != nInt) { + return false; } + return arg.integer == v; +} - MATCHER_P(IsPathEq, p, fmt("Is a path equal to \"%1%\"", p)) { - if (arg.type() != nPath) { - *result_listener << "Expected a path got " << arg.type(); - return false; - } else if (std::string_view(arg.string.s) != p) { - *result_listener << "Expected a path that equals \"" << p << "\" but got: " << arg.string.s; - return false; - } - return true; +MATCHER_P(IsFloatEq, v, fmt("The float is equal to \"%1%\"", v)) +{ + if (arg.type() != nFloat) { + return false; } + return arg.fpoint == v; +} - - MATCHER_P(IsListOfSize, n, fmt("Is a list of size [%1%]", n)) { - if (arg.type() != nList) { - *result_listener << "Expected list got " << arg.type(); - return false; - } else if (arg.listSize() != (size_t)n) { - *result_listener << "Expected as list of size " << n << " got " << arg.listSize(); - return false; - } - return true; +MATCHER(IsTrue, "") +{ + if (arg.type() != nBool) { + return false; } + return arg.boolean == true; +} - MATCHER_P(IsAttrsOfSize, n, fmt("Is a set of size [%1%]", n)) { - if (arg.type() != nAttrs) { - *result_listener << "Expexted set got " << arg.type(); - return false; - } else if (arg.attrs->size() != (size_t)n) { - *result_listener << "Expected a set with " << n << " attributes but got " << arg.attrs->size(); - return false; - } - return true; +MATCHER(IsFalse, "") +{ + if (arg.type() != nBool) { + return false; } - + return arg.boolean == false; +} + +MATCHER_P(IsPathEq, p, fmt("Is a path equal to \"%1%\"", p)) +{ + if (arg.type() != nPath) { + *result_listener << "Expected a path got " << arg.type(); + return false; + } else if (std::string_view(arg.string.s) != p) { + *result_listener << "Expected a path that equals \"" << p + << "\" but got: " << arg.string.s; + return false; + } + return true; +} + +MATCHER_P(IsListOfSize, n, fmt("Is a list of size [%1%]", n)) +{ + if (arg.type() != nList) { + *result_listener << "Expected list got " << arg.type(); + return false; + } else if (arg.listSize() != (size_t) n) { + *result_listener << "Expected as list of size " << n << " got " + << arg.listSize(); + return false; + } + return true; +} + +MATCHER_P(IsAttrsOfSize, n, fmt("Is a set of size [%1%]", n)) +{ + if (arg.type() != nAttrs) { + *result_listener << "Expexted set got " << arg.type(); + return false; + } else if (arg.attrs->size() != (size_t) n) { + *result_listener << "Expected a set with " << n + << " attributes but got " << arg.attrs->size(); + return false; + } + return true; +} } /* namespace nix */ diff --git a/src/libexpr/tests/primops.cc b/src/libexpr/tests/primops.cc index f65b6593d07b..ca7445129670 100644 --- a/src/libexpr/tests/primops.cc +++ b/src/libexpr/tests/primops.cc @@ -4,836 +4,933 @@ #include "libexprtests.hh" namespace nix { - class CaptureLogger : public Logger - { - std::ostringstream oss; - - public: - CaptureLogger() {} - - std::string get() const { - return oss.str(); - } - - void log(Verbosity lvl, const FormatOrString & fs) override { - oss << fs.s << std::endl; - } - - void logEI(const ErrorInfo & ei) override { - showErrorInfo(oss, ei, loggerSettings.showTrace.get()); - } - }; - - class CaptureLogging { - Logger * oldLogger; - std::unique_ptr tempLogger; - public: - CaptureLogging() : tempLogger(std::make_unique()) { - oldLogger = logger; - logger = tempLogger.get(); - } - - ~CaptureLogging() { - logger = oldLogger; - } - - std::string get() const { - return tempLogger->get(); - } - }; - - - // Testing eval of PrimOp's - class PrimOpTest : public LibExprTest {}; - - - TEST_F(PrimOpTest, throw) { - ASSERT_THROW(eval("throw \"foo\""), ThrownError); - } - - TEST_F(PrimOpTest, abort) { - ASSERT_THROW(eval("abort \"abort\""), Abort); - } - - TEST_F(PrimOpTest, ceil) { - auto v = eval("builtins.ceil 1.9"); - ASSERT_THAT(v, IsIntEq(2)); - } - - TEST_F(PrimOpTest, floor) { - auto v = eval("builtins.floor 1.9"); - ASSERT_THAT(v, IsIntEq(1)); - } - - TEST_F(PrimOpTest, tryEvalFailure) { - auto v = eval("builtins.tryEval (throw \"\")"); - ASSERT_THAT(v, IsAttrsOfSize(2)); - auto s = createSymbol("success"); - auto p = v.attrs->get(s); - ASSERT_NE(p, nullptr); - ASSERT_THAT(*p->value, IsFalse()); - } - - TEST_F(PrimOpTest, tryEvalSuccess) { - auto v = eval("builtins.tryEval 123"); - ASSERT_THAT(v, IsAttrs()); - auto s = createSymbol("success"); - auto p = v.attrs->get(s); - ASSERT_NE(p, nullptr); - ASSERT_THAT(*p->value, IsTrue()); - s = createSymbol("value"); - p = v.attrs->get(s); - ASSERT_NE(p, nullptr); - ASSERT_THAT(*p->value, IsIntEq(123)); - } - - TEST_F(PrimOpTest, getEnv) { - setenv("_NIX_UNIT_TEST_ENV_VALUE", "test value", 1); - auto v = eval("builtins.getEnv \"_NIX_UNIT_TEST_ENV_VALUE\""); - ASSERT_THAT(v, IsStringEq("test value")); - } - - TEST_F(PrimOpTest, seq) { - ASSERT_THROW(eval("let x = throw \"test\"; in builtins.seq x { }"), ThrownError); - } - - TEST_F(PrimOpTest, seqNotDeep) { - auto v = eval("let x = { z = throw \"test\"; }; in builtins.seq x { }"); - ASSERT_THAT(v, IsAttrs()); - } - - TEST_F(PrimOpTest, deepSeq) { - ASSERT_THROW(eval("let x = { z = throw \"test\"; }; in builtins.deepSeq x { }"), ThrownError); - } - - TEST_F(PrimOpTest, trace) { - CaptureLogging l; - auto v = eval("builtins.trace \"test string 123\" 123"); - ASSERT_THAT(v, IsIntEq(123)); - auto text = l.get(); - ASSERT_NE(text.find("test string 123"), std::string::npos); - } - - TEST_F(PrimOpTest, placeholder) { - auto v = eval("builtins.placeholder \"out\""); - ASSERT_THAT(v, IsStringEq("/1rz4g4znpzjwh1xymhjpm42vipw92pr73vdgl6xs1hycac8kf2n9")); - } - - TEST_F(PrimOpTest, baseNameOf) { - auto v = eval("builtins.baseNameOf /some/path"); - ASSERT_THAT(v, IsStringEq("path")); - } - - TEST_F(PrimOpTest, dirOf) { - auto v = eval("builtins.dirOf /some/path"); - ASSERT_THAT(v, IsPathEq("/some")); - } - - TEST_F(PrimOpTest, attrValues) { - auto v = eval("builtins.attrValues { x = \"foo\"; a = 1; }"); - ASSERT_THAT(v, IsListOfSize(2)); - ASSERT_THAT(*v.listElems()[0], IsIntEq(1)); - ASSERT_THAT(*v.listElems()[1], IsStringEq("foo")); - } - - TEST_F(PrimOpTest, getAttr) { - auto v = eval("builtins.getAttr \"x\" { x = \"foo\"; }"); - ASSERT_THAT(v, IsStringEq("foo")); - } - - TEST_F(PrimOpTest, getAttrNotFound) { - // FIXME: TypeError is really bad here, also the error wording is worse - // than on Nix <=2.3 - ASSERT_THROW(eval("builtins.getAttr \"y\" { }"), TypeError); - } - - TEST_F(PrimOpTest, unsafeGetAttrPos) { - // The `y` attribute is at position - const char* expr = "builtins.unsafeGetAttrPos \"y\" { y = \"x\"; }"; - auto v = eval(expr); - ASSERT_THAT(v, IsAttrsOfSize(3)); - - auto file = v.attrs->find(createSymbol("file")); - ASSERT_NE(file, nullptr); - // FIXME: The file when running these tests is the input string?!? - ASSERT_THAT(*file->value, IsStringEq(expr)); - - auto line = v.attrs->find(createSymbol("line")); - ASSERT_NE(line, nullptr); - ASSERT_THAT(*line->value, IsIntEq(1)); - - auto column = v.attrs->find(createSymbol("column")); - ASSERT_NE(column, nullptr); - ASSERT_THAT(*column->value, IsIntEq(33)); - } - - TEST_F(PrimOpTest, hasAttr) { - auto v = eval("builtins.hasAttr \"x\" { x = 1; }"); - ASSERT_THAT(v, IsTrue()); - } - - TEST_F(PrimOpTest, hasAttrNotFound) { - auto v = eval("builtins.hasAttr \"x\" { }"); - ASSERT_THAT(v, IsFalse()); - } - - TEST_F(PrimOpTest, isAttrs) { - auto v = eval("builtins.isAttrs {}"); - ASSERT_THAT(v, IsTrue()); - } - - TEST_F(PrimOpTest, isAttrsFalse) { - auto v = eval("builtins.isAttrs null"); - ASSERT_THAT(v, IsFalse()); - } - - TEST_F(PrimOpTest, removeAttrs) { - auto v = eval("builtins.removeAttrs { x = 1; } [\"x\"]"); - ASSERT_THAT(v, IsAttrsOfSize(0)); - } - - TEST_F(PrimOpTest, removeAttrsRetains) { - auto v = eval("builtins.removeAttrs { x = 1; y = 2; } [\"x\"]"); - ASSERT_THAT(v, IsAttrsOfSize(1)); - ASSERT_NE(v.attrs->find(createSymbol("y")), nullptr); - } - - TEST_F(PrimOpTest, listToAttrsEmptyList) { - auto v = eval("builtins.listToAttrs []"); - ASSERT_THAT(v, IsAttrsOfSize(0)); - ASSERT_EQ(v.type(), nAttrs); - ASSERT_EQ(v.attrs->size(), 0); - } +class CaptureLogger : public Logger { + std::ostringstream oss; - TEST_F(PrimOpTest, listToAttrsNotFieldName) { - ASSERT_THROW(eval("builtins.listToAttrs [{}]"), Error); - } - - TEST_F(PrimOpTest, listToAttrs) { - auto v = eval("builtins.listToAttrs [ { name = \"key\"; value = 123; } ]"); - ASSERT_THAT(v, IsAttrsOfSize(1)); - auto key = v.attrs->find(createSymbol("key")); - ASSERT_NE(key, nullptr); - ASSERT_THAT(*key->value, IsIntEq(123)); - } - - TEST_F(PrimOpTest, intersectAttrs) { - auto v = eval("builtins.intersectAttrs { a = 1; b = 2; } { b = 3; c = 4; }"); - ASSERT_THAT(v, IsAttrsOfSize(1)); - auto b = v.attrs->find(createSymbol("b")); - ASSERT_NE(b, nullptr); - ASSERT_THAT(*b->value, IsIntEq(3)); - } - - TEST_F(PrimOpTest, catAttrs) { - auto v = eval("builtins.catAttrs \"a\" [{a = 1;} {b = 0;} {a = 2;}]"); - ASSERT_THAT(v, IsListOfSize(2)); - ASSERT_THAT(*v.listElems()[0], IsIntEq(1)); - ASSERT_THAT(*v.listElems()[1], IsIntEq(2)); - } - - TEST_F(PrimOpTest, functionArgs) { - auto v = eval("builtins.functionArgs ({ x, y ? 123}: 1)"); - ASSERT_THAT(v, IsAttrsOfSize(2)); - - auto x = v.attrs->find(createSymbol("x")); - ASSERT_NE(x, nullptr); - ASSERT_THAT(*x->value, IsFalse()); - - auto y = v.attrs->find(createSymbol("y")); - ASSERT_NE(y, nullptr); - ASSERT_THAT(*y->value, IsTrue()); - } - - TEST_F(PrimOpTest, mapAttrs) { - auto v = eval("builtins.mapAttrs (name: value: value * 10) { a = 1; b = 2; }"); - ASSERT_THAT(v, IsAttrsOfSize(2)); - - auto a = v.attrs->find(createSymbol("a")); - ASSERT_NE(a, nullptr); - ASSERT_THAT(*a->value, IsThunk()); - state.forceValue(*a->value, noPos); - ASSERT_THAT(*a->value, IsIntEq(10)); - - auto b = v.attrs->find(createSymbol("b")); - ASSERT_NE(b, nullptr); - ASSERT_THAT(*b->value, IsThunk()); - state.forceValue(*b->value, noPos); - ASSERT_THAT(*b->value, IsIntEq(20)); - } - - TEST_F(PrimOpTest, isList) { - auto v = eval("builtins.isList []"); - ASSERT_THAT(v, IsTrue()); - } - - TEST_F(PrimOpTest, isListFalse) { - auto v = eval("builtins.isList null"); - ASSERT_THAT(v, IsFalse()); - } - - TEST_F(PrimOpTest, elemtAt) { - auto v = eval("builtins.elemAt [0 1 2 3] 3"); - ASSERT_THAT(v, IsIntEq(3)); - } - - TEST_F(PrimOpTest, elemtAtOutOfBounds) { - ASSERT_THROW(eval("builtins.elemAt [0 1 2 3] 5"), Error); - } - - TEST_F(PrimOpTest, head) { - auto v = eval("builtins.head [ 3 2 1 0 ]"); - ASSERT_THAT(v, IsIntEq(3)); - } - - TEST_F(PrimOpTest, headEmpty) { - ASSERT_THROW(eval("builtins.head [ ]"), Error); - } + public: + CaptureLogger() {} - TEST_F(PrimOpTest, headWrongType) { - ASSERT_THROW(eval("builtins.head { }"), Error); - } + std::string get() const { return oss.str(); } - TEST_F(PrimOpTest, tail) { - auto v = eval("builtins.tail [ 3 2 1 0 ]"); - ASSERT_THAT(v, IsListOfSize(3)); - for (const auto [n, elem] : enumerate(v.listItems())) - ASSERT_THAT(*elem, IsIntEq(2 - static_cast(n))); + void log(Verbosity lvl, const FormatOrString & fs) override + { + oss << fs.s << std::endl; } - TEST_F(PrimOpTest, tailEmpty) { - ASSERT_THROW(eval("builtins.tail []"), Error); + void logEI(const ErrorInfo & ei) override + { + showErrorInfo(oss, ei, loggerSettings.showTrace.get()); } +}; - TEST_F(PrimOpTest, map) { - auto v = eval("map (x: \"foo\" + x) [ \"bar\" \"bla\" \"abc\" ]"); - ASSERT_THAT(v, IsListOfSize(3)); - auto elem = v.listElems()[0]; - ASSERT_THAT(*elem, IsThunk()); - state.forceValue(*elem, noPos); - ASSERT_THAT(*elem, IsStringEq("foobar")); - - elem = v.listElems()[1]; - ASSERT_THAT(*elem, IsThunk()); - state.forceValue(*elem, noPos); - ASSERT_THAT(*elem, IsStringEq("foobla")); +class CaptureLogging { + Logger * oldLogger; + std::unique_ptr tempLogger; - elem = v.listElems()[2]; + public: + CaptureLogging() : tempLogger(std::make_unique()) + { + oldLogger = logger; + logger = tempLogger.get(); + } + + ~CaptureLogging() { logger = oldLogger; } + + std::string get() const { return tempLogger->get(); } +}; + +// Testing eval of PrimOp's +class PrimOpTest : public LibExprTest { +}; + +TEST_F(PrimOpTest, throw) { ASSERT_THROW(eval("throw \"foo\""), ThrownError); } + +TEST_F(PrimOpTest, abort) { ASSERT_THROW(eval("abort \"abort\""), Abort); } + +TEST_F(PrimOpTest, ceil) +{ + auto v = eval("builtins.ceil 1.9"); + ASSERT_THAT(v, IsIntEq(2)); +} + +TEST_F(PrimOpTest, floor) +{ + auto v = eval("builtins.floor 1.9"); + ASSERT_THAT(v, IsIntEq(1)); +} + +TEST_F(PrimOpTest, tryEvalFailure) +{ + auto v = eval("builtins.tryEval (throw \"\")"); + ASSERT_THAT(v, IsAttrsOfSize(2)); + auto s = createSymbol("success"); + auto p = v.attrs->get(s); + ASSERT_NE(p, nullptr); + ASSERT_THAT(*p->value, IsFalse()); +} + +TEST_F(PrimOpTest, tryEvalSuccess) +{ + auto v = eval("builtins.tryEval 123"); + ASSERT_THAT(v, IsAttrs()); + auto s = createSymbol("success"); + auto p = v.attrs->get(s); + ASSERT_NE(p, nullptr); + ASSERT_THAT(*p->value, IsTrue()); + s = createSymbol("value"); + p = v.attrs->get(s); + ASSERT_NE(p, nullptr); + ASSERT_THAT(*p->value, IsIntEq(123)); +} + +TEST_F(PrimOpTest, getEnv) +{ + setenv("_NIX_UNIT_TEST_ENV_VALUE", "test value", 1); + auto v = eval("builtins.getEnv \"_NIX_UNIT_TEST_ENV_VALUE\""); + ASSERT_THAT(v, IsStringEq("test value")); +} + +TEST_F(PrimOpTest, seq) +{ + ASSERT_THROW(eval("let x = throw \"test\"; in builtins.seq x { }"), + ThrownError); +} + +TEST_F(PrimOpTest, seqNotDeep) +{ + auto v = eval("let x = { z = throw \"test\"; }; in builtins.seq x { }"); + ASSERT_THAT(v, IsAttrs()); +} + +TEST_F(PrimOpTest, deepSeq) +{ + ASSERT_THROW( + eval("let x = { z = throw \"test\"; }; in builtins.deepSeq x { }"), + ThrownError); +} + +TEST_F(PrimOpTest, trace) +{ + CaptureLogging l; + auto v = eval("builtins.trace \"test string 123\" 123"); + ASSERT_THAT(v, IsIntEq(123)); + auto text = l.get(); + ASSERT_NE(text.find("test string 123"), std::string::npos); +} + +TEST_F(PrimOpTest, placeholder) +{ + auto v = eval("builtins.placeholder \"out\""); + ASSERT_THAT( + v, IsStringEq("/1rz4g4znpzjwh1xymhjpm42vipw92pr73vdgl6xs1hycac8kf2n9")); +} + +TEST_F(PrimOpTest, baseNameOf) +{ + auto v = eval("builtins.baseNameOf /some/path"); + ASSERT_THAT(v, IsStringEq("path")); +} + +TEST_F(PrimOpTest, dirOf) +{ + auto v = eval("builtins.dirOf /some/path"); + ASSERT_THAT(v, IsPathEq("/some")); +} + +TEST_F(PrimOpTest, attrValues) +{ + auto v = eval("builtins.attrValues { x = \"foo\"; a = 1; }"); + ASSERT_THAT(v, IsListOfSize(2)); + ASSERT_THAT(*v.listElems()[0], IsIntEq(1)); + ASSERT_THAT(*v.listElems()[1], IsStringEq("foo")); +} + +TEST_F(PrimOpTest, getAttr) +{ + auto v = eval("builtins.getAttr \"x\" { x = \"foo\"; }"); + ASSERT_THAT(v, IsStringEq("foo")); +} + +TEST_F(PrimOpTest, getAttrNotFound) +{ + // FIXME: TypeError is really bad here, also the error wording is worse + // than on Nix <=2.3 + ASSERT_THROW(eval("builtins.getAttr \"y\" { }"), TypeError); +} + +TEST_F(PrimOpTest, unsafeGetAttrPos) +{ + // The `y` attribute is at position + const char * expr = "builtins.unsafeGetAttrPos \"y\" { y = \"x\"; }"; + auto v = eval(expr); + ASSERT_THAT(v, IsAttrsOfSize(3)); + + auto file = v.attrs->find(createSymbol("file")); + ASSERT_NE(file, nullptr); + // FIXME: The file when running these tests is the input string?!? + ASSERT_THAT(*file->value, IsStringEq(expr)); + + auto line = v.attrs->find(createSymbol("line")); + ASSERT_NE(line, nullptr); + ASSERT_THAT(*line->value, IsIntEq(1)); + + auto column = v.attrs->find(createSymbol("column")); + ASSERT_NE(column, nullptr); + ASSERT_THAT(*column->value, IsIntEq(33)); +} + +TEST_F(PrimOpTest, hasAttr) +{ + auto v = eval("builtins.hasAttr \"x\" { x = 1; }"); + ASSERT_THAT(v, IsTrue()); +} + +TEST_F(PrimOpTest, hasAttrNotFound) +{ + auto v = eval("builtins.hasAttr \"x\" { }"); + ASSERT_THAT(v, IsFalse()); +} + +TEST_F(PrimOpTest, isAttrs) +{ + auto v = eval("builtins.isAttrs {}"); + ASSERT_THAT(v, IsTrue()); +} + +TEST_F(PrimOpTest, isAttrsFalse) +{ + auto v = eval("builtins.isAttrs null"); + ASSERT_THAT(v, IsFalse()); +} + +TEST_F(PrimOpTest, removeAttrs) +{ + auto v = eval("builtins.removeAttrs { x = 1; } [\"x\"]"); + ASSERT_THAT(v, IsAttrsOfSize(0)); +} + +TEST_F(PrimOpTest, removeAttrsRetains) +{ + auto v = eval("builtins.removeAttrs { x = 1; y = 2; } [\"x\"]"); + ASSERT_THAT(v, IsAttrsOfSize(1)); + ASSERT_NE(v.attrs->find(createSymbol("y")), nullptr); +} + +TEST_F(PrimOpTest, listToAttrsEmptyList) +{ + auto v = eval("builtins.listToAttrs []"); + ASSERT_THAT(v, IsAttrsOfSize(0)); + ASSERT_EQ(v.type(), nAttrs); + ASSERT_EQ(v.attrs->size(), 0); +} + +TEST_F(PrimOpTest, listToAttrsNotFieldName) +{ + ASSERT_THROW(eval("builtins.listToAttrs [{}]"), Error); +} + +TEST_F(PrimOpTest, listToAttrs) +{ + auto v = eval("builtins.listToAttrs [ { name = \"key\"; value = 123; } ]"); + ASSERT_THAT(v, IsAttrsOfSize(1)); + auto key = v.attrs->find(createSymbol("key")); + ASSERT_NE(key, nullptr); + ASSERT_THAT(*key->value, IsIntEq(123)); +} + +TEST_F(PrimOpTest, intersectAttrs) +{ + auto v = + eval("builtins.intersectAttrs { a = 1; b = 2; } { b = 3; c = 4; }"); + ASSERT_THAT(v, IsAttrsOfSize(1)); + auto b = v.attrs->find(createSymbol("b")); + ASSERT_NE(b, nullptr); + ASSERT_THAT(*b->value, IsIntEq(3)); +} + +TEST_F(PrimOpTest, catAttrs) +{ + auto v = eval("builtins.catAttrs \"a\" [{a = 1;} {b = 0;} {a = 2;}]"); + ASSERT_THAT(v, IsListOfSize(2)); + ASSERT_THAT(*v.listElems()[0], IsIntEq(1)); + ASSERT_THAT(*v.listElems()[1], IsIntEq(2)); +} + +TEST_F(PrimOpTest, functionArgs) +{ + auto v = eval("builtins.functionArgs ({ x, y ? 123}: 1)"); + ASSERT_THAT(v, IsAttrsOfSize(2)); + + auto x = v.attrs->find(createSymbol("x")); + ASSERT_NE(x, nullptr); + ASSERT_THAT(*x->value, IsFalse()); + + auto y = v.attrs->find(createSymbol("y")); + ASSERT_NE(y, nullptr); + ASSERT_THAT(*y->value, IsTrue()); +} + +TEST_F(PrimOpTest, mapAttrs) +{ + auto v = + eval("builtins.mapAttrs (name: value: value * 10) { a = 1; b = 2; }"); + ASSERT_THAT(v, IsAttrsOfSize(2)); + + auto a = v.attrs->find(createSymbol("a")); + ASSERT_NE(a, nullptr); + ASSERT_THAT(*a->value, IsThunk()); + state.forceValue(*a->value, noPos); + ASSERT_THAT(*a->value, IsIntEq(10)); + + auto b = v.attrs->find(createSymbol("b")); + ASSERT_NE(b, nullptr); + ASSERT_THAT(*b->value, IsThunk()); + state.forceValue(*b->value, noPos); + ASSERT_THAT(*b->value, IsIntEq(20)); +} + +TEST_F(PrimOpTest, isList) +{ + auto v = eval("builtins.isList []"); + ASSERT_THAT(v, IsTrue()); +} + +TEST_F(PrimOpTest, isListFalse) +{ + auto v = eval("builtins.isList null"); + ASSERT_THAT(v, IsFalse()); +} + +TEST_F(PrimOpTest, elemtAt) +{ + auto v = eval("builtins.elemAt [0 1 2 3] 3"); + ASSERT_THAT(v, IsIntEq(3)); +} + +TEST_F(PrimOpTest, elemtAtOutOfBounds) +{ + ASSERT_THROW(eval("builtins.elemAt [0 1 2 3] 5"), Error); +} + +TEST_F(PrimOpTest, head) +{ + auto v = eval("builtins.head [ 3 2 1 0 ]"); + ASSERT_THAT(v, IsIntEq(3)); +} + +TEST_F(PrimOpTest, headEmpty) +{ + ASSERT_THROW(eval("builtins.head [ ]"), Error); +} + +TEST_F(PrimOpTest, headWrongType) +{ + ASSERT_THROW(eval("builtins.head { }"), Error); +} + +TEST_F(PrimOpTest, tail) +{ + auto v = eval("builtins.tail [ 3 2 1 0 ]"); + ASSERT_THAT(v, IsListOfSize(3)); + for (const auto [n, elem] : enumerate(v.listItems())) + ASSERT_THAT(*elem, IsIntEq(2 - static_cast(n))); +} + +TEST_F(PrimOpTest, tailEmpty) { ASSERT_THROW(eval("builtins.tail []"), Error); } + +TEST_F(PrimOpTest, map) +{ + auto v = eval("map (x: \"foo\" + x) [ \"bar\" \"bla\" \"abc\" ]"); + ASSERT_THAT(v, IsListOfSize(3)); + auto elem = v.listElems()[0]; + ASSERT_THAT(*elem, IsThunk()); + state.forceValue(*elem, noPos); + ASSERT_THAT(*elem, IsStringEq("foobar")); + + elem = v.listElems()[1]; + ASSERT_THAT(*elem, IsThunk()); + state.forceValue(*elem, noPos); + ASSERT_THAT(*elem, IsStringEq("foobla")); + + elem = v.listElems()[2]; + ASSERT_THAT(*elem, IsThunk()); + state.forceValue(*elem, noPos); + ASSERT_THAT(*elem, IsStringEq("fooabc")); +} + +TEST_F(PrimOpTest, filter) +{ + auto v = eval("builtins.filter (x: x == 2) [ 3 2 3 2 3 2 ]"); + ASSERT_THAT(v, IsListOfSize(3)); + for (const auto elem : v.listItems()) + ASSERT_THAT(*elem, IsIntEq(2)); +} + +TEST_F(PrimOpTest, elemTrue) +{ + auto v = eval("builtins.elem 3 [ 1 2 3 4 5 ]"); + ASSERT_THAT(v, IsTrue()); +} + +TEST_F(PrimOpTest, elemFalse) +{ + auto v = eval("builtins.elem 6 [ 1 2 3 4 5 ]"); + ASSERT_THAT(v, IsFalse()); +} + +TEST_F(PrimOpTest, concatLists) +{ + auto v = eval("builtins.concatLists [[1 2] [3 4]]"); + ASSERT_THAT(v, IsListOfSize(4)); + for (const auto [i, elem] : enumerate(v.listItems())) + ASSERT_THAT(*elem, IsIntEq(static_cast(i) + 1)); +} + +TEST_F(PrimOpTest, length) +{ + auto v = eval("builtins.length [ 1 2 3 ]"); + ASSERT_THAT(v, IsIntEq(3)); +} + +TEST_F(PrimOpTest, foldStrict) +{ + auto v = eval("builtins.foldl' (a: b: a + b) 0 [1 2 3]"); + ASSERT_THAT(v, IsIntEq(6)); +} + +TEST_F(PrimOpTest, anyTrue) +{ + auto v = eval("builtins.any (x: x == 2) [ 1 2 3 ]"); + ASSERT_THAT(v, IsTrue()); +} + +TEST_F(PrimOpTest, anyFalse) +{ + auto v = eval("builtins.any (x: x == 5) [ 1 2 3 ]"); + ASSERT_THAT(v, IsFalse()); +} + +TEST_F(PrimOpTest, allTrue) +{ + auto v = eval("builtins.all (x: x > 0) [ 1 2 3 ]"); + ASSERT_THAT(v, IsTrue()); +} + +TEST_F(PrimOpTest, allFalse) +{ + auto v = eval("builtins.all (x: x <= 0) [ 1 2 3 ]"); + ASSERT_THAT(v, IsFalse()); +} + +TEST_F(PrimOpTest, genList) +{ + auto v = eval("builtins.genList (x: x + 1) 3"); + ASSERT_EQ(v.type(), nList); + ASSERT_EQ(v.listSize(), 3); + for (const auto [i, elem] : enumerate(v.listItems())) { ASSERT_THAT(*elem, IsThunk()); state.forceValue(*elem, noPos); - ASSERT_THAT(*elem, IsStringEq("fooabc")); - } - - TEST_F(PrimOpTest, filter) { - auto v = eval("builtins.filter (x: x == 2) [ 3 2 3 2 3 2 ]"); - ASSERT_THAT(v, IsListOfSize(3)); - for (const auto elem : v.listItems()) - ASSERT_THAT(*elem, IsIntEq(2)); - } - - TEST_F(PrimOpTest, elemTrue) { - auto v = eval("builtins.elem 3 [ 1 2 3 4 5 ]"); - ASSERT_THAT(v, IsTrue()); - } - - TEST_F(PrimOpTest, elemFalse) { - auto v = eval("builtins.elem 6 [ 1 2 3 4 5 ]"); - ASSERT_THAT(v, IsFalse()); - } - - TEST_F(PrimOpTest, concatLists) { - auto v = eval("builtins.concatLists [[1 2] [3 4]]"); - ASSERT_THAT(v, IsListOfSize(4)); - for (const auto [i, elem] : enumerate(v.listItems())) - ASSERT_THAT(*elem, IsIntEq(static_cast(i)+1)); - } - - TEST_F(PrimOpTest, length) { - auto v = eval("builtins.length [ 1 2 3 ]"); - ASSERT_THAT(v, IsIntEq(3)); - } - - TEST_F(PrimOpTest, foldStrict) { - auto v = eval("builtins.foldl' (a: b: a + b) 0 [1 2 3]"); - ASSERT_THAT(v, IsIntEq(6)); - } - - TEST_F(PrimOpTest, anyTrue) { - auto v = eval("builtins.any (x: x == 2) [ 1 2 3 ]"); - ASSERT_THAT(v, IsTrue()); - } - - TEST_F(PrimOpTest, anyFalse) { - auto v = eval("builtins.any (x: x == 5) [ 1 2 3 ]"); - ASSERT_THAT(v, IsFalse()); - } - - TEST_F(PrimOpTest, allTrue) { - auto v = eval("builtins.all (x: x > 0) [ 1 2 3 ]"); - ASSERT_THAT(v, IsTrue()); - } - - TEST_F(PrimOpTest, allFalse) { - auto v = eval("builtins.all (x: x <= 0) [ 1 2 3 ]"); - ASSERT_THAT(v, IsFalse()); - } - - TEST_F(PrimOpTest, genList) { - auto v = eval("builtins.genList (x: x + 1) 3"); - ASSERT_EQ(v.type(), nList); - ASSERT_EQ(v.listSize(), 3); - for (const auto [i, elem] : enumerate(v.listItems())) { - ASSERT_THAT(*elem, IsThunk()); - state.forceValue(*elem, noPos); - ASSERT_THAT(*elem, IsIntEq(static_cast(i)+1)); - } - } - - TEST_F(PrimOpTest, sortLessThan) { - auto v = eval("builtins.sort builtins.lessThan [ 483 249 526 147 42 77 ]"); - ASSERT_EQ(v.type(), nList); - ASSERT_EQ(v.listSize(), 6); - - const std::vector numbers = { 42, 77, 147, 249, 483, 526 }; - for (const auto [n, elem] : enumerate(v.listItems())) - ASSERT_THAT(*elem, IsIntEq(numbers[n])); - } - - TEST_F(PrimOpTest, partition) { - auto v = eval("builtins.partition (x: x > 10) [1 23 9 3 42]"); - ASSERT_THAT(v, IsAttrsOfSize(2)); - - auto right = v.attrs->get(createSymbol("right")); - ASSERT_NE(right, nullptr); - ASSERT_THAT(*right->value, IsListOfSize(2)); - ASSERT_THAT(*right->value->listElems()[0], IsIntEq(23)); - ASSERT_THAT(*right->value->listElems()[1], IsIntEq(42)); - - auto wrong = v.attrs->get(createSymbol("wrong")); - ASSERT_NE(wrong, nullptr); - ASSERT_EQ(wrong->value->type(), nList); - ASSERT_EQ(wrong->value->listSize(), 3); - ASSERT_THAT(*wrong->value, IsListOfSize(3)); - ASSERT_THAT(*wrong->value->listElems()[0], IsIntEq(1)); - ASSERT_THAT(*wrong->value->listElems()[1], IsIntEq(9)); - ASSERT_THAT(*wrong->value->listElems()[2], IsIntEq(3)); - } - - TEST_F(PrimOpTest, concatMap) { - auto v = eval("builtins.concatMap (x: x ++ [0]) [ [1 2] [3 4] ]"); - ASSERT_EQ(v.type(), nList); - ASSERT_EQ(v.listSize(), 6); - - const std::vector numbers = { 1, 2, 0, 3, 4, 0 }; - for (const auto [n, elem] : enumerate(v.listItems())) - ASSERT_THAT(*elem, IsIntEq(numbers[n])); - } - - TEST_F(PrimOpTest, addInt) { - auto v = eval("builtins.add 3 5"); - ASSERT_THAT(v, IsIntEq(8)); - } - - TEST_F(PrimOpTest, addFloat) { - auto v = eval("builtins.add 3.0 5.0"); - ASSERT_THAT(v, IsFloatEq(8.0)); - } - - TEST_F(PrimOpTest, addFloatToInt) { - auto v = eval("builtins.add 3.0 5"); - ASSERT_THAT(v, IsFloatEq(8.0)); - - v = eval("builtins.add 3 5.0"); - ASSERT_THAT(v, IsFloatEq(8.0)); - } - - TEST_F(PrimOpTest, subInt) { - auto v = eval("builtins.sub 5 2"); - ASSERT_THAT(v, IsIntEq(3)); - } - - TEST_F(PrimOpTest, subFloat) { - auto v = eval("builtins.sub 5.0 2.0"); - ASSERT_THAT(v, IsFloatEq(3.0)); - } - - TEST_F(PrimOpTest, subFloatFromInt) { - auto v = eval("builtins.sub 5.0 2"); - ASSERT_THAT(v, IsFloatEq(3.0)); - - v = eval("builtins.sub 4 2.0"); - ASSERT_THAT(v, IsFloatEq(2.0)); - } - - TEST_F(PrimOpTest, mulInt) { - auto v = eval("builtins.mul 3 5"); - ASSERT_THAT(v, IsIntEq(15)); - } - - TEST_F(PrimOpTest, mulFloat) { - auto v = eval("builtins.mul 3.0 5.0"); - ASSERT_THAT(v, IsFloatEq(15.0)); - } - - TEST_F(PrimOpTest, mulFloatMixed) { - auto v = eval("builtins.mul 3 5.0"); - ASSERT_THAT(v, IsFloatEq(15.0)); - - v = eval("builtins.mul 2.0 5"); - ASSERT_THAT(v, IsFloatEq(10.0)); - } - - TEST_F(PrimOpTest, divInt) { - auto v = eval("builtins.div 5 (-1)"); - ASSERT_THAT(v, IsIntEq(-5)); - } - - TEST_F(PrimOpTest, divIntZero) { - ASSERT_THROW(eval("builtins.div 5 0"), EvalError); - } - - TEST_F(PrimOpTest, divFloat) { - auto v = eval("builtins.div 5.0 (-1)"); - ASSERT_THAT(v, IsFloatEq(-5.0)); - } - - TEST_F(PrimOpTest, divFloatZero) { - ASSERT_THROW(eval("builtins.div 5.0 0.0"), EvalError); - } - - TEST_F(PrimOpTest, bitOr) { - auto v = eval("builtins.bitOr 1 2"); - ASSERT_THAT(v, IsIntEq(3)); - } - - TEST_F(PrimOpTest, bitXor) { - auto v = eval("builtins.bitXor 3 2"); - ASSERT_THAT(v, IsIntEq(1)); - } - - TEST_F(PrimOpTest, lessThanFalse) { - auto v = eval("builtins.lessThan 3 1"); - ASSERT_THAT(v, IsFalse()); - } - - TEST_F(PrimOpTest, lessThanTrue) { - auto v = eval("builtins.lessThan 1 3"); - ASSERT_THAT(v, IsTrue()); - } - - TEST_F(PrimOpTest, toStringAttrsThrows) { - ASSERT_THROW(eval("builtins.toString {}"), EvalError); - } - - TEST_F(PrimOpTest, toStringLambdaThrows) { - ASSERT_THROW(eval("builtins.toString (x: x)"), EvalError); - } - - class ToStringPrimOpTest : - public PrimOpTest, - public testing::WithParamInterface> - {}; - - TEST_P(ToStringPrimOpTest, toString) { - const auto [input, output] = GetParam(); - auto v = eval(input); - ASSERT_THAT(v, IsStringEq(output)); - } - -#define CASE(input, output) (std::make_tuple(std::string_view("builtins.toString " #input), std::string_view(output))) - INSTANTIATE_TEST_SUITE_P( - toString, - ToStringPrimOpTest, - testing::Values( - CASE("foo", "foo"), - CASE(1, "1"), - CASE([1 2 3], "1 2 3"), - CASE(.123, "0.123000"), - CASE(true, "1"), - CASE(false, ""), - CASE(null, ""), - CASE({ v = "bar"; __toString = self: self.v; }, "bar"), - CASE({ v = "bar"; __toString = self: self.v; outPath = "foo"; }, "bar"), - CASE({ outPath = "foo"; }, "foo"), - CASE(./test, "/test") - ) - ); + ASSERT_THAT(*elem, IsIntEq(static_cast(i) + 1)); + } +} + +TEST_F(PrimOpTest, sortLessThan) +{ + auto v = eval("builtins.sort builtins.lessThan [ 483 249 526 147 42 77 ]"); + ASSERT_EQ(v.type(), nList); + ASSERT_EQ(v.listSize(), 6); + + const std::vector numbers = {42, 77, 147, 249, 483, 526}; + for (const auto [n, elem] : enumerate(v.listItems())) + ASSERT_THAT(*elem, IsIntEq(numbers[n])); +} + +TEST_F(PrimOpTest, partition) +{ + auto v = eval("builtins.partition (x: x > 10) [1 23 9 3 42]"); + ASSERT_THAT(v, IsAttrsOfSize(2)); + + auto right = v.attrs->get(createSymbol("right")); + ASSERT_NE(right, nullptr); + ASSERT_THAT(*right->value, IsListOfSize(2)); + ASSERT_THAT(*right->value->listElems()[0], IsIntEq(23)); + ASSERT_THAT(*right->value->listElems()[1], IsIntEq(42)); + + auto wrong = v.attrs->get(createSymbol("wrong")); + ASSERT_NE(wrong, nullptr); + ASSERT_EQ(wrong->value->type(), nList); + ASSERT_EQ(wrong->value->listSize(), 3); + ASSERT_THAT(*wrong->value, IsListOfSize(3)); + ASSERT_THAT(*wrong->value->listElems()[0], IsIntEq(1)); + ASSERT_THAT(*wrong->value->listElems()[1], IsIntEq(9)); + ASSERT_THAT(*wrong->value->listElems()[2], IsIntEq(3)); +} + +TEST_F(PrimOpTest, concatMap) +{ + auto v = eval("builtins.concatMap (x: x ++ [0]) [ [1 2] [3 4] ]"); + ASSERT_EQ(v.type(), nList); + ASSERT_EQ(v.listSize(), 6); + + const std::vector numbers = {1, 2, 0, 3, 4, 0}; + for (const auto [n, elem] : enumerate(v.listItems())) + ASSERT_THAT(*elem, IsIntEq(numbers[n])); +} + +TEST_F(PrimOpTest, addInt) +{ + auto v = eval("builtins.add 3 5"); + ASSERT_THAT(v, IsIntEq(8)); +} + +TEST_F(PrimOpTest, addFloat) +{ + auto v = eval("builtins.add 3.0 5.0"); + ASSERT_THAT(v, IsFloatEq(8.0)); +} + +TEST_F(PrimOpTest, addFloatToInt) +{ + auto v = eval("builtins.add 3.0 5"); + ASSERT_THAT(v, IsFloatEq(8.0)); + + v = eval("builtins.add 3 5.0"); + ASSERT_THAT(v, IsFloatEq(8.0)); +} + +TEST_F(PrimOpTest, subInt) +{ + auto v = eval("builtins.sub 5 2"); + ASSERT_THAT(v, IsIntEq(3)); +} + +TEST_F(PrimOpTest, subFloat) +{ + auto v = eval("builtins.sub 5.0 2.0"); + ASSERT_THAT(v, IsFloatEq(3.0)); +} + +TEST_F(PrimOpTest, subFloatFromInt) +{ + auto v = eval("builtins.sub 5.0 2"); + ASSERT_THAT(v, IsFloatEq(3.0)); + + v = eval("builtins.sub 4 2.0"); + ASSERT_THAT(v, IsFloatEq(2.0)); +} + +TEST_F(PrimOpTest, mulInt) +{ + auto v = eval("builtins.mul 3 5"); + ASSERT_THAT(v, IsIntEq(15)); +} + +TEST_F(PrimOpTest, mulFloat) +{ + auto v = eval("builtins.mul 3.0 5.0"); + ASSERT_THAT(v, IsFloatEq(15.0)); +} + +TEST_F(PrimOpTest, mulFloatMixed) +{ + auto v = eval("builtins.mul 3 5.0"); + ASSERT_THAT(v, IsFloatEq(15.0)); + + v = eval("builtins.mul 2.0 5"); + ASSERT_THAT(v, IsFloatEq(10.0)); +} + +TEST_F(PrimOpTest, divInt) +{ + auto v = eval("builtins.div 5 (-1)"); + ASSERT_THAT(v, IsIntEq(-5)); +} + +TEST_F(PrimOpTest, divIntZero) +{ + ASSERT_THROW(eval("builtins.div 5 0"), EvalError); +} + +TEST_F(PrimOpTest, divFloat) +{ + auto v = eval("builtins.div 5.0 (-1)"); + ASSERT_THAT(v, IsFloatEq(-5.0)); +} + +TEST_F(PrimOpTest, divFloatZero) +{ + ASSERT_THROW(eval("builtins.div 5.0 0.0"), EvalError); +} + +TEST_F(PrimOpTest, bitOr) +{ + auto v = eval("builtins.bitOr 1 2"); + ASSERT_THAT(v, IsIntEq(3)); +} + +TEST_F(PrimOpTest, bitXor) +{ + auto v = eval("builtins.bitXor 3 2"); + ASSERT_THAT(v, IsIntEq(1)); +} + +TEST_F(PrimOpTest, lessThanFalse) +{ + auto v = eval("builtins.lessThan 3 1"); + ASSERT_THAT(v, IsFalse()); +} + +TEST_F(PrimOpTest, lessThanTrue) +{ + auto v = eval("builtins.lessThan 1 3"); + ASSERT_THAT(v, IsTrue()); +} + +TEST_F(PrimOpTest, toStringAttrsThrows) +{ + ASSERT_THROW(eval("builtins.toString {}"), EvalError); +} + +TEST_F(PrimOpTest, toStringLambdaThrows) +{ + ASSERT_THROW(eval("builtins.toString (x: x)"), EvalError); +} + +class ToStringPrimOpTest : public PrimOpTest, + public testing::WithParamInterface< + std::tuple> { +}; + +TEST_P(ToStringPrimOpTest, toString) +{ + const auto [input, output] = GetParam(); + auto v = eval(input); + ASSERT_THAT(v, IsStringEq(output)); +} + +#define CASE(input, output) \ + (std::make_tuple(std::string_view("builtins.toString " #input), \ + std::string_view(output))) +INSTANTIATE_TEST_SUITE_P( + toString, ToStringPrimOpTest, + testing::Values(CASE("foo", "foo"), CASE(1, "1"), CASE([1 2 3], "1 2 3"), + CASE(.123, "0.123000"), CASE(true, "1"), CASE(false, ""), + CASE(null, ""), + CASE( + { + v = "bar"; + __toString = self : self.v; + }, + "bar"), + CASE( + { + v = "bar"; + __toString = self : self.v; + outPath = "foo"; + }, + "bar"), + CASE({ outPath = "foo"; }, "foo"), CASE(./ test, "/test"))); #undef CASE - TEST_F(PrimOpTest, substring){ - auto v = eval("builtins.substring 0 3 \"nixos\""); - ASSERT_THAT(v, IsStringEq("nix")); - } - - TEST_F(PrimOpTest, substringSmallerString){ - auto v = eval("builtins.substring 0 3 \"n\""); - ASSERT_THAT(v, IsStringEq("n")); - } - - TEST_F(PrimOpTest, substringEmptyString){ - auto v = eval("builtins.substring 1 3 \"\""); - ASSERT_THAT(v, IsStringEq("")); - } - - TEST_F(PrimOpTest, stringLength) { - auto v = eval("builtins.stringLength \"123\""); - ASSERT_THAT(v, IsIntEq(3)); - } - TEST_F(PrimOpTest, hashStringMd5) { - auto v = eval("builtins.hashString \"md5\" \"asdf\""); - ASSERT_THAT(v, IsStringEq("912ec803b2ce49e4a541068d495ab570")); - } - - TEST_F(PrimOpTest, hashStringSha1) { - auto v = eval("builtins.hashString \"sha1\" \"asdf\""); - ASSERT_THAT(v, IsStringEq("3da541559918a808c2402bba5012f6c60b27661c")); - } - - TEST_F(PrimOpTest, hashStringSha256) { - auto v = eval("builtins.hashString \"sha256\" \"asdf\""); - ASSERT_THAT(v, IsStringEq("f0e4c2f76c58916ec258f246851bea091d14d4247a2fc3e18694461b1816e13b")); - } - - TEST_F(PrimOpTest, hashStringSha512) { - auto v = eval("builtins.hashString \"sha512\" \"asdf\""); - ASSERT_THAT(v, IsStringEq("401b09eab3c013d4ca54922bb802bec8fd5318192b0a75f201d8b3727429080fb337591abd3e44453b954555b7a0812e1081c39b740293f765eae731f5a65ed1")); - } - - TEST_F(PrimOpTest, hashStringInvalidHashType) { - ASSERT_THROW(eval("builtins.hashString \"foobar\" \"asdf\""), Error); - } - - TEST_F(PrimOpTest, nixPath) { - auto v = eval("builtins.nixPath"); - ASSERT_EQ(v.type(), nList); - // We can't test much more as currently the EvalSettings are a global - // that we can't easily swap / replace - } - - TEST_F(PrimOpTest, langVersion) { - auto v = eval("builtins.langVersion"); - ASSERT_EQ(v.type(), nInt); - } - - TEST_F(PrimOpTest, storeDir) { - auto v = eval("builtins.storeDir"); - ASSERT_THAT(v, IsStringEq("/nix/store")); - } - - TEST_F(PrimOpTest, nixVersion) { - auto v = eval("builtins.nixVersion"); - ASSERT_THAT(v, IsStringEq(nixVersion)); - } - - TEST_F(PrimOpTest, currentSystem) { - auto v = eval("builtins.currentSystem"); - ASSERT_THAT(v, IsStringEq(settings.thisSystem.get())); - } - - TEST_F(PrimOpTest, derivation) { - auto v = eval("derivation"); - ASSERT_EQ(v.type(), nFunction); - ASSERT_TRUE(v.isLambda()); - ASSERT_NE(v.lambda.fun, nullptr); - ASSERT_TRUE(v.lambda.fun->hasFormals()); - } - - TEST_F(PrimOpTest, currentTime) { - auto v = eval("builtins.currentTime"); - ASSERT_EQ(v.type(), nInt); - ASSERT_TRUE(v.integer > 0); - } - - TEST_F(PrimOpTest, splitVersion) { - auto v = eval("builtins.splitVersion \"1.2.3git\""); - ASSERT_THAT(v, IsListOfSize(4)); - - const std::vector strings = { "1", "2", "3", "git" }; - for (const auto [n, p] : enumerate(v.listItems())) - ASSERT_THAT(*p, IsStringEq(strings[n])); - } - - class CompareVersionsPrimOpTest : - public PrimOpTest, - public testing::WithParamInterface> - {}; - - TEST_P(CompareVersionsPrimOpTest, compareVersions) { - auto [expression, expectation] = GetParam(); - auto v = eval(expression); - ASSERT_THAT(v, IsIntEq(expectation)); - } - -#define CASE(a, b, expected) (std::make_tuple("builtins.compareVersions \"" #a "\" \"" #b "\"", expected)) - INSTANTIATE_TEST_SUITE_P( - compareVersions, - CompareVersionsPrimOpTest, - testing::Values( - // The first two are weird cases. Intuition tells they should - // be the same but they aren't. - CASE(1.0, 1.0.0, -1), - CASE(1.0.0, 1.0, 1), - // the following are from the nix-env manual: - CASE(1.0, 2.3, -1), - CASE(2.1, 2.3, -1), - CASE(2.3, 2.3, 0), - CASE(2.5, 2.3, 1), - CASE(3.1, 2.3, 1), - CASE(2.3.1, 2.3, 1), - CASE(2.3.1, 2.3a, 1), - CASE(2.3pre1, 2.3, -1), - CASE(2.3pre3, 2.3pre12, -1), - CASE(2.3a, 2.3c, -1), - CASE(2.3pre1, 2.3c, -1), - CASE(2.3pre1, 2.3q, -1) - ) - ); +TEST_F(PrimOpTest, substring) +{ + auto v = eval("builtins.substring 0 3 \"nixos\""); + ASSERT_THAT(v, IsStringEq("nix")); +} + +TEST_F(PrimOpTest, substringSmallerString) +{ + auto v = eval("builtins.substring 0 3 \"n\""); + ASSERT_THAT(v, IsStringEq("n")); +} + +TEST_F(PrimOpTest, substringEmptyString) +{ + auto v = eval("builtins.substring 1 3 \"\""); + ASSERT_THAT(v, IsStringEq("")); +} + +TEST_F(PrimOpTest, stringLength) +{ + auto v = eval("builtins.stringLength \"123\""); + ASSERT_THAT(v, IsIntEq(3)); +} +TEST_F(PrimOpTest, hashStringMd5) +{ + auto v = eval("builtins.hashString \"md5\" \"asdf\""); + ASSERT_THAT(v, IsStringEq("912ec803b2ce49e4a541068d495ab570")); +} + +TEST_F(PrimOpTest, hashStringSha1) +{ + auto v = eval("builtins.hashString \"sha1\" \"asdf\""); + ASSERT_THAT(v, IsStringEq("3da541559918a808c2402bba5012f6c60b27661c")); +} + +TEST_F(PrimOpTest, hashStringSha256) +{ + auto v = eval("builtins.hashString \"sha256\" \"asdf\""); + ASSERT_THAT(v, IsStringEq("f0e4c2f76c58916ec258f246851bea091d14d4247a2fc3e1" + "8694461b1816e13b")); +} + +TEST_F(PrimOpTest, hashStringSha512) +{ + auto v = eval("builtins.hashString \"sha512\" \"asdf\""); + ASSERT_THAT( + v, + IsStringEq( + "401b09eab3c013d4ca54922bb802bec8fd5318192b0a75f201d8b3727429080fb3" + "37591abd3e44453b954555b7a0812e1081c39b740293f765eae731f5a65ed1")); +} + +TEST_F(PrimOpTest, hashStringInvalidHashType) +{ + ASSERT_THROW(eval("builtins.hashString \"foobar\" \"asdf\""), Error); +} + +TEST_F(PrimOpTest, nixPath) +{ + auto v = eval("builtins.nixPath"); + ASSERT_EQ(v.type(), nList); + // We can't test much more as currently the EvalSettings are a global + // that we can't easily swap / replace +} + +TEST_F(PrimOpTest, langVersion) +{ + auto v = eval("builtins.langVersion"); + ASSERT_EQ(v.type(), nInt); +} + +TEST_F(PrimOpTest, storeDir) +{ + auto v = eval("builtins.storeDir"); + ASSERT_THAT(v, IsStringEq("/nix/store")); +} + +TEST_F(PrimOpTest, nixVersion) +{ + auto v = eval("builtins.nixVersion"); + ASSERT_THAT(v, IsStringEq(nixVersion)); +} + +TEST_F(PrimOpTest, currentSystem) +{ + auto v = eval("builtins.currentSystem"); + ASSERT_THAT(v, IsStringEq(settings.thisSystem.get())); +} + +TEST_F(PrimOpTest, derivation) +{ + auto v = eval("derivation"); + ASSERT_EQ(v.type(), nFunction); + ASSERT_TRUE(v.isLambda()); + ASSERT_NE(v.lambda.fun, nullptr); + ASSERT_TRUE(v.lambda.fun->hasFormals()); +} + +TEST_F(PrimOpTest, currentTime) +{ + auto v = eval("builtins.currentTime"); + ASSERT_EQ(v.type(), nInt); + ASSERT_TRUE(v.integer > 0); +} + +TEST_F(PrimOpTest, splitVersion) +{ + auto v = eval("builtins.splitVersion \"1.2.3git\""); + ASSERT_THAT(v, IsListOfSize(4)); + + const std::vector strings = {"1", "2", "3", "git"}; + for (const auto [n, p] : enumerate(v.listItems())) + ASSERT_THAT(*p, IsStringEq(strings[n])); +} + +class CompareVersionsPrimOpTest + : public PrimOpTest, + public testing::WithParamInterface> { +}; + +TEST_P(CompareVersionsPrimOpTest, compareVersions) +{ + auto [expression, expectation] = GetParam(); + auto v = eval(expression); + ASSERT_THAT(v, IsIntEq(expectation)); +} + +#define CASE(a, b, expected) \ + (std::make_tuple("builtins.compareVersions \"" #a "\" \"" #b "\"", \ + expected)) +INSTANTIATE_TEST_SUITE_P(compareVersions, CompareVersionsPrimOpTest, + testing::Values( + // The first two are weird cases. Intuition tells + // they should be the same but they aren't. + CASE(1.0, 1.0.0, -1), CASE(1.0.0, 1.0, 1), + // the following are from the nix-env manual: + CASE(1.0, 2.3, -1), CASE(2.1, 2.3, -1), + CASE(2.3, 2.3, 0), CASE(2.5, 2.3, 1), + CASE(3.1, 2.3, 1), CASE(2.3.1, 2.3, 1), + CASE(2.3.1, 2.3a, 1), CASE(2.3pre1, 2.3, -1), + CASE(2.3pre3, 2.3pre12, -1), CASE(2.3a, 2.3c, -1), + CASE(2.3pre1, 2.3c, -1), CASE(2.3pre1, 2.3q, -1))); #undef CASE - - class ParseDrvNamePrimOpTest : - public PrimOpTest, - public testing::WithParamInterface> - {}; - - TEST_P(ParseDrvNamePrimOpTest, parseDrvName) { - auto [input, expectedName, expectedVersion] = GetParam(); - const auto expr = fmt("builtins.parseDrvName \"%1%\"", input); - auto v = eval(expr); - ASSERT_THAT(v, IsAttrsOfSize(2)); - - auto name = v.attrs->find(createSymbol("name")); - ASSERT_TRUE(name); - ASSERT_THAT(*name->value, IsStringEq(expectedName)); - - auto version = v.attrs->find(createSymbol("version")); - ASSERT_TRUE(version); - ASSERT_THAT(*version->value, IsStringEq(expectedVersion)); - } - - INSTANTIATE_TEST_SUITE_P( - parseDrvName, - ParseDrvNamePrimOpTest, - testing::Values( - std::make_tuple("nix-0.12pre12876", "nix", "0.12pre12876"), - std::make_tuple("a-b-c-1234pre5+git", "a-b-c", "1234pre5+git") - ) - ); - - TEST_F(PrimOpTest, replaceStrings) { - // FIXME: add a test that verifies the string context is as expected - auto v = eval("builtins.replaceStrings [\"oo\" \"a\"] [\"a\" \"i\"] \"foobar\""); - ASSERT_EQ(v.type(), nString); - ASSERT_EQ(v.string.s, std::string_view("fabir")); - } - - TEST_F(PrimOpTest, concatStringsSep) { - // FIXME: add a test that verifies the string context is as expected - auto v = eval("builtins.concatStringsSep \"%\" [\"foo\" \"bar\" \"baz\"]"); - ASSERT_EQ(v.type(), nString); - ASSERT_EQ(std::string_view(v.string.s), "foo%bar%baz"); - } - - TEST_F(PrimOpTest, split1) { - // v = [ "" [ "a" ] "c" ] - auto v = eval("builtins.split \"(a)b\" \"abc\""); - ASSERT_THAT(v, IsListOfSize(3)); - - ASSERT_THAT(*v.listElems()[0], IsStringEq("")); - - ASSERT_THAT(*v.listElems()[1], IsListOfSize(1)); - ASSERT_THAT(*v.listElems()[1]->listElems()[0], IsStringEq("a")); - - ASSERT_THAT(*v.listElems()[2], IsStringEq("c")); - } - - TEST_F(PrimOpTest, split2) { - // v is expected to be a list [ "" [ "a" ] "b" [ "c"] "" ] - auto v = eval("builtins.split \"([ac])\" \"abc\""); - ASSERT_THAT(v, IsListOfSize(5)); - - ASSERT_THAT(*v.listElems()[0], IsStringEq("")); - - ASSERT_THAT(*v.listElems()[1], IsListOfSize(1)); - ASSERT_THAT(*v.listElems()[1]->listElems()[0], IsStringEq("a")); - - ASSERT_THAT(*v.listElems()[2], IsStringEq("b")); - - ASSERT_THAT(*v.listElems()[3], IsListOfSize(1)); - ASSERT_THAT(*v.listElems()[3]->listElems()[0], IsStringEq("c")); - - ASSERT_THAT(*v.listElems()[4], IsStringEq("")); - } - - TEST_F(PrimOpTest, split3) { - auto v = eval("builtins.split \"(a)|(c)\" \"abc\""); - ASSERT_THAT(v, IsListOfSize(5)); - - // First list element - ASSERT_THAT(*v.listElems()[0], IsStringEq("")); - - // 2nd list element is a list [ "" null ] - ASSERT_THAT(*v.listElems()[1], IsListOfSize(2)); - ASSERT_THAT(*v.listElems()[1]->listElems()[0], IsStringEq("a")); - ASSERT_THAT(*v.listElems()[1]->listElems()[1], IsNull()); - - // 3rd element - ASSERT_THAT(*v.listElems()[2], IsStringEq("b")); - - // 4th element is a list: [ null "c" ] - ASSERT_THAT(*v.listElems()[3], IsListOfSize(2)); - ASSERT_THAT(*v.listElems()[3]->listElems()[0], IsNull()); - ASSERT_THAT(*v.listElems()[3]->listElems()[1], IsStringEq("c")); - - // 5th element is the empty string - ASSERT_THAT(*v.listElems()[4], IsStringEq("")); - } - - TEST_F(PrimOpTest, split4) { - auto v = eval("builtins.split \"([[:upper:]]+)\" \" FOO \""); - ASSERT_THAT(v, IsListOfSize(3)); - auto first = v.listElems()[0]; - auto second = v.listElems()[1]; - auto third = v.listElems()[2]; - - ASSERT_THAT(*first, IsStringEq(" ")); - - ASSERT_THAT(*second, IsListOfSize(1)); - ASSERT_THAT(*second->listElems()[0], IsStringEq("FOO")); - - ASSERT_THAT(*third, IsStringEq(" ")); - } - - TEST_F(PrimOpTest, match1) { - auto v = eval("builtins.match \"ab\" \"abc\""); - ASSERT_THAT(v, IsNull()); - } - - TEST_F(PrimOpTest, match2) { - auto v = eval("builtins.match \"abc\" \"abc\""); - ASSERT_THAT(v, IsListOfSize(0)); - } - - TEST_F(PrimOpTest, match3) { - auto v = eval("builtins.match \"a(b)(c)\" \"abc\""); - ASSERT_THAT(v, IsListOfSize(2)); - ASSERT_THAT(*v.listElems()[0], IsStringEq("b")); - ASSERT_THAT(*v.listElems()[1], IsStringEq("c")); - } - - TEST_F(PrimOpTest, match4) { - auto v = eval("builtins.match \"[[:space:]]+([[:upper:]]+)[[:space:]]+\" \" FOO \""); - ASSERT_THAT(v, IsListOfSize(1)); - ASSERT_THAT(*v.listElems()[0], IsStringEq("FOO")); - } - - TEST_F(PrimOpTest, attrNames) { - auto v = eval("builtins.attrNames { x = 1; y = 2; z = 3; a = 2; }"); - ASSERT_THAT(v, IsListOfSize(4)); - - // ensure that the list is sorted - const std::vector expected { "a", "x", "y", "z" }; - for (const auto [n, elem] : enumerate(v.listItems())) - ASSERT_THAT(*elem, IsStringEq(expected[n])); - } +class ParseDrvNamePrimOpTest + : public PrimOpTest, + public testing::WithParamInterface< + std::tuple> { +}; + +TEST_P(ParseDrvNamePrimOpTest, parseDrvName) +{ + auto [input, expectedName, expectedVersion] = GetParam(); + const auto expr = fmt("builtins.parseDrvName \"%1%\"", input); + auto v = eval(expr); + ASSERT_THAT(v, IsAttrsOfSize(2)); + + auto name = v.attrs->find(createSymbol("name")); + ASSERT_TRUE(name); + ASSERT_THAT(*name->value, IsStringEq(expectedName)); + + auto version = v.attrs->find(createSymbol("version")); + ASSERT_TRUE(version); + ASSERT_THAT(*version->value, IsStringEq(expectedVersion)); +} + +INSTANTIATE_TEST_SUITE_P( + parseDrvName, ParseDrvNamePrimOpTest, + testing::Values(std::make_tuple("nix-0.12pre12876", "nix", "0.12pre12876"), + std::make_tuple("a-b-c-1234pre5+git", "a-b-c", + "1234pre5+git"))); + +TEST_F(PrimOpTest, replaceStrings) +{ + // FIXME: add a test that verifies the string context is as expected + auto v = + eval("builtins.replaceStrings [\"oo\" \"a\"] [\"a\" \"i\"] \"foobar\""); + ASSERT_EQ(v.type(), nString); + ASSERT_EQ(v.string.s, std::string_view("fabir")); +} + +TEST_F(PrimOpTest, concatStringsSep) +{ + // FIXME: add a test that verifies the string context is as expected + auto v = eval("builtins.concatStringsSep \"%\" [\"foo\" \"bar\" \"baz\"]"); + ASSERT_EQ(v.type(), nString); + ASSERT_EQ(std::string_view(v.string.s), "foo%bar%baz"); +} + +TEST_F(PrimOpTest, split1) +{ + // v = [ "" [ "a" ] "c" ] + auto v = eval("builtins.split \"(a)b\" \"abc\""); + ASSERT_THAT(v, IsListOfSize(3)); + + ASSERT_THAT(*v.listElems()[0], IsStringEq("")); + + ASSERT_THAT(*v.listElems()[1], IsListOfSize(1)); + ASSERT_THAT(*v.listElems()[1]->listElems()[0], IsStringEq("a")); + + ASSERT_THAT(*v.listElems()[2], IsStringEq("c")); +} + +TEST_F(PrimOpTest, split2) +{ + // v is expected to be a list [ "" [ "a" ] "b" [ "c"] "" ] + auto v = eval("builtins.split \"([ac])\" \"abc\""); + ASSERT_THAT(v, IsListOfSize(5)); + + ASSERT_THAT(*v.listElems()[0], IsStringEq("")); + + ASSERT_THAT(*v.listElems()[1], IsListOfSize(1)); + ASSERT_THAT(*v.listElems()[1]->listElems()[0], IsStringEq("a")); + + ASSERT_THAT(*v.listElems()[2], IsStringEq("b")); + + ASSERT_THAT(*v.listElems()[3], IsListOfSize(1)); + ASSERT_THAT(*v.listElems()[3]->listElems()[0], IsStringEq("c")); + + ASSERT_THAT(*v.listElems()[4], IsStringEq("")); +} + +TEST_F(PrimOpTest, split3) +{ + auto v = eval("builtins.split \"(a)|(c)\" \"abc\""); + ASSERT_THAT(v, IsListOfSize(5)); + + // First list element + ASSERT_THAT(*v.listElems()[0], IsStringEq("")); + + // 2nd list element is a list [ "" null ] + ASSERT_THAT(*v.listElems()[1], IsListOfSize(2)); + ASSERT_THAT(*v.listElems()[1]->listElems()[0], IsStringEq("a")); + ASSERT_THAT(*v.listElems()[1]->listElems()[1], IsNull()); + + // 3rd element + ASSERT_THAT(*v.listElems()[2], IsStringEq("b")); + + // 4th element is a list: [ null "c" ] + ASSERT_THAT(*v.listElems()[3], IsListOfSize(2)); + ASSERT_THAT(*v.listElems()[3]->listElems()[0], IsNull()); + ASSERT_THAT(*v.listElems()[3]->listElems()[1], IsStringEq("c")); + + // 5th element is the empty string + ASSERT_THAT(*v.listElems()[4], IsStringEq("")); +} + +TEST_F(PrimOpTest, split4) +{ + auto v = eval("builtins.split \"([[:upper:]]+)\" \" FOO \""); + ASSERT_THAT(v, IsListOfSize(3)); + auto first = v.listElems()[0]; + auto second = v.listElems()[1]; + auto third = v.listElems()[2]; + + ASSERT_THAT(*first, IsStringEq(" ")); + + ASSERT_THAT(*second, IsListOfSize(1)); + ASSERT_THAT(*second->listElems()[0], IsStringEq("FOO")); + + ASSERT_THAT(*third, IsStringEq(" ")); +} + +TEST_F(PrimOpTest, match1) +{ + auto v = eval("builtins.match \"ab\" \"abc\""); + ASSERT_THAT(v, IsNull()); +} + +TEST_F(PrimOpTest, match2) +{ + auto v = eval("builtins.match \"abc\" \"abc\""); + ASSERT_THAT(v, IsListOfSize(0)); +} + +TEST_F(PrimOpTest, match3) +{ + auto v = eval("builtins.match \"a(b)(c)\" \"abc\""); + ASSERT_THAT(v, IsListOfSize(2)); + ASSERT_THAT(*v.listElems()[0], IsStringEq("b")); + ASSERT_THAT(*v.listElems()[1], IsStringEq("c")); +} + +TEST_F(PrimOpTest, match4) +{ + auto v = eval("builtins.match \"[[:space:]]+([[:upper:]]+)[[:space:]]+\" " + "\" FOO \""); + ASSERT_THAT(v, IsListOfSize(1)); + ASSERT_THAT(*v.listElems()[0], IsStringEq("FOO")); +} + +TEST_F(PrimOpTest, attrNames) +{ + auto v = eval("builtins.attrNames { x = 1; y = 2; z = 3; a = 2; }"); + ASSERT_THAT(v, IsListOfSize(4)); + + // ensure that the list is sorted + const std::vector expected{"a", "x", "y", "z"}; + for (const auto [n, elem] : enumerate(v.listItems())) + ASSERT_THAT(*elem, IsStringEq(expected[n])); +} } /* namespace nix */ diff --git a/src/libexpr/tests/trivial.cc b/src/libexpr/tests/trivial.cc index 8ce276e52665..8efc6c45cbf9 100644 --- a/src/libexpr/tests/trivial.cc +++ b/src/libexpr/tests/trivial.cc @@ -1,196 +1,222 @@ #include "libexprtests.hh" namespace nix { - // Testing of trivial expressions - class TrivialExpressionTest : public LibExprTest {}; - - TEST_F(TrivialExpressionTest, true) { - auto v = eval("true"); - ASSERT_THAT(v, IsTrue()); - } - - TEST_F(TrivialExpressionTest, false) { - auto v = eval("false"); - ASSERT_THAT(v, IsFalse()); - } - - TEST_F(TrivialExpressionTest, null) { - auto v = eval("null"); - ASSERT_THAT(v, IsNull()); - } - - TEST_F(TrivialExpressionTest, 1) { - auto v = eval("1"); - ASSERT_THAT(v, IsIntEq(1)); - } - - TEST_F(TrivialExpressionTest, 1plus1) { - auto v = eval("1+1"); - ASSERT_THAT(v, IsIntEq(2)); - } - - TEST_F(TrivialExpressionTest, minus1) { - auto v = eval("-1"); - ASSERT_THAT(v, IsIntEq(-1)); - } - - TEST_F(TrivialExpressionTest, 1minus1) { - auto v = eval("1-1"); - ASSERT_THAT(v, IsIntEq(0)); - } - - TEST_F(TrivialExpressionTest, lambdaAdd) { - auto v = eval("let add = a: b: a + b; in add 1 2"); - ASSERT_THAT(v, IsIntEq(3)); - } - - TEST_F(TrivialExpressionTest, list) { - auto v = eval("[]"); - ASSERT_THAT(v, IsListOfSize(0)); - } - - TEST_F(TrivialExpressionTest, attrs) { - auto v = eval("{}"); - ASSERT_THAT(v, IsAttrsOfSize(0)); - } - - TEST_F(TrivialExpressionTest, float) { - auto v = eval("1.234"); - ASSERT_THAT(v, IsFloatEq(1.234)); - } - - TEST_F(TrivialExpressionTest, updateAttrs) { - auto v = eval("{ a = 1; } // { b = 2; a = 3; }"); - ASSERT_THAT(v, IsAttrsOfSize(2)); - auto a = v.attrs->find(createSymbol("a")); - ASSERT_NE(a, nullptr); - ASSERT_THAT(*a->value, IsIntEq(3)); - - auto b = v.attrs->find(createSymbol("b")); - ASSERT_NE(b, nullptr); - ASSERT_THAT(*b->value, IsIntEq(2)); - } - - TEST_F(TrivialExpressionTest, hasAttrOpFalse) { - auto v = eval("{} ? a"); - ASSERT_THAT(v, IsFalse()); - } - - TEST_F(TrivialExpressionTest, hasAttrOpTrue) { - auto v = eval("{ a = 123; } ? a"); - ASSERT_THAT(v, IsTrue()); - } - - TEST_F(TrivialExpressionTest, withFound) { - auto v = eval("with { a = 23; }; a"); - ASSERT_THAT(v, IsIntEq(23)); - } - - TEST_F(TrivialExpressionTest, withNotFound) { - ASSERT_THROW(eval("with {}; a"), Error); - } - - TEST_F(TrivialExpressionTest, withOverride) { - auto v = eval("with { a = 23; }; with { a = 42; }; a"); - ASSERT_THAT(v, IsIntEq(42)); - } - - TEST_F(TrivialExpressionTest, letOverWith) { - auto v = eval("let a = 23; in with { a = 1; }; a"); - ASSERT_THAT(v, IsIntEq(23)); - } - - TEST_F(TrivialExpressionTest, multipleLet) { - auto v = eval("let a = 23; in let a = 42; in a"); - ASSERT_THAT(v, IsIntEq(42)); - } - - TEST_F(TrivialExpressionTest, defaultFunctionArgs) { - auto v = eval("({ a ? 123 }: a) {}"); - ASSERT_THAT(v, IsIntEq(123)); - } - - TEST_F(TrivialExpressionTest, defaultFunctionArgsOverride) { - auto v = eval("({ a ? 123 }: a) { a = 5; }"); - ASSERT_THAT(v, IsIntEq(5)); - } - - TEST_F(TrivialExpressionTest, defaultFunctionArgsCaptureBack) { - auto v = eval("({ a ? 123 }@args: args) {}"); - ASSERT_THAT(v, IsAttrsOfSize(0)); - } - - TEST_F(TrivialExpressionTest, defaultFunctionArgsCaptureFront) { - auto v = eval("(args@{ a ? 123 }: args) {}"); - ASSERT_THAT(v, IsAttrsOfSize(0)); - } - - TEST_F(TrivialExpressionTest, assertThrows) { - ASSERT_THROW(eval("let x = arg: assert arg == 1; 123; in x 2"), Error); - } - - TEST_F(TrivialExpressionTest, assertPassed) { - auto v = eval("let x = arg: assert arg == 1; 123; in x 1"); - ASSERT_THAT(v, IsIntEq(123)); - } - - class AttrSetMergeTrvialExpressionTest : - public TrivialExpressionTest, - public testing::WithParamInterface - {}; - - TEST_P(AttrSetMergeTrvialExpressionTest, attrsetMergeLazy) { - // Usually Nix rejects duplicate keys in an attrset but it does allow - // so if it is an attribute set that contains disjoint sets of keys. - // The below is equivalent to `{a.b = 1; a.c = 2; }`. - // The attribute set `a` will be a Thunk at first as the attribuets - // have to be merged (or otherwise computed) and that is done in a lazy - // manner. - - auto expr = GetParam(); - auto v = eval(expr); - ASSERT_THAT(v, IsAttrsOfSize(1)); - - auto a = v.attrs->find(createSymbol("a")); - ASSERT_NE(a, nullptr); - - ASSERT_THAT(*a->value, IsThunk()); - state.forceValue(*a->value, noPos); - - ASSERT_THAT(*a->value, IsAttrsOfSize(2)); - - auto b = a->value->attrs->find(createSymbol("b")); - ASSERT_NE(b, nullptr); - ASSERT_THAT(*b->value, IsIntEq(1)); - - auto c = a->value->attrs->find(createSymbol("c")); - ASSERT_NE(c, nullptr); - ASSERT_THAT(*c->value, IsIntEq(2)); - } - - INSTANTIATE_TEST_SUITE_P( - attrsetMergeLazy, - AttrSetMergeTrvialExpressionTest, - testing::Values( - "{ a.b = 1; a.c = 2; }", - "{ a = { b = 1; }; a = { c = 2; }; }" - ) - ); - - TEST_F(TrivialExpressionTest, functor) { - auto v = eval("{ __functor = self: arg: self.v + arg; v = 10; } 5"); - ASSERT_THAT(v, IsIntEq(15)); - } - - TEST_F(TrivialExpressionTest, bindOr) { - auto v = eval("{ or = 1; }"); - ASSERT_THAT(v, IsAttrsOfSize(1)); - auto b = v.attrs->find(createSymbol("or")); - ASSERT_NE(b, nullptr); - ASSERT_THAT(*b->value, IsIntEq(1)); - } - - TEST_F(TrivialExpressionTest, orCantBeUsed) { - ASSERT_THROW(eval("let or = 1; in or"), Error); - } +// Testing of trivial expressions +class TrivialExpressionTest : public LibExprTest { +}; + +TEST_F(TrivialExpressionTest, true) +{ + auto v = eval("true"); + ASSERT_THAT(v, IsTrue()); +} + +TEST_F(TrivialExpressionTest, false) +{ + auto v = eval("false"); + ASSERT_THAT(v, IsFalse()); +} + +TEST_F(TrivialExpressionTest, null) +{ + auto v = eval("null"); + ASSERT_THAT(v, IsNull()); +} + +TEST_F(TrivialExpressionTest, 1) +{ + auto v = eval("1"); + ASSERT_THAT(v, IsIntEq(1)); +} + +TEST_F(TrivialExpressionTest, 1plus1) +{ + auto v = eval("1+1"); + ASSERT_THAT(v, IsIntEq(2)); +} + +TEST_F(TrivialExpressionTest, minus1) +{ + auto v = eval("-1"); + ASSERT_THAT(v, IsIntEq(-1)); +} + +TEST_F(TrivialExpressionTest, 1minus1) +{ + auto v = eval("1-1"); + ASSERT_THAT(v, IsIntEq(0)); +} + +TEST_F(TrivialExpressionTest, lambdaAdd) +{ + auto v = eval("let add = a: b: a + b; in add 1 2"); + ASSERT_THAT(v, IsIntEq(3)); +} + +TEST_F(TrivialExpressionTest, list) +{ + auto v = eval("[]"); + ASSERT_THAT(v, IsListOfSize(0)); +} + +TEST_F(TrivialExpressionTest, attrs) +{ + auto v = eval("{}"); + ASSERT_THAT(v, IsAttrsOfSize(0)); +} + +TEST_F(TrivialExpressionTest, float) +{ + auto v = eval("1.234"); + ASSERT_THAT(v, IsFloatEq(1.234)); +} + +TEST_F(TrivialExpressionTest, updateAttrs) +{ + auto v = eval("{ a = 1; } // { b = 2; a = 3; }"); + ASSERT_THAT(v, IsAttrsOfSize(2)); + auto a = v.attrs->find(createSymbol("a")); + ASSERT_NE(a, nullptr); + ASSERT_THAT(*a->value, IsIntEq(3)); + + auto b = v.attrs->find(createSymbol("b")); + ASSERT_NE(b, nullptr); + ASSERT_THAT(*b->value, IsIntEq(2)); +} + +TEST_F(TrivialExpressionTest, hasAttrOpFalse) +{ + auto v = eval("{} ? a"); + ASSERT_THAT(v, IsFalse()); +} + +TEST_F(TrivialExpressionTest, hasAttrOpTrue) +{ + auto v = eval("{ a = 123; } ? a"); + ASSERT_THAT(v, IsTrue()); +} + +TEST_F(TrivialExpressionTest, withFound) +{ + auto v = eval("with { a = 23; }; a"); + ASSERT_THAT(v, IsIntEq(23)); +} + +TEST_F(TrivialExpressionTest, withNotFound) +{ + ASSERT_THROW(eval("with {}; a"), Error); +} + +TEST_F(TrivialExpressionTest, withOverride) +{ + auto v = eval("with { a = 23; }; with { a = 42; }; a"); + ASSERT_THAT(v, IsIntEq(42)); +} + +TEST_F(TrivialExpressionTest, letOverWith) +{ + auto v = eval("let a = 23; in with { a = 1; }; a"); + ASSERT_THAT(v, IsIntEq(23)); +} + +TEST_F(TrivialExpressionTest, multipleLet) +{ + auto v = eval("let a = 23; in let a = 42; in a"); + ASSERT_THAT(v, IsIntEq(42)); +} + +TEST_F(TrivialExpressionTest, defaultFunctionArgs) +{ + auto v = eval("({ a ? 123 }: a) {}"); + ASSERT_THAT(v, IsIntEq(123)); +} + +TEST_F(TrivialExpressionTest, defaultFunctionArgsOverride) +{ + auto v = eval("({ a ? 123 }: a) { a = 5; }"); + ASSERT_THAT(v, IsIntEq(5)); +} + +TEST_F(TrivialExpressionTest, defaultFunctionArgsCaptureBack) +{ + auto v = eval("({ a ? 123 }@args: args) {}"); + ASSERT_THAT(v, IsAttrsOfSize(0)); +} + +TEST_F(TrivialExpressionTest, defaultFunctionArgsCaptureFront) +{ + auto v = eval("(args@{ a ? 123 }: args) {}"); + ASSERT_THAT(v, IsAttrsOfSize(0)); +} + +TEST_F(TrivialExpressionTest, assertThrows) +{ + ASSERT_THROW(eval("let x = arg: assert arg == 1; 123; in x 2"), Error); +} + +TEST_F(TrivialExpressionTest, assertPassed) +{ + auto v = eval("let x = arg: assert arg == 1; 123; in x 1"); + ASSERT_THAT(v, IsIntEq(123)); +} + +class AttrSetMergeTrvialExpressionTest + : public TrivialExpressionTest, + public testing::WithParamInterface { +}; + +TEST_P(AttrSetMergeTrvialExpressionTest, attrsetMergeLazy) +{ + // Usually Nix rejects duplicate keys in an attrset but it does allow + // so if it is an attribute set that contains disjoint sets of keys. + // The below is equivalent to `{a.b = 1; a.c = 2; }`. + // The attribute set `a` will be a Thunk at first as the attribuets + // have to be merged (or otherwise computed) and that is done in a lazy + // manner. + + auto expr = GetParam(); + auto v = eval(expr); + ASSERT_THAT(v, IsAttrsOfSize(1)); + + auto a = v.attrs->find(createSymbol("a")); + ASSERT_NE(a, nullptr); + + ASSERT_THAT(*a->value, IsThunk()); + state.forceValue(*a->value, noPos); + + ASSERT_THAT(*a->value, IsAttrsOfSize(2)); + + auto b = a->value->attrs->find(createSymbol("b")); + ASSERT_NE(b, nullptr); + ASSERT_THAT(*b->value, IsIntEq(1)); + + auto c = a->value->attrs->find(createSymbol("c")); + ASSERT_NE(c, nullptr); + ASSERT_THAT(*c->value, IsIntEq(2)); +} + +INSTANTIATE_TEST_SUITE_P( + attrsetMergeLazy, AttrSetMergeTrvialExpressionTest, + testing::Values("{ a.b = 1; a.c = 2; }", + "{ a = { b = 1; }; a = { c = 2; }; }")); + +TEST_F(TrivialExpressionTest, functor) +{ + auto v = eval("{ __functor = self: arg: self.v + arg; v = 10; } 5"); + ASSERT_THAT(v, IsIntEq(15)); +} + +TEST_F(TrivialExpressionTest, bindOr) +{ + auto v = eval("{ or = 1; }"); + ASSERT_THAT(v, IsAttrsOfSize(1)); + auto b = v.attrs->find(createSymbol("or")); + ASSERT_NE(b, nullptr); + ASSERT_THAT(*b->value, IsIntEq(1)); +} + +TEST_F(TrivialExpressionTest, orCantBeUsed) +{ + ASSERT_THROW(eval("let or = 1; in or"), Error); +} } /* namespace nix */ diff --git a/src/libexpr/value-to-json.cc b/src/libexpr/value-to-json.cc index 03504db6192b..2d3b522b5325 100644 --- a/src/libexpr/value-to-json.cc +++ b/src/libexpr/value-to-json.cc @@ -6,102 +6,105 @@ #include #include - namespace nix { -void printValueAsJSON(EvalState & state, bool strict, - Value & v, const PosIdx pos, JSONPlaceholder & out, PathSet & context) +void printValueAsJSON(EvalState & state, bool strict, Value & v, + const PosIdx pos, JSONPlaceholder & out, + PathSet & context) { checkInterrupt(); - if (strict) state.forceValue(v, pos); + if (strict) + state.forceValue(v, pos); switch (v.type()) { - case nInt: - out.write(v.integer); - break; + case nInt: + out.write(v.integer); + break; - case nBool: - out.write(v.boolean); - break; + case nBool: + out.write(v.boolean); + break; - case nString: - copyContext(v, context); - out.write(v.string.s); - break; + case nString: + copyContext(v, context); + out.write(v.string.s); + break; - case nPath: - out.write(state.copyPathToStore(context, v.path)); - break; + case nPath: + out.write(state.copyPathToStore(context, v.path)); + break; - case nNull: - out.write(nullptr); - break; + case nNull: + out.write(nullptr); + break; - case nAttrs: { - auto maybeString = state.tryAttrsToString(pos, v, context, false, false); - if (maybeString) { - out.write(*maybeString); - break; - } - auto i = v.attrs->find(state.sOutPath); - if (i == v.attrs->end()) { - auto obj(out.object()); - StringSet names; - for (auto & j : *v.attrs) - names.emplace(state.symbols[j.name]); - for (auto & j : names) { - Attr & a(*v.attrs->find(state.symbols.create(j))); - auto placeholder(obj.placeholder(j)); - printValueAsJSON(state, strict, *a.value, a.pos, placeholder, context); - } - } else - printValueAsJSON(state, strict, *i->value, i->pos, out, context); + case nAttrs: { + auto maybeString = + state.tryAttrsToString(pos, v, context, false, false); + if (maybeString) { + out.write(*maybeString); break; } - - case nList: { - auto list(out.list()); - for (auto elem : v.listItems()) { - auto placeholder(list.placeholder()); - printValueAsJSON(state, strict, *elem, pos, placeholder, context); + auto i = v.attrs->find(state.sOutPath); + if (i == v.attrs->end()) { + auto obj(out.object()); + StringSet names; + for (auto & j : *v.attrs) + names.emplace(state.symbols[j.name]); + for (auto & j : names) { + Attr & a(*v.attrs->find(state.symbols.create(j))); + auto placeholder(obj.placeholder(j)); + printValueAsJSON(state, strict, *a.value, a.pos, placeholder, + context); } - break; - } - - case nExternal: - v.external->printValueAsJSON(state, strict, out, context); - break; + } else + printValueAsJSON(state, strict, *i->value, i->pos, out, context); + break; + } - case nFloat: - out.write(v.fpoint); - break; + case nList: { + auto list(out.list()); + for (auto elem : v.listItems()) { + auto placeholder(list.placeholder()); + printValueAsJSON(state, strict, *elem, pos, placeholder, context); + } + break; + } - case nThunk: - case nFunction: - auto e = TypeError({ - .msg = hintfmt("cannot convert %1% to JSON", showType(v)), - .errPos = state.positions[v.determinePos(pos)] - }); - e.addTrace(state.positions[pos], hintfmt("message for the trace")); - state.debugThrowLastTrace(e); - throw e; + case nExternal: + v.external->printValueAsJSON(state, strict, out, context); + break; + + case nFloat: + out.write(v.fpoint); + break; + + case nThunk: + case nFunction: + auto e = TypeError( + {.msg = hintfmt("cannot convert %1% to JSON", showType(v)), + .errPos = state.positions[v.determinePos(pos)]}); + e.addTrace(state.positions[pos], hintfmt("message for the trace")); + state.debugThrowLastTrace(e); + throw e; } } -void printValueAsJSON(EvalState & state, bool strict, - Value & v, const PosIdx pos, std::ostream & str, PathSet & context) +void printValueAsJSON(EvalState & state, bool strict, Value & v, + const PosIdx pos, std::ostream & str, PathSet & context) { JSONPlaceholder out(str); printValueAsJSON(state, strict, v, pos, out, context); } void ExternalValueBase::printValueAsJSON(EvalState & state, bool strict, - JSONPlaceholder & out, PathSet & context) const + JSONPlaceholder & out, + PathSet & context) const { - state.debugThrowLastTrace(TypeError("cannot convert %1% to JSON", showType())); + state.debugThrowLastTrace( + TypeError("cannot convert %1% to JSON", showType())); } - } diff --git a/src/libexpr/value-to-json.hh b/src/libexpr/value-to-json.hh index c020a817ab7c..17d34aaf1431 100644 --- a/src/libexpr/value-to-json.hh +++ b/src/libexpr/value-to-json.hh @@ -10,10 +10,11 @@ namespace nix { class JSONPlaceholder; -void printValueAsJSON(EvalState & state, bool strict, - Value & v, const PosIdx pos, JSONPlaceholder & out, PathSet & context); +void printValueAsJSON(EvalState & state, bool strict, Value & v, + const PosIdx pos, JSONPlaceholder & out, + PathSet & context); -void printValueAsJSON(EvalState & state, bool strict, - Value & v, const PosIdx pos, std::ostream & str, PathSet & context); +void printValueAsJSON(EvalState & state, bool strict, Value & v, + const PosIdx pos, std::ostream & str, PathSet & context); } diff --git a/src/libexpr/value-to-xml.cc b/src/libexpr/value-to-xml.cc index 7c3bf9492622..3f865e0545b7 100644 --- a/src/libexpr/value-to-xml.cc +++ b/src/libexpr/value-to-xml.cc @@ -5,22 +5,19 @@ #include - namespace nix { - -static XMLAttrs singletonAttrs(const std::string & name, const std::string & value) +static XMLAttrs singletonAttrs(const std::string & name, + const std::string & value) { XMLAttrs attrs; attrs[name] = value; return attrs; } - static void printValueAsXML(EvalState & state, bool strict, bool location, - Value & v, XMLWriter & doc, PathSet & context, PathSet & drvsSeen, - const PosIdx pos); - + Value & v, XMLWriter & doc, PathSet & context, + PathSet & drvsSeen, const PosIdx pos); static void posToXML(EvalState & state, XMLAttrs & xmlAttrs, const Pos & pos) { @@ -29,9 +26,9 @@ static void posToXML(EvalState & state, XMLAttrs & xmlAttrs, const Pos & pos) xmlAttrs["column"] = (format("%1%") % pos.column).str(); } - static void showAttrs(EvalState & state, bool strict, bool location, - Bindings & attrs, XMLWriter & doc, PathSet & context, PathSet & drvsSeen) + Bindings & attrs, XMLWriter & doc, PathSet & context, + PathSet & drvsSeen) { StringSet names; @@ -43,137 +40,154 @@ static void showAttrs(EvalState & state, bool strict, bool location, XMLAttrs xmlAttrs; xmlAttrs["name"] = i; - if (location && a.pos) posToXML(state, xmlAttrs, state.positions[a.pos]); + if (location && a.pos) + posToXML(state, xmlAttrs, state.positions[a.pos]); XMLOpenElement _(doc, "attr", xmlAttrs); - printValueAsXML(state, strict, location, - *a.value, doc, context, drvsSeen, a.pos); + printValueAsXML(state, strict, location, *a.value, doc, context, + drvsSeen, a.pos); } } - static void printValueAsXML(EvalState & state, bool strict, bool location, - Value & v, XMLWriter & doc, PathSet & context, PathSet & drvsSeen, - const PosIdx pos) + Value & v, XMLWriter & doc, PathSet & context, + PathSet & drvsSeen, const PosIdx pos) { checkInterrupt(); - if (strict) state.forceValue(v, pos); + if (strict) + state.forceValue(v, pos); switch (v.type()) { - case nInt: - doc.writeEmptyElement("int", singletonAttrs("value", (format("%1%") % v.integer).str())); - break; + case nInt: + doc.writeEmptyElement( + "int", singletonAttrs("value", (format("%1%") % v.integer).str())); + break; - case nBool: - doc.writeEmptyElement("bool", singletonAttrs("value", v.boolean ? "true" : "false")); - break; + case nBool: + doc.writeEmptyElement( + "bool", singletonAttrs("value", v.boolean ? "true" : "false")); + break; - case nString: - /* !!! show the context? */ - copyContext(v, context); - doc.writeEmptyElement("string", singletonAttrs("value", v.string.s)); - break; + case nString: + /* !!! show the context? */ + copyContext(v, context); + doc.writeEmptyElement("string", singletonAttrs("value", v.string.s)); + break; - case nPath: - doc.writeEmptyElement("path", singletonAttrs("value", v.path)); - break; + case nPath: + doc.writeEmptyElement("path", singletonAttrs("value", v.path)); + break; - case nNull: - doc.writeEmptyElement("null"); - break; + case nNull: + doc.writeEmptyElement("null"); + break; + + case nAttrs: + if (state.isDerivation(v)) { + XMLAttrs xmlAttrs; + + Bindings::iterator a = + v.attrs->find(state.symbols.create("derivation")); - case nAttrs: - if (state.isDerivation(v)) { - XMLAttrs xmlAttrs; - - Bindings::iterator a = v.attrs->find(state.symbols.create("derivation")); - - Path drvPath; - a = v.attrs->find(state.sDrvPath); - if (a != v.attrs->end()) { - if (strict) state.forceValue(*a->value, a->pos); - if (a->value->type() == nString) - xmlAttrs["drvPath"] = drvPath = a->value->string.s; - } - - a = v.attrs->find(state.sOutPath); - if (a != v.attrs->end()) { - if (strict) state.forceValue(*a->value, a->pos); - if (a->value->type() == nString) - xmlAttrs["outPath"] = a->value->string.s; - } - - XMLOpenElement _(doc, "derivation", xmlAttrs); - - if (drvPath != "" && drvsSeen.insert(drvPath).second) - showAttrs(state, strict, location, *v.attrs, doc, context, drvsSeen); - else - doc.writeEmptyElement("repeated"); + Path drvPath; + a = v.attrs->find(state.sDrvPath); + if (a != v.attrs->end()) { + if (strict) + state.forceValue(*a->value, a->pos); + if (a->value->type() == nString) + xmlAttrs["drvPath"] = drvPath = a->value->string.s; } - else { - XMLOpenElement _(doc, "attrs"); - showAttrs(state, strict, location, *v.attrs, doc, context, drvsSeen); + a = v.attrs->find(state.sOutPath); + if (a != v.attrs->end()) { + if (strict) + state.forceValue(*a->value, a->pos); + if (a->value->type() == nString) + xmlAttrs["outPath"] = a->value->string.s; } - break; + XMLOpenElement _(doc, "derivation", xmlAttrs); - case nList: { - XMLOpenElement _(doc, "list"); - for (auto v2 : v.listItems()) - printValueAsXML(state, strict, location, *v2, doc, context, drvsSeen, pos); - break; + if (drvPath != "" && drvsSeen.insert(drvPath).second) + showAttrs(state, strict, location, *v.attrs, doc, context, + drvsSeen); + else + doc.writeEmptyElement("repeated"); } - case nFunction: { - if (!v.isLambda()) { - // FIXME: Serialize primops and primopapps - doc.writeEmptyElement("unevaluated"); - break; - } - XMLAttrs xmlAttrs; - if (location) posToXML(state, xmlAttrs, state.positions[v.lambda.fun->pos]); - XMLOpenElement _(doc, "function", xmlAttrs); - - if (v.lambda.fun->hasFormals()) { - XMLAttrs attrs; - if (v.lambda.fun->arg) attrs["name"] = state.symbols[v.lambda.fun->arg]; - if (v.lambda.fun->formals->ellipsis) attrs["ellipsis"] = "1"; - XMLOpenElement _(doc, "attrspat", attrs); - for (auto & i : v.lambda.fun->formals->lexicographicOrder(state.symbols)) - doc.writeEmptyElement("attr", singletonAttrs("name", state.symbols[i.name])); - } else - doc.writeEmptyElement("varpat", singletonAttrs("name", state.symbols[v.lambda.fun->arg])); + else { + XMLOpenElement _(doc, "attrs"); + showAttrs(state, strict, location, *v.attrs, doc, context, + drvsSeen); + } + + break; + case nList: { + XMLOpenElement _(doc, "list"); + for (auto v2 : v.listItems()) + printValueAsXML(state, strict, location, *v2, doc, context, + drvsSeen, pos); + break; + } + + case nFunction: { + if (!v.isLambda()) { + // FIXME: Serialize primops and primopapps + doc.writeEmptyElement("unevaluated"); break; } + XMLAttrs xmlAttrs; + if (location) + posToXML(state, xmlAttrs, state.positions[v.lambda.fun->pos]); + XMLOpenElement _(doc, "function", xmlAttrs); + + if (v.lambda.fun->hasFormals()) { + XMLAttrs attrs; + if (v.lambda.fun->arg) + attrs["name"] = state.symbols[v.lambda.fun->arg]; + if (v.lambda.fun->formals->ellipsis) + attrs["ellipsis"] = "1"; + XMLOpenElement _(doc, "attrspat", attrs); + for (auto & i : + v.lambda.fun->formals->lexicographicOrder(state.symbols)) + doc.writeEmptyElement( + "attr", singletonAttrs("name", state.symbols[i.name])); + } else + doc.writeEmptyElement( + "varpat", + singletonAttrs("name", state.symbols[v.lambda.fun->arg])); + + break; + } - case nExternal: - v.external->printValueAsXML(state, strict, location, doc, context, drvsSeen, pos); - break; + case nExternal: + v.external->printValueAsXML(state, strict, location, doc, context, + drvsSeen, pos); + break; - case nFloat: - doc.writeEmptyElement("float", singletonAttrs("value", (format("%1%") % v.fpoint).str())); - break; + case nFloat: + doc.writeEmptyElement( + "float", singletonAttrs("value", (format("%1%") % v.fpoint).str())); + break; - case nThunk: - doc.writeEmptyElement("unevaluated"); + case nThunk: + doc.writeEmptyElement("unevaluated"); } } - void ExternalValueBase::printValueAsXML(EvalState & state, bool strict, - bool location, XMLWriter & doc, PathSet & context, PathSet & drvsSeen, - const PosIdx pos) const + bool location, XMLWriter & doc, + PathSet & context, PathSet & drvsSeen, + const PosIdx pos) const { doc.writeEmptyElement("unevaluated"); } - -void printValueAsXML(EvalState & state, bool strict, bool location, - Value & v, std::ostream & out, PathSet & context, const PosIdx pos) +void printValueAsXML(EvalState & state, bool strict, bool location, Value & v, + std::ostream & out, PathSet & context, const PosIdx pos) { XMLWriter doc(true, out); XMLOpenElement root(doc, "expr"); @@ -181,5 +195,4 @@ void printValueAsXML(EvalState & state, bool strict, bool location, printValueAsXML(state, strict, location, v, doc, context, drvsSeen, pos); } - } diff --git a/src/libexpr/value-to-xml.hh b/src/libexpr/value-to-xml.hh index 506f32b6b702..a709d34d35fe 100644 --- a/src/libexpr/value-to-xml.hh +++ b/src/libexpr/value-to-xml.hh @@ -8,7 +8,7 @@ namespace nix { -void printValueAsXML(EvalState & state, bool strict, bool location, - Value & v, std::ostream & out, PathSet & context, const PosIdx pos); +void printValueAsXML(EvalState & state, bool strict, bool location, Value & v, + std::ostream & out, PathSet & context, const PosIdx pos); } diff --git a/src/libexpr/value.hh b/src/libexpr/value.hh index 2008df74d609..2d7fc65c287b 100644 --- a/src/libexpr/value.hh +++ b/src/libexpr/value.hh @@ -12,7 +12,6 @@ namespace nix { class BindingsBuilder; - typedef enum { tInt = 1, tBool, @@ -64,7 +63,6 @@ class EvalState; class XMLWriter; class JSONPlaceholder; - typedef int64_t NixInt; typedef double NixFloat; typedef std::pair NixStringContextElem; @@ -73,14 +71,15 @@ typedef std::vector NixStringContext; /* External values must descend from ExternalValueBase, so that * type-agnostic nix functions (e.g. showType) can be implemented */ -class ExternalValueBase -{ - friend std::ostream & operator << (std::ostream & str, const ExternalValueBase & v); - protected: +class ExternalValueBase { + friend std::ostream & operator<<(std::ostream & str, + const ExternalValueBase & v); + + protected: /* Print out the value */ virtual std::ostream & print(std::ostream & str) const = 0; - public: + public: /* Return a simple string describing the type */ virtual std::string showType() const = 0; @@ -90,42 +89,42 @@ class ExternalValueBase /* Coerce the value to a string. Defaults to uncoercable, i.e. throws an * error. */ - virtual std::string coerceToString(const Pos & pos, PathSet & context, bool copyMore, bool copyToStore) const; + virtual std::string coerceToString(const Pos & pos, PathSet & context, + bool copyMore, bool copyToStore) const; /* Compare to another value of the same type. Defaults to uncomparable, * i.e. always false. */ - virtual bool operator ==(const ExternalValueBase & b) const; + virtual bool operator==(const ExternalValueBase & b) const; - /* Print the value as JSON. Defaults to unconvertable, i.e. throws an error */ + /* Print the value as JSON. Defaults to unconvertable, i.e. throws an error + */ virtual void printValueAsJSON(EvalState & state, bool strict, - JSONPlaceholder & out, PathSet & context) const; + JSONPlaceholder & out, + PathSet & context) const; /* Print the value as XML. Defaults to unevaluated */ virtual void printValueAsXML(EvalState & state, bool strict, bool location, - XMLWriter & doc, PathSet & context, PathSet & drvsSeen, - const PosIdx pos) const; + XMLWriter & doc, PathSet & context, + PathSet & drvsSeen, const PosIdx pos) const; - virtual ~ExternalValueBase() - { - }; + virtual ~ExternalValueBase(){}; }; -std::ostream & operator << (std::ostream & str, const ExternalValueBase & v); - +std::ostream & operator<<(std::ostream & str, const ExternalValueBase & v); -struct Value -{ -private: +struct Value { + private: InternalType internalType; friend std::string showType(const Value & v); - void print(const SymbolTable & symbols, std::ostream & str, std::set * seen) const; + void print(const SymbolTable & symbols, std::ostream & str, + std::set * seen) const; -public: - - void print(const SymbolTable & symbols, std::ostream & str, bool showRepeated = false) const; + public: + void print(const SymbolTable & symbols, std::ostream & str, + bool showRepeated = false) const; // Functions needed to distinguish the type // These should be removed eventually, by putting the functionality that's @@ -141,8 +140,7 @@ public: inline bool isPrimOp() const { return internalType == tPrimOp; }; inline bool isPrimOpApp() const { return internalType == tPrimOpApp; }; - union - { + union { NixInt integer; bool boolean; @@ -168,14 +166,14 @@ public: For canonicity, the store paths should be in sorted order. */ struct { const char * s; - const char * * context; // must be in sorted order + const char ** context; // must be in sorted order } string; const char * path; Bindings * attrs; struct { size_t size; - Value * * elems; + Value ** elems; } bigList; Value * smallList[2]; struct { @@ -183,7 +181,7 @@ public: Expr * expr; } thunk; struct { - Value * left, * right; + Value *left, *right; } app; struct { Env * env; @@ -191,7 +189,7 @@ public: } lambda; PrimOp * primOp; struct { - Value * left, * right; + Value *left, *right; } primOpApp; ExternalValueBase * external; NixFloat fpoint; @@ -202,27 +200,41 @@ public: inline ValueType type() const { switch (internalType) { - case tInt: return nInt; - case tBool: return nBool; - case tString: return nString; - case tPath: return nPath; - case tNull: return nNull; - case tAttrs: return nAttrs; - case tList1: case tList2: case tListN: return nList; - case tLambda: case tPrimOp: case tPrimOpApp: return nFunction; - case tExternal: return nExternal; - case tFloat: return nFloat; - case tThunk: case tApp: case tBlackhole: return nThunk; + case tInt: + return nInt; + case tBool: + return nBool; + case tString: + return nString; + case tPath: + return nPath; + case tNull: + return nNull; + case tAttrs: + return nAttrs; + case tList1: + case tList2: + case tListN: + return nList; + case tLambda: + case tPrimOp: + case tPrimOpApp: + return nFunction; + case tExternal: + return nExternal; + case tFloat: + return nFloat; + case tThunk: + case tApp: + case tBlackhole: + return nThunk; } abort(); } /* After overwriting an app node, be sure to clear pointers in the Value to ensure that the target isn't kept alive unnecessarily. */ - inline void clearValue() - { - app.left = app.right = 0; - } + inline void clearValue() { app.left = app.right = 0; } inline void mkInt(NixInt n) { @@ -238,7 +250,7 @@ public: boolean = b; } - inline void mkString(const char * s, const char * * context = 0) + inline void mkString(const char * s, const char ** context = 0) { internalType = tString; string.s = s; @@ -322,7 +334,6 @@ public: primOp = p; } - inline void mkPrimOpApp(Value * l, Value * r) { internalType = tPrimOpApp; @@ -346,22 +357,27 @@ public: bool isList() const { - return internalType == tList1 || internalType == tList2 || internalType == tListN; + return internalType == tList1 || internalType == tList2 || + internalType == tListN; } - Value * * listElems() + Value ** listElems() { - return internalType == tList1 || internalType == tList2 ? smallList : bigList.elems; + return internalType == tList1 || internalType == tList2 ? smallList + : bigList.elems; } const Value * const * listElems() const { - return internalType == tList1 || internalType == tList2 ? smallList : bigList.elems; + return internalType == tList1 || internalType == tList2 ? smallList + : bigList.elems; } size_t listSize() const { - return internalType == tList1 ? 1 : internalType == tList2 ? 2 : bigList.size; + return internalType == tList1 ? 1 + : internalType == tList2 ? 2 + : bigList.size; } PosIdx determinePos(const PosIdx pos) const; @@ -375,8 +391,7 @@ public: auto listItems() { - struct ListIterable - { + struct ListIterable { typedef Value * const * iterator; iterator _begin, _end; iterator begin() const { return _begin; } @@ -384,13 +399,12 @@ public: }; assert(isList()); auto begin = listElems(); - return ListIterable { begin, begin + listSize() }; + return ListIterable{begin, begin + listSize()}; } auto listItems() const { - struct ConstListIterable - { + struct ConstListIterable { typedef const Value * const * iterator; iterator _begin, _end; iterator begin() const { return _begin; } @@ -398,22 +412,24 @@ public: }; assert(isList()); auto begin = listElems(); - return ConstListIterable { begin, begin + listSize() }; + return ConstListIterable{begin, begin + listSize()}; } }; - #if HAVE_BOEHMGC typedef std::vector> ValueVector; -typedef std::map, traceable_allocator>> ValueMap; -typedef std::map, traceable_allocator>> ValueVectorMap; +typedef std::map, + traceable_allocator>> + ValueMap; +typedef std::map, + traceable_allocator>> + ValueVectorMap; #else typedef std::vector ValueVector; typedef std::map ValueMap; typedef std::map ValueVectorMap; #endif - /* A value allocated in traceable memory. */ typedef std::shared_ptr RootValue; diff --git a/src/libfetchers/attrs.cc b/src/libfetchers/attrs.cc index a565d19d4e72..1eceec3c9e7c 100644 --- a/src/libfetchers/attrs.cc +++ b/src/libfetchers/attrs.cc @@ -15,7 +15,7 @@ Attrs jsonToAttrs(const nlohmann::json & json) else if (i.value().is_string()) attrs.emplace(i.key(), i.value().get()); else if (i.value().is_boolean()) - attrs.emplace(i.key(), Explicit { i.value().get() }); + attrs.emplace(i.key(), Explicit{i.value().get()}); else throw Error("unsupported input attribute type in lock file"); } @@ -33,18 +33,22 @@ nlohmann::json attrsToJSON(const Attrs & attrs) json[attr.first] = *v; } else if (auto v = std::get_if>(&attr.second)) { json[attr.first] = v->t; - } else abort(); + } else + abort(); } return json; } -std::optional maybeGetStrAttr(const Attrs & attrs, const std::string & name) +std::optional maybeGetStrAttr(const Attrs & attrs, + const std::string & name) { auto i = attrs.find(name); - if (i == attrs.end()) return {}; + if (i == attrs.end()) + return {}; if (auto v = std::get_if(&i->second)) return *v; - throw Error("input attribute '%s' is not a string %s", name, attrsToJSON(attrs).dump()); + throw Error("input attribute '%s' is not a string %s", name, + attrsToJSON(attrs).dump()); } std::string getStrAttr(const Attrs & attrs, const std::string & name) @@ -55,10 +59,12 @@ std::string getStrAttr(const Attrs & attrs, const std::string & name) return *s; } -std::optional maybeGetIntAttr(const Attrs & attrs, const std::string & name) +std::optional maybeGetIntAttr(const Attrs & attrs, + const std::string & name) { auto i = attrs.find(name); - if (i == attrs.end()) return {}; + if (i == attrs.end()) + return {}; if (auto v = std::get_if(&i->second)) return *v; throw Error("input attribute '%s' is not an integer", name); @@ -72,10 +78,12 @@ uint64_t getIntAttr(const Attrs & attrs, const std::string & name) return *s; } -std::optional maybeGetBoolAttr(const Attrs & attrs, const std::string & name) +std::optional maybeGetBoolAttr(const Attrs & attrs, + const std::string & name) { auto i = attrs.find(name); - if (i == attrs.end()) return {}; + if (i == attrs.end()) + return {}; if (auto v = std::get_if>(&i->second)) return v->t; throw Error("input attribute '%s' is not a Boolean", name); @@ -99,7 +107,8 @@ std::map attrsToQuery(const Attrs & attrs) query.insert_or_assign(attr.first, *v); } else if (auto v = std::get_if>(&attr.second)) { query.insert_or_assign(attr.first, v->t ? "1" : "0"); - } else abort(); + } else + abort(); } return query; } diff --git a/src/libfetchers/attrs.hh b/src/libfetchers/attrs.hh index e41037633823..a42f37b4a565 100644 --- a/src/libfetchers/attrs.hh +++ b/src/libfetchers/attrs.hh @@ -17,15 +17,18 @@ Attrs jsonToAttrs(const nlohmann::json & json); nlohmann::json attrsToJSON(const Attrs & attrs); -std::optional maybeGetStrAttr(const Attrs & attrs, const std::string & name); +std::optional maybeGetStrAttr(const Attrs & attrs, + const std::string & name); std::string getStrAttr(const Attrs & attrs, const std::string & name); -std::optional maybeGetIntAttr(const Attrs & attrs, const std::string & name); +std::optional maybeGetIntAttr(const Attrs & attrs, + const std::string & name); uint64_t getIntAttr(const Attrs & attrs, const std::string & name); -std::optional maybeGetBoolAttr(const Attrs & attrs, const std::string & name); +std::optional maybeGetBoolAttr(const Attrs & attrs, + const std::string & name); bool getBoolAttr(const Attrs & attrs, const std::string & name); diff --git a/src/libfetchers/cache.cc b/src/libfetchers/cache.cc index 0c8ecac9d48a..d0b205f908fa 100644 --- a/src/libfetchers/cache.cc +++ b/src/libfetchers/cache.cc @@ -19,10 +19,8 @@ create table if not exists Cache ( ); )sql"; -struct CacheImpl : Cache -{ - struct State - { +struct CacheImpl : Cache { + struct State { SQLite db; SQLiteStmt add, lookup; }; @@ -41,43 +39,38 @@ struct CacheImpl : Cache state->db.exec(schema); state->add.create(state->db, - "insert or replace into Cache(input, info, path, immutable, timestamp) values (?, ?, ?, ?, ?)"); + "insert or replace into Cache(input, info, path, " + "immutable, timestamp) values (?, ?, ?, ?, ?)"); - state->lookup.create(state->db, - "select info, path, immutable, timestamp from Cache where input = ?"); + state->lookup.create(state->db, "select info, path, immutable, " + "timestamp from Cache where input = ?"); } - void add( - ref store, - const Attrs & inAttrs, - const Attrs & infoAttrs, - const StorePath & storePath, - bool locked) override + void add(ref store, const Attrs & inAttrs, const Attrs & infoAttrs, + const StorePath & storePath, bool locked) override { - _state.lock()->add.use() - (attrsToJSON(inAttrs).dump()) - (attrsToJSON(infoAttrs).dump()) - (store->printStorePath(storePath)) - (locked) - (time(0)).exec(); + _state.lock() + ->add + .use()(attrsToJSON(inAttrs).dump())(attrsToJSON(infoAttrs).dump())( + store->printStorePath(storePath))(locked) (time(0)) + .exec(); } - std::optional> lookup( - ref store, - const Attrs & inAttrs) override + std::optional> + lookup(ref store, const Attrs & inAttrs) override { if (auto res = lookupExpired(store, inAttrs)) { if (!res->expired) - return std::make_pair(std::move(res->infoAttrs), std::move(res->storePath)); + return std::make_pair(std::move(res->infoAttrs), + std::move(res->storePath)); debug("ignoring expired cache entry '%s'", - attrsToJSON(inAttrs).dump()); + attrsToJSON(inAttrs).dump()); } return {}; } - std::optional lookupExpired( - ref store, - const Attrs & inAttrs) override + std::optional lookupExpired(ref store, + const Attrs & inAttrs) override { auto state(_state.lock()); @@ -101,14 +94,14 @@ struct CacheImpl : Cache return {}; } - debug("using cache entry '%s' -> '%s', '%s'", - inAttrsJSON, infoJSON, store->printStorePath(storePath)); + debug("using cache entry '%s' -> '%s', '%s'", inAttrsJSON, infoJSON, + store->printStorePath(storePath)); - return Result { - .expired = !locked && (settings.tarballTtl.get() == 0 || timestamp + settings.tarballTtl < time(0)), - .infoAttrs = jsonToAttrs(nlohmann::json::parse(infoJSON)), - .storePath = std::move(storePath) - }; + return Result{.expired = !locked && + (settings.tarballTtl.get() == 0 || + timestamp + settings.tarballTtl < time(0)), + .infoAttrs = jsonToAttrs(nlohmann::json::parse(infoJSON)), + .storePath = std::move(storePath)}; } }; diff --git a/src/libfetchers/cache.hh b/src/libfetchers/cache.hh index 3763ee2a6309..c44b0253f36b 100644 --- a/src/libfetchers/cache.hh +++ b/src/libfetchers/cache.hh @@ -4,31 +4,24 @@ namespace nix::fetchers { -struct Cache -{ - virtual ~Cache() { } - - virtual void add( - ref store, - const Attrs & inAttrs, - const Attrs & infoAttrs, - const StorePath & storePath, - bool locked) = 0; - - virtual std::optional> lookup( - ref store, - const Attrs & inAttrs) = 0; - - struct Result - { +struct Cache { + virtual ~Cache() {} + + virtual void add(ref store, const Attrs & inAttrs, + const Attrs & infoAttrs, const StorePath & storePath, + bool locked) = 0; + + virtual std::optional> + lookup(ref store, const Attrs & inAttrs) = 0; + + struct Result { bool expired = false; Attrs infoAttrs; StorePath storePath; }; - virtual std::optional lookupExpired( - ref store, - const Attrs & inAttrs) = 0; + virtual std::optional lookupExpired(ref store, + const Attrs & inAttrs) = 0; }; ref getCache(); diff --git a/src/libfetchers/fetch-settings.cc b/src/libfetchers/fetch-settings.cc index e7d5244dc2cd..68082a8d5784 100644 --- a/src/libfetchers/fetch-settings.cc +++ b/src/libfetchers/fetch-settings.cc @@ -2,9 +2,7 @@ namespace nix { -FetchSettings::FetchSettings() -{ -} +FetchSettings::FetchSettings() {} FetchSettings fetchSettings; diff --git a/src/libfetchers/fetch-settings.hh b/src/libfetchers/fetch-settings.hh index 04c9feda0af3..d16049b540d4 100644 --- a/src/libfetchers/fetch-settings.hh +++ b/src/libfetchers/fetch-settings.hh @@ -11,12 +11,13 @@ namespace nix { -struct FetchSettings : public Config -{ +struct FetchSettings : public Config { FetchSettings(); - Setting accessTokens{this, {}, "access-tokens", - R"( + Setting accessTokens{this, + {}, + "access-tokens", + R"( Access tokens used to access protected GitHub, GitLab, or other locations requiring token-based authentication. @@ -65,23 +66,28 @@ struct FetchSettings : public Config )"}; Setting allowDirty{this, true, "allow-dirty", - "Whether to allow dirty Git/Mercurial trees."}; + "Whether to allow dirty Git/Mercurial trees."}; Setting warnDirty{this, true, "warn-dirty", - "Whether to warn about dirty Git/Mercurial trees."}; + "Whether to warn about dirty Git/Mercurial trees."}; - Setting flakeRegistry{this, "https://github.com/NixOS/flake-registry/raw/master/flake-registry.json", "flake-registry", - "Path or URI of the global flake registry."}; + Setting flakeRegistry{ + this, + "https://github.com/NixOS/flake-registry/raw/master/" + "flake-registry.json", + "flake-registry", "Path or URI of the global flake registry."}; - Setting useRegistries{this, true, "use-registries", + Setting useRegistries{ + this, true, "use-registries", "Whether to use flake registries to resolve flake references."}; - Setting acceptFlakeConfig{this, false, "accept-flake-config", + Setting acceptFlakeConfig{ + this, false, "accept-flake-config", "Whether to accept nix configuration from a flake without prompting."}; - Setting commitLockFileSummary{ - this, "", "commit-lockfile-summary", - R"( + Setting commitLockFileSummary{this, "", + "commit-lockfile-summary", + R"( The commit summary to use when committing changed flake lock files. If empty, the summary is generated based on the action performed. )"}; diff --git a/src/libfetchers/fetchers.cc b/src/libfetchers/fetchers.cc index 6957d2da408c..5c994638536b 100644 --- a/src/libfetchers/fetchers.cc +++ b/src/libfetchers/fetchers.cc @@ -5,18 +5,18 @@ namespace nix::fetchers { -std::unique_ptr>> inputSchemes = nullptr; +std::unique_ptr>> inputSchemes = + nullptr; void registerInputScheme(std::shared_ptr && inputScheme) { - if (!inputSchemes) inputSchemes = std::make_unique>>(); + if (!inputSchemes) + inputSchemes = + std::make_unique>>(); inputSchemes->push_back(std::move(inputScheme)); } -Input Input::fromURL(const std::string & url) -{ - return fromURL(parseURL(url)); -} +Input Input::fromURL(const std::string & url) { return fromURL(parseURL(url)); } static void fixupInput(Input & input) { @@ -69,7 +69,8 @@ ParsedURL Input::toURL() const return scheme->toURL(*this); } -std::string Input::toURLString(const std::map & extraQuery) const +std::string +Input::toURLString(const std::map & extraQuery) const { auto url = toURL(); for (auto & attr : extraQuery) @@ -77,40 +78,37 @@ std::string Input::toURLString(const std::map & extraQ return url.to_string(); } -std::string Input::to_string() const -{ - return toURL().to_string(); -} +std::string Input::to_string() const { return toURL().to_string(); } -Attrs Input::toAttrs() const -{ - return attrs; -} +Attrs Input::toAttrs() const { return attrs; } bool Input::hasAllInfo() const { return getNarHash() && scheme && scheme->hasAllInfo(*this); } -bool Input::operator ==(const Input & other) const +bool Input::operator==(const Input & other) const { return attrs == other.attrs; } bool Input::contains(const Input & other) const { - if (*this == other) return true; + if (*this == other) + return true; auto other2(other); other2.attrs.erase("ref"); other2.attrs.erase("rev"); - if (*this == other2) return true; + if (*this == other2) + return true; return false; } std::pair Input::fetch(ref store) const { if (!scheme) - throw Error("cannot fetch unsupported input '%s'", attrsToJSON(toAttrs())); + throw Error("cannot fetch unsupported input '%s'", + attrsToJSON(toAttrs())); /* The tree may already be in the Nix store, or it could be substituted (which is often faster than fetching from the @@ -121,12 +119,15 @@ std::pair Input::fetch(ref store) const store->ensurePath(storePath); - debug("using substituted/cached input '%s' in '%s'", - to_string(), store->printStorePath(storePath)); + debug("using substituted/cached input '%s' in '%s'", to_string(), + store->printStorePath(storePath)); - return {Tree { .actualPath = store->toRealPath(storePath), .storePath = std::move(storePath) }, *this}; + return {Tree{.actualPath = store->toRealPath(storePath), + .storePath = std::move(storePath)}, + *this}; } catch (Error & e) { - debug("substitution of input '%s' failed: %s", to_string(), e.what()); + debug("substitution of input '%s' failed: %s", to_string(), + e.what()); } } @@ -139,7 +140,7 @@ std::pair Input::fetch(ref store) const } }(); - Tree tree { + Tree tree{ .actualPath = store->toRealPath(storePath), .storePath = storePath, }; @@ -149,19 +150,24 @@ std::pair Input::fetch(ref store) const if (auto prevNarHash = getNarHash()) { if (narHash != *prevNarHash) - 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)); + 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()) { if (input.getLastModified() != prevLastModified) - throw Error("'lastModified' attribute mismatch in input '%s', expected %d", + throw Error( + "'lastModified' attribute mismatch in input '%s', expected %d", input.to_string(), *prevLastModified); } if (auto prevRevCount = getRevCount()) { if (input.getRevCount() != prevRevCount) - throw Error("'revCount' attribute mismatch in input '%s', expected %d", + throw Error( + "'revCount' attribute mismatch in input '%s', expected %d", input.to_string(), *prevRevCount); } @@ -172,11 +178,11 @@ std::pair Input::fetch(ref store) const return {std::move(tree), input}; } -Input Input::applyOverrides( - std::optional ref, - std::optional rev) const +Input Input::applyOverrides(std::optional ref, + std::optional rev) const { - if (!scheme) return *this; + if (!scheme) + return *this; return scheme->applyOverrides(*this, ref, rev); } @@ -192,9 +198,8 @@ std::optional Input::getSourcePath() const return scheme->getSourcePath(*this); } -void Input::markChangedFile( - std::string_view file, - std::optional commitMsg) const +void Input::markChangedFile(std::string_view file, + std::optional commitMsg) const { assert(scheme); return scheme->markChangedFile(*this, file, commitMsg); @@ -209,14 +214,13 @@ StorePath Input::computeStorePath(Store & store) const { auto narHash = getNarHash(); if (!narHash) - throw Error("cannot compute store path for unlocked input '%s'", to_string()); - return store.makeFixedOutputPath(FileIngestionMethod::Recursive, *narHash, getName()); + throw Error("cannot compute store path for unlocked input '%s'", + to_string()); + return store.makeFixedOutputPath(FileIngestionMethod::Recursive, *narHash, + getName()); } -std::string Input::getType() const -{ - return getStrAttr(attrs, "type"); -} +std::string Input::getType() const { return getStrAttr(attrs, "type"); } std::optional Input::getNarHash() const { @@ -243,7 +247,7 @@ std::optional Input::getRev() const if (auto s = maybeGetStrAttr(attrs, "rev")) { try { hash = Hash::parseAnyPrefixed(*s); - } catch (BadHash &e) { + } catch (BadHash & e) { // Default to sha1 for backwards compatibility with existing flakes hash = Hash::parseAny(*s, htSHA1); } @@ -268,18 +272,21 @@ std::optional Input::getLastModified() const ParsedURL InputScheme::toURL(const Input & input) { - throw Error("don't know how to convert input '%s' to a URL", attrsToJSON(input.attrs)); + throw Error("don't know how to convert input '%s' to a URL", + attrsToJSON(input.attrs)); } -Input InputScheme::applyOverrides( - const Input & input, - std::optional ref, - std::optional rev) +Input InputScheme::applyOverrides(const Input & input, + std::optional ref, + std::optional rev) { if (ref) - throw Error("don't know how to set branch/tag name of input '%s' to '%s'", input.to_string(), *ref); + throw Error( + "don't know how to set branch/tag name of input '%s' to '%s'", + input.to_string(), *ref); if (rev) - throw Error("don't know how to set revision of input '%s' to '%s'", input.to_string(), rev->gitRev()); + throw Error("don't know how to set revision of input '%s' to '%s'", + input.to_string(), rev->gitRev()); return input; } @@ -288,7 +295,8 @@ std::optional InputScheme::getSourcePath(const Input & input) return {}; } -void InputScheme::markChangedFile(const Input & input, std::string_view file, std::optional commitMsg) +void InputScheme::markChangedFile(const Input & input, std::string_view file, + std::optional commitMsg) { assert(false); } diff --git a/src/libfetchers/fetchers.hh b/src/libfetchers/fetchers.hh index bc9a76b0bb4f..3b67ca5944e4 100644 --- a/src/libfetchers/fetchers.hh +++ b/src/libfetchers/fetchers.hh @@ -8,12 +8,13 @@ #include -namespace nix { class Store; } +namespace nix { +class Store; +} namespace nix::fetchers { -struct Tree -{ +struct Tree { Path actualPath; StorePath storePath; }; @@ -28,8 +29,7 @@ struct InputScheme; * the url or attrset specified in the flake file. */ -struct Input -{ +struct Input { friend struct InputScheme; std::shared_ptr scheme; // note: can be null @@ -40,7 +40,7 @@ struct Input /* path of the parent of this input, used for relative path resolution */ std::optional parent; -public: + public: static Input fromURL(const std::string & url); static Input fromURL(const ParsedURL & url); @@ -49,7 +49,8 @@ public: ParsedURL toURL() const; - std::string toURLString(const std::map & extraQuery = {}) const; + std::string toURLString( + const std::map & extraQuery = {}) const; std::string to_string() const; @@ -65,7 +66,7 @@ public: bool hasAllInfo() const; - bool operator ==(const Input & other) const; + bool operator==(const Input & other) const; bool contains(const Input & other) const; @@ -73,17 +74,15 @@ public: the Nix store and the locked input. */ std::pair fetch(ref store) const; - Input applyOverrides( - std::optional ref, - std::optional rev) const; + Input applyOverrides(std::optional ref, + std::optional rev) const; void clone(const Path & destDir) const; std::optional getSourcePath() const; - void markChangedFile( - std::string_view file, - std::optional commitMsg) const; + void markChangedFile(std::string_view file, + std::optional commitMsg) const; std::string getName() const; @@ -98,7 +97,6 @@ public: std::optional getLastModified() const; }; - /* The InputScheme represents a type of fetcher. Each fetcher * registers with nix at startup time. When processing an input for a * flake, each scheme is given an opportunity to "recognize" that @@ -108,10 +106,8 @@ public: * needs to actually perform the "fetch()" when called. */ -struct InputScheme -{ - virtual ~InputScheme() - { } +struct InputScheme { + virtual ~InputScheme() {} virtual std::optional inputFromURL(const ParsedURL & url) = 0; @@ -121,41 +117,36 @@ struct InputScheme virtual bool hasAllInfo(const Input & input) = 0; - virtual Input applyOverrides( - const Input & input, - std::optional ref, - std::optional rev); + virtual Input applyOverrides(const Input & input, + std::optional ref, + std::optional rev); virtual void clone(const Input & input, const Path & destDir); virtual std::optional getSourcePath(const Input & input); - virtual void markChangedFile(const Input & input, std::string_view file, std::optional commitMsg); + virtual void markChangedFile(const Input & input, std::string_view file, + std::optional commitMsg); - virtual std::pair fetch(ref store, const Input & input) = 0; + virtual std::pair fetch(ref store, + const Input & input) = 0; }; void registerInputScheme(std::shared_ptr && fetcher); -struct DownloadFileResult -{ +struct DownloadFileResult { StorePath storePath; std::string etag; std::string effectiveUrl; }; -DownloadFileResult downloadFile( - ref store, - const std::string & url, - const std::string & name, - bool locked, - const Headers & headers = {}); - -std::pair downloadTarball( - ref store, - const std::string & url, - const std::string & name, - bool locked, - const Headers & headers = {}); +DownloadFileResult downloadFile(ref store, const std::string & url, + const std::string & name, bool locked, + const Headers & headers = {}); + +std::pair downloadTarball(ref store, + const std::string & url, + const std::string & name, bool locked, + const Headers & headers = {}); } diff --git a/src/libfetchers/git.cc b/src/libfetchers/git.cc index 35fdf807afd5..b5613610b363 100644 --- a/src/libfetchers/git.cc +++ b/src/libfetchers/git.cc @@ -20,10 +20,11 @@ using namespace std::string_literals; namespace nix::fetchers { namespace { -// Explicit initial branch of our bare repo to suppress warnings from new version of git. -// The value itself does not matter, since we always fetch a specific revision or branch. -// It is set with `-c init.defaultBranch=` instead of `--initial-branch=` to stay compatible with -// old version of git, which will ignore unrecognized `-c` options. +// Explicit initial branch of our bare repo to suppress warnings from new +// version of git. The value itself does not matter, since we always fetch a +// specific revision or branch. It is set with `-c init.defaultBranch=` instead +// of `--initial-branch=` to stay compatible with old version of git, which will +// ignore unrecognized `-c` options. const std::string gitInitialBranch = "__nix_dummy_branch"; bool isCacheFileWithinTtl(const time_t now, const struct stat & st) @@ -31,21 +32,21 @@ bool isCacheFileWithinTtl(const time_t now, const struct stat & st) return st.st_mtime + settings.tarballTtl > now; } -bool touchCacheFile(const Path& path, const time_t& touch_time) +bool touchCacheFile(const Path & path, const time_t & touch_time) { - struct timeval times[2]; - times[0].tv_sec = touch_time; - times[0].tv_usec = 0; - times[1].tv_sec = touch_time; - times[1].tv_usec = 0; + struct timeval times[2]; + times[0].tv_sec = touch_time; + times[0].tv_usec = 0; + times[1].tv_sec = touch_time; + times[1].tv_usec = 0; - return lutimes(path.c_str(), times) == 0; + return lutimes(path.c_str(), times) == 0; } Path getCachePath(std::string key) { return getCacheDir() + "/nix/gitv3/" + - hashString(htSHA256, key).to_string(Base32, false); + hashString(htSHA256, key).to_string(Base32, false); } // Returns the name of the HEAD branch. @@ -57,7 +58,7 @@ Path getCachePath(std::string key) // ... std::optional readHead(const Path & path) { - auto [exit_code, output] = runProgram(RunOptions { + auto [exit_code, output] = runProgram(RunOptions{ .program = "git", .args = {"ls-remote", "--symref", path}, }); @@ -69,12 +70,14 @@ std::optional readHead(const Path & path) line = line.substr(0, line.find("\n")); if (const auto parseResult = git::parseLsRemoteLine(line)) { switch (parseResult->kind) { - case git::LsRemoteRefLine::Kind::Symbolic: - debug("resolved HEAD ref '%s' for repo '%s'", parseResult->target, path); - break; - case git::LsRemoteRefLine::Kind::Object: - debug("resolved HEAD rev '%s' for repo '%s'", parseResult->target, path); - break; + case git::LsRemoteRefLine::Kind::Symbolic: + debug("resolved HEAD ref '%s' for repo '%s'", parseResult->target, + path); + break; + case git::LsRemoteRefLine::Kind::Object: + debug("resolved HEAD rev '%s' for repo '%s'", parseResult->target, + path); + break; } return parseResult->target; } @@ -82,21 +85,25 @@ std::optional readHead(const Path & path) } // Persist the HEAD ref from the remote repo in the local cached repo. -bool storeCachedHead(const std::string& actualUrl, const std::string& headRef) +bool storeCachedHead(const std::string & actualUrl, const std::string & headRef) { Path cacheDir = getCachePath(actualUrl); auto gitDir = "."; try { - runProgram("git", true, { "-C", cacheDir, "--git-dir", gitDir, "symbolic-ref", "--", "HEAD", headRef }); - } catch (ExecError &e) { - if (!WIFEXITED(e.status)) throw; + runProgram("git", true, + {"-C", cacheDir, "--git-dir", gitDir, "symbolic-ref", "--", + "HEAD", headRef}); + } catch (ExecError & e) { + if (!WIFEXITED(e.status)) + throw; return false; } - /* No need to touch refs/HEAD, because `git symbolic-ref` updates the mtime. */ + /* No need to touch refs/HEAD, because `git symbolic-ref` updates the mtime. + */ return true; } -std::optional readHeadCached(const std::string& actualUrl) +std::optional readHeadCached(const std::string & actualUrl) { // Create a cache path to store the branch of the HEAD ref. Append something // in front of the URL to prevent collision with the repository itself. @@ -108,10 +115,10 @@ std::optional readHeadCached(const std::string& actualUrl) std::optional cachedRef; if (stat(headRefFile.c_str(), &st) == 0) { cachedRef = readHead(cacheDir); - if (cachedRef != std::nullopt && - *cachedRef != gitInitialBranch && + if (cachedRef != std::nullopt && *cachedRef != gitInitialBranch && isCacheFileWithinTtl(now, st)) { - debug("using cached HEAD ref '%s' for repo '%s'", *cachedRef, actualUrl); + debug("using cached HEAD ref '%s' for repo '%s'", *cachedRef, + actualUrl); return cachedRef; } } @@ -122,11 +129,13 @@ std::optional readHeadCached(const std::string& actualUrl) } if (cachedRef) { - // If the cached git ref is expired in fetch() below, and the 'git fetch' - // fails, it falls back to continuing with the most recent version. - // This function must behave the same way, so we return the expired - // cached ref here. - warn("could not get HEAD ref for repository '%s'; using expired cached ref '%s'", actualUrl, *cachedRef); + // If the cached git ref is expired in fetch() below, and the 'git + // fetch' fails, it falls back to continuing with the most recent + // version. This function must behave the same way, so we return the + // expired cached ref here. + warn("could not get HEAD ref for repository '%s'; using expired cached " + "ref '%s'", + actualUrl, *cachedRef); return *cachedRef; } @@ -138,8 +147,7 @@ bool isNotDotGitDirectory(const Path & path) return baseNameOf(path) != ".git"; } -struct WorkdirInfo -{ +struct WorkdirInfo { bool clean = false; bool hasHead = false; }; @@ -147,33 +155,38 @@ struct WorkdirInfo // Returns whether a git workdir is clean and has commits. WorkdirInfo getWorkdirInfo(const Input & input, const Path & workdir) { - const bool submodules = maybeGetBoolAttr(input.attrs, "submodules").value_or(false); + const bool submodules = + maybeGetBoolAttr(input.attrs, "submodules").value_or(false); std::string gitDir(".git"); auto env = getEnv(); - // Set LC_ALL to C: because we rely on the error messages from git rev-parse to determine what went wrong - // that way unknown errors can lead to a failure instead of continuing through the wrong code path + // Set LC_ALL to C: because we rely on the error messages from git rev-parse + // to determine what went wrong that way unknown errors can lead to a + // failure instead of continuing through the wrong code path env["LC_ALL"] = "C"; /* Check whether HEAD points to something that looks like a commit, since that is the refrence we want to use later on. */ - auto result = runProgram(RunOptions { - .program = "git", - .args = { "-C", workdir, "--git-dir", gitDir, "rev-parse", "--verify", "--no-revs", "HEAD^{commit}" }, - .environment = env, - .mergeStderrToStdout = true - }); + auto result = runProgram( + RunOptions{.program = "git", + .args = {"-C", workdir, "--git-dir", gitDir, "rev-parse", + "--verify", "--no-revs", "HEAD^{commit}"}, + .environment = env, + .mergeStderrToStdout = true}); auto exitCode = WEXITSTATUS(result.first); auto errorMessage = result.second; if (errorMessage.find("fatal: not a git repository") != std::string::npos) { throw Error("'%s' is not a Git repository", workdir); - } else if (errorMessage.find("fatal: Needed a single revision") != std::string::npos) { + } else if (errorMessage.find("fatal: Needed a single revision") != + std::string::npos) { // indicates that the repo does not have any commits // we want to proceed and will consider it dirty later } else if (exitCode != 0) { // any other errors should lead to a failure - throw Error("getting the HEAD of the Git tree '%s' failed with exit code %d:\n%s", workdir, exitCode, errorMessage); + throw Error("getting the HEAD of the Git tree '%s' failed with exit " + "code %d:\n%s", + workdir, exitCode, errorMessage); } bool clean = false; @@ -182,8 +195,10 @@ WorkdirInfo getWorkdirInfo(const Input & input, const Path & workdir) try { if (hasHead) { // Using git diff is preferrable over lower-level operations here, - // because its conceptually simpler and we only need the exit code anyways. - auto gitDiffOpts = Strings({ "-C", workdir, "--git-dir", gitDir, "diff", "HEAD", "--quiet"}); + // because its conceptually simpler and we only need the exit code + // anyways. + auto gitDiffOpts = Strings({"-C", workdir, "--git-dir", gitDir, + "diff", "HEAD", "--quiet"}); if (!submodules) { // Changes in submodules should only make the tree dirty // when those submodules will be copied as well. @@ -195,15 +210,19 @@ WorkdirInfo getWorkdirInfo(const Input & input, const Path & workdir) clean = true; } } catch (ExecError & e) { - if (!WIFEXITED(e.status) || WEXITSTATUS(e.status) != 1) throw; + if (!WIFEXITED(e.status) || WEXITSTATUS(e.status) != 1) + throw; } - return WorkdirInfo { .clean = clean, .hasHead = hasHead }; + return WorkdirInfo{.clean = clean, .hasHead = hasHead}; } -std::pair fetchFromWorkdir(ref store, Input & input, const Path & workdir, const WorkdirInfo & workdirInfo) +std::pair fetchFromWorkdir(ref store, Input & input, + const Path & workdir, + const WorkdirInfo & workdirInfo) { - const bool submodules = maybeGetBoolAttr(input.attrs, "submodules").value_or(false); + const bool submodules = + maybeGetBoolAttr(input.attrs, "submodules").value_or(false); auto gitDir = ".git"; if (!fetchSettings.allowDirty) @@ -212,7 +231,8 @@ std::pair fetchFromWorkdir(ref store, Input & input, co if (fetchSettings.warnDirty) warn("Git tree '%s' is dirty", workdir); - auto gitOpts = Strings({ "-C", workdir, "--git-dir", gitDir, "ls-files", "-z" }); + auto gitOpts = + Strings({"-C", workdir, "--git-dir", gitDir, "ls-files", "-z"}); if (submodules) gitOpts.emplace_back("--recurse-submodules"); @@ -236,40 +256,46 @@ std::pair fetchFromWorkdir(ref store, Input & input, co return files.count(file); }; - auto storePath = store->addToStore(input.getName(), actualPath, FileIngestionMethod::Recursive, htSHA256, filter); + auto storePath = + store->addToStore(input.getName(), actualPath, + FileIngestionMethod::Recursive, htSHA256, filter); // FIXME: maybe we should use the timestamp of the last // modified dirty file? input.attrs.insert_or_assign( "lastModified", - workdirInfo.hasHead ? std::stoull(runProgram("git", true, { "-C", actualPath, "--git-dir", gitDir, "log", "-1", "--format=%ct", "--no-show-signature", "HEAD" })) : 0); + workdirInfo.hasHead + ? std::stoull(runProgram("git", true, + {"-C", actualPath, "--git-dir", gitDir, + "log", "-1", "--format=%ct", + "--no-show-signature", "HEAD"})) + : 0); return {std::move(storePath), input}; } -} // end namespace +} // end namespace -struct GitInputScheme : InputScheme -{ +struct GitInputScheme : InputScheme { std::optional inputFromURL(const ParsedURL & url) override { - if (url.scheme != "git" && - url.scheme != "git+http" && - url.scheme != "git+https" && - url.scheme != "git+ssh" && - url.scheme != "git+file") return {}; + if (url.scheme != "git" && url.scheme != "git+http" && + url.scheme != "git+https" && url.scheme != "git+ssh" && + url.scheme != "git+file") + return {}; auto url2(url); - if (hasPrefix(url2.scheme, "git+")) url2.scheme = std::string(url2.scheme, 4); + if (hasPrefix(url2.scheme, "git+")) + url2.scheme = std::string(url2.scheme, 4); url2.query.clear(); Attrs attrs; attrs.emplace("type", "git"); - for (auto &[name, value] : url.query) { + for (auto & [name, value] : url.query) { if (name == "rev" || name == "ref") attrs.emplace(name, value); else if (name == "shallow" || name == "submodules") - attrs.emplace(name, Explicit { value == "1" }); + attrs.emplace(name, Explicit{value == "1"}); else url2.query.emplace(name, value); } @@ -281,10 +307,14 @@ struct GitInputScheme : InputScheme std::optional inputFromAttrs(const Attrs & attrs) override { - if (maybeGetStrAttr(attrs, "type") != "git") return {}; + if (maybeGetStrAttr(attrs, "type") != "git") + return {}; for (auto & [name, value] : attrs) - if (name != "type" && name != "url" && name != "ref" && name != "rev" && name != "shallow" && name != "submodules" && name != "lastModified" && name != "revCount" && name != "narHash" && name != "allRefs" && name != "name") + if (name != "type" && name != "url" && name != "ref" && + name != "rev" && name != "shallow" && name != "submodules" && + name != "lastModified" && name != "revCount" && + name != "narHash" && name != "allRefs" && name != "name") throw Error("unsupported Git input attribute '%s'", name); parseURL(getStrAttr(attrs, "url")); @@ -305,9 +335,12 @@ struct GitInputScheme : InputScheme ParsedURL toURL(const Input & input) override { auto url = parseURL(getStrAttr(input.attrs, "url")); - if (url.scheme != "git") url.scheme = "git+" + url.scheme; - if (auto rev = input.getRev()) url.query.insert_or_assign("rev", rev->gitRev()); - if (auto ref = input.getRef()) url.query.insert_or_assign("ref", *ref); + if (url.scheme != "git") + url.scheme = "git+" + url.scheme; + if (auto rev = input.getRev()) + url.query.insert_or_assign("rev", rev->gitRev()); + if (auto ref = input.getRef()) + url.query.insert_or_assign("ref", *ref); if (maybeGetBoolAttr(input.attrs, "shallow").value_or(false)) url.query.insert_or_assign("shallow", "1"); return url; @@ -317,21 +350,23 @@ struct GitInputScheme : InputScheme { bool maybeDirty = !input.getRef(); bool shallow = maybeGetBoolAttr(input.attrs, "shallow").value_or(false); - return - maybeGetIntAttr(input.attrs, "lastModified") - && (shallow || maybeDirty || maybeGetIntAttr(input.attrs, "revCount")); + return maybeGetIntAttr(input.attrs, "lastModified") && + (shallow || maybeDirty || + maybeGetIntAttr(input.attrs, "revCount")); } - Input applyOverrides( - const Input & input, - std::optional ref, - std::optional rev) override + Input applyOverrides(const Input & input, std::optional ref, + std::optional rev) override { auto res(input); - if (rev) res.attrs.insert_or_assign("rev", rev->gitRev()); - if (ref) res.attrs.insert_or_assign("ref", *ref); + if (rev) + res.attrs.insert_or_assign("rev", rev->gitRev()); + if (ref) + res.attrs.insert_or_assign("ref", *ref); if (!res.getRef() && res.getRev()) - throw Error("Git input '%s' has a commit hash but no branch/tag name", res.to_string()); + throw Error( + "Git input '%s' has a commit hash but no branch/tag name", + res.to_string()); return res; } @@ -348,7 +383,9 @@ struct GitInputScheme : InputScheme args.push_back(*ref); } - if (input.getRev()) throw UnimplementedError("cloning a specific revision is not implemented"); + if (input.getRev()) + throw UnimplementedError( + "cloning a specific revision is not implemented"); args.push_back(destDir); @@ -363,34 +400,39 @@ struct GitInputScheme : InputScheme return {}; } - void markChangedFile(const Input & input, std::string_view file, std::optional commitMsg) override + void markChangedFile(const Input & input, std::string_view file, + std::optional commitMsg) override { auto sourcePath = getSourcePath(input); assert(sourcePath); auto gitDir = ".git"; runProgram("git", true, - { "-C", *sourcePath, "--git-dir", gitDir, "add", "--force", "--intent-to-add", "--", std::string(file) }); + {"-C", *sourcePath, "--git-dir", gitDir, "add", "--force", + "--intent-to-add", "--", std::string(file)}); if (commitMsg) runProgram("git", true, - { "-C", *sourcePath, "--git-dir", gitDir, "commit", std::string(file), "-m", *commitMsg }); + {"-C", *sourcePath, "--git-dir", gitDir, "commit", + std::string(file), "-m", *commitMsg}); } std::pair getActualUrl(const Input & input) const { // file:// URIs are normally not cloned (but otherwise treated the // same as remote URIs, i.e. we don't use the working tree or - // HEAD). Exception: If _NIX_FORCE_HTTP is set, or the repo is a bare git - // repo, treat as a remote URI to force a clone. + // HEAD). Exception: If _NIX_FORCE_HTTP is set, or the repo is a bare + // git repo, treat as a remote URI to force a clone. static bool forceHttp = getEnv("_NIX_FORCE_HTTP") == "1"; // for testing auto url = parseURL(getStrAttr(input.attrs, "url")); - bool isBareRepository = url.scheme == "file" && !pathExists(url.path + "/.git"); + bool isBareRepository = + url.scheme == "file" && !pathExists(url.path + "/.git"); bool isLocal = url.scheme == "file" && !forceHttp && !isBareRepository; return {isLocal, isLocal ? url.path : url.base}; } - std::pair fetch(ref store, const Input & _input) override + std::pair fetch(ref store, + const Input & _input) override { Input input(_input); auto gitDir = ".git"; @@ -398,22 +440,27 @@ struct GitInputScheme : InputScheme std::string name = input.getName(); bool shallow = maybeGetBoolAttr(input.attrs, "shallow").value_or(false); - bool submodules = maybeGetBoolAttr(input.attrs, "submodules").value_or(false); + bool submodules = + maybeGetBoolAttr(input.attrs, "submodules").value_or(false); bool allRefs = maybeGetBoolAttr(input.attrs, "allRefs").value_or(false); std::string cacheType = "git"; - if (shallow) cacheType += "-shallow"; - if (submodules) cacheType += "-submodules"; - if (allRefs) cacheType += "-all-refs"; - - auto checkHashType = [&](const std::optional & hash) - { - if (hash.has_value() && !(hash->type == htSHA1 || hash->type == htSHA256)) - throw Error("Hash '%s' is not supported by Git. Supported types are sha1 and sha256.", hash->to_string(Base16, true)); + if (shallow) + cacheType += "-shallow"; + if (submodules) + cacheType += "-submodules"; + if (allRefs) + cacheType += "-all-refs"; + + auto checkHashType = [&](const std::optional & hash) { + if (hash.has_value() && + !(hash->type == htSHA1 || hash->type == htSHA256)) + throw Error("Hash '%s' is not supported by Git. Supported " + "types are sha1 and sha256.", + hash->to_string(Base16, true)); }; - auto getLockedAttrs = [&]() - { + auto getLockedAttrs = [&]() { checkHashType(input.getRev()); return Attrs({ @@ -423,14 +470,16 @@ struct GitInputScheme : InputScheme }); }; - auto makeResult = [&](const Attrs & infoAttrs, StorePath && storePath) - -> std::pair - { + auto makeResult = + [&](const Attrs & infoAttrs, + StorePath && storePath) -> std::pair { assert(input.getRev()); assert(!_input.getRev() || _input.getRev() == input.getRev()); if (!shallow) - input.attrs.insert_or_assign("revCount", getIntAttr(infoAttrs, "revCount")); - input.attrs.insert_or_assign("lastModified", getIntAttr(infoAttrs, "lastModified")); + input.attrs.insert_or_assign("revCount", + getIntAttr(infoAttrs, "revCount")); + input.attrs.insert_or_assign("lastModified", + getIntAttr(infoAttrs, "lastModified")); return {std::move(storePath), input}; }; @@ -463,7 +512,9 @@ struct GitInputScheme : InputScheme if (!input.getRef()) { auto head = readHead(actualUrl); if (!head) { - warn("could not read HEAD ref from repo at '%s', using 'master'", actualUrl); + warn("could not read HEAD ref from repo at '%s', using " + "'master'", + actualUrl); head = "master"; } input.attrs.insert_or_assign("ref", *head); @@ -471,8 +522,14 @@ struct GitInputScheme : InputScheme } if (!input.getRev()) - input.attrs.insert_or_assign("rev", - Hash::parseAny(chomp(runProgram("git", true, { "-C", actualUrl, "--git-dir", gitDir, "rev-parse", *input.getRef() })), htSHA1).gitRev()); + input.attrs.insert_or_assign( + "rev", + Hash::parseAny( + chomp(runProgram("git", true, + {"-C", actualUrl, "--git-dir", gitDir, + "rev-parse", *input.getRef()})), + htSHA1) + .gitRev()); repoDir = actualUrl; } else { @@ -480,7 +537,9 @@ struct GitInputScheme : InputScheme if (useHeadRef) { auto head = readHeadCached(actualUrl); if (!head) { - warn("could not read HEAD ref from repo at '%s', using 'master'", actualUrl); + warn("could not read HEAD ref from repo at '%s', using " + "'master'", + actualUrl); head = "master"; } input.attrs.insert_or_assign("ref", *head); @@ -488,7 +547,8 @@ struct GitInputScheme : InputScheme } if (auto res = getCache()->lookup(store, unlockedAttrs)) { - auto rev2 = Hash::parseAny(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)); @@ -503,13 +563,15 @@ struct GitInputScheme : InputScheme PathLocks cacheDirLock({cacheDir + ".lock"}); if (!pathExists(cacheDir)) { - runProgram("git", true, { "-c", "init.defaultBranch=" + gitInitialBranch, "init", "--bare", repoDir }); + runProgram("git", true, + {"-c", "init.defaultBranch=" + gitInitialBranch, + "init", "--bare", repoDir}); } Path localRefFile = input.getRef()->compare(0, 5, "refs/") == 0 - ? cacheDir + "/" + *input.getRef() - : cacheDir + "/refs/heads/" + *input.getRef(); + ? cacheDir + "/" + *input.getRef() + : cacheDir + "/refs/heads/" + *input.getRef(); bool doFetch; time_t now = time(0); @@ -518,7 +580,9 @@ struct GitInputScheme : InputScheme repo. */ if (input.getRev()) { try { - runProgram("git", true, { "-C", repoDir, "--git-dir", gitDir, "cat-file", "-e", input.getRev()->gitRev() }); + runProgram("git", true, + {"-C", repoDir, "--git-dir", gitDir, "cat-file", + "-e", input.getRev()->gitRev()}); doFetch = false; } catch (ExecError & e) { if (WIFEXITED(e.status)) { @@ -531,54 +595,69 @@ struct GitInputScheme : InputScheme if (allRefs) { doFetch = true; } else { - /* If the local ref is older than ‘tarball-ttl’ seconds, do a - git fetch to update the local ref to the remote ref. */ + /* If the local ref is older than ‘tarball-ttl’ seconds, do + a git fetch to update the local ref to the remote ref. */ struct stat st; doFetch = stat(localRefFile.c_str(), &st) != 0 || - !isCacheFileWithinTtl(now, st); + !isCacheFileWithinTtl(now, st); } } if (doFetch) { - Activity act(*logger, lvlTalkative, actUnknown, fmt("fetching Git repository '%s'", actualUrl)); + Activity act(*logger, lvlTalkative, actUnknown, + fmt("fetching Git repository '%s'", actualUrl)); // FIXME: git stderr messes up our progress indicator, so // we're using --quiet for now. Should process its stderr. try { auto ref = input.getRef(); - auto fetchRef = allRefs - ? "refs/*" - : ref->compare(0, 5, "refs/") == 0 - ? *ref - : ref == "HEAD" - ? *ref - : "refs/heads/" + *ref; - runProgram("git", true, { "-C", repoDir, "--git-dir", gitDir, "fetch", "--quiet", "--force", "--", actualUrl, fmt("%s:%s", fetchRef, fetchRef) }); + auto fetchRef = allRefs ? "refs/*" + : ref->compare(0, 5, "refs/") == 0 ? *ref + : ref == "HEAD" ? *ref + : "refs/heads/" + *ref; + runProgram("git", true, + {"-C", repoDir, "--git-dir", gitDir, "fetch", + "--quiet", "--force", "--", actualUrl, + fmt("%s:%s", fetchRef, fetchRef)}); } catch (Error & e) { - if (!pathExists(localRefFile)) throw; - warn("could not update local clone of Git repository '%s'; continuing with the most recent version", actualUrl); + if (!pathExists(localRefFile)) + throw; + warn("could not update local clone of Git repository '%s'; " + "continuing with the most recent version", + actualUrl); } if (!touchCacheFile(localRefFile, now)) - warn("could not update mtime for file '%s': %s", localRefFile, strerror(errno)); + warn("could not update mtime for file '%s': %s", + localRefFile, strerror(errno)); if (useHeadRef && !storeCachedHead(actualUrl, *input.getRef())) - warn("could not update cached head '%s' for '%s'", *input.getRef(), actualUrl); + warn("could not update cached head '%s' for '%s'", + *input.getRef(), actualUrl); } if (!input.getRev()) - input.attrs.insert_or_assign("rev", Hash::parseAny(chomp(readFile(localRefFile)), htSHA1).gitRev()); + input.attrs.insert_or_assign( + "rev", Hash::parseAny(chomp(readFile(localRefFile)), htSHA1) + .gitRev()); - // cache dir lock is removed at scope end; we will only use read-only operations on specific revisions in the remainder + // cache dir lock is removed at scope end; we will only use + // read-only operations on specific revisions in the remainder } - bool isShallow = chomp(runProgram("git", true, { "-C", repoDir, "--git-dir", gitDir, "rev-parse", "--is-shallow-repository" })) == "true"; + bool isShallow = + chomp(runProgram("git", true, + {"-C", repoDir, "--git-dir", gitDir, "rev-parse", + "--is-shallow-repository"})) == "true"; if (isShallow && !shallow) - throw Error("'%s' is a shallow Git repository, but a non-shallow repository is needed", actualUrl); + throw Error("'%s' is a shallow Git repository, but a non-shallow " + "repository is needed", + actualUrl); // FIXME: check whether rev is an ancestor of ref. - printTalkative("using revision %s of repo '%s'", input.getRev()->gitRev(), actualUrl); + printTalkative("using revision %s of repo '%s'", + input.getRev()->gitRev(), actualUrl); /* Now that we know the ref, check again whether we have it in the store. */ @@ -589,58 +668,67 @@ struct GitInputScheme : InputScheme AutoDelete delTmpDir(tmpDir, true); PathFilter filter = defaultPathFilter; - auto result = runProgram(RunOptions { - .program = "git", - .args = { "-C", repoDir, "--git-dir", gitDir, "cat-file", "commit", input.getRev()->gitRev() }, - .mergeStderrToStdout = true - }); - if (WEXITSTATUS(result.first) == 128 - && result.second.find("bad file") != std::string::npos) - { + auto result = runProgram( + RunOptions{.program = "git", + .args = {"-C", repoDir, "--git-dir", gitDir, "cat-file", + "commit", input.getRev()->gitRev()}, + .mergeStderrToStdout = true}); + if (WEXITSTATUS(result.first) == 128 && + result.second.find("bad file") != std::string::npos) { throw Error( "Cannot find Git revision '%s' in ref '%s' of repository '%s'! " - "Please make sure that the " ANSI_BOLD "rev" ANSI_NORMAL " exists on the " - ANSI_BOLD "ref" ANSI_NORMAL " you've specified or add " ANSI_BOLD - "allRefs = true;" ANSI_NORMAL " to " ANSI_BOLD "fetchGit" ANSI_NORMAL ".", - input.getRev()->gitRev(), - *input.getRef(), - actualUrl - ); + "Please make sure that the " ANSI_BOLD "rev" ANSI_NORMAL + " exists on the " ANSI_BOLD "ref" ANSI_NORMAL + " you've specified or add " ANSI_BOLD + "allRefs = true;" ANSI_NORMAL " to " ANSI_BOLD + "fetchGit" ANSI_NORMAL ".", + input.getRev()->gitRev(), *input.getRef(), actualUrl); } if (submodules) { Path tmpGitDir = createTempDir(); AutoDelete delTmpGitDir(tmpGitDir, true); - runProgram("git", true, { "-c", "init.defaultBranch=" + gitInitialBranch, "init", tmpDir, "--separate-git-dir", tmpGitDir }); + runProgram("git", true, + {"-c", "init.defaultBranch=" + gitInitialBranch, "init", + tmpDir, "--separate-git-dir", tmpGitDir}); // TODO: repoDir might lack the ref (it only checks if rev // exists, see FIXME above) so use a big hammer and fetch // everything to ensure we get the rev. - runProgram("git", true, { "-C", tmpDir, "fetch", "--quiet", "--force", - "--update-head-ok", "--", repoDir, "refs/*:refs/*" }); + runProgram("git", true, + {"-C", tmpDir, "fetch", "--quiet", "--force", + "--update-head-ok", "--", repoDir, "refs/*:refs/*"}); - runProgram("git", true, { "-C", tmpDir, "checkout", "--quiet", input.getRev()->gitRev() }); - runProgram("git", true, { "-C", tmpDir, "remote", "add", "origin", actualUrl }); - runProgram("git", true, { "-C", tmpDir, "submodule", "--quiet", "update", "--init", "--recursive" }); + runProgram("git", true, + {"-C", tmpDir, "checkout", "--quiet", + input.getRev()->gitRev()}); + runProgram("git", true, + {"-C", tmpDir, "remote", "add", "origin", actualUrl}); + runProgram("git", true, + {"-C", tmpDir, "submodule", "--quiet", "update", + "--init", "--recursive"}); filter = isNotDotGitDirectory; } else { // FIXME: should pipe this, or find some better way to extract a // revision. auto source = sinkToSource([&](Sink & sink) { - runProgram2({ - .program = "git", - .args = { "-C", repoDir, "--git-dir", gitDir, "archive", input.getRev()->gitRev() }, - .standardOut = &sink - }); + runProgram2({.program = "git", + .args = {"-C", repoDir, "--git-dir", gitDir, + "archive", input.getRev()->gitRev()}, + .standardOut = &sink}); }); unpackTarfile(*source, tmpDir); } - auto storePath = store->addToStore(name, tmpDir, FileIngestionMethod::Recursive, htSHA256, filter); + auto storePath = store->addToStore( + name, tmpDir, FileIngestionMethod::Recursive, htSHA256, filter); - auto lastModified = std::stoull(runProgram("git", true, { "-C", repoDir, "--git-dir", gitDir, "log", "-1", "--format=%ct", "--no-show-signature", input.getRev()->gitRev() })); + auto lastModified = std::stoull(runProgram( + "git", true, + {"-C", repoDir, "--git-dir", gitDir, "log", "-1", "--format=%ct", + "--no-show-signature", input.getRev()->gitRev()})); Attrs infoAttrs({ {"rev", input.getRev()->gitRev()}, @@ -648,28 +736,22 @@ struct GitInputScheme : InputScheme }); if (!shallow) - infoAttrs.insert_or_assign("revCount", - std::stoull(runProgram("git", true, { "-C", repoDir, "--git-dir", gitDir, "rev-list", "--count", input.getRev()->gitRev() }))); + infoAttrs.insert_or_assign( + "revCount", std::stoull(runProgram( + "git", true, + {"-C", repoDir, "--git-dir", gitDir, "rev-list", + "--count", input.getRev()->gitRev()}))); if (!_input.getRev()) - getCache()->add( - store, - unlockedAttrs, - infoAttrs, - storePath, - false); - - getCache()->add( - store, - getLockedAttrs(), - infoAttrs, - storePath, - true); + getCache()->add(store, unlockedAttrs, infoAttrs, storePath, false); + + getCache()->add(store, getLockedAttrs(), infoAttrs, storePath, true); return makeResult(infoAttrs, std::move(storePath)); } }; -static auto rGitInputScheme = OnStartup([] { registerInputScheme(std::make_unique()); }); +static auto rGitInputScheme = + OnStartup([] { registerInputScheme(std::make_unique()); }); } diff --git a/src/libfetchers/github.cc b/src/libfetchers/github.cc index a491d82a67a7..332d31f993a5 100644 --- a/src/libfetchers/github.cc +++ b/src/libfetchers/github.cc @@ -14,8 +14,7 @@ namespace nix::fetchers { -struct DownloadUrl -{ +struct DownloadUrl { std::string url; Headers headers; }; @@ -24,15 +23,16 @@ struct DownloadUrl const static std::string hostRegexS = "[a-zA-Z0-9.]*"; // FIXME: check std::regex hostRegex(hostRegexS, std::regex::ECMAScript); -struct GitArchiveInputScheme : InputScheme -{ +struct GitArchiveInputScheme : InputScheme { virtual std::string type() = 0; - virtual std::optional> accessHeaderFromToken(const std::string & token) const = 0; + virtual std::optional> + accessHeaderFromToken(const std::string & token) const = 0; std::optional inputFromURL(const ParsedURL & url) override { - if (url.scheme != type()) return {}; + if (url.scheme != type()) + return {}; auto path = tokenizeString>(url.path, "/"); @@ -47,7 +47,9 @@ struct GitArchiveInputScheme : InputScheme else if (std::regex_match(path[2], refRegex)) ref = path[2]; else - throw BadURL("in URL '%s', '%s' is not a commit hash or branch/tag name", url.url, path[2]); + throw BadURL( + "in URL '%s', '%s' is not a commit hash or branch/tag name", + url.url, path[2]); } else if (size > 3) { std::string rs; for (auto i = std::next(path.begin(), 2); i != path.end(); i++) { @@ -60,52 +62,63 @@ struct GitArchiveInputScheme : InputScheme if (std::regex_match(rs, refRegex)) { ref = rs; } else { - throw BadURL("in URL '%s', '%s' is not a branch/tag name", url.url, rs); + throw BadURL("in URL '%s', '%s' is not a branch/tag name", + url.url, rs); } } else if (size < 2) throw BadURL("URL '%s' is invalid", url.url); - for (auto &[name, value] : url.query) { + for (auto & [name, value] : url.query) { if (name == "rev") { if (rev) - throw BadURL("URL '%s' contains multiple commit hashes", url.url); + throw BadURL("URL '%s' contains multiple commit hashes", + url.url); rev = Hash::parseAny(value, htSHA1); - } - else if (name == "ref") { + } else if (name == "ref") { if (!std::regex_match(value, refRegex)) - throw BadURL("URL '%s' contains an invalid branch/tag name", url.url); + throw BadURL("URL '%s' contains an invalid branch/tag name", + url.url); if (ref) - throw BadURL("URL '%s' contains multiple branch/tag names", url.url); + throw BadURL("URL '%s' contains multiple branch/tag names", + url.url); ref = value; - } - else if (name == "host") { + } else if (name == "host") { if (!std::regex_match(value, hostRegex)) - throw BadURL("URL '%s' contains an invalid instance host", url.url); + throw BadURL("URL '%s' contains an invalid instance host", + url.url); host_url = value; } // FIXME: barf on unsupported attributes } if (ref && rev) - throw BadURL("URL '%s' contains both a commit hash and a branch/tag name %s %s", url.url, *ref, rev->gitRev()); + throw BadURL("URL '%s' contains both a commit hash and a " + "branch/tag name %s %s", + url.url, *ref, rev->gitRev()); Input input; input.attrs.insert_or_assign("type", type()); input.attrs.insert_or_assign("owner", path[0]); input.attrs.insert_or_assign("repo", path[1]); - if (rev) input.attrs.insert_or_assign("rev", rev->gitRev()); - if (ref) input.attrs.insert_or_assign("ref", *ref); - if (host_url) input.attrs.insert_or_assign("host", *host_url); + if (rev) + input.attrs.insert_or_assign("rev", rev->gitRev()); + if (ref) + input.attrs.insert_or_assign("ref", *ref); + if (host_url) + input.attrs.insert_or_assign("host", *host_url); return input; } std::optional inputFromAttrs(const Attrs & attrs) override { - if (maybeGetStrAttr(attrs, "type") != type()) return {}; + if (maybeGetStrAttr(attrs, "type") != type()) + return {}; for (auto & [name, value] : attrs) - if (name != "type" && name != "owner" && name != "repo" && name != "ref" && name != "rev" && name != "narHash" && name != "lastModified" && name != "host") + if (name != "type" && name != "owner" && name != "repo" && + name != "ref" && name != "rev" && name != "narHash" && + name != "lastModified" && name != "host") throw Error("unsupported input attribute '%s'", name); getStrAttr(attrs, "owner"); @@ -124,9 +137,11 @@ struct GitArchiveInputScheme : InputScheme auto rev = input.getRev(); auto path = owner + "/" + repo; assert(!(ref && rev)); - if (ref) path += "/" + *ref; - if (rev) path += "/" + rev->to_string(Base16, false); - return ParsedURL { + if (ref) + path += "/" + *ref; + if (rev) + path += "/" + rev->to_string(Base16, false); + return ParsedURL{ .scheme = type(), .path = path, }; @@ -137,15 +152,14 @@ struct GitArchiveInputScheme : InputScheme return input.getRev() && maybeGetIntAttr(input.attrs, "lastModified"); } - Input applyOverrides( - const Input & _input, - std::optional ref, - std::optional rev) override + Input applyOverrides(const Input & _input, std::optional ref, + std::optional rev) override { auto input(_input); if (rev && ref) - throw BadURL("cannot apply both a commit hash (%s) and a branch/tag name ('%s') to input '%s'", - rev->gitRev(), *ref, input.to_string()); + throw BadURL("cannot apply both a commit hash (%s) and a " + "branch/tag name ('%s') to input '%s'", + rev->gitRev(), *ref, input.to_string()); if (rev) { input.attrs.insert_or_assign("rev", rev->gitRev()); input.attrs.erase("ref"); @@ -179,18 +193,22 @@ struct GitArchiveInputScheme : InputScheme return headers; } - virtual Hash getRevFromRef(nix::ref store, const Input & input) const = 0; + virtual Hash getRevFromRef(nix::ref store, + const Input & input) const = 0; virtual DownloadUrl getDownloadUrl(const Input & input) const = 0; - std::pair fetch(ref store, const Input & _input) override + std::pair fetch(ref store, + const Input & _input) override { Input input(_input); - if (!maybeGetStrAttr(input.attrs, "ref")) input.attrs.insert_or_assign("ref", "HEAD"); + if (!maybeGetStrAttr(input.attrs, "ref")) + input.attrs.insert_or_assign("ref", "HEAD"); auto rev = input.getRev(); - if (!rev) rev = getRevFromRef(store, input); + if (!rev) + rev = getRevFromRef(store, input); input.attrs.erase("ref"); input.attrs.insert_or_assign("rev", rev->gitRev()); @@ -201,35 +219,32 @@ struct GitArchiveInputScheme : InputScheme }); if (auto res = getCache()->lookup(store, lockedAttrs)) { - input.attrs.insert_or_assign("lastModified", getIntAttr(res->first, "lastModified")); + input.attrs.insert_or_assign( + "lastModified", getIntAttr(res->first, "lastModified")); return {std::move(res->second), input}; } auto url = getDownloadUrl(input); - auto [tree, lastModified] = downloadTarball(store, url.url, input.getName(), true, url.headers); + auto [tree, lastModified] = + downloadTarball(store, url.url, input.getName(), true, url.headers); input.attrs.insert_or_assign("lastModified", uint64_t(lastModified)); getCache()->add( - store, - lockedAttrs, - { - {"rev", rev->gitRev()}, - {"lastModified", uint64_t(lastModified)} - }, - tree.storePath, - true); + store, lockedAttrs, + {{"rev", rev->gitRev()}, {"lastModified", uint64_t(lastModified)}}, + tree.storePath, true); return {std::move(tree.storePath), input}; } }; -struct GitHubInputScheme : GitArchiveInputScheme -{ +struct GitHubInputScheme : GitArchiveInputScheme { std::string type() override { return "github"; } - std::optional> accessHeaderFromToken(const std::string & token) const override + std::optional> + accessHeaderFromToken(const std::string & token) const override { // Github supports PAT/OAuth2 tokens and HTTP Basic // Authentication. The former simply specifies the token, the @@ -237,25 +252,25 @@ struct GitHubInputScheme : GitArchiveInputScheme // is used here. See // https://developer.github.com/v3/#authentication and // https://docs.github.com/en/developers/apps/authorizing-oath-apps - return std::pair("Authorization", fmt("token %s", token)); + return std::pair("Authorization", + fmt("token %s", token)); } - Hash getRevFromRef(nix::ref store, const Input & input) const override + Hash getRevFromRef(nix::ref store, + const Input & input) const override { auto host = maybeGetStrAttr(input.attrs, "host").value_or("github.com"); - auto url = fmt( - host == "github.com" - ? "https://api.%s/repos/%s/%s/commits/%s" - : "https://%s/api/v3/repos/%s/%s/commits/%s", - host, getStrAttr(input.attrs, "owner"), getStrAttr(input.attrs, "repo"), *input.getRef()); + auto url = fmt(host == "github.com" + ? "https://api.%s/repos/%s/%s/commits/%s" + : "https://%s/api/v3/repos/%s/%s/commits/%s", + host, getStrAttr(input.attrs, "owner"), + getStrAttr(input.attrs, "repo"), *input.getRef()); Headers headers = makeHeadersWithAuthTokens(host); - auto json = nlohmann::json::parse( - readFile( - store->toRealPath( - downloadFile(store, url, "source", false, headers).storePath))); - auto rev = Hash::parseAny(std::string { json["sha"] }, htSHA1); + auto json = nlohmann::json::parse(readFile(store->toRealPath( + downloadFile(store, url, "source", false, headers).storePath))); + auto rev = Hash::parseAny(std::string{json["sha"]}, htSHA1); debug("HEAD revision for '%s' is %s", url, rev.gitRev()); return rev; } @@ -265,32 +280,33 @@ struct GitHubInputScheme : GitArchiveInputScheme // FIXME: use regular /archive URLs instead? api.github.com // might have stricter rate limits. auto host = maybeGetStrAttr(input.attrs, "host").value_or("github.com"); - auto url = fmt( - host == "github.com" - ? "https://api.%s/repos/%s/%s/tarball/%s" - : "https://%s/api/v3/repos/%s/%s/tarball/%s", - host, getStrAttr(input.attrs, "owner"), getStrAttr(input.attrs, "repo"), - input.getRev()->to_string(Base16, false)); + auto url = fmt(host == "github.com" + ? "https://api.%s/repos/%s/%s/tarball/%s" + : "https://%s/api/v3/repos/%s/%s/tarball/%s", + host, getStrAttr(input.attrs, "owner"), + getStrAttr(input.attrs, "repo"), + input.getRev()->to_string(Base16, false)); Headers headers = makeHeadersWithAuthTokens(host); - return DownloadUrl { url, headers }; + return DownloadUrl{url, headers}; } void clone(const Input & input, const Path & destDir) override { auto host = maybeGetStrAttr(input.attrs, "host").value_or("github.com"); - Input::fromURL(fmt("git+https://%s/%s/%s.git", - host, getStrAttr(input.attrs, "owner"), getStrAttr(input.attrs, "repo"))) + Input::fromURL(fmt("git+https://%s/%s/%s.git", host, + getStrAttr(input.attrs, "owner"), + getStrAttr(input.attrs, "repo"))) .applyOverrides(input.getRef(), input.getRev()) .clone(destDir); } }; -struct GitLabInputScheme : GitArchiveInputScheme -{ +struct GitLabInputScheme : GitArchiveInputScheme { std::string type() override { return "gitlab"; } - std::optional> accessHeaderFromToken(const std::string & token) const override + std::optional> + accessHeaderFromToken(const std::string & token) const override { // Gitlab supports 4 kinds of authorization, two of which are // relevant here: OAuth2 and PAT (Private Access Token). The @@ -302,26 +318,29 @@ struct GitLabInputScheme : GitArchiveInputScheme auto fldsplit = token.find_first_of(':'); // n.b. C++20 would allow: if (token.starts_with("OAuth2:")) ... if ("OAuth2" == token.substr(0, fldsplit)) - return std::make_pair("Authorization", fmt("Bearer %s", token.substr(fldsplit+1))); + return std::make_pair("Authorization", + fmt("Bearer %s", token.substr(fldsplit + 1))); if ("PAT" == token.substr(0, fldsplit)) - return std::make_pair("Private-token", token.substr(fldsplit+1)); - warn("Unrecognized GitLab token type %s", token.substr(0, fldsplit)); - return std::make_pair(token.substr(0,fldsplit), token.substr(fldsplit+1)); + return std::make_pair("Private-token", token.substr(fldsplit + 1)); + warn("Unrecognized GitLab token type %s", token.substr(0, fldsplit)); + return std::make_pair(token.substr(0, fldsplit), + token.substr(fldsplit + 1)); } - Hash getRevFromRef(nix::ref store, const Input & input) const override + Hash getRevFromRef(nix::ref store, + const Input & input) const override { auto host = maybeGetStrAttr(input.attrs, "host").value_or("gitlab.com"); // See rate limiting note below - auto url = fmt("https://%s/api/v4/projects/%s%%2F%s/repository/commits?ref_name=%s", - host, getStrAttr(input.attrs, "owner"), getStrAttr(input.attrs, "repo"), *input.getRef()); + auto url = fmt("https://%s/api/v4/projects/%s%%2F%s/repository/" + "commits?ref_name=%s", + host, getStrAttr(input.attrs, "owner"), + getStrAttr(input.attrs, "repo"), *input.getRef()); Headers headers = makeHeadersWithAuthTokens(host); - auto json = nlohmann::json::parse( - readFile( - store->toRealPath( - downloadFile(store, url, "source", false, headers).storePath))); + auto json = nlohmann::json::parse(readFile(store->toRealPath( + downloadFile(store, url, "source", false, headers).storePath))); auto rev = Hash::parseAny(std::string(json[0]["id"]), htSHA1); debug("HEAD revision for '%s' is %s", url, rev.gitRev()); return rev; @@ -335,63 +354,73 @@ struct GitLabInputScheme : GitArchiveInputScheme // is 10 reqs/sec/ip-addr. See // https://docs.gitlab.com/ee/user/gitlab_com/index.html#gitlabcom-specific-rate-limits auto host = maybeGetStrAttr(input.attrs, "host").value_or("gitlab.com"); - auto url = fmt("https://%s/api/v4/projects/%s%%2F%s/repository/archive.tar.gz?sha=%s", - host, getStrAttr(input.attrs, "owner"), getStrAttr(input.attrs, "repo"), - input.getRev()->to_string(Base16, false)); + auto url = fmt("https://%s/api/v4/projects/%s%%2F%s/repository/" + "archive.tar.gz?sha=%s", + host, getStrAttr(input.attrs, "owner"), + getStrAttr(input.attrs, "repo"), + input.getRev()->to_string(Base16, false)); Headers headers = makeHeadersWithAuthTokens(host); - return DownloadUrl { url, headers }; + return DownloadUrl{url, headers}; } void clone(const Input & input, const Path & destDir) override { auto host = maybeGetStrAttr(input.attrs, "host").value_or("gitlab.com"); // FIXME: get username somewhere - Input::fromURL(fmt("git+https://%s/%s/%s.git", - host, getStrAttr(input.attrs, "owner"), getStrAttr(input.attrs, "repo"))) + Input::fromURL(fmt("git+https://%s/%s/%s.git", host, + getStrAttr(input.attrs, "owner"), + getStrAttr(input.attrs, "repo"))) .applyOverrides(input.getRef(), input.getRev()) .clone(destDir); } }; -struct SourceHutInputScheme : GitArchiveInputScheme -{ +struct SourceHutInputScheme : GitArchiveInputScheme { std::string type() override { return "sourcehut"; } - std::optional> accessHeaderFromToken(const std::string & token) const override + std::optional> + accessHeaderFromToken(const std::string & token) const override { // SourceHut supports both PAT and OAuth2. See // https://man.sr.ht/meta.sr.ht/oauth.md - return std::pair("Authorization", fmt("Bearer %s", token)); + return std::pair("Authorization", + fmt("Bearer %s", token)); // Note: This currently serves no purpose, as this kind of authorization // does not allow for downloading tarballs on sourcehut private repos. // Once it is implemented, however, should work as expected. } - Hash getRevFromRef(nix::ref store, const Input & input) const override + Hash getRevFromRef(nix::ref store, + const Input & input) const override { - // TODO: In the future, when the sourcehut graphql API is implemented for mercurial - // and with anonymous access, this method should use it instead. + // TODO: In the future, when the sourcehut graphql API is implemented + // for mercurial and with anonymous access, this method should use it + // instead. auto ref = *input.getRef(); auto host = maybeGetStrAttr(input.attrs, "host").value_or("git.sr.ht"); - auto base_url = fmt("https://%s/%s/%s", - host, getStrAttr(input.attrs, "owner"), getStrAttr(input.attrs, "repo")); + auto base_url = + fmt("https://%s/%s/%s", host, getStrAttr(input.attrs, "owner"), + getStrAttr(input.attrs, "repo")); Headers headers = makeHeadersWithAuthTokens(host); std::string refUri; if (ref == "HEAD") { - auto file = store->toRealPath( - downloadFile(store, fmt("%s/HEAD", base_url), "source", false, headers).storePath); + auto file = + store->toRealPath(downloadFile(store, fmt("%s/HEAD", base_url), + "source", false, headers) + .storePath); std::ifstream is(file); std::string line; getline(is, line); auto remoteLine = git::parseLsRemoteLine(line); if (!remoteLine) { - throw BadURL("in '%d', couldn't resolve HEAD ref '%d'", input.to_string(), ref); + throw BadURL("in '%d', couldn't resolve HEAD ref '%d'", + input.to_string(), ref); } refUri = remoteLine->target; } else { @@ -399,49 +428,59 @@ struct SourceHutInputScheme : GitArchiveInputScheme } std::regex refRegex(refUri); - auto file = store->toRealPath( - downloadFile(store, fmt("%s/info/refs", base_url), "source", false, headers).storePath); + auto file = + store->toRealPath(downloadFile(store, fmt("%s/info/refs", base_url), + "source", false, headers) + .storePath); std::ifstream is(file); std::string line; std::optional id; - while(!id && getline(is, line)) { + while (!id && getline(is, line)) { auto parsedLine = git::parseLsRemoteLine(line); - if (parsedLine && parsedLine->reference && std::regex_match(*parsedLine->reference, refRegex)) + if (parsedLine && parsedLine->reference && + std::regex_match(*parsedLine->reference, refRegex)) id = parsedLine->target; } - if(!id) - throw BadURL("in '%d', couldn't find ref '%d'", input.to_string(), ref); + if (!id) + throw BadURL("in '%d', couldn't find ref '%d'", input.to_string(), + ref); auto rev = Hash::parseAny(*id, htSHA1); - debug("HEAD revision for '%s' is %s", fmt("%s/%s", base_url, ref), rev.gitRev()); + debug("HEAD revision for '%s' is %s", fmt("%s/%s", base_url, ref), + rev.gitRev()); return rev; } DownloadUrl getDownloadUrl(const Input & input) const override { auto host = maybeGetStrAttr(input.attrs, "host").value_or("git.sr.ht"); - auto url = fmt("https://%s/%s/%s/archive/%s.tar.gz", - host, getStrAttr(input.attrs, "owner"), getStrAttr(input.attrs, "repo"), - input.getRev()->to_string(Base16, false)); + auto url = fmt("https://%s/%s/%s/archive/%s.tar.gz", host, + getStrAttr(input.attrs, "owner"), + getStrAttr(input.attrs, "repo"), + input.getRev()->to_string(Base16, false)); Headers headers = makeHeadersWithAuthTokens(host); - return DownloadUrl { url, headers }; + return DownloadUrl{url, headers}; } void clone(const Input & input, const Path & destDir) override { auto host = maybeGetStrAttr(input.attrs, "host").value_or("git.sr.ht"); - Input::fromURL(fmt("git+https://%s/%s/%s", - host, getStrAttr(input.attrs, "owner"), getStrAttr(input.attrs, "repo"))) + Input::fromURL(fmt("git+https://%s/%s/%s", host, + getStrAttr(input.attrs, "owner"), + getStrAttr(input.attrs, "repo"))) .applyOverrides(input.getRef(), input.getRev()) .clone(destDir); } }; -static auto rGitHubInputScheme = OnStartup([] { registerInputScheme(std::make_unique()); }); -static auto rGitLabInputScheme = OnStartup([] { registerInputScheme(std::make_unique()); }); -static auto rSourceHutInputScheme = OnStartup([] { registerInputScheme(std::make_unique()); }); +static auto rGitHubInputScheme = OnStartup( + [] { registerInputScheme(std::make_unique()); }); +static auto rGitLabInputScheme = OnStartup( + [] { registerInputScheme(std::make_unique()); }); +static auto rSourceHutInputScheme = OnStartup( + [] { registerInputScheme(std::make_unique()); }); } diff --git a/src/libfetchers/indirect.cc b/src/libfetchers/indirect.cc index 9288fc6cfcf5..6dc5245db054 100644 --- a/src/libfetchers/indirect.cc +++ b/src/libfetchers/indirect.cc @@ -5,11 +5,11 @@ namespace nix::fetchers { std::regex flakeRegex("[a-zA-Z][a-zA-Z0-9_-]*", std::regex::ECMAScript); -struct IndirectInputScheme : InputScheme -{ +struct IndirectInputScheme : InputScheme { std::optional inputFromURL(const ParsedURL & url) override { - if (url.scheme != "flake") return {}; + if (url.scheme != "flake") + return {}; auto path = tokenizeString>(url.path, "/"); @@ -23,13 +23,17 @@ struct IndirectInputScheme : InputScheme else if (std::regex_match(path[1], refRegex)) ref = path[1]; else - throw BadURL("in flake URL '%s', '%s' is not a commit hash or branch/tag name", url.url, path[1]); + throw BadURL("in flake URL '%s', '%s' is not a commit hash or " + "branch/tag name", + url.url, path[1]); } else if (path.size() == 3) { if (!std::regex_match(path[1], refRegex)) - throw BadURL("in flake URL '%s', '%s' is not a branch/tag name", url.url, path[1]); + throw BadURL("in flake URL '%s', '%s' is not a branch/tag name", + url.url, path[1]); 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]); + throw BadURL("in flake URL '%s', '%s' is not a commit hash", + url.url, path[2]); rev = Hash::parseAny(path[2], htSHA1); } else throw BadURL("GitHub URL '%s' is invalid", url.url); @@ -44,18 +48,22 @@ struct IndirectInputScheme : InputScheme input.direct = false; input.attrs.insert_or_assign("type", "indirect"); input.attrs.insert_or_assign("id", id); - if (rev) input.attrs.insert_or_assign("rev", rev->gitRev()); - if (ref) input.attrs.insert_or_assign("ref", *ref); + if (rev) + input.attrs.insert_or_assign("rev", rev->gitRev()); + if (ref) + input.attrs.insert_or_assign("ref", *ref); return input; } std::optional inputFromAttrs(const Attrs & attrs) override { - if (maybeGetStrAttr(attrs, "type") != "indirect") return {}; + if (maybeGetStrAttr(attrs, "type") != "indirect") + return {}; for (auto & [name, value] : attrs) - if (name != "type" && name != "id" && name != "ref" && name != "rev" && name != "narHash") + if (name != "type" && name != "id" && name != "ref" && + name != "rev" && name != "narHash") throw Error("unsupported indirect input attribute '%s'", name); auto id = getStrAttr(attrs, "id"); @@ -73,33 +81,39 @@ struct IndirectInputScheme : InputScheme ParsedURL url; url.scheme = "flake"; url.path = getStrAttr(input.attrs, "id"); - if (auto ref = input.getRef()) { url.path += '/'; url.path += *ref; }; - if (auto rev = input.getRev()) { url.path += '/'; url.path += rev->gitRev(); }; + if (auto ref = input.getRef()) { + url.path += '/'; + url.path += *ref; + }; + if (auto rev = input.getRev()) { + url.path += '/'; + url.path += rev->gitRev(); + }; return url; } - bool hasAllInfo(const Input & input) override - { - return false; - } + bool hasAllInfo(const Input & input) override { return false; } - Input applyOverrides( - const Input & _input, - std::optional ref, - std::optional rev) override + Input applyOverrides(const Input & _input, std::optional ref, + std::optional rev) override { auto input(_input); - if (rev) input.attrs.insert_or_assign("rev", rev->gitRev()); - if (ref) input.attrs.insert_or_assign("ref", *ref); + if (rev) + input.attrs.insert_or_assign("rev", rev->gitRev()); + if (ref) + input.attrs.insert_or_assign("ref", *ref); return input; } - std::pair fetch(ref store, const Input & input) override + std::pair fetch(ref store, + const Input & input) override { - throw Error("indirect input '%s' cannot be fetched directly", input.to_string()); + throw Error("indirect input '%s' cannot be fetched directly", + input.to_string()); } }; -static auto rIndirectInputScheme = OnStartup([] { registerInputScheme(std::make_unique()); }); +static auto rIndirectInputScheme = OnStartup( + [] { registerInputScheme(std::make_unique()); }); } diff --git a/src/libfetchers/mercurial.cc b/src/libfetchers/mercurial.cc index 5c56716816f2..5288641f7b38 100644 --- a/src/libfetchers/mercurial.cc +++ b/src/libfetchers/mercurial.cc @@ -16,19 +16,17 @@ namespace nix::fetchers { static RunOptions hgOptions(const Strings & args) { auto env = getEnv(); - // Set HGPLAIN: this means we get consistent output from hg and avoids leakage from a user or system .hgrc. + // Set HGPLAIN: this means we get consistent output from hg and avoids + // leakage from a user or system .hgrc. env["HGPLAIN"] = ""; return { - .program = "hg", - .searchPath = true, - .args = args, - .environment = env - }; + .program = "hg", .searchPath = true, .args = args, .environment = env}; } // runProgram wrapper that uses hgOptions instead of stock RunOptions. -static std::string runHg(const Strings & args, const std::optional & input = {}) +static std::string runHg(const Strings & args, + const std::optional & input = {}) { RunOptions opts = hgOptions(args); opts.input = input; @@ -41,14 +39,12 @@ static std::string runHg(const Strings & args, const std::optional return res.second; } -struct MercurialInputScheme : InputScheme -{ +struct MercurialInputScheme : InputScheme { std::optional inputFromURL(const ParsedURL & url) override { - if (url.scheme != "hg+http" && - url.scheme != "hg+https" && - url.scheme != "hg+ssh" && - url.scheme != "hg+file") return {}; + if (url.scheme != "hg+http" && url.scheme != "hg+https" && + url.scheme != "hg+ssh" && url.scheme != "hg+file") + return {}; auto url2(url); url2.scheme = std::string(url2.scheme, 3); @@ -57,7 +53,7 @@ struct MercurialInputScheme : InputScheme Attrs attrs; attrs.emplace("type", "hg"); - for (auto &[name, value] : url.query) { + for (auto & [name, value] : url.query) { if (name == "rev" || name == "ref") attrs.emplace(name, value); else @@ -71,10 +67,13 @@ struct MercurialInputScheme : InputScheme std::optional inputFromAttrs(const Attrs & attrs) override { - if (maybeGetStrAttr(attrs, "type") != "hg") return {}; + if (maybeGetStrAttr(attrs, "type") != "hg") + return {}; for (auto & [name, value] : attrs) - if (name != "type" && name != "url" && name != "ref" && name != "rev" && name != "revCount" && name != "narHash" && name != "name") + if (name != "type" && name != "url" && name != "ref" && + name != "rev" && name != "revCount" && name != "narHash" && + name != "name") throw Error("unsupported Mercurial input attribute '%s'", name); parseURL(getStrAttr(attrs, "url")); @@ -93,8 +92,10 @@ struct MercurialInputScheme : InputScheme { auto url = parseURL(getStrAttr(input.attrs, "url")); url.scheme = "hg+" + url.scheme; - if (auto rev = input.getRev()) url.query.insert_or_assign("rev", rev->gitRev()); - if (auto ref = input.getRef()) url.query.insert_or_assign("ref", *ref); + if (auto rev = input.getRev()) + url.query.insert_or_assign("rev", rev->gitRev()); + if (auto ref = input.getRef()) + url.query.insert_or_assign("ref", *ref); return url; } @@ -102,17 +103,18 @@ struct MercurialInputScheme : InputScheme { // FIXME: ugly, need to distinguish between dirty and clean // default trees. - return input.getRef() == "default" || maybeGetIntAttr(input.attrs, "revCount"); + return input.getRef() == "default" || + maybeGetIntAttr(input.attrs, "revCount"); } - Input applyOverrides( - const Input & input, - std::optional ref, - std::optional rev) override + Input applyOverrides(const Input & input, std::optional ref, + std::optional rev) override { auto res(input); - if (rev) res.attrs.insert_or_assign("rev", rev->gitRev()); - if (ref) res.attrs.insert_or_assign("ref", *ref); + if (rev) + res.attrs.insert_or_assign("rev", rev->gitRev()); + if (ref) + res.attrs.insert_or_assign("ref", *ref); return res; } @@ -124,18 +126,18 @@ struct MercurialInputScheme : InputScheme return {}; } - void markChangedFile(const Input & input, std::string_view file, std::optional commitMsg) override + void markChangedFile(const Input & input, std::string_view file, + std::optional commitMsg) override { auto sourcePath = getSourcePath(input); assert(sourcePath); // FIXME: shut up if file is already tracked. - runHg( - { "add", *sourcePath + "/" + std::string(file) }); + runHg({"add", *sourcePath + "/" + std::string(file)}); if (commitMsg) - runHg( - { "commit", *sourcePath + "/" + std::string(file), "-m", *commitMsg }); + runHg({"commit", *sourcePath + "/" + std::string(file), "-m", + *commitMsg}); } std::pair getActualUrl(const Input & input) const @@ -145,7 +147,8 @@ struct MercurialInputScheme : InputScheme return {isLocal, isLocal ? url.path : url.base}; } - std::pair fetch(ref store, const Input & _input) override + std::pair fetch(ref store, + const Input & _input) override { Input input(_input); @@ -158,9 +161,11 @@ struct MercurialInputScheme : InputScheme // FIXME: don't clone local repositories. - if (!input.getRef() && !input.getRev() && isLocal && pathExists(actualUrl + "/.hg")) { + if (!input.getRef() && !input.getRev() && isLocal && + pathExists(actualUrl + "/.hg")) { - bool clean = runHg({ "status", "-R", actualUrl, "--modified", "--added", "--removed" }) == ""; + bool clean = runHg({"status", "-R", actualUrl, "--modified", + "--added", "--removed"}) == ""; if (!clean) { @@ -173,10 +178,13 @@ struct MercurialInputScheme : InputScheme if (fetchSettings.warnDirty) warn("Mercurial tree '%s' is unclean", actualUrl); - input.attrs.insert_or_assign("ref", chomp(runHg({ "branch", "-R", actualUrl }))); + input.attrs.insert_or_assign( + "ref", chomp(runHg({"branch", "-R", actualUrl}))); auto files = tokenizeString>( - runHg({ "status", "-R", actualUrl, "--clean", "--modified", "--added", "--no-status", "--print0" }), "\0"s); + runHg({"status", "-R", actualUrl, "--clean", "--modified", + "--added", "--no-status", "--print0"}), + "\0"s); Path actualPath(absPath(actualUrl)); @@ -195,23 +203,25 @@ struct MercurialInputScheme : InputScheme return files.count(file); }; - auto storePath = store->addToStore(input.getName(), actualPath, FileIngestionMethod::Recursive, htSHA256, filter); + auto storePath = store->addToStore( + input.getName(), actualPath, FileIngestionMethod::Recursive, + htSHA256, filter); return {std::move(storePath), input}; } } - if (!input.getRef()) input.attrs.insert_or_assign("ref", "default"); + if (!input.getRef()) + input.attrs.insert_or_assign("ref", "default"); - auto checkHashType = [&](const std::optional & hash) - { + auto checkHashType = [&](const std::optional & hash) { if (hash.has_value() && hash->type != htSHA1) - throw Error("Hash '%s' is not supported by Mercurial. Only sha1 is supported.", hash->to_string(Base16, true)); + throw Error("Hash '%s' is not supported by Mercurial. Only " + "sha1 is supported.", + hash->to_string(Base16, true)); }; - - auto getLockedAttrs = [&]() - { + auto getLockedAttrs = [&]() { checkHashType(input.getRev()); return Attrs({ @@ -221,12 +231,13 @@ struct MercurialInputScheme : InputScheme }); }; - auto makeResult = [&](const Attrs & infoAttrs, StorePath && storePath) - -> std::pair - { + auto makeResult = + [&](const Attrs & infoAttrs, + StorePath && storePath) -> std::pair { assert(input.getRev()); assert(!_input.getRev() || _input.getRev() == input.getRev()); - input.attrs.insert_or_assign("revCount", getIntAttr(infoAttrs, "revCount")); + input.attrs.insert_or_assign("revCount", + getIntAttr(infoAttrs, "revCount")); return {std::move(storePath), input}; }; @@ -235,7 +246,8 @@ struct MercurialInputScheme : InputScheme return makeResult(res->first, std::move(res->second)); } - auto revOrRef = input.getRev() ? input.getRev()->gitRev() : *input.getRef(); + auto revOrRef = + input.getRev() ? input.getRev()->gitRev() : *input.getRef(); Attrs unlockedAttrs({ {"type", "hg"}, @@ -252,41 +264,48 @@ struct MercurialInputScheme : InputScheme } } - Path cacheDir = fmt("%s/nix/hg/%s", getCacheDir(), hashString(htSHA256, actualUrl).to_string(Base32, false)); + Path cacheDir = + fmt("%s/nix/hg/%s", getCacheDir(), + hashString(htSHA256, actualUrl).to_string(Base32, false)); /* If this is a commit hash that we already have, we don't have to pull again. */ - if (!(input.getRev() - && pathExists(cacheDir) - && runProgram(hgOptions({ "log", "-R", cacheDir, "-r", input.getRev()->gitRev(), "--template", "1" })).second == "1")) - { - Activity act(*logger, lvlTalkative, actUnknown, fmt("fetching Mercurial repository '%s'", actualUrl)); + if (!(input.getRev() && pathExists(cacheDir) && + runProgram( + hgOptions({"log", "-R", cacheDir, "-r", + input.getRev()->gitRev(), "--template", "1"})) + .second == "1")) { + Activity act(*logger, lvlTalkative, actUnknown, + fmt("fetching Mercurial repository '%s'", actualUrl)); if (pathExists(cacheDir)) { try { - runHg({ "pull", "-R", cacheDir, "--", actualUrl }); - } - catch (ExecError & e) { + runHg({"pull", "-R", cacheDir, "--", actualUrl}); + } catch (ExecError & e) { auto transJournal = cacheDir + "/.hg/store/journal"; - /* hg throws "abandoned transaction" error only if this file exists */ + /* hg throws "abandoned transaction" error only if this file + * exists */ if (pathExists(transJournal)) { - runHg({ "recover", "-R", cacheDir }); - runHg({ "pull", "-R", cacheDir, "--", actualUrl }); + runHg({"recover", "-R", cacheDir}); + runHg({"pull", "-R", cacheDir, "--", actualUrl}); } else { - throw ExecError(e.status, "'hg pull' %s", statusToString(e.status)); + throw ExecError(e.status, "'hg pull' %s", + statusToString(e.status)); } } } else { createDirs(dirOf(cacheDir)); - runHg({ "clone", "--noupdate", "--", actualUrl, cacheDir }); + runHg({"clone", "--noupdate", "--", actualUrl, cacheDir}); } } auto tokens = tokenizeString>( - runHg({ "log", "-R", cacheDir, "-r", revOrRef, "--template", "{node} {rev} {branch}" })); + runHg({"log", "-R", cacheDir, "-r", revOrRef, "--template", + "{node} {rev} {branch}"})); assert(tokens.size() == 3); - input.attrs.insert_or_assign("rev", Hash::parseAny(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]); @@ -296,7 +315,8 @@ struct MercurialInputScheme : InputScheme Path tmpDir = createTempDir(); AutoDelete delTmpDir(tmpDir, true); - runHg({ "archive", "-R", cacheDir, "-r", input.getRev()->gitRev(), tmpDir }); + runHg({"archive", "-R", cacheDir, "-r", input.getRev()->gitRev(), + tmpDir}); deletePath(tmpDir + "/.hg_archival.txt"); @@ -308,24 +328,15 @@ struct MercurialInputScheme : InputScheme }); if (!_input.getRev()) - getCache()->add( - store, - unlockedAttrs, - infoAttrs, - storePath, - false); - - getCache()->add( - store, - getLockedAttrs(), - infoAttrs, - storePath, - true); + getCache()->add(store, unlockedAttrs, infoAttrs, storePath, false); + + getCache()->add(store, getLockedAttrs(), infoAttrs, storePath, true); return makeResult(infoAttrs, std::move(storePath)); } }; -static auto rMercurialInputScheme = OnStartup([] { registerInputScheme(std::make_unique()); }); +static auto rMercurialInputScheme = OnStartup( + [] { registerInputScheme(std::make_unique()); }); } diff --git a/src/libfetchers/path.cc b/src/libfetchers/path.cc index f0ef97da5af0..b820bbb71cba 100644 --- a/src/libfetchers/path.cc +++ b/src/libfetchers/path.cc @@ -4,14 +4,15 @@ namespace nix::fetchers { -struct PathInputScheme : InputScheme -{ +struct PathInputScheme : InputScheme { std::optional inputFromURL(const ParsedURL & url) override { - if (url.scheme != "path") return {}; + if (url.scheme != "path") + return {}; if (url.authority && *url.authority != "") - throw Error("path URL '%s' should not have an authority ('%s')", url.url, *url.authority); + throw Error("path URL '%s' should not have an authority ('%s')", + url.url, *url.authority); Input input; input.attrs.insert_or_assign("type", "path"); @@ -24,17 +25,19 @@ struct PathInputScheme : InputScheme if (auto n = string2Int(value)) input.attrs.insert_or_assign(name, *n); else - throw Error("path URL '%s' has invalid parameter '%s'", url.to_string(), name); - } - else - throw Error("path URL '%s' has unsupported parameter '%s'", url.to_string(), name); + throw Error("path URL '%s' has invalid parameter '%s'", + url.to_string(), name); + } else + throw Error("path URL '%s' has unsupported parameter '%s'", + url.to_string(), name); return input; } std::optional inputFromAttrs(const Attrs & attrs) override { - if (maybeGetStrAttr(attrs, "type") != "path") return {}; + if (maybeGetStrAttr(attrs, "type") != "path") + return {}; getStrAttr(attrs, "path"); @@ -42,8 +45,11 @@ struct PathInputScheme : InputScheme /* Allow the user to pass in "fake" tree info attributes. This is useful for making a pinned tree work the same as the repository from which is exported - (e.g. path:/nix/store/...-source?lastModified=1585388205&rev=b0c285...). */ - if (name == "type" || name == "rev" || name == "revCount" || name == "lastModified" || name == "narHash" || name == "path") + (e.g. + path:/nix/store/...-source?lastModified=1585388205&rev=b0c285...). + */ + if (name == "type" || name == "rev" || name == "revCount" || + name == "lastModified" || name == "narHash" || name == "path") // checked in Input::fromAttrs ; else @@ -59,29 +65,28 @@ struct PathInputScheme : InputScheme auto query = attrsToQuery(input.attrs); query.erase("path"); query.erase("type"); - return ParsedURL { + return ParsedURL{ .scheme = "path", .path = getStrAttr(input.attrs, "path"), .query = query, }; } - bool hasAllInfo(const Input & input) override - { - return true; - } + bool hasAllInfo(const Input & input) override { return true; } std::optional getSourcePath(const Input & input) override { return getStrAttr(input.attrs, "path"); } - void markChangedFile(const Input & input, std::string_view file, std::optional commitMsg) override + void markChangedFile(const Input & input, std::string_view file, + std::optional commitMsg) override { // nothing to do } - std::pair fetch(ref store, const Input & _input) override + std::pair fetch(ref store, + const Input & _input) override { Input input(_input); std::string absPath; @@ -89,23 +94,30 @@ struct PathInputScheme : InputScheme if (path[0] != '/') { if (!input.parent) - throw Error("cannot fetch input '%s' because it uses a relative path", input.to_string()); + throw Error( + "cannot fetch input '%s' because it uses a relative path", + input.to_string()); auto parent = canonPath(*input.parent); // the path isn't relative, prefix it absPath = nix::absPath(path, parent); - // for security, ensure that if the parent is a store path, it's inside it + // for security, ensure that if the parent is a store path, it's + // inside it if (store->isInStore(parent)) { - auto storePath = store->printStorePath(store->toStorePath(parent).first); + auto storePath = + store->printStorePath(store->toStorePath(parent).first); if (!isDirOrInDir(absPath, storePath)) - throw BadStorePath("relative path '%s' points outside of its parent's store path '%s'", path, storePath); + throw BadStorePath("relative path '%s' points outside of " + "its parent's store path '%s'", + path, storePath); } } else absPath = path; - Activity act(*logger, lvlTalkative, actUnknown, fmt("copying '%s'", absPath)); + Activity act(*logger, lvlTalkative, actUnknown, + fmt("copying '%s'", absPath)); // FIXME: check whether access to 'path' is allowed. auto storePath = store->maybeParseStorePath(absPath); @@ -114,7 +126,8 @@ struct PathInputScheme : InputScheme store->addTempRoot(*storePath); time_t mtime = 0; - if (!storePath || storePath->name() != "source" || !store->isValidPath(*storePath)) { + if (!storePath || storePath->name() != "source" || + !store->isValidPath(*storePath)) { // FIXME: try to substitute storePath. auto src = sinkToSource([&](Sink & sink) { mtime = dumpPathAndGetMtime(absPath, sink, defaultPathFilter); @@ -127,6 +140,7 @@ struct PathInputScheme : InputScheme } }; -static auto rPathInputScheme = OnStartup([] { registerInputScheme(std::make_unique()); }); +static auto rPathInputScheme = + OnStartup([] { registerInputScheme(std::make_unique()); }); } diff --git a/src/libfetchers/registry.cc b/src/libfetchers/registry.cc index acd1ff8667d6..570b32578127 100644 --- a/src/libfetchers/registry.cc +++ b/src/libfetchers/registry.cc @@ -11,8 +11,7 @@ namespace nix::fetchers { -std::shared_ptr Registry::read( - const Path & path, RegistryType type) +std::shared_ptr Registry::read(const Path & path, RegistryType type) { auto registry = std::make_shared(type); @@ -36,17 +35,16 @@ std::shared_ptr Registry::read( } auto exact = i.find("exact"); registry->entries.push_back( - Entry { - .from = Input::fromAttrs(jsonToAttrs(i["from"])), - .to = Input::fromAttrs(std::move(toAttrs)), - .extraAttrs = extraAttrs, - .exact = exact != i.end() && exact.value() - }); + Entry{.from = Input::fromAttrs(jsonToAttrs(i["from"])), + .to = Input::fromAttrs(std::move(toAttrs)), + .extraAttrs = extraAttrs, + .exact = exact != i.end() && exact.value()}); } } else - throw Error("flake registry '%s' has unsupported version %d", path, version); + throw Error("flake registry '%s' has unsupported version %d", path, + version); } catch (nlohmann::json::exception & e) { warn("cannot parse flake registry '%s': %s", path, e.what()); @@ -79,23 +77,17 @@ void Registry::write(const Path & path) writeFile(path, json.dump(2)); } -void Registry::add( - const Input & from, - const Input & to, - const Attrs & extraAttrs) +void Registry::add(const Input & from, const Input & to, + const Attrs & extraAttrs) { entries.emplace_back( - Entry { - .from = from, - .to = to, - .extraAttrs = extraAttrs - }); + Entry{.from = from, .to = to, .extraAttrs = extraAttrs}); } void Registry::remove(const Input & input) { // FIXME: use C++20 std::erase. - for (auto i = entries.begin(); i != entries.end(); ) + for (auto i = entries.begin(); i != entries.end();) if (i->from == input) i = entries.erase(i); else @@ -114,10 +106,7 @@ static std::shared_ptr getSystemRegistry() return systemRegistry; } -Path getUserRegistryPath() -{ - return getConfigDir() + "/nix/registry.json"; -} +Path getUserRegistryPath() { return getConfigDir() + "/nix/registry.json"; } std::shared_ptr getUserRegistry() { @@ -128,23 +117,17 @@ std::shared_ptr getUserRegistry() std::shared_ptr getCustomRegistry(const Path & p) { - static auto customRegistry = - Registry::read(p, Registry::Custom); + static auto customRegistry = Registry::read(p, Registry::Custom); return customRegistry; } static std::shared_ptr flagRegistry = std::make_shared(Registry::Flag); -std::shared_ptr getFlagRegistry() -{ - return flagRegistry; -} +std::shared_ptr getFlagRegistry() { return flagRegistry; } -void overrideRegistry( - const Input & from, - const Input & to, - const Attrs & extraAttrs) +void overrideRegistry(const Input & from, const Input & to, + const Attrs & extraAttrs) { flagRegistry->add(from, to, extraAttrs); } @@ -155,9 +138,12 @@ static std::shared_ptr getGlobalRegistry(ref store) auto path = fetchSettings.flakeRegistry.get(); if (!hasPrefix(path, "/")) { - auto storePath = downloadFile(store, path, "flake-registry.json", false).storePath; + auto storePath = + downloadFile(store, path, "flake-registry.json", false) + .storePath; if (auto store2 = store.dynamic_pointer_cast()) - store2->addPermRoot(storePath, getCacheDir() + "/nix/flake-registry.json"); + store2->addPermRoot(storePath, + getCacheDir() + "/nix/flake-registry.json"); path = store->toRealPath(storePath); } @@ -177,18 +163,19 @@ Registries getRegistries(ref store) return registries; } -std::pair lookupInRegistries( - ref store, - const Input & _input) +std::pair lookupInRegistries(ref store, + const Input & _input) { Attrs extraAttrs; int n = 0; Input input(_input); - restart: +restart: n++; - if (n > 100) throw Error("cycle detected in flake registry for '%s'", input.to_string()); + if (n > 100) + throw Error("cycle detected in flake registry for '%s'", + input.to_string()); for (auto & registry : getRegistries(store)) { // FIXME: O(n) @@ -202,8 +189,12 @@ std::pair lookupInRegistries( } else { if (entry.from.contains(input)) { input = entry.to.applyOverrides( - !entry.from.getRef() && input.getRef() ? input.getRef() : std::optional(), - !entry.from.getRev() && input.getRev() ? input.getRev() : std::optional()); + !entry.from.getRef() && input.getRef() + ? input.getRef() + : std::optional(), + !entry.from.getRev() && input.getRev() + ? input.getRev() + : std::optional()); extraAttrs = entry.extraAttrs; goto restart; } @@ -212,7 +203,8 @@ std::pair lookupInRegistries( } if (!input.isDirect()) - throw Error("cannot find flake '%s' in the flake registries", input.to_string()); + throw Error("cannot find flake '%s' in the flake registries", + input.to_string()); debug("looked up '%s' -> '%s'", _input.to_string(), input.to_string()); diff --git a/src/libfetchers/registry.hh b/src/libfetchers/registry.hh index 260a2c460162..5cb89b066bee 100644 --- a/src/libfetchers/registry.hh +++ b/src/libfetchers/registry.hh @@ -3,12 +3,13 @@ #include "types.hh" #include "fetchers.hh" -namespace nix { class Store; } +namespace nix { +class Store; +} namespace nix::fetchers { -struct Registry -{ +struct Registry { enum RegistryType { Flag = 0, User = 1, @@ -19,8 +20,7 @@ struct Registry RegistryType type; - struct Entry - { + struct Entry { Input from, to; Attrs extraAttrs; bool exact = false; @@ -28,19 +28,13 @@ struct Registry std::vector entries; - Registry(RegistryType type) - : type(type) - { } + Registry(RegistryType type) : type(type) {} - static std::shared_ptr read( - const Path & path, RegistryType type); + static std::shared_ptr read(const Path & path, RegistryType type); void write(const Path & path); - void add( - const Input & from, - const Input & to, - const Attrs & extraAttrs); + void add(const Input & from, const Input & to, const Attrs & extraAttrs); void remove(const Input & input); }; @@ -55,13 +49,10 @@ Path getUserRegistryPath(); Registries getRegistries(ref store); -void overrideRegistry( - const Input & from, - const Input & to, - const Attrs & extraAttrs); +void overrideRegistry(const Input & from, const Input & to, + const Attrs & extraAttrs); -std::pair lookupInRegistries( - ref store, - const Input & input); +std::pair lookupInRegistries(ref store, + const Input & input); } diff --git a/src/libfetchers/tarball.cc b/src/libfetchers/tarball.cc index 6c551bd93f48..555d05c760c0 100644 --- a/src/libfetchers/tarball.cc +++ b/src/libfetchers/tarball.cc @@ -10,12 +10,9 @@ namespace nix::fetchers { -DownloadFileResult downloadFile( - ref store, - const std::string & url, - const std::string & name, - bool locked, - const Headers & headers) +DownloadFileResult downloadFile(ref store, const std::string & url, + const std::string & name, bool locked, + const Headers & headers) { // FIXME: check store @@ -27,13 +24,10 @@ DownloadFileResult downloadFile( auto cached = getCache()->lookupExpired(store, inAttrs); - auto useCached = [&]() -> DownloadFileResult - { - return { - .storePath = std::move(cached->storePath), - .etag = getStrAttr(cached->infoAttrs, "etag"), - .effectiveUrl = getStrAttr(cached->infoAttrs, "url") - }; + auto useCached = [&]() -> DownloadFileResult { + return {.storePath = std::move(cached->storePath), + .etag = getStrAttr(cached->infoAttrs, "etag"), + .effectiveUrl = getStrAttr(cached->infoAttrs, "url")}; }; if (cached && !cached->expired) @@ -70,12 +64,12 @@ DownloadFileResult downloadFile( StringSink sink; dumpString(res.data, sink); auto hash = hashString(htSHA256, res.data); - ValidPathInfo info { + ValidPathInfo info{ store->makeFixedOutputPath(FileIngestionMethod::Flat, hash, name), hashString(htSHA256, sink.s), }; info.narSize = sink.s.size(); - info.ca = FixedOutputHash { + info.ca = FixedOutputHash{ .method = FileIngestionMethod::Flat, .hash = hash, }; @@ -84,24 +78,16 @@ DownloadFileResult downloadFile( storePath = std::move(info.path); } - getCache()->add( - store, - inAttrs, - infoAttrs, - *storePath, - locked); + getCache()->add(store, inAttrs, infoAttrs, *storePath, locked); if (url != res.effectiveUri) - getCache()->add( - store, - { - {"type", "file"}, - {"url", res.effectiveUri}, - {"name", name}, - }, - infoAttrs, - *storePath, - locked); + getCache()->add(store, + { + {"type", "file"}, + {"url", res.effectiveUri}, + {"name", name}, + }, + infoAttrs, *storePath, locked); return { .storePath = std::move(*storePath), @@ -110,12 +96,10 @@ DownloadFileResult downloadFile( }; } -std::pair downloadTarball( - ref store, - const std::string & url, - const std::string & name, - bool locked, - const Headers & headers) +std::pair downloadTarball(ref store, + const std::string & url, + const std::string & name, bool locked, + const Headers & headers) { Attrs inAttrs({ {"type", "tarball"}, @@ -126,17 +110,17 @@ std::pair downloadTarball( auto cached = getCache()->lookupExpired(store, inAttrs); if (cached && !cached->expired) - return { - Tree { .actualPath = store->toRealPath(cached->storePath), .storePath = std::move(cached->storePath) }, - getIntAttr(cached->infoAttrs, "lastModified") - }; + return {Tree{.actualPath = store->toRealPath(cached->storePath), + .storePath = std::move(cached->storePath)}, + getIntAttr(cached->infoAttrs, "lastModified")}; auto res = downloadFile(store, url, name, locked, headers); std::optional unpackedStorePath; time_t lastModified; - if (cached && res.etag != "" && getStrAttr(cached->infoAttrs, "etag") == res.etag) { + if (cached && res.etag != "" && + getStrAttr(cached->infoAttrs, "etag") == res.etag) { unpackedStorePath = std::move(cached->storePath); lastModified = getIntAttr(cached->infoAttrs, "lastModified"); } else { @@ -145,10 +129,14 @@ std::pair downloadTarball( unpackTarfile(store->toRealPath(res.storePath), tmpDir); auto members = readDirectory(tmpDir); if (members.size() != 1) - throw nix::Error("tarball '%s' contains an unexpected number of top-level files", url); + throw nix::Error( + "tarball '%s' contains an unexpected number of top-level files", + url); auto topDir = tmpDir + "/" + members.begin()->name; lastModified = lstat(topDir).st_mtime; - unpackedStorePath = store->addToStore(name, topDir, FileIngestionMethod::Recursive, htSHA256, defaultPathFilter, NoRepair); + unpackedStorePath = + store->addToStore(name, topDir, FileIngestionMethod::Recursive, + htSHA256, defaultPathFilter, NoRepair); } Attrs infoAttrs({ @@ -156,31 +144,26 @@ std::pair downloadTarball( {"etag", res.etag}, }); - getCache()->add( - store, - inAttrs, - infoAttrs, - *unpackedStorePath, - locked); + getCache()->add(store, inAttrs, infoAttrs, *unpackedStorePath, locked); return { - Tree { .actualPath = store->toRealPath(*unpackedStorePath), .storePath = std::move(*unpackedStorePath) }, + Tree{.actualPath = store->toRealPath(*unpackedStorePath), + .storePath = std::move(*unpackedStorePath)}, lastModified, }; } // An input scheme corresponding to a curl-downloadable resource. -struct CurlInputScheme : InputScheme -{ +struct CurlInputScheme : InputScheme { virtual const std::string inputType() const = 0; const std::set transportUrlSchemes = {"file", "http", "https"}; const bool hasTarballExtension(std::string_view path) const { - return hasSuffix(path, ".zip") || hasSuffix(path, ".tar") - || hasSuffix(path, ".tgz") || hasSuffix(path, ".tar.gz") - || hasSuffix(path, ".tar.xz") || hasSuffix(path, ".tar.bz2") - || hasSuffix(path, ".tar.zst"); + return hasSuffix(path, ".zip") || hasSuffix(path, ".tar") || + hasSuffix(path, ".tgz") || hasSuffix(path, ".tar.gz") || + hasSuffix(path, ".tar.xz") || hasSuffix(path, ".tar.bz2") || + hasSuffix(path, ".tar.zst"); } virtual bool isValidURL(const ParsedURL & url) const = 0; @@ -193,10 +176,12 @@ struct CurlInputScheme : InputScheme Input input; auto urlWithoutApplicationScheme = url; - urlWithoutApplicationScheme.scheme = parseUrlScheme(url.scheme).transport; + urlWithoutApplicationScheme.scheme = + parseUrlScheme(url.scheme).transport; input.attrs.insert_or_assign("type", inputType()); - input.attrs.insert_or_assign("url", urlWithoutApplicationScheme.to_string()); + input.attrs.insert_or_assign("url", + urlWithoutApplicationScheme.to_string()); auto narHash = url.query.find("narHash"); if (narHash != url.query.end()) input.attrs.insert_or_assign("narHash", narHash->second); @@ -206,9 +191,11 @@ struct CurlInputScheme : InputScheme std::optional inputFromAttrs(const Attrs & attrs) override { auto type = maybeGetStrAttr(attrs, "type"); - if (type != inputType()) return {}; + if (type != inputType()) + return {}; - std::set allowedNames = {"type", "url", "narHash", "name", "unpack"}; + std::set allowedNames = {"type", "url", "narHash", "name", + "unpack"}; for (auto & [name, value] : attrs) if (!allowedNames.count(name)) throw Error("unsupported %s input attribute '%s'", *type, name); @@ -216,68 +203,73 @@ struct CurlInputScheme : InputScheme Input input; input.attrs = attrs; - //input.locked = (bool) maybeGetStrAttr(input.attrs, "hash"); + // input.locked = (bool) maybeGetStrAttr(input.attrs, "hash"); return input; } ParsedURL toURL(const Input & input) override { auto url = parseURL(getStrAttr(input.attrs, "url")); - // NAR hashes are preferred over file hashes since tar/zip files // don't have a canonical representation. + // NAR hashes are preferred over file hashes since tar/zip files // + // don't have a canonical representation. if (auto narHash = input.getNarHash()) - url.query.insert_or_assign("narHash", narHash->to_string(SRI, true)); + url.query.insert_or_assign("narHash", + narHash->to_string(SRI, true)); return url; } - bool hasAllInfo(const Input & input) override - { - return true; - } - + bool hasAllInfo(const Input & input) override { return true; } }; -struct FileInputScheme : CurlInputScheme -{ +struct FileInputScheme : CurlInputScheme { const std::string inputType() const override { return "file"; } bool isValidURL(const ParsedURL & url) const override { auto parsedUrlScheme = parseUrlScheme(url.scheme); - return transportUrlSchemes.count(std::string(parsedUrlScheme.transport)) - && (parsedUrlScheme.application + return transportUrlSchemes.count( + std::string(parsedUrlScheme.transport)) && + (parsedUrlScheme.application ? parsedUrlScheme.application.value() == inputType() : !hasTarballExtension(url.path)); } - std::pair fetch(ref store, const Input & input) override + std::pair fetch(ref store, + const Input & input) override { - auto file = downloadFile(store, getStrAttr(input.attrs, "url"), input.getName(), false); + auto file = downloadFile(store, getStrAttr(input.attrs, "url"), + input.getName(), false); return {std::move(file.storePath), input}; } }; -struct TarballInputScheme : CurlInputScheme -{ +struct TarballInputScheme : CurlInputScheme { const std::string inputType() const override { return "tarball"; } bool isValidURL(const ParsedURL & url) const override { auto parsedUrlScheme = parseUrlScheme(url.scheme); - return transportUrlSchemes.count(std::string(parsedUrlScheme.transport)) - && (parsedUrlScheme.application + return transportUrlSchemes.count( + std::string(parsedUrlScheme.transport)) && + (parsedUrlScheme.application ? parsedUrlScheme.application.value() == inputType() : hasTarballExtension(url.path)); } - std::pair fetch(ref store, const Input & input) override + std::pair fetch(ref store, + const Input & input) override { - auto tree = downloadTarball(store, getStrAttr(input.attrs, "url"), input.getName(), false).first; + auto tree = downloadTarball(store, getStrAttr(input.attrs, "url"), + input.getName(), false) + .first; return {std::move(tree.storePath), input}; } }; -static auto rTarballInputScheme = OnStartup([] { registerInputScheme(std::make_unique()); }); -static auto rFileInputScheme = OnStartup([] { registerInputScheme(std::make_unique()); }); +static auto rTarballInputScheme = OnStartup( + [] { registerInputScheme(std::make_unique()); }); +static auto rFileInputScheme = + OnStartup([] { registerInputScheme(std::make_unique()); }); } diff --git a/src/libmain/common-args.cc b/src/libmain/common-args.cc index 12f5403ea610..e458dc30236b 100644 --- a/src/libmain/common-args.cc +++ b/src/libmain/common-args.cc @@ -19,7 +19,10 @@ MixCommonArgs::MixCommonArgs(const std::string & programName) .longName = "quiet", .description = "Decrease the logging verbosity level.", .category = loggingCategory, - .handler = {[]() { verbosity = verbosity > lvlError ? (Verbosity) (verbosity - 1) : lvlError; }}, + .handler = {[]() { + verbosity = + verbosity > lvlError ? (Verbosity) (verbosity - 1) : lvlError; + }}, }); addFlag({ @@ -29,52 +32,51 @@ MixCommonArgs::MixCommonArgs(const std::string & programName) .handler = {[]() { verbosity = lvlDebug; }}, }); - addFlag({ - .longName = "option", - .description = "Set the Nix configuration setting *name* to *value* (overriding `nix.conf`).", - .labels = {"name", "value"}, - .handler = {[](std::string name, std::string value) { - try { - globalConfig.set(name, value); - } catch (UsageError & e) { - if (!completions) - warn(e.what()); - } - }}, - .completer = [](size_t index, std::string_view prefix) { - if (index == 0) { - std::map settings; - globalConfig.getSettings(settings); - for (auto & s : settings) - if (hasPrefix(s.first, prefix)) - completions->add(s.first, fmt("Set the `%s` setting.", s.first)); - } - } - }); + addFlag({.longName = "option", + .description = "Set the Nix configuration setting *name* to " + "*value* (overriding `nix.conf`).", + .labels = {"name", "value"}, + .handler = {[](std::string name, std::string value) { + try { + globalConfig.set(name, value); + } catch (UsageError & e) { + if (!completions) + warn(e.what()); + } + }}, + .completer = [](size_t index, std::string_view prefix) { + if (index == 0) { + std::map settings; + globalConfig.getSettings(settings); + for (auto & s : settings) + if (hasPrefix(s.first, prefix)) + completions->add( + s.first, + fmt("Set the `%s` setting.", s.first)); + } + }}); addFlag({ .longName = "log-format", - .description = "Set the format of log output; one of `raw`, `internal-json`, `bar` or `bar-with-logs`.", + .description = "Set the format of log output; one of `raw`, " + "`internal-json`, `bar` or `bar-with-logs`.", .category = loggingCategory, .labels = {"format"}, .handler = {[](std::string format) { setLogFormat(format); }}, }); - addFlag({ - .longName = "max-jobs", - .shortName = 'j', - .description = "The maximum number of parallel builds.", - .labels = Strings{"jobs"}, - .handler = {[=](std::string s) { - settings.set("max-jobs", s); - }} - }); + addFlag({.longName = "max-jobs", + .shortName = 'j', + .description = "The maximum number of parallel builds.", + .labels = Strings{"jobs"}, + .handler = {[=](std::string s) { settings.set("max-jobs", s); }}}); std::string cat = "Options to override configuration settings"; globalConfig.convertToArgs(*this, cat); // Backward compatibility hack: nix-env already had a --system flag. - if (programName == "nix-env") longFlags.erase("system"); + if (programName == "nix-env") + longFlags.erase("system"); hiddenCategories.insert(cat); } @@ -85,5 +87,4 @@ void MixCommonArgs::initialFlagsProcessed() pluginsInited(); } - } diff --git a/src/libmain/common-args.hh b/src/libmain/common-args.hh index 25453b8c63eb..4646527a1bc7 100644 --- a/src/libmain/common-args.hh +++ b/src/libmain/common-args.hh @@ -4,21 +4,21 @@ namespace nix { -//static constexpr auto commonArgsCategory = "Miscellaneous common options"; +// static constexpr auto commonArgsCategory = "Miscellaneous common options"; static constexpr auto loggingCategory = "Logging-related options"; -class MixCommonArgs : public virtual Args -{ +class MixCommonArgs : public virtual Args { void initialFlagsProcessed() override; -public: + + public: std::string programName; MixCommonArgs(const std::string & programName); -protected: + + protected: virtual void pluginsInited() {} }; -struct MixDryRun : virtual Args -{ +struct MixDryRun : virtual Args { bool dryRun = false; MixDryRun() @@ -32,15 +32,15 @@ struct MixDryRun : virtual Args } }; -struct MixJSON : virtual Args -{ +struct MixJSON : virtual Args { bool json = false; MixJSON() { addFlag({ .longName = "json", - .description = "Produce output in JSON format, suitable for consumption by another program.", + .description = "Produce output in JSON format, suitable for " + "consumption by another program.", //.category = commonArgsCategory, .handler = {&json, true}, }); diff --git a/src/libmain/loggers.cc b/src/libmain/loggers.cc index cdf23859baac..60e500013ac3 100644 --- a/src/libmain/loggers.cc +++ b/src/libmain/loggers.cc @@ -6,7 +6,8 @@ namespace nix { LogFormat defaultLogFormat = LogFormat::raw; -LogFormat parseLogFormat(const std::string & logFormatStr) { +LogFormat parseLogFormat(const std::string & logFormatStr) +{ if (logFormatStr == "raw" || getEnv("NIX_GET_COMPLETIONS")) return LogFormat::raw; else if (logFormatStr == "raw-with-logs") @@ -20,7 +21,8 @@ LogFormat parseLogFormat(const std::string & logFormatStr) { throw Error("option 'log-format' has an invalid value '%s'", logFormatStr); } -Logger * makeDefaultLogger() { +Logger * makeDefaultLogger() +{ switch (defaultLogFormat) { case LogFormat::raw: return makeSimpleLogger(false); @@ -37,17 +39,17 @@ Logger * makeDefaultLogger() { } } -void setLogFormat(const std::string & logFormatStr) { +void setLogFormat(const std::string & logFormatStr) +{ setLogFormat(parseLogFormat(logFormatStr)); } -void setLogFormat(const LogFormat & logFormat) { +void setLogFormat(const LogFormat & logFormat) +{ defaultLogFormat = logFormat; createDefaultLogger(); } -void createDefaultLogger() { - logger = makeDefaultLogger(); -} +void createDefaultLogger() { logger = makeDefaultLogger(); } } diff --git a/src/libmain/loggers.hh b/src/libmain/loggers.hh index f3c759193f5e..d8e99a44f6b4 100644 --- a/src/libmain/loggers.hh +++ b/src/libmain/loggers.hh @@ -5,11 +5,11 @@ namespace nix { enum class LogFormat { - raw, - rawWithLogs, - internalJSON, - bar, - barWithLogs, + raw, + rawWithLogs, + internalJSON, + bar, + barWithLogs, }; void setLogFormat(const std::string & logFormatStr); diff --git a/src/libmain/progress-bar.cc b/src/libmain/progress-bar.cc index f4306ab91165..da05f73d1263 100644 --- a/src/libmain/progress-bar.cc +++ b/src/libmain/progress-bar.cc @@ -11,7 +11,8 @@ namespace nix { -static std::string_view getS(const std::vector & fields, size_t n) +static std::string_view getS(const std::vector & fields, + size_t n) { assert(n < fields.size()); assert(fields[n].type == Logger::Field::tString); @@ -32,12 +33,9 @@ static std::string_view storePathToName(std::string_view path) return i == std::string::npos ? base.substr(0, 0) : base.substr(i + 1); } -class ProgressBar : public Logger -{ -private: - - struct ActInfo - { +class ProgressBar : public Logger { + private: + struct ActInfo { std::string s, lastLine, phase; ActivityType type = actUnknown; uint64_t done = 0; @@ -50,16 +48,14 @@ class ProgressBar : public Logger std::optional name; }; - struct ActivitiesByType - { + struct ActivitiesByType { std::map::iterator> its; uint64_t done = 0; uint64_t expected = 0; uint64_t failed = 0; }; - struct State - { + struct State { std::list activities; std::map::iterator> its; @@ -82,11 +78,9 @@ class ProgressBar : public Logger bool printBuildLogs; bool isTTY; -public: - + public: ProgressBar(bool printBuildLogs, bool isTTY) - : printBuildLogs(printBuildLogs) - , isTTY(isTTY) + : printBuildLogs(printBuildLogs), isTTY(isTTY) { state_.lock()->active = isTTY; updateThread = std::thread([&]() { @@ -100,16 +94,14 @@ class ProgressBar : public Logger }); } - ~ProgressBar() - { - stop(); - } + ~ProgressBar() { stop(); } void stop() override { { auto state(state_.lock()); - if (!state->active) return; + if (!state->active) + return; state->active = false; writeToStderr("\r\e[K"); updateCV.notify_one(); @@ -118,18 +110,17 @@ class ProgressBar : public Logger updateThread.join(); } - bool isVerbose() override { - return printBuildLogs; - } + bool isVerbose() override { return printBuildLogs; } void log(Verbosity lvl, const FormatOrString & fs) override { - if (lvl > verbosity) return; + if (lvl > verbosity) + return; auto state(state_.lock()); log(*state, lvl, fs.s); } - void logEI(const ErrorInfo &ei) override + void logEI(const ErrorInfo & ei) override { auto state(state_.lock()); @@ -142,17 +133,20 @@ class ProgressBar : public Logger void log(State & state, Verbosity lvl, const std::string & s) { if (state.active) { - writeToStderr("\r\e[K" + filterANSIEscapes(s, !isTTY) + ANSI_NORMAL "\n"); + writeToStderr("\r\e[K" + filterANSIEscapes(s, !isTTY) + + ANSI_NORMAL "\n"); draw(state); } else { auto s2 = s + ANSI_NORMAL "\n"; - if (!isTTY) s2 = filterANSIEscapes(s2, true); + if (!isTTY) + s2 = filterANSIEscapes(s2, true); writeToStderr(s2); } } void startActivity(ActivityId act, Verbosity lvl, ActivityType type, - const std::string & s, const Fields & fields, ActivityId parent) override + const std::string & s, const Fields & fields, + ActivityId parent) override { auto state(state_.lock()); @@ -185,11 +179,10 @@ class ProgressBar : public Logger if (type == actSubstitute) { auto name = storePathToName(getS(fields, 0)); auto sub = getS(fields, 1); - i->s = fmt( - hasPrefix(sub, "local") - ? "copying " ANSI_BOLD "%s" ANSI_NORMAL " from %s" - : "fetching " ANSI_BOLD "%s" ANSI_NORMAL " from %s", - name, sub); + i->s = fmt(hasPrefix(sub, "local") + ? "copying " ANSI_BOLD "%s" ANSI_NORMAL " from %s" + : "fetching " ANSI_BOLD "%s" ANSI_NORMAL " from %s", + name, sub); } if (type == actPostBuildHook) { @@ -202,12 +195,15 @@ class ProgressBar : public Logger if (type == actQueryPathInfo) { auto name = storePathToName(getS(fields, 0)); - i->s = fmt("querying " ANSI_BOLD "%s" ANSI_NORMAL " on %s", name, getS(fields, 1)); + i->s = fmt("querying " ANSI_BOLD "%s" ANSI_NORMAL " on %s", name, + getS(fields, 1)); } - if ((type == actFileTransfer && hasAncestor(*state, actCopyPath, parent)) - || (type == actFileTransfer && hasAncestor(*state, actQueryPathInfo, parent)) - || (type == actCopyPath && hasAncestor(*state, actSubstitute, parent))) + if ((type == actFileTransfer && + hasAncestor(*state, actCopyPath, parent)) || + (type == actFileTransfer && + hasAncestor(*state, actQueryPathInfo, parent)) || + (type == actCopyPath && hasAncestor(*state, actSubstitute, parent))) i->visible = false; update(*state); @@ -219,8 +215,10 @@ class ProgressBar : public Logger { while (act != 0) { auto i = state.its.find(act); - if (i == state.its.end()) break; - if (i->second->type == type) return true; + if (i == state.its.end()) + break; + if (i->second->type == type) + return true; act = i->second->parent; } return false; @@ -248,7 +246,8 @@ class ProgressBar : public Logger update(*state); } - void result(ActivityId act, ResultType type, const std::vector & fields) override + void result(ActivityId act, ResultType type, + const std::vector & fields) override { auto state(state_.lock()); @@ -269,7 +268,9 @@ class ProgressBar : public Logger if (type == resPostBuildLogLine) { suffix = " (post)> "; } - log(*state, lvlInfo, ANSI_FAINT + info.name.value_or("unnamed") + suffix + ANSI_NORMAL + lastLine); + log(*state, lvlInfo, + ANSI_FAINT + info.name.value_or("unnamed") + suffix + + ANSI_NORMAL + lastLine); } else { state->activities.erase(i->second); info.lastLine = lastLine; @@ -330,7 +331,8 @@ class ProgressBar : public Logger void draw(State & state) { state.haveUpdate = false; - if (!state.active) return; + if (!state.active) + return; std::string line; @@ -342,10 +344,12 @@ class ProgressBar : public Logger } if (!state.activities.empty()) { - if (!status.empty()) line += " "; + if (!status.empty()) + line += " "; auto i = state.activities.rbegin(); - while (i != state.activities.rend() && (!i->visible || (i->s.empty() && i->lastLine.empty()))) + while (i != state.activities.rend() && + (!i->visible || (i->s.empty() && i->lastLine.empty()))) ++i; if (i != state.activities.rend()) { @@ -356,16 +360,19 @@ class ProgressBar : public Logger line += ")"; } if (!i->lastLine.empty()) { - if (!i->s.empty()) line += ": "; + if (!i->s.empty()) + line += ": "; line += i->lastLine; } } } auto width = getWindowSize().second; - if (width <= 0) width = std::numeric_limits::max(); + if (width <= 0) + width = std::numeric_limits::max(); - writeToStderr("\r" + filterANSIEscapes(line, false, width) + ANSI_NORMAL + "\e[K"); + writeToStderr("\r" + filterANSIEscapes(line, false, width) + + ANSI_NORMAL + "\e[K"); } std::string getStatus(State & state) @@ -374,9 +381,13 @@ class ProgressBar : public Logger std::string res; - auto renderActivity = [&](ActivityType type, const std::string & itemFmt, const std::string & numberFmt = "%d", double unit = 1) { + auto renderActivity = [&](ActivityType type, + const std::string & itemFmt, + const std::string & numberFmt = "%d", + double unit = 1) { auto & act = state.activitiesByType[type]; - uint64_t done = act.done, expected = act.done, running = 0, failed = act.failed; + uint64_t done = act.done, expected = act.done, running = 0, + failed = act.failed; for (auto & j : act.its) { done += j.second->done; expected += j.second->expected; @@ -391,32 +402,45 @@ class ProgressBar : public Logger if (running || done || expected || failed) { if (running) if (expected != 0) - s = fmt(ANSI_BLUE + numberFmt + ANSI_NORMAL "/" ANSI_GREEN + numberFmt + ANSI_NORMAL "/" + numberFmt, - running / unit, done / unit, expected / unit); + s = fmt(ANSI_BLUE + numberFmt + + ANSI_NORMAL "/" ANSI_GREEN + numberFmt + + ANSI_NORMAL "/" + numberFmt, + running / unit, done / unit, expected / unit); else - s = fmt(ANSI_BLUE + numberFmt + ANSI_NORMAL "/" ANSI_GREEN + numberFmt + ANSI_NORMAL, - running / unit, done / unit); + s = fmt(ANSI_BLUE + numberFmt + + ANSI_NORMAL "/" ANSI_GREEN + numberFmt + + ANSI_NORMAL, + running / unit, done / unit); else if (expected != done) if (expected != 0) - s = fmt(ANSI_GREEN + numberFmt + ANSI_NORMAL "/" + numberFmt, - done / unit, expected / unit); + s = fmt(ANSI_GREEN + numberFmt + ANSI_NORMAL "/" + + numberFmt, + done / unit, expected / unit); else - s = fmt(ANSI_GREEN + numberFmt + ANSI_NORMAL, done / unit); + s = fmt(ANSI_GREEN + numberFmt + ANSI_NORMAL, + done / unit); else - s = fmt(done ? ANSI_GREEN + numberFmt + ANSI_NORMAL : numberFmt, done / unit); + s = fmt(done ? ANSI_GREEN + numberFmt + ANSI_NORMAL + : numberFmt, + done / unit); s = fmt(itemFmt, s); if (failed) - s += fmt(" (" ANSI_RED "%d failed" ANSI_NORMAL ")", failed / unit); + s += fmt(" (" ANSI_RED "%d failed" ANSI_NORMAL ")", + failed / unit); } return s; }; - auto showActivity = [&](ActivityType type, const std::string & itemFmt, const std::string & numberFmt = "%d", double unit = 1) { + auto showActivity = [&](ActivityType type, const std::string & itemFmt, + const std::string & numberFmt = "%d", + double unit = 1) { auto s = renderActivity(type, itemFmt, numberFmt, unit); - if (s.empty()) return; - if (!res.empty()) res += ", "; + if (s.empty()) + return; + if (!res.empty()) + res += ", "; res += s; }; @@ -426,9 +450,17 @@ class ProgressBar : public Logger auto s2 = renderActivity(actCopyPath, "%s MiB", "%.1f", MiB); if (!s1.empty() || !s2.empty()) { - if (!res.empty()) res += ", "; - if (s1.empty()) res += "0 copied"; else res += s1; - if (!s2.empty()) { res += " ("; res += s2; res += ')'; } + if (!res.empty()) + res += ", "; + if (s1.empty()) + res += "0 copied"; + else + res += s1; + if (!s2.empty()) { + res += " ("; + res += s2; + res += ')'; + } } showActivity(actFileTransfer, "%s MiB DL", "%.1f", MiB); @@ -436,8 +468,10 @@ class ProgressBar : public Logger { auto s = renderActivity(actOptimiseStore, "%s paths optimised"); if (s != "") { - s += fmt(", %.1f MiB / %d inodes freed", state.bytesLinked / MiB, state.filesLinked); - if (!res.empty()) res += ", "; + s += fmt(", %.1f MiB / %d inodes freed", + state.bytesLinked / MiB, state.filesLinked); + if (!res.empty()) + res += ", "; res += s; } } @@ -446,13 +480,17 @@ class ProgressBar : public Logger showActivity(actVerifyPaths, "%s paths verified"); if (state.corruptedPaths) { - if (!res.empty()) res += ", "; - res += fmt(ANSI_RED "%d corrupted" ANSI_NORMAL, state.corruptedPaths); + if (!res.empty()) + res += ", "; + res += + fmt(ANSI_RED "%d corrupted" ANSI_NORMAL, state.corruptedPaths); } if (state.untrustedPaths) { - if (!res.empty()) res += ", "; - res += fmt(ANSI_RED "%d untrusted" ANSI_NORMAL, state.untrustedPaths); + if (!res.empty()) + res += ", "; + res += + fmt(ANSI_RED "%d untrusted" ANSI_NORMAL, state.untrustedPaths); } return res; @@ -473,10 +511,12 @@ class ProgressBar : public Logger std::optional ask(std::string_view msg) override { auto state(state_.lock()); - if (!state->active || !isatty(STDIN_FILENO)) return {}; + if (!state->active || !isatty(STDIN_FILENO)) + return {}; std::cerr << fmt("\r\e[K%s ", msg); auto s = trim(readLine(STDIN_FILENO)); - if (s.size() != 1) return {}; + if (s.size() != 1) + return {}; draw(*state); return s[0]; } @@ -484,10 +524,7 @@ class ProgressBar : public Logger Logger * makeProgressBar(bool printBuildLogs) { - return new ProgressBar( - printBuildLogs, - shouldANSI() - ); + return new ProgressBar(printBuildLogs, shouldANSI()); } void startProgressBar(bool printBuildLogs) @@ -498,8 +535,8 @@ void startProgressBar(bool printBuildLogs) void stopProgressBar() { auto progressBar = dynamic_cast(logger); - if (progressBar) progressBar->stop(); - + if (progressBar) + progressBar->stop(); } } diff --git a/src/libmain/shared.cc b/src/libmain/shared.cc index 31454e49d170..8c849eee6864 100644 --- a/src/libmain/shared.cc +++ b/src/libmain/shared.cc @@ -29,40 +29,42 @@ #include - namespace nix { - static bool gcWarning = true; void printGCWarning() { - if (!gcWarning) return; + if (!gcWarning) + return; static bool haveWarned = false; warnOnce(haveWarned, - "you did not specify '--add-root'; " - "the result might be removed by the garbage collector"); + "you did not specify '--add-root'; " + "the result might be removed by the garbage collector"); } - -void printMissing(ref store, const std::vector & paths, Verbosity lvl) +void printMissing(ref store, const std::vector & paths, + Verbosity lvl) { uint64_t downloadSize, narSize; StorePathSet willBuild, willSubstitute, unknown; - store->queryMissing(paths, willBuild, willSubstitute, unknown, downloadSize, narSize); - printMissing(store, willBuild, willSubstitute, unknown, downloadSize, narSize, lvl); + store->queryMissing(paths, willBuild, willSubstitute, unknown, downloadSize, + narSize); + printMissing(store, willBuild, willSubstitute, unknown, downloadSize, + narSize, lvl); } - void printMissing(ref store, const StorePathSet & willBuild, - const StorePathSet & willSubstitute, const StorePathSet & unknown, - uint64_t downloadSize, uint64_t narSize, Verbosity lvl) + const StorePathSet & willSubstitute, + const StorePathSet & unknown, uint64_t downloadSize, + uint64_t narSize, Verbosity lvl) { if (!willBuild.empty()) { if (willBuild.size() == 1) printMsg(lvl, "this derivation will be built:"); else - printMsg(lvl, "these %d derivations will be built:", willBuild.size()); + printMsg(lvl, + "these %d derivations will be built:", willBuild.size()); auto sorted = store->topoSortPaths(willBuild); reverse(sorted.begin(), sorted.end()); for (auto & i : sorted) @@ -73,14 +75,15 @@ void printMissing(ref store, const StorePathSet & willBuild, const float downloadSizeMiB = downloadSize / (1024.f * 1024.f); const float narSizeMiB = narSize / (1024.f * 1024.f); if (willSubstitute.size() == 1) { - printMsg(lvl, "this path will be fetched (%.2f MiB download, %.2f MiB unpacked):", - downloadSizeMiB, - narSizeMiB); + printMsg(lvl, + "this path will be fetched (%.2f MiB download, %.2f MiB " + "unpacked):", + downloadSizeMiB, narSizeMiB); } else { - printMsg(lvl, "these %d paths will be fetched (%.2f MiB download, %.2f MiB unpacked):", - willSubstitute.size(), - downloadSizeMiB, - narSizeMiB); + printMsg(lvl, + "these %d paths will be fetched (%.2f MiB download, %.2f " + "MiB unpacked):", + willSubstitute.size(), downloadSizeMiB, narSizeMiB); } for (auto & i : willSubstitute) printMsg(lvl, " %s", store->printStorePath(i)); @@ -88,22 +91,23 @@ void printMissing(ref store, const StorePathSet & willBuild, if (!unknown.empty()) { printMsg(lvl, "don't know how to build these paths%s:", - (settings.readOnlyMode ? " (may be caused by read-only store access)" : "")); + (settings.readOnlyMode + ? " (may be caused by read-only store access)" + : "")); for (auto & i : unknown) printMsg(lvl, " %s", store->printStorePath(i)); } } - -std::string getArg(const std::string & opt, - Strings::iterator & i, const Strings::iterator & end) +std::string getArg(const std::string & opt, Strings::iterator & i, + const Strings::iterator & end) { ++i; - if (i == end) throw UsageError("'%1%' requires an argument", opt); + if (i == end) + throw UsageError("'%1%' requires an argument", opt); return *i; } - #if OPENSSL_VERSION_NUMBER < 0x10101000L /* OpenSSL is not thread-safe by default - it will randomly crash unless the user supplies a mutex locking function. So let's do @@ -121,28 +125,31 @@ static void opensslLockCallback(int mode, int type, const char * file, int line) static std::once_flag dns_resolve_flag; -static void preloadNSS() { - /* builtin:fetchurl can trigger a DNS lookup, which with glibc can trigger a dynamic library load of - one of the glibc NSS libraries in a sandboxed child, which will fail unless the library's already - been loaded in the parent. So we force a lookup of an invalid domain to force the NSS machinery to - load its lookup libraries in the parent before any child gets a chance to. */ +static void preloadNSS() +{ + /* builtin:fetchurl can trigger a DNS lookup, which with glibc can trigger a + dynamic library load of one of the glibc NSS libraries in a sandboxed + child, which will fail unless the library's already been loaded in the + parent. So we force a lookup of an invalid domain to force the NSS + machinery to load its lookup libraries in the parent before any child + gets a chance to. */ std::call_once(dns_resolve_flag, []() { #ifdef __GLIBC__ /* On linux, glibc will run every lookup through the nss layer. - * That means every lookup goes, by default, through nscd, which acts as a local - * cache. - * Because we run builds in a sandbox, we also remove access to nscd otherwise - * lookups would leak into the sandbox. + * That means every lookup goes, by default, through nscd, which acts as + * a local cache. Because we run builds in a sandbox, we also remove + * access to nscd otherwise lookups would leak into the sandbox. * - * But now we have a new problem, we need to make sure the nss_dns backend that - * does the dns lookups when nscd is not available is loaded or available. + * But now we have a new problem, we need to make sure the nss_dns + * backend that does the dns lookups when nscd is not available is + * loaded or available. * - * We can't make it available without leaking nix's environment, so instead we'll - * load the backend, and configure nss so it does not try to run dns lookups - * through nscd. + * We can't make it available without leaking nix's environment, so + * instead we'll load the backend, and configure nss so it does not try + * to run dns lookups through nscd. * - * This is technically only used for builtins:fetch* functions so we only care - * about dns. + * This is technically only used for builtins:fetch* functions so we + * only care about dns. * * All other platforms are unaffected. */ @@ -154,8 +161,7 @@ static void preloadNSS() { }); } -static void sigHandler(int signo) { } - +static void sigHandler(int signo) {} void initNix() { @@ -188,7 +194,8 @@ void initNix() /* Install a dummy SIGUSR1 handler for use with pthread_kill(). */ act.sa_handler = sigHandler; - if (sigaction(SIGUSR1, &act, 0)) throw SysError("handling SIGUSR1"); + if (sigaction(SIGUSR1, &act, 0)) + throw SysError("handling SIGUSR1"); #if __APPLE__ /* HACK: on darwin, we need can’t use sigprocmask with SIGWINCH. @@ -196,7 +203,8 @@ void initNix() * can handle the rest. */ struct sigaction sa; sa.sa_handler = sigHandler; - if (sigaction(SIGWINCH, &sa, 0)) throw SysError("handling SIGWINCH"); + if (sigaction(SIGWINCH, &sa, 0)) + throw SysError("handling SIGWINCH"); #endif /* Register a SIGSEGV handler to detect stack overflows. */ @@ -223,56 +231,60 @@ void initNix() preloadNSS(); } - -LegacyArgs::LegacyArgs(const std::string & programName, - std::function parseArg) +LegacyArgs::LegacyArgs( + const std::string & programName, + std::function + parseArg) : MixCommonArgs(programName), parseArg(parseArg) { addFlag({ .longName = "no-build-output", .shortName = 'Q', .description = "Do not show build output.", - .handler = {[&]() {setLogFormat(LogFormat::raw); }}, + .handler = {[&]() { setLogFormat(LogFormat::raw); }}, }); addFlag({ .longName = "keep-failed", - .shortName ='K', + .shortName = 'K', .description = "Keep temporary directories of failed builds.", - .handler = {&(bool&) settings.keepFailed, true}, + .handler = {&(bool &) settings.keepFailed, true}, }); addFlag({ .longName = "keep-going", - .shortName ='k', + .shortName = 'k', .description = "Keep going after a build fails.", - .handler = {&(bool&) settings.keepGoing, true}, + .handler = {&(bool &) settings.keepGoing, true}, }); addFlag({ .longName = "fallback", .description = "Build from source if substitution fails.", - .handler = {&(bool&) settings.tryFallback, true}, + .handler = {&(bool &) settings.tryFallback, true}, }); auto intSettingAlias = [&](char shortName, const std::string & longName, - const std::string & description, const std::string & dest) - { - addFlag({ - .longName = longName, - .shortName = shortName, - .description = description, - .labels = {"n"}, - .handler = {[=](std::string s) { - auto n = string2IntWithUnitPrefix(s); - settings.set(dest, std::to_string(n)); - }} - }); + const std::string & description, + const std::string & dest) { + addFlag({.longName = longName, + .shortName = shortName, + .description = description, + .labels = {"n"}, + .handler = {[=](std::string s) { + auto n = string2IntWithUnitPrefix(s); + settings.set(dest, std::to_string(n)); + }}}); }; - intSettingAlias(0, "cores", "Maximum number of CPU cores to use inside a build.", "cores"); - intSettingAlias(0, "max-silent-time", "Number of seconds of silence before a build is killed.", "max-silent-time"); - intSettingAlias(0, "timeout", "Number of seconds before a build is killed.", "timeout"); + intSettingAlias(0, "cores", + "Maximum number of CPU cores to use inside a build.", + "cores"); + intSettingAlias(0, "max-silent-time", + "Number of seconds of silence before a build is killed.", + "max-silent-time"); + intSettingAlias(0, "timeout", "Number of seconds before a build is killed.", + "timeout"); addFlag({ .longName = "readonly-mode", @@ -290,23 +302,24 @@ LegacyArgs::LegacyArgs(const std::string & programName, .longName = "store", .description = "The URL of the Nix store to use.", .labels = {"store-uri"}, - .handler = {&(std::string&) settings.storeUri}, + .handler = {&(std::string &) settings.storeUri}, }); } - bool LegacyArgs::processFlag(Strings::iterator & pos, Strings::iterator end) { - if (MixCommonArgs::processFlag(pos, end)) return true; + if (MixCommonArgs::processFlag(pos, end)) + return true; bool res = parseArg(pos, end); - if (res) ++pos; + if (res) + ++pos; return res; } - bool LegacyArgs::processArgs(const Strings & args, bool finish) { - if (args.empty()) return true; + if (args.empty()) + return true; assert(args.size() == 1); Strings ss(args); auto pos = ss.begin(); @@ -315,24 +328,27 @@ bool LegacyArgs::processArgs(const Strings & args, bool finish) return true; } - -void parseCmdLine(int argc, char * * argv, - std::function parseArg) +void parseCmdLine( + int argc, char ** argv, + std::function + parseArg) { - parseCmdLine(std::string(baseNameOf(argv[0])), argvToStrings(argc, argv), parseArg); + parseCmdLine(std::string(baseNameOf(argv[0])), argvToStrings(argc, argv), + parseArg); } - -void parseCmdLine(const std::string & programName, const Strings & args, - std::function parseArg) +void parseCmdLine( + const std::string & programName, const Strings & args, + std::function + parseArg) { LegacyArgs(programName, parseArg).parseCmdline(args); } - void printVersion(const std::string & programName) { - std::cout << format("%1% (Nix) %2%") % programName % nixVersion << std::endl; + std::cout << format("%1% (Nix) %2%") % programName % nixVersion + << std::endl; if (verbosity > lvlInfo) { Strings cfg; #if HAVE_BOEHMGC @@ -340,19 +356,21 @@ void printVersion(const std::string & programName) #endif cfg.push_back("signed-caches"); std::cout << "System type: " << settings.thisSystem << "\n"; - std::cout << "Additional system types: " << concatStringsSep(", ", settings.extraPlatforms.get()) << "\n"; + std::cout << "Additional system types: " + << concatStringsSep(", ", settings.extraPlatforms.get()) + << "\n"; std::cout << "Features: " << concatStringsSep(", ", cfg) << "\n"; - std::cout << "System configuration file: " << settings.nixConfDir + "/nix.conf" << "\n"; - std::cout << "User configuration files: " << - concatStringsSep(":", settings.nixUserConfFiles) - << "\n"; + std::cout << "System configuration file: " + << settings.nixConfDir + "/nix.conf" + << "\n"; + std::cout << "User configuration files: " + << concatStringsSep(":", settings.nixUserConfFiles) << "\n"; std::cout << "Store directory: " << settings.nixStore << "\n"; std::cout << "State directory: " << settings.nixStateDir << "\n"; } throw Exit(); } - void showManPage(const std::string & name) { restoreProcessContext(); @@ -361,7 +379,6 @@ void showManPage(const std::string & name) throw SysError("command 'man %1%' failed", name.c_str()); } - int handleExceptions(const std::string & programName, std::function fun) { ReceiveInterrupts receiveInterrupts; // FIXME: need better place for this @@ -389,7 +406,8 @@ int handleExceptions(const std::string & programName, std::function fun) } catch (BaseError & e) { logError(e.info()); if (e.hasTrace() && !loggerSettings.showTrace.get()) - printError("(use '--show-trace' to show detailed location information)"); + printError( + "(use '--show-trace' to show detailed location information)"); return e.status; } catch (std::bad_alloc & e) { printError(error + "out of memory"); @@ -402,13 +420,15 @@ int handleExceptions(const std::string & programName, std::function fun) return 0; } - RunPager::RunPager() { - if (!isatty(STDOUT_FILENO)) return; + if (!isatty(STDOUT_FILENO)) + return; char * pager = getenv("NIX_PAGER"); - if (!pager) pager = getenv("PAGER"); - if (pager && ((std::string) pager == "" || (std::string) pager == "cat")) return; + if (!pager) + pager = getenv("PAGER"); + if (pager && ((std::string) pager == "" || (std::string) pager == "cat")) + return; Pipe toPager; toPager.create(); @@ -433,7 +453,6 @@ RunPager::RunPager() throw SysError("dupping stdout"); } - RunPager::~RunPager() { try { @@ -447,15 +466,13 @@ RunPager::~RunPager() } } - PrintFreed::~PrintFreed() { if (show) std::cout << fmt("%d store paths deleted, %s freed\n", - results.paths.size(), - showBytes(results.bytesFreed)); + results.paths.size(), showBytes(results.bytesFreed)); } -Exit::~Exit() { } +Exit::~Exit() {} } diff --git a/src/libmain/shared.hh b/src/libmain/shared.hh index 0cc56d47d174..40f1b327e073 100644 --- a/src/libmain/shared.hh +++ b/src/libmain/shared.hh @@ -10,28 +10,31 @@ #include - namespace nix { -class Exit : public std::exception -{ -public: +class Exit : public std::exception { + public: int status; - Exit() : status(0) { } - Exit(int status) : status(status) { } + Exit() : status(0) {} + Exit(int status) : status(status) {} virtual ~Exit(); }; -int handleExceptions(const std::string & programName, std::function fun); +int handleExceptions(const std::string & programName, + std::function fun); /* Don't forget to call initPlugins() after settings are initialized! */ void initNix(); -void parseCmdLine(int argc, char * * argv, - std::function parseArg); +void parseCmdLine( + int argc, char ** argv, + std::function + parseArg); -void parseCmdLine(const std::string & programName, const Strings & args, - std::function parseArg); +void parseCmdLine( + const std::string & programName, const Strings & args, + std::function + parseArg); void printVersion(const std::string & programName); @@ -41,77 +44,75 @@ void printGCWarning(); class Store; struct StorePathWithOutputs; -void printMissing( - ref store, - const std::vector & paths, - Verbosity lvl = lvlInfo); +void printMissing(ref store, const std::vector & paths, + Verbosity lvl = lvlInfo); void printMissing(ref store, const StorePathSet & willBuild, - const StorePathSet & willSubstitute, const StorePathSet & unknown, - uint64_t downloadSize, uint64_t narSize, Verbosity lvl = lvlInfo); + const StorePathSet & willSubstitute, + const StorePathSet & unknown, uint64_t downloadSize, + uint64_t narSize, Verbosity lvl = lvlInfo); -std::string getArg(const std::string & opt, - Strings::iterator & i, const Strings::iterator & end); +std::string getArg(const std::string & opt, Strings::iterator & i, + const Strings::iterator & end); -template N getIntArg(const std::string & opt, - Strings::iterator & i, const Strings::iterator & end, bool allowUnit) +template +N getIntArg(const std::string & opt, Strings::iterator & i, + const Strings::iterator & end, bool allowUnit) { ++i; - if (i == end) throw UsageError("'%1%' requires an argument", opt); + if (i == end) + throw UsageError("'%1%' requires an argument", opt); return string2IntWithUnitPrefix(*i); } - -struct LegacyArgs : public MixCommonArgs -{ - std::function parseArg; +struct LegacyArgs : public MixCommonArgs { + std::function + parseArg; LegacyArgs(const std::string & programName, - std::function parseArg); + std::function + parseArg); bool processFlag(Strings::iterator & pos, Strings::iterator end) override; bool processArgs(const Strings & args, bool finish) override; }; - /* Show the manual page for the specified program. */ void showManPage(const std::string & name); /* The constructor of this class starts a pager if stdout is a terminal and $PAGER is set. Stdout is redirected to the pager. */ -class RunPager -{ -public: +class RunPager { + public: RunPager(); ~RunPager(); -private: + private: Pid pid; int stdout; }; extern volatile ::sig_atomic_t blockInt; - /* GC helpers. */ std::string showBytes(uint64_t bytes); struct GCResults; -struct PrintFreed -{ +struct PrintFreed { bool show; const GCResults & results; PrintFreed(bool show, const GCResults & results) - : show(show), results(results) { } + : show(show), results(results) + { + } ~PrintFreed(); }; - /* Install a SIGSEGV handler to detect stack overflows. */ void detectStackOverflow(); - } diff --git a/src/libmain/stack.cc b/src/libmain/stack.cc index b0a4a4c5dbe4..e32ffd1358a3 100644 --- a/src/libmain/stack.cc +++ b/src/libmain/stack.cc @@ -9,7 +9,6 @@ namespace nix { - static void sigsegvHandler(int signo, siginfo_t * info, void * ctx) { /* Detect stack overflows by comparing the faulting address with @@ -27,9 +26,11 @@ static void sigsegvHandler(int signo, siginfo_t * info, void * ctx) if (haveSP) { ptrdiff_t diff = (char *) info->si_addr - sp; - if (diff < 0) diff = -diff; + if (diff < 0) + diff = -diff; if (diff < 4096) { - char msg[] = "error: stack overflow (possible infinite recursion)\n"; + char msg[] = + "error: stack overflow (possible infinite recursion)\n"; [[gnu::unused]] auto res = write(2, msg, strlen(msg)); _exit(1); // maybe abort instead? } @@ -40,13 +41,13 @@ static void sigsegvHandler(int signo, siginfo_t * info, void * ctx) sigfillset(&act.sa_mask); act.sa_handler = SIG_DFL; act.sa_flags = 0; - if (sigaction(SIGSEGV, &act, 0)) abort(); + if (sigaction(SIGSEGV, &act, 0)) + abort(); } - void detectStackOverflow() { -#if defined(SA_SIGINFO) && defined (SA_ONSTACK) +#if defined(SA_SIGINFO) && defined(SA_ONSTACK) /* Install a SIGSEGV handler to detect stack overflows. This requires an alternative stack, otherwise the signal cannot be delivered when we're out of stack space. */ @@ -54,9 +55,11 @@ void detectStackOverflow() stack.ss_size = 4096 * 4 + MINSIGSTKSZ; static auto stackBuf = std::make_unique>(stack.ss_size); stack.ss_sp = stackBuf->data(); - if (!stack.ss_sp) throw Error("cannot allocate alternative stack"); + if (!stack.ss_sp) + throw Error("cannot allocate alternative stack"); stack.ss_flags = 0; - if (sigaltstack(&stack, 0) == -1) throw SysError("cannot set alternative stack"); + if (sigaltstack(&stack, 0) == -1) + throw SysError("cannot set alternative stack"); struct sigaction act; sigfillset(&act.sa_mask); @@ -67,5 +70,4 @@ void detectStackOverflow() #endif } - } diff --git a/src/libstore/binary-cache-store.cc b/src/libstore/binary-cache-store.cc index 9226c4e193c8..29d8171c034c 100644 --- a/src/libstore/binary-cache-store.cc +++ b/src/libstore/binary-cache-store.cc @@ -23,11 +23,11 @@ namespace nix { BinaryCacheStore::BinaryCacheStore(const Params & params) - : BinaryCacheStoreConfig(params) - , Store(params) + : BinaryCacheStoreConfig(params), Store(params) { if (secretKeyFile != "") - secretKey = std::unique_ptr(new SecretKey(readFile(secretKeyFile))); + secretKey = + std::unique_ptr(new SecretKey(readFile(secretKeyFile))); StringSink sink; sink << narVersionMagic1; @@ -40,17 +40,20 @@ void BinaryCacheStore::init() auto cacheInfo = getFile(cacheInfoFile); if (!cacheInfo) { - upsertFile(cacheInfoFile, "StoreDir: " + storeDir + "\n", "text/x-nix-cache-info"); + upsertFile(cacheInfoFile, "StoreDir: " + storeDir + "\n", + "text/x-nix-cache-info"); } else { for (auto & line : tokenizeString(*cacheInfo, "\n")) { - size_t colon= line.find(':'); - if (colon ==std::string::npos) continue; + size_t colon = line.find(':'); + if (colon == std::string::npos) + continue; auto name = line.substr(0, colon); auto value = trim(line.substr(colon + 1, std::string::npos)); if (name == "StoreDir") { if (value != storeDir) - throw Error("binary cache '%s' is for Nix stores with prefix '%s', not '%s'", - getUri(), value, storeDir); + throw Error("binary cache '%s' is for Nix stores with " + "prefix '%s', not '%s'", + getUri(), value, storeDir); } else if (name == "WantMassQuery") { wantMassQuery.setDefault(value == "1"); } else if (name == "Priority") { @@ -60,32 +63,34 @@ void BinaryCacheStore::init() } } -void BinaryCacheStore::upsertFile(const std::string & path, - std::string && data, - const std::string & mimeType) +void BinaryCacheStore::upsertFile(const std::string & path, std::string && data, + const std::string & mimeType) { - upsertFile(path, std::make_shared(std::move(data)), mimeType); + upsertFile(path, std::make_shared(std::move(data)), + mimeType); } -void BinaryCacheStore::getFile(const std::string & path, +void BinaryCacheStore::getFile( + const std::string & path, Callback> callback) noexcept { try { callback(getFile(path)); - } catch (...) { callback.rethrow(); } + } catch (...) { + callback.rethrow(); + } } void BinaryCacheStore::getFile(const std::string & path, Sink & sink) { std::promise> promise; - getFile(path, - {[&](std::future> result) { - try { - promise.set_value(result.get()); - } catch (...) { - promise.set_exception(std::current_exception()); - } - }}); + getFile(path, {[&](std::future> result) { + try { + promise.set_value(result.get()); + } catch (...) { + promise.set_exception(std::current_exception()); + } + }}); sink(*promise.get_future().get()); } @@ -115,11 +120,13 @@ void BinaryCacheStore::writeNarInfo(ref narInfo) auto state_(state.lock()); state_->pathInfoCache.upsert( std::string(narInfo->path.to_string()), - PathInfoCacheValue { .value = std::shared_ptr(narInfo) }); + PathInfoCacheValue{.value = std::shared_ptr(narInfo)}); } if (diskCache) - diskCache->upsertNarInfo(getUri(), std::string(narInfo->path.hashPart()), std::shared_ptr(narInfo)); + diskCache->upsertNarInfo(getUri(), + std::string(narInfo->path.hashPart()), + std::shared_ptr(narInfo)); } AutoCloseFD openFile(const Path & path) @@ -143,18 +150,20 @@ ref BinaryCacheStore::addToStoreCommon( /* Read the NAR simultaneously into a CompressionSink+FileSink (to write the compressed NAR to disk), into a HashSink (to get the NAR hash), and into a NarAccessor (to get the NAR listing). */ - HashSink fileHashSink { htSHA256 }; + HashSink fileHashSink{htSHA256}; std::shared_ptr narAccessor; - HashSink narHashSink { htSHA256 }; + HashSink narHashSink{htSHA256}; { - FdSink fileSink(fdTemp.get()); - TeeSink teeSinkCompressed { fileSink, fileHashSink }; - auto compressionSink = makeCompressionSink(compression, teeSinkCompressed, parallelCompression, compressionLevel); - TeeSink teeSinkUncompressed { *compressionSink, narHashSink }; - TeeSource teeSource { narSource, teeSinkUncompressed }; - narAccessor = makeNarAccessor(teeSource); - compressionSink->finish(); - fileSink.flush(); + FdSink fileSink(fdTemp.get()); + TeeSink teeSinkCompressed{fileSink, fileHashSink}; + auto compressionSink = + makeCompressionSink(compression, teeSinkCompressed, + parallelCompression, compressionLevel); + TeeSink teeSinkUncompressed{*compressionSink, narHashSink}; + TeeSource teeSource{narSource, teeSinkUncompressed}; + narAccessor = makeNarAccessor(teeSource); + compressionSink->finish(); + fileSink.flush(); } auto now2 = std::chrono::steady_clock::now(); @@ -165,20 +174,24 @@ ref BinaryCacheStore::addToStoreCommon( auto [fileHash, fileSize] = fileHashSink.finish(); narInfo->fileHash = fileHash; narInfo->fileSize = fileSize; - narInfo->url = "nar/" + narInfo->fileHash->to_string(Base32, false) + ".nar" - + (compression == "xz" ? ".xz" : - compression == "bzip2" ? ".bz2" : - compression == "zstd" ? ".zst" : - compression == "lzip" ? ".lzip" : - compression == "lz4" ? ".lz4" : - compression == "br" ? ".br" : - ""); - - auto duration = std::chrono::duration_cast(now2 - now1).count(); - printMsg(lvlTalkative, "copying path '%1%' (%2% bytes, compressed %3$.1f%% in %4% ms) to binary cache", - printStorePath(narInfo->path), info.narSize, - ((1.0 - (double) fileSize / info.narSize) * 100.0), - duration); + narInfo->url = "nar/" + narInfo->fileHash->to_string(Base32, false) + + ".nar" + + (compression == "xz" ? ".xz" + : compression == "bzip2" ? ".bz2" + : compression == "zstd" ? ".zst" + : compression == "lzip" ? ".lzip" + : compression == "lz4" ? ".lz4" + : compression == "br" ? ".br" + : ""); + + auto duration = + std::chrono::duration_cast(now2 - now1) + .count(); + printMsg(lvlTalkative, + "copying path '%1%' (%2% bytes, compressed %3$.1f%% in %4% ms) to " + "binary cache", + printStorePath(narInfo->path), info.narSize, + ((1.0 - (double) fileSize / info.narSize) * 100.0), duration); /* Verify that all references are valid. This may do some .narinfo reads, but typically they'll already be cached. */ @@ -187,8 +200,9 @@ ref BinaryCacheStore::addToStoreCommon( if (ref != info.path) queryPathInfo(ref); } catch (InvalidPath &) { - throw Error("cannot add '%s' to the binary cache because the reference '%s' is not valid", - printStorePath(info.path), printStorePath(ref)); + throw Error("cannot add '%s' to the binary cache because the " + "reference '%s' is not valid", + printStorePath(info.path), printStorePath(ref)); } /* Optionally write a JSON file containing a listing of the @@ -206,7 +220,8 @@ ref BinaryCacheStore::addToStoreCommon( } } - upsertFile(std::string(info.path.hashPart()) + ".ls", jsonOut.str(), "application/json"); + upsertFile(std::string(info.path.hashPart()) + ".ls", jsonOut.str(), + "application/json"); } /* Optionally maintain an index of DWARF debug info files @@ -220,7 +235,8 @@ ref BinaryCacheStore::addToStoreCommon( ThreadPool threadPool(25); - auto doFile = [&](std::string member, std::string key, std::string target) { + auto doFile = [&](std::string member, std::string key, + std::string target) { checkInterrupt(); nlohmann::json json; @@ -229,9 +245,12 @@ ref BinaryCacheStore::addToStoreCommon( // FIXME: or should we overwrite? The previous link may point // to a GC'ed file, so overwriting might be useful... - if (fileExists(key)) return; + if (fileExists(key)) + return; - printMsg(lvlTalkative, "creating debuginfo link from '%s' to '%s'", key, target); + printMsg(lvlTalkative, + "creating debuginfo link from '%s' to '%s'", key, + target); upsertFile(key, json.dump(), "application/json"); }; @@ -242,15 +261,16 @@ ref BinaryCacheStore::addToStoreCommon( for (auto & s1 : narAccessor->readDirectory(buildIdDir)) { auto dir = buildIdDir + "/" + s1; - if (narAccessor->stat(dir).type != FSAccessor::tDirectory - || !std::regex_match(s1, regex1)) + if (narAccessor->stat(dir).type != FSAccessor::tDirectory || + !std::regex_match(s1, regex1)) continue; for (auto & s2 : narAccessor->readDirectory(dir)) { auto debugPath = dir + "/" + s2; - if (narAccessor->stat(debugPath).type != FSAccessor::tRegular - || !std::regex_match(s2, regex2)) + if (narAccessor->stat(debugPath).type != + FSAccessor::tRegular || + !std::regex_match(s2, regex2)) continue; auto buildId = s1 + s2; @@ -258,7 +278,8 @@ ref BinaryCacheStore::addToStoreCommon( std::string key = "debuginfo/" + buildId; std::string target = "../" + narInfo->url; - threadPool.enqueue(std::bind(doFile, std::string(debugPath, 1), key, target)); + threadPool.enqueue(std::bind( + doFile, std::string(debugPath, 1), key, target)); } } @@ -270,8 +291,9 @@ ref BinaryCacheStore::addToStoreCommon( if (repair || !fileExists(narInfo->url)) { stats.narWrite++; upsertFile(narInfo->url, - std::make_shared(fnTemp, std::ios_base::in | std::ios_base::binary), - "application/x-nix-nar"); + std::make_shared( + fnTemp, std::ios_base::in | std::ios_base::binary), + "application/x-nix-nar"); } else stats.narWriteAverted++; @@ -280,7 +302,8 @@ ref BinaryCacheStore::addToStoreCommon( stats.narWriteCompressionTimeMs += duration; /* Atomically write the NAR info file.*/ - if (secretKey) narInfo->sign(*this, *secretKey); + if (secretKey) + narInfo->sign(*this, *secretKey); writeNarInfo(narInfo); @@ -289,8 +312,9 @@ ref BinaryCacheStore::addToStoreCommon( return narInfo; } -void BinaryCacheStore::addToStore(const ValidPathInfo & info, Source & narSource, - RepairFlag repair, CheckSigsFlag checkSigs) +void BinaryCacheStore::addToStore(const ValidPathInfo & info, + Source & narSource, RepairFlag repair, + CheckSigsFlag checkSigs) { if (!repair && isValidPath(info.path)) { // FIXME: copyNAR -> null sink @@ -299,28 +323,34 @@ void BinaryCacheStore::addToStore(const ValidPathInfo & info, Source & narSource } addToStoreCommon(narSource, repair, checkSigs, {[&](HashResult nar) { - /* FIXME reinstate these, once we can correctly do hash modulo sink as - needed. We need to throw here in case we uploaded a corrupted store path. */ - // assert(info.narHash == nar.first); - // assert(info.narSize == nar.second); - return info; - }}); + /* FIXME reinstate these, once we can correctly do hash + modulo sink as + needed. We need to throw here in case we uploaded a + corrupted store path. */ + // assert(info.narHash == nar.first); + // assert(info.narSize == nar.second); + return info; + }}); } -StorePath BinaryCacheStore::addToStoreFromDump(Source & dump, std::string_view name, - FileIngestionMethod method, HashType hashAlgo, RepairFlag repair, const StorePathSet & references) +StorePath BinaryCacheStore::addToStoreFromDump( + Source & dump, std::string_view name, FileIngestionMethod method, + HashType hashAlgo, RepairFlag repair, const StorePathSet & references) { if (method != FileIngestionMethod::Recursive || hashAlgo != htSHA256) unsupported("addToStoreFromDump"); - return addToStoreCommon(dump, repair, CheckSigs, [&](HashResult nar) { - ValidPathInfo info { - makeFixedOutputPath(method, nar.first, name, references), - nar.first, - }; - info.narSize = nar.second; - info.references = references; - return info; - })->path; + return addToStoreCommon( + dump, repair, CheckSigs, + [&](HashResult nar) { + ValidPathInfo info{ + makeFixedOutputPath(method, nar.first, name, references), + nar.first, + }; + info.narSize = nar.second; + info.references = references; + return info; + }) + ->path; } bool BinaryCacheStore::isValidPathUncached(const StorePath & storePath) @@ -336,7 +366,7 @@ void BinaryCacheStore::narFromPath(const StorePath & storePath, Sink & sink) auto info = queryPathInfo(storePath).cast(); LengthSink narSize; - TeeSink tee { sink, narSize }; + TeeSink tee{sink, narSize}; auto decompressor = makeDecompressionSink(info->compression, tee); @@ -349,56 +379,60 @@ void BinaryCacheStore::narFromPath(const StorePath & storePath, Sink & sink) decompressor->finish(); stats.narRead++; - //stats.narReadCompressedBytes += nar->size(); // FIXME + // stats.narReadCompressedBytes += nar->size(); // FIXME stats.narReadBytes += narSize.length; } -void BinaryCacheStore::queryPathInfoUncached(const StorePath & storePath, +void BinaryCacheStore::queryPathInfoUncached( + const StorePath & storePath, Callback> callback) noexcept { auto uri = getUri(); auto storePathS = printStorePath(storePath); - auto act = std::make_shared(*logger, lvlTalkative, actQueryPathInfo, - fmt("querying info about '%s' on '%s'", storePathS, uri), Logger::Fields{storePathS, uri}); + auto act = std::make_shared( + *logger, lvlTalkative, actQueryPathInfo, + fmt("querying info about '%s' on '%s'", storePathS, uri), + Logger::Fields{storePathS, uri}); PushActivity pact(act->id); auto narInfoFile = narInfoFileFor(storePath); - auto callbackPtr = std::make_shared(std::move(callback)); + auto callbackPtr = + std::make_shared(std::move(callback)); - getFile(narInfoFile, - {[=](std::future> fut) { - try { - auto data = fut.get(); + getFile(narInfoFile, {[=](std::future> fut) { + try { + auto data = fut.get(); - if (!data) return (*callbackPtr)({}); + if (!data) + return (*callbackPtr)({}); - stats.narInfoRead++; + stats.narInfoRead++; - (*callbackPtr)((std::shared_ptr) - std::make_shared(*this, *data, narInfoFile)); + (*callbackPtr)((std::shared_ptr) + std::make_shared(*this, *data, + narInfoFile)); - (void) act; // force Activity into this lambda to ensure it stays alive - } catch (...) { - callbackPtr->rethrow(); - } - }}); + (void) act; // force Activity into this lambda to ensure it + // stays alive + } catch (...) { + callbackPtr->rethrow(); + } + }}); } -StorePath BinaryCacheStore::addToStore( - std::string_view name, - const Path & srcPath, - FileIngestionMethod method, - HashType hashAlgo, - PathFilter & filter, - RepairFlag repair, - const StorePathSet & references) +StorePath BinaryCacheStore::addToStore(std::string_view name, + const Path & srcPath, + FileIngestionMethod method, + HashType hashAlgo, PathFilter & filter, + RepairFlag repair, + const StorePathSet & references) { /* FIXME: Make BinaryCacheStore::addToStoreCommon support non-recursive+sha256 so we can just use the default implementation of this method in terms of addToStoreFromDump. */ - HashSink sink { hashAlgo }; + HashSink sink{hashAlgo}; if (method == FileIngestionMethod::Recursive) { dumpPath(srcPath, sink, filter); } else { @@ -406,29 +440,30 @@ StorePath BinaryCacheStore::addToStore( } auto h = sink.finish().first; - auto source = sinkToSource([&](Sink & sink) { - dumpPath(srcPath, sink, filter); - }); - return addToStoreCommon(*source, repair, CheckSigs, [&](HashResult nar) { - ValidPathInfo info { - makeFixedOutputPath(method, h, name, references), - nar.first, - }; - info.narSize = nar.second; - info.references = references; - info.ca = FixedOutputHash { - .method = method, - .hash = h, - }; - return info; - })->path; + auto source = + sinkToSource([&](Sink & sink) { dumpPath(srcPath, sink, filter); }); + return addToStoreCommon( + *source, repair, CheckSigs, + [&](HashResult nar) { + ValidPathInfo info{ + makeFixedOutputPath(method, h, name, references), + nar.first, + }; + info.narSize = nar.second; + info.references = references; + info.ca = FixedOutputHash{ + .method = method, + .hash = h, + }; + return info; + }) + ->path; } -StorePath BinaryCacheStore::addTextToStore( - std::string_view name, - std::string_view s, - const StorePathSet & references, - RepairFlag repair) +StorePath BinaryCacheStore::addTextToStore(std::string_view name, + std::string_view s, + const StorePathSet & references, + RepairFlag repair) { auto textHash = hashString(htSHA256, s); auto path = makeTextPath(name, textHash, references); @@ -439,41 +474,48 @@ StorePath BinaryCacheStore::addTextToStore( StringSink sink; dumpString(s, sink); StringSource source(sink.s); - return addToStoreCommon(source, repair, CheckSigs, [&](HashResult nar) { - ValidPathInfo info { path, nar.first }; - info.narSize = nar.second; - info.ca = TextHash { textHash }; - info.references = references; - return info; - })->path; + return addToStoreCommon(source, repair, CheckSigs, + [&](HashResult nar) { + ValidPathInfo info{path, nar.first}; + info.narSize = nar.second; + info.ca = TextHash{textHash}; + info.references = references; + return info; + }) + ->path; } -void BinaryCacheStore::queryRealisationUncached(const DrvOutput & id, +void BinaryCacheStore::queryRealisationUncached( + const DrvOutput & id, Callback> callback) noexcept { - auto outputInfoFilePath = realisationsPrefix + "/" + id.to_string() + ".doi"; + auto outputInfoFilePath = + realisationsPrefix + "/" + id.to_string() + ".doi"; - auto callbackPtr = std::make_shared(std::move(callback)); + auto callbackPtr = + std::make_shared(std::move(callback)); Callback> newCallback = { [=](std::future> fut) { try { auto data = fut.get(); - if (!data) return (*callbackPtr)({}); + if (!data) + return (*callbackPtr)({}); auto realisation = Realisation::fromJSON( nlohmann::json::parse(*data), outputInfoFilePath); - return (*callbackPtr)(std::make_shared(realisation)); + return (*callbackPtr)( + std::make_shared(realisation)); } catch (...) { callbackPtr->rethrow(); } - } - }; + }}; getFile(outputInfoFilePath, std::move(newCallback)); } -void BinaryCacheStore::registerDrvOutput(const Realisation& info) { +void BinaryCacheStore::registerDrvOutput(const Realisation & info) +{ if (diskCache) diskCache->upsertRealisation(getUri(), info); auto filePath = realisationsPrefix + "/" + info.id.to_string() + ".doi"; @@ -482,10 +524,12 @@ void BinaryCacheStore::registerDrvOutput(const Realisation& info) { ref BinaryCacheStore::getFSAccessor() { - return make_ref(ref(shared_from_this()), localNarCache); + return make_ref(ref(shared_from_this()), + localNarCache); } -void BinaryCacheStore::addSignatures(const StorePath & storePath, const StringSet & sigs) +void BinaryCacheStore::addSignatures(const StorePath & storePath, + const StringSet & sigs) { /* Note: this is inherently racy since there is no locking on binary caches. In particular, with S3 this unreliable, even @@ -507,7 +551,8 @@ std::optional BinaryCacheStore::getBuildLog(const StorePath & path) try { auto info = queryPathInfo(path); // FIXME: add a "Log" field to .narinfo - if (!info->deriver) return std::nullopt; + if (!info->deriver) + return std::nullopt; drvPath = *info->deriver; } catch (InvalidPath &) { return std::nullopt; @@ -521,14 +566,14 @@ std::optional BinaryCacheStore::getBuildLog(const StorePath & path) return getFile(logPath); } -void BinaryCacheStore::addBuildLog(const StorePath & drvPath, std::string_view log) +void BinaryCacheStore::addBuildLog(const StorePath & drvPath, + std::string_view log) { assert(drvPath.isDerivation()); - upsertFile( - "log/" + std::string(drvPath.to_string()), - (std::string) log, // FIXME: don't copy - "text/plain; charset=utf-8"); + upsertFile("log/" + std::string(drvPath.to_string()), + (std::string) log, // FIXME: don't copy + "text/plain; charset=utf-8"); } } diff --git a/src/libstore/binary-cache-store.hh b/src/libstore/binary-cache-store.hh index ca538b3cbfb7..bc8bdb17231a 100644 --- a/src/libstore/binary-cache-store.hh +++ b/src/libstore/binary-cache-store.hh @@ -12,51 +12,60 @@ namespace nix { struct NarInfo; -struct BinaryCacheStoreConfig : virtual StoreConfig -{ +struct BinaryCacheStoreConfig : virtual StoreConfig { using StoreConfig::StoreConfig; - const Setting compression{(StoreConfig*) this, "xz", "compression", "NAR compression method ('xz', 'bzip2', 'gzip', 'zstd', or 'none')"}; - const Setting writeNARListing{(StoreConfig*) this, false, "write-nar-listing", "whether to write a JSON file listing the files in each NAR"}; - const Setting writeDebugInfo{(StoreConfig*) this, false, "index-debug-info", "whether to index DWARF debug info files by build ID"}; - const Setting secretKeyFile{(StoreConfig*) this, "", "secret-key", "path to secret key used to sign the binary cache"}; - const Setting localNarCache{(StoreConfig*) this, "", "local-nar-cache", "path to a local cache of NARs"}; - const Setting parallelCompression{(StoreConfig*) this, false, "parallel-compression", - "enable multi-threading compression for NARs, available for xz and zstd only currently"}; - const Setting compressionLevel{(StoreConfig*) this, -1, "compression-level", + const Setting compression{ + (StoreConfig *) this, "xz", "compression", + "NAR compression method ('xz', 'bzip2', 'gzip', 'zstd', or 'none')"}; + const Setting writeNARListing{ + (StoreConfig *) this, false, "write-nar-listing", + "whether to write a JSON file listing the files in each NAR"}; + const Setting writeDebugInfo{ + (StoreConfig *) this, false, "index-debug-info", + "whether to index DWARF debug info files by build ID"}; + const Setting secretKeyFile{ + (StoreConfig *) this, "", "secret-key", + "path to secret key used to sign the binary cache"}; + const Setting localNarCache{(StoreConfig *) this, "", + "local-nar-cache", + "path to a local cache of NARs"}; + const Setting parallelCompression{ + (StoreConfig *) this, false, "parallel-compression", + "enable multi-threading compression for NARs, available for xz and " + "zstd only currently"}; + const Setting compressionLevel{ + (StoreConfig *) this, -1, "compression-level", "specify 'preset level' of compression to be used with NARs: " - "meaning and accepted range of values depends on compression method selected, " - "other than -1 which we reserve to indicate Nix defaults should be used"}; + "meaning and accepted range of values depends on compression method " + "selected, " + "other than -1 which we reserve to indicate Nix defaults should be " + "used"}; }; class BinaryCacheStore : public virtual BinaryCacheStoreConfig, - public virtual Store, - public virtual LogStore -{ - -private: + public virtual Store, + public virtual LogStore { + private: std::unique_ptr secretKey; -protected: - + protected: // The prefix under which realisation infos will be stored const std::string realisationsPrefix = "realisations"; BinaryCacheStore(const Params & params); -public: - + public: virtual bool fileExists(const std::string & path) = 0; virtual void upsertFile(const std::string & path, - std::shared_ptr> istream, - const std::string & mimeType) = 0; + std::shared_ptr> istream, + const std::string & mimeType) = 0; void upsertFile(const std::string & path, - // FIXME: use std::string_view - std::string && data, - const std::string & mimeType); + // FIXME: use std::string_view + std::string && data, const std::string & mimeType); /* Note: subclasses must implement at least one of the two following getFile() methods. */ @@ -66,74 +75,73 @@ public: /* Fetch the specified file and call the specified callback with the result. A subclass may implement this asynchronously. */ - virtual void getFile( - const std::string & path, - Callback> callback) noexcept; + virtual void + getFile(const std::string & path, + Callback> callback) noexcept; std::optional getFile(const std::string & path); -public: - + public: virtual void init() override; -private: - + private: std::string narMagic; std::string narInfoFileFor(const StorePath & storePath); void writeNarInfo(ref narInfo); - ref addToStoreCommon( - Source & narSource, RepairFlag repair, CheckSigsFlag checkSigs, - std::function mkInfo); - -public: + ref + addToStoreCommon(Source & narSource, RepairFlag repair, + CheckSigsFlag checkSigs, + std::function mkInfo); + public: bool isValidPathUncached(const StorePath & path) override; void queryPathInfoUncached(const StorePath & path, - Callback> callback) noexcept override; + Callback> + callback) noexcept override; - std::optional queryPathFromHashPart(const std::string & hashPart) override - { unsupported("queryPathFromHashPart"); } + std::optional + queryPathFromHashPart(const std::string & hashPart) override + { + unsupported("queryPathFromHashPart"); + } void addToStore(const ValidPathInfo & info, Source & narSource, - RepairFlag repair, CheckSigsFlag checkSigs) override; + RepairFlag repair, CheckSigsFlag checkSigs) override; StorePath addToStoreFromDump(Source & dump, std::string_view name, - FileIngestionMethod method, HashType hashAlgo, RepairFlag repair, const StorePathSet & references) override; - - StorePath addToStore( - std::string_view name, - const Path & srcPath, - FileIngestionMethod method, - HashType hashAlgo, - PathFilter & filter, - RepairFlag repair, - const StorePathSet & references) override; - - StorePath addTextToStore( - std::string_view name, - std::string_view s, - const StorePathSet & references, - RepairFlag repair) override; + FileIngestionMethod method, HashType hashAlgo, + RepairFlag repair, + const StorePathSet & references) override; + + StorePath addToStore(std::string_view name, const Path & srcPath, + FileIngestionMethod method, HashType hashAlgo, + PathFilter & filter, RepairFlag repair, + const StorePathSet & references) override; + + StorePath addTextToStore(std::string_view name, std::string_view s, + const StorePathSet & references, + RepairFlag repair) override; void registerDrvOutput(const Realisation & info) override; void queryRealisationUncached(const DrvOutput &, - Callback> callback) noexcept override; + Callback> + callback) noexcept override; void narFromPath(const StorePath & path, Sink & sink) override; ref getFSAccessor() override; - void addSignatures(const StorePath & storePath, const StringSet & sigs) override; + void addSignatures(const StorePath & storePath, + const StringSet & sigs) override; std::optional getBuildLog(const StorePath & path) override; void addBuildLog(const StorePath & drvPath, std::string_view log) override; - }; MakeError(NoSuchBinaryCacheFile, Error); diff --git a/src/libstore/build-result.hh b/src/libstore/build-result.hh index 24fb1f763d47..58c92ca6f6c4 100644 --- a/src/libstore/build-result.hh +++ b/src/libstore/build-result.hh @@ -6,11 +6,9 @@ #include #include - namespace nix { -struct BuildResult -{ +struct BuildResult { /* Note: don't remove status codes, and only add new status codes at the end of the list, to prevent client/server incompatibilities in the nix-store --serve protocol. */ @@ -22,7 +20,7 @@ struct BuildResult InputRejected, OutputRejected, TransientFailure, // possibly transient - CachedFailure, // no longer used + CachedFailure, // no longer used TimedOut, MiscFailure, DependencyFailed, @@ -35,24 +33,40 @@ struct BuildResult // FIXME: include entire ErrorInfo object. std::string errorMsg; - std::string toString() const { + std::string toString() const + { auto strStatus = [&]() { switch (status) { - case Built: return "Built"; - case Substituted: return "Substituted"; - case AlreadyValid: return "AlreadyValid"; - case PermanentFailure: return "PermanentFailure"; - case InputRejected: return "InputRejected"; - case OutputRejected: return "OutputRejected"; - case TransientFailure: return "TransientFailure"; - case CachedFailure: return "CachedFailure"; - case TimedOut: return "TimedOut"; - case MiscFailure: return "MiscFailure"; - case DependencyFailed: return "DependencyFailed"; - case LogLimitExceeded: return "LogLimitExceeded"; - case NotDeterministic: return "NotDeterministic"; - case ResolvesToAlreadyValid: return "ResolvesToAlreadyValid"; - default: return "Unknown"; + case Built: + return "Built"; + case Substituted: + return "Substituted"; + case AlreadyValid: + return "AlreadyValid"; + case PermanentFailure: + return "PermanentFailure"; + case InputRejected: + return "InputRejected"; + case OutputRejected: + return "OutputRejected"; + case TransientFailure: + return "TransientFailure"; + case CachedFailure: + return "CachedFailure"; + case TimedOut: + return "TimedOut"; + case MiscFailure: + return "MiscFailure"; + case DependencyFailed: + return "DependencyFailed"; + case LogLimitExceeded: + return "LogLimitExceeded"; + case NotDeterministic: + return "NotDeterministic"; + case ResolvesToAlreadyValid: + return "ResolvesToAlreadyValid"; + default: + return "Unknown"; }; }(); return strStatus + ((errorMsg == "") ? "" : " : " + errorMsg); @@ -80,13 +94,11 @@ struct BuildResult bool success() { - return status == Built || status == Substituted || status == AlreadyValid || status == ResolvesToAlreadyValid; + return status == Built || status == Substituted || + status == AlreadyValid || status == ResolvesToAlreadyValid; } - void rethrow() - { - throw Error("%s", errorMsg); - } + void rethrow() { throw Error("%s", errorMsg); } }; } diff --git a/src/libstore/build/derivation-goal.cc b/src/libstore/build/derivation-goal.cc index 3fff2385fdac..1f06277d7352 100644 --- a/src/libstore/build/derivation-goal.cc +++ b/src/libstore/build/derivation-goal.cc @@ -49,7 +49,8 @@ #if HAVE_SECCOMP #include #endif -#define pivot_root(new_root, put_old) (syscall(SYS_pivot_root, new_root, put_old)) +#define pivot_root(new_root, put_old) \ + (syscall(SYS_pivot_root, new_root, put_old)) #endif #if __APPLE__ @@ -65,41 +66,43 @@ namespace nix { DerivationGoal::DerivationGoal(const StorePath & drvPath, - const StringSet & wantedOutputs, Worker & worker, BuildMode buildMode) - : Goal(worker, DerivedPath::Built { .drvPath = drvPath, .outputs = wantedOutputs }) - , useDerivation(true) - , drvPath(drvPath) - , wantedOutputs(wantedOutputs) - , buildMode(buildMode) + const StringSet & wantedOutputs, Worker & worker, + BuildMode buildMode) + : Goal(worker, + DerivedPath::Built{.drvPath = drvPath, .outputs = wantedOutputs}), + useDerivation(true), drvPath(drvPath), wantedOutputs(wantedOutputs), + buildMode(buildMode) { state = &DerivationGoal::getDerivation; - name = fmt( - "building of '%s' from .drv file", - DerivedPath::Built { drvPath, wantedOutputs }.to_string(worker.store)); + name = + fmt("building of '%s' from .drv file", + DerivedPath::Built{drvPath, wantedOutputs}.to_string(worker.store)); trace("created"); - mcExpectedBuilds = std::make_unique>(worker.expectedBuilds); + mcExpectedBuilds = + std::make_unique>(worker.expectedBuilds); worker.updateProgress(); } - -DerivationGoal::DerivationGoal(const StorePath & drvPath, const BasicDerivation & drv, - const StringSet & wantedOutputs, Worker & worker, BuildMode buildMode) - : Goal(worker, DerivedPath::Built { .drvPath = drvPath, .outputs = wantedOutputs }) - , useDerivation(false) - , drvPath(drvPath) - , wantedOutputs(wantedOutputs) - , buildMode(buildMode) +DerivationGoal::DerivationGoal(const StorePath & drvPath, + const BasicDerivation & drv, + const StringSet & wantedOutputs, Worker & worker, + BuildMode buildMode) + : Goal(worker, + DerivedPath::Built{.drvPath = drvPath, .outputs = wantedOutputs}), + useDerivation(false), drvPath(drvPath), wantedOutputs(wantedOutputs), + buildMode(buildMode) { this->drv = std::make_unique(drv); state = &DerivationGoal::haveDerivation; name = fmt( "building of '%s' from in-memory derivation", - DerivedPath::Built { drvPath, drv.outputNames() }.to_string(worker.store)); + DerivedPath::Built{drvPath, drv.outputNames()}.to_string(worker.store)); trace("created"); - mcExpectedBuilds = std::make_unique>(worker.expectedBuilds); + mcExpectedBuilds = + std::make_unique>(worker.expectedBuilds); worker.updateProgress(); /* Prevent the .chroot directory from being @@ -107,30 +110,28 @@ DerivationGoal::DerivationGoal(const StorePath & drvPath, const BasicDerivation worker.store.addTempRoot(this->drvPath); } - DerivationGoal::~DerivationGoal() { /* Careful: we should never ever throw an exception from a destructor. */ - try { closeLogFile(); } catch (...) { ignoreException(); } + try { + closeLogFile(); + } catch (...) { + ignoreException(); + } } - std::string DerivationGoal::key() { /* Ensure that derivations get built in order of their name, i.e. a derivation named "aardvark" always comes before "baboon". And substitution goals always happen before derivation goals (due to "b$"). */ - return "b$" + std::string(drvPath.name()) + "$" + worker.store.printStorePath(drvPath); -} - - -void DerivationGoal::killChild() -{ - hook.reset(); + return "b$" + std::string(drvPath.name()) + "$" + + worker.store.printStorePath(drvPath); } +void DerivationGoal::killChild() { hook.reset(); } void DerivationGoal::timedOut(Error && ex) { @@ -138,16 +139,13 @@ void DerivationGoal::timedOut(Error && ex) done(BuildResult::TimedOut, {}, ex); } - -void DerivationGoal::work() -{ - (this->*state)(); -} +void DerivationGoal::work() { (this->*state)(); } void DerivationGoal::addWantedOutputs(const StringSet & outputs) { /* If we already want all outputs, there is nothing to do. */ - if (wantedOutputs.empty()) return; + if (wantedOutputs.empty()) + return; if (outputs.empty()) { wantedOutputs.clear(); @@ -158,7 +156,6 @@ void DerivationGoal::addWantedOutputs(const StringSet & outputs) needRestart = true; } - void DerivationGoal::getDerivation() { trace("init"); @@ -176,13 +173,14 @@ void DerivationGoal::getDerivation() state = &DerivationGoal::loadDerivation; } - void DerivationGoal::loadDerivation() { trace("loading derivation"); if (nrFailed != 0) { - done(BuildResult::MiscFailure, {}, Error("cannot build missing derivation '%s'", worker.store.printStorePath(drvPath))); + done(BuildResult::MiscFailure, {}, + Error("cannot build missing derivation '%s'", + worker.store.printStorePath(drvPath))); return; } @@ -194,12 +192,12 @@ void DerivationGoal::loadDerivation() assert(worker.evalStore.isValidPath(drvPath)); /* Get the derivation. */ - drv = std::make_unique(worker.evalStore.readDerivation(drvPath)); + drv = + std::make_unique(worker.evalStore.readDerivation(drvPath)); haveDerivation(); } - void DerivationGoal::haveDerivation() { trace("have derivation"); @@ -213,19 +211,15 @@ void DerivationGoal::haveDerivation() settings.requireExperimentalFeature(Xp::ImpureDerivations); for (auto & [outputName, output] : drv->outputs) { - auto randomPath = StorePath::random(outputPathName(drv->name, outputName)); + auto randomPath = + StorePath::random(outputPathName(drv->name, outputName)); assert(!worker.store.isValidPath(randomPath)); - initialOutputs.insert({ - outputName, - InitialOutput { - .wanted = true, - .outputHash = impureOutputHash, - .known = InitialOutputStatus { - .path = randomPath, - .status = PathStatus::Absent - } - } - }); + initialOutputs.insert( + {outputName, InitialOutput{.wanted = true, + .outputHash = impureOutputHash, + .known = InitialOutputStatus{ + .path = randomPath, + .status = PathStatus::Absent}}}); } gaveUpOnSubstitution(); @@ -238,13 +232,10 @@ void DerivationGoal::haveDerivation() auto outputHashes = staticOutputHashes(worker.evalStore, *drv); for (auto & [outputName, outputHash] : outputHashes) - initialOutputs.insert({ - outputName, - InitialOutput { - .wanted = true, // Will be refined later - .outputHash = outputHash - } - }); + initialOutputs.insert( + {outputName, + InitialOutput{.wanted = true, // Will be refined later + .outputHash = outputHash}}); /* Check what outputs paths are not already valid. */ auto [allValid, validOutputs] = checkPathValidity(); @@ -260,16 +251,12 @@ void DerivationGoal::haveDerivation() them. */ if (settings.useSubstitutes && parsedDrv->substitutesAllowed()) for (auto & [outputName, status] : initialOutputs) { - if (!status.wanted) continue; + if (!status.wanted) + continue; if (!status.known) - addWaitee( - upcast_goal( - worker.makeDrvOutputSubstitutionGoal( - DrvOutput{status.outputHash, outputName}, - buildMode == bmRepair ? Repair : NoRepair - ) - ) - ); + addWaitee(upcast_goal(worker.makeDrvOutputSubstitutionGoal( + DrvOutput{status.outputHash, outputName}, + buildMode == bmRepair ? Repair : NoRepair))); else addWaitee(upcast_goal(worker.makePathSubstitutionGoal( status.known->path, @@ -283,17 +270,19 @@ void DerivationGoal::haveDerivation() state = &DerivationGoal::outputsSubstitutionTried; } - void DerivationGoal::outputsSubstitutionTried() { trace("all outputs substituted (maybe)"); assert(drv->type().isPure()); - if (nrFailed > 0 && nrFailed > nrNoSubstituters + nrIncompleteClosure && !settings.tryFallback) { + if (nrFailed > 0 && nrFailed > nrNoSubstituters + nrIncompleteClosure && + !settings.tryFallback) { done(BuildResult::TransientFailure, {}, - Error("some substitutes for the outputs of derivation '%s' failed (usually happens due to networking issues); try '--fallback' to build derivation from source ", - worker.store.printStorePath(drvPath))); + Error("some substitutes for the outputs of derivation '%s' failed " + "(usually happens due to networking issues); try " + "'--fallback' to build derivation from source ", + worker.store.printStorePath(drvPath))); return; } @@ -305,7 +294,8 @@ void DerivationGoal::outputsSubstitutionTried() In particular, it may be the case that the hole in the closure is an output of the current derivation, which causes a loop if retried. */ - if (nrIncompleteClosure > 0 && nrIncompleteClosure == nrFailed) retrySubstitution = true; + if (nrIncompleteClosure > 0 && nrIncompleteClosure == nrFailed) + retrySubstitution = true; nrFailed = nrNoSubstituters = nrIncompleteClosure = 0; @@ -326,14 +316,14 @@ void DerivationGoal::outputsSubstitutionTried() return; } if (buildMode == bmCheck && !allValid) - throw Error("some outputs of '%s' are not valid, so checking is not possible", + throw Error( + "some outputs of '%s' are not valid, so checking is not possible", worker.store.printStorePath(drvPath)); /* Nothing to wait for; tail call */ gaveUpOnSubstitution(); } - /* At least one of the output paths could not be produced using a substitute. So we have to build instead. */ void DerivationGoal::gaveUpOnSubstitution() @@ -347,12 +337,15 @@ void DerivationGoal::gaveUpOnSubstitution() if (drv->type().isPure() && !drv->type().isFixed()) { auto inputDrv = worker.evalStore.readDerivation(i.first); if (!inputDrv.type().isPure()) - throw Error("pure derivation '%s' depends on impure derivation '%s'", - worker.store.printStorePath(drvPath), - worker.store.printStorePath(i.first)); + throw Error("pure derivation '%s' depends on impure " + "derivation '%s'", + worker.store.printStorePath(drvPath), + worker.store.printStorePath(i.first)); } - addWaitee(worker.makeDerivationGoal(i.first, i.second, buildMode == bmRepair ? bmRepair : bmNormal)); + addWaitee(worker.makeDerivationGoal( + i.first, i.second, + buildMode == bmRepair ? bmRepair : bmNormal)); } /* Copy the input sources from the eval store to the build @@ -365,10 +358,13 @@ void DerivationGoal::gaveUpOnSubstitution() } for (auto & i : drv->inputSrcs) { - if (worker.store.isValidPath(i)) continue; + if (worker.store.isValidPath(i)) + continue; if (!settings.useSubstitutes) - throw Error("dependency '%s' of '%s' does not exist, and substitution is disabled", - worker.store.printStorePath(i), worker.store.printStorePath(drvPath)); + throw Error("dependency '%s' of '%s' does not exist, and " + "substitution is disabled", + worker.store.printStorePath(i), + worker.store.printStorePath(drvPath)); addWaitee(upcast_goal(worker.makePathSubstitutionGoal(i))); } @@ -378,7 +374,6 @@ void DerivationGoal::gaveUpOnSubstitution() state = &DerivationGoal::inputsRealised; } - void DerivationGoal::repairClosure() { assert(drv->type().isPure()); @@ -392,7 +387,8 @@ void DerivationGoal::repairClosure() auto outputs = queryDerivationOutputMap(); StorePathSet outputClosure; for (auto & i : outputs) { - if (!wantOutput(i.first, wantedOutputs)) continue; + if (!wantOutput(i.first, wantedOutputs)) + continue; worker.store.computeFSClosure(i.second, outputClosure); } @@ -404,7 +400,8 @@ void DerivationGoal::repairClosure() derivation is responsible for which path in the output closure. */ StorePathSet inputClosure; - if (useDerivation) worker.store.computeFSClosure(drvPath, inputClosure); + if (useDerivation) + worker.store.computeFSClosure(drvPath, inputClosure); std::map outputsToDrv; for (auto & i : inputClosure) if (i.isDerivation()) { @@ -416,15 +413,18 @@ void DerivationGoal::repairClosure() /* Check each path (slow!). */ for (auto & i : outputClosure) { - if (worker.pathContentsGood(i)) continue; - printError( - "found corrupted or missing path '%s' in the output closure of '%s'", - worker.store.printStorePath(i), worker.store.printStorePath(drvPath)); + if (worker.pathContentsGood(i)) + continue; + printError("found corrupted or missing path '%s' in the output closure " + "of '%s'", + worker.store.printStorePath(i), + worker.store.printStorePath(drvPath)); auto drvPath2 = outputsToDrv.find(i); if (drvPath2 == outputsToDrv.end()) addWaitee(upcast_goal(worker.makePathSubstitutionGoal(i, Repair))); else - addWaitee(worker.makeDerivationGoal(drvPath2->second, StringSet(), bmRepair)); + addWaitee(worker.makeDerivationGoal(drvPath2->second, StringSet(), + bmRepair)); } if (waitees.empty()) { @@ -435,27 +435,27 @@ void DerivationGoal::repairClosure() state = &DerivationGoal::closureRepaired; } - void DerivationGoal::closureRepaired() { trace("closure repaired"); if (nrFailed > 0) - throw Error("some paths in the output closure of derivation '%s' could not be repaired", - worker.store.printStorePath(drvPath)); + throw Error("some paths in the output closure of derivation '%s' could " + "not be repaired", + worker.store.printStorePath(drvPath)); done(BuildResult::AlreadyValid, assertPathValidity()); } - void DerivationGoal::inputsRealised() { trace("all inputs realised"); if (nrFailed != 0) { if (!useDerivation) - throw Error("some dependencies of '%s' are missing", worker.store.printStorePath(drvPath)); - done(BuildResult::DependencyFailed, {}, Error( - "%s dependencies of derivation '%s' failed to build", - nrFailed, worker.store.printStorePath(drvPath))); + throw Error("some dependencies of '%s' are missing", + worker.store.printStorePath(drvPath)); + done(BuildResult::DependencyFailed, {}, + Error("%s dependencies of derivation '%s' failed to build", + nrFailed, worker.store.printStorePath(drvPath))); return; } @@ -475,25 +475,25 @@ void DerivationGoal::inputsRealised() auto & fullDrv = *dynamic_cast(drv.get()); auto drvType = fullDrv.type(); - bool resolveDrv = std::visit(overloaded { - [&](const DerivationType::InputAddressed & ia) { - /* must resolve if deferred. */ - return ia.deferred; - }, - [&](const DerivationType::ContentAddressed & ca) { - return !fullDrv.inputDrvs.empty() && ( - ca.fixed - /* Can optionally resolve if fixed, which is good - for avoiding unnecessary rebuilds. */ - ? settings.isExperimentalFeatureEnabled(Xp::CaDerivations) - /* Must resolve if floating and there are any inputs - drvs. */ - : true); - }, - [&](const DerivationType::Impure &) { - return true; - } - }, drvType.raw()); + bool resolveDrv = std::visit( + overloaded{ + [&](const DerivationType::InputAddressed & ia) { + /* must resolve if deferred. */ + return ia.deferred; + }, + [&](const DerivationType::ContentAddressed & ca) { + return !fullDrv.inputDrvs.empty() && + (ca.fixed + /* Can optionally resolve if fixed, which is + good for avoiding unnecessary rebuilds. */ + ? settings.isExperimentalFeatureEnabled( + Xp::CaDerivations) + /* Must resolve if floating and there are any + inputs drvs. */ + : true); + }, + [&](const DerivationType::Impure &) { return true; }}, + drvType.raw()); if (resolveDrv && !fullDrv.inputDrvs.empty()) { settings.requireExperimentalFeature(Xp::CaDerivations); @@ -501,20 +501,22 @@ void DerivationGoal::inputsRealised() /* We are be able to resolve this derivation based on the now-known results of dependencies. If so, we become a stub goal aliasing that resolved derivation goal. */ - std::optional attempt = fullDrv.tryResolve(worker.store, inputDrvOutputs); + std::optional attempt = + fullDrv.tryResolve(worker.store, inputDrvOutputs); assert(attempt); - Derivation drvResolved { *std::move(attempt) }; + Derivation drvResolved{*std::move(attempt)}; auto pathResolved = writeDerivation(worker.store, drvResolved); auto msg = fmt("resolved derivation: '%s' -> '%s'", - worker.store.printStorePath(drvPath), - worker.store.printStorePath(pathResolved)); - act = std::make_unique(*logger, lvlInfo, actBuildWaiting, msg, - Logger::Fields { - worker.store.printStorePath(drvPath), - worker.store.printStorePath(pathResolved), - }); + worker.store.printStorePath(drvPath), + worker.store.printStorePath(pathResolved)); + act = std::make_unique( + *logger, lvlInfo, actBuildWaiting, msg, + Logger::Fields{ + worker.store.printStorePath(drvPath), + worker.store.printStorePath(pathResolved), + }); resolvedDrvGoal = worker.makeDerivationGoal( pathResolved, wantedOutputs, buildMode); @@ -529,12 +531,13 @@ void DerivationGoal::inputsRealised() `i' as input paths. Only add the closures of output paths that are specified as inputs. */ for (auto & j : wantedDepOutputs) - if (auto outPath = get(inputDrvOutputs, { depDrvPath, j })) + if (auto outPath = get(inputDrvOutputs, {depDrvPath, j})) worker.store.computeFSClosure(*outPath, inputPaths); else - throw Error( - "derivation '%s' requires non-existent output '%s' from input derivation '%s'", - worker.store.printStorePath(drvPath), j, worker.store.printStorePath(depDrvPath)); + throw Error("derivation '%s' requires non-existent output " + "'%s' from input derivation '%s'", + worker.store.printStorePath(drvPath), j, + worker.store.printStorePath(depDrvPath)); } } @@ -556,21 +559,25 @@ void DerivationGoal::inputsRealised() state = &DerivationGoal::tryToBuild; worker.wakeUp(shared_from_this()); - buildResult = BuildResult { .path = buildResult.path }; + buildResult = BuildResult{.path = buildResult.path}; } void DerivationGoal::started() { - auto msg = fmt( - buildMode == bmRepair ? "repairing outputs of '%s'" : - buildMode == bmCheck ? "checking outputs of '%s'" : - nrRounds > 1 ? "building '%s' (round %d/%d)" : - "building '%s'", worker.store.printStorePath(drvPath), curRound, nrRounds); + auto msg = fmt(buildMode == bmRepair ? "repairing outputs of '%s'" + : buildMode == bmCheck ? "checking outputs of '%s'" + : nrRounds > 1 ? "building '%s' (round %d/%d)" + : "building '%s'", + worker.store.printStorePath(drvPath), curRound, nrRounds); fmt("building '%s'", worker.store.printStorePath(drvPath)); - if (hook) msg += fmt(" on '%s'", machineName); - act = std::make_unique(*logger, lvlInfo, actBuild, msg, - Logger::Fields{worker.store.printStorePath(drvPath), hook ? machineName : "", curRound, nrRounds}); - mcRunningBuilds = std::make_unique>(worker.runningBuilds); + if (hook) + msg += fmt(" on '%s'", machineName); + act = std::make_unique( + *logger, lvlInfo, actBuild, msg, + Logger::Fields{worker.store.printStorePath(drvPath), + hook ? machineName : "", curRound, nrRounds}); + mcRunningBuilds = + std::make_unique>(worker.runningBuilds); worker.updateProgress(); } @@ -597,17 +604,18 @@ void DerivationGoal::tryToBuild() */ for (auto & i : drv->outputsAndOptPaths(worker.store)) { if (i.second.second) - lockFiles.insert(worker.store.Store::toRealPath(*i.second.second)); - else lockFiles.insert( - worker.store.Store::toRealPath(drvPath) + "." + i.first - ); + worker.store.Store::toRealPath(*i.second.second)); + else + lockFiles.insert(worker.store.Store::toRealPath(drvPath) + "." + + i.first); } } if (!outputLocks.lockPaths(lockFiles, "", false)) { if (!actLock) - actLock = std::make_unique(*logger, lvlWarn, actBuildWaiting, + actLock = std::make_unique( + *logger, lvlWarn, actBuildWaiting, fmt("waiting for lock on %s", yellowtxt(showPaths(lockFiles)))); worker.waitForAWhile(shared_from_this()); return; @@ -625,7 +633,8 @@ void DerivationGoal::tryToBuild() auto [allValid, validOutputs] = checkPathValidity(); if (buildMode != bmCheck && allValid) { - debug("skipping build of derivation '%s', someone beat us to it", worker.store.printStorePath(drvPath)); + debug("skipping build of derivation '%s', someone beat us to it", + worker.store.printStorePath(drvPath)); outputLocks.setDeletion(true); done(BuildResult::AlreadyValid, std::move(validOutputs)); return; @@ -634,9 +643,11 @@ void DerivationGoal::tryToBuild() /* If any of the outputs already exist but are not valid, delete them. */ for (auto & [_, status] : initialOutputs) { - if (!status.known || status.known->isValid()) continue; + if (!status.known || status.known->isValid()) + continue; auto storePath = status.known->path; - debug("removing invalid path '%s'", worker.store.printStorePath(status.known->path)); + debug("removing invalid path '%s'", + worker.store.printStorePath(status.known->path)); deletePath(worker.store.Store::toRealPath(storePath)); } @@ -644,31 +655,33 @@ void DerivationGoal::tryToBuild() `preferLocalBuild' set. Also, check and repair modes are only supported for local builds. */ bool buildLocally = - (buildMode != bmNormal || parsedDrv->willBuildLocally(worker.store)) - && settings.maxBuildJobs.get() != 0; + (buildMode != bmNormal || parsedDrv->willBuildLocally(worker.store)) && + settings.maxBuildJobs.get() != 0; if (!buildLocally) { switch (tryBuildHook()) { - case rpAccept: - /* Yes, it has started doing so. Wait until we get - EOF from the hook. */ - actLock.reset(); - buildResult.startTime = time(0); // inexact - state = &DerivationGoal::buildDone; - started(); - return; - case rpPostpone: - /* Not now; wait until at least one child finishes or - the wake-up timeout expires. */ - if (!actLock) - actLock = std::make_unique(*logger, lvlWarn, actBuildWaiting, - fmt("waiting for a machine to build '%s'", yellowtxt(worker.store.printStorePath(drvPath)))); - worker.waitForAWhile(shared_from_this()); - outputLocks.unlock(); - return; - case rpDecline: - /* We should do it ourselves. */ - break; + case rpAccept: + /* Yes, it has started doing so. Wait until we get + EOF from the hook. */ + actLock.reset(); + buildResult.startTime = time(0); // inexact + state = &DerivationGoal::buildDone; + started(); + return; + case rpPostpone: + /* Not now; wait until at least one child finishes or + the wake-up timeout expires. */ + if (!actLock) + actLock = std::make_unique( + *logger, lvlWarn, actBuildWaiting, + fmt("waiting for a machine to build '%s'", + yellowtxt(worker.store.printStorePath(drvPath)))); + worker.waitForAWhile(shared_from_this()); + outputLocks.unlock(); + return; + case rpDecline: + /* We should do it ourselves. */ + break; } } @@ -678,21 +691,21 @@ void DerivationGoal::tryToBuild() worker.wakeUp(shared_from_this()); } -void DerivationGoal::tryLocalBuild() { +void DerivationGoal::tryLocalBuild() +{ throw Error( "unable to build with a primary store that isn't a local store; " "either pass a different '--store' or enable remote builds." - "\nhttps://nixos.org/manual/nix/stable/advanced-topics/distributed-builds.html"); + "\nhttps://nixos.org/manual/nix/stable/advanced-topics/" + "distributed-builds.html"); } - static void chmod_(const Path & path, mode_t mode) { if (chmod(path.c_str(), mode) == -1) throw SysError("setting permissions on '%s'", path); } - /* Move/rename path 'src' to 'dst'. Temporarily make 'src' writable if it's a directory and we're not root (to be able to update the directory's parent link ".."). */ @@ -700,7 +713,8 @@ static void movePath(const Path & src, const Path & dst) { auto st = lstat(src); - bool changePerm = (geteuid() && S_ISDIR(st.st_mode) && !(st.st_mode & S_IWUSR)); + bool changePerm = + (geteuid() && S_ISDIR(st.st_mode) && !(st.st_mode & S_IWUSR)); if (changePerm) chmod_(src, st.st_mode | S_IWUSR); @@ -712,14 +726,14 @@ static void movePath(const Path & src, const Path & dst) chmod_(dst, st.st_mode); } - void replaceValidPath(const Path & storePath, const Path & tmpPath) { /* We can't atomically replace storePath (the original) with tmpPath (the replacement), so we have to move it out of the way first. We'd better not be interrupted here, because if we're repairing (say) Glibc, we end up with a broken system. */ - Path oldPath = (format("%1%.old-%2%-%3%") % storePath % getpid() % random()).str(); + Path oldPath = + (format("%1%.old-%2%-%3%") % storePath % getpid() % random()).str(); if (pathExists(storePath)) movePath(storePath, oldPath); @@ -738,12 +752,7 @@ void replaceValidPath(const Path & storePath, const Path & tmpPath) deletePath(oldPath); } - -int DerivationGoal::getChildStatus() -{ - return hook->pid.kill(); -} - +int DerivationGoal::getChildStatus() { return hook->pid.kill(); } void DerivationGoal::closeReadPipes() { @@ -751,64 +760,45 @@ void DerivationGoal::closeReadPipes() hook->fromHook.readSide = -1; } +void DerivationGoal::cleanupHookFinally() {} -void DerivationGoal::cleanupHookFinally() -{ -} - - -void DerivationGoal::cleanupPreChildKill() -{ -} - +void DerivationGoal::cleanupPreChildKill() {} -void DerivationGoal::cleanupPostChildKill() -{ -} +void DerivationGoal::cleanupPostChildKill() {} +bool DerivationGoal::cleanupDecideWhetherDiskFull() { return false; } -bool DerivationGoal::cleanupDecideWhetherDiskFull() -{ - return false; -} - - -void DerivationGoal::cleanupPostOutputsRegisteredModeCheck() -{ -} +void DerivationGoal::cleanupPostOutputsRegisteredModeCheck() {} +void DerivationGoal::cleanupPostOutputsRegisteredModeNonCheck() {} -void DerivationGoal::cleanupPostOutputsRegisteredModeNonCheck() -{ -} - -void runPostBuildHook( - Store & store, - Logger & logger, - const StorePath & drvPath, - const StorePathSet & outputPaths) +void runPostBuildHook(Store & store, Logger & logger, const StorePath & drvPath, + const StorePathSet & outputPaths) { auto hook = settings.postBuildHook; if (hook == "") return; Activity act(logger, lvlInfo, actPostBuildHook, - fmt("running post-build-hook '%s'", settings.postBuildHook), - Logger::Fields{store.printStorePath(drvPath)}); + fmt("running post-build-hook '%s'", settings.postBuildHook), + Logger::Fields{store.printStorePath(drvPath)}); PushActivity pact(act.id); std::map hookEnvironment = getEnv(); hookEnvironment.emplace("DRV_PATH", store.printStorePath(drvPath)); - hookEnvironment.emplace("OUT_PATHS", chomp(concatStringsSep(" ", store.printStorePathSet(outputPaths)))); + hookEnvironment.emplace( + "OUT_PATHS", + chomp(concatStringsSep(" ", store.printStorePathSet(outputPaths)))); hookEnvironment.emplace("NIX_CONFIG", globalConfig.toKeyValue()); struct LogSink : Sink { Activity & act; std::string currentLine; - LogSink(Activity & act) : act(act) { } + LogSink(Activity & act) : act(act) {} - void operator() (std::string_view data) override { + void operator()(std::string_view data) override + { for (auto c : data) { if (c == '\n') { flushLine(); @@ -818,12 +808,14 @@ void runPostBuildHook( } } - void flushLine() { + void flushLine() + { act.result(resPostBuildLogLine, currentLine); currentLine.clear(); } - ~LogSink() { + ~LogSink() + { if (currentLine != "") { currentLine += '\n'; flushLine(); @@ -844,7 +836,7 @@ void DerivationGoal::buildDone() { trace("build done"); - Finally releaseBuildUser([&](){ this->cleanupHookFinally(); }); + Finally releaseBuildUser([&]() { this->cleanupHookFinally(); }); cleanupPreChildKill(); @@ -854,7 +846,8 @@ void DerivationGoal::buildDone() kill it. */ int status = getChildStatus(); - debug("builder process for '%s' finished", worker.store.printStorePath(drvPath)); + debug("builder process for '%s' finished", + worker.store.printStorePath(drvPath)); buildResult.timesBuilt++; buildResult.stopTime = time(0); @@ -880,8 +873,8 @@ void DerivationGoal::buildDone() diskFull |= cleanupDecideWhetherDiskFull(); auto msg = fmt("builder for '%s' %s", - yellowtxt(worker.store.printStorePath(drvPath)), - statusToString(status)); + yellowtxt(worker.store.printStorePath(drvPath)), + statusToString(status)); if (!logger->isVerbose() && !logTail.empty()) { msg += fmt(";\nlast %d log lines:\n", logTail.size()); @@ -890,12 +883,14 @@ void DerivationGoal::buildDone() msg += line; msg += "\n"; } - msg += fmt("For full logs, run '" ANSI_BOLD "nix log %s" ANSI_NORMAL "'.", - worker.store.printStorePath(drvPath)); + msg += fmt("For full logs, run '" ANSI_BOLD + "nix log %s" ANSI_NORMAL "'.", + worker.store.printStorePath(drvPath)); } if (diskFull) - msg += "\nnote: build failure may have been caused by lack of free disk space"; + msg += "\nnote: build failure may have been caused by lack of " + "free disk space"; throw BuildError(msg); } @@ -907,12 +902,7 @@ void DerivationGoal::buildDone() StorePathSet outputPaths; for (auto & [_, output] : builtOutputs) outputPaths.insert(output.outPath); - runPostBuildHook( - worker.store, - *logger, - drvPath, - outputPaths - ); + runPostBuildHook(worker.store, *logger, drvPath, outputPaths); if (buildMode == bmCheck) { cleanupPostOutputsRegisteredModeCheck(); @@ -951,11 +941,12 @@ void DerivationGoal::buildDone() } else { - st = - dynamic_cast(&e) ? BuildResult::NotDeterministic : - statusOk(status) ? BuildResult::OutputRejected : - !derivationType.isSandboxed() || diskFull ? BuildResult::TransientFailure : - BuildResult::PermanentFailure; + st = dynamic_cast(&e) + ? BuildResult::NotDeterministic + : statusOk(status) ? BuildResult::OutputRejected + : !derivationType.isSandboxed() || diskFull + ? BuildResult::TransientFailure + : BuildResult::PermanentFailure; } done(st, {}, e); @@ -987,20 +978,25 @@ void DerivationGoal::resolvedFinished() auto initialOutput = get(initialOutputs, wantedOutput); auto resolvedHash = get(resolvedHashes, wantedOutput); if ((!initialOutput) || (!resolvedHash)) - throw Error( - "derivation '%s' doesn't have expected output '%s' (derivation-goal.cc/resolvedFinished,resolve)", - worker.store.printStorePath(drvPath), wantedOutput); - auto realisation = get(resolvedResult.builtOutputs, DrvOutput { *resolvedHash, wantedOutput }); + throw Error("derivation '%s' doesn't have expected output '%s' " + "(derivation-goal.cc/resolvedFinished,resolve)", + worker.store.printStorePath(drvPath), wantedOutput); + auto realisation = get(resolvedResult.builtOutputs, + DrvOutput{*resolvedHash, wantedOutput}); if (!realisation) throw Error( - "derivation '%s' doesn't have expected output '%s' (derivation-goal.cc/resolvedFinished,realisation)", - worker.store.printStorePath(resolvedDrvGoal->drvPath), wantedOutput); + "derivation '%s' doesn't have expected output '%s' " + "(derivation-goal.cc/resolvedFinished,realisation)", + worker.store.printStorePath(resolvedDrvGoal->drvPath), + wantedOutput); if (drv->type().isPure()) { auto newRealisation = *realisation; - newRealisation.id = DrvOutput { initialOutput->outputHash, wantedOutput }; + newRealisation.id = + DrvOutput{initialOutput->outputHash, wantedOutput}; newRealisation.signatures.clear(); if (!drv->type().isFixed()) - newRealisation.dependentRealisations = drvOutputReferences(worker.store, *drv, realisation->outPath); + newRealisation.dependentRealisations = drvOutputReferences( + worker.store, *drv, realisation->outPath); signRealisation(newRealisation); worker.store.registerDrvOutput(newRealisation); } @@ -1008,12 +1004,7 @@ void DerivationGoal::resolvedFinished() builtOutputs.emplace(realisation->id, *realisation); } - runPostBuildHook( - worker.store, - *logger, - drvPath, - outputPaths - ); + runPostBuildHook(worker.store, *logger, drvPath, outputPaths); } auto status = resolvedResult.status; @@ -1025,7 +1016,8 @@ void DerivationGoal::resolvedFinished() HookReply DerivationGoal::tryBuildHook() { - if (!worker.tryBuildHook || !useDerivation) return rpDecline; + if (!worker.tryBuildHook || !useDerivation) + return rpDecline; if (!worker.hook) worker.hook = std::make_unique(); @@ -1033,12 +1025,13 @@ HookReply DerivationGoal::tryBuildHook() try { /* Send the request to the hook. */ - worker.hook->sink - << "try" - << (worker.getNrLocalBuilds() < settings.maxBuildJobs ? 1 : 0) - << drv->platform - << worker.store.printStorePath(drvPath) - << parsedDrv->getRequiredSystemFeatures(); + worker.hook->sink << "try" + << (worker.getNrLocalBuilds() < settings.maxBuildJobs + ? 1 + : 0) + << drv->platform + << worker.store.printStorePath(drvPath) + << parsedDrv->getRequiredSystemFeatures(); worker.hook->sink.flush(); /* Read the first line of input, which should be a word indicating @@ -1049,17 +1042,18 @@ HookReply DerivationGoal::tryBuildHook() try { return readLine(worker.hook->fromHook.readSide.get()); } catch (Error & e) { - e.addTrace({}, "while reading the response from the build hook"); + e.addTrace( + {}, "while reading the response from the build hook"); throw; } }(); - if (handleJSONLogMessage(s, worker.act, worker.hook->activities, true)) + if (handleJSONLogMessage(s, worker.act, worker.hook->activities, + true)) ; else if (s.substr(0, 2) == "# ") { reply = s.substr(2); break; - } - else { + } else { s += "\n"; writeToStderr(s); } @@ -1073,17 +1067,15 @@ HookReply DerivationGoal::tryBuildHook() worker.tryBuildHook = false; worker.hook = 0; return rpDecline; - } - else if (reply == "postpone") + } else if (reply == "postpone") return rpPostpone; else if (reply != "accept") throw Error("bad hook reply '%s'", reply); } catch (SysError & e) { if (e.errNo == EPIPE) { - printError( - "build hook died unexpectedly: %s", - chomp(drainFD(worker.hook->fromHook.readSide.get()))); + printError("build hook died unexpectedly: %s", + chomp(drainFD(worker.hook->fromHook.readSide.get()))); worker.hook = 0; return rpDecline; } else @@ -1109,7 +1101,8 @@ HookReply DerivationGoal::tryBuildHook() StringSet missingOutputs; for (auto & [outputName, status] : initialOutputs) { // XXX: Does this include known CA outputs? - if (buildMode != bmCheck && status.known && status.known->isValid()) continue; + if (buildMode != bmCheck && status.known && status.known->isValid()) + continue; missingOutputs.insert(outputName); } worker_proto::write(worker.store, hook->sink, missingOutputs); @@ -1129,7 +1122,6 @@ HookReply DerivationGoal::tryBuildHook() return rpAccept; } - DrvOutputs DerivationGoal::registerOutputs() { /* When using a build hook, the build hook can register the output @@ -1146,9 +1138,11 @@ Path DerivationGoal::openLogFile() { logSize = 0; - if (!settings.keepLog) return ""; + if (!settings.keepLog) + return ""; - auto baseName = std::string(baseNameOf(worker.store.printStorePath(drvPath))); + auto baseName = + std::string(baseNameOf(worker.store.printStorePath(drvPath))); /* Create a log file. */ Path logDir; @@ -1156,36 +1150,40 @@ Path DerivationGoal::openLogFile() logDir = localStore->logDir; else logDir = settings.nixLogDir; - Path dir = fmt("%s/%s/%s/", logDir, LocalFSStore::drvsLogDir, baseName.substr(0, 2)); + Path dir = fmt("%s/%s/%s/", logDir, LocalFSStore::drvsLogDir, + baseName.substr(0, 2)); createDirs(dir); Path logFileName = fmt("%s/%s%s", dir, baseName.substr(2), - settings.compressLog ? ".bz2" : ""); + settings.compressLog ? ".bz2" : ""); - fdLogFile = open(logFileName.c_str(), O_CREAT | O_WRONLY | O_TRUNC | O_CLOEXEC, 0666); - if (!fdLogFile) throw SysError("creating log file '%1%'", logFileName); + fdLogFile = open(logFileName.c_str(), + O_CREAT | O_WRONLY | O_TRUNC | O_CLOEXEC, 0666); + if (!fdLogFile) + throw SysError("creating log file '%1%'", logFileName); logFileSink = std::make_shared(fdLogFile.get()); if (settings.compressLog) - logSink = std::shared_ptr(makeCompressionSink("bzip2", *logFileSink)); + logSink = std::shared_ptr( + makeCompressionSink("bzip2", *logFileSink)); else logSink = logFileSink; return logFileName; } - void DerivationGoal::closeLogFile() { auto logSink2 = std::dynamic_pointer_cast(logSink); - if (logSink2) logSink2->finish(); - if (logFileSink) logFileSink->flush(); + if (logSink2) + logSink2->finish(); + if (logFileSink) + logFileSink->flush(); logSink = logFileSink = 0; fdLogFile = -1; } - bool DerivationGoal::isReadDesc(int fd) { return fd == hook->builderOut.readSide.get(); @@ -1195,15 +1193,14 @@ void DerivationGoal::handleChildOutput(int fd, std::string_view data) { // local & `ssh://`-builds are dealt with here. auto isWrittenToLog = isReadDesc(fd); - if (isWrittenToLog) - { + if (isWrittenToLog) { logSize += data.size(); if (settings.maxLogSize && logSize > settings.maxLogSize) { killChild(); - done( - BuildResult::LogLimitExceeded, {}, - Error("%s killed after writing more than %d bytes of log output", - getName(), settings.maxLogSize)); + done(BuildResult::LogLimitExceeded, {}, + Error( + "%s killed after writing more than %d bytes of log output", + getName(), settings.maxLogSize)); return; } @@ -1218,7 +1215,8 @@ void DerivationGoal::handleChildOutput(int fd, std::string_view data) currentLogLine[currentLogLinePos++] = c; } - if (logSink) (*logSink)(data); + if (logSink) + (*logSink)(data); } if (hook && fd == hook->fromHook.readSide.get()) { @@ -1226,12 +1224,16 @@ void DerivationGoal::handleChildOutput(int fd, std::string_view data) if (c == '\n') { auto json = parseJSONMessage(currentHookLine); if (json) { - auto s = handleJSONLogMessage(*json, worker.act, hook->activities, true); - // ensure that logs from a builder using `ssh-ng://` as protocol - // are also available to `nix log`. - if (s && !isWrittenToLog && logSink && (*json)["type"] == resBuildLogLine) { + auto s = handleJSONLogMessage(*json, worker.act, + hook->activities, true); + // ensure that logs from a builder using `ssh-ng://` as + // protocol are also available to `nix log`. + if (s && !isWrittenToLog && logSink && + (*json)["type"] == resBuildLogLine) { auto f = (*json)["fields"]; - (*logSink)((f.size() > 0 ? f.at(0).get() : "") + "\n"); + (*logSink)( + (f.size() > 0 ? f.at(0).get() : "") + + "\n"); } } currentHookLine.clear(); @@ -1240,14 +1242,13 @@ void DerivationGoal::handleChildOutput(int fd, std::string_view data) } } - void DerivationGoal::handleEOF(int fd) { - if (!currentLogLine.empty()) flushLine(); + if (!currentLogLine.empty()) + flushLine(); worker.wakeUp(shared_from_this()); } - void DerivationGoal::flushLine() { if (handleJSONLogMessage(currentLogLine, *act, builderActivities, false)) @@ -1255,7 +1256,8 @@ void DerivationGoal::flushLine() else { logTail.push_back(currentLogLine); - if (logTail.size() > settings.logLines) logTail.pop_front(); + if (logTail.size() > settings.logLines) + logTail.pop_front(); act->result(resBuildLogLine, currentLogLine); } @@ -1264,14 +1266,15 @@ void DerivationGoal::flushLine() currentLogLinePos = 0; } - -std::map> DerivationGoal::queryPartialDerivationOutputMap() +std::map> +DerivationGoal::queryPartialDerivationOutputMap() { assert(drv->type().isPure()); if (!useDerivation || drv->type().hasKnownOutputPaths()) { std::map> res; for (auto & [name, output] : drv->outputs) - res.insert_or_assign(name, output.path(worker.store, drv->name, name)); + res.insert_or_assign(name, + output.path(worker.store, drv->name, name)); return res; } else { return worker.store.queryPartialDerivationOutputMap(drvPath); @@ -1291,10 +1294,10 @@ OutputPathMap DerivationGoal::queryDerivationOutputMap() } } - std::pair DerivationGoal::checkPathValidity() { - if (!drv->type().isPure()) return { false, {} }; + if (!drv->type().isPure()) + return {false, {}}; bool checkHash = buildMode == bmRepair; auto wantedOutputsLeft = wantedOutputs; @@ -1303,7 +1306,8 @@ std::pair DerivationGoal::checkPathValidity() for (auto & i : queryPartialDerivationOutputMap()) { auto initialOutput = get(initialOutputs, i.first); if (!initialOutput) - // this is an invalid output, gets catched with (!wantedOutputsLeft.empty()) + // this is an invalid output, gets catched with + // (!wantedOutputsLeft.empty()) continue; auto & info = *initialOutput; info.wanted = wantOutput(i.first, wantedOutputs); @@ -1314,10 +1318,10 @@ std::pair DerivationGoal::checkPathValidity() info.known = { .path = outputPath, .status = !worker.store.isValidPath(outputPath) - ? PathStatus::Absent - : !checkHash || worker.pathContentsGood(outputPath) - ? PathStatus::Valid - : PathStatus::Corrupt, + ? PathStatus::Absent + : !checkHash || worker.pathContentsGood(outputPath) + ? PathStatus::Valid + : PathStatus::Corrupt, }; } auto drvOutput = DrvOutput{info.outputHash, i.first}; @@ -1332,16 +1336,15 @@ std::pair DerivationGoal::checkPathValidity() // derivation, and the output path is valid, but we don't have // its realisation stored (probably because it has been built // without the `ca-derivations` experimental flag). - worker.store.registerDrvOutput( - Realisation { - drvOutput, - info.known->path, - } - ); + worker.store.registerDrvOutput(Realisation{ + drvOutput, + info.known->path, + }); } } if (info.wanted && info.known && info.known->isValid()) - validOutputs.emplace(drvOutput, Realisation { drvOutput, info.known->path }); + validOutputs.emplace(drvOutput, + Realisation{drvOutput, info.known->path}); } // If we requested all the outputs via the empty set, we are always fine. @@ -1349,22 +1352,22 @@ std::pair DerivationGoal::checkPathValidity() // ones, so any that are left must be invalid. if (!wantedOutputsLeft.empty()) throw Error("derivation '%s' does not have wanted outputs %s", - worker.store.printStorePath(drvPath), - concatStringsSep(", ", quoteStrings(wantedOutputsLeft))); + worker.store.printStorePath(drvPath), + concatStringsSep(", ", quoteStrings(wantedOutputsLeft))); bool allValid = true; for (auto & [_, status] : initialOutputs) { - if (!status.wanted) continue; + if (!status.wanted) + continue; if (!status.known || !status.known->isValid()) { allValid = false; break; } } - return { allValid, validOutputs }; + return {allValid, validOutputs}; } - DrvOutputs DerivationGoal::assertPathValidity() { auto [allValid, validOutputs] = checkPathValidity(); @@ -1373,11 +1376,8 @@ DrvOutputs DerivationGoal::assertPathValidity() return validOutputs; } - -void DerivationGoal::done( - BuildResult::Status status, - DrvOutputs builtOutputs, - std::optional ex) +void DerivationGoal::done(BuildResult::Status status, DrvOutputs builtOutputs, + std::optional ex) { buildResult.status = status; if (ex) @@ -1402,27 +1402,29 @@ void DerivationGoal::done( worker.updateProgress(); - auto traceBuiltOutputsFile = getEnv("_NIX_TRACE_BUILT_OUTPUTS").value_or(""); + auto traceBuiltOutputsFile = + getEnv("_NIX_TRACE_BUILT_OUTPUTS").value_or(""); if (traceBuiltOutputsFile != "") { std::fstream fs; fs.open(traceBuiltOutputsFile, std::fstream::out); - fs << worker.store.printStorePath(drvPath) << "\t" << buildResult.toString() << std::endl; + fs << worker.store.printStorePath(drvPath) << "\t" + << buildResult.toString() << std::endl; } amDone(buildResult.success() ? ecSuccess : ecFailed, ex); } - void DerivationGoal::waiteeDone(GoalPtr waitee, ExitCode result) { Goal::waiteeDone(waitee, result); if (waitee->buildResult.success()) - if (auto bfd = std::get_if(&waitee->buildResult.path)) - for (auto & [output, realisation] : waitee->buildResult.builtOutputs) + if (auto bfd = + std::get_if(&waitee->buildResult.path)) + for (auto & [output, realisation] : + waitee->buildResult.builtOutputs) inputDrvOutputs.insert_or_assign( - { bfd->drvPath, output.outputName }, - realisation.outPath); + {bfd->drvPath, output.outputName}, realisation.outPath); } } diff --git a/src/libstore/build/derivation-goal.hh b/src/libstore/build/derivation-goal.hh index 2d8bfd59293f..baa6c6c88d97 100644 --- a/src/libstore/build/derivation-goal.hh +++ b/src/libstore/build/derivation-goal.hh @@ -12,7 +12,7 @@ using std::map; struct HookInstance; -typedef enum {rpAccept, rpDecline, rpPostpone} HookReply; +typedef enum { rpAccept, rpDecline, rpPostpone } HookReply; /* Unless we are repairing, we don't both to test validity and just assume it, so the choices are `Absent` or `Valid`. */ @@ -26,13 +26,11 @@ struct InitialOutputStatus { StorePath path; PathStatus status; /* Valid in the store, and additionally non-corrupt if we are repairing */ - bool isValid() const { - return status == PathStatus::Valid; - } + bool isValid() const { return status == PathStatus::Valid; } /* Merely present, allowed to be corrupt */ - bool isPresent() const { - return status == PathStatus::Corrupt - || status == PathStatus::Valid; + bool isPresent() const + { + return status == PathStatus::Corrupt || status == PathStatus::Valid; } }; @@ -42,8 +40,7 @@ struct InitialOutput { std::optional known; }; -struct DerivationGoal : public Goal -{ +struct DerivationGoal : public Goal { /* Whether to use an on-disk .drv file. */ bool useDerivation; @@ -132,12 +129,11 @@ struct DerivationGoal : public Goal /* The remote machine on which we're building. */ std::string machineName; - DerivationGoal(const StorePath & drvPath, - const StringSet & wantedOutputs, Worker & worker, - BuildMode buildMode = bmNormal); + DerivationGoal(const StorePath & drvPath, const StringSet & wantedOutputs, + Worker & worker, BuildMode buildMode = bmNormal); DerivationGoal(const StorePath & drvPath, const BasicDerivation & drv, - const StringSet & wantedOutputs, Worker & worker, - BuildMode buildMode = bmNormal); + const StringSet & wantedOutputs, Worker & worker, + BuildMode buildMode = bmNormal); virtual ~DerivationGoal(); void timedOut(Error && ex) override; @@ -176,7 +172,7 @@ struct DerivationGoal : public Goal Path openLogFile(); /* Sign the newly built realisation if the store allows it */ - virtual void signRealisation(Realisation&) {} + virtual void signRealisation(Realisation &) {} /* Close the log file. */ void closeLogFile(); @@ -202,7 +198,8 @@ struct DerivationGoal : public Goal /* Wrappers around the corresponding Store methods that first consult the derivation. This is currently needed because when there is no drv file there also is no DB entry. */ - std::map> queryPartialDerivationOutputMap(); + std::map> + queryPartialDerivationOutputMap(); OutputPathMap queryDerivationOutputMap(); /* Update 'initialOutputs' to determine the current status of the @@ -224,10 +221,8 @@ struct DerivationGoal : public Goal void started(); - void done( - BuildResult::Status status, - DrvOutputs builtOutputs = {}, - std::optional ex = {}); + void done(BuildResult::Status status, DrvOutputs builtOutputs = {}, + std::optional ex = {}); void waiteeDone(GoalPtr waitee, ExitCode result) override; diff --git a/src/libstore/build/drv-output-substitution-goal.cc b/src/libstore/build/drv-output-substitution-goal.cc index b7f7b5ab129f..38b048e6b54b 100644 --- a/src/libstore/build/drv-output-substitution-goal.cc +++ b/src/libstore/build/drv-output-substitution-goal.cc @@ -7,19 +7,15 @@ namespace nix { DrvOutputSubstitutionGoal::DrvOutputSubstitutionGoal( - const DrvOutput & id, - Worker & worker, - RepairFlag repair, + const DrvOutput & id, Worker & worker, RepairFlag repair, std::optional ca) - : Goal(worker, DerivedPath::Opaque { StorePath::dummy }) - , id(id) + : Goal(worker, DerivedPath::Opaque{StorePath::dummy}), id(id) { state = &DrvOutputSubstitutionGoal::init; name = fmt("substitution of '%s'", id.to_string()); trace("created"); } - void DrvOutputSubstitutionGoal::init() { trace("init"); @@ -30,7 +26,8 @@ void DrvOutputSubstitutionGoal::init() return; } - subs = settings.useSubstitutes ? getDefaultSubstituters() : std::list>(); + subs = settings.useSubstitutes ? getDefaultSubstituters() + : std::list>(); tryNext(); } @@ -41,7 +38,9 @@ void DrvOutputSubstitutionGoal::tryNext() if (subs.size() == 0) { /* None left. Terminate this goal and let someone else deal with it. */ - debug("derivation output '%s' is required, but there is no substituter that can provide it", id.to_string()); + debug("derivation output '%s' is required, but there is no substituter " + "that can provide it", + id.to_string()); /* Hack: don't indicate failure if there were no substituters. In that case the calling derivation should just do a @@ -65,16 +64,17 @@ void DrvOutputSubstitutionGoal::tryNext() promise = decltype(promise)(); sub->queryRealisation( - id, { [&](std::future> res) { + id, {[&](std::future> res) { try { Finally updateStats([this]() { outPipe.writeSide.close(); }); promise.set_value(res.get()); } catch (...) { promise.set_exception(std::current_exception()); } - } }); + }}); - worker.childStarted(shared_from_this(), {outPipe.readSide.get()}, true, false); + worker.childStarted(shared_from_this(), {outPipe.readSide.get()}, true, + false); state = &DrvOutputSubstitutionGoal::realisationFetched; } @@ -98,15 +98,13 @@ void DrvOutputSubstitutionGoal::realisationFetched() if (depId != id) { if (auto localOutputInfo = worker.store.queryRealisation(depId); localOutputInfo && localOutputInfo->outPath != depPath) { - warn( - "substituter '%s' has an incompatible realisation for '%s', ignoring.\n" - "Local: %s\n" - "Remote: %s", - sub->getUri(), - depId.to_string(), - worker.store.printStorePath(localOutputInfo->outPath), - worker.store.printStorePath(depPath) - ); + warn("substituter '%s' has an incompatible realisation for " + "'%s', ignoring.\n" + "Local: %s\n" + "Remote: %s", + sub->getUri(), depId.to_string(), + worker.store.printStorePath(localOutputInfo->outPath), + worker.store.printStorePath(depPath)); tryNext(); return; } @@ -116,8 +114,10 @@ void DrvOutputSubstitutionGoal::realisationFetched() addWaitee(worker.makePathSubstitutionGoal(outputInfo->outPath)); - if (waitees.empty()) outPathValid(); - else state = &DrvOutputSubstitutionGoal::outPathValid; + if (waitees.empty()) + outPathValid(); + else + state = &DrvOutputSubstitutionGoal::outPathValid; } void DrvOutputSubstitutionGoal::outPathValid() @@ -126,8 +126,12 @@ void DrvOutputSubstitutionGoal::outPathValid() trace("output path substituted"); if (nrFailed > 0) { - debug("The output path of the derivation output '%s' could not be substituted", id.to_string()); - amDone(nrNoSubstituters > 0 || nrIncompleteClosure > 0 ? ecIncompleteClosure : ecFailed); + debug("The output path of the derivation output '%s' could not be " + "substituted", + id.to_string()); + amDone(nrNoSubstituters > 0 || nrIncompleteClosure > 0 + ? ecIncompleteClosure + : ecFailed); return; } @@ -148,15 +152,12 @@ std::string DrvOutputSubstitutionGoal::key() return "a$" + std::string(id.to_string()); } -void DrvOutputSubstitutionGoal::work() -{ - (this->*state)(); -} +void DrvOutputSubstitutionGoal::work() { (this->*state)(); } void DrvOutputSubstitutionGoal::handleEOF(int fd) { - if (fd == outPipe.readSide.get()) worker.wakeUp(shared_from_this()); + if (fd == outPipe.readSide.get()) + worker.wakeUp(shared_from_this()); } - } diff --git a/src/libstore/build/drv-output-substitution-goal.hh b/src/libstore/build/drv-output-substitution-goal.hh index 948dbda8fc8e..befdb3a8d4ca 100644 --- a/src/libstore/build/drv-output-substitution-goal.hh +++ b/src/libstore/build/drv-output-substitution-goal.hh @@ -16,7 +16,7 @@ class Worker; // 2. Substitute the corresponding output path // 3. Register the output info class DrvOutputSubstitutionGoal : public Goal { -private: + private: // The drv output we're trying to substitue DrvOutput id; @@ -37,8 +37,10 @@ private: /* Whether a substituter failed. */ bool substituterFailed = false; -public: - DrvOutputSubstitutionGoal(const DrvOutput& id, Worker & worker, RepairFlag repair = NoRepair, std::optional ca = std::nullopt); + public: + DrvOutputSubstitutionGoal(const DrvOutput & id, Worker & worker, + RepairFlag repair = NoRepair, + std::optional ca = std::nullopt); typedef void (DrvOutputSubstitutionGoal::*GoalState)(); GoalState state; diff --git a/src/libstore/build/entry-points.cc b/src/libstore/build/entry-points.cc index bea7363dbfae..eb1f506b3f52 100644 --- a/src/libstore/build/entry-points.cc +++ b/src/libstore/build/entry-points.cc @@ -5,20 +5,25 @@ namespace nix { -void Store::buildPaths(const std::vector & reqs, BuildMode buildMode, std::shared_ptr evalStore) +void Store::buildPaths(const std::vector & reqs, + BuildMode buildMode, std::shared_ptr evalStore) { Worker worker(*this, evalStore ? *evalStore : *this); Goals goals; for (const auto & br : reqs) { - std::visit(overloaded { - [&](const DerivedPath::Built & bfd) { - goals.insert(worker.makeDerivationGoal(bfd.drvPath, bfd.outputs, buildMode)); + std::visit( + overloaded{ + [&](const DerivedPath::Built & bfd) { + goals.insert(worker.makeDerivationGoal( + bfd.drvPath, bfd.outputs, buildMode)); + }, + [&](const DerivedPath::Opaque & bo) { + goals.insert(worker.makePathSubstitutionGoal( + bo.path, buildMode == bmRepair ? Repair : NoRepair)); + }, }, - [&](const DerivedPath::Opaque & bo) { - goals.insert(worker.makePathSubstitutionGoal(bo.path, buildMode == bmRepair ? Repair : NoRepair)); - }, - }, br.raw()); + br.raw()); } worker.run(goals); @@ -33,8 +38,10 @@ void Store::buildPaths(const std::vector & reqs, BuildMode buildMod ex = i->ex; } if (i->exitCode != Goal::ecSuccess) { - if (auto i2 = dynamic_cast(i.get())) failed.insert(i2->drvPath); - else if (auto i2 = dynamic_cast(i.get())) failed.insert(i2->storePath); + if (auto i2 = dynamic_cast(i.get())) + failed.insert(i2->drvPath); + else if (auto i2 = dynamic_cast(i.get())) + failed.insert(i2->storePath); } } @@ -42,28 +49,34 @@ void Store::buildPaths(const std::vector & reqs, BuildMode buildMod ex->status = worker.exitStatus(); throw *ex; } else if (!failed.empty()) { - if (ex) logError(ex->info()); - throw Error(worker.exitStatus(), "build of %s failed", showPaths(failed)); + if (ex) + logError(ex->info()); + throw Error(worker.exitStatus(), "build of %s failed", + showPaths(failed)); } } -std::vector Store::buildPathsWithResults( - const std::vector & reqs, - BuildMode buildMode, - std::shared_ptr evalStore) +std::vector +Store::buildPathsWithResults(const std::vector & reqs, + BuildMode buildMode, + std::shared_ptr evalStore) { Worker worker(*this, evalStore ? *evalStore : *this); Goals goals; for (const auto & br : reqs) { - std::visit(overloaded { - [&](const DerivedPath::Built & bfd) { - goals.insert(worker.makeDerivationGoal(bfd.drvPath, bfd.outputs, buildMode)); - }, - [&](const DerivedPath::Opaque & bo) { - goals.insert(worker.makePathSubstitutionGoal(bo.path, buildMode == bmRepair ? Repair : NoRepair)); + std::visit( + overloaded{ + [&](const DerivedPath::Built & bfd) { + goals.insert(worker.makeDerivationGoal( + bfd.drvPath, bfd.outputs, buildMode)); + }, + [&](const DerivedPath::Opaque & bo) { + goals.insert(worker.makePathSubstitutionGoal( + bo.path, buildMode == bmRepair ? Repair : NoRepair)); + }, }, - }, br.raw()); + br.raw()); } worker.run(goals); @@ -76,8 +89,9 @@ std::vector Store::buildPathsWithResults( return results; } -BuildResult Store::buildDerivation(const StorePath & drvPath, const BasicDerivation & drv, - BuildMode buildMode) +BuildResult Store::buildDerivation(const StorePath & drvPath, + const BasicDerivation & drv, + BuildMode buildMode) { Worker worker(*this, *this); auto goal = worker.makeBasicDerivationGoal(drvPath, drv, {}, buildMode); @@ -86,19 +100,19 @@ BuildResult Store::buildDerivation(const StorePath & drvPath, const BasicDerivat worker.run(Goals{goal}); return goal->buildResult; } catch (Error & e) { - return BuildResult { + return BuildResult{ .status = BuildResult::MiscFailure, .errorMsg = e.msg(), - .path = DerivedPath::Built { .drvPath = drvPath }, + .path = DerivedPath::Built{.drvPath = drvPath}, }; }; } - void Store::ensurePath(const StorePath & path) { /* If the path is already valid, we're done. */ - if (isValidPath(path)) return; + if (isValidPath(path)) + return; Worker worker(*this, *this); GoalPtr goal = worker.makePathSubstitutionGoal(path); @@ -111,11 +125,12 @@ void Store::ensurePath(const StorePath & path) goal->ex->status = worker.exitStatus(); throw *goal->ex; } else - throw Error(worker.exitStatus(), "path '%s' does not exist and cannot be created", printStorePath(path)); + throw Error(worker.exitStatus(), + "path '%s' does not exist and cannot be created", + printStorePath(path)); } } - void LocalStore::repairPath(const StorePath & path) { Worker worker(*this, *this); @@ -130,10 +145,12 @@ void LocalStore::repairPath(const StorePath & path) auto info = queryPathInfo(path); if (info->deriver && isValidPath(*info->deriver)) { goals.clear(); - goals.insert(worker.makeDerivationGoal(*info->deriver, StringSet(), bmRepair)); + goals.insert(worker.makeDerivationGoal(*info->deriver, StringSet(), + bmRepair)); worker.run(goals); } else - throw Error(worker.exitStatus(), "cannot repair path '%s'", printStorePath(path)); + throw Error(worker.exitStatus(), "cannot repair path '%s'", + printStorePath(path)); } } diff --git a/src/libstore/build/goal.cc b/src/libstore/build/goal.cc index 58e805f55075..52ed3663837a 100644 --- a/src/libstore/build/goal.cc +++ b/src/libstore/build/goal.cc @@ -3,14 +3,13 @@ namespace nix { - -bool CompareGoalPtrs::operator() (const GoalPtr & a, const GoalPtr & b) const { +bool CompareGoalPtrs::operator()(const GoalPtr & a, const GoalPtr & b) const +{ std::string s1 = a->key(); std::string s2 = b->key(); return s1 < s2; } - void addToWeakGoals(WeakGoals & goals, GoalPtr p) { if (goals.find(p) != goals.end()) @@ -18,14 +17,12 @@ void addToWeakGoals(WeakGoals & goals, GoalPtr p) goals.insert(p); } - void Goal::addWaitee(GoalPtr waitee) { waitees.insert(waitee); addToWeakGoals(waitee->waiters, shared_from_this()); } - void Goal::waiteeDone(GoalPtr waitee, ExitCode result) { assert(waitees.count(waitee)); @@ -33,11 +30,15 @@ void Goal::waiteeDone(GoalPtr waitee, ExitCode result) trace(fmt("waitee '%s' done; %d left", waitee->name, waitees.size())); - if (result == ecFailed || result == ecNoSubstituters || result == ecIncompleteClosure) ++nrFailed; + if (result == ecFailed || result == ecNoSubstituters || + result == ecIncompleteClosure) + ++nrFailed; - if (result == ecNoSubstituters) ++nrNoSubstituters; + if (result == ecNoSubstituters) + ++nrNoSubstituters; - if (result == ecIncompleteClosure) ++nrIncompleteClosure; + if (result == ecIncompleteClosure) + ++nrIncompleteClosure; if (waitees.empty() || (result == ecFailed && !settings.keepGoing)) { @@ -52,12 +53,12 @@ void Goal::waiteeDone(GoalPtr waitee, ExitCode result) } } - void Goal::amDone(ExitCode result, std::optional ex) { trace("done"); assert(exitCode == ecBusy); - assert(result == ecSuccess || result == ecFailed || result == ecNoSubstituters || result == ecIncompleteClosure); + assert(result == ecSuccess || result == ecFailed || + result == ecNoSubstituters || result == ecIncompleteClosure); exitCode = result; if (ex) { @@ -69,7 +70,8 @@ void Goal::amDone(ExitCode result, std::optional ex) for (auto & i : waiters) { GoalPtr goal = i.lock(); - if (goal) goal->waiteeDone(shared_from_this(), result); + if (goal) + goal->waiteeDone(shared_from_this(), result); } waiters.clear(); worker.removeGoal(shared_from_this()); @@ -77,10 +79,6 @@ void Goal::amDone(ExitCode result, std::optional ex) cleanup(); } - -void Goal::trace(const FormatOrString & fs) -{ - debug("%1%: %2%", name, fs.s); -} +void Goal::trace(const FormatOrString & fs) { debug("%1%: %2%", name, fs.s); } } diff --git a/src/libstore/build/goal.hh b/src/libstore/build/goal.hh index 35121c5d9e7c..b9d16c1be0a8 100644 --- a/src/libstore/build/goal.hh +++ b/src/libstore/build/goal.hh @@ -15,7 +15,7 @@ typedef std::shared_ptr GoalPtr; typedef std::weak_ptr WeakGoalPtr; struct CompareGoalPtrs { - bool operator() (const GoalPtr & a, const GoalPtr & b) const; + bool operator()(const GoalPtr & a, const GoalPtr & b) const; }; /* Set of goals. */ @@ -25,9 +25,14 @@ typedef std::set> WeakGoals; /* A map of paths to goals (and the other way around). */ typedef std::map WeakGoalMap; -struct Goal : public std::enable_shared_from_this -{ - typedef enum {ecBusy, ecSuccess, ecFailed, ecNoSubstituters, ecIncompleteClosure} ExitCode; +struct Goal : public std::enable_shared_from_this { + typedef enum { + ecBusy, + ecSuccess, + ecFailed, + ecNoSubstituters, + ecIncompleteClosure + } ExitCode; /* Backlink to the worker. */ Worker & worker; @@ -63,37 +68,25 @@ struct Goal : public std::enable_shared_from_this std::optional ex; Goal(Worker & worker, DerivedPath path) - : worker(worker) - , buildResult { .path = std::move(path) } - { } - - virtual ~Goal() + : worker(worker), buildResult{.path = std::move(path)} { - trace("goal destroyed"); } + virtual ~Goal() { trace("goal destroyed"); } + virtual void work() = 0; void addWaitee(GoalPtr waitee); virtual void waiteeDone(GoalPtr waitee, ExitCode result); - virtual void handleChildOutput(int fd, std::string_view data) - { - abort(); - } + virtual void handleChildOutput(int fd, std::string_view data) { abort(); } - virtual void handleEOF(int fd) - { - abort(); - } + virtual void handleEOF(int fd) { abort(); } void trace(const FormatOrString & fs); - std::string getName() - { - return name; - } + std::string getName() { return name; } /* Callback in case of a timeout. It should wake up its waiters, get rid of any running child processes that are being monitored @@ -104,7 +97,7 @@ struct Goal : public std::enable_shared_from_this void amDone(ExitCode result, std::optional ex = {}); - virtual void cleanup() { } + virtual void cleanup() {} }; void addToWeakGoals(WeakGoals & goals, GoalPtr p); diff --git a/src/libstore/build/hook-instance.cc b/src/libstore/build/hook-instance.cc index 1f19ddccc077..8f2b72ee63fb 100644 --- a/src/libstore/build/hook-instance.cc +++ b/src/libstore/build/hook-instance.cc @@ -7,7 +7,8 @@ HookInstance::HookInstance() { debug("starting build hook '%s'", settings.buildHook); - auto buildHookArgs = tokenizeString>(settings.buildHook.get()); + auto buildHookArgs = + tokenizeString>(settings.buildHook.get()); if (buildHookArgs.empty()) throw Error("'build-hook' setting is empty"); @@ -34,10 +35,10 @@ HookInstance::HookInstance() /* Fork the hook. */ pid = startProcess([&]() { - commonChildInit(fromHook); - if (chdir("/") == -1) throw SysError("changing into /"); + if (chdir("/") == -1) + throw SysError("changing into /"); /* Dup the communication pipes. */ if (dup2(toHook.readSide.get(), STDIN_FILENO) == -1) @@ -69,12 +70,12 @@ HookInstance::HookInstance() sink << 0; } - HookInstance::~HookInstance() { try { toHook.writeSide = -1; - if (pid != -1) pid.kill(); + if (pid != -1) + pid.kill(); } catch (...) { ignoreException(); } diff --git a/src/libstore/build/hook-instance.hh b/src/libstore/build/hook-instance.hh index 9e8cff128804..c2226fc3e32d 100644 --- a/src/libstore/build/hook-instance.hh +++ b/src/libstore/build/hook-instance.hh @@ -5,8 +5,7 @@ namespace nix { -struct HookInstance -{ +struct HookInstance { /* Pipes for talking to the build hook. */ Pipe toHook; diff --git a/src/libstore/build/local-derivation-goal.cc b/src/libstore/build/local-derivation-goal.cc index d1ec91ed5b23..dd70be986ad1 100644 --- a/src/libstore/build/local-derivation-goal.cc +++ b/src/libstore/build/local-derivation-goal.cc @@ -46,7 +46,8 @@ #if HAVE_SECCOMP #include #endif -#define pivot_root(new_root, put_old) (syscall(SYS_pivot_root, new_root, put_old)) +#define pivot_root(new_root, put_old) \ + (syscall(SYS_pivot_root, new_root, put_old)) #endif #if __APPLE__ @@ -59,27 +60,22 @@ namespace nix { -void handleDiffHook( - uid_t uid, uid_t gid, - const Path & tryA, const Path & tryB, - const Path & drvPath, const Path & tmpDir) +void handleDiffHook(uid_t uid, uid_t gid, const Path & tryA, const Path & tryB, + const Path & drvPath, const Path & tmpDir) { auto diffHook = settings.diffHook; if (diffHook != "" && settings.runDiffHook) { try { - auto diffRes = runProgram(RunOptions { - .program = diffHook, - .searchPath = true, - .args = {tryA, tryB, drvPath, tmpDir}, - .uid = uid, - .gid = gid, - .chdir = "/" - }); + auto diffRes = + runProgram(RunOptions{.program = diffHook, + .searchPath = true, + .args = {tryA, tryB, drvPath, tmpDir}, + .uid = uid, + .gid = gid, + .chdir = "/"}); if (!statusOk(diffRes.first)) - throw ExecError(diffRes.first, - "diff-hook program '%1%' %2%", - diffHook, - statusToString(diffRes.first)); + throw ExecError(diffRes.first, "diff-hook program '%1%' %2%", + diffHook, statusToString(diffRes.first)); if (diffRes.second != "") printError(chomp(diffRes.second)); @@ -94,17 +90,27 @@ void handleDiffHook( const Path LocalDerivationGoal::homeDir = "/homeless-shelter"; - LocalDerivationGoal::~LocalDerivationGoal() { /* Careful: we should never ever throw an exception from a destructor. */ - try { deleteTmpDir(false); } catch (...) { ignoreException(); } - try { killChild(); } catch (...) { ignoreException(); } - try { stopDaemon(); } catch (...) { ignoreException(); } + try { + deleteTmpDir(false); + } catch (...) { + ignoreException(); + } + try { + killChild(); + } catch (...) { + ignoreException(); + } + try { + stopDaemon(); + } catch (...) { + ignoreException(); + } } - inline bool LocalDerivationGoal::needsHashRewrite() { #if __linux__ @@ -115,7 +121,6 @@ inline bool LocalDerivationGoal::needsHashRewrite() #endif } - LocalStore & LocalDerivationGoal::getLocalStore() { auto p = dynamic_cast(&worker.store); @@ -123,7 +128,6 @@ LocalStore & LocalDerivationGoal::getLocalStore() return *p; } - void LocalDerivationGoal::killChild() { if (pid != -1) { @@ -148,8 +152,8 @@ void LocalDerivationGoal::killChild() DerivationGoal::killChild(); } - -void LocalDerivationGoal::tryLocalBuild() { +void LocalDerivationGoal::tryLocalBuild() +{ unsigned int curBuilds = worker.getNrLocalBuilds(); if (curBuilds >= settings.maxBuildJobs) { state = &DerivationGoal::tryToBuild; @@ -162,7 +166,8 @@ void LocalDerivationGoal::tryLocalBuild() { one of the members of that group. */ if (settings.buildUsersGroup != "" && getuid() == 0) { #if defined(__linux__) || defined(__APPLE__) - if (!buildUser) buildUser = std::make_unique(); + if (!buildUser) + buildUser = std::make_unique(); if (buildUser->findFreeUser()) { /* Make sure that no other processes are executing under this @@ -170,15 +175,18 @@ void LocalDerivationGoal::tryLocalBuild() { buildUser->kill(); } else { if (!actLock) - actLock = std::make_unique(*logger, lvlWarn, actBuildWaiting, - fmt("waiting for UID to build '%s'", yellowtxt(worker.store.printStorePath(drvPath)))); + actLock = std::make_unique( + *logger, lvlWarn, actBuildWaiting, + fmt("waiting for UID to build '%s'", + yellowtxt(worker.store.printStorePath(drvPath)))); worker.waitForAWhile(shared_from_this()); return; } #else /* Don't know how to block the creation of setuid/setgid binaries on this platform. */ - throw Error("build users are not supported on this platform for security reasons"); + throw Error("build users are not supported on this platform for " + "security reasons"); #endif } @@ -210,7 +218,6 @@ static void chmod_(const Path & path, mode_t mode) throw SysError("setting permissions on '%s'", path); } - /* Move/rename path 'src' to 'dst'. Temporarily make 'src' writable if it's a directory and we're not root (to be able to update the directory's parent link ".."). */ @@ -218,7 +225,8 @@ static void movePath(const Path & src, const Path & dst) { auto st = lstat(src); - bool changePerm = (geteuid() && S_ISDIR(st.st_mode) && !(st.st_mode & S_IWUSR)); + bool changePerm = + (geteuid() && S_ISDIR(st.st_mode) && !(st.st_mode & S_IWUSR)); if (changePerm) chmod_(src, st.st_mode | S_IWUSR); @@ -230,10 +238,8 @@ static void movePath(const Path & src, const Path & dst) chmod_(dst, st.st_mode); } - extern void replaceValidPath(const Path & storePath, const Path & tmpPath); - int LocalDerivationGoal::getChildStatus() { return hook ? DerivationGoal::getChildStatus() : pid.kill(); @@ -247,7 +253,6 @@ void LocalDerivationGoal::closeReadPipes() builderOut.readSide = -1; } - void LocalDerivationGoal::cleanupHookFinally() { /* Release the build user at the end of this function. We don't do @@ -256,14 +261,12 @@ void LocalDerivationGoal::cleanupHookFinally() buildUser.reset(); } - void LocalDerivationGoal::cleanupPreChildKill() { sandboxMountNamespace = -1; sandboxUserNamespace = -1; } - void LocalDerivationGoal::cleanupPostChildKill() { /* When running under a build user, make sure that all processes @@ -271,13 +274,13 @@ void LocalDerivationGoal::cleanupPostChildKill() malicious user from leaving behind a process that keeps files open and modifies them after they have been chown'ed to root. */ - if (buildUser) buildUser->kill(); + if (buildUser) + buildUser->kill(); /* Terminate the recursive Nix daemon. */ stopDaemon(); } - bool LocalDerivationGoal::cleanupDecideWhetherDiskFull() { bool diskFull = false; @@ -307,8 +310,10 @@ bool LocalDerivationGoal::cleanupDecideWhetherDiskFull() build failures. */ if (useChroot && buildMode == bmNormal) for (auto & [_, status] : initialOutputs) { - if (!status.known) continue; - if (buildMode != bmCheck && status.known->isValid()) continue; + if (!status.known) + continue; + if (buildMode != bmCheck && status.known->isValid()) + continue; auto p = worker.store.printStorePath(status.known->path); if (pathExists(chrootRootDir + p)) rename((chrootRootDir + p).c_str(), p.c_str()); @@ -317,13 +322,11 @@ bool LocalDerivationGoal::cleanupDecideWhetherDiskFull() return diskFull; } - void LocalDerivationGoal::cleanupPostOutputsRegisteredModeCheck() { deleteTmpDir(true); } - void LocalDerivationGoal::cleanupPostOutputsRegisteredModeNonCheck() { /* Delete unused redirected outputs (when doing hash rewriting). */ @@ -336,7 +339,6 @@ void LocalDerivationGoal::cleanupPostOutputsRegisteredModeNonCheck() cleanupPostOutputsRegisteredModeCheck(); } - int childEntry(void * arg) { ((LocalDerivationGoal *) arg)->runChild(); @@ -361,20 +363,21 @@ static void linkOrCopy(const Path & from, const Path & to) } #endif - void LocalDerivationGoal::startBuilder() { /* Right platform? */ if (!parsedDrv->canBuildLocally(worker.store)) - throw Error("a '%s' with features {%s} is required to build '%s', but I am a '%s' with features {%s}", + throw Error( + "a '%s' with features {%s} is required to build '%s', but I am a " + "'%s' with features {%s}", drv->platform, concatStringsSep(", ", parsedDrv->getRequiredSystemFeatures()), - worker.store.printStorePath(drvPath), - settings.thisSystem, + worker.store.printStorePath(drvPath), settings.thisSystem, concatStringsSep(", ", worker.store.systemFeatures)); #if __APPLE__ - additionalSandboxProfile = parsedDrv->getStringAttr("__sandboxProfile").value_or(""); + additionalSandboxProfile = + parsedDrv->getStringAttr("__sandboxProfile").value_or(""); #endif /* Are we doing a chroot build? */ @@ -383,15 +386,17 @@ void LocalDerivationGoal::startBuilder() if (settings.sandboxMode == smEnabled) { if (noChroot) throw Error("derivation '%s' has '__noChroot' set, " - "but that's not allowed when 'sandbox' is 'true'", worker.store.printStorePath(drvPath)); + "but that's not allowed when 'sandbox' is 'true'", + worker.store.printStorePath(drvPath)); #if __APPLE__ if (additionalSandboxProfile != "") - throw Error("derivation '%s' specifies a sandbox profile, " - "but this is only allowed when 'sandbox' is 'relaxed'", worker.store.printStorePath(drvPath)); + throw Error( + "derivation '%s' specifies a sandbox profile, " + "but this is only allowed when 'sandbox' is 'relaxed'", + worker.store.printStorePath(drvPath)); #endif useChroot = true; - } - else if (settings.sandboxMode == smDisabled) + } else if (settings.sandboxMode == smDisabled) useChroot = false; else if (settings.sandboxMode == smRelaxed) useChroot = derivationType.isSandboxed() && !noChroot; @@ -399,16 +404,18 @@ void LocalDerivationGoal::startBuilder() auto & localStore = getLocalStore(); if (localStore.storeDir != localStore.realStoreDir.get()) { - #if __linux__ - useChroot = true; - #else - throw Error("building using a diverted store is not supported on this platform"); - #endif +#if __linux__ + useChroot = true; +#else + throw Error("building using a diverted store is not supported on this " + "platform"); +#endif } /* Create a temporary directory where the build will take place. */ - tmpDir = createTempDir("", "nix-build-" + std::string(drvPath.name()), false, false, 0700); + tmpDir = createTempDir("", "nix-build-" + std::string(drvPath.name()), + false, false, 0700); chownToBuilder(tmpDir); @@ -425,46 +432,49 @@ void LocalDerivationGoal::startBuilder() contents of the new outputs to replace the dummy strings with the actual hashes. */ auto scratchPath = - !status.known - ? makeFallbackPath(outputName) + !status.known ? makeFallbackPath(outputName) : !needsHashRewrite() /* Can always use original path in sandbox */ ? status.known->path - : !status.known->isPresent() - /* If path doesn't yet exist can just use it */ - ? status.known->path - : buildMode != bmRepair && !status.known->isValid() - /* If we aren't repairing we'll delete a corrupted path, so we - can use original path */ - ? status.known->path - : /* If we are repairing or the path is totally valid, we'll need - to use a temporary path */ - makeFallbackPath(status.known->path); + : !status.known->isPresent() + /* If path doesn't yet exist can just use it */ + ? status.known->path + : buildMode != bmRepair && !status.known->isValid() + /* If we aren't repairing we'll delete a corrupted + path, so we can use original path */ + ? status.known->path + : /* If we are repairing or the path is totally + valid, we'll need to use a temporary path */ + makeFallbackPath(status.known->path); scratchOutputs.insert_or_assign(outputName, scratchPath); /* Substitute output placeholders with the scratch output paths. We'll use during the build. */ - inputRewrites[hashPlaceholder(outputName)] = worker.store.printStorePath(scratchPath); + inputRewrites[hashPlaceholder(outputName)] = + worker.store.printStorePath(scratchPath); /* Additional tasks if we know the final path a priori. */ - if (!status.known) continue; + if (!status.known) + continue; auto fixedFinalPath = status.known->path; /* Additional tasks if the final and scratch are both known and differ. */ - if (fixedFinalPath == scratchPath) continue; + if (fixedFinalPath == scratchPath) + continue; /* Ensure scratch path is ours to use. */ deletePath(worker.store.printStorePath(scratchPath)); /* Rewrite and unrewrite paths */ { - std::string h1 { fixedFinalPath.hashPart() }; - std::string h2 { scratchPath.hashPart() }; + std::string h1{fixedFinalPath.hashPart()}; + std::string h2{scratchPath.hashPart()}; inputRewrites[h1] = h2; } - redirectedOutputs.insert_or_assign(std::move(fixedFinalPath), std::move(scratchPath)); + redirectedOutputs.insert_or_assign(std::move(fixedFinalPath), + std::move(scratchPath)); } /* Construct the environment passed to the builder. */ @@ -484,22 +494,28 @@ void LocalDerivationGoal::startBuilder() auto s = getOr(drv->env, "exportReferencesGraph", ""); Strings ss = tokenizeString(s); if (ss.size() % 2 != 0) - throw BuildError("odd number of tokens in 'exportReferencesGraph': '%1%'", s); - for (Strings::iterator i = ss.begin(); i != ss.end(); ) { + throw BuildError( + "odd number of tokens in 'exportReferencesGraph': '%1%'", s); + for (Strings::iterator i = ss.begin(); i != ss.end();) { auto fileName = *i++; static std::regex regex("[A-Za-z_][A-Za-z0-9_.-]*"); if (!std::regex_match(fileName, regex)) - throw Error("invalid file name '%s' in 'exportReferencesGraph'", fileName); + throw Error("invalid file name '%s' in 'exportReferencesGraph'", + fileName); auto storePathS = *i++; if (!worker.store.isInStore(storePathS)) - throw BuildError("'exportReferencesGraph' contains a non-store path '%1%'", storePathS); + throw BuildError( + "'exportReferencesGraph' contains a non-store path '%1%'", + storePathS); auto storePath = worker.store.toStorePath(storePathS).first; /* Write closure info to . */ - writeFile(tmpDir + "/" + fileName, + writeFile( + tmpDir + "/" + fileName, worker.store.makeValidityRegistration( - worker.store.exportReferences({storePath}, inputPaths), false, false)); + worker.store.exportReferences({storePath}, inputPaths), + false, false)); } } @@ -510,7 +526,8 @@ void LocalDerivationGoal::startBuilder() dirsInChroot.clear(); for (auto i : settings.sandboxPaths.get()) { - if (i.empty()) continue; + if (i.empty()) + continue; bool optional = false; if (i[i.size() - 1] == '?') { optional = true; @@ -529,7 +546,9 @@ void LocalDerivationGoal::startBuilder() for (auto & i : dirsInChroot) try { if (worker.store.isInStore(i.second.source)) - worker.store.computeFSClosure(worker.store.toStorePath(i.second.source).first, closure); + worker.store.computeFSClosure( + worker.store.toStorePath(i.second.source).first, + closure); } catch (InvalidPath & e) { } catch (Error & e) { e.addTrace({}, "while processing 'sandbox-paths'"); @@ -543,7 +562,8 @@ void LocalDerivationGoal::startBuilder() PathSet allowedPaths = settings.allowedImpureHostPrefixes; /* This works like the above, except on a per-derivation level */ - auto impurePaths = parsedDrv->getStringsAttr("__impureHostDeps").value_or(Strings()); + auto impurePaths = + parsedDrv->getStringsAttr("__impureHostDeps").value_or(Strings()); for (auto & i : impurePaths) { bool found = false; @@ -551,7 +571,8 @@ void LocalDerivationGoal::startBuilder() giving a non-root user info about inaccessible files. */ Path canonI = canonPath(i); - /* If only we had a trie to do this more efficiently :) luckily, these are generally going to be pretty small */ + /* If only we had a trie to do this more efficiently :) luckily, + * these are generally going to be pretty small */ for (auto & a : allowedPaths) { Path canonA = canonPath(a); if (canonI == canonA || isInDir(canonI, canonA)) { @@ -560,8 +581,9 @@ void LocalDerivationGoal::startBuilder() } } if (!found) - throw Error("derivation '%s' requested impure path '%s', but it was not in allowed-impure-host-deps", - worker.store.printStorePath(drvPath), i); + throw Error("derivation '%s' requested impure path '%s', but " + "it was not in allowed-impure-host-deps", + worker.store.printStorePath(drvPath), i); /* Allow files in __impureHostDeps to be missing; e.g. macOS 11+ has no /usr/lib/libSystem*.dylib */ @@ -579,12 +601,14 @@ void LocalDerivationGoal::startBuilder() /* Clean up the chroot directory automatically. */ autoDelChroot = std::make_shared(chrootRootDir); - printMsg(lvlChatty, format("setting up chroot environment in '%1%'") % chrootRootDir); + printMsg(lvlChatty, format("setting up chroot environment in '%1%'") % + chrootRootDir); if (mkdir(chrootRootDir.c_str(), 0750) == -1) throw SysError("cannot create '%1%'", chrootRootDir); - if (buildUser && chown(chrootRootDir.c_str(), 0, buildUser->getGID()) == -1) + if (buildUser && + chown(chrootRootDir.c_str(), 0, buildUser->getGID()) == -1) throw SysError("cannot change ownership of '%1%'", chrootRootDir); /* Create a writable /tmp in the chroot. Many builders need @@ -601,14 +625,15 @@ void LocalDerivationGoal::startBuilder() /* Declare the build user's group so that programs get a consistent view of the system (e.g., "id -gn"). */ - writeFile(chrootRootDir + "/etc/group", - fmt("root:x:0:\n" - "nixbld:!:%1%:\n" - "nogroup:x:65534:\n", sandboxGid())); + writeFile(chrootRootDir + "/etc/group", fmt("root:x:0:\n" + "nixbld:!:%1%:\n" + "nogroup:x:65534:\n", + sandboxGid())); /* Create /etc/hosts with localhost entry. */ if (derivationType.isSandboxed()) - writeFile(chrootRootDir + "/etc/hosts", "127.0.0.1 localhost\n::1 localhost\n"); + writeFile(chrootRootDir + "/etc/hosts", + "127.0.0.1 localhost\n::1 localhost\n"); /* Make the closure of the inputs available in the chroot, rather than the whole Nix store. This prevents any access @@ -621,7 +646,8 @@ void LocalDerivationGoal::startBuilder() createDirs(chrootStoreDir); chmod_(chrootStoreDir, 01775); - if (buildUser && chown(chrootStoreDir.c_str(), 0, buildUser->getGID()) == -1) + if (buildUser && + chown(chrootStoreDir.c_str(), 0, buildUser->getGID()) == -1) throw SysError("cannot change ownership of '%1%'", chrootStoreDir); for (auto & i : inputPaths) { @@ -645,7 +671,8 @@ void LocalDerivationGoal::startBuilder() is already in the sandbox, so we don't need to worry about removing it. */ if (i.second.second) - dirsInChroot.erase(worker.store.printStorePath(*i.second.second)); + dirsInChroot.erase( + worker.store.printStorePath(*i.second.second)); } #elif __APPLE__ @@ -657,27 +684,29 @@ void LocalDerivationGoal::startBuilder() } if (needsHashRewrite() && pathExists(homeDir)) - throw Error("home directory '%1%' exists; please remove it to assure purity of builds without sandboxing", homeDir); - - if (useChroot && settings.preBuildHook != "" && dynamic_cast(drv.get())) { - printMsg(lvlChatty, format("executing pre-build hook '%1%'") - % settings.preBuildHook); - auto args = useChroot ? Strings({worker.store.printStorePath(drvPath), chrootRootDir}) : - Strings({ worker.store.printStorePath(drvPath) }); - enum BuildHookState { - stBegin, - stExtraChrootDirs - }; + throw Error("home directory '%1%' exists; please remove it to assure " + "purity of builds without sandboxing", + homeDir); + + if (useChroot && settings.preBuildHook != "" && + dynamic_cast(drv.get())) { + printMsg(lvlChatty, format("executing pre-build hook '%1%'") % + settings.preBuildHook); + auto args = + useChroot + ? Strings({worker.store.printStorePath(drvPath), chrootRootDir}) + : Strings({worker.store.printStorePath(drvPath)}); + enum BuildHookState { stBegin, stExtraChrootDirs }; auto state = stBegin; auto lines = runProgram(settings.preBuildHook, false, args); auto lastPos = std::string::size_type{0}; for (auto nlPos = lines.find('\n'); nlPos != std::string::npos; - nlPos = lines.find('\n', lastPos)) - { + nlPos = lines.find('\n', lastPos)) { auto line = lines.substr(lastPos, nlPos - lastPos); lastPos = nlPos + 1; if (state == stBegin) { - if (line == "extra-sandbox-paths" || line == "extra-chroot-dirs") { + if (line == "extra-sandbox-paths" || + line == "extra-chroot-dirs") { state = stExtraChrootDirs; } else { throw Error("unknown pre-build hook command '%1%'", line); @@ -703,15 +732,17 @@ void LocalDerivationGoal::startBuilder() /* Run the builder. */ printMsg(lvlChatty, "executing builder '%1%'", drv->builder); - printMsg(lvlChatty, "using builder args '%1%'", concatStringsSep(" ", drv->args)); + printMsg(lvlChatty, "using builder args '%1%'", + concatStringsSep(" ", drv->args)); for (auto & i : drv->env) - printMsg(lvlVomit, "setting builder env variable '%1%'='%2%'", i.first, i.second); + printMsg(lvlVomit, "setting builder env variable '%1%'='%2%'", i.first, + i.second); /* Create the log file. */ Path logFile = openLogFile(); /* Create a pipe to get the output of the builder. */ - //builderOut.create(); + // builderOut.create(); builderOut.readSide = posix_openpt(O_RDWR | O_NOCTTY); if (!builderOut.readSide) @@ -734,12 +765,12 @@ void LocalDerivationGoal::startBuilder() } #endif - #if 0 +#if 0 // Mount the pt in the sandbox so that the "tty" command works. // FIXME: this doesn't work with the new devpts in the sandbox. if (useChroot) dirsInChroot[slaveName] = {slaveName, false}; - #endif +#endif if (unlockpt(builderOut.readSide.get())) throw SysError("unlocking pseudoterminal"); @@ -805,13 +836,12 @@ void LocalDerivationGoal::startBuilder() Path maxUserNamespaces = "/proc/sys/user/max_user_namespaces"; static bool userNamespacesEnabled = - pathExists(maxUserNamespaces) - && trim(readFile(maxUserNamespaces)) != "0"; + pathExists(maxUserNamespaces) && + trim(readFile(maxUserNamespaces)) != "0"; usingUserNamespace = userNamespacesEnabled; Pid helper = startProcess([&]() { - /* Drop additional groups here because we can't do it after we've created the new user namespace. FIXME: this means that if we're not root in the parent @@ -824,11 +854,14 @@ void LocalDerivationGoal::startBuilder() throw SysError("setgroups failed"); size_t stackSize = 1 * 1024 * 1024; - char * stack = (char *) mmap(0, stackSize, - PROT_WRITE | PROT_READ, MAP_PRIVATE | MAP_ANONYMOUS | MAP_STACK, -1, 0); - if (stack == MAP_FAILED) throw SysError("allocating stack"); - - int flags = CLONE_NEWPID | CLONE_NEWNS | CLONE_NEWIPC | CLONE_NEWUTS | CLONE_PARENT | SIGCHLD; + char * stack = + (char *) mmap(0, stackSize, PROT_WRITE | PROT_READ, + MAP_PRIVATE | MAP_ANONYMOUS | MAP_STACK, -1, 0); + if (stack == MAP_FAILED) + throw SysError("allocating stack"); + + int flags = CLONE_NEWPID | CLONE_NEWNS | CLONE_NEWIPC | + CLONE_NEWUTS | CLONE_PARENT | SIGCHLD; if (privateNetwork) flags |= CLONE_NEWNET; if (usingUserNamespace) @@ -841,7 +874,8 @@ void LocalDerivationGoal::startBuilder() flags &= ~CLONE_NEWPID; child = clone(childEntry, stack + stackSize, flags, this); } - if (usingUserNamespace && child == -1 && (errno == EPERM || errno == EINVAL)) { + if (usingUserNamespace && child == -1 && + (errno == EPERM || errno == EINVAL)) { /* Some distros patch Linux to not allow unprivileged * user namespaces. If we get EPERM or EINVAL, try * without CLONE_NEWUSER and see if that works. @@ -853,12 +887,14 @@ void LocalDerivationGoal::startBuilder() /* Otherwise exit with EPERM so we can handle this in the parent. This is only done when sandbox-fallback is set to true (the default). */ - if (child == -1 && (errno == EPERM || errno == EINVAL) && settings.sandboxFallback) + if (child == -1 && (errno == EPERM || errno == EINVAL) && + settings.sandboxFallback) _exit(1); - if (child == -1) throw SysError("cloning builder process"); + if (child == -1) + throw SysError("cloning builder process"); writeFull(builderOut.writeSide.get(), - fmt("%d %d\n", usingUserNamespace, child)); + fmt("%d %d\n", usingUserNamespace, child)); _exit(0); }); @@ -874,11 +910,10 @@ void LocalDerivationGoal::startBuilder() /* Close the write side to prevent runChild() from hanging reading from this. */ - Finally cleanup([&]() { - userNamespaceSync.writeSide = -1; - }); + Finally cleanup([&]() { userNamespaceSync.writeSide = -1; }); - auto ss = tokenizeString>(readLine(builderOut.readSide.get())); + auto ss = tokenizeString>( + readLine(builderOut.readSide.get())); assert(ss.size() == 2); usingUserNamespace = ss[0] == "1"; pid = string2Int(ss[1]).value(); @@ -891,34 +926,38 @@ void LocalDerivationGoal::startBuilder() uid_t hostGid = buildUser ? buildUser->getGID() : getgid(); writeFile("/proc/" + std::to_string(pid) + "/uid_map", - fmt("%d %d 1", sandboxUid(), hostUid)); + fmt("%d %d 1", sandboxUid(), hostUid)); writeFile("/proc/" + std::to_string(pid) + "/setgroups", "deny"); writeFile("/proc/" + std::to_string(pid) + "/gid_map", - fmt("%d %d 1", sandboxGid(), hostGid)); + fmt("%d %d 1", sandboxGid(), hostGid)); } else { debug("note: not using a user namespace"); if (!buildUser) - throw Error("cannot perform a sandboxed build because user namespaces are not enabled; check /proc/sys/user/max_user_namespaces"); + throw Error("cannot perform a sandboxed build because user " + "namespaces are not enabled; check " + "/proc/sys/user/max_user_namespaces"); } /* Now that we now the sandbox uid, we can write /etc/passwd. */ - writeFile(chrootRootDir + "/etc/passwd", fmt( - "root:x:0:0:Nix build user:%3%:/noshell\n" - "nixbld:x:%1%:%2%:Nix build user:%3%:/noshell\n" - "nobody:x:65534:65534:Nobody:/:/noshell\n", - sandboxUid(), sandboxGid(), settings.sandboxBuildDir)); + writeFile(chrootRootDir + "/etc/passwd", + fmt("root:x:0:0:Nix build user:%3%:/noshell\n" + "nixbld:x:%1%:%2%:Nix build user:%3%:/noshell\n" + "nobody:x:65534:65534:Nobody:/:/noshell\n", + sandboxUid(), sandboxGid(), settings.sandboxBuildDir)); /* Save the mount- and user namespace of the child. We have to do this - *before* the child does a chroot. */ - sandboxMountNamespace = open(fmt("/proc/%d/ns/mnt", (pid_t) pid).c_str(), O_RDONLY); + *before* the child does a chroot. */ + sandboxMountNamespace = + open(fmt("/proc/%d/ns/mnt", (pid_t) pid).c_str(), O_RDONLY); if (sandboxMountNamespace.get() == -1) throw SysError("getting sandbox mount namespace"); if (usingUserNamespace) { - sandboxUserNamespace = open(fmt("/proc/%d/ns/user", (pid_t) pid).c_str(), O_RDONLY); + sandboxUserNamespace = + open(fmt("/proc/%d/ns/user", (pid_t) pid).c_str(), O_RDONLY); if (sandboxUserNamespace.get() == -1) throw SysError("getting sandbox user namespace"); } @@ -932,15 +971,14 @@ void LocalDerivationGoal::startBuilder() #if __linux__ fallback: #endif - pid = startProcess([&]() { - runChild(); - }); + pid = startProcess([&]() { runChild(); }); } /* parent */ pid.setSeparatePG(true); builderOut.writeSide = -1; - worker.childStarted(shared_from_this(), {builderOut.readSide.get()}, true, true); + worker.childStarted(shared_from_this(), {builderOut.readSide.get()}, true, + true); /* Check if setting up the build environment failed. */ std::vector msgs; @@ -950,14 +988,16 @@ void LocalDerivationGoal::startBuilder() return readLine(builderOut.readSide.get()); } catch (Error & e) { auto status = pid.wait(); - e.addTrace({}, "while waiting for the build environment for '%s' to initialize (%s, previous messages: %s)", - worker.store.printStorePath(drvPath), - statusToString(status), - concatStringsSep("|", msgs)); + e.addTrace({}, + "while waiting for the build environment for '%s' " + "to initialize (%s, previous messages: %s)", + worker.store.printStorePath(drvPath), + statusToString(status), concatStringsSep("|", msgs)); throw; } }(); - if (msg.substr(0, 1) == "\2") break; + if (msg.substr(0, 1) == "\2") + break; if (msg.substr(0, 1) == "\1") { FdSource source(builderOut.readSide.get()); auto ex = readError(source); @@ -969,8 +1009,8 @@ void LocalDerivationGoal::startBuilder() } } - -void LocalDerivationGoal::initTmpDir() { +void LocalDerivationGoal::initTmpDir() +{ /* In a sandbox, for determinism, always use the same temporary directory. */ #if __linux__ @@ -988,7 +1028,8 @@ void LocalDerivationGoal::initTmpDir() { there is no size constraint). */ if (!parsedDrv->getStructuredAttrs()) { - StringSet passAsFile = tokenizeString(getOr(drv->env, "passAsFile", "")); + StringSet passAsFile = + tokenizeString(getOr(drv->env, "passAsFile", "")); for (auto & i : drv->env) { if (passAsFile.find(i.first) == passAsFile.end()) { env[i.first] = i.second; @@ -1001,7 +1042,6 @@ void LocalDerivationGoal::initTmpDir() { env[i.first + "Path"] = tmpDirInSandbox + "/" + fn; } } - } /* For convenience, set an environment pointing to the top build @@ -1018,7 +1058,6 @@ void LocalDerivationGoal::initTmpDir() { env["PWD"] = tmpDirInSandbox; } - void LocalDerivationGoal::initEnv() { env.clear(); @@ -1051,7 +1090,8 @@ void LocalDerivationGoal::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 (derivationType.isFixed()) env["NIX_OUTPUT_CHECKED"] = "1"; + if (derivationType.isFixed()) + env["NIX_OUTPUT_CHECKED"] = "1"; /* *Only* if this is a fixed-output derivation, propagate the values of the environment variables specified in the @@ -1063,7 +1103,8 @@ void LocalDerivationGoal::initEnv() fixed-output derivations is by definition pure (since we already know the cryptographic hash of the output). */ if (!derivationType.isSandboxed()) { - for (auto & i : parsedDrv->getStringsAttr("impureEnvVars").value_or(Strings())) + for (auto & i : + parsedDrv->getStringsAttr("impureEnvVars").value_or(Strings())) env[i] = getEnv(i).value_or(""); } @@ -1076,15 +1117,16 @@ void LocalDerivationGoal::initEnv() env["TERM"] = "xterm-256color"; } - void LocalDerivationGoal::writeStructuredAttrs() { - if (auto structAttrsJson = parsedDrv->prepareStructuredAttrs(worker.store, inputPaths)) { + if (auto structAttrsJson = + parsedDrv->prepareStructuredAttrs(worker.store, inputPaths)) { auto json = structAttrsJson.value(); nlohmann::json rewritten; for (auto & [i, v] : json["outputs"].get()) { - /* The placeholder must have a rewrite, so we use it to cover both the - cases where we know or don't know the output path ahead of time. */ + /* The placeholder must have a rewrite, so we use it to cover both + the cases where we know or don't know the output path ahead of + time. */ rewritten[i] = rewriteStrings((std::string) v, inputRewrites); } @@ -1095,34 +1137,29 @@ void LocalDerivationGoal::writeStructuredAttrs() writeFile(tmpDir + "/.attrs.sh", rewriteStrings(jsonSh, inputRewrites)); chownToBuilder(tmpDir + "/.attrs.sh"); env["NIX_ATTRS_SH_FILE"] = tmpDir + "/.attrs.sh"; - writeFile(tmpDir + "/.attrs.json", rewriteStrings(json.dump(), inputRewrites)); + writeFile(tmpDir + "/.attrs.json", + rewriteStrings(json.dump(), inputRewrites)); chownToBuilder(tmpDir + "/.attrs.json"); env["NIX_ATTRS_JSON_FILE"] = tmpDir + "/.attrs.json"; } } - static StorePath pathPartOfReq(const DerivedPath & req) { - return std::visit(overloaded { - [&](const DerivedPath::Opaque & bo) { - return bo.path; + return std::visit( + overloaded{ + [&](const DerivedPath::Opaque & bo) { return bo.path; }, + [&](const DerivedPath::Built & bfd) { return bfd.drvPath; }, }, - [&](const DerivedPath::Built & bfd) { - return bfd.drvPath; - }, - }, req.raw()); + req.raw()); } - bool LocalDerivationGoal::isAllowed(const DerivedPath & req) { return this->isAllowed(pathPartOfReq(req)); } - -struct RestrictedStoreConfig : virtual LocalFSStoreConfig -{ +struct RestrictedStoreConfig : virtual LocalFSStoreConfig { using LocalFSStoreConfig::LocalFSStoreConfig; const std::string name() { return "Restricted Store"; } }; @@ -1130,42 +1167,44 @@ struct RestrictedStoreConfig : virtual LocalFSStoreConfig /* A wrapper around LocalStore that only allows building/querying of paths that are in the input closures of the build or were added via recursive Nix calls. */ -struct RestrictedStore : public virtual RestrictedStoreConfig, public virtual LocalFSStore, public virtual GcStore -{ +struct RestrictedStore : public virtual RestrictedStoreConfig, + public virtual LocalFSStore, + public virtual GcStore { ref next; LocalDerivationGoal & goal; - RestrictedStore(const Params & params, ref next, LocalDerivationGoal & goal) - : StoreConfig(params) - , LocalFSStoreConfig(params) - , RestrictedStoreConfig(params) - , Store(params) - , LocalFSStore(params) - , next(next), goal(goal) - { } + RestrictedStore(const Params & params, ref next, + LocalDerivationGoal & goal) + : StoreConfig(params), LocalFSStoreConfig(params), + RestrictedStoreConfig(params), Store(params), LocalFSStore(params), + next(next), goal(goal) + { + } - Path getRealStoreDir() override - { return next->realStoreDir; } + Path getRealStoreDir() override { return next->realStoreDir; } - std::string getUri() override - { return next->getUri(); } + std::string getUri() override { return next->getUri(); } StorePathSet queryAllValidPaths() override { StorePathSet paths; - for (auto & p : goal.inputPaths) paths.insert(p); - for (auto & p : goal.addedPaths) paths.insert(p); + for (auto & p : goal.inputPaths) + paths.insert(p); + for (auto & p : goal.addedPaths) + paths.insert(p); return paths; } void queryPathInfoUncached(const StorePath & path, - Callback> callback) noexcept override + Callback> + callback) noexcept override { if (goal.isAllowed(path)) { try { /* Censor impure information. */ - auto info = std::make_shared(*next->queryPathInfo(path)); + auto info = + std::make_shared(*next->queryPathInfo(path)); info->deriver.reset(); info->registrationTime = 0; info->ultimate = false; @@ -1178,56 +1217,59 @@ struct RestrictedStore : public virtual RestrictedStoreConfig, public virtual Lo callback(nullptr); }; - void queryReferrers(const StorePath & path, StorePathSet & referrers) override - { } + void queryReferrers(const StorePath & path, + StorePathSet & referrers) override + { + } - std::map> queryPartialDerivationOutputMap(const StorePath & path) override + std::map> + queryPartialDerivationOutputMap(const StorePath & path) override { if (!goal.isAllowed(path)) - throw InvalidPath("cannot query output map for unknown path '%s' in recursive Nix", printStorePath(path)); + throw InvalidPath("cannot query output map for unknown path '%s' " + "in recursive Nix", + printStorePath(path)); return next->queryPartialDerivationOutputMap(path); } - std::optional queryPathFromHashPart(const std::string & hashPart) override - { throw Error("queryPathFromHashPart"); } + std::optional + queryPathFromHashPart(const std::string & hashPart) override + { + throw Error("queryPathFromHashPart"); + } - StorePath addToStore( - std::string_view name, - const Path & srcPath, - FileIngestionMethod method, - HashType hashAlgo, - PathFilter & filter, - RepairFlag repair, - const StorePathSet & references) override - { throw Error("addToStore"); } + StorePath addToStore(std::string_view name, const Path & srcPath, + FileIngestionMethod method, HashType hashAlgo, + PathFilter & filter, RepairFlag repair, + const StorePathSet & references) override + { + throw Error("addToStore"); + } void addToStore(const ValidPathInfo & info, Source & narSource, - RepairFlag repair = NoRepair, CheckSigsFlag checkSigs = CheckSigs) override + RepairFlag repair = NoRepair, + CheckSigsFlag checkSigs = CheckSigs) override { next->addToStore(info, narSource, repair, checkSigs); goal.addDependency(info.path); } - StorePath addTextToStore( - std::string_view name, - std::string_view s, - const StorePathSet & references, - RepairFlag repair = NoRepair) override + StorePath addTextToStore(std::string_view name, std::string_view s, + const StorePathSet & references, + RepairFlag repair = NoRepair) override { auto path = next->addTextToStore(name, s, references, repair); goal.addDependency(path); return path; } - StorePath addToStoreFromDump( - Source & dump, - std::string_view name, - FileIngestionMethod method, - HashType hashAlgo, - RepairFlag repair, - const StorePathSet & references) override + StorePath addToStoreFromDump(Source & dump, std::string_view name, + FileIngestionMethod method, HashType hashAlgo, + RepairFlag repair, + const StorePathSet & references) override { - auto path = next->addToStoreFromDump(dump, name, method, hashAlgo, repair, references); + auto path = next->addToStoreFromDump(dump, name, method, hashAlgo, + repair, references); goal.addDependency(path); return path; } @@ -1235,23 +1277,29 @@ struct RestrictedStore : public virtual RestrictedStoreConfig, public virtual Lo void narFromPath(const StorePath & path, Sink & sink) override { if (!goal.isAllowed(path)) - throw InvalidPath("cannot dump unknown path '%s' in recursive Nix", printStorePath(path)); + throw InvalidPath("cannot dump unknown path '%s' in recursive Nix", + printStorePath(path)); LocalFSStore::narFromPath(path, sink); } void ensurePath(const StorePath & path) override { if (!goal.isAllowed(path)) - throw InvalidPath("cannot substitute unknown path '%s' in recursive Nix", printStorePath(path)); + throw InvalidPath( + "cannot substitute unknown path '%s' in recursive Nix", + printStorePath(path)); /* Nothing to be done; 'path' must already be valid. */ } void registerDrvOutput(const Realisation & info) override // XXX: This should probably be allowed as a no-op if the realisation // corresponds to an allowed derivation - { throw Error("registerDrvOutput"); } + { + throw Error("registerDrvOutput"); + } - void queryRealisationUncached(const DrvOutput & id, + void queryRealisationUncached( + const DrvOutput & id, Callback> callback) noexcept override // XXX: This should probably be allowed if the realisation corresponds to // an allowed derivation @@ -1261,28 +1309,32 @@ struct RestrictedStore : public virtual RestrictedStoreConfig, public virtual Lo next->queryRealisation(id, std::move(callback)); } - void buildPaths(const std::vector & paths, BuildMode buildMode, std::shared_ptr evalStore) override + void buildPaths(const std::vector & paths, BuildMode buildMode, + std::shared_ptr evalStore) override { for (auto & result : buildPathsWithResults(paths, buildMode, evalStore)) if (!result.success()) result.rethrow(); } - std::vector buildPathsWithResults( - const std::vector & paths, - BuildMode buildMode = bmNormal, - std::shared_ptr evalStore = nullptr) override + std::vector + buildPathsWithResults(const std::vector & paths, + BuildMode buildMode = bmNormal, + std::shared_ptr evalStore = nullptr) override { assert(!evalStore); - if (buildMode != bmNormal) throw Error("unsupported build mode"); + if (buildMode != bmNormal) + throw Error("unsupported build mode"); StorePathSet newPaths; std::set newRealisations; for (auto & req : paths) { if (!goal.isAllowed(req)) - throw InvalidPath("cannot build '%s' in recursive Nix because path is unknown", req.to_string(*next)); + throw InvalidPath("cannot build '%s' in recursive Nix because " + "path is unknown", + req.to_string(*next)); } auto results = next->buildPathsWithResults(paths, buildMode); @@ -1304,28 +1356,33 @@ struct RestrictedStore : public virtual RestrictedStoreConfig, public virtual Lo return results; } - BuildResult buildDerivation(const StorePath & drvPath, const BasicDerivation & drv, - BuildMode buildMode = bmNormal) override - { unsupported("buildDerivation"); } + BuildResult buildDerivation(const StorePath & drvPath, + const BasicDerivation & drv, + BuildMode buildMode = bmNormal) override + { + unsupported("buildDerivation"); + } - void addTempRoot(const StorePath & path) override - { } + void addTempRoot(const StorePath & path) override {} - void addIndirectRoot(const Path & path) override - { } + void addIndirectRoot(const Path & path) override {} - Roots findRoots(bool censor) override - { return Roots(); } + Roots findRoots(bool censor) override { return Roots(); } void collectGarbage(const GCOptions & options, GCResults & results) override - { } + { + } - void addSignatures(const StorePath & storePath, const StringSet & sigs) override - { unsupported("addSignatures"); } + void addSignatures(const StorePath & storePath, + const StringSet & sigs) override + { + unsupported("addSignatures"); + } void queryMissing(const std::vector & targets, - StorePathSet & willBuild, StorePathSet & willSubstitute, StorePathSet & unknown, - uint64_t & downloadSize, uint64_t & narSize) override + StorePathSet & willBuild, StorePathSet & willSubstitute, + StorePathSet & unknown, 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 @@ -1339,18 +1396,23 @@ struct RestrictedStore : public virtual RestrictedStoreConfig, public virtual Lo unknown.insert(pathPartOfReq(req)); } - next->queryMissing(allowed, willBuild, willSubstitute, - unknown, downloadSize, narSize); + next->queryMissing(allowed, willBuild, willSubstitute, unknown, + downloadSize, narSize); } - virtual std::optional getBuildLog(const StorePath & path) override - { return std::nullopt; } + virtual std::optional + getBuildLog(const StorePath & path) override + { + return std::nullopt; + } - virtual void addBuildLog(const StorePath & path, std::string_view log) override - { unsupported("addBuildLog"); } + virtual void addBuildLog(const StorePath & path, + std::string_view log) override + { + unsupported("addBuildLog"); + } }; - void LocalDerivationGoal::startDaemon() { settings.requireExperimentalFeature(Xp::RecursiveNix); @@ -1361,8 +1423,10 @@ void LocalDerivationGoal::startDaemon() params["root"] = getLocalStore().rootDir; params["state"] = "/no-such-path"; params["log"] = "/no-such-path"; - auto store = make_ref(params, - ref(std::dynamic_pointer_cast(worker.store.shared_from_this())), + auto store = make_ref( + params, + ref(std::dynamic_pointer_cast( + worker.store.shared_from_this())), *this); addedPaths.clear(); @@ -1376,18 +1440,20 @@ void LocalDerivationGoal::startDaemon() chownToBuilder(socketPath); daemonThread = std::thread([this, store]() { - while (true) { /* Accept a connection. */ struct sockaddr_un remoteAddr; socklen_t remoteAddrLen = sizeof(remoteAddr); - AutoCloseFD remote = accept(daemonSocket.get(), - (struct sockaddr *) &remoteAddr, &remoteAddrLen); + AutoCloseFD remote = + accept(daemonSocket.get(), (struct sockaddr *) &remoteAddr, + &remoteAddrLen); if (!remote) { - if (errno == EINTR || errno == EAGAIN) continue; - if (errno == EINVAL) break; + if (errno == EINTR || errno == EAGAIN) + continue; + if (errno == EINVAL) + break; throw SysError("accepting connection"); } @@ -1395,18 +1461,21 @@ void LocalDerivationGoal::startDaemon() debug("received daemon connection"); - auto workerThread = std::thread([store, remote{std::move(remote)}]() { - FdSource from(remote.get()); - FdSink to(remote.get()); - try { - daemon::processConnection(store, from, to, - daemon::NotTrusted, daemon::Recursive, - [&](Store & store) { store.createUser("nobody", 65535); }); - debug("terminated daemon connection"); - } catch (SysError &) { - ignoreException(); - } - }); + auto workerThread = + std::thread([store, remote{std::move(remote)}]() { + FdSource from(remote.get()); + FdSink to(remote.get()); + try { + daemon::processConnection( + store, from, to, daemon::NotTrusted, + daemon::Recursive, [&](Store & store) { + store.createUser("nobody", 65535); + }); + debug("terminated daemon connection"); + } catch (SysError &) { + ignoreException(); + } + }); daemonWorkerThreads.push_back(std::move(workerThread)); } @@ -1415,7 +1484,6 @@ void LocalDerivationGoal::startDaemon() }); } - void LocalDerivationGoal::stopDaemon() { if (daemonSocket && shutdown(daemonSocket.get(), SHUT_RDWR) == -1) @@ -1433,10 +1501,10 @@ void LocalDerivationGoal::stopDaemon() daemonSocket = -1; } - void LocalDerivationGoal::addDependency(const StorePath & path) { - if (isAllowed(path)) return; + if (isAllowed(path)) + return; addedPaths.insert(path); @@ -1444,77 +1512,80 @@ void LocalDerivationGoal::addDependency(const StorePath & path) appear in the sandbox. */ if (useChroot) { - debug("materialising '%s' in the sandbox", worker.store.printStorePath(path)); - - #if __linux__ - - Path source = worker.store.Store::toRealPath(path); - Path target = chrootRootDir + worker.store.printStorePath(path); - debug("bind-mounting %s -> %s", target, source); + debug("materialising '%s' in the sandbox", + worker.store.printStorePath(path)); - if (pathExists(target)) - throw Error("store path '%s' already exists in the sandbox", worker.store.printStorePath(path)); +#if __linux__ - auto st = lstat(source); + Path source = worker.store.Store::toRealPath(path); + Path target = chrootRootDir + worker.store.printStorePath(path); + debug("bind-mounting %s -> %s", target, source); - if (S_ISDIR(st.st_mode)) { + if (pathExists(target)) + throw Error("store path '%s' already exists in the sandbox", + worker.store.printStorePath(path)); - /* Bind-mount the path into the sandbox. This requires - entering its mount namespace, which is not possible - in multithreaded programs. So we do this in a - child process.*/ - Pid child(startProcess([&]() { + auto st = lstat(source); - if (usingUserNamespace && (setns(sandboxUserNamespace.get(), 0) == -1)) - throw SysError("entering sandbox user namespace"); + if (S_ISDIR(st.st_mode)) { - if (setns(sandboxMountNamespace.get(), 0) == -1) - throw SysError("entering sandbox mount namespace"); + /* Bind-mount the path into the sandbox. This requires + entering its mount namespace, which is not possible + in multithreaded programs. So we do this in a + child process.*/ + Pid child(startProcess([&]() { + if (usingUserNamespace && + (setns(sandboxUserNamespace.get(), 0) == -1)) + throw SysError("entering sandbox user namespace"); - createDirs(target); + if (setns(sandboxMountNamespace.get(), 0) == -1) + throw SysError("entering sandbox mount namespace"); - if (mount(source.c_str(), target.c_str(), "", MS_BIND, 0) == -1) - throw SysError("bind mount from '%s' to '%s' failed", source, target); + createDirs(target); - _exit(0); - })); + if (mount(source.c_str(), target.c_str(), "", MS_BIND, 0) == -1) + throw SysError("bind mount from '%s' to '%s' failed", + source, target); - int status = child.wait(); - if (status != 0) - throw Error("could not add path '%s' to sandbox", worker.store.printStorePath(path)); + _exit(0); + })); - } else - linkOrCopy(source, target); + int status = child.wait(); + if (status != 0) + throw Error("could not add path '%s' to sandbox", + worker.store.printStorePath(path)); - #else - throw Error("don't know how to make path '%s' (produced by a recursive Nix call) appear in the sandbox", - worker.store.printStorePath(path)); - #endif + } else + linkOrCopy(source, target); +#else + throw Error("don't know how to make path '%s' (produced by a recursive " + "Nix call) appear in the sandbox", + worker.store.printStorePath(path)); +#endif } } void LocalDerivationGoal::chownToBuilder(const Path & path) { - if (!buildUser) return; + if (!buildUser) + return; if (chown(path.c_str(), buildUser->getUID(), buildUser->getGID()) == -1) throw SysError("cannot change ownership of '%1%'", path); } - void setupSeccomp() { #if __linux__ - if (!settings.filterSyscalls) return; + if (!settings.filterSyscalls) + return; #if HAVE_SECCOMP scmp_filter_ctx ctx; if (!(ctx = seccomp_init(SCMP_ACT_ALLOW))) throw SysError("unable to initialize seccomp mode 2"); - Finally cleanup([&]() { - seccomp_release(ctx); - }); + Finally cleanup([&]() { seccomp_release(ctx); }); if (nativeSystem == "x86_64-linux" && seccomp_arch_add(ctx, SCMP_ARCH_X86) != 0) @@ -1526,45 +1597,53 @@ void setupSeccomp() if (nativeSystem == "aarch64-linux" && seccomp_arch_add(ctx, SCMP_ARCH_ARM) != 0) - printError("unable to add ARM seccomp architecture; this may result in spurious build failures if running 32-bit ARM processes"); + printError("unable to add ARM seccomp architecture; this may result in " + "spurious build failures if running 32-bit ARM processes"); /* Prevent builders from creating setuid/setgid binaries. */ - for (int perm : { S_ISUID, S_ISGID }) { + for (int perm : {S_ISUID, S_ISGID}) { if (seccomp_rule_add(ctx, SCMP_ACT_ERRNO(EPERM), SCMP_SYS(chmod), 1, - SCMP_A1(SCMP_CMP_MASKED_EQ, (scmp_datum_t) perm, (scmp_datum_t) perm)) != 0) + SCMP_A1(SCMP_CMP_MASKED_EQ, (scmp_datum_t) perm, + (scmp_datum_t) perm)) != 0) throw SysError("unable to add seccomp rule"); if (seccomp_rule_add(ctx, SCMP_ACT_ERRNO(EPERM), SCMP_SYS(fchmod), 1, - SCMP_A1(SCMP_CMP_MASKED_EQ, (scmp_datum_t) perm, (scmp_datum_t) perm)) != 0) + SCMP_A1(SCMP_CMP_MASKED_EQ, (scmp_datum_t) perm, + (scmp_datum_t) perm)) != 0) throw SysError("unable to add seccomp rule"); if (seccomp_rule_add(ctx, SCMP_ACT_ERRNO(EPERM), SCMP_SYS(fchmodat), 1, - SCMP_A2(SCMP_CMP_MASKED_EQ, (scmp_datum_t) perm, (scmp_datum_t) perm)) != 0) + SCMP_A2(SCMP_CMP_MASKED_EQ, (scmp_datum_t) perm, + (scmp_datum_t) perm)) != 0) throw SysError("unable to add seccomp rule"); } /* Prevent builders from creating EAs or ACLs. Not all filesystems support these, and they're not allowed in the Nix store because they're not representable in the NAR serialisation. */ - if (seccomp_rule_add(ctx, SCMP_ACT_ERRNO(ENOTSUP), SCMP_SYS(setxattr), 0) != 0 || - seccomp_rule_add(ctx, SCMP_ACT_ERRNO(ENOTSUP), SCMP_SYS(lsetxattr), 0) != 0 || - seccomp_rule_add(ctx, SCMP_ACT_ERRNO(ENOTSUP), SCMP_SYS(fsetxattr), 0) != 0) + if (seccomp_rule_add(ctx, SCMP_ACT_ERRNO(ENOTSUP), SCMP_SYS(setxattr), 0) != + 0 || + seccomp_rule_add(ctx, SCMP_ACT_ERRNO(ENOTSUP), SCMP_SYS(lsetxattr), + 0) != 0 || + seccomp_rule_add(ctx, SCMP_ACT_ERRNO(ENOTSUP), SCMP_SYS(fsetxattr), + 0) != 0) throw SysError("unable to add seccomp rule"); - if (seccomp_attr_set(ctx, SCMP_FLTATR_CTL_NNP, settings.allowNewPrivileges ? 0 : 1) != 0) + if (seccomp_attr_set(ctx, SCMP_FLTATR_CTL_NNP, + settings.allowNewPrivileges ? 0 : 1) != 0) throw SysError("unable to set 'no new privileges' seccomp attribute"); if (seccomp_load(ctx) != 0) throw SysError("unable to load seccomp BPF program"); #else - throw Error( - "seccomp is not supported on this platform; " - "you can bypass this error by setting the option 'filter-syscalls' to false, but note that untrusted builds can then create setuid binaries!"); + throw Error("seccomp is not supported on this platform; " + "you can bypass this error by setting the option " + "'filter-syscalls' to false, but note that untrusted builds " + "can then create setuid binaries!"); #endif #endif } - void LocalDerivationGoal::runChild() { /* Warning: in the child we should absolutely not make any SQLite @@ -1577,7 +1656,8 @@ void LocalDerivationGoal::runChild() try { setupSeccomp(); } catch (...) { - if (buildUser) throw; + if (buildUser) + throw; } bool setUser = true; @@ -1588,7 +1668,8 @@ void LocalDerivationGoal::runChild() try { if (drv->isBuiltin() && drv->builder == "builtin:fetchurl") netrcData = readFile(settings.netrcFile); - } catch (SysError &) { } + } catch (SysError &) { + } #if __linux__ if (useChroot) { @@ -1604,7 +1685,8 @@ void LocalDerivationGoal::runChild() /* Initialise the loopback interface. */ AutoCloseFD fd(socket(PF_INET, SOCK_DGRAM, IPPROTO_IP)); - if (!fd) throw SysError("cannot open IP socket"); + if (!fd) + throw SysError("cannot open IP socket"); struct ifreq ifr; strcpy(ifr.ifr_name, "lo"); @@ -1634,7 +1716,8 @@ void LocalDerivationGoal::runChild() /* Bind-mount chroot directory to itself, to treat it as a different filesystem from /, as needed for pivot_root. */ - if (mount(chrootRootDir.c_str(), chrootRootDir.c_str(), 0, MS_BIND, 0) == -1) + if (mount(chrootRootDir.c_str(), chrootRootDir.c_str(), 0, MS_BIND, + 0) == -1) throw SysError("unable to bind mount '%1%'", chrootRootDir); /* Bind-mount the sandbox's Nix store onto itself so that @@ -1647,8 +1730,10 @@ void LocalDerivationGoal::runChild() to fail with EINVAL. Don't know why. */ Path chrootStoreDir = chrootRootDir + worker.store.storeDir; - if (mount(chrootStoreDir.c_str(), chrootStoreDir.c_str(), 0, MS_BIND, 0) == -1) - throw SysError("unable to bind mount the Nix store", chrootStoreDir); + if (mount(chrootStoreDir.c_str(), chrootStoreDir.c_str(), 0, + MS_BIND, 0) == -1) + throw SysError("unable to bind mount the Nix store", + chrootStoreDir); if (mount(0, chrootStoreDir.c_str(), 0, MS_SHARED, 0) == -1) throw SysError("unable to make '%s' shared", chrootStoreDir); @@ -1660,7 +1745,8 @@ void LocalDerivationGoal::runChild() createDirs(chrootRootDir + "/dev/shm"); createDirs(chrootRootDir + "/dev/pts"); ss.push_back("/dev/full"); - if (worker.store.systemFeatures.get().count("kvm") && pathExists("/dev/kvm")) + if (worker.store.systemFeatures.get().count("kvm") && + pathExists("/dev/kvm")) ss.push_back("/dev/kvm"); ss.push_back("/dev/null"); ss.push_back("/dev/random"); @@ -1681,29 +1767,34 @@ void LocalDerivationGoal::runChild() // services. Don’t use it for anything else that may // be configured for this system. This limits the // potential impurities introduced in fixed-outputs. - writeFile(chrootRootDir + "/etc/nsswitch.conf", "hosts: files dns\nservices: files\n"); + writeFile(chrootRootDir + "/etc/nsswitch.conf", + "hosts: files dns\nservices: files\n"); /* N.B. it is realistic that these paths might not exist. It happens when testing Nix building fixed-output derivations within a pure derivation. */ - for (auto & path : { "/etc/resolv.conf", "/etc/services", "/etc/hosts" }) + for (auto & path : + {"/etc/resolv.conf", "/etc/services", "/etc/hosts"}) if (pathExists(path)) ss.push_back(path); } - for (auto & i : ss) dirsInChroot.emplace(i, i); + for (auto & i : ss) + dirsInChroot.emplace(i, i); /* Bind-mount all the directories from the "host" filesystem that we want in the chroot environment. */ - auto doBind = [&](const Path & source, const Path & target, bool optional = false) { + auto doBind = [&](const Path & source, const Path & target, + bool optional = false) { debug("bind mounting '%1%' to '%2%'", source, target); struct stat st; if (stat(source.c_str(), &st) == -1) { if (optional && errno == ENOENT) return; else - throw SysError("getting attributes of path '%1%'", source); + throw SysError("getting attributes of path '%1%'", + source); } if (S_ISDIR(st.st_mode)) createDirs(target); @@ -1711,36 +1802,43 @@ void LocalDerivationGoal::runChild() createDirs(dirOf(target)); writeFile(target, ""); } - if (mount(source.c_str(), target.c_str(), "", MS_BIND | MS_REC, 0) == -1) - throw SysError("bind mount from '%1%' to '%2%' failed", source, target); + if (mount(source.c_str(), target.c_str(), "", MS_BIND | MS_REC, + 0) == -1) + throw SysError("bind mount from '%1%' to '%2%' failed", + source, target); }; for (auto & i : dirsInChroot) { - if (i.second.source == "/proc") continue; // backwards compatibility + if (i.second.source == "/proc") + continue; // backwards compatibility - #if HAVE_EMBEDDED_SANDBOX_SHELL +#if HAVE_EMBEDDED_SANDBOX_SHELL if (i.second.source == "__embedded_sandbox_shell__") { static unsigned char sh[] = { - #include "embedded-sandbox-shell.gen.hh" +#include "embedded-sandbox-shell.gen.hh" }; auto dst = chrootRootDir + i.first; createDirs(dirOf(dst)); - writeFile(dst, std::string_view((const char *) sh, sizeof(sh))); + writeFile(dst, + std::string_view((const char *) sh, sizeof(sh))); chmod_(dst, 0555); } else - #endif - doBind(i.second.source, chrootRootDir + i.first, i.second.optional); +#endif + doBind(i.second.source, chrootRootDir + i.first, + i.second.optional); } /* Bind a new instance of procfs on /proc. */ createDirs(chrootRootDir + "/proc"); - if (mount("none", (chrootRootDir + "/proc").c_str(), "proc", 0, 0) == -1) + if (mount("none", (chrootRootDir + "/proc").c_str(), "proc", 0, + 0) == -1) throw SysError("mounting /proc"); /* Mount a new tmpfs on /dev/shm to ensure that whatever the builder puts in /dev/shm is cleaned up automatically. */ - if (pathExists("/dev/shm") && mount("none", (chrootRootDir + "/dev/shm").c_str(), "tmpfs", 0, - fmt("size=%s", settings.sandboxShmSize).c_str()) == -1) + if (pathExists("/dev/shm") && + mount("none", (chrootRootDir + "/dev/shm").c_str(), "tmpfs", 0, + fmt("size=%s", settings.sandboxShmSize).c_str()) == -1) throw SysError("mounting /dev/shm"); /* Mount a new devpts on /dev/pts. Note that this @@ -1748,11 +1846,10 @@ void LocalDerivationGoal::runChild() CONFIG_DEVPTS_MULTIPLE_INSTANCES=y (which is the case if /dev/ptx/ptmx exists). */ if (pathExists("/dev/pts/ptmx") && - !pathExists(chrootRootDir + "/dev/ptmx") - && !dirsInChroot.count("/dev/pts")) - { - if (mount("none", (chrootRootDir + "/dev/pts").c_str(), "devpts", 0, "newinstance,mode=0620") == 0) - { + !pathExists(chrootRootDir + "/dev/ptmx") && + !dirsInChroot.count("/dev/pts")) { + if (mount("none", (chrootRootDir + "/dev/pts").c_str(), + "devpts", 0, "newinstance,mode=0620") == 0) { createSymlink("/dev/pts/ptmx", chrootRootDir + "/dev/ptmx"); /* Make sure /dev/pts/ptmx is world-writable. With some @@ -1781,16 +1878,19 @@ void LocalDerivationGoal::runChild() /* Do the chroot(). */ if (chdir(chrootRootDir.c_str()) == -1) - throw SysError("cannot change directory to '%1%'", chrootRootDir); + throw SysError("cannot change directory to '%1%'", + chrootRootDir); if (mkdir("real-root", 0) == -1) throw SysError("cannot create real-root directory"); if (pivot_root(".", "real-root") == -1) - throw SysError("cannot pivot old root directory onto '%1%'", (chrootRootDir + "/real-root")); + throw SysError("cannot pivot old root directory onto '%1%'", + (chrootRootDir + "/real-root")); if (chroot(".") == -1) - throw SysError("cannot change root directory to '%1%'", chrootRootDir); + throw SysError("cannot change root directory to '%1%'", + chrootRootDir); if (umount2("real-root", MNT_DETACH) == -1) throw SysError("cannot unmount real root filesystem"); @@ -1821,31 +1921,35 @@ void LocalDerivationGoal::runChild() i686-linux build on an x86_64-linux machine. */ struct utsname utsbuf; uname(&utsbuf); - if ((drv->platform == "i686-linux" - && (settings.thisSystem == "x86_64-linux" - || (!strcmp(utsbuf.sysname, "Linux") && !strcmp(utsbuf.machine, "x86_64")))) - || drv->platform == "armv7l-linux" - || drv->platform == "armv6l-linux") - { + if ((drv->platform == "i686-linux" && + (settings.thisSystem == "x86_64-linux" || + (!strcmp(utsbuf.sysname, "Linux") && + !strcmp(utsbuf.machine, "x86_64")))) || + drv->platform == "armv7l-linux" || + drv->platform == "armv6l-linux") { if (personality(PER_LINUX32) == -1) throw SysError("cannot set 32-bit personality"); } /* Impersonate a Linux 2.6 machine to get some determinism in builds that depend on the kernel version. */ - if ((drv->platform == "i686-linux" || drv->platform == "x86_64-linux") && settings.impersonateLinux26) { + if ((drv->platform == "i686-linux" || + drv->platform == "x86_64-linux") && + settings.impersonateLinux26) { int cur = personality(0xffffffff); - if (cur != -1) personality(cur | 0x0020000 /* == UNAME26 */); + if (cur != -1) + personality(cur | 0x0020000 /* == UNAME26 */); } /* Disable address space randomization for improved determinism. */ int cur = personality(0xffffffff); - if (cur != -1) personality(cur | ADDR_NO_RANDOMIZE); + if (cur != -1) + personality(cur | ADDR_NO_RANDOMIZE); #endif /* Disable core dumps by default. */ - struct rlimit limit = { 0, RLIM_INFINITY }; + struct rlimit limit = {0, RLIM_INFINITY}; setrlimit(RLIMIT_CORE, &limit); // FIXME: set other limits to deterministic values? @@ -1853,7 +1957,8 @@ void LocalDerivationGoal::runChild() /* Fill in the environment. */ Strings envStrs; for (auto & i : env) - envStrs.push_back(rewriteStrings(i.first + "=" + i.second, inputRewrites)); + envStrs.push_back( + rewriteStrings(i.first + "=" + i.second, inputRewrites)); /* If we are running in `build-users' mode, then switch to the user we allocated above. Make sure that we drop all root @@ -1895,12 +2000,15 @@ void LocalDerivationGoal::runChild() if (useChroot) { - /* Lots and lots and lots of file functions freak out if they can't stat their full ancestry */ + /* Lots and lots and lots of file functions freak out if they + * can't stat their full ancestry */ PathSet ancestry; - /* We build the ancestry before adding all inputPaths to the store because we know they'll - all have the same parents (the store), and there might be lots of inputs. This isn't - particularly efficient... I doubt it'll be a bottleneck in practice */ + /* We build the ancestry before adding all inputPaths to the + store because we know they'll all have the same parents (the + store), and there might be lots of inputs. This isn't + particularly efficient... I doubt it'll be a bottleneck in + practice */ for (auto & i : dirsInChroot) { Path cur = i.first; while (cur.compare("/") != 0) { @@ -1909,8 +2017,10 @@ void LocalDerivationGoal::runChild() } } - /* And we want the store in there regardless of how empty dirsInChroot. We include the innermost - path component this time, since it's typically /nix/store and we care about that. */ + /* And we want the store in there regardless of how empty + dirsInChroot. We include the innermost path component this + time, since it's typically /nix/store and we care about that. + */ Path cur = worker.store.storeDir; while (cur.compare("/") != 0) { ancestry.insert(cur); @@ -1923,7 +2033,9 @@ void LocalDerivationGoal::runChild() dirsInChroot[p] = p; } - /* Violations will go to the syslog if you set this. Unfortunately the destination does not appear to be configurable */ + /* Violations will go to the syslog if you set this. + * Unfortunately the destination does not appear to be + * configurable */ if (settings.darwinLogSandboxViolations) { sandboxProfile += "(deny default)\n"; } else { @@ -1936,22 +2048,27 @@ void LocalDerivationGoal::runChild() sandboxProfile += "(import \"sandbox-network.sb\")\n"; /* Add the output paths we'll use at build-time to the chroot */ - sandboxProfile += "(allow file-read* file-write* process-exec\n"; + sandboxProfile += + "(allow file-read* file-write* process-exec\n"; for (auto & [_, path] : scratchOutputs) - sandboxProfile += fmt("\t(subpath \"%s\")\n", worker.store.printStorePath(path)); + sandboxProfile += fmt("\t(subpath \"%s\")\n", + worker.store.printStorePath(path)); sandboxProfile += ")\n"; - /* Our inputs (transitive dependencies and any impurities computed above) + /* Our inputs (transitive dependencies and any impurities + computed above) - without file-write* allowed, access() incorrectly returns EPERM + without file-write* allowed, access() incorrectly returns + EPERM */ - sandboxProfile += "(allow file-read* file-write* process-exec\n"; + sandboxProfile += + "(allow file-read* file-write* process-exec\n"; for (auto & i : dirsInChroot) { if (i.first != i.second.source) - throw Error( - "can't map '%1%' to '%2%': mismatched impure paths not supported on Darwin", - i.first, i.second.source); + throw Error("can't map '%1%' to '%2%': mismatched " + "impure paths not supported on Darwin", + i.first, i.second.source); std::string path = i.first; struct stat st; @@ -1967,7 +2084,8 @@ void LocalDerivationGoal::runChild() } sandboxProfile += ")\n"; - /* Allow file-read* on full directory hierarchy to self. Allows realpath() */ + /* Allow file-read* on full directory hierarchy to self. Allows + * realpath() */ sandboxProfile += "(allow file-read*\n"; for (auto & i : ancestry) { sandboxProfile += fmt("\t(literal \"%s\")\n", i); @@ -1985,14 +2103,19 @@ void LocalDerivationGoal::runChild() writeFile(sandboxFile, sandboxProfile); - bool allowLocalNetworking = parsedDrv->getBoolAttr("__darwinAllowLocalNetworking"); + bool allowLocalNetworking = + parsedDrv->getBoolAttr("__darwinAllowLocalNetworking"); - /* The tmpDir in scope points at the temporary build directory for our derivation. Some packages try different mechanisms - to find temporary directories, so we want to open up a broader place for them to dump their files, if needed. */ - Path globalTmpDir = canonPath(getEnv("TMPDIR").value_or("/tmp"), true); + /* The tmpDir in scope points at the temporary build directory for + our derivation. Some packages try different mechanisms to find + temporary directories, so we want to open up a broader place for + them to dump their files, if needed. */ + Path globalTmpDir = + canonPath(getEnv("TMPDIR").value_or("/tmp"), true); /* They don't like trailing slashes on subpath directives */ - if (globalTmpDir.back() == '/') globalTmpDir.pop_back(); + if (globalTmpDir.back() == '/') + globalTmpDir.pop_back(); if (getEnv("_NIX_TEST_NO_SANDBOX") != "1") { builder = "/usr/bin/sandbox-exec"; @@ -2002,7 +2125,8 @@ void LocalDerivationGoal::runChild() args.push_back("-D"); args.push_back("_GLOBAL_TMP_DIR=" + globalTmpDir); args.push_back("-D"); - args.push_back("IMPORT_DIR=" + settings.nixDataDir + "/nix/sandbox/"); + args.push_back("IMPORT_DIR=" + settings.nixDataDir + + "/nix/sandbox/"); if (allowLocalNetworking) { args.push_back("-D"); args.push_back(std::string("_ALLOW_LOCAL_NETWORKING=1")); @@ -2042,7 +2166,8 @@ void LocalDerivationGoal::runChild() else if (drv->builder == "builtin:unpack-channel") builtinUnpackChannel(drv2); else - throw Error("unsupported builtin builder '%1%'", drv->builder.substr(8)); + throw Error("unsupported builtin builder '%1%'", + drv->builder.substr(8)); _exit(0); } catch (std::exception & e) { writeFull(STDERR_FILENO, e.what() + std::string("\n")); @@ -2062,7 +2187,8 @@ void LocalDerivationGoal::runChild() if (drv->platform == "aarch64-darwin") { // Unset kern.curproc_arch_affinity so we can escape Rosetta int affinity = 0; - sysctlbyname("kern.curproc_arch_affinity", NULL, NULL, &affinity, sizeof(affinity)); + sysctlbyname("kern.curproc_arch_affinity", NULL, NULL, &affinity, + sizeof(affinity)); cpu_type_t cpu = CPU_TYPE_ARM64; posix_spawnattr_setbinpref_np(&attrp, 1, &cpu, NULL); @@ -2071,9 +2197,12 @@ void LocalDerivationGoal::runChild() posix_spawnattr_setbinpref_np(&attrp, 1, &cpu, NULL); } - posix_spawn(NULL, builder.c_str(), NULL, &attrp, stringsToCharPtrs(args).data(), stringsToCharPtrs(envStrs).data()); + posix_spawn(NULL, builder.c_str(), NULL, &attrp, + stringsToCharPtrs(args).data(), + stringsToCharPtrs(envStrs).data()); #else - execve(builder.c_str(), stringsToCharPtrs(args).data(), stringsToCharPtrs(envStrs).data()); + execve(builder.c_str(), stringsToCharPtrs(args).data(), + stringsToCharPtrs(envStrs).data()); #endif throw SysError("executing '%1%'", drv->builder); @@ -2087,7 +2216,6 @@ void LocalDerivationGoal::runChild() } } - DrvOutputs LocalDerivationGoal::registerOutputs() { /* When using a build hook, the build hook can register the output @@ -2116,16 +2244,18 @@ DrvOutputs LocalDerivationGoal::registerOutputs() output paths, and any paths that have been built via recursive Nix calls. */ StorePathSet referenceablePaths; - for (auto & p : inputPaths) referenceablePaths.insert(p); - for (auto & i : scratchOutputs) referenceablePaths.insert(i.second); - for (auto & p : addedPaths) referenceablePaths.insert(p); + for (auto & p : inputPaths) + referenceablePaths.insert(p); + for (auto & i : scratchOutputs) + referenceablePaths.insert(i.second); + for (auto & p : addedPaths) + referenceablePaths.insert(p); /* FIXME `needsHashRewrite` should probably be removed and we get to the real reason why we aren't using the chroot dir */ auto toRealPathChroot = [&](const Path & p) -> Path { - return useChroot && !needsHashRewrite() - ? chrootRootDir + p - : worker.store.toRealPath(p); + return useChroot && !needsHashRewrite() ? chrootRootDir + p + : worker.store.toRealPath(p); }; /* Check whether the output paths were created, and make all @@ -2134,44 +2264,51 @@ DrvOutputs LocalDerivationGoal::registerOutputs() that are most definitely already installed, we just store their final name so we can also use it in rewrites. */ StringSet outputsToSort; - struct AlreadyRegistered { StorePath path; }; - struct PerhapsNeedToRegister { StorePathSet refs; }; - std::map> outputReferencesIfUnregistered; + struct AlreadyRegistered { + StorePath path; + }; + struct PerhapsNeedToRegister { + StorePathSet refs; + }; + std::map> + outputReferencesIfUnregistered; std::map outputStats; for (auto & [outputName, _] : drv->outputs) { auto scratchOutput = get(scratchOutputs, outputName); if (!scratchOutput) - throw BuildError( - "builder for '%s' has no scratch output for '%s'", - worker.store.printStorePath(drvPath), outputName); - auto actualPath = toRealPathChroot(worker.store.printStorePath(*scratchOutput)); + throw BuildError("builder for '%s' has no scratch output for '%s'", + worker.store.printStorePath(drvPath), outputName); + auto actualPath = + toRealPathChroot(worker.store.printStorePath(*scratchOutput)); outputsToSort.insert(outputName); - /* Updated wanted info to remove the outputs we definitely don't need to register */ + /* Updated wanted info to remove the outputs we definitely don't need to + * register */ auto initialOutput = get(initialOutputs, outputName); if (!initialOutput) - throw BuildError( - "builder for '%s' has no initial output for '%s'", - worker.store.printStorePath(drvPath), outputName); + throw BuildError("builder for '%s' has no initial output for '%s'", + worker.store.printStorePath(drvPath), outputName); auto & initialInfo = *initialOutput; /* Don't register if already valid, and not checking */ - initialInfo.wanted = buildMode == bmCheck - || !(initialInfo.known && initialInfo.known->isValid()); + initialInfo.wanted = + buildMode == bmCheck || + !(initialInfo.known && initialInfo.known->isValid()); if (!initialInfo.wanted) { outputReferencesIfUnregistered.insert_or_assign( - outputName, - AlreadyRegistered { .path = initialInfo.known->path }); + outputName, AlreadyRegistered{.path = initialInfo.known->path}); continue; } struct stat st; if (lstat(actualPath.c_str(), &st) == -1) { if (errno == ENOENT) - throw BuildError( - "builder for '%s' failed to produce output path for output '%s' at '%s'", - worker.store.printStorePath(drvPath), outputName, actualPath); + throw BuildError("builder for '%s' failed to produce output " + "path for output '%s' at '%s'", + worker.store.printStorePath(drvPath), + outputName, actualPath); throw SysError("getting attributes of path '%s'", actualPath); } @@ -2182,56 +2319,63 @@ DrvOutputs LocalDerivationGoal::registerOutputs() user. */ if ((!S_ISLNK(st.st_mode) && (st.st_mode & (S_IWGRP | S_IWOTH))) || (buildUser && st.st_uid != buildUser->getUID())) - throw BuildError( - "suspicious ownership or permission on '%s' for output '%s'; rejecting this build output", - actualPath, outputName); + throw BuildError("suspicious ownership or permission on '%s' for " + "output '%s'; rejecting this build output", + actualPath, outputName); #endif /* Canonicalise first. This ensures that the path we're rewriting doesn't contain a hard link to /etc/shadow or something like that. */ - canonicalisePathMetaData(actualPath, buildUser ? buildUser->getUID() : -1, inodesSeen); + canonicalisePathMetaData( + actualPath, buildUser ? buildUser->getUID() : -1, inodesSeen); - debug("scanning for references for output '%s' in temp location '%s'", outputName, actualPath); + debug("scanning for references for output '%s' in temp location '%s'", + outputName, actualPath); /* Pass blank Sink as we are not ready to hash data at this stage. */ NullSink blank; - auto references = scanForReferences(blank, actualPath, referenceablePaths); + auto references = + scanForReferences(blank, actualPath, referenceablePaths); outputReferencesIfUnregistered.insert_or_assign( - outputName, - PerhapsNeedToRegister { .refs = references }); + outputName, PerhapsNeedToRegister{.refs = references}); outputStats.insert_or_assign(outputName, std::move(st)); } - auto sortedOutputNames = topoSort(outputsToSort, - {[&](const std::string & name) { + auto sortedOutputNames = topoSort( + outputsToSort, {[&](const std::string & name) { auto orifu = get(outputReferencesIfUnregistered, name); if (!orifu) throw BuildError( - "no output reference for '%s' in build of '%s'", - name, worker.store.printStorePath(drvPath)); - return std::visit(overloaded { - /* Since we'll use the already installed versions of these, we - can treat them as leaves and ignore any references they - have. */ - [&](const AlreadyRegistered &) { return StringSet {}; }, - [&](const PerhapsNeedToRegister & refs) { - StringSet referencedOutputs; - /* FIXME build inverted map up front so no quadratic waste here */ - for (auto & r : refs.refs) - for (auto & [o, p] : scratchOutputs) - if (r == p) - referencedOutputs.insert(o); - return referencedOutputs; + "no output reference for '%s' in build of '%s'", name, + worker.store.printStorePath(drvPath)); + return std::visit( + overloaded{ + /* Since we'll use the already installed versions of these, + we can treat them as leaves and ignore any references + they have. */ + [&](const AlreadyRegistered &) { return StringSet{}; }, + [&](const PerhapsNeedToRegister & refs) { + StringSet referencedOutputs; + /* FIXME build inverted map up front so no quadratic + * waste here */ + for (auto & r : refs.refs) + for (auto & [o, p] : scratchOutputs) + if (r == p) + referencedOutputs.insert(o); + return referencedOutputs; + }, }, - }, *orifu); + *orifu); }}, {[&](const std::string & path, const std::string & parent) { - // TODO with more -vvvv also show the temporary paths for manual inspection. - return BuildError( - "cycle detected in build of '%s' in the references of output '%s' from output '%s'", - worker.store.printStorePath(drvPath), path, parent); + // TODO with more -vvvv also show the temporary paths for manual + // inspection. + return BuildError("cycle detected in build of '%s' in the " + "references of output '%s' from output '%s'", + worker.store.printStorePath(drvPath), path, + parent); }}); std::reverse(sortedOutputNames.begin(), sortedOutputNames.end()); @@ -2242,7 +2386,8 @@ DrvOutputs LocalDerivationGoal::registerOutputs() auto output = get(drv->outputs, outputName); auto scratchPath = get(scratchOutputs, outputName); assert(output && scratchPath); - auto actualPath = toRealPathChroot(worker.store.printStorePath(*scratchPath)); + auto actualPath = + toRealPathChroot(worker.store.printStorePath(*scratchPath)); auto finish = [&](StorePath finalStorePath) { /* Store the final path */ @@ -2251,21 +2396,24 @@ DrvOutputs LocalDerivationGoal::registerOutputs() use. This is why the topological sort is essential to do first before this for loop. */ if (*scratchPath != finalStorePath) - outputRewrites[std::string { scratchPath->hashPart() }] = std::string { finalStorePath.hashPart() }; + outputRewrites[std::string{scratchPath->hashPart()}] = + std::string{finalStorePath.hashPart()}; }; auto orifu = get(outputReferencesIfUnregistered, outputName); assert(orifu); - std::optional referencesOpt = std::visit(overloaded { - [&](const AlreadyRegistered & skippedFinalPath) -> std::optional { - finish(skippedFinalPath.path); - return std::nullopt; - }, - [&](const PerhapsNeedToRegister & r) -> std::optional { - return r.refs; + std::optional referencesOpt = std::visit( + overloaded{ + [&](const AlreadyRegistered & skippedFinalPath) + -> std::optional { + finish(skippedFinalPath.path); + return std::nullopt; + }, + [&](const PerhapsNeedToRegister & r) + -> std::optional { return r.refs; }, }, - }, *orifu); + *orifu); if (!referencesOpt) continue; @@ -2291,20 +2439,20 @@ DrvOutputs LocalDerivationGoal::registerOutputs() final path, therefore we look for a *non-rewritten self-reference, and use a bool rather try to solve the computationally intractable fixed point. */ - std::pair res { + std::pair res{ false, {}, }; for (auto & r : references) { auto name = r.name(); - auto origHash = std::string { r.hashPart() }; + auto origHash = std::string{r.hashPart()}; if (r == *scratchPath) { res.first = true; } else if (auto outputRewrite = get(outputRewrites, origHash)) { std::string newRef = *outputRewrite; newRef += '-'; newRef += name; - res.second.insert(StorePath { newRef }); + res.second.insert(StorePath{newRef}); } else { res.second.insert(r); } @@ -2312,24 +2460,26 @@ DrvOutputs LocalDerivationGoal::registerOutputs() return res; }; - auto newInfoFromCA = [&](const DerivationOutput::CAFloating outputHash) -> ValidPathInfo { + auto newInfoFromCA = [&](const DerivationOutput::CAFloating outputHash) + -> ValidPathInfo { auto st = get(outputStats, outputName); if (!st) - throw BuildError( - "output path %1% without valid stats info", - actualPath); + throw BuildError("output path %1% without valid stats info", + actualPath); if (outputHash.method == FileIngestionMethod::Flat) { - /* The output path should be a regular file without execute permission. */ + /* 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( - "output path '%1%' should be a non-executable regular file " - "since recursive hashing is not enabled (outputHashMode=flat)", - actualPath); + throw BuildError("output path '%1%' should be a " + "non-executable regular file " + "since recursive hashing is not enabled " + "(outputHashMode=flat)", + actualPath); } rewriteOutput(); /* FIXME optimize and deduplicate with addToStore */ - std::string oldHashPart { scratchPath->hashPart() }; - HashModuloSink caSink { outputHash.hashType, oldHashPart }; + std::string oldHashPart{scratchPath->hashPart()}; + HashModuloSink caSink{outputHash.hashType, oldHashPart}; switch (outputHash.method) { case FileIngestionMethod::Recursive: dumpPath(actualPath, caSink); @@ -2342,17 +2492,16 @@ DrvOutputs LocalDerivationGoal::registerOutputs() auto refs = rewriteRefs(); auto finalPath = worker.store.makeFixedOutputPath( - outputHash.method, - got, - outputPathName(drv->name, outputName), - refs.second, - refs.first); + outputHash.method, got, outputPathName(drv->name, outputName), + refs.second, refs.first); if (*scratchPath != finalPath) { // Also rewrite the output path auto source = sinkToSource([&](Sink & nextSink) { StringSink sink; dumpPath(actualPath, sink); - RewritingSink rsink2(oldHashPart, std::string(finalPath.hashPart()), nextSink); + RewritingSink rsink2(oldHashPart, + std::string(finalPath.hashPart()), + nextSink); rsink2(sink.s); rsink2.flush(); }); @@ -2363,13 +2512,13 @@ DrvOutputs LocalDerivationGoal::registerOutputs() } HashResult narHashAndSize = hashPath(htSHA256, actualPath); - ValidPathInfo newInfo0 { + ValidPathInfo newInfo0{ finalPath, narHashAndSize.first, }; newInfo0.narSize = narHashAndSize.second; - newInfo0.ca = FixedOutputHash { + newInfo0.ca = FixedOutputHash{ .method = outputHash.method, .hash = got, }; @@ -2381,69 +2530,74 @@ DrvOutputs LocalDerivationGoal::registerOutputs() return newInfo0; }; - ValidPathInfo newInfo = std::visit(overloaded { - - [&](const DerivationOutput::InputAddressed & output) { - /* input-addressed case */ - auto requiredFinalPath = output.path; - /* Preemptively add rewrite rule for final hash, as that is - what the NAR hash will use rather than normalized-self references */ - if (*scratchPath != requiredFinalPath) - outputRewrites.insert_or_assign( - std::string { scratchPath->hashPart() }, - std::string { requiredFinalPath.hashPart() }); - rewriteOutput(); - auto narHashAndSize = hashPath(htSHA256, actualPath); - ValidPathInfo newInfo0 { requiredFinalPath, narHashAndSize.first }; - newInfo0.narSize = narHashAndSize.second; - auto refs = rewriteRefs(); - newInfo0.references = refs.second; - if (refs.first) - newInfo0.references.insert(newInfo0.path); - return newInfo0; - }, - - [&](const DerivationOutput::CAFixed & dof) { - auto newInfo0 = newInfoFromCA(DerivationOutput::CAFloating { - .method = dof.hash.method, - .hashType = dof.hash.hash.type, - }); + ValidPathInfo newInfo = std::visit( + overloaded{ + + [&](const DerivationOutput::InputAddressed & output) { + /* input-addressed case */ + auto requiredFinalPath = output.path; + /* Preemptively add rewrite rule for final hash, as that is + what the NAR hash will use rather than normalized-self + references */ + if (*scratchPath != requiredFinalPath) + outputRewrites.insert_or_assign( + std::string{scratchPath->hashPart()}, + std::string{requiredFinalPath.hashPart()}); + rewriteOutput(); + auto narHashAndSize = hashPath(htSHA256, actualPath); + ValidPathInfo newInfo0{requiredFinalPath, + narHashAndSize.first}; + newInfo0.narSize = narHashAndSize.second; + auto refs = rewriteRefs(); + newInfo0.references = refs.second; + if (refs.first) + newInfo0.references.insert(newInfo0.path); + return newInfo0; + }, - /* Check wanted hash */ - const Hash & wanted = dof.hash.hash; - assert(newInfo0.ca); - auto got = getContentAddressHash(*newInfo0.ca); - if (wanted != got) { - /* Throw an error after registering the path as - valid. */ - worker.hashMismatch = true; - delayedException = std::make_exception_ptr( - BuildError("hash mismatch in fixed-output derivation '%s':\n specified: %s\n got: %s", + [&](const DerivationOutput::CAFixed & dof) { + auto newInfo0 = newInfoFromCA(DerivationOutput::CAFloating{ + .method = dof.hash.method, + .hashType = dof.hash.hash.type, + }); + + /* Check wanted hash */ + const Hash & wanted = dof.hash.hash; + assert(newInfo0.ca); + auto got = getContentAddressHash(*newInfo0.ca); + if (wanted != got) { + /* Throw an error after registering the path as + valid. */ + worker.hashMismatch = true; + delayedException = std::make_exception_ptr(BuildError( + "hash mismatch in fixed-output derivation '%s':\n " + "specified: %s\n got: %s", worker.store.printStorePath(drvPath), wanted.to_string(SRI, true), got.to_string(SRI, true))); - } - return newInfo0; - }, + } + return newInfo0; + }, - [&](const DerivationOutput::CAFloating & dof) { - return newInfoFromCA(dof); - }, + [&](const DerivationOutput::CAFloating & dof) { + return newInfoFromCA(dof); + }, - [&](const DerivationOutput::Deferred &) -> ValidPathInfo { - // No derivation should reach that point without having been - // rewritten first - assert(false); - }, + [&](const DerivationOutput::Deferred &) -> ValidPathInfo { + // No derivation should reach that point without having been + // rewritten first + assert(false); + }, - [&](const DerivationOutput::Impure & doi) { - return newInfoFromCA(DerivationOutput::CAFloating { - .method = doi.method, - .hashType = doi.hashType, - }); - }, + [&](const DerivationOutput::Impure & doi) { + return newInfoFromCA(DerivationOutput::CAFloating{ + .method = doi.method, + .hashType = doi.hashType, + }); + }, - }, output->raw()); + }, + output->raw()); /* FIXME: set proper permissions in restorePath() so we don't have to do another traversal. */ @@ -2461,17 +2615,18 @@ DrvOutputs LocalDerivationGoal::registerOutputs() dynamicOutputLock.setDeletion(true); auto optFixedPath = output->path(worker.store, drv->name, outputName); if (!optFixedPath || - worker.store.printStorePath(*optFixedPath) != finalDestPath) - { + worker.store.printStorePath(*optFixedPath) != finalDestPath) { assert(newInfo.ca); - dynamicOutputLock.lockPaths({worker.store.toRealPath(finalDestPath)}); + dynamicOutputLock.lockPaths( + {worker.store.toRealPath(finalDestPath)}); } /* Move files, if needed */ if (worker.store.toRealPath(finalDestPath) != actualPath) { if (buildMode == bmRepair) { /* Path already exists, need to replace it */ - replaceValidPath(worker.store.toRealPath(finalDestPath), actualPath); + replaceValidPath(worker.store.toRealPath(finalDestPath), + actualPath); actualPath = worker.store.toRealPath(finalDestPath); } else if (buildMode == bmCheck) { /* Path already exists, and we want to compare, so we leave out @@ -2492,25 +2647,34 @@ DrvOutputs LocalDerivationGoal::registerOutputs() if (buildMode == bmCheck) { - if (!worker.store.isValidPath(newInfo.path)) continue; + if (!worker.store.isValidPath(newInfo.path)) + continue; ValidPathInfo oldInfo(*worker.store.queryPathInfo(newInfo.path)); if (newInfo.narHash != oldInfo.narHash) { worker.checkMismatch = true; if (settings.runDiffHook || settings.keepFailed) { - auto dst = worker.store.toRealPath(finalDestPath + checkSuffix); + auto dst = + worker.store.toRealPath(finalDestPath + checkSuffix); deletePath(dst); movePath(actualPath, dst); - handleDiffHook( - buildUser ? buildUser->getUID() : getuid(), - buildUser ? buildUser->getGID() : getgid(), - finalDestPath, dst, worker.store.printStorePath(drvPath), tmpDir); - - throw NotDeterministic("derivation '%s' may not be deterministic: output '%s' differs from '%s'", - worker.store.printStorePath(drvPath), worker.store.toRealPath(finalDestPath), dst); + handleDiffHook(buildUser ? buildUser->getUID() : getuid(), + buildUser ? buildUser->getGID() : getgid(), + finalDestPath, dst, + worker.store.printStorePath(drvPath), + tmpDir); + + throw NotDeterministic( + "derivation '%s' may not be deterministic: output '%s' " + "differs from '%s'", + worker.store.printStorePath(drvPath), + worker.store.toRealPath(finalDestPath), dst); } else - throw NotDeterministic("derivation '%s' may not be deterministic: output '%s' differs", - worker.store.printStorePath(drvPath), worker.store.toRealPath(finalDestPath)); + throw NotDeterministic( + "derivation '%s' may not be deterministic: output '%s' " + "differs", + worker.store.printStorePath(drvPath), + worker.store.toRealPath(finalDestPath)); } /* Since we verified the build, it's now ultimately trusted. */ @@ -2526,13 +2690,17 @@ DrvOutputs LocalDerivationGoal::registerOutputs() /* For debugging, print out the referenced and unreferenced paths. */ for (auto & i : inputPaths) { if (references.count(i)) - debug("referenced input: '%1%'", worker.store.printStorePath(i)); + debug("referenced input: '%1%'", + worker.store.printStorePath(i)); else - debug("unreferenced input: '%1%'", worker.store.printStorePath(i)); + debug("unreferenced input: '%1%'", + worker.store.printStorePath(i)); } if (curRound == nrRounds) { - localStore.optimisePath(actualPath, NoRepair); // FIXME: combine with scanForReferences() + localStore.optimisePath( + actualPath, + NoRepair); // FIXME: combine with scanForReferences() worker.markContentsGood(newInfo.path); } @@ -2567,22 +2735,28 @@ DrvOutputs LocalDerivationGoal::registerOutputs() path is different, if any.*/ if (curRound > 1 && prevInfos != infos) { assert(prevInfos.size() == infos.size()); - for (auto i = prevInfos.begin(), j = infos.begin(); i != prevInfos.end(); ++i, ++j) + for (auto i = prevInfos.begin(), j = infos.begin(); + i != prevInfos.end(); ++i, ++j) if (!(*i == *j)) { buildResult.isNonDeterministic = true; - Path prev = worker.store.printStorePath(i->second.path) + checkSuffix; + Path prev = + worker.store.printStorePath(i->second.path) + checkSuffix; bool prevExists = keepPreviousRound && pathExists(prev); - hintformat hint = prevExists - ? hintfmt("output '%s' of '%s' differs from '%s' from previous round", - worker.store.printStorePath(i->second.path), worker.store.printStorePath(drvPath), prev) - : hintfmt("output '%s' of '%s' differs from previous round", - worker.store.printStorePath(i->second.path), worker.store.printStorePath(drvPath)); - - handleDiffHook( - buildUser ? buildUser->getUID() : getuid(), - buildUser ? buildUser->getGID() : getgid(), - prev, worker.store.printStorePath(i->second.path), - worker.store.printStorePath(drvPath), tmpDir); + hintformat hint = + prevExists + ? hintfmt("output '%s' of '%s' differs from '%s' from " + "previous round", + worker.store.printStorePath(i->second.path), + worker.store.printStorePath(drvPath), prev) + : hintfmt( + "output '%s' of '%s' differs from previous round", + worker.store.printStorePath(i->second.path), + worker.store.printStorePath(drvPath)); + + handleDiffHook(buildUser ? buildUser->getUID() : getuid(), + buildUser ? buildUser->getGID() : getgid(), prev, + worker.store.printStorePath(i->second.path), + worker.store.printStorePath(drvPath), tmpDir); if (settings.enforceDeterminism) throw NotDeterministic(hint); @@ -2593,8 +2767,10 @@ DrvOutputs LocalDerivationGoal::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) { + /* 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 & [_, outputStorePath] : finalOutputs) { auto path = worker.store.printStorePath(outputStorePath); Path prev = path + checkSuffix; @@ -2614,7 +2790,8 @@ DrvOutputs LocalDerivationGoal::registerOutputs() if the result was not determistic? */ if (curRound == nrRounds) { for (auto & [_, outputStorePath] : finalOutputs) { - Path prev = worker.store.printStorePath(outputStorePath) + checkSuffix; + Path prev = + worker.store.printStorePath(outputStorePath) + checkSuffix; deletePath(prev); } } @@ -2647,16 +2824,11 @@ DrvOutputs LocalDerivationGoal::registerOutputs() for (auto & [outputName, newInfo] : infos) { auto oldinfo = get(initialOutputs, outputName); assert(oldinfo); - auto thisRealisation = Realisation { - .id = DrvOutput { - oldinfo->outputHash, - outputName - }, - .outPath = newInfo.path - }; - if (settings.isExperimentalFeatureEnabled(Xp::CaDerivations) - && drv->type().isPure()) - { + auto thisRealisation = + Realisation{.id = DrvOutput{oldinfo->outputHash, outputName}, + .outPath = newInfo.path}; + if (settings.isExperimentalFeatureEnabled(Xp::CaDerivations) && + drv->type().isPure()) { signRealisation(thisRealisation); worker.store.registerDrvOutput(thisRealisation); } @@ -2672,29 +2844,29 @@ void LocalDerivationGoal::signRealisation(Realisation & realisation) getLocalStore().signRealisation(realisation); } - -void LocalDerivationGoal::checkOutputs(const std::map & outputs) +void LocalDerivationGoal::checkOutputs( + const std::map & outputs) { std::map outputsByPath; for (auto & output : outputs) - outputsByPath.emplace(worker.store.printStorePath(output.second.path), output.second); + outputsByPath.emplace(worker.store.printStorePath(output.second.path), + output.second); for (auto & output : outputs) { auto & outputName = output.first; auto & info = output.second; - struct Checks - { + struct Checks { bool ignoreSelfRefs = false; std::optional maxSize, maxClosureSize; - std::optional allowedReferences, allowedRequisites, disallowedReferences, disallowedRequisites; + std::optional allowedReferences, allowedRequisites, + disallowedReferences, disallowedRequisites; }; /* Compute the closure and closure size of some output. This is slightly tricky because some of its references (namely other outputs) may not be valid yet. */ - auto getClosure = [&](const StorePath & path) - { + auto getClosure = [&](const StorePath & path) { uint64_t closureSize = 0; StorePathSet pathsDone; std::queue pathsLeft; @@ -2703,7 +2875,8 @@ void LocalDerivationGoal::checkOutputs(const std::map *checks.maxSize) - throw BuildError("path '%s' is too large at %d bytes; limit is %d bytes", - worker.store.printStorePath(info.path), info.narSize, *checks.maxSize); + throw BuildError( + "path '%s' is too large at %d bytes; limit is %d bytes", + worker.store.printStorePath(info.path), info.narSize, + *checks.maxSize); if (checks.maxClosureSize) { uint64_t closureSize = getClosure(info.path).second; if (closureSize > *checks.maxClosureSize) - throw BuildError("closure of path '%s' is too large at %d bytes; limit is %d bytes", - worker.store.printStorePath(info.path), closureSize, *checks.maxClosureSize); + throw BuildError("closure of path '%s' is too large at %d " + "bytes; limit is %d bytes", + worker.store.printStorePath(info.path), + closureSize, *checks.maxClosureSize); } - auto checkRefs = [&](const std::optional & value, bool allowed, bool recursive) - { - if (!value) return; + auto checkRefs = [&](const std::optional & value, + bool allowed, bool recursive) { + if (!value) + return; /* Parse a list of reference specifiers. Each element must either be a store path, or the symbolic name of the output @@ -2748,12 +2925,13 @@ void LocalDerivationGoal::checkOutputs(const std::mappath); else - throw BuildError("derivation contains an illegal reference specifier '%s'", i); + throw BuildError("derivation contains an illegal " + "reference specifier '%s'", + i); } - auto used = recursive - ? getClosure(info.path).first - : info.references; + auto used = + recursive ? getClosure(info.path).first : info.references; if (recursive && checks.ignoreSelfRefs) used.erase(info.path); @@ -2775,8 +2953,10 @@ void LocalDerivationGoal::checkOutputs(const std::mapget(); - auto get_ = [&](const std::string & name) -> std::optional { + auto get_ = [&](const std::string & name) + -> std::optional { if (auto i = get(*output, name)) { Strings res; for (auto j = i->begin(); j != i->end(); ++j) { if (!j->is_string()) - throw Error("attribute '%s' of derivation '%s' must be a list of strings", name, worker.store.printStorePath(drvPath)); + throw Error( + "attribute '%s' of derivation '%s' " + "must be a list of strings", + name, + worker.store.printStorePath(drvPath)); res.push_back(j->get()); } checks.disallowedRequisites = res; @@ -2823,16 +3008,19 @@ void LocalDerivationGoal::checkOutputs(const std::mapgetStringsAttr("allowedReferences"); - checks.allowedRequisites = parsedDrv->getStringsAttr("allowedRequisites"); - checks.disallowedReferences = parsedDrv->getStringsAttr("disallowedReferences"); - checks.disallowedRequisites = parsedDrv->getStringsAttr("disallowedRequisites"); + checks.allowedReferences = + parsedDrv->getStringsAttr("allowedReferences"); + checks.allowedRequisites = + parsedDrv->getStringsAttr("allowedRequisites"); + checks.disallowedReferences = + parsedDrv->getStringsAttr("disallowedReferences"); + checks.disallowedRequisites = + parsedDrv->getStringsAttr("disallowedRequisites"); applyChecks(checks); } } } - void LocalDerivationGoal::deleteTmpDir(bool force) { if (tmpDir != "") { @@ -2841,35 +3029,32 @@ void LocalDerivationGoal::deleteTmpDir(bool force) if (settings.keepFailed && !force && !drv->isBuiltin()) { printError("note: keeping build directory '%s'", tmpDir); chmod(tmpDir.c_str(), 0755); - } - else + } else deletePath(tmpDir); tmpDir = ""; } } - bool LocalDerivationGoal::isReadDesc(int fd) { return (hook && DerivationGoal::isReadDesc(fd)) || - (!hook && fd == builderOut.readSide.get()); + (!hook && fd == builderOut.readSide.get()); } - StorePath LocalDerivationGoal::makeFallbackPath(std::string_view outputName) { return worker.store.makeStorePath( - "rewrite:" + std::string(drvPath.to_string()) + ":name:" + std::string(outputName), + "rewrite:" + std::string(drvPath.to_string()) + + ":name:" + std::string(outputName), Hash(htSHA256), outputPathName(drv->name, outputName)); } - StorePath LocalDerivationGoal::makeFallbackPath(const StorePath & path) { return worker.store.makeStorePath( - "rewrite:" + std::string(drvPath.to_string()) + ":" + std::string(path.to_string()), + "rewrite:" + std::string(drvPath.to_string()) + ":" + + std::string(path.to_string()), Hash(htSHA256), path.name()); } - } diff --git a/src/libstore/build/local-derivation-goal.hh b/src/libstore/build/local-derivation-goal.hh index d456e9cae979..c79e2b800506 100644 --- a/src/libstore/build/local-derivation-goal.hh +++ b/src/libstore/build/local-derivation-goal.hh @@ -5,8 +5,7 @@ namespace nix { -struct LocalDerivationGoal : public DerivationGoal -{ +struct LocalDerivationGoal : public DerivationGoal { LocalStore & getLocalStore(); /* User selected for running the builder. */ @@ -27,8 +26,8 @@ struct LocalDerivationGoal : public DerivationGoal /* Pipe for synchronising updates to the builder namespaces. */ Pipe userNamespaceSync; - /* The mount namespace and user namespace of the builder, used to add additional - paths to the sandbox as a result of recursive Nix calls. */ + /* The mount namespace and user namespace of the builder, used to add + additional paths to the sandbox as a result of recursive Nix calls. */ AutoCloseFD sandboxMountNamespace; AutoCloseFD sandboxUserNamespace; @@ -53,9 +52,11 @@ struct LocalDerivationGoal : public DerivationGoal bool optional; ChrootPath(Path source = "", bool optional = false) : source(source), optional(optional) - { } + { + } }; - typedef map DirsInChroot; // maps target path to source path + typedef map + DirsInChroot; // maps target path to source path DirsInChroot dirsInChroot; typedef map Environment; @@ -92,8 +93,14 @@ struct LocalDerivationGoal : public DerivationGoal result. */ std::map prevInfos; - uid_t sandboxUid() { return usingUserNamespace ? 1000 : buildUser->getUID(); } - gid_t sandboxGid() { return usingUserNamespace ? 100 : buildUser->getGID(); } + uid_t sandboxUid() + { + return usingUserNamespace ? 1000 : buildUser->getUID(); + } + gid_t sandboxGid() + { + return usingUserNamespace ? 100 : buildUser->getGID(); + } const static Path homeDir; @@ -120,10 +127,7 @@ struct LocalDerivationGoal : public DerivationGoal { return inputPaths.count(path) || addedPaths.count(path); } - bool isAllowed(const DrvOutput & id) - { - return addedDrvOutputs.count(id); - } + bool isAllowed(const DrvOutput & id) { return addedDrvOutputs.count(id); } bool isAllowed(const DerivedPath & req); @@ -133,7 +137,8 @@ struct LocalDerivationGoal : public DerivationGoal virtual ~LocalDerivationGoal() override; - /* Whether we need to perform hash rewriting if there are valid output paths. */ + /* Whether we need to perform hash rewriting if there are valid output + * paths. */ bool needsHashRewrite(); /* The additional states. */ diff --git a/src/libstore/build/substitution-goal.cc b/src/libstore/build/substitution-goal.cc index 2af105b4d31a..4ccaa73765bf 100644 --- a/src/libstore/build/substitution-goal.cc +++ b/src/libstore/build/substitution-goal.cc @@ -5,29 +5,24 @@ namespace nix { -PathSubstitutionGoal::PathSubstitutionGoal(const StorePath & storePath, Worker & worker, RepairFlag repair, std::optional ca) - : Goal(worker, DerivedPath::Opaque { storePath }) - , storePath(storePath) - , repair(repair) - , ca(ca) +PathSubstitutionGoal::PathSubstitutionGoal(const StorePath & storePath, + Worker & worker, RepairFlag repair, + std::optional ca) + : Goal(worker, DerivedPath::Opaque{storePath}), storePath(storePath), + repair(repair), ca(ca) { state = &PathSubstitutionGoal::init; - name = fmt("substitution of '%s'", worker.store.printStorePath(this->storePath)); + name = fmt("substitution of '%s'", + worker.store.printStorePath(this->storePath)); trace("created"); - maintainExpectedSubstitutions = std::make_unique>(worker.expectedSubstitutions); + maintainExpectedSubstitutions = + std::make_unique>(worker.expectedSubstitutions); } +PathSubstitutionGoal::~PathSubstitutionGoal() { cleanup(); } -PathSubstitutionGoal::~PathSubstitutionGoal() -{ - cleanup(); -} - - -void PathSubstitutionGoal::done( - ExitCode result, - BuildResult::Status status, - std::optional errorMsg) +void PathSubstitutionGoal::done(ExitCode result, BuildResult::Status status, + std::optional errorMsg) { buildResult.status = status; if (errorMsg) { @@ -37,12 +32,7 @@ void PathSubstitutionGoal::done( amDone(result); } - -void PathSubstitutionGoal::work() -{ - (this->*state)(); -} - +void PathSubstitutionGoal::work() { (this->*state)(); } void PathSubstitutionGoal::init() { @@ -57,14 +47,16 @@ void PathSubstitutionGoal::init() } if (settings.readOnlyMode) - throw Error("cannot substitute path '%s' - no write access to the Nix store", worker.store.printStorePath(storePath)); + throw Error( + "cannot substitute path '%s' - no write access to the Nix store", + worker.store.printStorePath(storePath)); - subs = settings.useSubstitutes ? getDefaultSubstituters() : std::list>(); + subs = settings.useSubstitutes ? getDefaultSubstituters() + : std::list>(); tryNext(); } - void PathSubstitutionGoal::tryNext() { trace("trying next substituter"); @@ -78,10 +70,11 @@ void PathSubstitutionGoal::tryNext() /* Hack: don't indicate failure if there were no substituters. In that case the calling derivation should just do a build. */ - done( - substituterFailed ? ecFailed : ecNoSubstituters, - BuildResult::NoSubstituters, - fmt("path '%s' is required, but there is no substituter that can build it", worker.store.printStorePath(storePath))); + done(substituterFailed ? ecFailed : ecNoSubstituters, + BuildResult::NoSubstituters, + fmt("path '%s' is required, but there is no substituter that can " + "build it", + worker.store.printStorePath(storePath))); if (substituterFailed) { worker.failedSubstitutions++; @@ -130,8 +123,9 @@ void PathSubstitutionGoal::tryNext() info2->path = storePath; info = info2; } else { - printError("asked '%s' for '%s' but got '%s'", - sub->getUri(), worker.store.printStorePath(storePath), sub->printStorePath(info->path)); + printError("asked '%s' for '%s' but got '%s'", sub->getUri(), + worker.store.printStorePath(storePath), + sub->printStorePath(info->path)); tryNext(); return; } @@ -140,22 +134,24 @@ void PathSubstitutionGoal::tryNext() /* Update the total expected download size. */ auto narInfo = std::dynamic_pointer_cast(info); - maintainExpectedNar = std::make_unique>(worker.expectedNarSize, info->narSize); + maintainExpectedNar = std::make_unique>( + worker.expectedNarSize, info->narSize); maintainExpectedDownload = narInfo && narInfo->fileSize - ? std::make_unique>(worker.expectedDownloadSize, narInfo->fileSize) - : nullptr; + ? std::make_unique>( + worker.expectedDownloadSize, narInfo->fileSize) + : nullptr; worker.updateProgress(); /* Bail out early if this substituter lacks a valid signature. LocalStore::addToStore() also checks for this, but only after we've downloaded the path. */ - if (!sub->isTrusted && worker.store.pathInfoIsUntrusted(*info)) - { - warn("ignoring substitute for '%s' from '%s', as it's not signed by any of the keys in 'trusted-public-keys'", - worker.store.printStorePath(storePath), sub->getUri()); + if (!sub->isTrusted && worker.store.pathInfoIsUntrusted(*info)) { + warn("ignoring substitute for '%s' from '%s', as it's not signed by " + "any of the keys in 'trusted-public-keys'", + worker.store.printStorePath(storePath), sub->getUri()); tryNext(); return; } @@ -172,16 +168,17 @@ void PathSubstitutionGoal::tryNext() state = &PathSubstitutionGoal::referencesValid; } - void PathSubstitutionGoal::referencesValid() { trace("all references realised"); if (nrFailed > 0) { - done( - nrNoSubstituters > 0 || nrIncompleteClosure > 0 ? ecIncompleteClosure : ecFailed, - BuildResult::DependencyFailed, - fmt("some references of path '%s' could not be realised", worker.store.printStorePath(storePath))); + done(nrNoSubstituters > 0 || nrIncompleteClosure > 0 + ? ecIncompleteClosure + : ecFailed, + BuildResult::DependencyFailed, + fmt("some references of path '%s' could not be realised", + worker.store.printStorePath(storePath))); return; } @@ -193,7 +190,6 @@ void PathSubstitutionGoal::referencesValid() worker.wakeUp(shared_from_this()); } - void PathSubstitutionGoal::tryToRun() { trace("trying to run"); @@ -202,12 +198,14 @@ void PathSubstitutionGoal::tryToRun() if maxBuildJobs == 0 (no local builds allowed), we still allow a substituter to run. This is because substitutions cannot be distributed to another machine via the build hook. */ - if (worker.getNrLocalBuilds() >= std::max(1U, (unsigned int) settings.maxBuildJobs)) { + if (worker.getNrLocalBuilds() >= + std::max(1U, (unsigned int) settings.maxBuildJobs)) { worker.waitForBuildSlot(shared_from_this()); return; } - maintainRunningSubstitutions = std::make_unique>(worker.runningSubstitutions); + maintainRunningSubstitutions = + std::make_unique>(worker.runningSubstitutions); worker.updateProgress(); outPipe.create(); @@ -219,11 +217,13 @@ void PathSubstitutionGoal::tryToRun() /* Wake up the worker loop when we're done. */ Finally updateStats([this]() { outPipe.writeSide.close(); }); - Activity act(*logger, actSubstitute, Logger::Fields{worker.store.printStorePath(storePath), sub->getUri()}); + Activity act(*logger, actSubstitute, + Logger::Fields{worker.store.printStorePath(storePath), + sub->getUri()}); PushActivity pact(act.id); - copyStorePath(*sub, worker.store, - subPath ? *subPath : storePath, repair, sub->isTrusted ? NoCheckSigs : CheckSigs); + copyStorePath(*sub, worker.store, subPath ? *subPath : storePath, + repair, sub->isTrusted ? NoCheckSigs : CheckSigs); promise.set_value(); } catch (...) { @@ -231,12 +231,12 @@ void PathSubstitutionGoal::tryToRun() } }); - worker.childStarted(shared_from_this(), {outPipe.readSide.get()}, true, false); + worker.childStarted(shared_from_this(), {outPipe.readSide.get()}, true, + false); state = &PathSubstitutionGoal::finished; } - void PathSubstitutionGoal::finished() { trace("substitute finished"); @@ -268,7 +268,8 @@ void PathSubstitutionGoal::finished() worker.markContentsGood(storePath); - printMsg(lvlChatty, "substitution of path '%s' succeeded", worker.store.printStorePath(storePath)); + printMsg(lvlChatty, "substitution of path '%s' succeeded", + worker.store.printStorePath(storePath)); maintainRunningSubstitutions.reset(); @@ -289,18 +290,14 @@ void PathSubstitutionGoal::finished() done(ecSuccess, BuildResult::Substituted); } - -void PathSubstitutionGoal::handleChildOutput(int fd, std::string_view data) -{ -} - +void PathSubstitutionGoal::handleChildOutput(int fd, std::string_view data) {} void PathSubstitutionGoal::handleEOF(int fd) { - if (fd == outPipe.readSide.get()) worker.wakeUp(shared_from_this()); + if (fd == outPipe.readSide.get()) + worker.wakeUp(shared_from_this()); } - void PathSubstitutionGoal::cleanup() { try { @@ -316,5 +313,4 @@ void PathSubstitutionGoal::cleanup() } } - } diff --git a/src/libstore/build/substitution-goal.hh b/src/libstore/build/substitution-goal.hh index a73f8e6669a8..2c0cceee70d4 100644 --- a/src/libstore/build/substitution-goal.hh +++ b/src/libstore/build/substitution-goal.hh @@ -8,8 +8,7 @@ namespace nix { class Worker; -struct PathSubstitutionGoal : public Goal -{ +struct PathSubstitutionGoal : public Goal { /* The store path that should be realised through a substitute. */ StorePath storePath; @@ -45,7 +44,8 @@ struct PathSubstitutionGoal : public Goal Path destPath; std::unique_ptr> maintainExpectedSubstitutions, - maintainRunningSubstitutions, maintainExpectedNar, maintainExpectedDownload; + maintainRunningSubstitutions, maintainExpectedNar, + maintainExpectedDownload; typedef void (PathSubstitutionGoal::*GoalState)(); GoalState state; @@ -53,13 +53,13 @@ struct PathSubstitutionGoal : public Goal /* Content address for recomputing store path */ std::optional ca; - void done( - ExitCode result, - BuildResult::Status status, - std::optional errorMsg = {}); + void done(ExitCode result, BuildResult::Status status, + std::optional errorMsg = {}); -public: - PathSubstitutionGoal(const StorePath & storePath, Worker & worker, RepairFlag repair = NoRepair, std::optional ca = std::nullopt); + public: + PathSubstitutionGoal(const StorePath & storePath, Worker & worker, + RepairFlag repair = NoRepair, + std::optional ca = std::nullopt); ~PathSubstitutionGoal(); void timedOut(Error && ex) override { abort(); }; @@ -68,7 +68,8 @@ public: { /* "a$" ensures substitution goals happen before derivation goals. */ - return "a$" + std::string(storePath.name()) + "$" + worker.store.printStorePath(storePath); + return "a$" + std::string(storePath.name()) + "$" + + worker.store.printStorePath(storePath); } void work() override; diff --git a/src/libstore/build/worker.cc b/src/libstore/build/worker.cc index b192fbc778dc..f475bd6fd991 100644 --- a/src/libstore/build/worker.cc +++ b/src/libstore/build/worker.cc @@ -10,11 +10,9 @@ namespace nix { Worker::Worker(Store & store, Store & evalStore) - : act(*logger, actRealise) - , actDerivations(*logger, actBuilds) - , actSubstitutions(*logger, actCopyPaths) - , store(store) - , evalStore(evalStore) + : act(*logger, actRealise), actDerivations(*logger, actBuilds), + actSubstitutions(*logger, actCopyPaths), store(store), + evalStore(evalStore) { /* Debugging: prevent recursive workers. */ nrLocalBuilds = 0; @@ -25,7 +23,6 @@ Worker::Worker(Store & store, Store & evalStore) checkMismatch = false; } - Worker::~Worker() { /* Explicitly get rid of all strong pointers now. After this all @@ -39,10 +36,8 @@ Worker::~Worker() assert(expectedNarSize == 0); } - std::shared_ptr Worker::makeDerivationGoalCommon( - const StorePath & drvPath, - const StringSet & wantedOutputs, + const StorePath & drvPath, const StringSet & wantedOutputs, std::function()> mkDrvGoal) { std::weak_ptr & goal_weak = derivationGoals[drvPath]; @@ -57,30 +52,37 @@ std::shared_ptr Worker::makeDerivationGoalCommon( return goal; } - -std::shared_ptr Worker::makeDerivationGoal(const StorePath & drvPath, - const StringSet & wantedOutputs, BuildMode buildMode) +std::shared_ptr +Worker::makeDerivationGoal(const StorePath & drvPath, + const StringSet & wantedOutputs, BuildMode buildMode) { - return makeDerivationGoalCommon(drvPath, wantedOutputs, [&]() -> std::shared_ptr { - return !dynamic_cast(&store) - ? std::make_shared(drvPath, wantedOutputs, *this, buildMode) - : std::make_shared(drvPath, wantedOutputs, *this, buildMode); - }); + return makeDerivationGoalCommon( + drvPath, wantedOutputs, [&]() -> std::shared_ptr { + return !dynamic_cast(&store) + ? std::make_shared( + drvPath, wantedOutputs, *this, buildMode) + : std::make_shared( + drvPath, wantedOutputs, *this, buildMode); + }); } - -std::shared_ptr Worker::makeBasicDerivationGoal(const StorePath & drvPath, - const BasicDerivation & drv, const StringSet & wantedOutputs, BuildMode buildMode) +std::shared_ptr Worker::makeBasicDerivationGoal( + const StorePath & drvPath, const BasicDerivation & drv, + const StringSet & wantedOutputs, BuildMode buildMode) { - return makeDerivationGoalCommon(drvPath, wantedOutputs, [&]() -> std::shared_ptr { - return !dynamic_cast(&store) - ? std::make_shared(drvPath, drv, wantedOutputs, *this, buildMode) - : std::make_shared(drvPath, drv, wantedOutputs, *this, buildMode); - }); + return makeDerivationGoalCommon( + drvPath, wantedOutputs, [&]() -> std::shared_ptr { + return !dynamic_cast(&store) + ? std::make_shared( + drvPath, drv, wantedOutputs, *this, buildMode) + : std::make_shared( + drvPath, drv, wantedOutputs, *this, buildMode); + }); } - -std::shared_ptr Worker::makePathSubstitutionGoal(const StorePath & path, RepairFlag repair, std::optional ca) +std::shared_ptr +Worker::makePathSubstitutionGoal(const StorePath & path, RepairFlag repair, + std::optional ca) { std::weak_ptr & goal_weak = substitutionGoals[path]; auto goal = goal_weak.lock(); // FIXME @@ -92,12 +94,16 @@ std::shared_ptr Worker::makePathSubstitutionGoal(const Sto return goal; } -std::shared_ptr Worker::makeDrvOutputSubstitutionGoal(const DrvOutput& id, RepairFlag repair, std::optional ca) +std::shared_ptr +Worker::makeDrvOutputSubstitutionGoal(const DrvOutput & id, RepairFlag repair, + std::optional ca) { - std::weak_ptr & goal_weak = drvOutputSubstitutionGoals[id]; + std::weak_ptr & goal_weak = + drvOutputSubstitutionGoals[id]; auto goal = goal_weak.lock(); // FIXME if (!goal) { - goal = std::make_shared(id, *this, repair, ca); + goal = + std::make_shared(id, *this, repair, ca); goal_weak = goal; wakeUp(goal); } @@ -105,27 +111,29 @@ std::shared_ptr Worker::makeDrvOutputSubstitutionGoal } template -static void removeGoal(std::shared_ptr goal, std::map> & goalMap) +static void removeGoal(std::shared_ptr goal, + std::map> & goalMap) { /* !!! inefficient */ - for (auto i = goalMap.begin(); - i != goalMap.end(); ) + for (auto i = goalMap.begin(); i != goalMap.end();) if (i->second.lock() == goal) { - auto j = i; ++j; + auto j = i; + ++j; goalMap.erase(i); i = j; - } - else ++i; + } else + ++i; } - void Worker::removeGoal(GoalPtr goal) { if (auto drvGoal = std::dynamic_pointer_cast(goal)) nix::removeGoal(drvGoal, derivationGoals); - else if (auto subGoal = std::dynamic_pointer_cast(goal)) + else if (auto subGoal = + std::dynamic_pointer_cast(goal)) nix::removeGoal(subGoal, substitutionGoals); - else if (auto subGoal = std::dynamic_pointer_cast(goal)) + else if (auto subGoal = + std::dynamic_pointer_cast(goal)) nix::removeGoal(subGoal, drvOutputSubstitutionGoals); else assert(false); @@ -141,28 +149,23 @@ void Worker::removeGoal(GoalPtr goal) /* Wake up goals waiting for any goal to finish. */ for (auto & i : waitingForAnyGoal) { GoalPtr goal = i.lock(); - if (goal) wakeUp(goal); + if (goal) + wakeUp(goal); } waitingForAnyGoal.clear(); } - void Worker::wakeUp(GoalPtr goal) { goal->trace("woken up"); addToWeakGoals(awake, goal); } - -unsigned Worker::getNrLocalBuilds() -{ - return nrLocalBuilds; -} - +unsigned Worker::getNrLocalBuilds() { return nrLocalBuilds; } void Worker::childStarted(GoalPtr goal, const std::set & fds, - bool inBuildSlot, bool respectTimeouts) + bool inBuildSlot, bool respectTimeouts) { Child child; child.goal = goal; @@ -172,15 +175,17 @@ void Worker::childStarted(GoalPtr goal, const std::set & fds, child.inBuildSlot = inBuildSlot; child.respectTimeouts = respectTimeouts; children.emplace_back(child); - if (inBuildSlot) nrLocalBuilds++; + if (inBuildSlot) + nrLocalBuilds++; } - void Worker::childTerminated(Goal * goal, bool wakeSleepers) { - auto i = std::find_if(children.begin(), children.end(), - [&](const Child & child) { return child.goal2 == goal; }); - if (i == children.end()) return; + auto i = + std::find_if(children.begin(), children.end(), + [&](const Child & child) { return child.goal2 == goal; }); + if (i == children.end()) + return; if (i->inBuildSlot) { assert(nrLocalBuilds > 0); @@ -194,14 +199,14 @@ void Worker::childTerminated(Goal * goal, bool wakeSleepers) /* Wake up goals waiting for a build slot. */ for (auto & j : wantingToBuild) { GoalPtr goal = j.lock(); - if (goal) wakeUp(goal); + if (goal) + wakeUp(goal); } wantingToBuild.clear(); } } - void Worker::waitForBuildSlot(GoalPtr goal) { debug("wait for build slot"); @@ -211,21 +216,18 @@ void Worker::waitForBuildSlot(GoalPtr goal) addToWeakGoals(wantingToBuild, goal); } - void Worker::waitForAnyGoal(GoalPtr goal) { debug("wait for any goal"); addToWeakGoals(waitingForAnyGoal, goal); } - void Worker::waitForAWhile(GoalPtr goal) { debug("wait for a while"); addToWeakGoals(waitingForAWhile, goal); } - void Worker::run(const Goals & _topGoals) { std::vector topPaths; @@ -233,7 +235,8 @@ void Worker::run(const Goals & _topGoals) for (auto & i : _topGoals) { topGoals.insert(i); if (auto goal = dynamic_cast(i.get())) { - topPaths.push_back(DerivedPath::Built{goal->drvPath, goal->wantedOutputs}); + topPaths.push_back( + DerivedPath::Built{goal->drvPath, goal->wantedOutputs}); } else if (auto goal = dynamic_cast(i.get())) { topPaths.push_back(DerivedPath::Opaque{goal->storePath}); } @@ -242,7 +245,8 @@ void Worker::run(const Goals & _topGoals) /* Call queryMissing() to efficiently query substitutes. */ StorePathSet willBuild, willSubstitute, unknown; uint64_t downloadSize, narSize; - store.queryMissing(topPaths, willBuild, willSubstitute, unknown, downloadSize, narSize); + store.queryMissing(topPaths, willBuild, willSubstitute, unknown, + downloadSize, narSize); debug("entered goal loop"); @@ -260,33 +264,38 @@ void Worker::run(const Goals & _topGoals) Goals awake2; for (auto & i : awake) { GoalPtr goal = i.lock(); - if (goal) awake2.insert(goal); + if (goal) + awake2.insert(goal); } awake.clear(); for (auto & goal : awake2) { checkInterrupt(); goal->work(); - if (topGoals.empty()) break; // stuff may have been cancelled + if (topGoals.empty()) + break; // stuff may have been cancelled } } - if (topGoals.empty()) break; + if (topGoals.empty()) + break; /* Wait for input. */ if (!children.empty() || !waitingForAWhile.empty()) waitForInput(); else { - if (awake.empty() && 0 == settings.maxBuildJobs) - { + if (awake.empty() && 0 == settings.maxBuildJobs) { if (getMachines().empty()) - throw Error("unable to start any build; either increase '--max-jobs' " - "or enable remote builds." - "\nhttps://nixos.org/manual/nix/stable/advanced-topics/distributed-builds.html"); + throw Error("unable to start any build; either increase " + "'--max-jobs' " + "or enable remote builds." + "\nhttps://nixos.org/manual/nix/stable/" + "advanced-topics/distributed-builds.html"); else - throw Error("unable to start any build; remote machines may not have " - "all required system features." - "\nhttps://nixos.org/manual/nix/stable/advanced-topics/distributed-builds.html"); - + throw Error("unable to start any build; remote machines " + "may not have " + "all required system features." + "\nhttps://nixos.org/manual/nix/stable/" + "advanced-topics/distributed-builds.html"); } assert(!awake.empty()); } @@ -322,14 +331,22 @@ void Worker::waitForInput() // Periodicallty wake up to see if we need to run the garbage collector. nearest = before + std::chrono::seconds(10); for (auto & i : children) { - if (!i.respectTimeouts) continue; + if (!i.respectTimeouts) + continue; if (0 != settings.maxSilentTime) - nearest = std::min(nearest, i.lastOutput + std::chrono::seconds(settings.maxSilentTime)); + nearest = std::min( + nearest, + i.lastOutput + std::chrono::seconds(settings.maxSilentTime)); if (0 != settings.buildTimeout) - nearest = std::min(nearest, i.timeStarted + std::chrono::seconds(settings.buildTimeout)); + nearest = std::min(nearest, + i.timeStarted + + std::chrono::seconds(settings.buildTimeout)); } if (nearest != steady_time_point::max()) { - timeout = std::max(1L, (long) std::chrono::duration_cast(nearest - before).count()); + timeout = std::max( + 1L, (long) std::chrono::duration_cast( + nearest - before) + .count()); useTimeout = true; } @@ -337,11 +354,15 @@ void Worker::waitForInput() up after a few seconds at most. */ if (!waitingForAWhile.empty()) { useTimeout = true; - if (lastWokenUp == steady_time_point::min() || lastWokenUp > before) lastWokenUp = before; - timeout = std::max(1L, - (long) std::chrono::duration_cast( - lastWokenUp + std::chrono::seconds(settings.pollInterval) - before).count()); - } else lastWokenUp = steady_time_point::min(); + if (lastWokenUp == steady_time_point::min() || lastWokenUp > before) + lastWokenUp = before; + timeout = std::max( + 1L, (long) std::chrono::duration_cast( + lastWokenUp + std::chrono::seconds(settings.pollInterval) - + before) + .count()); + } else + lastWokenUp = steady_time_point::min(); if (useTimeout) vomit("sleeping %d seconds", timeout); @@ -353,14 +374,15 @@ void Worker::waitForInput() std::map fdToPollStatus; for (auto & i : children) { for (auto & j : i.fds) { - pollStatus.push_back((struct pollfd) { .fd = j, .events = POLLIN }); + pollStatus.push_back((struct pollfd){.fd = j, .events = POLLIN}); fdToPollStatus[j] = pollStatus.size() - 1; } } if (poll(pollStatus.data(), pollStatus.size(), - useTimeout ? timeout * 1000 : -1) == -1) { - if (errno == EINTR) return; + useTimeout ? timeout * 1000 : -1) == -1) { + if (errno == EINTR) + return; throw SysError("waiting for input"); } @@ -395,8 +417,8 @@ void Worker::waitForInput() if (errno != EINTR) throw SysError("%s: read failed", goal->getName()); } else { - printMsg(lvlVomit, "%1%: read %2% bytes", - goal->getName(), rd); + printMsg(lvlVomit, "%1%: read %2% bytes", goal->getName(), + rd); std::string data((char *) buffer.data(), rd); j->lastOutput = after; goal->handleChildOutput(k, data); @@ -404,38 +426,35 @@ void Worker::waitForInput() } } - if (goal->exitCode == Goal::ecBusy && - 0 != settings.maxSilentTime && + if (goal->exitCode == Goal::ecBusy && 0 != settings.maxSilentTime && j->respectTimeouts && - after - j->lastOutput >= std::chrono::seconds(settings.maxSilentTime)) - { - goal->timedOut(Error( - "%1% timed out after %2% seconds of silence", - goal->getName(), settings.maxSilentTime)); + after - j->lastOutput >= + std::chrono::seconds(settings.maxSilentTime)) { + goal->timedOut(Error("%1% timed out after %2% seconds of silence", + goal->getName(), settings.maxSilentTime)); } - else if (goal->exitCode == Goal::ecBusy && - 0 != settings.buildTimeout && - j->respectTimeouts && - after - j->timeStarted >= std::chrono::seconds(settings.buildTimeout)) - { - goal->timedOut(Error( - "%1% timed out after %2% seconds", - goal->getName(), settings.buildTimeout)); + else if (goal->exitCode == Goal::ecBusy && 0 != settings.buildTimeout && + j->respectTimeouts && + after - j->timeStarted >= + std::chrono::seconds(settings.buildTimeout)) { + goal->timedOut(Error("%1% timed out after %2% seconds", + goal->getName(), settings.buildTimeout)); } } - if (!waitingForAWhile.empty() && lastWokenUp + std::chrono::seconds(settings.pollInterval) <= after) { + if (!waitingForAWhile.empty() && + lastWokenUp + std::chrono::seconds(settings.pollInterval) <= after) { lastWokenUp = after; for (auto & i : waitingForAWhile) { GoalPtr goal = i.lock(); - if (goal) wakeUp(goal); + if (goal) + wakeUp(goal); } waitingForAWhile.clear(); } } - unsigned int Worker::exitStatus() { /* @@ -449,13 +468,13 @@ unsigned int Worker::exitStatus() unsigned int mask = 0; bool buildFailure = permanentFailure || timedOut || hashMismatch; if (buildFailure) - mask |= 0x04; // 100 + mask |= 0x04; // 100 if (timedOut) - mask |= 0x01; // 101 + mask |= 0x01; // 101 if (hashMismatch) - mask |= 0x02; // 102 + mask |= 0x02; // 102 if (checkMismatch) { - mask |= 0x08; // 104 + mask |= 0x08; // 104 } if (mask) @@ -463,38 +482,40 @@ unsigned int Worker::exitStatus() return mask ? mask : 1; } - bool Worker::pathContentsGood(const StorePath & path) { auto i = pathContentsGoodCache.find(path); - if (i != pathContentsGoodCache.end()) return i->second; + if (i != pathContentsGoodCache.end()) + return i->second; printInfo("checking path '%s'...", store.printStorePath(path)); auto info = store.queryPathInfo(path); bool res; 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; } pathContentsGoodCache.insert_or_assign(path, res); if (!res) - printError("path '%s' is corrupted or missing!", store.printStorePath(path)); + printError("path '%s' is corrupted or missing!", + store.printStorePath(path)); return res; } - void Worker::markContentsGood(const StorePath & path) { pathContentsGoodCache.insert_or_assign(path, true); } - -GoalPtr upcast_goal(std::shared_ptr subGoal) { +GoalPtr upcast_goal(std::shared_ptr subGoal) +{ return subGoal; } -GoalPtr upcast_goal(std::shared_ptr subGoal) { +GoalPtr upcast_goal(std::shared_ptr subGoal) +{ return subGoal; } diff --git a/src/libstore/build/worker.hh b/src/libstore/build/worker.hh index a1e036a96b50..289acaac2292 100644 --- a/src/libstore/build/worker.hh +++ b/src/libstore/build/worker.hh @@ -23,19 +23,17 @@ class DrvOutputSubstitutionGoal; even when Goal is a complete type. This is still a static cast. The purpose of exporting it is to define it in - a place where `PathSubstitutionGoal` is concrete, and use it in a place where it - is opaque. */ + a place where `PathSubstitutionGoal` is concrete, and use it in a place where + it is opaque. */ GoalPtr upcast_goal(std::shared_ptr subGoal); GoalPtr upcast_goal(std::shared_ptr subGoal); typedef std::chrono::time_point steady_time_point; - /* A mapping used to remember for each child process to what goal it belongs, and file descriptors for receiving log data and output path creation commands. */ -struct Child -{ +struct Child { WeakGoalPtr goal; Goal * goal2; // ugly hackery std::set fds; @@ -49,10 +47,8 @@ struct Child struct HookInstance; /* The worker class. */ -class Worker -{ -private: - +class Worker { + private: /* Note: the worker should only have strong pointers to the top-level goals. */ @@ -76,7 +72,8 @@ private: same derivation / path. */ std::map> derivationGoals; std::map> substitutionGoals; - std::map> drvOutputSubstitutionGoals; + std::map> + drvOutputSubstitutionGoals; /* Goals waiting for busy paths to be unlocked. */ WeakGoals waitingForAnyGoal; @@ -90,8 +87,7 @@ private: /* Cache for pathContentsGood(). */ std::map pathContentsGoodCache; -public: - + public: const Activity act; const Activity actDerivations; const Activity actSubstitutions; @@ -138,21 +134,28 @@ public: /* Make a goal (with caching). */ /* derivation goal */ -private: + private: std::shared_ptr makeDerivationGoalCommon( const StorePath & drvPath, const StringSet & wantedOutputs, std::function()> mkDrvGoal); -public: - std::shared_ptr makeDerivationGoal( - const StorePath & drvPath, - const StringSet & wantedOutputs, BuildMode buildMode = bmNormal); + + public: + std::shared_ptr + makeDerivationGoal(const StorePath & drvPath, + const StringSet & wantedOutputs, + BuildMode buildMode = bmNormal); std::shared_ptr makeBasicDerivationGoal( const StorePath & drvPath, const BasicDerivation & drv, const StringSet & wantedOutputs, BuildMode buildMode = bmNormal); /* substitution goal */ - std::shared_ptr makePathSubstitutionGoal(const StorePath & storePath, RepairFlag repair = NoRepair, std::optional ca = std::nullopt); - std::shared_ptr makeDrvOutputSubstitutionGoal(const DrvOutput & id, RepairFlag repair = NoRepair, std::optional ca = std::nullopt); + std::shared_ptr + makePathSubstitutionGoal(const StorePath & storePath, + RepairFlag repair = NoRepair, + std::optional ca = std::nullopt); + std::shared_ptr makeDrvOutputSubstitutionGoal( + const DrvOutput & id, RepairFlag repair = NoRepair, + std::optional ca = std::nullopt); /* Remove a dead goal. */ void removeGoal(GoalPtr goal); @@ -167,8 +170,8 @@ public: /* Registers a running child process. `inBuildSlot' means that the process counts towards the jobs limit. */ - void childStarted(GoalPtr goal, const std::set & fds, - bool inBuildSlot, bool respectTimeouts); + void childStarted(GoalPtr goal, const std::set & fds, bool inBuildSlot, + bool respectTimeouts); /* Unregisters a running child process. `wakeSleepers' should be false if there is no sense in waking up goals that are sleeping @@ -206,9 +209,13 @@ public: void updateProgress() { - actDerivations.progress(doneBuilds, expectedBuilds + doneBuilds, runningBuilds, failedBuilds); - actSubstitutions.progress(doneSubstitutions, expectedSubstitutions + doneSubstitutions, runningSubstitutions, failedSubstitutions); - act.setExpected(actFileTransfer, expectedDownloadSize + doneDownloadSize); + actDerivations.progress(doneBuilds, expectedBuilds + doneBuilds, + runningBuilds, failedBuilds); + actSubstitutions.progress(doneSubstitutions, + expectedSubstitutions + doneSubstitutions, + runningSubstitutions, failedSubstitutions); + act.setExpected(actFileTransfer, + expectedDownloadSize + doneDownloadSize); act.setExpected(actCopyPath, expectedNarSize + doneNarSize); } }; diff --git a/src/libstore/builtins.hh b/src/libstore/builtins.hh index 66597e456c40..e86e0dd43425 100644 --- a/src/libstore/builtins.hh +++ b/src/libstore/builtins.hh @@ -5,7 +5,8 @@ namespace nix { // TODO: make pluggable. -void builtinFetchurl(const BasicDerivation & drv, const std::string & netrcData); +void builtinFetchurl(const BasicDerivation & drv, + const std::string & netrcData); void builtinUnpackChannel(const BasicDerivation & drv); } diff --git a/src/libstore/builtins/buildenv.cc b/src/libstore/builtins/buildenv.cc index 47458a388d48..e281ceb54d2a 100644 --- a/src/libstore/builtins/buildenv.cc +++ b/src/libstore/builtins/buildenv.cc @@ -7,14 +7,14 @@ namespace nix { -struct State -{ +struct State { std::map priorities; unsigned long symlinks = 0; }; /* For each activated package, create symlinks */ -static void createLinks(State & state, const Path & srcDir, const Path & dstDir, int priority) +static void createLinks(State & state, const Path & srcDir, const Path & dstDir, + int priority) { DirEntries srcFiles; @@ -22,7 +22,9 @@ static void createLinks(State & state, const Path & srcDir, const Path & dstDir, srcFiles = readDirectory(srcDir); } catch (SysError & e) { if (e.errNo == ENOTDIR) { - warn("not including '%s' in the user environment because it's not a directory", srcDir); + warn("not including '%s' in the user environment because it's not " + "a directory", + srcDir); return; } throw; @@ -56,8 +58,7 @@ static void createLinks(State & state, const Path & srcDir, const Path & dstDir, if (hasSuffix(srcFile, "/propagated-build-inputs") || hasSuffix(srcFile, "/nix-support") || hasSuffix(srcFile, "/perllocal.pod") || - hasSuffix(srcFile, "/info/dir") || - hasSuffix(srcFile, "/log") || + hasSuffix(srcFile, "/info/dir") || hasSuffix(srcFile, "/log") || hasSuffix(srcFile, "/manifest.nix") || hasSuffix(srcFile, "/manifest.json")) continue; @@ -72,12 +73,15 @@ static void createLinks(State & state, const Path & srcDir, const Path & dstDir, } else if (S_ISLNK(dstSt.st_mode)) { auto target = canonPath(dstFile, true); if (!S_ISDIR(lstat(target).st_mode)) - throw Error("collision between '%1%' and non-directory '%2%'", srcFile, target); + throw Error( + "collision between '%1%' and non-directory '%2%'", + srcFile, target); if (unlink(dstFile.c_str()) == -1) throw SysError("unlinking '%1%'", dstFile); if (mkdir(dstFile.c_str(), 0755) == -1) throw SysError("creating directory '%1%'", dstFile); - createLinks(state, target, dstFile, state.priorities[dstFile]); + createLinks(state, target, dstFile, + state.priorities[dstFile]); createLinks(state, srcFile, dstFile, priority); continue; } @@ -93,18 +97,23 @@ static void createLinks(State & state, const Path & srcDir, const Path & dstDir, auto prevPriority = state.priorities[dstFile]; if (prevPriority == priority) throw Error( - "files '%1%' and '%2%' have the same priority %3%; " - "use 'nix-env --set-flag priority NUMBER INSTALLED_PKGNAME' " - "or type 'nix profile install --help' if using 'nix profile' to find out how" - "to change the priority of one of the conflicting packages" - " (0 being the highest priority)", - srcFile, readLink(dstFile), priority); + "files '%1%' and '%2%' have the same priority %3%; " + "use 'nix-env --set-flag priority NUMBER " + "INSTALLED_PKGNAME' " + "or type 'nix profile install --help' if using " + "'nix profile' to find out how" + "to change the priority of one of the conflicting " + "packages" + " (0 being the highest priority)", + srcFile, readLink(dstFile), priority); if (prevPriority < priority) continue; if (unlink(dstFile.c_str()) == -1) throw SysError("unlinking '%1%'", dstFile); } else if (S_ISDIR(dstSt.st_mode)) - throw Error("collision between non-directory '%1%' and directory '%2%'", srcFile, dstFile); + throw Error("collision between non-directory '%1%' and " + "directory '%2%'", + srcFile, dstFile); } else if (errno != ENOENT) throw SysError("getting status of '%1%'", dstFile); } @@ -122,16 +131,20 @@ void buildProfile(const Path & out, Packages && pkgs) std::set done, postponed; auto addPkg = [&](const Path & pkgDir, int priority) { - if (!done.insert(pkgDir).second) return; + if (!done.insert(pkgDir).second) + return; createLinks(state, pkgDir, out, priority); try { for (const auto & p : tokenizeString>( - readFile(pkgDir + "/nix-support/propagated-user-env-packages"), " \n")) + readFile(pkgDir + + "/nix-support/propagated-user-env-packages"), + " \n")) if (!done.count(p)) postponed.insert(p); } catch (SysError & e) { - if (e.errNo != ENOENT && e.errNo != ENOTDIR) throw; + if (e.errNo != ENOENT && e.errNo != ENOTDIR) + throw; } }; @@ -139,9 +152,11 @@ void buildProfile(const Path & out, Packages && pkgs) * user. Process in priority order to reduce unnecessary * symlink/unlink steps. */ - std::sort(pkgs.begin(), pkgs.end(), [](const Package & a, const Package & b) { - return a.priority < b.priority || (a.priority == b.priority && a.path < b.path); - }); + std::sort(pkgs.begin(), pkgs.end(), + [](const Package & a, const Package & b) { + return a.priority < b.priority || + (a.priority == b.priority && a.path < b.path); + }); for (const auto & pkg : pkgs) if (pkg.active) addPkg(pkg.path, pkg.priority); @@ -166,7 +181,8 @@ void builtinBuildenv(const BasicDerivation & drv) { auto getAttr = [&](const std::string & name) { auto i = drv.env.find(name); - if (i == drv.env.end()) throw Error("attribute '%s' missing", name); + if (i == drv.env.end()) + throw Error("attribute '%s' missing", name); return i->second; }; @@ -178,12 +194,17 @@ void builtinBuildenv(const BasicDerivation & drv) Packages pkgs; auto derivations = tokenizeString(getAttr("derivations")); while (!derivations.empty()) { - /* !!! We're trusting the caller to structure derivations env var correctly */ - auto active = derivations.front(); derivations.pop_front(); - auto priority = stoi(derivations.front()); derivations.pop_front(); - auto outputs = stoi(derivations.front()); derivations.pop_front(); + /* !!! We're trusting the caller to structure derivations env var + * correctly */ + auto active = derivations.front(); + derivations.pop_front(); + auto priority = stoi(derivations.front()); + derivations.pop_front(); + auto outputs = stoi(derivations.front()); + derivations.pop_front(); for (auto n = 0; n < outputs; n++) { - auto path = derivations.front(); derivations.pop_front(); + auto path = derivations.front(); + derivations.pop_front(); pkgs.emplace_back(path, active != "false", priority); } } diff --git a/src/libstore/builtins/buildenv.hh b/src/libstore/builtins/buildenv.hh index 73c0f5f7fd3f..774684a71e3c 100644 --- a/src/libstore/builtins/buildenv.hh +++ b/src/libstore/builtins/buildenv.hh @@ -9,7 +9,10 @@ struct Package { Path path; bool active; int priority; - Package(const Path & path, bool active, int priority) : path{path}, active{active}, priority{priority} {} + Package(const Path & path, bool active, int priority) + : path{path}, active{active}, priority{priority} + { + } }; typedef std::vector Packages; diff --git a/src/libstore/builtins/fetchurl.cc b/src/libstore/builtins/fetchurl.cc index 7d7924d77339..5c7c538bd847 100644 --- a/src/libstore/builtins/fetchurl.cc +++ b/src/libstore/builtins/fetchurl.cc @@ -18,7 +18,8 @@ void builtinFetchurl(const BasicDerivation & drv, const std::string & netrcData) auto getAttr = [&](const std::string & name) { auto i = drv.env.find(name); - if (i == drv.env.end()) throw Error("attribute '%s' missing", name); + if (i == drv.env.end()) + throw Error("attribute '%s' missing", name); return i->second; }; @@ -31,9 +32,7 @@ void builtinFetchurl(const BasicDerivation & drv, const std::string & netrcData) auto fileTransfer = makeFileTransfer(); auto fetch = [&](const std::string & url) { - auto source = sinkToSource([&](Sink & sink) { - /* No need to do TLS verification, because we check the hash of the result anyway. */ FileTransferRequest request(url); @@ -62,10 +61,13 @@ void builtinFetchurl(const BasicDerivation & drv, const std::string & netrcData) if (getAttr("outputHashMode") == "flat") for (auto hashedMirror : settings.hashedMirrors.get()) try { - if (!hasSuffix(hashedMirror, "/")) hashedMirror += '/'; - std::optional ht = parseHashTypeOpt(getAttr("outputHashAlgo")); + if (!hasSuffix(hashedMirror, "/")) + hashedMirror += '/'; + std::optional ht = + parseHashTypeOpt(getAttr("outputHashAlgo")); Hash h = newHashAllowEmpty(getAttr("outputHash"), ht); - fetch(hashedMirror + printHashType(h.type) + "/" + h.to_string(Base16, false)); + fetch(hashedMirror + printHashType(h.type) + "/" + + h.to_string(Base16, false)); return; } catch (Error & e) { debug(e.what()); diff --git a/src/libstore/builtins/unpack-channel.cc b/src/libstore/builtins/unpack-channel.cc index 426d58a53a4a..241232c9ce0f 100644 --- a/src/libstore/builtins/unpack-channel.cc +++ b/src/libstore/builtins/unpack-channel.cc @@ -7,7 +7,8 @@ void builtinUnpackChannel(const BasicDerivation & drv) { auto getAttr = [&](const std::string & name) { auto i = drv.env.find(name); - if (i == drv.env.end()) throw Error("attribute '%s' missing", name); + if (i == drv.env.end()) + throw Error("attribute '%s' missing", name); return i->second; }; @@ -22,7 +23,8 @@ void builtinUnpackChannel(const BasicDerivation & drv) auto entries = readDirectory(out); if (entries.size() != 1) throw Error("channel tarball '%s' contains more than one file", src); - if (rename((out + "/" + entries[0].name).c_str(), (out + "/" + channelName).c_str()) == -1) + if (rename((out + "/" + entries[0].name).c_str(), + (out + "/" + channelName).c_str()) == -1) throw SysError("renaming channel directory"); } diff --git a/src/libstore/content-address.cc b/src/libstore/content-address.cc index cf32ccdc461c..e6697705ba57 100644 --- a/src/libstore/content-address.cc +++ b/src/libstore/content-address.cc @@ -23,54 +23,61 @@ std::string makeFileIngestionPrefix(const FileIngestionMethod m) std::string makeFixedOutputCA(FileIngestionMethod method, const Hash & hash) { - return "fixed:" - + makeFileIngestionPrefix(method) - + hash.to_string(Base32, true); + return "fixed:" + makeFileIngestionPrefix(method) + + hash.to_string(Base32, true); } std::string renderContentAddress(ContentAddress ca) { - return std::visit(overloaded { - [](TextHash & th) { - return "text:" + th.hash.to_string(Base32, true); - }, - [](FixedOutputHash & fsh) { - return makeFixedOutputCA(fsh.method, fsh.hash); - } - }, ca); + return std::visit( + overloaded{[](TextHash & th) { + return "text:" + th.hash.to_string(Base32, true); + }, + [](FixedOutputHash & fsh) { + return makeFixedOutputCA(fsh.method, fsh.hash); + }}, + ca); } std::string renderContentAddressMethod(ContentAddressMethod cam) { - return std::visit(overloaded { - [](TextHashMethod & th) { - return std::string{"text:"} + printHashType(htSHA256); - }, - [](FixedOutputHashMethod & fshm) { - return "fixed:" + makeFileIngestionPrefix(fshm.fileIngestionMethod) + printHashType(fshm.hashType); - } - }, cam); + return std::visit(overloaded{[](TextHashMethod & th) { + return std::string{"text:"} + + printHashType(htSHA256); + }, + [](FixedOutputHashMethod & fshm) { + return "fixed:" + + makeFileIngestionPrefix( + fshm.fileIngestionMethod) + + printHashType(fshm.hashType); + }}, + cam); } /* Parses content address strings up to the hash. */ -static ContentAddressMethod parseContentAddressMethodPrefix(std::string_view & rest) +static ContentAddressMethod +parseContentAddressMethodPrefix(std::string_view & rest) { - std::string_view wholeInput { rest }; + std::string_view wholeInput{rest}; std::string_view prefix; { auto optPrefix = splitPrefixTo(rest, ':'); if (!optPrefix) - throw UsageError("not a content address because it is not in the form ':': %s", wholeInput); + throw UsageError("not a content address because it is not in the " + "form ':': %s", + wholeInput); prefix = *optPrefix; } - auto parseHashType_ = [&](){ + auto parseHashType_ = [&]() { auto hashTypeRaw = splitPrefixTo(rest, ':'); if (!hashTypeRaw) - throw UsageError("content address hash must be in form ':', but found: %s", wholeInput); + throw UsageError("content address hash must be in form " + "':', but found: %s", + wholeInput); HashType hashType = parseHashType(*hashTypeRaw); return std::move(hashType); }; @@ -80,42 +87,47 @@ static ContentAddressMethod parseContentAddressMethodPrefix(std::string_view & r // No parsing of the ingestion method, "text" only support flat. HashType hashType = parseHashType_(); if (hashType != htSHA256) - throw Error("text content address hash should use %s, but instead uses %s", + throw Error( + "text content address hash should use %s, but instead uses %s", printHashType(htSHA256), printHashType(hashType)); - return TextHashMethod {}; + return TextHashMethod{}; } else if (prefix == "fixed") { // Parse method auto method = FileIngestionMethod::Flat; if (splitPrefix(rest, "r:")) method = FileIngestionMethod::Recursive; HashType hashType = parseHashType_(); - return FixedOutputHashMethod { + return FixedOutputHashMethod{ .fileIngestionMethod = method, .hashType = std::move(hashType), }; } else - throw UsageError("content address prefix '%s' is unrecognized. Recogonized prefixes are 'text' or 'fixed'", prefix); + throw UsageError("content address prefix '%s' is unrecognized. " + "Recogonized prefixes are 'text' or 'fixed'", + prefix); } -ContentAddress parseContentAddress(std::string_view rawCa) { +ContentAddress parseContentAddress(std::string_view rawCa) +{ auto rest = rawCa; ContentAddressMethod caMethod = parseContentAddressMethodPrefix(rest); return std::visit( - overloaded { + overloaded{ [&](TextHashMethod & thm) { - return ContentAddress(TextHash { - .hash = Hash::parseNonSRIUnprefixed(rest, htSHA256) - }); + return ContentAddress(TextHash{ + .hash = Hash::parseNonSRIUnprefixed(rest, htSHA256)}); }, [&](FixedOutputHashMethod & fohMethod) { - return ContentAddress(FixedOutputHash { + return ContentAddress(FixedOutputHash{ .method = fohMethod.fileIngestionMethod, - .hash = Hash::parseNonSRIUnprefixed(rest, std::move(fohMethod.hashType)), + .hash = Hash::parseNonSRIUnprefixed( + rest, std::move(fohMethod.hashType)), }); }, - }, caMethod); + }, + caMethod); } ContentAddressMethod parseContentAddressMethod(std::string_view caMethod) @@ -128,7 +140,8 @@ ContentAddressMethod parseContentAddressMethod(std::string_view caMethod) std::optional parseContentAddressOpt(std::string_view rawCaOpt) { - return rawCaOpt == "" ? std::optional() : parseContentAddress(rawCaOpt); + return rawCaOpt == "" ? std::optional() + : parseContentAddress(rawCaOpt); }; std::string renderContentAddress(std::optional ca) @@ -138,14 +151,10 @@ std::string renderContentAddress(std::optional ca) Hash getContentAddressHash(const ContentAddress & ca) { - return std::visit(overloaded { - [](const TextHash & th) { - return th.hash; - }, - [](const FixedOutputHash & fsh) { - return fsh.hash; - } - }, ca); + return std::visit( + overloaded{[](const TextHash & th) { return th.hash; }, + [](const FixedOutputHash & fsh) { return fsh.hash; }}, + ca); } } diff --git a/src/libstore/content-address.hh b/src/libstore/content-address.hh index f6a6f5140048..f489cfcc6e60 100644 --- a/src/libstore/content-address.hh +++ b/src/libstore/content-address.hh @@ -5,10 +5,7 @@ namespace nix { -enum struct FileIngestionMethod : uint8_t { - Flat = false, - Recursive = true -}; +enum struct FileIngestionMethod : uint8_t { Flat = false, Recursive = true }; struct TextHash { Hash hash; @@ -32,10 +29,11 @@ struct FixedOutputHash { * ‘fixed:::’: For paths computed by makeFixedOutputPath() / addToStore(). */ -typedef std::variant< - TextHash, // for paths computed by makeTextPath() / addTextToStore - FixedOutputHash // for path computed by makeFixedOutputPath -> ContentAddress; +typedef std::variant + ContentAddress; /* Compute the prefix to the hash algorithm which indicates how the files were ingested. */ @@ -59,16 +57,15 @@ Hash getContentAddressHash(const ContentAddress & ca); We only have one way to hash text with references, so this is single-value type is only useful in std::variant. */ -struct TextHashMethod { }; +struct TextHashMethod { +}; struct FixedOutputHashMethod { - FileIngestionMethod fileIngestionMethod; - HashType hashType; + FileIngestionMethod fileIngestionMethod; + HashType hashType; }; -typedef std::variant< - TextHashMethod, - FixedOutputHashMethod - > ContentAddressMethod; +typedef std::variant + ContentAddressMethod; ContentAddressMethod parseContentAddressMethod(std::string_view rawCaMethod); diff --git a/src/libstore/crypto.cc b/src/libstore/crypto.cc index 1027469c9eee..64b7c0957da3 100644 --- a/src/libstore/crypto.cc +++ b/src/libstore/crypto.cc @@ -27,13 +27,9 @@ Key::Key(std::string_view s) key = base64Decode(key); } -std::string Key::to_string() const -{ - return name + ":" + base64Encode(key); -} +std::string Key::to_string() const { return name + ":" + base64Encode(key); } -SecretKey::SecretKey(std::string_view s) - : Key(s) +SecretKey::SecretKey(std::string_view s) : Key(s) { if (key.size() != crypto_sign_SECRETKEYBYTES) throw Error("secret key is not valid"); @@ -43,8 +39,8 @@ std::string SecretKey::signDetached(std::string_view data) const { unsigned char sig[crypto_sign_BYTES]; unsigned long long sigLen; - crypto_sign_detached(sig, &sigLen, (unsigned char *) data.data(), data.size(), - (unsigned char *) key.data()); + crypto_sign_detached(sig, &sigLen, (unsigned char *) data.data(), + data.size(), (unsigned char *) key.data()); return name + ":" + base64Encode(std::string((char *) sig, sigLen)); } @@ -52,7 +48,8 @@ PublicKey SecretKey::toPublicKey() const { unsigned char pk[crypto_sign_PUBLICKEYBYTES]; crypto_sign_ed25519_sk_to_pk(pk, (unsigned char *) key.data()); - return PublicKey(name, std::string((char *) pk, crypto_sign_PUBLICKEYBYTES)); + return PublicKey(name, + std::string((char *) pk, crypto_sign_PUBLICKEYBYTES)); } SecretKey SecretKey::generate(std::string_view name) @@ -62,31 +59,32 @@ SecretKey SecretKey::generate(std::string_view name) if (crypto_sign_keypair(pk, sk) != 0) throw Error("key generation failed"); - return SecretKey(name, std::string((char *) sk, crypto_sign_SECRETKEYBYTES)); + return SecretKey(name, + std::string((char *) sk, crypto_sign_SECRETKEYBYTES)); } -PublicKey::PublicKey(std::string_view s) - : Key(s) +PublicKey::PublicKey(std::string_view s) : Key(s) { if (key.size() != crypto_sign_PUBLICKEYBYTES) throw Error("public key is not valid"); } bool verifyDetached(const std::string & data, const std::string & sig, - const PublicKeys & publicKeys) + const PublicKeys & publicKeys) { auto ss = split(sig); auto key = publicKeys.find(std::string(ss.first)); - if (key == publicKeys.end()) return false; + if (key == publicKeys.end()) + return false; auto sig2 = base64Decode(ss.second); if (sig2.size() != crypto_sign_BYTES) throw Error("signature is not valid"); - return crypto_sign_verify_detached((unsigned char *) sig2.data(), - (unsigned char *) data.data(), data.size(), - (unsigned char *) key->second.key.data()) == 0; + return crypto_sign_verify_detached( + (unsigned char *) sig2.data(), (unsigned char *) data.data(), + data.size(), (unsigned char *) key->second.key.data()) == 0; } PublicKeys getDefaultPublicKeys() diff --git a/src/libstore/crypto.hh b/src/libstore/crypto.hh index 03f85c103109..ae8ecbbd940a 100644 --- a/src/libstore/crypto.hh +++ b/src/libstore/crypto.hh @@ -6,8 +6,7 @@ namespace nix { -struct Key -{ +struct Key { std::string name; std::string key; @@ -17,15 +16,16 @@ struct Key std::string to_string() const; -protected: + protected: Key(std::string_view name, std::string && key) - : name(name), key(std::move(key)) { } + : name(name), key(std::move(key)) + { + } }; struct PublicKey; -struct SecretKey : Key -{ +struct SecretKey : Key { SecretKey(std::string_view s); /* Return a detached signature of the given string. */ @@ -35,18 +35,21 @@ struct SecretKey : Key static SecretKey generate(std::string_view name); -private: + private: SecretKey(std::string_view name, std::string && key) - : Key(name, std::move(key)) { } + : Key(name, std::move(key)) + { + } }; -struct PublicKey : Key -{ +struct PublicKey : Key { PublicKey(std::string_view data); -private: + private: PublicKey(std::string_view name, std::string && key) - : Key(name, std::move(key)) { } + : Key(name, std::move(key)) + { + } friend struct SecretKey; }; @@ -55,7 +58,7 @@ typedef std::map PublicKeys; /* Return true iff ‘sig’ is a correct signature over ‘data’ using one of the given public keys. */ bool verifyDetached(const std::string & data, const std::string & sig, - const PublicKeys & publicKeys); + const PublicKeys & publicKeys); PublicKeys getDefaultPublicKeys(); diff --git a/src/libstore/daemon.cc b/src/libstore/daemon.cc index de69b50eec7a..b3538e59e918 100644 --- a/src/libstore/daemon.cc +++ b/src/libstore/daemon.cc @@ -14,7 +14,7 @@ namespace nix::daemon { -Sink & operator << (Sink & sink, const Logger::Fields & fields) +Sink & operator<<(Sink & sink, const Logger::Fields & fields) { sink << fields.size(); for (auto & f : fields) { @@ -23,7 +23,8 @@ Sink & operator << (Sink & sink, const Logger::Fields & fields) sink << f.i; else if (f.type == Logger::Field::tString) sink << f.s; - else abort(); + else + abort(); } return sink; } @@ -31,12 +32,10 @@ Sink & operator << (Sink & sink, const Logger::Fields & fields) /* Logger that forwards log messages to the client, *if* we're in a state where the protocol allows it (i.e., when canSendStderr is true). */ -struct TunnelLogger : public Logger -{ +struct TunnelLogger : public Logger { FdSink & to; - struct State - { + struct State { bool canSendStderr = false; std::vector pendingMsgs; }; @@ -46,7 +45,9 @@ struct TunnelLogger : public Logger unsigned int clientVersion; TunnelLogger(FdSink & to, unsigned int clientVersion) - : to(to), clientVersion(clientVersion) { } + : to(to), clientVersion(clientVersion) + { + } void enqueueMsg(const std::string & s) { @@ -69,7 +70,8 @@ struct TunnelLogger : public Logger void log(Verbosity lvl, const FormatOrString & fs) override { - if (lvl > verbosity) return; + if (lvl > verbosity) + return; StringSink buf; buf << STDERR_NEXT << (fs.s + "\n"); @@ -78,7 +80,8 @@ struct TunnelLogger : public Logger void logEI(const ErrorInfo & ei) override { - if (ei.level > verbosity) return; + if (ei.level > verbosity) + return; std::stringstream oss; showErrorInfo(oss, ei, false); @@ -123,7 +126,8 @@ struct TunnelLogger : public Logger } void startActivity(ActivityId act, Verbosity lvl, ActivityType type, - const std::string & s, const Fields & fields, ActivityId parent) override + const std::string & s, const Fields & fields, + ActivityId parent) override { if (GET_PROTOCOL_MINOR(clientVersion) < 20) { if (!s.empty()) @@ -132,13 +136,15 @@ struct TunnelLogger : public Logger } StringSink buf; - buf << STDERR_START_ACTIVITY << act << lvl << type << s << fields << parent; + buf << STDERR_START_ACTIVITY << act << lvl << type << s << fields + << parent; enqueueMsg(buf.s); } void stopActivity(ActivityId act) override { - if (GET_PROTOCOL_MINOR(clientVersion) < 20) return; + if (GET_PROTOCOL_MINOR(clientVersion) < 20) + return; StringSink buf; buf << STDERR_STOP_ACTIVITY << act; enqueueMsg(buf.s); @@ -146,41 +152,40 @@ struct TunnelLogger : public Logger void result(ActivityId act, ResultType type, const Fields & fields) override { - if (GET_PROTOCOL_MINOR(clientVersion) < 20) return; + if (GET_PROTOCOL_MINOR(clientVersion) < 20) + return; StringSink buf; buf << STDERR_RESULT << act << type << fields; enqueueMsg(buf.s); } }; -struct TunnelSink : Sink -{ +struct TunnelSink : Sink { Sink & to; - TunnelSink(Sink & to) : to(to) { } - void operator () (std::string_view data) + TunnelSink(Sink & to) : to(to) {} + void operator()(std::string_view data) { to << STDERR_WRITE; writeString(data, to); } }; -struct TunnelSource : BufferedSource -{ +struct TunnelSource : BufferedSource { Source & from; BufferedSink & to; - TunnelSource(Source & from, BufferedSink & to) : from(from), to(to) { } + TunnelSource(Source & from, BufferedSink & to) : from(from), to(to) {} size_t readUnbuffered(char * data, size_t len) override { to << STDERR_READ << len; to.flush(); size_t n = readString(data, len, from); - if (n == 0) throw EndOfFile("unexpected end-of-file"); + if (n == 0) + throw EndOfFile("unexpected end-of-file"); return n; } }; -struct ClientSettings -{ +struct ClientSettings { bool keepFailed; bool keepGoing; bool tryFallback; @@ -233,19 +238,22 @@ struct ClientSettings else if (name == settings.experimentalFeatures.name) { // We don’t want to forward the experimental features to // the daemon, as that could cause some pretty weird stuff - if (parseFeatures(tokenizeString(value)) != settings.experimentalFeatures.get()) - debug("Ignoring the client-specified experimental features"); - } - else if (trusted - || name == settings.buildTimeout.name - || name == settings.buildRepeat.name - || name == "connect-timeout" - || (name == "builders" && value == "")) + if (parseFeatures(tokenizeString(value)) != + settings.experimentalFeatures.get()) + debug("Ignoring the client-specified experimental " + "features"); + } else if (trusted || name == settings.buildTimeout.name || + name == settings.buildRepeat.name || + name == "connect-timeout" || + (name == "builders" && value == "")) settings.set(name, value); else if (setSubstituters(settings.substituters)) ; else - debug("ignoring the client-specified setting '%s', because it is a restricted setting and you are not a trusted user", name); + debug("ignoring the client-specified setting '%s', because " + "it is a restricted setting and you are not a " + "trusted user", + name); } catch (UsageError & e) { warn(e.what()); } @@ -253,11 +261,13 @@ struct ClientSettings } }; -static std::vector readDerivedPaths(Store & store, unsigned int clientVersion, Source & from) +static std::vector +readDerivedPaths(Store & store, unsigned int clientVersion, Source & from) { std::vector reqs; if (GET_PROTOCOL_MINOR(clientVersion) >= 30) { - reqs = worker_proto::read(store, from, Phantom> {}); + reqs = worker_proto::read(store, from, + Phantom>{}); } else { for (auto & s : readStrings(from)) reqs.push_back(parsePathWithOutputs(store, s).toDerivedPath()); @@ -266,8 +276,9 @@ static std::vector readDerivedPaths(Store & store, unsigned int cli } static void performOp(TunnelLogger * logger, ref store, - TrustedFlag trusted, RecursiveFlag recursive, unsigned int clientVersion, - Source & from, BufferedSink & to, unsigned int op) + TrustedFlag trusted, RecursiveFlag recursive, + unsigned int clientVersion, Source & from, + BufferedSink & to, unsigned int op) { switch (op) { @@ -281,7 +292,7 @@ static void performOp(TunnelLogger * logger, ref store, } case wopQueryValidPaths: { - auto paths = worker_proto::read(*store, from, Phantom {}); + auto paths = worker_proto::read(*store, from, Phantom{}); SubstituteFlag substitute = NoSubstitute; if (GET_PROTOCOL_MINOR(clientVersion) >= 27) { @@ -310,7 +321,7 @@ static void performOp(TunnelLogger * logger, ref store, } case wopQuerySubstitutablePaths: { - auto paths = worker_proto::read(*store, from, Phantom {}); + auto paths = worker_proto::read(*store, from, Phantom{}); logger->startWork(); auto res = store->querySubstitutablePaths(paths); logger->stopWork(); @@ -341,7 +352,8 @@ static void performOp(TunnelLogger * logger, ref store, store->queryReferrers(path, paths); else if (op == wopQueryValidDerivers) paths = store->queryValidDerivers(path); - else paths = store->queryDerivationOutputs(path); + else + paths = store->queryDerivationOutputs(path); logger->stopWork(); worker_proto::write(*store, to, paths); break; @@ -387,29 +399,39 @@ static void performOp(TunnelLogger * logger, ref store, if (GET_PROTOCOL_MINOR(clientVersion) >= 25) { auto name = readString(from); auto camStr = readString(from); - auto refs = worker_proto::read(*store, from, Phantom {}); + auto refs = + worker_proto::read(*store, from, Phantom{}); bool repairBool; from >> repairBool; auto repair = RepairFlag{repairBool}; logger->startWork(); auto pathInfo = [&]() { - // NB: FramedSource must be out of scope before logger->stopWork(); - ContentAddressMethod contentAddressMethod = parseContentAddressMethod(camStr); + // NB: FramedSource must be out of scope before + // logger->stopWork(); + ContentAddressMethod contentAddressMethod = + parseContentAddressMethod(camStr); FramedSource source(from); - // TODO this is essentially RemoteStore::addCAToStore. Move it up to Store. - return std::visit(overloaded { - [&](TextHashMethod &) { - // We could stream this by changing Store - std::string contents = source.drain(); - auto path = store->addTextToStore(name, contents, refs, repair); - return store->queryPathInfo(path); - }, - [&](FixedOutputHashMethod & fohm) { - auto path = store->addToStoreFromDump(source, name, fohm.fileIngestionMethod, fohm.hashType, repair, refs); - return store->queryPathInfo(path); - }, - }, contentAddressMethod); + // TODO this is essentially RemoteStore::addCAToStore. Move it + // up to Store. + return std::visit(overloaded{ + [&](TextHashMethod &) { + // We could stream this by changing + // Store + std::string contents = source.drain(); + auto path = store->addTextToStore( + name, contents, refs, repair); + return store->queryPathInfo(path); + }, + [&](FixedOutputHashMethod & fohm) { + auto path = store->addToStoreFromDump( + source, name, + fohm.fileIngestionMethod, + fohm.hashType, repair, refs); + return store->queryPathInfo(path); + }, + }, + contentAddressMethod); }(); logger->stopWork(); @@ -422,10 +444,13 @@ static void performOp(TunnelLogger * logger, ref store, bool fixed; uint8_t recursive; std::string hashAlgoRaw; - from >> baseName >> fixed /* obsolete */ >> recursive >> hashAlgoRaw; + from >> baseName >> fixed /* obsolete */ >> recursive >> + hashAlgoRaw; if (recursive > (uint8_t) FileIngestionMethod::Recursive) - throw Error("unsupported FileIngestionMethod with value of %i; you may need to upgrade nix-daemon", recursive); - method = FileIngestionMethod { recursive }; + throw Error("unsupported FileIngestionMethod with value of " + "%i; you may need to upgrade nix-daemon", + recursive); + method = FileIngestionMethod{recursive}; /* Compatibility hack. */ if (!fixed) { hashAlgoRaw = "sha256"; @@ -451,13 +476,15 @@ static void performOp(TunnelLogger * logger, ref store, /* Incrementally parse the NAR file, stripping the metadata, and streaming the sole file we expect into `saved`. */ - RetrieveRegularNARSink savedRegular { saved }; + RetrieveRegularNARSink savedRegular{saved}; parseDump(savedRegular, from); - if (!savedRegular.regular) throw Error("regular file expected"); + if (!savedRegular.regular) + throw Error("regular file expected"); } }); logger->startWork(); - auto path = store->addToStoreFromDump(*dumpSource, baseName, method, hashAlgo); + auto path = store->addToStoreFromDump(*dumpSource, baseName, method, + hashAlgo); logger->stopWork(); to << store->printStorePath(path); @@ -474,9 +501,8 @@ static void performOp(TunnelLogger * logger, ref store, logger->startWork(); { FramedSource source(from); - store->addMultipleToStore(source, - RepairFlag{repair}, - dontCheckSigs ? NoCheckSigs : CheckSigs); + store->addMultipleToStore(source, RepairFlag{repair}, + dontCheckSigs ? NoCheckSigs : CheckSigs); } logger->stopWork(); break; @@ -485,7 +511,7 @@ static void performOp(TunnelLogger * logger, ref store, case wopAddTextToStore: { std::string suffix = readString(from); std::string s = readString(from); - auto refs = worker_proto::read(*store, from, Phantom {}); + auto refs = worker_proto::read(*store, from, Phantom{}); logger->startWork(); auto path = store->addTextToStore(suffix, s, refs, NoRepair); logger->stopWork(); @@ -507,11 +533,12 @@ static void performOp(TunnelLogger * logger, ref store, case wopImportPaths: { logger->startWork(); TunnelSource source(from, to); - auto paths = store->importPaths(source, - trusted ? NoCheckSigs : CheckSigs); + auto paths = + store->importPaths(source, trusted ? NoCheckSigs : CheckSigs); logger->stopWork(); Strings paths2; - for (auto & i : paths) paths2.push_back(store->printStorePath(i)); + for (auto & i : paths) + paths2.push_back(store->printStorePath(i)); to << paths2; break; } @@ -525,7 +552,8 @@ static void performOp(TunnelLogger * logger, ref store, /* Repairing is not atomic, so disallowed for "untrusted" clients. */ if (mode == bmRepair && !trusted) - throw Error("repairing is not allowed because you are not in 'trusted-users'"); + throw Error("repairing is not allowed because you are not in " + "'trusted-users'"); } logger->startWork(); store->buildPaths(drvs, mode); @@ -542,7 +570,8 @@ static void performOp(TunnelLogger * logger, ref store, /* Repairing is not atomic, so disallowed for "untrusted" clients. */ if (mode == bmRepair && !trusted) - throw Error("repairing is not allowed because you are not in 'trusted-users'"); + throw Error("repairing is not allowed because you are not in " + "'trusted-users'"); logger->startWork(); auto results = store->buildPathsWithResults(drvs, mode); @@ -562,9 +591,9 @@ static void performOp(TunnelLogger * logger, ref store, auto drvType = drv.type(); - /* Content-addressed derivations are trustless because their output paths - are verified by their content alone, so any derivation is free to - try to produce such a path. + /* Content-addressed derivations are trustless because their output + paths are verified by their content alone, so any derivation is free + to try to produce such a path. Input-addressed derivation output paths, however, are calculated from the derivation closure that produced them---even knowing the @@ -595,7 +624,8 @@ static void performOp(TunnelLogger * logger, ref store, store the hashes, so there aren't two competing sources of truth an attacker could exploit. */ if (!(drvType.isCA() || trusted)) - throw Error("you are not privileged to build input-addressed derivations"); + throw Error( + "you are not privileged to build input-addressed derivations"); /* Make sure that the non-input-addressed derivations that got this far are in fact content-addressed if we don't trust them. */ @@ -612,14 +642,15 @@ static void performOp(TunnelLogger * logger, ref store, Derivation drv2; static_cast(drv2) = drv; - drvPath = writeDerivation(*store, Derivation { drv2 }); + drvPath = writeDerivation(*store, Derivation{drv2}); } auto res = store->buildDerivation(drvPath, drv, buildMode); logger->stopWork(); to << res.status << res.errorMsg; if (GET_PROTOCOL_MINOR(clientVersion) >= 29) { - to << res.timesBuilt << res.isNonDeterministic << res.startTime << res.stopTime; + to << res.timesBuilt << res.isNonDeterministic << res.startTime + << res.stopTime; } if (GET_PROTOCOL_MINOR(clientVersion) >= 28) { worker_proto::write(*store, to, res.builtOutputs); @@ -687,7 +718,8 @@ static void performOp(TunnelLogger * logger, ref store, case wopCollectGarbage: { GCOptions options; options.action = (GCOptions::GCAction) readInt(from); - options.pathsToDelete = worker_proto::read(*store, from, Phantom {}); + options.pathsToDelete = + worker_proto::read(*store, from, Phantom{}); from >> options.ignoreLiveness >> options.maxFreed; // obsolete fields readInt(from); @@ -756,10 +788,10 @@ static void performOp(TunnelLogger * logger, ref store, to << 0; else { to << 1 - << (i->second.deriver ? store->printStorePath(*i->second.deriver) : ""); + << (i->second.deriver ? store->printStorePath(*i->second.deriver) + : ""); worker_proto::write(*store, to, i->second.references); - to << i->second.downloadSize - << i->second.narSize; + to << i->second.downloadSize << i->second.narSize; } break; } @@ -768,18 +800,21 @@ static void performOp(TunnelLogger * logger, ref store, SubstitutablePathInfos infos; StorePathCAMap pathsMap = {}; if (GET_PROTOCOL_MINOR(clientVersion) < 22) { - auto paths = worker_proto::read(*store, from, Phantom {}); + auto paths = + worker_proto::read(*store, from, Phantom{}); for (auto & path : paths) pathsMap.emplace(path, std::nullopt); } else - pathsMap = worker_proto::read(*store, from, Phantom {}); + pathsMap = + worker_proto::read(*store, from, Phantom{}); logger->startWork(); store->querySubstitutablePathInfos(pathsMap, infos); logger->stopWork(); to << infos.size(); for (auto & i : infos) { to << store->printStorePath(i.first) - << (i.second.deriver ? store->printStorePath(*i.second.deriver) : ""); + << (i.second.deriver ? store->printStorePath(*i.second.deriver) + : ""); worker_proto::write(*store, to, i.second.references); to << i.second.downloadSize << i.second.narSize; } @@ -801,7 +836,8 @@ static void performOp(TunnelLogger * logger, ref store, try { info = store->queryPathInfo(path); } catch (InvalidPath &) { - if (GET_PROTOCOL_MINOR(clientVersion) < 17) throw; + if (GET_PROTOCOL_MINOR(clientVersion) < 17) + throw; } logger->stopWork(); if (info) { @@ -859,10 +895,11 @@ static void performOp(TunnelLogger * logger, ref store, auto path = store->parseStorePath(readString(from)); auto deriver = readString(from); auto narHash = Hash::parseAny(readString(from), htSHA256); - ValidPathInfo info { path, narHash }; + ValidPathInfo info{path, narHash}; if (deriver != "") info.deriver = store->parseStorePath(deriver); - info.references = worker_proto::read(*store, from, Phantom {}); + info.references = + worker_proto::read(*store, from, Phantom{}); from >> info.registrationTime >> info.narSize >> info.ultimate; info.sigs = readStrings(from); info.ca = parseContentAddressOpt(readString(from)); @@ -877,7 +914,7 @@ static void performOp(TunnelLogger * logger, ref store, { FramedSource source(from); store->addToStore(info, source, (RepairFlag) repair, - dontCheckSigs ? NoCheckSigs : CheckSigs); + dontCheckSigs ? NoCheckSigs : CheckSigs); } logger->stopWork(); } @@ -888,7 +925,7 @@ static void performOp(TunnelLogger * logger, ref store, if (GET_PROTOCOL_MINOR(clientVersion) >= 21) source = std::make_unique(from, to); else { - TeeSource tee { from, saved }; + TeeSource tee{from, saved}; ParseSink ether; parseDump(ether, tee); source = std::make_unique(saved.s); @@ -898,7 +935,7 @@ static void performOp(TunnelLogger * logger, ref store, // FIXME: race if addToStore doesn't read source? store->addToStore(info, *source, (RepairFlag) repair, - dontCheckSigs ? NoCheckSigs : CheckSigs); + dontCheckSigs ? NoCheckSigs : CheckSigs); logger->stopWork(); } @@ -911,7 +948,8 @@ static void performOp(TunnelLogger * logger, ref store, logger->startWork(); StorePathSet willBuild, willSubstitute, unknown; uint64_t downloadSize, narSize; - store->queryMissing(targets, willBuild, willSubstitute, unknown, downloadSize, narSize); + store->queryMissing(targets, willBuild, willSubstitute, unknown, + downloadSize, narSize); logger->stopWork(); worker_proto::write(*store, to, willBuild); worker_proto::write(*store, to, willSubstitute); @@ -925,10 +963,11 @@ static void performOp(TunnelLogger * logger, ref store, if (GET_PROTOCOL_MINOR(clientVersion) < 31) { auto outputId = DrvOutput::parse(readString(from)); auto outputPath = StorePath(readString(from)); - store->registerDrvOutput(Realisation{ - .id = outputId, .outPath = outputPath}); + store->registerDrvOutput( + Realisation{.id = outputId, .outPath = outputPath}); } else { - auto realisation = worker_proto::read(*store, from, Phantom()); + auto realisation = + worker_proto::read(*store, from, Phantom()); store->registerDrvOutput(realisation); } logger->stopWork(); @@ -942,11 +981,13 @@ static void performOp(TunnelLogger * logger, ref store, logger->stopWork(); if (GET_PROTOCOL_MINOR(clientVersion) < 31) { std::set outPaths; - if (info) outPaths.insert(info->outPath); + if (info) + outPaths.insert(info->outPath); worker_proto::write(*store, to, outPaths); } else { std::set realisations; - if (info) realisations.insert(*info); + if (info) + realisations.insert(*info); worker_proto::write(*store, to, realisations); } break; @@ -974,19 +1015,17 @@ static void performOp(TunnelLogger * logger, ref store, } } -void processConnection( - ref store, - FdSource & from, - FdSink & to, - TrustedFlag trusted, - RecursiveFlag recursive, - std::function authHook) +void processConnection(ref store, FdSource & from, FdSink & to, + TrustedFlag trusted, RecursiveFlag recursive, + std::function authHook) { - auto monitor = !recursive ? std::make_unique(from.fd) : nullptr; + auto monitor = + !recursive ? std::make_unique(from.fd) : nullptr; /* Exchange the greeting. */ unsigned int magic = readInt(from); - if (magic != WORKER_MAGIC_1) throw Error("protocol mismatch"); + if (magic != WORKER_MAGIC_1) + throw Error("protocol mismatch"); to << WORKER_MAGIC_2 << PROTOCOL_VERSION; to.flush(); unsigned int clientVersion = readInt(from); @@ -1024,7 +1063,7 @@ void processConnection( try { /* If we can't accept clientVersion, then throw an error - *here* (not above). */ + *here* (not above). */ authHook(*store); tunnelLogger->stopWork(); @@ -1046,7 +1085,8 @@ void processConnection( opCount++; try { - performOp(tunnelLogger, store, trusted, recursive, clientVersion, from, to, op); + performOp(tunnelLogger, store, trusted, recursive, + clientVersion, from, to, op); } catch (Error & e) { /* If we're not in a state where we can send replies, then something went wrong processing the input of the @@ -1055,7 +1095,8 @@ void processConnection( happens, just send the error message and exit. */ bool errorAllowed = tunnelLogger->state_.lock()->canSendStderr; tunnelLogger->stopWork(&e); - if (!errorAllowed) throw; + if (!errorAllowed) + throw; } catch (std::bad_alloc & e) { auto ex = Error("Nix daemon out of memory"); tunnelLogger->stopWork(&ex); diff --git a/src/libstore/daemon.hh b/src/libstore/daemon.hh index 67755d54effc..c9fb014234dc 100644 --- a/src/libstore/daemon.hh +++ b/src/libstore/daemon.hh @@ -9,10 +9,7 @@ enum TrustedFlag : bool { NotTrusted = false, Trusted = true }; enum RecursiveFlag : bool { NotRecursive = false, Recursive = true }; void processConnection( - ref store, - FdSource & from, - FdSink & to, - TrustedFlag trusted, + ref store, FdSource & from, FdSink & to, TrustedFlag trusted, RecursiveFlag recursive, /* Arbitrary hook to check authorization / initialize user data / whatever after the protocol has been negotiated. The idea is that this function diff --git a/src/libstore/derivations.cc b/src/libstore/derivations.cc index fe99c3c5eb38..a6554c7baf7d 100644 --- a/src/libstore/derivations.cc +++ b/src/libstore/derivations.cc @@ -8,128 +8,101 @@ namespace nix { -std::optional DerivationOutput::path(const Store & store, std::string_view drvName, std::string_view outputName) const -{ - return std::visit(overloaded { - [](const DerivationOutput::InputAddressed & doi) -> std::optional { - return { doi.path }; - }, - [&](const DerivationOutput::CAFixed & dof) -> std::optional { - return { - dof.path(store, drvName, outputName) - }; - }, - [](const DerivationOutput::CAFloating & dof) -> std::optional { - return std::nullopt; - }, - [](const DerivationOutput::Deferred &) -> std::optional { - return std::nullopt; - }, - [](const DerivationOutput::Impure &) -> std::optional { - return std::nullopt; +std::optional +DerivationOutput::path(const Store & store, std::string_view drvName, + std::string_view outputName) const +{ + return std::visit( + overloaded{ + [](const DerivationOutput::InputAddressed & doi) + -> std::optional { return {doi.path}; }, + [&](const DerivationOutput::CAFixed & dof) + -> std::optional { + return {dof.path(store, drvName, outputName)}; + }, + [](const DerivationOutput::CAFloating & dof) + -> std::optional { return std::nullopt; }, + [](const DerivationOutput::Deferred &) -> std::optional { + return std::nullopt; + }, + [](const DerivationOutput::Impure &) -> std::optional { + return std::nullopt; + }, }, - }, raw()); + raw()); } - -StorePath DerivationOutput::CAFixed::path(const Store & store, std::string_view drvName, std::string_view outputName) const +StorePath DerivationOutput::CAFixed::path(const Store & store, + std::string_view drvName, + std::string_view outputName) const { - return store.makeFixedOutputPath( - hash.method, hash.hash, - outputPathName(drvName, outputName)); + return store.makeFixedOutputPath(hash.method, hash.hash, + outputPathName(drvName, outputName)); } - bool DerivationType::isCA() const { /* Normally we do the full `std::visit` to make sure we have exhaustively handled all variants, but so long as there is a variant called `ContentAddressed`, it must be the only one for which `isCA` is true for this to make sense!. */ - return std::visit(overloaded { - [](const InputAddressed & ia) { - return false; - }, - [](const ContentAddressed & ca) { - return true; - }, - [](const Impure &) { - return true; - }, - }, raw()); + return std::visit(overloaded{ + [](const InputAddressed & ia) { return false; }, + [](const ContentAddressed & ca) { return true; }, + [](const Impure &) { return true; }, + }, + raw()); } bool DerivationType::isFixed() const { - return std::visit(overloaded { - [](const InputAddressed & ia) { - return false; - }, - [](const ContentAddressed & ca) { - return ca.fixed; - }, - [](const Impure &) { - return false; - }, - }, raw()); + return std::visit(overloaded{ + [](const InputAddressed & ia) { return false; }, + [](const ContentAddressed & ca) { return ca.fixed; }, + [](const Impure &) { return false; }, + }, + raw()); } bool DerivationType::hasKnownOutputPaths() const { - return std::visit(overloaded { - [](const InputAddressed & ia) { - return !ia.deferred; - }, - [](const ContentAddressed & ca) { - return ca.fixed; - }, - [](const Impure &) { - return false; + return std::visit( + overloaded{ + [](const InputAddressed & ia) { return !ia.deferred; }, + [](const ContentAddressed & ca) { return ca.fixed; }, + [](const Impure &) { return false; }, }, - }, raw()); + raw()); } - bool DerivationType::isSandboxed() const { - return std::visit(overloaded { - [](const InputAddressed & ia) { - return true; - }, - [](const ContentAddressed & ca) { - return ca.sandboxed; + return std::visit( + overloaded{ + [](const InputAddressed & ia) { return true; }, + [](const ContentAddressed & ca) { return ca.sandboxed; }, + [](const Impure &) { return false; }, }, - [](const Impure &) { - return false; - }, - }, raw()); + raw()); } - bool DerivationType::isPure() const { - return std::visit(overloaded { - [](const InputAddressed & ia) { - return true; - }, - [](const ContentAddressed & ca) { - return true; - }, - [](const Impure &) { - return false; - }, - }, raw()); + return std::visit(overloaded{ + [](const InputAddressed & ia) { return true; }, + [](const ContentAddressed & ca) { return true; }, + [](const Impure &) { return false; }, + }, + raw()); } - bool BasicDerivation::isBuiltin() const { return builder.substr(0, 8) == "builtin:"; } - -StorePath writeDerivation(Store & store, - const Derivation & drv, RepairFlag repair, bool readOnly) +StorePath writeDerivation(Store & store, const Derivation & drv, + RepairFlag repair, bool readOnly) { auto references = drv.inputSrcs; for (auto & i : drv.inputDrvs) @@ -140,11 +113,10 @@ StorePath writeDerivation(Store & store, auto suffix = std::string(drv.name) + drvExtension; auto contents = drv.unparse(store, false); return readOnly || settings.readOnlyMode - ? store.computeStorePathForText(suffix, contents, references) - : store.addTextToStore(suffix, contents, references, repair); + ? store.computeStorePathForText(suffix, contents, references) + : store.addTextToStore(suffix, contents, references, repair); } - /* Read string `s' from stream `str'. */ static void expect(std::istream & str, std::string_view s) { @@ -154,7 +126,6 @@ static void expect(std::istream & str, std::string_view s) throw FormatError("expected string '%1%'", s); } - /* Read a C-style string from stream `str'. */ static std::string parseString(std::istream & str) { @@ -164,16 +135,21 @@ static std::string parseString(std::istream & str) while ((c = str.get()) != '"') if (c == '\\') { c = str.get(); - if (c == 'n') res += '\n'; - else if (c == 'r') res += '\r'; - else if (c == 't') res += '\t'; - else res += c; - } - else res += c; + if (c == 'n') + res += '\n'; + else if (c == 'r') + res += '\r'; + else if (c == 't') + res += '\t'; + else + res += c; + } else + res += c; return res; } -static void validatePath(std::string_view s) { +static void validatePath(std::string_view s) +{ if (s.size() == 0 || s[0] != '/') throw FormatError("bad path '%1%' in derivation", s); } @@ -185,7 +161,6 @@ static Path parsePath(std::istream & str) return s; } - static bool endOfList(std::istream & str) { if (str.peek() == ',') { @@ -199,7 +174,6 @@ static bool endOfList(std::istream & str) return false; } - static StringSet parseStrings(std::istream & str, bool arePaths) { StringSet res; @@ -208,9 +182,10 @@ static StringSet parseStrings(std::istream & str, bool arePaths) return res; } - static DerivationOutput parseDerivationOutput(const Store & store, - std::string_view pathS, std::string_view hashAlgo, std::string_view hash) + std::string_view pathS, + std::string_view hashAlgo, + std::string_view hash) { if (hashAlgo != "") { auto method = FileIngestionMethod::Flat; @@ -222,49 +197,54 @@ static DerivationOutput parseDerivationOutput(const Store & store, if (hash == "impure") { settings.requireExperimentalFeature(Xp::ImpureDerivations); assert(pathS == ""); - return DerivationOutput::Impure { + return DerivationOutput::Impure{ .method = std::move(method), .hashType = std::move(hashType), }; } else if (hash != "") { validatePath(pathS); - return DerivationOutput::CAFixed { - .hash = FixedOutputHash { - .method = std::move(method), - .hash = Hash::parseNonSRIUnprefixed(hash, hashType), - }, + return DerivationOutput::CAFixed{ + .hash = + FixedOutputHash{ + .method = std::move(method), + .hash = Hash::parseNonSRIUnprefixed(hash, hashType), + }, }; } else { settings.requireExperimentalFeature(Xp::CaDerivations); assert(pathS == ""); - return DerivationOutput::CAFloating { + return DerivationOutput::CAFloating{ .method = std::move(method), .hashType = std::move(hashType), }; } } else { if (pathS == "") { - return DerivationOutput::Deferred { }; + return DerivationOutput::Deferred{}; } validatePath(pathS); - return DerivationOutput::InputAddressed { + return DerivationOutput::InputAddressed{ .path = store.parseStorePath(pathS), }; } } -static DerivationOutput parseDerivationOutput(const Store & store, std::istringstream & str) -{ - expect(str, ","); const auto pathS = parseString(str); - expect(str, ","); const auto hashAlgo = parseString(str); - expect(str, ","); const auto hash = parseString(str); +static DerivationOutput parseDerivationOutput(const Store & store, + std::istringstream & str) +{ + expect(str, ","); + const auto pathS = parseString(str); + expect(str, ","); + const auto hashAlgo = parseString(str); + expect(str, ","); + const auto hash = parseString(str); expect(str, ")"); return parseDerivationOutput(store, pathS, hashAlgo, hash); } - -Derivation parseDerivation(const Store & store, std::string && s, std::string_view name) +Derivation parseDerivation(const Store & store, std::string && s, + std::string_view name) { Derivation drv; drv.name = name; @@ -274,7 +254,8 @@ Derivation parseDerivation(const Store & store, std::string && s, std::string_vi /* Parse the list of outputs. */ while (!endOfList(str)) { - expect(str, "("); std::string id = parseString(str); + expect(str, "("); + std::string id = parseString(str); auto output = parseDerivationOutput(store, str); drv.outputs.emplace(std::move(id), std::move(output)); } @@ -285,13 +266,17 @@ Derivation parseDerivation(const Store & store, std::string && s, std::string_vi expect(str, "("); Path drvPath = parsePath(str); expect(str, ",["); - drv.inputDrvs.insert_or_assign(store.parseStorePath(drvPath), parseStrings(str, false)); + drv.inputDrvs.insert_or_assign(store.parseStorePath(drvPath), + parseStrings(str, false)); expect(str, ")"); } - expect(str, ",["); drv.inputSrcs = store.parseStorePathSet(parseStrings(str, true)); - expect(str, ","); drv.platform = parseString(str); - expect(str, ","); drv.builder = parseString(str); + expect(str, ",["); + drv.inputSrcs = store.parseStorePathSet(parseStrings(str, true)); + expect(str, ","); + drv.platform = parseString(str); + expect(str, ","); + drv.builder = parseString(str); /* Parse the builder arguments. */ expect(str, ",["); @@ -301,8 +286,10 @@ Derivation parseDerivation(const Store & store, std::string && s, std::string_vi /* Parse the environment variables. */ expect(str, ",["); while (!endOfList(str)) { - expect(str, "("); auto name = parseString(str); - expect(str, ","); auto value = parseString(str); + expect(str, "("); + auto name = parseString(str); + expect(str, ","); + auto value = parseString(str); expect(str, ")"); drv.env[name] = value; } @@ -311,7 +298,6 @@ Derivation parseDerivation(const Store & store, std::string && s, std::string_vi return drv; } - static void printString(std::string & res, std::string_view s) { boost::container::small_vector buffer; @@ -320,16 +306,24 @@ static void printString(std::string & res, std::string_view s) char * p = buf; *p++ = '"'; for (auto c : s) - if (c == '\"' || c == '\\') { *p++ = '\\'; *p++ = c; } - else if (c == '\n') { *p++ = '\\'; *p++ = 'n'; } - else if (c == '\r') { *p++ = '\\'; *p++ = 'r'; } - else if (c == '\t') { *p++ = '\\'; *p++ = 't'; } - else *p++ = c; + if (c == '\"' || c == '\\') { + *p++ = '\\'; + *p++ = c; + } else if (c == '\n') { + *p++ = '\\'; + *p++ = 'n'; + } else if (c == '\r') { + *p++ = '\\'; + *p++ = 'r'; + } else if (c == '\t') { + *p++ = '\\'; + *p++ = 't'; + } else + *p++ = c; *p++ = '"'; res.append(buf, p - buf); } - static void printUnquotedString(std::string & res, std::string_view s) { res += '"'; @@ -337,35 +331,41 @@ static void printUnquotedString(std::string & res, std::string_view s) res += '"'; } - template -static void printStrings(std::string & res, ForwardIterator i, ForwardIterator j) +static void printStrings(std::string & res, ForwardIterator i, + ForwardIterator j) { res += '['; bool first = true; - for ( ; i != j; ++i) { - if (first) first = false; else res += ','; + for (; i != j; ++i) { + if (first) + first = false; + else + res += ','; printString(res, *i); } res += ']'; } - template -static void printUnquotedStrings(std::string & res, ForwardIterator i, ForwardIterator j) +static void printUnquotedStrings(std::string & res, ForwardIterator i, + ForwardIterator j) { res += '['; bool first = true; - for ( ; i != j; ++i) { - if (first) first = false; else res += ','; + for (; i != j; ++i) { + if (first) + first = false; + else + res += ','; printUnquotedString(res, *i); } res += ']'; } - -std::string Derivation::unparse(const Store & store, bool maskOutputs, - std::map * actualInputs) const +std::string +Derivation::unparse(const Store & store, bool maskOutputs, + std::map * actualInputs) const { std::string s; s.reserve(65536); @@ -373,36 +373,63 @@ std::string Derivation::unparse(const Store & store, bool maskOutputs, bool first = true; for (auto & i : outputs) { - if (first) first = false; else s += ','; - s += '('; printUnquotedString(s, i.first); - std::visit(overloaded { - [&](const DerivationOutput::InputAddressed & doi) { - s += ','; printUnquotedString(s, maskOutputs ? "" : store.printStorePath(doi.path)); - s += ','; printUnquotedString(s, ""); - s += ','; printUnquotedString(s, ""); - }, - [&](const DerivationOutput::CAFixed & dof) { - s += ','; printUnquotedString(s, maskOutputs ? "" : store.printStorePath(dof.path(store, name, i.first))); - s += ','; printUnquotedString(s, dof.hash.printMethodAlgo()); - s += ','; printUnquotedString(s, dof.hash.hash.to_string(Base16, false)); - }, - [&](const DerivationOutput::CAFloating & dof) { - s += ','; printUnquotedString(s, ""); - s += ','; printUnquotedString(s, makeFileIngestionPrefix(dof.method) + printHashType(dof.hashType)); - s += ','; printUnquotedString(s, ""); - }, - [&](const DerivationOutput::Deferred &) { - s += ','; printUnquotedString(s, ""); - s += ','; printUnquotedString(s, ""); - s += ','; printUnquotedString(s, ""); - }, - [&](const DerivationOutputImpure & doi) { - // FIXME - s += ','; printUnquotedString(s, ""); - s += ','; printUnquotedString(s, makeFileIngestionPrefix(doi.method) + printHashType(doi.hashType)); - s += ','; printUnquotedString(s, "impure"); - } - }, i.second.raw()); + if (first) + first = false; + else + s += ','; + s += '('; + printUnquotedString(s, i.first); + std::visit( + overloaded{ + [&](const DerivationOutput::InputAddressed & doi) { + s += ','; + printUnquotedString( + s, maskOutputs ? "" : store.printStorePath(doi.path)); + s += ','; + printUnquotedString(s, ""); + s += ','; + printUnquotedString(s, ""); + }, + [&](const DerivationOutput::CAFixed & dof) { + s += ','; + printUnquotedString(s, maskOutputs + ? "" + : store.printStorePath(dof.path( + store, name, i.first))); + s += ','; + printUnquotedString(s, dof.hash.printMethodAlgo()); + s += ','; + printUnquotedString(s, + dof.hash.hash.to_string(Base16, false)); + }, + [&](const DerivationOutput::CAFloating & dof) { + s += ','; + printUnquotedString(s, ""); + s += ','; + printUnquotedString(s, makeFileIngestionPrefix(dof.method) + + printHashType(dof.hashType)); + s += ','; + printUnquotedString(s, ""); + }, + [&](const DerivationOutput::Deferred &) { + s += ','; + printUnquotedString(s, ""); + s += ','; + printUnquotedString(s, ""); + s += ','; + printUnquotedString(s, ""); + }, + [&](const DerivationOutputImpure & doi) { + // FIXME + s += ','; + printUnquotedString(s, ""); + s += ','; + printUnquotedString(s, makeFileIngestionPrefix(doi.method) + + printHashType(doi.hashType)); + s += ','; + printUnquotedString(s, "impure"); + }}, + i.second.raw()); s += ')'; } @@ -410,16 +437,26 @@ std::string Derivation::unparse(const Store & store, bool maskOutputs, first = true; if (actualInputs) { for (auto & i : *actualInputs) { - if (first) first = false; else s += ','; - s += '('; printUnquotedString(s, i.first); - s += ','; printUnquotedStrings(s, i.second.begin(), i.second.end()); + if (first) + first = false; + else + s += ','; + s += '('; + printUnquotedString(s, i.first); + s += ','; + printUnquotedStrings(s, i.second.begin(), i.second.end()); s += ')'; } } else { for (auto & i : inputDrvs) { - if (first) first = false; else s += ','; - s += '('; printUnquotedString(s, store.printStorePath(i.first)); - s += ','; printUnquotedStrings(s, i.second.begin(), i.second.end()); + if (first) + first = false; + else + s += ','; + s += '('; + printUnquotedString(s, store.printStorePath(i.first)); + s += ','; + printUnquotedStrings(s, i.second.begin(), i.second.end()); s += ')'; } } @@ -428,16 +465,24 @@ std::string Derivation::unparse(const Store & store, bool maskOutputs, auto paths = store.printStorePathSet(inputSrcs); // FIXME: slow printUnquotedStrings(s, paths.begin(), paths.end()); - s += ','; printUnquotedString(s, platform); - s += ','; printString(s, builder); - s += ','; printStrings(s, args.begin(), args.end()); + s += ','; + printUnquotedString(s, platform); + s += ','; + printString(s, builder); + s += ','; + printStrings(s, args.begin(), args.end()); s += ",["; first = true; for (auto & i : env) { - if (first) first = false; else s += ','; - s += '('; printString(s, i.first); - s += ','; printString(s, maskOutputs && outputs.count(i.first) ? "" : i.second); + if (first) + first = false; + else + s += ','; + s += '('; + printString(s, i.first); + s += ','; + printString(s, maskOutputs && outputs.count(i.first) ? "" : i.second); s += ')'; } @@ -446,16 +491,16 @@ std::string Derivation::unparse(const Store & store, bool maskOutputs, return s; } - // FIXME: remove bool isDerivation(const std::string & fileName) { return hasSuffix(fileName, drvExtension); } - -std::string outputPathName(std::string_view drvName, std::string_view outputName) { - std::string res { drvName }; +std::string outputPathName(std::string_view drvName, + std::string_view outputName) +{ + std::string res{drvName}; if (outputName != "out") { res += "-"; res += outputName; @@ -463,106 +508,89 @@ std::string outputPathName(std::string_view drvName, std::string_view outputName return res; } - DerivationType BasicDerivation::type() const { - std::set - inputAddressedOutputs, - fixedCAOutputs, - floatingCAOutputs, - deferredIAOutputs, - impureOutputs; + std::set inputAddressedOutputs, fixedCAOutputs, + floatingCAOutputs, deferredIAOutputs, impureOutputs; std::optional floatingHashType; for (auto & i : outputs) { - std::visit(overloaded { - [&](const DerivationOutput::InputAddressed &) { - inputAddressedOutputs.insert(i.first); - }, - [&](const DerivationOutput::CAFixed &) { - fixedCAOutputs.insert(i.first); - }, - [&](const DerivationOutput::CAFloating & dof) { - floatingCAOutputs.insert(i.first); - if (!floatingHashType) { - floatingHashType = dof.hashType; - } else { - if (*floatingHashType != dof.hashType) - throw Error("all floating outputs must use the same hash type"); - } - }, - [&](const DerivationOutput::Deferred &) { - deferredIAOutputs.insert(i.first); - }, - [&](const DerivationOutput::Impure &) { - impureOutputs.insert(i.first); - }, - }, i.second.raw()); + std::visit(overloaded{ + [&](const DerivationOutput::InputAddressed &) { + inputAddressedOutputs.insert(i.first); + }, + [&](const DerivationOutput::CAFixed &) { + fixedCAOutputs.insert(i.first); + }, + [&](const DerivationOutput::CAFloating & dof) { + floatingCAOutputs.insert(i.first); + if (!floatingHashType) { + floatingHashType = dof.hashType; + } else { + if (*floatingHashType != dof.hashType) + throw Error("all floating outputs must use " + "the same hash type"); + } + }, + [&](const DerivationOutput::Deferred &) { + deferredIAOutputs.insert(i.first); + }, + [&](const DerivationOutput::Impure &) { + impureOutputs.insert(i.first); + }, + }, + i.second.raw()); } - if (inputAddressedOutputs.empty() - && fixedCAOutputs.empty() - && floatingCAOutputs.empty() - && deferredIAOutputs.empty() - && impureOutputs.empty()) + if (inputAddressedOutputs.empty() && fixedCAOutputs.empty() && + floatingCAOutputs.empty() && deferredIAOutputs.empty() && + impureOutputs.empty()) throw Error("must have at least one output"); - if (!inputAddressedOutputs.empty() - && fixedCAOutputs.empty() - && floatingCAOutputs.empty() - && deferredIAOutputs.empty() - && impureOutputs.empty()) - return DerivationType::InputAddressed { + if (!inputAddressedOutputs.empty() && fixedCAOutputs.empty() && + floatingCAOutputs.empty() && deferredIAOutputs.empty() && + impureOutputs.empty()) + return DerivationType::InputAddressed{ .deferred = false, }; - if (inputAddressedOutputs.empty() - && !fixedCAOutputs.empty() - && floatingCAOutputs.empty() - && deferredIAOutputs.empty() - && impureOutputs.empty()) - { + if (inputAddressedOutputs.empty() && !fixedCAOutputs.empty() && + floatingCAOutputs.empty() && deferredIAOutputs.empty() && + impureOutputs.empty()) { if (fixedCAOutputs.size() > 1) // FIXME: Experimental feature? throw Error("only one fixed output is allowed for now"); if (*fixedCAOutputs.begin() != "out") throw Error("single fixed output must be named \"out\""); - return DerivationType::ContentAddressed { + return DerivationType::ContentAddressed{ .sandboxed = false, .fixed = true, }; } - if (inputAddressedOutputs.empty() - && fixedCAOutputs.empty() - && !floatingCAOutputs.empty() - && deferredIAOutputs.empty() - && impureOutputs.empty()) - return DerivationType::ContentAddressed { + if (inputAddressedOutputs.empty() && fixedCAOutputs.empty() && + !floatingCAOutputs.empty() && deferredIAOutputs.empty() && + impureOutputs.empty()) + return DerivationType::ContentAddressed{ .sandboxed = true, .fixed = false, }; - if (inputAddressedOutputs.empty() - && fixedCAOutputs.empty() - && floatingCAOutputs.empty() - && !deferredIAOutputs.empty() - && impureOutputs.empty()) - return DerivationType::InputAddressed { + if (inputAddressedOutputs.empty() && fixedCAOutputs.empty() && + floatingCAOutputs.empty() && !deferredIAOutputs.empty() && + impureOutputs.empty()) + return DerivationType::InputAddressed{ .deferred = true, }; - if (inputAddressedOutputs.empty() - && fixedCAOutputs.empty() - && floatingCAOutputs.empty() - && deferredIAOutputs.empty() - && !impureOutputs.empty()) - return DerivationType::Impure { }; + if (inputAddressedOutputs.empty() && fixedCAOutputs.empty() && + floatingCAOutputs.empty() && deferredIAOutputs.empty() && + !impureOutputs.empty()) + return DerivationType::Impure{}; throw Error("can't mix derivation output types"); } - Sync drvHashes; /* pathDerivationModulo and hashDerivationModulo are mutually recursive @@ -571,7 +599,8 @@ Sync drvHashes; /* Look up the derivation by value and memoize the `hashDerivationModulo` call. */ -static const DrvHash pathDerivationModulo(Store & store, const StorePath & drvPath) +static const DrvHash pathDerivationModulo(Store & store, + const StorePath & drvPath) { { auto hashes = drvHashes.lock(); @@ -580,10 +609,8 @@ static const DrvHash pathDerivationModulo(Store & store, const StorePath & drvPa return h->second; } } - auto h = hashDerivationModulo( - store, - store.readInvalidDerivation(drvPath), - false); + auto h = hashDerivationModulo(store, store.readInvalidDerivation(drvPath), + false); // Cache it drvHashes.lock()->insert_or_assign(drvPath, h); return h; @@ -606,7 +633,8 @@ static const DrvHash pathDerivationModulo(Store & store, const StorePath & drvPa don't leak the provenance of fixed outputs, reducing pointless cache misses as the build itself won't know this. */ -DrvHash hashDerivationModulo(Store & store, const Derivation & drv, bool maskOutputs) +DrvHash hashDerivationModulo(Store & store, const Derivation & drv, + bool maskOutputs) { auto type = drv.type(); @@ -615,13 +643,14 @@ DrvHash hashDerivationModulo(Store & store, const Derivation & drv, bool maskOut std::map outputHashes; for (const auto & i : drv.outputs) { auto & dof = std::get(i.second.raw()); - auto hash = hashString(htSHA256, "fixed:out:" - + dof.hash.printMethodAlgo() + ":" - + dof.hash.hash.to_string(Base16, false) + ":" - + store.printStorePath(dof.path(store, drv.name, i.first))); + auto hash = hashString( + htSHA256, + "fixed:out:" + dof.hash.printMethodAlgo() + ":" + + dof.hash.hash.to_string(Base16, false) + ":" + + store.printStorePath(dof.path(store, drv.name, i.first))); outputHashes.insert_or_assign(i.first, std::move(hash)); } - return DrvHash { + return DrvHash{ .hashes = outputHashes, .kind = DrvHash::Kind::Regular, }; @@ -631,27 +660,26 @@ DrvHash hashDerivationModulo(Store & store, const Derivation & drv, bool maskOut std::map outputHashes; for (const auto & [outputName, _] : drv.outputs) outputHashes.insert_or_assign(outputName, impureOutputHash); - return DrvHash { + return DrvHash{ .hashes = outputHashes, .kind = DrvHash::Kind::Deferred, }; } - auto kind = std::visit(overloaded { - [](const DerivationType::InputAddressed & ia) { - /* This might be a "pesimistically" deferred output, so we don't - "taint" the kind yet. */ - return DrvHash::Kind::Regular; - }, - [](const DerivationType::ContentAddressed & ca) { - return ca.fixed - ? DrvHash::Kind::Regular - : DrvHash::Kind::Deferred; - }, - [](const DerivationType::Impure &) -> DrvHash::Kind { - assert(false); - } - }, drv.type().raw()); + auto kind = std::visit( + overloaded{[](const DerivationType::InputAddressed & ia) { + /* This might be a "pesimistically" deferred output, so + we don't "taint" the kind yet. */ + return DrvHash::Kind::Regular; + }, + [](const DerivationType::ContentAddressed & ca) { + return ca.fixed ? DrvHash::Kind::Regular + : DrvHash::Kind::Deferred; + }, + [](const DerivationType::Impure &) -> DrvHash::Kind { + assert(false); + }}, + drv.type().raw()); std::map inputs2; for (auto & [drvPath, inputOutputs0] : drv.inputDrvs) { @@ -663,7 +691,8 @@ DrvHash hashDerivationModulo(Store & store, const Derivation & drv, bool maskOut for (auto & outputName : inputOutputs) { const auto h = get(res.hashes, outputName); if (!h) - throw Error("no hash for output '%s' of derivation '%s'", outputName, drv.name); + throw Error("no hash for output '%s' of derivation '%s'", + outputName, drv.name); inputs2[h->to_string(Base16, false)].insert(outputName); } } @@ -675,25 +704,24 @@ DrvHash hashDerivationModulo(Store & store, const Derivation & drv, bool maskOut outputHashes.insert_or_assign(outputName, hash); } - return DrvHash { + return DrvHash{ .hashes = outputHashes, .kind = kind, }; } - -std::map staticOutputHashes(Store & store, const Derivation & drv) +std::map staticOutputHashes(Store & store, + const Derivation & drv) { return hashDerivationModulo(store, drv, true).hashes; } - -bool wantOutput(const std::string & output, const std::set & wanted) +bool wantOutput(const std::string & output, + const std::set & wanted) { return wanted.empty() || wanted.find(output) != wanted.end(); } - static DerivationOutput readDerivationOutput(Source & in, const Store & store) { const auto pathS = readString(in); @@ -711,15 +739,15 @@ StringSet BasicDerivation::outputNames() const return names; } -DerivationOutputsAndOptPaths BasicDerivation::outputsAndOptPaths(const Store & store) const +DerivationOutputsAndOptPaths +BasicDerivation::outputsAndOptPaths(const Store & store) const { DerivationOutputsAndOptPaths outsAndOptPaths; for (auto output : outputs) outsAndOptPaths.insert(std::make_pair( output.first, - std::make_pair(output.second, output.second.path(store, name, output.first)) - ) - ); + std::make_pair(output.second, + output.second.path(store, name, output.first)))); return outsAndOptPaths; } @@ -732,8 +760,8 @@ std::string_view BasicDerivation::nameFromPath(const StorePath & drvPath) return nameWithSuffix; } - -Source & readDerivation(Source & in, const Store & store, BasicDerivation & drv, std::string_view name) +Source & readDerivation(Source & in, const Store & store, BasicDerivation & drv, + std::string_view name) { drv.name = name; @@ -745,7 +773,7 @@ Source & readDerivation(Source & in, const Store & store, BasicDerivation & drv, drv.outputs.emplace(std::move(name), std::move(output)); } - drv.inputSrcs = worker_proto::read(store, in, Phantom {}); + drv.inputSrcs = worker_proto::read(store, in, Phantom{}); in >> drv.platform >> drv.builder; drv.args = readStrings(in); @@ -759,39 +787,42 @@ Source & readDerivation(Source & in, const Store & store, BasicDerivation & drv, return in; } - -void writeDerivation(Sink & out, const Store & store, const BasicDerivation & drv) +void writeDerivation(Sink & out, const Store & store, + const BasicDerivation & drv) { out << drv.outputs.size(); for (auto & i : drv.outputs) { out << i.first; - std::visit(overloaded { - [&](const DerivationOutput::InputAddressed & doi) { - out << store.printStorePath(doi.path) - << "" - << ""; - }, - [&](const DerivationOutput::CAFixed & dof) { - out << store.printStorePath(dof.path(store, drv.name, i.first)) - << dof.hash.printMethodAlgo() - << dof.hash.hash.to_string(Base16, false); - }, - [&](const DerivationOutput::CAFloating & dof) { - out << "" - << (makeFileIngestionPrefix(dof.method) + printHashType(dof.hashType)) - << ""; - }, - [&](const DerivationOutput::Deferred &) { - out << "" - << "" - << ""; - }, - [&](const DerivationOutput::Impure & doi) { - out << "" - << (makeFileIngestionPrefix(doi.method) + printHashType(doi.hashType)) - << "impure"; - }, - }, i.second.raw()); + std::visit(overloaded{ + [&](const DerivationOutput::InputAddressed & doi) { + out << store.printStorePath(doi.path) << "" + << ""; + }, + [&](const DerivationOutput::CAFixed & dof) { + out << store.printStorePath( + dof.path(store, drv.name, i.first)) + << dof.hash.printMethodAlgo() + << dof.hash.hash.to_string(Base16, false); + }, + [&](const DerivationOutput::CAFloating & dof) { + out << "" + << (makeFileIngestionPrefix(dof.method) + + printHashType(dof.hashType)) + << ""; + }, + [&](const DerivationOutput::Deferred &) { + out << "" + << "" + << ""; + }, + [&](const DerivationOutput::Impure & doi) { + out << "" + << (makeFileIngestionPrefix(doi.method) + + printHashType(doi.hashType)) + << "impure"; + }, + }, + i.second.raw()); } worker_proto::write(store, out, drv.inputSrcs); out << drv.platform << drv.builder << drv.args; @@ -800,23 +831,27 @@ void writeDerivation(Sink & out, const Store & store, const BasicDerivation & dr out << i.first << i.second; } - std::string hashPlaceholder(const std::string_view outputName) { // FIXME: memoize? - return "/" + hashString(htSHA256, concatStrings("nix-output:", outputName)).to_string(Base32, false); + return "/" + hashString(htSHA256, concatStrings("nix-output:", outputName)) + .to_string(Base32, false); } -std::string downstreamPlaceholder(const Store & store, const StorePath & drvPath, std::string_view outputName) +std::string downstreamPlaceholder(const Store & store, + const StorePath & drvPath, + std::string_view outputName) { auto drvNameWithExtension = drvPath.name(); - auto drvName = drvNameWithExtension.substr(0, drvNameWithExtension.size() - 4); - auto clearText = "nix-upstream-output:" + std::string { drvPath.hashPart() } + ":" + outputPathName(drvName, outputName); + auto drvName = + drvNameWithExtension.substr(0, drvNameWithExtension.size() - 4); + auto clearText = "nix-upstream-output:" + std::string{drvPath.hashPart()} + + ":" + outputPathName(drvName, outputName); return "/" + hashString(htSHA256, clearText).to_string(Base32, false); } - -static void rewriteDerivation(Store & store, BasicDerivation & drv, const StringMap & rewrites) +static void rewriteDerivation(Store & store, BasicDerivation & drv, + const StringMap & rewrites) { for (auto & rewrite : rewrites) { debug("rewriting %s as %s", rewrite.first, rewrite.second); @@ -840,16 +875,16 @@ static void rewriteDerivation(Store & store, BasicDerivation & drv, const String if (std::holds_alternative(output.raw())) { auto h = get(hashModulo.hashes, outputName); if (!h) - throw Error("derivation '%s' output '%s' has no hash (derivations.cc/rewriteDerivation)", - drv.name, outputName); + throw Error("derivation '%s' output '%s' has no hash " + "(derivations.cc/rewriteDerivation)", + drv.name, outputName); auto outPath = store.makeOutputPath(outputName, *h, drv.name); drv.env[outputName] = store.printStorePath(outPath); - output = DerivationOutput::InputAddressed { + output = DerivationOutput::InputAddressed{ .path = std::move(outPath), }; } } - } std::optional Derivation::tryResolve(Store & store) const @@ -857,33 +892,37 @@ std::optional Derivation::tryResolve(Store & store) const std::map, StorePath> inputDrvOutputs; for (auto & input : inputDrvs) - for (auto & [outputName, outputPath] : store.queryPartialDerivationOutputMap(input.first)) + for (auto & [outputName, outputPath] : + store.queryPartialDerivationOutputMap(input.first)) if (outputPath) - inputDrvOutputs.insert_or_assign({input.first, outputName}, *outputPath); + inputDrvOutputs.insert_or_assign({input.first, outputName}, + *outputPath); return tryResolve(store, inputDrvOutputs); } -std::optional Derivation::tryResolve( - Store & store, - const std::map, StorePath> & inputDrvOutputs) const +std::optional +Derivation::tryResolve(Store & store, + const std::map, + StorePath> & inputDrvOutputs) const { - BasicDerivation resolved { *this }; + BasicDerivation resolved{*this}; // Input paths that we'll want to rewrite in the derivation StringMap inputRewrites; for (auto & [inputDrv, inputOutputs] : inputDrvs) { for (auto & outputName : inputOutputs) { - if (auto actualPath = get(inputDrvOutputs, { inputDrv, outputName })) { + if (auto actualPath = + get(inputDrvOutputs, {inputDrv, outputName})) { inputRewrites.emplace( downstreamPlaceholder(store, inputDrv, outputName), store.printStorePath(*actualPath)); resolved.inputSrcs.insert(*actualPath); } else { - warn("output '%s' of input '%s' missing, aborting the resolving", - outputName, - store.printStorePath(inputDrv)); + warn( + "output '%s' of input '%s' missing, aborting the resolving", + outputName, store.printStorePath(inputDrv)); return {}; } } diff --git a/src/libstore/derivations.hh b/src/libstore/derivations.hh index af198a76732f..babaf7569026 100644 --- a/src/libstore/derivations.hh +++ b/src/libstore/derivations.hh @@ -10,31 +10,27 @@ #include #include - namespace nix { - /* Abstract syntax of derivations. */ /* The traditional non-fixed-output derivation type. */ -struct DerivationOutputInputAddressed -{ +struct DerivationOutputInputAddressed { StorePath path; }; /* Fixed-output derivations, whose output paths are content addressed according to that fixed output. */ -struct DerivationOutputCAFixed -{ +struct DerivationOutputCAFixed { FixedOutputHash hash; /* hash used for expected hash computation */ - StorePath path(const Store & store, std::string_view drvName, std::string_view outputName) const; + StorePath path(const Store & store, std::string_view drvName, + std::string_view outputName) const; }; /* Floating-output derivations, whose output paths are content addressed, but not fixed, and so are dynamically calculated from whatever the output ends up being. */ -struct DerivationOutputCAFloating -{ +struct DerivationOutputCAFloating { /* information used for expected hash computation */ FileIngestionMethod method; HashType hashType; @@ -43,28 +39,24 @@ struct DerivationOutputCAFloating /* Input-addressed output which depends on a (CA) derivation whose hash isn't * known yet. */ -struct DerivationOutputDeferred {}; +struct DerivationOutputDeferred { +}; /* Impure output which is moved to a content-addressed location (like CAFloating) but isn't registered as a realization. */ -struct DerivationOutputImpure -{ +struct DerivationOutputImpure { /* information used for expected hash computation */ FileIngestionMethod method; HashType hashType; }; -typedef std::variant< - DerivationOutputInputAddressed, - DerivationOutputCAFixed, - DerivationOutputCAFloating, - DerivationOutputDeferred, - DerivationOutputImpure -> _DerivationOutputRaw; +typedef std::variant + _DerivationOutputRaw; -struct DerivationOutput : _DerivationOutputRaw -{ +struct DerivationOutput : _DerivationOutputRaw { using Raw = _DerivationOutputRaw; using Raw::Raw; @@ -77,11 +69,10 @@ struct DerivationOutput : _DerivationOutputRaw /* Note, when you use this function you should make sure that you're passing the right derivation name. When in doubt, you should use the safer interface provided by BasicDerivation::outputsAndOptPaths */ - std::optional path(const Store & store, std::string_view drvName, std::string_view outputName) const; + std::optional path(const Store & store, std::string_view drvName, + std::string_view outputName) const; - inline const Raw & raw() const { - return static_cast(*this); - } + inline const Raw & raw() const { return static_cast(*this); } }; typedef std::map DerivationOutputs; @@ -90,8 +81,9 @@ typedef std::map DerivationOutputs; also contains, for each output, the (optional) store path in which it would be written. To calculate values of these types, see the corresponding functions in BasicDerivation */ -typedef std::map>> - DerivationOutputsAndOptPaths; +typedef std::map>> + DerivationOutputsAndOptPaths; /* For inputs that are sub-derivations, we specify exactly which output IDs we are interested in. */ @@ -109,11 +101,9 @@ struct DerivationType_ContentAddressed { struct DerivationType_Impure { }; -typedef std::variant< - DerivationType_InputAddressed, - DerivationType_ContentAddressed, - DerivationType_Impure -> _DerivationTypeRaw; +typedef std::variant + _DerivationTypeRaw; struct DerivationType : _DerivationTypeRaw { using Raw = _DerivationTypeRaw; @@ -122,8 +112,8 @@ struct DerivationType : _DerivationTypeRaw { using ContentAddressed = DerivationType_ContentAddressed; using Impure = DerivationType_Impure; - /* Do the outputs of the derivation have paths calculated from their content, - or from the derivation itself? */ + /* Do the outputs of the derivation have paths calculated from their + content, or from the derivation itself? */ bool isCA() const; /* Is the content of the outputs fixed a-priori via a hash? Never true for @@ -149,15 +139,12 @@ struct DerivationType : _DerivationTypeRaw { */ bool hasKnownOutputPaths() const; - inline const Raw & raw() const { - return static_cast(*this); - } + inline const Raw & raw() const { return static_cast(*this); } }; -struct BasicDerivation -{ +struct BasicDerivation { DerivationOutputs outputs; /* keyed on symbolic IDs */ - StorePathSet inputSrcs; /* inputs that are sources */ + StorePathSet inputSrcs; /* inputs that are sources */ std::string platform; Path builder; Strings args; @@ -165,7 +152,7 @@ struct BasicDerivation std::string name; BasicDerivation() = default; - virtual ~BasicDerivation() { }; + virtual ~BasicDerivation(){}; bool isBuiltin() const; @@ -183,13 +170,13 @@ struct BasicDerivation static std::string_view nameFromPath(const StorePath & storePath); }; -struct Derivation : BasicDerivation -{ +struct Derivation : BasicDerivation { DerivationInputs inputDrvs; /* inputs that are sub-derivations */ /* Print a derivation. */ - std::string unparse(const Store & store, bool maskOutputs, - std::map * actualInputs = nullptr) const; + std::string + unparse(const Store & store, bool maskOutputs, + std::map * actualInputs = nullptr) const; /* Return the underlying basic derivation but with these changes: @@ -202,26 +189,25 @@ struct Derivation : BasicDerivation /* Like the above, but instead of querying the Nix database for realisations, uses a given mapping from input derivation paths + output names to actual output store paths. */ - std::optional tryResolve( - Store & store, - const std::map, StorePath> & inputDrvOutputs) const; + std::optional + tryResolve(Store & store, + const std::map, StorePath> & + inputDrvOutputs) const; Derivation() = default; - Derivation(const BasicDerivation & bd) : BasicDerivation(bd) { } - Derivation(BasicDerivation && bd) : BasicDerivation(std::move(bd)) { } + Derivation(const BasicDerivation & bd) : BasicDerivation(bd) {} + Derivation(BasicDerivation && bd) : BasicDerivation(std::move(bd)) {} }; - class Store; /* Write a derivation to the Nix store, and return its path. */ -StorePath writeDerivation(Store & store, - const Derivation & drv, - RepairFlag repair = NoRepair, - bool readOnly = false); +StorePath writeDerivation(Store & store, const Derivation & drv, + RepairFlag repair = NoRepair, bool readOnly = false); /* Read a derivation from a file. */ -Derivation parseDerivation(const Store & store, std::string && s, std::string_view name); +Derivation parseDerivation(const Store & store, std::string && s, + std::string_view name); // FIXME: remove bool isDerivation(const std::string & fileName); @@ -231,8 +217,8 @@ bool isDerivation(const std::string & fileName); This is usually -, but is just when the output name is "out". */ -std::string outputPathName(std::string_view drvName, std::string_view outputName); - +std::string outputPathName(std::string_view drvName, + std::string_view outputName); // The hashes modulo of a derivation. // @@ -253,7 +239,7 @@ struct DrvHash { Kind kind; }; -void operator |= (DrvHash::Kind & self, const DrvHash::Kind & other) noexcept; +void operator|=(DrvHash::Kind & self, const DrvHash::Kind & other) noexcept; /* Returns hashes with the details of fixed-output subderivations expunged. @@ -278,7 +264,8 @@ void operator |= (DrvHash::Kind & self, const DrvHash::Kind & other) noexcept; ATerm, after subderivations have been likewise expunged from that derivation. */ -DrvHash hashDerivationModulo(Store & store, const Derivation & drv, bool maskOutputs); +DrvHash hashDerivationModulo(Store & store, const Derivation & drv, + bool maskOutputs); /* Return a map associating each output to a hash that uniquely identifies its @@ -286,7 +273,8 @@ DrvHash hashDerivationModulo(Store & store, const Derivation & drv, bool maskOut FIXME: what is the Hash in this map? */ -std::map staticOutputHashes(Store & store, const Derivation & drv); +std::map staticOutputHashes(Store & store, + const Derivation & drv); /* Memoisation of hashDerivationModulo(). */ typedef std::map DrvHashes; @@ -294,13 +282,16 @@ typedef std::map DrvHashes; // FIXME: global, though at least thread-safe. extern Sync drvHashes; -bool wantOutput(const std::string & output, const std::set & wanted); +bool wantOutput(const std::string & output, + const std::set & wanted); struct Source; struct Sink; -Source & readDerivation(Source & in, const Store & store, BasicDerivation & drv, std::string_view name); -void writeDerivation(Sink & out, const Store & store, const BasicDerivation & drv); +Source & readDerivation(Source & in, const Store & store, BasicDerivation & drv, + std::string_view name); +void writeDerivation(Sink & out, const Store & store, + const BasicDerivation & drv); /* This creates an opaque and almost certainly unique string deterministically from the output name. @@ -317,7 +308,9 @@ std::string hashPlaceholder(const std::string_view outputName); content-addressed paths whose content --- and thus the path themselves --- isn't yet known. This occurs when a derivation has a dependency which is a CA derivation. */ -std::string downstreamPlaceholder(const Store & store, const StorePath & drvPath, std::string_view outputName); +std::string downstreamPlaceholder(const Store & store, + const StorePath & drvPath, + std::string_view outputName); extern const Hash impureOutputHash; diff --git a/src/libstore/derived-path.cc b/src/libstore/derived-path.cc index 44587ae789f3..76d6df36bc73 100644 --- a/src/libstore/derived-path.cc +++ b/src/libstore/derived-path.cc @@ -8,31 +8,34 @@ namespace nix { -nlohmann::json DerivedPath::Opaque::toJSON(ref store) const { +nlohmann::json DerivedPath::Opaque::toJSON(ref store) const +{ nlohmann::json res; res["path"] = store->printStorePath(path); return res; } -nlohmann::json DerivedPath::Built::toJSON(ref store) const { +nlohmann::json DerivedPath::Built::toJSON(ref store) const +{ nlohmann::json res; res["drvPath"] = store->printStorePath(drvPath); // Fallback for the input-addressed derivation case: We expect to always be // able to print the output paths, so let’s do it const auto knownOutputs = store->queryPartialDerivationOutputMap(drvPath); - for (const auto& output : outputs) { + for (const auto & output : outputs) { auto knownOutput = get(knownOutputs, output); res["outputs"][output] = (knownOutput && *knownOutput) - ? store->printStorePath(**knownOutput) - : nullptr; + ? store->printStorePath(**knownOutput) + : nullptr; } return res; } -nlohmann::json BuiltPath::Built::toJSON(ref store) const { +nlohmann::json BuiltPath::Built::toJSON(ref store) const +{ nlohmann::json res; res["drvPath"] = store->printStorePath(drvPath); - for (const auto& [output, path] : outputs) { + for (const auto & [output, path] : outputs) { res["outputs"][output] = store->printStorePath(path); } return res; @@ -49,51 +52,58 @@ StorePathSet BuiltPath::outPaths() const res.insert(path); return res; }, - }, raw() - ); + }, + raw()); } template -nlohmann::json stuffToJSON(const std::vector & ts, ref store) { +nlohmann::json stuffToJSON(const std::vector & ts, ref store) +{ auto res = nlohmann::json::array(); for (const T & t : ts) { - std::visit([&res, store](const auto & t) { - res.push_back(t.toJSON(store)); - }, t.raw()); + std::visit( + [&res, store](const auto & t) { res.push_back(t.toJSON(store)); }, + t.raw()); } return res; } -nlohmann::json derivedPathsWithHintsToJSON(const BuiltPaths & buildables, ref store) -{ return stuffToJSON(buildables, store); } +nlohmann::json derivedPathsWithHintsToJSON(const BuiltPaths & buildables, + ref store) +{ + return stuffToJSON(buildables, store); +} nlohmann::json derivedPathsToJSON(const DerivedPaths & paths, ref store) -{ return stuffToJSON(paths, store); } - +{ + return stuffToJSON(paths, store); +} -std::string DerivedPath::Opaque::to_string(const Store & store) const { +std::string DerivedPath::Opaque::to_string(const Store & store) const +{ return store.printStorePath(path); } -std::string DerivedPath::Built::to_string(const Store & store) const { - return store.printStorePath(drvPath) - + "!" - + (outputs.empty() ? std::string { "*" } : concatStringsSep(",", outputs)); +std::string DerivedPath::Built::to_string(const Store & store) const +{ + return store.printStorePath(drvPath) + "!" + + (outputs.empty() ? std::string{"*"} + : concatStringsSep(",", outputs)); } std::string DerivedPath::to_string(const Store & store) const { - return std::visit( - [&](const auto & req) { return req.to_string(store); }, - this->raw()); + return std::visit([&](const auto & req) { return req.to_string(store); }, + this->raw()); } - -DerivedPath::Opaque DerivedPath::Opaque::parse(const Store & store, std::string_view s) +DerivedPath::Opaque DerivedPath::Opaque::parse(const Store & store, + std::string_view s) { return {store.parseStorePath(s)}; } -DerivedPath::Built DerivedPath::Built::parse(const Store & store, std::string_view s) +DerivedPath::Built DerivedPath::Built::parse(const Store & store, + std::string_view s) { size_t n = s.find("!"); assert(n != s.npos); @@ -108,9 +118,8 @@ DerivedPath::Built DerivedPath::Built::parse(const Store & store, std::string_vi DerivedPath DerivedPath::parse(const Store & store, std::string_view s) { size_t n = s.find("!"); - return n == s.npos - ? (DerivedPath) DerivedPath::Opaque::parse(store, s) - : (DerivedPath) DerivedPath::Built::parse(store, s); + return n == s.npos ? (DerivedPath) DerivedPath::Opaque::parse(store, s) + : (DerivedPath) DerivedPath::Built::parse(store, s); } RealisedPath::Set BuiltPath::toRealisedPaths(Store & store) const @@ -122,18 +131,19 @@ RealisedPath::Set BuiltPath::toRealisedPaths(Store & store) const [&](const BuiltPath::Built & p) { auto drvHashes = staticOutputHashes(store, store.readDerivation(p.drvPath)); - for (auto& [outputName, outputPath] : p.outputs) { + for (auto & [outputName, outputPath] : p.outputs) { if (settings.isExperimentalFeatureEnabled( - Xp::CaDerivations)) { + Xp::CaDerivations)) { auto drvOutput = get(drvHashes, outputName); if (!drvOutput) throw Error( - "the derivation '%s' has unrealised output '%s' (derived-path.cc/toRealisedPaths)", + "the derivation '%s' has unrealised output " + "'%s' (derived-path.cc/toRealisedPaths)", store.printStorePath(p.drvPath), outputName); auto thisRealisation = store.queryRealisation( DrvOutput{*drvOutput, outputName}); - assert(thisRealisation); // We’ve built it, so we must - // have the realisation + assert(thisRealisation); // We’ve built it, so we must + // have the realisation res.insert(*thisRealisation); } else { res.insert(outputPath); diff --git a/src/libstore/derived-path.hh b/src/libstore/derived-path.hh index 24a0ae773592..bc86e04b300d 100644 --- a/src/libstore/derived-path.hh +++ b/src/libstore/derived-path.hh @@ -26,8 +26,7 @@ struct DerivedPathOpaque { std::string to_string(const Store & store) const; static DerivedPathOpaque parse(const Store & store, std::string_view); - bool operator < (const DerivedPathOpaque & b) const - { return path < b.path; } + bool operator<(const DerivedPathOpaque & b) const { return path < b.path; } }; /** @@ -50,14 +49,14 @@ struct DerivedPathBuilt { static DerivedPathBuilt parse(const Store & store, std::string_view); nlohmann::json toJSON(ref store) const; - bool operator < (const DerivedPathBuilt & b) const - { return std::make_pair(drvPath, outputs) < std::make_pair(b.drvPath, b.outputs); } + bool operator<(const DerivedPathBuilt & b) const + { + return std::make_pair(drvPath, outputs) < + std::make_pair(b.drvPath, b.outputs); + } }; -using _DerivedPathRaw = std::variant< - DerivedPathOpaque, - DerivedPathBuilt ->; +using _DerivedPathRaw = std::variant; /** * A "derived path" is a very simple sort of expression that evaluates @@ -76,16 +75,15 @@ struct DerivedPath : _DerivedPathRaw { using Opaque = DerivedPathOpaque; using Built = DerivedPathBuilt; - inline const Raw & raw() const { - return static_cast(*this); - } + inline const Raw & raw() const { return static_cast(*this); } std::string to_string(const Store & store) const; static DerivedPath parse(const Store & store, std::string_view); }; /** - * A built derived path with hints in the form of optional concrete output paths. + * A built derived path with hints in the form of optional concrete output + * paths. * * See 'BuiltPath' for more an explanation. */ @@ -97,10 +95,7 @@ struct BuiltPathBuilt { static BuiltPathBuilt parse(const Store & store, std::string_view); }; -using _BuiltPathRaw = std::variant< - DerivedPath::Opaque, - BuiltPathBuilt ->; +using _BuiltPathRaw = std::variant; /** * A built path. Similar to a `DerivedPath`, but enriched with the corresponding @@ -113,19 +108,17 @@ struct BuiltPath : _BuiltPathRaw { using Opaque = DerivedPathOpaque; using Built = BuiltPathBuilt; - inline const Raw & raw() const { - return static_cast(*this); - } + inline const Raw & raw() const { return static_cast(*this); } StorePathSet outPaths() const; RealisedPath::Set toRealisedPaths(Store & store) const; - }; typedef std::vector DerivedPaths; typedef std::vector BuiltPaths; -nlohmann::json derivedPathsWithHintsToJSON(const BuiltPaths & buildables, ref store); -nlohmann::json derivedPathsToJSON(const DerivedPaths & , ref store); +nlohmann::json derivedPathsWithHintsToJSON(const BuiltPaths & buildables, + ref store); +nlohmann::json derivedPathsToJSON(const DerivedPaths &, ref store); } diff --git a/src/libstore/dummy-store.cc b/src/libstore/dummy-store.cc index b4fbe0b702ca..75850be0b2f8 100644 --- a/src/libstore/dummy-store.cc +++ b/src/libstore/dummy-store.cc @@ -9,53 +9,59 @@ struct DummyStoreConfig : virtual StoreConfig { const std::string name() override { return "Dummy Store"; } }; -struct DummyStore : public virtual DummyStoreConfig, public virtual Store -{ - DummyStore(const std::string scheme, const std::string uri, const Params & params) +struct DummyStore : public virtual DummyStoreConfig, public virtual Store { + DummyStore(const std::string scheme, const std::string uri, + const Params & params) : DummyStore(params) - { } + { + } DummyStore(const Params & params) - : StoreConfig(params) - , DummyStoreConfig(params) - , Store(params) - { } - - std::string getUri() override + : StoreConfig(params), DummyStoreConfig(params), Store(params) { - return *uriSchemes().begin(); } + std::string getUri() override { return *uriSchemes().begin(); } + void queryPathInfoUncached(const StorePath & path, - Callback> callback) noexcept override + Callback> + callback) noexcept override { callback(nullptr); } - static std::set uriSchemes() { - return {"dummy"}; - } + static std::set uriSchemes() { return {"dummy"}; } - std::optional queryPathFromHashPart(const std::string & hashPart) override - { unsupported("queryPathFromHashPart"); } + std::optional + queryPathFromHashPart(const std::string & hashPart) override + { + unsupported("queryPathFromHashPart"); + } void addToStore(const ValidPathInfo & info, Source & source, - RepairFlag repair, CheckSigsFlag checkSigs) override - { unsupported("addToStore"); } + RepairFlag repair, CheckSigsFlag checkSigs) override + { + unsupported("addToStore"); + } - StorePath addTextToStore( - std::string_view name, - std::string_view s, - const StorePathSet & references, - RepairFlag repair) override - { unsupported("addTextToStore"); } + StorePath addTextToStore(std::string_view name, std::string_view s, + const StorePathSet & references, + RepairFlag repair) override + { + unsupported("addTextToStore"); + } void narFromPath(const StorePath & path, Sink & sink) override - { unsupported("narFromPath"); } + { + unsupported("narFromPath"); + } - void queryRealisationUncached(const DrvOutput &, + void queryRealisationUncached( + const DrvOutput &, Callback> callback) noexcept override - { callback(nullptr); } + { + callback(nullptr); + } }; static RegisterStoreImplementation regDummyStore; diff --git a/src/libstore/export-import.cc b/src/libstore/export-import.cc index 9875da909066..8f6b82a7316d 100644 --- a/src/libstore/export-import.cc +++ b/src/libstore/export-import.cc @@ -13,13 +13,13 @@ void Store::exportPaths(const StorePathSet & paths, Sink & sink) std::reverse(sorted.begin(), sorted.end()); std::string doneLabel("paths exported"); - //logger->incExpected(doneLabel, sorted.size()); + // logger->incExpected(doneLabel, sorted.size()); for (auto & path : sorted) { - //Activity act(*logger, lvlInfo, format("exporting path '%s'") % path); + // Activity act(*logger, lvlInfo, format("exporting path '%s'") % path); sink << 1; exportPath(path, sink); - //logger->incProgress(doneLabel); + // logger->incProgress(doneLabel); } sink << 0; @@ -40,15 +40,12 @@ void Store::exportPath(const StorePath & path, Sink & sink) Hash hash = hashSink.currentHash().first; if (hash != info->narHash && info->narHash != Hash(info->narHash.type)) throw Error("hash of path '%s' has changed from '%s' to '%s'!", - printStorePath(path), info->narHash.to_string(Base32, true), hash.to_string(Base32, true)); + printStorePath(path), info->narHash.to_string(Base32, true), + hash.to_string(Base32, true)); - teeSink - << exportMagic - << printStorePath(path); + teeSink << exportMagic << printStorePath(path); worker_proto::write(*this, teeSink, info->references); - teeSink - << (info->deriver ? printStorePath(*info->deriver) : "") - << 0; + teeSink << (info->deriver ? printStorePath(*info->deriver) : "") << 0; } StorePaths Store::importPaths(Source & source, CheckSigsFlag checkSigs) @@ -56,12 +53,15 @@ StorePaths Store::importPaths(Source & source, CheckSigsFlag checkSigs) StorePaths res; while (true) { auto n = readNum(source); - if (n == 0) break; - if (n != 1) throw Error("input doesn't look like something created by 'nix-store --export'"); + if (n == 0) + break; + if (n != 1) + throw Error("input doesn't look like something created by " + "'nix-store --export'"); /* Extract the NAR from the source. */ StringSink saved; - TeeSource tee { source, saved }; + TeeSource tee{source, saved}; ParseSink ether; parseDump(ether, tee); @@ -71,13 +71,15 @@ StorePaths Store::importPaths(Source & source, CheckSigsFlag checkSigs) auto path = parseStorePath(readString(source)); - //Activity act(*logger, lvlInfo, format("importing path '%s'") % info.path); + // Activity act(*logger, lvlInfo, format("importing path '%s'") % + // info.path); - auto references = worker_proto::read(*this, source, Phantom {}); + auto references = + worker_proto::read(*this, source, Phantom{}); auto deriver = readString(source); auto narHash = hashString(htSHA256, saved.s); - ValidPathInfo info { path, narHash }; + ValidPathInfo info{path, narHash}; if (deriver != "") info.deriver = parseStorePath(deriver); info.references = references; diff --git a/src/libstore/filetransfer.cc b/src/libstore/filetransfer.cc index 8454ad7d2a4f..e457e35f8126 100644 --- a/src/libstore/filetransfer.cc +++ b/src/libstore/filetransfer.cc @@ -36,28 +36,29 @@ static GlobalConfig::Register rFileTransferSettings(&fileTransferSettings); std::string resolveUri(std::string_view uri) { if (uri.compare(0, 8, "channel:") == 0) - return "https://nixos.org/channels/" + std::string(uri.substr(8)) + "/nixexprs.tar.xz"; + return "https://nixos.org/channels/" + std::string(uri.substr(8)) + + "/nixexprs.tar.xz"; else return std::string(uri); } -struct curlFileTransfer : public FileTransfer -{ +struct curlFileTransfer : public FileTransfer { CURLM * curlm = 0; std::random_device rd; std::mt19937 mt19937; - struct TransferItem : public std::enable_shared_from_this - { + struct TransferItem : public std::enable_shared_from_this { curlFileTransfer & fileTransfer; FileTransferRequest request; FileTransferResult result; Activity act; - bool done = false; // whether either the success or failure function has been called + bool done = false; // whether either the success or failure function has + // been called Callback callback; CURL * req = 0; - bool active = false; // whether the handle has been added to the multi object + bool active = + false; // whether the handle has been added to the multi object std::string statusMsg; unsigned int attempt = 0; @@ -74,7 +75,8 @@ struct curlFileTransfer : public FileTransfer curl_off_t writtenToSink = 0; - inline static const std::set successfulStatuses {200, 201, 204, 206, 304, 0 /* other protocol */}; + inline static const std::set successfulStatuses{ + 200, 201, 204, 206, 304, 0 /* other protocol */}; /* Get the HTTP status code, or 0 for other protocols. */ long getHTTPStatus() { @@ -87,34 +89,41 @@ struct curlFileTransfer : public FileTransfer } TransferItem(curlFileTransfer & fileTransfer, - const FileTransferRequest & request, - Callback && callback) - : fileTransfer(fileTransfer) - , request(request) - , act(*logger, lvlTalkative, actFileTransfer, - fmt(request.data ? "uploading '%s'" : "downloading '%s'", request.uri), - {request.uri}, request.parentAct) - , callback(std::move(callback)) - , finalSink([this](std::string_view data) { - if (this->request.dataCallback) { - auto httpStatus = getHTTPStatus(); - - /* Only write data to the sink if this is a - successful response. */ - if (successfulStatuses.count(httpStatus)) { - writtenToSink += data.size(); - this->request.dataCallback(data); - } - } else - this->result.data.append(data); + const FileTransferRequest & request, + Callback && callback) + : fileTransfer(fileTransfer), request(request), + act(*logger, lvlTalkative, actFileTransfer, + fmt(request.data ? "uploading '%s'" : "downloading '%s'", + request.uri), + {request.uri}, request.parentAct), + callback(std::move(callback)), + finalSink([this](std::string_view data) { + if (this->request.dataCallback) { + auto httpStatus = getHTTPStatus(); + + /* Only write data to the sink if this is a + successful response. */ + if (successfulStatuses.count(httpStatus)) { + writtenToSink += data.size(); + this->request.dataCallback(data); + } + } else + this->result.data.append(data); }) { if (!request.expectedETag.empty()) - requestHeaders = curl_slist_append(requestHeaders, ("If-None-Match: " + request.expectedETag).c_str()); + requestHeaders = curl_slist_append( + requestHeaders, + ("If-None-Match: " + request.expectedETag).c_str()); if (!request.mimeType.empty()) - requestHeaders = curl_slist_append(requestHeaders, ("Content-Type: " + request.mimeType).c_str()); - for (auto it = request.headers.begin(); it != request.headers.end(); ++it){ - requestHeaders = curl_slist_append(requestHeaders, fmt("%s: %s", it->first, it->second).c_str()); + requestHeaders = curl_slist_append( + requestHeaders, + ("Content-Type: " + request.mimeType).c_str()); + for (auto it = request.headers.begin(); it != request.headers.end(); + ++it) { + requestHeaders = curl_slist_append( + requestHeaders, + fmt("%s: %s", it->first, it->second).c_str()); } } @@ -125,10 +134,13 @@ struct curlFileTransfer : public FileTransfer curl_multi_remove_handle(fileTransfer.curlm, req); curl_easy_cleanup(req); } - if (requestHeaders) curl_slist_free_all(requestHeaders); + if (requestHeaders) + curl_slist_free_all(requestHeaders); try { if (!done) - fail(FileTransferError(Interrupted, {}, "download of '%s' was interrupted", request.uri)); + fail(FileTransferError(Interrupted, {}, + "download of '%s' was interrupted", + request.uri)); } catch (...) { ignoreException(); } @@ -141,8 +153,7 @@ struct curlFileTransfer : public FileTransfer callback.rethrow(ex); } - template - void fail(const T & e) + template void fail(const T & e) { failEx(std::make_exception_ptr(e)); } @@ -160,13 +171,14 @@ struct curlFileTransfer : public FileTransfer result.bodySize += realSize; if (!decompressionSink) { - decompressionSink = makeDecompressionSink(encoding, finalSink); - if (! successfulStatuses.count(getHTTPStatus())) { + decompressionSink = + makeDecompressionSink(encoding, finalSink); + if (!successfulStatuses.count(getHTTPStatus())) { // In this case we want to construct a TeeSink, to keep // the response around (which we figure won't be big // like an actual download should be) to improve error // messages. - errorSink = StringSink { }; + errorSink = StringSink{}; } } @@ -181,17 +193,22 @@ struct curlFileTransfer : public FileTransfer } } - static size_t writeCallbackWrapper(void * contents, size_t size, size_t nmemb, void * userp) + static size_t writeCallbackWrapper(void * contents, size_t size, + size_t nmemb, void * userp) { - return ((TransferItem *) userp)->writeCallback(contents, size, nmemb); + return ((TransferItem *) userp) + ->writeCallback(contents, size, nmemb); } size_t headerCallback(void * contents, size_t size, size_t nmemb) { size_t realSize = size * nmemb; std::string line((char *) contents, realSize); - printMsg(lvlVomit, format("got header for '%s': %s") % request.uri % trim(line)); - static std::regex statusLine("HTTP/[^ ]+ +[0-9]+(.*)", std::regex::extended | std::regex::icase); + printMsg(lvlVomit, format("got header for '%s': %s") % request.uri % + trim(line)); + static std::regex statusLine("HTTP/[^ ]+ +[0-9]+(.*)", + std::regex::extended | + std::regex::icase); std::smatch match; if (std::regex_match(line, match, statusLine)) { result.etag = ""; @@ -212,41 +229,50 @@ struct curlFileTransfer : public FileTransfer down the connection because we already have the data. */ long httpStatus = 0; - curl_easy_getinfo(req, CURLINFO_RESPONSE_CODE, &httpStatus); - if (result.etag == request.expectedETag && httpStatus == 200) { - debug(format("shutting down on 200 HTTP response with expected ETag")); + curl_easy_getinfo(req, CURLINFO_RESPONSE_CODE, + &httpStatus); + if (result.etag == request.expectedETag && + httpStatus == 200) { + debug(format("shutting down on 200 HTTP response " + "with expected ETag")); return 0; } } else if (name == "content-encoding") encoding = trim(line.substr(i + 1)); - else if (name == "accept-ranges" && toLower(trim(line.substr(i + 1))) == "bytes") + else if (name == "accept-ranges" && + toLower(trim(line.substr(i + 1))) == "bytes") acceptRanges = true; } } return realSize; } - static size_t headerCallbackWrapper(void * contents, size_t size, size_t nmemb, void * userp) + static size_t headerCallbackWrapper(void * contents, size_t size, + size_t nmemb, void * userp) { - return ((TransferItem *) userp)->headerCallback(contents, size, nmemb); + return ((TransferItem *) userp) + ->headerCallback(contents, size, nmemb); } int progressCallback(double dltotal, double dlnow) { try { - act.progress(dlnow, dltotal); + act.progress(dlnow, dltotal); } catch (nix::Interrupted &) { - assert(_isInterrupted); + assert(_isInterrupted); } return _isInterrupted; } - static int progressCallbackWrapper(void * userp, double dltotal, double dlnow, double ultotal, double ulnow) + static int progressCallbackWrapper(void * userp, double dltotal, + double dlnow, double ultotal, + double ulnow) { return ((TransferItem *) userp)->progressCallback(dltotal, dlnow); } - static int debugCallback(CURL * handle, curl_infotype type, char * data, size_t size, void * userptr) + static int debugCallback(CURL * handle, curl_infotype type, char * data, + size_t size, void * userptr) { if (type == CURLINFO_TEXT) vomit("curl: %s", chomp(std::string(data, size))); @@ -254,55 +280,68 @@ struct curlFileTransfer : public FileTransfer } size_t readOffset = 0; - size_t readCallback(char *buffer, size_t size, size_t nitems) + size_t readCallback(char * buffer, size_t size, size_t nitems) { if (readOffset == request.data->length()) return 0; - auto count = std::min(size * nitems, request.data->length() - readOffset); + auto count = + std::min(size * nitems, request.data->length() - readOffset); assert(count); memcpy(buffer, request.data->data() + readOffset, count); readOffset += count; return count; } - static size_t readCallbackWrapper(char *buffer, size_t size, size_t nitems, void * userp) + static size_t readCallbackWrapper(char * buffer, size_t size, + size_t nitems, void * userp) { return ((TransferItem *) userp)->readCallback(buffer, size, nitems); } void init() { - if (!req) req = curl_easy_init(); + if (!req) + req = curl_easy_init(); curl_easy_reset(req); if (verbosity >= lvlVomit) { curl_easy_setopt(req, CURLOPT_VERBOSE, 1); - curl_easy_setopt(req, CURLOPT_DEBUGFUNCTION, TransferItem::debugCallback); + curl_easy_setopt(req, CURLOPT_DEBUGFUNCTION, + TransferItem::debugCallback); } curl_easy_setopt(req, CURLOPT_URL, request.uri.c_str()); curl_easy_setopt(req, CURLOPT_FOLLOWLOCATION, 1L); curl_easy_setopt(req, CURLOPT_MAXREDIRS, 10); curl_easy_setopt(req, CURLOPT_NOSIGNAL, 1); - curl_easy_setopt(req, CURLOPT_USERAGENT, + curl_easy_setopt( + req, CURLOPT_USERAGENT, ("curl/" LIBCURL_VERSION " Nix/" + nixVersion + - (fileTransferSettings.userAgentSuffix != "" ? " " + fileTransferSettings.userAgentSuffix.get() : "")).c_str()); - #if LIBCURL_VERSION_NUM >= 0x072b00 + (fileTransferSettings.userAgentSuffix != "" + ? " " + fileTransferSettings.userAgentSuffix.get() + : "")) + .c_str()); +#if LIBCURL_VERSION_NUM >= 0x072b00 curl_easy_setopt(req, CURLOPT_PIPEWAIT, 1); - #endif - #if LIBCURL_VERSION_NUM >= 0x072f00 +#endif +#if LIBCURL_VERSION_NUM >= 0x072f00 if (fileTransferSettings.enableHttp2) - curl_easy_setopt(req, CURLOPT_HTTP_VERSION, CURL_HTTP_VERSION_2TLS); + curl_easy_setopt(req, CURLOPT_HTTP_VERSION, + CURL_HTTP_VERSION_2TLS); else - curl_easy_setopt(req, CURLOPT_HTTP_VERSION, CURL_HTTP_VERSION_1_1); - #endif - curl_easy_setopt(req, CURLOPT_WRITEFUNCTION, TransferItem::writeCallbackWrapper); + curl_easy_setopt(req, CURLOPT_HTTP_VERSION, + CURL_HTTP_VERSION_1_1); +#endif + curl_easy_setopt(req, CURLOPT_WRITEFUNCTION, + TransferItem::writeCallbackWrapper); curl_easy_setopt(req, CURLOPT_WRITEDATA, this); - curl_easy_setopt(req, CURLOPT_HEADERFUNCTION, TransferItem::headerCallbackWrapper); + curl_easy_setopt(req, CURLOPT_HEADERFUNCTION, + TransferItem::headerCallbackWrapper); curl_easy_setopt(req, CURLOPT_HEADERDATA, this); - curl_easy_setopt(req, CURLOPT_PROGRESSFUNCTION, progressCallbackWrapper); + curl_easy_setopt(req, CURLOPT_PROGRESSFUNCTION, + progressCallbackWrapper); curl_easy_setopt(req, CURLOPT_PROGRESSDATA, this); curl_easy_setopt(req, CURLOPT_NOPROGRESS, 0); @@ -313,28 +352,34 @@ struct curlFileTransfer : public FileTransfer if (request.data) { curl_easy_setopt(req, CURLOPT_UPLOAD, 1L); - curl_easy_setopt(req, CURLOPT_READFUNCTION, readCallbackWrapper); + curl_easy_setopt(req, CURLOPT_READFUNCTION, + readCallbackWrapper); curl_easy_setopt(req, CURLOPT_READDATA, this); - curl_easy_setopt(req, CURLOPT_INFILESIZE_LARGE, (curl_off_t) request.data->length()); + curl_easy_setopt(req, CURLOPT_INFILESIZE_LARGE, + (curl_off_t) request.data->length()); } if (request.verifyTLS) { debug("verify TLS: Nix CA file = '%s'", settings.caFile); if (settings.caFile != "") - curl_easy_setopt(req, CURLOPT_CAINFO, settings.caFile.c_str()); + curl_easy_setopt(req, CURLOPT_CAINFO, + settings.caFile.c_str()); } else { curl_easy_setopt(req, CURLOPT_SSL_VERIFYPEER, 0); curl_easy_setopt(req, CURLOPT_SSL_VERIFYHOST, 0); } - curl_easy_setopt(req, CURLOPT_CONNECTTIMEOUT, fileTransferSettings.connectTimeout.get()); + curl_easy_setopt(req, CURLOPT_CONNECTTIMEOUT, + fileTransferSettings.connectTimeout.get()); curl_easy_setopt(req, CURLOPT_LOW_SPEED_LIMIT, 1L); - curl_easy_setopt(req, CURLOPT_LOW_SPEED_TIME, fileTransferSettings.stalledDownloadTimeout.get()); + curl_easy_setopt(req, CURLOPT_LOW_SPEED_TIME, + fileTransferSettings.stalledDownloadTimeout.get()); /* If no file exist in the specified path, curl continues to work anyway as if netrc support was disabled. */ - curl_easy_setopt(req, CURLOPT_NETRC_FILE, settings.netrcFile.get().c_str()); + curl_easy_setopt(req, CURLOPT_NETRC_FILE, + settings.netrcFile.get().c_str()); curl_easy_setopt(req, CURLOPT_NETRC, CURL_NETRC_OPTIONAL); if (writtenToSink) @@ -353,8 +398,10 @@ struct curlFileTransfer : public FileTransfer if (effectiveUriCStr) result.effectiveUri = effectiveUriCStr; - debug("finished %s of '%s'; curl status = %d, HTTP status = %d, body = %d bytes", - request.verb(), request.uri, code, httpStatus, result.bodySize); + debug("finished %s of '%s'; curl status = %d, HTTP status = %d, " + "body = %d bytes", + request.verb(), request.uri, code, httpStatus, + result.bodySize); if (decompressionSink) { try { @@ -364,7 +411,8 @@ struct curlFileTransfer : public FileTransfer } } - if (code == CURLE_WRITE_ERROR && result.etag == request.expectedETag) { + if (code == CURLE_WRITE_ERROR && + result.etag == request.expectedETag) { code = CURLE_OK; httpStatus = 304; } @@ -372,8 +420,7 @@ struct curlFileTransfer : public FileTransfer if (writeException) failEx(writeException); - else if (code == CURLE_OK && successfulStatuses.count(httpStatus)) - { + else if (code == CURLE_OK && successfulStatuses.count(httpStatus)) { result.cached = httpStatus == 304; // In 2021, GitHub responds to If-None-Match with 304, @@ -388,22 +435,31 @@ struct curlFileTransfer : public FileTransfer } else { - // We treat most errors as transient, but won't retry when hopeless + // We treat most errors as transient, but won't retry when + // hopeless Error err = Transient; - if (httpStatus == 404 || httpStatus == 410 || code == CURLE_FILE_COULDNT_READ_FILE) { + if (httpStatus == 404 || httpStatus == 410 || + code == CURLE_FILE_COULDNT_READ_FILE) { // The file is definitely not there err = NotFound; - } else if (httpStatus == 401 || httpStatus == 403 || httpStatus == 407) { + } else if (httpStatus == 401 || httpStatus == 403 || + httpStatus == 407) { // Don't retry on authentication/authorization failures err = Forbidden; - } else if (httpStatus >= 400 && httpStatus < 500 && httpStatus != 408 && httpStatus != 429) { - // Most 4xx errors are client errors and are probably not worth retrying: - // * 408 means the server timed out waiting for us, so we try again - // * 429 means too many requests, so we retry (with a delay) + } else if (httpStatus >= 400 && httpStatus < 500 && + httpStatus != 408 && httpStatus != 429) { + // Most 4xx errors are client errors and are probably not + // worth retrying: + // * 408 means the server timed out waiting for us, so we + // try again + // * 429 means too many requests, so we retry (with a + // delay) err = Misc; - } else if (httpStatus == 501 || httpStatus == 505 || httpStatus == 511) { - // Let's treat most 5xx (server) errors as transient, except for a handful: + } else if (httpStatus == 501 || httpStatus == 505 || + httpStatus == 511) { + // Let's treat most 5xx (server) errors as transient, except + // for a handful: // * 501 not implemented // * 505 http version not supported // * 511 we're behind a captive portal @@ -411,24 +467,24 @@ struct curlFileTransfer : public FileTransfer } else { // Don't bother retrying on certain cURL errors either switch (code) { - case CURLE_FAILED_INIT: - case CURLE_URL_MALFORMAT: - case CURLE_NOT_BUILT_IN: - case CURLE_REMOTE_ACCESS_DENIED: - case CURLE_FILE_COULDNT_READ_FILE: - case CURLE_FUNCTION_NOT_FOUND: - case CURLE_ABORTED_BY_CALLBACK: - case CURLE_BAD_FUNCTION_ARGUMENT: - case CURLE_INTERFACE_FAILED: - case CURLE_UNKNOWN_OPTION: - case CURLE_SSL_CACERT_BADFILE: - case CURLE_TOO_MANY_REDIRECTS: - case CURLE_WRITE_ERROR: - case CURLE_UNSUPPORTED_PROTOCOL: - err = Misc; - break; - default: // Shut up warnings - break; + case CURLE_FAILED_INIT: + case CURLE_URL_MALFORMAT: + case CURLE_NOT_BUILT_IN: + case CURLE_REMOTE_ACCESS_DENIED: + case CURLE_FILE_COULDNT_READ_FILE: + case CURLE_FUNCTION_NOT_FOUND: + case CURLE_ABORTED_BY_CALLBACK: + case CURLE_BAD_FUNCTION_ARGUMENT: + case CURLE_INTERFACE_FAILED: + case CURLE_UNKNOWN_OPTION: + case CURLE_SSL_CACERT_BADFILE: + case CURLE_TOO_MANY_REDIRECTS: + case CURLE_WRITE_ERROR: + case CURLE_UNSUPPORTED_PROTOCOL: + err = Misc; + break; + default: // Shut up warnings + break; } } @@ -439,51 +495,61 @@ struct curlFileTransfer : public FileTransfer response = std::move(errorSink->s); auto exc = code == CURLE_ABORTED_BY_CALLBACK && _isInterrupted - ? FileTransferError(Interrupted, std::move(response), "%s of '%s' was interrupted", request.verb(), request.uri) + ? FileTransferError(Interrupted, std::move(response), + "%s of '%s' was interrupted", + request.verb(), request.uri) : httpStatus != 0 - ? FileTransferError(err, - std::move(response), - "unable to %s '%s': HTTP error %d%s", - request.verb(), request.uri, httpStatus, - code == CURLE_OK ? "" : fmt(" (curl error: %s)", curl_easy_strerror(code))) - : FileTransferError(err, - std::move(response), - "unable to %s '%s': %s (%d)", - request.verb(), request.uri, curl_easy_strerror(code), code); + ? FileTransferError( + err, std::move(response), + "unable to %s '%s': HTTP error %d%s", + request.verb(), request.uri, httpStatus, + code == CURLE_OK ? "" + : fmt(" (curl error: %s)", + curl_easy_strerror(code))) + : FileTransferError(err, std::move(response), + "unable to %s '%s': %s (%d)", + request.verb(), request.uri, + curl_easy_strerror(code), code); /* If this is a transient error, then maybe retry the download after a while. If we're writing to a sink, we can only retry if the server supports ranged requests. */ - if (err == Transient - && attempt < request.tries - && (!this->request.dataCallback - || writtenToSink == 0 - || (acceptRanges && encoding.empty()))) - { - int ms = request.baseRetryTimeMs * std::pow(2.0f, attempt - 1 + std::uniform_real_distribution<>(0.0, 0.5)(fileTransfer.mt19937)); + if (err == Transient && attempt < request.tries && + (!this->request.dataCallback || writtenToSink == 0 || + (acceptRanges && encoding.empty()))) { + int ms = + request.baseRetryTimeMs * + std::pow(2.0f, attempt - 1 + + std::uniform_real_distribution<>( + 0.0, 0.5)(fileTransfer.mt19937)); if (writtenToSink) - warn("%s; retrying from offset %d in %d ms", exc.what(), writtenToSink, ms); + warn("%s; retrying from offset %d in %d ms", exc.what(), + writtenToSink, ms); else warn("%s; retrying in %d ms", exc.what(), ms); - embargo = std::chrono::steady_clock::now() + std::chrono::milliseconds(ms); + embargo = std::chrono::steady_clock::now() + + std::chrono::milliseconds(ms); fileTransfer.enqueueItem(shared_from_this()); - } - else + } else fail(exc); } } }; - struct State - { + struct State { struct EmbargoComparator { - bool operator() (const std::shared_ptr & i1, const std::shared_ptr & i2) { + bool operator()(const std::shared_ptr & i1, + const std::shared_ptr & i2) + { return i1->embargo > i2->embargo; } }; bool quit = false; - std::priority_queue, std::vector>, EmbargoComparator> incoming; + std::priority_queue, + std::vector>, + EmbargoComparator> + incoming; }; Sync state_; @@ -495,21 +561,20 @@ struct curlFileTransfer : public FileTransfer std::thread workerThread; - curlFileTransfer() - : mt19937(rd()) + curlFileTransfer() : mt19937(rd()) { static std::once_flag globalInit; std::call_once(globalInit, curl_global_init, CURL_GLOBAL_ALL); curlm = curl_multi_init(); - #if LIBCURL_VERSION_NUM >= 0x072b00 // Multiplex requires >= 7.43.0 +#if LIBCURL_VERSION_NUM >= 0x072b00 // Multiplex requires >= 7.43.0 curl_multi_setopt(curlm, CURLMOPT_PIPELINING, CURLPIPE_MULTIPLEX); - #endif - #if LIBCURL_VERSION_NUM >= 0x071e00 // Max connections requires >= 7.30.0 +#endif +#if LIBCURL_VERSION_NUM >= 0x071e00 // Max connections requires >= 7.30.0 curl_multi_setopt(curlm, CURLMOPT_MAX_TOTAL_CONNECTIONS, - fileTransferSettings.httpConnections.get()); - #endif + fileTransferSettings.httpConnections.get()); +#endif wakeupPipe.create(); fcntl(wakeupPipe.readSide.get(), F_SETFL, O_NONBLOCK); @@ -523,7 +588,8 @@ struct curlFileTransfer : public FileTransfer workerThread.join(); - if (curlm) curl_multi_cleanup(curlm); + if (curlm) + curl_multi_cleanup(curlm); } void stopWorkerThread() @@ -539,9 +605,7 @@ struct curlFileTransfer : public FileTransfer void workerThreadMain() { /* Cause this thread to be notified on SIGINT. */ - auto callback = createInterruptCallback([&]() { - stopWorkerThread(); - }); + auto callback = createInterruptCallback([&]() { stopWorkerThread(); }); unshareFilesystem(); @@ -558,7 +622,9 @@ struct curlFileTransfer : public FileTransfer int running; CURLMcode mc = curl_multi_perform(curlm, &running); if (mc != CURLM_OK) - throw nix::Error("unexpected error from curl_multi_perform(): %s", curl_multi_strerror(mc)); + throw nix::Error( + "unexpected error from curl_multi_perform(): %s", + curl_multi_strerror(mc)); /* Set the promises of any finished requests. */ CURLMsg * msg; @@ -583,12 +649,17 @@ struct curlFileTransfer : public FileTransfer long maxSleepTimeMs = items.empty() ? 10000 : 100; auto sleepTimeMs = nextWakeup != std::chrono::steady_clock::time_point() - ? std::max(0, (int) std::chrono::duration_cast(nextWakeup - std::chrono::steady_clock::now()).count()) - : maxSleepTimeMs; + ? std::max( + 0, (int) std::chrono::duration_cast< + std::chrono::milliseconds>( + nextWakeup - std::chrono::steady_clock::now()) + .count()) + : maxSleepTimeMs; vomit("download thread waiting for %d ms", sleepTimeMs); mc = curl_multi_wait(curlm, extraFDs, 1, sleepTimeMs, &numfds); if (mc != CURLM_OK) - throw nix::Error("unexpected error from curl_multi_wait(): %s", curl_multi_strerror(mc)); + throw nix::Error("unexpected error from curl_multi_wait(): %s", + curl_multi_strerror(mc)); nextWakeup = std::chrono::steady_clock::time_point(); @@ -613,8 +684,9 @@ struct curlFileTransfer : public FileTransfer incoming.push_back(item); state->incoming.pop(); } else { - if (nextWakeup == std::chrono::steady_clock::time_point() - || item->embargo < nextWakeup) + if (nextWakeup == + std::chrono::steady_clock::time_point() || + item->embargo < nextWakeup) nextWakeup = item->embargo; break; } @@ -623,7 +695,8 @@ struct curlFileTransfer : public FileTransfer } for (auto & item : incoming) { - debug("starting %s of %s", item->request.verb(), item->request.uri); + debug("starting %s of %s", item->request.verb(), + item->request.uri); item->init(); curl_multi_add_handle(curlm, item->req); item->active = true; @@ -645,35 +718,38 @@ struct curlFileTransfer : public FileTransfer { auto state(state_.lock()); - while (!state->incoming.empty()) state->incoming.pop(); + while (!state->incoming.empty()) + state->incoming.pop(); state->quit = true; } } void enqueueItem(std::shared_ptr item) { - if (item->request.data - && !hasPrefix(item->request.uri, "http://") - && !hasPrefix(item->request.uri, "https://")) - throw nix::Error("uploading to '%s' is not supported", item->request.uri); + if (item->request.data && !hasPrefix(item->request.uri, "http://") && + !hasPrefix(item->request.uri, "https://")) + throw nix::Error("uploading to '%s' is not supported", + item->request.uri); { auto state(state_.lock()); if (state->quit) - throw nix::Error("cannot enqueue download request because the download thread is shutting down"); + throw nix::Error("cannot enqueue download request because the " + "download thread is shutting down"); state->incoming.push(item); } writeFull(wakeupPipe.writeSide.get(), " "); } #if ENABLE_S3 - std::tuple parseS3Uri(std::string uri) + std::tuple + parseS3Uri(std::string uri) { auto [path, params] = splitUriAndParams(uri); auto slash = path.find('/', 5); // 5 is the length of "s3://" prefix - if (slash == std::string::npos) - throw nix::Error("bad S3 URI '%s'", path); + if (slash == std::string::npos) + throw nix::Error("bad S3 URI '%s'", path); std::string bucketName(path, 5, slash - 5); std::string key(path, slash + 1); @@ -683,7 +759,7 @@ struct curlFileTransfer : public FileTransfer #endif void enqueueFileTransfer(const FileTransferRequest & request, - Callback callback) override + Callback callback) override { /* Ugly hack to support s3:// URIs. */ if (hasPrefix(request.uri, "s3://")) { @@ -693,7 +769,8 @@ struct curlFileTransfer : public FileTransfer auto [bucketName, key, params] = parseS3Uri(request.uri); std::string profile = getOr(params, "profile", ""); - std::string region = getOr(params, "region", Aws::Region::US_EAST_1); + std::string region = + getOr(params, "region", Aws::Region::US_EAST_1); std::string scheme = getOr(params, "scheme", ""); std::string endpoint = getOr(params, "endpoint", ""); @@ -703,17 +780,23 @@ struct curlFileTransfer : public FileTransfer auto s3Res = s3Helper.getObject(bucketName, key); FileTransferResult res; if (!s3Res.data) - throw FileTransferError(NotFound, "S3 object '%s' does not exist", request.uri); + throw FileTransferError( + NotFound, "S3 object '%s' does not exist", request.uri); res.data = std::move(*s3Res.data); callback(std::move(res)); #else - throw nix::Error("cannot download '%s' because Nix is not built with S3 support", request.uri); + throw nix::Error("cannot download '%s' because Nix is not " + "built with S3 support", + request.uri); #endif - } catch (...) { callback.rethrow(); } + } catch (...) { + callback.rethrow(); + } return; } - enqueueItem(std::make_shared(*this, request, std::move(callback))); + enqueueItem(std::make_shared(*this, request, + std::move(callback))); } }; @@ -732,16 +815,14 @@ ref getFileTransfer() return fileTransfer; } -ref makeFileTransfer() -{ - return makeCurlFileTransfer(); -} +ref makeFileTransfer() { return makeCurlFileTransfer(); } -std::future FileTransfer::enqueueFileTransfer(const FileTransferRequest & request) +std::future +FileTransfer::enqueueFileTransfer(const FileTransferRequest & request) { auto promise = std::make_shared>(); - enqueueFileTransfer(request, - {[promise](std::future fut) { + enqueueFileTransfer( + request, {[promise](std::future fut) { try { promise->set_value(fut.get()); } catch (...) { @@ -790,10 +871,10 @@ void FileTransfer::download(FileTransferRequest && request, Sink & sink) }); request.dataCallback = [_state](std::string_view data) { - auto state(_state->lock()); - if (state->quit) return; + if (state->quit) + return; /* If the buffer is full, then go to sleep until the calling thread wakes us up (i.e. when it has removed data from the @@ -812,17 +893,17 @@ void FileTransfer::download(FileTransferRequest && request, Sink & sink) }; enqueueFileTransfer(request, - {[_state](std::future fut) { - auto state(_state->lock()); - state->quit = true; - try { - fut.get(); - } catch (...) { - state->exc = std::current_exception(); - } - state->avail.notify_one(); - state->request.notify_one(); - }}); + {[_state](std::future fut) { + auto state(_state->lock()); + state->quit = true; + try { + fut.get(); + } catch (...) { + state->exc = std::current_exception(); + } + state->avail.notify_one(); + state->request.notify_one(); + }}); while (true) { checkInterrupt(); @@ -837,7 +918,8 @@ void FileTransfer::download(FileTransferRequest && request, Sink & sink) while (state->data.empty()) { if (state->quit) { - if (state->exc) std::rethrow_exception(state->exc); + if (state->exc) + std::rethrow_exception(state->exc); return; } @@ -858,27 +940,34 @@ void FileTransfer::download(FileTransferRequest && request, Sink & sink) } template -FileTransferError::FileTransferError(FileTransfer::Error error, std::optional response, const Args & ... args) +FileTransferError::FileTransferError(FileTransfer::Error error, + std::optional response, + const Args &... args) : Error(args...), error(error), response(response) { const auto hf = hintfmt(args...); // FIXME: Due to https://github.com/NixOS/nix/issues/3841 we don't know how // to print different messages for different verbosity levels. For now // we add some heuristics for detecting when we want to show the response. - if (response && (response->size() < 1024 || response->find("") != std::string::npos)) - err.msg = hintfmt("%1%\n\nresponse body:\n\n%2%", normaltxt(hf.str()), chomp(*response)); + if (response && (response->size() < 1024 || + response->find("") != std::string::npos)) + err.msg = hintfmt("%1%\n\nresponse body:\n\n%2%", normaltxt(hf.str()), + chomp(*response)); else err.msg = hf; } bool isUri(std::string_view s) { - if (s.compare(0, 8, "channel:") == 0) return true; + if (s.compare(0, 8, "channel:") == 0) + return true; size_t pos = s.find("://"); - if (pos == std::string::npos) return false; + if (pos == std::string::npos) + return false; std::string scheme(s, 0, pos); - return scheme == "http" || scheme == "https" || scheme == "file" || scheme == "channel" || scheme == "git" || scheme == "s3" || scheme == "ssh"; + return scheme == "http" || scheme == "https" || scheme == "file" || + scheme == "channel" || scheme == "git" || scheme == "s3" || + scheme == "ssh"; } - } diff --git a/src/libstore/filetransfer.hh b/src/libstore/filetransfer.hh index 40e7cf52c8ee..d0041f390e3e 100644 --- a/src/libstore/filetransfer.hh +++ b/src/libstore/filetransfer.hh @@ -9,47 +9,47 @@ namespace nix { -struct FileTransferSettings : Config -{ +struct FileTransferSettings : Config { Setting enableHttp2{this, true, "http2", - "Whether to enable HTTP/2 support."}; + "Whether to enable HTTP/2 support."}; - Setting userAgentSuffix{this, "", "user-agent-suffix", + Setting userAgentSuffix{ + this, "", "user-agent-suffix", "String appended to the user agent in HTTP requests."}; - Setting httpConnections{ - this, 25, "http-connections", - R"( + Setting httpConnections{this, + 25, + "http-connections", + R"( The maximum number of parallel TCP connections used to fetch files from binary caches and by other downloads. It defaults to 25. 0 means no limit. )", - {"binary-caches-parallel-connections"}}; + {"binary-caches-parallel-connections"}}; - Setting connectTimeout{ - this, 0, "connect-timeout", - R"( + Setting connectTimeout{this, 0, "connect-timeout", + R"( The timeout (in seconds) for establishing connections in the binary cache substituter. It corresponds to `curl`’s `--connect-timeout` option. A value of 0 means no limit. )"}; - Setting stalledDownloadTimeout{ - this, 300, "stalled-download-timeout", - R"( + Setting stalledDownloadTimeout{this, 300, + "stalled-download-timeout", + R"( The timeout (in seconds) for receiving data from servers during download. Nix cancels idle downloads after this timeout's duration. )"}; - Setting tries{this, 5, "download-attempts", + Setting tries{ + this, 5, "download-attempts", "How often Nix will attempt to download a file before giving up."}; }; extern FileTransferSettings fileTransferSettings; -struct FileTransferRequest -{ +struct FileTransferRequest { std::string uri; Headers headers; std::string expectedETag; @@ -64,16 +64,14 @@ struct FileTransferRequest std::function dataCallback; FileTransferRequest(std::string_view uri) - : uri(uri), parentAct(getCurActivity()) { } - - std::string verb() + : uri(uri), parentAct(getCurActivity()) { - return data ? "upload" : "download"; } + + std::string verb() { return data ? "upload" : "download"; } }; -struct FileTransferResult -{ +struct FileTransferResult { bool cached = false; std::string etag; std::string effectiveUri; @@ -83,17 +81,17 @@ struct FileTransferResult class Store; -struct FileTransfer -{ - virtual ~FileTransfer() { } +struct FileTransfer { + virtual ~FileTransfer() {} /* Enqueue a data transfer request, returning a future to the result of the download. The future may throw a FileTransferError exception. */ virtual void enqueueFileTransfer(const FileTransferRequest & request, - Callback callback) = 0; + Callback callback) = 0; - std::future enqueueFileTransfer(const FileTransferRequest & request); + std::future + enqueueFileTransfer(const FileTransferRequest & request); /* Synchronously download a file. */ FileTransferResult download(const FileTransferRequest & request); @@ -115,14 +113,15 @@ ref getFileTransfer(); /* Return a new FileTransfer object. */ ref makeFileTransfer(); -class FileTransferError : public Error -{ -public: +class FileTransferError : public Error { + public: FileTransfer::Error error; std::optional response; // intentionally optional template - FileTransferError(FileTransfer::Error error, std::optional response, const Args & ... args); + FileTransferError(FileTransfer::Error error, + std::optional response, + const Args &... args); }; bool isUri(std::string_view s); diff --git a/src/libstore/fs-accessor.hh b/src/libstore/fs-accessor.hh index c825e84f2d70..399f4837b9da 100644 --- a/src/libstore/fs-accessor.hh +++ b/src/libstore/fs-accessor.hh @@ -6,20 +6,18 @@ namespace nix { /* An abstract class for accessing a filesystem-like structure, such as a (possibly remote) Nix store or the contents of a NAR file. */ -class FSAccessor -{ -public: +class FSAccessor { + public: enum Type { tMissing, tRegular, tSymlink, tDirectory }; - struct Stat - { + struct Stat { Type type = tMissing; - uint64_t fileSize = 0; // regular files only + uint64_t fileSize = 0; // regular files only bool isExecutable = false; // regular files only - uint64_t narOffset = 0; // regular files only + uint64_t narOffset = 0; // regular files only }; - virtual ~FSAccessor() { } + virtual ~FSAccessor() {} virtual Stat stat(const Path & path) = 0; @@ -32,7 +30,8 @@ public: * inside a valid store path, otherwise it just needs to be physically * present (but not necessarily properly registered) */ - virtual std::string readFile(const Path & path, bool requireValidPath = true) = 0; + virtual std::string readFile(const Path & path, + bool requireValidPath = true) = 0; virtual std::string readLink(const Path & path) = 0; }; diff --git a/src/libstore/gc-store.hh b/src/libstore/gc-store.hh index b3cbbad74472..ce887a9863fa 100644 --- a/src/libstore/gc-store.hh +++ b/src/libstore/gc-store.hh @@ -2,15 +2,11 @@ #include "store-api.hh" - namespace nix { - typedef std::unordered_map> Roots; - -struct GCOptions -{ +struct GCOptions { /* Garbage collector operation: - `gcReturnLive': return the set of paths reachable from @@ -46,9 +42,7 @@ struct GCOptions uint64_t maxFreed{std::numeric_limits::max()}; }; - -struct GCResults -{ +struct GCResults { /* Depending on the action, the GC roots, or the paths that would be or have been deleted. */ PathSet paths; @@ -58,9 +52,7 @@ struct GCResults uint64_t bytesFreed = 0; }; - -struct GcStore : public virtual Store -{ +struct GcStore : public virtual Store { inline static std::string operationName = "Garbage collection"; /* Add an indirect root, which is merely a symlink to `path' from @@ -78,7 +70,8 @@ struct GcStore : public virtual Store virtual Roots findRoots(bool censor) = 0; /* Perform a garbage collection. */ - virtual void collectGarbage(const GCOptions & options, GCResults & results) = 0; + virtual void collectGarbage(const GCOptions & options, + GCResults & results) = 0; }; } diff --git a/src/libstore/gc.cc b/src/libstore/gc.cc index d58ed78b149a..a28ebecc0454 100644 --- a/src/libstore/gc.cc +++ b/src/libstore/gc.cc @@ -23,44 +23,42 @@ namespace nix { - static std::string gcSocketPath = "/gc-socket/socket"; static std::string gcRootsDir = "gcroots"; - static void makeSymlink(const Path & link, const Path & target) { /* Create directories up to `gcRoot'. */ createDirs(dirOf(link)); /* Create the new symlink. */ - Path tempLink = (format("%1%.tmp-%2%-%3%") - % link % getpid() % random()).str(); + Path tempLink = + (format("%1%.tmp-%2%-%3%") % link % getpid() % random()).str(); createSymlink(target, tempLink); /* Atomically replace the old one. */ if (rename(tempLink.c_str(), link.c_str()) == -1) - throw SysError("cannot rename '%1%' to '%2%'", - tempLink , link); + throw SysError("cannot rename '%1%' to '%2%'", tempLink, link); } - void LocalStore::addIndirectRoot(const Path & path) { std::string hash = hashString(htSHA1, path).to_string(Base32, false); - Path realRoot = canonPath(fmt("%1%/%2%/auto/%3%", stateDir, gcRootsDir, hash)); + Path realRoot = + canonPath(fmt("%1%/%2%/auto/%3%", stateDir, gcRootsDir, hash)); makeSymlink(realRoot, path); } - -Path LocalFSStore::addPermRoot(const StorePath & storePath, const Path & _gcRoot) +Path LocalFSStore::addPermRoot(const StorePath & storePath, + const Path & _gcRoot) { Path gcRoot(canonPath(_gcRoot)); if (isInStore(gcRoot)) - throw Error( - "creating a garbage collector root (%1%) in the Nix store is forbidden " - "(are you running nix-build inside the store?)", gcRoot); + throw Error("creating a garbage collector root (%1%) in the Nix store " + "is forbidden " + "(are you running nix-build inside the store?)", + gcRoot); /* Register this root with the garbage collector, if it's running. This should be superfluous since the caller should @@ -78,7 +76,6 @@ Path LocalFSStore::addPermRoot(const StorePath & storePath, const Path & _gcRoot return gcRoot; } - void LocalStore::addTempRoot(const StorePath & path) { auto state(_state.lock()); @@ -102,19 +99,19 @@ void LocalStore::addTempRoot(const StorePath & path) struct stat st; if (fstat(state->fdTempRoots.get(), &st) == -1) throw SysError("statting '%1%'", fnTempRoots); - if (st.st_size == 0) break; + if (st.st_size == 0) + break; /* The garbage collector deleted this file before we could get a lock. (It won't delete the file after we get a lock.) Try again. */ } - } if (!state->fdGCLock) state->fdGCLock = openGCLock(); - restart: +restart: FdLock gcLock(state->fdGCLock.get(), ltRead, false, ""); if (!gcLock.acquired) { @@ -141,7 +138,8 @@ void LocalStore::addTempRoot(const StorePath & path) try { debug("sending GC root '%s'", printStorePath(path)); - writeFull(state->fdRootsSocket.get(), printStorePath(path) + "\n", false); + writeFull(state->fdRootsSocket.get(), printStorePath(path) + "\n", + false); char c; readFull(state->fdRootsSocket.get(), &c, 1); assert(c == '1'); @@ -167,18 +165,16 @@ void LocalStore::addTempRoot(const StorePath & path) writeFull(state->fdTempRoots.get(), s); } - static std::string censored = "{censored}"; - void LocalStore::findTempRoots(Roots & tempRoots, bool censor) { /* Read the `temproots' directory for per-process temporary root files. */ for (auto & i : readDirectory(tempRootsDir)) { if (i.name[0] == '.') { - // Ignore hidden files. Some package managers (notably portage) create - // those to keep the directory alive. + // Ignore hidden files. Some package managers (notably portage) + // create those to keep the directory alive. continue; } Path path = tempRootsDir + "/" + i.name; @@ -189,7 +185,8 @@ void LocalStore::findTempRoots(Roots & tempRoots, bool censor) AutoCloseFD fd(open(path.c_str(), O_CLOEXEC | O_RDWR, 0666)); if (!fd) { /* It's okay if the file has disappeared. */ - if (errno == ENOENT) continue; + if (errno == ENOENT) + continue; throw SysError("opening temporary roots file '%1%'", path); } @@ -212,13 +209,13 @@ void LocalStore::findTempRoots(Roots & tempRoots, bool censor) while ((end = contents.find((char) 0, pos)) != std::string::npos) { Path root(contents, pos, end - pos); debug("got temporary root '%s'", root); - tempRoots[parseStorePath(root)].emplace(censor ? censored : fmt("{temp:%d}", pid)); + tempRoots[parseStorePath(root)].emplace( + censor ? censored : fmt("{temp:%d}", pid)); pos = end + 1; } } } - void LocalStore::findRoots(const Path & path, unsigned char type, Roots & roots) { auto foundRoot = [&](const Path & path, const Path & target) { @@ -227,8 +224,10 @@ void LocalStore::findRoots(const Path & path, unsigned char type, Roots & roots) if (isValidPath(storePath)) roots[std::move(storePath)].emplace(path); else - printInfo("skipping invalid root from '%1%' to '%2%'", path, target); - } catch (BadStorePath &) { } + printInfo("skipping invalid root from '%1%' to '%2%'", path, + target); + } catch (BadStorePath &) { + } }; try { @@ -251,20 +250,25 @@ void LocalStore::findRoots(const Path & path, unsigned char type, Roots & roots) target = absPath(target, dirOf(path)); if (!pathExists(target)) { if (isInDir(path, stateDir + "/" + gcRootsDir + "/auto")) { - printInfo(format("removing stale link from '%1%' to '%2%'") % path % target); + printInfo( + format("removing stale link from '%1%' to '%2%'") % + path % target); unlink(path.c_str()); } } else { struct stat st2 = lstat(target); - if (!S_ISLNK(st2.st_mode)) return; + if (!S_ISLNK(st2.st_mode)) + return; Path target2 = readLink(target); - if (isInStore(target2)) foundRoot(target, target2); + if (isInStore(target2)) + foundRoot(target, target2); } } } else if (type == DT_REG) { - auto storePath = maybeParseStorePath(storeDir + "/" + std::string(baseNameOf(path))); + auto storePath = maybeParseStorePath(storeDir + "/" + + std::string(baseNameOf(path))); if (storePath && isValidPath(*storePath)) roots[std::move(*storePath)].emplace(path); } @@ -280,7 +284,6 @@ void LocalStore::findRoots(const Path & path, unsigned char type, Roots & roots) } } - void LocalStore::findRootsNoTemp(Roots & roots, bool censor) { /* Process direct roots in {gcroots,profiles}. */ @@ -293,7 +296,6 @@ void LocalStore::findRootsNoTemp(Roots & roots, bool censor) findRuntimeRoots(roots, censor); } - Roots LocalStore::findRoots(bool censor) { Roots roots; @@ -304,7 +306,8 @@ Roots LocalStore::findRoots(bool censor) return roots; } -typedef std::unordered_map> UncheckedRoots; +typedef std::unordered_map> + UncheckedRoots; static void readProcLink(const std::string & file, UncheckedRoots & roots) { @@ -325,8 +328,7 @@ static void readProcLink(const std::string & file, UncheckedRoots & roots) goto try_again; } if (res > 0 && buf[0] == '/') - roots[std::string(static_cast(buf), res)] - .emplace(file); + roots[std::string(static_cast(buf), res)].emplace(file); } static std::string quoteRegexChars(const std::string & raw) @@ -355,12 +357,14 @@ void LocalStore::findRuntimeRoots(Roots & roots, bool censor) if (procDir) { struct dirent * ent; auto digitsRegex = std::regex(R"(^\d+$)"); - auto mapRegex = std::regex(R"(^\s*\S+\s+\S+\s+\S+\s+\S+\s+\S+\s+(/\S+)\s*$)"); - auto storePathRegex = std::regex(quoteRegexChars(storeDir) + R"(/[0-9a-z]+[0-9a-zA-Z\+\-\._\?=]*)"); + auto mapRegex = + std::regex(R"(^\s*\S+\s+\S+\s+\S+\s+\S+\s+\S+\s+(/\S+)\s*$)"); + auto storePathRegex = std::regex(quoteRegexChars(storeDir) + + R"(/[0-9a-z]+[0-9a-zA-Z\+\-\._\?=]*)"); while (errno = 0, ent = readdir(procDir.get())) { checkInterrupt(); if (std::regex_match(ent->d_name, digitsRegex)) { - readProcLink(fmt("/proc/%s/exe" ,ent->d_name), unchecked); + readProcLink(fmt("/proc/%s/exe", ent->d_name), unchecked); readProcLink(fmt("/proc/%s/cwd", ent->d_name), unchecked); auto fdStr = fmt("/proc/%s/fd", ent->d_name); @@ -373,7 +377,8 @@ void LocalStore::findRuntimeRoots(Roots & roots, bool censor) struct dirent * fd_ent; while (errno = 0, fd_ent = readdir(fdDir.get())) { if (fd_ent->d_name[0] != '.') - readProcLink(fmt("%s/%s", fdStr, fd_ent->d_name), unchecked); + readProcLink(fmt("%s/%s", fdStr, fd_ent->d_name), + unchecked); } if (errno) { if (errno == ESRCH) @@ -384,7 +389,8 @@ void LocalStore::findRuntimeRoots(Roots & roots, bool censor) try { auto mapFile = fmt("/proc/%s/maps", ent->d_name); - auto mapLines = tokenizeString>(readFile(mapFile), "\n"); + auto mapLines = tokenizeString>( + readFile(mapFile), "\n"); for (const auto & line : mapLines) { auto match = std::smatch{}; if (std::regex_match(line, match, mapRegex)) @@ -394,7 +400,10 @@ void LocalStore::findRuntimeRoots(Roots & roots, bool censor) auto envFile = fmt("/proc/%s/environ", ent->d_name); auto envString = readFile(envFile); auto env_end = std::sregex_iterator{}; - for (auto i = std::sregex_iterator{envString.begin(), envString.end(), storePathRegex}; i != env_end; ++i) + for (auto i = std::sregex_iterator{envString.begin(), + envString.end(), + storePathRegex}; + i != env_end; ++i) unchecked[i->str()].emplace(envFile); } catch (SysError & e) { if (errno == ENOENT || errno == EACCES || errno == ESRCH) @@ -408,14 +417,14 @@ void LocalStore::findRuntimeRoots(Roots & roots, bool censor) } #if !defined(__linux__) - // lsof is really slow on OS X. This actually causes the gc-concurrent.sh test to fail. - // See: https://github.com/NixOS/nix/issues/3011 - // Because of this we disable lsof when running the tests. + // lsof is really slow on OS X. This actually causes the gc-concurrent.sh + // test to fail. See: https://github.com/NixOS/nix/issues/3011 Because of + // this we disable lsof when running the tests. if (getEnv("_NIX_TEST_NO_LSOF") != "1") { try { std::regex lsofRegex(R"(^n(/.*)$)"); - auto lsofLines = - tokenizeString>(runProgram(LSOF, true, { "-n", "-w", "-F", "n" }), "\n"); + auto lsofLines = tokenizeString>( + runProgram(LSOF, true, {"-n", "-w", "-F", "n"}), "\n"); for (const auto & line : lsofLines) { std::smatch match; if (std::regex_match(line, match, lsofRegex)) @@ -434,33 +443,35 @@ void LocalStore::findRuntimeRoots(Roots & roots, bool censor) #endif for (auto & [target, links] : unchecked) { - if (!isInStore(target)) continue; + if (!isInStore(target)) + continue; try { auto path = toStorePath(target).first; - if (!isValidPath(path)) continue; + if (!isValidPath(path)) + continue; debug("got additional root '%1%'", printStorePath(path)); if (censor) roots[path].insert(censored); else roots[path].insert(links.begin(), links.end()); - } catch (BadStorePath &) { } + } catch (BadStorePath &) { + } } } - -struct GCLimitReached { }; - +struct GCLimitReached { +}; void LocalStore::collectGarbage(const GCOptions & options, GCResults & results) { - bool shouldDelete = options.action == GCOptions::gcDeleteDead || options.action == GCOptions::gcDeleteSpecific; + bool shouldDelete = options.action == GCOptions::gcDeleteDead || + options.action == GCOptions::gcDeleteSpecific; bool gcKeepOutputs = settings.gcKeepOutputs; bool gcKeepDerivations = settings.gcKeepDerivations; StorePathSet roots, dead, alive; - struct Shared - { + struct Shared { // The temp roots only store the hash part to make it easier to // ignore suffixes like '.lock', '.chroot' and '.check'. std::unordered_set tempRoots; @@ -478,7 +489,8 @@ void LocalStore::collectGarbage(const GCOptions & options, GCResults & results) consequences if `keep-outputs' or `keep-derivations' are true (the garbage collector will recurse into deleting the outputs or derivers, respectively). So disable them. */ - if (options.action == GCOptions::gcDeleteSpecific && options.ignoreLiveness) { + if (options.action == GCOptions::gcDeleteSpecific && + options.ignoreLiveness) { gcKeepOutputs = false; gcKeepDerivations = false; } @@ -490,14 +502,16 @@ void LocalStore::collectGarbage(const GCOptions & options, GCResults & results) here because then in auto-gc mode, another thread could downgrade our exclusive lock. */ auto fdGCLock = openGCLock(); - FdLock gcLock(fdGCLock.get(), ltWrite, true, "waiting for the big garbage collector lock..."); + FdLock gcLock(fdGCLock.get(), ltWrite, true, + "waiting for the big garbage collector lock..."); /* Start the server for receiving new roots. */ auto socketPath = stateDir.get() + gcSocketPath; createDirs(dirOf(socketPath)); auto fdServer = createUnixDomainSocket(socketPath, 0666); - if (fcntl(fdServer.get(), F_SETFL, fcntl(fdServer.get(), F_GETFL) | O_NONBLOCK) == -1) + if (fcntl(fdServer.get(), F_SETFL, + fcntl(fdServer.get(), F_GETFL) | O_NONBLOCK) == -1) throw SysError("making socket '%1%' non-blocking", socketPath); Pipe shutdownPipe; @@ -510,7 +524,8 @@ void LocalStore::collectGarbage(const GCOptions & options, GCResults & results) debug("GC roots server shutting down"); while (true) { auto item = remove_begin(*connections.lock()); - if (!item) break; + if (!item) + break; auto & [fd, thread] = *item; shutdown(fd, SHUT_RDWR); thread.join(); @@ -519,7 +534,8 @@ void LocalStore::collectGarbage(const GCOptions & options, GCResults & results) while (true) { std::vector fds; - fds.push_back({.fd = shutdownPipe.readSide.get(), .events = POLLIN}); + fds.push_back( + {.fd = shutdownPipe.readSide.get(), .events = POLLIN}); fds.push_back({.fd = fdServer.get(), .events = POLLIN}); auto count = poll(fds.data(), fds.size(), -1); assert(count != -1); @@ -532,7 +548,8 @@ void LocalStore::collectGarbage(const GCOptions & options, GCResults & results) /* Accept a new connection. */ assert(fds[1].revents & POLLIN); AutoCloseFD fdClient = accept(fdServer.get(), nullptr, nullptr); - if (!fdClient) continue; + if (!fdClient) + continue; debug("GC roots server accepted new client"); @@ -551,7 +568,9 @@ void LocalStore::collectGarbage(const GCOptions & options, GCResults & results) /* On macOS, accepted sockets inherit the non-blocking flag from the server socket, so explicitly make it blocking. */ - if (fcntl(fdServer.get(), F_SETFL, fcntl(fdServer.get(), F_GETFL) & ~O_NONBLOCK) == -1) + if (fcntl(fdServer.get(), F_SETFL, + fcntl(fdServer.get(), F_GETFL) & ~O_NONBLOCK) == + -1) abort(); while (true) { @@ -560,7 +579,8 @@ void LocalStore::collectGarbage(const GCOptions & options, GCResults & results) auto storePath = maybeParseStorePath(path); if (storePath) { debug("got new GC root '%s'", path); - auto hashPart = std::string(storePath->hashPart()); + auto hashPart = + std::string(storePath->hashPart()); auto shared(_shared.lock()); shared->tempRoots.insert(hashPart); /* If this path is currently being @@ -572,11 +592,14 @@ void LocalStore::collectGarbage(const GCOptions & options, GCResults & results) FD for this so we don't block the poll loop. */ while (shared->pending == hashPart) { - debug("synchronising with deletion of path '%s'", path); + debug("synchronising with deletion of path " + "'%s'", + path); shared.wait(wakeup); } } else - printError("received garbage instead of a root from client"); + printError("received garbage instead of a root " + "from client"); writeFull(fdClient.get(), "1", false); } catch (Error & e) { debug("reading GC root from client: %s", e.msg()); @@ -585,7 +608,8 @@ void LocalStore::collectGarbage(const GCOptions & options, GCResults & results) } }); - connections.lock()->insert({fdClient_, std::move(clientThread)}); + connections.lock()->insert( + {fdClient_, std::move(clientThread)}); } } }); @@ -593,7 +617,8 @@ void LocalStore::collectGarbage(const GCOptions & options, GCResults & results) Finally stopServer([&]() { writeFull(shutdownPipe.writeSide.get(), "x", false); wakeup.notify_all(); - if (serverThread.joinable()) serverThread.join(); + if (serverThread.joinable()) + serverThread.join(); }); /* Find the roots. Since we've grabbed the GC lock, the set of @@ -603,7 +628,8 @@ void LocalStore::collectGarbage(const GCOptions & options, GCResults & results) if (!options.ignoreLiveness) findRootsNoTemp(rootMap, true); - for (auto & i : rootMap) roots.insert(i.first); + for (auto & i : rootMap) + roots.insert(i.first); /* Read the temporary roots created before we acquired the global GC root. Any new roots will be sent to our socket. */ @@ -616,8 +642,7 @@ void LocalStore::collectGarbage(const GCOptions & options, GCResults & results) /* Helper function that deletes a path from the store and throws GCLimitReached if we've deleted enough garbage. */ - auto deleteFromStore = [&](std::string_view baseName) - { + auto deleteFromStore = [&](std::string_view baseName) { Path path = storeDir + "/" + std::string(baseName); Path realPath = realStoreDir + "/" + std::string(baseName); @@ -672,36 +697,40 @@ void LocalStore::collectGarbage(const GCOptions & options, GCResults & results) /* If we've previously deleted this path, we don't have to handle it again. */ - if (dead.count(*path)) continue; + if (dead.count(*path)) + continue; - auto markAlive = [&]() - { + auto markAlive = [&]() { alive.insert(*path); alive.insert(start); try { StorePathSet closure; computeFSClosure(*path, closure, - /* flipDirection */ false, gcKeepOutputs, gcKeepDerivations); + /* flipDirection */ false, gcKeepOutputs, + gcKeepDerivations); for (auto & p : closure) alive.insert(p); - } catch (InvalidPath &) { } + } catch (InvalidPath &) { + } }; /* If this is a root, bail out. */ if (roots.count(*path)) { - debug("cannot delete '%s' because it's a root", printStorePath(*path)); + debug("cannot delete '%s' because it's a root", + printStorePath(*path)); return markAlive(); } - if (options.action == GCOptions::gcDeleteSpecific - && !options.pathsToDelete.count(*path)) + if (options.action == GCOptions::gcDeleteSpecific && + !options.pathsToDelete.count(*path)) return; { auto hashPart = std::string(path->hashPart()); auto shared(_shared.lock()); if (shared->tempRoots.count(hashPart)) { - debug("cannot delete '%s' because it's a temporary root", printStorePath(*path)); + debug("cannot delete '%s' because it's a temporary root", + printStorePath(*path)); return markAlive(); } shared->pending = hashPart; @@ -723,9 +752,9 @@ void LocalStore::collectGarbage(const GCOptions & options, GCResults & results) /* If keep-derivations is set and this is a derivation, then visit the derivation outputs. */ if (gcKeepDerivations && path->isDerivation()) { - for (auto & [name, maybeOutPath] : queryPartialDerivationOutputMap(*path)) - if (maybeOutPath && - isValidPath(*maybeOutPath) && + for (auto & [name, maybeOutPath] : + queryPartialDerivationOutputMap(*path)) + if (maybeOutPath && isValidPath(*maybeOutPath) && queryPathInfo(*maybeOutPath)->deriver == *path) enqueue(*maybeOutPath); } @@ -740,7 +769,8 @@ void LocalStore::collectGarbage(const GCOptions & options, GCResults & results) } for (auto & path : topoSortPaths(visited)) { - if (!dead.insert(path).second) continue; + if (!dead.insert(path).second) + continue; if (shouldDelete) { invalidatePathChecked(path); deleteFromStore(path.to_string()); @@ -760,11 +790,10 @@ void LocalStore::collectGarbage(const GCOptions & options, GCResults & results) for (auto & i : options.pathsToDelete) { deleteReferrersClosure(i); if (!dead.count(i)) - throw Error( - "Cannot delete path '%1%' since it is still alive. " - "To find out why, use: " - "nix-store --query --roots", - printStorePath(i)); + throw Error("Cannot delete path '%1%' since it is still alive. " + "To find out why, use: " + "nix-store --query --roots", + printStorePath(i)); } } else if (options.maxFreed > 0) { @@ -776,7 +805,8 @@ void LocalStore::collectGarbage(const GCOptions & options, GCResults & results) try { AutoCloseDir dir(opendir(realStoreDir.get().c_str())); - if (!dir) throw SysError("opening directory '%1%'", realStoreDir); + if (!dir) + throw SysError("opening directory '%1%'", realStoreDir); /* Read the store and delete all paths that are invalid or unreachable. We don't use readDirectory() here so that @@ -787,13 +817,13 @@ void LocalStore::collectGarbage(const GCOptions & options, GCResults & results) while (errno = 0, dirent = readdir(dir.get())) { checkInterrupt(); std::string name = dirent->d_name; - if (name == "." || name == ".." || name == linksName) continue; + if (name == "." || name == ".." || name == linksName) + continue; if (auto storePath = maybeParseStorePath(storeDir + "/" + name)) deleteReferrersClosure(*storePath); else deleteFromStore(name); - } } catch (GCLimitReached & e) { } @@ -816,11 +846,13 @@ void LocalStore::collectGarbage(const GCOptions & options, GCResults & results) safely deleted. FIXME: race condition with optimisePath(): we might see a link count of 1 just before optimisePath() increases the link count. */ - if (options.action == GCOptions::gcDeleteDead || options.action == GCOptions::gcDeleteSpecific) { + if (options.action == GCOptions::gcDeleteDead || + options.action == GCOptions::gcDeleteSpecific) { printInfo("deleting unused links..."); AutoCloseDir dir(opendir(linksDir.c_str())); - if (!dir) throw SysError("opening directory '%1%'", linksDir); + if (!dir) + throw SysError("opening directory '%1%'", linksDir); int64_t actualSize = 0, unsharedSize = 0; @@ -828,7 +860,8 @@ void LocalStore::collectGarbage(const GCOptions & options, GCResults & results) while (errno = 0, dirent = readdir(dir.get())) { checkInterrupt(); std::string name = dirent->d_name; - if (name == "." || name == "..") continue; + if (name == "." || name == "..") + continue; Path path = linksDir + "/" + name; auto st = lstat(path); @@ -854,14 +887,13 @@ void LocalStore::collectGarbage(const GCOptions & options, GCResults & results) int64_t overhead = st.st_blocks * 512ULL; printInfo("note: currently hard linking saves %.2f MiB", - ((unsharedSize - actualSize - overhead) / (1024.0 * 1024.0))); + ((unsharedSize - actualSize - overhead) / (1024.0 * 1024.0))); } /* While we're at it, vacuum the database. */ - //if (options.action == GCOptions::gcDeleteDead) vacuumDB(); + // if (options.action == GCOptions::gcDeleteDead) vacuumDB(); } - void LocalStore::autoGC(bool sync) { static auto fakeFreeSpaceFile = getEnv("_NIX_TEST_FREE_SPACE_FILE"); @@ -890,23 +922,27 @@ void LocalStore::autoGC(bool sync) auto now = std::chrono::steady_clock::now(); - if (now < state->lastGCCheck + std::chrono::seconds(settings.minFreeCheckInterval)) return; + if (now < state->lastGCCheck + + std::chrono::seconds(settings.minFreeCheckInterval)) + return; auto avail = getAvail(); state->lastGCCheck = now; - if (avail >= settings.minFree || avail >= settings.maxFree) return; + if (avail >= settings.minFree || avail >= settings.maxFree) + return; - if (avail > state->availAfterGC * 0.97) return; + if (avail > state->availAfterGC * 0.97) + return; state->gcRunning = true; std::promise promise; future = state->gcFuture = promise.get_future().share(); - std::thread([promise{std::move(promise)}, this, avail, getAvail]() mutable { - + std::thread([promise{std::move(promise)}, this, avail, + getAvail]() mutable { try { /* Wake up any threads waiting for the auto-GC to finish. */ @@ -933,14 +969,13 @@ void LocalStore::autoGC(bool sync) // future, but we don't really care. ignoreException(); } - }).detach(); } - sync: +sync: // Wait for the future outside of the state lock. - if (sync) future.get(); + if (sync) + future.get(); } - } diff --git a/src/libstore/globals.cc b/src/libstore/globals.cc index 0f2ca4b15cad..ff7ea090fad5 100644 --- a/src/libstore/globals.cc +++ b/src/libstore/globals.cc @@ -13,10 +13,8 @@ #include - namespace nix { - /* The default location of the daemon socket, relative to nixStateDir. The socket is in a directory to allow you to control access to the Nix daemon by setting the mode/ownership of the directory @@ -29,24 +27,31 @@ Settings settings; static GlobalConfig::Register rSettings(&settings); Settings::Settings() - : nixPrefix(NIX_PREFIX) - , nixStore(canonPath(getEnv("NIX_STORE_DIR").value_or(getEnv("NIX_STORE").value_or(NIX_STORE_DIR)))) - , nixDataDir(canonPath(getEnv("NIX_DATA_DIR").value_or(NIX_DATA_DIR))) - , nixLogDir(canonPath(getEnv("NIX_LOG_DIR").value_or(NIX_LOG_DIR))) - , nixStateDir(canonPath(getEnv("NIX_STATE_DIR").value_or(NIX_STATE_DIR))) - , nixConfDir(canonPath(getEnv("NIX_CONF_DIR").value_or(NIX_CONF_DIR))) - , nixUserConfFiles(getUserConfigFiles()) - , nixBinDir(canonPath(getEnv("NIX_BIN_DIR").value_or(NIX_BIN_DIR))) - , nixManDir(canonPath(NIX_MAN_DIR)) - , nixDaemonSocketFile(canonPath(getEnv("NIX_DAEMON_SOCKET_PATH").value_or(nixStateDir + DEFAULT_SOCKET_PATH))) + : nixPrefix(NIX_PREFIX), + nixStore(canonPath( + getEnv("NIX_STORE_DIR") + .value_or(getEnv("NIX_STORE").value_or(NIX_STORE_DIR)))), + nixDataDir(canonPath(getEnv("NIX_DATA_DIR").value_or(NIX_DATA_DIR))), + nixLogDir(canonPath(getEnv("NIX_LOG_DIR").value_or(NIX_LOG_DIR))), + nixStateDir(canonPath(getEnv("NIX_STATE_DIR").value_or(NIX_STATE_DIR))), + nixConfDir(canonPath(getEnv("NIX_CONF_DIR").value_or(NIX_CONF_DIR))), + nixUserConfFiles(getUserConfigFiles()), + nixBinDir(canonPath(getEnv("NIX_BIN_DIR").value_or(NIX_BIN_DIR))), + nixManDir(canonPath(NIX_MAN_DIR)), + nixDaemonSocketFile( + canonPath(getEnv("NIX_DAEMON_SOCKET_PATH") + .value_or(nixStateDir + DEFAULT_SOCKET_PATH))) { buildUsersGroup = getuid() == 0 ? "nixbld" : ""; lockCPU = getEnv("NIX_AFFINITY_HACK") == "1"; allowSymlinkedStore = getEnv("NIX_IGNORE_SYMLINK_STORE") == "1"; - caFile = getEnv("NIX_SSL_CERT_FILE").value_or(getEnv("SSL_CERT_FILE").value_or("")); + caFile = getEnv("NIX_SSL_CERT_FILE") + .value_or(getEnv("SSL_CERT_FILE").value_or("")); if (caFile == "") { - for (auto & fn : {"/etc/ssl/certs/ca-certificates.crt", "/nix/var/nix/profiles/default/etc/ssl/certs/ca-bundle.crt"}) + for (auto & fn : + {"/etc/ssl/certs/ca-certificates.crt", + "/nix/var/nix/profiles/default/etc/ssl/certs/ca-bundle.crt"}) if (pathExists(fn)) { caFile = fn; break; @@ -68,8 +73,11 @@ Settings::Settings() /* chroot-like behavior from Apple's sandbox */ #if __APPLE__ - sandboxPaths = tokenizeString("/System/Library/Frameworks /System/Library/PrivateFrameworks /bin/sh /bin/bash /private/tmp /private/var/tmp /usr/lib"); - allowedImpureHostPrefixes = tokenizeString("/System/Library /usr/lib /dev /bin/sh"); + sandboxPaths = tokenizeString( + "/System/Library/Frameworks /System/Library/PrivateFrameworks /bin/sh " + "/bin/bash /private/tmp /private/var/tmp /usr/lib"); + allowedImpureHostPrefixes = + tokenizeString("/System/Library /usr/lib /dev /bin/sh"); #endif buildHook = getSelfExe().value_or("nix") + " __build-remote"; @@ -92,7 +100,6 @@ void loadConfFile() if (nixConfEnv.has_value()) { globalConfig.applyConfig(nixConfEnv.value(), "NIX_CONFIG"); } - } std::vector getUserConfigFiles() @@ -100,7 +107,8 @@ std::vector getUserConfigFiles() // Use the paths specified in NIX_USER_CONF_FILES if it has been defined auto nixConfFiles = getEnv("NIX_USER_CONF_FILES"); if (nixConfFiles.has_value()) { - return tokenizeString>(nixConfFiles.value(), ":"); + return tokenizeString>(nixConfFiles.value(), + ":"); } // Use the paths specified by the XDG spec @@ -124,10 +132,10 @@ StringSet Settings::getDefaultSystemFeatures() actually require anything special on the machines. */ StringSet features{"nixos-test", "benchmark", "big-parallel"}; - #if __linux__ +#if __linux__ if (access("/dev/kvm", R_OK | W_OK) == 0) features.insert("kvm"); - #endif +#endif return features; } @@ -148,7 +156,8 @@ StringSet Settings::getDefaultExtraPlatforms() // machines. Note that we can’t force processes from executing // x86_64 in aarch64 environments or vice versa since they can // always exec with their own binary preferences. - if (pathExists("/Library/Apple/System/Library/LaunchDaemons/com.apple.oahd.plist") || + if (pathExists("/Library/Apple/System/Library/LaunchDaemons/" + "com.apple.oahd.plist") || pathExists("/System/Library/LaunchDaemons/com.apple.oahd.plist")) { if (std::string{SYSTEM} == "x86_64-darwin") extraPlatforms.insert("aarch64-darwin"); @@ -183,75 +192,81 @@ bool Settings::isWSL1() const std::string nixVersion = PACKAGE_VERSION; -NLOHMANN_JSON_SERIALIZE_ENUM(SandboxMode, { - {SandboxMode::smEnabled, true}, - {SandboxMode::smRelaxed, "relaxed"}, - {SandboxMode::smDisabled, false}, -}); +NLOHMANN_JSON_SERIALIZE_ENUM(SandboxMode, + { + {SandboxMode::smEnabled, true}, + {SandboxMode::smRelaxed, "relaxed"}, + {SandboxMode::smDisabled, false}, + }); -template<> void BaseSetting::set(const std::string & str, bool append) +template<> +void BaseSetting::set(const std::string & str, bool append) { - if (str == "true") value = smEnabled; - else if (str == "relaxed") value = smRelaxed; - else if (str == "false") value = smDisabled; - else throw UsageError("option '%s' has invalid value '%s'", name, str); + if (str == "true") + value = smEnabled; + else if (str == "relaxed") + value = smRelaxed; + else if (str == "false") + value = smDisabled; + else + throw UsageError("option '%s' has invalid value '%s'", name, str); } -template<> bool BaseSetting::isAppendable() -{ - return false; -} +template<> bool BaseSetting::isAppendable() { return false; } template<> std::string BaseSetting::to_string() const { - if (value == smEnabled) return "true"; - else if (value == smRelaxed) return "relaxed"; - else if (value == smDisabled) return "false"; - else abort(); + if (value == smEnabled) + return "true"; + else if (value == smRelaxed) + return "relaxed"; + else if (value == smDisabled) + return "false"; + else + abort(); } -template<> void BaseSetting::convertToArg(Args & args, const std::string & category) +template<> +void BaseSetting::convertToArg(Args & args, + const std::string & category) { - args.addFlag({ - .longName = name, - .description = "Enable sandboxing.", - .category = category, - .handler = {[=]() { override(smEnabled); }} - }); - args.addFlag({ - .longName = "no-" + name, - .description = "Disable sandboxing.", - .category = category, - .handler = {[=]() { override(smDisabled); }} - }); - args.addFlag({ - .longName = "relaxed-" + name, - .description = "Enable sandboxing, but allow builds to disable it.", - .category = category, - .handler = {[=]() { override(smRelaxed); }} - }); + args.addFlag({.longName = name, + .description = "Enable sandboxing.", + .category = category, + .handler = {[=]() { override(smEnabled); }}}); + args.addFlag({.longName = "no-" + name, + .description = "Disable sandboxing.", + .category = category, + .handler = {[=]() { override(smDisabled); }}}); + args.addFlag( + {.longName = "relaxed-" + name, + .description = "Enable sandboxing, but allow builds to disable it.", + .category = category, + .handler = {[=]() { override(smRelaxed); }}}); } void MaxBuildJobsSetting::set(const std::string & str, bool append) { - if (str == "auto") value = std::max(1U, std::thread::hardware_concurrency()); + if (str == "auto") + value = std::max(1U, std::thread::hardware_concurrency()); else { if (auto n = string2Int(str)) value = *n; else - throw UsageError("configuration setting '%s' should be 'auto' or an integer", name); + throw UsageError( + "configuration setting '%s' should be 'auto' or an integer", + name); } } - void PluginFilesSetting::set(const std::string & str, bool append) { if (pluginsLoaded) - throw UsageError("plugin-files set after plugins were loaded, you may need to move the flag before the subcommand"); + throw UsageError("plugin-files set after plugins were loaded, you may " + "need to move the flag before the subcommand"); BaseSetting::set(str, append); } - void initPlugins() { assert(!settings.pluginFiles.pluginsLoaded); @@ -269,10 +284,10 @@ void initPlugins() for (const auto & file : pluginFiles) { /* handle is purposefully leaked as there may be state in the DSO needed by the action of the plugin. */ - void *handle = - dlopen(file.c_str(), RTLD_LAZY | RTLD_LOCAL); + void * handle = dlopen(file.c_str(), RTLD_LAZY | RTLD_LOCAL); if (!handle) - throw Error("could not dynamically open plugin file '%s': %s", file, dlerror()); + throw Error("could not dynamically open plugin file '%s': %s", + file, dlerror()); } } @@ -281,7 +296,8 @@ void initPlugins() globalConfig.reapplyUnknownSettings(); globalConfig.warnUnknownSettings(); - /* Tell the user if they try to set plugin-files after we've already loaded */ + /* Tell the user if they try to set plugin-files after we've already loaded + */ settings.pluginFiles.pluginsLoaded = true; } diff --git a/src/libstore/globals.hh b/src/libstore/globals.hh index d7f351166ad7..936efc1812b9 100644 --- a/src/libstore/globals.hh +++ b/src/libstore/globals.hh @@ -14,13 +14,11 @@ namespace nix { typedef enum { smEnabled, smRelaxed, smDisabled } SandboxMode; -struct MaxBuildJobsSetting : public BaseSetting -{ - MaxBuildJobsSetting(Config * options, - unsigned int def, - const std::string & name, - const std::string & description, - const std::set & aliases = {}) +struct MaxBuildJobsSetting : public BaseSetting { + MaxBuildJobsSetting(Config * options, unsigned int def, + const std::string & name, + const std::string & description, + const std::set & aliases = {}) : BaseSetting(def, true, name, description, aliases) { options->addSetting(this); @@ -29,15 +27,13 @@ struct MaxBuildJobsSetting : public BaseSetting void set(const std::string & str, bool append = false) override; }; -struct PluginFilesSetting : public BaseSetting -{ +struct PluginFilesSetting : public BaseSetting { bool pluginsLoaded = false; - PluginFilesSetting(Config * options, - const Paths & def, - const std::string & name, - const std::string & description, - const std::set & aliases = {}) + PluginFilesSetting(Config * options, const Paths & def, + const std::string & name, + const std::string & description, + const std::set & aliases = {}) : BaseSetting(def, true, name, description, aliases) { options->addSetting(this); @@ -56,8 +52,7 @@ class Settings : public Config { bool isWSL1(); -public: - + public: Settings(); Path nixPrefix; @@ -88,34 +83,38 @@ public: /* File name of the socket the daemon listens to. */ Path nixDaemonSocketFile; - Setting storeUri{this, getEnv("NIX_REMOTE").value_or("auto"), "store", - "The default Nix store to use."}; + Setting storeUri{this, getEnv("NIX_REMOTE").value_or("auto"), + "store", "The default Nix store to use."}; - Setting keepFailed{this, false, "keep-failed", + Setting keepFailed{ + this, false, "keep-failed", "Whether to keep temporary directories of failed builds."}; - Setting keepGoing{this, false, "keep-going", + Setting keepGoing{ + this, false, "keep-going", "Whether to keep building derivations when another build fails."}; - Setting tryFallback{ - this, false, "fallback", - R"( + Setting tryFallback{this, + false, + "fallback", + R"( If set to `true`, Nix will fall back to building from source if a binary substitute fails. This is equivalent to the `--fallback` flag. The default is `false`. )", - {"build-fallback"}}; + {"build-fallback"}}; /* Whether to show build log output in real time. */ bool verboseBuild = true; Setting logLines{this, 10, "log-lines", - "The number of lines of the tail of " - "the log to show if a build fails."}; + "The number of lines of the tail of " + "the log to show if a build fails."}; - MaxBuildJobsSetting maxBuildJobs{ - this, 1, "max-jobs", - R"( + MaxBuildJobsSetting maxBuildJobs{this, + 1, + "max-jobs", + R"( This option defines the maximum number of jobs that Nix will try to build in parallel. The default is `1`. The special value `auto` causes Nix to use the number of CPUs in your system. `0` is useful @@ -124,13 +123,10 @@ public: regardless). It can be overridden using the `--max-jobs` (`-j`) command line switch. )", - {"build-max-jobs"}}; + {"build-max-jobs"}}; - Setting buildCores{ - this, - getDefaultCores(), - "cores", - R"( + Setting buildCores{this, getDefaultCores(), "cores", + R"( Sets the value of the `NIX_BUILD_CORES` environment variable in the invocation of builders. Builders can use this variable at their discretion to control the maximum amount of parallelism. For @@ -139,16 +135,14 @@ public: `-jN` flag to GNU Make. It can be overridden using the `--cores` command line switch and defaults to `1`. The value `0` means that the builder should use all available CPU cores in the system. - )", - {"build-cores"}, false}; + )", {"build-cores"}, false}; /* Read-only mode. Don't copy stuff to the store, don't change the database. */ bool readOnlyMode = false; - Setting thisSystem{ - this, SYSTEM, "system", - R"( + Setting thisSystem{this, SYSTEM, "system", + R"( This option specifies the canonical Nix system name of the current installation, such as `i686-linux` or `x86_64-darwin`. Nix can only build derivations whose `system` attribute equals the value @@ -163,9 +157,10 @@ public: at build time. )"}; - Setting maxSilentTime{ - this, 0, "max-silent-time", - R"( + Setting maxSilentTime{this, + 0, + "max-silent-time", + R"( This option defines the maximum number of seconds that a builder can go without producing any data on standard output or standard error. This is useful (for instance in an automated build system) to catch @@ -176,11 +171,12 @@ public: The value `0` means that there is no timeout. This is also the default. )", - {"build-max-silent-time"}}; + {"build-max-silent-time"}}; - Setting buildTimeout{ - this, 0, "timeout", - R"( + Setting buildTimeout{this, + 0, + "timeout", + R"( This option defines the maximum number of seconds that a builder can run. This is useful (for instance in an automated build system) to catch builds that are stuck in an infinite loop but keep writing to @@ -190,21 +186,22 @@ public: The value `0` means that there is no timeout. This is also the default. )", - {"build-timeout"}}; + {"build-timeout"}}; PathSetting buildHook{this, true, "", "build-hook", - "The path of the helper program that executes builds to remote machines."}; + "The path of the helper program that executes builds " + "to remote machines."}; - Setting builders{ - this, "@" + nixConfDir + "/machines", "builders", - R"( + Setting builders{this, "@" + nixConfDir + "/machines", + "builders", + R"( A semicolon-separated list of build machines. For the exact format and examples, see [the manual chapter on remote builds](../advanced-topics/distributed-builds.md) )"}; - Setting buildersUseSubstitutes{ - this, false, "builders-use-substitutes", - R"( + Setting buildersUseSubstitutes{this, false, + "builders-use-substitutes", + R"( If set to `true`, Nix will instruct remote build machines to use their own binary substitutes if available. In practical terms, this means that remote hosts will fetch as many build dependencies as @@ -214,12 +211,12 @@ public: this computer and the remote build host is slow. )"}; - Setting reservedSize{this, 8 * 1024 * 1024, "gc-reserved-space", + Setting reservedSize{ + this, 8 * 1024 * 1024, "gc-reserved-space", "Amount of reserved disk space for the garbage collector."}; - Setting fsyncMetadata{ - this, true, "fsync-metadata", - R"( + Setting fsyncMetadata{this, true, "fsync-metadata", + R"( If set to `true`, changes to the Nix store metadata (in `/nix/var/nix/db`) are synchronously flushed to disk. This improves robustness in case of system crashes, but reduces performance. The @@ -227,23 +224,24 @@ public: )"}; Setting useSQLiteWAL{this, !isWSL1(), "use-sqlite-wal", - "Whether SQLite should use WAL mode."}; + "Whether SQLite should use WAL mode."}; - Setting syncBeforeRegistering{this, false, "sync-before-registering", + Setting syncBeforeRegistering{ + this, false, "sync-before-registering", "Whether to call `sync()` before registering a path as valid."}; - Setting useSubstitutes{ - this, true, "substitute", - R"( + Setting useSubstitutes{this, + true, + "substitute", + R"( If set to `true` (default), Nix will use binary substitutes if available. This option can be disabled to force building from source. )", - {"build-use-substitutes"}}; + {"build-use-substitutes"}}; - Setting buildUsersGroup{ - this, "", "build-users-group", - R"( + Setting buildUsersGroup{this, "", "build-users-group", + R"( This options specifies the Unix group containing the Nix build user accounts. In multi-user Nix installations, builds should not be performed by the Nix account since that would allow users to @@ -275,49 +273,57 @@ public: multi-user settings with untrusted users. )"}; - Setting impersonateLinux26{this, false, "impersonate-linux-26", + Setting impersonateLinux26{ + this, + false, + "impersonate-linux-26", "Whether to impersonate a Linux 2.6 machine on newer kernels.", {"build-impersonate-linux-26"}}; - Setting keepLog{ - this, true, "keep-build-log", - R"( + Setting keepLog{this, + true, + "keep-build-log", + R"( If set to `true` (the default), Nix will write the build log of a derivation (i.e. the standard output and error of its builder) to the directory `/nix/var/log/nix/drvs`. The build log can be retrieved using the command `nix-store -l path`. )", - {"build-keep-log"}}; + {"build-keep-log"}}; - Setting compressLog{ - this, true, "compress-build-log", - R"( + Setting compressLog{this, + true, + "compress-build-log", + R"( If set to `true` (the default), build logs written to `/nix/var/log/nix/drvs` will be compressed on the fly using bzip2. Otherwise, they will not be compressed. )", - {"build-compress-log"}}; + {"build-compress-log"}}; - Setting maxLogSize{ - this, 0, "max-build-log-size", - R"( + Setting maxLogSize{this, + 0, + "max-build-log-size", + R"( This option defines the maximum number of bytes that a builder can write to its stdout/stderr. If the builder exceeds this limit, it’s killed. A value of `0` (the default) means that there is no limit. )", - {"build-max-log-size"}}; + {"build-max-log-size"}}; /* When buildRepeat > 0 and verboseBuild == true, whether to print repeated builds (i.e. builds other than the first one) to stderr. Hack to prevent Hydra logs from being polluted. */ bool printRepeatedBuilds = true; - Setting pollInterval{this, 5, "build-poll-interval", + Setting pollInterval{ + this, 5, "build-poll-interval", "How often (in seconds) to poll for locks."}; - Setting gcKeepOutputs{ - this, false, "keep-outputs", - R"( + Setting gcKeepOutputs{this, + false, + "keep-outputs", + R"( If `true`, the garbage collector will keep the outputs of non-garbage derivations. If `false` (default), outputs will be deleted unless they are GC roots themselves (or reachable from other @@ -329,11 +335,12 @@ public: time (e.g., the C compiler, or source tarballs downloaded from the network). To prevent it from doing so, set this option to `true`. )", - {"gc-keep-outputs"}}; + {"gc-keep-outputs"}}; - Setting gcKeepDerivations{ - this, true, "keep-derivations", - R"( + Setting gcKeepDerivations{this, + true, + "keep-derivations", + R"( If `true` (default), the garbage collector will keep the derivations from which non-garbage store paths were built. If `false`, they will be deleted unless explicitly registered as a root (or reachable from @@ -345,11 +352,10 @@ public: to save a bit of disk space (or a lot if `keep-outputs` is also turned on). )", - {"gc-keep-derivations"}}; + {"gc-keep-derivations"}}; - Setting autoOptimiseStore{ - this, false, "auto-optimise-store", - R"( + Setting autoOptimiseStore{this, false, "auto-optimise-store", + R"( If set to `true`, Nix automatically detects files in the store that have identical contents, and replaces them with hard links to a single copy. This saves disk space. If set to `false` (the @@ -357,9 +363,10 @@ public: duplicate files. )"}; - Setting envKeepDerivations{ - this, false, "keep-env-derivations", - R"( + Setting envKeepDerivations{this, + false, + "keep-env-derivations", + R"( If `false` (default), derivations are not stored in Nix user environments. That is, the derivations of any build-time-only dependencies may be garbage-collected. @@ -376,20 +383,22 @@ public: while this option was enabled, while `keep-derivations` only applies at the moment the garbage collector is run. )", - {"env-keep-derivations"}}; + {"env-keep-derivations"}}; /* Whether to lock the Nix client and worker to the same CPU. */ bool lockCPU; - Setting sandboxMode{ + Setting sandboxMode + { this, - #if __linux__ - smEnabled - #else - smDisabled - #endif - , "sandbox", - R"( +#if __linux__ + smEnabled +#else + smDisabled +#endif + , + "sandbox", + R"( If set to `true`, builds will be performed in a *sandboxed environment*, i.e., they’re isolated from the normal file system hierarchy and will only see their dependencies in the Nix store, @@ -414,11 +423,15 @@ public: The default is `true` on Linux and `false` on all other platforms. )", - {"build-use-chroot", "build-use-sandbox"}}; - - Setting sandboxPaths{ - this, {}, "sandbox-paths", - R"( + { + "build-use-chroot", "build-use-sandbox" + } + }; + + Setting sandboxPaths{this, + {}, + "sandbox-paths", + R"( A list of paths bind-mounted into Nix sandbox environments. You can use the syntax `target=source` to mount a path in a different location in the sandbox; for instance, `/bin=/nix-bin` will mount @@ -430,14 +443,16 @@ public: Depending on how Nix was built, the default value for this option may be empty or provide `/bin/sh` as a bind-mount of `bash`. )", - {"build-chroot-dirs", "build-sandbox-paths"}}; + {"build-chroot-dirs", "build-sandbox-paths"}}; - Setting sandboxFallback{this, true, "sandbox-fallback", + Setting sandboxFallback{ + this, true, "sandbox-fallback", "Whether to disable sandboxing when the kernel doesn't allow it."}; - Setting buildRepeat{ - this, 0, "repeat", - R"( + Setting buildRepeat{this, + 0, + "repeat", + R"( How many times to repeat builds to check whether they are deterministic. The default value is 0. If the value is non-zero, every build is repeated the specified number of times. If the @@ -446,12 +461,11 @@ public: resulting store paths are not registered as “valid” in Nix’s database. )", - {"build-repeat"}}; + {"build-repeat"}}; #if __linux__ - Setting sandboxShmSize{ - this, "50%", "sandbox-dev-shm-size", - R"( + Setting sandboxShmSize{this, "50%", "sandbox-dev-shm-size", + R"( This option determines the maximum size of the `tmpfs` filesystem mounted on `/dev/shm` in Linux sandboxes. For the format, see the description of the `size` option of `tmpfs` in mount8. The default @@ -459,20 +473,24 @@ public: )"}; Setting sandboxBuildDir{this, "/build", "sandbox-build-dir", - "The build directory inside the sandbox."}; + "The build directory inside the sandbox."}; #endif - Setting allowedImpureHostPrefixes{this, {}, "allowed-impure-host-deps", - "Which prefixes to allow derivations to ask for access to (primarily for Darwin)."}; + Setting allowedImpureHostPrefixes{ + this, + {}, + "allowed-impure-host-deps", + "Which prefixes to allow derivations to ask for access to (primarily " + "for Darwin)."}; #if __APPLE__ - Setting darwinLogSandboxViolations{this, false, "darwin-log-sandbox-violations", + Setting darwinLogSandboxViolations{ + this, false, "darwin-log-sandbox-violations", "Whether to log Darwin sandbox access violations to the system log."}; #endif - Setting runDiffHook{ - this, false, "run-diff-hook", - R"( + Setting runDiffHook{this, false, "run-diff-hook", + R"( If true, enable the execution of the `diff-hook` program. When using the Nix daemon, `run-diff-hook` must be set in the @@ -480,9 +498,8 @@ public: line. )"}; - PathSetting diffHook{ - this, true, "", "diff-hook", - R"( + PathSetting diffHook{this, true, "", "diff-hook", + R"( Absolute path to an executable capable of diffing build results. The hook is executed if `run-diff-hook` is true, and the output of a build is known to not be the same. This program is not @@ -511,9 +528,9 @@ public: configuration file, and cannot be passed at the command line. )"}; - Setting enforceDeterminism{ - this, true, "enforce-determinism", - "Whether to fail if repeated builds produce different output. See `repeat`."}; + Setting enforceDeterminism{this, true, "enforce-determinism", + "Whether to fail if repeated builds " + "produce different output. See `repeat`."}; Setting trustedPublicKeys{ this, @@ -528,9 +545,10 @@ public: )", {"binary-cache-public-keys"}}; - Setting secretKeyFiles{ - this, {}, "secret-key-files", - R"( + Setting secretKeyFiles{this, + {}, + "secret-key-files", + R"( A whitespace-separated list of files containing secret (private) keys. These are used to sign locally-built paths. They can be generated using `nix-store --generate-binary-cache-key`. The @@ -538,9 +556,8 @@ public: can add it to `trusted-public-keys` in their `nix.conf`. )"}; - Setting tarballTtl{ - this, 60 * 60, "tarball-ttl", - R"( + Setting tarballTtl{this, 60 * 60, "tarball-ttl", + R"( The number of seconds a downloaded tarball is considered fresh. If the cached tarball is stale, Nix will check whether it is still up to date using the ETag header. Nix will download a new version if @@ -555,9 +572,8 @@ public: `fetchTarball`, and `fetchurl` respect this TTL. )"}; - Setting requireSigs{ - this, true, "require-sigs", - R"( + Setting requireSigs{this, true, "require-sigs", + R"( If set to `true` (the default), any non-content-addressed path added or copied to the Nix store (e.g. when substituting from a binary cache) must have a valid signature, that is, be signed using one of @@ -565,11 +581,10 @@ public: to `false` to disable signature checking. )"}; - Setting extraPlatforms{ - this, - getDefaultExtraPlatforms(), - "extra-platforms", - R"( + Setting extraPlatforms{this, + getDefaultExtraPlatforms(), + "extra-platforms", + R"( Platforms other than the native one which this machine is capable of building for. This can be useful for supporting additional architectures on compatible machines: i686-linux can be built on @@ -582,13 +597,14 @@ public: platform and generate incompatible code, so you may wish to cross-check the results of using this option against proper natively-built versions of your derivations. - )", {}, false}; + )", + {}, + false}; - Setting systemFeatures{ - this, - getDefaultSystemFeatures(), - "system-features", - R"( + Setting systemFeatures{this, + getDefaultSystemFeatures(), + "system-features", + R"( A set of system “features” supported by this machine, e.g. `kvm`. Derivations can express a dependency on such features through the derivation attribute `requiredSystemFeatures`. For example, the @@ -602,34 +618,37 @@ public: This setting by default includes `kvm` if `/dev/kvm` is accessible, and the pseudo-features `nixos-test`, `benchmark` and `big-parallel` that are used in Nixpkgs to route builds to specific machines. - )", {}, false}; + )", + {}, + false}; - Setting substituters{ - this, - Strings{"https://cache.nixos.org/"}, - "substituters", - R"( + Setting substituters{this, + Strings{"https://cache.nixos.org/"}, + "substituters", + R"( A list of URLs of substituters, separated by whitespace. Substituters are tried based on their Priority value, which each substituter can set independently. Lower value means higher priority. The default is `https://cache.nixos.org`, with a Priority of 40. )", - {"binary-caches"}}; + {"binary-caches"}}; - Setting trustedSubstituters{ - this, {}, "trusted-substituters", - R"( + Setting trustedSubstituters{this, + {}, + "trusted-substituters", + R"( A list of URLs of substituters, separated by whitespace. These are not used by default, but can be enabled by users of the Nix daemon by specifying `--option substituters urls` on the command line. Unprivileged users are only allowed to pass a subset of the URLs listed in `substituters` and `trusted-substituters`. )", - {"trusted-binary-caches"}}; + {"trusted-binary-caches"}}; - Setting trustedUsers{ - this, {"root"}, "trusted-users", - R"( + Setting trustedUsers{this, + {"root"}, + "trusted-users", + R"( A list of names of users (separated by whitespace) that have additional rights when connecting to the Nix daemon, such as the ability to specify additional binary caches, or to import unsigned @@ -645,18 +664,18 @@ public: > directories that are otherwise inacessible to them. )"}; - Setting ttlNegativeNarInfoCache{ - this, 3600, "narinfo-cache-negative-ttl", - R"( + Setting ttlNegativeNarInfoCache{this, 3600, + "narinfo-cache-negative-ttl", + R"( The TTL in seconds for negative lookups. If a store path is queried from a substituter but was not found, there will be a negative lookup cached in the local disk cache database for the specified duration. )"}; - Setting ttlPositiveNarInfoCache{ - this, 30 * 24 * 3600, "narinfo-cache-positive-ttl", - R"( + Setting ttlPositiveNarInfoCache{this, 30 * 24 * 3600, + "narinfo-cache-positive-ttl", + R"( The TTL in seconds for positive lookups. If a store path is queried from a substituter, the result of the query will be cached in the local disk cache database including some of the NAR metadata. The @@ -668,9 +687,10 @@ public: )"}; /* ?Who we trust to use the daemon in safe ways */ - Setting allowedUsers{ - this, {"*"}, "allowed-users", - R"( + Setting allowedUsers{this, + {"*"}, + "allowed-users", + R"( A list of names of users (separated by whitespace) that are allowed to connect to the Nix daemon. As with the `trusted-users` option, you can specify groups by prefixing them with `@`. Also, you can @@ -679,12 +699,12 @@ public: Note that trusted users are always allowed to connect. )"}; - Setting printMissing{this, true, "print-missing", + Setting printMissing{ + this, true, "print-missing", "Whether to print what paths need to be built or downloaded."}; - Setting preBuildHook{ - this, "", "pre-build-hook", - R"( + Setting preBuildHook{this, "", "pre-build-hook", + R"( If set, the path to a program that can set extra derivation-specific settings for this system. This is used for settings that can't be captured by the derivation model itself and are too variable between @@ -701,9 +721,8 @@ public: empty line. Entries have the same format as `sandbox-paths`. )"}; - Setting postBuildHook{ - this, "", "post-build-hook", - R"( + Setting postBuildHook{this, "", "post-build-hook", + R"( Optional. The path to a program to execute after each build. This option is only settable in the global `nix.conf`, or on the @@ -746,9 +765,9 @@ public: /nix/store/xfghy8ixrhz3kyy6p724iv3cxji088dx-bash-4.4-p23`. )"}; - Setting netrcFile{ - this, fmt("%s/%s", nixConfDir, "netrc"), "netrc-file", - R"( + Setting netrcFile{this, fmt("%s/%s", nixConfDir, "netrc"), + "netrc-file", + R"( If set to an absolute path to a `netrc` file, Nix will use the HTTP authentication credentials in this file when trying to download from a remote host through HTTP or HTTPS. Defaults to @@ -775,18 +794,16 @@ public: Path caFile; #if __linux__ - Setting filterSyscalls{ - this, true, "filter-syscalls", - R"( + Setting filterSyscalls{this, true, "filter-syscalls", + R"( Whether to prevent certain dangerous system calls, such as creation of setuid/setgid files or adding ACLs or extended attributes. Only disable this if you're aware of the security implications. )"}; - Setting allowNewPrivileges{ - this, false, "allow-new-privileges", - R"( + Setting allowNewPrivileges{this, false, "allow-new-privileges", + R"( (Linux-specific.) By default, builders on Linux cannot acquire new privileges by calling setuid/setgid programs or programs that have file capabilities. For example, programs such as `sudo` or `ping` @@ -799,7 +816,9 @@ public: )"}; Setting ignoredAcls{ - this, {"security.selinux", "system.nfs4_acl", "security.csm"}, "ignored-acls", + this, + {"security.selinux", "system.nfs4_acl", "security.csm"}, + "ignored-acls", R"( A list of ACLs that should be ignored, normally Nix attempts to remove all ACLs from files and directories in the Nix store, but @@ -808,9 +827,10 @@ public: )"}; #endif - Setting hashedMirrors{ - this, {}, "hashed-mirrors", - R"( + Setting hashedMirrors{this, + {}, + "hashed-mirrors", + R"( 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 @@ -831,29 +851,30 @@ public: first. If it is not available there, if will try the original URI. )"}; - Setting minFree{ - this, 0, "min-free", - R"( + Setting minFree{this, 0, "min-free", + R"( When free disk space in `/nix/store` drops below `min-free` during a build, Nix performs a garbage-collection until `max-free` bytes are available or there is no more garbage. A value of `0` (the default) disables this feature. )"}; - Setting maxFree{ - this, std::numeric_limits::max(), "max-free", - R"( + Setting maxFree{this, std::numeric_limits::max(), + "max-free", + R"( When a garbage collection is triggered by the `min-free` option, it stops as soon as `max-free` bytes are available. The default is infinity (i.e. delete all garbage). )"}; - Setting minFreeCheckInterval{this, 5, "min-free-check-interval", + Setting minFreeCheckInterval{ + this, 5, "min-free-check-interval", "Number of seconds between checking free disk space."}; - PluginFilesSetting pluginFiles{ - this, {}, "plugin-files", - R"( + PluginFilesSetting pluginFiles{this, + {}, + "plugin-files", + R"( A list of plugin files to be loaded by Nix. Each of these files will be dlopened by Nix, allowing them to affect execution through static initialization. In particular, these plugins may construct static @@ -877,19 +898,22 @@ public: are loaded as plugins (non-recursively). )"}; - Setting> experimentalFeatures{this, {}, "experimental-features", + Setting> experimentalFeatures{ + this, + {}, + "experimental-features", "Experimental Nix features to enable."}; bool isExperimentalFeatureEnabled(const ExperimentalFeature &); void requireExperimentalFeature(const ExperimentalFeature &); - Setting narBufferSize{this, 32 * 1024 * 1024, "nar-buffer-size", + Setting narBufferSize{ + this, 32 * 1024 * 1024, "nar-buffer-size", "Maximum size of NARs before spilling them to disk."}; - Setting allowSymlinkedStore{ - this, false, "allow-symlinked-store", - R"( + Setting allowSymlinkedStore{this, false, "allow-symlinked-store", + R"( If set to `true`, Nix will stop complaining if the store directory (typically /nix/store) contains symlink components. @@ -901,7 +925,6 @@ public: )"}; }; - // FIXME: don't use a global variable. extern Settings settings; diff --git a/src/libstore/http-binary-cache-store.cc b/src/libstore/http-binary-cache-store.cc index 73bcd6e817da..8a16ba401913 100644 --- a/src/libstore/http-binary-cache-store.cc +++ b/src/libstore/http-binary-cache-store.cc @@ -8,39 +8,30 @@ namespace nix { MakeError(UploadToHTTP, Error); -struct HttpBinaryCacheStoreConfig : virtual BinaryCacheStoreConfig -{ +struct HttpBinaryCacheStoreConfig : virtual BinaryCacheStoreConfig { using BinaryCacheStoreConfig::BinaryCacheStoreConfig; const std::string name() override { return "Http Binary Cache Store"; } }; -class HttpBinaryCacheStore : public virtual HttpBinaryCacheStoreConfig, public virtual BinaryCacheStore -{ -private: - +class HttpBinaryCacheStore : public virtual HttpBinaryCacheStoreConfig, + public virtual BinaryCacheStore { + private: Path cacheUri; - struct State - { + struct State { bool enabled = true; std::chrono::steady_clock::time_point disabledUntil; }; Sync _state; -public: - - HttpBinaryCacheStore( - const std::string & scheme, - const Path & _cacheUri, - const Params & params) - : StoreConfig(params) - , BinaryCacheStoreConfig(params) - , HttpBinaryCacheStoreConfig(params) - , Store(params) - , BinaryCacheStore(params) - , cacheUri(scheme + "://" + _cacheUri) + public: + HttpBinaryCacheStore(const std::string & scheme, const Path & _cacheUri, + const Params & params) + : StoreConfig(params), BinaryCacheStoreConfig(params), + HttpBinaryCacheStoreConfig(params), Store(params), + BinaryCacheStore(params), cacheUri(scheme + "://" + _cacheUri) { if (cacheUri.back() == '/') cacheUri.pop_back(); @@ -48,10 +39,7 @@ class HttpBinaryCacheStore : public virtual HttpBinaryCacheStoreConfig, public v diskCache = getNarInfoDiskCache(); } - std::string getUri() override - { - return cacheUri; - } + std::string getUri() override { return cacheUri; } void init() override { @@ -63,7 +51,8 @@ class HttpBinaryCacheStore : public virtual HttpBinaryCacheStoreConfig, public v try { BinaryCacheStore::init(); } catch (UploadToHTTP &) { - throw Error("'%s' does not appear to be a binary cache", cacheUri); + throw Error("'%s' does not appear to be a binary cache", + cacheUri); } diskCache->createCache(cacheUri, storeDir, wantMassQuery, priority); } @@ -73,27 +62,30 @@ class HttpBinaryCacheStore : public virtual HttpBinaryCacheStoreConfig, public v { static bool forceHttp = getEnv("_NIX_FORCE_HTTP") == "1"; auto ret = std::set({"http", "https"}); - if (forceHttp) ret.insert("file"); + if (forceHttp) + ret.insert("file"); return ret; } -protected: - + protected: void maybeDisable() { auto state(_state.lock()); if (state->enabled && settings.tryFallback) { int t = 60; - printError("disabling binary cache '%s' for %s seconds", getUri(), t); + printError("disabling binary cache '%s' for %s seconds", getUri(), + t); state->enabled = false; - state->disabledUntil = std::chrono::steady_clock::now() + std::chrono::seconds(t); + state->disabledUntil = + std::chrono::steady_clock::now() + std::chrono::seconds(t); } } void checkEnabled() { auto state(_state.lock()); - if (state->enabled) return; + if (state->enabled) + return; if (std::chrono::steady_clock::now() > state->disabledUntil) { state->enabled = true; debug("re-enabling binary cache '%s'", getUri()); @@ -114,7 +106,8 @@ class HttpBinaryCacheStore : public virtual HttpBinaryCacheStoreConfig, public v } catch (FileTransferError & e) { /* S3 buckets return 403 if a file doesn't exist and the bucket is unlistable, so treat 403 as 404. */ - if (e.error == FileTransfer::NotFound || e.error == FileTransfer::Forbidden) + if (e.error == FileTransfer::NotFound || + e.error == FileTransfer::Forbidden) return false; maybeDisable(); throw; @@ -122,8 +115,8 @@ class HttpBinaryCacheStore : public virtual HttpBinaryCacheStoreConfig, public v } void upsertFile(const std::string & path, - std::shared_ptr> istream, - const std::string & mimeType) override + std::shared_ptr> istream, + const std::string & mimeType) override { auto req = makeRequest(path); req.data = StreamToSourceAdapter(istream).drain(); @@ -131,17 +124,19 @@ class HttpBinaryCacheStore : public virtual HttpBinaryCacheStoreConfig, public v try { getFileTransfer()->upload(req); } catch (FileTransferError & e) { - throw UploadToHTTP("while uploading to HTTP binary cache at '%s': %s", cacheUri, e.msg()); + throw UploadToHTTP( + "while uploading to HTTP binary cache at '%s': %s", cacheUri, + e.msg()); } } FileTransferRequest makeRequest(const std::string & path) { - return FileTransferRequest( - hasPrefix(path, "https://") || hasPrefix(path, "http://") || hasPrefix(path, "file://") - ? path - : cacheUri + "/" + path); - + return FileTransferRequest(hasPrefix(path, "https://") || + hasPrefix(path, "http://") || + hasPrefix(path, "file://") + ? path + : cacheUri + "/" + path); } void getFile(const std::string & path, Sink & sink) override @@ -151,15 +146,19 @@ class HttpBinaryCacheStore : public virtual HttpBinaryCacheStoreConfig, public v try { getFileTransfer()->download(std::move(request), sink); } catch (FileTransferError & e) { - if (e.error == FileTransfer::NotFound || e.error == FileTransfer::Forbidden) - throw NoSuchBinaryCacheFile("file '%s' does not exist in binary cache '%s'", path, getUri()); + if (e.error == FileTransfer::NotFound || + e.error == FileTransfer::Forbidden) + throw NoSuchBinaryCacheFile( + "file '%s' does not exist in binary cache '%s'", path, + getUri()); maybeDisable(); throw; } } - void getFile(const std::string & path, - Callback> callback) noexcept override + void + getFile(const std::string & path, + Callback> callback) noexcept override { try { checkEnabled(); @@ -170,14 +169,17 @@ class HttpBinaryCacheStore : public virtual HttpBinaryCacheStoreConfig, public v auto request(makeRequest(path)); - auto callbackPtr = std::make_shared(std::move(callback)); + auto callbackPtr = + std::make_shared(std::move(callback)); - getFileTransfer()->enqueueFileTransfer(request, + getFileTransfer()->enqueueFileTransfer( + request, {[callbackPtr, this](std::future result) { try { (*callbackPtr)(std::move(result.get().data)); } catch (FileTransferError & e) { - if (e.error == FileTransfer::NotFound || e.error == FileTransfer::Forbidden) + if (e.error == FileTransfer::NotFound || + e.error == FileTransfer::Forbidden) return (*callbackPtr)({}); maybeDisable(); callbackPtr->rethrow(); @@ -186,9 +188,10 @@ class HttpBinaryCacheStore : public virtual HttpBinaryCacheStoreConfig, public v } }}); } - }; -static RegisterStoreImplementation regHttpBinaryCacheStore; +static RegisterStoreImplementation + regHttpBinaryCacheStore; } diff --git a/src/libstore/legacy-ssh-store.cc b/src/libstore/legacy-ssh-store.cc index dd34b19c6832..ea187a368b92 100644 --- a/src/libstore/legacy-ssh-store.cc +++ b/src/libstore/legacy-ssh-store.cc @@ -12,28 +12,38 @@ namespace nix { -struct LegacySSHStoreConfig : virtual StoreConfig -{ +struct LegacySSHStoreConfig : virtual StoreConfig { using StoreConfig::StoreConfig; - const Setting maxConnections{(StoreConfig*) this, 1, "max-connections", "maximum number of concurrent SSH connections"}; - const Setting sshKey{(StoreConfig*) this, "", "ssh-key", "path to an SSH private key"}; - const Setting sshPublicHostKey{(StoreConfig*) this, "", "base64-ssh-public-host-key", "The public half of the host's SSH key"}; - const Setting compress{(StoreConfig*) this, false, "compress", "whether to compress the connection"}; - const Setting remoteProgram{(StoreConfig*) this, "nix-store", "remote-program", "path to the nix-store executable on the remote system"}; - const Setting remoteStore{(StoreConfig*) this, "", "remote-store", "URI of the store on the remote system"}; + const Setting maxConnections{ + (StoreConfig *) this, 1, "max-connections", + "maximum number of concurrent SSH connections"}; + const Setting sshKey{(StoreConfig *) this, "", "ssh-key", + "path to an SSH private key"}; + const Setting sshPublicHostKey{ + (StoreConfig *) this, "", "base64-ssh-public-host-key", + "The public half of the host's SSH key"}; + const Setting compress{(StoreConfig *) this, false, "compress", + "whether to compress the connection"}; + const Setting remoteProgram{ + (StoreConfig *) this, "nix-store", "remote-program", + "path to the nix-store executable on the remote system"}; + const Setting remoteStore{ + (StoreConfig *) this, "", "remote-store", + "URI of the store on the remote system"}; const std::string name() override { return "Legacy SSH Store"; } }; -struct LegacySSHStore : public virtual LegacySSHStoreConfig, public virtual Store -{ +struct LegacySSHStore : public virtual LegacySSHStoreConfig, + public virtual Store { // Hack for getting remote build log output. // Intentionally not in `LegacySSHStoreConfig` so that it doesn't appear in // the documentation - const Setting logFD{(StoreConfig*) this, -1, "log-fd", "file descriptor to which SSH's stderr is connected"}; + const Setting logFD{ + (StoreConfig *) this, -1, "log-fd", + "file descriptor to which SSH's stderr is connected"}; - struct Connection - { + struct Connection { std::unique_ptr sshConn; FdSink to; FdSource from; @@ -49,24 +59,16 @@ struct LegacySSHStore : public virtual LegacySSHStoreConfig, public virtual Stor static std::set uriSchemes() { return {"ssh"}; } - LegacySSHStore(const std::string & scheme, const std::string & host, const Params & params) - : StoreConfig(params) - , LegacySSHStoreConfig(params) - , Store(params) - , host(host) - , connections(make_ref>( - std::max(1, (int) maxConnections), - [this]() { return openConnection(); }, - [](const ref & r) { return r->good; } - )) - , master( - host, - sshKey, - sshPublicHostKey, - // Use SSH master only if using more than 1 connection. - connections->capacity() > 1, - compress, - logFD) + LegacySSHStore(const std::string & scheme, const std::string & host, + const Params & params) + : StoreConfig(params), LegacySSHStoreConfig(params), Store(params), + host(host), connections(make_ref>( + std::max(1, (int) maxConnections), + [this]() { return openConnection(); }, + [](const ref & r) { return r->good; })), + master(host, sshKey, sshPublicHostKey, + // Use SSH master only if using more than 1 connection. + connections->capacity() > 1, compress, logFD) { } @@ -74,8 +76,10 @@ struct LegacySSHStore : public virtual LegacySSHStoreConfig, public virtual Stor { auto conn = make_ref(); conn->sshConn = master.startCommand( - fmt("%s --serve --write", remoteProgram) - + (remoteStore.get() == "" ? "" : " --store " + shellEscape(remoteStore.get()))); + fmt("%s --serve --write", remoteProgram) + + (remoteStore.get() == "" + ? "" + : " --store " + shellEscape(remoteStore.get()))); conn->to = FdSink(conn->sshConn->in.get()); conn->from = FdSource(conn->sshConn->out.get()); @@ -88,18 +92,23 @@ struct LegacySSHStore : public virtual LegacySSHStoreConfig, public virtual Stor TeeSource tee(conn->from, saved); unsigned int magic = readInt(tee); if (magic != SERVE_MAGIC_2) - throw Error("'nix-store --serve' protocol mismatch from '%s'", host); + throw Error( + "'nix-store --serve' protocol mismatch from '%s'", + host); } catch (SerialisationError & e) { /* In case the other side is waiting for our input, close it. */ conn->sshConn->in.close(); auto msg = conn->from.drain(); - throw Error("'nix-store --serve' protocol mismatch from '%s', got '%s'", + throw Error( + "'nix-store --serve' protocol mismatch from '%s', got '%s'", host, chomp(saved.s + msg)); } conn->remoteVersion = readInt(conn->from); if (GET_PROTOCOL_MAJOR(conn->remoteVersion) != 0x200) - throw Error("unsupported 'nix-store --serve' protocol version on '%s'", host); + throw Error( + "unsupported 'nix-store --serve' protocol version on '%s'", + host); } catch (EndOfFile & e) { throw Error("cannot connect to '%1%'", host); @@ -114,7 +123,8 @@ struct LegacySSHStore : public virtual LegacySSHStoreConfig, public virtual Stor } void queryPathInfoUncached(const StorePath & path, - Callback> callback) noexcept override + Callback> + callback) noexcept override { try { auto conn(connections->get()); @@ -122,13 +132,15 @@ struct LegacySSHStore : public virtual LegacySSHStoreConfig, public virtual Stor /* No longer support missing NAR hash */ assert(GET_PROTOCOL_MINOR(conn->remoteVersion) >= 4); - debug("querying remote host '%s' for info on '%s'", host, printStorePath(path)); + debug("querying remote host '%s' for info on '%s'", host, + printStorePath(path)); conn->to << cmdQueryPathInfos << PathSet{printStorePath(path)}; conn->to.flush(); auto p = readString(conn->from); - if (p.empty()) return callback(nullptr); + if (p.empty()) + return callback(nullptr); auto path2 = parseStorePath(p); assert(path == path2); /* Hash will be set below. FIXME construct ValidPathInfo at end. */ @@ -138,7 +150,8 @@ struct LegacySSHStore : public virtual LegacySSHStoreConfig, public virtual Stor auto deriver = readString(conn->from); if (deriver != "") info->deriver = parseStorePath(deriver); - info->references = worker_proto::read(*this, conn->from, Phantom {}); + info->references = + worker_proto::read(*this, conn->from, Phantom{}); readLongLong(conn->from); // download size info->narSize = readLongLong(conn->from); @@ -155,30 +168,27 @@ struct LegacySSHStore : public virtual LegacySSHStoreConfig, public virtual Stor assert(s == ""); callback(std::move(info)); - } catch (...) { callback.rethrow(); } + } catch (...) { + callback.rethrow(); + } } void addToStore(const ValidPathInfo & info, Source & source, - RepairFlag repair, CheckSigsFlag checkSigs) override + RepairFlag repair, CheckSigsFlag checkSigs) override { - debug("adding path '%s' to remote host '%s'", printStorePath(info.path), host); + debug("adding path '%s' to remote host '%s'", printStorePath(info.path), + host); auto conn(connections->get()); if (GET_PROTOCOL_MINOR(conn->remoteVersion) >= 5) { - conn->to - << cmdAddToStoreNar - << printStorePath(info.path) - << (info.deriver ? printStorePath(*info.deriver) : "") - << info.narHash.to_string(Base16, false); + conn->to << cmdAddToStoreNar << printStorePath(info.path) + << (info.deriver ? printStorePath(*info.deriver) : "") + << info.narHash.to_string(Base16, false); worker_proto::write(*this, conn->to, info.references); - conn->to - << info.registrationTime - << info.narSize - << info.ultimate - << info.sigs - << renderContentAddress(info.ca); + conn->to << info.registrationTime << info.narSize << info.ultimate + << info.sigs << renderContentAddress(info.ca); try { copyNAR(source, conn->to); } catch (...) { @@ -189,29 +199,23 @@ struct LegacySSHStore : public virtual LegacySSHStoreConfig, public virtual Stor } else { - conn->to - << cmdImportPaths - << 1; + conn->to << cmdImportPaths << 1; try { copyNAR(source, conn->to); } catch (...) { conn->good = false; throw; } - conn->to - << exportMagic - << printStorePath(info.path); + conn->to << exportMagic << printStorePath(info.path); worker_proto::write(*this, conn->to, info.references); - conn->to - << (info.deriver ? printStorePath(*info.deriver) : "") - << 0 - << 0; + conn->to << (info.deriver ? printStorePath(*info.deriver) : "") << 0 + << 0; conn->to.flush(); - } if (readInt(conn->from) != 1) - throw Error("failed to add path '%s' to remote host '%s'", printStorePath(info.path), host); + throw Error("failed to add path '%s' to remote host '%s'", + printStorePath(info.path), host); } void narFromPath(const StorePath & path, Sink & sink) override @@ -223,78 +227,76 @@ struct LegacySSHStore : public virtual LegacySSHStoreConfig, public virtual Stor copyNAR(conn->from, sink); } - std::optional queryPathFromHashPart(const std::string & hashPart) override - { unsupported("queryPathFromHashPart"); } - - StorePath addToStore( - std::string_view name, - const Path & srcPath, - FileIngestionMethod method, - HashType hashAlgo, - PathFilter & filter, - RepairFlag repair, - const StorePathSet & references) override - { unsupported("addToStore"); } + std::optional + queryPathFromHashPart(const std::string & hashPart) override + { + unsupported("queryPathFromHashPart"); + } - StorePath addTextToStore( - std::string_view name, - std::string_view s, - const StorePathSet & references, - RepairFlag repair) override - { unsupported("addTextToStore"); } + StorePath addToStore(std::string_view name, const Path & srcPath, + FileIngestionMethod method, HashType hashAlgo, + PathFilter & filter, RepairFlag repair, + const StorePathSet & references) override + { + unsupported("addToStore"); + } -private: + StorePath addTextToStore(std::string_view name, std::string_view s, + const StorePathSet & references, + RepairFlag repair) override + { + unsupported("addTextToStore"); + } + private: void putBuildSettings(Connection & conn) { - conn.to - << settings.maxSilentTime - << settings.buildTimeout; + conn.to << settings.maxSilentTime << settings.buildTimeout; if (GET_PROTOCOL_MINOR(conn.remoteVersion) >= 2) - conn.to - << settings.maxLogSize; + conn.to << settings.maxLogSize; if (GET_PROTOCOL_MINOR(conn.remoteVersion) >= 3) - conn.to - << settings.buildRepeat - << settings.enforceDeterminism; + conn.to << settings.buildRepeat << settings.enforceDeterminism; if (GET_PROTOCOL_MINOR(conn.remoteVersion) >= 7) { conn.to << ((int) settings.keepFailed); } } -public: - - BuildResult buildDerivation(const StorePath & drvPath, const BasicDerivation & drv, - BuildMode buildMode) override + public: + BuildResult buildDerivation(const StorePath & drvPath, + const BasicDerivation & drv, + BuildMode buildMode) override { auto conn(connections->get()); - conn->to - << cmdBuildDerivation - << printStorePath(drvPath); + conn->to << cmdBuildDerivation << printStorePath(drvPath); writeDerivation(conn->to, *this, drv); putBuildSettings(*conn); conn->to.flush(); - BuildResult status { .path = DerivedPath::Built { .drvPath = drvPath } }; + BuildResult status{.path = DerivedPath::Built{.drvPath = drvPath}}; status.status = (BuildResult::Status) readInt(conn->from); conn->from >> status.errorMsg; if (GET_PROTOCOL_MINOR(conn->remoteVersion) >= 3) - conn->from >> status.timesBuilt >> status.isNonDeterministic >> status.startTime >> status.stopTime; + conn->from >> status.timesBuilt >> status.isNonDeterministic >> + status.startTime >> status.stopTime; if (GET_PROTOCOL_MINOR(conn->remoteVersion) >= 6) { - status.builtOutputs = worker_proto::read(*this, conn->from, Phantom {}); + status.builtOutputs = + worker_proto::read(*this, conn->from, Phantom{}); } return status; } - void buildPaths(const std::vector & drvPaths, BuildMode buildMode, std::shared_ptr evalStore) override + void buildPaths(const std::vector & drvPaths, + BuildMode buildMode, + std::shared_ptr evalStore) override { if (evalStore && evalStore.get() != this) - throw Error("building on an SSH store is incompatible with '--eval-store'"); + throw Error( + "building on an SSH store is incompatible with '--eval-store'"); auto conn(connections->get()); @@ -302,14 +304,21 @@ struct LegacySSHStore : public virtual LegacySSHStoreConfig, public virtual Stor Strings ss; for (auto & p : drvPaths) { auto sOrDrvPath = StorePathWithOutputs::tryFromDerivedPath(p); - std::visit(overloaded { - [&](const StorePathWithOutputs & s) { - ss.push_back(s.to_string(*this)); - }, - [&](const StorePath & drvPath) { - throw Error("wanted to fetch '%s' but the legacy ssh protocol doesn't support merely substituting drv files via the build paths command. It would build them instead. Try using ssh-ng://", printStorePath(drvPath)); + std::visit( + overloaded{ + [&](const StorePathWithOutputs & s) { + ss.push_back(s.to_string(*this)); + }, + [&](const StorePath & drvPath) { + throw Error( + "wanted to fetch '%s' but the legacy ssh protocol " + "doesn't support merely substituting drv files via " + "the build paths command. It would build them " + "instead. Try using ssh-ng://", + printStorePath(drvPath)); + }, }, - }, sOrDrvPath); + sOrDrvPath); } conn->to << ss; @@ -317,7 +326,7 @@ struct LegacySSHStore : public virtual LegacySSHStoreConfig, public virtual Stor conn->to.flush(); - BuildResult result { .path = DerivedPath::Opaque { StorePath::dummy } }; + BuildResult result{.path = DerivedPath::Opaque{StorePath::dummy}}; result.status = (BuildResult::Status) readInt(conn->from); if (!result.success()) { @@ -327,48 +336,47 @@ struct LegacySSHStore : public virtual LegacySSHStoreConfig, public virtual Stor } void ensurePath(const StorePath & path) override - { unsupported("ensurePath"); } + { + unsupported("ensurePath"); + } - void computeFSClosure(const StorePathSet & paths, - StorePathSet & out, bool flipDirection = false, - bool includeOutputs = false, bool includeDerivers = false) override + void computeFSClosure(const StorePathSet & paths, StorePathSet & out, + bool flipDirection = false, + bool includeOutputs = false, + bool includeDerivers = false) override { if (flipDirection || includeDerivers) { - Store::computeFSClosure(paths, out, flipDirection, includeOutputs, includeDerivers); + Store::computeFSClosure(paths, out, flipDirection, includeOutputs, + includeDerivers); return; } auto conn(connections->get()); - conn->to - << cmdQueryClosure - << includeOutputs; + conn->to << cmdQueryClosure << includeOutputs; worker_proto::write(*this, conn->to, paths); conn->to.flush(); - for (auto & i : worker_proto::read(*this, conn->from, Phantom {})) + for (auto & i : + worker_proto::read(*this, conn->from, Phantom{})) out.insert(i); } - StorePathSet queryValidPaths(const StorePathSet & paths, - SubstituteFlag maybeSubstitute = NoSubstitute) override + StorePathSet + queryValidPaths(const StorePathSet & paths, + SubstituteFlag maybeSubstitute = NoSubstitute) override { auto conn(connections->get()); - conn->to - << cmdQueryValidPaths - << false // lock - << maybeSubstitute; + conn->to << cmdQueryValidPaths << false // lock + << maybeSubstitute; worker_proto::write(*this, conn->to, paths); conn->to.flush(); - return worker_proto::read(*this, conn->from, Phantom {}); + return worker_proto::read(*this, conn->from, Phantom{}); } - void connect() override - { - auto conn(connections->get()); - } + void connect() override { auto conn(connections->get()); } unsigned int getProtocol() override { @@ -376,12 +384,16 @@ struct LegacySSHStore : public virtual LegacySSHStoreConfig, public virtual Stor return conn->remoteVersion; } - void queryRealisationUncached(const DrvOutput &, + void queryRealisationUncached( + const DrvOutput &, Callback> callback) noexcept override // TODO: Implement - { unsupported("queryRealisation"); } + { + unsupported("queryRealisation"); + } }; -static RegisterStoreImplementation regLegacySSHStore; +static RegisterStoreImplementation + regLegacySSHStore; } diff --git a/src/libstore/local-binary-cache-store.cc b/src/libstore/local-binary-cache-store.cc index ba4416f6d8e3..6fcf09bb588c 100644 --- a/src/libstore/local-binary-cache-store.cc +++ b/src/libstore/local-binary-cache-store.cc @@ -6,50 +6,38 @@ namespace nix { -struct LocalBinaryCacheStoreConfig : virtual BinaryCacheStoreConfig -{ +struct LocalBinaryCacheStoreConfig : virtual BinaryCacheStoreConfig { using BinaryCacheStoreConfig::BinaryCacheStoreConfig; const std::string name() override { return "Local Binary Cache Store"; } }; -class LocalBinaryCacheStore : public virtual LocalBinaryCacheStoreConfig, public virtual BinaryCacheStore -{ -private: - +class LocalBinaryCacheStore : public virtual LocalBinaryCacheStoreConfig, + public virtual BinaryCacheStore { + private: Path binaryCacheDir; -public: - - LocalBinaryCacheStore( - const std::string scheme, - const Path & binaryCacheDir, - const Params & params) - : StoreConfig(params) - , BinaryCacheStoreConfig(params) - , LocalBinaryCacheStoreConfig(params) - , Store(params) - , BinaryCacheStore(params) - , binaryCacheDir(binaryCacheDir) + public: + LocalBinaryCacheStore(const std::string scheme, const Path & binaryCacheDir, + const Params & params) + : StoreConfig(params), BinaryCacheStoreConfig(params), + LocalBinaryCacheStoreConfig(params), Store(params), + BinaryCacheStore(params), binaryCacheDir(binaryCacheDir) { } void init() override; - std::string getUri() override - { - return "file://" + binaryCacheDir; - } + std::string getUri() override { return "file://" + binaryCacheDir; } static std::set uriSchemes(); -protected: - + protected: bool fileExists(const std::string & path) override; void upsertFile(const std::string & path, - std::shared_ptr> istream, - const std::string & mimeType) override + std::shared_ptr> istream, + const std::string & mimeType) override { auto path2 = binaryCacheDir + "/" + path; static std::atomic counter{0}; @@ -68,7 +56,8 @@ class LocalBinaryCacheStore : public virtual LocalBinaryCacheStoreConfig, public readFile(binaryCacheDir + "/" + path, sink); } catch (SysError & e) { if (e.errNo == ENOENT) - throw NoSuchBinaryCacheFile("file '%s' does not exist in binary cache", path); + throw NoSuchBinaryCacheFile( + "file '%s' does not exist in binary cache", path); throw; } } @@ -78,17 +67,15 @@ class LocalBinaryCacheStore : public virtual LocalBinaryCacheStoreConfig, public StorePathSet paths; for (auto & entry : readDirectory(binaryCacheDir)) { - if (entry.name.size() != 40 || - !hasSuffix(entry.name, ".narinfo")) + if (entry.name.size() != 40 || !hasSuffix(entry.name, ".narinfo")) continue; paths.insert(parseStorePath( - storeDir + "/" + entry.name.substr(0, entry.name.size() - 8) - + "-" + MissingName)); + storeDir + "/" + entry.name.substr(0, entry.name.size() - 8) + + "-" + MissingName)); } return paths; } - }; void LocalBinaryCacheStore::init() @@ -114,6 +101,8 @@ std::set LocalBinaryCacheStore::uriSchemes() return {"file"}; } -static RegisterStoreImplementation regLocalBinaryCacheStore; +static RegisterStoreImplementation + regLocalBinaryCacheStore; } diff --git a/src/libstore/local-fs-store.cc b/src/libstore/local-fs-store.cc index c5ae7536f25f..8c710a1228b8 100644 --- a/src/libstore/local-fs-store.cc +++ b/src/libstore/local-fs-store.cc @@ -8,23 +8,21 @@ namespace nix { -LocalFSStore::LocalFSStore(const Params & params) - : Store(params) -{ -} +LocalFSStore::LocalFSStore(const Params & params) : Store(params) {} -struct LocalStoreAccessor : public FSAccessor -{ +struct LocalStoreAccessor : public FSAccessor { ref store; - LocalStoreAccessor(ref store) : store(store) { } + LocalStoreAccessor(ref store) : store(store) {} Path toRealPath(const Path & path, bool requireValidPath = true) { auto storePath = store->toStorePath(path).first; if (requireValidPath && !store->isValidPath(storePath)) - throw InvalidPath("path '%1%' is not a valid store path", store->printStorePath(storePath)); - return store->getRealStoreDir() + std::string(path, store->storeDir.size()); + throw InvalidPath("path '%1%' is not a valid store path", + store->printStorePath(storePath)); + return store->getRealStoreDir() + + std::string(path, store->storeDir.size()); } FSAccessor::Stat stat(const Path & path) override @@ -33,19 +31,20 @@ struct LocalStoreAccessor : public FSAccessor struct stat st; if (lstat(realPath.c_str(), &st)) { - if (errno == ENOENT || errno == ENOTDIR) return {Type::tMissing, 0, false}; + if (errno == ENOENT || errno == ENOTDIR) + return {Type::tMissing, 0, false}; throw SysError("getting status of '%1%'", path); } - if (!S_ISREG(st.st_mode) && !S_ISDIR(st.st_mode) && !S_ISLNK(st.st_mode)) + if (!S_ISREG(st.st_mode) && !S_ISDIR(st.st_mode) && + !S_ISLNK(st.st_mode)) throw Error("file '%1%' has unsupported type", path); - return { - S_ISREG(st.st_mode) ? Type::tRegular : - S_ISLNK(st.st_mode) ? Type::tSymlink : - Type::tDirectory, - S_ISREG(st.st_mode) ? (uint64_t) st.st_size : 0, - S_ISREG(st.st_mode) && st.st_mode & S_IXUSR}; + return {S_ISREG(st.st_mode) ? Type::tRegular + : S_ISLNK(st.st_mode) ? Type::tSymlink + : Type::tDirectory, + S_ISREG(st.st_mode) ? (uint64_t) st.st_size : 0, + S_ISREG(st.st_mode) && st.st_mode & S_IXUSR}; } StringSet readDirectory(const Path & path) override @@ -61,7 +60,8 @@ struct LocalStoreAccessor : public FSAccessor return res; } - std::string readFile(const Path & path, bool requireValidPath = true) override + std::string readFile(const Path & path, + bool requireValidPath = true) override { return nix::readFile(toRealPath(path, requireValidPath)); } @@ -75,14 +75,16 @@ struct LocalStoreAccessor : public FSAccessor ref LocalFSStore::getFSAccessor() { return make_ref(ref( - std::dynamic_pointer_cast(shared_from_this()))); + std::dynamic_pointer_cast(shared_from_this()))); } void LocalFSStore::narFromPath(const StorePath & path, Sink & sink) { if (!isValidPath(path)) throw Error("path '%s' is not valid", printStorePath(path)); - dumpPath(getRealStoreDir() + std::string(printStorePath(path), storeDir.size()), sink); + dumpPath(getRealStoreDir() + + std::string(printStorePath(path), storeDir.size()), + sink); } const std::string LocalFSStore::drvsLogDir = "drvs"; @@ -94,7 +96,8 @@ std::optional LocalFSStore::getBuildLog(const StorePath & path_) if (!path.isDerivation()) { try { auto info = queryPathInfo(path); - if (!info->deriver) return std::nullopt; + if (!info->deriver) + return std::nullopt; path = *info->deriver; } catch (InvalidPath &) { return std::nullopt; @@ -105,10 +108,9 @@ std::optional LocalFSStore::getBuildLog(const StorePath & path_) for (int j = 0; j < 2; j++) { - Path logPath = - j == 0 - ? fmt("%s/%s/%s/%s", logDir, drvsLogDir, baseName.substr(0, 2), baseName.substr(2)) - : fmt("%s/%s/%s", logDir, drvsLogDir, baseName); + Path logPath = j == 0 ? fmt("%s/%s/%s/%s", logDir, drvsLogDir, + baseName.substr(0, 2), baseName.substr(2)) + : fmt("%s/%s/%s", logDir, drvsLogDir, baseName); Path logBz2Path = logPath + ".bz2"; if (pathExists(logPath)) @@ -117,9 +119,9 @@ std::optional LocalFSStore::getBuildLog(const StorePath & path_) else if (pathExists(logBz2Path)) { try { return decompress("bzip2", readFile(logBz2Path)); - } catch (Error &) { } + } catch (Error &) { + } } - } return std::nullopt; diff --git a/src/libstore/local-fs-store.hh b/src/libstore/local-fs-store.hh index e6fb3201a496..938b0900aec2 100644 --- a/src/libstore/local-fs-store.hh +++ b/src/libstore/local-fs-store.hh @@ -6,32 +6,32 @@ namespace nix { -struct LocalFSStoreConfig : virtual StoreConfig -{ +struct LocalFSStoreConfig : virtual StoreConfig { using StoreConfig::StoreConfig; // FIXME: the (StoreConfig*) cast works around a bug in gcc that causes // it to omit the call to the Setting constructor. Clang works fine // either way. - const PathSetting rootDir{(StoreConfig*) this, true, "", - "root", "directory prefixed to all other paths"}; - const PathSetting stateDir{(StoreConfig*) this, false, - rootDir != "" ? rootDir + "/nix/var/nix" : settings.nixStateDir, - "state", "directory where Nix will store state"}; - const PathSetting logDir{(StoreConfig*) this, false, - rootDir != "" ? rootDir + "/nix/var/log/nix" : settings.nixLogDir, - "log", "directory where Nix will store state"}; - const PathSetting realStoreDir{(StoreConfig*) this, false, - rootDir != "" ? rootDir + "/nix/store" : storeDir, "real", - "physical path to the Nix store"}; + const PathSetting rootDir{(StoreConfig *) this, true, "", "root", + "directory prefixed to all other paths"}; + const PathSetting stateDir{(StoreConfig *) this, false, + rootDir != "" ? rootDir + "/nix/var/nix" + : settings.nixStateDir, + "state", "directory where Nix will store state"}; + const PathSetting logDir{(StoreConfig *) this, false, + rootDir != "" ? rootDir + "/nix/var/log/nix" + : settings.nixLogDir, + "log", "directory where Nix will store state"}; + const PathSetting realStoreDir{(StoreConfig *) this, false, + rootDir != "" ? rootDir + "/nix/store" + : storeDir, + "real", "physical path to the Nix store"}; }; class LocalFSStore : public virtual LocalFSStoreConfig, - public virtual Store, - public virtual GcStore, - public virtual LogStore -{ -public: - + public virtual Store, + public virtual GcStore, + public virtual LogStore { + public: const static std::string drvsLogDir; LocalFSStore(const Params & params); @@ -47,11 +47,11 @@ public: Path toRealPath(const Path & storePath) override { assert(isInStore(storePath)); - return getRealStoreDir() + "/" + std::string(storePath, storeDir.size() + 1); + return getRealStoreDir() + "/" + + std::string(storePath, storeDir.size() + 1); } std::optional getBuildLog(const StorePath & path) override; - }; } diff --git a/src/libstore/local-store.cc b/src/libstore/local-store.cc index eba3b0fa5e60..adc67f85977f 100644 --- a/src/libstore/local-store.cc +++ b/src/libstore/local-store.cc @@ -41,7 +41,6 @@ #include - namespace nix { struct LocalStore::State::Stmts { @@ -79,24 +78,26 @@ int getSchema(Path schemaPath) return curSchema; } -void migrateCASchema(SQLite& db, Path schemaPath, AutoCloseFD& lockFd) +void migrateCASchema(SQLite & db, Path schemaPath, AutoCloseFD & lockFd) { const int nixCASchemaVersion = 4; int curCASchema = getSchema(schemaPath); if (curCASchema != nixCASchemaVersion) { if (curCASchema > nixCASchemaVersion) { - throw Error("current Nix store ca-schema is version %1%, but I only support %2%", - curCASchema, nixCASchemaVersion); + throw Error("current Nix store ca-schema is version %1%, but I " + "only support %2%", + curCASchema, nixCASchemaVersion); } if (!lockFile(lockFd.get(), ltWrite, false)) { - printInfo("waiting for exclusive access to the Nix store for ca drvs..."); + printInfo( + "waiting for exclusive access to the Nix store for ca drvs..."); lockFile(lockFd.get(), ltWrite, true); } if (curCASchema == 0) { static const char schema[] = - #include "ca-specific-schema.sql.gen.hh" +#include "ca-specific-schema.sql.gen.hh" ; db.exec(schema); curCASchema = nixCASchemaVersion; @@ -104,7 +105,8 @@ void migrateCASchema(SQLite& db, Path schemaPath, AutoCloseFD& lockFd) if (curCASchema < 2) { SQLiteTxn txn(db); - // Ugly little sql dance to add a new `id` column and make it the primary key + // Ugly little sql dance to add a new `id` column and make it the + // primary key db.exec(R"( create table Realisations2 ( id integer primary key autoincrement not null, @@ -164,18 +166,12 @@ void migrateCASchema(SQLite& db, Path schemaPath, AutoCloseFD& lockFd) } LocalStore::LocalStore(const Params & params) - : StoreConfig(params) - , LocalFSStoreConfig(params) - , LocalStoreConfig(params) - , Store(params) - , LocalFSStore(params) - , dbDir(stateDir + "/db") - , linksDir(realStoreDir + "/.links") - , reservedPath(dbDir + "/reserved") - , schemaPath(dbDir + "/schema") - , tempRootsDir(stateDir + "/temproots") - , fnTempRoots(fmt("%s/%d", tempRootsDir, getpid())) - , locksHeld(tokenizeString(getEnv("NIX_HELD_LOCKS").value_or(""))) + : StoreConfig(params), LocalFSStoreConfig(params), LocalStoreConfig(params), + Store(params), LocalFSStore(params), dbDir(stateDir + "/db"), + linksDir(realStoreDir + "/.links"), reservedPath(dbDir + "/reserved"), + schemaPath(dbDir + "/schema"), tempRootsDir(stateDir + "/temproots"), + fnTempRoots(fmt("%s/%d", tempRootsDir, getpid())), + locksHeld(tokenizeString(getEnv("NIX_HELD_LOCKS").value_or(""))) { auto state(_state.lock()); state->stmts = std::make_unique(); @@ -194,10 +190,12 @@ LocalStore::LocalStore(const Params & params) createSymlink(profilesDir, gcRootsDir + "/profiles"); } - for (auto & perUserDir : {profilesDir + "/per-user", gcRootsDir + "/per-user"}) { + for (auto & perUserDir : + {profilesDir + "/per-user", gcRootsDir + "/per-user"}) { createDirs(perUserDir); if (chmod(perUserDir.c_str(), 0755) == -1) - throw SysError("could not set permissions on '%s' to 755", perUserDir); + throw SysError("could not set permissions on '%s' to 755", + perUserDir); } createUser(getUserName(), getuid()); @@ -209,17 +207,23 @@ LocalStore::LocalStore(const Params & params) struct group * gr = getgrnam(settings.buildUsersGroup.get().c_str()); if (!gr) - printError("warning: the group '%1%' specified in 'build-users-group' does not exist", settings.buildUsersGroup); + printError("warning: the group '%1%' specified in " + "'build-users-group' does not exist", + settings.buildUsersGroup); else { struct stat st; if (stat(realStoreDir.get().c_str(), &st)) - throw SysError("getting attributes of path '%1%'", realStoreDir); + throw SysError("getting attributes of path '%1%'", + realStoreDir); - if (st.st_uid != 0 || st.st_gid != gr->gr_gid || (st.st_mode & ~S_IFMT) != perm) { + if (st.st_uid != 0 || st.st_gid != gr->gr_gid || + (st.st_mode & ~S_IFMT) != perm) { if (chown(realStoreDir.get().c_str(), 0, gr->gr_gid) == -1) - throw SysError("changing ownership of path '%1%'", realStoreDir); + throw SysError("changing ownership of path '%1%'", + realStoreDir); if (chmod(realStoreDir.get().c_str(), perm) == -1) - throw SysError("changing permissions on path '%1%'", realStoreDir); + throw SysError("changing permissions on path '%1%'", + realStoreDir); } } } @@ -231,10 +235,10 @@ LocalStore::LocalStore(const Params & params) while (path != "/") { st = lstat(path); if (S_ISLNK(st.st_mode)) - throw Error( - "the path '%1%' is a symlink; " - "this is not allowed for the Nix store and its parent directories", - path); + throw Error("the path '%1%' is a symlink; " + "this is not allowed for the Nix store and its " + "parent directories", + path); path = dirOf(path); } } @@ -246,16 +250,17 @@ LocalStore::LocalStore(const Params & params) try { struct stat st; if (stat(reservedPath.c_str(), &st) == -1 || - st.st_size != settings.reservedSize) - { - AutoCloseFD fd = open(reservedPath.c_str(), O_WRONLY | O_CREAT | O_CLOEXEC, 0600); + st.st_size != settings.reservedSize) { + AutoCloseFD fd = open(reservedPath.c_str(), + O_WRONLY | O_CREAT | O_CLOEXEC, 0600); int res = -1; #if HAVE_POSIX_FALLOCATE res = posix_fallocate(fd.get(), 0, settings.reservedSize); #endif if (res == -1) { writeFull(fd.get(), std::string(settings.reservedSize, 'X')); - [[gnu::unused]] auto res2 = ftruncate(fd.get(), settings.reservedSize); + [[gnu::unused]] auto res2 = + ftruncate(fd.get(), settings.reservedSize); } } } catch (SysError & e) { /* don't care about errors */ @@ -275,8 +280,9 @@ LocalStore::LocalStore(const Params & params) upgrade. */ int curSchema = getSchema(); if (curSchema > nixSchemaVersion) - throw Error("current Nix store schema is version %1%, but I only support %2%", - curSchema, nixSchemaVersion); + throw Error( + "current Nix store schema is version %1%, but I only support %2%", + curSchema, nixSchemaVersion); else if (curSchema == 0) { /* new store */ curSchema = nixSchemaVersion; @@ -306,13 +312,16 @@ LocalStore::LocalStore(const Params & params) have performed the upgrade already. */ curSchema = getSchema(); - if (curSchema < 7) { upgradeStore7(); } + if (curSchema < 7) { + upgradeStore7(); + } openDB(*state, false); if (curSchema < 8) { SQLiteTxn txn(state->db); - state->db.exec("alter table ValidPaths add column ultimate integer"); + state->db.exec( + "alter table ValidPaths add column ultimate integer"); state->db.exec("alter table ValidPaths add column sigs text"); txn.commit(); } @@ -334,47 +343,59 @@ LocalStore::LocalStore(const Params & params) lockFile(globalLock.get(), ltRead, true); } - else openDB(*state, false); + else + openDB(*state, false); if (settings.isExperimentalFeatureEnabled(Xp::CaDerivations)) { migrateCASchema(state->db, dbDir + "/ca-schema", globalLock); } /* Prepare SQL statements. */ - state->stmts->RegisterValidPath.create(state->db, - "insert into ValidPaths (path, hash, registrationTime, deriver, narSize, ultimate, sigs, ca) values (?, ?, ?, ?, ?, ?, ?, ?);"); - state->stmts->UpdatePathInfo.create(state->db, - "update ValidPaths set narSize = ?, hash = ?, ultimate = ?, sigs = ?, ca = ? where path = ?;"); - state->stmts->AddReference.create(state->db, + state->stmts->RegisterValidPath.create( + state->db, + "insert into ValidPaths (path, hash, registrationTime, deriver, " + "narSize, ultimate, sigs, ca) values (?, ?, ?, ?, ?, ?, ?, ?);"); + state->stmts->UpdatePathInfo.create( + state->db, "update ValidPaths set narSize = ?, hash = ?, ultimate = ?, " + "sigs = ?, ca = ? where path = ?;"); + state->stmts->AddReference.create( + state->db, "insert or replace into Refs (referrer, reference) values (?, ?);"); - state->stmts->QueryPathInfo.create(state->db, - "select id, hash, registrationTime, deriver, narSize, ultimate, sigs, ca from ValidPaths where path = ?;"); - state->stmts->QueryReferences.create(state->db, - "select path from Refs join ValidPaths on reference = id where referrer = ?;"); - state->stmts->QueryReferrers.create(state->db, - "select path from Refs join ValidPaths on referrer = id where reference = (select id from ValidPaths where path = ?);"); - state->stmts->InvalidatePath.create(state->db, - "delete from ValidPaths where path = ?;"); - state->stmts->AddDerivationOutput.create(state->db, - "insert or replace into DerivationOutputs (drv, id, path) values (?, ?, ?);"); - state->stmts->QueryValidDerivers.create(state->db, - "select v.id, v.path from DerivationOutputs d join ValidPaths v on d.drv = v.id where d.path = ?;"); - state->stmts->QueryDerivationOutputs.create(state->db, - "select id, path from DerivationOutputs where drv = ?;"); + state->stmts->QueryPathInfo.create( + state->db, "select id, hash, registrationTime, deriver, narSize, " + "ultimate, sigs, ca from ValidPaths where path = ?;"); + state->stmts->QueryReferences.create( + state->db, "select path from Refs join ValidPaths on reference = id " + "where referrer = ?;"); + state->stmts->QueryReferrers.create( + state->db, + "select path from Refs join ValidPaths on referrer = id where " + "reference = (select id from ValidPaths where path = ?);"); + state->stmts->InvalidatePath.create( + state->db, "delete from ValidPaths where path = ?;"); + state->stmts->AddDerivationOutput.create( + state->db, "insert or replace into DerivationOutputs (drv, id, path) " + "values (?, ?, ?);"); + state->stmts->QueryValidDerivers.create( + state->db, "select v.id, v.path from DerivationOutputs d join " + "ValidPaths v on d.drv = v.id where d.path = ?;"); + state->stmts->QueryDerivationOutputs.create( + state->db, "select id, path from DerivationOutputs where drv = ?;"); // Use "path >= ?" with limit 1 rather than "path like '?%'" to // ensure efficient lookup. - state->stmts->QueryPathFromHashPart.create(state->db, - "select path from ValidPaths where path >= ? limit 1;"); - state->stmts->QueryValidPaths.create(state->db, "select path from ValidPaths"); + state->stmts->QueryPathFromHashPart.create( + state->db, "select path from ValidPaths where path >= ? limit 1;"); + state->stmts->QueryValidPaths.create(state->db, + "select path from ValidPaths"); if (settings.isExperimentalFeatureEnabled(Xp::CaDerivations)) { state->stmts->RegisterRealisedOutput.create(state->db, - R"( + R"( insert into Realisations (drvPath, outputName, outputPath, signatures) values (?, ?, (select id from ValidPaths where path = ?), ?) ; )"); state->stmts->UpdateRealisedOutput.create(state->db, - R"( + R"( update Realisations set signatures = ? where @@ -383,27 +404,27 @@ LocalStore::LocalStore(const Params & params) ; )"); state->stmts->QueryRealisedOutput.create(state->db, - R"( + R"( select Realisations.id, Output.path, Realisations.signatures from Realisations inner join ValidPaths as Output on Output.id = Realisations.outputPath where drvPath = ? and outputName = ? ; )"); state->stmts->QueryAllRealisedOutputs.create(state->db, - R"( + R"( select outputName, Output.path from Realisations inner join ValidPaths as Output on Output.id = Realisations.outputPath where drvPath = ? ; )"); state->stmts->QueryRealisationReferences.create(state->db, - R"( + R"( select drvPath, outputName from Realisations join RealisationsRefs on realisationReference = Realisations.id where referrer = ?; )"); state->stmts->AddRealisationReference.create(state->db, - R"( + R"( insert or replace into RealisationsRefs (referrer, realisationReference) values ( (select id from Realisations where drvPath = ? and outputName = ?), @@ -412,7 +433,6 @@ LocalStore::LocalStore(const Params & params) } } - AutoCloseFD LocalStore::openGCLock() { Path fnGCLock = stateDir + "/gc.lock"; @@ -422,7 +442,6 @@ AutoCloseFD LocalStore::openGCLock() return fdGCLock; } - LocalStore::~LocalStore() { std::shared_future future; @@ -449,15 +468,9 @@ LocalStore::~LocalStore() } } +std::string LocalStore::getUri() { return "local"; } -std::string LocalStore::getUri() -{ - return "local"; -} - - -int LocalStore::getSchema() -{ return nix::getSchema(schemaPath); } +int LocalStore::getSchema() { return nix::getSchema(schemaPath); } void LocalStore::openDB(State & state, bool create) { @@ -501,13 +514,15 @@ void LocalStore::openDB(State & state, bool create) prevMode = std::string((const char *) sqlite3_column_text(stmt, 0)); } if (prevMode != mode && - sqlite3_exec(db, ("pragma main.journal_mode = " + mode + ";").c_str(), 0, 0, 0) != SQLITE_OK) + sqlite3_exec(db, ("pragma main.journal_mode = " + mode + ";").c_str(), + 0, 0, 0) != SQLITE_OK) SQLiteError::throw_(db, "setting journal mode"); /* Increase the auto-checkpoint interval to 40000 pages. This seems enough to ensure that instantiating the NixOS system derivation is done in a single fsync(). */ - if (mode == "wal" && sqlite3_exec(db, "pragma wal_autocheckpoint = 40000;", 0, 0, 0) != SQLITE_OK) + if (mode == "wal" && sqlite3_exec(db, "pragma wal_autocheckpoint = 40000;", + 0, 0, 0) != SQLITE_OK) SQLiteError::throw_(db, "setting autocheckpoint interval"); /* Initialise the database schema, if necessary. */ @@ -519,30 +534,30 @@ void LocalStore::openDB(State & state, bool create) } } - /* To improve purity, users may want to make the Nix store a read-only bind mount. So make the Nix store writable for this process. */ void LocalStore::makeStoreWritable() { #if __linux__ - if (getuid() != 0) return; + if (getuid() != 0) + return; /* Check if /nix/store is on a read-only mount. */ struct statvfs stat; if (statvfs(realStoreDir.get().c_str(), &stat) != 0) throw SysError("getting info about the Nix store mount point"); if (stat.f_flag & ST_RDONLY) { - if (mount(0, realStoreDir.get().c_str(), "none", MS_REMOUNT | MS_BIND, 0) == -1) + if (mount(0, realStoreDir.get().c_str(), "none", MS_REMOUNT | MS_BIND, + 0) == -1) throw SysError("remounting %1% writable", realStoreDir); } #endif } - const time_t mtimeStore = 1; /* 1 second into the epoch */ - -static void canonicaliseTimestampAndPermissions(const Path & path, const struct stat & st) +static void canonicaliseTimestampAndPermissions(const Path & path, + const struct stat & st) { if (!S_ISLNK(st.st_mode)) { @@ -550,13 +565,11 @@ static void canonicaliseTimestampAndPermissions(const Path & path, const struct mode_t mode = st.st_mode & ~S_IFMT; if (mode != 0444 && mode != 0555) { - mode = (st.st_mode & S_IFMT) - | 0444 - | (st.st_mode & S_IXUSR ? 0111 : 0); + mode = (st.st_mode & S_IFMT) | 0444 | + (st.st_mode & S_IXUSR ? 0111 : 0); if (chmod(path.c_str(), mode) == -1) throw SysError("changing mode of '%1%' to %2$o", path, mode); } - } if (st.st_mtime != mtimeStore) { @@ -572,18 +585,17 @@ static void canonicaliseTimestampAndPermissions(const Path & path, const struct #else if (!S_ISLNK(st.st_mode) && utimes(path.c_str(), times) == -1) #endif - throw SysError("changing modification time of '%1%'", path); + throw SysError("changing modification time of '%1%'", path); } } - void canonicaliseTimestampAndPermissions(const Path & path) { canonicaliseTimestampAndPermissions(path, lstat(path)); } - -static void canonicalisePathMetaData_(const Path & path, uid_t fromUid, InodesSeen & inodesSeen) +static void canonicalisePathMetaData_(const Path & path, uid_t fromUid, + InodesSeen & inodesSeen) { checkInterrupt(); @@ -616,12 +628,15 @@ static void canonicalisePathMetaData_(const Path & path, uid_t fromUid, InodesSe if ((eaSize = llistxattr(path.c_str(), eaBuf.data(), eaBuf.size())) < 0) throw SysError("querying extended attributes of '%s'", path); - for (auto & eaName: tokenizeString(std::string(eaBuf.data(), eaSize), std::string("\000", 1))) { - if (settings.ignoredAcls.get().count(eaName)) continue; + for (auto & eaName : tokenizeString( + std::string(eaBuf.data(), eaSize), std::string("\000", 1))) { + if (settings.ignoredAcls.get().count(eaName)) + continue; if (lremovexattr(path.c_str(), eaName.c_str()) == -1) - throw SysError("removing extended attribute '%s' from '%s'", eaName, path); + throw SysError("removing extended attribute '%s' from '%s'", + eaName, path); } - } + } #endif /* Fail if the file is not owned by the build user. This prevents @@ -631,10 +646,13 @@ static void canonicalisePathMetaData_(const Path & path, uid_t fromUid, InodesSe ensure that we don't fail on hard links within the same build (i.e. "touch $out/foo; ln $out/foo $out/bar"). */ if (fromUid != (uid_t) -1 && st.st_uid != fromUid) { - if (S_ISDIR(st.st_mode) || !inodesSeen.count(Inode(st.st_dev, st.st_ino))) + if (S_ISDIR(st.st_mode) || + !inodesSeen.count(Inode(st.st_dev, st.st_ino))) throw BuildError("invalid ownership on file '%1%'", path); mode_t mode = st.st_mode & ~S_IFMT; - assert(S_ISLNK(st.st_mode) || (st.st_uid == geteuid() && (mode == 0444 || mode == 0555) && st.st_mtime == mtimeStore)); + assert(S_ISLNK(st.st_mode) || + (st.st_uid == geteuid() && (mode == 0444 || mode == 0555) && + st.st_mtime == mtimeStore)); return; } @@ -656,8 +674,7 @@ static void canonicalisePathMetaData_(const Path & path, uid_t fromUid, InodesSe if (!S_ISLNK(st.st_mode) && chown(path.c_str(), geteuid(), getegid()) == -1) #endif - throw SysError("changing owner of '%1%' to %2%", - path, geteuid()); + throw SysError("changing owner of '%1%' to %2%", path, geteuid()); } if (S_ISDIR(st.st_mode)) { @@ -667,8 +684,8 @@ static void canonicalisePathMetaData_(const Path & path, uid_t fromUid, InodesSe } } - -void canonicalisePathMetaData(const Path & path, uid_t fromUid, InodesSeen & inodesSeen) +void canonicalisePathMetaData(const Path & path, uid_t fromUid, + InodesSeen & inodesSeen) { canonicalisePathMetaData_(path, fromUid, inodesSeen); @@ -682,29 +699,29 @@ void canonicalisePathMetaData(const Path & path, uid_t fromUid, InodesSeen & ino } } - void canonicalisePathMetaData(const Path & path, uid_t fromUid) { InodesSeen inodesSeen; canonicalisePathMetaData(path, fromUid, inodesSeen); } - -void LocalStore::checkDerivationOutputs(const StorePath & drvPath, const Derivation & drv) +void LocalStore::checkDerivationOutputs(const StorePath & drvPath, + const Derivation & drv) { assert(drvPath.isDerivation()); std::string drvName(drvPath.name()); drvName = drvName.substr(0, drvName.size() - drvExtension.size()); - auto envHasRightPath = [&](const StorePath & actual, const std::string & varName) - { + auto envHasRightPath = [&](const StorePath & actual, + const std::string & varName) { auto j = drv.env.find(varName); if (j == drv.env.end() || parseStorePath(j->second) != actual) - throw Error("derivation '%s' has incorrect environment variable '%s', should be '%s'", - printStorePath(drvPath), varName, printStorePath(actual)); + throw Error("derivation '%s' has incorrect environment variable " + "'%s', should be '%s'", + printStorePath(drvPath), varName, + printStorePath(actual)); }; - // Don't need the answer, but do this anyways to assert is proper // combination. The code below is more general and naturally allows // combinations that are currently prohibited. @@ -712,46 +729,59 @@ void LocalStore::checkDerivationOutputs(const StorePath & drvPath, const Derivat std::optional hashesModulo; for (auto & i : drv.outputs) { - std::visit(overloaded { - [&](const DerivationOutput::InputAddressed & doia) { - if (!hashesModulo) { - // somewhat expensive so we do lazily - hashesModulo = hashDerivationModulo(*this, drv, true); - } - auto currentOutputHash = get(hashesModulo->hashes, i.first); - if (!currentOutputHash) - throw Error("derivation '%s' has unexpected output '%s' (local-store / hashesModulo) named '%s'", - printStorePath(drvPath), printStorePath(doia.path), i.first); - StorePath recomputed = makeOutputPath(i.first, *currentOutputHash, drvName); - if (doia.path != recomputed) - throw Error("derivation '%s' has incorrect output '%s', should be '%s'", - printStorePath(drvPath), printStorePath(doia.path), printStorePath(recomputed)); - envHasRightPath(doia.path, i.first); - }, - [&](const DerivationOutput::CAFixed & dof) { - StorePath path = makeFixedOutputPath(dof.hash.method, dof.hash.hash, drvName); - envHasRightPath(path, i.first); - }, - [&](const DerivationOutput::CAFloating &) { - /* Nothing to check */ - }, - [&](const DerivationOutput::Deferred &) { - /* Nothing to check */ - }, - [&](const DerivationOutput::Impure &) { - /* Nothing to check */ + std::visit( + overloaded{ + [&](const DerivationOutput::InputAddressed & doia) { + if (!hashesModulo) { + // somewhat expensive so we do lazily + hashesModulo = hashDerivationModulo(*this, drv, true); + } + auto currentOutputHash = get(hashesModulo->hashes, i.first); + if (!currentOutputHash) + throw Error( + "derivation '%s' has unexpected output '%s' " + "(local-store / hashesModulo) named '%s'", + printStorePath(drvPath), printStorePath(doia.path), + i.first); + StorePath recomputed = + makeOutputPath(i.first, *currentOutputHash, drvName); + if (doia.path != recomputed) + throw Error("derivation '%s' has incorrect output " + "'%s', should be '%s'", + printStorePath(drvPath), + printStorePath(doia.path), + printStorePath(recomputed)); + envHasRightPath(doia.path, i.first); + }, + [&](const DerivationOutput::CAFixed & dof) { + StorePath path = makeFixedOutputPath( + dof.hash.method, dof.hash.hash, drvName); + envHasRightPath(path, i.first); + }, + [&](const DerivationOutput::CAFloating &) { + /* Nothing to check */ + }, + [&](const DerivationOutput::Deferred &) { + /* Nothing to check */ + }, + [&](const DerivationOutput::Impure &) { + /* Nothing to check */ + }, }, - }, i.second.raw()); + i.second.raw()); } } -void LocalStore::registerDrvOutput(const Realisation & info, CheckSigsFlag checkSigs) +void LocalStore::registerDrvOutput(const Realisation & info, + CheckSigsFlag checkSigs) { settings.requireExperimentalFeature(Xp::CaDerivations); if (checkSigs == NoCheckSigs || !realisationIsUntrusted(info)) registerDrvOutput(info); else - throw Error("cannot register realisation '%s' because it lacks a valid signature", info.outPath.to_string()); + throw Error("cannot register realisation '%s' because it lacks a valid " + "signature", + info.outPath.to_string()); } void LocalStore::registerDrvOutput(const Realisation & info) @@ -763,28 +793,24 @@ void LocalStore::registerDrvOutput(const Realisation & info) if (info.isCompatibleWith(*oldR)) { auto combinedSignatures = oldR->signatures; combinedSignatures.insert(info.signatures.begin(), - info.signatures.end()); - state->stmts->UpdateRealisedOutput.use() - (concatStringsSep(" ", combinedSignatures)) - (info.id.strHash()) - (info.id.outputName) + info.signatures.end()); + state->stmts->UpdateRealisedOutput + .use()(concatStringsSep(" ", combinedSignatures))( + info.id.strHash())(info.id.outputName) .exec(); } else { - throw Error("Trying to register a realisation of '%s', but we already " - "have another one locally.\n" - "Local: %s\n" - "Remote: %s", - info.id.to_string(), - printStorePath(oldR->outPath), - printStorePath(info.outPath) - ); + throw Error( + "Trying to register a realisation of '%s', but we already " + "have another one locally.\n" + "Local: %s\n" + "Remote: %s", + info.id.to_string(), printStorePath(oldR->outPath), + printStorePath(info.outPath)); } } else { - state->stmts->RegisterRealisedOutput.use() - (info.id.strHash()) - (info.id.outputName) - (printStorePath(info.outPath)) - (concatStringsSep(" ", info.signatures)) + state->stmts->RegisterRealisedOutput + .use()(info.id.strHash())(info.id.outputName)(printStorePath( + info.outPath))(concatStringsSep(" ", info.signatures)) .exec(); } for (auto & [outputId, depPath] : info.dependentRealisations) { @@ -792,54 +818,47 @@ void LocalStore::registerDrvOutput(const Realisation & info) if (!localRealisation) throw Error("unable to register the derivation '%s' as it " "depends on the non existent '%s'", - info.id.to_string(), outputId.to_string()); + info.id.to_string(), outputId.to_string()); if (localRealisation->second.outPath != depPath) throw Error("unable to register the derivation '%s' as it " "depends on a realisation of '%s' that doesn’t" "match what we have locally", - info.id.to_string(), outputId.to_string()); - state->stmts->AddRealisationReference.use() - (info.id.strHash()) - (info.id.outputName) - (outputId.strHash()) - (outputId.outputName) + info.id.to_string(), outputId.to_string()); + state->stmts->AddRealisationReference + .use()(info.id.strHash())(info.id.outputName)( + outputId.strHash())(outputId.outputName) .exec(); } }); } -void LocalStore::cacheDrvOutputMapping( - State & state, - const uint64_t deriver, - const std::string & outputName, - const StorePath & output) +void LocalStore::cacheDrvOutputMapping(State & state, const uint64_t deriver, + const std::string & outputName, + const StorePath & output) { retrySQLite([&]() { - state.stmts->AddDerivationOutput.use() - (deriver) - (outputName) - (printStorePath(output)) + state.stmts->AddDerivationOutput + .use()(deriver)(outputName) (printStorePath(output)) .exec(); }); } - -uint64_t LocalStore::addValidPath(State & state, - const ValidPathInfo & info, bool checkOutputs) +uint64_t LocalStore::addValidPath(State & state, const ValidPathInfo & info, + bool checkOutputs) { if (info.ca.has_value() && !info.isContentAddressed(*this)) - throw Error("cannot add path '%s' to the Nix store because it claims to be content-addressed but isn't", - printStorePath(info.path)); - - state.stmts->RegisterValidPath.use() - (printStorePath(info.path)) - (info.narHash.to_string(Base16, true)) - (info.registrationTime == 0 ? time(0) : info.registrationTime) - (info.deriver ? printStorePath(*info.deriver) : "", (bool) info.deriver) - (info.narSize, info.narSize != 0) - (info.ultimate ? 1 : 0, info.ultimate) - (concatStringsSep(" ", info.sigs), !info.sigs.empty()) - (renderContentAddress(info.ca), (bool) info.ca) + throw Error("cannot add path '%s' to the Nix store because it claims " + "to be content-addressed but isn't", + printStorePath(info.path)); + + state.stmts->RegisterValidPath + .use()(printStorePath(info.path))(info.narHash.to_string(Base16, true))( + info.registrationTime == 0 ? time(0) : info.registrationTime)( + info.deriver ? printStorePath(*info.deriver) : "", + (bool) info.deriver)(info.narSize, info.narSize != 0)( + info.ultimate ? 1 : 0, info.ultimate)( + concatStringsSep(" ", info.sigs), + !info.sigs.empty())(renderContentAddress(info.ca), (bool) info.ca) .exec(); uint64_t id = state.db.getLastInsertedRowId(); @@ -855,7 +874,8 @@ uint64_t LocalStore::addValidPath(State & state, derivations). Note that if this throws an error, then the DB transaction is rolled back, so the path validity registration above is undone. */ - if (checkOutputs) checkDerivationOutputs(info.path, drv); + if (checkOutputs) + checkDerivationOutputs(info.path, drv); for (auto & i : drv.outputsAndOptPaths(*this)) { /* Floating CA derivations have indeterminate output paths until @@ -867,15 +887,17 @@ uint64_t LocalStore::addValidPath(State & state, { auto state_(Store::state.lock()); - state_->pathInfoCache.upsert(std::string(info.path.to_string()), - PathInfoCacheValue{ .value = std::make_shared(info) }); + state_->pathInfoCache.upsert( + std::string(info.path.to_string()), + PathInfoCacheValue{ + .value = std::make_shared(info)}); } return id; } - -void LocalStore::queryPathInfoUncached(const StorePath & path, +void LocalStore::queryPathInfoUncached( + const StorePath & path, Callback> callback) noexcept { try { @@ -884,14 +906,17 @@ void LocalStore::queryPathInfoUncached(const StorePath & path, return queryPathInfoInternal(*state, path); })); - } catch (...) { callback.rethrow(); } + } catch (...) { + callback.rethrow(); + } } - -std::shared_ptr LocalStore::queryPathInfoInternal(State & state, const StorePath & path) +std::shared_ptr +LocalStore::queryPathInfoInternal(State & state, const StorePath & path) { /* Get the path info. */ - auto useQueryPathInfo(state.stmts->QueryPathInfo.use()(printStorePath(path))); + auto useQueryPathInfo( + state.stmts->QueryPathInfo.use()(printStorePath(path))); if (!useQueryPathInfo.next()) return std::shared_ptr(); @@ -902,7 +927,8 @@ std::shared_ptr LocalStore::queryPathInfoInternal(State & s try { narHash = Hash::parseAnyPrefixed(useQueryPathInfo.getStr(1)); } catch (BadHash & e) { - throw Error("invalid-path entry for '%s': %s", printStorePath(path), e.what()); + throw Error("invalid-path entry for '%s': %s", printStorePath(path), + e.what()); } auto info = std::make_shared(path, narHash); @@ -912,7 +938,8 @@ std::shared_ptr LocalStore::queryPathInfoInternal(State & s info->registrationTime = useQueryPathInfo.getInt(2); auto s = (const char *) sqlite3_column_text(state.stmts->QueryPathInfo, 3); - if (s) info->deriver = parseStorePath(s); + if (s) + info->deriver = parseStorePath(s); /* Note that narSize = NULL yields 0. */ info->narSize = useQueryPathInfo.getInt(4); @@ -920,10 +947,12 @@ std::shared_ptr LocalStore::queryPathInfoInternal(State & s info->ultimate = useQueryPathInfo.getInt(5) == 1; s = (const char *) sqlite3_column_text(state.stmts->QueryPathInfo, 6); - if (s) info->sigs = tokenizeString(s, " "); + if (s) + info->sigs = tokenizeString(s, " "); s = (const char *) sqlite3_column_text(state.stmts->QueryPathInfo, 7); - if (s) info->ca = parseContentAddressOpt(s); + if (s) + info->ca = parseContentAddressOpt(s); /* Get the references. */ auto useQueryReferences(state.stmts->QueryReferences.use()(info->id)); @@ -934,21 +963,18 @@ std::shared_ptr LocalStore::queryPathInfoInternal(State & s return info; } - /* Update path info in the database. */ void LocalStore::updatePathInfo(State & state, const ValidPathInfo & info) { - state.stmts->UpdatePathInfo.use() - (info.narSize, info.narSize != 0) - (info.narHash.to_string(Base16, true)) - (info.ultimate ? 1 : 0, info.ultimate) - (concatStringsSep(" ", info.sigs), !info.sigs.empty()) - (renderContentAddress(info.ca), (bool) info.ca) - (printStorePath(info.path)) + state.stmts->UpdatePathInfo + .use()(info.narSize, info.narSize != 0)(info.narHash.to_string( + Base16, true))(info.ultimate ? 1 : 0, info.ultimate)( + concatStringsSep(" ", info.sigs), + !info.sigs.empty())(renderContentAddress(info.ca), + (bool) info.ca)(printStorePath(info.path)) .exec(); } - uint64_t LocalStore::queryValidPathId(State & state, const StorePath & path) { auto use(state.stmts->QueryPathInfo.use()(printStorePath(path))); @@ -957,13 +983,11 @@ uint64_t LocalStore::queryValidPathId(State & state, const StorePath & path) return use.getInt(0); } - bool LocalStore::isValidPath_(State & state, const StorePath & path) { return state.stmts->QueryPathInfo.use()(printStorePath(path)).next(); } - bool LocalStore::isValidPathUncached(const StorePath & path) { return retrySQLite([&]() { @@ -972,38 +996,40 @@ bool LocalStore::isValidPathUncached(const StorePath & path) }); } - -StorePathSet LocalStore::queryValidPaths(const StorePathSet & paths, SubstituteFlag maybeSubstitute) +StorePathSet LocalStore::queryValidPaths(const StorePathSet & paths, + SubstituteFlag maybeSubstitute) { StorePathSet res; for (auto & i : paths) - if (isValidPath(i)) res.insert(i); + if (isValidPath(i)) + res.insert(i); return res; } - StorePathSet LocalStore::queryAllValidPaths() { return retrySQLite([&]() { auto state(_state.lock()); auto use(state->stmts->QueryValidPaths.use()); StorePathSet res; - while (use.next()) res.insert(parseStorePath(use.getStr(0))); + while (use.next()) + res.insert(parseStorePath(use.getStr(0))); return res; }); } - -void LocalStore::queryReferrers(State & state, const StorePath & path, StorePathSet & referrers) +void LocalStore::queryReferrers(State & state, const StorePath & path, + StorePathSet & referrers) { - auto useQueryReferrers(state.stmts->QueryReferrers.use()(printStorePath(path))); + auto useQueryReferrers( + state.stmts->QueryReferrers.use()(printStorePath(path))); while (useQueryReferrers.next()) referrers.insert(parseStorePath(useQueryReferrers.getStr(0))); } - -void LocalStore::queryReferrers(const StorePath & path, StorePathSet & referrers) +void LocalStore::queryReferrers(const StorePath & path, + StorePathSet & referrers) { return retrySQLite([&]() { auto state(_state.lock()); @@ -1011,13 +1037,13 @@ void LocalStore::queryReferrers(const StorePath & path, StorePathSet & referrers }); } - StorePathSet LocalStore::queryValidDerivers(const StorePath & path) { return retrySQLite([&]() { auto state(_state.lock()); - auto useQueryValidDerivers(state->stmts->QueryValidDerivers.use()(printStorePath(path))); + auto useQueryValidDerivers( + state->stmts->QueryValidDerivers.use()(printStorePath(path))); StorePathSet derivers; while (useQueryValidDerivers.next()) @@ -1027,30 +1053,30 @@ StorePathSet LocalStore::queryValidDerivers(const StorePath & path) }); } - std::map> LocalStore::queryPartialDerivationOutputMap(const StorePath & path_) { auto path = path_; - auto outputs = retrySQLite>>([&]() { - auto state(_state.lock()); - std::map> outputs; - uint64_t drvId; - drvId = queryValidPathId(*state, path); - auto use(state->stmts->QueryDerivationOutputs.use()(drvId)); - while (use.next()) - outputs.insert_or_assign( - use.getStr(0), parseStorePath(use.getStr(1))); - - return outputs; - }); + auto outputs = + retrySQLite>>([&]() { + auto state(_state.lock()); + std::map> outputs; + uint64_t drvId; + drvId = queryValidPathId(*state, path); + auto use(state->stmts->QueryDerivationOutputs.use()(drvId)); + while (use.next()) + outputs.insert_or_assign(use.getStr(0), + parseStorePath(use.getStr(1))); + + return outputs; + }); if (!settings.isExperimentalFeatureEnabled(Xp::CaDerivations)) return outputs; auto drv = readInvalidDerivation(path); auto drvHashes = staticOutputHashes(*this, drv); - for (auto& [outputName, hash] : drvHashes) { + for (auto & [outputName, hash] : drvHashes) { auto realisation = queryRealisation(DrvOutput{hash, outputName}); if (realisation) outputs.insert_or_assign(outputName, realisation->outPath); @@ -1061,30 +1087,36 @@ LocalStore::queryPartialDerivationOutputMap(const StorePath & path_) return outputs; } -std::optional LocalStore::queryPathFromHashPart(const std::string & hashPart) +std::optional +LocalStore::queryPathFromHashPart(const std::string & hashPart) { - if (hashPart.size() != StorePath::HashLen) throw Error("invalid hash part"); + if (hashPart.size() != StorePath::HashLen) + throw Error("invalid hash part"); Path prefix = storeDir + "/" + hashPart; - return retrySQLite>([&]() -> std::optional { - auto state(_state.lock()); + return retrySQLite>( + [&]() -> std::optional { + auto state(_state.lock()); - auto useQueryPathFromHashPart(state->stmts->QueryPathFromHashPart.use()(prefix)); + auto useQueryPathFromHashPart( + state->stmts->QueryPathFromHashPart.use()(prefix)); - if (!useQueryPathFromHashPart.next()) return {}; + if (!useQueryPathFromHashPart.next()) + return {}; - const char * s = (const char *) sqlite3_column_text(state->stmts->QueryPathFromHashPart, 0); - if (s && prefix.compare(0, prefix.size(), s, prefix.size()) == 0) - return parseStorePath(s); - return {}; - }); + const char * s = (const char *) sqlite3_column_text( + state->stmts->QueryPathFromHashPart, 0); + if (s && prefix.compare(0, prefix.size(), s, prefix.size()) == 0) + return parseStorePath(s); + return {}; + }); } - StorePathSet LocalStore::querySubstitutablePaths(const StorePathSet & paths) { - if (!settings.useSubstitutes) return StorePathSet(); + if (!settings.useSubstitutes) + return StorePathSet(); StorePathSet remaining; for (auto & i : paths) @@ -1093,9 +1125,12 @@ StorePathSet LocalStore::querySubstitutablePaths(const StorePathSet & paths) StorePathSet res; for (auto & sub : getDefaultSubstituters()) { - if (remaining.empty()) break; - if (sub->storeDir != storeDir) continue; - if (!sub->wantMassQuery) continue; + if (remaining.empty()) + break; + if (sub->storeDir != storeDir) + continue; + if (!sub->wantMassQuery) + continue; auto valid = sub->queryValidPaths(remaining); @@ -1112,11 +1147,12 @@ StorePathSet LocalStore::querySubstitutablePaths(const StorePathSet & paths) return res; } - // FIXME: move this, it's not specific to LocalStore. -void LocalStore::querySubstitutablePathInfos(const StorePathCAMap & paths, SubstitutablePathInfos & infos) +void LocalStore::querySubstitutablePathInfos(const StorePathCAMap & paths, + SubstitutablePathInfos & infos) { - if (!settings.useSubstitutes) return; + if (!settings.useSubstitutes) + return; for (auto & sub : getDefaultSubstituters()) { for (auto & path : paths) { if (infos.count(path.first)) @@ -1127,27 +1163,34 @@ void LocalStore::querySubstitutablePathInfos(const StorePathCAMap & paths, Subst // Recompute store path so that we can use a different store root. if (path.second) { - subPath = makeFixedOutputPathFromCA(path.first.name(), *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("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)); + debug("checking substituter '%s' for path '%s'", sub->getUri(), + sub->printStorePath(subPath)); try { auto info = sub->queryPathInfo(subPath); - if (sub->storeDir != storeDir && !(info->isContentAddressed(*sub) && info->references.empty())) + if (sub->storeDir != storeDir && + !(info->isContentAddressed(*sub) && + info->references.empty())) continue; auto narInfo = std::dynamic_pointer_cast( std::shared_ptr(info)); - infos.insert_or_assign(path.first, SubstitutablePathInfo{ - info->deriver, - info->references, - narInfo ? narInfo->fileSize : 0, - info->narSize}); + infos.insert_or_assign( + path.first, + SubstitutablePathInfo{info->deriver, info->references, + narInfo ? narInfo->fileSize : 0, + info->narSize}); } catch (InvalidPath &) { } catch (SubstituterDisabled &) { } catch (Error & e) { @@ -1160,20 +1203,19 @@ void LocalStore::querySubstitutablePathInfos(const StorePathCAMap & paths, Subst } } - void LocalStore::registerValidPath(const ValidPathInfo & info) { registerValidPaths({{info.path, info}}); } - void LocalStore::registerValidPaths(const ValidPathInfos & infos) { /* SQLite will fsync by default, but the new valid paths may not be fsync-ed. So some may want to fsync them before registering the validity, at the expense of some speed of the path registering operation. */ - if (settings.syncBeforeRegistering) sync(); + if (settings.syncBeforeRegistering) + sync(); return retrySQLite([&]() { auto state(_state.lock()); @@ -1193,7 +1235,9 @@ void LocalStore::registerValidPaths(const ValidPathInfos & infos) for (auto & [_, i] : infos) { auto referrer = queryValidPathId(*state, i.path); for (auto & j : i.references) - state->stmts->AddReference.use()(referrer)(queryValidPathId(*state, j)).exec(); + state->stmts->AddReference + .use()(referrer)(queryValidPathId(*state, j)) + .exec(); } /* Check that the derivation outputs are correct. We can't do @@ -1201,32 +1245,30 @@ void LocalStore::registerValidPaths(const ValidPathInfos & infos) not be valid yet. */ for (auto & [_, i] : infos) if (i.path.isDerivation()) { - // FIXME: inefficient; we already loaded the derivation in addValidPath(). - checkDerivationOutputs(i.path, - readInvalidDerivation(i.path)); + // FIXME: inefficient; we already loaded the derivation in + // addValidPath(). + checkDerivationOutputs(i.path, readInvalidDerivation(i.path)); } /* Do a topological sort of the paths. This will throw an error if a cycle is detected and roll back the transaction. Cycles can only occur when a derivation has multiple outputs. */ - topoSort(paths, - {[&](const StorePath & path) { - auto i = infos.find(path); - return i == infos.end() ? StorePathSet() : i->second.references; - }}, - {[&](const StorePath & path, const StorePath & parent) { - return BuildError( - "cycle detected in the references of '%s' from '%s'", - printStorePath(path), - printStorePath(parent)); - }}); + topoSort(paths, {[&](const StorePath & path) { + auto i = infos.find(path); + return i == infos.end() ? StorePathSet() + : i->second.references; + }}, + {[&](const StorePath & path, const StorePath & parent) { + return BuildError( + "cycle detected in the references of '%s' from '%s'", + printStorePath(path), printStorePath(parent)); + }}); txn.commit(); }); } - /* Invalidate a path. The caller is responsible for checking that there are no referrers. */ void LocalStore::invalidatePath(State & state, const StorePath & path) @@ -1248,7 +1290,8 @@ const PublicKeys & LocalStore::getPublicKeys() { auto state(_state.lock()); if (!state->publicKeys) - state->publicKeys = std::make_unique(getDefaultPublicKeys()); + state->publicKeys = + std::make_unique(getDefaultPublicKeys()); return *state->publicKeys; } @@ -1263,10 +1306,11 @@ bool LocalStore::realisationIsUntrusted(const Realisation & realisation) } void LocalStore::addToStore(const ValidPathInfo & info, Source & source, - RepairFlag repair, CheckSigsFlag checkSigs) + RepairFlag repair, CheckSigsFlag checkSigs) { if (checkSigs && pathInfoIsUntrusted(info)) - throw Error("cannot add path '%s' because it lacks a valid signature", printStorePath(info.path)); + throw Error("cannot add path '%s' because it lacks a valid signature", + printStorePath(info.path)); addTempRoot(info.path); @@ -1290,41 +1334,46 @@ void LocalStore::addToStore(const ValidPathInfo & info, Source & source, of the NAR. */ HashSink hashSink(htSHA256); - TeeSource wrapperSource { source, hashSink }; + TeeSource wrapperSource{source, hashSink}; restorePath(realPath, wrapperSource); auto hashResult = hashSink.finish(); if (hashResult.first != info.narHash) - throw Error("hash mismatch importing path '%s';\n specified: %s\n got: %s", - printStorePath(info.path), info.narHash.to_string(Base32, true), hashResult.first.to_string(Base32, true)); + throw Error("hash mismatch importing path '%s';\n specified: " + "%s\n got: %s", + printStorePath(info.path), + info.narHash.to_string(Base32, true), + hashResult.first.to_string(Base32, true)); if (hashResult.second != info.narSize) - throw Error("size mismatch importing path '%s';\n specified: %s\n got: %s", - printStorePath(info.path), info.narSize, hashResult.second); + throw Error("size mismatch importing path '%s';\n specified: " + "%s\n got: %s", + printStorePath(info.path), info.narSize, + hashResult.second); if (info.ca) { if (auto foHash = std::get_if(&*info.ca)) { auto actualFoHash = hashCAPath( - foHash->method, - foHash->hash.type, - info.path - ); + foHash->method, foHash->hash.type, info.path); if (foHash->hash != actualFoHash.hash) { - throw Error("ca hash mismatch importing path '%s';\n specified: %s\n got: %s", - printStorePath(info.path), - foHash->hash.to_string(Base32, true), - actualFoHash.hash.to_string(Base32, true)); + throw Error("ca hash mismatch importing path '%s';\n " + "specified: %s\n got: %s", + printStorePath(info.path), + foHash->hash.to_string(Base32, true), + actualFoHash.hash.to_string(Base32, true)); } } if (auto textHash = std::get_if(&*info.ca)) { - auto actualTextHash = hashString(htSHA256, readFile(realPath)); + auto actualTextHash = + hashString(htSHA256, readFile(realPath)); if (textHash->hash != actualTextHash) { - throw Error("ca hash mismatch importing path '%s';\n specified: %s\n got: %s", - printStorePath(info.path), - textHash->hash.to_string(Base32, true), - actualTextHash.to_string(Base32, true)); + throw Error("ca hash mismatch importing path '%s';\n " + "specified: %s\n got: %s", + printStorePath(info.path), + textHash->hash.to_string(Base32, true), + actualTextHash.to_string(Base32, true)); } } } @@ -1342,13 +1391,15 @@ void LocalStore::addToStore(const ValidPathInfo & info, Source & source, } } - -StorePath LocalStore::addToStoreFromDump(Source & source0, std::string_view name, - FileIngestionMethod method, HashType hashAlgo, RepairFlag repair, const StorePathSet & references) +StorePath LocalStore::addToStoreFromDump(Source & source0, + std::string_view name, + FileIngestionMethod method, + HashType hashAlgo, RepairFlag repair, + const StorePathSet & references) { /* For computing the store path. */ auto hashSink = std::make_unique(hashAlgo); - TeeSource source { source0, *hashSink }; + TeeSource source{source0, *hashSink}; /* Read the source path into memory, but only if it's up to narBufferSize bytes. If it's larger, write it to a temporary @@ -1369,9 +1420,7 @@ StorePath LocalStore::addToStoreFromDump(Source & source0, std::string_view name auto want = std::min(chunkSize, settings.narBufferSize - oldSize); dump.resize(oldSize + want); auto got = 0; - Finally cleanup([&]() { - dump.resize(oldSize + got); - }); + Finally cleanup([&]() { dump.resize(oldSize + got); }); try { got = source.read(dump.data() + oldSize, want); } catch (EndOfFile &) { @@ -1385,8 +1434,8 @@ StorePath LocalStore::addToStoreFromDump(Source & source0, std::string_view name if (!inMemory) { /* Drain what we pulled so far, and then keep on pulling */ - StringSource dumpSource { dump }; - ChainSource bothSource { dumpSource, source }; + StringSource dumpSource{dump}; + ChainSource bothSource{dumpSource, source}; auto tempDir = createTempDir(realStoreDir, "add"); delTempDir = std::make_unique(tempDir); @@ -1422,7 +1471,7 @@ StorePath LocalStore::addToStoreFromDump(Source & source0, std::string_view name autoGC(); if (inMemory) { - StringSource dumpSource { dump }; + StringSource dumpSource{dump}; /* Restore from the NAR in memory. */ if (method == FileIngestionMethod::Recursive) restorePath(realPath, dumpSource); @@ -1436,21 +1485,23 @@ StorePath LocalStore::addToStoreFromDump(Source & source0, std::string_view name /* For computing the nar hash. In recursive SHA-256 mode, this is the same as the store hash, so no need to do it again. */ - auto narHash = std::pair { hash, size }; - if (method != FileIngestionMethod::Recursive || hashAlgo != htSHA256) { - HashSink narSink { htSHA256 }; + auto narHash = std::pair{hash, size}; + if (method != FileIngestionMethod::Recursive || + hashAlgo != htSHA256) { + HashSink narSink{htSHA256}; dumpPath(realPath, narSink); narHash = narSink.finish(); } - canonicalisePathMetaData(realPath, -1); // FIXME: merge into restorePath + canonicalisePathMetaData(realPath, + -1); // FIXME: merge into restorePath optimisePath(realPath, repair); - ValidPathInfo info { dstPath, narHash.first }; + ValidPathInfo info{dstPath, narHash.first}; info.narSize = narHash.second; info.references = references; - info.ca = FixedOutputHash { .method = method, .hash = hash }; + info.ca = FixedOutputHash{.method = method, .hash = hash}; registerValidPath(info); } @@ -1460,11 +1511,9 @@ StorePath LocalStore::addToStoreFromDump(Source & source0, std::string_view name return dstPath; } - -StorePath LocalStore::addTextToStore( - std::string_view name, - std::string_view s, - const StorePathSet & references, RepairFlag repair) +StorePath LocalStore::addTextToStore(std::string_view name, std::string_view s, + const StorePathSet & references, + RepairFlag repair) { auto hash = hashString(htSHA256, s); auto dstPath = makeTextPath(name, hash, references); @@ -1493,10 +1542,10 @@ StorePath LocalStore::addTextToStore( optimisePath(realPath, repair); - ValidPathInfo info { dstPath, narHash }; + ValidPathInfo info{dstPath, narHash}; info.narSize = sink.s.size(); info.references = references; - info.ca = TextHash { .hash = hash }; + info.ca = TextHash{.hash = hash}; registerValidPath(info); } @@ -1506,7 +1555,6 @@ StorePath LocalStore::addTextToStore( return dstPath; } - /* Create a temporary directory in the store that won't be garbage-collected. */ Path LocalStore::createTempDirInStore() @@ -1522,7 +1570,6 @@ Path LocalStore::createTempDirInStore() return tmpDir; } - void LocalStore::invalidatePathChecked(const StorePath & path) { retrySQLite([&]() { @@ -1531,10 +1578,12 @@ void LocalStore::invalidatePathChecked(const StorePath & path) SQLiteTxn txn(state->db); if (isValidPath_(*state, path)) { - StorePathSet referrers; queryReferrers(*state, path, referrers); + StorePathSet referrers; + queryReferrers(*state, path, referrers); referrers.erase(path); /* ignore self-references */ if (!referrers.empty()) - throw PathInUse("cannot delete path '%s' because it is in use by %s", + throw PathInUse( + "cannot delete path '%s' because it is in use by %s", printStorePath(path), showPaths(referrers)); invalidatePath(*state, path); } @@ -1543,7 +1592,6 @@ void LocalStore::invalidatePathChecked(const StorePath & path) }); } - bool LocalStore::verifyStore(bool checkContents, RepairFlag repair) { printInfo(format("reading the Nix store...")); @@ -1553,10 +1601,12 @@ bool LocalStore::verifyStore(bool checkContents, RepairFlag repair) /* Acquire the global GC lock to get a consistent snapshot of existing and valid paths. */ auto fdGCLock = openGCLock(); - FdLock gcLock(fdGCLock.get(), ltRead, true, "waiting for the big garbage collector lock..."); + FdLock gcLock(fdGCLock.get(), ltRead, true, + "waiting for the big garbage collector lock..."); StringSet store; - for (auto & i : readDirectory(realStoreDir)) store.insert(i.name); + for (auto & i : readDirectory(realStoreDir)) + store.insert(i.name); /* Check whether all valid paths actually exist. */ printInfo("checking path existence..."); @@ -1575,9 +1625,11 @@ bool LocalStore::verifyStore(bool checkContents, RepairFlag repair) for (auto & link : readDirectory(linksDir)) { printMsg(lvlTalkative, "checking contents of '%s'", link.name); Path linkPath = linksDir + "/" + link.name; - std::string hash = hashPath(htSHA256, linkPath).first.to_string(Base32, false); + std::string hash = + hashPath(htSHA256, linkPath).first.to_string(Base32, false); if (hash != link.name) { - printError("link '%s' was modified! expected hash '%s', got '%s'", + printError( + "link '%s' was modified! expected hash '%s', got '%s'", linkPath, link.name, hash); if (repair) { if (unlink(linkPath.c_str()) == 0) @@ -1596,34 +1648,45 @@ bool LocalStore::verifyStore(bool checkContents, RepairFlag repair) for (auto & i : validPaths) { try { - auto info = std::const_pointer_cast(std::shared_ptr(queryPathInfo(i))); + auto info = std::const_pointer_cast( + std::shared_ptr(queryPathInfo(i))); /* Check the content hash (optionally - slow). */ - printMsg(lvlTalkative, "checking contents of '%s'", printStorePath(i)); + printMsg(lvlTalkative, "checking contents of '%s'", + printStorePath(i)); auto hashSink = HashSink(info->narHash.type); dumpPath(Store::toRealPath(i), hashSink); auto current = hashSink.finish(); - if (info->narHash != nullHash && info->narHash != current.first) { - printError("path '%s' was modified! expected hash '%s', got '%s'", - printStorePath(i), info->narHash.to_string(Base32, true), current.first.to_string(Base32, true)); - if (repair) repairPath(i); else errors = true; + if (info->narHash != nullHash && + info->narHash != current.first) { + printError( + "path '%s' was modified! expected hash '%s', got '%s'", + printStorePath(i), + info->narHash.to_string(Base32, true), + current.first.to_string(Base32, true)); + if (repair) + repairPath(i); + else + errors = true; } else { bool update = false; /* Fill in missing hashes. */ if (info->narHash == nullHash) { - printInfo("fixing missing hash on '%s'", printStorePath(i)); + printInfo("fixing missing hash on '%s'", + printStorePath(i)); info->narHash = current.first; update = true; } /* Fill in missing narSize fields (from old stores). */ if (info->narSize == 0) { - printInfo("updating size field on '%s' to %s", printStorePath(i), current.second); + printInfo("updating size field on '%s' to %s", + printStorePath(i), current.second); info->narSize = current.second; update = true; } @@ -1632,7 +1695,6 @@ bool LocalStore::verifyStore(bool checkContents, RepairFlag repair) auto state(_state.lock()); updatePathInfo(*state, *info); } - } } catch (Error & e) { @@ -1650,13 +1712,14 @@ bool LocalStore::verifyStore(bool checkContents, RepairFlag repair) return errors; } - void LocalStore::verifyPath(const Path & pathS, const StringSet & store, - PathSet & done, StorePathSet & validPaths, RepairFlag repair, bool & errors) + PathSet & done, StorePathSet & validPaths, + RepairFlag repair, bool & errors) { checkInterrupt(); - if (!done.insert(pathS).second) return; + if (!done.insert(pathS).second) + return; if (!isStorePath(pathS)) { printError("path '%s' is not in the Nix store", pathS); @@ -1669,20 +1732,25 @@ void LocalStore::verifyPath(const Path & pathS, const StringSet & store, /* Check any referrers first. If we can invalidate them first, then we can invalidate this path as well. */ bool canInvalidate = true; - StorePathSet referrers; queryReferrers(path, referrers); + StorePathSet referrers; + queryReferrers(path, referrers); for (auto & i : referrers) if (i != path) { - verifyPath(printStorePath(i), store, done, validPaths, repair, errors); + verifyPath(printStorePath(i), store, done, validPaths, repair, + errors); if (validPaths.count(i)) canInvalidate = false; } if (canInvalidate) { - printInfo("path '%s' disappeared, removing from database...", pathS); + printInfo("path '%s' disappeared, removing from database...", + pathS); auto state(_state.lock()); invalidatePath(*state, path); } else { - printError("path '%s' disappeared, but it still has valid referrers!", pathS); + printError( + "path '%s' disappeared, but it still has valid referrers!", + pathS); if (repair) try { repairPath(path); @@ -1690,7 +1758,8 @@ void LocalStore::verifyPath(const Path & pathS, const StringSet & store, logWarning(e.info()); errors = true; } - else errors = true; + else + errors = true; } return; @@ -1699,14 +1768,10 @@ void LocalStore::verifyPath(const Path & pathS, const StringSet & store, validPaths.insert(std::move(path)); } +unsigned int LocalStore::getProtocol() { return PROTOCOL_VERSION; } -unsigned int LocalStore::getProtocol() -{ - return PROTOCOL_VERSION; -} - - -#if defined(FS_IOC_SETFLAGS) && defined(FS_IOC_GETFLAGS) && defined(FS_IMMUTABLE_FL) +#if defined(FS_IOC_SETFLAGS) && defined(FS_IOC_GETFLAGS) && \ + defined(FS_IMMUTABLE_FL) static void makeMutable(const Path & path) { @@ -1714,7 +1779,8 @@ static void makeMutable(const Path & path) auto st = lstat(path); - if (!S_ISDIR(st.st_mode) && !S_ISREG(st.st_mode)) return; + if (!S_ISDIR(st.st_mode) && !S_ISREG(st.st_mode)) + return; if (S_ISDIR(st.st_mode)) { for (auto & i : readDirectory(path)) @@ -1726,7 +1792,8 @@ static void makeMutable(const Path & path) security hole). */ AutoCloseFD fd = open(path.c_str(), O_RDONLY | O_NOFOLLOW | O_CLOEXEC); if (fd == -1) { - if (errno == ELOOP) return; // it's a symlink + if (errno == ELOOP) + return; // it's a symlink throw SysError("opening file '%1%'", path); } @@ -1734,45 +1801,48 @@ static void makeMutable(const Path & path) /* Silently ignore errors getting/setting the immutable flag so that we work correctly on filesystems that don't support it. */ - if (ioctl(fd, FS_IOC_GETFLAGS, &flags)) return; + if (ioctl(fd, FS_IOC_GETFLAGS, &flags)) + return; old = flags; flags &= ~FS_IMMUTABLE_FL; - if (old == flags) return; - if (ioctl(fd, FS_IOC_SETFLAGS, &flags)) return; + if (old == flags) + return; + if (ioctl(fd, FS_IOC_SETFLAGS, &flags)) + return; } /* Upgrade from schema 6 (Nix 0.15) to schema 7 (Nix >= 1.3). */ void LocalStore::upgradeStore7() { - if (getuid() != 0) return; - printInfo("removing immutable bits from the Nix store (this may take a while)..."); + if (getuid() != 0) + return; + printInfo("removing immutable bits from the Nix store (this may take a " + "while)..."); makeMutable(realStoreDir); } #else -void LocalStore::upgradeStore7() -{ -} +void LocalStore::upgradeStore7() {} #endif - void LocalStore::vacuumDB() { auto state(_state.lock()); state->db.exec("vacuum"); } - -void LocalStore::addSignatures(const StorePath & storePath, const StringSet & sigs) +void LocalStore::addSignatures(const StorePath & storePath, + const StringSet & sigs) { retrySQLite([&]() { auto state(_state.lock()); SQLiteTxn txn(state->db); - auto info = std::const_pointer_cast(queryPathInfoInternal(*state, storePath)); + auto info = std::const_pointer_cast( + queryPathInfoInternal(*state, storePath)); info->sigs.insert(sigs.begin(), sigs.end()); @@ -1782,7 +1852,6 @@ void LocalStore::addSignatures(const StorePath & storePath, const StringSet & si }); } - void LocalStore::signRealisation(Realisation & realisation) { // FIXME: keep secret keys in memory. @@ -1807,13 +1876,10 @@ void LocalStore::signPathInfo(ValidPathInfo & info) } } - void LocalStore::createUser(const std::string & userName, uid_t userId) { - for (auto & dir : { - fmt("%s/profiles/per-user/%s", stateDir, userName), - fmt("%s/gcroots/per-user/%s", stateDir, userName) - }) { + for (auto & dir : {fmt("%s/profiles/per-user/%s", stateDir, userName), + fmt("%s/gcroots/per-user/%s", stateDir, userName)}) { createDirs(dir); if (chmod(dir.c_str(), 0755) == -1) throw SysError("changing permissions of directory '%s'", dir); @@ -1822,14 +1888,12 @@ void LocalStore::createUser(const std::string & userName, uid_t userId) } } -std::optional> LocalStore::queryRealisationCore_( - LocalStore::State & state, - const DrvOutput & id) +std::optional> +LocalStore::queryRealisationCore_(LocalStore::State & state, + const DrvOutput & id) { auto useQueryRealisedOutput( - state.stmts->QueryRealisedOutput.use() - (id.strHash()) - (id.outputName)); + state.stmts->QueryRealisedOutput.use()(id.strHash())(id.outputName)); if (!useQueryRealisedOutput.next()) return std::nullopt; auto realisationDbId = useQueryRealisedOutput.getInt(0); @@ -1837,19 +1901,15 @@ std::optional> LocalStore::queryRealisationCore_ auto signatures = tokenizeString(useQueryRealisedOutput.getStr(2)); - return {{ - realisationDbId, - Realisation{ - .id = id, - .outPath = outputPath, - .signatures = signatures, - } - }}; + return {{realisationDbId, Realisation{ + .id = id, + .outPath = outputPath, + .signatures = signatures, + }}}; } -std::optional LocalStore::queryRealisation_( - LocalStore::State & state, - const DrvOutput & id) +std::optional +LocalStore::queryRealisation_(LocalStore::State & state, const DrvOutput & id) { auto maybeCore = queryRealisationCore_(state, id); if (!maybeCore) @@ -1858,10 +1918,9 @@ std::optional LocalStore::queryRealisation_( std::map dependentRealisations; auto useRealisationRefs( - state.stmts->QueryRealisationReferences.use() - (realisationDbId)); + state.stmts->QueryRealisationReferences.use()(realisationDbId)); while (useRealisationRefs.next()) { - auto depId = DrvOutput { + auto depId = DrvOutput{ Hash::parseAnyPrefixed(useRealisationRefs.getStr(0)), useRealisationRefs.getStr(1), }; @@ -1873,18 +1932,19 @@ std::optional LocalStore::queryRealisation_( res.dependentRealisations = dependentRealisations; - return { res }; + return {res}; } -void LocalStore::queryRealisationUncached(const DrvOutput & id, - Callback> callback) noexcept +void LocalStore::queryRealisationUncached( + const DrvOutput & id, + Callback> callback) noexcept { try { - auto maybeRealisation - = retrySQLite>([&]() { - auto state(_state.lock()); - return queryRealisation_(*state, id); - }); + auto maybeRealisation = + retrySQLite>([&]() { + auto state(_state.lock()); + return queryRealisation_(*state, id); + }); if (maybeRealisation) callback( std::make_shared(maybeRealisation.value())); @@ -1896,21 +1956,20 @@ void LocalStore::queryRealisationUncached(const DrvOutput & id, } } -FixedOutputHash LocalStore::hashCAPath( - const FileIngestionMethod & method, const HashType & hashType, - const StorePath & path) +FixedOutputHash LocalStore::hashCAPath(const FileIngestionMethod & method, + const HashType & hashType, + const StorePath & path) { - return hashCAPath(method, hashType, Store::toRealPath(path), path.hashPart()); + return hashCAPath(method, hashType, Store::toRealPath(path), + path.hashPart()); } -FixedOutputHash LocalStore::hashCAPath( - const FileIngestionMethod & method, - const HashType & hashType, - const Path & path, - const std::string_view pathHash -) +FixedOutputHash LocalStore::hashCAPath(const FileIngestionMethod & method, + const HashType & hashType, + const Path & path, + const std::string_view pathHash) { - HashModuloSink caSink ( hashType, std::string(pathHash) ); + HashModuloSink caSink(hashType, std::string(pathHash)); switch (method) { case FileIngestionMethod::Recursive: dumpPath(path, caSink); @@ -1932,9 +1991,11 @@ void LocalStore::addBuildLog(const StorePath & drvPath, std::string_view log) auto baseName = drvPath.to_string(); - auto logPath = fmt("%s/%s/%s/%s.bz2", logDir, drvsLogDir, baseName.substr(0, 2), baseName.substr(2)); + auto logPath = fmt("%s/%s/%s/%s.bz2", logDir, drvsLogDir, + baseName.substr(0, 2), baseName.substr(2)); - if (pathExists(logPath)) return; + if (pathExists(logPath)) + return; createDirs(dirOf(logPath)); @@ -1946,10 +2007,6 @@ void LocalStore::addBuildLog(const StorePath & drvPath, std::string_view log) throw SysError("renaming '%1%' to '%2%'", tmpFile, logPath); } -std::optional LocalStore::getVersion() -{ - return nixVersion; -} - +std::optional LocalStore::getVersion() { return nixVersion; } -} // namespace nix +} // namespace nix diff --git a/src/libstore/local-store.hh b/src/libstore/local-store.hh index 70d225be320f..c421d8d17531 100644 --- a/src/libstore/local-store.hh +++ b/src/libstore/local-store.hh @@ -14,45 +14,38 @@ #include #include - namespace nix { - /* Nix store and database schema version. Version 1 (or 0) was Nix <= 0.7. Version 2 was Nix 0.8 and 0.9. Version 3 is Nix 0.10. Version 4 is Nix 0.11. Version 5 is Nix 0.12-0.16. Version 6 is Nix 1.0. Version 7 is Nix 1.3. Version 10 is 2.0. */ const int nixSchemaVersion = 10; - -struct OptimiseStats -{ +struct OptimiseStats { unsigned long filesLinked = 0; uint64_t bytesFreed = 0; uint64_t blocksFreed = 0; }; -struct LocalStoreConfig : virtual LocalFSStoreConfig -{ +struct LocalStoreConfig : virtual LocalFSStoreConfig { using LocalFSStoreConfig::LocalFSStoreConfig; - Setting requireSigs{(StoreConfig*) this, - settings.requireSigs, - "require-sigs", "whether store paths should have a trusted signature on import"}; + Setting requireSigs{ + (StoreConfig *) this, settings.requireSigs, "require-sigs", + "whether store paths should have a trusted signature on import"}; const std::string name() override { return "Local Store"; } }; - -class LocalStore : public virtual LocalStoreConfig, public virtual LocalFSStore, public virtual GcStore -{ -private: - +class LocalStore : public virtual LocalStoreConfig, + public virtual LocalFSStore, + public virtual GcStore { + private: /* Lock file used for upgrading. */ AutoCloseFD globalLock; - struct State - { + struct State { /* The SQLite database object. */ SQLite db; @@ -88,8 +81,7 @@ private: Sync _state; -public: - + public: const Path dbDir; const Path linksDir; const Path reservedPath; @@ -97,12 +89,10 @@ public: const Path tempRootsDir; const Path fnTempRoots; -private: - + private: const PublicKeys & getPublicKeys(); -public: - + public: // Hack for build-remote.cc. PathSet locksHeld; @@ -118,57 +108,61 @@ public: bool isValidPathUncached(const StorePath & path) override; - StorePathSet queryValidPaths(const StorePathSet & paths, - SubstituteFlag maybeSubstitute = NoSubstitute) override; + StorePathSet + queryValidPaths(const StorePathSet & paths, + SubstituteFlag maybeSubstitute = NoSubstitute) override; StorePathSet queryAllValidPaths() override; void queryPathInfoUncached(const StorePath & path, - Callback> callback) noexcept override; + Callback> + callback) noexcept override; - void queryReferrers(const StorePath & path, StorePathSet & referrers) override; + void queryReferrers(const StorePath & path, + StorePathSet & referrers) override; StorePathSet queryValidDerivers(const StorePath & path) override; - std::map> queryPartialDerivationOutputMap(const StorePath & path) override; + std::map> + queryPartialDerivationOutputMap(const StorePath & path) override; - std::optional queryPathFromHashPart(const std::string & hashPart) override; + std::optional + queryPathFromHashPart(const std::string & hashPart) override; StorePathSet querySubstitutablePaths(const StorePathSet & paths) override; void querySubstitutablePathInfos(const StorePathCAMap & paths, - SubstitutablePathInfos & infos) override; + SubstitutablePathInfos & infos) override; bool pathInfoIsUntrusted(const ValidPathInfo &) override; - bool realisationIsUntrusted(const Realisation & ) override; + bool realisationIsUntrusted(const Realisation &) override; void addToStore(const ValidPathInfo & info, Source & source, - RepairFlag repair, CheckSigsFlag checkSigs) override; + RepairFlag repair, CheckSigsFlag checkSigs) override; StorePath addToStoreFromDump(Source & dump, std::string_view name, - FileIngestionMethod method, HashType hashAlgo, RepairFlag repair, const StorePathSet & references) override; + FileIngestionMethod method, HashType hashAlgo, + RepairFlag repair, + const StorePathSet & references) override; - StorePath addTextToStore( - std::string_view name, - std::string_view s, - const StorePathSet & references, - RepairFlag repair) override; + StorePath addTextToStore(std::string_view name, std::string_view s, + const StorePathSet & references, + RepairFlag repair) override; void addTempRoot(const StorePath & path) override; void addIndirectRoot(const Path & path) override; -private: - + private: void findTempRoots(Roots & roots, bool censor); AutoCloseFD openGCLock(); -public: - + public: Roots findRoots(bool censor) override; - void collectGarbage(const GCOptions & options, GCResults & results) override; + void collectGarbage(const GCOptions & options, + GCResults & results) override; /* Optimise the disk space usage of the Nix store by hard-linking files with the same contents. */ @@ -198,7 +192,8 @@ public: void repairPath(const StorePath & path) override; - void addSignatures(const StorePath & storePath, const StringSet & sigs) override; + void addSignatures(const StorePath & storePath, + const StringSet & sigs) override; /* If free disk space in /nix/store if below minFree, delete garbage until it exceeds maxFree. */ @@ -207,22 +202,23 @@ public: /* Register the store path 'output' as the output named 'outputName' of derivation 'deriver'. */ void registerDrvOutput(const Realisation & info) override; - void registerDrvOutput(const Realisation & info, CheckSigsFlag checkSigs) override; - void cacheDrvOutputMapping( - State & state, - const uint64_t deriver, - const std::string & outputName, - const StorePath & output); - - std::optional queryRealisation_(State & state, const DrvOutput & id); - std::optional> queryRealisationCore_(State & state, const DrvOutput & id); - void queryRealisationUncached(const DrvOutput&, - Callback> callback) noexcept override; + void registerDrvOutput(const Realisation & info, + CheckSigsFlag checkSigs) override; + void cacheDrvOutputMapping(State & state, const uint64_t deriver, + const std::string & outputName, + const StorePath & output); + + std::optional queryRealisation_(State & state, + const DrvOutput & id); + std::optional> + queryRealisationCore_(State & state, const DrvOutput & id); + void queryRealisationUncached(const DrvOutput &, + Callback> + callback) noexcept override; std::optional getVersion() override; -private: - + private: int getSchema(); void openDB(State & state, bool create); @@ -231,17 +227,20 @@ private: uint64_t queryValidPathId(State & state, const StorePath & path); - uint64_t addValidPath(State & state, const ValidPathInfo & info, bool checkOutputs = true); + uint64_t addValidPath(State & state, const ValidPathInfo & info, + bool checkOutputs = true); void invalidatePath(State & state, const StorePath & path); /* Delete a path from the Nix store. */ void invalidatePathChecked(const StorePath & path); - void verifyPath(const Path & path, const StringSet & store, - PathSet & done, StorePathSet & validPaths, RepairFlag repair, bool & errors); + void verifyPath(const Path & path, const StringSet & store, PathSet & done, + StorePathSet & validPaths, RepairFlag repair, + bool & errors); - std::shared_ptr queryPathInfoInternal(State & state, const StorePath & path); + std::shared_ptr + queryPathInfoInternal(State & state, const StorePath & path); void updatePathInfo(State & state, const ValidPathInfo & info); @@ -258,17 +257,21 @@ private: Path createTempDirInStore(); - void checkDerivationOutputs(const StorePath & drvPath, const Derivation & drv); + void checkDerivationOutputs(const StorePath & drvPath, + const Derivation & drv); typedef std::unordered_set InodeHash; InodeHash loadInodeHash(); - Strings readDirectoryIgnoringInodes(const Path & path, const InodeHash & inodeHash); - void optimisePath_(Activity * act, OptimiseStats & stats, const Path & path, InodeHash & inodeHash, RepairFlag repair); + Strings readDirectoryIgnoringInodes(const Path & path, + const InodeHash & inodeHash); + void optimisePath_(Activity * act, OptimiseStats & stats, const Path & path, + InodeHash & inodeHash, RepairFlag repair); // Internal versions that are not wrapped in retry_sqlite. bool isValidPath_(State & state, const StorePath & path); - void queryReferrers(State & state, const StorePath & path, StorePathSet & referrers); + void queryReferrers(State & state, const StorePath & path, + StorePathSet & referrers); /* Add signatures to a ValidPathInfo or Realisation using the secret keys specified by the ‘secret-key-files’ option. */ @@ -278,17 +281,13 @@ private: void createUser(const std::string & userName, uid_t userId) override; // XXX: Make a generic `Store` method - FixedOutputHash hashCAPath( - const FileIngestionMethod & method, - const HashType & hashType, - const StorePath & path); - - FixedOutputHash hashCAPath( - const FileIngestionMethod & method, - const HashType & hashType, - const Path & path, - const std::string_view pathHash - ); + FixedOutputHash hashCAPath(const FileIngestionMethod & method, + const HashType & hashType, + const StorePath & path); + + FixedOutputHash hashCAPath(const FileIngestionMethod & method, + const HashType & hashType, const Path & path, + const std::string_view pathHash); void addBuildLog(const StorePath & drvPath, std::string_view log) override; @@ -298,11 +297,9 @@ private: friend struct DerivationGoal; }; - typedef std::pair Inode; typedef std::set InodesSeen; - /* "Fix", or canonicalise, the meta-data of the files in a store path after it has been built. In particular: - the last modification date on each file is set to 1 (i.e., @@ -311,7 +308,8 @@ typedef std::set InodesSeen; without execute permission; setuid bits etc. are cleared) - the owner and group are set to the Nix user and group, if we're running as root. */ -void canonicalisePathMetaData(const Path & path, uid_t fromUid, InodesSeen & inodesSeen); +void canonicalisePathMetaData(const Path & path, uid_t fromUid, + InodesSeen & inodesSeen); void canonicalisePathMetaData(const Path & path, uid_t fromUid); void canonicaliseTimestampAndPermissions(const Path & path); diff --git a/src/libstore/lock.cc b/src/libstore/lock.cc index fa718f55d017..840619b98e28 100644 --- a/src/libstore/lock.cc +++ b/src/libstore/lock.cc @@ -16,26 +16,29 @@ UserLock::UserLock() createDirs(settings.nixStateDir + "/userpool"); } -bool UserLock::findFreeUser() { - if (enabled()) return true; +bool UserLock::findFreeUser() +{ + if (enabled()) + return true; /* Get the members of the build-users-group. */ struct group * gr = getgrnam(settings.buildUsersGroup.get().c_str()); if (!gr) - throw Error("the group '%1%' specified in 'build-users-group' does not exist", + throw Error( + "the group '%1%' specified in 'build-users-group' does not exist", settings.buildUsersGroup); gid = gr->gr_gid; /* Copy the result of getgrnam. */ Strings users; - for (char * * p = gr->gr_mem; *p; ++p) { + for (char ** p = gr->gr_mem; *p; ++p) { debug("found build user '%1%'", *p); users.push_back(*p); } if (users.empty()) throw Error("the build users group '%1%' has no members", - settings.buildUsersGroup); + settings.buildUsersGroup); /* Find a user account that isn't currently in use for another build. */ @@ -44,13 +47,15 @@ bool UserLock::findFreeUser() { struct passwd * pw = getpwnam(i.c_str()); if (!pw) - throw Error("the user '%1%' in the group '%2%' does not exist", - i, settings.buildUsersGroup); - + throw Error("the user '%1%' in the group '%2%' does not exist", i, + settings.buildUsersGroup); - fnUserLock = (format("%1%/userpool/%2%") % settings.nixStateDir % pw->pw_uid).str(); + fnUserLock = + (format("%1%/userpool/%2%") % settings.nixStateDir % pw->pw_uid) + .str(); - AutoCloseFD fd = open(fnUserLock.c_str(), O_RDWR | O_CREAT | O_CLOEXEC, 0600); + AutoCloseFD fd = + open(fnUserLock.c_str(), O_RDWR | O_CREAT | O_CLOEXEC, 0600); if (!fd) throw SysError("opening user lock '%1%'", fnUserLock); @@ -62,7 +67,7 @@ bool UserLock::findFreeUser() { /* Sanity check... */ if (uid == getuid() || uid == geteuid()) throw Error("the Nix user should not be a member of '%1%'", - settings.buildUsersGroup); + settings.buildUsersGroup); #if __linux__ /* Get the list of supplementary groups of this build user. This @@ -70,21 +75,22 @@ bool UserLock::findFreeUser() { int ngroups = 32; // arbitrary initial guess supplementaryGIDs.resize(ngroups); - int err = getgrouplist(pw->pw_name, pw->pw_gid, supplementaryGIDs.data(), - &ngroups); + int err = getgrouplist(pw->pw_name, pw->pw_gid, + supplementaryGIDs.data(), &ngroups); // Our initial size of 32 wasn't sufficient, the correct size has // been stored in ngroups, so we try again. if (err == -1) { supplementaryGIDs.resize(ngroups); - err = getgrouplist(pw->pw_name, pw->pw_gid, supplementaryGIDs.data(), - &ngroups); + err = getgrouplist(pw->pw_name, pw->pw_gid, + supplementaryGIDs.data(), &ngroups); } // If it failed once more, then something must be broken. if (err == -1) - throw Error("failed to get list of supplementary groups for '%1%'", - pw->pw_name); + throw Error( + "failed to get list of supplementary groups for '%1%'", + pw->pw_name); // Finally, trim back the GID list to its real size supplementaryGIDs.resize(ngroups); @@ -98,9 +104,6 @@ bool UserLock::findFreeUser() { return false; } -void UserLock::kill() -{ - killUser(uid); -} +void UserLock::kill() { killUser(uid); } } diff --git a/src/libstore/lock.hh b/src/libstore/lock.hh index 3d29a7b5ba7d..19bb23a246fe 100644 --- a/src/libstore/lock.hh +++ b/src/libstore/lock.hh @@ -6,9 +6,8 @@ namespace nix { -class UserLock -{ -private: +class UserLock { + private: Path fnUserLock; AutoCloseFD fdUserLock; @@ -18,20 +17,27 @@ private: gid_t gid = 0; std::vector supplementaryGIDs; -public: + public: UserLock(); void kill(); std::string getUser() { return user; } - uid_t getUID() { assert(uid); return uid; } - uid_t getGID() { assert(gid); return gid; } + uid_t getUID() + { + assert(uid); + return uid; + } + uid_t getGID() + { + assert(gid); + return gid; + } std::vector getSupplementaryGIDs() { return supplementaryGIDs; } bool findFreeUser(); bool enabled() { return isEnabled; } - }; } diff --git a/src/libstore/log-store.hh b/src/libstore/log-store.hh index ff1b92e174a1..115ac91d5c28 100644 --- a/src/libstore/log-store.hh +++ b/src/libstore/log-store.hh @@ -2,11 +2,9 @@ #include "store-api.hh" - namespace nix { -struct LogStore : public virtual Store -{ +struct LogStore : public virtual Store { inline static std::string operationName = "Build log storage and retrieval"; /* Return the build log of the specified store path, if available, diff --git a/src/libstore/machines.cc b/src/libstore/machines.cc index e87f469800dc..0ce5472b4839 100644 --- a/src/libstore/machines.cc +++ b/src/libstore/machines.cc @@ -7,53 +7,45 @@ namespace nix { -Machine::Machine(decltype(storeUri) storeUri, - decltype(systemTypes) systemTypes, - decltype(sshKey) sshKey, - decltype(maxJobs) maxJobs, - decltype(speedFactor) speedFactor, - decltype(supportedFeatures) supportedFeatures, - decltype(mandatoryFeatures) mandatoryFeatures, - decltype(sshPublicHostKey) sshPublicHostKey) : - storeUri( - // Backwards compatibility: if the URI is schemeless, is not a path, - // and is not one of the special store connection words, prepend - // ssh://. - storeUri.find("://") != std::string::npos - || storeUri.find("/") != std::string::npos - || storeUri == "auto" - || storeUri == "daemon" - || storeUri == "local" - || hasPrefix(storeUri, "auto?") - || hasPrefix(storeUri, "daemon?") - || hasPrefix(storeUri, "local?") - || hasPrefix(storeUri, "?") - ? storeUri - : "ssh://" + storeUri), - systemTypes(systemTypes), - sshKey(sshKey), - maxJobs(maxJobs), - speedFactor(std::max(1U, speedFactor)), - supportedFeatures(supportedFeatures), - mandatoryFeatures(mandatoryFeatures), - sshPublicHostKey(sshPublicHostKey) -{} +Machine::Machine(decltype(storeUri) storeUri, decltype(systemTypes) systemTypes, + decltype(sshKey) sshKey, decltype(maxJobs) maxJobs, + decltype(speedFactor) speedFactor, + decltype(supportedFeatures) supportedFeatures, + decltype(mandatoryFeatures) mandatoryFeatures, + decltype(sshPublicHostKey) sshPublicHostKey) + : storeUri( + // Backwards compatibility: if the URI is schemeless, is not a path, + // and is not one of the special store connection words, prepend + // ssh://. + storeUri.find("://") != std::string::npos || + storeUri.find("/") != std::string::npos || + storeUri == "auto" || storeUri == "daemon" || + storeUri == "local" || hasPrefix(storeUri, "auto?") || + hasPrefix(storeUri, "daemon?") || + hasPrefix(storeUri, "local?") || hasPrefix(storeUri, "?") + ? storeUri + : "ssh://" + storeUri), + systemTypes(systemTypes), sshKey(sshKey), maxJobs(maxJobs), + speedFactor(std::max(1U, speedFactor)), + supportedFeatures(supportedFeatures), + mandatoryFeatures(mandatoryFeatures), sshPublicHostKey(sshPublicHostKey) +{ +} bool Machine::allSupported(const std::set & features) const { return std::all_of(features.begin(), features.end(), - [&](const std::string & feature) { - return supportedFeatures.count(feature) || - mandatoryFeatures.count(feature); - }); + [&](const std::string & feature) { + return supportedFeatures.count(feature) || + mandatoryFeatures.count(feature); + }); } bool Machine::mandatoryMet(const std::set & features) const { - return std::all_of(mandatoryFeatures.begin(), mandatoryFeatures.end(), - [&](const std::string & feature) { - return features.count(feature); - }); + return std::all_of( + mandatoryFeatures.begin(), mandatoryFeatures.end(), + [&](const std::string & feature) { return features.count(feature); }); } ref Machine::openStore() const @@ -75,7 +67,8 @@ ref Machine::openStore() const auto & fs = storeParams["system-features"]; auto append = [&](auto feats) { for (auto & f : feats) { - if (fs.size() > 0) fs += ' '; + if (fs.size() > 0) + fs += ' '; fs += f; } }; @@ -89,10 +82,12 @@ ref Machine::openStore() const static std::vector expandBuilderLines(const std::string & builders) { std::vector result; - for (auto line : tokenizeString>(builders, "\n;")) { + for (auto line : + tokenizeString>(builders, "\n;")) { trim(line); line.erase(std::find(line.begin(), line.end(), '#'), line.end()); - if (line.empty()) continue; + if (line.empty()) + continue; if (line[0] == '@') { const std::string path = trim(std::string(line, 1)); @@ -120,13 +115,16 @@ static Machine parseBuilderLine(const std::string & line) const auto tokens = tokenizeString>(line); auto isSet = [&](size_t fieldIndex) { - return tokens.size() > fieldIndex && tokens[fieldIndex] != "" && tokens[fieldIndex] != "-"; + return tokens.size() > fieldIndex && tokens[fieldIndex] != "" && + tokens[fieldIndex] != "-"; }; auto parseUnsignedIntField = [&](size_t fieldIndex) { const auto result = string2Int(tokens[fieldIndex]); if (!result) { - throw FormatError("bad machine specification: failed to convert column #%lu in a row: '%s' to 'unsigned int'", fieldIndex, line); + throw FormatError("bad machine specification: failed to convert " + "column #%lu in a row: '%s' to 'unsigned int'", + fieldIndex, line); } return result.value(); }; @@ -136,30 +134,36 @@ static Machine parseBuilderLine(const std::string & line) try { base64Decode(str); } catch (const Error & e) { - throw FormatError("bad machine specification: a column #%lu in a row: '%s' is not valid base64 string: %s", fieldIndex, line, e.what()); + throw FormatError("bad machine specification: a column #%lu in a " + "row: '%s' is not valid base64 string: %s", + fieldIndex, line, e.what()); } return str; }; if (!isSet(0)) - throw FormatError("bad machine specification: store URL was not found at the first column of a row: '%s'", line); - - return { - tokens[0], - isSet(1) ? tokenizeString>(tokens[1], ",") : std::vector{settings.thisSystem}, - isSet(2) ? tokens[2] : "", - isSet(3) ? parseUnsignedIntField(3) : 1U, - isSet(4) ? parseUnsignedIntField(4) : 1U, - isSet(5) ? tokenizeString>(tokens[5], ",") : std::set{}, - isSet(6) ? tokenizeString>(tokens[6], ",") : std::set{}, - isSet(7) ? ensureBase64(7) : "" - }; + throw FormatError("bad machine specification: store URL was not found " + "at the first column of a row: '%s'", + line); + + return {tokens[0], + isSet(1) ? tokenizeString>(tokens[1], ",") + : std::vector{settings.thisSystem}, + isSet(2) ? tokens[2] : "", + isSet(3) ? parseUnsignedIntField(3) : 1U, + isSet(4) ? parseUnsignedIntField(4) : 1U, + isSet(5) ? tokenizeString>(tokens[5], ",") + : std::set{}, + isSet(6) ? tokenizeString>(tokens[6], ",") + : std::set{}, + isSet(7) ? ensureBase64(7) : ""}; } static Machines parseBuilderLines(const std::vector & builders) { Machines result; - std::transform(builders.begin(), builders.end(), std::back_inserter(result), parseBuilderLine); + std::transform(builders.begin(), builders.end(), std::back_inserter(result), + parseBuilderLine); return result; } diff --git a/src/libstore/machines.hh b/src/libstore/machines.hh index 834626de9cf0..a2aa1ceb0c8a 100644 --- a/src/libstore/machines.hh +++ b/src/libstore/machines.hh @@ -22,14 +22,12 @@ struct Machine { bool mandatoryMet(const std::set & features) const; - Machine(decltype(storeUri) storeUri, - decltype(systemTypes) systemTypes, - decltype(sshKey) sshKey, - decltype(maxJobs) maxJobs, - decltype(speedFactor) speedFactor, - decltype(supportedFeatures) supportedFeatures, - decltype(mandatoryFeatures) mandatoryFeatures, - decltype(sshPublicHostKey) sshPublicHostKey); + Machine(decltype(storeUri) storeUri, decltype(systemTypes) systemTypes, + decltype(sshKey) sshKey, decltype(maxJobs) maxJobs, + decltype(speedFactor) speedFactor, + decltype(supportedFeatures) supportedFeatures, + decltype(mandatoryFeatures) mandatoryFeatures, + decltype(sshPublicHostKey) sshPublicHostKey); ref openStore() const; }; diff --git a/src/libstore/make-content-addressed.cc b/src/libstore/make-content-addressed.cc index 64d172918a54..d615e5800e33 100644 --- a/src/libstore/make-content-addressed.cc +++ b/src/libstore/make-content-addressed.cc @@ -3,10 +3,9 @@ namespace nix { -std::map makeContentAddressed( - Store & srcStore, - Store & dstStore, - const StorePathSet & storePaths) +std::map +makeContentAddressed(Store & srcStore, Store & dstStore, + const StorePathSet & storePaths) { StorePathSet closure; srcStore.computeFSClosure(storePaths, closure); @@ -37,7 +36,9 @@ std::map makeContentAddressed( auto replacement = i != remappings.end() ? i->second : ref; // FIXME: warn about unremapped paths? if (replacement != ref) - rewrites.insert_or_assign(srcStore.printStorePath(ref), srcStore.printStorePath(replacement)); + rewrites.insert_or_assign( + srcStore.printStorePath(ref), + srcStore.printStorePath(replacement)); references.insert(std::move(replacement)); } } @@ -50,20 +51,24 @@ std::map makeContentAddressed( auto narModuloHash = hashModuloSink.finish().first; auto dstPath = dstStore.makeFixedOutputPath( - FileIngestionMethod::Recursive, narModuloHash, path.name(), references, hasSelfReference); + FileIngestionMethod::Recursive, narModuloHash, path.name(), + references, hasSelfReference); - printInfo("rewriting '%s' to '%s'", pathS, srcStore.printStorePath(dstPath)); + printInfo("rewriting '%s' to '%s'", pathS, + srcStore.printStorePath(dstPath)); StringSink sink2; - RewritingSink rsink2(oldHashPart, std::string(dstPath.hashPart()), sink2); + RewritingSink rsink2(oldHashPart, std::string(dstPath.hashPart()), + sink2); rsink2(sink.s); rsink2.flush(); - ValidPathInfo info { dstPath, hashString(htSHA256, sink2.s) }; + ValidPathInfo info{dstPath, hashString(htSHA256, sink2.s)}; info.references = std::move(references); - if (hasSelfReference) info.references.insert(info.path); + if (hasSelfReference) + info.references.insert(info.path); info.narSize = sink.s.size(); - info.ca = FixedOutputHash { + info.ca = FixedOutputHash{ .method = FileIngestionMethod::Recursive, .hash = narModuloHash, }; diff --git a/src/libstore/make-content-addressed.hh b/src/libstore/make-content-addressed.hh index c4a66ed414cb..7c3abdd8fe79 100644 --- a/src/libstore/make-content-addressed.hh +++ b/src/libstore/make-content-addressed.hh @@ -4,9 +4,8 @@ namespace nix { -std::map makeContentAddressed( - Store & srcStore, - Store & dstStore, - const StorePathSet & storePaths); +std::map +makeContentAddressed(Store & srcStore, Store & dstStore, + const StorePathSet & storePaths); } diff --git a/src/libstore/misc.cc b/src/libstore/misc.cc index fb985c97bc4d..fdc2c77be6eb 100644 --- a/src/libstore/misc.cc +++ b/src/libstore/misc.cc @@ -12,40 +12,45 @@ namespace nix { void Store::computeFSClosure(const StorePathSet & startPaths, - StorePathSet & paths_, bool flipDirection, bool includeOutputs, bool includeDerivers) + StorePathSet & paths_, bool flipDirection, + bool includeOutputs, bool includeDerivers) { - std::function(const StorePath & path, std::future> &)> queryDeps; + std::function(const StorePath & path, + std::future> &)> + queryDeps; if (flipDirection) - queryDeps = [&](const StorePath& path, + queryDeps = [&](const StorePath & path, std::future> & fut) { StorePathSet res; StorePathSet referrers; queryReferrers(path, referrers); - for (auto& ref : referrers) + for (auto & ref : referrers) if (ref != path) res.insert(ref); if (includeOutputs) - for (auto& i : queryValidDerivers(path)) + for (auto & i : queryValidDerivers(path)) res.insert(i); if (includeDerivers && path.isDerivation()) - for (auto& [_, maybeOutPath] : queryPartialDerivationOutputMap(path)) + for (auto & [_, maybeOutPath] : + queryPartialDerivationOutputMap(path)) if (maybeOutPath && isValidPath(*maybeOutPath)) res.insert(*maybeOutPath); return res; }; else - queryDeps = [&](const StorePath& path, + queryDeps = [&](const StorePath & path, std::future> & fut) { StorePathSet res; auto info = fut.get(); - for (auto& ref : info->references) + for (auto & ref : info->references) if (ref != path) res.insert(ref); if (includeOutputs && path.isDerivation()) - for (auto& [_, maybeOutPath] : queryPartialDerivationOutputMap(path)) + for (auto & [_, maybeOutPath] : + queryPartialDerivationOutputMap(path)) if (maybeOutPath && isValidPath(*maybeOutPath)) res.insert(*maybeOutPath); @@ -56,8 +61,8 @@ void Store::computeFSClosure(const StorePathSet & startPaths, computeClosure( startPaths, paths_, - [&](const StorePath& path, - std::function>&)> + [&](const StorePath & path, + std::function> &)> processEdges) { std::promise> promise; std::function>)> @@ -74,53 +79,57 @@ void Store::computeFSClosure(const StorePathSet & startPaths, }); } -void Store::computeFSClosure(const StorePath & startPath, - StorePathSet & paths_, bool flipDirection, bool includeOutputs, bool includeDerivers) +void Store::computeFSClosure(const StorePath & startPath, StorePathSet & paths_, + bool flipDirection, bool includeOutputs, + bool includeDerivers) { StorePathSet paths; paths.insert(startPath); - computeFSClosure(paths, paths_, flipDirection, includeOutputs, includeDerivers); + computeFSClosure(paths, paths_, flipDirection, includeOutputs, + includeDerivers); } - std::optional getDerivationCA(const BasicDerivation & drv) { auto out = drv.outputs.find("out"); if (out != drv.outputs.end()) { - if (const auto * v = std::get_if(&out->second.raw())) + if (const auto * v = + std::get_if(&out->second.raw())) return v->hash; } return std::nullopt; } void Store::queryMissing(const std::vector & targets, - StorePathSet & willBuild_, StorePathSet & willSubstitute_, StorePathSet & unknown_, - uint64_t & downloadSize_, uint64_t & narSize_) + StorePathSet & willBuild_, + StorePathSet & willSubstitute_, + StorePathSet & unknown_, 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"); downloadSize_ = narSize_ = 0; // FIXME: make async. ThreadPool pool(fileTransferSettings.httpConnections); - struct State - { + struct State { std::unordered_set done; - StorePathSet & unknown, & willSubstitute, & willBuild; + StorePathSet &unknown, &willSubstitute, &willBuild; uint64_t & downloadSize; uint64_t & narSize; }; - struct DrvState - { + struct DrvState { size_t left; bool done = false; StorePathSet outPaths; - DrvState(size_t left) : left(left) { } + DrvState(size_t left) : left(left) {} }; - Sync state_(State{{}, unknown_, willSubstitute_, willBuild_, downloadSize_, narSize_}); + Sync state_(State{ + {}, unknown_, willSubstitute_, willBuild_, downloadSize_, narSize_}); std::function doPath; @@ -131,13 +140,15 @@ void Store::queryMissing(const std::vector & targets, } for (auto & i : drv.inputDrvs) - pool.enqueue(std::bind(doPath, DerivedPath::Built { i.first, i.second })); + pool.enqueue( + std::bind(doPath, DerivedPath::Built{i.first, i.second})); }; - auto checkOutput = [&]( - const StorePath & drvPath, ref drv, const StorePath & outPath, ref> drvState_) - { - if (drvState_->lock()->done) return; + auto checkOutput = [&](const StorePath & drvPath, ref drv, + const StorePath & outPath, + ref> drvState_) { + if (drvState_->lock()->done) + return; SubstitutablePathInfos infos; querySubstitutablePathInfos({{outPath, getDerivationCA(*drv)}}, infos); @@ -148,86 +159,98 @@ void Store::queryMissing(const std::vector & targets, } else { { auto drvState(drvState_->lock()); - if (drvState->done) return; + if (drvState->done) + return; assert(drvState->left); drvState->left--; drvState->outPaths.insert(outPath); if (!drvState->left) { for (auto & path : drvState->outPaths) - pool.enqueue(std::bind(doPath, DerivedPath::Opaque { path } )); + pool.enqueue( + std::bind(doPath, DerivedPath::Opaque{path})); } } } }; doPath = [&](const DerivedPath & req) { - { auto state(state_.lock()); - if (!state->done.insert(req.to_string(*this)).second) return; - } - - std::visit(overloaded { - [&](const DerivedPath::Built & bfd) { - if (!isValidPath(bfd.drvPath)) { - // FIXME: we could try to substitute the derivation. - auto state(state_.lock()); - state->unknown.insert(bfd.drvPath); + if (!state->done.insert(req.to_string(*this)).second) return; - } - - StorePathSet invalid; - /* true for regular derivations, and CA derivations for which we - have a trust mapping for all wanted outputs. */ - auto knownOutputPaths = true; - for (auto & [outputName, pathOpt] : queryPartialDerivationOutputMap(bfd.drvPath)) { - if (!pathOpt) { - knownOutputPaths = false; - break; - } - if (wantOutput(outputName, bfd.outputs) && !isValidPath(*pathOpt)) - invalid.insert(*pathOpt); - } - if (knownOutputPaths && invalid.empty()) return; - - auto drv = make_ref(derivationFromPath(bfd.drvPath)); - ParsedDerivation parsedDrv(StorePath(bfd.drvPath), *drv); - - if (knownOutputPaths && settings.useSubstitutes && parsedDrv.substitutesAllowed()) { - auto drvState = make_ref>(DrvState(invalid.size())); - for (auto & output : invalid) - pool.enqueue(std::bind(checkOutput, bfd.drvPath, drv, output, drvState)); - } else - mustBuildDrv(bfd.drvPath, *drv); - - }, - [&](const DerivedPath::Opaque & bo) { - - if (isValidPath(bo.path)) return; - - SubstitutablePathInfos infos; - querySubstitutablePathInfos({{bo.path, std::nullopt}}, infos); - - if (infos.empty()) { - auto state(state_.lock()); - state->unknown.insert(bo.path); - return; - } - - auto info = infos.find(bo.path); - assert(info != infos.end()); - - { - auto state(state_.lock()); - state->willSubstitute.insert(bo.path); - state->downloadSize += info->second.downloadSize; - state->narSize += info->second.narSize; - } + } - for (auto & ref : info->second.references) - pool.enqueue(std::bind(doPath, DerivedPath::Opaque { ref })); - }, - }, req.raw()); + std::visit( + overloaded{ + [&](const DerivedPath::Built & bfd) { + if (!isValidPath(bfd.drvPath)) { + // FIXME: we could try to substitute the derivation. + auto state(state_.lock()); + state->unknown.insert(bfd.drvPath); + return; + } + + StorePathSet invalid; + /* true for regular derivations, and CA derivations for + which we have a trust mapping for all wanted outputs. */ + auto knownOutputPaths = true; + for (auto & [outputName, pathOpt] : + queryPartialDerivationOutputMap(bfd.drvPath)) { + if (!pathOpt) { + knownOutputPaths = false; + break; + } + if (wantOutput(outputName, bfd.outputs) && + !isValidPath(*pathOpt)) + invalid.insert(*pathOpt); + } + if (knownOutputPaths && invalid.empty()) + return; + + auto drv = + make_ref(derivationFromPath(bfd.drvPath)); + ParsedDerivation parsedDrv(StorePath(bfd.drvPath), *drv); + + if (knownOutputPaths && settings.useSubstitutes && + parsedDrv.substitutesAllowed()) { + auto drvState = + make_ref>(DrvState(invalid.size())); + for (auto & output : invalid) + pool.enqueue(std::bind(checkOutput, bfd.drvPath, + drv, output, drvState)); + } else + mustBuildDrv(bfd.drvPath, *drv); + }, + [&](const DerivedPath::Opaque & bo) { + if (isValidPath(bo.path)) + return; + + SubstitutablePathInfos infos; + querySubstitutablePathInfos({{bo.path, std::nullopt}}, + infos); + + if (infos.empty()) { + auto state(state_.lock()); + state->unknown.insert(bo.path); + return; + } + + auto info = infos.find(bo.path); + assert(info != infos.end()); + + { + auto state(state_.lock()); + state->willSubstitute.insert(bo.path); + state->downloadSize += info->second.downloadSize; + state->narSize += info->second.narSize; + } + + for (auto & ref : info->second.references) + pool.enqueue( + std::bind(doPath, DerivedPath::Opaque{ref})); + }, + }, + req.raw()); }; for (auto & path : targets) @@ -236,11 +259,10 @@ void Store::queryMissing(const std::vector & targets, pool.process(); } - StorePaths Store::topoSortPaths(const StorePathSet & paths) { - return topoSort(paths, - {[&](const StorePath & path) { + return topoSort( + paths, {[&](const StorePath & path) { try { return queryPathInfo(path)->references; } catch (InvalidPath &) { @@ -250,14 +272,13 @@ StorePaths Store::topoSortPaths(const StorePathSet & paths) {[&](const StorePath & path, const StorePath & parent) { return BuildError( "cycle detected in the references of '%s' from '%s'", - printStorePath(path), - printStorePath(parent)); + printStorePath(path), printStorePath(parent)); }}); } -std::map drvOutputReferences( - const std::set & inputRealisations, - const StorePathSet & pathReferences) +std::map +drvOutputReferences(const std::set & inputRealisations, + const StorePathSet & pathReferences) { std::map res; @@ -270,10 +291,9 @@ std::map drvOutputReferences( return res; } -std::map drvOutputReferences( - Store & store, - const Derivation & drv, - const StorePath & outputPath) +std::map drvOutputReferences(Store & store, + const Derivation & drv, + const StorePath & outputPath) { std::set inputRealisations; @@ -283,22 +303,21 @@ std::map drvOutputReferences( for (const auto & outputName : outputNames) { auto outputHash = get(outputHashes, outputName); if (!outputHash) - throw Error( - "output '%s' of derivation '%s' isn't realised", outputName, - store.printStorePath(inputDrv)); - auto thisRealisation = store.queryRealisation( - DrvOutput{*outputHash, outputName}); + throw Error("output '%s' of derivation '%s' isn't realised", + outputName, store.printStorePath(inputDrv)); + auto thisRealisation = + store.queryRealisation(DrvOutput{*outputHash, outputName}); if (!thisRealisation) - throw Error( - "output '%s' of derivation '%s' isn't built", outputName, - store.printStorePath(inputDrv)); + throw Error("output '%s' of derivation '%s' isn't built", + outputName, store.printStorePath(inputDrv)); inputRealisations.insert(*thisRealisation); } } auto info = store.queryPathInfo(outputPath); - return drvOutputReferences(Realisation::closure(store, inputRealisations), info->references); + return drvOutputReferences(Realisation::closure(store, inputRealisations), + info->references); } } diff --git a/src/libstore/names.cc b/src/libstore/names.cc index 277aabf0f930..c8d3eef8b4a5 100644 --- a/src/libstore/names.cc +++ b/src/libstore/names.cc @@ -3,21 +3,13 @@ #include - namespace nix { - -struct Regex -{ +struct Regex { std::regex regex; }; - -DrvName::DrvName() -{ - name = ""; -} - +DrvName::DrvName() { name = ""; } /* Parse a derivation name. The `name' part of a derivation name is everything up to but not including the first dash *not* followed by @@ -37,10 +29,7 @@ DrvName::DrvName(std::string_view s) : hits(0) } } - -DrvName::~DrvName() -{ } - +DrvName::~DrvName() {} bool DrvName::matches(const DrvName & n) { @@ -49,27 +38,31 @@ bool DrvName::matches(const DrvName & n) regex = std::make_unique(); regex->regex = std::regex(name, std::regex::extended); } - if (!std::regex_match(n.name, regex->regex)) return false; + if (!std::regex_match(n.name, regex->regex)) + return false; } - if (version != "" && version != n.version) return false; + if (version != "" && version != n.version) + return false; return true; } - std::string_view nextComponent(std::string_view::const_iterator & p, - const std::string_view::const_iterator end) + const std::string_view::const_iterator end) { /* Skip any dots and dashes (component separators). */ - while (p != end && (*p == '.' || *p == '-')) ++p; + while (p != end && (*p == '.' || *p == '-')) + ++p; - if (p == end) return ""; + if (p == end) + return ""; /* If the first character is a digit, consume the longest sequence of digits. Otherwise, consume the longest sequence of non-digit, non-separator characters. */ auto s = p; if (isdigit(*p)) - while (p != end && isdigit(*p)) p++; + while (p != end && isdigit(*p)) + p++; else while (p != end && (!isdigit(*p) && *p != '.' && *p != '-')) p++; @@ -77,23 +70,28 @@ std::string_view nextComponent(std::string_view::const_iterator & p, return {s, size_t(p - s)}; } - static bool componentsLT(const std::string_view c1, const std::string_view c2) { auto n1 = string2Int(c1); auto n2 = string2Int(c2); - if (n1 && n2) return *n1 < *n2; - else if (c1 == "" && n2) return true; - else if (c1 == "pre" && c2 != "pre") return true; - else if (c2 == "pre") return false; + if (n1 && n2) + return *n1 < *n2; + else if (c1 == "" && n2) + return true; + else if (c1 == "pre" && c2 != "pre") + return true; + else if (c2 == "pre") + return false; /* Assume that `2.3a' < `2.3.1'. */ - else if (n2) return true; - else if (n1) return false; - else return c1 < c2; + else if (n2) + return true; + else if (n1) + return false; + else + return c1 < c2; } - int compareVersions(const std::string_view v1, const std::string_view v2) { auto p1 = v1.begin(); @@ -102,14 +100,15 @@ int compareVersions(const std::string_view v1, const std::string_view v2) while (p1 != v1.end() || p2 != v2.end()) { auto c1 = nextComponent(p1, v1.end()); auto c2 = nextComponent(p2, v2.end()); - if (componentsLT(c1, c2)) return -1; - else if (componentsLT(c2, c1)) return 1; + if (componentsLT(c1, c2)) + return -1; + else if (componentsLT(c2, c1)) + return 1; } return 0; } - DrvNames drvNamesFromArgs(const Strings & opArgs) { DrvNames result; @@ -118,5 +117,4 @@ DrvNames drvNamesFromArgs(const Strings & opArgs) return result; } - } diff --git a/src/libstore/names.hh b/src/libstore/names.hh index 3977fc6cc9bb..416ec7bae1a1 100644 --- a/src/libstore/names.hh +++ b/src/libstore/names.hh @@ -8,8 +8,7 @@ namespace nix { struct Regex; -struct DrvName -{ +struct DrvName { std::string fullName; std::string name; std::string version; @@ -21,14 +20,14 @@ struct DrvName bool matches(const DrvName & n); -private: + private: std::unique_ptr regex; }; typedef std::list DrvNames; std::string_view nextComponent(std::string_view::const_iterator & p, - const std::string_view::const_iterator end); + const std::string_view::const_iterator end); int compareVersions(const std::string_view v1, const std::string_view v2); DrvNames drvNamesFromArgs(const Strings & opArgs); diff --git a/src/libstore/nar-accessor.cc b/src/libstore/nar-accessor.cc index 72d41cc943fe..dec97e919da3 100644 --- a/src/libstore/nar-accessor.cc +++ b/src/libstore/nar-accessor.cc @@ -10,8 +10,7 @@ namespace nix { -struct NarMember -{ +struct NarMember { FSAccessor::Type type = FSAccessor::Type::tMissing; bool isExecutable = false; @@ -26,16 +25,14 @@ struct NarMember std::map children; }; -struct NarAccessor : public FSAccessor -{ +struct NarAccessor : public FSAccessor { std::optional nar; GetNarBytes getNarBytes; NarMember root; - struct NarIndexer : ParseSink, Source - { + struct NarIndexer : ParseSink, Source { NarAccessor & acc; Source & source; @@ -47,20 +44,24 @@ struct NarAccessor : public FSAccessor NarIndexer(NarAccessor & acc, Source & source) : acc(acc), source(source) - { } + { + } void createMember(const Path & path, NarMember member) { size_t level = std::count(path.begin(), path.end(), '/'); - while (parents.size() > level) parents.pop(); + while (parents.size() > level) + parents.pop(); if (parents.empty()) { acc.root = std::move(member); parents.push(&acc.root); } else { if (parents.top()->type != FSAccessor::Type::tDirectory) - throw Error("NAR file missing parent directory of path '%s'", path); - auto result = parents.top()->children.emplace(baseNameOf(path), std::move(member)); + throw Error( + "NAR file missing parent directory of path '%s'", path); + auto result = parents.top()->children.emplace( + baseNameOf(path), std::move(member)); parents.push(&result.first->second); } } @@ -75,10 +76,7 @@ struct NarAccessor : public FSAccessor createMember(path, {FSAccessor::Type::tRegular, false, 0, 0}); } - void isExecutable() override - { - parents.top()->isExecutable = true; - } + void isExecutable() override { parents.top()->isExecutable = true; } void preallocateContents(uint64_t size) override { @@ -87,13 +85,13 @@ struct NarAccessor : public FSAccessor parents.top()->start = pos; } - void receiveContents(std::string_view data) override - { } + void receiveContents(std::string_view data) override {} - void createSymlink(const Path & path, const std::string & target) override + void createSymlink(const Path & path, + const std::string & target) override { - createMember(path, - NarMember{FSAccessor::Type::tSymlink, false, 0, 0, target}); + createMember(path, NarMember{FSAccessor::Type::tSymlink, false, 0, + 0, target}); } size_t read(char * data, size_t len) override @@ -129,7 +127,8 @@ struct NarAccessor : public FSAccessor if (type == "directory") { member.type = FSAccessor::Type::tDirectory; - for (auto i = v["entries"].begin(); i != v["entries"].end(); ++i) { + for (auto i = v["entries"].begin(); i != v["entries"].end(); + ++i) { std::string name = i.key(); recurse(member.children[name], i.value()); } @@ -141,7 +140,8 @@ struct NarAccessor : public FSAccessor } else if (type == "symlink") { member.type = FSAccessor::Type::tSymlink; member.target = v.value("target", ""); - } else return; + } else + return; }; json v = json::parse(listing); @@ -153,10 +153,11 @@ struct NarAccessor : public FSAccessor Path canon = path == "" ? "" : canonPath(path); NarMember * current = &root; auto end = path.end(); - for (auto it = path.begin(); it != end; ) { - // because it != end, the remaining component is non-empty so we need - // a directory - if (current->type != FSAccessor::Type::tDirectory) return nullptr; + for (auto it = path.begin(); it != end;) { + // because it != end, the remaining component is non-empty so we + // need a directory + if (current->type != FSAccessor::Type::tDirectory) + return nullptr; // skip slash (canonPath above ensures that this is always a slash) assert(*it == '/'); @@ -165,7 +166,8 @@ struct NarAccessor : public FSAccessor // lookup current component auto next = std::find(it, end, '/'); auto child = current->children.find(std::string(it, next)); - if (child == current->children.end()) return nullptr; + if (child == current->children.end()) + return nullptr; current = &child->second; it = next; @@ -174,7 +176,8 @@ struct NarAccessor : public FSAccessor return current; } - NarMember & get(const Path & path) { + NarMember & get(const Path & path) + { auto result = find(path); if (result == nullptr) throw Error("NAR file does not contain path '%1%'", path); @@ -203,13 +206,16 @@ struct NarAccessor : public FSAccessor return res; } - std::string readFile(const Path & path, bool requireValidPath = true) override + std::string readFile(const Path & path, + bool requireValidPath = true) override { auto i = get(path); if (i.type != FSAccessor::Type::tRegular) - throw Error("path '%1%' inside NAR file is not a regular file", path); + throw Error("path '%1%' inside NAR file is not a regular file", + path); - if (getNarBytes) return getNarBytes(i.start, i.size); + if (getNarBytes) + return getNarBytes(i.start, i.size); assert(nar); return std::string(*nar, i.start, i.size); @@ -235,13 +241,13 @@ ref makeNarAccessor(Source & source) } ref makeLazyNarAccessor(const std::string & listing, - GetNarBytes getNarBytes) + GetNarBytes getNarBytes) { return make_ref(listing, getNarBytes); } -void listNar(JSONPlaceholder & res, ref accessor, - const Path & path, bool recurse) +void listNar(JSONPlaceholder & res, ref accessor, const Path & path, + bool recurse) { auto st = accessor->stat(path); diff --git a/src/libstore/nar-accessor.hh b/src/libstore/nar-accessor.hh index c2241a04c00f..b6669a75086f 100644 --- a/src/libstore/nar-accessor.hh +++ b/src/libstore/nar-accessor.hh @@ -20,15 +20,14 @@ ref makeNarAccessor(Source & source); inside the NAR. */ typedef std::function GetNarBytes; -ref makeLazyNarAccessor( - const std::string & listing, - GetNarBytes getNarBytes); +ref makeLazyNarAccessor(const std::string & listing, + GetNarBytes getNarBytes); class JSONPlaceholder; /* Write a JSON representation of the contents of a NAR (except file contents). */ -void listNar(JSONPlaceholder & res, ref accessor, - const Path & path, bool recurse); +void listNar(JSONPlaceholder & res, ref accessor, const Path & path, + bool recurse); } diff --git a/src/libstore/nar-info-disk-cache.cc b/src/libstore/nar-info-disk-cache.cc index 9dd81ddfb959..1ceeb8463b0b 100644 --- a/src/libstore/nar-info-disk-cache.cc +++ b/src/libstore/nar-info-disk-cache.cc @@ -55,23 +55,19 @@ create table if not exists LastPurge ( )sql"; -class NarInfoDiskCacheImpl : public NarInfoDiskCache -{ -public: - +class NarInfoDiskCacheImpl : public NarInfoDiskCache { + public: /* How often to purge expired entries from the cache. */ const int purgeInterval = 24 * 3600; - struct Cache - { + struct Cache { int id; Path storeDir; bool wantMassQuery; int priority; }; - struct State - { + struct State { SQLite db; SQLiteStmt insertCache, queryCache, insertNAR, insertMissingNAR, queryNAR, insertRealisation, insertMissingRealisation, @@ -94,36 +90,46 @@ class NarInfoDiskCacheImpl : public NarInfoDiskCache state->db.exec(schema); - state->insertCache.create(state->db, - "insert or replace into BinaryCaches(url, timestamp, storeDir, wantMassQuery, priority) values (?, ?, ?, ?, ?)"); + state->insertCache.create( + state->db, + "insert or replace into BinaryCaches(url, timestamp, storeDir, " + "wantMassQuery, priority) values (?, ?, ?, ?, ?)"); state->queryCache.create(state->db, - "select id, storeDir, wantMassQuery, priority from BinaryCaches where url = ?"); - - state->insertNAR.create(state->db, - "insert or replace into NARs(cache, hashPart, namePart, url, compression, fileHash, fileSize, narHash, " - "narSize, refs, deriver, sigs, ca, timestamp, present) values (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, 1)"); - - state->insertMissingNAR.create(state->db, - "insert or replace into NARs(cache, hashPart, timestamp, present) values (?, ?, ?, 0)"); - - state->queryNAR.create(state->db, - "select present, namePart, url, compression, fileHash, fileSize, narHash, narSize, refs, deriver, sigs, ca from NARs where cache = ? and hashPart = ? and ((present = 0 and timestamp > ?) or (present = 1 and timestamp > ?))"); + "select id, storeDir, wantMassQuery, priority " + "from BinaryCaches where url = ?"); + + state->insertNAR.create( + state->db, "insert or replace into NARs(cache, hashPart, namePart, " + "url, compression, fileHash, fileSize, narHash, " + "narSize, refs, deriver, sigs, ca, timestamp, present) " + "values (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, 1)"); + + state->insertMissingNAR.create( + state->db, "insert or replace into NARs(cache, hashPart, " + "timestamp, present) values (?, ?, ?, 0)"); + + state->queryNAR.create( + state->db, + "select present, namePart, url, compression, fileHash, fileSize, " + "narHash, narSize, refs, deriver, sigs, ca from NARs where cache = " + "? and hashPart = ? and ((present = 0 and timestamp > ?) or " + "(present = 1 and timestamp > ?))"); state->insertRealisation.create(state->db, - R"( + R"( insert or replace into Realisations(cache, outputId, content, timestamp) values (?, ?, ?, ?) )"); state->insertMissingRealisation.create(state->db, - R"( + R"( insert or replace into Realisations(cache, outputId, timestamp) values (?, ?, ?) )"); state->queryRealisation.create(state->db, - R"( + R"( select content from Realisations where cache = ? and outputId = ? and ((content is null and timestamp > ?) or @@ -137,21 +143,27 @@ class NarInfoDiskCacheImpl : public NarInfoDiskCache SQLiteStmt queryLastPurge(state->db, "select value from LastPurge"); auto queryLastPurge_(queryLastPurge.use()); - if (!queryLastPurge_.next() || queryLastPurge_.getInt(0) < now - purgeInterval) { + if (!queryLastPurge_.next() || + queryLastPurge_.getInt(0) < now - purgeInterval) { SQLiteStmt(state->db, - "delete from NARs where ((present = 0 and timestamp < ?) or (present = 1 and timestamp < ?))") + "delete from NARs where ((present = 0 and timestamp " + "< ?) or (present = 1 and timestamp < ?))") .use() // Use a minimum TTL to prevent --refresh from // nuking the entire disk cache. - (now - std::max(settings.ttlNegativeNarInfoCache.get(), 3600U)) - (now - std::max(settings.ttlPositiveNarInfoCache.get(), 30 * 24 * 3600U)) + (now - + std::max(settings.ttlNegativeNarInfoCache.get(), 3600U))( + now - std::max(settings.ttlPositiveNarInfoCache.get(), + 30 * 24 * 3600U)) .exec(); - debug("deleted %d entries from the NAR info disk cache", sqlite3_changes(state->db)); + debug("deleted %d entries from the NAR info disk cache", + sqlite3_changes(state->db)); - SQLiteStmt(state->db, - "insert or replace into LastPurge(dummy, value) values ('', ?)") - .use()(now).exec(); + SQLiteStmt(state->db, "insert or replace into LastPurge(dummy, " + "value) values ('', ?)") + .use()(now) + .exec(); } }); } @@ -159,126 +171,133 @@ class NarInfoDiskCacheImpl : public NarInfoDiskCache Cache & getCache(State & state, const std::string & uri) { auto i = state.caches.find(uri); - if (i == state.caches.end()) abort(); + if (i == state.caches.end()) + abort(); return i->second; } - void createCache(const std::string & uri, const Path & storeDir, bool wantMassQuery, int priority) override + void createCache(const std::string & uri, const Path & storeDir, + bool wantMassQuery, int priority) override { retrySQLite([&]() { auto state(_state.lock()); // FIXME: race - state->insertCache.use()(uri)(time(0))(storeDir)(wantMassQuery)(priority).exec(); + state->insertCache + .use()(uri)(time(0))(storeDir) (wantMassQuery) (priority) + .exec(); assert(sqlite3_changes(state->db) == 1); - state->caches[uri] = Cache{(int) sqlite3_last_insert_rowid(state->db), storeDir, wantMassQuery, priority}; + state->caches[uri] = + Cache{(int) sqlite3_last_insert_rowid(state->db), storeDir, + wantMassQuery, priority}; }); } std::optional cacheExists(const std::string & uri) override { - return retrySQLite>([&]() -> std::optional { - auto state(_state.lock()); - - auto i = state->caches.find(uri); - if (i == state->caches.end()) { - auto queryCache(state->queryCache.use()(uri)); - if (!queryCache.next()) - return std::nullopt; - state->caches.emplace(uri, - Cache{(int) queryCache.getInt(0), queryCache.getStr(1), queryCache.getInt(2) != 0, (int) queryCache.getInt(3)}); - } - - auto & cache(getCache(*state, uri)); - - return CacheInfo { - .wantMassQuery = cache.wantMassQuery, - .priority = cache.priority - }; - }); + return retrySQLite>( + [&]() -> std::optional { + auto state(_state.lock()); + + auto i = state->caches.find(uri); + if (i == state->caches.end()) { + auto queryCache(state->queryCache.use()(uri)); + if (!queryCache.next()) + return std::nullopt; + state->caches.emplace(uri, + Cache{(int) queryCache.getInt(0), + queryCache.getStr(1), + queryCache.getInt(2) != 0, + (int) queryCache.getInt(3)}); + } + + auto & cache(getCache(*state, uri)); + + return CacheInfo{.wantMassQuery = cache.wantMassQuery, + .priority = cache.priority}; + }); } - std::pair> lookupNarInfo( - const std::string & uri, const std::string & hashPart) override + std::pair> + lookupNarInfo(const std::string & uri, + const std::string & hashPart) override { return retrySQLite>>( [&]() -> std::pair> { - auto state(_state.lock()); - - auto & cache(getCache(*state, uri)); - - auto now = time(0); - - auto queryNAR(state->queryNAR.use() - (cache.id) - (hashPart) - (now - settings.ttlNegativeNarInfoCache) - (now - settings.ttlPositiveNarInfoCache)); - - if (!queryNAR.next()) - return {oUnknown, 0}; - - if (!queryNAR.getInt(0)) - return {oInvalid, 0}; - - auto namePart = queryNAR.getStr(1); - auto narInfo = make_ref( - StorePath(hashPart + "-" + namePart), - Hash::parseAnyPrefixed(queryNAR.getStr(6))); - narInfo->url = queryNAR.getStr(2); - narInfo->compression = queryNAR.getStr(3); - if (!queryNAR.isNull(4)) - narInfo->fileHash = Hash::parseAnyPrefixed(queryNAR.getStr(4)); - narInfo->fileSize = queryNAR.getInt(5); - narInfo->narSize = queryNAR.getInt(7); - for (auto & r : tokenizeString(queryNAR.getStr(8), " ")) - narInfo->references.insert(StorePath(r)); - if (!queryNAR.isNull(9)) - narInfo->deriver = StorePath(queryNAR.getStr(9)); - for (auto & sig : tokenizeString(queryNAR.getStr(10), " ")) - narInfo->sigs.insert(sig); - narInfo->ca = parseContentAddressOpt(queryNAR.getStr(11)); - - return {oValid, narInfo}; - }); + auto state(_state.lock()); + + auto & cache(getCache(*state, uri)); + + auto now = time(0); + + auto queryNAR(state->queryNAR.use()(cache.id)( + hashPart) (now - settings.ttlNegativeNarInfoCache)( + now - settings.ttlPositiveNarInfoCache)); + + if (!queryNAR.next()) + return {oUnknown, 0}; + + if (!queryNAR.getInt(0)) + return {oInvalid, 0}; + + auto namePart = queryNAR.getStr(1); + auto narInfo = make_ref( + StorePath(hashPart + "-" + namePart), + Hash::parseAnyPrefixed(queryNAR.getStr(6))); + narInfo->url = queryNAR.getStr(2); + narInfo->compression = queryNAR.getStr(3); + if (!queryNAR.isNull(4)) + narInfo->fileHash = + Hash::parseAnyPrefixed(queryNAR.getStr(4)); + narInfo->fileSize = queryNAR.getInt(5); + narInfo->narSize = queryNAR.getInt(7); + for (auto & r : + tokenizeString(queryNAR.getStr(8), " ")) + narInfo->references.insert(StorePath(r)); + if (!queryNAR.isNull(9)) + narInfo->deriver = StorePath(queryNAR.getStr(9)); + for (auto & sig : + tokenizeString(queryNAR.getStr(10), " ")) + narInfo->sigs.insert(sig); + narInfo->ca = parseContentAddressOpt(queryNAR.getStr(11)); + + return {oValid, narInfo}; + }); } - std::pair> lookupRealisation( - const std::string & uri, const DrvOutput & id) override + std::pair> + lookupRealisation(const std::string & uri, const DrvOutput & id) override { return retrySQLite>>( [&]() -> std::pair> { - auto state(_state.lock()); + auto state(_state.lock()); - auto & cache(getCache(*state, uri)); + auto & cache(getCache(*state, uri)); - auto now = time(0); + auto now = time(0); - auto queryRealisation(state->queryRealisation.use() - (cache.id) - (id.to_string()) - (now - settings.ttlNegativeNarInfoCache) - (now - settings.ttlPositiveNarInfoCache)); + auto queryRealisation(state->queryRealisation.use()(cache.id)( + id.to_string())(now - settings.ttlNegativeNarInfoCache)( + now - settings.ttlPositiveNarInfoCache)); - if (!queryRealisation.next()) - return {oUnknown, 0}; + if (!queryRealisation.next()) + return {oUnknown, 0}; - if (queryRealisation.isNull(0)) - return {oInvalid, 0}; + if (queryRealisation.isNull(0)) + return {oInvalid, 0}; - auto realisation = - std::make_shared(Realisation::fromJSON( - nlohmann::json::parse(queryRealisation.getStr(0)), - "Local disk cache")); + auto realisation = + std::make_shared(Realisation::fromJSON( + nlohmann::json::parse(queryRealisation.getStr(0)), + "Local disk cache")); - return {oValid, realisation}; - }); + return {oValid, realisation}; + }); } - void upsertNarInfo( - const std::string & uri, const std::string & hashPart, - std::shared_ptr info) override + void upsertNarInfo(const std::string & uri, const std::string & hashPart, + std::shared_ptr info) override { retrySQLite([&]() { auto state(_state.lock()); @@ -289,63 +308,59 @@ class NarInfoDiskCacheImpl : public NarInfoDiskCache auto narInfo = std::dynamic_pointer_cast(info); - //assert(hashPart == storePathToHash(info->path)); - - state->insertNAR.use() - (cache.id) - (hashPart) - (std::string(info->path.name())) - (narInfo ? narInfo->url : "", narInfo != 0) - (narInfo ? narInfo->compression : "", narInfo != 0) - (narInfo && narInfo->fileHash ? narInfo->fileHash->to_string(Base32, true) : "", narInfo && narInfo->fileHash) - (narInfo ? narInfo->fileSize : 0, narInfo != 0 && narInfo->fileSize) - (info->narHash.to_string(Base32, true)) - (info->narSize) - (concatStringsSep(" ", info->shortRefs())) - (info->deriver ? std::string(info->deriver->to_string()) : "", (bool) info->deriver) - (concatStringsSep(" ", info->sigs)) - (renderContentAddress(info->ca)) - (time(0)).exec(); + // assert(hashPart == storePathToHash(info->path)); + + state->insertNAR + .use()(cache.id)(hashPart) (std::string(info->path.name()))( + narInfo ? narInfo->url : "", narInfo != 0)( + narInfo ? narInfo->compression : "", narInfo != 0)( + narInfo && narInfo->fileHash + ? narInfo->fileHash->to_string(Base32, true) + : "", + narInfo && narInfo->fileHash)( + narInfo ? narInfo->fileSize : 0, + narInfo != 0 && narInfo->fileSize)( + info->narHash.to_string(Base32, true))(info->narSize)( + concatStringsSep(" ", info->shortRefs()))( + info->deriver ? std::string(info->deriver->to_string()) + : "", + (bool) info->deriver)( + concatStringsSep(" ", info->sigs))( + renderContentAddress(info->ca))(time(0)) + .exec(); } else { - state->insertMissingNAR.use() - (cache.id) - (hashPart) - (time(0)).exec(); + state->insertMissingNAR.use()(cache.id)(hashPart) (time(0)) + .exec(); } }); } - void upsertRealisation( - const std::string & uri, - const Realisation & realisation) override + void upsertRealisation(const std::string & uri, + const Realisation & realisation) override { retrySQLite([&]() { auto state(_state.lock()); auto & cache(getCache(*state, uri)); - state->insertRealisation.use() - (cache.id) - (realisation.id.to_string()) - (realisation.toJSON().dump()) - (time(0)).exec(); + state->insertRealisation + .use()(cache.id)(realisation.id.to_string())( + realisation.toJSON().dump())(time(0)) + .exec(); }); - } - virtual void upsertAbsentRealisation( - const std::string & uri, - const DrvOutput & id) override + virtual void upsertAbsentRealisation(const std::string & uri, + const DrvOutput & id) override { retrySQLite([&]() { auto state(_state.lock()); auto & cache(getCache(*state, uri)); - state->insertMissingRealisation.use() - (cache.id) - (id.to_string()) - (time(0)).exec(); + state->insertMissingRealisation + .use()(cache.id)(id.to_string())(time(0)) + .exec(); }); } }; diff --git a/src/libstore/nar-info-disk-cache.hh b/src/libstore/nar-info-disk-cache.hh index 2dcaa76a4903..4ac7dc751e28 100644 --- a/src/libstore/nar-info-disk-cache.hh +++ b/src/libstore/nar-info-disk-cache.hh @@ -6,39 +6,35 @@ namespace nix { -class NarInfoDiskCache -{ -public: +class NarInfoDiskCache { + public: typedef enum { oValid, oInvalid, oUnknown } Outcome; - virtual ~NarInfoDiskCache() { } + virtual ~NarInfoDiskCache() {} virtual void createCache(const std::string & uri, const Path & storeDir, - bool wantMassQuery, int priority) = 0; + bool wantMassQuery, int priority) = 0; - struct CacheInfo - { + struct CacheInfo { bool wantMassQuery; int priority; }; virtual std::optional cacheExists(const std::string & uri) = 0; - virtual std::pair> lookupNarInfo( - const std::string & uri, const std::string & hashPart) = 0; - - virtual void upsertNarInfo( - const std::string & uri, const std::string & hashPart, - std::shared_ptr info) = 0; - - virtual void upsertRealisation( - const std::string & uri, - const Realisation & realisation) = 0; - virtual void upsertAbsentRealisation( - const std::string & uri, - const DrvOutput & id) = 0; - virtual std::pair> lookupRealisation( - const std::string & uri, const DrvOutput & id) = 0; + virtual std::pair> + lookupNarInfo(const std::string & uri, const std::string & hashPart) = 0; + + virtual void upsertNarInfo(const std::string & uri, + const std::string & hashPart, + std::shared_ptr info) = 0; + + virtual void upsertRealisation(const std::string & uri, + const Realisation & realisation) = 0; + virtual void upsertAbsentRealisation(const std::string & uri, + const DrvOutput & id) = 0; + virtual std::pair> + lookupRealisation(const std::string & uri, const DrvOutput & id) = 0; }; /* Return a singleton cache object that can be used concurrently by diff --git a/src/libstore/nar-info.cc b/src/libstore/nar-info.cc index 071d8355e5c0..4649506b5707 100644 --- a/src/libstore/nar-info.cc +++ b/src/libstore/nar-info.cc @@ -4,8 +4,10 @@ namespace nix { -NarInfo::NarInfo(const Store & store, const std::string & s, const std::string & whence) - : ValidPathInfo(StorePath(StorePath::dummy), Hash(Hash::dummy)) // FIXME: hack +NarInfo::NarInfo(const Store & store, const std::string & s, + const std::string & whence) + : ValidPathInfo(StorePath(StorePath::dummy), + Hash(Hash::dummy)) // FIXME: hack { auto corrupt = [&]() { return Error("NAR info file '%1%' is corrupt", whence); @@ -26,20 +28,21 @@ NarInfo::NarInfo(const Store & store, const std::string & s, const std::string & while (pos < s.size()) { size_t colon = s.find(':', pos); - if (colon == std::string::npos) throw corrupt(); + if (colon == std::string::npos) + throw corrupt(); std::string name(s, pos, colon - pos); size_t eol = s.find('\n', colon + 2); - if (eol == std::string::npos) throw corrupt(); + if (eol == std::string::npos) + throw corrupt(); std::string value(s, colon + 2, eol - colon - 2); if (name == "StorePath") { path = store.parseStorePath(value); havePath = true; - } - else if (name == "URL") + } else if (name == "URL") url = value; else if (name == "Compression") compression = value; @@ -47,32 +50,31 @@ NarInfo::NarInfo(const Store & store, const std::string & s, const std::string & fileHash = parseHashField(value); else if (name == "FileSize") { auto n = string2Int(value); - if (!n) throw corrupt(); + if (!n) + throw corrupt(); fileSize = *n; - } - else if (name == "NarHash") { + } else if (name == "NarHash") { narHash = parseHashField(value); haveNarHash = true; - } - else if (name == "NarSize") { + } else if (name == "NarSize") { auto n = string2Int(value); - if (!n) throw corrupt(); + if (!n) + throw corrupt(); narSize = *n; - } - else if (name == "References") { + } else if (name == "References") { auto refs = tokenizeString(value, " "); - if (!references.empty()) throw corrupt(); + if (!references.empty()) + throw corrupt(); for (auto & r : refs) references.insert(StorePath(r)); - } - else if (name == "Deriver") { + } else if (name == "Deriver") { if (value != "unknown-deriver") deriver = StorePath(value); - } - else if (name == "Sig") + } else if (name == "Sig") sigs.insert(value); else if (name == "CA") { - if (ca) throw corrupt(); + if (ca) + throw corrupt(); // FIXME: allow blank ca or require skipping field? ca = parseContentAddressOpt(value); } @@ -80,9 +82,11 @@ NarInfo::NarInfo(const Store & store, const std::string & s, const std::string & pos = eol + 1; } - if (compression == "") compression = "bzip2"; + if (compression == "") + compression = "bzip2"; - if (!havePath || !haveNarHash || url.empty() || narSize == 0) throw corrupt(); + if (!havePath || !haveNarHash || url.empty() || narSize == 0) + throw corrupt(); } std::string NarInfo::to_string(const Store & store) const diff --git a/src/libstore/nar-info.hh b/src/libstore/nar-info.hh index 01683ec731ac..4ce6ede75236 100644 --- a/src/libstore/nar-info.hh +++ b/src/libstore/nar-info.hh @@ -8,17 +8,20 @@ namespace nix { class Store; -struct NarInfo : ValidPathInfo -{ +struct NarInfo : ValidPathInfo { std::string url; std::string compression; std::optional fileHash; uint64_t fileSize = 0; NarInfo() = delete; - NarInfo(StorePath && path, Hash narHash) : ValidPathInfo(std::move(path), narHash) { } - NarInfo(const ValidPathInfo & info) : ValidPathInfo(info) { } - NarInfo(const Store & store, const std::string & s, const std::string & whence); + NarInfo(StorePath && path, Hash narHash) + : ValidPathInfo(std::move(path), narHash) + { + } + NarInfo(const ValidPathInfo & info) : ValidPathInfo(info) {} + NarInfo(const Store & store, const std::string & s, + const std::string & whence); std::string to_string(const Store & store) const; }; diff --git a/src/libstore/optimise-store.cc b/src/libstore/optimise-store.cc index 8af9b1dde288..2274cb0a5469 100644 --- a/src/libstore/optimise-store.cc +++ b/src/libstore/optimise-store.cc @@ -11,10 +11,8 @@ #include #include - namespace nix { - static void makeWritable(const Path & path) { auto st = lstat(path); @@ -22,30 +20,29 @@ static void makeWritable(const Path & path) throw SysError("changing writability of '%1%'", path); } - -struct MakeReadOnly -{ +struct MakeReadOnly { Path path; - MakeReadOnly(const PathView path) : path(path) { } + MakeReadOnly(const PathView path) : path(path) {} ~MakeReadOnly() { try { /* This will make the path read-only. */ - if (path != "") canonicaliseTimestampAndPermissions(path); + if (path != "") + canonicaliseTimestampAndPermissions(path); } catch (...) { ignoreException(); } } }; - LocalStore::InodeHash LocalStore::loadInodeHash() { debug("loading hash inodes in memory"); InodeHash inodeHash; AutoCloseDir dir(opendir(linksDir.c_str())); - if (!dir) throw SysError("opening directory '%1%'", linksDir); + if (!dir) + throw SysError("opening directory '%1%'", linksDir); struct dirent * dirent; while (errno = 0, dirent = readdir(dir.get())) { /* sic */ @@ -53,20 +50,22 @@ LocalStore::InodeHash LocalStore::loadInodeHash() // We don't care if we hit non-hash files, anything goes inodeHash.insert(dirent->d_ino); } - if (errno) throw SysError("reading directory '%1%'", linksDir); + if (errno) + throw SysError("reading directory '%1%'", linksDir); printMsg(lvlTalkative, format("loaded %1% hash inodes") % inodeHash.size()); return inodeHash; } - -Strings LocalStore::readDirectoryIgnoringInodes(const Path & path, const InodeHash & inodeHash) +Strings LocalStore::readDirectoryIgnoringInodes(const Path & path, + const InodeHash & inodeHash) { Strings names; AutoCloseDir dir(opendir(path.c_str())); - if (!dir) throw SysError("opening directory '%1%'", path); + if (!dir) + throw SysError("opening directory '%1%'", path); struct dirent * dirent; while (errno = 0, dirent = readdir(dir.get())) { /* sic */ @@ -78,30 +77,31 @@ Strings LocalStore::readDirectoryIgnoringInodes(const Path & path, const InodeHa } std::string name = dirent->d_name; - if (name == "." || name == "..") continue; + if (name == "." || name == "..") + continue; names.push_back(name); } - if (errno) throw SysError("reading directory '%1%'", path); + if (errno) + throw SysError("reading directory '%1%'", path); return names; } - void LocalStore::optimisePath_(Activity * act, OptimiseStats & stats, - const Path & path, InodeHash & inodeHash, RepairFlag repair) + const Path & path, InodeHash & inodeHash, + RepairFlag repair) { checkInterrupt(); auto st = lstat(path); #if __APPLE__ - /* HFS/macOS has some undocumented security feature disabling hardlinking for - special files within .app dirs. *.app/Contents/PkgInfo and + /* HFS/macOS has some undocumented security feature disabling hardlinking + for special files within .app dirs. *.app/Contents/PkgInfo and *.app/Contents/Resources/\*.lproj seem to be the only paths affected. See https://github.com/NixOS/nix/issues/1443 for more discussion. */ - if (std::regex_search(path, std::regex("\\.app/Contents/.+$"))) - { + if (std::regex_search(path, std::regex("\\.app/Contents/.+$"))) { debug(format("'%1%' is not allowed to be linked in macOS") % path); return; } @@ -119,7 +119,8 @@ void LocalStore::optimisePath_(Activity * act, OptimiseStats & stats, #if CAN_LINK_SYMLINK && !S_ISLNK(st.st_mode) #endif - ) return; + ) + return; /* Sometimes SNAFUs can cause files in the Nix store to be modified, in particular when running programs as root under @@ -132,7 +133,8 @@ void LocalStore::optimisePath_(Activity * act, OptimiseStats & stats, /* This can still happen on top-level files. */ if (st.st_nlink > 1 && inodeHash.count(st.st_ino)) { - debug("'%s' is already linked, with %d other file(s)", path, st.st_nlink - 2); + debug("'%s' is already linked, with %d other file(s)", path, + st.st_nlink - 2); return; } @@ -154,13 +156,13 @@ void LocalStore::optimisePath_(Activity * act, OptimiseStats & stats, /* Maybe delete the link, if it has been corrupted. */ if (pathExists(linkPath)) { auto stLink = lstat(linkPath); - if (st.st_size != stLink.st_size - || (repair && hash != hashPath(htSHA256, linkPath).first)) - { + if (st.st_size != stLink.st_size || + (repair && hash != hashPath(htSHA256, linkPath).first)) { // XXX: Consider overwriting linkPath with our valid version. warn("removing corrupted link '%s'", linkPath); warn("There may be more corrupted paths." - "\nYou should run `nix-store --verify --check-contents --repair` to fix them all"); + "\nYou should run `nix-store --verify --check-contents " + "--repair` to fix them all"); unlink(linkPath.c_str()); } } @@ -183,7 +185,8 @@ void LocalStore::optimisePath_(Activity * act, OptimiseStats & stats, full. When that happens, it's fine to ignore it: we just effectively disable deduplication of this file. */ - printInfo("cannot link '%s' to '%s': %s", linkPath, path, strerror(errno)); + printInfo("cannot link '%s' to '%s': %s", linkPath, path, + strerror(errno)); return; default: @@ -207,14 +210,16 @@ void LocalStore::optimisePath_(Activity * act, OptimiseStats & stats, permissions). */ const Path dirOfPath(dirOf(path)); bool mustToggle = dirOfPath != realStoreDir.get(); - if (mustToggle) makeWritable(dirOfPath); + if (mustToggle) + makeWritable(dirOfPath); /* When we're done, make the directory read-only again and reset its timestamp back to 0. */ MakeReadOnly makeReadOnly(mustToggle ? dirOfPath : ""); - Path tempLink = (format("%1%/.tmp-link-%2%-%3%") - % realStoreDir % getpid() % random()).str(); + Path tempLink = + (format("%1%/.tmp-link-%2%-%3%") % realStoreDir % getpid() % random()) + .str(); if (link(linkPath.c_str(), tempLink.c_str()) == -1) { if (errno == EMLINK) { @@ -222,7 +227,8 @@ void LocalStore::optimisePath_(Activity * act, OptimiseStats & stats, systems). This is likely to happen with empty files. Just shrug and ignore. */ if (st.st_size) - printInfo(format("'%1%' has maximum number of links") % linkPath); + printInfo(format("'%1%' has maximum number of links") % + linkPath); return; } throw SysError("cannot link '%1%' to '%2%'", tempLink, linkPath); @@ -251,7 +257,6 @@ void LocalStore::optimisePath_(Activity * act, OptimiseStats & stats, act->result(resFileLinked, st.st_size, st.st_blocks); } - void LocalStore::optimiseStore(OptimiseStats & stats) { Activity act(*logger, actOptimiseStore); @@ -265,10 +270,14 @@ void LocalStore::optimiseStore(OptimiseStats & stats) for (auto & i : paths) { addTempRoot(i); - if (!isValidPath(i)) continue; /* path was GC'ed, probably */ + if (!isValidPath(i)) + continue; /* path was GC'ed, probably */ { - Activity act(*logger, lvlTalkative, actUnknown, fmt("optimising path '%s'", printStorePath(i))); - optimisePath_(&act, stats, realStoreDir + "/" + std::string(i.to_string()), inodeHash, NoRepair); + Activity act(*logger, lvlTalkative, actUnknown, + fmt("optimising path '%s'", printStorePath(i))); + optimisePath_(&act, stats, + realStoreDir + "/" + std::string(i.to_string()), + inodeHash, NoRepair); } done++; act.progress(done, paths.size()); @@ -281,9 +290,8 @@ void LocalStore::optimiseStore() optimiseStore(stats); - printInfo("%s freed by hard-linking %d files", - showBytes(stats.bytesFreed), - stats.filesLinked); + printInfo("%s freed by hard-linking %d files", showBytes(stats.bytesFreed), + stats.filesLinked); } void LocalStore::optimisePath(const Path & path, RepairFlag repair) @@ -291,8 +299,8 @@ void LocalStore::optimisePath(const Path & path, RepairFlag repair) OptimiseStats stats; InodeHash inodeHash; - if (settings.autoOptimiseStore) optimisePath_(nullptr, stats, path, inodeHash, repair); + if (settings.autoOptimiseStore) + optimisePath_(nullptr, stats, path, inodeHash, repair); } - } diff --git a/src/libstore/parsed-derivations.cc b/src/libstore/parsed-derivations.cc index f2288a04ed81..12268124f72d 100644 --- a/src/libstore/parsed-derivations.cc +++ b/src/libstore/parsed-derivations.cc @@ -6,23 +6,27 @@ namespace nix { -ParsedDerivation::ParsedDerivation(const StorePath & drvPath, BasicDerivation & drv) +ParsedDerivation::ParsedDerivation(const StorePath & drvPath, + BasicDerivation & drv) : drvPath(drvPath), drv(drv) { /* Parse the __json attribute, if any. */ auto jsonAttr = drv.env.find("__json"); if (jsonAttr != drv.env.end()) { try { - structuredAttrs = std::make_unique(nlohmann::json::parse(jsonAttr->second)); + structuredAttrs = std::make_unique( + nlohmann::json::parse(jsonAttr->second)); } catch (std::exception & e) { - throw Error("cannot process __json attribute of '%s': %s", drvPath.to_string(), e.what()); + throw Error("cannot process __json attribute of '%s': %s", + drvPath.to_string(), e.what()); } } } -ParsedDerivation::~ParsedDerivation() { } +ParsedDerivation::~ParsedDerivation() {} -std::optional ParsedDerivation::getStringAttr(const std::string & name) const +std::optional +ParsedDerivation::getStringAttr(const std::string & name) const { if (structuredAttrs) { auto i = structuredAttrs->find(name); @@ -30,7 +34,9 @@ std::optional ParsedDerivation::getStringAttr(const std::string & n return {}; else { if (!i->is_string()) - throw Error("attribute '%s' of derivation '%s' must be a string", name, drvPath.to_string()); + throw Error( + "attribute '%s' of derivation '%s' must be a string", name, + drvPath.to_string()); return i->get(); } } else { @@ -50,7 +56,9 @@ bool ParsedDerivation::getBoolAttr(const std::string & name, bool def) const return def; else { if (!i->is_boolean()) - throw Error("attribute '%s' of derivation '%s' must be a Boolean", name, drvPath.to_string()); + throw Error( + "attribute '%s' of derivation '%s' must be a Boolean", name, + drvPath.to_string()); return i->get(); } } else { @@ -62,7 +70,8 @@ bool ParsedDerivation::getBoolAttr(const std::string & name, bool def) const } } -std::optional ParsedDerivation::getStringsAttr(const std::string & name) const +std::optional +ParsedDerivation::getStringsAttr(const std::string & name) const { if (structuredAttrs) { auto i = structuredAttrs->find(name); @@ -70,11 +79,15 @@ std::optional ParsedDerivation::getStringsAttr(const std::string & name return {}; else { if (!i->is_array()) - throw Error("attribute '%s' of derivation '%s' must be a list of strings", name, drvPath.to_string()); + throw Error("attribute '%s' of derivation '%s' must be a list " + "of strings", + name, drvPath.to_string()); Strings res; for (auto j = i->begin(); j != i->end(); ++j) { if (!j->is_string()) - throw Error("attribute '%s' of derivation '%s' must be a list of strings", name, drvPath.to_string()); + throw Error("attribute '%s' of derivation '%s' must be a " + "list of strings", + name, drvPath.to_string()); res.push_back(j->get()); } return res; @@ -91,7 +104,8 @@ std::optional ParsedDerivation::getStringsAttr(const std::string & name StringSet ParsedDerivation::getRequiredSystemFeatures() const { StringSet res; - for (auto & i : getStringsAttr("requiredSystemFeatures").value_or(Strings())) + for (auto & i : + getStringsAttr("requiredSystemFeatures").value_or(Strings())) res.insert(i); if (!drv.type().hasKnownOutputPaths()) res.insert("ca-derivations"); @@ -100,17 +114,16 @@ StringSet ParsedDerivation::getRequiredSystemFeatures() const bool ParsedDerivation::canBuildLocally(Store & localStore) const { - if (drv.platform != settings.thisSystem.get() - && !settings.extraPlatforms.get().count(drv.platform) - && !drv.isBuiltin()) + if (drv.platform != settings.thisSystem.get() && + !settings.extraPlatforms.get().count(drv.platform) && !drv.isBuiltin()) return false; - if (settings.maxBuildJobs.get() == 0 - && !drv.isBuiltin()) + if (settings.maxBuildJobs.get() == 0 && !drv.isBuiltin()) return false; for (auto & feature : getRequiredSystemFeatures()) - if (!localStore.systemFeatures.get().count(feature)) return false; + if (!localStore.systemFeatures.get().count(feature)) + return false; return true; } @@ -127,10 +140,13 @@ bool ParsedDerivation::substitutesAllowed() const static std::regex shVarName("[A-Za-z_][A-Za-z0-9_]*"); -std::optional ParsedDerivation::prepareStructuredAttrs(Store & store, const StorePathSet & inputPaths) +std::optional +ParsedDerivation::prepareStructuredAttrs(Store & store, + const StorePathSet & inputPaths) { auto structuredAttrs = getStructuredAttrs(); - if (!structuredAttrs) return std::nullopt; + if (!structuredAttrs) + return std::nullopt; auto json = *structuredAttrs; @@ -149,9 +165,11 @@ std::optional ParsedDerivation::prepareStructuredAttrs(Store & s JSONPlaceholder jsonRoot(str, true); StorePathSet storePaths; for (auto & p : *i) - storePaths.insert(store.parseStorePath(p.get())); - store.pathInfoToJSON(jsonRoot, - store.exportReferences(storePaths, inputPaths), false, true); + storePaths.insert( + store.parseStorePath(p.get())); + store.pathInfoToJSON( + jsonRoot, store.exportReferences(storePaths, inputPaths), + false, true); } json[i.key()] = nlohmann::json::parse(str.str()); // urgh } @@ -168,7 +186,8 @@ std::optional ParsedDerivation::prepareStructuredAttrs(Store & s std::string writeStructuredAttrsShell(const nlohmann::json & json) { - auto handleSimpleType = [](const nlohmann::json & value) -> std::optional { + auto handleSimpleType = + [](const nlohmann::json & value) -> std::optional { if (value.is_string()) return shellEscape(value.get()); @@ -191,7 +210,8 @@ std::string writeStructuredAttrsShell(const nlohmann::json & json) for (auto & [key, value] : json.items()) { - if (!std::regex_match(key, shVarName)) continue; + if (!std::regex_match(key, shVarName)) + continue; auto s = handleSimpleType(value); if (s) @@ -203,8 +223,12 @@ std::string writeStructuredAttrsShell(const nlohmann::json & json) for (auto & value2 : value) { auto s3 = handleSimpleType(value2); - if (!s3) { good = false; break; } - s2 += *s3; s2 += ' '; + if (!s3) { + good = false; + break; + } + s2 += *s3; + s2 += ' '; } if (good) @@ -217,7 +241,10 @@ std::string writeStructuredAttrsShell(const nlohmann::json & json) for (auto & [key2, value2] : value.items()) { auto s3 = handleSimpleType(value2); - if (!s3) { good = false; break; } + if (!s3) { + good = false; + break; + } s2 += fmt("[%s]=%s ", shellEscape(key2), *s3); } diff --git a/src/libstore/parsed-derivations.hh b/src/libstore/parsed-derivations.hh index 95bec21e851e..4ec6ee8fc69e 100644 --- a/src/libstore/parsed-derivations.hh +++ b/src/libstore/parsed-derivations.hh @@ -7,14 +7,12 @@ namespace nix { -class ParsedDerivation -{ +class ParsedDerivation { StorePath drvPath; BasicDerivation & drv; std::unique_ptr structuredAttrs; -public: - + public: ParsedDerivation(const StorePath & drvPath, BasicDerivation & drv); ~ParsedDerivation(); @@ -38,7 +36,8 @@ public: bool substitutesAllowed() const; - std::optional prepareStructuredAttrs(Store & store, const StorePathSet & inputPaths); + std::optional + prepareStructuredAttrs(Store & store, const StorePathSet & inputPaths); }; std::string writeStructuredAttrsShell(const nlohmann::json & json); diff --git a/src/libstore/path-info.cc b/src/libstore/path-info.cc index fda55b2b6183..d1980059b54b 100644 --- a/src/libstore/path-info.cc +++ b/src/libstore/path-info.cc @@ -3,18 +3,23 @@ namespace nix { -ValidPathInfo ValidPathInfo::read(Source & source, const Store & store, unsigned int format) +ValidPathInfo ValidPathInfo::read(Source & source, const Store & store, + unsigned int format) { - return read(source, store, format, store.parseStorePath(readString(source))); + return read(source, store, format, + store.parseStorePath(readString(source))); } -ValidPathInfo ValidPathInfo::read(Source & source, const Store & store, unsigned int format, StorePath && path) +ValidPathInfo ValidPathInfo::read(Source & source, const Store & store, + unsigned int format, StorePath && path) { auto deriver = readString(source); auto narHash = Hash::parseAny(readString(source), htSHA256); ValidPathInfo info(path, narHash); - if (deriver != "") info.deriver = store.parseStorePath(deriver); - info.references = worker_proto::read(store, source, Phantom {}); + if (deriver != "") + info.deriver = store.parseStorePath(deriver); + info.references = + worker_proto::read(store, source, Phantom{}); source >> info.registrationTime >> info.narSize; if (format >= 16) { source >> info.ultimate; @@ -24,11 +29,8 @@ ValidPathInfo ValidPathInfo::read(Source & source, const Store & store, unsigned return info; } -void ValidPathInfo::write( - Sink & sink, - const Store & store, - unsigned int format, - bool includePath) const +void ValidPathInfo::write(Sink & sink, const Store & store, unsigned int format, + bool includePath) const { if (includePath) sink << store.printStorePath(path); @@ -37,9 +39,7 @@ void ValidPathInfo::write( worker_proto::write(store, sink, references); sink << registrationTime << narSize; if (format >= 16) { - sink << ultimate - << sigs - << renderContentAddress(ca); + sink << ultimate << sigs << renderContentAddress(ca); } } diff --git a/src/libstore/path-info.hh b/src/libstore/path-info.hh index b4b54e593c15..831744dcc281 100644 --- a/src/libstore/path-info.hh +++ b/src/libstore/path-info.hh @@ -10,23 +10,18 @@ namespace nix { - class Store; - -struct SubstitutablePathInfo -{ +struct SubstitutablePathInfo { std::optional deriver; StorePathSet references; uint64_t downloadSize; /* 0 = unknown or inapplicable */ - uint64_t narSize; /* 0 = unknown */ + uint64_t narSize; /* 0 = unknown */ }; typedef std::map SubstitutablePathInfos; - -struct ValidPathInfo -{ +struct ValidPathInfo { StorePath path; std::optional deriver; // TODO document this @@ -34,7 +29,7 @@ struct ValidPathInfo StorePathSet references; time_t registrationTime = 0; uint64_t narSize = 0; // 0 = unknown - uint64_t id; // internal use only + uint64_t id; // internal use only /* Whether the path is ultimately trusted, that is, it's a derivation output that was built locally. */ @@ -60,12 +55,10 @@ struct ValidPathInfo */ std::optional ca; - bool operator == (const ValidPathInfo & i) const + bool operator==(const ValidPathInfo & i) const { - return - path == i.path - && narHash == i.narHash - && references == i.references; + return path == i.path && narHash == i.narHash && + references == i.references; } /* Return a fingerprint of the store path to be used in binary @@ -92,24 +85,31 @@ struct ValidPathInfo /* Return the number of signatures on this .narinfo that were produced by one of the specified keys, or maxSigs if the path is content-addressed. */ - size_t checkSignatures(const Store & store, const PublicKeys & publicKeys) const; + size_t checkSignatures(const Store & store, + const PublicKeys & publicKeys) const; /* Verify a single signature. */ - bool checkSignature(const Store & store, const PublicKeys & publicKeys, const std::string & sig) const; + bool checkSignature(const Store & store, const PublicKeys & publicKeys, + const std::string & sig) const; Strings shortRefs() const; ValidPathInfo(const ValidPathInfo & other) = default; - ValidPathInfo(StorePath && path, Hash narHash) : path(std::move(path)), narHash(narHash) { }; - ValidPathInfo(const StorePath & path, Hash narHash) : path(path), narHash(narHash) { }; + ValidPathInfo(StorePath && path, Hash narHash) + : path(std::move(path)), narHash(narHash){}; + ValidPathInfo(const StorePath & path, Hash narHash) + : path(path), narHash(narHash){}; - virtual ~ValidPathInfo() { } + virtual ~ValidPathInfo() {} - static ValidPathInfo read(Source & source, const Store & store, unsigned int format); - static ValidPathInfo read(Source & source, const Store & store, unsigned int format, StorePath && path); + static ValidPathInfo read(Source & source, const Store & store, + unsigned int format); + static ValidPathInfo read(Source & source, const Store & store, + unsigned int format, StorePath && path); - void write(Sink & sink, const Store & store, unsigned int format, bool includePath = true) const; + void write(Sink & sink, const Store & store, unsigned int format, + bool includePath = true) const; }; typedef std::map ValidPathInfos; diff --git a/src/libstore/path-with-outputs.cc b/src/libstore/path-with-outputs.cc index d6d67ea05b9b..264e40a95368 100644 --- a/src/libstore/path-with-outputs.cc +++ b/src/libstore/path-with-outputs.cc @@ -8,67 +8,74 @@ namespace nix { std::string StorePathWithOutputs::to_string(const Store & store) const { - return outputs.empty() - ? store.printStorePath(path) - : store.printStorePath(path) + "!" + concatStringsSep(",", outputs); + return outputs.empty() ? store.printStorePath(path) + : store.printStorePath(path) + "!" + + concatStringsSep(",", outputs); } - DerivedPath StorePathWithOutputs::toDerivedPath() const { if (!outputs.empty() || path.isDerivation()) - return DerivedPath::Built { path, outputs }; + return DerivedPath::Built{path, outputs}; else - return DerivedPath::Opaque { path }; + return DerivedPath::Opaque{path}; } - -std::vector toDerivedPaths(const std::vector ss) +std::vector +toDerivedPaths(const std::vector ss) { std::vector reqs; - for (auto & s : ss) reqs.push_back(s.toDerivedPath()); + for (auto & s : ss) + reqs.push_back(s.toDerivedPath()); return reqs; } - -std::variant StorePathWithOutputs::tryFromDerivedPath(const DerivedPath & p) +std::variant +StorePathWithOutputs::tryFromDerivedPath(const DerivedPath & p) { - return std::visit(overloaded { - [&](const DerivedPath::Opaque & bo) -> std::variant { - if (bo.path.isDerivation()) { - // drv path gets interpreted as "build", not "get drv file itself" - return bo.path; - } - return StorePathWithOutputs { bo.path }; - }, - [&](const DerivedPath::Built & bfd) -> std::variant { - return StorePathWithOutputs { bfd.drvPath, bfd.outputs }; + return std::visit( + overloaded{ + [&](const DerivedPath::Opaque & bo) + -> std::variant { + if (bo.path.isDerivation()) { + // drv path gets interpreted as "build", not "get drv file + // itself" + return bo.path; + } + return StorePathWithOutputs{bo.path}; + }, + [&](const DerivedPath::Built & bfd) + -> std::variant { + return StorePathWithOutputs{bfd.drvPath, bfd.outputs}; + }, }, - }, p.raw()); + p.raw()); } - std::pair parsePathWithOutputs(std::string_view s) { size_t n = s.find("!"); return n == s.npos - ? std::make_pair(s, std::set()) - : std::make_pair(((std::string_view) s).substr(0, n), - tokenizeString>(((std::string_view) s).substr(n + 1), ",")); + ? std::make_pair(s, std::set()) + : std::make_pair(((std::string_view) s).substr(0, n), + tokenizeString>( + ((std::string_view) s).substr(n + 1), ",")); } - -StorePathWithOutputs parsePathWithOutputs(const Store & store, std::string_view pathWithOutputs) +StorePathWithOutputs parsePathWithOutputs(const Store & store, + std::string_view pathWithOutputs) { auto [path, outputs] = parsePathWithOutputs(pathWithOutputs); - return StorePathWithOutputs { store.parseStorePath(path), std::move(outputs) }; + return StorePathWithOutputs{store.parseStorePath(path), std::move(outputs)}; } - -StorePathWithOutputs followLinksToStorePathWithOutputs(const Store & store, std::string_view pathWithOutputs) +StorePathWithOutputs +followLinksToStorePathWithOutputs(const Store & store, + std::string_view pathWithOutputs) { auto [path, outputs] = parsePathWithOutputs(pathWithOutputs); - return StorePathWithOutputs { store.followLinksToStorePath(path), std::move(outputs) }; + return StorePathWithOutputs{store.followLinksToStorePath(path), + std::move(outputs)}; } std::pair parseOutputsSpec(const std::string & s) diff --git a/src/libstore/path-with-outputs.hh b/src/libstore/path-with-outputs.hh index 0cb5eb2231c0..f81bb1ee2b7d 100644 --- a/src/libstore/path-with-outputs.hh +++ b/src/libstore/path-with-outputs.hh @@ -8,8 +8,7 @@ namespace nix { -struct StorePathWithOutputs -{ +struct StorePathWithOutputs { StorePath path; std::set outputs; @@ -17,10 +16,12 @@ struct StorePathWithOutputs DerivedPath toDerivedPath() const; - static std::variant tryFromDerivedPath(const DerivedPath &); + static std::variant + tryFromDerivedPath(const DerivedPath &); }; -std::vector toDerivedPaths(const std::vector); +std::vector +toDerivedPaths(const std::vector); std::pair parsePathWithOutputs(std::string_view s); @@ -29,18 +30,21 @@ class Store; /* Split a string specifying a derivation and a set of outputs (/nix/store/hash-foo!out1,out2,...) into the derivation path and the outputs. */ -StorePathWithOutputs parsePathWithOutputs(const Store & store, std::string_view pathWithOutputs); +StorePathWithOutputs parsePathWithOutputs(const Store & store, + std::string_view pathWithOutputs); -StorePathWithOutputs followLinksToStorePathWithOutputs(const Store & store, std::string_view pathWithOutputs); +StorePathWithOutputs +followLinksToStorePathWithOutputs(const Store & store, + std::string_view pathWithOutputs); typedef std::set OutputNames; struct AllOutputs { - bool operator < (const AllOutputs & _) const { return false; } + bool operator<(const AllOutputs & _) const { return false; } }; struct DefaultOutputs { - bool operator < (const DefaultOutputs & _) const { return false; } + bool operator<(const DefaultOutputs & _) const { return false; } }; typedef std::variant OutputsSpec; diff --git a/src/libstore/path.cc b/src/libstore/path.cc index 392db225e25e..bd68f831d819 100644 --- a/src/libstore/path.cc +++ b/src/libstore/path.cc @@ -9,24 +9,27 @@ static void checkName(std::string_view path, std::string_view name) if (name.empty()) throw BadStorePath("store path '%s' has an empty name", path); if (name.size() > 211) - throw BadStorePath("store path '%s' has a name longer than 211 characters", path); + throw BadStorePath( + "store path '%s' has a name longer than 211 characters", path); for (auto c : name) - if (!((c >= '0' && c <= '9') - || (c >= 'a' && c <= 'z') - || (c >= 'A' && c <= 'Z') - || c == '+' || c == '-' || c == '.' || c == '_' || c == '?' || c == '=')) - throw BadStorePath("store path '%s' contains illegal character '%s'", path, c); + if (!((c >= '0' && c <= '9') || (c >= 'a' && c <= 'z') || + (c >= 'A' && c <= 'Z') || c == '+' || c == '-' || c == '.' || + c == '_' || c == '?' || c == '=')) + throw BadStorePath( + "store path '%s' contains illegal character '%s'", path, c); } -StorePath::StorePath(std::string_view _baseName) - : baseName(_baseName) +StorePath::StorePath(std::string_view _baseName) : baseName(_baseName) { if (baseName.size() < HashLen + 1) - throw BadStorePath("'%s' is too short to be a valid store path", baseName); + throw BadStorePath("'%s' is too short to be a valid store path", + baseName); for (auto c : hashPart()) - if (c == 'e' || c == 'o' || c == 'u' || c == 't' - || !((c >= '0' && c <= '9') || (c >= 'a' && c <= 'z'))) - throw BadStorePath("store path '%s' contains illegal base-32 character '%s'", baseName, c); + if (c == 'e' || c == 'o' || c == 'u' || c == 't' || + !((c >= '0' && c <= '9') || (c >= 'a' && c <= 'z'))) + throw BadStorePath( + "store path '%s' contains illegal base-32 character '%s'", + baseName, c); checkName(baseName, name()); } @@ -36,10 +39,7 @@ StorePath::StorePath(const Hash & hash, std::string_view _name) checkName(baseName, name()); } -bool StorePath::isDerivation() const -{ - return hasSuffix(name(), drvExtension); -} +bool StorePath::isDerivation() const { return hasSuffix(name(), drvExtension); } StorePath StorePath::dummy("ffffffffffffffffffffffffffffffff-x"); @@ -75,7 +75,8 @@ bool Store::isStorePath(std::string_view path) const StorePathSet Store::parseStorePathSet(const PathSet & paths) const { StorePathSet res; - for (auto & i : paths) res.insert(parseStorePath(i)); + for (auto & i : paths) + res.insert(parseStorePath(i)); return res; } @@ -87,7 +88,8 @@ std::string Store::printStorePath(const StorePath & path) const PathSet Store::printStorePathSet(const StorePathSet & paths) const { PathSet res; - for (auto & i : paths) res.insert(printStorePath(i)); + for (auto & i : paths) + res.insert(printStorePath(i)); return res; } diff --git a/src/libstore/path.hh b/src/libstore/path.hh index 77fd0f8dce22..3ed800dd9ef9 100644 --- a/src/libstore/path.hh +++ b/src/libstore/path.hh @@ -8,12 +8,10 @@ namespace nix { class Store; struct Hash; -class StorePath -{ +class StorePath { std::string baseName; -public: - + public: /* Size of the hash part of store paths, in base-32 characters. */ constexpr static size_t HashLen = 32; // i.e. 160 bits @@ -23,22 +21,19 @@ public: StorePath(const Hash & hash, std::string_view name); - std::string_view to_string() const - { - return baseName; - } + std::string_view to_string() const { return baseName; } - bool operator < (const StorePath & other) const + bool operator<(const StorePath & other) const { return baseName < other.baseName; } - bool operator == (const StorePath & other) const + bool operator==(const StorePath & other) const { return baseName == other.baseName; } - bool operator != (const StorePath & other) const + bool operator!=(const StorePath & other) const { return baseName != other.baseName; } @@ -78,7 +73,7 @@ namespace std { template<> struct hash { std::size_t operator()(const nix::StorePath & path) const noexcept { - return * (std::size_t *) path.to_string().data(); + return *(std::size_t *) path.to_string().data(); } }; diff --git a/src/libstore/pathlocks.cc b/src/libstore/pathlocks.cc index 42023cd0a437..fc8c839c3a9e 100644 --- a/src/libstore/pathlocks.cc +++ b/src/libstore/pathlocks.cc @@ -10,10 +10,8 @@ #include #include - namespace nix { - AutoCloseFD openLockFile(const Path & path, bool create) { AutoCloseFD fd; @@ -25,7 +23,6 @@ AutoCloseFD openLockFile(const Path & path, bool create) return fd; } - void deleteLockFile(const Path & path, int fd) { /* Get rid of the lock file. Have to be careful not to introduce @@ -38,14 +35,17 @@ void deleteLockFile(const Path & path, int fd) file is an optimisation, not a necessity. */ } - bool lockFile(int fd, LockType lockType, bool wait) { int type; - if (lockType == ltRead) type = LOCK_SH; - else if (lockType == ltWrite) type = LOCK_EX; - else if (lockType == ltNone) type = LOCK_UN; - else abort(); + if (lockType == ltRead) + type = LOCK_SH; + else if (lockType == ltWrite) + type = LOCK_EX; + else if (lockType == ltNone) + type = LOCK_UN; + else + abort(); if (wait) { while (flock(fd, type) != 0) { @@ -58,7 +58,8 @@ bool lockFile(int fd, LockType lockType, bool wait) } else { while (flock(fd, type | LOCK_NB) != 0) { checkInterrupt(); - if (errno == EWOULDBLOCK) return false; + if (errno == EWOULDBLOCK) + return false; if (errno != EINTR) throw SysError("acquiring/releasing lock"); } @@ -67,12 +68,7 @@ bool lockFile(int fd, LockType lockType, bool wait) return true; } - -PathLocks::PathLocks() - : deletePaths(false) -{ -} - +PathLocks::PathLocks() : deletePaths(false) {} PathLocks::PathLocks(const PathSet & paths, const std::string & waitMsg) : deletePaths(false) @@ -80,9 +76,8 @@ PathLocks::PathLocks(const PathSet & paths, const std::string & waitMsg) lockPaths(paths, waitMsg); } - -bool PathLocks::lockPaths(const PathSet & paths, - const std::string & waitMsg, bool wait) +bool PathLocks::lockPaths(const PathSet & paths, const std::string & waitMsg, + bool wait) { assert(fds.empty()); @@ -108,7 +103,8 @@ bool PathLocks::lockPaths(const PathSet & paths, /* Acquire an exclusive lock. */ if (!lockFile(fd.get(), ltWrite, false)) { if (wait) { - if (waitMsg != "") printError(waitMsg); + if (waitMsg != "") + printError(waitMsg); lockFile(fd.get(), ltWrite, true); } else { /* Failed to lock this path; release all other @@ -130,7 +126,8 @@ bool PathLocks::lockPaths(const PathSet & paths, a lock on a deleted file. This means that other processes may create and acquire a lock on `lockPath', and proceed. So we must retry. */ - debug(format("open lock file '%1%' has become stale") % lockPath); + debug(format("open lock file '%1%' has become stale") % + lockPath); else break; } @@ -142,7 +139,6 @@ bool PathLocks::lockPaths(const PathSet & paths, return true; } - PathLocks::~PathLocks() { try { @@ -152,16 +148,15 @@ PathLocks::~PathLocks() } } - void PathLocks::unlock() { for (auto & i : fds) { - if (deletePaths) deleteLockFile(i.second, i.first); + if (deletePaths) + deleteLockFile(i.second, i.first); if (close(i.first) == -1) - printError( - "error (ignored): cannot close lock file on '%1%'", - i.second); + printError("error (ignored): cannot close lock file on '%1%'", + i.second); debug(format("lock released on '%1%'") % i.second); } @@ -169,13 +164,11 @@ void PathLocks::unlock() fds.clear(); } - void PathLocks::setDeletion(bool deletePaths) { this->deletePaths = deletePaths; } - FdLock::FdLock(int fd, LockType lockType, bool wait, std::string_view waitMsg) : fd(fd) { @@ -188,5 +181,4 @@ FdLock::FdLock(int fd, LockType lockType, bool wait, std::string_view waitMsg) acquired = lockFile(fd, lockType, false); } - } diff --git a/src/libstore/pathlocks.hh b/src/libstore/pathlocks.hh index 5e3a734b4379..d2bfdd4ed737 100644 --- a/src/libstore/pathlocks.hh +++ b/src/libstore/pathlocks.hh @@ -16,27 +16,23 @@ enum LockType { ltRead, ltWrite, ltNone }; bool lockFile(int fd, LockType lockType, bool wait); -class PathLocks -{ -private: +class PathLocks { + private: typedef std::pair FDPair; std::list fds; bool deletePaths; -public: + public: PathLocks(); - PathLocks(const PathSet & paths, - const std::string & waitMsg = ""); - bool lockPaths(const PathSet & _paths, - const std::string & waitMsg = "", - bool wait = true); + PathLocks(const PathSet & paths, const std::string & waitMsg = ""); + bool lockPaths(const PathSet & _paths, const std::string & waitMsg = "", + bool wait = true); ~PathLocks(); void unlock(); void setDeletion(bool deletePaths); }; -struct FdLock -{ +struct FdLock { int fd; bool acquired = false; diff --git a/src/libstore/profiles.cc b/src/libstore/profiles.cc index 3e4188188f69..fdf6e8e599bc 100644 --- a/src/libstore/profiles.cc +++ b/src/libstore/profiles.cc @@ -9,27 +9,27 @@ #include #include - namespace nix { - /* Parse a generation name of the format `--link'. */ -static std::optional parseName(const std::string & profileName, const std::string & name) +static std::optional +parseName(const std::string & profileName, const std::string & name) { - if (name.substr(0, profileName.size() + 1) != profileName + "-") return {}; + if (name.substr(0, profileName.size() + 1) != profileName + "-") + return {}; auto s = name.substr(profileName.size() + 1); auto p = s.find("-link"); - if (p == std::string::npos) return {}; + if (p == std::string::npos) + return {}; if (auto n = string2Int(s.substr(0, p))) return *n; else return {}; } - - -std::pair> findGenerations(Path profile) +std::pair> +findGenerations(Path profile) { Generations gens; @@ -39,36 +39,27 @@ std::pair> findGenerations(Path pro for (auto & i : readDirectory(profileDir)) { if (auto n = parseName(profileName, i.name)) { auto path = profileDir + "/" + i.name; - gens.push_back({ - .number = *n, - .path = path, - .creationTime = lstat(path).st_mtime - }); + gens.push_back({.number = *n, + .path = path, + .creationTime = lstat(path).st_mtime}); } } - gens.sort([](const Generation & a, const Generation & b) - { + gens.sort([](const Generation & a, const Generation & b) { return a.number < b.number; }); - return { - gens, - pathExists(profile) - ? parseName(profileName, readLink(profile)) - : std::nullopt - }; + return {gens, pathExists(profile) + ? parseName(profileName, readLink(profile)) + : std::nullopt}; } - -static void makeName(const Path & profile, GenerationNumber num, - Path & outLink) +static void makeName(const Path & profile, GenerationNumber num, Path & outLink) { Path prefix = (format("%1%-%2%") % profile % num).str(); outLink = prefix + "-link"; } - Path createGeneration(ref store, Path profile, StorePath outPath) { /* The new generation number should be higher than old the @@ -107,14 +98,12 @@ Path createGeneration(ref store, Path profile, StorePath outPath) return generation; } - static void removeFile(const Path & path) { if (remove(path.c_str()) == -1) throw SysError("cannot unlink '%1%'", path); } - void deleteGeneration(const Path & profile, GenerationNumber gen) { Path generation; @@ -122,8 +111,8 @@ void deleteGeneration(const Path & profile, GenerationNumber gen) removeFile(generation); } - -static void deleteGeneration2(const Path & profile, GenerationNumber gen, bool dryRun) +static void deleteGeneration2(const Path & profile, GenerationNumber gen, + bool dryRun) { if (dryRun) notice("would remove profile version %1%", gen); @@ -133,8 +122,9 @@ static void deleteGeneration2(const Path & profile, GenerationNumber gen, bool d } } - -void deleteGenerations(const Path & profile, const std::set & gensToDelete, bool dryRun) +void deleteGenerations(const Path & profile, + const std::set & gensToDelete, + bool dryRun) { PathLocks lock; lockProfile(lock, profile); @@ -145,12 +135,14 @@ void deleteGenerations(const Path & profile, const std::set & throw Error("cannot delete current version of profile %1%'", profile); for (auto & i : gens) { - if (!gensToDelete.count(i.number)) continue; + if (!gensToDelete.count(i.number)) + continue; deleteGeneration2(profile, i.number, dryRun); } } -void deleteGenerationsGreaterThan(const Path & profile, GenerationNumber max, bool dryRun) +void deleteGenerationsGreaterThan(const Path & profile, GenerationNumber max, + bool dryRun) { PathLocks lock; lockProfile(lock, profile); @@ -185,7 +177,6 @@ void deleteOldGenerations(const Path & profile, bool dryRun) deleteGeneration2(profile, i.number, dryRun); } - void deleteGenerationsOlderThan(const Path & profile, time_t t, bool dryRun) { PathLocks lock; @@ -208,11 +199,13 @@ void deleteGenerationsOlderThan(const Path & profile, time_t t, bool dryRun) } } - -void deleteGenerationsOlderThan(const Path & profile, std::string_view timeSpec, bool dryRun) +void deleteGenerationsOlderThan(const Path & profile, std::string_view timeSpec, + bool dryRun) { if (timeSpec.empty() || timeSpec[timeSpec.size() - 1] != 'd') - throw UsageError("invalid number of days specifier '%1%', expected something like '14d'", timeSpec); + throw UsageError("invalid number of days specifier '%1%', expected " + "something like '14d'", + timeSpec); time_t curTime = time(0); auto strDays = timeSpec.substr(0, timeSpec.size() - 1); @@ -226,20 +219,17 @@ void deleteGenerationsOlderThan(const Path & profile, std::string_view timeSpec, deleteGenerationsOlderThan(profile, oldTime, dryRun); } - void switchLink(Path link, Path target) { /* Hacky. */ - if (dirOf(target) == dirOf(link)) target = baseNameOf(target); + if (dirOf(target) == dirOf(link)) + target = baseNameOf(target); replaceSymlink(target, link); } - -void switchGeneration( - const Path & profile, - std::optional dstGen, - bool dryRun) +void switchGeneration(const Path & profile, + std::optional dstGen, bool dryRun) { PathLocks lock; lockProfile(lock, profile); @@ -248,48 +238,50 @@ void switchGeneration( std::optional dst; for (auto & i : gens) - if ((!dstGen && i.number < curGen) || - (dstGen && i.number == *dstGen)) + if ((!dstGen && i.number < curGen) || (dstGen && i.number == *dstGen)) dst = i; if (!dst) { if (dstGen) throw Error("profile version %1% does not exist", *dstGen); else - throw Error("no profile version older than the current (%1%) exists", curGen.value_or(0)); + throw Error( + "no profile version older than the current (%1%) exists", + curGen.value_or(0)); } - notice("switching profile from version %d to %d", curGen.value_or(0), dst->number); + notice("switching profile from version %d to %d", curGen.value_or(0), + dst->number); - if (dryRun) return; + if (dryRun) + return; switchLink(profile, dst->path); } - void lockProfile(PathLocks & lock, const Path & profile) { - lock.lockPaths({profile}, (format("waiting for lock on profile '%1%'") % profile).str()); + lock.lockPaths( + {profile}, + (format("waiting for lock on profile '%1%'") % profile).str()); lock.setDeletion(true); } - std::string optimisticLockProfile(const Path & profile) { return pathExists(profile) ? readLink(profile) : ""; } - Path getDefaultProfile() { Path profileLink = getHome() + "/.nix-profile"; try { if (!pathExists(profileLink)) { - replaceSymlink( - getuid() == 0 - ? settings.nixStateDir + "/profiles/default" - : fmt("%s/profiles/per-user/%s/profile", settings.nixStateDir, getUserName()), - profileLink); + replaceSymlink(getuid() == 0 + ? settings.nixStateDir + "/profiles/default" + : fmt("%s/profiles/per-user/%s/profile", + settings.nixStateDir, getUserName()), + profileLink); } return absPath(readLink(profileLink), dirOf(profileLink)); } catch (Error &) { @@ -297,5 +289,4 @@ Path getDefaultProfile() } } - } diff --git a/src/libstore/profiles.hh b/src/libstore/profiles.hh index 408ca039cfbd..21ecae8f9b98 100644 --- a/src/libstore/profiles.hh +++ b/src/libstore/profiles.hh @@ -5,16 +5,13 @@ #include - namespace nix { class StorePath; - typedef uint64_t GenerationNumber; -struct Generation -{ +struct Generation { GenerationNumber number; Path path; time_t creationTime; @@ -22,11 +19,11 @@ struct Generation typedef std::list Generations; - /* Returns the list of currently present generations for the specified profile, sorted by generation number. Also returns the number of the current generation. */ -std::pair> findGenerations(Path profile); +std::pair> +findGenerations(Path profile); class LocalFSStore; @@ -34,24 +31,26 @@ Path createGeneration(ref store, Path profile, StorePath outPath); void deleteGeneration(const Path & profile, GenerationNumber gen); -void deleteGenerations(const Path & profile, const std::set & gensToDelete, bool dryRun); +void deleteGenerations(const Path & profile, + const std::set & gensToDelete, + bool dryRun); -void deleteGenerationsGreaterThan(const Path & profile, GenerationNumber max, bool dryRun); +void deleteGenerationsGreaterThan(const Path & profile, GenerationNumber max, + bool dryRun); void deleteOldGenerations(const Path & profile, bool dryRun); void deleteGenerationsOlderThan(const Path & profile, time_t t, bool dryRun); -void deleteGenerationsOlderThan(const Path & profile, std::string_view timeSpec, bool dryRun); +void deleteGenerationsOlderThan(const Path & profile, std::string_view timeSpec, + bool dryRun); void switchLink(Path link, Path target); /* Roll back a profile to the specified generation, or to the most recent one older than the current. */ -void switchGeneration( - const Path & profile, - std::optional dstGen, - bool dryRun); +void switchGeneration(const Path & profile, + std::optional dstGen, bool dryRun); /* Ensure exclusive access to a profile. Any command that modifies the profile first acquires this lock. */ diff --git a/src/libstore/realisation.cc b/src/libstore/realisation.cc index d63ec5ea2a71..b6b62b77e0d5 100644 --- a/src/libstore/realisation.cc +++ b/src/libstore/realisation.cc @@ -7,46 +7,52 @@ namespace nix { MakeError(InvalidDerivationOutputId, Error); -DrvOutput DrvOutput::parse(const std::string &strRep) { +DrvOutput DrvOutput::parse(const std::string & strRep) +{ size_t n = strRep.find("!"); if (n == strRep.npos) - throw InvalidDerivationOutputId("Invalid derivation output id %s", strRep); + throw InvalidDerivationOutputId("Invalid derivation output id %s", + strRep); return DrvOutput{ .drvHash = Hash::parseAnyPrefixed(strRep.substr(0, n)), - .outputName = strRep.substr(n+1), + .outputName = strRep.substr(n + 1), }; } -std::string DrvOutput::to_string() const { +std::string DrvOutput::to_string() const +{ return strHash() + "!" + outputName; } -std::set Realisation::closure(Store & store, const std::set & startOutputs) +std::set +Realisation::closure(Store & store, const std::set & startOutputs) { std::set res; Realisation::closure(store, startOutputs, res); return res; } -void Realisation::closure(Store & store, const std::set & startOutputs, std::set & res) +void Realisation::closure(Store & store, + const std::set & startOutputs, + std::set & res) { - auto getDeps = [&](const Realisation& current) -> std::set { + auto getDeps = [&](const Realisation & current) -> std::set { std::set res; - for (auto& [currentDep, _] : current.dependentRealisations) { + for (auto & [currentDep, _] : current.dependentRealisations) { if (auto currentRealisation = store.queryRealisation(currentDep)) res.insert(*currentRealisation); else - throw Error( - "Unrealised derivation '%s'", currentDep.to_string()); + throw Error("Unrealised derivation '%s'", + currentDep.to_string()); } return res; }; computeClosure( startOutputs, res, - [&](const Realisation& current, - std::function>&)> + [&](const Realisation & current, + std::function> &)> processEdges) { std::promise> promise; try { @@ -59,10 +65,12 @@ void Realisation::closure(Store & store, const std::set & startOutp }); } -nlohmann::json Realisation::toJSON() const { +nlohmann::json Realisation::toJSON() const +{ auto jsonDependentRealisations = nlohmann::json::object(); for (auto & [depId, depOutPath] : dependentRealisations) - jsonDependentRealisations.emplace(depId.to_string(), depOutPath.to_string()); + jsonDependentRealisations.emplace(depId.to_string(), + depOutPath.to_string()); return nlohmann::json{ {"id", id.to_string()}, {"outPath", outPath.to_string()}, @@ -71,10 +79,11 @@ nlohmann::json Realisation::toJSON() const { }; } -Realisation Realisation::fromJSON( - const nlohmann::json& json, - const std::string& whence) { - auto getOptionalField = [&](std::string fieldName) -> std::optional { +Realisation Realisation::fromJSON(const nlohmann::json & json, + const std::string & whence) +{ + auto getOptionalField = + [&](std::string fieldName) -> std::optional { auto fieldIterator = json.find(fieldName); if (fieldIterator == json.end()) return std::nullopt; @@ -90,13 +99,18 @@ Realisation Realisation::fromJSON( }; StringSet signatures; - if (auto signaturesIterator = json.find("signatures"); signaturesIterator != json.end()) - signatures.insert(signaturesIterator->begin(), signaturesIterator->end()); - - std::map dependentRealisations; - if (auto jsonDependencies = json.find("dependentRealisations"); jsonDependencies != json.end()) - for (auto & [jsonDepId, jsonDepOutPath] : jsonDependencies->get>()) - dependentRealisations.insert({DrvOutput::parse(jsonDepId), StorePath(jsonDepOutPath)}); + if (auto signaturesIterator = json.find("signatures"); + signaturesIterator != json.end()) + signatures.insert(signaturesIterator->begin(), + signaturesIterator->end()); + + std::map dependentRealisations; + if (auto jsonDependencies = json.find("dependentRealisations"); + jsonDependencies != json.end()) + for (auto & [jsonDepId, jsonDepOutPath] : + jsonDependencies->get>()) + dependentRealisations.insert( + {DrvOutput::parse(jsonDepId), StorePath(jsonDepOutPath)}); return Realisation{ .id = DrvOutput::parse(getField("id")), @@ -118,7 +132,8 @@ void Realisation::sign(const SecretKey & secretKey) signatures.insert(secretKey.signDetached(fingerprint())); } -bool Realisation::checkSignature(const PublicKeys & publicKeys, const std::string & sig) const +bool Realisation::checkSignature(const PublicKeys & publicKeys, + const std::string & sig) const { return verifyDetached(fingerprint(), sig, publicKeys); } @@ -136,20 +151,21 @@ size_t Realisation::checkSignatures(const PublicKeys & publicKeys) const return good; } -StorePath RealisedPath::path() const { +StorePath RealisedPath::path() const +{ return std::visit([](auto && arg) { return arg.getPath(); }, raw); } bool Realisation::isCompatibleWith(const Realisation & other) const { - assert (id == other.id); + assert(id == other.id); if (outPath == other.outPath) { - if (dependentRealisations.empty() != other.dependentRealisations.empty()) { - warn( - "Encountered a realisation for '%s' with an empty set of " - "dependencies. This is likely an artifact from an older Nix. " - "I’ll try to fix the realisation if I can", - id.to_string()); + if (dependentRealisations.empty() != + other.dependentRealisations.empty()) { + warn("Encountered a realisation for '%s' with an empty set of " + "dependencies. This is likely an artifact from an older Nix. " + "I’ll try to fix the realisation if I can", + id.to_string()); return true; } else if (dependentRealisations == other.dependentRealisations) { return true; @@ -158,27 +174,25 @@ bool Realisation::isCompatibleWith(const Realisation & other) const return false; } -void RealisedPath::closure( - Store& store, - const RealisedPath::Set& startPaths, - RealisedPath::Set& ret) +void RealisedPath::closure(Store & store, const RealisedPath::Set & startPaths, + RealisedPath::Set & ret) { // FIXME: This only builds the store-path closure, not the real realisation // closure StorePathSet initialStorePaths, pathsClosure; - for (auto& path : startPaths) + for (auto & path : startPaths) initialStorePaths.insert(path.path()); store.computeFSClosure(initialStorePaths, pathsClosure); ret.insert(startPaths.begin(), startPaths.end()); ret.insert(pathsClosure.begin(), pathsClosure.end()); } -void RealisedPath::closure(Store& store, RealisedPath::Set & ret) const +void RealisedPath::closure(Store & store, RealisedPath::Set & ret) const { RealisedPath::closure(store, {*this}, ret); } -RealisedPath::Set RealisedPath::closure(Store& store) const +RealisedPath::Set RealisedPath::closure(Store & store) const { RealisedPath::Set ret; closure(store, ret); diff --git a/src/libstore/realisation.hh b/src/libstore/realisation.hh index 9070a6ee21d4..4cfb7ce0c55b 100644 --- a/src/libstore/realisation.hh +++ b/src/libstore/realisation.hh @@ -14,8 +14,7 @@ struct DrvOutput { std::string to_string() const; - std::string strHash() const - { return drvHash.to_string(Base16, true); } + std::string strHash() const { return drvHash.to_string(Base16, true); } static DrvOutput parse(const std::string &); @@ -37,15 +36,19 @@ struct Realisation { std::map dependentRealisations; nlohmann::json toJSON() const; - static Realisation fromJSON(const nlohmann::json& json, const std::string& whence); + static Realisation fromJSON(const nlohmann::json & json, + const std::string & whence); std::string fingerprint() const; void sign(const SecretKey &); - bool checkSignature(const PublicKeys & publicKeys, const std::string & sig) const; + bool checkSignature(const PublicKeys & publicKeys, + const std::string & sig) const; size_t checkSignatures(const PublicKeys & publicKeys) const; - static std::set closure(Store &, const std::set &); - static void closure(Store &, const std::set &, std::set & res); + static std::set closure(Store &, + const std::set &); + static void closure(Store &, const std::set &, + std::set & res); bool isCompatibleWith(const Realisation & other) const; @@ -64,7 +67,6 @@ struct OpaquePath { GENERATE_CMP(OpaquePath, me->path); }; - /** * A store path with all the history of how it went into the store */ @@ -86,9 +88,9 @@ struct RealisedPath { */ StorePath path() const; - void closure(Store& store, Set& ret) const; - static void closure(Store& store, const Set& startPaths, Set& ret); - Set closure(Store& store) const; + void closure(Store & store, Set & ret) const; + static void closure(Store & store, const Set & startPaths, Set & ret); + Set closure(Store & store) const; GENERATE_CMP(RealisedPath, me->raw); }; diff --git a/src/libstore/references.cc b/src/libstore/references.cc index 34dce092cb2f..3f2621e46b89 100644 --- a/src/libstore/references.cc +++ b/src/libstore/references.cc @@ -7,27 +7,22 @@ #include #include - namespace nix { - static size_t refLength = 32; /* characters */ - -static void search( - std::string_view s, - StringSet & hashes, - StringSet & seen) +static void search(std::string_view s, StringSet & hashes, StringSet & seen) { static std::once_flag initialised; static bool isBase32[256]; - std::call_once(initialised, [](){ - for (unsigned int i = 0; i < 256; ++i) isBase32[i] = false; + std::call_once(initialised, []() { + for (unsigned int i = 0; i < 256; ++i) + isBase32[i] = false; for (unsigned int i = 0; i < base32Chars.size(); ++i) isBase32[(unsigned char) base32Chars[i]] = true; }); - for (size_t i = 0; i + refLength <= s.size(); ) { + for (size_t i = 0; i + refLength <= s.size();) { int j; bool match = true; for (j = refLength - 1; j >= 0; --j) @@ -36,19 +31,18 @@ static void search( match = false; break; } - if (!match) continue; + if (!match) + continue; std::string ref(s.substr(i, refLength)); if (hashes.erase(ref)) { - debug(format("found reference to '%1%' at offset '%2%'") - % ref % i); + debug(format("found reference to '%1%' at offset '%2%'") % ref % i); seen.insert(ref); } ++i; } } - -void RefScanSink::operator () (std::string_view data) +void RefScanSink::operator()(std::string_view data) { /* It's possible that a reference spans the previous and current fragment, so search in the concatenation of the tail of the @@ -66,21 +60,17 @@ void RefScanSink::operator () (std::string_view data) tail.append(data.data() + data.size() - tailLen, tailLen); } - -std::pair scanForReferences( - const std::string & path, - const StorePathSet & refs) +std::pair scanForReferences(const std::string & path, + const StorePathSet & refs) { - HashSink hashSink { htSHA256 }; + HashSink hashSink{htSHA256}; auto found = scanForReferences(hashSink, path, refs); auto hash = hashSink.finish(); return std::pair(found, hash); } -StorePathSet scanForReferences( - Sink & toTee, - const Path & path, - const StorePathSet & refs) +StorePathSet scanForReferences(Sink & toTee, const Path & path, + const StorePathSet & refs) { StringSet hashes; std::map backMap; @@ -94,7 +84,7 @@ StorePathSet scanForReferences( /* Look for the hashes in the NAR dump of the path. */ RefScanSink refsSink(std::move(hashes)); - TeeSink sink { refsSink, toTee }; + TeeSink sink{refsSink, toTee}; dumpPath(path, sink); /* Map the hashes found back to their store paths. */ @@ -108,14 +98,14 @@ StorePathSet scanForReferences( return found; } - -RewritingSink::RewritingSink(const std::string & from, const std::string & to, Sink & nextSink) +RewritingSink::RewritingSink(const std::string & from, const std::string & to, + Sink & nextSink) : from(from), to(to), nextSink(nextSink) { assert(from.size() == to.size()); } -void RewritingSink::operator () (std::string_view data) +void RewritingSink::operator()(std::string_view data) { std::string s(prev); s.append(data); @@ -126,33 +116,34 @@ void RewritingSink::operator () (std::string_view data) s.replace(j, from.size(), to); } - prev = s.size() < from.size() ? s : std::string(s, s.size() - from.size() + 1, from.size() - 1); + prev = s.size() < from.size() + ? s + : std::string(s, s.size() - from.size() + 1, from.size() - 1); auto consumed = s.size() - prev.size(); pos += consumed; - if (consumed) nextSink(s.substr(0, consumed)); + if (consumed) + nextSink(s.substr(0, consumed)); } void RewritingSink::flush() { - if (prev.empty()) return; + if (prev.empty()) + return; pos += prev.size(); nextSink(prev); prev.clear(); } HashModuloSink::HashModuloSink(HashType ht, const std::string & modulus) - : hashSink(ht) - , rewritingSink(modulus, std::string(modulus.size(), 0), hashSink) + : hashSink(ht), + rewritingSink(modulus, std::string(modulus.size(), 0), hashSink) { } -void HashModuloSink::operator () (std::string_view data) -{ - rewritingSink(data); -} +void HashModuloSink::operator()(std::string_view data) { rewritingSink(data); } HashResult HashModuloSink::finish() { diff --git a/src/libstore/references.hh b/src/libstore/references.hh index a6119c861904..c5060b8a5a2e 100644 --- a/src/libstore/references.hh +++ b/src/libstore/references.hh @@ -5,51 +5,48 @@ namespace nix { -std::pair scanForReferences(const Path & path, const StorePathSet & refs); +std::pair +scanForReferences(const Path & path, const StorePathSet & refs); -StorePathSet scanForReferences(Sink & toTee, const Path & path, const StorePathSet & refs); +StorePathSet scanForReferences(Sink & toTee, const Path & path, + const StorePathSet & refs); -class RefScanSink : public Sink -{ +class RefScanSink : public Sink { StringSet hashes; StringSet seen; std::string tail; -public: + public: + RefScanSink(StringSet && hashes) : hashes(hashes) {} - RefScanSink(StringSet && hashes) : hashes(hashes) - { } + StringSet & getResult() { return seen; } - StringSet & getResult() - { return seen; } - - void operator () (std::string_view data) override; + void operator()(std::string_view data) override; }; -struct RewritingSink : Sink -{ +struct RewritingSink : Sink { std::string from, to, prev; Sink & nextSink; uint64_t pos = 0; std::vector matches; - RewritingSink(const std::string & from, const std::string & to, Sink & nextSink); + RewritingSink(const std::string & from, const std::string & to, + Sink & nextSink); - void operator () (std::string_view data) override; + void operator()(std::string_view data) override; void flush(); }; -struct HashModuloSink : AbstractHashSink -{ +struct HashModuloSink : AbstractHashSink { HashSink hashSink; RewritingSink rewritingSink; HashModuloSink(HashType ht, const std::string & modulus); - void operator () (std::string_view data) override; + void operator()(std::string_view data) override; HashResult finish() override; }; diff --git a/src/libstore/remote-fs-accessor.cc b/src/libstore/remote-fs-accessor.cc index 0ce335646756..d6213a620cd2 100644 --- a/src/libstore/remote-fs-accessor.cc +++ b/src/libstore/remote-fs-accessor.cc @@ -9,20 +9,21 @@ namespace nix { RemoteFSAccessor::RemoteFSAccessor(ref store, const Path & cacheDir) - : store(store) - , cacheDir(cacheDir) + : store(store), cacheDir(cacheDir) { if (cacheDir != "") createDirs(cacheDir); } -Path RemoteFSAccessor::makeCacheFile(std::string_view hashPart, const std::string & ext) +Path RemoteFSAccessor::makeCacheFile(std::string_view hashPart, + const std::string & ext) { assert(cacheDir != ""); return fmt("%s/%s.%s", cacheDir, hashPart, ext); } -ref RemoteFSAccessor::addToCache(std::string_view hashPart, std::string && nar) +ref RemoteFSAccessor::addToCache(std::string_view hashPart, + std::string && nar) { if (cacheDir != "") { try { @@ -50,32 +51,37 @@ ref RemoteFSAccessor::addToCache(std::string_view hashPart, std::str return narAccessor; } -std::pair, Path> RemoteFSAccessor::fetch(const Path & path_, bool requireValidPath) +std::pair, Path> RemoteFSAccessor::fetch(const Path & path_, + bool requireValidPath) { auto path = canonPath(path_); auto [storePath, restPath] = store->toStorePath(path); if (requireValidPath && !store->isValidPath(storePath)) - throw InvalidPath("path '%1%' is not a valid store path", store->printStorePath(storePath)); + throw InvalidPath("path '%1%' is not a valid store path", + store->printStorePath(storePath)); auto i = nars.find(std::string(storePath.hashPart())); - if (i != nars.end()) return {i->second, restPath}; + if (i != nars.end()) + return {i->second, restPath}; std::string listing; Path cacheFile; - if (cacheDir != "" && pathExists(cacheFile = makeCacheFile(storePath.hashPart(), "nar"))) { + if (cacheDir != "" && + pathExists(cacheFile = makeCacheFile(storePath.hashPart(), "nar"))) { try { listing = nix::readFile(makeCacheFile(storePath.hashPart(), "ls")); - auto narAccessor = makeLazyNarAccessor(listing, - [cacheFile](uint64_t offset, uint64_t length) { - - AutoCloseFD fd = open(cacheFile.c_str(), O_RDONLY | O_CLOEXEC); + auto narAccessor = makeLazyNarAccessor( + listing, [cacheFile](uint64_t offset, uint64_t length) { + AutoCloseFD fd = + open(cacheFile.c_str(), O_RDONLY | O_CLOEXEC); if (!fd) - throw SysError("opening NAR cache file '%s'", cacheFile); + throw SysError("opening NAR cache file '%s'", + cacheFile); if (lseek(fd.get(), offset, SEEK_SET) != (off_t) offset) throw SysError("seeking in '%s'", cacheFile); @@ -89,13 +95,15 @@ std::pair, Path> RemoteFSAccessor::fetch(const Path & path_, boo nars.emplace(storePath.hashPart(), narAccessor); return {narAccessor, restPath}; - } catch (SysError &) { } + } catch (SysError &) { + } try { auto narAccessor = makeNarAccessor(nix::readFile(cacheFile)); nars.emplace(storePath.hashPart(), narAccessor); return {narAccessor, restPath}; - } catch (SysError &) { } + } catch (SysError &) { + } } StringSink sink; diff --git a/src/libstore/remote-fs-accessor.hh b/src/libstore/remote-fs-accessor.hh index 99f5544ef08a..c87542ef5cdf 100644 --- a/src/libstore/remote-fs-accessor.hh +++ b/src/libstore/remote-fs-accessor.hh @@ -6,15 +6,15 @@ namespace nix { -class RemoteFSAccessor : public FSAccessor -{ +class RemoteFSAccessor : public FSAccessor { ref store; std::map> nars; Path cacheDir; - std::pair, Path> fetch(const Path & path_, bool requireValidPath = true); + std::pair, Path> fetch(const Path & path_, + bool requireValidPath = true); friend class BinaryCacheStore; @@ -22,16 +22,16 @@ class RemoteFSAccessor : public FSAccessor ref addToCache(std::string_view hashPart, std::string && nar); -public: - + public: RemoteFSAccessor(ref store, - const /* FIXME: use std::optional */ Path & cacheDir = ""); + const /* FIXME: use std::optional */ Path & cacheDir = ""); Stat stat(const Path & path) override; StringSet readDirectory(const Path & path) override; - std::string readFile(const Path & path, bool requireValidPath = true) override; + std::string readFile(const Path & path, + bool requireValidPath = true) override; std::string readLink(const Path & path) override; }; diff --git a/src/libstore/remote-store.cc b/src/libstore/remote-store.cc index bc36aef5d0fa..3dfc3c439614 100644 --- a/src/libstore/remote-store.cc +++ b/src/libstore/remote-store.cc @@ -30,7 +30,6 @@ void write(const Store & store, Sink & out, const std::string & str) out << str; } - StorePath read(const Store & store, Source & from, Phantom _) { return store.parseStorePath(readString(from)); @@ -41,8 +40,8 @@ void write(const Store & store, Sink & out, const StorePath & storePath) out << store.printStorePath(storePath); } - -ContentAddress read(const Store & store, Source & from, Phantom _) +ContentAddress read(const Store & store, Source & from, + Phantom _) { return parseContentAddress(readString(from)); } @@ -52,7 +51,6 @@ void write(const Store & store, Sink & out, const ContentAddress & ca) out << renderContentAddress(ca); } - DerivedPath read(const Store & store, Source & from, Phantom _) { auto s = readString(from); @@ -64,14 +62,11 @@ void write(const Store & store, Sink & out, const DerivedPath & req) out << req.to_string(store); } - Realisation read(const Store & store, Source & from, Phantom _) { std::string rawInput = readString(from); - return Realisation::fromJSON( - nlohmann::json::parse(rawInput), - "remote-protocol" - ); + return Realisation::fromJSON(nlohmann::json::parse(rawInput), + "remote-protocol"); } void write(const Store & store, Sink & out, const Realisation & realisation) @@ -79,7 +74,6 @@ void write(const Store & store, Sink & out, const Realisation & realisation) out << realisation.toJSON().dump(); } - DrvOutput read(const Store & store, Source & from, Phantom _) { return DrvOutput::parse(readString(from)); @@ -90,93 +84,83 @@ void write(const Store & store, Sink & out, const DrvOutput & drvOutput) out << drvOutput.to_string(); } - BuildResult read(const Store & store, Source & from, Phantom _) { - auto path = worker_proto::read(store, from, Phantom {}); - BuildResult res { .path = path }; + auto path = worker_proto::read(store, from, Phantom{}); + BuildResult res{.path = path}; res.status = (BuildResult::Status) readInt(from); - from - >> res.errorMsg - >> res.timesBuilt - >> res.isNonDeterministic - >> res.startTime - >> res.stopTime; - res.builtOutputs = worker_proto::read(store, from, Phantom {}); + from >> res.errorMsg >> res.timesBuilt >> res.isNonDeterministic >> + res.startTime >> res.stopTime; + res.builtOutputs = worker_proto::read(store, from, Phantom{}); return res; } void write(const Store & store, Sink & to, const BuildResult & res) { worker_proto::write(store, to, res.path); - to - << res.status - << res.errorMsg - << res.timesBuilt - << res.isNonDeterministic - << res.startTime - << res.stopTime; + to << res.status << res.errorMsg << res.timesBuilt << res.isNonDeterministic + << res.startTime << res.stopTime; worker_proto::write(store, to, res.builtOutputs); } - -std::optional read(const Store & store, Source & from, Phantom> _) +std::optional read(const Store & store, Source & from, + Phantom> _) { auto s = readString(from); - return s == "" ? std::optional {} : store.parseStorePath(s); + return s == "" ? std::optional{} : store.parseStorePath(s); } -void write(const Store & store, Sink & out, const std::optional & storePathOpt) +void write(const Store & store, Sink & out, + const std::optional & storePathOpt) { out << (storePathOpt ? store.printStorePath(*storePathOpt) : ""); } - -std::optional read(const Store & store, Source & from, Phantom> _) +std::optional read(const Store & store, Source & from, + Phantom> _) { return parseContentAddressOpt(readString(from)); } -void write(const Store & store, Sink & out, const std::optional & caOpt) +void write(const Store & store, Sink & out, + const std::optional & caOpt) { out << (caOpt ? renderContentAddress(*caOpt) : ""); } } - -/* TODO: Separate these store impls into different files, give them better names */ +/* TODO: Separate these store impls into different files, give them better names + */ RemoteStore::RemoteStore(const Params & params) - : RemoteStoreConfig(params) - , Store(params) - , connections(make_ref>( - std::max(1, (int) maxConnections), - [this]() { - auto conn = openConnectionWrapper(); - try { - initConnection(*conn); - } catch (...) { - failed = true; - throw; - } - return conn; - }, - [this](const ref & r) { - return - r->to.good() - && r->from.good() - && std::chrono::duration_cast( - std::chrono::steady_clock::now() - r->startTime).count() < maxConnectionAge; - } - )) + : RemoteStoreConfig(params), Store(params), + connections(make_ref>( + std::max(1, (int) maxConnections), + [this]() { + auto conn = openConnectionWrapper(); + try { + initConnection(*conn); + } catch (...) { + failed = true; + throw; + } + return conn; + }, + [this](const ref & r) { + return r->to.good() && r->from.good() && + std::chrono::duration_cast( + std::chrono::steady_clock::now() - r->startTime) + .count() < maxConnectionAge; + })) { } - ref RemoteStore::openConnectionWrapper() { if (failed) - throw Error("opening a connection to remote store '%s' previously failed", getUri()); + throw Error( + "opening a connection to remote store '%s' previously failed", + getUri()); try { return openConnection(); } catch (...) { @@ -185,7 +169,6 @@ ref RemoteStore::openConnectionWrapper() } } - void RemoteStore::initConnection(Connection & conn) { /* Send the magic greeting, check for the reply. */ @@ -207,7 +190,8 @@ void RemoteStore::initConnection(Connection & conn) } conn.from >> conn.daemonVersion; - if (GET_PROTOCOL_MAJOR(conn.daemonVersion) != GET_PROTOCOL_MAJOR(PROTOCOL_VERSION)) + if (GET_PROTOCOL_MAJOR(conn.daemonVersion) != + GET_PROTOCOL_MAJOR(PROTOCOL_VERSION)) throw Error("Nix daemon protocol version not supported"); if (GET_PROTOCOL_MINOR(conn.daemonVersion) < 10) throw Error("the Nix daemon version is too old"); @@ -227,31 +211,25 @@ void RemoteStore::initConnection(Connection & conn) } auto ex = conn.processStderr(); - if (ex) std::rethrow_exception(ex); - } - catch (Error & e) { - throw Error("cannot open connection to remote store '%s': %s", getUri(), e.what()); + if (ex) + std::rethrow_exception(ex); + } catch (Error & e) { + throw Error("cannot open connection to remote store '%s': %s", getUri(), + e.what()); } setOptions(conn); } - void RemoteStore::setOptions(Connection & conn) { - conn.to << wopSetOptions - << settings.keepFailed - << settings.keepGoing - << settings.tryFallback - << verbosity - << settings.maxBuildJobs - << settings.maxSilentTime - << true - << (settings.verboseBuild ? lvlError : lvlVomit) - << 0 // obsolete log type - << 0 /* obsolete print build trace */ - << settings.buildCores - << settings.useSubstitutes; + conn.to << wopSetOptions << settings.keepFailed << settings.keepGoing + << settings.tryFallback << verbosity << settings.maxBuildJobs + << settings.maxSilentTime << true + << (settings.verboseBuild ? lvlError : lvlVomit) + << 0 // obsolete log type + << 0 /* obsolete print build trace */ + << settings.buildCores << settings.useSubstitutes; if (GET_PROTOCOL_MINOR(conn.daemonVersion) >= 12) { std::map overrides; @@ -272,27 +250,25 @@ void RemoteStore::setOptions(Connection & conn) } auto ex = conn.processStderr(); - if (ex) std::rethrow_exception(ex); + if (ex) + std::rethrow_exception(ex); } - /* A wrapper around Pool::Handle that marks the connection as bad (causing it to be closed) if a non-daemon exception is thrown before the handle is closed. Such an exception causes a deviation from the expected protocol and therefore a desynchronization between the client and daemon. */ -struct ConnectionHandle -{ +struct ConnectionHandle { Pool::Handle handle; bool daemonException = false; ConnectionHandle(Pool::Handle && handle) : handle(std::move(handle)) - { } + { + } - ConnectionHandle(ConnectionHandle && h) - : handle(std::move(h.handle)) - { } + ConnectionHandle(ConnectionHandle && h) : handle(std::move(h.handle)) {} ~ConnectionHandle() { @@ -302,7 +278,7 @@ struct ConnectionHandle } } - RemoteStore::Connection * operator -> () { return &*handle; } + RemoteStore::Connection * operator->() { return &*handle; } void processStderr(Sink * sink = 0, Source * source = 0, bool flush = true) { @@ -316,16 +292,12 @@ struct ConnectionHandle void withFramedSink(std::function fun); }; - ConnectionHandle RemoteStore::getConnection() { return ConnectionHandle(connections->get()); } -void RemoteStore::setOptions() -{ - setOptions(*(getConnection().handle)); -} +void RemoteStore::setOptions() { setOptions(*(getConnection().handle)); } bool RemoteStore::isValidPathUncached(const StorePath & path) { @@ -335,14 +307,15 @@ bool RemoteStore::isValidPathUncached(const StorePath & path) return readInt(conn->from); } - -StorePathSet RemoteStore::queryValidPaths(const StorePathSet & paths, SubstituteFlag maybeSubstitute) +StorePathSet RemoteStore::queryValidPaths(const StorePathSet & paths, + SubstituteFlag maybeSubstitute) { auto conn(getConnection()); if (GET_PROTOCOL_MINOR(conn->daemonVersion) < 12) { StorePathSet res; for (auto & i : paths) - if (isValidPath(i)) res.insert(i); + if (isValidPath(i)) + res.insert(i); return res; } else { conn->to << wopQueryValidPaths; @@ -351,20 +324,18 @@ StorePathSet RemoteStore::queryValidPaths(const StorePathSet & paths, Substitute conn->to << (settings.buildersUseSubstitutes ? 1 : 0); } conn.processStderr(); - return worker_proto::read(*this, conn->from, Phantom {}); + return worker_proto::read(*this, conn->from, Phantom{}); } } - StorePathSet RemoteStore::queryAllValidPaths() { auto conn(getConnection()); conn->to << wopQueryAllValidPaths; conn.processStderr(); - return worker_proto::read(*this, conn->from, Phantom {}); + return worker_proto::read(*this, conn->from, Phantom{}); } - StorePathSet RemoteStore::querySubstitutablePaths(const StorePathSet & paths) { auto conn(getConnection()); @@ -373,21 +344,23 @@ StorePathSet RemoteStore::querySubstitutablePaths(const StorePathSet & paths) for (auto & i : paths) { conn->to << wopHasSubstitutes << printStorePath(i); conn.processStderr(); - if (readInt(conn->from)) res.insert(i); + if (readInt(conn->from)) + res.insert(i); } return res; } else { conn->to << wopQuerySubstitutablePaths; worker_proto::write(*this, conn->to, paths); conn.processStderr(); - return worker_proto::read(*this, conn->from, Phantom {}); + return worker_proto::read(*this, conn->from, Phantom{}); } } - -void RemoteStore::querySubstitutablePathInfos(const StorePathCAMap & pathsMap, SubstitutablePathInfos & infos) +void RemoteStore::querySubstitutablePathInfos(const StorePathCAMap & pathsMap, + SubstitutablePathInfos & infos) { - if (pathsMap.empty()) return; + if (pathsMap.empty()) + return; auto conn(getConnection()); @@ -395,14 +368,17 @@ void RemoteStore::querySubstitutablePathInfos(const StorePathCAMap & pathsMap, S for (auto & i : pathsMap) { SubstitutablePathInfo info; - conn->to << wopQuerySubstitutablePathInfo << printStorePath(i.first); + conn->to << wopQuerySubstitutablePathInfo + << printStorePath(i.first); conn.processStderr(); unsigned int reply = readInt(conn->from); - if (reply == 0) continue; + if (reply == 0) + continue; auto deriver = readString(conn->from); if (deriver != "") info.deriver = parseStorePath(deriver); - info.references = worker_proto::read(*this, conn->from, Phantom {}); + info.references = + worker_proto::read(*this, conn->from, Phantom{}); info.downloadSize = readLongLong(conn->from); info.narSize = readLongLong(conn->from); infos.insert_or_assign(i.first, std::move(info)); @@ -421,20 +397,21 @@ void RemoteStore::querySubstitutablePathInfos(const StorePathCAMap & pathsMap, S conn.processStderr(); size_t count = readNum(conn->from); for (size_t n = 0; n < count; n++) { - SubstitutablePathInfo & info(infos[parseStorePath(readString(conn->from))]); + SubstitutablePathInfo & info( + infos[parseStorePath(readString(conn->from))]); auto deriver = readString(conn->from); if (deriver != "") info.deriver = parseStorePath(deriver); - info.references = worker_proto::read(*this, conn->from, Phantom {}); + info.references = + worker_proto::read(*this, conn->from, Phantom{}); info.downloadSize = readLongLong(conn->from); info.narSize = readLongLong(conn->from); } - } } - -void RemoteStore::queryPathInfoUncached(const StorePath & path, +void RemoteStore::queryPathInfoUncached( + const StorePath & path, Callback> callback) noexcept { try { @@ -451,37 +428,41 @@ void RemoteStore::queryPathInfoUncached(const StorePath & path, throw; } if (GET_PROTOCOL_MINOR(conn->daemonVersion) >= 17) { - bool valid; conn->from >> valid; - if (!valid) throw InvalidPath("path '%s' is not valid", printStorePath(path)); + bool valid; + conn->from >> valid; + if (!valid) + throw InvalidPath("path '%s' is not valid", + printStorePath(path)); } - info = std::make_shared( - ValidPathInfo::read(conn->from, *this, GET_PROTOCOL_MINOR(conn->daemonVersion), StorePath{path})); + info = std::make_shared(ValidPathInfo::read( + conn->from, *this, GET_PROTOCOL_MINOR(conn->daemonVersion), + StorePath{path})); } callback(std::move(info)); - } catch (...) { callback.rethrow(); } + } catch (...) { + callback.rethrow(); + } } - void RemoteStore::queryReferrers(const StorePath & path, - StorePathSet & referrers) + StorePathSet & referrers) { auto conn(getConnection()); conn->to << wopQueryReferrers << printStorePath(path); conn.processStderr(); - for (auto & i : worker_proto::read(*this, conn->from, Phantom {})) + for (auto & i : + worker_proto::read(*this, conn->from, Phantom{})) referrers.insert(i); } - StorePathSet RemoteStore::queryValidDerivers(const StorePath & path) { auto conn(getConnection()); conn->to << wopQueryValidDerivers << printStorePath(path); conn.processStderr(); - return worker_proto::read(*this, conn->from, Phantom {}); + return worker_proto::read(*this, conn->from, Phantom{}); } - StorePathSet RemoteStore::queryDerivationOutputs(const StorePath & path) { if (GET_PROTOCOL_MINOR(getProtocol()) >= 0x16) { @@ -490,17 +471,19 @@ StorePathSet RemoteStore::queryDerivationOutputs(const StorePath & path) auto conn(getConnection()); conn->to << wopQueryDerivationOutputs << printStorePath(path); conn.processStderr(); - return worker_proto::read(*this, conn->from, Phantom {}); + return worker_proto::read(*this, conn->from, Phantom{}); } - -std::map> RemoteStore::queryPartialDerivationOutputMap(const StorePath & path) +std::map> +RemoteStore::queryPartialDerivationOutputMap(const StorePath & path) { if (GET_PROTOCOL_MINOR(getProtocol()) >= 0x16) { auto conn(getConnection()); conn->to << wopQueryDerivationOutputMap << printStorePath(path); conn.processStderr(); - return worker_proto::read(*this, conn->from, Phantom>> {}); + return worker_proto::read( + *this, conn->from, + Phantom>>{}); } else { // Fallback for old daemon versions. // For floating-CA derivations (and their co-dependencies) this is an @@ -518,33 +501,30 @@ std::map> RemoteStore::queryPartialDerivat } } -std::optional RemoteStore::queryPathFromHashPart(const std::string & hashPart) +std::optional +RemoteStore::queryPathFromHashPart(const std::string & hashPart) { auto conn(getConnection()); conn->to << wopQueryPathFromHashPart << hashPart; conn.processStderr(); Path path = readString(conn->from); - if (path.empty()) return {}; + if (path.empty()) + return {}; return parseStorePath(path); } - -ref RemoteStore::addCAToStore( - Source & dump, - std::string_view name, - ContentAddressMethod caMethod, - const StorePathSet & references, - RepairFlag repair) +ref +RemoteStore::addCAToStore(Source & dump, std::string_view name, + ContentAddressMethod caMethod, + const StorePathSet & references, RepairFlag repair) { std::optional conn_(getConnection()); auto & conn = *conn_; if (GET_PROTOCOL_MINOR(conn->daemonVersion) >= 25) { - conn->to - << wopAddToStore - << name - << renderContentAddressMethod(caMethod); + conn->to << wopAddToStore << name + << renderContentAddressMethod(caMethod); worker_proto::write(*this, conn->to, references); conn->to << repair; @@ -552,59 +532,66 @@ ref RemoteStore::addCAToStore( connections->incCapacity(); { Finally cleanup([&]() { connections->decCapacity(); }); - conn.withFramedSink([&](Sink & sink) { - dump.drainInto(sink); - }); + conn.withFramedSink([&](Sink & sink) { dump.drainInto(sink); }); } - return make_ref( - ValidPathInfo::read(conn->from, *this, GET_PROTOCOL_MINOR(conn->daemonVersion))); - } - else { - if (repair) throw Error("repairing is not supported when building through the Nix daemon protocol < 1.25"); - - std::visit(overloaded { - [&](const TextHashMethod & thm) -> void { - std::string s = dump.drain(); - conn->to << wopAddTextToStore << name << s; - worker_proto::write(*this, conn->to, references); - conn.processStderr(); - }, - [&](const FixedOutputHashMethod & fohm) -> void { - conn->to - << wopAddToStore - << name - << ((fohm.hashType == htSHA256 && fohm.fileIngestionMethod == FileIngestionMethod::Recursive) ? 0 : 1) /* backwards compatibility hack */ - << (fohm.fileIngestionMethod == FileIngestionMethod::Recursive ? 1 : 0) - << printHashType(fohm.hashType); - - try { - conn->to.written = 0; - conn->to.warn = true; - connections->incCapacity(); - { - Finally cleanup([&]() { connections->decCapacity(); }); - if (fohm.fileIngestionMethod == FileIngestionMethod::Recursive) { - dump.drainInto(conn->to); - } else { - std::string contents = dump.drain(); - dumpString(contents, conn->to); - } - } - conn->to.warn = false; - conn.processStderr(); - } catch (SysError & e) { - /* Daemon closed while we were sending the path. Probably OOM - or I/O error. */ - if (e.errNo == EPIPE) - try { - conn.processStderr(); - } catch (EndOfFile & e) { } - throw; - } - - } - }, caMethod); + return make_ref(ValidPathInfo::read( + conn->from, *this, GET_PROTOCOL_MINOR(conn->daemonVersion))); + } else { + if (repair) + throw Error("repairing is not supported when building through the " + "Nix daemon protocol < 1.25"); + + std::visit( + overloaded{[&](const TextHashMethod & thm) -> void { + std::string s = dump.drain(); + conn->to << wopAddTextToStore << name << s; + worker_proto::write(*this, conn->to, references); + conn.processStderr(); + }, + [&](const FixedOutputHashMethod & fohm) -> void { + conn->to + << wopAddToStore << name + << ((fohm.hashType == htSHA256 && + fohm.fileIngestionMethod == + FileIngestionMethod::Recursive) + ? 0 + : 1) /* backwards compatibility hack */ + << (fohm.fileIngestionMethod == + FileIngestionMethod::Recursive + ? 1 + : 0) + << printHashType(fohm.hashType); + + try { + conn->to.written = 0; + conn->to.warn = true; + connections->incCapacity(); + { + Finally cleanup( + [&]() { connections->decCapacity(); }); + if (fohm.fileIngestionMethod == + FileIngestionMethod::Recursive) { + dump.drainInto(conn->to); + } else { + std::string contents = dump.drain(); + dumpString(contents, conn->to); + } + } + conn->to.warn = false; + conn.processStderr(); + } catch (SysError & e) { + /* Daemon closed while we were sending the path. + Probably OOM or I/O error. */ + if (e.errNo == EPIPE) + try { + conn.processStderr(); + } catch (EndOfFile & e) { + } + throw; + } + }}, + caMethod); auto path = parseStorePath(readString(conn->from)); // Release our connection to prevent a deadlock in queryPathInfo(). conn_.reset(); @@ -612,16 +599,20 @@ ref RemoteStore::addCAToStore( } } - StorePath RemoteStore::addToStoreFromDump(Source & dump, std::string_view name, - FileIngestionMethod method, HashType hashType, RepairFlag repair, const StorePathSet & references) + FileIngestionMethod method, + HashType hashType, RepairFlag repair, + const StorePathSet & references) { - return addCAToStore(dump, name, FixedOutputHashMethod{ .fileIngestionMethod = method, .hashType = hashType }, references, repair)->path; + return addCAToStore(dump, name, + FixedOutputHashMethod{.fileIngestionMethod = method, + .hashType = hashType}, + references, repair) + ->path; } - void RemoteStore::addToStore(const ValidPathInfo & info, Source & source, - RepairFlag repair, CheckSigsFlag checkSigs) + RepairFlag repair, CheckSigsFlag checkSigs) { auto conn(getConnection()); @@ -632,37 +623,32 @@ void RemoteStore::addToStore(const ValidPathInfo & info, Source & source, sink << 1 // == path follows ; copyNAR(source, sink); - sink - << exportMagic - << printStorePath(info.path); + sink << exportMagic << printStorePath(info.path); worker_proto::write(*this, sink, info.references); - sink - << (info.deriver ? printStorePath(*info.deriver) : "") - << 0 // == no legacy signature - << 0 // == no path follows + sink << (info.deriver ? printStorePath(*info.deriver) : "") + << 0 // == no legacy signature + << 0 // == no path follows ; }); conn.processStderr(0, source2.get()); - auto importedPaths = worker_proto::read(*this, conn->from, Phantom {}); + auto importedPaths = + worker_proto::read(*this, conn->from, Phantom{}); assert(importedPaths.size() <= 1); } else { - conn->to << wopAddToStoreNar - << printStorePath(info.path) + conn->to << wopAddToStoreNar << printStorePath(info.path) << (info.deriver ? printStorePath(*info.deriver) : "") << info.narHash.to_string(Base16, false); worker_proto::write(*this, conn->to, info.references); - conn->to << info.registrationTime << info.narSize - << info.ultimate << info.sigs << renderContentAddress(info.ca) - << repair << !checkSigs; + conn->to << info.registrationTime << info.narSize << info.ultimate + << info.sigs << renderContentAddress(info.ca) << repair + << !checkSigs; if (GET_PROTOCOL_MINOR(conn->daemonVersion) >= 23) { - conn.withFramedSink([&](Sink & sink) { - copyNAR(source, sink); - }); + conn.withFramedSink([&](Sink & sink) { copyNAR(source, sink); }); } else if (GET_PROTOCOL_MINOR(conn->daemonVersion) >= 21) { conn.processStderr(0, &source); } else { @@ -672,34 +658,24 @@ void RemoteStore::addToStore(const ValidPathInfo & info, Source & source, } } - -void RemoteStore::addMultipleToStore( - Source & source, - RepairFlag repair, - CheckSigsFlag checkSigs) +void RemoteStore::addMultipleToStore(Source & source, RepairFlag repair, + CheckSigsFlag checkSigs) { if (GET_PROTOCOL_MINOR(getConnection()->daemonVersion) >= 32) { auto conn(getConnection()); - conn->to - << wopAddMultipleToStore - << repair - << !checkSigs; - conn.withFramedSink([&](Sink & sink) { - source.drainInto(sink); - }); + conn->to << wopAddMultipleToStore << repair << !checkSigs; + conn.withFramedSink([&](Sink & sink) { source.drainInto(sink); }); } else Store::addMultipleToStore(source, repair, checkSigs); } - -StorePath RemoteStore::addTextToStore( - std::string_view name, - std::string_view s, - const StorePathSet & references, - RepairFlag repair) +StorePath RemoteStore::addTextToStore(std::string_view name, std::string_view s, + const StorePathSet & references, + RepairFlag repair) { StringSource source(s); - return addCAToStore(source, name, TextHashMethod{}, references, repair)->path; + return addCAToStore(source, name, TextHashMethod{}, references, repair) + ->path; } void RemoteStore::registerDrvOutput(const Realisation & info) @@ -715,14 +691,16 @@ void RemoteStore::registerDrvOutput(const Realisation & info) conn.processStderr(); } -void RemoteStore::queryRealisationUncached(const DrvOutput & id, +void RemoteStore::queryRealisationUncached( + const DrvOutput & id, Callback> callback) noexcept { try { auto conn(getConnection()); if (GET_PROTOCOL_MINOR(conn->daemonVersion) < 27) { - warn("the daemon is too old to support content-addressed derivations, please upgrade it to 2.4"); + warn("the daemon is too old to support content-addressed " + "derivations, please upgrade it to 2.4"); return callback(nullptr); } @@ -733,24 +711,29 @@ void RemoteStore::queryRealisationUncached(const DrvOutput & id, auto real = [&]() -> std::shared_ptr { if (GET_PROTOCOL_MINOR(conn->daemonVersion) < 31) { auto outPaths = worker_proto::read( - *this, conn->from, Phantom> {}); + *this, conn->from, Phantom>{}); if (outPaths.empty()) return nullptr; - return std::make_shared(Realisation { .id = id, .outPath = *outPaths.begin() }); + return std::make_shared( + Realisation{.id = id, .outPath = *outPaths.begin()}); } else { auto realisations = worker_proto::read( - *this, conn->from, Phantom> {}); + *this, conn->from, Phantom>{}); if (realisations.empty()) return nullptr; - return std::make_shared(*realisations.begin()); + return std::make_shared( + *realisations.begin()); } }(); callback(std::shared_ptr(real)); - } catch (...) { return callback.rethrow(); } + } catch (...) { + return callback.rethrow(); + } } -static void writeDerivedPaths(RemoteStore & store, ConnectionHandle & conn, const std::vector & reqs) +static void writeDerivedPaths(RemoteStore & store, ConnectionHandle & conn, + const std::vector & reqs) { if (GET_PROTOCOL_MINOR(conn->daemonVersion) >= 30) { worker_proto::write(store, conn->to, reqs); @@ -758,25 +741,28 @@ static void writeDerivedPaths(RemoteStore & store, ConnectionHandle & conn, cons Strings ss; for (auto & p : reqs) { auto sOrDrvPath = StorePathWithOutputs::tryFromDerivedPath(p); - std::visit(overloaded { - [&](const StorePathWithOutputs & s) { - ss.push_back(s.to_string(store)); - }, - [&](const StorePath & drvPath) { - throw Error("trying to request '%s', but daemon protocol %d.%d is too old (< 1.29) to request a derivation file", - store.printStorePath(drvPath), - GET_PROTOCOL_MAJOR(conn->daemonVersion), - GET_PROTOCOL_MINOR(conn->daemonVersion)); + std::visit( + overloaded{ + [&](const StorePathWithOutputs & s) { + ss.push_back(s.to_string(store)); + }, + [&](const StorePath & drvPath) { + throw Error( + "trying to request '%s', but daemon protocol %d.%d " + "is too old (< 1.29) to request a derivation file", + store.printStorePath(drvPath), + GET_PROTOCOL_MAJOR(conn->daemonVersion), + GET_PROTOCOL_MINOR(conn->daemonVersion)); + }, }, - }, sOrDrvPath); + sOrDrvPath); } conn->to << ss; } } -void RemoteStore::copyDrvsFromEvalStore( - const std::vector & paths, - std::shared_ptr evalStore) +void RemoteStore::copyDrvsFromEvalStore(const std::vector & paths, + std::shared_ptr evalStore) { if (evalStore && evalStore.get() != this) { /* The remote doesn't have a way to access evalStore, so copy @@ -789,7 +775,9 @@ void RemoteStore::copyDrvsFromEvalStore( } } -void RemoteStore::buildPaths(const std::vector & drvPaths, BuildMode buildMode, std::shared_ptr evalStore) +void RemoteStore::buildPaths(const std::vector & drvPaths, + BuildMode buildMode, + std::shared_ptr evalStore) { copyDrvsFromEvalStore(drvPaths, evalStore); @@ -803,15 +791,16 @@ void RemoteStore::buildPaths(const std::vector & drvPaths, BuildMod /* Old daemons did not take a 'buildMode' parameter, so we need to validate it here on the client side. */ if (buildMode != bmNormal) - throw Error("repairing or checking is not supported when building through the Nix daemon"); + throw Error("repairing or checking is not supported when building " + "through the Nix daemon"); conn.processStderr(); readInt(conn->from); } -std::vector RemoteStore::buildPathsWithResults( - const std::vector & paths, - BuildMode buildMode, - std::shared_ptr evalStore) +std::vector +RemoteStore::buildPathsWithResults(const std::vector & paths, + BuildMode buildMode, + std::shared_ptr evalStore) { copyDrvsFromEvalStore(paths, evalStore); @@ -823,7 +812,8 @@ std::vector RemoteStore::buildPathsWithResults( writeDerivedPaths(*this, conn, paths); conn->to << buildMode; conn.processStderr(); - return worker_proto::read(*this, conn->from, Phantom> {}); + return worker_proto::read(*this, conn->from, + Phantom>{}); } else { // Avoid deadlock. conn_.reset(); @@ -836,39 +826,43 @@ std::vector RemoteStore::buildPathsWithResults( for (auto & path : paths) { std::visit( - overloaded { + overloaded{ [&](const DerivedPath::Opaque & bo) { - results.push_back(BuildResult { + results.push_back(BuildResult{ .status = BuildResult::Substituted, .path = bo, }); }, [&](const DerivedPath::Built & bfd) { - BuildResult res { + BuildResult res{ .status = BuildResult::Built, .path = bfd, }; OutputPathMap outputs; auto drv = evalStore->readDerivation(bfd.drvPath); - const auto outputHashes = staticOutputHashes(*evalStore, drv); // FIXME: expensive + const auto outputHashes = staticOutputHashes( + *evalStore, drv); // FIXME: expensive const auto drvOutputs = drv.outputsAndOptPaths(*this); for (auto & output : bfd.outputs) { auto outputHash = get(outputHashes, output); if (!outputHash) - throw Error( - "the derivation '%s' doesn't have an output named '%s'", - printStorePath(bfd.drvPath), output); - auto outputId = DrvOutput{ *outputHash, output }; - if (settings.isExperimentalFeatureEnabled(Xp::CaDerivations)) { - auto realisation = - queryRealisation(outputId); + throw Error("the derivation '%s' doesn't have " + "an output named '%s'", + printStorePath(bfd.drvPath), + output); + auto outputId = DrvOutput{*outputHash, output}; + if (settings.isExperimentalFeatureEnabled( + Xp::CaDerivations)) { + auto realisation = queryRealisation(outputId); if (!realisation) throw Error( - "cannot operate on an output of unbuilt " + "cannot operate on an output of " + "unbuilt " "content-addressed derivation '%s'", outputId.to_string()); - res.builtOutputs.emplace(realisation->id, *realisation); + res.builtOutputs.emplace(realisation->id, + *realisation); } else { // If ca-derivations isn't enabled, assume that // the output path is statically known. @@ -876,17 +870,15 @@ std::vector RemoteStore::buildPathsWithResults( assert(drvOutput); assert(drvOutput->second); res.builtOutputs.emplace( - outputId, - Realisation { - .id = outputId, - .outPath = *drvOutput->second, - }); + outputId, Realisation{ + .id = outputId, + .outPath = *drvOutput->second, + }); } } results.push_back(res); - } - }, + }}, path.raw()); } @@ -894,29 +886,30 @@ std::vector RemoteStore::buildPathsWithResults( } } - -BuildResult RemoteStore::buildDerivation(const StorePath & drvPath, const BasicDerivation & drv, - BuildMode buildMode) +BuildResult RemoteStore::buildDerivation(const StorePath & drvPath, + const BasicDerivation & drv, + BuildMode buildMode) { auto conn(getConnection()); conn->to << wopBuildDerivation << printStorePath(drvPath); writeDerivation(conn->to, *this, drv); conn->to << buildMode; conn.processStderr(); - BuildResult res { .path = DerivedPath::Built { .drvPath = drvPath } }; + BuildResult res{.path = DerivedPath::Built{.drvPath = drvPath}}; res.status = (BuildResult::Status) readInt(conn->from); conn->from >> res.errorMsg; if (GET_PROTOCOL_MINOR(conn->daemonVersion) >= 29) { - conn->from >> res.timesBuilt >> res.isNonDeterministic >> res.startTime >> res.stopTime; + conn->from >> res.timesBuilt >> res.isNonDeterministic >> + res.startTime >> res.stopTime; } if (GET_PROTOCOL_MINOR(conn->daemonVersion) >= 28) { - auto builtOutputs = worker_proto::read(*this, conn->from, Phantom {}); + auto builtOutputs = + worker_proto::read(*this, conn->from, Phantom{}); res.builtOutputs = builtOutputs; } return res; } - void RemoteStore::ensurePath(const StorePath & path) { auto conn(getConnection()); @@ -925,7 +918,6 @@ void RemoteStore::ensurePath(const StorePath & path) readInt(conn->from); } - void RemoteStore::addTempRoot(const StorePath & path) { auto conn(getConnection()); @@ -934,7 +926,6 @@ void RemoteStore::addTempRoot(const StorePath & path) readInt(conn->from); } - void RemoteStore::addIndirectRoot(const Path & path) { auto conn(getConnection()); @@ -943,7 +934,6 @@ void RemoteStore::addIndirectRoot(const Path & path) readInt(conn->from); } - Roots RemoteStore::findRoots(bool censor) { auto conn(getConnection()); @@ -959,18 +949,16 @@ Roots RemoteStore::findRoots(bool censor) return result; } - void RemoteStore::collectGarbage(const GCOptions & options, GCResults & results) { auto conn(getConnection()); - conn->to - << wopCollectGarbage << options.action; + conn->to << wopCollectGarbage << options.action; worker_proto::write(*this, conn->to, options.pathsToDelete); conn->to << options.ignoreLiveness - << options.maxFreed - /* removed options */ - << 0 << 0 << 0; + << options.maxFreed + /* removed options */ + << 0 << 0 << 0; conn.processStderr(); @@ -984,7 +972,6 @@ void RemoteStore::collectGarbage(const GCOptions & options, GCResults & results) } } - void RemoteStore::optimiseStore() { auto conn(getConnection()); @@ -993,7 +980,6 @@ void RemoteStore::optimiseStore() readInt(conn->from); } - bool RemoteStore::verifyStore(bool checkContents, RepairFlag repair) { auto conn(getConnection()); @@ -1002,8 +988,8 @@ bool RemoteStore::verifyStore(bool checkContents, RepairFlag repair) return readInt(conn->from); } - -void RemoteStore::addSignatures(const StorePath & storePath, const StringSet & sigs) +void RemoteStore::addSignatures(const StorePath & storePath, + const StringSet & sigs) { auto conn(getConnection()); conn->to << wopAddSignatures << printStorePath(storePath) << sigs; @@ -1011,10 +997,11 @@ void RemoteStore::addSignatures(const StorePath & storePath, const StringSet & s readInt(conn->from); } - void RemoteStore::queryMissing(const std::vector & targets, - StorePathSet & willBuild, StorePathSet & willSubstitute, StorePathSet & unknown, - uint64_t & downloadSize, uint64_t & narSize) + StorePathSet & willBuild, + StorePathSet & willSubstitute, + StorePathSet & unknown, uint64_t & downloadSize, + uint64_t & narSize) { { auto conn(getConnection()); @@ -1025,43 +1012,37 @@ void RemoteStore::queryMissing(const std::vector & targets, conn->to << wopQueryMissing; writeDerivedPaths(*this, conn, targets); conn.processStderr(); - willBuild = worker_proto::read(*this, conn->from, Phantom {}); - willSubstitute = worker_proto::read(*this, conn->from, Phantom {}); - unknown = worker_proto::read(*this, conn->from, Phantom {}); + willBuild = + worker_proto::read(*this, conn->from, Phantom{}); + willSubstitute = + worker_proto::read(*this, conn->from, Phantom{}); + unknown = + worker_proto::read(*this, conn->from, Phantom{}); conn->from >> downloadSize >> narSize; return; } - fallback: - return Store::queryMissing(targets, willBuild, willSubstitute, - unknown, downloadSize, narSize); +fallback: + return Store::queryMissing(targets, willBuild, willSubstitute, unknown, + downloadSize, narSize); } - void RemoteStore::addBuildLog(const StorePath & drvPath, std::string_view log) { auto conn(getConnection()); conn->to << wopAddBuildLog << drvPath.to_string(); StringSource source(log); - conn.withFramedSink([&](Sink & sink) { - source.drainInto(sink); - }); + conn.withFramedSink([&](Sink & sink) { source.drainInto(sink); }); readInt(conn->from); } - std::optional RemoteStore::getVersion() { auto conn(getConnection()); return conn->daemonNixVersion; } - -void RemoteStore::connect() -{ - auto conn(getConnection()); -} - +void RemoteStore::connect() { auto conn(getConnection()); } unsigned int RemoteStore::getProtocol() { @@ -1069,12 +1050,7 @@ unsigned int RemoteStore::getProtocol() return conn->daemonVersion; } - -void RemoteStore::flushBadConnections() -{ - connections->flushBad(); -} - +void RemoteStore::flushBadConnections() { connections->flushBad(); } RemoteStore::Connection::~Connection() { @@ -1109,13 +1085,14 @@ static Logger::Fields readFields(Source & from) else if (type == Logger::Field::tString) fields.push_back(readString(from)); else - throw Error("got unsupported field type %x from Nix daemon", (int) type); + throw Error("got unsupported field type %x from Nix daemon", + (int) type); } return fields; } - -std::exception_ptr RemoteStore::Connection::processStderr(Sink * sink, Source * source, bool flush) +std::exception_ptr +RemoteStore::Connection::processStderr(Sink * sink, Source * source, bool flush) { if (flush) to.flush(); @@ -1126,15 +1103,18 @@ std::exception_ptr RemoteStore::Connection::processStderr(Sink * sink, Source * if (msg == STDERR_WRITE) { auto s = readString(from); - if (!sink) throw Error("no sink"); + if (!sink) + throw Error("no sink"); (*sink)(s); } else if (msg == STDERR_READ) { - if (!source) throw Error("no source"); + if (!source) + throw Error("no source"); size_t len = readNum(from); auto buf = std::make_unique(len); - writeString({(const char *) buf.get(), source->read(buf.get(), len)}, to); + writeString( + {(const char *) buf.get(), source->read(buf.get(), len)}, to); to.flush(); } @@ -1191,8 +1171,7 @@ void ConnectionHandle::withFramedSink(std::function fun) /* Handle log messages / exceptions from the remote on a separate thread. */ - std::thread stderrThread([&]() - { + std::thread stderrThread([&]() { try { processStderr(nullptr, nullptr, false); } catch (...) { @@ -1200,8 +1179,7 @@ void ConnectionHandle::withFramedSink(std::function fun) } }); - Finally joinStderrThread([&]() - { + Finally joinStderrThread([&]() { if (stderrThread.joinable()) { stderrThread.join(); if (ex) { diff --git a/src/libstore/remote-store.hh b/src/libstore/remote-store.hh index 8493be6fc544..4da7b33b38c8 100644 --- a/src/libstore/remote-store.hh +++ b/src/libstore/remote-store.hh @@ -7,10 +7,8 @@ #include "gc-store.hh" #include "log-store.hh" - namespace nix { - class Pipe; class Pid; struct FdSink; @@ -18,26 +16,25 @@ struct FdSource; template class Pool; struct ConnectionHandle; -struct RemoteStoreConfig : virtual StoreConfig -{ +struct RemoteStoreConfig : virtual StoreConfig { using StoreConfig::StoreConfig; - const Setting maxConnections{(StoreConfig*) this, 1, - "max-connections", "maximum number of concurrent connections to the Nix daemon"}; + const Setting maxConnections{ + (StoreConfig *) this, 1, "max-connections", + "maximum number of concurrent connections to the Nix daemon"}; - const Setting maxConnectionAge{(StoreConfig*) this, std::numeric_limits::max(), - "max-connection-age", "number of seconds to reuse a connection"}; + const Setting maxConnectionAge{ + (StoreConfig *) this, std::numeric_limits::max(), + "max-connection-age", "number of seconds to reuse a connection"}; }; /* FIXME: RemoteStore is a misnomer - should be something like DaemonStore. */ class RemoteStore : public virtual RemoteStoreConfig, - public virtual Store, - public virtual GcStore, - public virtual LogStore -{ -public: - + public virtual Store, + public virtual GcStore, + public virtual LogStore { + public: virtual bool sameMachine() = 0; RemoteStore(const Params & params); @@ -46,68 +43,74 @@ public: bool isValidPathUncached(const StorePath & path) override; - StorePathSet queryValidPaths(const StorePathSet & paths, - SubstituteFlag maybeSubstitute = NoSubstitute) override; + StorePathSet + queryValidPaths(const StorePathSet & paths, + SubstituteFlag maybeSubstitute = NoSubstitute) override; StorePathSet queryAllValidPaths() override; void queryPathInfoUncached(const StorePath & path, - Callback> callback) noexcept override; + Callback> + callback) noexcept override; - void queryReferrers(const StorePath & path, StorePathSet & referrers) override; + void queryReferrers(const StorePath & path, + StorePathSet & referrers) override; StorePathSet queryValidDerivers(const StorePath & path) override; StorePathSet queryDerivationOutputs(const StorePath & path) override; - std::map> queryPartialDerivationOutputMap(const StorePath & path) override; - std::optional queryPathFromHashPart(const std::string & hashPart) override; + std::map> + queryPartialDerivationOutputMap(const StorePath & path) override; + std::optional + queryPathFromHashPart(const std::string & hashPart) override; StorePathSet querySubstitutablePaths(const StorePathSet & paths) override; void querySubstitutablePathInfos(const StorePathCAMap & paths, - SubstitutablePathInfos & infos) override; + SubstitutablePathInfos & infos) override; /* Add a content-addressable store path. `dump` will be drained. */ - ref addCAToStore( - Source & dump, - std::string_view name, - ContentAddressMethod caMethod, - const StorePathSet & references, - RepairFlag repair); - - /* Add a content-addressable store path. Does not support references. `dump` will be drained. */ - StorePath addToStoreFromDump(Source & dump, std::string_view name, - FileIngestionMethod method = FileIngestionMethod::Recursive, HashType hashAlgo = htSHA256, RepairFlag repair = NoRepair, const StorePathSet & references = StorePathSet()) override; - - void addToStore(const ValidPathInfo & info, Source & nar, - RepairFlag repair, CheckSigsFlag checkSigs) override; - - void addMultipleToStore( - Source & source, - RepairFlag repair, - CheckSigsFlag checkSigs) override; - - StorePath addTextToStore( - std::string_view name, - std::string_view s, - const StorePathSet & references, - RepairFlag repair) override; + ref addCAToStore(Source & dump, std::string_view name, + ContentAddressMethod caMethod, + const StorePathSet & references, + RepairFlag repair); + + /* Add a content-addressable store path. Does not support references. `dump` + * will be drained. */ + StorePath addToStoreFromDump( + Source & dump, std::string_view name, + FileIngestionMethod method = FileIngestionMethod::Recursive, + HashType hashAlgo = htSHA256, RepairFlag repair = NoRepair, + const StorePathSet & references = StorePathSet()) override; + + void addToStore(const ValidPathInfo & info, Source & nar, RepairFlag repair, + CheckSigsFlag checkSigs) override; + + void addMultipleToStore(Source & source, RepairFlag repair, + CheckSigsFlag checkSigs) override; + + StorePath addTextToStore(std::string_view name, std::string_view s, + const StorePathSet & references, + RepairFlag repair) override; void registerDrvOutput(const Realisation & info) override; void queryRealisationUncached(const DrvOutput &, - Callback> callback) noexcept override; + Callback> + callback) noexcept override; - void buildPaths(const std::vector & paths, BuildMode buildMode, std::shared_ptr evalStore) override; + void buildPaths(const std::vector & paths, BuildMode buildMode, + std::shared_ptr evalStore) override; - std::vector buildPathsWithResults( - const std::vector & paths, - BuildMode buildMode, - std::shared_ptr evalStore) override; + std::vector + buildPathsWithResults(const std::vector & paths, + BuildMode buildMode, + std::shared_ptr evalStore) override; - BuildResult buildDerivation(const StorePath & drvPath, const BasicDerivation & drv, - BuildMode buildMode) override; + BuildResult buildDerivation(const StorePath & drvPath, + const BasicDerivation & drv, + BuildMode buildMode) override; void ensurePath(const StorePath & path) override; @@ -117,17 +120,20 @@ public: Roots findRoots(bool censor) override; - void collectGarbage(const GCOptions & options, GCResults & results) override; + void collectGarbage(const GCOptions & options, + GCResults & results) override; void optimiseStore() override; bool verifyStore(bool checkContents, RepairFlag repair) override; - void addSignatures(const StorePath & storePath, const StringSet & sigs) override; + void addSignatures(const StorePath & storePath, + const StringSet & sigs) override; void queryMissing(const std::vector & targets, - StorePathSet & willBuild, StorePathSet & willSubstitute, StorePathSet & unknown, - uint64_t & downloadSize, uint64_t & narSize) override; + StorePathSet & willBuild, StorePathSet & willSubstitute, + StorePathSet & unknown, uint64_t & downloadSize, + uint64_t & narSize) override; void addBuildLog(const StorePath & drvPath, std::string_view log) override; @@ -139,8 +145,7 @@ public: void flushBadConnections(); - struct Connection - { + struct Connection { FdSink to; FdSource from; unsigned int daemonVersion; @@ -151,13 +156,13 @@ public: virtual void closeWrite() = 0; - std::exception_ptr processStderr(Sink * sink = 0, Source * source = 0, bool flush = true); + std::exception_ptr processStderr(Sink * sink = 0, Source * source = 0, + bool flush = true); }; ref openConnectionWrapper(); -protected: - + protected: virtual ref openConnection() = 0; void initConnection(Connection & conn); @@ -176,14 +181,11 @@ protected: virtual void narFromPath(const StorePath & path, Sink & sink) override; -private: - + private: std::atomic_bool failed{false}; - void copyDrvsFromEvalStore( - const std::vector & paths, - std::shared_ptr evalStore); + void copyDrvsFromEvalStore(const std::vector & paths, + std::shared_ptr evalStore); }; - } diff --git a/src/libstore/s3-binary-cache-store.cc b/src/libstore/s3-binary-cache-store.cc index 844553ad3094..ad9e7cd7c662 100644 --- a/src/libstore/s3-binary-cache-store.cc +++ b/src/libstore/s3-binary-cache-store.cc @@ -28,13 +28,12 @@ using namespace Aws::Transfer; namespace nix { -struct S3Error : public Error -{ +struct S3Error : public Error { Aws::S3::S3Errors err; template - S3Error(Aws::S3::S3Errors err, const Args & ... args) - : Error(args...), err(err) { }; + S3Error(Aws::S3::S3Errors err, const Args &... args) + : Error(args...), err(err){}; }; /* Helper: given an Outcome, return R in case of success, or @@ -43,14 +42,12 @@ template R && checkAws(const FormatOrString & fs, Aws::Utils::Outcome && outcome) { if (!outcome.IsSuccess()) - throw S3Error( - outcome.GetError().GetErrorType(), - fs.s + ": " + outcome.GetError().GetMessage()); + throw S3Error(outcome.GetError().GetErrorType(), + fs.s + ": " + outcome.GetError().GetMessage()); return outcome.GetResultWithOwnership(); } -class AwsLogger : public Aws::Utils::Logging::FormattedLogSystem -{ +class AwsLogger : public Aws::Utils::Logging::FormattedLogSystem { using Aws::Utils::Logging::FormattedLogSystem::FormattedLogSystem; void ProcessFormattedStatement(Aws::String && statement) override @@ -58,7 +55,8 @@ class AwsLogger : public Aws::Utils::Logging::FormattedLogSystem debug("AWS: %s", chomp(statement)); } -#if !(AWS_VERSION_MAJOR <= 1 && AWS_VERSION_MINOR <= 7 && AWS_VERSION_PATCH <= 115) +#if !(AWS_VERSION_MAJOR <= 1 && AWS_VERSION_MINOR <= 7 && \ + AWS_VERSION_PATCH <= 115) void Flush() override {} #endif }; @@ -75,11 +73,11 @@ static void initAWS() if (verbosity >= lvlDebug) { options.loggingOptions.logLevel = - verbosity == lvlDebug - ? Aws::Utils::Logging::LogLevel::Debug - : Aws::Utils::Logging::LogLevel::Trace; + verbosity == lvlDebug ? Aws::Utils::Logging::LogLevel::Debug + : Aws::Utils::Logging::LogLevel::Trace; options.loggingOptions.logger_create_fn = [options]() { - return std::make_shared(options.loggingOptions.logLevel); + return std::make_shared( + options.loggingOptions.logLevel); }; } @@ -87,48 +85,48 @@ static void initAWS() }); } -S3Helper::S3Helper( - const std::string & profile, - const std::string & region, - const std::string & scheme, - const std::string & endpoint) - : config(makeConfig(region, scheme, endpoint)) - , client(make_ref( - profile == "" - ? std::dynamic_pointer_cast( - std::make_shared()) - : std::dynamic_pointer_cast( - std::make_shared(profile.c_str())), - *config, - // FIXME: https://github.com/aws/aws-sdk-cpp/issues/759 +S3Helper::S3Helper(const std::string & profile, const std::string & region, + const std::string & scheme, const std::string & endpoint) + : config(makeConfig(region, scheme, endpoint)), + client(make_ref( + profile == "" + ? std::dynamic_pointer_cast( + std::make_shared< + Aws::Auth::DefaultAWSCredentialsProviderChain>()) + : std::dynamic_pointer_cast( + std::make_shared< + Aws::Auth::ProfileConfigFileAWSCredentialsProvider>( + profile.c_str())), + *config, +// FIXME: https://github.com/aws/aws-sdk-cpp/issues/759 #if AWS_VERSION_MAJOR == 1 && AWS_VERSION_MINOR < 3 - false, + false, #else - Aws::Client::AWSAuthV4Signer::PayloadSigningPolicy::Never, + Aws::Client::AWSAuthV4Signer::PayloadSigningPolicy::Never, #endif - endpoint.empty())) + endpoint.empty())) { } /* Log AWS retries. */ -class RetryStrategy : public Aws::Client::DefaultRetryStrategy -{ - bool ShouldRetry(const Aws::Client::AWSError& error, long attemptedRetries) const override +class RetryStrategy : public Aws::Client::DefaultRetryStrategy { + bool + ShouldRetry(const Aws::Client::AWSError & error, + long attemptedRetries) const override { - auto retry = Aws::Client::DefaultRetryStrategy::ShouldRetry(error, attemptedRetries); + auto retry = Aws::Client::DefaultRetryStrategy::ShouldRetry( + error, attemptedRetries); if (retry) printError("AWS error '%s' (%s), will retry in %d ms", - error.GetExceptionName(), - error.GetMessage(), - CalculateDelayBeforeNextRetry(error, attemptedRetries)); + error.GetExceptionName(), error.GetMessage(), + CalculateDelayBeforeNextRetry(error, attemptedRetries)); return retry; } }; -ref S3Helper::makeConfig( - const std::string & region, - const std::string & scheme, - const std::string & endpoint) +ref +S3Helper::makeConfig(const std::string & region, const std::string & scheme, + const std::string & endpoint) { initAWS(); auto res = make_ref(); @@ -146,19 +144,16 @@ ref S3Helper::makeConfig( return res; } -S3Helper::FileTransferResult S3Helper::getObject( - const std::string & bucketName, const std::string & key) +S3Helper::FileTransferResult S3Helper::getObject(const std::string & bucketName, + const std::string & key) { debug("fetching 's3://%s/%s'...", bucketName, key); auto request = - Aws::S3::Model::GetObjectRequest() - .WithBucket(bucketName) - .WithKey(key); + Aws::S3::Model::GetObjectRequest().WithBucket(bucketName).WithKey(key); - request.SetResponseStreamFactory([&]() { - return Aws::New("STRINGSTREAM"); - }); + request.SetResponseStreamFactory( + [&]() { return Aws::New("STRINGSTREAM"); }); FileTransferResult res; @@ -167,74 +162,84 @@ S3Helper::FileTransferResult S3Helper::getObject( try { auto result = checkAws(fmt("AWS error fetching '%s'", key), - client->GetObject(request)); + client->GetObject(request)); - res.data = decompress(result.GetContentEncoding(), + res.data = decompress( + result.GetContentEncoding(), dynamic_cast(result.GetBody()).str()); } catch (S3Error & e) { if ((e.err != Aws::S3::S3Errors::NO_SUCH_KEY) && - (e.err != Aws::S3::S3Errors::ACCESS_DENIED)) throw; + (e.err != Aws::S3::S3Errors::ACCESS_DENIED)) + throw; } auto now2 = std::chrono::steady_clock::now(); - res.durationMs = std::chrono::duration_cast(now2 - now1).count(); + res.durationMs = + std::chrono::duration_cast(now2 - now1) + .count(); return res; } S3BinaryCacheStore::S3BinaryCacheStore(const Params & params) - : BinaryCacheStoreConfig(params) - , BinaryCacheStore(params) -{ } - -struct S3BinaryCacheStoreConfig : virtual BinaryCacheStoreConfig + : BinaryCacheStoreConfig(params), BinaryCacheStore(params) { +} + +struct S3BinaryCacheStoreConfig : virtual BinaryCacheStoreConfig { using BinaryCacheStoreConfig::BinaryCacheStoreConfig; - const Setting profile{(StoreConfig*) this, "", "profile", "The name of the AWS configuration profile to use."}; - const Setting region{(StoreConfig*) this, Aws::Region::US_EAST_1, "region", {"aws-region"}}; - const Setting scheme{(StoreConfig*) this, "", "scheme", "The scheme to use for S3 requests, https by default."}; - const Setting endpoint{(StoreConfig*) this, "", "endpoint", "An optional override of the endpoint to use when talking to S3."}; - const Setting narinfoCompression{(StoreConfig*) this, "", "narinfo-compression", "compression method for .narinfo files"}; - const Setting lsCompression{(StoreConfig*) this, "", "ls-compression", "compression method for .ls files"}; - const Setting logCompression{(StoreConfig*) this, "", "log-compression", "compression method for log/* files"}; - const Setting multipartUpload{ - (StoreConfig*) this, false, "multipart-upload", "whether to use multi-part uploads"}; + const Setting profile{ + (StoreConfig *) this, "", "profile", + "The name of the AWS configuration profile to use."}; + const Setting region{ + (StoreConfig *) this, Aws::Region::US_EAST_1, "region", {"aws-region"}}; + const Setting scheme{ + (StoreConfig *) this, "", "scheme", + "The scheme to use for S3 requests, https by default."}; + const Setting endpoint{ + (StoreConfig *) this, "", "endpoint", + "An optional override of the endpoint to use when talking to S3."}; + const Setting narinfoCompression{ + (StoreConfig *) this, "", "narinfo-compression", + "compression method for .narinfo files"}; + const Setting lsCompression{ + (StoreConfig *) this, "", "ls-compression", + "compression method for .ls files"}; + const Setting logCompression{ + (StoreConfig *) this, "", "log-compression", + "compression method for log/* files"}; + const Setting multipartUpload{(StoreConfig *) this, false, + "multipart-upload", + "whether to use multi-part uploads"}; const Setting bufferSize{ - (StoreConfig*) this, 5 * 1024 * 1024, "buffer-size", "size (in bytes) of each part in multi-part uploads"}; + (StoreConfig *) this, 5 * 1024 * 1024, "buffer-size", + "size (in bytes) of each part in multi-part uploads"}; const std::string name() override { return "S3 Binary Cache Store"; } }; -struct S3BinaryCacheStoreImpl : virtual S3BinaryCacheStoreConfig, public virtual S3BinaryCacheStore -{ +struct S3BinaryCacheStoreImpl : virtual S3BinaryCacheStoreConfig, + public virtual S3BinaryCacheStore { std::string bucketName; Stats stats; S3Helper s3Helper; - S3BinaryCacheStoreImpl( - const std::string & uriScheme, - const std::string & bucketName, - const Params & params) - : StoreConfig(params) - , BinaryCacheStoreConfig(params) - , S3BinaryCacheStoreConfig(params) - , Store(params) - , BinaryCacheStore(params) - , S3BinaryCacheStore(params) - , bucketName(bucketName) - , s3Helper(profile, region, scheme, endpoint) + S3BinaryCacheStoreImpl(const std::string & uriScheme, + const std::string & bucketName, + const Params & params) + : StoreConfig(params), BinaryCacheStoreConfig(params), + S3BinaryCacheStoreConfig(params), Store(params), + BinaryCacheStore(params), S3BinaryCacheStore(params), + bucketName(bucketName), s3Helper(profile, region, scheme, endpoint) { diskCache = getNarInfoDiskCache(); } - std::string getUri() override - { - return "s3://" + bucketName; - } + std::string getUri() override { return "s3://" + bucketName; } void init() override { @@ -247,10 +252,7 @@ struct S3BinaryCacheStoreImpl : virtual S3BinaryCacheStoreConfig, public virtual } } - const Stats & getS3Stats() override - { - return stats; - } + const Stats & getS3Stats() override { return stats; } /* This is a specialisation of isValidPath() that optimistically fetches the .narinfo file, rather than first checking for its @@ -270,19 +272,20 @@ struct S3BinaryCacheStoreImpl : virtual S3BinaryCacheStoreConfig, public virtual { stats.head++; - auto res = s3Helper.client->HeadObject( - Aws::S3::Model::HeadObjectRequest() - .WithBucket(bucketName) - .WithKey(path)); + auto res = + s3Helper.client->HeadObject(Aws::S3::Model::HeadObjectRequest() + .WithBucket(bucketName) + .WithKey(path)); if (!res.IsSuccess()) { auto & error = res.GetError(); - if (error.GetErrorType() == Aws::S3::S3Errors::RESOURCE_NOT_FOUND - || error.GetErrorType() == Aws::S3::S3Errors::NO_SUCH_KEY + if (error.GetErrorType() == Aws::S3::S3Errors::RESOURCE_NOT_FOUND || + error.GetErrorType() == Aws::S3::S3Errors::NO_SUCH_KEY // If bucket listing is disabled, 404s turn into 403s || error.GetErrorType() == Aws::S3::S3Errors::ACCESS_DENIED) return false; - throw Error("AWS error fetching '%s': %s", path, error.GetMessage()); + throw Error("AWS error fetching '%s': %s", path, + error.GetMessage()); } return true; @@ -292,9 +295,9 @@ struct S3BinaryCacheStoreImpl : virtual S3BinaryCacheStoreConfig, public virtual std::once_flag transferManagerCreated; void uploadFile(const std::string & path, - std::shared_ptr> istream, - const std::string & mimeType, - const std::string & contentEncoding) + std::shared_ptr> istream, + const std::string & mimeType, + const std::string & contentEncoding) { istream->seekg(0, istream->end); auto size = istream->tellg(); @@ -303,10 +306,11 @@ struct S3BinaryCacheStoreImpl : virtual S3BinaryCacheStoreConfig, public virtual auto maxThreads = std::thread::hardware_concurrency(); static std::shared_ptr - executor = std::make_shared(maxThreads); + executor = + std::make_shared( + maxThreads); - std::call_once(transferManagerCreated, [&]() - { + std::call_once(transferManagerCreated, [&]() { if (multipartUpload) { TransferManagerConfiguration transferConfig(executor.get()); @@ -314,16 +318,15 @@ struct S3BinaryCacheStoreImpl : virtual S3BinaryCacheStoreConfig, public virtual transferConfig.bufferSize = bufferSize; transferConfig.uploadProgressCallback = - [](const TransferManager *transferManager, - const std::shared_ptr - &transferHandle) - { - //FIXME: find a way to properly abort the multipart upload. - //checkInterrupt(); + [](const TransferManager * transferManager, + const std::shared_ptr & + transferHandle) { + // FIXME: find a way to properly abort the multipart + // upload. checkInterrupt(); debug("upload progress ('%s'): '%d' of '%d' bytes", - transferHandle->GetKey(), - transferHandle->GetBytesTransferred(), - transferHandle->GetBytesTotalSize()); + transferHandle->GetKey(), + transferHandle->GetBytesTransferred(), + transferHandle->GetBytesTotalSize()); }; transferManager = TransferManager::Create(transferConfig); @@ -335,7 +338,8 @@ struct S3BinaryCacheStoreImpl : virtual S3BinaryCacheStoreConfig, public virtual if (transferManager) { if (contentEncoding != "") - throw Error("setting a content encoding is not supported with S3 multi-part uploads"); + throw Error("setting a content encoding is not supported with " + "S3 multi-part uploads"); std::shared_ptr transferHandle = transferManager->UploadFile( @@ -347,18 +351,19 @@ struct S3BinaryCacheStoreImpl : virtual S3BinaryCacheStoreConfig, public virtual if (transferHandle->GetStatus() == TransferStatus::FAILED) throw Error("AWS error: failed to upload 's3://%s/%s': %s", - bucketName, path, transferHandle->GetLastError().GetMessage()); + bucketName, path, + transferHandle->GetLastError().GetMessage()); if (transferHandle->GetStatus() != TransferStatus::COMPLETED) - throw Error("AWS error: transfer status of 's3://%s/%s' in unexpected state", - bucketName, path); + throw Error("AWS error: transfer status of 's3://%s/%s' in " + "unexpected state", + bucketName, path); } else { - auto request = - Aws::S3::Model::PutObjectRequest() - .WithBucket(bucketName) - .WithKey(path); + auto request = Aws::S3::Model::PutObjectRequest() + .WithBucket(bucketName) + .WithKey(path); request.SetContentType(mimeType); @@ -368,7 +373,7 @@ struct S3BinaryCacheStoreImpl : virtual S3BinaryCacheStoreConfig, public virtual request.SetBody(istream); auto result = checkAws(fmt("AWS error uploading '%s'", path), - s3Helper.client->PutObject(request)); + s3Helper.client->PutObject(request)); } auto now2 = std::chrono::steady_clock::now(); @@ -377,8 +382,8 @@ struct S3BinaryCacheStoreImpl : virtual S3BinaryCacheStoreConfig, public virtual std::chrono::duration_cast(now2 - now1) .count(); - printInfo("uploaded 's3://%s/%s' (%d bytes) in %d ms", - bucketName, path, size, duration); + printInfo("uploaded 's3://%s/%s' (%d bytes) in %d ms", bucketName, path, + size, duration); stats.putTimeMs += duration; stats.putBytes += std::max(size, (decltype(size)) 0); @@ -386,21 +391,23 @@ struct S3BinaryCacheStoreImpl : virtual S3BinaryCacheStoreConfig, public virtual } void upsertFile(const std::string & path, - std::shared_ptr> istream, - const std::string & mimeType) override + std::shared_ptr> istream, + const std::string & mimeType) override { - auto compress = [&](std::string compression) - { - auto compressed = nix::compress(compression, StreamToSourceAdapter(istream).drain()); + auto compress = [&](std::string compression) { + auto compressed = nix::compress( + compression, StreamToSourceAdapter(istream).drain()); return std::make_shared(std::move(compressed)); }; if (narinfoCompression != "" && hasSuffix(path, ".narinfo")) - uploadFile(path, compress(narinfoCompression), mimeType, narinfoCompression); + uploadFile(path, compress(narinfoCompression), mimeType, + narinfoCompression); else if (lsCompression != "" && hasSuffix(path, ".ls")) uploadFile(path, compress(lsCompression), mimeType, lsCompression); else if (logCompression != "" && hasPrefix(path, "log/")) - uploadFile(path, compress(logCompression), mimeType, logCompression); + uploadFile(path, compress(logCompression), mimeType, + logCompression); else uploadFile(path, istream, mimeType, ""); } @@ -417,11 +424,13 @@ struct S3BinaryCacheStoreImpl : virtual S3BinaryCacheStoreConfig, public virtual if (res.data) { printTalkative("downloaded 's3://%s/%s' (%d bytes) in %d ms", - bucketName, path, res.data->size(), res.durationMs); + bucketName, path, res.data->size(), res.durationMs); sink(*res.data); } else - throw NoSuchBinaryCacheFile("file '%s' does not exist in binary cache '%s'", path, getUri()); + throw NoSuchBinaryCacheFile( + "file '%s' does not exist in binary cache '%s'", path, + getUri()); } StorePathSet queryAllValidPaths() override @@ -430,24 +439,29 @@ struct S3BinaryCacheStoreImpl : virtual S3BinaryCacheStoreConfig, public virtual std::string marker; do { - debug(format("listing bucket 's3://%s' from key '%s'...") % bucketName % marker); + debug(format("listing bucket 's3://%s' from key '%s'...") % + bucketName % marker); - auto res = checkAws(format("AWS error listing bucket '%s'") % bucketName, - s3Helper.client->ListObjects( - Aws::S3::Model::ListObjectsRequest() - .WithBucket(bucketName) - .WithDelimiter("/") - .WithMarker(marker))); + auto res = + checkAws(format("AWS error listing bucket '%s'") % bucketName, + s3Helper.client->ListObjects( + Aws::S3::Model::ListObjectsRequest() + .WithBucket(bucketName) + .WithDelimiter("/") + .WithMarker(marker))); auto & contents = res.GetContents(); - debug(format("got %d keys, next marker '%s'") - % contents.size() % res.GetNextMarker()); + debug(format("got %d keys, next marker '%s'") % contents.size() % + res.GetNextMarker()); for (auto object : contents) { auto & key = object.GetKey(); - if (key.size() != 40 || !hasSuffix(key, ".narinfo")) continue; - paths.insert(parseStorePath(storeDir + "/" + key.substr(0, key.size() - 8) + "-" + MissingName)); + if (key.size() != 40 || !hasSuffix(key, ".narinfo")) + continue; + paths.insert(parseStorePath(storeDir + "/" + + key.substr(0, key.size() - 8) + + "-" + MissingName)); } marker = res.GetNextMarker(); @@ -457,10 +471,11 @@ struct S3BinaryCacheStoreImpl : virtual S3BinaryCacheStoreConfig, public virtual } static std::set uriSchemes() { return {"s3"}; } - }; -static RegisterStoreImplementation regS3BinaryCacheStore; +static RegisterStoreImplementation + regS3BinaryCacheStore; } diff --git a/src/libstore/s3-binary-cache-store.hh b/src/libstore/s3-binary-cache-store.hh index bce828b111f6..4510dfebffbe 100644 --- a/src/libstore/s3-binary-cache-store.hh +++ b/src/libstore/s3-binary-cache-store.hh @@ -6,16 +6,12 @@ namespace nix { -class S3BinaryCacheStore : public virtual BinaryCacheStore -{ -protected: - +class S3BinaryCacheStore : public virtual BinaryCacheStore { + protected: S3BinaryCacheStore(const Params & params); -public: - - struct Stats - { + public: + struct Stats { std::atomic put{0}; std::atomic putBytes{0}; std::atomic putTimeMs{0}; diff --git a/src/libstore/s3.hh b/src/libstore/s3.hh index cdb3e59080e5..82848f77d4ac 100644 --- a/src/libstore/s3.hh +++ b/src/libstore/s3.hh @@ -7,28 +7,37 @@ #include #include -namespace Aws { namespace Client { class ClientConfiguration; } } -namespace Aws { namespace S3 { class S3Client; } } +namespace Aws { +namespace Client { +class ClientConfiguration; +} +} +namespace Aws { +namespace S3 { +class S3Client; +} +} namespace nix { -struct S3Helper -{ +struct S3Helper { ref config; ref client; - S3Helper(const std::string & profile, const std::string & region, const std::string & scheme, const std::string & endpoint); + S3Helper(const std::string & profile, const std::string & region, + const std::string & scheme, const std::string & endpoint); - ref makeConfig(const std::string & region, const std::string & scheme, const std::string & endpoint); + ref + makeConfig(const std::string & region, const std::string & scheme, + const std::string & endpoint); - struct FileTransferResult - { + struct FileTransferResult { std::optional data; unsigned int durationMs; }; - FileTransferResult getObject( - const std::string & bucketName, const std::string & key); + FileTransferResult getObject(const std::string & bucketName, + const std::string & key); }; } diff --git a/src/libstore/serve-protocol.hh b/src/libstore/serve-protocol.hh index 3f76baa82ead..eece9283a3e6 100644 --- a/src/libstore/serve-protocol.hh +++ b/src/libstore/serve-protocol.hh @@ -6,8 +6,8 @@ namespace nix { #define SERVE_MAGIC_2 0x5452eecb #define SERVE_PROTOCOL_VERSION (2 << 8 | 7) -#define GET_PROTOCOL_MAJOR(x) ((x) & 0xff00) -#define GET_PROTOCOL_MINOR(x) ((x) & 0x00ff) +#define GET_PROTOCOL_MAJOR(x) ((x) &0xff00) +#define GET_PROTOCOL_MINOR(x) ((x) &0x00ff) typedef enum { cmdQueryValidPaths = 1, diff --git a/src/libstore/sqlite.cc b/src/libstore/sqlite.cc index 2090beabdfa5..68e6e324e1c0 100644 --- a/src/libstore/sqlite.cc +++ b/src/libstore/sqlite.cc @@ -8,13 +8,13 @@ namespace nix { -SQLiteError::SQLiteError(const char *path, int errNo, int extendedErrNo, hintformat && hf) - : Error(""), path(path), errNo(errNo), extendedErrNo(extendedErrNo) +SQLiteError::SQLiteError(const char * path, int errNo, int extendedErrNo, + hintformat && hf) + : Error(""), path(path), errNo(errNo), extendedErrNo(extendedErrNo) { - err.msg = hintfmt("%s: %s (in '%s')", - normaltxt(hf.str()), - sqlite3_errstr(extendedErrNo), - path ? path : "(in-memory)"); + err.msg = + hintfmt("%s: %s (in '%s')", normaltxt(hf.str()), + sqlite3_errstr(extendedErrNo), path ? path : "(in-memory)"); } [[noreturn]] void SQLiteError::throw_(sqlite3 * db, hintformat && hf) @@ -26,11 +26,11 @@ SQLiteError::SQLiteError(const char *path, int errNo, int extendedErrNo, hintfor if (err == SQLITE_BUSY || err == SQLITE_PROTOCOL) { auto exp = SQLiteBusy(path, err, exterr, std::move(hf)); - exp.err.msg = hintfmt( - err == SQLITE_PROTOCOL - ? "SQLite database '%s' is busy (SQLITE_PROTOCOL)" - : "SQLite database '%s' is busy", - path ? path : "(in-memory)"); + exp.err.msg = + hintfmt(err == SQLITE_PROTOCOL + ? "SQLite database '%s' is busy (SQLITE_PROTOCOL)" + : "SQLite database '%s' is busy", + path ? path : "(in-memory)"); throw exp; } else throw SQLiteError(path, err, exterr, std::move(hf)); @@ -41,9 +41,11 @@ SQLite::SQLite(const Path & path, bool create) // useSQLiteWAL also indicates what virtual file system we need. Using // `unix-dotfile` is needed on NFS file systems and on Windows' Subsystem // for Linux (WSL) where useSQLiteWAL should be false by default. - const char *vfs = settings.useSQLiteWAL ? 0 : "unix-dotfile"; + const char * vfs = settings.useSQLiteWAL ? 0 : "unix-dotfile"; if (sqlite3_open_v2(path.c_str(), &db, - SQLITE_OPEN_READWRITE | (create ? SQLITE_OPEN_CREATE : 0), vfs) != SQLITE_OK) + SQLITE_OPEN_READWRITE | + (create ? SQLITE_OPEN_CREATE : 0), + vfs) != SQLITE_OK) throw Error("cannot open SQLite database '%s'", path); if (sqlite3_busy_timeout(db, 60 * 60 * 1000) != SQLITE_OK) @@ -101,8 +103,7 @@ SQLiteStmt::~SQLiteStmt() } } -SQLiteStmt::Use::Use(SQLiteStmt & stmt) - : stmt(stmt) +SQLiteStmt::Use::Use(SQLiteStmt & stmt) : stmt(stmt) { assert(stmt.stmt); /* Note: sqlite3_reset() returns the error code for the most @@ -110,32 +111,33 @@ SQLiteStmt::Use::Use(SQLiteStmt & stmt) sqlite3_reset(stmt); } -SQLiteStmt::Use::~Use() -{ - sqlite3_reset(stmt); -} +SQLiteStmt::Use::~Use() { sqlite3_reset(stmt); } -SQLiteStmt::Use & SQLiteStmt::Use::operator () (std::string_view value, bool notNull) +SQLiteStmt::Use & SQLiteStmt::Use::operator()(std::string_view value, + bool notNull) { if (notNull) { - if (sqlite3_bind_text(stmt, curArg++, value.data(), -1, SQLITE_TRANSIENT) != SQLITE_OK) + if (sqlite3_bind_text(stmt, curArg++, value.data(), -1, + SQLITE_TRANSIENT) != SQLITE_OK) SQLiteError::throw_(stmt.db, "binding argument"); } else bind(); return *this; } -SQLiteStmt::Use & SQLiteStmt::Use::operator () (const unsigned char * data, size_t len, bool notNull) +SQLiteStmt::Use & SQLiteStmt::Use::operator()(const unsigned char * data, + size_t len, bool notNull) { if (notNull) { - if (sqlite3_bind_blob(stmt, curArg++, data, len, SQLITE_TRANSIENT) != SQLITE_OK) + if (sqlite3_bind_blob(stmt, curArg++, data, len, SQLITE_TRANSIENT) != + SQLITE_OK) SQLiteError::throw_(stmt.db, "binding argument"); } else bind(); return *this; } -SQLiteStmt::Use & SQLiteStmt::Use::operator () (int64_t value, bool notNull) +SQLiteStmt::Use & SQLiteStmt::Use::operator()(int64_t value, bool notNull) { if (notNull) { if (sqlite3_bind_int64(stmt, curArg++, value) != SQLITE_OK) @@ -152,24 +154,23 @@ SQLiteStmt::Use & SQLiteStmt::Use::bind() return *this; } -int SQLiteStmt::Use::step() -{ - return sqlite3_step(stmt); -} +int SQLiteStmt::Use::step() { return sqlite3_step(stmt); } void SQLiteStmt::Use::exec() { int r = step(); assert(r != SQLITE_ROW); if (r != SQLITE_DONE) - SQLiteError::throw_(stmt.db, fmt("executing SQLite statement '%s'", sqlite3_expanded_sql(stmt.stmt))); + SQLiteError::throw_(stmt.db, fmt("executing SQLite statement '%s'", + sqlite3_expanded_sql(stmt.stmt))); } bool SQLiteStmt::Use::next() { int r = step(); if (r != SQLITE_DONE && r != SQLITE_ROW) - SQLiteError::throw_(stmt.db, fmt("executing SQLite query '%s'", sqlite3_expanded_sql(stmt.stmt))); + SQLiteError::throw_(stmt.db, fmt("executing SQLite query '%s'", + sqlite3_expanded_sql(stmt.stmt))); return r == SQLITE_ROW; } @@ -224,9 +225,7 @@ void handleSQLiteBusy(const SQLiteBusy & e) if (now > lastWarned + 10) { lastWarned = now; - logWarning({ - .msg = hintfmt(e.what()) - }); + logWarning({.msg = hintfmt(e.what())}); } /* Sleep for a while since retrying the transaction right away diff --git a/src/libstore/sqlite.hh b/src/libstore/sqlite.hh index 1d1c553ea505..db0e916670e8 100644 --- a/src/libstore/sqlite.hh +++ b/src/libstore/sqlite.hh @@ -11,16 +11,20 @@ struct sqlite3_stmt; namespace nix { /* RAII wrapper to close a SQLite database automatically. */ -struct SQLite -{ +struct SQLite { sqlite3 * db = 0; - SQLite() { } + SQLite() {} SQLite(const Path & path, bool create = true); SQLite(const SQLite & from) = delete; - SQLite& operator = (const SQLite & from) = delete; - SQLite& operator = (SQLite && from) { db = from.db; from.db = 0; return *this; } + SQLite & operator=(const SQLite & from) = delete; + SQLite & operator=(SQLite && from) + { + db = from.db; + from.db = 0; + return *this; + } ~SQLite(); - operator sqlite3 * () { return db; } + operator sqlite3 *() { return db; } /* Disable synchronous mode, set truncate journal mode. */ void isCache(); @@ -31,34 +35,33 @@ struct SQLite }; /* RAII wrapper to create and destroy SQLite prepared statements. */ -struct SQLiteStmt -{ +struct SQLiteStmt { sqlite3 * db = 0; sqlite3_stmt * stmt = 0; std::string sql; - SQLiteStmt() { } + SQLiteStmt() {} SQLiteStmt(sqlite3 * db, const std::string & sql) { create(db, sql); } void create(sqlite3 * db, const std::string & s); ~SQLiteStmt(); - operator sqlite3_stmt * () { return stmt; } + operator sqlite3_stmt *() { return stmt; } /* Helper for binding / executing statements. */ - class Use - { + class Use { friend struct SQLiteStmt; - private: + + private: SQLiteStmt & stmt; unsigned int curArg = 1; Use(SQLiteStmt & stmt); - public: - + public: ~Use(); /* Bind the next parameter. */ - Use & operator () (std::string_view value, bool notNull = true); - Use & operator () (const unsigned char * data, size_t len, bool notNull = true); - Use & operator () (int64_t value, bool notNull = true); + Use & operator()(std::string_view value, bool notNull = true); + Use & operator()(const unsigned char * data, size_t len, + bool notNull = true); + Use & operator()(int64_t value, bool notNull = true); Use & bind(); // null int step(); @@ -75,16 +78,12 @@ struct SQLiteStmt bool isNull(int col); }; - Use use() - { - return Use(*this); - } + Use use() { return Use(*this); } }; /* RAII helper that ensures transactions are aborted unless explicitly committed. */ -struct SQLiteTxn -{ +struct SQLiteTxn { bool active = false; sqlite3 * db; @@ -95,28 +94,29 @@ struct SQLiteTxn ~SQLiteTxn(); }; - -struct SQLiteError : Error -{ - const char *path; +struct SQLiteError : Error { + const char * path; int errNo, extendedErrNo; template - [[noreturn]] static void throw_(sqlite3 * db, const std::string & fs, const Args & ... args) { + [[noreturn]] static void throw_(sqlite3 * db, const std::string & fs, + const Args &... args) + { throw_(db, hintfmt(fs, args...)); } - SQLiteError(const char *path, int errNo, int extendedErrNo, hintformat && hf); - -protected: + SQLiteError(const char * path, int errNo, int extendedErrNo, + hintformat && hf); + protected: template - SQLiteError(const char *path, int errNo, int extendedErrNo, const std::string & fs, const Args & ... args) - : SQLiteError(path, errNo, extendedErrNo, hintfmt(fs, args...)) - { } + SQLiteError(const char * path, int errNo, int extendedErrNo, + const std::string & fs, const Args &... args) + : SQLiteError(path, errNo, extendedErrNo, hintfmt(fs, args...)) + { + } [[noreturn]] static void throw_(sqlite3 * db, hintformat && hf); - }; MakeError(SQLiteBusy, SQLiteError); @@ -125,8 +125,7 @@ void handleSQLiteBusy(const SQLiteBusy & e); /* Convenience function for retrying a SQLite transaction when the database is busy. */ -template -T retrySQLite(F && fun) +template T retrySQLite(F && fun) { while (true) { try { diff --git a/src/libstore/ssh-store.cc b/src/libstore/ssh-store.cc index 62daa838ce63..131fc37d5ab0 100644 --- a/src/libstore/ssh-store.cc +++ b/src/libstore/ssh-store.cc @@ -8,37 +8,36 @@ namespace nix { -struct SSHStoreConfig : virtual RemoteStoreConfig -{ +struct SSHStoreConfig : virtual RemoteStoreConfig { using RemoteStoreConfig::RemoteStoreConfig; - const Setting sshKey{(StoreConfig*) this, "", "ssh-key", "path to an SSH private key"}; - const Setting sshPublicHostKey{(StoreConfig*) this, "", "base64-ssh-public-host-key", "The public half of the host's SSH key"}; - const Setting compress{(StoreConfig*) this, false, "compress", "whether to compress the connection"}; - const Setting remoteProgram{(StoreConfig*) this, "nix-daemon", "remote-program", "path to the nix-daemon executable on the remote system"}; - const Setting remoteStore{(StoreConfig*) this, "", "remote-store", "URI of the store on the remote system"}; + const Setting sshKey{(StoreConfig *) this, "", "ssh-key", + "path to an SSH private key"}; + const Setting sshPublicHostKey{ + (StoreConfig *) this, "", "base64-ssh-public-host-key", + "The public half of the host's SSH key"}; + const Setting compress{(StoreConfig *) this, false, "compress", + "whether to compress the connection"}; + const Setting remoteProgram{ + (StoreConfig *) this, "nix-daemon", "remote-program", + "path to the nix-daemon executable on the remote system"}; + const Setting remoteStore{ + (StoreConfig *) this, "", "remote-store", + "URI of the store on the remote system"}; const std::string name() override { return "SSH Store"; } }; -class SSHStore : public virtual SSHStoreConfig, public virtual RemoteStore -{ -public: - - SSHStore(const std::string & scheme, const std::string & host, const Params & params) - : StoreConfig(params) - , RemoteStoreConfig(params) - , SSHStoreConfig(params) - , Store(params) - , RemoteStore(params) - , host(host) - , master( - host, - sshKey, - sshPublicHostKey, - // Use SSH master only if using more than 1 connection. - connections->capacity() > 1, - compress) +class SSHStore : public virtual SSHStoreConfig, public virtual RemoteStore { + public: + SSHStore(const std::string & scheme, const std::string & host, + const Params & params) + : StoreConfig(params), RemoteStoreConfig(params), + SSHStoreConfig(params), Store(params), RemoteStore(params), + host(host), + master(host, sshKey, sshPublicHostKey, + // Use SSH master only if using more than 1 connection. + connections->capacity() > 1, compress) { } @@ -49,23 +48,19 @@ class SSHStore : public virtual SSHStoreConfig, public virtual RemoteStore return *uriSchemes().begin() + "://" + host; } - bool sameMachine() override - { return false; } + bool sameMachine() override { return false; } // FIXME extend daemon protocol, move implementation to RemoteStore std::optional getBuildLog(const StorePath & path) override - { unsupported("getBuildLog"); } - -private: - - struct Connection : RemoteStore::Connection { + unsupported("getBuildLog"); + } + + private: + struct Connection : RemoteStore::Connection { std::unique_ptr sshConn; - void closeWrite() override - { - sshConn->in.close(); - } + void closeWrite() override { sshConn->in.close(); } }; ref openConnection() override; @@ -74,8 +69,7 @@ class SSHStore : public virtual SSHStoreConfig, public virtual RemoteStore SSHMaster master; - void setOptions(RemoteStore::Connection & conn) override - { + void setOptions(RemoteStore::Connection & conn) override{ /* TODO Add a way to explicitly ask for some options to be forwarded. One option: A way to query the daemon for its settings, and then a series of params to SSHStore like @@ -89,8 +83,10 @@ ref SSHStore::openConnection() { auto conn = make_ref(); conn->sshConn = master.startCommand( - fmt("%s --stdio", remoteProgram) - + (remoteStore.get() == "" ? "" : " --store " + shellEscape(remoteStore.get()))); + fmt("%s --stdio", remoteProgram) + + (remoteStore.get() == "" + ? "" + : " --store " + shellEscape(remoteStore.get()))); conn->to = FdSink(conn->sshConn->in.get()); conn->from = FdSource(conn->sshConn->out.get()); return conn; diff --git a/src/libstore/ssh.cc b/src/libstore/ssh.cc index 1bbad71f259c..5e094d5f86c1 100644 --- a/src/libstore/ssh.cc +++ b/src/libstore/ssh.cc @@ -2,20 +2,19 @@ namespace nix { -SSHMaster::SSHMaster(const std::string & host, const std::string & keyFile, const std::string & sshPublicHostKey, bool useMaster, bool compress, int logFD) - : host(host) - , fakeSSH(host == "localhost") - , keyFile(keyFile) - , sshPublicHostKey(sshPublicHostKey) - , useMaster(useMaster && !fakeSSH) - , compress(compress) - , logFD(logFD) +SSHMaster::SSHMaster(const std::string & host, const std::string & keyFile, + const std::string & sshPublicHostKey, bool useMaster, + bool compress, int logFD) + : host(host), fakeSSH(host == "localhost"), keyFile(keyFile), + sshPublicHostKey(sshPublicHostKey), useMaster(useMaster && !fakeSSH), + compress(compress), logFD(logFD) { if (host == "" || hasPrefix(host, "-")) throw Error("invalid SSH host name '%s'", host); auto state(state_.lock()); - state->tmpDir = std::make_unique(createTempDir("", "nix", true, true, 0700)); + state->tmpDir = std::make_unique( + createTempDir("", "nix", true, true, 0700)); } void SSHMaster::addCommonSSHOpts(Strings & args) @@ -29,15 +28,18 @@ void SSHMaster::addCommonSSHOpts(Strings & args) if (!sshPublicHostKey.empty()) { Path fileName = (Path) *state->tmpDir + "/host-key"; auto p = host.rfind("@"); - std::string thost = p != std::string::npos ? std::string(host, p + 1) : host; - writeFile(fileName, thost + " " + base64Decode(sshPublicHostKey) + "\n"); + std::string thost = + p != std::string::npos ? std::string(host, p + 1) : host; + writeFile(fileName, + thost + " " + base64Decode(sshPublicHostKey) + "\n"); args.insert(args.end(), {"-oUserKnownHostsFile=" + fileName}); } if (compress) args.push_back("-C"); } -std::unique_ptr SSHMaster::startCommand(const std::string & command) +std::unique_ptr +SSHMaster::startCommand(const std::string & command) { Path socketPath = startMaster(); @@ -49,39 +51,40 @@ std::unique_ptr SSHMaster::startCommand(const std::string ProcessOptions options; options.dieWithParent = false; - conn->sshPid = startProcess([&]() { - restoreProcessContext(); - - close(in.writeSide.get()); - close(out.readSide.get()); - - if (dup2(in.readSide.get(), STDIN_FILENO) == -1) - throw SysError("duping over stdin"); - if (dup2(out.writeSide.get(), STDOUT_FILENO) == -1) - throw SysError("duping over stdout"); - if (logFD != -1 && dup2(logFD, STDERR_FILENO) == -1) - throw SysError("duping over stderr"); - - Strings args; - - if (fakeSSH) { - args = { "bash", "-c" }; - } else { - args = { "ssh", host.c_str(), "-x", "-a" }; - addCommonSSHOpts(args); - if (socketPath != "") - args.insert(args.end(), {"-S", socketPath}); - if (verbosity >= lvlChatty) - args.push_back("-v"); - } - - args.push_back(command); - execvp(args.begin()->c_str(), stringsToCharPtrs(args).data()); - - // could not exec ssh/bash - throw SysError("unable to execute '%s'", args.front()); - }, options); - + conn->sshPid = startProcess( + [&]() { + restoreProcessContext(); + + close(in.writeSide.get()); + close(out.readSide.get()); + + if (dup2(in.readSide.get(), STDIN_FILENO) == -1) + throw SysError("duping over stdin"); + if (dup2(out.writeSide.get(), STDOUT_FILENO) == -1) + throw SysError("duping over stdout"); + if (logFD != -1 && dup2(logFD, STDERR_FILENO) == -1) + throw SysError("duping over stderr"); + + Strings args; + + if (fakeSSH) { + args = {"bash", "-c"}; + } else { + args = {"ssh", host.c_str(), "-x", "-a"}; + addCommonSSHOpts(args); + if (socketPath != "") + args.insert(args.end(), {"-S", socketPath}); + if (verbosity >= lvlChatty) + args.push_back("-v"); + } + + args.push_back(command); + execvp(args.begin()->c_str(), stringsToCharPtrs(args).data()); + + // could not exec ssh/bash + throw SysError("unable to execute '%s'", args.front()); + }, + options); in.readSide = -1; out.writeSide = -1; @@ -94,12 +97,13 @@ std::unique_ptr SSHMaster::startCommand(const std::string Path SSHMaster::startMaster() { - if (!useMaster) return ""; + if (!useMaster) + return ""; auto state(state_.lock()); - if (state->sshMaster != -1) return state->socketPath; - + if (state->sshMaster != -1) + return state->socketPath; state->socketPath = (Path) *state->tmpDir + "/ssh.sock"; @@ -109,33 +113,36 @@ Path SSHMaster::startMaster() ProcessOptions options; options.dieWithParent = false; - state->sshMaster = startProcess([&]() { - restoreProcessContext(); + state->sshMaster = startProcess( + [&]() { + restoreProcessContext(); - close(out.readSide.get()); + close(out.readSide.get()); - if (dup2(out.writeSide.get(), STDOUT_FILENO) == -1) - throw SysError("duping over stdout"); + if (dup2(out.writeSide.get(), STDOUT_FILENO) == -1) + throw SysError("duping over stdout"); - Strings args = - { "ssh", host.c_str(), "-M", "-N", "-S", state->socketPath - , "-o", "LocalCommand=echo started" - , "-o", "PermitLocalCommand=yes" - }; - if (verbosity >= lvlChatty) - args.push_back("-v"); - addCommonSSHOpts(args); - execvp(args.begin()->c_str(), stringsToCharPtrs(args).data()); + Strings args = {"ssh", host.c_str(), + "-M", "-N", + "-S", state->socketPath, + "-o", "LocalCommand=echo started", + "-o", "PermitLocalCommand=yes"}; + if (verbosity >= lvlChatty) + args.push_back("-v"); + addCommonSSHOpts(args); + execvp(args.begin()->c_str(), stringsToCharPtrs(args).data()); - throw SysError("unable to execute '%s'", args.front()); - }, options); + throw SysError("unable to execute '%s'", args.front()); + }, + options); out.writeSide = -1; std::string reply; try { reply = readLine(out.readSide.get()); - } catch (EndOfFile & e) { } + } catch (EndOfFile & e) { + } if (reply != "started") throw Error("failed to start SSH master connection to '%s'", host); diff --git a/src/libstore/ssh.hh b/src/libstore/ssh.hh index dabbcedda095..894220562fa8 100644 --- a/src/libstore/ssh.hh +++ b/src/libstore/ssh.hh @@ -5,10 +5,8 @@ namespace nix { -class SSHMaster -{ -private: - +class SSHMaster { + private: const std::string host; bool fakeSSH; const std::string keyFile; @@ -17,8 +15,7 @@ private: const bool compress; const int logFD; - struct State - { + struct State { Pid sshMaster; std::unique_ptr tmpDir; Path socketPath; @@ -28,12 +25,12 @@ private: void addCommonSSHOpts(Strings & args); -public: - - SSHMaster(const std::string & host, const std::string & keyFile, const std::string & sshPublicHostKey, bool useMaster, bool compress, int logFD = -1); + public: + SSHMaster(const std::string & host, const std::string & keyFile, + const std::string & sshPublicHostKey, bool useMaster, + bool compress, int logFD = -1); - struct Connection - { + struct Connection { Pid sshPid; AutoCloseFD out, in; }; diff --git a/src/libstore/store-api.cc b/src/libstore/store-api.cc index 53b1a8777f3e..75f0801e1db3 100644 --- a/src/libstore/store-api.cc +++ b/src/libstore/store-api.cc @@ -16,13 +16,11 @@ namespace nix { - bool Store::isInStore(const Path & path) const { return isInDir(path, storeDir); } - std::pair Store::toStorePath(const Path & path) const { if (!isInStore(path)) @@ -31,15 +29,16 @@ std::pair Store::toStorePath(const Path & path) const if (slash == Path::npos) return {parseStorePath(path), ""}; else - return {parseStorePath(std::string_view(path).substr(0, slash)), path.substr(slash)}; + return {parseStorePath(std::string_view(path).substr(0, slash)), + path.substr(slash)}; } - Path Store::followLinksToStore(std::string_view _path) const { Path path = absPath(std::string(_path)); while (!isInStore(path)) { - if (!isLink(path)) break; + if (!isLink(path)) + break; auto target = readLink(path); path = absPath(target, dirOf(path)); } @@ -48,13 +47,11 @@ Path Store::followLinksToStore(std::string_view _path) const return path; } - StorePath Store::followLinksToStorePath(std::string_view path) const { return toStorePath(followLinksToStore(path)).first; } - /* Store paths have the following form: = /- @@ -134,83 +131,81 @@ StorePath Store::followLinksToStorePath(std::string_view path) const "source:". */ - -StorePath Store::makeStorePath(std::string_view type, - std::string_view hash, std::string_view name) const +StorePath Store::makeStorePath(std::string_view type, std::string_view hash, + std::string_view name) const { /* e.g., "source:sha256:1abc...:/nix/store:foo.tar.gz" */ - auto s = std::string(type) + ":" + std::string(hash) - + ":" + storeDir + ":" + std::string(name); + auto s = std::string(type) + ":" + std::string(hash) + ":" + storeDir + + ":" + std::string(name); auto h = compressHash(hashString(htSHA256, s), 20); return StorePath(h, name); } - -StorePath Store::makeStorePath(std::string_view type, - const Hash & hash, std::string_view name) const +StorePath Store::makeStorePath(std::string_view type, const Hash & hash, + std::string_view name) const { return makeStorePath(type, hash.to_string(Base16, true), name); } - -StorePath Store::makeOutputPath(std::string_view id, - const Hash & hash, std::string_view name) const +StorePath Store::makeOutputPath(std::string_view id, const Hash & hash, + std::string_view name) const { - return makeStorePath("output:" + std::string { id }, hash, outputPathName(name, id)); + return makeStorePath("output:" + std::string{id}, hash, + outputPathName(name, id)); } - -static std::string makeType( - const Store & store, - std::string && type, - const StorePathSet & references, - bool hasSelfReference = false) +static std::string makeType(const Store & store, std::string && type, + const StorePathSet & references, + bool hasSelfReference = false) { for (auto & i : references) { type += ":"; type += store.printStorePath(i); } - if (hasSelfReference) type += ":self"; + if (hasSelfReference) + type += ":self"; return std::move(type); } - -StorePath Store::makeFixedOutputPath( - FileIngestionMethod method, - const Hash & hash, - std::string_view name, - const StorePathSet & references, - bool hasSelfReference) const +StorePath Store::makeFixedOutputPath(FileIngestionMethod method, + const Hash & hash, std::string_view name, + const StorePathSet & references, + bool hasSelfReference) const { if (hash.type == htSHA256 && method == FileIngestionMethod::Recursive) { - return makeStorePath(makeType(*this, "source", references, hasSelfReference), hash, name); + return makeStorePath( + makeType(*this, "source", references, hasSelfReference), hash, + name); } else { assert(references.empty()); - return makeStorePath("output:out", + return makeStorePath( + "output:out", hashString(htSHA256, - "fixed:out:" - + makeFileIngestionPrefix(method) - + hash.to_string(Base16, true) + ":"), + "fixed:out:" + makeFileIngestionPrefix(method) + + hash.to_string(Base16, true) + ":"), name); } } -StorePath Store::makeFixedOutputPathFromCA(std::string_view name, ContentAddress ca, - const StorePathSet & references, bool hasSelfReference) const +StorePath Store::makeFixedOutputPathFromCA(std::string_view name, + ContentAddress ca, + const StorePathSet & references, + bool hasSelfReference) const { // New template - return std::visit(overloaded { - [&](const TextHash & th) { - return makeTextPath(name, th.hash, references); - }, - [&](const FixedOutputHash & fsh) { - return makeFixedOutputPath(fsh.method, fsh.hash, name, references, hasSelfReference); - } - }, ca); + return std::visit( + overloaded{[&](const TextHash & th) { + return makeTextPath(name, th.hash, references); + }, + [&](const FixedOutputHash & fsh) { + return makeFixedOutputPath(fsh.method, fsh.hash, name, + references, hasSelfReference); + }}, + ca); } StorePath Store::makeTextPath(std::string_view name, const Hash & hash, - const StorePathSet & references) const + const StorePathSet & references) const { assert(hash.type == htSHA256); /* Stuff the references (if any) into the type. This is a bit @@ -219,34 +214,28 @@ StorePath Store::makeTextPath(std::string_view name, const Hash & hash, return makeStorePath(makeType(*this, "text", references), hash, name); } - -std::pair Store::computeStorePathForPath(std::string_view name, - const Path & srcPath, FileIngestionMethod method, HashType hashAlgo, PathFilter & filter) const +std::pair +Store::computeStorePathForPath(std::string_view name, const Path & srcPath, + FileIngestionMethod method, HashType hashAlgo, + PathFilter & filter) const { Hash h = method == FileIngestionMethod::Recursive - ? hashPath(hashAlgo, srcPath, filter).first - : hashFile(hashAlgo, srcPath); + ? hashPath(hashAlgo, srcPath, filter).first + : hashFile(hashAlgo, srcPath); return std::make_pair(makeFixedOutputPath(method, h, name), h); } - -StorePath Store::computeStorePathForText( - std::string_view name, - std::string_view s, - const StorePathSet & references) const +StorePath Store::computeStorePathForText(std::string_view name, + std::string_view s, + const StorePathSet & references) const { return makeTextPath(name, hashString(htSHA256, s), references); } - -StorePath Store::addToStore( - std::string_view name, - const Path & _srcPath, - FileIngestionMethod method, - HashType hashAlgo, - PathFilter & filter, - RepairFlag repair, - const StorePathSet & references) +StorePath Store::addToStore(std::string_view name, const Path & _srcPath, + FileIngestionMethod method, HashType hashAlgo, + PathFilter & filter, RepairFlag repair, + const StorePathSet & references) { Path srcPath(absPath(_srcPath)); auto source = sinkToSource([&](Sink & sink) { @@ -255,14 +244,12 @@ StorePath Store::addToStore( else readFile(srcPath, sink); }); - return addToStoreFromDump(*source, name, method, hashAlgo, repair, references); + return addToStoreFromDump(*source, name, method, hashAlgo, repair, + references); } - -void Store::addMultipleToStore( - Source & source, - RepairFlag repair, - CheckSigsFlag checkSigs) +void Store::addMultipleToStore(Source & source, RepairFlag repair, + CheckSigsFlag checkSigs) { auto expected = readNum(source); for (uint64_t i = 0; i < expected; ++i) { @@ -272,7 +259,6 @@ void Store::addMultipleToStore( } } - /* The aim of this function is to compute in one pass the correct ValidPathInfo for the files that we are trying to add to the store. To accomplish that in one @@ -297,37 +283,36 @@ digraph graphname { } */ ValidPathInfo Store::addToStoreSlow(std::string_view name, const Path & srcPath, - FileIngestionMethod method, HashType hashAlgo, - std::optional expectedCAHash) + FileIngestionMethod method, + HashType hashAlgo, + std::optional expectedCAHash) { - HashSink narHashSink { htSHA256 }; - HashSink caHashSink { hashAlgo }; + HashSink narHashSink{htSHA256}; + HashSink caHashSink{hashAlgo}; /* Note that fileSink and unusualHashTee must be mutually exclusive, since they both write to caHashSink. Note that that requisite is currently true because the former is only used in the flat case. */ - RetrieveRegularNARSink fileSink { caHashSink }; - TeeSink unusualHashTee { narHashSink, caHashSink }; + RetrieveRegularNARSink fileSink{caHashSink}; + TeeSink unusualHashTee{narHashSink, caHashSink}; - auto & narSink = method == FileIngestionMethod::Recursive && hashAlgo != htSHA256 - ? static_cast(unusualHashTee) - : narHashSink; + auto & narSink = + method == FileIngestionMethod::Recursive && hashAlgo != htSHA256 + ? static_cast(unusualHashTee) + : narHashSink; /* Functionally, this means that fileSource will yield the content of srcPath. The fact that we use scratchpadSink as a temporary buffer here is an implementation detail. */ - auto fileSource = sinkToSource([&](Sink & scratchpadSink) { - dumpPath(srcPath, scratchpadSink); - }); + auto fileSource = sinkToSource( + [&](Sink & scratchpadSink) { dumpPath(srcPath, scratchpadSink); }); /* tapped provides the same data as fileSource, but we also write all the information to narSink. */ - TeeSource tapped { *fileSource, narSink }; + TeeSource tapped{*fileSource, narSink}; ParseSink blank; - auto & parseSink = method == FileIngestionMethod::Flat - ? fileSink - : blank; + auto & parseSink = method == FileIngestionMethod::Flat ? fileSink : blank; /* The information that flows from tapped (besides being replicated in narSink), is now put in parseSink. */ @@ -338,23 +323,22 @@ ValidPathInfo Store::addToStoreSlow(std::string_view name, const Path & srcPath, auto [narHash, narSize] = narHashSink.finish(); auto hash = method == FileIngestionMethod::Recursive && hashAlgo == htSHA256 - ? narHash - : caHashSink.finish().first; + ? narHash + : caHashSink.finish().first; if (expectedCAHash && expectedCAHash != hash) throw Error("hash mismatch for '%s'", srcPath); - ValidPathInfo info { + ValidPathInfo info{ makeFixedOutputPath(method, hash, name), narHash, }; info.narSize = narSize; - info.ca = FixedOutputHash { .method = method, .hash = hash }; + info.ca = FixedOutputHash{.method = method, .hash = hash}; if (!isValidPath(info.path)) { - auto source = sinkToSource([&](Sink & scratchpadSink) { - dumpPath(srcPath, scratchpadSink); - }); + auto source = sinkToSource( + [&](Sink & scratchpadSink) { dumpPath(srcPath, scratchpadSink); }); addToStore(info, *source); } @@ -375,42 +359,41 @@ StringSet StoreConfig::getDefaultSystemFeatures() } Store::Store(const Params & params) - : StoreConfig(params) - , state({(size_t) pathInfoCacheSize}) + : StoreConfig(params), state({(size_t) pathInfoCacheSize}) { } - -std::string Store::getUri() -{ - return ""; -} +std::string Store::getUri() { return ""; } bool Store::PathInfoCacheValue::isKnownNow() { - std::chrono::duration ttl = didExist() - ? std::chrono::seconds(settings.ttlPositiveNarInfoCache) - : std::chrono::seconds(settings.ttlNegativeNarInfoCache); + std::chrono::duration ttl = + didExist() ? std::chrono::seconds(settings.ttlPositiveNarInfoCache) + : std::chrono::seconds(settings.ttlNegativeNarInfoCache); return std::chrono::steady_clock::now() < time_point + ttl; } -std::map> Store::queryPartialDerivationOutputMap(const StorePath & path) +std::map> +Store::queryPartialDerivationOutputMap(const StorePath & path) { std::map> outputs; auto drv = readInvalidDerivation(path); - for (auto& [outputName, output] : drv.outputsAndOptPaths(*this)) { + for (auto & [outputName, output] : drv.outputsAndOptPaths(*this)) { outputs.emplace(outputName, output.second); } return outputs; } -OutputPathMap Store::queryDerivationOutputMap(const StorePath & path) { +OutputPathMap Store::queryDerivationOutputMap(const StorePath & path) +{ auto resp = queryPartialDerivationOutputMap(path); OutputPathMap result; for (auto & [outName, optOutPath] : resp) { if (!optOutPath) - throw Error("output '%s' of derivation '%s' has no store path mapped to it", outName, printStorePath(path)); + throw Error( + "output '%s' of derivation '%s' has no store path mapped to it", + outName, printStorePath(path)); result.insert_or_assign(outName, *optOutPath); } return result; @@ -420,7 +403,7 @@ StorePathSet Store::queryDerivationOutputs(const StorePath & path) { auto outputMap = this->queryDerivationOutputMap(path); StorePathSet outputPaths; - for (auto & i: outputMap) { + for (auto & i : outputMap) { outputPaths.emplace(std::move(i.second)); } return outputPaths; @@ -430,7 +413,8 @@ bool Store::isValidPath(const StorePath & storePath) { { auto state_(state.lock()); - auto res = state_->pathInfoCache.get(std::string(storePath.to_string())); + auto res = + state_->pathInfoCache.get(std::string(storePath.to_string())); if (res && res->isKnownNow()) { stats.narInfoReadAverted++; return res->didExist(); @@ -438,12 +422,16 @@ bool Store::isValidPath(const StorePath & storePath) } if (diskCache) { - auto res = diskCache->lookupNarInfo(getUri(), std::string(storePath.hashPart())); + auto res = diskCache->lookupNarInfo(getUri(), + std::string(storePath.hashPart())); if (res.first != NarInfoDiskCache::oUnknown) { stats.narInfoReadAverted++; auto state_(state.lock()); - state_->pathInfoCache.upsert(std::string(storePath.to_string()), - res.first == NarInfoDiskCache::oInvalid ? PathInfoCacheValue{} : PathInfoCacheValue { .value = res.second }); + state_->pathInfoCache.upsert( + std::string(storePath.to_string()), + res.first == NarInfoDiskCache::oInvalid + ? PathInfoCacheValue{} + : PathInfoCacheValue{.value = res.second}); return res.first == NarInfoDiskCache::oValid; } } @@ -452,12 +440,12 @@ bool Store::isValidPath(const StorePath & storePath) if (diskCache && !valid) // FIXME: handle valid = true case. - diskCache->upsertNarInfo(getUri(), std::string(storePath.hashPart()), 0); + diskCache->upsertNarInfo(getUri(), std::string(storePath.hashPart()), + 0); return valid; } - /* Default implementation for stores that only implement queryPathInfoUncached(). */ bool Store::isValidPathUncached(const StorePath & path) @@ -470,44 +458,43 @@ bool Store::isValidPathUncached(const StorePath & path) } } - ref Store::queryPathInfo(const StorePath & storePath) { std::promise> promise; queryPathInfo(storePath, - {[&](std::future> result) { - try { - promise.set_value(result.get()); - } catch (...) { - promise.set_exception(std::current_exception()); - } - }}); + {[&](std::future> result) { + try { + promise.set_value(result.get()); + } catch (...) { + promise.set_exception(std::current_exception()); + } + }}); return promise.get_future().get(); } - static bool goodStorePath(const StorePath & expected, const StorePath & actual) { - return - expected.hashPart() == actual.hashPart() - && (expected.name() == Store::MissingName || expected.name() == actual.name()); + return expected.hashPart() == actual.hashPart() && + (expected.name() == Store::MissingName || + expected.name() == actual.name()); } - void Store::queryPathInfo(const StorePath & storePath, - Callback> callback) noexcept + Callback> callback) noexcept { auto hashPart = std::string(storePath.hashPart()); try { { - auto res = state.lock()->pathInfoCache.get(std::string(storePath.to_string())); + auto res = state.lock()->pathInfoCache.get( + std::string(storePath.to_string())); if (res && res->isKnownNow()) { stats.narInfoReadAverted++; if (!res->didExist()) - throw InvalidPath("path '%s' is not valid", printStorePath(storePath)); + throw InvalidPath("path '%s' is not valid", + printStorePath(storePath)); return callback(ref(res->value)); } } @@ -518,23 +505,30 @@ void Store::queryPathInfo(const StorePath & storePath, stats.narInfoReadAverted++; { auto state_(state.lock()); - state_->pathInfoCache.upsert(std::string(storePath.to_string()), - res.first == NarInfoDiskCache::oInvalid ? PathInfoCacheValue{} : PathInfoCacheValue{ .value = res.second }); + state_->pathInfoCache.upsert( + std::string(storePath.to_string()), + res.first == NarInfoDiskCache::oInvalid + ? PathInfoCacheValue{} + : PathInfoCacheValue{.value = res.second}); if (res.first == NarInfoDiskCache::oInvalid || !goodStorePath(storePath, res.second->path)) - throw InvalidPath("path '%s' is not valid", printStorePath(storePath)); + throw InvalidPath("path '%s' is not valid", + printStorePath(storePath)); } return callback(ref(res.second)); } } - } catch (...) { return callback.rethrow(); } - - auto callbackPtr = std::make_shared(std::move(callback)); + } catch (...) { + return callback.rethrow(); + } - queryPathInfoUncached(storePath, - {[this, storePath, hashPart, callbackPtr](std::future> fut) { + auto callbackPtr = + std::make_shared(std::move(callback)); + queryPathInfoUncached( + storePath, {[this, storePath, hashPart, callbackPtr]( + std::future> fut) { try { auto info = fut.get(); @@ -543,36 +537,41 @@ void Store::queryPathInfo(const StorePath & storePath, { auto state_(state.lock()); - state_->pathInfoCache.upsert(std::string(storePath.to_string()), PathInfoCacheValue { .value = info }); + state_->pathInfoCache.upsert( + std::string(storePath.to_string()), + PathInfoCacheValue{.value = info}); } if (!info || !goodStorePath(storePath, info->path)) { stats.narInfoMissing++; - throw InvalidPath("path '%s' is not valid", printStorePath(storePath)); + throw InvalidPath("path '%s' is not valid", + printStorePath(storePath)); } (*callbackPtr)(ref(info)); - } catch (...) { callbackPtr->rethrow(); } + } catch (...) { + callbackPtr->rethrow(); + } }}); } -void Store::queryRealisation(const DrvOutput & id, - Callback> callback) noexcept +void Store::queryRealisation( + const DrvOutput & id, + Callback> callback) noexcept { try { if (diskCache) { - auto [cacheOutcome, maybeCachedRealisation] - = diskCache->lookupRealisation(getUri(), id); + auto [cacheOutcome, maybeCachedRealisation] = + diskCache->lookupRealisation(getUri(), id); switch (cacheOutcome) { case NarInfoDiskCache::oValid: debug("Returning a cached realisation for %s", id.to_string()); callback(maybeCachedRealisation); return; case NarInfoDiskCache::oInvalid: - debug( - "Returning a cached missing realisation for %s", - id.to_string()); + debug("Returning a cached missing realisation for %s", + id.to_string()); callback(nullptr); return; case NarInfoDiskCache::oUnknown: @@ -583,13 +582,12 @@ void Store::queryRealisation(const DrvOutput & id, return callback.rethrow(); } - auto callbackPtr - = std::make_shared(std::move(callback)); + auto callbackPtr = + std::make_shared(std::move(callback)); queryRealisationUncached( - id, - { [this, id, callbackPtr]( - std::future> fut) { + id, {[this, id, callbackPtr]( + std::future> fut) { try { auto info = fut.get(); @@ -605,7 +603,7 @@ void Store::queryRealisation(const DrvOutput & id, } catch (...) { callbackPtr->rethrow(); } - } }); + }}); } std::shared_ptr Store::queryRealisation(const DrvOutput & id) @@ -613,14 +611,13 @@ std::shared_ptr Store::queryRealisation(const DrvOutput & id) using RealPtr = std::shared_ptr; std::promise promise; - queryRealisation(id, - {[&](std::future result) { - try { - promise.set_value(result.get()); - } catch (...) { - promise.set_exception(std::current_exception()); - } - }}); + queryRealisation(id, {[&](std::future result) { + try { + promise.set_value(result.get()); + } catch (...) { + promise.set_exception(std::current_exception()); + } + }}); return promise.get_future().get(); } @@ -633,24 +630,24 @@ void Store::substitutePaths(const StorePathSet & paths) paths2.push_back(DerivedPath::Opaque{path}); uint64_t downloadSize, narSize; StorePathSet willBuild, willSubstitute, unknown; - queryMissing(paths2, - willBuild, willSubstitute, unknown, downloadSize, narSize); + queryMissing(paths2, willBuild, willSubstitute, unknown, downloadSize, + narSize); if (!willSubstitute.empty()) try { std::vector subs; - for (auto & p : willSubstitute) subs.push_back(DerivedPath::Opaque{p}); + for (auto & p : willSubstitute) + subs.push_back(DerivedPath::Opaque{p}); buildPaths(subs); } catch (Error & e) { logWarning(e.info()); } } - -StorePathSet Store::queryValidPaths(const StorePathSet & paths, SubstituteFlag maybeSubstitute) +StorePathSet Store::queryValidPaths(const StorePathSet & paths, + SubstituteFlag maybeSubstitute) { - struct State - { + struct State { size_t left; StorePathSet valid; std::exception_ptr exc; @@ -663,19 +660,21 @@ StorePathSet Store::queryValidPaths(const StorePathSet & paths, SubstituteFlag m auto doQuery = [&](const Path & path) { checkInterrupt(); - queryPathInfo(parseStorePath(path), {[path, this, &state_, &wakeup](std::future> fut) { - auto state(state_.lock()); - try { - auto info = fut.get(); - state->valid.insert(parseStorePath(path)); - } catch (InvalidPath &) { - } catch (...) { - state->exc = std::current_exception(); - } - assert(state->left); - if (!--state->left) - wakeup.notify_one(); - }}); + queryPathInfo(parseStorePath(path), + {[path, this, &state_, + &wakeup](std::future> fut) { + auto state(state_.lock()); + try { + auto info = fut.get(); + state->valid.insert(parseStorePath(path)); + } catch (InvalidPath &) { + } catch (...) { + state->exc = std::current_exception(); + } + assert(state->left); + if (!--state->left) + wakeup.notify_one(); + }}); }; for (auto & path : paths) @@ -686,19 +685,19 @@ StorePathSet Store::queryValidPaths(const StorePathSet & paths, SubstituteFlag m while (true) { auto state(state_.lock()); if (!state->left) { - if (state->exc) std::rethrow_exception(state->exc); + if (state->exc) + std::rethrow_exception(state->exc); return std::move(state->valid); } state.wait(wakeup); } } - /* Return a string accepted by decodeValidPathInfo() that registers the specified paths as valid. Note: it's the responsibility of the caller to provide a closure. */ std::string Store::makeValidityRegistration(const StorePathSet & paths, - bool showDerivers, bool showHash) + bool showDerivers, bool showHash) { std::string s = ""; @@ -712,7 +711,8 @@ std::string Store::makeValidityRegistration(const StorePathSet & paths, s += (format("%1%\n") % info->narSize).str(); } - auto deriver = showDerivers && info->deriver ? printStorePath(*info->deriver) : ""; + auto deriver = + showDerivers && info->deriver ? printStorePath(*info->deriver) : ""; s += deriver + "\n"; s += (format("%1%\n") % info->references.size()).str(); @@ -724,14 +724,16 @@ std::string Store::makeValidityRegistration(const StorePathSet & paths, return s; } - -StorePathSet Store::exportReferences(const StorePathSet & storePaths, const StorePathSet & inputPaths) +StorePathSet Store::exportReferences(const StorePathSet & storePaths, + const StorePathSet & inputPaths) { StorePathSet paths; for (auto & storePath : storePaths) { if (!inputPaths.count(storePath)) - throw BuildError("cannot export references of path '%s' because it is not in the input closure of the derivation", printStorePath(storePath)); + throw BuildError("cannot export references of path '%s' because it " + "is not in the input closure of the derivation", + printStorePath(storePath)); computeFSClosure({storePath}, paths); } @@ -751,7 +753,9 @@ StorePathSet Store::exportReferences(const StorePathSet & storePaths, const Stor `computeFSClosure` on the output path, rather than derivation itself. That doesn't seem right to me, so I won't try to implemented this for CA derivations. */ - throw UnimplementedError("exportReferences on CA derivations is not yet implemented"); + throw UnimplementedError( + "exportReferences on CA derivations is not yet " + "implemented"); computeFSClosure(*k.second.second, paths); } } @@ -760,11 +764,10 @@ StorePathSet Store::exportReferences(const StorePathSet & storePaths, const Stor return paths; } - -void Store::pathInfoToJSON(JSONPlaceholder & jsonOut, const StorePathSet & storePaths, - bool includeImpureInfo, bool showClosureSize, - Base hashBase, - AllowInvalidFlag allowInvalid) +void Store::pathInfoToJSON(JSONPlaceholder & jsonOut, + const StorePathSet & storePaths, + bool includeImpureInfo, bool showClosureSize, + Base hashBase, AllowInvalidFlag allowInvalid) { auto jsonList = jsonOut.list(); @@ -775,8 +778,7 @@ void Store::pathInfoToJSON(JSONPlaceholder & jsonOut, const StorePathSet & store auto info = queryPathInfo(storePath); jsonPath.attr("path", printStorePath(info->path)); - jsonPath - .attr("narHash", info->narHash.to_string(hashBase, true)) + jsonPath.attr("narHash", info->narHash.to_string(hashBase, true)) .attr("narSize", info->narSize); { @@ -819,11 +821,14 @@ void Store::pathInfoToJSON(JSONPlaceholder & jsonOut, const StorePathSet & store if (!narInfo->url.empty()) jsonPath.attr("url", narInfo->url); if (narInfo->fileHash) - jsonPath.attr("downloadHash", narInfo->fileHash->to_string(hashBase, true)); + jsonPath.attr( + "downloadHash", + narInfo->fileHash->to_string(hashBase, true)); if (narInfo->fileSize) jsonPath.attr("downloadSize", narInfo->fileSize); if (showClosureSize) - jsonPath.attr("closureDownloadSize", closureSizes.second); + jsonPath.attr("closureDownloadSize", + closureSizes.second); } } @@ -834,7 +839,6 @@ void Store::pathInfoToJSON(JSONPlaceholder & jsonOut, const StorePathSet & store } } - std::pair Store::getClosureSize(const StorePath & storePath) { uint64_t totalNarSize = 0, totalDownloadSize = 0; @@ -851,7 +855,6 @@ std::pair Store::getClosureSize(const StorePath & storePath) return {totalNarSize, totalDownloadSize}; } - const Store::Stats & Store::getStats() { { @@ -861,33 +864,28 @@ const Store::Stats & Store::getStats() return stats; } - -static std::string makeCopyPathMessage( - std::string_view srcUri, - std::string_view dstUri, - std::string_view storePath) +static std::string makeCopyPathMessage(std::string_view srcUri, + std::string_view dstUri, + std::string_view storePath) { return srcUri == "local" || srcUri == "daemon" - ? fmt("copying path '%s' to '%s'", storePath, dstUri) - : dstUri == "local" || dstUri == "daemon" - ? fmt("copying path '%s' from '%s'", storePath, srcUri) - : fmt("copying path '%s' from '%s' to '%s'", storePath, srcUri, dstUri); + ? fmt("copying path '%s' to '%s'", storePath, dstUri) + : dstUri == "local" || dstUri == "daemon" + ? fmt("copying path '%s' from '%s'", storePath, srcUri) + : fmt("copying path '%s' from '%s' to '%s'", storePath, srcUri, + dstUri); } - -void copyStorePath( - Store & srcStore, - Store & dstStore, - const StorePath & storePath, - RepairFlag repair, - CheckSigsFlag checkSigs) +void copyStorePath(Store & srcStore, Store & dstStore, + const StorePath & storePath, RepairFlag repair, + CheckSigsFlag checkSigs) { auto srcUri = srcStore.getUri(); auto dstUri = dstStore.getUri(); auto storePathS = srcStore.printStorePath(storePath); Activity act(*logger, lvlInfo, actCopyPath, - makeCopyPathMessage(srcUri, dstUri, storePathS), - {storePathS, srcUri, dstUri}); + makeCopyPathMessage(srcUri, dstUri, storePathS), + {storePathS, srcUri, dstUri}); PushActivity pact(act.id); auto info = srcStore.queryPathInfo(storePath); @@ -897,7 +895,8 @@ void copyStorePath( // recompute store path on the chance dstStore does it differently if (info->ca && info->references.empty()) { auto info2 = make_ref(*info); - info2->path = dstStore.makeFixedOutputPathFromCA(info->path.name(), *info->ca); + info2->path = + dstStore.makeFixedOutputPathFromCA(info->path.name(), *info->ca); if (dstStore.storeDir == srcStore.storeDir) assert(info->path == info2->path); info = info2; @@ -909,28 +908,27 @@ void copyStorePath( info = info2; } - auto source = sinkToSource([&](Sink & sink) { - LambdaSink progressSink([&](std::string_view data) { - total += data.size(); - act.progress(total, info->narSize); + auto source = sinkToSource( + [&](Sink & sink) { + LambdaSink progressSink([&](std::string_view data) { + total += data.size(); + act.progress(total, info->narSize); + }); + TeeSink tee{sink, progressSink}; + srcStore.narFromPath(storePath, tee); + }, + [&]() { + throw EndOfFile("NAR for '%s' fetched from '%s' is incomplete", + srcStore.printStorePath(storePath), + srcStore.getUri()); }); - TeeSink tee { sink, progressSink }; - srcStore.narFromPath(storePath, tee); - }, [&]() { - throw EndOfFile("NAR for '%s' fetched from '%s' is incomplete", srcStore.printStorePath(storePath), srcStore.getUri()); - }); dstStore.addToStore(*info, *source, repair, checkSigs); } - -std::map copyPaths( - Store & srcStore, - Store & dstStore, - const RealisedPath::Set & paths, - RepairFlag repair, - CheckSigsFlag checkSigs, - SubstituteFlag substitute) +std::map +copyPaths(Store & srcStore, Store & dstStore, const RealisedPath::Set & paths, + RepairFlag repair, CheckSigsFlag checkSigs, SubstituteFlag substitute) { StorePathSet storePaths; std::set toplevelRealisations; @@ -941,7 +939,8 @@ std::map copyPaths( toplevelRealisations.insert(*realisation); } } - auto pathsMap = copyPaths(srcStore, dstStore, storePaths, repair, checkSigs, substitute); + auto pathsMap = copyPaths(srcStore, dstStore, storePaths, repair, checkSigs, + substitute); ThreadPool pool; @@ -951,18 +950,19 @@ std::map copyPaths( pool, Realisation::closure(srcStore, toplevelRealisations), [&](const Realisation & current) -> std::set { std::set children; - for (const auto & [drvOutput, _] : current.dependentRealisations) { + for (const auto & [drvOutput, _] : + current.dependentRealisations) { auto currentChild = srcStore.queryRealisation(drvOutput); if (!currentChild) - throw Error( - "incomplete realisation closure: '%s' is a " - "dependency of '%s' but isn't registered", - drvOutput.to_string(), current.id.to_string()); + throw Error("incomplete realisation closure: '%s' is a " + "dependency of '%s' but isn't registered", + drvOutput.to_string(), + current.id.to_string()); children.insert(*currentChild); } return children; }, - [&](const Realisation& current) -> void { + [&](const Realisation & current) -> void { dstStore.registerDrvOutput(current, checkSigs); }); } catch (MissingExperimentalFeature & e) { @@ -978,25 +978,23 @@ std::map copyPaths( return pathsMap; } -std::map copyPaths( - Store & srcStore, - Store & dstStore, - const StorePathSet & storePaths, - RepairFlag repair, - CheckSigsFlag checkSigs, - SubstituteFlag substitute) +std::map +copyPaths(Store & srcStore, Store & dstStore, const StorePathSet & storePaths, + RepairFlag repair, CheckSigsFlag checkSigs, SubstituteFlag substitute) { auto valid = dstStore.queryValidPaths(storePaths, substitute); StorePathSet missing; for (auto & path : storePaths) - if (!valid.count(path)) missing.insert(path); + if (!valid.count(path)) + missing.insert(path); std::map pathsMap; for (auto & path : storePaths) pathsMap.insert_or_assign(path, path); - Activity act(*logger, lvlInfo, actCopyPaths, fmt("copying %d paths", missing.size())); + Activity act(*logger, lvlInfo, actCopyPaths, + fmt("copying %d paths", missing.size())); auto sorted = srcStore.topoSortPaths(missing); std::reverse(sorted.begin(), sorted.end()); @@ -1008,8 +1006,8 @@ std::map copyPaths( auto dstUri = dstStore.getUri(); auto storePathS = srcStore.printStorePath(storePath); Activity act(*logger, lvlInfo, actCopyPath, - makeCopyPathMessage(srcUri, dstUri, storePathS), - {storePathS, srcUri, dstUri}); + makeCopyPathMessage(srcUri, dstUri, storePathS), + {storePathS, srcUri, dstUri}); PushActivity pact(act.id); auto info = srcStore.queryPathInfo(storePath); @@ -1020,7 +1018,7 @@ std::map copyPaths( dstStore.addMultipleToStore(*source, repair, checkSigs); - #if 0 +#if 0 std::atomic nrDone{0}; std::atomic nrFailed{0}; std::atomic bytesExpected{0}; @@ -1098,20 +1096,17 @@ std::map copyPaths( nrDone++; showProgress(); }); - #endif +#endif return pathsMap; } -void copyClosure( - Store & srcStore, - Store & dstStore, - const RealisedPath::Set & paths, - RepairFlag repair, - CheckSigsFlag checkSigs, - SubstituteFlag substitute) +void copyClosure(Store & srcStore, Store & dstStore, + const RealisedPath::Set & paths, RepairFlag repair, + CheckSigsFlag checkSigs, SubstituteFlag substitute) { - if (&srcStore == &dstStore) return; + if (&srcStore == &dstStore) + return; RealisedPath::Set closure; RealisedPath::closure(srcStore, paths, closure); @@ -1119,83 +1114,84 @@ void copyClosure( copyPaths(srcStore, dstStore, closure, repair, checkSigs, substitute); } -void copyClosure( - Store & srcStore, - Store & dstStore, - const StorePathSet & storePaths, - RepairFlag repair, - CheckSigsFlag checkSigs, - SubstituteFlag substitute) +void copyClosure(Store & srcStore, Store & dstStore, + const StorePathSet & storePaths, RepairFlag repair, + CheckSigsFlag checkSigs, SubstituteFlag substitute) { - if (&srcStore == &dstStore) return; + if (&srcStore == &dstStore) + return; StorePathSet closure; srcStore.computeFSClosure(storePaths, closure); copyPaths(srcStore, dstStore, closure, repair, checkSigs, substitute); } -std::optional decodeValidPathInfo(const Store & store, std::istream & str, std::optional hashGiven) +std::optional +decodeValidPathInfo(const Store & store, std::istream & str, + std::optional hashGiven) { std::string path; getline(str, path); - if (str.eof()) { return {}; } + if (str.eof()) { + return {}; + } if (!hashGiven) { std::string s; getline(str, s); auto narHash = Hash::parseAny(s, htSHA256); getline(str, s); auto narSize = string2Int(s); - if (!narSize) throw Error("number expected"); - hashGiven = { narHash, *narSize }; + if (!narSize) + throw Error("number expected"); + hashGiven = {narHash, *narSize}; } ValidPathInfo info(store.parseStorePath(path), hashGiven->first); info.narSize = hashGiven->second; std::string deriver; getline(str, deriver); - if (deriver != "") info.deriver = store.parseStorePath(deriver); + if (deriver != "") + info.deriver = store.parseStorePath(deriver); std::string s; getline(str, s); auto n = string2Int(s); - if (!n) throw Error("number expected"); + if (!n) + throw Error("number expected"); while ((*n)--) { getline(str, s); info.references.insert(store.parseStorePath(s)); } - if (!str || str.eof()) throw Error("missing input"); + if (!str || str.eof()) + throw Error("missing input"); return std::optional(std::move(info)); } - std::string Store::showPaths(const StorePathSet & paths) { std::string s; for (auto & i : paths) { - if (s.size() != 0) s += ", "; + if (s.size() != 0) + s += ", "; s += "'" + printStorePath(i) + "'"; } return s; } - std::string showPaths(const PathSet & paths) { return concatStringsSep(", ", quoteStrings(paths)); } - std::string ValidPathInfo::fingerprint(const Store & store) const { if (narSize == 0) - throw Error("cannot calculate fingerprint of path '%s' because its size is not known", - store.printStorePath(path)); - return - "1;" + store.printStorePath(path) + ";" - + narHash.to_string(Base32, true) + ";" - + std::to_string(narSize) + ";" - + concatStringsSep(",", store.printStorePathSet(references)); + throw Error("cannot calculate fingerprint of path '%s' because its " + "size is not known", + store.printStorePath(path)); + return "1;" + store.printStorePath(path) + ";" + + narHash.to_string(Base32, true) + ";" + std::to_string(narSize) + + ";" + concatStringsSep(",", store.printStorePathSet(references)); } - void ValidPathInfo::sign(const Store & store, const SecretKey & secretKey) { sigs.insert(secretKey.signDetached(fingerprint(store))); @@ -1203,35 +1199,42 @@ void ValidPathInfo::sign(const Store & store, const SecretKey & secretKey) bool ValidPathInfo::isContentAddressed(const Store & store) const { - if (! ca) return false; + if (!ca) + return false; - auto caPath = std::visit(overloaded { - [&](const TextHash & th) { - return store.makeTextPath(path.name(), th.hash, references); - }, - [&](const FixedOutputHash & fsh) { - auto refs = references; - bool hasSelfReference = false; - if (refs.count(path)) { - hasSelfReference = true; - refs.erase(path); - } - return store.makeFixedOutputPath(fsh.method, fsh.hash, path.name(), refs, hasSelfReference); - } - }, *ca); + auto caPath = std::visit( + overloaded{[&](const TextHash & th) { + return store.makeTextPath(path.name(), th.hash, + references); + }, + [&](const FixedOutputHash & fsh) { + auto refs = references; + bool hasSelfReference = false; + if (refs.count(path)) { + hasSelfReference = true; + refs.erase(path); + } + return store.makeFixedOutputPath(fsh.method, fsh.hash, + path.name(), refs, + hasSelfReference); + }}, + *ca); bool res = caPath == path; if (!res) - printError("warning: path '%s' claims to be content-addressed but isn't", store.printStorePath(path)); + printError( + "warning: path '%s' claims to be content-addressed but isn't", + store.printStorePath(path)); return res; } - -size_t ValidPathInfo::checkSignatures(const Store & store, const PublicKeys & publicKeys) const +size_t ValidPathInfo::checkSignatures(const Store & store, + const PublicKeys & publicKeys) const { - if (isContentAddressed(store)) return maxSigs; + if (isContentAddressed(store)) + return maxSigs; size_t good = 0; for (auto & sig : sigs) @@ -1240,13 +1243,13 @@ size_t ValidPathInfo::checkSignatures(const Store & store, const PublicKeys & pu return good; } - -bool ValidPathInfo::checkSignature(const Store & store, const PublicKeys & publicKeys, const std::string & sig) const +bool ValidPathInfo::checkSignature(const Store & store, + const PublicKeys & publicKeys, + const std::string & sig) const { return verifyDetached(fingerprint(store), sig, publicKeys); } - Strings ValidPathInfo::shortRefs() const { Strings refs; @@ -1255,42 +1258,47 @@ Strings ValidPathInfo::shortRefs() const return refs; } - Derivation Store::derivationFromPath(const StorePath & drvPath) { ensurePath(drvPath); return readDerivation(drvPath); } -Derivation readDerivationCommon(Store& store, const StorePath& drvPath, bool requireValidPath) +Derivation readDerivationCommon(Store & store, const StorePath & drvPath, + bool requireValidPath) { auto accessor = store.getFSAccessor(); try { - return parseDerivation(store, + return parseDerivation( + store, accessor->readFile(store.printStorePath(drvPath), requireValidPath), Derivation::nameFromPath(drvPath)); } catch (FormatError & e) { - throw Error("error parsing derivation '%s': %s", store.printStorePath(drvPath), e.msg()); + throw Error("error parsing derivation '%s': %s", + store.printStorePath(drvPath), e.msg()); } } Derivation Store::readDerivation(const StorePath & drvPath) -{ return readDerivationCommon(*this, drvPath, true); } +{ + return readDerivationCommon(*this, drvPath, true); +} Derivation Store::readInvalidDerivation(const StorePath & drvPath) -{ return readDerivationCommon(*this, drvPath, false); } - +{ + return readDerivationCommon(*this, drvPath, false); } +} #include "local-store.hh" #include "uds-remote-store.hh" - namespace nix { /* Split URI into protocol+hierarchy part and its parameter set. */ -std::pair splitUriAndParams(const std::string & uri_) +std::pair +splitUriAndParams(const std::string & uri_) { auto uri(uri_); Store::Params params; @@ -1312,7 +1320,8 @@ static bool isNonUriPath(const std::string & spec) && spec.find("/") != std::string::npos; } -std::shared_ptr openFromNonUri(const std::string & uri, const Store::Params & params) +std::shared_ptr openFromNonUri(const std::string & uri, + const Store::Params & params) { if (uri == "" || uri == "auto") { auto stateDir = getOr(params, "state", settings.nixStateDir); @@ -1320,21 +1329,25 @@ std::shared_ptr openFromNonUri(const std::string & uri, const Store::Para return std::make_shared(params); else if (pathExists(settings.nixDaemonSocketFile)) return std::make_shared(params); - #if __linux__ +#if __linux__ else if (!pathExists(stateDir) && params.empty() && getuid() != 0) { /* If /nix doesn't exist, there is no daemon socket, and we're not root, then automatically set up a chroot store in ~/.local/share/nix/root. */ auto chrootStore = getDataDir() + "/nix/root"; if (!pathExists(chrootStore)) - warn("'/nix' does not exist, so Nix will use '%s' as a chroot store", chrootStore); + warn("'/nix' does not exist, so Nix will use '%s' as a chroot " + "store", + chrootStore); else - debug("'/nix' does not exist, so Nix will use '%s' as a chroot store", chrootStore); + debug("'/nix' does not exist, so Nix will use '%s' as a chroot " + "store", + chrootStore); Store::Params params2; params2["root"] = chrootStore; return std::make_shared(params2); } - #endif +#endif else return std::make_shared(params); } else if (uri == "daemon") { @@ -1356,12 +1369,14 @@ std::shared_ptr openFromNonUri(const std::string & uri, const Store::Para // // This function now ensures that a usable connection string is available: // * If the store to be opened is not an SSH store, nothing will be done. -// * If the URL looks like `root@[::1]` (which is allowed by the URL parser and probably +// * If the URL looks like `root@[::1]` (which is allowed by the URL parser and +// probably // needed to pass further flags), it // will be transformed into `root@::1` for SSH (same for `[::1]` -> `::1`). // * If the URL looks like `root@::1` it will be left as-is. // * In any other case, the string will be left as-is. -static std::string extractConnStr(const std::string &proto, const std::string &connStr) +static std::string extractConnStr(const std::string & proto, + const std::string & connStr) { if (proto.rfind("ssh") != std::string::npos) { std::smatch result; @@ -1379,17 +1394,16 @@ static std::string extractConnStr(const std::string &proto, const std::string &c } ref openStore(const std::string & uri_, - const Store::Params & extraParams) + const Store::Params & extraParams) { auto params = extraParams; try { auto parsedUri = parseURL(uri_); params.insert(parsedUri.query.begin(), parsedUri.query.end()); - auto baseURI = extractConnStr( - parsedUri.scheme, - parsedUri.authority.value_or("") + parsedUri.path - ); + auto baseURI = + extractConnStr(parsedUri.scheme, + parsedUri.authority.value_or("") + parsedUri.path); for (auto implem : *Implementations::registered) { if (implem.uriSchemes.count(parsedUri.scheme)) { @@ -1401,8 +1415,7 @@ ref openStore(const std::string & uri_, } } } - } - catch (BadURL &) { + } catch (BadURL &) { auto [uri, uriParams] = splitUriAndParams(uri_); params.insert(uriParams.begin(), uriParams.end()); @@ -1423,7 +1436,8 @@ std::list> getDefaultSubstituters() StringSet done; auto addStore = [&](const std::string & uri) { - if (!done.insert(uri).second) return; + if (!done.insert(uri).second) + return; try { stores.push_back(openStore(uri)); } catch (Error & e) { @@ -1439,7 +1453,7 @@ std::list> getDefaultSubstituters() }); return stores; - } ()); + }()); return stores; } diff --git a/src/libstore/store-api.hh b/src/libstore/store-api.hh index 0c8a4db5667c..00d5a0f9acdd 100644 --- a/src/libstore/store-api.hh +++ b/src/libstore/store-api.hh @@ -23,7 +23,6 @@ #include #include - namespace nix { /** @@ -69,7 +68,6 @@ class NarInfoDiskCache; class Store; class JSONPlaceholder; - enum CheckSigsFlag : bool { NoCheckSigs = false, CheckSigs = true }; enum SubstituteFlag : bool { NoSubstitute = false, Substitute = true }; enum AllowInvalidFlag : bool { DisallowInvalid = false, AllowInvalid = true }; @@ -77,54 +75,59 @@ enum AllowInvalidFlag : bool { DisallowInvalid = false, AllowInvalid = true }; /* Magic header of exportPath() output (obsolete). */ const uint32_t exportMagic = 0x4558494e; - enum BuildMode { bmNormal, bmRepair, bmCheck }; struct BuildResult; - -struct StoreConfig : public Config -{ +struct StoreConfig : public Config { using Config::Config; StoreConfig() = delete; StringSet getDefaultSystemFeatures(); - virtual ~StoreConfig() { } + virtual ~StoreConfig() {} virtual const std::string name() = 0; - const PathSetting storeDir_{this, false, settings.nixStore, - "store", "path to the Nix store"}; + const PathSetting storeDir_{this, false, settings.nixStore, "store", + "path to the Nix store"}; const Path storeDir = storeDir_; - const Setting pathInfoCacheSize{this, 65536, "path-info-cache-size", "size of the in-memory store path information cache"}; - - const Setting isTrusted{this, false, "trusted", "whether paths from this store can be used as substitutes even when they lack trusted signatures"}; + const Setting pathInfoCacheSize{ + this, 65536, "path-info-cache-size", + "size of the in-memory store path information cache"}; - Setting priority{this, 0, "priority", "priority of this substituter (lower value means higher priority)"}; + const Setting isTrusted{ + this, false, "trusted", + "whether paths from this store can be used as substitutes even when " + "they lack trusted signatures"}; - Setting wantMassQuery{this, false, "want-mass-query", "whether this substituter can be queried efficiently for path validity"}; + Setting priority{ + this, 0, "priority", + "priority of this substituter (lower value means higher priority)"}; - Setting systemFeatures{this, getDefaultSystemFeatures(), - "system-features", - "Optional features that the system this store builds on implements (like \"kvm\")."}; + Setting wantMassQuery{this, false, "want-mass-query", + "whether this substituter can be queried " + "efficiently for path validity"}; + Setting systemFeatures{ + this, getDefaultSystemFeatures(), "system-features", + "Optional features that the system this store builds on implements " + "(like \"kvm\")."}; }; -class Store : public std::enable_shared_from_this, public virtual StoreConfig -{ -public: - +class Store : public std::enable_shared_from_this, + public virtual StoreConfig { + public: typedef std::map Params; -protected: - + protected: struct PathInfoCacheValue { // Time of cache entry creation or update - std::chrono::time_point time_point = std::chrono::steady_clock::now(); + std::chrono::time_point time_point = + std::chrono::steady_clock::now(); // Null if missing std::shared_ptr value; @@ -134,13 +137,10 @@ protected: // Past tense, because a path can only be assumed to exists when // isKnownNow() && didExist() - inline bool didExist() { - return value != nullptr; - } + inline bool didExist() { return value != nullptr; } }; - struct State - { + struct State { LRUCache pathInfoCache; }; @@ -150,14 +150,14 @@ protected: Store(const Params & params); -public: + public: /** * Perform any necessary effectful operation to make the store up and * running */ - virtual void init() {}; + virtual void init(){}; - virtual ~Store() { } + virtual ~Store() {} virtual std::string getUri() = 0; @@ -196,32 +196,35 @@ public: StorePath followLinksToStorePath(std::string_view path) const; /* Constructs a unique store path name. */ - StorePath makeStorePath(std::string_view type, - std::string_view hash, std::string_view name) const; - StorePath makeStorePath(std::string_view type, - const Hash & hash, std::string_view name) const; + StorePath makeStorePath(std::string_view type, std::string_view hash, + std::string_view name) const; + StorePath makeStorePath(std::string_view type, const Hash & hash, + std::string_view name) const; - StorePath makeOutputPath(std::string_view id, - const Hash & hash, std::string_view name) const; + StorePath makeOutputPath(std::string_view id, const Hash & hash, + std::string_view name) const; - StorePath makeFixedOutputPath(FileIngestionMethod method, - const Hash & hash, std::string_view name, - const StorePathSet & references = {}, - bool hasSelfReference = false) const; + StorePath makeFixedOutputPath(FileIngestionMethod method, const Hash & hash, + std::string_view name, + const StorePathSet & references = {}, + bool hasSelfReference = false) const; 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; + 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 store path to which srcPath is to be copied. Returns the store path and the cryptographic hash of the contents of srcPath. */ - std::pair computeStorePathForPath(std::string_view name, - const Path & srcPath, FileIngestionMethod method = FileIngestionMethod::Recursive, - HashType hashAlgo = htSHA256, PathFilter & filter = defaultPathFilter) const; + std::pair computeStorePathForPath( + std::string_view name, const Path & srcPath, + FileIngestionMethod method = FileIngestionMethod::Recursive, + HashType hashAlgo = htSHA256, + PathFilter & filter = defaultPathFilter) const; /* Preparatory part of addTextToStore(). @@ -237,20 +240,16 @@ public: simply yield a different store path, so other users wouldn't be affected), but it has some backwards compatibility issues (the hashing scheme changes), so I'm not doing that for now. */ - StorePath computeStorePathForText( - std::string_view name, - std::string_view s, - const StorePathSet & references) const; + StorePath computeStorePathForText(std::string_view name, std::string_view s, + const StorePathSet & references) const; /* Check whether a path is valid. */ bool isValidPath(const StorePath & path); -protected: - + protected: virtual bool isValidPathUncached(const StorePath & path); -public: - + public: /* If requested, substitute missing paths. This implements nix-copy-closure's --use-substitutes flag. */ @@ -258,8 +257,9 @@ public: /* Query which of the given paths is valid. Optionally, try to substitute missing paths. */ - virtual StorePathSet queryValidPaths(const StorePathSet & paths, - SubstituteFlag maybeSubstitute = NoSubstitute); + virtual StorePathSet + queryValidPaths(const StorePathSet & paths, + SubstituteFlag maybeSubstitute = NoSubstitute); /* Query the set of all valid paths. Note that for some store backends, the name part of store paths may be replaced by 'x' @@ -268,7 +268,9 @@ public: full store path. FIXME: should return a set of std::variant to get rid of this hack. */ virtual StorePathSet queryAllValidPaths() - { unsupported("queryAllValidPaths"); } + { + unsupported("queryAllValidPaths"); + } constexpr static const char * MissingName = "x"; @@ -278,16 +280,16 @@ public: /* Asynchronous version of queryPathInfo(). */ void queryPathInfo(const StorePath & path, - Callback> callback) noexcept; + Callback> callback) noexcept; /* Query the information about a realisation. */ std::shared_ptr queryRealisation(const DrvOutput &); /* Asynchronous version of queryRealisation(). */ - void queryRealisation(const DrvOutput &, + void queryRealisation( + const DrvOutput &, Callback> callback) noexcept; - /* Check whether the given valid path info is sufficiently attested, by either being signed by a trusted public key or content-addressed, in order to be included in the given store. @@ -298,35 +300,35 @@ public: we don't really want to add the dependencies listed in a nar info we don't trust anyyways. */ - virtual bool pathInfoIsUntrusted(const ValidPathInfo &) - { - return true; - } + virtual bool pathInfoIsUntrusted(const ValidPathInfo &) { return true; } - virtual bool realisationIsUntrusted(const Realisation & ) - { - return true; - } + virtual bool realisationIsUntrusted(const Realisation &) { return true; } -protected: - - virtual void queryPathInfoUncached(const StorePath & path, + protected: + virtual void queryPathInfoUncached( + const StorePath & path, Callback> callback) noexcept = 0; - virtual void queryRealisationUncached(const DrvOutput &, + virtual void queryRealisationUncached( + const DrvOutput &, Callback> callback) noexcept = 0; -public: - + public: /* Queries the set of incoming FS references for a store path. The result is not cleared. */ - virtual void queryReferrers(const StorePath & path, StorePathSet & referrers) - { unsupported("queryReferrers"); } + virtual void queryReferrers(const StorePath & path, + StorePathSet & referrers) + { + unsupported("queryReferrers"); + } /* Return all currently valid derivations that have `path' as an output. (Note that the result of `queryDeriver()' is the derivation that was actually used to produce `path', which may not exist anymore.) */ - virtual StorePathSet queryValidDerivers(const StorePath & path) { return {}; }; + virtual StorePathSet queryValidDerivers(const StorePath & path) + { + return {}; + }; /* Query the outputs of the derivation denoted by `path'. */ virtual StorePathSet queryDerivationOutputs(const StorePath & path); @@ -334,7 +336,8 @@ public: /* Query the mapping outputName => outputPath for the given derivation. All outputs are mentioned so ones mising the mapping are mapped to `std::nullopt`. */ - virtual std::map> queryPartialDerivationOutputMap(const StorePath & path); + virtual std::map> + queryPartialDerivationOutputMap(const StorePath & path); /* Query the mapping outputName=>outputPath for the given derivation. Assume every output has a mapping and throw an exception otherwise. */ @@ -342,10 +345,14 @@ public: /* Query the full store path given the hash part of a valid store path, or empty if the path doesn't exist. */ - virtual std::optional queryPathFromHashPart(const std::string & hashPart) = 0; + virtual std::optional + queryPathFromHashPart(const std::string & hashPart) = 0; /* Query which of the given paths have substitutes. */ - virtual StorePathSet querySubstitutablePaths(const StorePathSet & paths) { return {}; }; + virtual StorePathSet querySubstitutablePaths(const StorePathSet & paths) + { + return {}; + }; /* Query substitute info (i.e. references, derivers and download sizes) of a map of paths to their optional ca values. The info @@ -353,37 +360,41 @@ public: returned. If a path does not have substitute info, it's omitted from the resulting ‘infos’ map. */ virtual void querySubstitutablePathInfos(const StorePathCAMap & paths, - SubstitutablePathInfos & infos) { return; }; + SubstitutablePathInfos & infos) + { + return; + }; /* Import a path into the store. */ virtual void addToStore(const ValidPathInfo & info, Source & narSource, - RepairFlag repair = NoRepair, CheckSigsFlag checkSigs = CheckSigs) = 0; + RepairFlag repair = NoRepair, + CheckSigsFlag checkSigs = CheckSigs) = 0; /* Import multiple paths into the store. */ - virtual void addMultipleToStore( - Source & source, - RepairFlag repair = NoRepair, - CheckSigsFlag checkSigs = CheckSigs); + virtual void addMultipleToStore(Source & source, + RepairFlag repair = NoRepair, + CheckSigsFlag checkSigs = CheckSigs); /* Copy the contents of a path to the store and register the validity the resulting path. The resulting path is returned. The function object `filter' can be used to exclude files (see libutil/archive.hh). */ - virtual StorePath addToStore( - std::string_view name, - const Path & srcPath, - FileIngestionMethod method = FileIngestionMethod::Recursive, - HashType hashAlgo = htSHA256, - PathFilter & filter = defaultPathFilter, - RepairFlag repair = NoRepair, - const StorePathSet & references = StorePathSet()); + virtual StorePath + addToStore(std::string_view name, const Path & srcPath, + FileIngestionMethod method = FileIngestionMethod::Recursive, + HashType hashAlgo = htSHA256, + PathFilter & filter = defaultPathFilter, + RepairFlag repair = NoRepair, + const StorePathSet & references = StorePathSet()); /* Copy the contents of a path to the store and register the validity the resulting path, using a constant amount of memory. */ - ValidPathInfo addToStoreSlow(std::string_view name, const Path & srcPath, - FileIngestionMethod method = FileIngestionMethod::Recursive, HashType hashAlgo = htSHA256, - std::optional expectedCAHash = {}); + ValidPathInfo + addToStoreSlow(std::string_view name, const Path & srcPath, + FileIngestionMethod method = FileIngestionMethod::Recursive, + HashType hashAlgo = htSHA256, + std::optional expectedCAHash = {}); /* Like addToStore(), but the contents of the path are contained in `dump', which is either a NAR serialisation (if recursive == @@ -391,22 +402,24 @@ public: false). `dump` may be drained */ // FIXME: remove? - virtual StorePath addToStoreFromDump(Source & dump, std::string_view name, - FileIngestionMethod method = FileIngestionMethod::Recursive, HashType hashAlgo = htSHA256, RepairFlag repair = NoRepair, + virtual StorePath addToStoreFromDump( + Source & dump, std::string_view name, + FileIngestionMethod method = FileIngestionMethod::Recursive, + HashType hashAlgo = htSHA256, RepairFlag repair = NoRepair, const StorePathSet & references = StorePathSet()) - { unsupported("addToStoreFromDump"); } + { + unsupported("addToStoreFromDump"); + } /* Like addToStore, but the contents written to the output path is a regular file containing the given string. */ - virtual StorePath addTextToStore( - std::string_view name, - std::string_view s, - const StorePathSet & references, - RepairFlag repair = NoRepair) = 0; + virtual StorePath addTextToStore(std::string_view name, std::string_view s, + const StorePathSet & references, + RepairFlag repair = NoRepair) = 0; /** - * Add a mapping indicating that `deriver!outputName` maps to the output path - * `output`. + * Add a mapping indicating that `deriver!outputName` maps to the output + * path `output`. * * This is redundant for known-input-addressed and fixed-output derivations * as this information is already present in the drv file, but necessary for @@ -414,9 +427,14 @@ public: * retrieve this information otherwise. */ virtual void registerDrvOutput(const Realisation & output) - { unsupported("registerDrvOutput"); } - virtual void registerDrvOutput(const Realisation & output, CheckSigsFlag checkSigs) - { return registerDrvOutput(output); } + { + unsupported("registerDrvOutput"); + } + virtual void registerDrvOutput(const Realisation & output, + CheckSigsFlag checkSigs) + { + return registerDrvOutput(output); + } /* Write a NAR dump of a store path. */ virtual void narFromPath(const StorePath & path, Sink & sink) = 0; @@ -429,20 +447,19 @@ public: output paths can be created by running the builder, after recursively building any sub-derivations. For inputs that are not derivations, substitute them. */ - virtual void buildPaths( - const std::vector & paths, - BuildMode buildMode = bmNormal, - std::shared_ptr evalStore = nullptr); + virtual void buildPaths(const std::vector & paths, + BuildMode buildMode = bmNormal, + std::shared_ptr evalStore = nullptr); /* Like `buildPaths()`, but return a vector of `BuildResult`s corresponding to each element in `paths`. Note that in case of a build/substitution error, this function won't throw an exception, but return a `BuildResult` containing an error message. */ - virtual std::vector buildPathsWithResults( - const std::vector & paths, - BuildMode buildMode = bmNormal, - std::shared_ptr evalStore = nullptr); + virtual std::vector + buildPathsWithResults(const std::vector & paths, + BuildMode buildMode = bmNormal, + std::shared_ptr evalStore = nullptr); /* Build a single non-materialized derivation (i.e. not from an on-disk .drv file). @@ -477,8 +494,9 @@ public: up with multiple different versions of dependencies without explicitly choosing to allow it). */ - virtual BuildResult buildDerivation(const StorePath & drvPath, const BasicDerivation & drv, - BuildMode buildMode = bmNormal); + virtual BuildResult buildDerivation(const StorePath & drvPath, + const BasicDerivation & drv, + BuildMode buildMode = bmNormal); /* Ensure that a path is valid. If it is not currently valid, it may be made valid by running a substitute (if defined for the @@ -488,23 +506,25 @@ public: /* Add a store path as a temporary root of the garbage collector. The root disappears as soon as we exit. */ virtual void addTempRoot(const StorePath & path) - { debug("not creating temporary root, store doesn't support GC"); } + { + debug("not creating temporary root, store doesn't support GC"); + } /* Return a string representing information about the path that can be loaded into the database using `nix-store --load-db' or `nix-store --register-validity'. */ std::string makeValidityRegistration(const StorePathSet & paths, - bool showDerivers, bool showHash); + bool showDerivers, bool showHash); /* Write a JSON representation of store path metadata, such as the hash and the references. If ‘includeImpureInfo’ is true, variable elements such as the registration time are included. If ‘showClosureSize’ is true, the closure size of each path is included. */ - void pathInfoToJSON(JSONPlaceholder & jsonOut, const StorePathSet & storePaths, - bool includeImpureInfo, bool showClosureSize, - Base hashBase = Base32, - AllowInvalidFlag allowInvalid = DisallowInvalid); + void pathInfoToJSON(JSONPlaceholder & jsonOut, + const StorePathSet & storePaths, bool includeImpureInfo, + bool showClosureSize, Base hashBase = Base32, + AllowInvalidFlag allowInvalid = DisallowInvalid); /* Return the size of the closure of the specified path, that is, the sum of the size of the NAR serialisation of each path in @@ -513,25 +533,32 @@ public: /* Optimise the disk space usage of the Nix store by hard-linking files with the same contents. */ - virtual void optimiseStore() { }; + virtual void optimiseStore(){}; /* Check the integrity of the Nix store. Returns true if errors remain. */ - virtual bool verifyStore(bool checkContents, RepairFlag repair = NoRepair) { return false; }; + virtual bool verifyStore(bool checkContents, RepairFlag repair = NoRepair) + { + return false; + }; /* Return an object to access files in the Nix store. */ - virtual ref getFSAccessor() - { unsupported("getFSAccessor"); } + virtual ref getFSAccessor() { unsupported("getFSAccessor"); } /* Repair the contents of the given path by redownloading it using a substituter (if available). */ virtual void repairPath(const StorePath & path) - { unsupported("repairPath"); } + { + unsupported("repairPath"); + } /* Add signatures to the specified store path. The signatures are not verified. */ - virtual void addSignatures(const StorePath & storePath, const StringSet & sigs) - { unsupported("addSignatures"); } + virtual void addSignatures(const StorePath & storePath, + const StringSet & sigs) + { + unsupported("addSignatures"); + } /* Utility functions. */ @@ -553,19 +580,24 @@ public: `referrers' relation instead of the `references' relation is returned. */ virtual void computeFSClosure(const StorePathSet & paths, - StorePathSet & out, bool flipDirection = false, - bool includeOutputs = false, bool includeDerivers = false); + StorePathSet & out, + bool flipDirection = false, + bool includeOutputs = false, + bool includeDerivers = false); - void computeFSClosure(const StorePath & path, - StorePathSet & out, bool flipDirection = false, - bool includeOutputs = false, bool includeDerivers = false); + void computeFSClosure(const StorePath & path, StorePathSet & out, + bool flipDirection = false, + bool includeOutputs = false, + bool includeDerivers = false); /* Given a set of paths that are to be built, return the set of derivations that will be built, and the set of output paths that will be substituted. */ virtual void queryMissing(const std::vector & targets, - StorePathSet & willBuild, StorePathSet & willSubstitute, StorePathSet & unknown, - uint64_t & downloadSize, uint64_t & narSize); + StorePathSet & willBuild, + StorePathSet & willSubstitute, + StorePathSet & unknown, uint64_t & downloadSize, + uint64_t & narSize); /* Sort a set of paths topologically under the references relation. If p refers to q, then p precedes q in this list. */ @@ -581,10 +613,10 @@ public: the Nix store. Optionally, the contents of the NARs are preloaded into the specified FS accessor to speed up subsequent access. */ - StorePaths importPaths(Source & source, CheckSigsFlag checkSigs = CheckSigs); + StorePaths importPaths(Source & source, + CheckSigsFlag checkSigs = CheckSigs); - struct Stats - { + struct Stats { std::atomic narInfoRead{0}; std::atomic narInfoReadAverted{0}; std::atomic narInfoMissing{0}; @@ -605,67 +637,52 @@ public: /* Computes the full closure of of a set of store-paths for e.g. derivations that need this information for `exportReferencesGraph`. */ - StorePathSet exportReferences(const StorePathSet & storePaths, const StorePathSet & inputPaths); + StorePathSet exportReferences(const StorePathSet & storePaths, + const StorePathSet & inputPaths); /* Hack to allow long-running processes like hydra-queue-runner to occasionally flush their path info cache. */ - void clearPathInfoCache() - { - state.lock()->pathInfoCache.clear(); - } + void clearPathInfoCache() { state.lock()->pathInfoCache.clear(); } /* Establish a connection to the store, for store types that have a notion of connection. Otherwise this is a no-op. */ - virtual void connect() { }; + virtual void connect(){}; /* Get the protocol version of this store or it's connection. */ - virtual unsigned int getProtocol() - { - return 0; - }; + virtual unsigned int getProtocol() { return 0; }; - virtual Path toRealPath(const Path & storePath) - { - return storePath; - } + virtual Path toRealPath(const Path & storePath) { return storePath; } Path toRealPath(const StorePath & storePath) { return toRealPath(printStorePath(storePath)); } - virtual void createUser(const std::string & userName, uid_t userId) - { } + virtual void createUser(const std::string & userName, uid_t userId) {} /* * Synchronises the options of the client with those of the daemon * (a no-op when there’s no daemon) */ - virtual void setOptions() { } + virtual void setOptions() {} virtual std::optional getVersion() { return {}; } -protected: - + protected: Stats stats; /* Unsupported methods. */ [[noreturn]] void unsupported(const std::string & op) { - throw Unsupported("operation '%s' is not supported by store '%s'", op, getUri()); + throw Unsupported("operation '%s' is not supported by store '%s'", op, + getUri()); } - }; - /* Copy a path from one store to another. */ -void copyStorePath( - Store & srcStore, - Store & dstStore, - const StorePath & storePath, - RepairFlag repair = NoRepair, - CheckSigsFlag checkSigs = CheckSigs); - +void copyStorePath(Store & srcStore, Store & dstStore, + const StorePath & storePath, RepairFlag repair = NoRepair, + CheckSigsFlag checkSigs = CheckSigs); /* Copy store paths from one store to another. The paths may be copied in parallel. They are copied in a topologically sorted order (i.e. @@ -673,41 +690,32 @@ void copyStorePath( of store paths is not automatically closed; use copyClosure() for that. Returns a map of what each path was copied to the dstStore as. */ -std::map copyPaths( - Store & srcStore, Store & dstStore, - const RealisedPath::Set &, - RepairFlag repair = NoRepair, - CheckSigsFlag checkSigs = CheckSigs, - SubstituteFlag substitute = NoSubstitute); - -std::map copyPaths( - Store & srcStore, Store & dstStore, - const StorePathSet & paths, - RepairFlag repair = NoRepair, - CheckSigsFlag checkSigs = CheckSigs, - SubstituteFlag substitute = NoSubstitute); +std::map +copyPaths(Store & srcStore, Store & dstStore, const RealisedPath::Set &, + RepairFlag repair = NoRepair, CheckSigsFlag checkSigs = CheckSigs, + SubstituteFlag substitute = NoSubstitute); + +std::map +copyPaths(Store & srcStore, Store & dstStore, const StorePathSet & paths, + RepairFlag repair = NoRepair, CheckSigsFlag checkSigs = CheckSigs, + SubstituteFlag substitute = NoSubstitute); /* Copy the closure of `paths` from `srcStore` to `dstStore`. */ -void copyClosure( - Store & srcStore, Store & dstStore, - const RealisedPath::Set & paths, - RepairFlag repair = NoRepair, - CheckSigsFlag checkSigs = CheckSigs, - SubstituteFlag substitute = NoSubstitute); - -void copyClosure( - Store & srcStore, Store & dstStore, - const StorePathSet & paths, - RepairFlag repair = NoRepair, - CheckSigsFlag checkSigs = CheckSigs, - SubstituteFlag substitute = NoSubstitute); +void copyClosure(Store & srcStore, Store & dstStore, + const RealisedPath::Set & paths, RepairFlag repair = NoRepair, + CheckSigsFlag checkSigs = CheckSigs, + SubstituteFlag substitute = NoSubstitute); + +void copyClosure(Store & srcStore, Store & dstStore, const StorePathSet & paths, + RepairFlag repair = NoRepair, + CheckSigsFlag checkSigs = CheckSigs, + SubstituteFlag substitute = NoSubstitute); /* Remove the temporary roots file for this process. Any temporary root becomes garbage after this point unless it has been registered as a (permanent) root. */ void removeTempRoots(); - /* Return a Store object to access the Nix store denoted by ‘uri’ (slight misnomer...). Supported values are: @@ -738,71 +746,62 @@ void removeTempRoots(); ‘?key=value&key=value&...’ to the URI. */ ref openStore(const std::string & uri = settings.storeUri.get(), - const Store::Params & extraParams = Store::Params()); - + const Store::Params & extraParams = Store::Params()); /* Return the default substituter stores, defined by the ‘substituters’ option and various legacy options. */ std::list> getDefaultSubstituters(); -struct StoreFactory -{ +struct StoreFactory { std::set uriSchemes; - std::function (const std::string & scheme, const std::string & uri, const Store::Params & params)> create; - std::function ()> getConfig; + std::function(const std::string & scheme, + const std::string & uri, + const Store::Params & params)> + create; + std::function()> getConfig; }; -struct Implementations -{ +struct Implementations { static std::vector * registered; - template - static void add() + template static void add() { - if (!registered) registered = new std::vector(); + if (!registered) + registered = new std::vector(); StoreFactory factory{ .uriSchemes = T::uriSchemes(), .create = - ([](const std::string & scheme, const std::string & uri, const Store::Params & params) - -> std::shared_ptr - { return std::make_shared(scheme, uri, params); }), - .getConfig = - ([]() - -> std::shared_ptr - { return std::make_shared(StringMap({})); }) - }; + ([](const std::string & scheme, const std::string & uri, + const Store::Params & params) -> std::shared_ptr { + return std::make_shared(scheme, uri, params); + }), + .getConfig = ([]() -> std::shared_ptr { + return std::make_shared(StringMap({})); + })}; registered->push_back(factory); } }; -template -struct RegisterStoreImplementation -{ - RegisterStoreImplementation() - { - Implementations::add(); - } +template struct RegisterStoreImplementation { + RegisterStoreImplementation() { Implementations::add(); } }; - /* Display a set of paths in human-readable form (i.e., between quotes and separated by commas). */ std::string showPaths(const PathSet & paths); - -std::optional decodeValidPathInfo( - const Store & store, - std::istream & str, - std::optional hashGiven = std::nullopt); +std::optional +decodeValidPathInfo(const Store & store, std::istream & str, + std::optional hashGiven = std::nullopt); /* Split URI into protocol+hierarchy part and its parameter set. */ -std::pair splitUriAndParams(const std::string & uri); +std::pair +splitUriAndParams(const std::string & uri); std::optional getDerivationCA(const BasicDerivation & drv); -std::map drvOutputReferences( - Store & store, - const Derivation & drv, - const StorePath & outputPath); +std::map +drvOutputReferences(Store & store, const Derivation & drv, + const StorePath & outputPath); } diff --git a/src/libstore/store-cast.hh b/src/libstore/store-cast.hh index ff62fc3593ef..b89ffc0cef95 100644 --- a/src/libstore/store-cast.hh +++ b/src/libstore/store-cast.hh @@ -4,12 +4,12 @@ namespace nix { -template -T & require(Store & store) +template T & require(Store & store) { auto * castedStore = dynamic_cast(&store); if (!castedStore) - throw UsageError("%s not supported by store '%s'", T::operationName, store.getUri()); + throw UsageError("%s not supported by store '%s'", T::operationName, + store.getUri()); return *castedStore; } diff --git a/src/libstore/tests/machines.cc b/src/libstore/tests/machines.cc index f51052b14626..435d6e24ae53 100644 --- a/src/libstore/tests/machines.cc +++ b/src/libstore/tests/machines.cc @@ -24,21 +24,25 @@ class Environment : public ::testing::Environment { void SetUp() override { settings.thisSystem = "TEST_ARCH-TEST_OS"; } }; -testing::Environment* const foo_env = +testing::Environment * const foo_env = testing::AddGlobalTestEnvironment(new Environment); -TEST(machines, getMachinesWithEmptyBuilders) { +TEST(machines, getMachinesWithEmptyBuilders) +{ settings.builders = ""; Machines actual = getMachines(); ASSERT_THAT(actual, SizeIs(0)); } -TEST(machines, getMachinesUriOnly) { +TEST(machines, getMachinesUriOnly) +{ settings.builders = "nix@scratchy.labs.cs.uu.nl"; Machines actual = getMachines(); ASSERT_THAT(actual, SizeIs(1)); - EXPECT_THAT(actual[0], Field(&Machine::storeUri, Eq("ssh://nix@scratchy.labs.cs.uu.nl"))); - EXPECT_THAT(actual[0], Field(&Machine::systemTypes, ElementsAre("TEST_ARCH-TEST_OS"))); + EXPECT_THAT(actual[0], Field(&Machine::storeUri, + Eq("ssh://nix@scratchy.labs.cs.uu.nl"))); + EXPECT_THAT(actual[0], + Field(&Machine::systemTypes, ElementsAre("TEST_ARCH-TEST_OS"))); EXPECT_THAT(actual[0], Field(&Machine::sshKey, SizeIs(0))); EXPECT_THAT(actual[0], Field(&Machine::maxJobs, Eq(1))); EXPECT_THAT(actual[0], Field(&Machine::speedFactor, Eq(1))); @@ -47,12 +51,15 @@ TEST(machines, getMachinesUriOnly) { EXPECT_THAT(actual[0], Field(&Machine::sshPublicHostKey, SizeIs(0))); } -TEST(machines, getMachinesDefaults) { +TEST(machines, getMachinesDefaults) +{ settings.builders = "nix@scratchy.labs.cs.uu.nl - - - - - - -"; Machines actual = getMachines(); ASSERT_THAT(actual, SizeIs(1)); - EXPECT_THAT(actual[0], Field(&Machine::storeUri, Eq("ssh://nix@scratchy.labs.cs.uu.nl"))); - EXPECT_THAT(actual[0], Field(&Machine::systemTypes, ElementsAre("TEST_ARCH-TEST_OS"))); + EXPECT_THAT(actual[0], Field(&Machine::storeUri, + Eq("ssh://nix@scratchy.labs.cs.uu.nl"))); + EXPECT_THAT(actual[0], + Field(&Machine::systemTypes, ElementsAre("TEST_ARCH-TEST_OS"))); EXPECT_THAT(actual[0], Field(&Machine::sshKey, SizeIs(0))); EXPECT_THAT(actual[0], Field(&Machine::maxJobs, Eq(1))); EXPECT_THAT(actual[0], Field(&Machine::speedFactor, Eq(1))); @@ -61,69 +68,99 @@ TEST(machines, getMachinesDefaults) { EXPECT_THAT(actual[0], Field(&Machine::sshPublicHostKey, SizeIs(0))); } -TEST(machines, getMachinesWithNewLineSeparator) { +TEST(machines, getMachinesWithNewLineSeparator) +{ settings.builders = "nix@scratchy.labs.cs.uu.nl\nnix@itchy.labs.cs.uu.nl"; Machines actual = getMachines(); ASSERT_THAT(actual, SizeIs(2)); - EXPECT_THAT(actual, Contains(Field(&Machine::storeUri, EndsWith("nix@scratchy.labs.cs.uu.nl")))); - EXPECT_THAT(actual, Contains(Field(&Machine::storeUri, EndsWith("nix@itchy.labs.cs.uu.nl")))); + EXPECT_THAT(actual, + Contains(Field(&Machine::storeUri, + EndsWith("nix@scratchy.labs.cs.uu.nl")))); + EXPECT_THAT(actual, Contains(Field(&Machine::storeUri, + EndsWith("nix@itchy.labs.cs.uu.nl")))); } -TEST(machines, getMachinesWithSemicolonSeparator) { +TEST(machines, getMachinesWithSemicolonSeparator) +{ settings.builders = "nix@scratchy.labs.cs.uu.nl ; nix@itchy.labs.cs.uu.nl"; Machines actual = getMachines(); EXPECT_THAT(actual, SizeIs(2)); - EXPECT_THAT(actual, Contains(Field(&Machine::storeUri, EndsWith("nix@scratchy.labs.cs.uu.nl")))); - EXPECT_THAT(actual, Contains(Field(&Machine::storeUri, EndsWith("nix@itchy.labs.cs.uu.nl")))); + EXPECT_THAT(actual, + Contains(Field(&Machine::storeUri, + EndsWith("nix@scratchy.labs.cs.uu.nl")))); + EXPECT_THAT(actual, Contains(Field(&Machine::storeUri, + EndsWith("nix@itchy.labs.cs.uu.nl")))); } -TEST(machines, getMachinesWithCorrectCompleteSingleBuilder) { +TEST(machines, getMachinesWithCorrectCompleteSingleBuilder) +{ settings.builders = "nix@scratchy.labs.cs.uu.nl i686-linux " "/home/nix/.ssh/id_scratchy_auto 8 3 kvm " "benchmark SSH+HOST+PUBLIC+KEY+BASE64+ENCODED=="; Machines actual = getMachines(); ASSERT_THAT(actual, SizeIs(1)); - EXPECT_THAT(actual[0], Field(&Machine::storeUri, EndsWith("nix@scratchy.labs.cs.uu.nl"))); - EXPECT_THAT(actual[0], Field(&Machine::systemTypes, ElementsAre("i686-linux"))); - EXPECT_THAT(actual[0], Field(&Machine::sshKey, Eq("/home/nix/.ssh/id_scratchy_auto"))); + EXPECT_THAT(actual[0], Field(&Machine::storeUri, + EndsWith("nix@scratchy.labs.cs.uu.nl"))); + EXPECT_THAT(actual[0], + Field(&Machine::systemTypes, ElementsAre("i686-linux"))); + EXPECT_THAT(actual[0], + Field(&Machine::sshKey, Eq("/home/nix/.ssh/id_scratchy_auto"))); EXPECT_THAT(actual[0], Field(&Machine::maxJobs, Eq(8))); EXPECT_THAT(actual[0], Field(&Machine::speedFactor, Eq(3))); - EXPECT_THAT(actual[0], Field(&Machine::supportedFeatures, ElementsAre("kvm"))); - EXPECT_THAT(actual[0], Field(&Machine::mandatoryFeatures, ElementsAre("benchmark"))); - EXPECT_THAT(actual[0], Field(&Machine::sshPublicHostKey, Eq("SSH+HOST+PUBLIC+KEY+BASE64+ENCODED=="))); + EXPECT_THAT(actual[0], + Field(&Machine::supportedFeatures, ElementsAre("kvm"))); + EXPECT_THAT(actual[0], + Field(&Machine::mandatoryFeatures, ElementsAre("benchmark"))); + EXPECT_THAT(actual[0], Field(&Machine::sshPublicHostKey, + Eq("SSH+HOST+PUBLIC+KEY+BASE64+ENCODED=="))); } TEST(machines, - getMachinesWithCorrectCompleteSingleBuilderWithTabColumnDelimiter) { + getMachinesWithCorrectCompleteSingleBuilderWithTabColumnDelimiter) +{ settings.builders = "nix@scratchy.labs.cs.uu.nl\ti686-linux\t/home/nix/.ssh/" "id_scratchy_auto\t8\t3\tkvm\tbenchmark\tSSH+HOST+PUBLIC+" "KEY+BASE64+ENCODED=="; Machines actual = getMachines(); ASSERT_THAT(actual, SizeIs(1)); - EXPECT_THAT(actual[0], Field(&Machine::storeUri, EndsWith("nix@scratchy.labs.cs.uu.nl"))); - EXPECT_THAT(actual[0], Field(&Machine::systemTypes, ElementsAre("i686-linux"))); - EXPECT_THAT(actual[0], Field(&Machine::sshKey, Eq("/home/nix/.ssh/id_scratchy_auto"))); + EXPECT_THAT(actual[0], Field(&Machine::storeUri, + EndsWith("nix@scratchy.labs.cs.uu.nl"))); + EXPECT_THAT(actual[0], + Field(&Machine::systemTypes, ElementsAre("i686-linux"))); + EXPECT_THAT(actual[0], + Field(&Machine::sshKey, Eq("/home/nix/.ssh/id_scratchy_auto"))); EXPECT_THAT(actual[0], Field(&Machine::maxJobs, Eq(8))); EXPECT_THAT(actual[0], Field(&Machine::speedFactor, Eq(3))); - EXPECT_THAT(actual[0], Field(&Machine::supportedFeatures, ElementsAre("kvm"))); - EXPECT_THAT(actual[0], Field(&Machine::mandatoryFeatures, ElementsAre("benchmark"))); - EXPECT_THAT(actual[0], Field(&Machine::sshPublicHostKey, Eq("SSH+HOST+PUBLIC+KEY+BASE64+ENCODED=="))); + EXPECT_THAT(actual[0], + Field(&Machine::supportedFeatures, ElementsAre("kvm"))); + EXPECT_THAT(actual[0], + Field(&Machine::mandatoryFeatures, ElementsAre("benchmark"))); + EXPECT_THAT(actual[0], Field(&Machine::sshPublicHostKey, + Eq("SSH+HOST+PUBLIC+KEY+BASE64+ENCODED=="))); } -TEST(machines, getMachinesWithMultiOptions) { +TEST(machines, getMachinesWithMultiOptions) +{ settings.builders = "nix@scratchy.labs.cs.uu.nl Arch1,Arch2 - - - " "SupportedFeature1,SupportedFeature2 " "MandatoryFeature1,MandatoryFeature2"; Machines actual = getMachines(); ASSERT_THAT(actual, SizeIs(1)); - EXPECT_THAT(actual[0], Field(&Machine::storeUri, EndsWith("nix@scratchy.labs.cs.uu.nl"))); - EXPECT_THAT(actual[0], Field(&Machine::systemTypes, ElementsAre("Arch1", "Arch2"))); - EXPECT_THAT(actual[0], Field(&Machine::supportedFeatures, ElementsAre("SupportedFeature1", "SupportedFeature2"))); - EXPECT_THAT(actual[0], Field(&Machine::mandatoryFeatures, ElementsAre("MandatoryFeature1", "MandatoryFeature2"))); + EXPECT_THAT(actual[0], Field(&Machine::storeUri, + EndsWith("nix@scratchy.labs.cs.uu.nl"))); + EXPECT_THAT(actual[0], + Field(&Machine::systemTypes, ElementsAre("Arch1", "Arch2"))); + EXPECT_THAT(actual[0], + Field(&Machine::supportedFeatures, + ElementsAre("SupportedFeature1", "SupportedFeature2"))); + EXPECT_THAT(actual[0], + Field(&Machine::mandatoryFeatures, + ElementsAre("MandatoryFeature1", "MandatoryFeature2"))); } -TEST(machines, getMachinesWithIncorrectFormat) { +TEST(machines, getMachinesWithIncorrectFormat) +{ settings.builders = "nix@scratchy.labs.cs.uu.nl - - eight"; EXPECT_THROW(getMachines(), FormatError); settings.builders = "nix@scratchy.labs.cs.uu.nl - - -1"; @@ -136,19 +173,25 @@ TEST(machines, getMachinesWithIncorrectFormat) { EXPECT_THROW(getMachines(), FormatError); } -TEST(machines, getMachinesWithCorrectFileReference) { +TEST(machines, getMachinesWithCorrectFileReference) +{ auto path = absPath("src/libstore/tests/test-data/machines.valid"); ASSERT_TRUE(pathExists(path)); settings.builders = std::string("@") + path; Machines actual = getMachines(); ASSERT_THAT(actual, SizeIs(3)); - EXPECT_THAT(actual, Contains(Field(&Machine::storeUri, EndsWith("nix@scratchy.labs.cs.uu.nl")))); - EXPECT_THAT(actual, Contains(Field(&Machine::storeUri, EndsWith("nix@itchy.labs.cs.uu.nl")))); - EXPECT_THAT(actual, Contains(Field(&Machine::storeUri, EndsWith("nix@poochie.labs.cs.uu.nl")))); + EXPECT_THAT(actual, + Contains(Field(&Machine::storeUri, + EndsWith("nix@scratchy.labs.cs.uu.nl")))); + EXPECT_THAT(actual, Contains(Field(&Machine::storeUri, + EndsWith("nix@itchy.labs.cs.uu.nl")))); + EXPECT_THAT(actual, Contains(Field(&Machine::storeUri, + EndsWith("nix@poochie.labs.cs.uu.nl")))); } -TEST(machines, getMachinesWithCorrectFileReferenceToEmptyFile) { +TEST(machines, getMachinesWithCorrectFileReferenceToEmptyFile) +{ auto path = "/dev/null"; ASSERT_TRUE(pathExists(path)); @@ -157,13 +200,17 @@ TEST(machines, getMachinesWithCorrectFileReferenceToEmptyFile) { ASSERT_THAT(actual, SizeIs(0)); } -TEST(machines, getMachinesWithIncorrectFileReference) { +TEST(machines, getMachinesWithIncorrectFileReference) +{ settings.builders = std::string("@") + absPath("/not/a/file"); Machines actual = getMachines(); ASSERT_THAT(actual, SizeIs(0)); } -TEST(machines, getMachinesWithCorrectFileReferenceToIncorrectFile) { - settings.builders = std::string("@") + absPath("src/libstore/tests/test-data/machines.bad_format"); +TEST(machines, getMachinesWithCorrectFileReferenceToIncorrectFile) +{ + settings.builders = + std::string("@") + + absPath("src/libstore/tests/test-data/machines.bad_format"); EXPECT_THROW(getMachines(), FormatError); } diff --git a/src/libstore/tests/path-with-outputs.cc b/src/libstore/tests/path-with-outputs.cc index 350ea7ffd599..fb75902df887 100644 --- a/src/libstore/tests/path-with-outputs.cc +++ b/src/libstore/tests/path-with-outputs.cc @@ -27,13 +27,15 @@ TEST(parseOutputsSpec, basic) { auto [prefix, outputsSpec] = parseOutputsSpec("foo^out,bin"); ASSERT_EQ(prefix, "foo"); - ASSERT_TRUE(std::get(outputsSpec) == OutputNames({"out", "bin"})); + ASSERT_TRUE(std::get(outputsSpec) == + OutputNames({"out", "bin"})); } { auto [prefix, outputsSpec] = parseOutputsSpec("foo^bar^out,bin"); ASSERT_EQ(prefix, "foo^bar"); - ASSERT_TRUE(std::get(outputsSpec) == OutputNames({"out", "bin"})); + ASSERT_TRUE(std::get(outputsSpec) == + OutputNames({"out", "bin"})); } { diff --git a/src/libstore/uds-remote-store.cc b/src/libstore/uds-remote-store.cc index 5c38323cd337..ad3d41e31801 100644 --- a/src/libstore/uds-remote-store.cc +++ b/src/libstore/uds-remote-store.cc @@ -10,31 +10,22 @@ #include - namespace nix { UDSRemoteStore::UDSRemoteStore(const Params & params) - : StoreConfig(params) - , LocalFSStoreConfig(params) - , RemoteStoreConfig(params) - , UDSRemoteStoreConfig(params) - , Store(params) - , LocalFSStore(params) - , RemoteStore(params) + : StoreConfig(params), LocalFSStoreConfig(params), + RemoteStoreConfig(params), UDSRemoteStoreConfig(params), Store(params), + LocalFSStore(params), RemoteStore(params) { } - -UDSRemoteStore::UDSRemoteStore( - const std::string scheme, - std::string socket_path, - const Params & params) +UDSRemoteStore::UDSRemoteStore(const std::string scheme, + std::string socket_path, const Params & params) : UDSRemoteStore(params) { path.emplace(socket_path); } - std::string UDSRemoteStore::getUri() { if (path) { @@ -44,12 +35,7 @@ std::string UDSRemoteStore::getUri() } } - -void UDSRemoteStore::Connection::closeWrite() -{ - shutdown(fd.get(), SHUT_WR); -} - +void UDSRemoteStore::Connection::closeWrite() { shutdown(fd.get(), SHUT_WR); } ref UDSRemoteStore::openConnection() { @@ -68,7 +54,7 @@ ref UDSRemoteStore::openConnection() return conn; } - -static RegisterStoreImplementation regUDSRemoteStore; +static RegisterStoreImplementation + regUDSRemoteStore; } diff --git a/src/libstore/uds-remote-store.hh b/src/libstore/uds-remote-store.hh index f8dfcca704a0..276d33bfcefd 100644 --- a/src/libstore/uds-remote-store.hh +++ b/src/libstore/uds-remote-store.hh @@ -5,43 +5,43 @@ namespace nix { -struct UDSRemoteStoreConfig : virtual LocalFSStoreConfig, virtual RemoteStoreConfig -{ +struct UDSRemoteStoreConfig : virtual LocalFSStoreConfig, + virtual RemoteStoreConfig { UDSRemoteStoreConfig(const Store::Params & params) - : StoreConfig(params) - , LocalFSStoreConfig(params) - , RemoteStoreConfig(params) + : StoreConfig(params), LocalFSStoreConfig(params), + RemoteStoreConfig(params) { } const std::string name() override { return "Local Daemon Store"; } }; -class UDSRemoteStore : public virtual UDSRemoteStoreConfig, public virtual LocalFSStore, public virtual RemoteStore -{ -public: - +class UDSRemoteStore : public virtual UDSRemoteStoreConfig, + public virtual LocalFSStore, + public virtual RemoteStore { + public: UDSRemoteStore(const Params & params); - UDSRemoteStore(const std::string scheme, std::string path, const Params & params); + UDSRemoteStore(const std::string scheme, std::string path, + const Params & params); std::string getUri() override; - static std::set uriSchemes() - { return {"unix"}; } + static std::set uriSchemes() { return {"unix"}; } - bool sameMachine() override - { return true; } + bool sameMachine() override { return true; } ref getFSAccessor() override - { return LocalFSStore::getFSAccessor(); } + { + return LocalFSStore::getFSAccessor(); + } void narFromPath(const StorePath & path, Sink & sink) override - { LocalFSStore::narFromPath(path, sink); } - -private: - - struct Connection : RemoteStore::Connection { + LocalFSStore::narFromPath(path, sink); + } + + private: + struct Connection : RemoteStore::Connection { AutoCloseFD fd; void closeWrite() override; }; diff --git a/src/libstore/worker-protocol.hh b/src/libstore/worker-protocol.hh index 87088a3ac105..3f9856e82f5e 100644 --- a/src/libstore/worker-protocol.hh +++ b/src/libstore/worker-protocol.hh @@ -5,19 +5,17 @@ namespace nix { - #define WORKER_MAGIC_1 0x6e697863 #define WORKER_MAGIC_2 0x6478696f #define PROTOCOL_VERSION (1 << 8 | 34) -#define GET_PROTOCOL_MAJOR(x) ((x) & 0xff00) -#define GET_PROTOCOL_MINOR(x) ((x) & 0x00ff) - +#define GET_PROTOCOL_MAJOR(x) ((x) &0xff00) +#define GET_PROTOCOL_MINOR(x) ((x) &0x00ff) typedef enum { wopIsValidPath = 1, wopHasSubstitutes = 3, - wopQueryPathHash = 4, // obsolete + wopQueryPathHash = 4, // obsolete wopQueryReferences = 5, // obsolete wopQueryReferrers = 6, wopAddToStore = 7, @@ -28,7 +26,7 @@ typedef enum { wopAddIndirectRoot = 12, wopSyncWithGC = 13, wopFindRoots = 14, - wopExportPath = 16, // obsolete + wopExportPath = 16, // obsolete wopQueryDeriver = 18, // obsolete wopSetOptions = 19, wopCollectGarbage = 20, @@ -38,7 +36,7 @@ typedef enum { wopQueryFailedPaths = 24, wopClearFailedPaths = 25, wopQueryPathInfo = 26, - wopImportPaths = 27, // obsolete + wopImportPaths = 27, // obsolete wopQueryDerivationOutputNames = 28, // obsolete wopQueryPathFromHashPart = 29, wopQuerySubstitutablePathInfos = 30, @@ -60,30 +58,27 @@ typedef enum { wopBuildPathsWithResults = 46, } WorkerOp; - -#define STDERR_NEXT 0x6f6c6d67 -#define STDERR_READ 0x64617461 // data needed from source +#define STDERR_NEXT 0x6f6c6d67 +#define STDERR_READ 0x64617461 // data needed from source #define STDERR_WRITE 0x64617416 // data for sink -#define STDERR_LAST 0x616c7473 +#define STDERR_LAST 0x616c7473 #define STDERR_ERROR 0x63787470 #define STDERR_START_ACTIVITY 0x53545254 -#define STDERR_STOP_ACTIVITY 0x53544f50 -#define STDERR_RESULT 0x52534c54 - +#define STDERR_STOP_ACTIVITY 0x53544f50 +#define STDERR_RESULT 0x52534c54 class Store; struct Source; /* To guide overloading */ -template -struct Phantom {}; - +template struct Phantom { +}; namespace worker_proto { /* FIXME maybe move more stuff inside here */ -#define MAKE_WORKER_PROTO(TEMPLATE, T) \ - TEMPLATE T read(const Store & store, Source & from, Phantom< T > _); \ +#define MAKE_WORKER_PROTO(TEMPLATE, T) \ + TEMPLATE T read(const Store & store, Source & from, Phantom _); \ TEMPLATE void write(const Store & store, Sink & out, const T & str) MAKE_WORKER_PROTO(, std::string); @@ -120,12 +115,13 @@ MAKE_WORKER_PROTO(, std::optional); MAKE_WORKER_PROTO(, std::optional); template -std::vector read(const Store & store, Source & from, Phantom> _) +std::vector read(const Store & store, Source & from, + Phantom> _) { std::vector resSet; auto size = readNum(from); while (size--) { - resSet.push_back(read(store, from, Phantom {})); + resSet.push_back(read(store, from, Phantom{})); } return resSet; } @@ -145,7 +141,7 @@ std::set read(const Store & store, Source & from, Phantom> _) std::set resSet; auto size = readNum(from); while (size--) { - resSet.insert(read(store, from, Phantom {})); + resSet.insert(read(store, from, Phantom{})); } return resSet; } @@ -160,13 +156,14 @@ void write(const Store & store, Sink & out, const std::set & resSet) } template -std::map read(const Store & store, Source & from, Phantom> _) +std::map read(const Store & store, Source & from, + Phantom> _) { std::map resMap; auto size = readNum(from); while (size--) { - auto k = read(store, from, Phantom {}); - auto v = read(store, from, Phantom {}); + auto k = read(store, from, Phantom{}); + auto v = read(store, from, Phantom{}); resMap.insert_or_assign(std::move(k), std::move(v)); } return resMap; diff --git a/src/libutil/archive.cc b/src/libutil/archive.cc index 30b471af546c..d19a6b676df5 100644 --- a/src/libutil/archive.cc +++ b/src/libutil/archive.cc @@ -17,17 +17,21 @@ namespace nix { -struct ArchiveSettings : Config -{ - Setting useCaseHack{this, - #if __APPLE__ +struct ArchiveSettings : Config { + Setting useCaseHack + { + this, +#if __APPLE__ true, - #else +#else false, - #endif - "use-case-hack", - "Whether to enable a Darwin-specific hack for dealing with file name collisions."}; - Setting preallocateContents{this, false, "preallocate-contents", +#endif + "use-case-hack", + "Whether to enable a Darwin-specific hack for dealing with file " + "name collisions." + }; + Setting preallocateContents{ + this, false, "preallocate-contents", "Whether to preallocate files when writing objects with known size."}; }; @@ -41,14 +45,13 @@ static std::string caseHackSuffix = "~nix~case~hack~"; PathFilter defaultPathFilter = [](const Path &) { return true; }; - -static void dumpContents(const Path & path, off_t size, - Sink & sink) +static void dumpContents(const Path & path, off_t size, Sink & sink) { sink << "contents" << size; AutoCloseFD fd = open(path.c_str(), O_RDONLY | O_CLOEXEC); - if (!fd) throw SysError("opening file '%1%'", path); + if (!fd) + throw SysError("opening file '%1%'", path); std::vector buf(65536); size_t left = size; @@ -63,7 +66,6 @@ static void dumpContents(const Path & path, off_t size, writePadding(size, sink); } - static time_t dump(const Path & path, Sink & sink, PathFilter & filter) { checkInterrupt(); @@ -74,14 +76,17 @@ static time_t dump(const Path & path, Sink & sink, PathFilter & filter) sink << "("; if (S_ISREG(st.st_mode)) { - sink << "type" << "regular"; + sink << "type" + << "regular"; if (st.st_mode & S_IXUSR) - sink << "executable" << ""; + sink << "executable" + << ""; dumpContents(path, st.st_size, sink); } else if (S_ISDIR(st.st_mode)) { - sink << "type" << "directory"; + sink << "type" + << "directory"; /* If we're on a case-insensitive system like macOS, undo the case hack applied by restorePath(). */ @@ -91,19 +96,22 @@ static time_t dump(const Path & path, Sink & sink, PathFilter & filter) std::string name(i.name); size_t pos = i.name.find(caseHackSuffix); if (pos != std::string::npos) { - debug(format("removing case hack suffix from '%1%'") % (path + "/" + i.name)); + debug(format("removing case hack suffix from '%1%'") % + (path + "/" + i.name)); name.erase(pos); } if (!unhacked.emplace(name, i.name).second) - throw Error("file name collision in between '%1%' and '%2%'", - (path + "/" + unhacked[name]), - (path + "/" + i.name)); + throw Error( + "file name collision in between '%1%' and '%2%'", + (path + "/" + unhacked[name]), (path + "/" + i.name)); } else unhacked.emplace(i.name, i.name); for (auto & i : unhacked) if (filter(path + "/" + i.first)) { - sink << "entry" << "(" << "name" << i.first << "node"; + sink << "entry" + << "(" + << "name" << i.first << "node"; auto tmp_mtime = dump(path + "/" + i.second, sink, filter); if (tmp_mtime > result) { result = tmp_mtime; @@ -113,16 +121,18 @@ static time_t dump(const Path & path, Sink & sink, PathFilter & filter) } else if (S_ISLNK(st.st_mode)) - sink << "type" << "symlink" << "target" << readLink(path); + sink << "type" + << "symlink" + << "target" << readLink(path); - else throw Error("file '%1%' has an unsupported type", path); + else + throw Error("file '%1%' has an unsupported type", path); sink << ")"; return result; } - time_t dumpPathAndGetMtime(const Path & path, Sink & sink, PathFilter & filter) { sink << narVersionMagic1; @@ -134,19 +144,19 @@ void dumpPath(const Path & path, Sink & sink, PathFilter & filter) dumpPathAndGetMtime(path, sink, filter); } - void dumpString(std::string_view s, Sink & sink) { - sink << narVersionMagic1 << "(" << "type" << "regular" << "contents" << s << ")"; + sink << narVersionMagic1 << "(" + << "type" + << "regular" + << "contents" << s << ")"; } - static SerialisationError badArchive(const std::string & s) { return SerialisationError("bad archive: " + s); } - #if 0 static void skipGeneric(Source & source) { @@ -157,7 +167,6 @@ static void skipGeneric(Source & source) } #endif - static void parseContents(ParseSink & sink, Source & source, const Path & path) { uint64_t size = readLongLong(source); @@ -170,7 +179,8 @@ static void parseContents(ParseSink & sink, Source & source, const Path & path) while (left) { checkInterrupt(); auto n = buf.size(); - if ((uint64_t)n > left) n = left; + if ((uint64_t) n > left) + n = left; source(buf.data(), n); sink.receiveContents({buf.data(), n}); left -= n; @@ -179,22 +189,20 @@ static void parseContents(ParseSink & sink, Source & source, const Path & path) readPadding(size, source); } - -struct CaseInsensitiveCompare -{ - bool operator() (const std::string & a, const std::string & b) const +struct CaseInsensitiveCompare { + bool operator()(const std::string & a, const std::string & b) const { return strcasecmp(a.c_str(), b.c_str()) < 0; } }; - static void parse(ParseSink & sink, Source & source, const Path & path) { std::string s; s = readString(source); - if (s != "(") throw badArchive("expected open tag"); + if (s != "(") + throw badArchive("expected open tag"); enum { tpUnknown, tpRegular, tpDirectory, tpSymlink } type = tpUnknown; @@ -228,7 +236,8 @@ static void parse(ParseSink & sink, Source & source, const Path & path) type = tpSymlink; } - else throw badArchive("unknown file type " + t); + else + throw badArchive("unknown file type " + t); } @@ -238,7 +247,8 @@ static void parse(ParseSink & sink, Source & source, const Path & path) else if (s == "executable" && type == tpRegular) { auto s = readString(source); - if (s != "") throw badArchive("executable marker has non-empty value"); + if (s != "") + throw badArchive("executable marker has non-empty value"); sink.isExecutable(); } @@ -246,7 +256,8 @@ static void parse(ParseSink & sink, Source & source, const Path & path) std::string name, prevName; s = readString(source); - if (s != "(") throw badArchive("expected open tag"); + if (s != "(") + throw badArchive("expected open tag"); while (1) { checkInterrupt(); @@ -257,22 +268,29 @@ static void parse(ParseSink & sink, Source & source, const Path & path) break; } else if (s == "name") { name = readString(source); - if (name.empty() || name == "." || name == ".." || name.find('/') != std::string::npos || name.find((char) 0) != std::string::npos) - throw Error("NAR contains invalid file name '%1%'", name); + if (name.empty() || name == "." || name == ".." || + name.find('/') != std::string::npos || + name.find((char) 0) != std::string::npos) + throw Error("NAR contains invalid file name '%1%'", + name); if (name <= prevName) throw Error("NAR directory is not sorted"); prevName = name; if (archiveSettings.useCaseHack) { auto i = names.find(name); if (i != names.end()) { - debug(format("case collision between '%1%' and '%2%'") % i->first % name); + debug( + format( + "case collision between '%1%' and '%2%'") % + i->first % name); name += caseHackSuffix; name += std::to_string(++i->second); } else names[name] = 0; } } else if (s == "node") { - if (name.empty()) throw badArchive("entry name missing"); + if (name.empty()) + throw badArchive("entry name missing"); parse(sink, source, path + "/" + name); } else throw badArchive("unknown field " + s); @@ -289,7 +307,6 @@ static void parse(ParseSink & sink, Source & source, const Path & path) } } - void parseDump(ParseSink & sink, Source & source) { std::string version; @@ -304,9 +321,7 @@ void parseDump(ParseSink & sink, Source & source) parse(sink, source, ""); } - -struct RestoreSink : ParseSink -{ +struct RestoreSink : ParseSink { Path dstPath; AutoCloseFD fd; @@ -321,7 +336,8 @@ struct RestoreSink : ParseSink { Path p = dstPath + path; fd = open(p.c_str(), O_CREAT | O_EXCL | O_WRONLY | O_CLOEXEC, 0666); - if (!fd) throw SysError("creating file '%1%'", p); + if (!fd) + throw SysError("creating file '%1%'", p); } void isExecutable() override @@ -345,7 +361,8 @@ struct RestoreSink : ParseSink filesystem doesn't support preallocation (e.g. on OpenSolaris). Since preallocation is just an optimisation, ignore it. */ - if (errno && errno != EINVAL && errno != EOPNOTSUPP && errno != ENOSYS) + if (errno && errno != EINVAL && errno != EOPNOTSUPP && + errno != ENOSYS) throw SysError("preallocating file of %1% bytes", len); } #endif @@ -363,7 +380,6 @@ struct RestoreSink : ParseSink } }; - void restorePath(const Path & path, Source & source) { RestoreSink sink; @@ -371,7 +387,6 @@ void restorePath(const Path & path, Source & source) parseDump(sink, source); } - void copyNAR(Source & source, Sink & sink) { // FIXME: if 'source' is the output of dumpPath() followed by EOF, @@ -379,19 +394,15 @@ void copyNAR(Source & source, Sink & sink) ParseSink parseSink; /* null sink; just parse the NAR */ - TeeSource wrapper { source, sink }; + TeeSource wrapper{source, sink}; parseDump(parseSink, wrapper); } - void copyPath(const Path & from, const Path & to) { - auto source = sinkToSource([&](Sink & sink) { - dumpPath(from, sink); - }); + auto source = sinkToSource([&](Sink & sink) { dumpPath(from, sink); }); restorePath(to, *source); } - } diff --git a/src/libutil/archive.hh b/src/libutil/archive.hh index 79ce08df0c4f..ef8999e987e2 100644 --- a/src/libutil/archive.hh +++ b/src/libutil/archive.hh @@ -3,10 +3,8 @@ #include "types.hh" #include "serialise.hh" - namespace nix { - /* dumpPath creates a Nix archive of the specified path. The format is as follows: @@ -44,47 +42,38 @@ namespace nix { `+' denotes string concatenation. */ - void dumpPath(const Path & path, Sink & sink, - PathFilter & filter = defaultPathFilter); + PathFilter & filter = defaultPathFilter); /* Same as `void dumpPath()`, but returns the last modified date of the path */ time_t dumpPathAndGetMtime(const Path & path, Sink & sink, - PathFilter & filter = defaultPathFilter); + PathFilter & filter = defaultPathFilter); void dumpString(std::string_view s, Sink & sink); /* FIXME: fix this API, it sucks. */ -struct ParseSink -{ - virtual void createDirectory(const Path & path) { }; +struct ParseSink { + virtual void createDirectory(const Path & path){}; - virtual void createRegularFile(const Path & path) { }; - virtual void isExecutable() { }; - virtual void preallocateContents(uint64_t size) { }; - virtual void receiveContents(std::string_view data) { }; + virtual void createRegularFile(const Path & path){}; + virtual void isExecutable(){}; + virtual void preallocateContents(uint64_t size){}; + virtual void receiveContents(std::string_view data){}; - virtual void createSymlink(const Path & path, const std::string & target) { }; + virtual void createSymlink(const Path & path, const std::string & target){}; }; /* If the NAR archive contains a single file at top-level, then save the contents of the file to `s'. Otherwise barf. */ -struct RetrieveRegularNARSink : ParseSink -{ +struct RetrieveRegularNARSink : ParseSink { bool regular = true; Sink & sink; - RetrieveRegularNARSink(Sink & sink) : sink(sink) { } + RetrieveRegularNARSink(Sink & sink) : sink(sink) {} - void createDirectory(const Path & path) override - { - regular = false; - } + void createDirectory(const Path & path) override { regular = false; } - void receiveContents(std::string_view data) override - { - sink(data); - } + void receiveContents(std::string_view data) override { sink(data); } void createSymlink(const Path & path, const std::string & target) override { @@ -101,8 +90,6 @@ void copyNAR(Source & source, Sink & sink); void copyPath(const Path & from, const Path & to); - extern const std::string narVersionMagic1; - } diff --git a/src/libutil/args.cc b/src/libutil/args.cc index 4b8c55686f51..3924a95b9cf3 100644 --- a/src/libutil/args.cc +++ b/src/libutil/args.cc @@ -16,28 +16,30 @@ void Args::addFlag(Flag && flag_) longFlags[flag->longName] = flag; for (auto & alias : flag->aliases) longFlags[alias] = flag; - if (flag->shortName) shortFlags[flag->shortName] = flag; + if (flag->shortName) + shortFlags[flag->shortName] = flag; } void Args::removeFlag(const std::string & longName) { auto flag = longFlags.find(longName); assert(flag != longFlags.end()); - if (flag->second->shortName) shortFlags.erase(flag->second->shortName); + if (flag->second->shortName) + shortFlags.erase(flag->second->shortName); longFlags.erase(flag); } void Completions::add(std::string completion, std::string description) { assert(description.find('\n') == std::string::npos); - insert(Completion { - .completion = completion, - .description = description - }); + insert(Completion{.completion = completion, .description = description}); } bool Completion::operator<(const Completion & other) const -{ return completion < other.completion || (completion == other.completion && description < other.description); } +{ + return completion < other.completion || + (completion == other.completion && description < other.description); +} CompletionType completionType = ctNormal; std::shared_ptr completions; @@ -46,7 +48,8 @@ std::string completionMarker = "___COMPLETE___"; std::optional needsCompletion(std::string_view s) { - if (!completions) return {}; + if (!completions) + return {}; auto i = s.find(completionMarker); if (i != std::string::npos) return std::string(s.begin(), i); @@ -69,15 +72,17 @@ void Args::parseCmdline(const Strings & _cmdline) } bool argsSeen = false; - for (auto pos = cmdline.begin(); pos != cmdline.end(); ) { + for (auto pos = cmdline.begin(); pos != cmdline.end();) { auto arg = *pos; /* Expand compound dash options (i.e., `-qlf' -> `-q -l -f', `-j3` -> `-j 3`). */ - if (!dashDash && arg.length() > 2 && arg[0] == '-' && arg[1] != '-' && isalpha(arg[1])) { + if (!dashDash && arg.length() > 2 && arg[0] == '-' && arg[1] != '-' && + isalpha(arg[1])) { *pos = (std::string) "-" + arg[1]; - auto next = pos; ++next; + auto next = pos; + ++next; for (unsigned int j = 2; j < arg.length(); j++) if (isalpha(arg[j])) cmdline.insert(next, (std::string) "-" + arg[j]); @@ -91,12 +96,10 @@ void Args::parseCmdline(const Strings & _cmdline) if (!dashDash && arg == "--") { dashDash = true; ++pos; - } - else if (!dashDash && std::string(arg, 0, 1) == "-") { + } else if (!dashDash && std::string(arg, 0, 1) == "-") { if (!processFlag(pos, cmdline.end())) throw UsageError("unrecognised flag '%1%'", arg); - } - else { + } else { if (!argsSeen) { argsSeen = true; initialFlagsProcessed(); @@ -122,10 +125,12 @@ bool Args::processFlag(Strings::iterator & pos, Strings::iterator end) ++pos; std::vector args; bool anyCompleted = false; - for (size_t n = 0 ; n < flag.handler.arity; ++n) { + for (size_t n = 0; n < flag.handler.arity; ++n) { if (pos == end) { - if (flag.handler.arity == ArityAny) break; - throw UsageError("flag '%s' requires %d argument(s)", name, flag.handler.arity); + if (flag.handler.arity == ArityAny) + break; + throw UsageError("flag '%s' requires %d argument(s)", name, + flag.handler.arity); } if (auto prefix = needsCompletion(*pos)) { anyCompleted = true; @@ -142,21 +147,23 @@ bool Args::processFlag(Strings::iterator & pos, Strings::iterator end) if (std::string(*pos, 0, 2) == "--") { if (auto prefix = needsCompletion(*pos)) { for (auto & [name, flag] : longFlags) { - if (!hiddenCategories.count(flag->category) - && hasPrefix(name, std::string(*prefix, 2))) + if (!hiddenCategories.count(flag->category) && + hasPrefix(name, std::string(*prefix, 2))) completions->add("--" + name, flag->description); } return false; } auto i = longFlags.find(std::string(*pos, 2)); - if (i == longFlags.end()) return false; + if (i == longFlags.end()) + return false; return process("--" + i->first, *i->second); } if (std::string(*pos, 0, 1) == "-" && pos->size() == 2) { auto c = (*pos)[1]; auto i = shortFlags.find(c); - if (i == shortFlags.end()) return false; + if (i == shortFlags.end()) + return false; return process(std::string("-") + c, *i->second); } @@ -164,7 +171,8 @@ bool Args::processFlag(Strings::iterator & pos, Strings::iterator end) if (prefix == "-") { completions->add("--"); for (auto & [flagName, flag] : shortFlags) - completions->add(std::string("-") + flagName, flag->description); + completions->add(std::string("-") + flagName, + flag->description); } } @@ -184,10 +192,9 @@ bool Args::processArgs(const Strings & args, bool finish) bool res = false; if ((exp.handler.arity == ArityAny && finish) || - (exp.handler.arity != ArityAny && args.size() == exp.handler.arity)) - { + (exp.handler.arity != ArityAny && args.size() == exp.handler.arity)) { std::vector ss; - for (const auto &[n, s] : enumerate(args)) { + for (const auto & [n, s] : enumerate(args)) { if (auto prefix = needsCompletion(s)) { ss.push_back(*prefix); if (exp.completer) @@ -212,7 +219,8 @@ nlohmann::json Args::toJSON() for (auto & [name, flag] : longFlags) { auto j = nlohmann::json::object(); - if (flag->aliases.count(name)) continue; + if (flag->aliases.count(name)) + continue; if (flag->shortName) j["shortName"] = std::string(1, flag->shortName); if (flag->description != "") @@ -241,7 +249,8 @@ nlohmann::json Args::toJSON() res["flags"] = std::move(flags); res["args"] = std::move(args); auto s = doc(); - if (s != "") res.emplace("doc", stripIndentation(s)); + if (s != "") + res.emplace("doc", stripIndentation(s)); return res; } @@ -254,28 +263,26 @@ static void hashTypeCompleter(size_t index, std::string_view prefix) Args::Flag Args::Flag::mkHashTypeFlag(std::string && longName, HashType * ht) { - return Flag { - .longName = std::move(longName), - .description = "hash algorithm ('md5', 'sha1', 'sha256', or 'sha512')", - .labels = {"hash-algo"}, - .handler = {[ht](std::string s) { - *ht = parseHashType(s); - }}, - .completer = hashTypeCompleter - }; + return Flag{.longName = std::move(longName), + .description = + "hash algorithm ('md5', 'sha1', 'sha256', or 'sha512')", + .labels = {"hash-algo"}, + .handler = {[ht](std::string s) { *ht = parseHashType(s); }}, + .completer = hashTypeCompleter}; } -Args::Flag Args::Flag::mkHashTypeOptFlag(std::string && longName, std::optional * oht) +Args::Flag Args::Flag::mkHashTypeOptFlag(std::string && longName, + std::optional * oht) { - return Flag { - .longName = std::move(longName), - .description = "hash algorithm ('md5', 'sha1', 'sha256', or 'sha512'). Optional as can also be gotten from SRI hash itself.", - .labels = {"hash-algo"}, - .handler = {[oht](std::string s) { - *oht = std::optional { parseHashType(s) }; - }}, - .completer = hashTypeCompleter - }; + return Flag{.longName = std::move(longName), + .description = + "hash algorithm ('md5', 'sha1', 'sha256', or 'sha512'). " + "Optional as can also be gotten from SRI hash itself.", + .labels = {"hash-algo"}, + .handler = {[oht](std::string s) { + *oht = std::optional{parseHashType(s)}; + }}, + .completer = hashTypeCompleter}; } static void _completePath(std::string_view prefix, bool onlyDirs) @@ -283,16 +290,19 @@ static void _completePath(std::string_view prefix, bool onlyDirs) completionType = ctFilenames; glob_t globbuf; int flags = GLOB_NOESCAPE; - #ifdef GLOB_ONLYDIR +#ifdef GLOB_ONLYDIR if (onlyDirs) flags |= GLOB_ONLYDIR; - #endif - // using expandTilde here instead of GLOB_TILDE(_CHECK) so that ~ expands to /home/user/ - if (glob((expandTilde(prefix) + "*").c_str(), flags, nullptr, &globbuf) == 0) { +#endif + // using expandTilde here instead of GLOB_TILDE(_CHECK) so that ~ + // expands to /home/user/ + if (glob((expandTilde(prefix) + "*").c_str(), flags, nullptr, &globbuf) == + 0) { for (size_t i = 0; i < globbuf.gl_pathc; ++i) { if (onlyDirs) { auto st = stat(globbuf.gl_pathv[i]); - if (!S_ISDIR(st.st_mode)) continue; + if (!S_ISDIR(st.st_mode)) + continue; } completions->add(globbuf.gl_pathv[i]); } @@ -310,47 +320,50 @@ void completeDir(size_t, std::string_view prefix) _completePath(prefix, true); } -Strings argvToStrings(int argc, char * * argv) +Strings argvToStrings(int argc, char ** argv) { Strings args; - argc--; argv++; - while (argc--) args.push_back(*argv++); + argc--; + argv++; + while (argc--) + args.push_back(*argv++); return args; } -MultiCommand::MultiCommand(const Commands & commands_) - : commands(commands_) +MultiCommand::MultiCommand(const Commands & commands_) : commands(commands_) { - expectArgs({ - .label = "subcommand", - .optional = true, - .handler = {[=](std::string s) { - assert(!command); - auto i = commands.find(s); - if (i == commands.end()) { - std::set commandNames; - for (auto & [name, _] : commands) - commandNames.insert(name); - auto suggestions = Suggestions::bestMatches(commandNames, s); - throw UsageError(suggestions, "'%s' is not a recognised command", s); - } - command = {s, i->second()}; - command->second->parent = this; - }}, - .completer = {[&](size_t, std::string_view prefix) { - for (auto & [name, command] : commands) - if (hasPrefix(name, prefix)) - completions->add(name); - }} - }); + expectArgs({.label = "subcommand", + .optional = true, + .handler = {[=](std::string s) { + assert(!command); + auto i = commands.find(s); + if (i == commands.end()) { + std::set commandNames; + for (auto & [name, _] : commands) + commandNames.insert(name); + auto suggestions = + Suggestions::bestMatches(commandNames, s); + throw UsageError(suggestions, + "'%s' is not a recognised command", s); + } + command = {s, i->second()}; + command->second->parent = this; + }}, + .completer = {[&](size_t, std::string_view prefix) { + for (auto & [name, command] : commands) + if (hasPrefix(name, prefix)) + completions->add(name); + }}}); categories[Command::catDefault] = "Available commands"; } bool MultiCommand::processFlag(Strings::iterator & pos, Strings::iterator end) { - if (Args::processFlag(pos, end)) return true; - if (command && command->second->processFlag(pos, end)) return true; + if (Args::processFlag(pos, end)) + return true; + if (command && command->second->processFlag(pos, end)) + return true; return false; } diff --git a/src/libutil/args.hh b/src/libutil/args.hh index 07c017719e95..6ba9b9d3f4b6 100644 --- a/src/libutil/args.hh +++ b/src/libutil/args.hh @@ -14,10 +14,8 @@ enum HashType : char; class MultiCommand; -class Args -{ -public: - +class Args { + public: /* Parse the command line, throwing a UsageError if something goes wrong. */ void parseCmdline(const Strings & cmdline); @@ -30,82 +28,87 @@ public: /* Return documentation about this command, in Markdown format. */ virtual std::string doc() { return ""; } -protected: - + protected: static const size_t ArityAny = std::numeric_limits::max(); - struct Handler - { + struct Handler { std::function)> fun; size_t arity; Handler() {} Handler(std::function)> && fun) - : fun(std::move(fun)) - , arity(ArityAny) - { } + : fun(std::move(fun)), arity(ArityAny) + { + } Handler(std::function && handler) - : fun([handler{std::move(handler)}](std::vector) { handler(); }) - , arity(0) - { } + : fun([handler{std::move(handler)}](std::vector) { + handler(); + }), + arity(0) + { + } Handler(std::function && handler) : fun([handler{std::move(handler)}](std::vector ss) { - handler(std::move(ss[0])); - }) - , arity(1) - { } + handler(std::move(ss[0])); + }), + arity(1) + { + } Handler(std::function && handler) : fun([handler{std::move(handler)}](std::vector ss) { - handler(std::move(ss[0]), std::move(ss[1])); - }) - , arity(2) - { } + handler(std::move(ss[0]), std::move(ss[1])); + }), + arity(2) + { + } Handler(std::vector * dest) - : fun([=](std::vector ss) { *dest = ss; }) - , arity(ArityAny) - { } + : fun([=](std::vector ss) { *dest = ss; }), + arity(ArityAny) + { + } Handler(std::string * dest) - : fun([=](std::vector ss) { *dest = ss[0]; }) - , arity(1) - { } + : fun([=](std::vector ss) { *dest = ss[0]; }), arity(1) + { + } Handler(std::optional * dest) - : fun([=](std::vector ss) { *dest = ss[0]; }) - , arity(1) - { } + : fun([=](std::vector ss) { *dest = ss[0]; }), arity(1) + { + } template Handler(T * dest, const T & val) - : fun([=](std::vector ss) { *dest = val; }) - , arity(0) - { } + : fun([=](std::vector ss) { *dest = val; }), arity(0) + { + } template Handler(I * dest) : fun([=](std::vector ss) { - *dest = string2IntWithUnitPrefix(ss[0]); - }) - , arity(1) - { } + *dest = string2IntWithUnitPrefix(ss[0]); + }), + arity(1) + { + } template Handler(std::optional * dest) : fun([=](std::vector ss) { - *dest = string2IntWithUnitPrefix(ss[0]); - }) - , arity(1) - { } + *dest = string2IntWithUnitPrefix(ss[0]); + }), + arity(1) + { + } }; /* Options. */ - struct Flag - { + struct Flag { typedef std::shared_ptr ptr; std::string longName; @@ -118,7 +121,8 @@ protected: std::function completer; static Flag mkHashTypeFlag(std::string && longName, HashType * ht); - static Flag mkHashTypeOptFlag(std::string && longName, std::optional * oht); + static Flag mkHashTypeOptFlag(std::string && longName, + std::optional * oht); }; std::map longFlags; @@ -127,8 +131,7 @@ protected: virtual bool processFlag(Strings::iterator & pos, Strings::iterator end); /* Positional arguments. */ - struct ExpectedArg - { + struct ExpectedArg { std::string label; bool optional = false; Handler handler; @@ -140,7 +143,9 @@ protected: virtual bool processArgs(const Strings & args, bool finish); virtual Strings::iterator rewriteArgs(Strings & args, Strings::iterator pos) - { return pos; } + { + return pos; + } std::set hiddenCategories; @@ -148,8 +153,7 @@ protected: argument (if any) have been processed. */ virtual void initialFlagsProcessed() {} -public: - + public: void addFlag(Flag && flag); void removeFlag(const std::string & longName); @@ -160,22 +164,16 @@ public: } /* Expect a string argument. */ - void expectArg(const std::string & label, std::string * dest, bool optional = false) + void expectArg(const std::string & label, std::string * dest, + bool optional = false) { - expectArgs({ - .label = label, - .optional = optional, - .handler = {dest} - }); + expectArgs({.label = label, .optional = optional, .handler = {dest}}); } /* Expect 0 or more arguments. */ void expectArgs(const std::string & label, std::vector * dest) { - expectArgs({ - .label = label, - .handler = {dest} - }); + expectArgs({.label = label, .handler = {dest}}); } virtual nlohmann::json toJSON(); @@ -187,13 +185,12 @@ public: /* A command is an argument parser that can be executed by calling its run() method. */ -struct Command : virtual public Args -{ +struct Command : virtual public Args { friend class MultiCommand; - virtual ~Command() { } + virtual ~Command() {} - virtual void prepare() { }; + virtual void prepare(){}; virtual void run() = 0; typedef int Category; @@ -207,9 +204,8 @@ typedef std::map()>> Commands; /* An argument parser that supports multiple subcommands, i.e. ‘ ’. */ -class MultiCommand : virtual public Args -{ -public: +class MultiCommand : virtual public Args { + public: Commands commands; std::map categories; @@ -226,7 +222,7 @@ public: nlohmann::json toJSON() override; }; -Strings argvToStrings(int argc, char * * argv); +Strings argvToStrings(int argc, char ** argv); struct Completion { std::string completion; @@ -235,16 +231,12 @@ struct Completion { bool operator<(const Completion & other) const; }; class Completions : public std::set { -public: + public: void add(std::string completion, std::string description = ""); }; extern std::shared_ptr completions; -enum CompletionType { - ctNormal, - ctFilenames, - ctAttrs -}; +enum CompletionType { ctNormal, ctFilenames, ctAttrs }; extern CompletionType completionType; std::optional needsCompletion(std::string_view s); diff --git a/src/libutil/callback.hh b/src/libutil/callback.hh index ef31794bee30..76b470ef1254 100644 --- a/src/libutil/callback.hh +++ b/src/libutil/callback.hh @@ -8,20 +8,18 @@ namespace nix { /* A callback is a wrapper around a lambda that accepts a valid of type T or an exception. (We abuse std::future to pass the value or exception.) */ -template -class Callback -{ +template class Callback { std::function)> fun; std::atomic_flag done = ATOMIC_FLAG_INIT; -public: - - Callback(std::function)> fun) : fun(fun) { } + public: + Callback(std::function)> fun) : fun(fun) {} Callback(Callback && callback) : fun(std::move(callback.fun)) { auto prev = callback.done.test_and_set(); - if (prev) done.test_and_set(); + if (prev) + done.test_and_set(); } void operator()(T && t) noexcept @@ -33,7 +31,8 @@ public: fun(promise.get_future()); } - void rethrow(const std::exception_ptr & exc = std::current_exception()) noexcept + void + rethrow(const std::exception_ptr & exc = std::current_exception()) noexcept { auto prev = done.test_and_set(); assert(!prev); diff --git a/src/libutil/chunked-vector.hh b/src/libutil/chunked-vector.hh index 0a4f0b400d14..2ae07461a043 100644 --- a/src/libutil/chunked-vector.hh +++ b/src/libutil/chunked-vector.hh @@ -14,15 +14,13 @@ namespace nix { on large data sets by on average (growth factor)/2, mostly eliminates copies within the vector during resizing, and provides stable references to its elements. */ -template -class ChunkedVector { -private: +template class ChunkedVector { + private: uint32_t size_ = 0; std::vector> chunks; /* keep this out of the ::add hot path */ - [[gnu::noinline]] - auto & addChunk() + [[gnu::noinline]] auto & addChunk() { if (size_ >= std::numeric_limits::max() - ChunkSize) abort(); @@ -31,7 +29,7 @@ private: return chunks.back(); } -public: + public: ChunkedVector(uint32_t reserve) { chunks.reserve(reserve); @@ -43,11 +41,13 @@ public: std::pair add(T value) { const auto idx = size_++; - auto & chunk = [&] () -> auto & { + auto & chunk = [&]() -> auto & + { if (auto & back = chunks.back(); back.size() < ChunkSize) return back; return addChunk(); - }(); + } + (); auto & result = chunk.emplace_back(std::move(value)); return {result, idx}; } @@ -57,8 +57,7 @@ public: return chunks[idx / ChunkSize][idx % ChunkSize]; } - template - void forEach(Fn fn) const + template void forEach(Fn fn) const { for (const auto & c : chunks) for (const auto & e : c) diff --git a/src/libutil/closure.hh b/src/libutil/closure.hh index 779b9b2d54a3..2afee0d65de8 100644 --- a/src/libutil/closure.hh +++ b/src/libutil/closure.hh @@ -7,17 +7,14 @@ using std::set; namespace nix { template -using GetEdgesAsync = std::function> &)>)>; +using GetEdgesAsync = + std::function> &)>)>; template -void computeClosure( - const set startElts, - set & res, - GetEdgesAsync getEdgesAsync -) +void computeClosure(const set startElts, set & res, + GetEdgesAsync getEdgesAsync) { - struct State - { + struct State { size_t pending; set & res; std::exception_ptr exc; @@ -32,8 +29,10 @@ void computeClosure( enqueue = [&](const T & current) -> void { { auto state(state_.lock()); - if (state->exc) return; - if (!state->res.insert(current).second) return; + if (state->exc) + return; + if (!state->res.insert(current).second) + return; state->pending++; } @@ -45,13 +44,16 @@ void computeClosure( { auto state(state_.lock()); assert(state->pending); - if (!--state->pending) done.notify_one(); + if (!--state->pending) + done.notify_one(); } } catch (...) { auto state(state_.lock()); - if (!state->exc) state->exc = std::current_exception(); + if (!state->exc) + state->exc = std::current_exception(); assert(state->pending); - if (!--state->pending) done.notify_one(); + if (!--state->pending) + done.notify_one(); }; }); }; @@ -61,8 +63,10 @@ void computeClosure( { auto state(state_.lock()); - while (state->pending) state.wait(done); - if (state->exc) std::rethrow_exception(state->exc); + while (state->pending) + state.wait(done); + if (state->exc) + std::rethrow_exception(state->exc); } } diff --git a/src/libutil/comparator.hh b/src/libutil/comparator.hh index eecd5b819c63..e185738650de 100644 --- a/src/libutil/comparator.hh +++ b/src/libutil/comparator.hh @@ -15,18 +15,19 @@ * } * ``` */ -#define GENERATE_ONE_CMP(COMPARATOR, MY_TYPE, FIELDS...) \ - bool operator COMPARATOR(const MY_TYPE& other) const { \ - const MY_TYPE* me = this; \ - auto fields1 = std::make_tuple( FIELDS ); \ - me = &other; \ - auto fields2 = std::make_tuple( FIELDS ); \ - return fields1 COMPARATOR fields2; \ +#define GENERATE_ONE_CMP(COMPARATOR, MY_TYPE, FIELDS...) \ + bool operator COMPARATOR(const MY_TYPE & other) const \ + { \ + const MY_TYPE * me = this; \ + auto fields1 = std::make_tuple(FIELDS); \ + me = &other; \ + auto fields2 = std::make_tuple(FIELDS); \ + return fields1 COMPARATOR fields2; \ } #define GENERATE_EQUAL(args...) GENERATE_ONE_CMP(==, args) #define GENERATE_LEQ(args...) GENERATE_ONE_CMP(<, args) #define GENERATE_NEQ(args...) GENERATE_ONE_CMP(!=, args) -#define GENERATE_CMP(args...) \ - GENERATE_EQUAL(args) \ - GENERATE_LEQ(args) \ +#define GENERATE_CMP(args...) \ + GENERATE_EQUAL(args) \ + GENERATE_LEQ(args) \ GENERATE_NEQ(args) diff --git a/src/libutil/compression.cc b/src/libutil/compression.cc index 89180e7a756f..7f829ff5b6e9 100644 --- a/src/libutil/compression.cc +++ b/src/libutil/compression.cc @@ -19,8 +19,7 @@ namespace nix { static const int COMPRESSION_LEVEL_DEFAULT = -1; // Don't feed brotli too much at once. -struct ChunkedCompressionSink : CompressionSink -{ +struct ChunkedCompressionSink : CompressionSink { uint8_t outbuf[32 * 1024]; void write(std::string_view data) override @@ -36,24 +35,26 @@ struct ChunkedCompressionSink : CompressionSink virtual void writeInternal(std::string_view data) = 0; }; -struct ArchiveDecompressionSource : Source -{ +struct ArchiveDecompressionSource : Source { std::unique_ptr archive = 0; Source & src; ArchiveDecompressionSource(Source & src) : src(src) {} ~ArchiveDecompressionSource() override {} - size_t read(char * data, size_t len) override { + size_t read(char * data, size_t len) override + { struct archive_entry * ae; if (!archive) { archive = std::make_unique(src, true); - this->archive->check(archive_read_next_header(this->archive->archive, &ae), + this->archive->check( + archive_read_next_header(this->archive->archive, &ae), "failed to read header (%s)"); if (archive_filter_count(this->archive->archive) < 2) { throw CompressionError("input compression not recognized"); } } ssize_t result = archive_read_data(this->archive->archive, data, len); - if (result > 0) return result; + if (result > 0) + return result; if (result == 0) { throw EndOfFile("reached end of compressed file"); } @@ -62,21 +63,27 @@ struct ArchiveDecompressionSource : Source } }; -struct ArchiveCompressionSink : CompressionSink -{ +struct ArchiveCompressionSink : CompressionSink { Sink & nextSink; struct archive * archive; - ArchiveCompressionSink(Sink & nextSink, std::string format, bool parallel, int level = COMPRESSION_LEVEL_DEFAULT) : nextSink(nextSink) + ArchiveCompressionSink(Sink & nextSink, std::string format, bool parallel, + int level = COMPRESSION_LEVEL_DEFAULT) + : nextSink(nextSink) { archive = archive_write_new(); - if (!archive) throw Error("failed to initialize libarchive"); - check(archive_write_add_filter_by_name(archive, format.c_str()), "couldn't initialize compression (%s)"); + if (!archive) + throw Error("failed to initialize libarchive"); + check(archive_write_add_filter_by_name(archive, format.c_str()), + "couldn't initialize compression (%s)"); check(archive_write_set_format_raw(archive)); if (parallel) - check(archive_write_set_filter_option(archive, format.c_str(), "threads", "0")); + check(archive_write_set_filter_option(archive, format.c_str(), + "threads", "0")); if (level != COMPRESSION_LEVEL_DEFAULT) - check(archive_write_set_filter_option(archive, format.c_str(), "compression-level", std::to_string(level).c_str())); + check(archive_write_set_filter_option( + archive, format.c_str(), "compression-level", + std::to_string(level).c_str())); // disable internal buffering check(archive_write_set_bytes_per_block(archive, 0)); // disable output padding @@ -86,7 +93,8 @@ struct ArchiveCompressionSink : CompressionSink ~ArchiveCompressionSink() override { - if (archive) archive_write_free(archive); + if (archive) + archive_write_free(archive); } void finish() override @@ -105,21 +113,26 @@ struct ArchiveCompressionSink : CompressionSink void write(std::string_view data) override { - ssize_t result = archive_write_data(archive, data.data(), data.length()); - if (result <= 0) check(result); + ssize_t result = + archive_write_data(archive, data.data(), data.length()); + if (result <= 0) + check(result); } -private: + private: void open() { - check(archive_write_open(archive, this, nullptr, ArchiveCompressionSink::callback_write, nullptr)); + check(archive_write_open(archive, this, nullptr, + ArchiveCompressionSink::callback_write, + nullptr)); auto ae = archive_entry_new(); archive_entry_set_filetype(ae, AE_IFREG); check(archive_write_header(archive, ae)); archive_entry_free(ae); } - static ssize_t callback_write(struct archive * archive, void * _self, const void * buffer, size_t length) + static ssize_t callback_write(struct archive * archive, void * _self, + const void * buffer, size_t length) { auto self = (ArchiveCompressionSink *) _self; self->nextSink({(const char *) buffer, length}); @@ -127,20 +140,21 @@ struct ArchiveCompressionSink : CompressionSink } }; -struct NoneSink : CompressionSink -{ +struct NoneSink : CompressionSink { Sink & nextSink; - NoneSink(Sink & nextSink, int level = COMPRESSION_LEVEL_DEFAULT) : nextSink(nextSink) + NoneSink(Sink & nextSink, int level = COMPRESSION_LEVEL_DEFAULT) + : nextSink(nextSink) { if (level != COMPRESSION_LEVEL_DEFAULT) - warn("requested compression level '%d' not supported by compression method 'none'", level); + warn("requested compression level '%d' not supported by " + "compression method 'none'", + level); } void finish() override { flush(); } void write(std::string_view data) override { nextSink(data); } }; -struct BrotliDecompressionSink : ChunkedCompressionSink -{ +struct BrotliDecompressionSink : ChunkedCompressionSink { Sink & nextSink; BrotliDecoderState * state; bool finished = false; @@ -152,10 +166,7 @@ struct BrotliDecompressionSink : ChunkedCompressionSink throw CompressionError("unable to initialize brotli decoder"); } - ~BrotliDecompressionSink() - { - BrotliDecoderDestroyInstance(state); - } + ~BrotliDecompressionSink() { BrotliDecoderDestroyInstance(state); } void finish() override { @@ -173,10 +184,8 @@ struct BrotliDecompressionSink : ChunkedCompressionSink while (!finished && (!data.data() || avail_in)) { checkInterrupt(); - if (!BrotliDecoderDecompressStream(state, - &avail_in, &next_in, - &avail_out, &next_out, - nullptr)) + if (!BrotliDecoderDecompressStream(state, &avail_in, &next_in, + &avail_out, &next_out, nullptr)) throw CompressionError("error while decompressing brotli file"); if (avail_out < sizeof(outbuf) || avail_in == 0) { @@ -199,7 +208,8 @@ std::string decompress(const std::string & method, std::string_view in) return std::move(ssink.s); } -std::unique_ptr makeDecompressionSink(const std::string & method, Sink & nextSink) +std::unique_ptr makeDecompressionSink(const std::string & method, + Sink & nextSink) { if (method == "none" || method == "") return std::make_unique(nextSink); @@ -207,13 +217,13 @@ std::unique_ptr makeDecompressionSink(const std::string & method, Si return std::make_unique(nextSink); else return sourceToSink([&](Source & source) { - auto decompressionSource = std::make_unique(source); + auto decompressionSource = + std::make_unique(source); decompressionSource->drainInto(nextSink); }); } -struct BrotliCompressionSink : ChunkedCompressionSink -{ +struct BrotliCompressionSink : ChunkedCompressionSink { Sink & nextSink; uint8_t outbuf[BUFSIZ]; BrotliEncoderState * state; @@ -226,10 +236,7 @@ struct BrotliCompressionSink : ChunkedCompressionSink throw CompressionError("unable to initialise brotli encoder"); } - ~BrotliCompressionSink() - { - BrotliEncoderDestroyInstance(state); - } + ~BrotliCompressionSink() { BrotliEncoderDestroyInstance(state); } void finish() override { @@ -247,12 +254,13 @@ struct BrotliCompressionSink : ChunkedCompressionSink while (!finished && (!data.data() || avail_in)) { checkInterrupt(); - if (!BrotliEncoderCompressStream(state, - data.data() ? BROTLI_OPERATION_PROCESS : BROTLI_OPERATION_FINISH, - &avail_in, &next_in, - &avail_out, &next_out, - nullptr)) - throw CompressionError("error while compressing brotli compression"); + if (!BrotliEncoderCompressStream( + state, + data.data() ? BROTLI_OPERATION_PROCESS + : BROTLI_OPERATION_FINISH, + &avail_in, &next_in, &avail_out, &next_out, nullptr)) + throw CompressionError( + "error while compressing brotli compression"); if (avail_out < sizeof(outbuf) || avail_in == 0) { nextSink({(const char *) outbuf, sizeof(outbuf) - avail_out}); @@ -265,23 +273,29 @@ struct BrotliCompressionSink : ChunkedCompressionSink } }; -ref makeCompressionSink(const std::string & method, Sink & nextSink, const bool parallel, int level) +ref makeCompressionSink(const std::string & method, + Sink & nextSink, const bool parallel, + int level) { std::vector la_supports = { - "bzip2", "compress", "grzip", "gzip", "lrzip", "lz4", "lzip", "lzma", "lzop", "xz", "zstd" - }; - if (std::find(la_supports.begin(), la_supports.end(), method) != la_supports.end()) { - return make_ref(nextSink, method, parallel, level); + "bzip2", "compress", "grzip", "gzip", "lrzip", "lz4", + "lzip", "lzma", "lzop", "xz", "zstd"}; + if (std::find(la_supports.begin(), la_supports.end(), method) != + la_supports.end()) { + return make_ref(nextSink, method, parallel, + level); } if (method == "none") return make_ref(nextSink); else if (method == "br") return make_ref(nextSink); else - throw UnknownCompressionMethod("unknown compression method '%s'", method); + throw UnknownCompressionMethod("unknown compression method '%s'", + method); } -std::string compress(const std::string & method, std::string_view in, const bool parallel, int level) +std::string compress(const std::string & method, std::string_view in, + const bool parallel, int level) { StringSink ssink; auto sink = makeCompressionSink(method, ssink, parallel, level); diff --git a/src/libutil/compression.hh b/src/libutil/compression.hh index c470b82a5093..0d747b7872a2 100644 --- a/src/libutil/compression.hh +++ b/src/libutil/compression.hh @@ -8,20 +8,24 @@ namespace nix { -struct CompressionSink : BufferedSink, FinishSink -{ - using BufferedSink::operator (); +struct CompressionSink : BufferedSink, FinishSink { + using BufferedSink::operator(); using BufferedSink::write; using FinishSink::finish; }; std::string decompress(const std::string & method, std::string_view in); -std::unique_ptr makeDecompressionSink(const std::string & method, Sink & nextSink); +std::unique_ptr makeDecompressionSink(const std::string & method, + Sink & nextSink); -std::string compress(const std::string & method, std::string_view in, const bool parallel = false, int level = -1); +std::string compress(const std::string & method, std::string_view in, + const bool parallel = false, int level = -1); -ref makeCompressionSink(const std::string & method, Sink & nextSink, const bool parallel = false, int level = -1); +ref makeCompressionSink(const std::string & method, + Sink & nextSink, + const bool parallel = false, + int level = -1); MakeError(UnknownCompressionMethod, Error); diff --git a/src/libutil/compute-levels.cc b/src/libutil/compute-levels.cc index 19eaedfa8d1c..2a85055cfb39 100644 --- a/src/libutil/compute-levels.cc +++ b/src/libutil/compute-levels.cc @@ -8,7 +8,8 @@ namespace nix { #if HAVE_LIBCPUID -StringSet computeLevels() { +StringSet computeLevels() +{ StringSet levels; if (!cpuid_present()) @@ -23,45 +24,34 @@ StringSet computeLevels() { if (cpu_identify(&raw, &data) < 0) return levels; - if (!(data.flags[CPU_FEATURE_CMOV] && - data.flags[CPU_FEATURE_CX8] && - data.flags[CPU_FEATURE_FPU] && - data.flags[CPU_FEATURE_FXSR] && - data.flags[CPU_FEATURE_MMX] && - data.flags[CPU_FEATURE_SSE] && - data.flags[CPU_FEATURE_SSE2])) + if (!(data.flags[CPU_FEATURE_CMOV] && data.flags[CPU_FEATURE_CX8] && + data.flags[CPU_FEATURE_FPU] && data.flags[CPU_FEATURE_FXSR] && + data.flags[CPU_FEATURE_MMX] && data.flags[CPU_FEATURE_SSE] && + data.flags[CPU_FEATURE_SSE2])) return levels; levels.insert("x86_64-v1"); - if (!(data.flags[CPU_FEATURE_CX16] && - data.flags[CPU_FEATURE_LAHF_LM] && - data.flags[CPU_FEATURE_POPCNT] && - // SSE3 - data.flags[CPU_FEATURE_PNI] && - data.flags[CPU_FEATURE_SSSE3] && - data.flags[CPU_FEATURE_SSE4_1] && - data.flags[CPU_FEATURE_SSE4_2])) + if (!(data.flags[CPU_FEATURE_CX16] && data.flags[CPU_FEATURE_LAHF_LM] && + data.flags[CPU_FEATURE_POPCNT] && + // SSE3 + data.flags[CPU_FEATURE_PNI] && data.flags[CPU_FEATURE_SSSE3] && + data.flags[CPU_FEATURE_SSE4_1] && data.flags[CPU_FEATURE_SSE4_2])) return levels; levels.insert("x86_64-v2"); - if (!(data.flags[CPU_FEATURE_AVX] && - data.flags[CPU_FEATURE_AVX2] && - data.flags[CPU_FEATURE_F16C] && - data.flags[CPU_FEATURE_FMA3] && - // LZCNT - data.flags[CPU_FEATURE_ABM] && - data.flags[CPU_FEATURE_MOVBE])) + if (!(data.flags[CPU_FEATURE_AVX] && data.flags[CPU_FEATURE_AVX2] && + data.flags[CPU_FEATURE_F16C] && data.flags[CPU_FEATURE_FMA3] && + // LZCNT + data.flags[CPU_FEATURE_ABM] && data.flags[CPU_FEATURE_MOVBE])) return levels; levels.insert("x86_64-v3"); - if (!(data.flags[CPU_FEATURE_AVX512F] && - data.flags[CPU_FEATURE_AVX512BW] && - data.flags[CPU_FEATURE_AVX512CD] && - data.flags[CPU_FEATURE_AVX512DQ] && - data.flags[CPU_FEATURE_AVX512VL])) + if (!(data.flags[CPU_FEATURE_AVX512F] && data.flags[CPU_FEATURE_AVX512BW] && + data.flags[CPU_FEATURE_AVX512CD] && + data.flags[CPU_FEATURE_AVX512DQ] && data.flags[CPU_FEATURE_AVX512VL])) return levels; levels.insert("x86_64-v4"); @@ -71,9 +61,7 @@ StringSet computeLevels() { #else -StringSet computeLevels() { - return StringSet{}; -} +StringSet computeLevels() { return StringSet{}; } #endif // HAVE_LIBCPUID diff --git a/src/libutil/config.cc b/src/libutil/config.cc index 9bb412b4f5ea..7a04c2f1cbaa 100644 --- a/src/libutil/config.cc +++ b/src/libutil/config.cc @@ -45,8 +45,9 @@ void Config::addSetting(AbstractSetting * setting) auto i = unknownSettings.find(alias); if (i != unknownSettings.end()) { if (set) - warn("setting '%s' is set, but it's an alias of '%s' which is also set", - alias, setting->name); + warn("setting '%s' is set, but it's an alias of '%s' which is " + "also set", + alias, setting->name); else { setting->set(i->second); setting->overridden = true; @@ -70,14 +71,20 @@ void AbstractConfig::reapplyUnknownSettings() set(s.first, s.second); } -void Config::getSettings(std::map & res, bool overriddenOnly) +void Config::getSettings(std::map & res, + bool overriddenOnly) { for (auto & opt : _settings) - if (!opt.second.isAlias && (!overriddenOnly || opt.second.setting->overridden)) - res.emplace(opt.first, SettingInfo{opt.second.setting->to_string(), opt.second.setting->description}); + if (!opt.second.isAlias && + (!overriddenOnly || opt.second.setting->overridden)) + res.emplace(opt.first, + SettingInfo{opt.second.setting->to_string(), + opt.second.setting->description}); } -void AbstractConfig::applyConfig(const std::string & contents, const std::string & path) { +void AbstractConfig::applyConfig(const std::string & contents, + const std::string & path) +{ unsigned int pos = 0; while (pos < contents.size()) { @@ -91,10 +98,12 @@ void AbstractConfig::applyConfig(const std::string & contents, const std::string line = std::string(line, 0, hash); auto tokens = tokenizeString>(line); - if (tokens.empty()) continue; + if (tokens.empty()) + continue; if (tokens.size() < 2) - throw UsageError("illegal configuration line '%1%' in '%2%'", line, path); + throw UsageError("illegal configuration line '%1%' in '%2%'", line, + path); auto include = false; auto ignoreMissing = false; @@ -107,25 +116,29 @@ void AbstractConfig::applyConfig(const std::string & contents, const std::string if (include) { if (tokens.size() != 2) - throw UsageError("illegal configuration line '%1%' in '%2%'", line, path); + throw UsageError("illegal configuration line '%1%' in '%2%'", + line, path); auto p = absPath(tokens[1], dirOf(path)); if (pathExists(p)) { applyConfigFile(p); } else if (!ignoreMissing) { - throw Error("file '%1%' included from '%2%' not found", p, path); + throw Error("file '%1%' included from '%2%' not found", p, + path); } continue; } if (tokens[1] != "=") - throw UsageError("illegal configuration line '%1%' in '%2%'", line, path); + throw UsageError("illegal configuration line '%1%' in '%2%'", line, + path); std::string name = tokens[0]; auto i = tokens.begin(); advance(i, 2); - set(name, concatStringsSep(" ", Strings(i, tokens.end()))); // FIXME: slow + set(name, + concatStringsSep(" ", Strings(i, tokens.end()))); // FIXME: slow }; } @@ -134,7 +147,8 @@ void AbstractConfig::applyConfigFile(const Path & path) try { std::string contents = readFile(path); applyConfig(contents, path); - } catch (SysError &) { } + } catch (SysError &) { + } } void Config::resetOverridden() @@ -170,10 +184,9 @@ void Config::convertToArgs(Args & args, const std::string & category) s.second.setting->convertToArg(args, category); } -AbstractSetting::AbstractSetting( - const std::string & name, - const std::string & description, - const std::set & aliases) +AbstractSetting::AbstractSetting(const std::string & name, + const std::string & description, + const std::set & aliases) : name(name), description(stripIndentation(description)), aliases(aliases) { } @@ -191,15 +204,9 @@ std::map AbstractSetting::toJSONObject() return obj; } -void AbstractSetting::convertToArg(Args & args, const std::string & category) -{ -} +void AbstractSetting::convertToArg(Args & args, const std::string & category) {} -template -bool BaseSetting::isAppendable() -{ - return false; -} +template bool BaseSetting::isAppendable() { return false; } template void BaseSetting::convertToArg(Args & args, const std::string & category) @@ -209,7 +216,10 @@ void BaseSetting::convertToArg(Args & args, const std::string & category) .description = fmt("Set the `%s` setting.", name), .category = category, .labels = {"value"}, - .handler = {[=](std::string s) { overridden = true; set(s); }}, + .handler = {[=](std::string s) { + overridden = true; + set(s); + }}, }); if (isAppendable()) @@ -218,11 +228,15 @@ void BaseSetting::convertToArg(Args & args, const std::string & category) .description = fmt("Append to the `%s` setting.", name), .category = category, .labels = {"value"}, - .handler = {[=](std::string s) { overridden = true; set(s, true); }}, + .handler = {[=](std::string s) { + overridden = true; + set(s, true); + }}, }); } -template<> void BaseSetting::set(const std::string & str, bool append) +template<> +void BaseSetting::set(const std::string & str, bool append) { value = str; } @@ -242,8 +256,7 @@ void BaseSetting::set(const std::string & str, bool append) throw UsageError("setting '%s' has invalid value '%s'", name, str); } -template -std::string BaseSetting::to_string() const +template std::string BaseSetting::to_string() const { static_assert(std::is_integral::value, "Integer required."); return std::to_string(value); @@ -256,7 +269,8 @@ template<> void BaseSetting::set(const std::string & str, bool append) else if (str == "false" || str == "no" || str == "0") value = false; else - throw UsageError("Boolean setting '%s' has invalid value '%s'", name, str); + throw UsageError("Boolean setting '%s' has invalid value '%s'", name, + str); } template<> std::string BaseSetting::to_string() const @@ -264,59 +278,57 @@ template<> std::string BaseSetting::to_string() const return value ? "true" : "false"; } -template<> void BaseSetting::convertToArg(Args & args, const std::string & category) +template<> +void BaseSetting::convertToArg(Args & args, const std::string & category) { - args.addFlag({ - .longName = name, - .description = fmt("Enable the `%s` setting.", name), - .category = category, - .handler = {[=]() { override(true); }} - }); - args.addFlag({ - .longName = "no-" + name, - .description = fmt("Disable the `%s` setting.", name), - .category = category, - .handler = {[=]() { override(false); }} - }); + args.addFlag({.longName = name, + .description = fmt("Enable the `%s` setting.", name), + .category = category, + .handler = {[=]() { override(true); }}}); + args.addFlag({.longName = "no-" + name, + .description = fmt("Disable the `%s` setting.", name), + .category = category, + .handler = {[=]() { override(false); }}}); } template<> void BaseSetting::set(const std::string & str, bool append) { auto ss = tokenizeString(str); - if (!append) value.clear(); - for (auto & s : ss) value.push_back(std::move(s)); + if (!append) + value.clear(); + for (auto & s : ss) + value.push_back(std::move(s)); } -template<> bool BaseSetting::isAppendable() -{ - return true; -} +template<> bool BaseSetting::isAppendable() { return true; } template<> std::string BaseSetting::to_string() const { return concatStringsSep(" ", value); } -template<> void BaseSetting::set(const std::string & str, bool append) +template<> +void BaseSetting::set(const std::string & str, bool append) { - if (!append) value.clear(); + if (!append) + value.clear(); for (auto & s : tokenizeString(str)) value.insert(s); } -template<> bool BaseSetting::isAppendable() -{ - return true; -} +template<> bool BaseSetting::isAppendable() { return true; } template<> std::string BaseSetting::to_string() const { return concatStringsSep(" ", value); } -template<> void BaseSetting>::set(const std::string & str, bool append) +template<> +void BaseSetting>::set(const std::string & str, + bool append) { - if (!append) value.clear(); + if (!append) + value.clear(); for (auto & s : tokenizeString(str)) { auto thisXpFeature = parseExperimentalFeature(s); if (thisXpFeature) @@ -331,17 +343,21 @@ template<> bool BaseSetting>::isAppendable() return true; } -template<> std::string BaseSetting>::to_string() const +template<> +std::string BaseSetting>::to_string() const { StringSet stringifiedXpFeatures; for (auto & feature : value) - stringifiedXpFeatures.insert(std::string(showExperimentalFeature(feature))); + stringifiedXpFeatures.insert( + std::string(showExperimentalFeature(feature))); return concatStringsSep(" ", stringifiedXpFeatures); } -template<> void BaseSetting::set(const std::string & str, bool append) +template<> +void BaseSetting::set(const std::string & str, bool append) { - if (!append) value.clear(); + if (!append) + value.clear(); for (auto & s : tokenizeString(str)) { auto eq = s.find_first_of('='); if (std::string::npos != eq) @@ -350,16 +366,14 @@ template<> void BaseSetting::set(const std::string & str, bool append } } -template<> bool BaseSetting::isAppendable() -{ - return true; -} +template<> bool BaseSetting::isAppendable() { return true; } template<> std::string BaseSetting::to_string() const { Strings kvstrs; - std::transform(value.begin(), value.end(), back_inserter(kvstrs), - [&](auto kvpair){ return kvpair.first + "=" + kvpair.second; }); + std::transform( + value.begin(), value.end(), back_inserter(kvstrs), + [&](auto kvpair) { return kvpair.first + "=" + kvpair.second; }); return concatStringsSep(" ", kvstrs); } @@ -390,14 +404,16 @@ void PathSetting::set(const std::string & str, bool append) bool GlobalConfig::set(const std::string & name, const std::string & value) { for (auto & config : *configRegistrations) - if (config->set(name, value)) return true; + if (config->set(name, value)) + return true; unknownSettings.emplace(name, value); return false; } -void GlobalConfig::getSettings(std::map & res, bool overriddenOnly) +void GlobalConfig::getSettings(std::map & res, + bool overriddenOnly) { for (auto & config : *configRegistrations) config->getSettings(res, overriddenOnly); diff --git a/src/libutil/config.hh b/src/libutil/config.hh index 79ec0f9cf363..38837eaef60c 100644 --- a/src/libutil/config.hh +++ b/src/libutil/config.hh @@ -25,7 +25,8 @@ namespace nix { * `Config` object instance: * * Config config; - * Setting systemSetting{&config, "x86_64-linux", "system", "the current system"}; + * Setting systemSetting{&config, "x86_64-linux", "system", "the + * current system"}; * * The above creates a `Config` object and registers a setting called "system" * via the variable `systemSetting` with it. The setting defaults to the string @@ -45,25 +46,22 @@ namespace nix { class Args; class AbstractSetting; -class AbstractConfig -{ -protected: +class AbstractConfig { + protected: StringMap unknownSettings; - AbstractConfig(const StringMap & initials = {}) - : unknownSettings(initials) - { } - -public: + AbstractConfig(const StringMap & initials = {}) : unknownSettings(initials) + { + } + public: /** * Sets the value referenced by `name` to `value`. Returns true if the * setting is known, false otherwise. */ virtual bool set(const std::string & name, const std::string & value) = 0; - struct SettingInfo - { + struct SettingInfo { std::string value; std::string description; }; @@ -71,16 +69,19 @@ public: /** * Adds the currently known settings to the given result map `res`. * - res: map to store settings in - * - overriddenOnly: when set to true only overridden settings will be added to `res` + * - overriddenOnly: when set to true only overridden settings will be added + * to `res` */ - virtual void getSettings(std::map & res, bool overriddenOnly = false) = 0; + virtual void getSettings(std::map & res, + bool overriddenOnly = false) = 0; /** * Parses the configuration in `contents` and applies it * - contents: configuration contents to be parsed and applied * - path: location of the configuration file */ - void applyConfig(const std::string & contents, const std::string & path = ""); + void applyConfig(const std::string & contents, + const std::string & path = ""); /** * Applies a nix configuration file @@ -138,38 +139,33 @@ public: }; */ -class Config : public AbstractConfig -{ +class Config : public AbstractConfig { friend class AbstractSetting; -public: - - struct SettingData - { + public: + struct SettingData { bool isAlias; AbstractSetting * setting; SettingData(bool isAlias, AbstractSetting * setting) : isAlias(isAlias), setting(setting) - { } + { + } }; typedef std::map Settings; -private: - + private: Settings _settings; -public: - - Config(const StringMap & initials = {}) - : AbstractConfig(initials) - { } + public: + Config(const StringMap & initials = {}) : AbstractConfig(initials) {} bool set(const std::string & name, const std::string & value) override; void addSetting(AbstractSetting * setting); - void getSettings(std::map & res, bool overriddenOnly = false) override; + void getSettings(std::map & res, + bool overriddenOnly = false) override; void resetOverridden() override; @@ -180,12 +176,10 @@ public: void convertToArgs(Args & args, const std::string & category) override; }; -class AbstractSetting -{ +class AbstractSetting { friend class Config; -public: - + public: const std::string name; const std::string description; const std::set aliases; @@ -194,12 +188,9 @@ public: bool overridden = false; -protected: - - AbstractSetting( - const std::string & name, - const std::string & description, - const std::set & aliases); + protected: + AbstractSetting(const std::string & name, const std::string & description, + const std::set & aliases); virtual ~AbstractSetting() { @@ -210,8 +201,7 @@ protected: virtual void set(const std::string & value, bool append = false) = 0; - virtual bool isAppendable() - { return false; } + virtual bool isAppendable() { return false; } virtual std::string to_string() const = 0; @@ -225,36 +215,33 @@ protected: }; /* A setting of type T. */ -template -class BaseSetting : public AbstractSetting -{ -protected: - +template class BaseSetting : public AbstractSetting { + protected: T value; const T defaultValue; const bool documentDefault; -public: - - BaseSetting(const T & def, - const bool documentDefault, - const std::string & name, - const std::string & description, - const std::set & aliases = {}) - : AbstractSetting(name, description, aliases) - , value(def) - , defaultValue(def) - , documentDefault(documentDefault) - { } + public: + BaseSetting(const T & def, const bool documentDefault, + const std::string & name, const std::string & description, + const std::set & aliases = {}) + : AbstractSetting(name, description, aliases), value(def), + defaultValue(def), documentDefault(documentDefault) + { + } operator const T &() const { return value; } operator T &() { return value; } const T & get() const { return value; } - bool operator ==(const T & v2) const { return value == v2; } - bool operator !=(const T & v2) const { return value != v2; } - void operator =(const T & v) { assign(v); } + bool operator==(const T & v2) const { return value == v2; } + bool operator!=(const T & v2) const { return value != v2; } + void operator=(const T & v) { assign(v); } virtual void assign(const T & v) { value = v; } - void setDefault(const T & v) { if (!overridden) value = v; } + void setDefault(const T & v) + { + if (!overridden) + value = v; + } void set(const std::string & str, bool append = false) override; @@ -274,68 +261,61 @@ public: }; template -std::ostream & operator <<(std::ostream & str, const BaseSetting & opt) +std::ostream & operator<<(std::ostream & str, const BaseSetting & opt) { str << (const T &) opt; return str; } -template -bool operator ==(const T & v1, const BaseSetting & v2) { return v1 == (const T &) v2; } - -template -class Setting : public BaseSetting +template bool operator==(const T & v1, const BaseSetting & v2) { -public: - Setting(Config * options, - const T & def, - const std::string & name, - const std::string & description, - const std::set & aliases = {}, - const bool documentDefault = true) + return v1 == (const T &) v2; +} + +template class Setting : public BaseSetting { + public: + Setting(Config * options, const T & def, const std::string & name, + const std::string & description, + const std::set & aliases = {}, + const bool documentDefault = true) : BaseSetting(def, documentDefault, name, description, aliases) { options->addSetting(this); } - void operator =(const T & v) { this->assign(v); } + void operator=(const T & v) { this->assign(v); } }; /* A special setting for Paths. These are automatically canonicalised (e.g. "/foo//bar/" becomes "/foo/bar"). */ -class PathSetting : public BaseSetting -{ +class PathSetting : public BaseSetting { bool allowEmpty; -public: - - PathSetting(Config * options, - bool allowEmpty, - const Path & def, - const std::string & name, - const std::string & description, - const std::set & aliases = {}) - : BaseSetting(def, true, name, description, aliases) - , allowEmpty(allowEmpty) + public: + PathSetting(Config * options, bool allowEmpty, const Path & def, + const std::string & name, const std::string & description, + const std::set & aliases = {}) + : BaseSetting(def, true, name, description, aliases), + allowEmpty(allowEmpty) { options->addSetting(this); } void set(const std::string & str, bool append = false) override; - Path operator +(const char * p) const { return value + p; } + Path operator+(const char * p) const { return value + p; } - void operator =(const Path & v) { this->assign(v); } + void operator=(const Path & v) { this->assign(v); } }; -struct GlobalConfig : public AbstractConfig -{ - typedef std::vector ConfigRegistrations; +struct GlobalConfig : public AbstractConfig { + typedef std::vector ConfigRegistrations; static ConfigRegistrations * configRegistrations; bool set(const std::string & name, const std::string & value) override; - void getSettings(std::map & res, bool overriddenOnly = false) override; + void getSettings(std::map & res, + bool overriddenOnly = false) override; void resetOverridden() override; @@ -345,8 +325,7 @@ struct GlobalConfig : public AbstractConfig void convertToArgs(Args & args, const std::string & category) override; - struct Register - { + struct Register { Register(Config * config); }; }; diff --git a/src/libutil/error.cc b/src/libutil/error.cc index 9172f67a65de..4c1f63d434a2 100644 --- a/src/libutil/error.cc +++ b/src/libutil/error.cc @@ -11,11 +11,12 @@ const std::string nativeSystem = SYSTEM; void BaseError::addTrace(std::optional e, hintformat hint) { - err.traces.push_front(Trace { .pos = e, .hint = hint }); + err.traces.push_front(Trace{.pos = e, .hint = hint}); } // c++ std::exception descendants must have a 'const char* what()' function. -// This stringifies the error and caches it for use by what(), or similarly by msg(). +// This stringifies the error and caches it for use by what(), or similarly by +// msg(). const std::string & BaseError::calcWhat() const { if (what_.has_value()) @@ -43,8 +44,7 @@ std::string showErrPos(const ErrPos & errPos) } else { return fmt("%d", errPos.line); } - } - else { + } else { return ""; } } @@ -60,14 +60,14 @@ std::optional getCodeLines(const ErrPos & errPos) // FIXME: when running as the daemon, make sure we don't // open a file to which the client doesn't have access. AutoCloseFD fd = open(errPos.file.c_str(), O_RDONLY | O_CLOEXEC); - if (!fd) return {}; + if (!fd) + return {}; // count the newlines. int count = 0; std::string line; int pl = errPos.line - 1; - do - { + do { line = readLine(fd.get()); ++count; if (count < pl) @@ -82,14 +82,12 @@ std::optional getCodeLines(const ErrPos & errPos) } } while (true); return loc; - } - catch (EndOfFile & eof) { + } catch (EndOfFile & eof) { if (loc.errLineOfCode.has_value()) return loc; else return std::nullopt; - } - catch (std::exception & e) { + } catch (std::exception & e) { return std::nullopt; } } else { @@ -101,15 +99,12 @@ std::optional getCodeLines(const ErrPos & errPos) LinesOfCode loc; - do - { + do { std::getline(iss, line); ++count; - if (count < pl) - { + if (count < pl) { ; - } - else if (count == pl) { + } else if (count == pl) { loc.prevLineOfCode = line; } else if (count == pl + 1) { loc.errLineOfCode = line; @@ -127,27 +122,21 @@ std::optional getCodeLines(const ErrPos & errPos) } // print lines of code to the ostream, indicating the error column. -void printCodeLines(std::ostream & out, - const std::string & prefix, - const ErrPos & errPos, - const LinesOfCode & loc) +void printCodeLines(std::ostream & out, const std::string & prefix, + const ErrPos & errPos, const LinesOfCode & loc) { // previous line of code. if (loc.prevLineOfCode.has_value()) { out << std::endl - << fmt("%1% %|2$5d|| %3%", - prefix, - (errPos.line - 1), - *loc.prevLineOfCode); + << fmt("%1% %|2$5d|| %3%", prefix, (errPos.line - 1), + *loc.prevLineOfCode); } if (loc.errLineOfCode.has_value()) { // line of code containing the error. out << std::endl - << fmt("%1% %|2$5d|| %3%", - prefix, - (errPos.line), - *loc.errLineOfCode); + << fmt("%1% %|2$5d|| %3%", prefix, (errPos.line), + *loc.errLineOfCode); // error arrows for the column range. if (errPos.column > 0) { int start = errPos.column; @@ -159,20 +148,16 @@ void printCodeLines(std::ostream & out, std::string arrows("^"); out << std::endl - << fmt("%1% |%2%" ANSI_RED "%3%" ANSI_NORMAL, - prefix, - spaces, - arrows); + << fmt("%1% |%2%" ANSI_RED "%3%" ANSI_NORMAL, prefix, + spaces, arrows); } } // next line of code. if (loc.nextLineOfCode.has_value()) { out << std::endl - << fmt("%1% %|2$5d|| %3%", - prefix, - (errPos.line + 1), - *loc.nextLineOfCode); + << fmt("%1% %|2$5d|| %3%", prefix, (errPos.line + 1), + *loc.nextLineOfCode); } } @@ -180,79 +165,89 @@ void printAtPos(const ErrPos & pos, std::ostream & out) { if (pos) { switch (pos.origin) { - case foFile: { - out << fmt(ANSI_BLUE "at " ANSI_WARNING "%s:%s" ANSI_NORMAL ":", pos.file, showErrPos(pos)); - break; - } - case foString: { - out << fmt(ANSI_BLUE "at " ANSI_WARNING "«string»:%s" ANSI_NORMAL ":", showErrPos(pos)); - break; - } - case foStdin: { - out << fmt(ANSI_BLUE "at " ANSI_WARNING "«stdin»:%s" ANSI_NORMAL ":", showErrPos(pos)); - break; - } - default: - throw Error("invalid FileOrigin in errPos"); + case foFile: { + out << fmt(ANSI_BLUE "at " ANSI_WARNING "%s:%s" ANSI_NORMAL ":", + pos.file, showErrPos(pos)); + break; + } + case foString: { + out << fmt(ANSI_BLUE "at " ANSI_WARNING "«string»:%s" ANSI_NORMAL + ":", + showErrPos(pos)); + break; + } + case foStdin: { + out << fmt(ANSI_BLUE "at " ANSI_WARNING "«stdin»:%s" ANSI_NORMAL + ":", + showErrPos(pos)); + break; + } + default: + throw Error("invalid FileOrigin in errPos"); } } } -static std::string indent(std::string_view indentFirst, std::string_view indentRest, std::string_view s) +static std::string indent(std::string_view indentFirst, + std::string_view indentRest, std::string_view s) { std::string res; bool first = true; while (!s.empty()) { auto end = s.find('\n'); - if (!first) res += "\n"; - res += chomp(std::string(first ? indentFirst : indentRest) + std::string(s.substr(0, end))); + if (!first) + res += "\n"; + res += chomp(std::string(first ? indentFirst : indentRest) + + std::string(s.substr(0, end))); first = false; - if (end == s.npos) break; + if (end == s.npos) + break; s = s.substr(end + 1); } return res; } -std::ostream & showErrorInfo(std::ostream & out, const ErrorInfo & einfo, bool showTrace) +std::ostream & showErrorInfo(std::ostream & out, const ErrorInfo & einfo, + bool showTrace) { std::string prefix; switch (einfo.level) { - case Verbosity::lvlError: { - prefix = ANSI_RED "error"; - break; - } - case Verbosity::lvlNotice: { - prefix = ANSI_RED "note"; - break; - } - case Verbosity::lvlWarn: { - prefix = ANSI_WARNING "warning"; - break; - } - case Verbosity::lvlInfo: { - prefix = ANSI_GREEN "info"; - break; - } - case Verbosity::lvlTalkative: { - prefix = ANSI_GREEN "talk"; - break; - } - case Verbosity::lvlChatty: { - prefix = ANSI_GREEN "chat"; - break; - } - case Verbosity::lvlVomit: { - prefix = ANSI_GREEN "vomit"; - break; - } - case Verbosity::lvlDebug: { - prefix = ANSI_WARNING "debug"; - break; - } - default: - assert(false); + case Verbosity::lvlError: { + prefix = ANSI_RED "error"; + break; + } + case Verbosity::lvlNotice: { + prefix = ANSI_RED "note"; + break; + } + case Verbosity::lvlWarn: { + prefix = ANSI_WARNING "warning"; + break; + } + case Verbosity::lvlInfo: { + prefix = ANSI_GREEN "info"; + break; + } + case Verbosity::lvlTalkative: { + prefix = ANSI_GREEN "talk"; + break; + } + case Verbosity::lvlChatty: { + prefix = ANSI_GREEN "chat"; + break; + } + case Verbosity::lvlVomit: { + prefix = ANSI_GREEN "vomit"; + break; + } + case Verbosity::lvlDebug: { + prefix = ANSI_WARNING "debug"; + break; + } + default: + assert(false); } // FIXME: show the program name as part of the trace? @@ -279,16 +274,16 @@ std::ostream & showErrorInfo(std::ostream & out, const ErrorInfo & einfo, bool s } auto suggestions = einfo.suggestions.trim(); - if (! suggestions.suggestions.empty()){ - oss << "Did you mean " << - suggestions.trim() << - "?" << std::endl; + if (!suggestions.suggestions.empty()) { + oss << "Did you mean " << suggestions.trim() << "?" << std::endl; } // traces if (showTrace && !einfo.traces.empty()) { - for (auto iter = einfo.traces.rbegin(); iter != einfo.traces.rend(); ++iter) { - oss << "\n" << "… " << iter->hint.str() << "\n"; + for (auto iter = einfo.traces.rbegin(); iter != einfo.traces.rend(); + ++iter) { + oss << "\n" + << "… " << iter->hint.str() << "\n"; if (iter->pos.has_value() && (*iter->pos)) { auto pos = iter->pos.value(); @@ -305,7 +300,9 @@ std::ostream & showErrorInfo(std::ostream & out, const ErrorInfo & einfo, bool s } } - out << indent(prefix, std::string(filterANSIEscapes(prefix, true).size(), ' '), chomp(oss.str())); + out << indent(prefix, + std::string(filterANSIEscapes(prefix, true).size(), ' '), + chomp(oss.str())); return out; } diff --git a/src/libutil/error.hh b/src/libutil/error.hh index a53e9802e01b..e2cbf24d7d4a 100644 --- a/src/libutil/error.hh +++ b/src/libutil/error.hh @@ -30,14 +30,14 @@ namespace nix { This file defines two main structs/classes used in nix error handling. - ErrorInfo provides a standard payload of error information, with conversion to string - happening in the logger rather than at the call site. + ErrorInfo provides a standard payload of error information, with conversion + to string happening in the logger rather than at the call site. - BaseError is the ancestor of nix specific exceptions (and Interrupted), and contains - an ErrorInfo. + BaseError is the ancestor of nix specific exceptions (and Interrupted), and + contains an ErrorInfo. - ErrorInfo structs are sent to the logger as part of an exception, or directly with the - logError or logWarning macros. + ErrorInfo structs are sent to the logger as part of an exception, or directly + with the logError or logWarning macros. See libutil/tests/logging.cc for usage examples. @@ -55,11 +55,7 @@ typedef enum { } Verbosity; /* adjust Pos::origin bit width when adding stuff here */ -typedef enum { - foFile, - foStdin, - foString -} FileOrigin; +typedef enum { foFile, foStdin, foString } FileOrigin; // the lines of code surrounding an error. struct LinesOfCode { @@ -75,14 +71,10 @@ struct ErrPos { std::string file; FileOrigin origin; - operator bool() const - { - return line != 0; - } + operator bool() const { return line != 0; } // convert from the Pos struct, found in libexpr. - template - ErrPos & operator=(const P & pos) + template ErrPos & operator=(const P & pos) { origin = pos.origin; line = pos.line; @@ -91,19 +83,13 @@ struct ErrPos { return *this; } - template - ErrPos(const P & p) - { - *this = p; - } + template ErrPos(const P & p) { *this = p; } }; std::optional getCodeLines(const ErrPos & errPos); -void printCodeLines(std::ostream & out, - const std::string & prefix, - const ErrPos & errPos, - const LinesOfCode & loc); +void printCodeLines(std::ostream & out, const std::string & prefix, + const ErrPos & errPos, const LinesOfCode & loc); void printAtPos(const ErrPos & pos, std::ostream & out); @@ -123,61 +109,62 @@ struct ErrorInfo { static std::optional programName; }; -std::ostream & showErrorInfo(std::ostream & out, const ErrorInfo & einfo, bool showTrace); +std::ostream & showErrorInfo(std::ostream & out, const ErrorInfo & einfo, + bool showTrace); /* BaseError should generally not be caught, as it has Interrupted as a subclass. Catch Error instead. */ -class BaseError : public std::exception -{ -protected: +class BaseError : public std::exception { + protected: mutable ErrorInfo err; mutable std::optional what_; const std::string & calcWhat() const; -public: + public: unsigned int status = 1; // exit status template - BaseError(unsigned int status, const Args & ... args) - : err { .level = lvlError, .msg = hintfmt(args...) } - , status(status) - { } + BaseError(unsigned int status, const Args &... args) + : err{.level = lvlError, .msg = hintfmt(args...)}, status(status) + { + } template - explicit BaseError(const std::string & fs, const Args & ... args) - : err { .level = lvlError, .msg = hintfmt(fs, args...) } - { } + explicit BaseError(const std::string & fs, const Args &... args) + : err{.level = lvlError, .msg = hintfmt(fs, args...)} + { + } template - BaseError(const Suggestions & sug, const Args & ... args) - : err { .level = lvlError, .msg = hintfmt(args...), .suggestions = sug } - { } + BaseError(const Suggestions & sug, const Args &... args) + : err{.level = lvlError, .msg = hintfmt(args...), .suggestions = sug} + { + } - BaseError(hintformat hint) - : err { .level = lvlError, .msg = hint } - { } + BaseError(hintformat hint) : err{.level = lvlError, .msg = hint} {} - BaseError(ErrorInfo && e) - : err(std::move(e)) - { } + BaseError(ErrorInfo && e) : err(std::move(e)) {} - BaseError(const ErrorInfo & e) - : err(e) - { } + BaseError(const ErrorInfo & e) : err(e) {} #ifdef EXCEPTION_NEEDS_THROW_SPEC - ~BaseError() throw () { }; - const char * what() const throw () { return calcWhat().c_str(); } + ~BaseError() throw(){}; + const char * what() const throw() { return calcWhat().c_str(); } #else const char * what() const noexcept override { return calcWhat().c_str(); } #endif const std::string & msg() const { return calcWhat(); } - const ErrorInfo & info() const { calcWhat(); return err; } + const ErrorInfo & info() const + { + calcWhat(); + return err; + } template - void addTrace(std::optional e, const std::string & fs, const Args & ... args) + void addTrace(std::optional e, const std::string & fs, + const Args &... args) { addTrace(e, hintfmt(fs, args...)); } @@ -187,25 +174,21 @@ public: bool hasTrace() const { return !err.traces.empty(); } }; -#define MakeError(newClass, superClass) \ - class newClass : public superClass \ - { \ - public: \ - using superClass::superClass; \ +#define MakeError(newClass, superClass) \ + class newClass : public superClass { \ + public: \ + using superClass::superClass; \ } MakeError(Error, BaseError); MakeError(UsageError, Error); MakeError(UnimplementedError, Error); -class SysError : public Error -{ -public: +class SysError : public Error { + public: int errNo; - template - SysError(const Args & ... args) - : Error("") + template SysError(const Args &... args) : Error("") { errNo = errno; auto hf = hintfmt(args...); diff --git a/src/libutil/experimental-features.cc b/src/libutil/experimental-features.cc index 315de64a4014..c524787487fb 100644 --- a/src/libutil/experimental-features.cc +++ b/src/libutil/experimental-features.cc @@ -6,21 +6,21 @@ namespace nix { std::map stringifiedXpFeatures = { - { Xp::CaDerivations, "ca-derivations" }, - { Xp::ImpureDerivations, "impure-derivations" }, - { Xp::Flakes, "flakes" }, - { Xp::NixCommand, "nix-command" }, - { Xp::RecursiveNix, "recursive-nix" }, - { Xp::NoUrlLiterals, "no-url-literals" }, - { Xp::FetchClosure, "fetch-closure" }, + {Xp::CaDerivations, "ca-derivations"}, + {Xp::ImpureDerivations, "impure-derivations"}, + {Xp::Flakes, "flakes"}, + {Xp::NixCommand, "nix-command"}, + {Xp::RecursiveNix, "recursive-nix"}, + {Xp::NoUrlLiterals, "no-url-literals"}, + {Xp::FetchClosure, "fetch-closure"}, }; -const std::optional parseExperimentalFeature(const std::string_view & name) +const std::optional +parseExperimentalFeature(const std::string_view & name) { using ReverseXpMap = std::map; - static auto reverseXpMap = []() - { + static auto reverseXpMap = []() { auto reverseXpMap = std::make_unique(); for (auto & [feature, name] : stringifiedXpFeatures) (*reverseXpMap)[name] = feature; @@ -40,7 +40,8 @@ std::string_view showExperimentalFeature(const ExperimentalFeature feature) return *ret; } -std::set parseFeatures(const std::set & rawFeatures) +std::set +parseFeatures(const std::set & rawFeatures) { std::set res; for (auto & rawFeature : rawFeatures) { @@ -50,12 +51,17 @@ std::set parseFeatures(const std::set & rawFea return res; } -MissingExperimentalFeature::MissingExperimentalFeature(ExperimentalFeature feature) - : Error("experimental Nix feature '%1%' is disabled; use '--extra-experimental-features %1%' to override", showExperimentalFeature(feature)) - , missingFeature(feature) -{} +MissingExperimentalFeature::MissingExperimentalFeature( + ExperimentalFeature feature) + : Error("experimental Nix feature '%1%' is disabled; use " + "'--extra-experimental-features %1%' to override", + showExperimentalFeature(feature)), + missingFeature(feature) +{ +} -std::ostream & operator <<(std::ostream & str, const ExperimentalFeature & feature) +std::ostream & operator<<(std::ostream & str, + const ExperimentalFeature & feature) { return str << showExperimentalFeature(feature); } diff --git a/src/libutil/experimental-features.hh b/src/libutil/experimental-features.hh index 57512830c707..5de14abe25d1 100644 --- a/src/libutil/experimental-features.hh +++ b/src/libutil/experimental-features.hh @@ -13,8 +13,7 @@ namespace nix { * If you update this, don’t forget to also change the map defining their * string representation in the corresponding `.cc` file. **/ -enum struct ExperimentalFeature -{ +enum struct ExperimentalFeature { CaDerivations, ImpureDerivations, Flakes, @@ -29,13 +28,12 @@ enum struct ExperimentalFeature */ using Xp = ExperimentalFeature; -const std::optional parseExperimentalFeature( - const std::string_view & name); +const std::optional +parseExperimentalFeature(const std::string_view & name); std::string_view showExperimentalFeature(const ExperimentalFeature); -std::ostream & operator<<( - std::ostream & str, - const ExperimentalFeature & feature); +std::ostream & operator<<(std::ostream & str, + const ExperimentalFeature & feature); /** * Parse a set of strings to the corresponding set of experimental features, @@ -43,9 +41,8 @@ std::ostream & operator<<( */ std::set parseFeatures(const std::set &); -class MissingExperimentalFeature : public Error -{ -public: +class MissingExperimentalFeature : public Error { + public: ExperimentalFeature missingFeature; MissingExperimentalFeature(ExperimentalFeature); diff --git a/src/libutil/finally.hh b/src/libutil/finally.hh index dee2e8d2ff27..6be219aad5e9 100644 --- a/src/libutil/finally.hh +++ b/src/libutil/finally.hh @@ -1,13 +1,11 @@ #pragma once /* A trivial class to run a function at the end of a scope. */ -template -class Finally -{ -private: +template class Finally { + private: Fn fun; -public: - Finally(Fn fun) : fun(std::move(fun)) { } + public: + Finally(Fn fun) : fun(std::move(fun)) {} ~Finally() { fun(); } }; diff --git a/src/libutil/fmt.hh b/src/libutil/fmt.hh index 7664e5c0419a..c0b53d1f00f6 100644 --- a/src/libutil/fmt.hh +++ b/src/libutil/fmt.hh @@ -4,62 +4,45 @@ #include #include "ansicolor.hh" - namespace nix { - /* Inherit some names from other namespaces for convenience. */ using boost::format; - /* A variadic template that does nothing. Useful to call a function for all variadic arguments but ignoring the result. */ -struct nop { template nop(T...) {} }; - +struct nop { + template nop(T...) {} +}; -struct FormatOrString -{ +struct FormatOrString { std::string s; - FormatOrString(std::string s) : s(std::move(s)) { }; - template - FormatOrString(const F & f) : s(f.str()) { }; - FormatOrString(const char * s) : s(s) { }; + FormatOrString(std::string s) : s(std::move(s)){}; + template FormatOrString(const F & f) : s(f.str()){}; + FormatOrString(const char * s) : s(s){}; }; - /* A helper for formatting strings. ‘fmt(format, a_0, ..., a_n)’ is equivalent to ‘boost::format(format) % a_0 % ... % ... a_n’. However, ‘fmt(s)’ is equivalent to ‘s’ (so no %-expansion takes place). */ -template -inline void formatHelper(F & f) -{ -} +template inline void formatHelper(F & f) {} template -inline void formatHelper(F & f, const T & x, const Args & ... args) +inline void formatHelper(F & f, const T & x, const Args &... args) { formatHelper(f % x, args...); } -inline std::string fmt(const std::string & s) -{ - return s; -} +inline std::string fmt(const std::string & s) { return s; } -inline std::string fmt(const char * s) -{ - return s; -} +inline std::string fmt(const char * s) { return s; } -inline std::string fmt(const FormatOrString & fs) -{ - return fs.s; -} +inline std::string fmt(const FormatOrString & fs) { return fs.s; } template -inline std::string fmt(const std::string & fs, const Args & ... args) +inline std::string fmt(const std::string & fs, const Args &... args) { boost::format f(fs); f.exceptions(boost::io::all_error_bits ^ boost::io::too_many_args_bit); @@ -71,35 +54,30 @@ inline std::string fmt(const std::string & fs, const Args & ... args) // format function for hints in errors. same as fmt, except templated values // are always in yellow. -template -struct yellowtxt -{ - yellowtxt(const T &s) : value(s) {} +template struct yellowtxt { + yellowtxt(const T & s) : value(s) {} const T & value; }; -template +template std::ostream & operator<<(std::ostream & out, const yellowtxt & y) { return out << ANSI_WARNING << y.value << ANSI_NORMAL; } -template -struct normaltxt -{ +template struct normaltxt { normaltxt(const T & s) : value(s) {} const T & value; }; -template +template std::ostream & operator<<(std::ostream & out, const normaltxt & y) { return out << ANSI_NORMAL << y.value; } -class hintformat -{ -public: +class hintformat { + public: hintformat(const std::string & format) : fmt(format) { fmt.exceptions(boost::io::all_error_bits ^ @@ -107,41 +85,32 @@ public: boost::io::too_few_args_bit); } - hintformat(const hintformat & hf) - : fmt(hf.fmt) - { } + hintformat(const hintformat & hf) : fmt(hf.fmt) {} - hintformat(format && fmt) - : fmt(std::move(fmt)) - { } + hintformat(format && fmt) : fmt(std::move(fmt)) {} - template - hintformat & operator%(const T & value) + template hintformat & operator%(const T & value) { fmt % yellowtxt(value); return *this; } - template - hintformat & operator%(const normaltxt & value) + template hintformat & operator%(const normaltxt & value) { fmt % value.value; return *this; } - std::string str() const - { - return fmt.str(); - } + std::string str() const { return fmt.str(); } -private: + private: format fmt; }; std::ostream & operator<<(std::ostream & os, const hintformat & hf); template -inline hintformat hintfmt(const std::string & fs, const Args & ... args) +inline hintformat hintfmt(const std::string & fs, const Args &... args) { hintformat f(fs); formatHelper(f, args...); @@ -150,7 +119,8 @@ inline hintformat hintfmt(const std::string & fs, const Args & ... args) inline hintformat hintfmt(std::string plain_string) { - // we won't be receiving any args in this case, so just print the original string + // we won't be receiving any args in this case, so just print the original + // string return hintfmt("%s", normaltxt(plain_string)); } diff --git a/src/libutil/git.cc b/src/libutil/git.cc index f35c2fdb75cf..7b55a2d300de 100644 --- a/src/libutil/git.cc +++ b/src/libutil/git.cc @@ -12,13 +12,13 @@ std::optional parseLsRemoteLine(std::string_view line) if (!std::regex_match(line.cbegin(), line.cend(), match, line_regex)) return std::nullopt; - return LsRemoteRefLine { - .kind = match[1].length() == 0 - ? LsRemoteRefLine::Kind::Object - : LsRemoteRefLine::Kind::Symbolic, + return LsRemoteRefLine{ + .kind = match[1].length() == 0 ? LsRemoteRefLine::Kind::Object + : LsRemoteRefLine::Kind::Symbolic, .target = match[2], - .reference = match[3].length() == 0 ? std::nullopt : std::optional{ match[3] } - }; + .reference = match[3].length() == 0 + ? std::nullopt + : std::optional{match[3]}}; } } diff --git a/src/libutil/git.hh b/src/libutil/git.hh index cb13ef0e5bb1..842c08728fe3 100644 --- a/src/libutil/git.hh +++ b/src/libutil/git.hh @@ -24,10 +24,7 @@ namespace git { // // where {target} is a commit id and {reference} is mandatory struct LsRemoteRefLine { - enum struct Kind { - Symbolic, - Object - }; + enum struct Kind { Symbolic, Object }; Kind kind; std::string target; std::optional reference; diff --git a/src/libutil/hash.cc b/src/libutil/hash.cc index d2fd0c15a1e5..069ce304f2e9 100644 --- a/src/libutil/hash.cc +++ b/src/libutil/hash.cc @@ -16,20 +16,22 @@ namespace nix { - -static size_t regularHashSize(HashType type) { +static size_t regularHashSize(HashType type) +{ switch (type) { - case htMD5: return md5HashSize; - case htSHA1: return sha1HashSize; - case htSHA256: return sha256HashSize; - case htSHA512: return sha512HashSize; + case htMD5: + return md5HashSize; + case htSHA1: + return sha1HashSize; + case htSHA256: + return sha256HashSize; + case htSHA512: + return sha512HashSize; } abort(); } - -std::set hashTypes = { "md5", "sha1", "sha256", "sha512" }; - +std::set hashTypes = {"md5", "sha1", "sha256", "sha512"}; Hash::Hash(HashType type) : type(type) { @@ -38,37 +40,35 @@ Hash::Hash(HashType type) : type(type) memset(hash, 0, maxHashSize); } - -bool Hash::operator == (const Hash & h2) const +bool Hash::operator==(const Hash & h2) const { - if (hashSize != h2.hashSize) return false; + if (hashSize != h2.hashSize) + return false; for (unsigned int i = 0; i < hashSize; i++) - if (hash[i] != h2.hash[i]) return false; + if (hash[i] != h2.hash[i]) + return false; return true; } +bool Hash::operator!=(const Hash & h2) const { return !(*this == h2); } -bool Hash::operator != (const Hash & h2) const -{ - return !(*this == h2); -} - - -bool Hash::operator < (const Hash & h) const +bool Hash::operator<(const Hash & h) const { - if (hashSize < h.hashSize) return true; - if (hashSize > h.hashSize) return false; + if (hashSize < h.hashSize) + return true; + if (hashSize > h.hashSize) + return false; for (unsigned int i = 0; i < hashSize; i++) { - if (hash[i] < h.hash[i]) return true; - if (hash[i] > h.hash[i]) return false; + if (hash[i] < h.hash[i]) + return true; + if (hash[i] > h.hash[i]) + return false; } return false; } - const std::string base16Chars = "0123456789abcdef"; - static std::string printHash16(const Hash & hash) { char buf[hash.hashSize * 2]; @@ -79,11 +79,9 @@ static std::string printHash16(const Hash & hash) return std::string(buf, hash.hashSize * 2); } - // omitted: E O U T const std::string base32Chars = "0123456789abcdfghijklmnpqrsvwxyz"; - static std::string printHash32(const Hash & hash) { assert(hash.hashSize); @@ -98,22 +96,20 @@ static std::string printHash32(const Hash & hash) unsigned int i = b / 8; unsigned int j = b % 8; unsigned char c = - (hash.hash[i] >> j) - | (i >= hash.hashSize - 1 ? 0 : hash.hash[i + 1] << (8 - j)); + (hash.hash[i] >> j) | + (i >= hash.hashSize - 1 ? 0 : hash.hash[i + 1] << (8 - j)); s.push_back(base32Chars[c & 0x1f]); } return s; } - std::string printHash16or32(const Hash & hash) { assert(hash.type); return hash.to_string(hash.type == htMD5 ? Base16 : Base32, false); } - std::string Hash::to_string(Base base, bool includeType) const { std::string s; @@ -138,7 +134,8 @@ std::string Hash::to_string(Base base, bool includeType) const Hash Hash::dummy(htSHA256); -Hash Hash::parseSRI(std::string_view original) { +Hash Hash::parseSRI(std::string_view original) +{ auto rest = original; // Parse the has type before the separater, if there was one. @@ -151,7 +148,8 @@ Hash Hash::parseSRI(std::string_view original) { } // Mutates the string to eliminate the prefixes when found -static std::pair, bool> getParsedTypeAndSRI(std::string_view & rest) +static std::pair, bool> +getParsedTypeAndSRI(std::string_view & rest) { bool isSRI = false; @@ -193,9 +191,12 @@ Hash Hash::parseAny(std::string_view original, std::optional optType) // Either the string or user must provide the type, if they both do they // must agree. if (!optParsedType && !optType) - throw BadHash("hash '%s' does not include a type, nor is the type otherwise known from context", rest); + throw BadHash("hash '%s' does not include a type, nor is the type " + "otherwise known from context", + rest); else if (optParsedType && optType && *optParsedType != *optType) - throw BadHash("hash '%s' should have type '%s'", original, printHashType(*optType)); + throw BadHash("hash '%s' should have type '%s'", original, + printHashType(*optType)); HashType hashType = optParsedType ? *optParsedType : *optType; return Hash(rest, hashType, isSRI); @@ -206,22 +207,23 @@ Hash Hash::parseNonSRIUnprefixed(std::string_view s, HashType type) return Hash(s, type, false); } -Hash::Hash(std::string_view rest, HashType type, bool isSRI) - : Hash(type) +Hash::Hash(std::string_view rest, HashType type, bool isSRI) : Hash(type) { if (!isSRI && rest.size() == base16Len()) { auto parseHexDigit = [&](char c) { - if (c >= '0' && c <= '9') return c - '0'; - if (c >= 'A' && c <= 'F') return c - 'A' + 10; - if (c >= 'a' && c <= 'f') return c - 'a' + 10; + if (c >= '0' && c <= '9') + return c - '0'; + if (c >= 'A' && c <= 'F') + return c - 'A' + 10; + if (c >= 'a' && c <= 'f') + return c - 'a' + 10; throw BadHash("invalid base-16 hash '%s'", rest); }; for (unsigned int i = 0; i < hashSize; i++) { - hash[i] = - parseHexDigit(rest[i * 2]) << 4 - | parseHexDigit(rest[i * 2 + 1]); + hash[i] = parseHexDigit(rest[i * 2]) << 4 | + parseHexDigit(rest[i * 2 + 1]); } } @@ -231,7 +233,8 @@ Hash::Hash(std::string_view rest, HashType type, bool isSRI) char c = rest[rest.size() - n - 1]; unsigned char digit; for (digit = 0; digit < base32Chars.size(); ++digit) /* !!! slow */ - if (base32Chars[digit] == c) break; + if (base32Chars[digit] == c) + break; if (digit >= 32) throw BadHash("invalid base-32 hash '%s'", rest); unsigned int b = n * 5; @@ -251,13 +254,15 @@ Hash::Hash(std::string_view rest, HashType type, bool isSRI) else if (isSRI || rest.size() == base64Len()) { auto d = base64Decode(rest); if (d.size() != hashSize) - throw BadHash("invalid %s hash '%s'", isSRI ? "SRI" : "base-64", rest); + throw BadHash("invalid %s hash '%s'", isSRI ? "SRI" : "base-64", + rest); assert(hashSize); memcpy(hash, d.data(), hashSize); } else - throw BadHash("hash '%s' has wrong length for hash type '%s'", rest, printHashType(this->type)); + throw BadHash("hash '%s' has wrong length for hash type '%s'", rest, + printHashType(this->type)); } Hash newHashAllowEmpty(std::string_view hashStr, std::optional ht) @@ -272,44 +277,49 @@ Hash newHashAllowEmpty(std::string_view hashStr, std::optional ht) return Hash::parseAny(hashStr, ht); } - -union Ctx -{ +union Ctx { MD5_CTX md5; SHA_CTX sha1; SHA256_CTX sha256; SHA512_CTX sha512; }; - static void start(HashType ht, Ctx & ctx) { - if (ht == htMD5) MD5_Init(&ctx.md5); - else if (ht == htSHA1) SHA1_Init(&ctx.sha1); - else if (ht == htSHA256) SHA256_Init(&ctx.sha256); - else if (ht == htSHA512) SHA512_Init(&ctx.sha512); + if (ht == htMD5) + MD5_Init(&ctx.md5); + else if (ht == htSHA1) + SHA1_Init(&ctx.sha1); + else if (ht == htSHA256) + SHA256_Init(&ctx.sha256); + else if (ht == htSHA512) + SHA512_Init(&ctx.sha512); } - -static void update(HashType ht, Ctx & ctx, - std::string_view data) +static void update(HashType ht, Ctx & ctx, std::string_view data) { - if (ht == htMD5) MD5_Update(&ctx.md5, data.data(), data.size()); - else if (ht == htSHA1) SHA1_Update(&ctx.sha1, data.data(), data.size()); - else if (ht == htSHA256) SHA256_Update(&ctx.sha256, data.data(), data.size()); - else if (ht == htSHA512) SHA512_Update(&ctx.sha512, data.data(), data.size()); + if (ht == htMD5) + MD5_Update(&ctx.md5, data.data(), data.size()); + else if (ht == htSHA1) + SHA1_Update(&ctx.sha1, data.data(), data.size()); + else if (ht == htSHA256) + SHA256_Update(&ctx.sha256, data.data(), data.size()); + else if (ht == htSHA512) + SHA512_Update(&ctx.sha512, data.data(), data.size()); } - static void finish(HashType ht, Ctx & ctx, unsigned char * hash) { - if (ht == htMD5) MD5_Final(hash, &ctx.md5); - else if (ht == htSHA1) SHA1_Final(hash, &ctx.sha1); - else if (ht == htSHA256) SHA256_Final(hash, &ctx.sha256); - else if (ht == htSHA512) SHA512_Final(hash, &ctx.sha512); + if (ht == htMD5) + MD5_Final(hash, &ctx.md5); + else if (ht == htSHA1) + SHA1_Final(hash, &ctx.sha1); + else if (ht == htSHA256) + SHA256_Final(hash, &ctx.sha256); + else if (ht == htSHA512) + SHA512_Final(hash, &ctx.sha512); } - Hash hashString(HashType ht, std::string_view s) { Ctx ctx; @@ -320,7 +330,6 @@ Hash hashString(HashType ht, std::string_view s) return hash; } - Hash hashFile(HashType ht, const Path & path) { HashSink sink(ht); @@ -328,7 +337,6 @@ Hash hashFile(HashType ht, const Path & path) return sink.finish().first; } - HashSink::HashSink(HashType ht) : ht(ht) { ctx = new Ctx; @@ -365,16 +373,13 @@ HashResult HashSink::currentHash() return HashResult(hash, bytes); } - -HashResult hashPath( - HashType ht, const Path & path, PathFilter & filter) +HashResult hashPath(HashType ht, const Path & path, PathFilter & filter) { HashSink sink(ht); dumpPath(path, sink, filter); return sink.finish(); } - Hash compressHash(const Hash & hash, unsigned int newSize) { Hash h(hash.type); @@ -384,14 +389,18 @@ Hash compressHash(const Hash & hash, unsigned int newSize) return h; } - std::optional parseHashTypeOpt(std::string_view s) { - if (s == "md5") return htMD5; - else if (s == "sha1") return htSHA1; - else if (s == "sha256") return htSHA256; - else if (s == "sha512") return htSHA512; - else return std::optional {}; + if (s == "md5") + return htMD5; + else if (s == "sha1") + return htSHA1; + else if (s == "sha256") + return htSHA256; + else if (s == "sha512") + return htSHA512; + else + return std::optional{}; } HashType parseHashType(std::string_view s) @@ -406,10 +415,14 @@ HashType parseHashType(std::string_view s) std::string printHashType(HashType ht) { switch (ht) { - case htMD5: return "md5"; - case htSHA1: return "sha1"; - case htSHA256: return "sha256"; - case htSHA512: return "sha512"; + case htMD5: + return "md5"; + case htSHA1: + return "sha1"; + case htSHA256: + return "sha256"; + case htSHA512: + return "sha512"; default: // illegal hash type enum value internally, as opposed to external input // which should be validated with nice error message. diff --git a/src/libutil/hash.hh b/src/libutil/hash.hh index 00f70a572957..4fa3176c4485 100644 --- a/src/libutil/hash.hh +++ b/src/libutil/hash.hh @@ -3,16 +3,12 @@ #include "types.hh" #include "serialise.hh" - namespace nix { - MakeError(BadHash, Error); - enum HashType : char { htMD5 = 42, htSHA1, htSHA256, htSHA512 }; - const int md5HashSize = 16; const int sha1HashSize = 20; const int sha256HashSize = 32; @@ -24,9 +20,7 @@ extern const std::string base32Chars; enum Base : int { Base64, Base32, Base16, SRI }; - -struct Hash -{ +struct Hash { constexpr static size_t maxHashSize = 64; size_t hashSize = 0; uint8_t hash[maxHashSize] = {}; @@ -53,48 +47,36 @@ struct Hash static Hash parseSRI(std::string_view original); -private: + private: /* The type must be provided, the string view must not include prefix. `isSRI` helps disambigate the various base-* encodings. */ Hash(std::string_view s, HashType type, bool isSRI); -public: + public: /* Check whether two hash are equal. */ - bool operator == (const Hash & h2) const; + bool operator==(const Hash & h2) const; /* Check whether two hash are not equal. */ - bool operator != (const Hash & h2) const; + bool operator!=(const Hash & h2) const; /* For sorting. */ - bool operator < (const Hash & h) const; + bool operator<(const Hash & h) const; /* Returns the length of a base-16 representation of this hash. */ - size_t base16Len() const - { - return hashSize * 2; - } + size_t base16Len() const { return hashSize * 2; } /* Returns the length of a base-32 representation of this hash. */ - size_t base32Len() const - { - return (hashSize * 8 - 1) / 5 + 1; - } + size_t base32Len() const { return (hashSize * 8 - 1) / 5 + 1; } /* Returns the length of a base-64 representation of this hash. */ - size_t base64Len() const - { - return ((4 * hashSize / 3) + 3) & ~3; - } + size_t base64Len() const { return ((4 * hashSize / 3) + 3) & ~3; } /* Return a string representation of the hash, in base-16, base-32 or base-64. By default, this is prefixed by the hash type (e.g. "sha256:"). */ std::string to_string(Base base, bool includeType) const; - std::string gitRev() const - { - return to_string(Base16, false); - } + std::string gitRev() const { return to_string(Base16, false); } std::string gitShortRev() const { @@ -120,7 +102,7 @@ Hash hashFile(HashType ht, const Path & path); (essentially) hashString(ht, dumpPath(path)). */ typedef std::pair HashResult; HashResult hashPath(HashType ht, const Path & path, - PathFilter & filter = defaultPathFilter); + PathFilter & filter = defaultPathFilter); /* Compress a hash to the specified number of bytes by cyclically XORing bytes together. */ @@ -135,22 +117,19 @@ std::optional parseHashTypeOpt(std::string_view s); /* And the reverse. */ std::string printHashType(HashType ht); - union Ctx; -struct AbstractHashSink : virtual Sink -{ +struct AbstractHashSink : virtual Sink { virtual HashResult finish() = 0; }; -class HashSink : public BufferedSink, public AbstractHashSink -{ -private: +class HashSink : public BufferedSink, public AbstractHashSink { + private: HashType ht; Ctx * ctx; uint64_t bytes; -public: + public: HashSink(HashType ht); HashSink(const HashSink & h); ~HashSink(); @@ -159,5 +138,4 @@ public: HashResult currentHash(); }; - } diff --git a/src/libutil/hilite.cc b/src/libutil/hilite.cc index e5088230d7c3..812177a1cf71 100644 --- a/src/libutil/hilite.cc +++ b/src/libutil/hilite.cc @@ -2,19 +2,17 @@ namespace nix { -std::string hiliteMatches( - std::string_view s, - std::vector matches, - std::string_view prefix, - std::string_view postfix) +std::string hiliteMatches(std::string_view s, std::vector matches, + std::string_view prefix, std::string_view postfix) { // Avoid extra work on zero matches if (matches.size() == 0) return std::string(s); - std::sort(matches.begin(), matches.end(), [](const auto & a, const auto & b) { - return a.position() < b.position(); - }); + std::sort(matches.begin(), matches.end(), + [](const auto & a, const auto & b) { + return a.position() < b.position(); + }); std::string out; ssize_t last_end = 0; diff --git a/src/libutil/hilite.hh b/src/libutil/hilite.hh index f8bdbfc5515c..66c4a50982a5 100644 --- a/src/libutil/hilite.hh +++ b/src/libutil/hilite.hh @@ -11,10 +11,7 @@ namespace nix { If some matches overlap, then their union will be wrapped rather than the individual matches. */ -std::string hiliteMatches( - std::string_view s, - std::vector matches, - std::string_view prefix, - std::string_view postfix); +std::string hiliteMatches(std::string_view s, std::vector matches, + std::string_view prefix, std::string_view postfix); } diff --git a/src/libutil/json-utils.hh b/src/libutil/json-utils.hh index b8a03122743a..470587b50c7d 100644 --- a/src/libutil/json-utils.hh +++ b/src/libutil/json-utils.hh @@ -7,14 +7,16 @@ namespace nix { const nlohmann::json * get(const nlohmann::json & map, const std::string & key) { auto i = map.find(key); - if (i == map.end()) return nullptr; + if (i == map.end()) + return nullptr; return &*i; } nlohmann::json * get(nlohmann::json & map, const std::string & key) { auto i = map.find(key); - if (i == map.end()) return nullptr; + if (i == map.end()) + return nullptr; return &*i; } diff --git a/src/libutil/json.cc b/src/libutil/json.cc index b0a5d7e75821..4d8159b46c56 100644 --- a/src/libutil/json.cc +++ b/src/libutil/json.cc @@ -16,27 +16,34 @@ void toJSON(std::ostream & str, const char * start, const char * end) str.write(buf, bufPos); bufPos = 0; }; - const auto put = [&] (char c) { - buf[bufPos++] = c; - }; + const auto put = [&](char c) { buf[bufPos++] = c; }; put('"'); for (auto i = start; i != end; i++) { - if (bufPos >= BUF_SIZE) flush(); - if (*i == '\"' || *i == '\\') { put('\\'); put(*i); } - else if (*i == '\n') { put('\\'); put('n'); } - else if (*i == '\r') { put('\\'); put('r'); } - else if (*i == '\t') { put('\\'); put('t'); } - else if (*i >= 0 && *i < 32) { + if (bufPos >= BUF_SIZE) + flush(); + if (*i == '\"' || *i == '\\') { + put('\\'); + put(*i); + } else if (*i == '\n') { + put('\\'); + put('n'); + } else if (*i == '\r') { + put('\\'); + put('r'); + } else if (*i == '\t') { + put('\\'); + put('t'); + } else if (*i >= 0 && *i < 32) { const char hex[17] = "0123456789abcdef"; put('\\'); put('u'); put(hex[(uint16_t(*i) >> 12) & 0xf]); - put(hex[(uint16_t(*i) >> 8) & 0xf]); - put(hex[(uint16_t(*i) >> 4) & 0xf]); - put(hex[(uint16_t(*i) >> 0) & 0xf]); - } - else put(*i); + put(hex[(uint16_t(*i) >> 8) & 0xf]); + put(hex[(uint16_t(*i) >> 4) & 0xf]); + put(hex[(uint16_t(*i) >> 0) & 0xf]); + } else + put(*i); } put('"'); flush(); @@ -44,17 +51,38 @@ void toJSON(std::ostream & str, const char * start, const char * end) void toJSON(std::ostream & str, const char * s) { - if (!s) str << "null"; else toJSON(str, s, s + strlen(s)); + if (!s) + str << "null"; + else + toJSON(str, s, s + strlen(s)); } template<> void toJSON(std::ostream & str, const int & n) { str << n; } -template<> void toJSON(std::ostream & str, const unsigned int & n) { str << n; } +template<> void toJSON(std::ostream & str, const unsigned int & n) +{ + str << n; +} template<> void toJSON(std::ostream & str, const long & n) { str << n; } -template<> void toJSON(std::ostream & str, const unsigned long & n) { str << n; } -template<> void toJSON(std::ostream & str, const long long & n) { str << n; } -template<> void toJSON(std::ostream & str, const unsigned long long & n) { str << n; } +template<> +void toJSON(std::ostream & str, const unsigned long & n) +{ + str << n; +} +template<> void toJSON(std::ostream & str, const long long & n) +{ + str << n; +} +template<> +void toJSON(std::ostream & str, + const unsigned long long & n) +{ + str << n; +} template<> void toJSON(std::ostream & str, const float & n) { str << n; } -template<> void toJSON(std::ostream & str, const double & n) { str << n; } +template<> void toJSON(std::ostream & str, const double & n) +{ + str << n; +} template<> void toJSON(std::ostream & str, const std::string & s) { @@ -66,7 +94,8 @@ template<> void toJSON(std::ostream & str, const bool & b) str << (b ? "true" : "false"); } -template<> void toJSON(std::ostream & str, const std::nullptr_t & b) +template<> +void toJSON(std::ostream & str, const std::nullptr_t & b) { str << "null"; } @@ -77,18 +106,15 @@ JSONWriter::JSONWriter(std::ostream & str, bool indent) state->stack++; } -JSONWriter::JSONWriter(JSONState * state) - : state(state) -{ - state->stack++; -} +JSONWriter::JSONWriter(JSONState * state) : state(state) { state->stack++; } JSONWriter::~JSONWriter() { if (state) { assertActive(); state->stack--; - if (state->stack == 0) delete state; + if (state->stack == 0) + delete state; } } @@ -100,7 +126,8 @@ void JSONWriter::comma() } else { state->str << ','; } - if (state->indent) indent(); + if (state->indent) + indent(); } void JSONWriter::indent() @@ -117,7 +144,8 @@ void JSONList::open() JSONList::~JSONList() { state->depth--; - if (state->indent && !first) indent(); + if (state->indent && !first) + indent(); state->str << "]"; } @@ -149,7 +177,8 @@ JSONObject::~JSONObject() { if (state) { state->depth--; - if (state->indent && !first) indent(); + if (state->indent && !first) + indent(); state->str << "}"; } } @@ -159,7 +188,8 @@ void JSONObject::attr(const std::string & s) comma(); toJSON(state->str, s); state->str << ':'; - if (state->indent) state->str << ' '; + if (state->indent) + state->str << ' '; } JSONList JSONObject::list(const std::string & name) diff --git a/src/libutil/json.hh b/src/libutil/json.hh index 83213ca6664c..0cc116d65ac4 100644 --- a/src/libutil/json.hh +++ b/src/libutil/json.hh @@ -9,24 +9,17 @@ namespace nix { void toJSON(std::ostream & str, const char * start, const char * end); void toJSON(std::ostream & str, const char * s); -template -void toJSON(std::ostream & str, const T & n); +template void toJSON(std::ostream & str, const T & n); -class JSONWriter -{ -protected: - - struct JSONState - { +class JSONWriter { + protected: + struct JSONState { std::ostream & str; bool indent; size_t depth = 0; size_t stack = 0; - JSONState(std::ostream & str, bool indent) : str(str), indent(indent) { } - ~JSONState() - { - assert(stack == 0); - } + JSONState(std::ostream & str, bool indent) : str(str), indent(indent) {} + ~JSONState() { assert(stack == 0); } }; JSONState * state; @@ -39,10 +32,7 @@ protected: ~JSONWriter(); - void assertActive() - { - assert(state->stack != 0); - } + void assertActive() { assert(state->stack != 0); } void comma(); @@ -52,33 +42,24 @@ protected: class JSONObject; class JSONPlaceholder; -class JSONList : JSONWriter -{ -private: - +class JSONList : JSONWriter { + private: friend class JSONObject; friend class JSONPlaceholder; void open(); - JSONList(JSONState * state) - : JSONWriter(state) - { - open(); - } - -public: + JSONList(JSONState * state) : JSONWriter(state) { open(); } - JSONList(std::ostream & str, bool indent = false) - : JSONWriter(str, indent) + public: + JSONList(std::ostream & str, bool indent = false) : JSONWriter(str, indent) { open(); } ~JSONList(); - template - JSONList & elem(const T & v) + template JSONList & elem(const T & v) { comma(); toJSON(state->str, v); @@ -92,25 +73,18 @@ public: JSONPlaceholder placeholder(); }; -class JSONObject : JSONWriter -{ -private: - +class JSONObject : JSONWriter { + private: friend class JSONList; friend class JSONPlaceholder; void open(); - JSONObject(JSONState * state) - : JSONWriter(state) - { - open(); - } + JSONObject(JSONState * state) : JSONWriter(state) { open(); } void attr(const std::string & s); -public: - + public: JSONObject(std::ostream & str, bool indent = false) : JSONWriter(str, indent) { @@ -119,11 +93,7 @@ public: JSONObject(const JSONObject & obj) = delete; - JSONObject(JSONObject && obj) - : JSONWriter(obj.state) - { - obj.state = 0; - } + JSONObject(JSONObject && obj) : JSONWriter(obj.state) { obj.state = 0; } ~JSONObject(); @@ -142,18 +112,13 @@ public: JSONPlaceholder placeholder(const std::string & name); }; -class JSONPlaceholder : JSONWriter -{ - -private: +class JSONPlaceholder : JSONWriter { + private: friend class JSONList; friend class JSONObject; - JSONPlaceholder(JSONState * state) - : JSONWriter(state) - { - } + JSONPlaceholder(JSONState * state) : JSONWriter(state) {} void assertValid() { @@ -161,8 +126,7 @@ private: assert(first); } -public: - + public: JSONPlaceholder(std::ostream & str, bool indent = false) : JSONWriter(str, indent) { @@ -170,8 +134,7 @@ public: ~JSONPlaceholder(); - template - void write(const T & v) + template void write(const T & v) { assertValid(); first = false; diff --git a/src/libutil/logging.cc b/src/libutil/logging.cc index cb2b15b41aad..17b3b531051e 100644 --- a/src/libutil/logging.cc +++ b/src/libutil/logging.cc @@ -14,14 +14,8 @@ static GlobalConfig::Register rLoggerSettings(&loggerSettings); static thread_local ActivityId curActivity = 0; -ActivityId getCurActivity() -{ - return curActivity; -} -void setCurActivity(const ActivityId activityId) -{ - curActivity = activityId; -} +ActivityId getCurActivity() { return curActivity; } +void setCurActivity(const ActivityId activityId) { curActivity = activityId; } Logger * logger = makeSimpleLogger(true); @@ -30,43 +24,46 @@ void Logger::warn(const std::string & msg) log(lvlWarn, ANSI_WARNING "warning:" ANSI_NORMAL " " + msg); } -void Logger::writeToStdout(std::string_view s) -{ - std::cout << s << "\n"; -} - -class SimpleLogger : public Logger -{ -public: +void Logger::writeToStdout(std::string_view s) { std::cout << s << "\n"; } +class SimpleLogger : public Logger { + public: bool systemd, tty; bool printBuildLogs; - SimpleLogger(bool printBuildLogs) - : printBuildLogs(printBuildLogs) + SimpleLogger(bool printBuildLogs) : printBuildLogs(printBuildLogs) { systemd = getEnv("IN_SYSTEMD") == "1"; tty = shouldANSI(); } - bool isVerbose() override { - return printBuildLogs; - } + bool isVerbose() override { return printBuildLogs; } void log(Verbosity lvl, const FormatOrString & fs) override { - if (lvl > verbosity) return; + if (lvl > verbosity) + return; std::string prefix; if (systemd) { char c; switch (lvl) { - case lvlError: c = '3'; break; - case lvlWarn: c = '4'; break; - case lvlInfo: c = '5'; break; - case lvlTalkative: case lvlChatty: c = '6'; break; - default: c = '7'; + case lvlError: + c = '3'; + break; + case lvlWarn: + c = '4'; + break; + case lvlInfo: + c = '5'; + break; + case lvlTalkative: + case lvlChatty: + c = '6'; + break; + default: + c = '7'; } prefix = std::string("<") + c + ">"; } @@ -83,8 +80,8 @@ class SimpleLogger : public Logger } void startActivity(ActivityId act, Verbosity lvl, ActivityType type, - const std::string & s, const Fields & fields, ActivityId parent) - override + const std::string & s, const Fields & fields, + ActivityId parent) override { if (lvl <= verbosity && !s.empty()) log(lvl, s + "..."); @@ -95,8 +92,7 @@ class SimpleLogger : public Logger if (type == resBuildLogLine && printBuildLogs) { auto lastLine = fields[0].s; printError(lastLine); - } - else if (type == resPostBuildLogLine && printBuildLogs) { + } else if (type == resPostBuildLogLine && printBuildLogs) { auto lastLine = fields[0].s; printError("post-build-hook: " + lastLine); } @@ -133,7 +129,8 @@ Logger * makeSimpleLogger(bool printBuildLogs) std::atomic nextId{(uint64_t) getpid() << 32}; Activity::Activity(Logger & logger, Verbosity lvl, ActivityType type, - const std::string & s, const Logger::Fields & fields, ActivityId parent) + const std::string & s, const Logger::Fields & fields, + ActivityId parent) : logger(logger), id(nextId++) { logger.startActivity(id, lvl, type, s, fields, parent); @@ -142,15 +139,14 @@ Activity::Activity(Logger & logger, Verbosity lvl, ActivityType type, struct JSONLogger : Logger { Logger & prevLogger; - JSONLogger(Logger & prevLogger) : prevLogger(prevLogger) { } + JSONLogger(Logger & prevLogger) : prevLogger(prevLogger) {} - bool isVerbose() override { - return true; - } + bool isVerbose() override { return true; } void addFields(nlohmann::json & json, const Fields & fields) { - if (fields.empty()) return; + if (fields.empty()) + return; auto & arr = json["fields"] = nlohmann::json::array(); for (auto & f : fields) if (f.type == Logger::Field::tInt) @@ -163,7 +159,10 @@ struct JSONLogger : Logger { void write(const nlohmann::json & json) { - prevLogger.log(lvlError, "@nix " + json.dump(-1, ' ', false, nlohmann::json::error_handler_t::replace)); + prevLogger.log(lvlError, + "@nix " + + json.dump(-1, ' ', false, + nlohmann::json::error_handler_t::replace)); } void log(Verbosity lvl, const FormatOrString & fs) override @@ -198,7 +197,8 @@ struct JSONLogger : Logger { if (loggerSettings.showTrace.get() && !ei.traces.empty()) { nlohmann::json traces = nlohmann::json::array(); - for (auto iter = ei.traces.rbegin(); iter != ei.traces.rend(); ++iter) { + for (auto iter = ei.traces.rbegin(); iter != ei.traces.rend(); + ++iter) { nlohmann::json stackFrame; stackFrame["raw_msg"] = iter->hint.str(); if (iter->pos.has_value() && (*iter->pos)) { @@ -216,7 +216,8 @@ struct JSONLogger : Logger { } void startActivity(ActivityId act, Verbosity lvl, ActivityType type, - const std::string & s, const Fields & fields, ActivityId parent) override + const std::string & s, const Fields & fields, + ActivityId parent) override { nlohmann::json json; json["action"] = "start"; @@ -261,14 +262,16 @@ static Logger::Fields getFields(nlohmann::json & json) fields.emplace_back(Logger::Field(f.get())); else if (f.type() == nlohmann::json::value_t::string) fields.emplace_back(Logger::Field(f.get())); - else throw Error("unsupported JSON type %d", (int) f.type()); + else + throw Error("unsupported JSON type %d", (int) f.type()); } return fields; } std::optional parseJSONMessage(const std::string & msg) { - if (!hasPrefix(msg, "@nix ")) return std::nullopt; + if (!hasPrefix(msg, "@nix ")) + return std::nullopt; try { return nlohmann::json::parse(std::string(msg, 5)); } catch (std::exception & e) { @@ -277,19 +280,20 @@ std::optional parseJSONMessage(const std::string & msg) return std::nullopt; } -bool handleJSONLogMessage(nlohmann::json & json, - const Activity & act, std::map & activities, - bool trusted) +bool handleJSONLogMessage(nlohmann::json & json, const Activity & act, + std::map & activities, + bool trusted) { std::string action = json["action"]; if (action == "start") { auto type = (ActivityType) json["type"]; if (trusted || type == actFileTransfer) - activities.emplace(std::piecewise_construct, - std::forward_as_tuple(json["id"]), + activities.emplace( + std::piecewise_construct, std::forward_as_tuple(json["id"]), std::forward_as_tuple(*logger, (Verbosity) json["level"], type, - json["text"], getFields(json["fields"]), act.id)); + json["text"], getFields(json["fields"]), + act.id)); } else if (action == "stop") @@ -298,7 +302,8 @@ bool handleJSONLogMessage(nlohmann::json & json, else if (action == "result") { auto i = activities.find((ActivityId) json["id"]); if (i != activities.end()) - i->second.result((ResultType) json["type"], getFields(json["fields"])); + i->second.result((ResultType) json["type"], + getFields(json["fields"])); } else if (action == "setPhase") { @@ -314,11 +319,13 @@ bool handleJSONLogMessage(nlohmann::json & json, return true; } -bool handleJSONLogMessage(const std::string & msg, - const Activity & act, std::map & activities, bool trusted) +bool handleJSONLogMessage(const std::string & msg, const Activity & act, + std::map & activities, + bool trusted) { auto json = parseJSONMessage(msg); - if (!json) return false; + if (!json) + return false; return handleJSONLogMessage(*json, act, activities, trusted); } diff --git a/src/libutil/logging.hh b/src/libutil/logging.hh index 6f81b92de07d..aac8938b1844 100644 --- a/src/libutil/logging.hh +++ b/src/libutil/logging.hh @@ -37,11 +37,9 @@ typedef enum { typedef uint64_t ActivityId; -struct LoggerSettings : Config -{ - Setting showTrace{ - this, false, "show-trace", - R"( +struct LoggerSettings : Config { + Setting showTrace{this, false, "show-trace", + R"( Whether Nix should print out a stack trace in case of Nix expression evaluation errors. )"}; @@ -49,40 +47,34 @@ struct LoggerSettings : Config extern LoggerSettings loggerSettings; -class Logger -{ +class Logger { friend struct Activity; -public: - - struct Field - { + public: + struct Field { // FIXME: use std::variant. enum { tInt = 0, tString = 1 } type; uint64_t i = 0; std::string s; - Field(const std::string & s) : type(tString), s(s) { } - Field(const char * s) : type(tString), s(s) { } - Field(const uint64_t & i) : type(tInt), i(i) { } + Field(const std::string & s) : type(tString), s(s) {} + Field(const char * s) : type(tString), s(s) {} + Field(const uint64_t & i) : type(tInt), i(i) {} }; typedef std::vector Fields; - virtual ~Logger() { } + virtual ~Logger() {} - virtual void stop() { }; + virtual void stop(){}; // Whether the logger prints the whole build log virtual bool isVerbose() { return false; } virtual void log(Verbosity lvl, const FormatOrString & fs) = 0; - void log(const FormatOrString & fs) - { - log(lvlInfo, fs); - } + void log(const FormatOrString & fs) { log(lvlInfo, fs); } - virtual void logEI(const ErrorInfo &ei) = 0; + virtual void logEI(const ErrorInfo & ei) = 0; void logEI(Verbosity lvl, ErrorInfo ei) { @@ -93,54 +85,61 @@ public: virtual void warn(const std::string & msg); virtual void startActivity(ActivityId act, Verbosity lvl, ActivityType type, - const std::string & s, const Fields & fields, ActivityId parent) { }; + const std::string & s, const Fields & fields, + ActivityId parent){}; - virtual void stopActivity(ActivityId act) { }; + virtual void stopActivity(ActivityId act){}; - virtual void result(ActivityId act, ResultType type, const Fields & fields) { }; + virtual void result(ActivityId act, ResultType type, + const Fields & fields){}; virtual void writeToStdout(std::string_view s); template - inline void cout(const std::string & fs, const Args & ... args) + inline void cout(const std::string & fs, const Args &... args) { boost::format f(fs); formatHelper(f, args...); writeToStdout(f.str()); } - virtual std::optional ask(std::string_view s) - { return {}; } + virtual std::optional ask(std::string_view s) { return {}; } }; ActivityId getCurActivity(); void setCurActivity(const ActivityId activityId); -struct Activity -{ +struct Activity { Logger & logger; const ActivityId id; - Activity(Logger & logger, Verbosity lvl, ActivityType type, const std::string & s = "", - const Logger::Fields & fields = {}, ActivityId parent = getCurActivity()); + Activity(Logger & logger, Verbosity lvl, ActivityType type, + const std::string & s = "", const Logger::Fields & fields = {}, + ActivityId parent = getCurActivity()); Activity(Logger & logger, ActivityType type, - const Logger::Fields & fields = {}, ActivityId parent = getCurActivity()) - : Activity(logger, lvlError, type, "", fields, parent) { }; + const Logger::Fields & fields = {}, + ActivityId parent = getCurActivity()) + : Activity(logger, lvlError, type, "", fields, parent){}; Activity(const Activity & act) = delete; ~Activity(); - void progress(uint64_t done = 0, uint64_t expected = 0, uint64_t running = 0, uint64_t failed = 0) const - { result(resProgress, done, expected, running, failed); } + void progress(uint64_t done = 0, uint64_t expected = 0, + uint64_t running = 0, uint64_t failed = 0) const + { + result(resProgress, done, expected, running, failed); + } void setExpected(ActivityType type2, uint64_t expected) const - { result(resSetExpected, type2, expected); } + { + result(resSetExpected, type2, expected); + } template - void result(ResultType type, const Args & ... args) const + void result(ResultType type, const Args &... args) const { Logger::Fields fields; nop{(fields.emplace_back(Logger::Field(args)), 1)...}; @@ -155,10 +154,12 @@ struct Activity friend class Logger; }; -struct PushActivity -{ +struct PushActivity { const ActivityId prevAct; - PushActivity(ActivityId act) : prevAct(getCurActivity()) { setCurActivity(act); } + PushActivity(ActivityId act) : prevAct(getCurActivity()) + { + setCurActivity(act); + } ~PushActivity() { setCurActivity(prevAct); } }; @@ -170,25 +171,25 @@ Logger * makeJSONLogger(Logger & prevLogger); std::optional parseJSONMessage(const std::string & msg); -bool handleJSONLogMessage(nlohmann::json & json, - const Activity & act, std::map & activities, - bool trusted); +bool handleJSONLogMessage(nlohmann::json & json, const Activity & act, + std::map & activities, + bool trusted); -bool handleJSONLogMessage(const std::string & msg, - const Activity & act, std::map & activities, - bool trusted); +bool handleJSONLogMessage(const std::string & msg, const Activity & act, + std::map & activities, + bool trusted); extern Verbosity verbosity; /* suppress msgs > this */ /* Print a message with the standard ErrorInfo format. - In general, use these 'log' macros for reporting problems that may require user - intervention or that need more explanation. Use the 'print' macros for more - lightweight status messages. */ -#define logErrorInfo(level, errorInfo...) \ - do { \ - if ((level) <= nix::verbosity) { \ - logger->logEI((level), errorInfo); \ - } \ + In general, use these 'log' macros for reporting problems that may require + user intervention or that need more explanation. Use the 'print' macros for + more lightweight status messages. */ +#define logErrorInfo(level, errorInfo...) \ + do { \ + if ((level) <= nix::verbosity) { \ + logger->logEI((level), errorInfo); \ + } \ } while (0) #define logError(errorInfo...) logErrorInfo(lvlError, errorInfo) @@ -197,12 +198,12 @@ extern Verbosity verbosity; /* suppress msgs > this */ /* Print a string message if the current log level is at least the specified level. Note that this has to be implemented as a macro to ensure that the arguments are evaluated lazily. */ -#define printMsgUsing(loggerParam, level, args...) \ - do { \ - auto __lvl = level; \ - if (__lvl <= nix::verbosity) { \ - loggerParam->log(__lvl, fmt(args)); \ - } \ +#define printMsgUsing(loggerParam, level, args...) \ + do { \ + auto __lvl = level; \ + if (__lvl <= nix::verbosity) { \ + loggerParam->log(__lvl, fmt(args)); \ + } \ } while (0) #define printMsg(level, args...) printMsgUsing(logger, level, args) @@ -215,7 +216,7 @@ extern Verbosity verbosity; /* suppress msgs > this */ /* if verbosity >= lvlWarn, print a message with a yellow 'warning:' prefix. */ template -inline void warn(const std::string & fs, const Args & ... args) +inline void warn(const std::string & fs, const Args &... args) { boost::format f(fs); formatHelper(f, args...); diff --git a/src/libutil/lru-cache.hh b/src/libutil/lru-cache.hh index 6ef4a3e067d6..167695fc31ee 100644 --- a/src/libutil/lru-cache.hh +++ b/src/libutil/lru-cache.hh @@ -8,11 +8,8 @@ namespace nix { /* A simple least-recently used cache. Not thread-safe. */ -template -class LRUCache -{ -private: - +template class LRUCache { + private: size_t capacity; // Stupid wrapper to get around circular dependency between Data @@ -22,19 +19,21 @@ private: using Data = std::map>; using LRU = std::list; - struct LRUIterator { typename LRU::iterator it; }; + struct LRUIterator { + typename LRU::iterator it; + }; Data data; LRU lru; -public: - - LRUCache(size_t capacity) : capacity(capacity) { } + public: + LRUCache(size_t capacity) : capacity(capacity) {} /* Insert or upsert an item in the cache. */ void upsert(const Key & key, const Value & value) { - if (capacity == 0) return; + if (capacity == 0) + return; erase(key); @@ -57,7 +56,8 @@ public: bool erase(const Key & key) { auto i = data.find(key); - if (i == data.end()) return false; + if (i == data.end()) + return false; lru.erase(i->second.first.it); data.erase(i); return true; @@ -68,7 +68,8 @@ public: std::optional get(const Key & key) { auto i = data.find(key); - if (i == data.end()) return {}; + if (i == data.end()) + return {}; /* Move this item to the back of the LRU list. */ lru.erase(i->second.first.it); @@ -78,10 +79,7 @@ public: return i->second.second; } - size_t size() - { - return data.size(); - } + size_t size() { return data.size(); } void clear() { diff --git a/src/libutil/monitor-fd.hh b/src/libutil/monitor-fd.hh index 5ee0b88ef50f..8512ac1a104f 100644 --- a/src/libutil/monitor-fd.hh +++ b/src/libutil/monitor-fd.hh @@ -11,38 +11,38 @@ namespace nix { - -class MonitorFdHup -{ -private: +class MonitorFdHup { + private: std::thread thread; -public: + public: MonitorFdHup(int fd) { thread = std::thread([fd]() { while (true) { - /* Wait indefinitely until a POLLHUP occurs. */ - struct pollfd fds[1]; - fds[0].fd = fd; - /* This shouldn't be necessary, but macOS doesn't seem to - like a zeroed out events field. - See rdar://37537852. - */ - fds[0].events = POLLHUP; - auto count = poll(fds, 1, -1); - if (count == -1) abort(); // can't happen - /* This shouldn't happen, but can on macOS due to a bug. - See rdar://37550628. - - This may eventually need a delay or further - coordination with the main thread if spinning proves - too harmful. - */ - if (count == 0) continue; - assert(fds[0].revents & POLLHUP); - triggerInterrupt(); - break; + /* Wait indefinitely until a POLLHUP occurs. */ + struct pollfd fds[1]; + fds[0].fd = fd; + /* This shouldn't be necessary, but macOS doesn't seem to + like a zeroed out events field. + See rdar://37537852. + */ + fds[0].events = POLLHUP; + auto count = poll(fds, 1, -1); + if (count == -1) + abort(); // can't happen + /* This shouldn't happen, but can on macOS due to a bug. + See rdar://37550628. + + This may eventually need a delay or further + coordination with the main thread if spinning proves + too harmful. + */ + if (count == 0) + continue; + assert(fds[0].revents & POLLHUP); + triggerInterrupt(); + break; } }); }; @@ -54,5 +54,4 @@ public: } }; - } diff --git a/src/libutil/pool.hh b/src/libutil/pool.hh index d49067bb95dc..af824fae2759 100644 --- a/src/libutil/pool.hh +++ b/src/libutil/pool.hh @@ -28,11 +28,8 @@ namespace nix { returned to the pool when ‘conn’ goes out of scope. */ -template -class Pool -{ -public: - +template class Pool { + public: /* A function that produces new instances of R on demand. */ typedef std::function()> Factory; @@ -40,13 +37,11 @@ public: usable. Unusable instances are removed from the pool. */ typedef std::function &)> Validator; -private: - + private: Factory factory; Validator validator; - struct State - { + struct State { size_t inUse = 0; size_t max; std::vector> idle; @@ -56,13 +51,12 @@ private: std::condition_variable wakeup; -public: - - Pool(size_t max = std::numeric_limits::max(), + public: + Pool( + size_t max = std::numeric_limits::max(), const Factory & factory = []() { return make_ref(); }, const Validator & validator = [](ref r) { return true; }) - : factory(factory) - , validator(validator) + : factory(factory), validator(validator) { auto state_(state.lock()); state_->max = max; @@ -92,25 +86,25 @@ public: state_->idle.clear(); } - class Handle - { - private: + class Handle { + private: Pool & pool; std::shared_ptr r; bool bad = false; friend Pool; - Handle(Pool & pool, std::shared_ptr r) : pool(pool), r(r) { } + Handle(Pool & pool, std::shared_ptr r) : pool(pool), r(r) {} - public: + public: Handle(Handle && h) : pool(h.pool), r(h.r) { h.r.reset(); } Handle(const Handle & l) = delete; ~Handle() { - if (!r) return; + if (!r) + return; { auto state_(pool.state.lock()); if (!bad) @@ -121,8 +115,8 @@ public: pool.wakeup.notify_one(); } - R * operator -> () { return &*r; } - R & operator * () { return *r; } + R * operator->() { return &*r; } + R & operator*() { return *r; } void markBad() { bad = true; } }; @@ -168,10 +162,7 @@ public: return state_->idle.size() + state_->inUse; } - size_t capacity() - { - return state.lock()->max; - } + size_t capacity() { return state.lock()->max; } void flushBad() { diff --git a/src/libutil/ref.hh b/src/libutil/ref.hh index bf26321db4ca..0f56f47caa74 100644 --- a/src/libutil/ref.hh +++ b/src/libutil/ref.hh @@ -8,92 +8,58 @@ namespace nix { /* A simple non-nullable reference-counted pointer. Actually a wrapper around std::shared_ptr that prevents null constructions. */ -template -class ref -{ -private: - +template class ref { + private: std::shared_ptr p; -public: - - ref(const ref & r) - : p(r.p) - { } + public: + ref(const ref & r) : p(r.p) {} - explicit ref(const std::shared_ptr & p) - : p(p) + explicit ref(const std::shared_ptr & p) : p(p) { if (!p) throw std::invalid_argument("null pointer cast to ref"); } - explicit ref(T * p) - : p(p) + explicit ref(T * p) : p(p) { if (!p) throw std::invalid_argument("null pointer cast to ref"); } - T* operator ->() const - { - return &*p; - } + T * operator->() const { return &*p; } - T& operator *() const - { - return *p; - } + T & operator*() const { return *p; } - operator std::shared_ptr () const - { - return p; - } + operator std::shared_ptr() const { return p; } - std::shared_ptr get_ptr() const - { - return p; - } + std::shared_ptr get_ptr() const { return p; } - template - ref cast() const + template ref cast() const { return ref(std::dynamic_pointer_cast(p)); } - template - std::shared_ptr dynamic_pointer_cast() const + template std::shared_ptr dynamic_pointer_cast() const { return std::dynamic_pointer_cast(p); } - template - operator ref () const + template operator ref() const { return ref((std::shared_ptr) p); } - bool operator == (const ref & other) const - { - return p == other.p; - } - - bool operator != (const ref & other) const - { - return p != other.p; - } + bool operator==(const ref & other) const { return p == other.p; } -private: + bool operator!=(const ref & other) const { return p != other.p; } + private: template - friend ref - make_ref(Args&&... args); - + friend ref make_ref(Args &&... args); }; -template -inline ref -make_ref(Args&&... args) +template inline ref make_ref(Args &&... args) { auto p = std::make_shared(std::forward(args)...); return ref(p); diff --git a/src/libutil/serialise.cc b/src/libutil/serialise.cc index 8ff90458304d..ffd9df5951fd 100644 --- a/src/libutil/serialise.cc +++ b/src/libutil/serialise.cc @@ -7,13 +7,12 @@ #include - namespace nix { - -void BufferedSink::operator () (std::string_view data) +void BufferedSink::operator()(std::string_view data) { - if (!buffer) buffer = decltype(buffer)(new char[bufSize]); + if (!buffer) + buffer = decltype(buffer)(new char[bufSize]); while (!data.empty()) { /* Optimisation: bypass the buffer if the data exceeds the @@ -25,29 +24,34 @@ void BufferedSink::operator () (std::string_view data) } /* Otherwise, copy the bytes to the buffer. Flush the buffer when it's full. */ - size_t n = bufPos + data.size() > bufSize ? bufSize - bufPos : data.size(); + size_t n = + bufPos + data.size() > bufSize ? bufSize - bufPos : data.size(); memcpy(buffer.get() + bufPos, data.data(), n); - data.remove_prefix(n); bufPos += n; - if (bufPos == bufSize) flush(); + data.remove_prefix(n); + bufPos += n; + if (bufPos == bufSize) + flush(); } } - void BufferedSink::flush() { - if (bufPos == 0) return; + if (bufPos == 0) + return; size_t n = bufPos; bufPos = 0; // don't trigger the assert() in ~BufferedSink() write({buffer.get(), n}); } - FdSink::~FdSink() { - try { flush(); } catch (...) { ignoreException(); } + try { + flush(); + } catch (...) { + ignoreException(); + } } - size_t threshold = 256 * 1024 * 1024; static void warnLargeDump() @@ -55,7 +59,6 @@ static void warnLargeDump() warn("dumping very large path (> 256 MiB); this may run out of memory"); } - void FdSink::write(std::string_view data) { written += data.size(); @@ -74,22 +77,17 @@ void FdSink::write(std::string_view data) } } +bool FdSink::good() { return _good; } -bool FdSink::good() -{ - return _good; -} - - -void Source::operator () (char * data, size_t len) +void Source::operator()(char * data, size_t len) { while (len) { size_t n = read(data, len); - data += n; len -= n; + data += n; + len -= n; } } - void Source::drainInto(Sink & sink) { std::string s; @@ -105,7 +103,6 @@ void Source::drainInto(Sink & sink) } } - std::string Source::drain() { StringSink s; @@ -113,27 +110,24 @@ std::string Source::drain() return std::move(s.s); } - size_t BufferedSource::read(char * data, size_t len) { - if (!buffer) buffer = decltype(buffer)(new char[bufSize]); + if (!buffer) + buffer = decltype(buffer)(new char[bufSize]); - if (!bufPosIn) bufPosIn = readUnbuffered(buffer.get(), bufSize); + if (!bufPosIn) + bufPosIn = readUnbuffered(buffer.get(), bufSize); /* Copy out the data in the buffer. */ size_t n = len > bufPosIn - bufPosOut ? bufPosIn - bufPosOut : len; memcpy(data, buffer.get() + bufPosOut, n); bufPosOut += n; - if (bufPosIn == bufPosOut) bufPosIn = bufPosOut = 0; + if (bufPosIn == bufPosOut) + bufPosIn = bufPosOut = 0; return n; } - -bool BufferedSource::hasData() -{ - return bufPosOut < bufPosIn; -} - +bool BufferedSource::hasData() { return bufPosOut < bufPosIn; } size_t FdSource::readUnbuffered(char * data, size_t len) { @@ -142,127 +136,126 @@ size_t FdSource::readUnbuffered(char * data, size_t len) checkInterrupt(); n = ::read(fd, data, len); } while (n == -1 && errno == EINTR); - if (n == -1) { _good = false; throw SysError("reading from file"); } - if (n == 0) { _good = false; throw EndOfFile("unexpected end-of-file"); } + if (n == -1) { + _good = false; + throw SysError("reading from file"); + } + if (n == 0) { + _good = false; + throw EndOfFile("unexpected end-of-file"); + } read += n; return n; } - -bool FdSource::good() -{ - return _good; -} - +bool FdSource::good() { return _good; } size_t StringSource::read(char * data, size_t len) { - if (pos == s.size()) throw EndOfFile("end of string reached"); + if (pos == s.size()) + throw EndOfFile("end of string reached"); size_t n = s.copy(data, len, pos); pos += n; return n; } - #if BOOST_VERSION >= 106300 && BOOST_VERSION < 106600 #error Coroutines are broken in this version of Boost! #endif /* A concrete datatype allow virtual dispatch of stack allocation methods. */ struct VirtualStackAllocator { - StackAllocator *allocator = StackAllocator::defaultAllocator; + StackAllocator * allocator = StackAllocator::defaultAllocator; - boost::context::stack_context allocate() { - return allocator->allocate(); - } + boost::context::stack_context allocate() { return allocator->allocate(); } - void deallocate(boost::context::stack_context sctx) { + void deallocate(boost::context::stack_context sctx) + { allocator->deallocate(sctx); } }; - /* This class reifies the default boost coroutine stack allocation strategy with a virtual interface. */ class DefaultStackAllocator : public StackAllocator { boost::coroutines2::default_stack stack; - boost::context::stack_context allocate() { - return stack.allocate(); - } + boost::context::stack_context allocate() { return stack.allocate(); } - void deallocate(boost::context::stack_context sctx) { + void deallocate(boost::context::stack_context sctx) + { stack.deallocate(sctx); } }; static DefaultStackAllocator defaultAllocatorSingleton; -StackAllocator *StackAllocator::defaultAllocator = &defaultAllocatorSingleton; - +StackAllocator * StackAllocator::defaultAllocator = &defaultAllocatorSingleton; std::unique_ptr sourceToSink(std::function fun) { - struct SourceToSink : FinishSink - { + struct SourceToSink : FinishSink { typedef boost::coroutines2::coroutine coro_t; std::function fun; std::optional coro; - SourceToSink(std::function fun) : fun(fun) - { - } + SourceToSink(std::function fun) : fun(fun) {} std::string_view cur; - void operator () (std::string_view in) override + void operator()(std::string_view in) override { - if (in.empty()) return; + if (in.empty()) + return; cur = in; if (!coro) - coro = coro_t::push_type(VirtualStackAllocator{}, [&](coro_t::pull_type & yield) { - LambdaSource source([&](char *out, size_t out_len) { - if (cur.empty()) { - yield(); - if (yield.get()) { - return (size_t)0; + coro = coro_t::push_type( + VirtualStackAllocator{}, [&](coro_t::pull_type & yield) { + LambdaSource source([&](char * out, size_t out_len) { + if (cur.empty()) { + yield(); + if (yield.get()) { + return (size_t) 0; + } } - } - size_t n = std::min(cur.size(), out_len); - memcpy(out, cur.data(), n); - cur.remove_prefix(n); - return n; + size_t n = std::min(cur.size(), out_len); + memcpy(out, cur.data(), n); + cur.remove_prefix(n); + return n; + }); + fun(source); }); - fun(source); - }); - if (!*coro) { abort(); } + if (!*coro) { + abort(); + } - if (!cur.empty()) (*coro)(false); + if (!cur.empty()) + (*coro)(false); } void finish() override { - if (!coro) return; - if (!*coro) abort(); + if (!coro) + return; + if (!*coro) + abort(); (*coro)(true); - if (*coro) abort(); + if (*coro) + abort(); } }; return std::make_unique(fun); } - -std::unique_ptr sinkToSource( - std::function fun, - std::function eof) +std::unique_ptr sinkToSource(std::function fun, + std::function eof) { - struct SinkToSource : Source - { + struct SinkToSource : Source { typedef boost::coroutines2::coroutine coro_t; std::function fun; @@ -280,17 +273,23 @@ std::unique_ptr sinkToSource( size_t read(char * data, size_t len) override { if (!coro) - coro = coro_t::pull_type(VirtualStackAllocator{}, [&](coro_t::push_type & yield) { - LambdaSink sink([&](std::string_view data) { - if (!data.empty()) yield(std::string(data)); + coro = coro_t::pull_type( + VirtualStackAllocator{}, [&](coro_t::push_type & yield) { + LambdaSink sink([&](std::string_view data) { + if (!data.empty()) + yield(std::string(data)); + }); + fun(sink); }); - fun(sink); - }); - if (!*coro) { eof(); abort(); } + if (!*coro) { + eof(); + abort(); + } if (pos == cur.size()) { - if (!cur.empty()) (*coro)(); + if (!cur.empty()) + (*coro)(); cur = coro->get(); pos = 0; } @@ -306,7 +305,6 @@ std::unique_ptr sinkToSource( return std::make_unique(fun, eof); } - void writePadding(size_t len, Sink & sink) { if (len % 8) { @@ -316,7 +314,6 @@ void writePadding(size_t len, Sink & sink) } } - void writeString(std::string_view data, Sink & sink) { sink << data.size(); @@ -324,14 +321,12 @@ void writeString(std::string_view data, Sink & sink) writePadding(data.size(), sink); } - -Sink & operator << (Sink & sink, std::string_view s) +Sink & operator<<(Sink & sink, std::string_view s) { writeString(s, sink); return sink; } - template void writeStrings(const T & ss, Sink & sink) { sink << ss.size(); @@ -339,28 +334,24 @@ template void writeStrings(const T & ss, Sink & sink) sink << i; } -Sink & operator << (Sink & sink, const Strings & s) +Sink & operator<<(Sink & sink, const Strings & s) { writeStrings(s, sink); return sink; } -Sink & operator << (Sink & sink, const StringSet & s) +Sink & operator<<(Sink & sink, const StringSet & s) { writeStrings(s, sink); return sink; } -Sink & operator << (Sink & sink, const Error & ex) +Sink & operator<<(Sink & sink, const Error & ex) { auto info = ex.info(); - sink - << "Error" - << info.level - << "Error" // removed - << info.msg.str() - << 0 // FIXME: info.errPos - << info.traces.size(); + sink << "Error" << info.level << "Error" // removed + << info.msg.str() << 0 // FIXME: info.errPos + << info.traces.size(); for (auto & trace : info.traces) { sink << 0; // FIXME: trace.pos sink << trace.hint.str(); @@ -368,7 +359,6 @@ Sink & operator << (Sink & sink, const Error & ex) return sink; } - void readPadding(size_t len, Source & source) { if (len % 8) { @@ -376,38 +366,38 @@ void readPadding(size_t len, Source & source) size_t n = 8 - (len % 8); source(zero, n); for (unsigned int i = 0; i < n; i++) - if (zero[i]) throw SerialisationError("non-zero padding"); + if (zero[i]) + throw SerialisationError("non-zero padding"); } } - size_t readString(char * buf, size_t max, Source & source) { auto len = readNum(source); - if (len > max) throw SerialisationError("string is too long"); + if (len > max) + throw SerialisationError("string is too long"); source(buf, len); readPadding(len, source); return len; } - std::string readString(Source & source, size_t max) { auto len = readNum(source); - if (len > max) throw SerialisationError("string is too long"); + if (len > max) + throw SerialisationError("string is too long"); std::string res(len, 0); source(res.data(), len); readPadding(len, source); return res; } -Source & operator >> (Source & in, std::string & s) +Source & operator>>(Source & in, std::string & s) { s = readString(in); return in; } - template T readStrings(Source & source) { auto count = readNum(source); @@ -420,7 +410,6 @@ template T readStrings(Source & source) template Paths readStrings(Source & source); template PathSet readStrings(Source & source); - Error readError(Source & source) { auto type = readString(source); @@ -428,7 +417,7 @@ Error readError(Source & source) auto level = (Verbosity) readInt(source); auto name = readString(source); // removed auto msg = readString(source); - ErrorInfo info { + ErrorInfo info{ .level = level, .msg = hintformat(std::move(format("%s") % msg)), }; @@ -438,15 +427,13 @@ Error readError(Source & source) for (size_t i = 0; i < nrTraces; ++i) { havePos = readNum(source); assert(havePos == 0); - info.traces.push_back(Trace { - .hint = hintformat(std::move(format("%s") % readString(source))) - }); + info.traces.push_back(Trace{ + .hint = hintformat(std::move(format("%s") % readString(source)))}); } return Error(std::move(info)); } - -void StringSink::operator () (std::string_view data) +void StringSink::operator()(std::string_view data) { static bool warned = false; if (!warned && s.size() > threshold) { diff --git a/src/libutil/serialise.hh b/src/libutil/serialise.hh index 13da26c6acbc..ca58ce371ade 100644 --- a/src/libutil/serialise.hh +++ b/src/libutil/serialise.hh @@ -5,60 +5,54 @@ #include "types.hh" #include "util.hh" -namespace boost::context { struct stack_context; } +namespace boost::context { +struct stack_context; +} namespace nix { - /* Abstract destination of binary data. */ -struct Sink -{ - virtual ~Sink() { } - virtual void operator () (std::string_view data) = 0; +struct Sink { + virtual ~Sink() {} + virtual void operator()(std::string_view data) = 0; virtual bool good() { return true; } }; /* Just throws away data. */ -struct NullSink : Sink -{ - void operator () (std::string_view data) override - { } +struct NullSink : Sink { + void operator()(std::string_view data) override {} }; - -struct FinishSink : virtual Sink -{ +struct FinishSink : virtual Sink { virtual void finish() = 0; }; - /* A buffered abstract sink. Warning: a BufferedSink should not be used from multiple threads concurrently. */ -struct BufferedSink : virtual Sink -{ +struct BufferedSink : virtual Sink { size_t bufSize, bufPos; std::unique_ptr buffer; BufferedSink(size_t bufSize = 32 * 1024) - : bufSize(bufSize), bufPos(0), buffer(nullptr) { } + : bufSize(bufSize), bufPos(0), buffer(nullptr) + { + } - void operator () (std::string_view data) override; + void operator()(std::string_view data) override; void flush(); virtual void write(std::string_view data) = 0; }; - /* Abstract source of binary data. */ -struct Source -{ - virtual ~Source() { } +struct Source { + virtual ~Source() {} /* Store exactly ‘len’ bytes in the buffer pointed to by ‘data’. It blocks until all the requested data is available, or throws an error if it is not going to be available. */ - void operator () (char * data, size_t len); + void operator()(char * data, size_t len); /* Store up to ‘len’ in the buffer pointed to by ‘data’, and return the number of bytes stored. It blocks until at least @@ -72,39 +66,37 @@ struct Source std::string drain(); }; - /* A buffered abstract source. Warning: a BufferedSource should not be used from multiple threads concurrently. */ -struct BufferedSource : Source -{ +struct BufferedSource : Source { size_t bufSize, bufPosIn, bufPosOut; std::unique_ptr buffer; BufferedSource(size_t bufSize = 32 * 1024) - : bufSize(bufSize), bufPosIn(0), bufPosOut(0), buffer(nullptr) { } + : bufSize(bufSize), bufPosIn(0), bufPosOut(0), buffer(nullptr) + { + } size_t read(char * data, size_t len) override; bool hasData(); -protected: + protected: /* Underlying read call, to be overridden. */ virtual size_t readUnbuffered(char * data, size_t len) = 0; }; - /* A sink that writes data to a file descriptor. */ -struct FdSink : BufferedSink -{ +struct FdSink : BufferedSink { int fd; bool warn = false; size_t written = 0; - FdSink() : fd(-1) { } - FdSink(int fd) : fd(fd) { } - FdSink(FdSink&&) = default; + FdSink() : fd(-1) {} + FdSink(int fd) : fd(fd) {} + FdSink(FdSink &&) = default; - FdSink& operator=(FdSink && s) + FdSink & operator=(FdSink && s) { flush(); fd = s.fd; @@ -120,22 +112,20 @@ struct FdSink : BufferedSink bool good() override; -private: + private: bool _good = true; }; - /* A source that reads data from a file descriptor. */ -struct FdSource : BufferedSource -{ +struct FdSource : BufferedSource { int fd; size_t read = 0; - FdSource() : fd(-1) { } - FdSource(int fd) : fd(fd) { } - FdSource(FdSource&&) = default; + FdSource() : fd(-1) {} + FdSource(int fd) : fd(fd) {} + FdSource(FdSource &&) = default; - FdSource& operator=(FdSource && s) + FdSource & operator=(FdSource && s) { fd = s.fd; s.fd = -1; @@ -144,57 +134,47 @@ struct FdSource : BufferedSource } bool good() override; -protected: + + protected: size_t readUnbuffered(char * data, size_t len) override; -private: + + private: bool _good = true; }; - /* A sink that writes data to a string. */ -struct StringSink : Sink -{ +struct StringSink : Sink { std::string s; - StringSink() { } - explicit StringSink(const size_t reservedSize) - { - s.reserve(reservedSize); - }; - StringSink(std::string && s) : s(std::move(s)) { }; - void operator () (std::string_view data) override; + StringSink() {} + explicit StringSink(const size_t reservedSize) { s.reserve(reservedSize); }; + StringSink(std::string && s) : s(std::move(s)){}; + void operator()(std::string_view data) override; }; - /* A source that reads data from a string. */ -struct StringSource : Source -{ +struct StringSource : Source { std::string_view s; size_t pos; - StringSource(std::string_view s) : s(s), pos(0) { } + StringSource(std::string_view s) : s(s), pos(0) {} size_t read(char * data, size_t len) override; }; - /* A sink that writes all incoming data to two other sinks. */ -struct TeeSink : Sink -{ - Sink & sink1, & sink2; - TeeSink(Sink & sink1, Sink & sink2) : sink1(sink1), sink2(sink2) { } - virtual void operator () (std::string_view data) +struct TeeSink : Sink { + Sink &sink1, &sink2; + TeeSink(Sink & sink1, Sink & sink2) : sink1(sink1), sink2(sink2) {} + virtual void operator()(std::string_view data) { sink1(data); sink2(data); } }; - /* Adapter class of a Source that saves all data read to a sink. */ -struct TeeSource : Source -{ +struct TeeSource : Source { Source & orig; Sink & sink; - TeeSource(Source & orig, Sink & sink) - : orig(orig), sink(sink) { } + TeeSource(Source & orig, Sink & sink) : orig(orig), sink(sink) {} size_t read(char * data, size_t len) { size_t n = orig.read(data, len); @@ -204,12 +184,10 @@ struct TeeSource : Source }; /* A reader that consumes the original Source until 'size'. */ -struct SizedSource : Source -{ +struct SizedSource : Source { Source & orig; size_t remain; - SizedSource(Source & orig, size_t size) - : orig(orig), remain(size) { } + SizedSource(Source & orig, size_t size) : orig(orig), remain(size) {} size_t read(char * data, size_t len) { if (this->remain <= 0) { @@ -235,56 +213,40 @@ struct SizedSource : Source }; /* A sink that that just counts the number of bytes given to it */ -struct LengthSink : Sink -{ +struct LengthSink : Sink { uint64_t length = 0; - void operator () (std::string_view data) override - { - length += data.size(); - } + void operator()(std::string_view data) override { length += data.size(); } }; /* Convert a function into a sink. */ -struct LambdaSink : Sink -{ +struct LambdaSink : Sink { typedef std::function lambda_t; lambda_t lambda; - LambdaSink(const lambda_t & lambda) : lambda(lambda) { } + LambdaSink(const lambda_t & lambda) : lambda(lambda) {} - void operator () (std::string_view data) override - { - lambda(data); - } + void operator()(std::string_view data) override { lambda(data); } }; - /* Convert a function into a source. */ -struct LambdaSource : Source -{ +struct LambdaSource : Source { typedef std::function lambda_t; lambda_t lambda; - LambdaSource(const lambda_t & lambda) : lambda(lambda) { } + LambdaSource(const lambda_t & lambda) : lambda(lambda) {} - size_t read(char * data, size_t len) override - { - return lambda(data, len); - } + size_t read(char * data, size_t len) override { return lambda(data, len); } }; /* Chain two sources together so after the first is exhausted, the second is used */ -struct ChainSource : Source -{ - Source & source1, & source2; +struct ChainSource : Source { + Source &source1, &source2; bool useSecond = false; - ChainSource(Source & s1, Source & s2) - : source1(s1), source2(s2) - { } + ChainSource(Source & s1, Source & s2) : source1(s1), source2(s2) {} size_t read(char * data, size_t len) override; }; @@ -294,16 +256,14 @@ std::unique_ptr sourceToSink(std::function fun); /* Convert a function that feeds data into a Sink into a Source. The Source executes the function as a coroutine. */ std::unique_ptr sinkToSource( - std::function fun, - std::function eof = []() { + std::function fun, std::function eof = []() { throw EndOfFile("coroutine has finished"); }); - void writePadding(size_t len, Sink & sink); void writeString(std::string_view s, Sink & sink); -inline Sink & operator << (Sink & sink, uint64_t n) +inline Sink & operator<<(Sink & sink, uint64_t n) { unsigned char buf[8]; buf[0] = n & 0xff; @@ -318,66 +278,56 @@ inline Sink & operator << (Sink & sink, uint64_t n) return sink; } -Sink & operator << (Sink & in, const Error & ex); -Sink & operator << (Sink & sink, std::string_view s); -Sink & operator << (Sink & sink, const Strings & s); -Sink & operator << (Sink & sink, const StringSet & s); - +Sink & operator<<(Sink & in, const Error & ex); +Sink & operator<<(Sink & sink, std::string_view s); +Sink & operator<<(Sink & sink, const Strings & s); +Sink & operator<<(Sink & sink, const StringSet & s); MakeError(SerialisationError, Error); - -template -T readNum(Source & source) +template T readNum(Source & source) { unsigned char buf[8]; source((char *) buf, sizeof(buf)); - uint64_t n = - ((uint64_t) buf[0]) | - ((uint64_t) buf[1] << 8) | - ((uint64_t) buf[2] << 16) | - ((uint64_t) buf[3] << 24) | - ((uint64_t) buf[4] << 32) | - ((uint64_t) buf[5] << 40) | - ((uint64_t) buf[6] << 48) | - ((uint64_t) buf[7] << 56); + uint64_t n = ((uint64_t) buf[0]) | ((uint64_t) buf[1] << 8) | + ((uint64_t) buf[2] << 16) | ((uint64_t) buf[3] << 24) | + ((uint64_t) buf[4] << 32) | ((uint64_t) buf[5] << 40) | + ((uint64_t) buf[6] << 48) | ((uint64_t) buf[7] << 56); - if (n > (uint64_t)std::numeric_limits::max()) - throw SerialisationError("serialised integer %d is too large for type '%s'", n, typeid(T).name()); + if (n > (uint64_t) std::numeric_limits::max()) + throw SerialisationError( + "serialised integer %d is too large for type '%s'", n, + typeid(T).name()); return (T) n; } - inline unsigned int readInt(Source & source) { return readNum(source); } - inline uint64_t readLongLong(Source & source) { return readNum(source); } - void readPadding(size_t len, Source & source); size_t readString(char * buf, size_t max, Source & source); -std::string readString(Source & source, size_t max = std::numeric_limits::max()); +std::string readString(Source & source, + size_t max = std::numeric_limits::max()); template T readStrings(Source & source); -Source & operator >> (Source & in, std::string & s); +Source & operator>>(Source & in, std::string & s); -template -Source & operator >> (Source & in, T & n) +template Source & operator>>(Source & in, T & n) { n = readNum(in); return in; } -template -Source & operator >> (Source & in, bool & b) +template Source & operator>>(Source & in, bool & b) { b = readNum(in); return in; @@ -385,15 +335,14 @@ Source & operator >> (Source & in, bool & b) Error readError(Source & source); - /* An adapter that converts a std::basic_istream into a source. */ -struct StreamToSourceAdapter : Source -{ +struct StreamToSourceAdapter : Source { std::shared_ptr> istream; StreamToSourceAdapter(std::shared_ptr> istream) : istream(istream) - { } + { + } size_t read(char * data, size_t len) override { @@ -408,7 +357,6 @@ struct StreamToSourceAdapter : Source } }; - /* A source that reads a distinct format of concatenated chunks back into its logical form, in order to guarantee a known state to the original stream, even in the event of errors. @@ -416,22 +364,21 @@ struct StreamToSourceAdapter : Source Use with FramedSink, which also allows the logical stream to be terminated in the event of an exception. */ -struct FramedSource : Source -{ +struct FramedSource : Source { Source & from; bool eof = false; std::vector pending; size_t pos = 0; - FramedSource(Source & from) : from(from) - { } + FramedSource(Source & from) : from(from) {} ~FramedSource() { if (!eof) { while (true) { auto n = readInt(from); - if (!n) break; + if (!n) + break; std::vector data(n); from(data.data(), n); } @@ -440,7 +387,8 @@ struct FramedSource : Source size_t read(char * data, size_t len) override { - if (eof) throw EndOfFile("reached end of FramedSource"); + if (eof) + throw EndOfFile("reached end of FramedSource"); if (pos >= pending.size()) { size_t len = readInt(from); @@ -465,13 +413,11 @@ struct FramedSource : Source The exception_ptr reference can be used to terminate the stream when you detect that an error has occurred on the remote end. */ -struct FramedSink : nix::BufferedSink -{ +struct FramedSink : nix::BufferedSink { BufferedSink & to; std::exception_ptr & ex; - FramedSink(BufferedSink & to, std::exception_ptr & ex) : to(to), ex(ex) - { } + FramedSink(BufferedSink & to, std::exception_ptr & ex) : to(to), ex(ex) {} ~FramedSink() { @@ -508,7 +454,7 @@ struct StackAllocator { /* The stack allocator to use in sinkToSource and potentially elsewhere. It is reassigned by the initGC() method in libexpr. */ - static StackAllocator *defaultAllocator; + static StackAllocator * defaultAllocator; }; } diff --git a/src/libutil/split.hh b/src/libutil/split.hh index 87a23b13e3fa..2c4b2c8ba48b 100644 --- a/src/libutil/split.hh +++ b/src/libutil/split.hh @@ -11,19 +11,23 @@ namespace nix { // separator, and modify the string argument to contain only the part after the // separator. Otherwise, we return `std::nullopt`, and we leave the argument // string alone. -static inline std::optional splitPrefixTo(std::string_view & string, char separator) { +static inline std::optional +splitPrefixTo(std::string_view & string, char separator) +{ auto sepInstance = string.find(separator); if (sepInstance != std::string_view::npos) { auto prefix = string.substr(0, sepInstance); - string.remove_prefix(sepInstance+1); + string.remove_prefix(sepInstance + 1); return prefix; } return std::nullopt; } -static inline bool splitPrefix(std::string_view & string, std::string_view prefix) { +static inline bool splitPrefix(std::string_view & string, + std::string_view prefix) +{ bool res = hasPrefix(string, prefix); if (res) string.remove_prefix(prefix.length()); diff --git a/src/libutil/suggestions.cc b/src/libutil/suggestions.cc index 9510a5f0c415..e2260e0ac8e9 100644 --- a/src/libutil/suggestions.cc +++ b/src/libutil/suggestions.cc @@ -13,20 +13,21 @@ int levenshteinDistance(std::string_view first, std::string_view second) int m = first.size(); int n = second.size(); - auto v0 = std::vector(n+1); - auto v1 = std::vector(n+1); + auto v0 = std::vector(n + 1); + auto v1 = std::vector(n + 1); for (auto i = 0; i <= n; i++) v0[i] = i; for (auto i = 0; i < m; i++) { - v1[0] = i+1; + v1[0] = i + 1; for (auto j = 0; j < n; j++) { - auto deletionCost = v0[j+1] + 1; + auto deletionCost = v0[j + 1] + 1; auto insertionCost = v1[j] + 1; auto substitutionCost = first[i] == second[j] ? v0[j] : v0[j] + 1; - v1[j+1] = std::min({deletionCost, insertionCost, substitutionCost}); + v1[j + 1] = + std::min({deletionCost, insertionCost, substitutionCost}); } std::swap(v0, v1); @@ -35,18 +36,17 @@ int levenshteinDistance(std::string_view first, std::string_view second) return v0[n]; } -Suggestions Suggestions::bestMatches ( - std::set allMatches, - std::string query) +Suggestions Suggestions::bestMatches(std::set allMatches, + std::string query) { std::set res; for (const auto & possibleMatch : allMatches) { - res.insert(Suggestion { + res.insert(Suggestion{ .distance = levenshteinDistance(query, possibleMatch), .suggestion = possibleMatch, }); } - return Suggestions { res }; + return Suggestions{res}; } Suggestions Suggestions::trim(int limit, int maxDistance) const @@ -73,31 +73,30 @@ std::string Suggestion::to_string() const std::string Suggestions::to_string() const { switch (suggestions.size()) { - case 0: - return ""; - case 1: - return suggestions.begin()->to_string(); - default: { - std::string res = "one of "; - auto iter = suggestions.begin(); - res += iter->to_string(); // Iter can’t be end() because the container isn’t null - iter++; - auto last = suggestions.end(); last--; - for ( ; iter != suggestions.end() ; iter++) { - res += (iter == last) ? " or " : ", "; - res += iter->to_string(); - } - return res; + case 0: + return ""; + case 1: + return suggestions.begin()->to_string(); + default: { + std::string res = "one of "; + auto iter = suggestions.begin(); + res += iter->to_string(); // Iter can’t be end() because the container + // isn’t null + iter++; + auto last = suggestions.end(); + last--; + for (; iter != suggestions.end(); iter++) { + res += (iter == last) ? " or " : ", "; + res += iter->to_string(); } + return res; + } } } Suggestions & Suggestions::operator+=(const Suggestions & other) { - suggestions.insert( - other.suggestions.begin(), - other.suggestions.end() - ); + suggestions.insert(other.suggestions.begin(), other.suggestions.end()); return *this; } diff --git a/src/libutil/suggestions.hh b/src/libutil/suggestions.hh index d54dd8e31eae..381361e9e9dd 100644 --- a/src/libutil/suggestions.hh +++ b/src/libutil/suggestions.hh @@ -12,7 +12,7 @@ int levenshteinDistance(std::string_view first, std::string_view second); * A potential suggestion for the cli interface. */ class Suggestion { -public: + public: int distance; // The smaller the better std::string suggestion; @@ -22,59 +22,38 @@ public: }; class Suggestions { -public: + public: std::set suggestions; std::string to_string() const; - Suggestions trim( - int limit = 5, - int maxDistance = 2 - ) const; + Suggestions trim(int limit = 5, int maxDistance = 2) const; - static Suggestions bestMatches ( - std::set allMatches, - std::string query - ); + static Suggestions bestMatches(std::set allMatches, + std::string query); - Suggestions& operator+=(const Suggestions & other); + Suggestions & operator+=(const Suggestions & other); }; std::ostream & operator<<(std::ostream & str, const Suggestion &); std::ostream & operator<<(std::ostream & str, const Suggestions &); // Either a value of type `T`, or some suggestions -template -class OrSuggestions { -public: +template class OrSuggestions { + public: using Raw = std::variant; Raw raw; - T* operator ->() - { - return &**this; - } + T * operator->() { return &**this; } - T& operator *() - { - return std::get(raw); - } + T & operator*() { return std::get(raw); } - operator bool() const noexcept - { - return std::holds_alternative(raw); - } + operator bool() const noexcept { return std::holds_alternative(raw); } - OrSuggestions(T t) - : raw(t) - { - } + OrSuggestions(T t) : raw(t) {} - OrSuggestions() - : raw(Suggestions{}) - { - } + OrSuggestions() : raw(Suggestions{}) {} static OrSuggestions failed(const Suggestions & s) { @@ -96,7 +75,6 @@ public: else return noSuggestions; } - }; } diff --git a/src/libutil/sync.hh b/src/libutil/sync.hh index e1d591d77a84..589d660179cc 100644 --- a/src/libutil/sync.hh +++ b/src/libutil/sync.hh @@ -23,32 +23,29 @@ namespace nix { scope. */ -template -class Sync -{ -private: +template class Sync { + private: M mutex; T data; -public: + public: + Sync() {} + Sync(const T & data) : data(data) {} + Sync(T && data) noexcept : data(std::move(data)) {} - Sync() { } - Sync(const T & data) : data(data) { } - Sync(T && data) noexcept : data(std::move(data)) { } - - class Lock - { - private: + class Lock { + private: Sync * s; std::unique_lock lk; friend Sync; - Lock(Sync * s) : s(s), lk(s->mutex) { } - public: + Lock(Sync * s) : s(s), lk(s->mutex) {} + + public: Lock(Lock && l) : s(l.s) { abort(); } Lock(const Lock & l) = delete; - ~Lock() { } - T * operator -> () { return &s->data; } - T & operator * () { return s->data; } + ~Lock() {} + T * operator->() { return &s->data; } + T & operator*() { return s->data; } void wait(std::condition_variable & cv) { @@ -57,8 +54,9 @@ public: } template - std::cv_status wait_for(std::condition_variable & cv, - const std::chrono::duration & duration) + std::cv_status + wait_for(std::condition_variable & cv, + const std::chrono::duration & duration) { assert(s); return cv.wait_for(lk, duration); @@ -66,16 +64,17 @@ public: template bool wait_for(std::condition_variable & cv, - const std::chrono::duration & duration, - Predicate pred) + const std::chrono::duration & duration, + Predicate pred) { assert(s); return cv.wait_for(lk, duration, pred); } template - std::cv_status wait_until(std::condition_variable & cv, - const std::chrono::time_point & duration) + std::cv_status + wait_until(std::condition_variable & cv, + const std::chrono::time_point & duration) { assert(s); return cv.wait_until(lk, duration); diff --git a/src/libutil/tarfile.cc b/src/libutil/tarfile.cc index a7db58559cb6..8d1c1b2f3a20 100644 --- a/src/libutil/tarfile.cc +++ b/src/libutil/tarfile.cc @@ -6,12 +6,10 @@ namespace nix { -static int callback_open(struct archive *, void * self) -{ - return ARCHIVE_OK; -} +static int callback_open(struct archive *, void * self) { return ARCHIVE_OK; } -static ssize_t callback_read(struct archive * archive, void * _self, const void * * buffer) +static ssize_t callback_read(struct archive * archive, void * _self, + const void ** buffer) { auto self = (TarArchive *) _self; *buffer = self->buffer.data(); @@ -21,15 +19,13 @@ static ssize_t callback_read(struct archive * archive, void * _self, const void } catch (EndOfFile &) { return 0; } catch (std::exception & err) { - archive_set_error(archive, EIO, "Source threw exception: %s", err.what()); + archive_set_error(archive, EIO, "Source threw exception: %s", + err.what()); return -1; } } -static int callback_close(struct archive *, void * self) -{ - return ARCHIVE_OK; -} +static int callback_close(struct archive *, void * self) { return ARCHIVE_OK; } void TarArchive::check(int err, const std::string & reason) { @@ -52,17 +48,19 @@ TarArchive::TarArchive(Source & source, bool raw) : buffer(4096) archive_read_support_format_raw(archive); archive_read_support_format_empty(archive); } - check(archive_read_open(archive, (void *)this, callback_open, callback_read, callback_close), "Failed to open archive (%s)"); + check(archive_read_open(archive, (void *) this, callback_open, + callback_read, callback_close), + "Failed to open archive (%s)"); } - TarArchive::TarArchive(const Path & path) { this->archive = archive_read_new(); archive_read_support_filter_all(archive); archive_read_support_format_all(archive); - check(archive_read_open_filename(archive, path.c_str(), 16384), "failed to open archive: %s"); + check(archive_read_open_filename(archive, path.c_str(), 16384), + "failed to open archive: %s"); } void TarArchive::close() @@ -72,37 +70,37 @@ void TarArchive::close() TarArchive::~TarArchive() { - if (this->archive) archive_read_free(this->archive); + if (this->archive) + archive_read_free(this->archive); } static void extract_archive(TarArchive & archive, const Path & destDir) { - int flags = ARCHIVE_EXTRACT_FFLAGS - | ARCHIVE_EXTRACT_PERM - | ARCHIVE_EXTRACT_TIME - | ARCHIVE_EXTRACT_SECURE_SYMLINKS - | ARCHIVE_EXTRACT_SECURE_NODOTDOT; + int flags = ARCHIVE_EXTRACT_FFLAGS | ARCHIVE_EXTRACT_PERM | + ARCHIVE_EXTRACT_TIME | ARCHIVE_EXTRACT_SECURE_SYMLINKS | + ARCHIVE_EXTRACT_SECURE_NODOTDOT; for (;;) { struct archive_entry * entry; int r = archive_read_next_header(archive.archive, &entry); - if (r == ARCHIVE_EOF) break; + if (r == ARCHIVE_EOF) + break; auto name = archive_entry_pathname(entry); if (!name) - throw Error("cannot get archive member name: %s", archive_error_string(archive.archive)); + throw Error("cannot get archive member name: %s", + archive_error_string(archive.archive)); if (r == ARCHIVE_WARN) warn(archive_error_string(archive.archive)); else archive.check(r); - archive_entry_copy_pathname(entry, - (destDir + "/" + name).c_str()); + archive_entry_copy_pathname(entry, (destDir + "/" + name).c_str()); // Patch hardlink path - const char *original_hardlink = archive_entry_hardlink(entry); + const char * original_hardlink = archive_entry_hardlink(entry); if (original_hardlink) { - archive_entry_copy_hardlink(entry, - (destDir + "/" + original_hardlink).c_str()); + archive_entry_copy_hardlink( + entry, (destDir + "/" + original_hardlink).c_str()); } archive.check(archive_read_extract(archive.archive, entry, flags)); diff --git a/src/libutil/tarfile.hh b/src/libutil/tarfile.hh index 4d9141fd458a..52e00a0eeb27 100644 --- a/src/libutil/tarfile.hh +++ b/src/libutil/tarfile.hh @@ -8,7 +8,8 @@ struct TarArchive { Source * source; std::vector buffer; - void check(int err, const std::string & reason = "failed to extract archive (%s)"); + void check(int err, + const std::string & reason = "failed to extract archive (%s)"); TarArchive(Source & source, bool raw = false); diff --git a/src/libutil/tests/chunked-vector.cc b/src/libutil/tests/chunked-vector.cc index 868d11f6f370..5b471ca6f309 100644 --- a/src/libutil/tests/chunked-vector.cc +++ b/src/libutil/tests/chunked-vector.cc @@ -3,52 +3,54 @@ #include namespace nix { - TEST(ChunkedVector, InitEmpty) { - auto v = ChunkedVector(100); - ASSERT_EQ(v.size(), 0); - } +TEST(ChunkedVector, InitEmpty) +{ + auto v = ChunkedVector(100); + ASSERT_EQ(v.size(), 0); +} - TEST(ChunkedVector, GrowsCorrectly) { - auto v = ChunkedVector(100); - for (auto i = 1; i < 20; i++) { - v.add(i); - ASSERT_EQ(v.size(), i); - } +TEST(ChunkedVector, GrowsCorrectly) +{ + auto v = ChunkedVector(100); + for (auto i = 1; i < 20; i++) { + v.add(i); + ASSERT_EQ(v.size(), i); } +} - TEST(ChunkedVector, AddAndGet) { - auto v = ChunkedVector(100); - for (auto i = 1; i < 20; i++) { - auto [i2, idx] = v.add(i); - auto & i3 = v[idx]; - ASSERT_EQ(i, i2); - ASSERT_EQ(&i2, &i3); - } +TEST(ChunkedVector, AddAndGet) +{ + auto v = ChunkedVector(100); + for (auto i = 1; i < 20; i++) { + auto [i2, idx] = v.add(i); + auto & i3 = v[idx]; + ASSERT_EQ(i, i2); + ASSERT_EQ(&i2, &i3); } +} - TEST(ChunkedVector, ForEach) { - auto v = ChunkedVector(100); - for (auto i = 1; i < 20; i++) { - v.add(i); - } - int count = 0; - v.forEach([&count](int elt) { - count++; - }); - ASSERT_EQ(count, v.size()); +TEST(ChunkedVector, ForEach) +{ + auto v = ChunkedVector(100); + for (auto i = 1; i < 20; i++) { + v.add(i); } + int count = 0; + v.forEach([&count](int elt) { count++; }); + ASSERT_EQ(count, v.size()); +} - TEST(ChunkedVector, OverflowOK) { - // Similar to the AddAndGet, but intentionnally use a small - // initial ChunkedVector to force it to overflow - auto v = ChunkedVector(2); - for (auto i = 1; i < 20; i++) { - auto [i2, idx] = v.add(i); - auto & i3 = v[idx]; - ASSERT_EQ(i, i2); - ASSERT_EQ(&i2, &i3); - } +TEST(ChunkedVector, OverflowOK) +{ + // Similar to the AddAndGet, but intentionnally use a small + // initial ChunkedVector to force it to overflow + auto v = ChunkedVector(2); + for (auto i = 1; i < 20; i++) { + auto [i2, idx] = v.add(i); + auto & i3 = v[idx]; + ASSERT_EQ(i, i2); + ASSERT_EQ(&i2, &i3); } - } +} diff --git a/src/libutil/tests/closure.cc b/src/libutil/tests/closure.cc index 7597e78073b4..7bd6da982733 100644 --- a/src/libutil/tests/closure.cc +++ b/src/libutil/tests/closure.cc @@ -6,65 +6,63 @@ namespace nix { using namespace std; map> testGraph = { - { "A", { "B", "C", "G" } }, - { "B", { "A" } }, // Loops back to A - { "C", { "F" } }, // Indirect reference - { "D", { "A" } }, // Not reachable, but has backreferences - { "E", {} }, // Just not reachable - { "F", {} }, - { "G", { "G" } }, // Self reference + {"A", {"B", "C", "G"}}, + {"B", {"A"}}, // Loops back to A + {"C", {"F"}}, // Indirect reference + {"D", {"A"}}, // Not reachable, but has backreferences + {"E", {}}, // Just not reachable + {"F", {}}, + {"G", {"G"}}, // Self reference }; -TEST(closure, correctClosure) { +TEST(closure, correctClosure) +{ set aClosure; set expectedClosure = {"A", "B", "C", "F", "G"}; computeClosure( - {"A"}, - aClosure, - [&](const string currentNode, function> &)> processEdges) { + {"A"}, aClosure, + [&](const string currentNode, + function> &)> processEdges) { promise> promisedNodes; promisedNodes.set_value(testGraph[currentNode]); processEdges(promisedNodes); - } - ); + }); ASSERT_EQ(aClosure, expectedClosure); } -TEST(closure, properlyHandlesDirectExceptions) { - struct TestExn {}; +TEST(closure, properlyHandlesDirectExceptions) +{ + struct TestExn { + }; set aClosure; - EXPECT_THROW( - computeClosure( - {"A"}, - aClosure, - [&](const string currentNode, function> &)> processEdges) { - throw TestExn(); - } - ), - TestExn - ); + EXPECT_THROW(computeClosure( + {"A"}, aClosure, + [&](const string currentNode, + function> &)> processEdges) { + throw TestExn(); + }), + TestExn); } -TEST(closure, properlyHandlesExceptionsInPromise) { - struct TestExn {}; +TEST(closure, properlyHandlesExceptionsInPromise) +{ + struct TestExn { + }; set aClosure; - EXPECT_THROW( - computeClosure( - {"A"}, - aClosure, - [&](const string currentNode, function> &)> processEdges) { - promise> promise; - try { - throw TestExn(); - } catch (...) { - promise.set_exception(std::current_exception()); - } - processEdges(promise); - } - ), - TestExn - ); + EXPECT_THROW(computeClosure( + {"A"}, aClosure, + [&](const string currentNode, + function> &)> processEdges) { + promise> promise; + try { + throw TestExn(); + } catch (...) { + promise.set_exception(std::current_exception()); + } + processEdges(promise); + }), + TestExn); } } diff --git a/src/libutil/tests/compression.cc b/src/libutil/tests/compression.cc index bbbf3500fbfe..92770a23aa1c 100644 --- a/src/libutil/tests/compression.cc +++ b/src/libutil/tests/compression.cc @@ -3,94 +3,107 @@ namespace nix { - /* ---------------------------------------------------------------------------- - * compress / decompress - * --------------------------------------------------------------------------*/ - - TEST(compress, compressWithUnknownMethod) { - ASSERT_THROW(compress("invalid-method", "something-to-compress"), UnknownCompressionMethod); - } - - TEST(compress, noneMethodDoesNothingToTheInput) { - auto o = compress("none", "this-is-a-test"); - - ASSERT_EQ(o, "this-is-a-test"); - } - - TEST(decompress, decompressNoneCompressed) { - auto method = "none"; - auto str = "slfja;sljfklsa;jfklsjfkl;sdjfkl;sadjfkl;sdjf;lsdfjsadlf"; - auto o = decompress(method, str); - - ASSERT_EQ(o, str); - } - - TEST(decompress, decompressEmptyCompressed) { - // Empty-method decompression used e.g. by S3 store - // (Content-Encoding == ""). - auto method = ""; - auto str = "slfja;sljfklsa;jfklsjfkl;sdjfkl;sadjfkl;sdjf;lsdfjsadlf"; - auto o = decompress(method, str); - - ASSERT_EQ(o, str); - } - - TEST(decompress, decompressXzCompressed) { - auto method = "xz"; - auto str = "slfja;sljfklsa;jfklsjfkl;sdjfkl;sadjfkl;sdjf;lsdfjsadlf"; - auto o = decompress(method, compress(method, str)); - - ASSERT_EQ(o, str); - } - - TEST(decompress, decompressBzip2Compressed) { - auto method = "bzip2"; - auto str = "slfja;sljfklsa;jfklsjfkl;sdjfkl;sadjfkl;sdjf;lsdfjsadlf"; - auto o = decompress(method, compress(method, str)); - - ASSERT_EQ(o, str); - } - - TEST(decompress, decompressBrCompressed) { - auto method = "br"; - auto str = "slfja;sljfklsa;jfklsjfkl;sdjfkl;sadjfkl;sdjf;lsdfjsadlf"; - auto o = decompress(method, compress(method, str)); - - ASSERT_EQ(o, str); - } - - TEST(decompress, decompressInvalidInputThrowsCompressionError) { - auto method = "bzip2"; - auto str = "this is a string that does not qualify as valid bzip2 data"; - - ASSERT_THROW(decompress(method, str), CompressionError); - } - - /* ---------------------------------------------------------------------------- - * compression sinks - * --------------------------------------------------------------------------*/ - - TEST(makeCompressionSink, noneSinkDoesNothingToInput) { - StringSink strSink; - auto inputString = "slfja;sljfklsa;jfklsjfkl;sdjfkl;sadjfkl;sdjf;lsdfjsadlf"; - auto sink = makeCompressionSink("none", strSink); - (*sink)(inputString); - sink->finish(); - - ASSERT_STREQ(strSink.s.c_str(), inputString); - } - - TEST(makeCompressionSink, compressAndDecompress) { - StringSink strSink; - auto inputString = "slfja;sljfklsa;jfklsjfkl;sdjfkl;sadjfkl;sdjf;lsdfjsadlf"; - auto decompressionSink = makeDecompressionSink("bzip2", strSink); - auto sink = makeCompressionSink("bzip2", *decompressionSink); - - (*sink)(inputString); - sink->finish(); - decompressionSink->finish(); - - ASSERT_STREQ(strSink.s.c_str(), inputString); - } +/* ---------------------------------------------------------------------------- + * compress / decompress + * --------------------------------------------------------------------------*/ + +TEST(compress, compressWithUnknownMethod) +{ + ASSERT_THROW(compress("invalid-method", "something-to-compress"), + UnknownCompressionMethod); +} + +TEST(compress, noneMethodDoesNothingToTheInput) +{ + auto o = compress("none", "this-is-a-test"); + + ASSERT_EQ(o, "this-is-a-test"); +} + +TEST(decompress, decompressNoneCompressed) +{ + auto method = "none"; + auto str = "slfja;sljfklsa;jfklsjfkl;sdjfkl;sadjfkl;sdjf;lsdfjsadlf"; + auto o = decompress(method, str); + + ASSERT_EQ(o, str); +} + +TEST(decompress, decompressEmptyCompressed) +{ + // Empty-method decompression used e.g. by S3 store + // (Content-Encoding == ""). + auto method = ""; + auto str = "slfja;sljfklsa;jfklsjfkl;sdjfkl;sadjfkl;sdjf;lsdfjsadlf"; + auto o = decompress(method, str); + + ASSERT_EQ(o, str); +} + +TEST(decompress, decompressXzCompressed) +{ + auto method = "xz"; + auto str = "slfja;sljfklsa;jfklsjfkl;sdjfkl;sadjfkl;sdjf;lsdfjsadlf"; + auto o = decompress(method, compress(method, str)); + + ASSERT_EQ(o, str); +} + +TEST(decompress, decompressBzip2Compressed) +{ + auto method = "bzip2"; + auto str = "slfja;sljfklsa;jfklsjfkl;sdjfkl;sadjfkl;sdjf;lsdfjsadlf"; + auto o = decompress(method, compress(method, str)); + + ASSERT_EQ(o, str); +} + +TEST(decompress, decompressBrCompressed) +{ + auto method = "br"; + auto str = "slfja;sljfklsa;jfklsjfkl;sdjfkl;sadjfkl;sdjf;lsdfjsadlf"; + auto o = decompress(method, compress(method, str)); + + ASSERT_EQ(o, str); +} + +TEST(decompress, decompressInvalidInputThrowsCompressionError) +{ + auto method = "bzip2"; + auto str = "this is a string that does not qualify as valid bzip2 data"; + + ASSERT_THROW(decompress(method, str), CompressionError); +} + +/* ---------------------------------------------------------------------------- + * compression sinks + * --------------------------------------------------------------------------*/ + +TEST(makeCompressionSink, noneSinkDoesNothingToInput) +{ + StringSink strSink; + auto inputString = + "slfja;sljfklsa;jfklsjfkl;sdjfkl;sadjfkl;sdjf;lsdfjsadlf"; + auto sink = makeCompressionSink("none", strSink); + (*sink)(inputString); + sink->finish(); + + ASSERT_STREQ(strSink.s.c_str(), inputString); +} + +TEST(makeCompressionSink, compressAndDecompress) +{ + StringSink strSink; + auto inputString = + "slfja;sljfklsa;jfklsjfkl;sdjfkl;sadjfkl;sdjf;lsdfjsadlf"; + auto decompressionSink = makeDecompressionSink("bzip2", strSink); + auto sink = makeCompressionSink("bzip2", *decompressionSink); + + (*sink)(inputString); + sink->finish(); + decompressionSink->finish(); + + ASSERT_STREQ(strSink.s.c_str(), inputString); +} } diff --git a/src/libutil/tests/config.cc b/src/libutil/tests/config.cc index 8be6730dd8df..03cbea110df8 100644 --- a/src/libutil/tests/config.cc +++ b/src/libutil/tests/config.cc @@ -7,245 +7,272 @@ namespace nix { - /* ---------------------------------------------------------------------------- - * Config - * --------------------------------------------------------------------------*/ +/* ---------------------------------------------------------------------------- + * Config + * --------------------------------------------------------------------------*/ + +TEST(Config, setUndefinedSetting) +{ + Config config; + ASSERT_EQ(config.set("undefined-key", "value"), false); +} - TEST(Config, setUndefinedSetting) { - Config config; - ASSERT_EQ(config.set("undefined-key", "value"), false); - } +TEST(Config, setDefinedSetting) +{ + Config config; + std::string value; + Setting foo{&config, value, "name-of-the-setting", + "description"}; + ASSERT_EQ(config.set("name-of-the-setting", "value"), true); +} - TEST(Config, setDefinedSetting) { - Config config; - std::string value; - Setting foo{&config, value, "name-of-the-setting", "description"}; - ASSERT_EQ(config.set("name-of-the-setting", "value"), true); - } +TEST(Config, getDefinedSetting) +{ + Config config; + std::string value; + std::map settings; + Setting foo{&config, value, "name-of-the-setting", + "description"}; + + config.getSettings(settings, /* overriddenOnly = */ false); + const auto iter = settings.find("name-of-the-setting"); + ASSERT_NE(iter, settings.end()); + ASSERT_EQ(iter->second.value, ""); + ASSERT_EQ(iter->second.description, "description\n"); +} - TEST(Config, getDefinedSetting) { - Config config; - std::string value; - std::map settings; - Setting foo{&config, value, "name-of-the-setting", "description"}; +TEST(Config, getDefinedOverriddenSettingNotSet) +{ + Config config; + std::string value; + std::map settings; + Setting foo{&config, value, "name-of-the-setting", + "description"}; + + config.getSettings(settings, /* overriddenOnly = */ true); + const auto e = settings.find("name-of-the-setting"); + ASSERT_EQ(e, settings.end()); +} - config.getSettings(settings, /* overriddenOnly = */ false); - const auto iter = settings.find("name-of-the-setting"); - ASSERT_NE(iter, settings.end()); - ASSERT_EQ(iter->second.value, ""); - ASSERT_EQ(iter->second.description, "description\n"); - } +TEST(Config, getDefinedSettingSet1) +{ + Config config; + std::string value; + std::map settings; + Setting setting{&config, value, "name-of-the-setting", + "description"}; + + setting.assign("value"); + + config.getSettings(settings, /* overriddenOnly = */ false); + const auto iter = settings.find("name-of-the-setting"); + ASSERT_NE(iter, settings.end()); + ASSERT_EQ(iter->second.value, "value"); + ASSERT_EQ(iter->second.description, "description\n"); +} - TEST(Config, getDefinedOverriddenSettingNotSet) { - Config config; - std::string value; - std::map settings; - Setting foo{&config, value, "name-of-the-setting", "description"}; +TEST(Config, getDefinedSettingSet2) +{ + Config config; + std::map settings; + Setting setting{&config, "", "name-of-the-setting", + "description"}; - config.getSettings(settings, /* overriddenOnly = */ true); - const auto e = settings.find("name-of-the-setting"); - ASSERT_EQ(e, settings.end()); - } + ASSERT_TRUE(config.set("name-of-the-setting", "value")); - TEST(Config, getDefinedSettingSet1) { - Config config; - std::string value; - std::map settings; - Setting setting{&config, value, "name-of-the-setting", "description"}; + config.getSettings(settings, /* overriddenOnly = */ false); + const auto e = settings.find("name-of-the-setting"); + ASSERT_NE(e, settings.end()); + ASSERT_EQ(e->second.value, "value"); + ASSERT_EQ(e->second.description, "description\n"); +} - setting.assign("value"); +TEST(Config, addSetting) +{ + class TestSetting : public AbstractSetting { + public: + TestSetting() : AbstractSetting("test", "test", {}) {} + void set(const std::string & value, bool append) override {} + std::string to_string() const override { return {}; } + }; + + Config config; + TestSetting setting; + + ASSERT_FALSE(config.set("test", "value")); + config.addSetting(&setting); + ASSERT_TRUE(config.set("test", "value")); +} - config.getSettings(settings, /* overriddenOnly = */ false); - const auto iter = settings.find("name-of-the-setting"); - ASSERT_NE(iter, settings.end()); - ASSERT_EQ(iter->second.value, "value"); - ASSERT_EQ(iter->second.description, "description\n"); - } +TEST(Config, withInitialValue) +{ + const StringMap initials = { + {"key", "value"}, + }; + Config config(initials); - TEST(Config, getDefinedSettingSet2) { - Config config; + { std::map settings; - Setting setting{&config, "", "name-of-the-setting", "description"}; + config.getSettings(settings, /* overriddenOnly = */ false); + ASSERT_EQ(settings.find("key"), settings.end()); + } - ASSERT_TRUE(config.set("name-of-the-setting", "value")); + Setting setting{&config, "default-value", "key", + "description"}; + { + std::map settings; config.getSettings(settings, /* overriddenOnly = */ false); - const auto e = settings.find("name-of-the-setting"); - ASSERT_NE(e, settings.end()); - ASSERT_EQ(e->second.value, "value"); - ASSERT_EQ(e->second.description, "description\n"); + ASSERT_EQ(settings["key"].value, "value"); } +} - TEST(Config, addSetting) { - class TestSetting : public AbstractSetting { - public: - TestSetting() : AbstractSetting("test", "test", {}) {} - void set(const std::string & value, bool append) override {} - std::string to_string() const override { return {}; } - }; - - Config config; - TestSetting setting; +TEST(Config, resetOverridden) +{ + Config config; + config.resetOverridden(); +} - ASSERT_FALSE(config.set("test", "value")); - config.addSetting(&setting); - ASSERT_TRUE(config.set("test", "value")); - } +TEST(Config, resetOverriddenWithSetting) +{ + Config config; + Setting setting{&config, "", "name-of-the-setting", + "description"}; - TEST(Config, withInitialValue) { - const StringMap initials = { - { "key", "value" }, - }; - Config config(initials); - - { - std::map settings; - config.getSettings(settings, /* overriddenOnly = */ false); - ASSERT_EQ(settings.find("key"), settings.end()); - } - - Setting setting{&config, "default-value", "key", "description"}; - - { - std::map settings; - config.getSettings(settings, /* overriddenOnly = */ false); - ASSERT_EQ(settings["key"].value, "value"); - } - } + { + std::map settings; - TEST(Config, resetOverridden) { - Config config; - config.resetOverridden(); + setting.set("foo"); + ASSERT_EQ(setting.get(), "foo"); + config.getSettings(settings, /* overriddenOnly = */ true); + ASSERT_TRUE(settings.empty()); } - TEST(Config, resetOverriddenWithSetting) { - Config config; - Setting setting{&config, "", "name-of-the-setting", "description"}; - - { - std::map settings; - - setting.set("foo"); - ASSERT_EQ(setting.get(), "foo"); - config.getSettings(settings, /* overriddenOnly = */ true); - ASSERT_TRUE(settings.empty()); - } - - { - std::map settings; - - setting.override("bar"); - ASSERT_TRUE(setting.overridden); - ASSERT_EQ(setting.get(), "bar"); - config.getSettings(settings, /* overriddenOnly = */ true); - ASSERT_FALSE(settings.empty()); - } - - { - std::map settings; - - config.resetOverridden(); - ASSERT_FALSE(setting.overridden); - config.getSettings(settings, /* overriddenOnly = */ true); - ASSERT_TRUE(settings.empty()); - } - } + { + std::map settings; - TEST(Config, toJSONOnEmptyConfig) { - ASSERT_EQ(Config().toJSON().dump(), "{}"); + setting.override("bar"); + ASSERT_TRUE(setting.overridden); + ASSERT_EQ(setting.get(), "bar"); + config.getSettings(settings, /* overriddenOnly = */ true); + ASSERT_FALSE(settings.empty()); } - TEST(Config, toJSONOnNonEmptyConfig) { - Config config; + { std::map settings; - Setting setting{&config, "", "name-of-the-setting", "description"}; - setting.assign("value"); - ASSERT_EQ(config.toJSON().dump(), R"#({"name-of-the-setting":{"aliases":[],"defaultValue":"","description":"description\n","documentDefault":true,"value":"value"}})#"); + config.resetOverridden(); + ASSERT_FALSE(setting.overridden); + config.getSettings(settings, /* overriddenOnly = */ true); + ASSERT_TRUE(settings.empty()); } +} - TEST(Config, setSettingAlias) { - Config config; - Setting setting{&config, "", "some-int", "best number", { "another-int" }}; - ASSERT_TRUE(config.set("some-int", "1")); - ASSERT_EQ(setting.get(), "1"); - ASSERT_TRUE(config.set("another-int", "2")); - ASSERT_EQ(setting.get(), "2"); - ASSERT_TRUE(config.set("some-int", "3")); - ASSERT_EQ(setting.get(), "3"); - } +TEST(Config, toJSONOnEmptyConfig) { ASSERT_EQ(Config().toJSON().dump(), "{}"); } - /* FIXME: The reapplyUnknownSettings method doesn't seem to do anything - * useful (these days). Whenever we add a new setting to Config the - * unknown settings are always considered. In which case is this function - * actually useful? Is there some way to register a Setting without calling - * addSetting? */ - TEST(Config, DISABLED_reapplyUnknownSettings) { - Config config; - ASSERT_FALSE(config.set("name-of-the-setting", "unknownvalue")); - Setting setting{&config, "default", "name-of-the-setting", "description"}; - ASSERT_EQ(setting.get(), "default"); - config.reapplyUnknownSettings(); - ASSERT_EQ(setting.get(), "unknownvalue"); - } +TEST(Config, toJSONOnNonEmptyConfig) +{ + Config config; + std::map settings; + Setting setting{&config, "", "name-of-the-setting", + "description"}; + setting.assign("value"); - TEST(Config, applyConfigEmpty) { - Config config; - std::map settings; - config.applyConfig(""); - config.getSettings(settings); - ASSERT_TRUE(settings.empty()); - } + ASSERT_EQ( + config.toJSON().dump(), + R"#({"name-of-the-setting":{"aliases":[],"defaultValue":"","description":"description\n","documentDefault":true,"value":"value"}})#"); +} - TEST(Config, applyConfigEmptyWithComment) { - Config config; - std::map settings; - config.applyConfig("# just a comment"); - config.getSettings(settings); - ASSERT_TRUE(settings.empty()); - } +TEST(Config, setSettingAlias) +{ + Config config; + Setting setting{ + &config, "", "some-int", "best number", {"another-int"}}; + ASSERT_TRUE(config.set("some-int", "1")); + ASSERT_EQ(setting.get(), "1"); + ASSERT_TRUE(config.set("another-int", "2")); + ASSERT_EQ(setting.get(), "2"); + ASSERT_TRUE(config.set("some-int", "3")); + ASSERT_EQ(setting.get(), "3"); +} - TEST(Config, applyConfigAssignment) { - Config config; - std::map settings; - Setting setting{&config, "", "name-of-the-setting", "description"}; - config.applyConfig( - "name-of-the-setting = value-from-file #useful comment\n" - "# name-of-the-setting = foo\n" - ); - config.getSettings(settings); - ASSERT_FALSE(settings.empty()); - ASSERT_EQ(settings["name-of-the-setting"].value, "value-from-file"); - } +/* FIXME: The reapplyUnknownSettings method doesn't seem to do anything + * useful (these days). Whenever we add a new setting to Config the + * unknown settings are always considered. In which case is this function + * actually useful? Is there some way to register a Setting without calling + * addSetting? */ +TEST(Config, DISABLED_reapplyUnknownSettings) +{ + Config config; + ASSERT_FALSE(config.set("name-of-the-setting", "unknownvalue")); + Setting setting{&config, "default", "name-of-the-setting", + "description"}; + ASSERT_EQ(setting.get(), "default"); + config.reapplyUnknownSettings(); + ASSERT_EQ(setting.get(), "unknownvalue"); +} - TEST(Config, applyConfigWithReassignedSetting) { - Config config; - std::map settings; - Setting setting{&config, "", "name-of-the-setting", "description"}; - config.applyConfig( - "name-of-the-setting = first-value\n" - "name-of-the-setting = second-value\n" - ); - config.getSettings(settings); - ASSERT_FALSE(settings.empty()); - ASSERT_EQ(settings["name-of-the-setting"].value, "second-value"); - } +TEST(Config, applyConfigEmpty) +{ + Config config; + std::map settings; + config.applyConfig(""); + config.getSettings(settings); + ASSERT_TRUE(settings.empty()); +} - TEST(Config, applyConfigFailsOnMissingIncludes) { - Config config; - std::map settings; - Setting setting{&config, "", "name-of-the-setting", "description"}; +TEST(Config, applyConfigEmptyWithComment) +{ + Config config; + std::map settings; + config.applyConfig("# just a comment"); + config.getSettings(settings); + ASSERT_TRUE(settings.empty()); +} - ASSERT_THROW(config.applyConfig( - "name-of-the-setting = value-from-file\n" - "# name-of-the-setting = foo\n" - "include /nix/store/does/not/exist.nix" - ), Error); - } +TEST(Config, applyConfigAssignment) +{ + Config config; + std::map settings; + Setting setting{&config, "", "name-of-the-setting", + "description"}; + config.applyConfig("name-of-the-setting = value-from-file #useful comment\n" + "# name-of-the-setting = foo\n"); + config.getSettings(settings); + ASSERT_FALSE(settings.empty()); + ASSERT_EQ(settings["name-of-the-setting"].value, "value-from-file"); +} - TEST(Config, applyConfigInvalidThrows) { - Config config; - ASSERT_THROW(config.applyConfig("value == key"), UsageError); - ASSERT_THROW(config.applyConfig("value "), UsageError); - } +TEST(Config, applyConfigWithReassignedSetting) +{ + Config config; + std::map settings; + Setting setting{&config, "", "name-of-the-setting", + "description"}; + config.applyConfig("name-of-the-setting = first-value\n" + "name-of-the-setting = second-value\n"); + config.getSettings(settings); + ASSERT_FALSE(settings.empty()); + ASSERT_EQ(settings["name-of-the-setting"].value, "second-value"); +} + +TEST(Config, applyConfigFailsOnMissingIncludes) +{ + Config config; + std::map settings; + Setting setting{&config, "", "name-of-the-setting", + "description"}; + + ASSERT_THROW(config.applyConfig("name-of-the-setting = value-from-file\n" + "# name-of-the-setting = foo\n" + "include /nix/store/does/not/exist.nix"), + Error); +} + +TEST(Config, applyConfigInvalidThrows) +{ + Config config; + ASSERT_THROW(config.applyConfig("value == key"), UsageError); + ASSERT_THROW(config.applyConfig("value "), UsageError); +} } diff --git a/src/libutil/tests/git.cc b/src/libutil/tests/git.cc index 5b5715fc26bb..f335d1abe207 100644 --- a/src/libutil/tests/git.cc +++ b/src/libutil/tests/git.cc @@ -3,31 +3,33 @@ namespace nix { - TEST(GitLsRemote, parseSymrefLineWithReference) { - auto line = "ref: refs/head/main HEAD"; - auto res = git::parseLsRemoteLine(line); - ASSERT_TRUE(res.has_value()); - ASSERT_EQ(res->kind, git::LsRemoteRefLine::Kind::Symbolic); - ASSERT_EQ(res->target, "refs/head/main"); - ASSERT_EQ(res->reference, "HEAD"); - } - - TEST(GitLsRemote, parseSymrefLineWithNoReference) { - auto line = "ref: refs/head/main"; - auto res = git::parseLsRemoteLine(line); - ASSERT_TRUE(res.has_value()); - ASSERT_EQ(res->kind, git::LsRemoteRefLine::Kind::Symbolic); - ASSERT_EQ(res->target, "refs/head/main"); - ASSERT_EQ(res->reference, std::nullopt); - } +TEST(GitLsRemote, parseSymrefLineWithReference) +{ + auto line = "ref: refs/head/main HEAD"; + auto res = git::parseLsRemoteLine(line); + ASSERT_TRUE(res.has_value()); + ASSERT_EQ(res->kind, git::LsRemoteRefLine::Kind::Symbolic); + ASSERT_EQ(res->target, "refs/head/main"); + ASSERT_EQ(res->reference, "HEAD"); +} - TEST(GitLsRemote, parseObjectRefLine) { - auto line = "abc123 refs/head/main"; - auto res = git::parseLsRemoteLine(line); - ASSERT_TRUE(res.has_value()); - ASSERT_EQ(res->kind, git::LsRemoteRefLine::Kind::Object); - ASSERT_EQ(res->target, "abc123"); - ASSERT_EQ(res->reference, "refs/head/main"); - } +TEST(GitLsRemote, parseSymrefLineWithNoReference) +{ + auto line = "ref: refs/head/main"; + auto res = git::parseLsRemoteLine(line); + ASSERT_TRUE(res.has_value()); + ASSERT_EQ(res->kind, git::LsRemoteRefLine::Kind::Symbolic); + ASSERT_EQ(res->target, "refs/head/main"); + ASSERT_EQ(res->reference, std::nullopt); } +TEST(GitLsRemote, parseObjectRefLine) +{ + auto line = "abc123 refs/head/main"; + auto res = git::parseLsRemoteLine(line); + ASSERT_TRUE(res.has_value()); + ASSERT_EQ(res->kind, git::LsRemoteRefLine::Kind::Object); + ASSERT_EQ(res->target, "abc123"); + ASSERT_EQ(res->reference, "refs/head/main"); +} +} diff --git a/src/libutil/tests/hash.cc b/src/libutil/tests/hash.cc index 412c03030347..3d6758464fed 100644 --- a/src/libutil/tests/hash.cc +++ b/src/libutil/tests/hash.cc @@ -3,73 +3,90 @@ namespace nix { - /* ---------------------------------------------------------------------------- - * hashString - * --------------------------------------------------------------------------*/ +/* ---------------------------------------------------------------------------- + * hashString + * --------------------------------------------------------------------------*/ - TEST(hashString, testKnownMD5Hashes1) { - // values taken from: https://tools.ietf.org/html/rfc1321 - auto s1 = ""; - auto hash = hashString(HashType::htMD5, s1); - ASSERT_EQ(hash.to_string(Base::Base16, true), "md5:d41d8cd98f00b204e9800998ecf8427e"); - } +TEST(hashString, testKnownMD5Hashes1) +{ + // values taken from: https://tools.ietf.org/html/rfc1321 + auto s1 = ""; + auto hash = hashString(HashType::htMD5, s1); + ASSERT_EQ(hash.to_string(Base::Base16, true), + "md5:d41d8cd98f00b204e9800998ecf8427e"); +} - TEST(hashString, testKnownMD5Hashes2) { - // values taken from: https://tools.ietf.org/html/rfc1321 - auto s2 = "abc"; - auto hash = hashString(HashType::htMD5, s2); - ASSERT_EQ(hash.to_string(Base::Base16, true), "md5:900150983cd24fb0d6963f7d28e17f72"); - } +TEST(hashString, testKnownMD5Hashes2) +{ + // values taken from: https://tools.ietf.org/html/rfc1321 + auto s2 = "abc"; + auto hash = hashString(HashType::htMD5, s2); + ASSERT_EQ(hash.to_string(Base::Base16, true), + "md5:900150983cd24fb0d6963f7d28e17f72"); +} - TEST(hashString, testKnownSHA1Hashes1) { - // values taken from: https://tools.ietf.org/html/rfc3174 - auto s = "abc"; - auto hash = hashString(HashType::htSHA1, s); - ASSERT_EQ(hash.to_string(Base::Base16, true),"sha1:a9993e364706816aba3e25717850c26c9cd0d89d"); - } +TEST(hashString, testKnownSHA1Hashes1) +{ + // values taken from: https://tools.ietf.org/html/rfc3174 + auto s = "abc"; + auto hash = hashString(HashType::htSHA1, s); + ASSERT_EQ(hash.to_string(Base::Base16, true), + "sha1:a9993e364706816aba3e25717850c26c9cd0d89d"); +} - TEST(hashString, testKnownSHA1Hashes2) { - // values taken from: https://tools.ietf.org/html/rfc3174 - auto s = "abcdbcdecdefdefgefghfghighijhijkijkljklmklmnlmnomnopnopq"; - auto hash = hashString(HashType::htSHA1, s); - ASSERT_EQ(hash.to_string(Base::Base16, true),"sha1:84983e441c3bd26ebaae4aa1f95129e5e54670f1"); - } +TEST(hashString, testKnownSHA1Hashes2) +{ + // values taken from: https://tools.ietf.org/html/rfc3174 + auto s = "abcdbcdecdefdefgefghfghighijhijkijkljklmklmnlmnomnopnopq"; + auto hash = hashString(HashType::htSHA1, s); + ASSERT_EQ(hash.to_string(Base::Base16, true), + "sha1:84983e441c3bd26ebaae4aa1f95129e5e54670f1"); +} - TEST(hashString, testKnownSHA256Hashes1) { - // values taken from: https://tools.ietf.org/html/rfc4634 - auto s = "abc"; +TEST(hashString, testKnownSHA256Hashes1) +{ + // values taken from: https://tools.ietf.org/html/rfc4634 + auto s = "abc"; - auto hash = hashString(HashType::htSHA256, s); - ASSERT_EQ(hash.to_string(Base::Base16, true), - "sha256:ba7816bf8f01cfea414140de5dae2223b00361a396177a9cb410ff61f20015ad"); - } + auto hash = hashString(HashType::htSHA256, s); + ASSERT_EQ( + hash.to_string(Base::Base16, true), + "sha256:" + "ba7816bf8f01cfea414140de5dae2223b00361a396177a9cb410ff61f20015ad"); +} - TEST(hashString, testKnownSHA256Hashes2) { - // values taken from: https://tools.ietf.org/html/rfc4634 - auto s = "abcdbcdecdefdefgefghfghighijhijkijkljklmklmnlmnomnopnopq"; - auto hash = hashString(HashType::htSHA256, s); - ASSERT_EQ(hash.to_string(Base::Base16, true), - "sha256:248d6a61d20638b8e5c026930c3e6039a33ce45964ff2167f6ecedd419db06c1"); - } +TEST(hashString, testKnownSHA256Hashes2) +{ + // values taken from: https://tools.ietf.org/html/rfc4634 + auto s = "abcdbcdecdefdefgefghfghighijhijkijkljklmklmnlmnomnopnopq"; + auto hash = hashString(HashType::htSHA256, s); + ASSERT_EQ( + hash.to_string(Base::Base16, true), + "sha256:" + "248d6a61d20638b8e5c026930c3e6039a33ce45964ff2167f6ecedd419db06c1"); +} - TEST(hashString, testKnownSHA512Hashes1) { - // values taken from: https://tools.ietf.org/html/rfc4634 - auto s = "abc"; - auto hash = hashString(HashType::htSHA512, s); - ASSERT_EQ(hash.to_string(Base::Base16, true), - "sha512:ddaf35a193617abacc417349ae20413112e6fa4e89a9" - "7ea20a9eeee64b55d39a2192992a274fc1a836ba3c23a3feebbd" - "454d4423643ce80e2a9ac94fa54ca49f"); - } +TEST(hashString, testKnownSHA512Hashes1) +{ + // values taken from: https://tools.ietf.org/html/rfc4634 + auto s = "abc"; + auto hash = hashString(HashType::htSHA512, s); + ASSERT_EQ(hash.to_string(Base::Base16, true), + "sha512:ddaf35a193617abacc417349ae20413112e6fa4e89a9" + "7ea20a9eeee64b55d39a2192992a274fc1a836ba3c23a3feebbd" + "454d4423643ce80e2a9ac94fa54ca49f"); +} - TEST(hashString, testKnownSHA512Hashes2) { - // values taken from: https://tools.ietf.org/html/rfc4634 - auto s = "abcdefghbcdefghicdefghijdefghijkefghijklfghijklmghijklmnhijklmnoijklmnopjklmnopqklmnopqrlmnopqrsmnopqrstnopqrstu"; +TEST(hashString, testKnownSHA512Hashes2) +{ + // values taken from: https://tools.ietf.org/html/rfc4634 + auto s = "abcdefghbcdefghicdefghijdefghijkefghijklfghijklmghijklmnhijklmnoi" + "jklmnopjklmnopqklmnopqrlmnopqrsmnopqrstnopqrstu"; - auto hash = hashString(HashType::htSHA512, s); - ASSERT_EQ(hash.to_string(Base::Base16, true), - "sha512:8e959b75dae313da8cf4f72814fc143f8f7779c6eb9f7fa1" - "7299aeadb6889018501d289e4900f7e4331b99dec4b5433a" - "c7d329eeb6dd26545e96e55b874be909"); - } + auto hash = hashString(HashType::htSHA512, s); + ASSERT_EQ(hash.to_string(Base::Base16, true), + "sha512:8e959b75dae313da8cf4f72814fc143f8f7779c6eb9f7fa1" + "7299aeadb6889018501d289e4900f7e4331b99dec4b5433a" + "c7d329eeb6dd26545e96e55b874be909"); +} } diff --git a/src/libutil/tests/hilite.cc b/src/libutil/tests/hilite.cc index 1ff5980d5831..8cea8991f185 100644 --- a/src/libutil/tests/hilite.cc +++ b/src/libutil/tests/hilite.cc @@ -3,64 +3,72 @@ #include namespace nix { -/* ----------- tests for fmt.hh -------------------------------------------------*/ +/* ----------- tests for fmt.hh + * -------------------------------------------------*/ - TEST(hiliteMatches, noHighlight) { - ASSERT_STREQ(hiliteMatches("Hello, world!", std::vector(), "(", ")").c_str(), "Hello, world!"); - } +TEST(hiliteMatches, noHighlight) +{ + ASSERT_STREQ( + hiliteMatches("Hello, world!", std::vector(), "(", ")") + .c_str(), + "Hello, world!"); +} - TEST(hiliteMatches, simpleHighlight) { - std::string str = "Hello, world!"; - std::regex re = std::regex("world"); - auto matches = std::vector(std::sregex_iterator(str.begin(), str.end(), re), std::sregex_iterator()); - ASSERT_STREQ( - hiliteMatches(str, matches, "(", ")").c_str(), - "Hello, (world)!" - ); - } +TEST(hiliteMatches, simpleHighlight) +{ + std::string str = "Hello, world!"; + std::regex re = std::regex("world"); + auto matches = std::vector(std::sregex_iterator(str.begin(), str.end(), re), + std::sregex_iterator()); + ASSERT_STREQ(hiliteMatches(str, matches, "(", ")").c_str(), + "Hello, (world)!"); +} - TEST(hiliteMatches, multipleMatches) { - std::string str = "Hello, world, world, world, world, world, world, Hello!"; - std::regex re = std::regex("world"); - auto matches = std::vector(std::sregex_iterator(str.begin(), str.end(), re), std::sregex_iterator()); - ASSERT_STREQ( - hiliteMatches(str, matches, "(", ")").c_str(), - "Hello, (world), (world), (world), (world), (world), (world), Hello!" - ); - } +TEST(hiliteMatches, multipleMatches) +{ + std::string str = "Hello, world, world, world, world, world, world, Hello!"; + std::regex re = std::regex("world"); + auto matches = std::vector(std::sregex_iterator(str.begin(), str.end(), re), + std::sregex_iterator()); + ASSERT_STREQ( + hiliteMatches(str, matches, "(", ")").c_str(), + "Hello, (world), (world), (world), (world), (world), (world), Hello!"); +} - TEST(hiliteMatches, overlappingMatches) { - std::string str = "world, Hello, world, Hello, world, Hello, world, Hello, world!"; - std::regex re = std::regex("Hello, world"); - std::regex re2 = std::regex("world, Hello"); - auto v = std::vector(std::sregex_iterator(str.begin(), str.end(), re), std::sregex_iterator()); - for(auto it = std::sregex_iterator(str.begin(), str.end(), re2); it != std::sregex_iterator(); ++it) { - v.push_back(*it); - } - ASSERT_STREQ( - hiliteMatches(str, v, "(", ")").c_str(), - "(world, Hello, world, Hello, world, Hello, world, Hello, world)!" - ); +TEST(hiliteMatches, overlappingMatches) +{ + std::string str = + "world, Hello, world, Hello, world, Hello, world, Hello, world!"; + std::regex re = std::regex("Hello, world"); + std::regex re2 = std::regex("world, Hello"); + auto v = std::vector(std::sregex_iterator(str.begin(), str.end(), re), + std::sregex_iterator()); + for (auto it = std::sregex_iterator(str.begin(), str.end(), re2); + it != std::sregex_iterator(); ++it) { + v.push_back(*it); } + ASSERT_STREQ( + hiliteMatches(str, v, "(", ")").c_str(), + "(world, Hello, world, Hello, world, Hello, world, Hello, world)!"); +} - TEST(hiliteMatches, complexOverlappingMatches) { - std::string str = "legacyPackages.x86_64-linux.git-crypt"; - std::vector regexes = { - std::regex("t-cry"), - std::regex("ux\\.git-cry"), - std::regex("git-c"), - std::regex("pt"), - }; - std::vector matches; - for(auto regex : regexes) - { - for(auto it = std::sregex_iterator(str.begin(), str.end(), regex); it != std::sregex_iterator(); ++it) { - matches.push_back(*it); - } +TEST(hiliteMatches, complexOverlappingMatches) +{ + std::string str = "legacyPackages.x86_64-linux.git-crypt"; + std::vector regexes = { + std::regex("t-cry"), + std::regex("ux\\.git-cry"), + std::regex("git-c"), + std::regex("pt"), + }; + std::vector matches; + for (auto regex : regexes) { + for (auto it = std::sregex_iterator(str.begin(), str.end(), regex); + it != std::sregex_iterator(); ++it) { + matches.push_back(*it); } - ASSERT_STREQ( - hiliteMatches(str, matches, "(", ")").c_str(), - "legacyPackages.x86_64-lin(ux.git-crypt)" - ); } + ASSERT_STREQ(hiliteMatches(str, matches, "(", ")").c_str(), + "legacyPackages.x86_64-lin(ux.git-crypt)"); +} } diff --git a/src/libutil/tests/json.cc b/src/libutil/tests/json.cc index dea73f53a91b..90e053e3d554 100644 --- a/src/libutil/tests/json.cc +++ b/src/libutil/tests/json.cc @@ -4,190 +4,208 @@ namespace nix { - /* ---------------------------------------------------------------------------- - * toJSON - * --------------------------------------------------------------------------*/ +/* ---------------------------------------------------------------------------- + * toJSON + * --------------------------------------------------------------------------*/ - TEST(toJSON, quotesCharPtr) { - const char* input = "test"; - std::stringstream out; - toJSON(out, input); +TEST(toJSON, quotesCharPtr) +{ + const char * input = "test"; + std::stringstream out; + toJSON(out, input); - ASSERT_EQ(out.str(), "\"test\""); - } + ASSERT_EQ(out.str(), "\"test\""); +} - TEST(toJSON, quotesStdString) { - std::string input = "test"; - std::stringstream out; - toJSON(out, input); +TEST(toJSON, quotesStdString) +{ + std::string input = "test"; + std::stringstream out; + toJSON(out, input); - ASSERT_EQ(out.str(), "\"test\""); - } - - TEST(toJSON, convertsNullptrtoNull) { - auto input = nullptr; - std::stringstream out; - toJSON(out, input); + ASSERT_EQ(out.str(), "\"test\""); +} - ASSERT_EQ(out.str(), "null"); - } +TEST(toJSON, convertsNullptrtoNull) +{ + auto input = nullptr; + std::stringstream out; + toJSON(out, input); - TEST(toJSON, convertsNullToNull) { - const char* input = 0; - std::stringstream out; - toJSON(out, input); + ASSERT_EQ(out.str(), "null"); +} - ASSERT_EQ(out.str(), "null"); - } +TEST(toJSON, convertsNullToNull) +{ + const char * input = 0; + std::stringstream out; + toJSON(out, input); + ASSERT_EQ(out.str(), "null"); +} - TEST(toJSON, convertsFloat) { - auto input = 1.024f; - std::stringstream out; - toJSON(out, input); +TEST(toJSON, convertsFloat) +{ + auto input = 1.024f; + std::stringstream out; + toJSON(out, input); - ASSERT_EQ(out.str(), "1.024"); - } + ASSERT_EQ(out.str(), "1.024"); +} - TEST(toJSON, convertsDouble) { - const double input = 1.024; - std::stringstream out; - toJSON(out, input); +TEST(toJSON, convertsDouble) +{ + const double input = 1.024; + std::stringstream out; + toJSON(out, input); - ASSERT_EQ(out.str(), "1.024"); - } + ASSERT_EQ(out.str(), "1.024"); +} - TEST(toJSON, convertsBool) { - auto input = false; - std::stringstream out; - toJSON(out, input); +TEST(toJSON, convertsBool) +{ + auto input = false; + std::stringstream out; + toJSON(out, input); - ASSERT_EQ(out.str(), "false"); - } + ASSERT_EQ(out.str(), "false"); +} - TEST(toJSON, quotesTab) { - std::stringstream out; - toJSON(out, "\t"); +TEST(toJSON, quotesTab) +{ + std::stringstream out; + toJSON(out, "\t"); - ASSERT_EQ(out.str(), "\"\\t\""); - } + ASSERT_EQ(out.str(), "\"\\t\""); +} - TEST(toJSON, quotesNewline) { - std::stringstream out; - toJSON(out, "\n"); +TEST(toJSON, quotesNewline) +{ + std::stringstream out; + toJSON(out, "\n"); - ASSERT_EQ(out.str(), "\"\\n\""); - } + ASSERT_EQ(out.str(), "\"\\n\""); +} - TEST(toJSON, quotesCreturn) { - std::stringstream out; - toJSON(out, "\r"); +TEST(toJSON, quotesCreturn) +{ + std::stringstream out; + toJSON(out, "\r"); - ASSERT_EQ(out.str(), "\"\\r\""); - } + ASSERT_EQ(out.str(), "\"\\r\""); +} - TEST(toJSON, quotesCreturnNewLine) { - std::stringstream out; - toJSON(out, "\r\n"); +TEST(toJSON, quotesCreturnNewLine) +{ + std::stringstream out; + toJSON(out, "\r\n"); - ASSERT_EQ(out.str(), "\"\\r\\n\""); - } + ASSERT_EQ(out.str(), "\"\\r\\n\""); +} - TEST(toJSON, quotesDoublequotes) { - std::stringstream out; - toJSON(out, "\""); +TEST(toJSON, quotesDoublequotes) +{ + std::stringstream out; + toJSON(out, "\""); - ASSERT_EQ(out.str(), "\"\\\"\""); - } + ASSERT_EQ(out.str(), "\"\\\"\""); +} - TEST(toJSON, substringEscape) { - std::stringstream out; - const char *s = "foo\t"; - toJSON(out, s+3, s + strlen(s)); +TEST(toJSON, substringEscape) +{ + std::stringstream out; + const char * s = "foo\t"; + toJSON(out, s + 3, s + strlen(s)); - ASSERT_EQ(out.str(), "\"\\t\""); - } + ASSERT_EQ(out.str(), "\"\\t\""); +} - /* ---------------------------------------------------------------------------- - * JSONObject - * --------------------------------------------------------------------------*/ +/* ---------------------------------------------------------------------------- + * JSONObject + * --------------------------------------------------------------------------*/ - TEST(JSONObject, emptyObject) { - std::stringstream out; - { - JSONObject t(out); - } - ASSERT_EQ(out.str(), "{}"); +TEST(JSONObject, emptyObject) +{ + std::stringstream out; + { + JSONObject t(out); } + ASSERT_EQ(out.str(), "{}"); +} - TEST(JSONObject, objectWithList) { - std::stringstream out; - { - JSONObject t(out); - auto l = t.list("list"); - l.elem("element"); - } - ASSERT_EQ(out.str(), R"#({"list":["element"]})#"); +TEST(JSONObject, objectWithList) +{ + std::stringstream out; + { + JSONObject t(out); + auto l = t.list("list"); + l.elem("element"); } + ASSERT_EQ(out.str(), R"#({"list":["element"]})#"); +} - TEST(JSONObject, objectWithListIndent) { - std::stringstream out; - { - JSONObject t(out, true); - auto l = t.list("list"); - l.elem("element"); - } - ASSERT_EQ(out.str(), -R"#({ +TEST(JSONObject, objectWithListIndent) +{ + std::stringstream out; + { + JSONObject t(out, true); + auto l = t.list("list"); + l.elem("element"); + } + ASSERT_EQ(out.str(), + R"#({ "list": [ "element" ] })#"); - } - - TEST(JSONObject, objectWithPlaceholderAndList) { - std::stringstream out; - { - JSONObject t(out); - auto l = t.placeholder("list"); - l.list().elem("element"); - } +} - ASSERT_EQ(out.str(), R"#({"list":["element"]})#"); +TEST(JSONObject, objectWithPlaceholderAndList) +{ + std::stringstream out; + { + JSONObject t(out); + auto l = t.placeholder("list"); + l.list().elem("element"); } - TEST(JSONObject, objectWithPlaceholderAndObject) { - std::stringstream out; - { - JSONObject t(out); - auto l = t.placeholder("object"); - l.object().attr("key", "value"); - } + ASSERT_EQ(out.str(), R"#({"list":["element"]})#"); +} - ASSERT_EQ(out.str(), R"#({"object":{"key":"value"}})#"); +TEST(JSONObject, objectWithPlaceholderAndObject) +{ + std::stringstream out; + { + JSONObject t(out); + auto l = t.placeholder("object"); + l.object().attr("key", "value"); } - /* ---------------------------------------------------------------------------- - * JSONList - * --------------------------------------------------------------------------*/ + ASSERT_EQ(out.str(), R"#({"object":{"key":"value"}})#"); +} - TEST(JSONList, empty) { - std::stringstream out; - { - JSONList l(out); - } - ASSERT_EQ(out.str(), R"#([])#"); - } +/* ---------------------------------------------------------------------------- + * JSONList + * --------------------------------------------------------------------------*/ - TEST(JSONList, withElements) { - std::stringstream out; - { - JSONList l(out); - l.elem("one"); - l.object(); - l.placeholder().write("three"); - } - ASSERT_EQ(out.str(), R"#(["one",{},"three"])#"); +TEST(JSONList, empty) +{ + std::stringstream out; + { + JSONList l(out); } + ASSERT_EQ(out.str(), R"#([])#"); } +TEST(JSONList, withElements) +{ + std::stringstream out; + { + JSONList l(out); + l.elem("one"); + l.object(); + l.placeholder().write("three"); + } + ASSERT_EQ(out.str(), R"#(["one",{},"three"])#"); +} +} diff --git a/src/libutil/tests/lru-cache.cc b/src/libutil/tests/lru-cache.cc index 091d3d5ede19..d3c461dbf06f 100644 --- a/src/libutil/tests/lru-cache.cc +++ b/src/libutil/tests/lru-cache.cc @@ -3,128 +3,141 @@ namespace nix { - /* ---------------------------------------------------------------------------- - * size - * --------------------------------------------------------------------------*/ - - TEST(LRUCache, sizeOfEmptyCacheIsZero) { - LRUCache c(10); - ASSERT_EQ(c.size(), 0); - } - - TEST(LRUCache, sizeOfSingleElementCacheIsOne) { - LRUCache c(10); - c.upsert("foo", "bar"); - ASSERT_EQ(c.size(), 1); - } - - /* ---------------------------------------------------------------------------- - * upsert / get - * --------------------------------------------------------------------------*/ - - TEST(LRUCache, getFromEmptyCache) { - LRUCache c(10); - auto val = c.get("x"); - ASSERT_EQ(val.has_value(), false); - } - - TEST(LRUCache, getExistingValue) { - LRUCache c(10); - c.upsert("foo", "bar"); - auto val = c.get("foo"); - ASSERT_EQ(val, "bar"); - } - - TEST(LRUCache, getNonExistingValueFromNonEmptyCache) { - LRUCache c(10); - c.upsert("foo", "bar"); - auto val = c.get("another"); - ASSERT_EQ(val.has_value(), false); - } - - TEST(LRUCache, upsertOnZeroCapacityCache) { - LRUCache c(0); - c.upsert("foo", "bar"); - auto val = c.get("foo"); - ASSERT_EQ(val.has_value(), false); - } - - TEST(LRUCache, updateExistingValue) { - LRUCache c(1); - c.upsert("foo", "bar"); - - auto val = c.get("foo"); - ASSERT_EQ(val.value_or("error"), "bar"); - ASSERT_EQ(c.size(), 1); - - c.upsert("foo", "changed"); - val = c.get("foo"); - ASSERT_EQ(val.value_or("error"), "changed"); - ASSERT_EQ(c.size(), 1); - } - - TEST(LRUCache, overwriteOldestWhenCapacityIsReached) { - LRUCache c(3); - c.upsert("one", "eins"); - c.upsert("two", "zwei"); - c.upsert("three", "drei"); - - ASSERT_EQ(c.size(), 3); - ASSERT_EQ(c.get("one").value_or("error"), "eins"); - - // exceed capacity - c.upsert("another", "whatever"); - - ASSERT_EQ(c.size(), 3); - // Retrieving "one" makes it the most recent element thus - // two will be the oldest one and thus replaced. - ASSERT_EQ(c.get("two").has_value(), false); - ASSERT_EQ(c.get("another").value(), "whatever"); - } - - /* ---------------------------------------------------------------------------- - * clear - * --------------------------------------------------------------------------*/ - - TEST(LRUCache, clearEmptyCache) { - LRUCache c(10); - c.clear(); - ASSERT_EQ(c.size(), 0); - } - - TEST(LRUCache, clearNonEmptyCache) { - LRUCache c(10); - c.upsert("one", "eins"); - c.upsert("two", "zwei"); - c.upsert("three", "drei"); - ASSERT_EQ(c.size(), 3); - c.clear(); - ASSERT_EQ(c.size(), 0); - } - - /* ---------------------------------------------------------------------------- - * erase - * --------------------------------------------------------------------------*/ - - TEST(LRUCache, eraseFromEmptyCache) { - LRUCache c(10); - ASSERT_EQ(c.erase("foo"), false); - ASSERT_EQ(c.size(), 0); - } - - TEST(LRUCache, eraseMissingFromNonEmptyCache) { - LRUCache c(10); - c.upsert("one", "eins"); - ASSERT_EQ(c.erase("foo"), false); - ASSERT_EQ(c.size(), 1); - ASSERT_EQ(c.get("one").value_or("error"), "eins"); - } - - TEST(LRUCache, eraseFromNonEmptyCache) { - LRUCache c(10); - c.upsert("one", "eins"); - ASSERT_EQ(c.erase("one"), true); - ASSERT_EQ(c.size(), 0); - ASSERT_EQ(c.get("one").value_or("empty"), "empty"); - } +/* ---------------------------------------------------------------------------- + * size + * --------------------------------------------------------------------------*/ + +TEST(LRUCache, sizeOfEmptyCacheIsZero) +{ + LRUCache c(10); + ASSERT_EQ(c.size(), 0); +} + +TEST(LRUCache, sizeOfSingleElementCacheIsOne) +{ + LRUCache c(10); + c.upsert("foo", "bar"); + ASSERT_EQ(c.size(), 1); +} + +/* ---------------------------------------------------------------------------- + * upsert / get + * --------------------------------------------------------------------------*/ + +TEST(LRUCache, getFromEmptyCache) +{ + LRUCache c(10); + auto val = c.get("x"); + ASSERT_EQ(val.has_value(), false); +} + +TEST(LRUCache, getExistingValue) +{ + LRUCache c(10); + c.upsert("foo", "bar"); + auto val = c.get("foo"); + ASSERT_EQ(val, "bar"); +} + +TEST(LRUCache, getNonExistingValueFromNonEmptyCache) +{ + LRUCache c(10); + c.upsert("foo", "bar"); + auto val = c.get("another"); + ASSERT_EQ(val.has_value(), false); +} + +TEST(LRUCache, upsertOnZeroCapacityCache) +{ + LRUCache c(0); + c.upsert("foo", "bar"); + auto val = c.get("foo"); + ASSERT_EQ(val.has_value(), false); +} + +TEST(LRUCache, updateExistingValue) +{ + LRUCache c(1); + c.upsert("foo", "bar"); + + auto val = c.get("foo"); + ASSERT_EQ(val.value_or("error"), "bar"); + ASSERT_EQ(c.size(), 1); + + c.upsert("foo", "changed"); + val = c.get("foo"); + ASSERT_EQ(val.value_or("error"), "changed"); + ASSERT_EQ(c.size(), 1); +} + +TEST(LRUCache, overwriteOldestWhenCapacityIsReached) +{ + LRUCache c(3); + c.upsert("one", "eins"); + c.upsert("two", "zwei"); + c.upsert("three", "drei"); + + ASSERT_EQ(c.size(), 3); + ASSERT_EQ(c.get("one").value_or("error"), "eins"); + + // exceed capacity + c.upsert("another", "whatever"); + + ASSERT_EQ(c.size(), 3); + // Retrieving "one" makes it the most recent element thus + // two will be the oldest one and thus replaced. + ASSERT_EQ(c.get("two").has_value(), false); + ASSERT_EQ(c.get("another").value(), "whatever"); +} + +/* ---------------------------------------------------------------------------- + * clear + * --------------------------------------------------------------------------*/ + +TEST(LRUCache, clearEmptyCache) +{ + LRUCache c(10); + c.clear(); + ASSERT_EQ(c.size(), 0); +} + +TEST(LRUCache, clearNonEmptyCache) +{ + LRUCache c(10); + c.upsert("one", "eins"); + c.upsert("two", "zwei"); + c.upsert("three", "drei"); + ASSERT_EQ(c.size(), 3); + c.clear(); + ASSERT_EQ(c.size(), 0); +} + +/* ---------------------------------------------------------------------------- + * erase + * --------------------------------------------------------------------------*/ + +TEST(LRUCache, eraseFromEmptyCache) +{ + LRUCache c(10); + ASSERT_EQ(c.erase("foo"), false); + ASSERT_EQ(c.size(), 0); +} + +TEST(LRUCache, eraseMissingFromNonEmptyCache) +{ + LRUCache c(10); + c.upsert("one", "eins"); + ASSERT_EQ(c.erase("foo"), false); + ASSERT_EQ(c.size(), 1); + ASSERT_EQ(c.get("one").value_or("error"), "eins"); +} + +TEST(LRUCache, eraseFromNonEmptyCache) +{ + LRUCache c(10); + c.upsert("one", "eins"); + ASSERT_EQ(c.erase("one"), true); + ASSERT_EQ(c.size(), 0); + ASSERT_EQ(c.get("one").value_or("empty"), "empty"); +} } diff --git a/src/libutil/tests/pool.cc b/src/libutil/tests/pool.cc index 127e42dda2b1..71f8b6517f83 100644 --- a/src/libutil/tests/pool.cc +++ b/src/libutil/tests/pool.cc @@ -3,125 +3,142 @@ namespace nix { - struct TestResource +struct TestResource { + + TestResource() { + static int counter = 0; + num = counter++; + } - TestResource() { - static int counter = 0; - num = counter++; - } + int dummyValue = 1; + bool good = true; + int num; +}; - int dummyValue = 1; - bool good = true; - int num; - }; +/* ---------------------------------------------------------------------------- + * Pool + * --------------------------------------------------------------------------*/ - /* ---------------------------------------------------------------------------- - * Pool - * --------------------------------------------------------------------------*/ +TEST(Pool, freshPoolHasZeroCountAndSpecifiedCapacity) +{ + auto isGood = [](const ref & r) { return r->good; }; + auto createResource = []() { return make_ref(); }; - TEST(Pool, freshPoolHasZeroCountAndSpecifiedCapacity) { - auto isGood = [](const ref & r) { return r->good; }; - auto createResource = []() { return make_ref(); }; + Pool pool = + Pool((size_t) 1, createResource, isGood); - Pool pool = Pool((size_t)1, createResource, isGood); + ASSERT_EQ(pool.count(), 0); + ASSERT_EQ(pool.capacity(), 1); +} - ASSERT_EQ(pool.count(), 0); - ASSERT_EQ(pool.capacity(), 1); - } +TEST(Pool, freshPoolCanGetAResource) +{ + auto isGood = [](const ref & r) { return r->good; }; + auto createResource = []() { return make_ref(); }; - TEST(Pool, freshPoolCanGetAResource) { - auto isGood = [](const ref & r) { return r->good; }; - auto createResource = []() { return make_ref(); }; + Pool pool = + Pool((size_t) 1, createResource, isGood); + ASSERT_EQ(pool.count(), 0); - Pool pool = Pool((size_t)1, createResource, isGood); - ASSERT_EQ(pool.count(), 0); + TestResource r = *(pool.get()); - TestResource r = *(pool.get()); + ASSERT_EQ(pool.count(), 1); + ASSERT_EQ(pool.capacity(), 1); + ASSERT_EQ(r.dummyValue, 1); + ASSERT_EQ(r.good, true); +} - ASSERT_EQ(pool.count(), 1); - ASSERT_EQ(pool.capacity(), 1); - ASSERT_EQ(r.dummyValue, 1); - ASSERT_EQ(r.good, true); - } +TEST(Pool, capacityCanBeIncremented) +{ + auto isGood = [](const ref & r) { return r->good; }; + auto createResource = []() { return make_ref(); }; - TEST(Pool, capacityCanBeIncremented) { - auto isGood = [](const ref & r) { return r->good; }; - auto createResource = []() { return make_ref(); }; + Pool pool = + Pool((size_t) 1, createResource, isGood); + ASSERT_EQ(pool.capacity(), 1); + pool.incCapacity(); + ASSERT_EQ(pool.capacity(), 2); +} - Pool pool = Pool((size_t)1, createResource, isGood); - ASSERT_EQ(pool.capacity(), 1); - pool.incCapacity(); - ASSERT_EQ(pool.capacity(), 2); - } +TEST(Pool, capacityCanBeDecremented) +{ + auto isGood = [](const ref & r) { return r->good; }; + auto createResource = []() { return make_ref(); }; + + Pool pool = + Pool((size_t) 1, createResource, isGood); + ASSERT_EQ(pool.capacity(), 1); + pool.decCapacity(); + ASSERT_EQ(pool.capacity(), 0); +} + +TEST(Pool, flushBadDropsOutOfScopeResources) +{ + auto isGood = [](const ref & r) { return false; }; + auto createResource = []() { return make_ref(); }; - TEST(Pool, capacityCanBeDecremented) { - auto isGood = [](const ref & r) { return r->good; }; - auto createResource = []() { return make_ref(); }; + Pool pool = + Pool((size_t) 1, createResource, isGood); - Pool pool = Pool((size_t)1, createResource, isGood); - ASSERT_EQ(pool.capacity(), 1); - pool.decCapacity(); - ASSERT_EQ(pool.capacity(), 0); + { + auto _r = pool.get(); + ASSERT_EQ(pool.count(), 1); } - TEST(Pool, flushBadDropsOutOfScopeResources) { - auto isGood = [](const ref & r) { return false; }; - auto createResource = []() { return make_ref(); }; + pool.flushBad(); + ASSERT_EQ(pool.count(), 0); +} - Pool pool = Pool((size_t)1, createResource, isGood); +// Test that the resources we allocate are being reused when they are still +// good. +TEST(Pool, reuseResource) +{ + auto isGood = [](const ref & r) { return true; }; + auto createResource = []() { return make_ref(); }; - { - auto _r = pool.get(); - ASSERT_EQ(pool.count(), 1); - } + Pool pool = + Pool((size_t) 1, createResource, isGood); - pool.flushBad(); - ASSERT_EQ(pool.count(), 0); + // Compare the instance counter between the two handles. We expect them to + // be equal as the pool should hand out the same (still) good one again. + int counter = -1; + { + Pool::Handle h = pool.get(); + counter = h->num; + } // the first handle goes out of scope + + { // the second handle should contain the same resource (with the same + // counter value) + Pool::Handle h = pool.get(); + ASSERT_EQ(h->num, counter); } +} - // Test that the resources we allocate are being reused when they are still good. - TEST(Pool, reuseResource) { - auto isGood = [](const ref & r) { return true; }; - auto createResource = []() { return make_ref(); }; - - Pool pool = Pool((size_t)1, createResource, isGood); - - // Compare the instance counter between the two handles. We expect them to be equal - // as the pool should hand out the same (still) good one again. - int counter = -1; - { - Pool::Handle h = pool.get(); - counter = h->num; - } // the first handle goes out of scope - - { // the second handle should contain the same resource (with the same counter value) - Pool::Handle h = pool.get(); - ASSERT_EQ(h->num, counter); - } - } +// Test that the resources we allocate are being thrown away when they are no +// longer good. +TEST(Pool, badResourceIsNotReused) +{ + auto isGood = [](const ref & r) { return false; }; + auto createResource = []() { return make_ref(); }; - // Test that the resources we allocate are being thrown away when they are no longer good. - TEST(Pool, badResourceIsNotReused) { - auto isGood = [](const ref & r) { return false; }; - auto createResource = []() { return make_ref(); }; - - Pool pool = Pool((size_t)1, createResource, isGood); - - // Compare the instance counter between the two handles. We expect them - // to *not* be equal as the pool should hand out a new instance after - // the first one was returned. - int counter = -1; - { - Pool::Handle h = pool.get(); - counter = h->num; - } // the first handle goes out of scope - - { - // the second handle should contain a different resource (with a - //different counter value) - Pool::Handle h = pool.get(); - ASSERT_NE(h->num, counter); - } + Pool pool = + Pool((size_t) 1, createResource, isGood); + + // Compare the instance counter between the two handles. We expect them + // to *not* be equal as the pool should hand out a new instance after + // the first one was returned. + int counter = -1; + { + Pool::Handle h = pool.get(); + counter = h->num; + } // the first handle goes out of scope + + { + // the second handle should contain a different resource (with a + // different counter value) + Pool::Handle h = pool.get(); + ASSERT_NE(h->num, counter); } } +} diff --git a/src/libutil/tests/suggestions.cc b/src/libutil/tests/suggestions.cc index 279994abc67e..4939ecdf4ffc 100644 --- a/src/libutil/tests/suggestions.cc +++ b/src/libutil/tests/suggestions.cc @@ -3,41 +3,42 @@ namespace nix { - struct LevenshteinDistanceParam { - std::string s1, s2; - int distance; - }; - - class LevenshteinDistanceTest : - public testing::TestWithParam { - }; - - TEST_P(LevenshteinDistanceTest, CorrectlyComputed) { - auto params = GetParam(); - - ASSERT_EQ(levenshteinDistance(params.s1, params.s2), params.distance); - ASSERT_EQ(levenshteinDistance(params.s2, params.s1), params.distance); - } - - INSTANTIATE_TEST_SUITE_P(LevenshteinDistance, LevenshteinDistanceTest, - testing::Values( - LevenshteinDistanceParam{"foo", "foo", 0}, - LevenshteinDistanceParam{"foo", "", 3}, - LevenshteinDistanceParam{"", "", 0}, - LevenshteinDistanceParam{"foo", "fo", 1}, - LevenshteinDistanceParam{"foo", "oo", 1}, - LevenshteinDistanceParam{"foo", "fao", 1}, - LevenshteinDistanceParam{"foo", "abc", 3} - ) - ); - - TEST(Suggestions, Trim) { - auto suggestions = Suggestions::bestMatches({"foooo", "bar", "fo", "gao"}, "foo"); - auto onlyOne = suggestions.trim(1); - ASSERT_EQ(onlyOne.suggestions.size(), 1); - ASSERT_TRUE(onlyOne.suggestions.begin()->suggestion == "fo"); - - auto closest = suggestions.trim(999, 2); - ASSERT_EQ(closest.suggestions.size(), 3); - } +struct LevenshteinDistanceParam { + std::string s1, s2; + int distance; +}; + +class LevenshteinDistanceTest + : public testing::TestWithParam { +}; + +TEST_P(LevenshteinDistanceTest, CorrectlyComputed) +{ + auto params = GetParam(); + + ASSERT_EQ(levenshteinDistance(params.s1, params.s2), params.distance); + ASSERT_EQ(levenshteinDistance(params.s2, params.s1), params.distance); +} + +INSTANTIATE_TEST_SUITE_P( + LevenshteinDistance, LevenshteinDistanceTest, + testing::Values(LevenshteinDistanceParam{"foo", "foo", 0}, + LevenshteinDistanceParam{"foo", "", 3}, + LevenshteinDistanceParam{"", "", 0}, + LevenshteinDistanceParam{"foo", "fo", 1}, + LevenshteinDistanceParam{"foo", "oo", 1}, + LevenshteinDistanceParam{"foo", "fao", 1}, + LevenshteinDistanceParam{"foo", "abc", 3})); + +TEST(Suggestions, Trim) +{ + auto suggestions = + Suggestions::bestMatches({"foooo", "bar", "fo", "gao"}, "foo"); + auto onlyOne = suggestions.trim(1); + ASSERT_EQ(onlyOne.suggestions.size(), 1); + ASSERT_TRUE(onlyOne.suggestions.begin()->suggestion == "fo"); + + auto closest = suggestions.trim(999, 2); + ASSERT_EQ(closest.suggestions.size(), 3); +} } diff --git a/src/libutil/tests/tests.cc b/src/libutil/tests/tests.cc index 6e325db9835b..ea4a0a04bb2d 100644 --- a/src/libutil/tests/tests.cc +++ b/src/libutil/tests/tests.cc @@ -8,616 +8,657 @@ namespace nix { -/* ----------- tests for util.hh ------------------------------------------------*/ +/* ----------- tests for util.hh + * ------------------------------------------------*/ - /* ---------------------------------------------------------------------------- - * absPath - * --------------------------------------------------------------------------*/ +/* ---------------------------------------------------------------------------- + * absPath + * --------------------------------------------------------------------------*/ - TEST(absPath, doesntChangeRoot) { - auto p = absPath("/"); +TEST(absPath, doesntChangeRoot) +{ + auto p = absPath("/"); - ASSERT_EQ(p, "/"); - } + ASSERT_EQ(p, "/"); +} +TEST(absPath, turnsEmptyPathIntoCWD) +{ + char cwd[PATH_MAX + 1]; + auto p = absPath(""); + ASSERT_EQ(p, getcwd((char *) &cwd, PATH_MAX)); +} +TEST(absPath, usesOptionalBasePathWhenGiven) +{ + char _cwd[PATH_MAX + 1]; + char * cwd = getcwd((char *) &_cwd, PATH_MAX); - TEST(absPath, turnsEmptyPathIntoCWD) { - char cwd[PATH_MAX+1]; - auto p = absPath(""); + auto p = absPath("", cwd); - ASSERT_EQ(p, getcwd((char*)&cwd, PATH_MAX)); - } + ASSERT_EQ(p, cwd); +} - TEST(absPath, usesOptionalBasePathWhenGiven) { - char _cwd[PATH_MAX+1]; - char* cwd = getcwd((char*)&_cwd, PATH_MAX); +TEST(absPath, isIdempotent) +{ + char _cwd[PATH_MAX + 1]; + char * cwd = getcwd((char *) &_cwd, PATH_MAX); + auto p1 = absPath(cwd); + auto p2 = absPath(p1); - auto p = absPath("", cwd); + ASSERT_EQ(p1, p2); +} - ASSERT_EQ(p, cwd); - } +TEST(absPath, pathIsCanonicalised) +{ + auto path = "/some/path/with/trailing/dot/."; + auto p1 = absPath(path); + auto p2 = absPath(p1); - TEST(absPath, isIdempotent) { - char _cwd[PATH_MAX+1]; - char* cwd = getcwd((char*)&_cwd, PATH_MAX); - auto p1 = absPath(cwd); - auto p2 = absPath(p1); + ASSERT_EQ(p1, "/some/path/with/trailing/dot"); + ASSERT_EQ(p1, p2); +} - ASSERT_EQ(p1, p2); - } +/* ---------------------------------------------------------------------------- + * canonPath + * --------------------------------------------------------------------------*/ +TEST(canonPath, removesTrailingSlashes) +{ + auto path = "/this/is/a/path//"; + auto p = canonPath(path); - TEST(absPath, pathIsCanonicalised) { - auto path = "/some/path/with/trailing/dot/."; - auto p1 = absPath(path); - auto p2 = absPath(p1); + ASSERT_EQ(p, "/this/is/a/path"); +} - ASSERT_EQ(p1, "/some/path/with/trailing/dot"); - ASSERT_EQ(p1, p2); - } +TEST(canonPath, removesDots) +{ + auto path = "/this/./is/a/path/./"; + auto p = canonPath(path); - /* ---------------------------------------------------------------------------- - * canonPath - * --------------------------------------------------------------------------*/ + ASSERT_EQ(p, "/this/is/a/path"); +} - TEST(canonPath, removesTrailingSlashes) { - auto path = "/this/is/a/path//"; - auto p = canonPath(path); +TEST(canonPath, removesDots2) +{ + auto path = "/this/a/../is/a////path/foo/.."; + auto p = canonPath(path); - ASSERT_EQ(p, "/this/is/a/path"); - } + ASSERT_EQ(p, "/this/is/a/path"); +} - TEST(canonPath, removesDots) { - auto path = "/this/./is/a/path/./"; - auto p = canonPath(path); +TEST(canonPath, requiresAbsolutePath) +{ + ASSERT_ANY_THROW(canonPath(".")); + ASSERT_ANY_THROW(canonPath("..")); + ASSERT_ANY_THROW(canonPath("../")); + ASSERT_DEATH({ canonPath(""); }, "path != \"\""); +} - ASSERT_EQ(p, "/this/is/a/path"); - } +/* ---------------------------------------------------------------------------- + * dirOf + * --------------------------------------------------------------------------*/ - TEST(canonPath, removesDots2) { - auto path = "/this/a/../is/a////path/foo/.."; - auto p = canonPath(path); +TEST(dirOf, returnsEmptyStringForRoot) +{ + auto p = dirOf("/"); - ASSERT_EQ(p, "/this/is/a/path"); - } + ASSERT_EQ(p, "/"); +} - TEST(canonPath, requiresAbsolutePath) { - ASSERT_ANY_THROW(canonPath(".")); - ASSERT_ANY_THROW(canonPath("..")); - ASSERT_ANY_THROW(canonPath("../")); - ASSERT_DEATH({ canonPath(""); }, "path != \"\""); - } +TEST(dirOf, returnsFirstPathComponent) +{ + auto p1 = dirOf("/dir/"); + ASSERT_EQ(p1, "/dir"); + auto p2 = dirOf("/dir"); + ASSERT_EQ(p2, "/"); + auto p3 = dirOf("/dir/.."); + ASSERT_EQ(p3, "/dir"); + auto p4 = dirOf("/dir/../"); + ASSERT_EQ(p4, "/dir/.."); +} - /* ---------------------------------------------------------------------------- - * dirOf - * --------------------------------------------------------------------------*/ +/* ---------------------------------------------------------------------------- + * baseNameOf + * --------------------------------------------------------------------------*/ - TEST(dirOf, returnsEmptyStringForRoot) { - auto p = dirOf("/"); +TEST(baseNameOf, emptyPath) +{ + auto p1 = baseNameOf(""); + ASSERT_EQ(p1, ""); +} - ASSERT_EQ(p, "/"); - } +TEST(baseNameOf, pathOnRoot) +{ + auto p1 = baseNameOf("/dir"); + ASSERT_EQ(p1, "dir"); +} - TEST(dirOf, returnsFirstPathComponent) { - auto p1 = dirOf("/dir/"); - ASSERT_EQ(p1, "/dir"); - auto p2 = dirOf("/dir"); - ASSERT_EQ(p2, "/"); - auto p3 = dirOf("/dir/.."); - ASSERT_EQ(p3, "/dir"); - auto p4 = dirOf("/dir/../"); - ASSERT_EQ(p4, "/dir/.."); - } +TEST(baseNameOf, relativePath) +{ + auto p1 = baseNameOf("dir/foo"); + ASSERT_EQ(p1, "foo"); +} - /* ---------------------------------------------------------------------------- - * baseNameOf - * --------------------------------------------------------------------------*/ +TEST(baseNameOf, pathWithTrailingSlashRoot) +{ + auto p1 = baseNameOf("/"); + ASSERT_EQ(p1, ""); +} - TEST(baseNameOf, emptyPath) { - auto p1 = baseNameOf(""); - ASSERT_EQ(p1, ""); - } +TEST(baseNameOf, trailingSlash) +{ + auto p1 = baseNameOf("/dir/"); + ASSERT_EQ(p1, "dir"); +} - TEST(baseNameOf, pathOnRoot) { - auto p1 = baseNameOf("/dir"); - ASSERT_EQ(p1, "dir"); - } - - TEST(baseNameOf, relativePath) { - auto p1 = baseNameOf("dir/foo"); - ASSERT_EQ(p1, "foo"); - } - - TEST(baseNameOf, pathWithTrailingSlashRoot) { - auto p1 = baseNameOf("/"); - ASSERT_EQ(p1, ""); - } - - TEST(baseNameOf, trailingSlash) { - auto p1 = baseNameOf("/dir/"); - ASSERT_EQ(p1, "dir"); - } - - /* ---------------------------------------------------------------------------- - * isInDir - * --------------------------------------------------------------------------*/ - - TEST(isInDir, trivialCase) { - auto p1 = isInDir("/foo/bar", "/foo"); - ASSERT_EQ(p1, true); - } - - TEST(isInDir, notInDir) { - auto p1 = isInDir("/zes/foo/bar", "/foo"); - ASSERT_EQ(p1, false); - } - - // XXX: hm, bug or feature? :) Looking at the implementation - // this might be problematic. - TEST(isInDir, emptyDir) { - auto p1 = isInDir("/zes/foo/bar", ""); - ASSERT_EQ(p1, true); - } - - /* ---------------------------------------------------------------------------- - * isDirOrInDir - * --------------------------------------------------------------------------*/ - - TEST(isDirOrInDir, trueForSameDirectory) { - ASSERT_EQ(isDirOrInDir("/nix", "/nix"), true); - ASSERT_EQ(isDirOrInDir("/", "/"), true); - } - - TEST(isDirOrInDir, trueForEmptyPaths) { - ASSERT_EQ(isDirOrInDir("", ""), true); - } - - TEST(isDirOrInDir, falseForDisjunctPaths) { - ASSERT_EQ(isDirOrInDir("/foo", "/bar"), false); - } - - TEST(isDirOrInDir, relativePaths) { - ASSERT_EQ(isDirOrInDir("/foo/..", "/foo"), true); - } - - // XXX: while it is possible to use "." or ".." in the - // first argument this doesn't seem to work in the second. - TEST(isDirOrInDir, DISABLED_shouldWork) { - ASSERT_EQ(isDirOrInDir("/foo/..", "/foo/."), true); - - } - - /* ---------------------------------------------------------------------------- - * pathExists - * --------------------------------------------------------------------------*/ - - TEST(pathExists, rootExists) { - ASSERT_TRUE(pathExists("/")); - } - - TEST(pathExists, cwdExists) { - ASSERT_TRUE(pathExists(".")); - } - - TEST(pathExists, bogusPathDoesNotExist) { - ASSERT_FALSE(pathExists("/home/schnitzel/darmstadt/pommes")); - } - - /* ---------------------------------------------------------------------------- - * concatStringsSep - * --------------------------------------------------------------------------*/ - - TEST(concatStringsSep, buildCommaSeparatedString) { - Strings strings; - strings.push_back("this"); - strings.push_back("is"); - strings.push_back("great"); - - ASSERT_EQ(concatStringsSep(",", strings), "this,is,great"); - } - - TEST(concatStringsSep, buildStringWithEmptySeparator) { - Strings strings; - strings.push_back("this"); - strings.push_back("is"); - strings.push_back("great"); - - ASSERT_EQ(concatStringsSep("", strings), "thisisgreat"); - } - - TEST(concatStringsSep, buildSingleString) { - Strings strings; - strings.push_back("this"); - - ASSERT_EQ(concatStringsSep(",", strings), "this"); - } - - /* ---------------------------------------------------------------------------- - * hasPrefix - * --------------------------------------------------------------------------*/ - - TEST(hasPrefix, emptyStringHasNoPrefix) { - ASSERT_FALSE(hasPrefix("", "foo")); - } - - TEST(hasPrefix, emptyStringIsAlwaysPrefix) { - ASSERT_TRUE(hasPrefix("foo", "")); - ASSERT_TRUE(hasPrefix("jshjkfhsadf", "")); - } - - TEST(hasPrefix, trivialCase) { - ASSERT_TRUE(hasPrefix("foobar", "foo")); - } - - /* ---------------------------------------------------------------------------- - * hasSuffix - * --------------------------------------------------------------------------*/ - - TEST(hasSuffix, emptyStringHasNoSuffix) { - ASSERT_FALSE(hasSuffix("", "foo")); - } +/* ---------------------------------------------------------------------------- + * isInDir + * --------------------------------------------------------------------------*/ - TEST(hasSuffix, trivialCase) { - ASSERT_TRUE(hasSuffix("foo", "foo")); - ASSERT_TRUE(hasSuffix("foobar", "bar")); - } +TEST(isInDir, trivialCase) +{ + auto p1 = isInDir("/foo/bar", "/foo"); + ASSERT_EQ(p1, true); +} - /* ---------------------------------------------------------------------------- - * base64Encode - * --------------------------------------------------------------------------*/ +TEST(isInDir, notInDir) +{ + auto p1 = isInDir("/zes/foo/bar", "/foo"); + ASSERT_EQ(p1, false); +} - TEST(base64Encode, emptyString) { - ASSERT_EQ(base64Encode(""), ""); - } +// XXX: hm, bug or feature? :) Looking at the implementation +// this might be problematic. +TEST(isInDir, emptyDir) +{ + auto p1 = isInDir("/zes/foo/bar", ""); + ASSERT_EQ(p1, true); +} - TEST(base64Encode, encodesAString) { - ASSERT_EQ(base64Encode("quod erat demonstrandum"), "cXVvZCBlcmF0IGRlbW9uc3RyYW5kdW0="); - } +/* ---------------------------------------------------------------------------- + * isDirOrInDir + * --------------------------------------------------------------------------*/ - TEST(base64Encode, encodeAndDecode) { - auto s = "quod erat demonstrandum"; - auto encoded = base64Encode(s); - auto decoded = base64Decode(encoded); +TEST(isDirOrInDir, trueForSameDirectory) +{ + ASSERT_EQ(isDirOrInDir("/nix", "/nix"), true); + ASSERT_EQ(isDirOrInDir("/", "/"), true); +} - ASSERT_EQ(decoded, s); - } +TEST(isDirOrInDir, trueForEmptyPaths) { ASSERT_EQ(isDirOrInDir("", ""), true); } - TEST(base64Encode, encodeAndDecodeNonPrintable) { - char s[256]; - std::iota(std::rbegin(s), std::rend(s), 0); +TEST(isDirOrInDir, falseForDisjunctPaths) +{ + ASSERT_EQ(isDirOrInDir("/foo", "/bar"), false); +} - auto encoded = base64Encode(s); - auto decoded = base64Decode(encoded); +TEST(isDirOrInDir, relativePaths) +{ + ASSERT_EQ(isDirOrInDir("/foo/..", "/foo"), true); +} - EXPECT_EQ(decoded.length(), 255); - ASSERT_EQ(decoded, s); - } +// XXX: while it is possible to use "." or ".." in the +// first argument this doesn't seem to work in the second. +TEST(isDirOrInDir, DISABLED_shouldWork) +{ + ASSERT_EQ(isDirOrInDir("/foo/..", "/foo/."), true); +} - /* ---------------------------------------------------------------------------- - * base64Decode - * --------------------------------------------------------------------------*/ +/* ---------------------------------------------------------------------------- + * pathExists + * --------------------------------------------------------------------------*/ - TEST(base64Decode, emptyString) { - ASSERT_EQ(base64Decode(""), ""); - } +TEST(pathExists, rootExists) { ASSERT_TRUE(pathExists("/")); } - TEST(base64Decode, decodeAString) { - ASSERT_EQ(base64Decode("cXVvZCBlcmF0IGRlbW9uc3RyYW5kdW0="), "quod erat demonstrandum"); - } +TEST(pathExists, cwdExists) { ASSERT_TRUE(pathExists(".")); } - TEST(base64Decode, decodeThrowsOnInvalidChar) { - ASSERT_THROW(base64Decode("cXVvZCBlcm_0IGRlbW9uc3RyYW5kdW0="), Error); - } +TEST(pathExists, bogusPathDoesNotExist) +{ + ASSERT_FALSE(pathExists("/home/schnitzel/darmstadt/pommes")); +} - /* ---------------------------------------------------------------------------- - * toLower - * --------------------------------------------------------------------------*/ +/* ---------------------------------------------------------------------------- + * concatStringsSep + * --------------------------------------------------------------------------*/ - TEST(toLower, emptyString) { - ASSERT_EQ(toLower(""), ""); - } +TEST(concatStringsSep, buildCommaSeparatedString) +{ + Strings strings; + strings.push_back("this"); + strings.push_back("is"); + strings.push_back("great"); - TEST(toLower, nonLetters) { - auto s = "!@(*$#)(@#=\\234_"; - ASSERT_EQ(toLower(s), s); - } + ASSERT_EQ(concatStringsSep(",", strings), "this,is,great"); +} - // std::tolower() doesn't handle unicode characters. In the context of - // store paths this isn't relevant but doesn't hurt to record this behavior - // here. - TEST(toLower, umlauts) { - auto s = "ÄÖÜ"; - ASSERT_EQ(toLower(s), "ÄÖÜ"); - } +TEST(concatStringsSep, buildStringWithEmptySeparator) +{ + Strings strings; + strings.push_back("this"); + strings.push_back("is"); + strings.push_back("great"); - /* ---------------------------------------------------------------------------- - * string2Float - * --------------------------------------------------------------------------*/ + ASSERT_EQ(concatStringsSep("", strings), "thisisgreat"); +} - TEST(string2Float, emptyString) { - ASSERT_EQ(string2Float(""), std::nullopt); - } +TEST(concatStringsSep, buildSingleString) +{ + Strings strings; + strings.push_back("this"); - TEST(string2Float, trivialConversions) { - ASSERT_EQ(string2Float("1.0"), 1.0); + ASSERT_EQ(concatStringsSep(",", strings), "this"); +} - ASSERT_EQ(string2Float("0.0"), 0.0); +/* ---------------------------------------------------------------------------- + * hasPrefix + * --------------------------------------------------------------------------*/ - ASSERT_EQ(string2Float("-100.25"), -100.25); - } +TEST(hasPrefix, emptyStringHasNoPrefix) { ASSERT_FALSE(hasPrefix("", "foo")); } - /* ---------------------------------------------------------------------------- - * string2Int - * --------------------------------------------------------------------------*/ +TEST(hasPrefix, emptyStringIsAlwaysPrefix) +{ + ASSERT_TRUE(hasPrefix("foo", "")); + ASSERT_TRUE(hasPrefix("jshjkfhsadf", "")); +} - TEST(string2Int, emptyString) { - ASSERT_EQ(string2Int(""), std::nullopt); - } +TEST(hasPrefix, trivialCase) { ASSERT_TRUE(hasPrefix("foobar", "foo")); } - TEST(string2Int, trivialConversions) { - ASSERT_EQ(string2Int("1"), 1); +/* ---------------------------------------------------------------------------- + * hasSuffix + * --------------------------------------------------------------------------*/ - ASSERT_EQ(string2Int("0"), 0); +TEST(hasSuffix, emptyStringHasNoSuffix) { ASSERT_FALSE(hasSuffix("", "foo")); } - ASSERT_EQ(string2Int("-100"), -100); - } +TEST(hasSuffix, trivialCase) +{ + ASSERT_TRUE(hasSuffix("foo", "foo")); + ASSERT_TRUE(hasSuffix("foobar", "bar")); +} - /* ---------------------------------------------------------------------------- - * statusOk - * --------------------------------------------------------------------------*/ +/* ---------------------------------------------------------------------------- + * base64Encode + * --------------------------------------------------------------------------*/ - TEST(statusOk, zeroIsOk) { - ASSERT_EQ(statusOk(0), true); - ASSERT_EQ(statusOk(1), false); - } +TEST(base64Encode, emptyString) { ASSERT_EQ(base64Encode(""), ""); } +TEST(base64Encode, encodesAString) +{ + ASSERT_EQ(base64Encode("quod erat demonstrandum"), + "cXVvZCBlcmF0IGRlbW9uc3RyYW5kdW0="); +} - /* ---------------------------------------------------------------------------- - * rewriteStrings - * --------------------------------------------------------------------------*/ +TEST(base64Encode, encodeAndDecode) +{ + auto s = "quod erat demonstrandum"; + auto encoded = base64Encode(s); + auto decoded = base64Decode(encoded); - TEST(rewriteStrings, emptyString) { - StringMap rewrites; - rewrites["this"] = "that"; + ASSERT_EQ(decoded, s); +} - ASSERT_EQ(rewriteStrings("", rewrites), ""); - } +TEST(base64Encode, encodeAndDecodeNonPrintable) +{ + char s[256]; + std::iota(std::rbegin(s), std::rend(s), 0); - TEST(rewriteStrings, emptyRewrites) { - StringMap rewrites; + auto encoded = base64Encode(s); + auto decoded = base64Decode(encoded); - ASSERT_EQ(rewriteStrings("this and that", rewrites), "this and that"); - } + EXPECT_EQ(decoded.length(), 255); + ASSERT_EQ(decoded, s); +} - TEST(rewriteStrings, successfulRewrite) { - StringMap rewrites; - rewrites["this"] = "that"; +/* ---------------------------------------------------------------------------- + * base64Decode + * --------------------------------------------------------------------------*/ - ASSERT_EQ(rewriteStrings("this and that", rewrites), "that and that"); - } +TEST(base64Decode, emptyString) { ASSERT_EQ(base64Decode(""), ""); } - TEST(rewriteStrings, doesntOccur) { - StringMap rewrites; - rewrites["foo"] = "bar"; +TEST(base64Decode, decodeAString) +{ + ASSERT_EQ(base64Decode("cXVvZCBlcmF0IGRlbW9uc3RyYW5kdW0="), + "quod erat demonstrandum"); +} - ASSERT_EQ(rewriteStrings("this and that", rewrites), "this and that"); - } +TEST(base64Decode, decodeThrowsOnInvalidChar) +{ + ASSERT_THROW(base64Decode("cXVvZCBlcm_0IGRlbW9uc3RyYW5kdW0="), Error); +} - /* ---------------------------------------------------------------------------- - * replaceStrings - * --------------------------------------------------------------------------*/ +/* ---------------------------------------------------------------------------- + * toLower + * --------------------------------------------------------------------------*/ - TEST(replaceStrings, emptyString) { - ASSERT_EQ(replaceStrings("", "this", "that"), ""); - ASSERT_EQ(replaceStrings("this and that", "", ""), "this and that"); - } +TEST(toLower, emptyString) { ASSERT_EQ(toLower(""), ""); } - TEST(replaceStrings, successfulReplace) { - ASSERT_EQ(replaceStrings("this and that", "this", "that"), "that and that"); - } +TEST(toLower, nonLetters) +{ + auto s = "!@(*$#)(@#=\\234_"; + ASSERT_EQ(toLower(s), s); +} - TEST(replaceStrings, doesntOccur) { - ASSERT_EQ(replaceStrings("this and that", "foo", "bar"), "this and that"); - } +// std::tolower() doesn't handle unicode characters. In the context of +// store paths this isn't relevant but doesn't hurt to record this behavior +// here. +TEST(toLower, umlauts) +{ + auto s = "ÄÖÜ"; + ASSERT_EQ(toLower(s), "ÄÖÜ"); +} - /* ---------------------------------------------------------------------------- - * trim - * --------------------------------------------------------------------------*/ +/* ---------------------------------------------------------------------------- + * string2Float + * --------------------------------------------------------------------------*/ - TEST(trim, emptyString) { - ASSERT_EQ(trim(""), ""); - } +TEST(string2Float, emptyString) +{ + ASSERT_EQ(string2Float(""), std::nullopt); +} - TEST(trim, removesWhitespace) { - ASSERT_EQ(trim("foo"), "foo"); - ASSERT_EQ(trim(" foo "), "foo"); - ASSERT_EQ(trim(" foo bar baz"), "foo bar baz"); - ASSERT_EQ(trim(" \t foo bar baz\n"), "foo bar baz"); - } +TEST(string2Float, trivialConversions) +{ + ASSERT_EQ(string2Float("1.0"), 1.0); - /* ---------------------------------------------------------------------------- - * chomp - * --------------------------------------------------------------------------*/ + ASSERT_EQ(string2Float("0.0"), 0.0); - TEST(chomp, emptyString) { - ASSERT_EQ(chomp(""), ""); - } + ASSERT_EQ(string2Float("-100.25"), -100.25); +} - TEST(chomp, removesWhitespace) { - ASSERT_EQ(chomp("foo"), "foo"); - ASSERT_EQ(chomp("foo "), "foo"); - ASSERT_EQ(chomp(" foo "), " foo"); - ASSERT_EQ(chomp(" foo bar baz "), " foo bar baz"); - ASSERT_EQ(chomp("\t foo bar baz\n"), "\t foo bar baz"); - } +/* ---------------------------------------------------------------------------- + * string2Int + * --------------------------------------------------------------------------*/ - /* ---------------------------------------------------------------------------- - * quoteStrings - * --------------------------------------------------------------------------*/ +TEST(string2Int, emptyString) { ASSERT_EQ(string2Int(""), std::nullopt); } - TEST(quoteStrings, empty) { - Strings s = { }; - Strings expected = { }; +TEST(string2Int, trivialConversions) +{ + ASSERT_EQ(string2Int("1"), 1); - ASSERT_EQ(quoteStrings(s), expected); - } + ASSERT_EQ(string2Int("0"), 0); - TEST(quoteStrings, emptyStrings) { - Strings s = { "", "", "" }; - Strings expected = { "''", "''", "''" }; - ASSERT_EQ(quoteStrings(s), expected); + ASSERT_EQ(string2Int("-100"), -100); +} - } +/* ---------------------------------------------------------------------------- + * statusOk + * --------------------------------------------------------------------------*/ - TEST(quoteStrings, trivialQuote) { - Strings s = { "foo", "bar", "baz" }; - Strings expected = { "'foo'", "'bar'", "'baz'" }; +TEST(statusOk, zeroIsOk) +{ + ASSERT_EQ(statusOk(0), true); + ASSERT_EQ(statusOk(1), false); +} - ASSERT_EQ(quoteStrings(s), expected); - } +/* ---------------------------------------------------------------------------- + * rewriteStrings + * --------------------------------------------------------------------------*/ - TEST(quoteStrings, quotedStrings) { - Strings s = { "'foo'", "'bar'", "'baz'" }; - Strings expected = { "''foo''", "''bar''", "''baz''" }; +TEST(rewriteStrings, emptyString) +{ + StringMap rewrites; + rewrites["this"] = "that"; - ASSERT_EQ(quoteStrings(s), expected); - } + ASSERT_EQ(rewriteStrings("", rewrites), ""); +} - /* ---------------------------------------------------------------------------- - * tokenizeString - * --------------------------------------------------------------------------*/ +TEST(rewriteStrings, emptyRewrites) +{ + StringMap rewrites; - TEST(tokenizeString, empty) { - Strings expected = { }; + ASSERT_EQ(rewriteStrings("this and that", rewrites), "this and that"); +} - ASSERT_EQ(tokenizeString(""), expected); - } +TEST(rewriteStrings, successfulRewrite) +{ + StringMap rewrites; + rewrites["this"] = "that"; - TEST(tokenizeString, tokenizeSpacesWithDefaults) { - auto s = "foo bar baz"; - Strings expected = { "foo", "bar", "baz" }; + ASSERT_EQ(rewriteStrings("this and that", rewrites), "that and that"); +} - ASSERT_EQ(tokenizeString(s), expected); - } +TEST(rewriteStrings, doesntOccur) +{ + StringMap rewrites; + rewrites["foo"] = "bar"; - TEST(tokenizeString, tokenizeTabsWithDefaults) { - auto s = "foo\tbar\tbaz"; - Strings expected = { "foo", "bar", "baz" }; + ASSERT_EQ(rewriteStrings("this and that", rewrites), "this and that"); +} - ASSERT_EQ(tokenizeString(s), expected); - } +/* ---------------------------------------------------------------------------- + * replaceStrings + * --------------------------------------------------------------------------*/ - TEST(tokenizeString, tokenizeTabsSpacesWithDefaults) { - auto s = "foo\t bar\t baz"; - Strings expected = { "foo", "bar", "baz" }; +TEST(replaceStrings, emptyString) +{ + ASSERT_EQ(replaceStrings("", "this", "that"), ""); + ASSERT_EQ(replaceStrings("this and that", "", ""), "this and that"); +} - ASSERT_EQ(tokenizeString(s), expected); - } +TEST(replaceStrings, successfulReplace) +{ + ASSERT_EQ(replaceStrings("this and that", "this", "that"), "that and that"); +} - TEST(tokenizeString, tokenizeTabsSpacesNewlineWithDefaults) { - auto s = "foo\t\n bar\t\n baz"; - Strings expected = { "foo", "bar", "baz" }; +TEST(replaceStrings, doesntOccur) +{ + ASSERT_EQ(replaceStrings("this and that", "foo", "bar"), "this and that"); +} - ASSERT_EQ(tokenizeString(s), expected); - } +/* ---------------------------------------------------------------------------- + * trim + * --------------------------------------------------------------------------*/ - TEST(tokenizeString, tokenizeTabsSpacesNewlineRetWithDefaults) { - auto s = "foo\t\n\r bar\t\n\r baz"; - Strings expected = { "foo", "bar", "baz" }; +TEST(trim, emptyString) { ASSERT_EQ(trim(""), ""); } - ASSERT_EQ(tokenizeString(s), expected); +TEST(trim, removesWhitespace) +{ + ASSERT_EQ(trim("foo"), "foo"); + ASSERT_EQ(trim(" foo "), "foo"); + ASSERT_EQ(trim(" foo bar baz"), "foo bar baz"); + ASSERT_EQ(trim(" \t foo bar baz\n"), "foo bar baz"); +} + +/* ---------------------------------------------------------------------------- + * chomp + * --------------------------------------------------------------------------*/ + +TEST(chomp, emptyString) { ASSERT_EQ(chomp(""), ""); } + +TEST(chomp, removesWhitespace) +{ + ASSERT_EQ(chomp("foo"), "foo"); + ASSERT_EQ(chomp("foo "), "foo"); + ASSERT_EQ(chomp(" foo "), " foo"); + ASSERT_EQ(chomp(" foo bar baz "), " foo bar baz"); + ASSERT_EQ(chomp("\t foo bar baz\n"), "\t foo bar baz"); +} + +/* ---------------------------------------------------------------------------- + * quoteStrings + * --------------------------------------------------------------------------*/ + +TEST(quoteStrings, empty) +{ + Strings s = {}; + Strings expected = {}; + + ASSERT_EQ(quoteStrings(s), expected); +} + +TEST(quoteStrings, emptyStrings) +{ + Strings s = {"", "", ""}; + Strings expected = {"''", "''", "''"}; + ASSERT_EQ(quoteStrings(s), expected); +} + +TEST(quoteStrings, trivialQuote) +{ + Strings s = {"foo", "bar", "baz"}; + Strings expected = {"'foo'", "'bar'", "'baz'"}; + + ASSERT_EQ(quoteStrings(s), expected); +} + +TEST(quoteStrings, quotedStrings) +{ + Strings s = {"'foo'", "'bar'", "'baz'"}; + Strings expected = {"''foo''", "''bar''", "''baz''"}; + + ASSERT_EQ(quoteStrings(s), expected); +} + +/* ---------------------------------------------------------------------------- + * tokenizeString + * --------------------------------------------------------------------------*/ + +TEST(tokenizeString, empty) +{ + Strings expected = {}; + + ASSERT_EQ(tokenizeString(""), expected); +} + +TEST(tokenizeString, tokenizeSpacesWithDefaults) +{ + auto s = "foo bar baz"; + Strings expected = {"foo", "bar", "baz"}; + + ASSERT_EQ(tokenizeString(s), expected); +} + +TEST(tokenizeString, tokenizeTabsWithDefaults) +{ + auto s = "foo\tbar\tbaz"; + Strings expected = {"foo", "bar", "baz"}; - auto s2 = "foo \t\n\r bar \t\n\r baz"; - Strings expected2 = { "foo", "bar", "baz" }; + ASSERT_EQ(tokenizeString(s), expected); +} - ASSERT_EQ(tokenizeString(s2), expected2); - } +TEST(tokenizeString, tokenizeTabsSpacesWithDefaults) +{ + auto s = "foo\t bar\t baz"; + Strings expected = {"foo", "bar", "baz"}; - TEST(tokenizeString, tokenizeWithCustomSep) { - auto s = "foo\n,bar\n,baz\n"; - Strings expected = { "foo\n", "bar\n", "baz\n" }; + ASSERT_EQ(tokenizeString(s), expected); +} - ASSERT_EQ(tokenizeString(s, ","), expected); - } +TEST(tokenizeString, tokenizeTabsSpacesNewlineWithDefaults) +{ + auto s = "foo\t\n bar\t\n baz"; + Strings expected = {"foo", "bar", "baz"}; - /* ---------------------------------------------------------------------------- - * get - * --------------------------------------------------------------------------*/ + ASSERT_EQ(tokenizeString(s), expected); +} - TEST(get, emptyContainer) { - StringMap s = { }; - auto expected = nullptr; +TEST(tokenizeString, tokenizeTabsSpacesNewlineRetWithDefaults) +{ + auto s = "foo\t\n\r bar\t\n\r baz"; + Strings expected = {"foo", "bar", "baz"}; - ASSERT_EQ(get(s, "one"), expected); - } + ASSERT_EQ(tokenizeString(s), expected); - TEST(get, getFromContainer) { - StringMap s; - s["one"] = "yi"; - s["two"] = "er"; - auto expected = "yi"; + auto s2 = "foo \t\n\r bar \t\n\r baz"; + Strings expected2 = {"foo", "bar", "baz"}; - ASSERT_EQ(*get(s, "one"), expected); - } + ASSERT_EQ(tokenizeString(s2), expected2); +} - TEST(getOr, emptyContainer) { - StringMap s = { }; - auto expected = "yi"; +TEST(tokenizeString, tokenizeWithCustomSep) +{ + auto s = "foo\n,bar\n,baz\n"; + Strings expected = {"foo\n", "bar\n", "baz\n"}; - ASSERT_EQ(getOr(s, "one", "yi"), expected); - } + ASSERT_EQ(tokenizeString(s, ","), expected); +} - TEST(getOr, getFromContainer) { - StringMap s; - s["one"] = "yi"; - s["two"] = "er"; - auto expected = "yi"; +/* ---------------------------------------------------------------------------- + * get + * --------------------------------------------------------------------------*/ - ASSERT_EQ(getOr(s, "one", "nope"), expected); - } +TEST(get, emptyContainer) +{ + StringMap s = {}; + auto expected = nullptr; - /* ---------------------------------------------------------------------------- - * filterANSIEscapes - * --------------------------------------------------------------------------*/ + ASSERT_EQ(get(s, "one"), expected); +} - TEST(filterANSIEscapes, emptyString) { - auto s = ""; - auto expected = ""; +TEST(get, getFromContainer) +{ + StringMap s; + s["one"] = "yi"; + s["two"] = "er"; + auto expected = "yi"; - ASSERT_EQ(filterANSIEscapes(s), expected); - } + ASSERT_EQ(*get(s, "one"), expected); +} - TEST(filterANSIEscapes, doesntChangePrintableChars) { - auto s = "09 2q304ruyhr slk2-19024 kjsadh sar f"; +TEST(getOr, emptyContainer) +{ + StringMap s = {}; + auto expected = "yi"; - ASSERT_EQ(filterANSIEscapes(s), s); - } + ASSERT_EQ(getOr(s, "one", "yi"), expected); +} - TEST(filterANSIEscapes, filtersColorCodes) { - auto s = "\u001b[30m A \u001b[31m B \u001b[32m C \u001b[33m D \u001b[0m"; +TEST(getOr, getFromContainer) +{ + StringMap s; + s["one"] = "yi"; + s["two"] = "er"; + auto expected = "yi"; - ASSERT_EQ(filterANSIEscapes(s, true, 2), " A" ); - ASSERT_EQ(filterANSIEscapes(s, true, 3), " A " ); - ASSERT_EQ(filterANSIEscapes(s, true, 4), " A " ); - ASSERT_EQ(filterANSIEscapes(s, true, 5), " A B" ); - ASSERT_EQ(filterANSIEscapes(s, true, 8), " A B C" ); - } + ASSERT_EQ(getOr(s, "one", "nope"), expected); +} - TEST(filterANSIEscapes, expandsTabs) { - auto s = "foo\tbar\tbaz"; +/* ---------------------------------------------------------------------------- + * filterANSIEscapes + * --------------------------------------------------------------------------*/ - ASSERT_EQ(filterANSIEscapes(s, true), "foo bar baz" ); - } +TEST(filterANSIEscapes, emptyString) +{ + auto s = ""; + auto expected = ""; - TEST(filterANSIEscapes, utf8) { - ASSERT_EQ(filterANSIEscapes("foobar", true, 5), "fooba"); - ASSERT_EQ(filterANSIEscapes("fóóbär", true, 6), "fóóbär"); - ASSERT_EQ(filterANSIEscapes("fóóbär", true, 5), "fóóbä"); - ASSERT_EQ(filterANSIEscapes("fóóbär", true, 3), "fóó"); - ASSERT_EQ(filterANSIEscapes("f€€bär", true, 4), "f€€b"); - ASSERT_EQ(filterANSIEscapes("f𐍈𐍈bär", true, 4), "f𐍈𐍈b"); - } + ASSERT_EQ(filterANSIEscapes(s), expected); +} + +TEST(filterANSIEscapes, doesntChangePrintableChars) +{ + auto s = "09 2q304ruyhr slk2-19024 kjsadh sar f"; + + ASSERT_EQ(filterANSIEscapes(s), s); +} + +TEST(filterANSIEscapes, filtersColorCodes) +{ + auto s = "\u001b[30m A \u001b[31m B \u001b[32m C \u001b[33m D \u001b[0m"; + + ASSERT_EQ(filterANSIEscapes(s, true, 2), " A"); + ASSERT_EQ(filterANSIEscapes(s, true, 3), " A "); + ASSERT_EQ(filterANSIEscapes(s, true, 4), " A "); + ASSERT_EQ(filterANSIEscapes(s, true, 5), " A B"); + ASSERT_EQ(filterANSIEscapes(s, true, 8), " A B C"); +} + +TEST(filterANSIEscapes, expandsTabs) +{ + auto s = "foo\tbar\tbaz"; + + ASSERT_EQ(filterANSIEscapes(s, true), "foo bar baz"); +} + +TEST(filterANSIEscapes, utf8) +{ + ASSERT_EQ(filterANSIEscapes("foobar", true, 5), "fooba"); + ASSERT_EQ(filterANSIEscapes("fóóbär", true, 6), "fóóbär"); + ASSERT_EQ(filterANSIEscapes("fóóbär", true, 5), "fóóbä"); + ASSERT_EQ(filterANSIEscapes("fóóbär", true, 3), "fóó"); + ASSERT_EQ(filterANSIEscapes("f€€bär", true, 4), "f€€b"); + ASSERT_EQ(filterANSIEscapes("f𐍈𐍈bär", true, 4), "f𐍈𐍈b"); +} } diff --git a/src/libutil/tests/url.cc b/src/libutil/tests/url.cc index c3b233797c39..78df757ca612 100644 --- a/src/libutil/tests/url.cc +++ b/src/libutil/tests/url.cc @@ -3,282 +3,298 @@ namespace nix { -/* ----------- tests for url.hh --------------------------------------------------*/ - - std::string print_map(std::map m) { - std::map::iterator it; - std::string s = "{ "; - for (it = m.begin(); it != m.end(); ++it) { - s += "{ "; - s += it->first; - s += " = "; - s += it->second; - s += " } "; - } - s += "}"; - return s; +/* ----------- tests for url.hh + * --------------------------------------------------*/ + +std::string print_map(std::map m) +{ + std::map::iterator it; + std::string s = "{ "; + for (it = m.begin(); it != m.end(); ++it) { + s += "{ "; + s += it->first; + s += " = "; + s += it->second; + s += " } "; } + s += "}"; + return s; +} +std::ostream & operator<<(std::ostream & os, const ParsedURL & p) +{ + return os << "\n" + << "url: " << p.url << "\n" + << "base: " << p.base << "\n" + << "scheme: " << p.scheme << "\n" + << "authority: " << p.authority.value() << "\n" + << "path: " << p.path << "\n" + << "query: " << print_map(p.query) << "\n" + << "fragment: " << p.fragment << "\n"; +} - std::ostream& operator<<(std::ostream& os, const ParsedURL& p) { - return os << "\n" - << "url: " << p.url << "\n" - << "base: " << p.base << "\n" - << "scheme: " << p.scheme << "\n" - << "authority: " << p.authority.value() << "\n" - << "path: " << p.path << "\n" - << "query: " << print_map(p.query) << "\n" - << "fragment: " << p.fragment << "\n"; - } - - TEST(parseURL, parsesSimpleHttpUrl) { - auto s = "http://www.example.org/file.tar.gz"; - auto parsed = parseURL(s); - - ParsedURL expected { - .url = "http://www.example.org/file.tar.gz", - .base = "http://www.example.org/file.tar.gz", - .scheme = "http", - .authority = "www.example.org", - .path = "/file.tar.gz", - .query = (StringMap) { }, - .fragment = "", - }; - - ASSERT_EQ(parsed, expected); - } - - TEST(parseURL, parsesSimpleHttpsUrl) { - auto s = "https://www.example.org/file.tar.gz"; - auto parsed = parseURL(s); - - ParsedURL expected { - .url = "https://www.example.org/file.tar.gz", - .base = "https://www.example.org/file.tar.gz", - .scheme = "https", - .authority = "www.example.org", - .path = "/file.tar.gz", - .query = (StringMap) { }, - .fragment = "", - }; - - ASSERT_EQ(parsed, expected); - } - - TEST(parseURL, parsesSimpleHttpUrlWithQueryAndFragment) { - auto s = "https://www.example.org/file.tar.gz?download=fast&when=now#hello"; - auto parsed = parseURL(s); - - ParsedURL expected { - .url = "https://www.example.org/file.tar.gz", - .base = "https://www.example.org/file.tar.gz", - .scheme = "https", - .authority = "www.example.org", - .path = "/file.tar.gz", - .query = (StringMap) { { "download", "fast" }, { "when", "now" } }, - .fragment = "hello", - }; - - ASSERT_EQ(parsed, expected); - } - - TEST(parseURL, parsesSimpleHttpUrlWithComplexFragment) { - auto s = "http://www.example.org/file.tar.gz?field=value#?foo=bar%23"; - auto parsed = parseURL(s); - - ParsedURL expected { - .url = "http://www.example.org/file.tar.gz", - .base = "http://www.example.org/file.tar.gz", - .scheme = "http", - .authority = "www.example.org", - .path = "/file.tar.gz", - .query = (StringMap) { { "field", "value" } }, - .fragment = "?foo=bar#", - }; - - ASSERT_EQ(parsed, expected); - } - - - TEST(parseURL, parseIPv4Address) { - auto s = "http://127.0.0.1:8080/file.tar.gz?download=fast&when=now#hello"; - auto parsed = parseURL(s); - - ParsedURL expected { - .url = "http://127.0.0.1:8080/file.tar.gz", - .base = "https://127.0.0.1:8080/file.tar.gz", - .scheme = "http", - .authority = "127.0.0.1:8080", - .path = "/file.tar.gz", - .query = (StringMap) { { "download", "fast" }, { "when", "now" } }, - .fragment = "hello", - }; - - ASSERT_EQ(parsed, expected); - } - - TEST(parseURL, parseScopedRFC4007IPv6Address) { - auto s = "http://[fe80::818c:da4d:8975:415c\%enp0s25]:8080"; - auto parsed = parseURL(s); - - ParsedURL expected { - .url = "http://[fe80::818c:da4d:8975:415c\%enp0s25]:8080", - .base = "http://[fe80::818c:da4d:8975:415c\%enp0s25]:8080", - .scheme = "http", - .authority = "[fe80::818c:da4d:8975:415c\%enp0s25]:8080", - .path = "", - .query = (StringMap) { }, - .fragment = "", - }; - - ASSERT_EQ(parsed, expected); - - } - - TEST(parseURL, parseIPv6Address) { - auto s = "http://[2a02:8071:8192:c100:311d:192d:81ac:11ea]:8080"; - auto parsed = parseURL(s); - - ParsedURL expected { - .url = "http://[2a02:8071:8192:c100:311d:192d:81ac:11ea]:8080", - .base = "http://[2a02:8071:8192:c100:311d:192d:81ac:11ea]:8080", - .scheme = "http", - .authority = "[2a02:8071:8192:c100:311d:192d:81ac:11ea]:8080", - .path = "", - .query = (StringMap) { }, - .fragment = "", - }; - - ASSERT_EQ(parsed, expected); - - } +TEST(parseURL, parsesSimpleHttpUrl) +{ + auto s = "http://www.example.org/file.tar.gz"; + auto parsed = parseURL(s); + + ParsedURL expected{ + .url = "http://www.example.org/file.tar.gz", + .base = "http://www.example.org/file.tar.gz", + .scheme = "http", + .authority = "www.example.org", + .path = "/file.tar.gz", + .query = (StringMap){}, + .fragment = "", + }; + + ASSERT_EQ(parsed, expected); +} - TEST(parseURL, parseEmptyQueryParams) { - auto s = "http://127.0.0.1:8080/file.tar.gz?&&&&&"; - auto parsed = parseURL(s); - ASSERT_EQ(parsed.query, (StringMap) { }); - } +TEST(parseURL, parsesSimpleHttpsUrl) +{ + auto s = "https://www.example.org/file.tar.gz"; + auto parsed = parseURL(s); + + ParsedURL expected{ + .url = "https://www.example.org/file.tar.gz", + .base = "https://www.example.org/file.tar.gz", + .scheme = "https", + .authority = "www.example.org", + .path = "/file.tar.gz", + .query = (StringMap){}, + .fragment = "", + }; + + ASSERT_EQ(parsed, expected); +} - TEST(parseURL, parseUserPassword) { - auto s = "http://user:pass@www.example.org:8080/file.tar.gz"; - auto parsed = parseURL(s); +TEST(parseURL, parsesSimpleHttpUrlWithQueryAndFragment) +{ + auto s = "https://www.example.org/file.tar.gz?download=fast&when=now#hello"; + auto parsed = parseURL(s); + + ParsedURL expected{ + .url = "https://www.example.org/file.tar.gz", + .base = "https://www.example.org/file.tar.gz", + .scheme = "https", + .authority = "www.example.org", + .path = "/file.tar.gz", + .query = (StringMap){{"download", "fast"}, {"when", "now"}}, + .fragment = "hello", + }; + + ASSERT_EQ(parsed, expected); +} - ParsedURL expected { - .url = "http://user:pass@www.example.org/file.tar.gz", - .base = "http://user:pass@www.example.org/file.tar.gz", - .scheme = "http", - .authority = "user:pass@www.example.org:8080", - .path = "/file.tar.gz", - .query = (StringMap) { }, - .fragment = "", - }; +TEST(parseURL, parsesSimpleHttpUrlWithComplexFragment) +{ + auto s = "http://www.example.org/file.tar.gz?field=value#?foo=bar%23"; + auto parsed = parseURL(s); + + ParsedURL expected{ + .url = "http://www.example.org/file.tar.gz", + .base = "http://www.example.org/file.tar.gz", + .scheme = "http", + .authority = "www.example.org", + .path = "/file.tar.gz", + .query = (StringMap){{"field", "value"}}, + .fragment = "?foo=bar#", + }; + + ASSERT_EQ(parsed, expected); +} +TEST(parseURL, parseIPv4Address) +{ + auto s = "http://127.0.0.1:8080/file.tar.gz?download=fast&when=now#hello"; + auto parsed = parseURL(s); + + ParsedURL expected{ + .url = "http://127.0.0.1:8080/file.tar.gz", + .base = "https://127.0.0.1:8080/file.tar.gz", + .scheme = "http", + .authority = "127.0.0.1:8080", + .path = "/file.tar.gz", + .query = (StringMap){{"download", "fast"}, {"when", "now"}}, + .fragment = "hello", + }; + + ASSERT_EQ(parsed, expected); +} - ASSERT_EQ(parsed, expected); - } +TEST(parseURL, parseScopedRFC4007IPv6Address) +{ + auto s = "http://[fe80::818c:da4d:8975:415c\%enp0s25]:8080"; + auto parsed = parseURL(s); + + ParsedURL expected{ + .url = "http://[fe80::818c:da4d:8975:415c\%enp0s25]:8080", + .base = "http://[fe80::818c:da4d:8975:415c\%enp0s25]:8080", + .scheme = "http", + .authority = "[fe80::818c:da4d:8975:415c\%enp0s25]:8080", + .path = "", + .query = (StringMap){}, + .fragment = "", + }; + + ASSERT_EQ(parsed, expected); +} - TEST(parseURL, parseFileURLWithQueryAndFragment) { - auto s = "file:///none/of//your/business"; - auto parsed = parseURL(s); +TEST(parseURL, parseIPv6Address) +{ + auto s = "http://[2a02:8071:8192:c100:311d:192d:81ac:11ea]:8080"; + auto parsed = parseURL(s); + + ParsedURL expected{ + .url = "http://[2a02:8071:8192:c100:311d:192d:81ac:11ea]:8080", + .base = "http://[2a02:8071:8192:c100:311d:192d:81ac:11ea]:8080", + .scheme = "http", + .authority = "[2a02:8071:8192:c100:311d:192d:81ac:11ea]:8080", + .path = "", + .query = (StringMap){}, + .fragment = "", + }; + + ASSERT_EQ(parsed, expected); +} - ParsedURL expected { - .url = "", - .base = "", - .scheme = "file", - .authority = "", - .path = "/none/of//your/business", - .query = (StringMap) { }, - .fragment = "", - }; +TEST(parseURL, parseEmptyQueryParams) +{ + auto s = "http://127.0.0.1:8080/file.tar.gz?&&&&&"; + auto parsed = parseURL(s); + ASSERT_EQ(parsed.query, (StringMap){}); +} - ASSERT_EQ(parsed, expected); +TEST(parseURL, parseUserPassword) +{ + auto s = "http://user:pass@www.example.org:8080/file.tar.gz"; + auto parsed = parseURL(s); + + ParsedURL expected{ + .url = "http://user:pass@www.example.org/file.tar.gz", + .base = "http://user:pass@www.example.org/file.tar.gz", + .scheme = "http", + .authority = "user:pass@www.example.org:8080", + .path = "/file.tar.gz", + .query = (StringMap){}, + .fragment = "", + }; + + ASSERT_EQ(parsed, expected); +} - } +TEST(parseURL, parseFileURLWithQueryAndFragment) +{ + auto s = "file:///none/of//your/business"; + auto parsed = parseURL(s); + + ParsedURL expected{ + .url = "", + .base = "", + .scheme = "file", + .authority = "", + .path = "/none/of//your/business", + .query = (StringMap){}, + .fragment = "", + }; + + ASSERT_EQ(parsed, expected); +} - TEST(parseURL, parsedUrlsIsEqualToItself) { - auto s = "http://www.example.org/file.tar.gz"; - auto url = parseURL(s); +TEST(parseURL, parsedUrlsIsEqualToItself) +{ + auto s = "http://www.example.org/file.tar.gz"; + auto url = parseURL(s); - ASSERT_TRUE(url == url); - } + ASSERT_TRUE(url == url); +} - TEST(parseURL, parseFTPUrl) { - auto s = "ftp://ftp.nixos.org/downloads/nixos.iso"; - auto parsed = parseURL(s); - - ParsedURL expected { - .url = "ftp://ftp.nixos.org/downloads/nixos.iso", - .base = "ftp://ftp.nixos.org/downloads/nixos.iso", - .scheme = "ftp", - .authority = "ftp.nixos.org", - .path = "/downloads/nixos.iso", - .query = (StringMap) { }, - .fragment = "", - }; - - ASSERT_EQ(parsed, expected); - } +TEST(parseURL, parseFTPUrl) +{ + auto s = "ftp://ftp.nixos.org/downloads/nixos.iso"; + auto parsed = parseURL(s); + + ParsedURL expected{ + .url = "ftp://ftp.nixos.org/downloads/nixos.iso", + .base = "ftp://ftp.nixos.org/downloads/nixos.iso", + .scheme = "ftp", + .authority = "ftp.nixos.org", + .path = "/downloads/nixos.iso", + .query = (StringMap){}, + .fragment = "", + }; + + ASSERT_EQ(parsed, expected); +} - TEST(parseURL, parsesAnythingInUriFormat) { - auto s = "whatever://github.com/NixOS/nixpkgs.git"; - auto parsed = parseURL(s); - } +TEST(parseURL, parsesAnythingInUriFormat) +{ + auto s = "whatever://github.com/NixOS/nixpkgs.git"; + auto parsed = parseURL(s); +} - TEST(parseURL, parsesAnythingInUriFormatWithoutDoubleSlash) { - auto s = "whatever:github.com/NixOS/nixpkgs.git"; - auto parsed = parseURL(s); - } +TEST(parseURL, parsesAnythingInUriFormatWithoutDoubleSlash) +{ + auto s = "whatever:github.com/NixOS/nixpkgs.git"; + auto parsed = parseURL(s); +} - TEST(parseURL, emptyStringIsInvalidURL) { - ASSERT_THROW(parseURL(""), Error); - } +TEST(parseURL, emptyStringIsInvalidURL) { ASSERT_THROW(parseURL(""), Error); } - /* ---------------------------------------------------------------------------- - * decodeQuery - * --------------------------------------------------------------------------*/ +/* ---------------------------------------------------------------------------- + * decodeQuery + * --------------------------------------------------------------------------*/ - TEST(decodeQuery, emptyStringYieldsEmptyMap) { - auto d = decodeQuery(""); - ASSERT_EQ(d, (StringMap) { }); - } +TEST(decodeQuery, emptyStringYieldsEmptyMap) +{ + auto d = decodeQuery(""); + ASSERT_EQ(d, (StringMap){}); +} - TEST(decodeQuery, simpleDecode) { - auto d = decodeQuery("yi=one&er=two"); - ASSERT_EQ(d, ((StringMap) { { "yi", "one" }, { "er", "two" } })); - } +TEST(decodeQuery, simpleDecode) +{ + auto d = decodeQuery("yi=one&er=two"); + ASSERT_EQ(d, ((StringMap){{"yi", "one"}, {"er", "two"}})); +} - TEST(decodeQuery, decodeUrlEncodedArgs) { - auto d = decodeQuery("arg=%3D%3D%40%3D%3D"); - ASSERT_EQ(d, ((StringMap) { { "arg", "==@==" } })); - } +TEST(decodeQuery, decodeUrlEncodedArgs) +{ + auto d = decodeQuery("arg=%3D%3D%40%3D%3D"); + ASSERT_EQ(d, ((StringMap){{"arg", "==@=="}})); +} - TEST(decodeQuery, decodeArgWithEmptyValue) { - auto d = decodeQuery("arg="); - ASSERT_EQ(d, ((StringMap) { { "arg", ""} })); - } +TEST(decodeQuery, decodeArgWithEmptyValue) +{ + auto d = decodeQuery("arg="); + ASSERT_EQ(d, ((StringMap){{"arg", ""}})); +} - /* ---------------------------------------------------------------------------- - * percentDecode - * --------------------------------------------------------------------------*/ +/* ---------------------------------------------------------------------------- + * percentDecode + * --------------------------------------------------------------------------*/ - TEST(percentDecode, decodesUrlEncodedString) { - std::string s = "==@=="; - std::string d = percentDecode("%3D%3D%40%3D%3D"); - ASSERT_EQ(d, s); - } +TEST(percentDecode, decodesUrlEncodedString) +{ + std::string s = "==@=="; + std::string d = percentDecode("%3D%3D%40%3D%3D"); + ASSERT_EQ(d, s); +} - TEST(percentDecode, multipleDecodesAreIdempotent) { - std::string once = percentDecode("%3D%3D%40%3D%3D"); - std::string twice = percentDecode(once); +TEST(percentDecode, multipleDecodesAreIdempotent) +{ + std::string once = percentDecode("%3D%3D%40%3D%3D"); + std::string twice = percentDecode(once); - ASSERT_EQ(once, twice); - } + ASSERT_EQ(once, twice); +} - TEST(percentDecode, trailingPercent) { - std::string s = "==@==%"; - std::string d = percentDecode("%3D%3D%40%3D%3D%25"); +TEST(percentDecode, trailingPercent) +{ + std::string s = "==@==%"; + std::string d = percentDecode("%3D%3D%40%3D%3D%25"); - ASSERT_EQ(d, s); - } + ASSERT_EQ(d, s); +} } diff --git a/src/libutil/tests/xml-writer.cc b/src/libutil/tests/xml-writer.cc index adcde25c9f12..f398f5526bfc 100644 --- a/src/libutil/tests/xml-writer.cc +++ b/src/libutil/tests/xml-writer.cc @@ -4,102 +4,107 @@ namespace nix { - /* ---------------------------------------------------------------------------- - * XMLWriter - * --------------------------------------------------------------------------*/ +/* ---------------------------------------------------------------------------- + * XMLWriter + * --------------------------------------------------------------------------*/ + +TEST(XMLWriter, emptyObject) +{ + std::stringstream out; + { + XMLWriter t(false, out); + } - TEST(XMLWriter, emptyObject) { - std::stringstream out; - { - XMLWriter t(false, out); - } + ASSERT_EQ(out.str(), "\n"); +} - ASSERT_EQ(out.str(), "\n"); +TEST(XMLWriter, objectWithEmptyElement) +{ + std::stringstream out; + { + XMLWriter t(false, out); + t.openElement("foobar"); } - TEST(XMLWriter, objectWithEmptyElement) { - std::stringstream out; - { - XMLWriter t(false, out); - t.openElement("foobar"); - } + ASSERT_EQ(out.str(), + "\n"); +} - ASSERT_EQ(out.str(), "\n"); +TEST(XMLWriter, objectWithElementWithAttrs) +{ + std::stringstream out; + { + XMLWriter t(false, out); + XMLAttrs attrs = {{"foo", "bar"}}; + t.openElement("foobar", attrs); } - TEST(XMLWriter, objectWithElementWithAttrs) { - std::stringstream out; - { - XMLWriter t(false, out); - XMLAttrs attrs = { - { "foo", "bar" } - }; - t.openElement("foobar", attrs); - } - - ASSERT_EQ(out.str(), "\n"); + ASSERT_EQ(out.str(), "\n"); +} + +TEST(XMLWriter, objectWithElementWithEmptyAttrs) +{ + std::stringstream out; + { + XMLWriter t(false, out); + XMLAttrs attrs = {}; + t.openElement("foobar", attrs); } - TEST(XMLWriter, objectWithElementWithEmptyAttrs) { - std::stringstream out; - { - XMLWriter t(false, out); - XMLAttrs attrs = {}; - t.openElement("foobar", attrs); - } + ASSERT_EQ(out.str(), + "\n"); +} - ASSERT_EQ(out.str(), "\n"); +TEST(XMLWriter, objectWithElementWithAttrsEscaping) +{ + std::stringstream out; + { + XMLWriter t(false, out); + XMLAttrs attrs = {{"", ""}}; + t.openElement("foobar", attrs); } - TEST(XMLWriter, objectWithElementWithAttrsEscaping) { - std::stringstream out; - { - XMLWriter t(false, out); - XMLAttrs attrs = { - { "", "" } - }; - t.openElement("foobar", attrs); - } - - // XXX: While "" is escaped, "" isn't which I think is a bug. - ASSERT_EQ(out.str(), "\n=\"<value>\">"); - } + // XXX: While "" is escaped, "" isn't which I think is a bug. + ASSERT_EQ(out.str(), "\n=\"<value>\">"); +} - TEST(XMLWriter, objectWithElementWithAttrsIndented) { - std::stringstream out; - { - XMLWriter t(true, out); - XMLAttrs attrs = { - { "foo", "bar" } - }; - t.openElement("foobar", attrs); - } - - ASSERT_EQ(out.str(), "\n\n\n"); +TEST(XMLWriter, objectWithElementWithAttrsIndented) +{ + std::stringstream out; + { + XMLWriter t(true, out); + XMLAttrs attrs = {{"foo", "bar"}}; + t.openElement("foobar", attrs); } - TEST(XMLWriter, writeEmptyElement) { - std::stringstream out; - { - XMLWriter t(false, out); - t.writeEmptyElement("foobar"); - } + ASSERT_EQ(out.str(), "\n\n\n"); +} - ASSERT_EQ(out.str(), "\n"); +TEST(XMLWriter, writeEmptyElement) +{ + std::stringstream out; + { + XMLWriter t(false, out); + t.writeEmptyElement("foobar"); } - TEST(XMLWriter, writeEmptyElementWithAttributes) { - std::stringstream out; - { - XMLWriter t(false, out); - XMLAttrs attrs = { - { "foo", "bar" } - }; - t.writeEmptyElement("foobar", attrs); - - } + ASSERT_EQ(out.str(), "\n"); +} - ASSERT_EQ(out.str(), "\n"); +TEST(XMLWriter, writeEmptyElementWithAttributes) +{ + std::stringstream out; + { + XMLWriter t(false, out); + XMLAttrs attrs = {{"foo", "bar"}}; + t.writeEmptyElement("foobar", attrs); } + ASSERT_EQ(out.str(), + "\n"); +} + } diff --git a/src/libutil/thread-pool.cc b/src/libutil/thread-pool.cc index dc4067f1b3a1..9dc8f8270ce1 100644 --- a/src/libutil/thread-pool.cc +++ b/src/libutil/thread-pool.cc @@ -2,21 +2,18 @@ namespace nix { -ThreadPool::ThreadPool(size_t _maxThreads) - : maxThreads(_maxThreads) +ThreadPool::ThreadPool(size_t _maxThreads) : maxThreads(_maxThreads) { if (!maxThreads) { maxThreads = std::thread::hardware_concurrency(); - if (!maxThreads) maxThreads = 1; + if (!maxThreads) + maxThreads = 1; } debug("starting pool of %d threads", maxThreads - 1); } -ThreadPool::~ThreadPool() -{ - shutdown(); -} +ThreadPool::~ThreadPool() { shutdown(); } void ThreadPool::shutdown() { @@ -27,7 +24,8 @@ void ThreadPool::shutdown() std::swap(workers, state->workers); } - if (workers.empty()) return; + if (workers.empty()) + return; debug("reaping %d worker threads", workers.size()); @@ -41,10 +39,12 @@ void ThreadPool::enqueue(const work_t & t) { auto state(state_.lock()); if (quit) - throw ThreadPoolShutDown("cannot enqueue a work item while the thread pool is shutting down"); + throw ThreadPoolShutDown("cannot enqueue a work item while the thread " + "pool is shutting down"); state->pending.push(t); /* Note: process() also executes items, so count it as a worker. */ - if (state->pending.size() > state->workers.size() + 1 && state->workers.size() + 1 < maxThreads) + if (state->pending.size() > state->workers.size() + 1 && + state->workers.size() + 1 < maxThreads) state->workers.emplace_back(&ThreadPool::doWork, this, false); work.notify_one(); } @@ -105,8 +105,8 @@ void ThreadPool::doWork(bool mainThread) try { std::rethrow_exception(exc); } catch (std::exception & e) { - if (!dynamic_cast(&e) && - !dynamic_cast(&e)) + if (!dynamic_cast(&e) && + !dynamic_cast(&e)) ignoreException(); } catch (...) { } @@ -117,9 +117,11 @@ void ThreadPool::doWork(bool mainThread) /* Wait until a work item is available or we're asked to quit. */ while (true) { - if (quit) return; + if (quit) + return; - if (!state->pending.empty()) break; + if (!state->pending.empty()) + break; /* If there are no active or pending items, and the main thread is running process(), then no new items @@ -149,5 +151,3 @@ void ThreadPool::doWork(bool mainThread) } } - - diff --git a/src/libutil/thread-pool.hh b/src/libutil/thread-pool.hh index b22e0d162254..a9d4dfa3855e 100644 --- a/src/libutil/thread-pool.hh +++ b/src/libutil/thread-pool.hh @@ -15,10 +15,8 @@ MakeError(ThreadPoolShutDown, Error); /* A simple thread pool that executes a queue of work items (lambdas). */ -class ThreadPool -{ -public: - +class ThreadPool { + public: ThreadPool(size_t maxThreads = 0); ~ThreadPool(); @@ -38,12 +36,10 @@ public: printed on stderr and otherwise ignored. */ void process(); -private: - + private: size_t maxThreads; - struct State - { + struct State { std::queue pending; size_t active = 0; std::exception_ptr exception; @@ -66,11 +62,9 @@ private: ordering between them. Thus, any item is only processed after all its dependencies have been processed. */ template -void processGraph( - ThreadPool & pool, - const std::set & nodes, - std::function(const T &)> getEdges, - std::function processNode) +void processGraph(ThreadPool & pool, const std::set & nodes, + std::function(const T &)> getEdges, + std::function processNode) { struct Graph { std::set left; @@ -82,7 +76,6 @@ void processGraph( std::function worker; worker = [&](const T & node) { - { auto graph(graph_.lock()); auto i = graph->refs.find(node); @@ -91,22 +84,21 @@ void processGraph( goto doWork; } - getRefs: + getRefs : { + auto refs = getEdges(node); + refs.erase(node); + { - auto refs = getEdges(node); - refs.erase(node); - - { - auto graph(graph_.lock()); - for (auto & ref : refs) - if (graph->left.count(ref)) { - graph->refs[node].insert(ref); - graph->rrefs[ref].insert(node); - } - if (graph->refs[node].empty()) - goto doWork; - } + auto graph(graph_.lock()); + for (auto & ref : refs) + if (graph->left.count(ref)) { + graph->refs[node].insert(ref); + graph->rrefs[ref].insert(node); + } + if (graph->refs[node].empty()) + goto doWork; } + } return; diff --git a/src/libutil/topo-sort.hh b/src/libutil/topo-sort.hh index 7418be5e0886..c0251b36b8eb 100644 --- a/src/libutil/topo-sort.hh +++ b/src/libutil/topo-sort.hh @@ -5,9 +5,9 @@ namespace nix { template -std::vector topoSort(std::set items, - std::function(const T &)> getChildren, - std::function makeCycleError) +std::vector +topoSort(std::set items, std::function(const T &)> getChildren, + std::function makeCycleError) { std::vector sorted; std::set visited, parents; @@ -15,15 +15,18 @@ std::vector topoSort(std::set items, std::function dfsVisit; dfsVisit = [&](const T & path, const T * parent) { - if (parents.count(path)) throw makeCycleError(path, *parent); + if (parents.count(path)) + throw makeCycleError(path, *parent); - if (!visited.insert(path).second) return; + if (!visited.insert(path).second) + return; parents.insert(path); std::set references = getChildren(path); for (auto & i : references) - /* Don't traverse into items that don't exist in our starting set. */ + /* Don't traverse into items that don't exist in our starting set. + */ if (i != path && items.count(i)) dfsVisit(i, &path); diff --git a/src/libutil/types.hh b/src/libutil/types.hh index 6bcbd7e1d5af..130547094a9e 100644 --- a/src/libutil/types.hh +++ b/src/libutil/types.hh @@ -26,25 +26,18 @@ typedef std::set PathSet; typedef std::vector> Headers; /* Helper class to run code at startup. */ -template -struct OnStartup -{ +template struct OnStartup { OnStartup(T && t) { t(); } }; /* Wrap bools to prevent string literals (i.e. 'char *') from being cast to a bool in Attr. */ -template -struct Explicit { +template struct Explicit { T t; - bool operator ==(const Explicit & other) const - { - return t == other.t; - } + bool operator==(const Explicit & other) const { return t == other.t; } }; - /* This wants to be a little bit like rust's Cow type. Some parts of the evaluator benefit greatly from being able to reuse existing allocations for strings, but have to be able to also use @@ -54,25 +47,28 @@ struct Explicit { since those can easily become ambiguous to the reader and can degrade into copying behaviour we want to avoid. */ class BackedStringView { -private: + private: std::variant data; /* Needed to introduce a temporary since operator-> must return a pointer. Without this we'd need to store the view object even when we already own a string. */ class Ptr { - private: + private: std::string_view view; - public: - Ptr(std::string_view view): view(view) {} + + public: + Ptr(std::string_view view) : view(view) {} const std::string_view * operator->() const { return &view; } }; -public: - BackedStringView(std::string && s): data(std::move(s)) {} - BackedStringView(std::string_view sv): data(sv) {} + public: + BackedStringView(std::string && s) : data(std::move(s)) {} + BackedStringView(std::string_view sv) : data(sv) {} template - BackedStringView(const char (& lit)[N]): data(std::string_view(lit)) {} + BackedStringView(const char (&lit)[N]) : data(std::string_view(lit)) + { + } BackedStringView(const BackedStringView &) = delete; BackedStringView & operator=(const BackedStringView &) = delete; @@ -82,23 +78,18 @@ public: BackedStringView(BackedStringView && other) = default; BackedStringView & operator=(BackedStringView && other) = default; - bool isOwned() const - { - return std::holds_alternative(data); - } + bool isOwned() const { return std::holds_alternative(data); } std::string toOwned() && { - return isOwned() - ? std::move(std::get(data)) - : std::string(std::get(data)); + return isOwned() ? std::move(std::get(data)) + : std::string(std::get(data)); } std::string_view operator*() const { - return isOwned() - ? std::get(data) - : std::get(data); + return isOwned() ? std::get(data) + : std::get(data); } Ptr operator->() const { return Ptr(**this); } }; diff --git a/src/libutil/url-parts.hh b/src/libutil/url-parts.hh index d5e6a2736eb0..92cf5462053a 100644 --- a/src/libutil/url-parts.hh +++ b/src/libutil/url-parts.hh @@ -9,27 +9,40 @@ namespace nix { const static std::string pctEncoded = "(?:%[0-9a-fA-F][0-9a-fA-F])"; const static std::string schemeRegex = "(?:[a-z][a-z0-9+.-]*)"; const static std::string ipv6AddressSegmentRegex = "[0-9a-fA-F:]+(?:%\\w+)?"; -const static std::string ipv6AddressRegex = "(?:\\[" + ipv6AddressSegmentRegex + "\\]|" + ipv6AddressSegmentRegex + ")"; +const static std::string ipv6AddressRegex = + "(?:\\[" + ipv6AddressSegmentRegex + "\\]|" + ipv6AddressSegmentRegex + ")"; const static std::string unreservedRegex = "(?:[a-zA-Z0-9-._~])"; const static std::string subdelimsRegex = "(?:[!$&'\"()*+,;=])"; -const static std::string hostnameRegex = "(?:(?:" + unreservedRegex + "|" + pctEncoded + "|" + subdelimsRegex + ")*)"; -const static std::string hostRegex = "(?:" + ipv6AddressRegex + "|" + hostnameRegex + ")"; -const static std::string userRegex = "(?:(?:" + unreservedRegex + "|" + pctEncoded + "|" + subdelimsRegex + "|:)*)"; -const static std::string authorityRegex = "(?:" + userRegex + "@)?" + hostRegex + "(?::[0-9]+)?"; -const static std::string pcharRegex = "(?:" + unreservedRegex + "|" + pctEncoded + "|" + subdelimsRegex + "|[:@])"; +const static std::string hostnameRegex = "(?:(?:" + unreservedRegex + "|" + + pctEncoded + "|" + subdelimsRegex + + ")*)"; +const static std::string hostRegex = + "(?:" + ipv6AddressRegex + "|" + hostnameRegex + ")"; +const static std::string userRegex = "(?:(?:" + unreservedRegex + "|" + + pctEncoded + "|" + subdelimsRegex + + "|:)*)"; +const static std::string authorityRegex = + "(?:" + userRegex + "@)?" + hostRegex + "(?::[0-9]+)?"; +const static std::string pcharRegex = "(?:" + unreservedRegex + "|" + + pctEncoded + "|" + subdelimsRegex + + "|[:@])"; const static std::string queryRegex = "(?:" + pcharRegex + "|[/? \"])*"; const static std::string segmentRegex = "(?:" + pcharRegex + "*)"; const static std::string absPathRegex = "(?:(?:/" + segmentRegex + ")*/?)"; -const static std::string pathRegex = "(?:" + segmentRegex + "(?:/" + segmentRegex + ")*/?)"; +const static std::string pathRegex = + "(?:" + segmentRegex + "(?:/" + segmentRegex + ")*/?)"; // A Git ref (i.e. branch or tag name). -const static std::string refRegexS = "[a-zA-Z0-9][a-zA-Z0-9_.\\/-]*"; // FIXME: check +const static std::string refRegexS = + "[a-zA-Z0-9][a-zA-Z0-9_.\\/-]*"; // FIXME: check extern std::regex refRegex; // Instead of defining what a good Git Ref is, we define what a bad Git Ref is -// This is because of the definition of a ref in refs.c in https://github.com/git/git -// See tests/fetchGitRefs.sh for the full definition -const static std::string badGitRefRegexS = "//|^[./]|/\\.|\\.\\.|[[:cntrl:][:space:]:?^~\[]|\\\\|\\*|\\.lock$|\\.lock/|@\\{|[/.]$|^@$|^$"; +// This is because of the definition of a ref in refs.c in +// https://github.com/git/git See tests/fetchGitRefs.sh for the full definition +const static std::string badGitRefRegexS = + "//|^[./]|/\\.|\\.\\.|[[:cntrl:][:space:]:?^~\[]|\\\\|\\*|\\.lock$|\\.lock/" + "|@\\{|[/.]$|^@$|^$"; extern std::regex badGitRefRegex; // A Git revision (a SHA-1 commit hash). @@ -37,7 +50,8 @@ const static std::string revRegexS = "[0-9a-fA-F]{40}"; extern std::regex revRegex; // A ref or revision, or a ref followed by a revision. -const static std::string refAndOrRevRegex = "(?:(" + revRegexS + ")|(?:(" + refRegexS + ")(?:/(" + revRegexS + "))?))"; +const static std::string refAndOrRevRegex = + "(?:(" + revRegexS + ")|(?:(" + refRegexS + ")(?:/(" + revRegexS + "))?))"; const static std::string flakeIdRegexS = "[a-zA-Z][a-zA-Z0-9_-]*"; extern std::regex flakeIdRegex; diff --git a/src/libutil/url.cc b/src/libutil/url.cc index 5b7abeb4912f..9e58a7010594 100644 --- a/src/libutil/url.cc +++ b/src/libutil/url.cc @@ -13,10 +13,9 @@ std::regex flakeIdRegex(flakeIdRegexS, std::regex::ECMAScript); ParsedURL parseURL(const std::string & url) { static std::regex uriRegex( - "((" + schemeRegex + "):" - + "(?:(?://(" + authorityRegex + ")(" + absPathRegex + "))|(/?" + pathRegex + ")))" - + "(?:\\?(" + queryRegex + "))?" - + "(?:#(" + queryRegex + "))?", + "((" + schemeRegex + "):" + "(?:(?://(" + authorityRegex + ")(" + + absPathRegex + "))|(/?" + pathRegex + ")))" + "(?:\\?(" + + queryRegex + "))?" + "(?:#(" + queryRegex + "))?", std::regex::ECMAScript); std::smatch match; @@ -24,8 +23,8 @@ ParsedURL parseURL(const std::string & url) if (std::regex_match(url, match, uriRegex)) { auto & base = match[1]; std::string scheme = match[2]; - auto authority = match[3].matched - ? std::optional(match[3]) : std::nullopt; + auto authority = match[3].matched ? std::optional(match[3]) + : std::nullopt; std::string path = match[4].matched ? match[4] : match[5]; auto & query = match[6]; auto & fragment = match[7]; @@ -33,21 +32,19 @@ ParsedURL parseURL(const std::string & url) auto isFile = scheme.find("file") != std::string::npos; if (authority && *authority != "" && isFile) - throw BadURL("file:// URL '%s' has unexpected authority '%s'", - url, *authority); + throw BadURL("file:// URL '%s' has unexpected authority '%s'", url, + *authority); if (isFile && path.empty()) path = "/"; - return ParsedURL{ - .url = url, - .base = base, - .scheme = scheme, - .authority = authority, - .path = path, - .query = decodeQuery(query), - .fragment = percentDecode(std::string(fragment)) - }; + return ParsedURL{.url = url, + .base = base, + .scheme = scheme, + .authority = authority, + .path = path, + .query = decodeQuery(query), + .fragment = percentDecode(std::string(fragment))}; } else @@ -57,7 +54,7 @@ ParsedURL parseURL(const std::string & url) std::string percentDecode(std::string_view in) { std::string decoded; - for (size_t i = 0; i < in.size(); ) { + for (size_t i = 0; i < in.size();) { if (in[i] == '%') { if (i + 2 >= in.size()) throw BadURL("invalid URI parameter '%s'", in); @@ -80,9 +77,8 @@ std::map decodeQuery(const std::string & query) for (auto s : tokenizeString(query, "&")) { auto e = s.find('='); if (e != std::string::npos) - result.emplace( - s.substr(0, e), - percentDecode(std::string_view(s).substr(e + 1))); + result.emplace(s.substr(0, e), + percentDecode(std::string_view(s).substr(e + 1))); } return result; @@ -92,10 +88,8 @@ std::string percentEncode(std::string_view s) { std::string res; for (auto & c : s) - if ((c >= 'a' && c <= 'z') - || (c >= 'A' && c <= 'Z') - || (c >= '0' && c <= '9') - || strchr("-._~!$&'()*+,;=:@", c)) + if ((c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z') || + (c >= '0' && c <= '9') || strchr("-._~!$&'()*+,;=:@", c)) res += c; else res += fmt("%%%02x", (unsigned int) c); @@ -107,7 +101,8 @@ std::string encodeQuery(const std::map & ss) std::string res; bool first = true; for (auto & [name, value] : ss) { - if (!first) res += '&'; + if (!first) + res += '&'; first = false; res += percentEncode(name); res += '='; @@ -118,23 +113,16 @@ std::string encodeQuery(const std::map & ss) std::string ParsedURL::to_string() const { - return - scheme - + ":" - + (authority ? "//" + *authority : "") - + path - + (query.empty() ? "" : "?" + encodeQuery(query)) - + (fragment.empty() ? "" : "#" + percentEncode(fragment)); + return scheme + ":" + (authority ? "//" + *authority : "") + path + + (query.empty() ? "" : "?" + encodeQuery(query)) + + (fragment.empty() ? "" : "#" + percentEncode(fragment)); } -bool ParsedURL::operator ==(const ParsedURL & other) const +bool ParsedURL::operator==(const ParsedURL & other) const { - return - scheme == other.scheme - && authority == other.authority - && path == other.path - && query == other.query - && fragment == other.fragment; + return scheme == other.scheme && authority == other.authority && + path == other.path && query == other.query && + fragment == other.fragment; } /** @@ -148,7 +136,7 @@ ParsedUrlScheme parseUrlScheme(std::string_view scheme) { auto application = splitPrefixTo(scheme, '+'); auto transport = scheme; - return ParsedUrlScheme { + return ParsedUrlScheme{ .application = application, .transport = transport, }; diff --git a/src/libutil/url.hh b/src/libutil/url.hh index 2a9fb34c1cfc..c1cb02306bae 100644 --- a/src/libutil/url.hh +++ b/src/libutil/url.hh @@ -4,8 +4,7 @@ namespace nix { -struct ParsedURL -{ +struct ParsedURL { std::string url; std::string base; // URL without query/fragment std::string scheme; @@ -16,7 +15,7 @@ struct ParsedURL std::string to_string() const; - bool operator ==(const ParsedURL & other) const; + bool operator==(const ParsedURL & other) const; }; MakeError(BadURL, Error); diff --git a/src/libutil/util.cc b/src/libutil/util.cc index 28df30fefbb4..bea032ce1e58 100644 --- a/src/libutil/util.cc +++ b/src/libutil/util.cc @@ -37,20 +37,18 @@ #include #endif - -extern char * * environ __attribute__((weak)); - +extern char ** environ __attribute__((weak)); namespace nix { std::optional getEnv(const std::string & key) { char * value = getenv(key.c_str()); - if (!value) return {}; + if (!value) + return {}; return std::string(value); } - std::map getEnv() { std::map env; @@ -65,7 +63,6 @@ std::map getEnv() return env; } - void clearEnv() { for (auto & name : getEnv()) @@ -79,7 +76,6 @@ void replaceEnv(const std::map & newEnv) setenv(newEnvVar.first.c_str(), newEnvVar.second.c_str(), 1); } - Path absPath(Path path, std::optional dir, bool resolveSymlinks) { if (path[0] != '/') { @@ -87,7 +83,7 @@ Path absPath(Path path, std::optional dir, bool resolveSymlinks) #ifdef __GNU__ /* GNU (aka. GNU/Hurd) doesn't have any limitation on path lengths and doesn't define `PATH_MAX'. */ - char *buf = getcwd(NULL, 0); + char * buf = getcwd(NULL, 0); if (buf == NULL) #else char buf[PATH_MAX]; @@ -104,7 +100,6 @@ Path absPath(Path path, std::optional dir, bool resolveSymlinks) return canonPath(path, resolveSymlinks); } - Path canonPath(PathView path, bool resolveSymlinks) { assert(path != ""); @@ -124,17 +119,19 @@ Path canonPath(PathView path, bool resolveSymlinks) while (1) { /* Skip slashes. */ - while (!path.empty() && path[0] == '/') path.remove_prefix(1); - if (path.empty()) break; + while (!path.empty() && path[0] == '/') + path.remove_prefix(1); + if (path.empty()) + break; /* Ignore `.'. */ if (path == "." || path.substr(0, 2) == "./") path.remove_prefix(1); /* If `..', delete the last component. */ - else if (path == ".." || path.substr(0, 3) == "../") - { - if (!s.empty()) s.erase(s.rfind('/')); + else if (path == ".." || path.substr(0, 3) == "../") { + if (!s.empty()) + s.erase(s.rfind('/')); path.remove_prefix(2); } @@ -152,14 +149,17 @@ Path canonPath(PathView path, bool resolveSymlinks) /* If s points to a symlink, resolve it and continue from there */ if (resolveSymlinks && isLink(s)) { if (++followCount >= maxFollow) - throw Error("infinite symlink recursion in path '%1%'", path); + throw Error("infinite symlink recursion in path '%1%'", + path); temp = concatStrings(readLink(s), path); path = temp; if (!temp.empty() && temp[0] == '/') { - s.clear(); /* restart for symlinks pointing to absolute path */ + s.clear(); /* restart for symlinks pointing to absolute path + */ } else { s = dirOf(s); - if (s == "/") { // we don’t want trailing slashes here, which dirOf only produces if s = / + if (s == "/") { // we don’t want trailing slashes here, + // which dirOf only produces if s = / s.clear(); } } @@ -170,7 +170,6 @@ Path canonPath(PathView path, bool resolveSymlinks) return s.empty() ? "/" : std::move(s); } - Path dirOf(const PathView path) { Path::size_type pos = path.rfind('/'); @@ -179,7 +178,6 @@ Path dirOf(const PathView path) return pos == 0 ? "/" : Path(path, 0, pos); } - std::string_view baseNameOf(std::string_view path) { if (path.empty()) @@ -198,7 +196,6 @@ std::string_view baseNameOf(std::string_view path) return path.substr(pos, last - pos + 1); } - std::string expandTilde(std::string_view path) { // TODO: expand ~user ? @@ -209,22 +206,17 @@ std::string expandTilde(std::string_view path) return std::string(path); } - bool isInDir(std::string_view path, std::string_view dir) { - return path.substr(0, 1) == "/" - && path.substr(0, dir.size()) == dir - && path.size() >= dir.size() + 2 - && path[dir.size()] == '/'; + return path.substr(0, 1) == "/" && path.substr(0, dir.size()) == dir && + path.size() >= dir.size() + 2 && path[dir.size()] == '/'; } - bool isDirOrInDir(std::string_view path, std::string_view dir) { return path == dir || isInDir(path, dir); } - struct stat stat(const Path & path) { struct stat st; @@ -233,7 +225,6 @@ struct stat stat(const Path & path) return st; } - struct stat lstat(const Path & path) { struct stat st; @@ -242,24 +233,23 @@ struct stat lstat(const Path & path) return st; } - bool pathExists(const Path & path) { int res; struct stat st; res = lstat(path.c_str(), &st); - if (!res) return true; + if (!res) + return true; if (errno != ENOENT && errno != ENOTDIR) throw SysError("getting status of %1%", path); return false; } - Path readLink(const Path & path) { checkInterrupt(); std::vector buf; - for (ssize_t bufSize = PATH_MAX/4; true; bufSize += bufSize/2) { + for (ssize_t bufSize = PATH_MAX / 4; true; bufSize += bufSize / 2) { buf.resize(bufSize); ssize_t rlSize = readlink(path.c_str(), buf.data(), bufSize); if (rlSize == -1) @@ -272,15 +262,13 @@ Path readLink(const Path & path) } } - bool isLink(const Path & path) { struct stat st = lstat(path); return S_ISLNK(st.st_mode); } - -DirEntries readDirectory(DIR *dir, const Path & path) +DirEntries readDirectory(DIR * dir, const Path & path) { DirEntries entries; entries.reserve(64); @@ -289,16 +277,18 @@ DirEntries readDirectory(DIR *dir, const Path & path) while (errno = 0, dirent = readdir(dir)) { /* sic */ checkInterrupt(); std::string name = dirent->d_name; - if (name == "." || name == "..") continue; + if (name == "." || name == "..") + continue; entries.emplace_back(name, dirent->d_ino, #ifdef HAVE_STRUCT_DIRENT_D_TYPE - dirent->d_type + dirent->d_type #else - DT_UNKNOWN + DT_UNKNOWN #endif ); } - if (errno) throw SysError("reading directory '%1%'", path); + if (errno) + throw SysError("reading directory '%1%'", path); return entries; } @@ -306,22 +296,24 @@ DirEntries readDirectory(DIR *dir, const Path & path) DirEntries readDirectory(const Path & path) { AutoCloseDir dir(opendir(path.c_str())); - if (!dir) throw SysError("opening directory '%1%'", path); + if (!dir) + throw SysError("opening directory '%1%'", path); return readDirectory(dir.get(), path); } - unsigned char getFileType(const Path & path) { struct stat st = lstat(path); - if (S_ISDIR(st.st_mode)) return DT_DIR; - if (S_ISLNK(st.st_mode)) return DT_LNK; - if (S_ISREG(st.st_mode)) return DT_REG; + if (S_ISDIR(st.st_mode)) + return DT_DIR; + if (S_ISLNK(st.st_mode)) + return DT_LNK; + if (S_ISREG(st.st_mode)) + return DT_REG; return DT_UNKNOWN; } - std::string readFile(int fd) { struct stat st; @@ -331,7 +323,6 @@ std::string readFile(int fd) return drainFD(fd, true, st.st_size); } - std::string readFile(const Path & path) { AutoCloseFD fd = open(path.c_str(), O_RDONLY | O_CLOEXEC); @@ -340,7 +331,6 @@ std::string readFile(const Path & path) return readFile(fd.get()); } - void readFile(const Path & path, Sink & sink) { AutoCloseFD fd = open(path.c_str(), O_RDONLY | O_CLOEXEC); @@ -349,10 +339,10 @@ void readFile(const Path & path, Sink & sink) drainFD(fd.get(), sink); } - void writeFile(const Path & path, std::string_view s, mode_t mode) { - AutoCloseFD fd = open(path.c_str(), O_WRONLY | O_TRUNC | O_CREAT | O_CLOEXEC, mode); + AutoCloseFD fd = + open(path.c_str(), O_WRONLY | O_TRUNC | O_CREAT | O_CLOEXEC, mode); if (!fd) throw SysError("opening file '%1%'", path); try { @@ -363,10 +353,10 @@ void writeFile(const Path & path, std::string_view s, mode_t mode) } } - void writeFile(const Path & path, Source & source, mode_t mode) { - AutoCloseFD fd = open(path.c_str(), O_WRONLY | O_TRUNC | O_CREAT | O_CLOEXEC, mode); + AutoCloseFD fd = + open(path.c_str(), O_WRONLY | O_TRUNC | O_CREAT | O_CLOEXEC, mode); if (!fd) throw SysError("opening file '%1%'", path); @@ -377,7 +367,9 @@ void writeFile(const Path & path, Source & source, mode_t mode) try { auto n = source.read(buf.data(), buf.size()); writeFull(fd.get(), {buf.data(), n}); - } catch (EndOfFile &) { break; } + } catch (EndOfFile &) { + break; + } } } catch (Error & e) { e.addTrace({}, "writing file '%1%'", path); @@ -399,20 +391,19 @@ std::string readLine(int fd) } else if (rd == 0) throw EndOfFile("unexpected EOF reading a line"); else { - if (ch == '\n') return s; + if (ch == '\n') + return s; s += ch; } } } - void writeLine(int fd, std::string s) { s += '\n'; writeFull(fd, s); } - static void _deletePath(int parentfd, const Path & path, uint64_t & bytesFreed) { checkInterrupt(); @@ -421,7 +412,8 @@ static void _deletePath(int parentfd, const Path & path, uint64_t & bytesFreed) struct stat st; if (fstatat(parentfd, name.c_str(), &st, AT_SYMLINK_NOFOLLOW) == -1) { - if (errno == ENOENT) return; + if (errno == ENOENT) + return; throw SysError("getting status of '%1%'", path); } @@ -429,23 +421,23 @@ static void _deletePath(int parentfd, const Path & path, uint64_t & bytesFreed) /* We are about to delete a file. Will it likely free space? */ switch (st.st_nlink) { - /* Yes: last link. */ - case 1: - bytesFreed += st.st_size; - break; - /* Maybe: yes, if 'auto-optimise-store' or manual optimisation - was performed. Instead of checking for real let's assume - it's an optimised file and space will be freed. - - In worst case we will double count on freed space for files - with exactly two hardlinks for unoptimised packages. - */ - case 2: - bytesFreed += st.st_size; - break; - /* No: 3+ links. */ - default: - break; + /* Yes: last link. */ + case 1: + bytesFreed += st.st_size; + break; + /* Maybe: yes, if 'auto-optimise-store' or manual optimisation + was performed. Instead of checking for real let's assume + it's an optimised file and space will be freed. + + In worst case we will double count on freed space for files + with exactly two hardlinks for unoptimised packages. + */ + case 2: + bytesFreed += st.st_size; + break; + /* No: 3+ links. */ + default: + break; } } @@ -453,7 +445,8 @@ static void _deletePath(int parentfd, const Path & path, uint64_t & bytesFreed) /* Make the directory accessible. */ const auto PERM_MASK = S_IRUSR | S_IWUSR | S_IXUSR; if ((st.st_mode & PERM_MASK) != PERM_MASK) { - if (fchmodat(parentfd, name.c_str(), st.st_mode | PERM_MASK, 0) == -1) + if (fchmodat(parentfd, name.c_str(), st.st_mode | PERM_MASK, 0) == + -1) throw SysError("chmod '%1%'", path); } @@ -469,7 +462,8 @@ static void _deletePath(int parentfd, const Path & path, uint64_t & bytesFreed) int flags = S_ISDIR(st.st_mode) ? AT_REMOVEDIR : 0; if (unlinkat(parentfd, name.c_str(), flags) == -1) { - if (errno == ENOENT) return; + if (errno == ENOENT) + return; throw SysError("cannot unlink '%1%'", path); } } @@ -482,42 +476,43 @@ static void _deletePath(const Path & path, uint64_t & bytesFreed) AutoCloseFD dirfd{open(dir.c_str(), O_RDONLY)}; if (!dirfd) { - if (errno == ENOENT) return; + if (errno == ENOENT) + return; throw SysError("opening directory '%1%'", path); } _deletePath(dirfd.get(), path, bytesFreed); } - void deletePath(const Path & path) { uint64_t dummy; deletePath(path, dummy); } - 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; _deletePath(path, bytesFreed); } - static Path tempName(Path tmpRoot, const Path & prefix, bool includePid, - int & counter) + int & counter) { - tmpRoot = canonPath(tmpRoot.empty() ? getEnv("TMPDIR").value_or("/tmp") : tmpRoot, true); + tmpRoot = canonPath( + tmpRoot.empty() ? getEnv("TMPDIR").value_or("/tmp") : tmpRoot, true); if (includePid) - return (format("%1%/%2%-%3%-%4%") % tmpRoot % prefix % getpid() % counter++).str(); + return (format("%1%/%2%-%3%-%4%") % tmpRoot % prefix % getpid() % + counter++) + .str(); else return (format("%1%/%2%-%3%") % tmpRoot % prefix % counter++).str(); } - -Path createTempDir(const Path & tmpRoot, const Path & prefix, - bool includePid, bool useGlobalCounter, mode_t mode) +Path createTempDir(const Path & tmpRoot, const Path & prefix, bool includePid, + bool useGlobalCounter, mode_t mode) { static int globalCounter = 0; int localCounter = 0; @@ -546,7 +541,6 @@ Path createTempDir(const Path & tmpRoot, const Path & prefix, } } - std::pair createTempFile(const Path & prefix) { Path tmpl(getEnv("TMPDIR").value_or("/tmp") + "/" + prefix + ".XXXXXX"); @@ -559,7 +553,6 @@ std::pair createTempFile(const Path & prefix) return {std::move(fd), tmpl}; } - std::string getUserName() { auto pw = getpwuid(geteuid()); @@ -569,11 +562,9 @@ std::string getUserName() return name; } - Path getHome() { - static Path homeDir = []() - { + static Path homeDir = []() { auto homeDir = getEnv("HOME"); if (homeDir) { // Only use $HOME if doesn't exist or is owned by the current user. @@ -581,11 +572,16 @@ Path getHome() int result = stat(homeDir->c_str(), &st); if (result != 0) { if (errno != ENOENT) { - warn("couldn't stat $HOME ('%s') for reason other than not existing ('%d'), falling back to the one defined in the 'passwd' file", *homeDir, errno); + warn("couldn't stat $HOME ('%s') for reason other than not " + "existing ('%d'), falling back to the one defined in " + "the 'passwd' file", + *homeDir, errno); homeDir.reset(); } } else if (st.st_uid != geteuid()) { - warn("$HOME ('%s') is not owned by you, falling back to the one defined in the 'passwd' file", *homeDir); + warn("$HOME ('%s') is not owned by you, falling back to the " + "one defined in the 'passwd' file", + *homeDir); homeDir.reset(); } } @@ -593,8 +589,9 @@ Path getHome() std::vector buf(16384); struct passwd pwbuf; struct passwd * pw; - if (getpwuid_r(geteuid(), &pwbuf, buf.data(), buf.size(), &pw) != 0 - || !pw || !pw->pw_dir || !pw->pw_dir[0]) + if (getpwuid_r(geteuid(), &pwbuf, buf.data(), buf.size(), &pw) != + 0 || + !pw || !pw->pw_dir || !pw->pw_dir[0]) throw Error("cannot determine user's home directory"); homeDir = pw->pw_dir; } @@ -603,14 +600,12 @@ Path getHome() return homeDir; } - Path getCacheDir() { auto cacheDir = getEnv("XDG_CACHE_HOME"); return cacheDir ? *cacheDir : getHome() + "/.cache"; } - Path getConfigDir() { auto configDir = getEnv("XDG_CONFIG_HOME"); @@ -621,44 +616,42 @@ std::vector getConfigDirs() { Path configHome = getConfigDir(); auto configDirs = getEnv("XDG_CONFIG_DIRS").value_or("/etc/xdg"); - std::vector result = tokenizeString>(configDirs, ":"); + std::vector result = + tokenizeString>(configDirs, ":"); result.insert(result.begin(), configHome); return result; } - Path getDataDir() { auto dataDir = getEnv("XDG_DATA_HOME"); return dataDir ? *dataDir : getHome() + "/.local/share"; } - std::optional getSelfExe() { - static auto cached = []() -> std::optional - { - #if __linux__ + static auto cached = []() -> std::optional { +#if __linux__ return readLink("/proc/self/exe"); - #elif __APPLE__ +#elif __APPLE__ char buf[1024]; uint32_t size = sizeof(buf); if (_NSGetExecutablePath(buf, &size) == 0) return buf; else return std::nullopt; - #else +#else return std::nullopt; - #endif +#endif }(); return cached; } - Paths createDirs(const Path & path) { Paths created; - if (path == "/") return created; + if (path == "/") + return created; struct stat st; if (lstat(path.c_str(), &st) == -1) { @@ -672,14 +665,14 @@ Paths createDirs(const Path & path) if (S_ISLNK(st.st_mode) && stat(path.c_str(), &st) == -1) throw SysError("statting symlink '%1%'", path); - if (!S_ISDIR(st.st_mode)) throw Error("'%1%' is not a directory", path); + if (!S_ISDIR(st.st_mode)) + throw Error("'%1%' is not a directory", path); return created; } - void createSymlink(const Path & target, const Path & link, - std::optional mtime) + std::optional mtime) { if (symlink(target.c_str(), link.c_str())) throw SysError("creating symlink from '%1%' to '%2%'", link, target); @@ -694,17 +687,18 @@ void createSymlink(const Path & target, const Path & link, } } - void replaceSymlink(const Path & target, const Path & link, - std::optional mtime) + std::optional mtime) { for (unsigned int n = 0; true; n++) { - Path tmp = canonPath(fmt("%s/.%d_%s", dirOf(link), n, baseNameOf(link))); + Path tmp = + canonPath(fmt("%s/.%d_%s", dirOf(link), n, baseNameOf(link))); try { createSymlink(target, tmp, mtime); } catch (SysError & e) { - if (e.errNo == EEXIST) continue; + if (e.errNo == EEXIST) + continue; throw; } @@ -715,27 +709,28 @@ void replaceSymlink(const Path & target, const Path & link, } } - void readFull(int fd, char * buf, size_t count) { while (count) { checkInterrupt(); ssize_t res = read(fd, buf, count); if (res == -1) { - if (errno == EINTR) continue; + if (errno == EINTR) + continue; throw SysError("reading from file"); } - if (res == 0) throw EndOfFile("unexpected end-of-file"); + if (res == 0) + throw EndOfFile("unexpected end-of-file"); count -= res; buf += res; } } - void writeFull(int fd, std::string_view s, bool allowInterrupts) { while (!s.empty()) { - if (allowInterrupts) checkInterrupt(); + if (allowInterrupts) + checkInterrupt(); ssize_t res = write(fd, s.data(), s.size()); if (res == -1 && errno != EINTR) throw SysError("writing to file"); @@ -744,17 +739,15 @@ void writeFull(int fd, std::string_view s, bool allowInterrupts) } } - std::string drainFD(int fd, bool block, const size_t reserveSize) { - // the parser needs two extra bytes to append terminating characters, other users will - // not care very much about the extra memory. + // the parser needs two extra bytes to append terminating characters, other + // users will not care very much about the extra memory. StringSink sink(reserveSize + 2); drainFD(fd, sink, block); return std::move(sink.s); } - void drainFD(int fd, Sink & sink, bool block) { // silence GCC maybe-uninitialized warning in finally @@ -782,17 +775,15 @@ void drainFD(int fd, Sink & sink, bool block) break; if (errno != EINTR) throw SysError("reading from file"); - } - else if (rd == 0) break; - else sink({(char *) buf.data(), (size_t) rd}); + } else if (rd == 0) + break; + else + sink({(char *) buf.data(), (size_t) rd}); } } - - ////////////////////////////////////////////////////////////////////// - AutoDelete::AutoDelete() : del{false} {} AutoDelete::AutoDelete(const std::string & p, bool recursive) : path(p) @@ -817,35 +808,24 @@ AutoDelete::~AutoDelete() } } -void AutoDelete::cancel() -{ - del = false; -} +void AutoDelete::cancel() { del = false; } -void AutoDelete::reset(const Path & p, bool recursive) { +void AutoDelete::reset(const Path & p, bool recursive) +{ path = p; this->recursive = recursive; del = true; } - - ////////////////////////////////////////////////////////////////////// - AutoCloseFD::AutoCloseFD() : fd{-1} {} - AutoCloseFD::AutoCloseFD(int fd) : fd{fd} {} +AutoCloseFD::AutoCloseFD(AutoCloseFD && that) : fd{that.fd} { that.fd = -1; } -AutoCloseFD::AutoCloseFD(AutoCloseFD && that) : fd{that.fd} -{ - that.fd = -1; -} - - -AutoCloseFD & AutoCloseFD::operator =(AutoCloseFD && that) +AutoCloseFD & AutoCloseFD::operator=(AutoCloseFD && that) { close(); fd = that.fd; @@ -853,7 +833,6 @@ AutoCloseFD & AutoCloseFD::operator =(AutoCloseFD && that) return *this; } - AutoCloseFD::~AutoCloseFD() { try { @@ -863,12 +842,7 @@ AutoCloseFD::~AutoCloseFD() } } - -int AutoCloseFD::get() const -{ - return fd; -} - +int AutoCloseFD::get() const { return fd; } void AutoCloseFD::close() { @@ -880,12 +854,7 @@ void AutoCloseFD::close() } } - -AutoCloseFD::operator bool() const -{ - return fd != -1; -} - +AutoCloseFD::operator bool() const { return fd != -1; } int AutoCloseFD::release() { @@ -894,14 +863,15 @@ int AutoCloseFD::release() return oldFD; } - void Pipe::create() { int fds[2]; #if HAVE_PIPE2 - if (pipe2(fds, O_CLOEXEC) != 0) throw SysError("creating pipe"); + if (pipe2(fds, O_CLOEXEC) != 0) + throw SysError("creating pipe"); #else - if (pipe(fds) != 0) throw SysError("creating pipe"); + if (pipe(fds) != 0) + throw SysError("creating pipe"); closeOnExec(fds[0]); closeOnExec(fds[1]); #endif @@ -909,47 +879,33 @@ void Pipe::create() writeSide = fds[1]; } - void Pipe::close() { readSide.close(); writeSide.close(); } - ////////////////////////////////////////////////////////////////////// +Pid::Pid() {} -Pid::Pid() -{ -} - - -Pid::Pid(pid_t pid) - : pid(pid) -{ -} - +Pid::Pid(pid_t pid) : pid(pid) {} Pid::~Pid() { - if (pid != -1) kill(); + if (pid != -1) + kill(); } - -void Pid::operator =(pid_t pid) +void Pid::operator=(pid_t pid) { - if (this->pid != -1 && this->pid != pid) kill(); + if (this->pid != -1 && this->pid != pid) + kill(); this->pid = pid; killSignal = SIGKILL; // reset signal to default } - -Pid::operator pid_t() -{ - return pid; -} - +Pid::operator pid_t() { return pid; } int Pid::kill() { @@ -973,7 +929,6 @@ int Pid::kill() return wait(); } - int Pid::wait() { assert(pid != -1); @@ -990,18 +945,9 @@ int Pid::wait() } } +void Pid::setSeparatePG(bool separatePG) { this->separatePG = separatePG; } -void Pid::setSeparatePG(bool separatePG) -{ - this->separatePG = separatePG; -} - - -void Pid::setKillSignal(int signal) -{ - this->killSignal = signal; -} - +void Pid::setKillSignal(int signal) { this->killSignal = signal; } pid_t Pid::release() { @@ -1010,7 +956,6 @@ pid_t Pid::release() return p; } - void killUser(uid_t uid) { debug("killing all processes running under uid '%1%'", uid); @@ -1022,7 +967,6 @@ void killUser(uid_t uid) fork a process, switch to uid, and send a mass kill. */ Pid pid = startProcess([&]() { - if (setuid(uid) == -1) throw SysError("setting uid"); @@ -1033,11 +977,14 @@ void killUser(uid_t uid) calling process. In the OSX libc, it's set to true, which means "follow POSIX", which we don't want here */ - if (syscall(SYS_kill, -1, SIGKILL, false) == 0) break; + if (syscall(SYS_kill, -1, SIGKILL, false) == 0) + break; #else - if (kill(-1, SIGKILL) == 0) break; + if (kill(-1, SIGKILL) == 0) + break; #endif - if (errno == ESRCH || errno == EPERM) break; /* no more processes */ + if (errno == ESRCH || errno == EPERM) + break; /* no more processes */ if (errno != EINTR) throw SysError("cannot kill processes for uid '%1%'", uid); } @@ -1047,7 +994,8 @@ void killUser(uid_t uid) int status = pid.wait(); if (status != 0) - throw Error("cannot kill processes for uid '%1%': %2%", uid, statusToString(status)); + throw Error("cannot kill processes for uid '%1%': %2%", uid, + statusToString(status)); /* !!! We should really do some check to make sure that there are no processes left running under `uid', but there is no portable @@ -1055,13 +1003,12 @@ void killUser(uid_t uid) uid | grep -q $uid'. */ } - ////////////////////////////////////////////////////////////////////// - /* Wrapper around vfork to prevent the child process from clobbering the caller's stack frame in the parent. */ -static pid_t doFork(bool allowVfork, std::function fun) __attribute__((noinline)); +static pid_t doFork(bool allowVfork, std::function fun) + __attribute__((noinline)); static pid_t doFork(bool allowVfork, std::function fun) { #ifdef __linux__ @@ -1069,12 +1016,12 @@ static pid_t doFork(bool allowVfork, std::function fun) #else pid_t pid = fork(); #endif - if (pid != 0) return pid; + if (pid != 0) + return pid; fun(); abort(); } - pid_t startProcess(std::function fun, const ProcessOptions & options) { auto wrapper = [&]() { @@ -1089,8 +1036,10 @@ pid_t startProcess(std::function fun, const ProcessOptions & options) } catch (std::exception & e) { try { std::cerr << options.errorPrefix << e.what() << "\n"; - } catch (...) { } - } catch (...) { } + } catch (...) { + } + } catch (...) { + } if (options.runExitHandlers) exit(1); else @@ -1098,27 +1047,32 @@ pid_t startProcess(std::function fun, const ProcessOptions & options) }; pid_t pid = doFork(options.allowVfork, wrapper); - if (pid == -1) throw SysError("unable to fork"); + if (pid == -1) + throw SysError("unable to fork"); return pid; } - std::vector stringsToCharPtrs(const Strings & ss) { std::vector res; - for (auto & s : ss) res.push_back((char *) s.c_str()); + for (auto & s : ss) + res.push_back((char *) s.c_str()); res.push_back(0); return res; } std::string runProgram(Path program, bool searchPath, const Strings & args, - const std::optional & input) + const std::optional & input) { - auto res = runProgram(RunOptions {.program = program, .searchPath = searchPath, .args = args, .input = input}); + auto res = runProgram(RunOptions{.program = program, + .searchPath = searchPath, + .args = args, + .input = input}); if (!statusOk(res.first)) - throw ExecError(res.first, "program '%1%' %2%", program, statusToString(res.first)); + throw ExecError(res.first, "program '%1%' %2%", program, + statusToString(res.first)); return res.second; } @@ -1156,8 +1110,10 @@ void runProgram2(const RunOptions & options) /* Create a pipe. */ Pipe out, in; - if (options.standardOut) out.create(); - if (source) in.create(); + if (options.standardOut) + out.create(); + if (source) + in.create(); ProcessOptions processOptions; // vfork implies that the environment of the main process and the fork will @@ -1166,41 +1122,45 @@ void runProgram2(const RunOptions & options) processOptions.allowVfork = !options.environment; /* Fork. */ - Pid pid = startProcess([&]() { - if (options.environment) - replaceEnv(*options.environment); - if (options.standardOut && dup2(out.writeSide.get(), STDOUT_FILENO) == -1) - throw SysError("dupping stdout"); - if (options.mergeStderrToStdout) - if (dup2(STDOUT_FILENO, STDERR_FILENO) == -1) - throw SysError("cannot dup stdout into stderr"); - if (source && dup2(in.readSide.get(), STDIN_FILENO) == -1) - throw SysError("dupping stdin"); - - if (options.chdir && chdir((*options.chdir).c_str()) == -1) - throw SysError("chdir failed"); - if (options.gid && setgid(*options.gid) == -1) - throw SysError("setgid failed"); - /* Drop all other groups if we're setgid. */ - if (options.gid && setgroups(0, 0) == -1) - throw SysError("setgroups failed"); - if (options.uid && setuid(*options.uid) == -1) - throw SysError("setuid failed"); - - Strings args_(options.args); - args_.push_front(options.program); - - restoreProcessContext(); - - if (options.searchPath) - execvp(options.program.c_str(), stringsToCharPtrs(args_).data()); + Pid pid = startProcess( + [&]() { + if (options.environment) + replaceEnv(*options.environment); + if (options.standardOut && + dup2(out.writeSide.get(), STDOUT_FILENO) == -1) + throw SysError("dupping stdout"); + if (options.mergeStderrToStdout) + if (dup2(STDOUT_FILENO, STDERR_FILENO) == -1) + throw SysError("cannot dup stdout into stderr"); + if (source && dup2(in.readSide.get(), STDIN_FILENO) == -1) + throw SysError("dupping stdin"); + + if (options.chdir && chdir((*options.chdir).c_str()) == -1) + throw SysError("chdir failed"); + if (options.gid && setgid(*options.gid) == -1) + throw SysError("setgid failed"); + /* Drop all other groups if we're setgid. */ + if (options.gid && setgroups(0, 0) == -1) + throw SysError("setgroups failed"); + if (options.uid && setuid(*options.uid) == -1) + throw SysError("setuid failed"); + + Strings args_(options.args); + args_.push_front(options.program); + + restoreProcessContext(); + + if (options.searchPath) + execvp(options.program.c_str(), + stringsToCharPtrs(args_).data()); // This allows you to refer to a program with a pathname relative // to the PATH variable. - else - execv(options.program.c_str(), stringsToCharPtrs(args_).data()); + else + execv(options.program.c_str(), stringsToCharPtrs(args_).data()); - throw SysError("executing '%1%'", options.program); - }, processOptions); + throw SysError("executing '%1%'", options.program); + }, + processOptions); out.writeSide.close(); @@ -1213,7 +1173,6 @@ void runProgram2(const RunOptions & options) writerThread.join(); }); - if (source) { in.readSide.close(); writerThread = std::thread([&]() { @@ -1243,13 +1202,14 @@ void runProgram2(const RunOptions & options) int status = pid.wait(); /* Wait for the writer thread to finish. */ - if (source) promise.get_future().get(); + if (source) + promise.get_future().get(); if (status) - throw ExecError(status, "program '%1%' %2%", options.program, statusToString(status)); + throw ExecError(status, "program '%1%' %2%", options.program, + statusToString(status)); } - void closeMostFDs(const std::set & exceptions) { #if __linux__ @@ -1273,7 +1233,6 @@ void closeMostFDs(const std::set & exceptions) close(fd); /* ignore result */ } - void closeOnExec(int fd) { int prev; @@ -1282,19 +1241,14 @@ void closeOnExec(int fd) throw SysError("setting close-on-exec flag"); } - ////////////////////////////////////////////////////////////////////// - std::atomic _isInterrupted = false; static thread_local bool interruptThrown = false; thread_local std::function interruptCheck; -void setInterruptThrown() -{ - interruptThrown = true; -} +void setInterruptThrown() { interruptThrown = true; } void _interrupted() { @@ -1307,27 +1261,29 @@ void _interrupted() } } - ////////////////////////////////////////////////////////////////////// - -template C tokenizeString(std::string_view s, std::string_view separators) +template +C tokenizeString(std::string_view s, std::string_view separators) { C result; auto pos = s.find_first_not_of(separators, 0); while (pos != std::string_view::npos) { auto end = s.find_first_of(separators, pos + 1); - if (end == std::string_view::npos) end = s.size(); + if (end == std::string_view::npos) + end = s.size(); result.insert(result.end(), std::string(s, pos, end - pos)); pos = s.find_first_not_of(separators, end); } return result; } -template Strings tokenizeString(std::string_view s, std::string_view separators); -template StringSet tokenizeString(std::string_view s, std::string_view separators); -template std::vector tokenizeString(std::string_view s, std::string_view separators); - +template Strings tokenizeString(std::string_view s, + std::string_view separators); +template StringSet tokenizeString(std::string_view s, + std::string_view separators); +template std::vector tokenizeString(std::string_view s, + std::string_view separators); std::string chomp(std::string_view s) { @@ -1335,22 +1291,20 @@ std::string chomp(std::string_view s) return i == std::string_view::npos ? "" : std::string(s, 0, i + 1); } - std::string trim(std::string_view s, std::string_view whitespace) { auto i = s.find_first_not_of(whitespace); - if (i == s.npos) return ""; + if (i == s.npos) + return ""; auto j = s.find_last_not_of(whitespace); return std::string(s, i, j == s.npos ? j : j - i + 1); } - -std::string replaceStrings( - std::string res, - std::string_view from, - std::string_view to) +std::string replaceStrings(std::string res, std::string_view from, + std::string_view to) { - if (from.empty()) return res; + if (from.empty()) + return res; size_t pos = 0; while ((pos = res.find(from, pos)) != std::string::npos) { res.replace(pos, from.size(), to); @@ -1359,11 +1313,11 @@ std::string replaceStrings( return res; } - std::string rewriteStrings(std::string s, const StringMap & rewrites) { for (auto & i : rewrites) { - if (i.first == i.second) continue; + if (i.first == i.second) + continue; size_t j = 0; while ((j = s.find(i.first, j)) != std::string::npos) s.replace(j, i.first.size(), i.second); @@ -1371,46 +1325,44 @@ std::string rewriteStrings(std::string s, const StringMap & rewrites) return s; } - std::string statusToString(int status) { if (!WIFEXITED(status) || WEXITSTATUS(status) != 0) { if (WIFEXITED(status)) - return (format("failed with exit code %1%") % WEXITSTATUS(status)).str(); + return (format("failed with exit code %1%") % WEXITSTATUS(status)) + .str(); else if (WIFSIGNALED(status)) { int sig = WTERMSIG(status); #if HAVE_STRSIGNAL const char * description = strsignal(sig); - return (format("failed due to signal %1% (%2%)") % sig % description).str(); + return (format("failed due to signal %1% (%2%)") % sig % + description) + .str(); #else return (format("failed due to signal %1%") % sig).str(); #endif - } - else + } else return "died abnormally"; - } else return "succeeded"; + } else + return "succeeded"; } - bool statusOk(int status) { return WIFEXITED(status) && WEXITSTATUS(status) == 0; } - bool hasPrefix(std::string_view s, std::string_view prefix) { return s.compare(0, prefix.size(), prefix) == 0; } - bool hasSuffix(std::string_view s, std::string_view suffix) { - return s.size() >= suffix.size() - && s.substr(s.size() - suffix.size()) == suffix; + return s.size() >= suffix.size() && + s.substr(s.size() - suffix.size()) == suffix; } - std::string toLower(const std::string & s) { std::string r(s); @@ -1419,19 +1371,20 @@ std::string toLower(const std::string & s) return r; } - std::string shellEscape(const std::string_view s) { std::string r; r.reserve(s.size() + 2); r += "'"; for (auto & i : s) - if (i == '\'') r += "'\\''"; else r += i; + if (i == '\'') + r += "'\\''"; + else + r += i; r += '\''; return r; } - void ignoreException() { /* Make sure no exceptions leave this function. @@ -1442,17 +1395,18 @@ void ignoreException() } catch (std::exception & e) { printError("error (ignored): %1%", e.what()); } - } catch (...) { } + } catch (...) { + } } bool shouldANSI() { - return isatty(STDERR_FILENO) - && getEnv("TERM").value_or("dumb") != "dumb" - && !getEnv("NO_COLOR").has_value(); + return isatty(STDERR_FILENO) && getEnv("TERM").value_or("dumb") != "dumb" && + !getEnv("NO_COLOR").has_value(); } -std::string filterANSIEscapes(const std::string & s, bool filterAll, unsigned int width) +std::string filterANSIEscapes(const std::string & s, bool filterAll, + unsigned int width) { std::string t, e; size_t w = 0; @@ -1468,13 +1422,17 @@ std::string filterANSIEscapes(const std::string & s, bool filterAll, unsigned in if (i != s.end() && *i == '[') { e += *i++; // eat parameter bytes - while (i != s.end() && *i >= 0x30 && *i <= 0x3f) e += *i++; + while (i != s.end() && *i >= 0x30 && *i <= 0x3f) + e += *i++; // eat intermediate bytes - while (i != s.end() && *i >= 0x20 && *i <= 0x2f) e += *i++; + while (i != s.end() && *i >= 0x20 && *i <= 0x2f) + e += *i++; // eat final byte - if (i != s.end() && *i >= 0x40 && *i <= 0x7e) e += last = *i++; + if (i != s.end() && *i >= 0x40 && *i <= 0x7e) + e += last = *i++; } else { - if (i != s.end() && *i >= 0x40 && *i <= 0x5f) e += *i++; + if (i != s.end() && *i >= 0x40 && *i <= 0x5f) + e += *i++; } if (!filterAll && last == 'm') @@ -1482,9 +1440,12 @@ std::string filterANSIEscapes(const std::string & s, bool filterAll, unsigned in } else if (*i == '\t') { - i++; t += ' '; w++; + i++; + t += ' '; + w++; while (w < (size_t) width && w % 8) { - t += ' '; w++; + t += ' '; + w++; } } @@ -1497,12 +1458,14 @@ std::string filterANSIEscapes(const std::string & s, bool filterAll, unsigned in // Copy one UTF-8 character. if ((*i & 0xe0) == 0xc0) { t += *i++; - if (i != s.end() && ((*i & 0xc0) == 0x80)) t += *i++; + if (i != s.end() && ((*i & 0xc0) == 0x80)) + t += *i++; } else if ((*i & 0xf0) == 0xe0) { t += *i++; if (i != s.end() && ((*i & 0xc0) == 0x80)) { t += *i++; - if (i != s.end() && ((*i & 0xc0) == 0x80)) t += *i++; + if (i != s.end() && ((*i & 0xc0) == 0x80)) + t += *i++; } } else if ((*i & 0xf8) == 0xf0) { t += *i++; @@ -1510,7 +1473,8 @@ std::string filterANSIEscapes(const std::string & s, bool filterAll, unsigned in t += *i++; if (i != s.end() && ((*i & 0xc0) == 0x80)) { t += *i++; - if (i != s.end() && ((*i & 0xc0) == 0x80)) t += *i++; + if (i != s.end() && ((*i & 0xc0) == 0x80)) + t += *i++; } } } else @@ -1521,8 +1485,8 @@ std::string filterANSIEscapes(const std::string & s, bool filterAll, unsigned in return t; } - -constexpr char base64Chars[] = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"; +constexpr char base64Chars[] = + "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"; std::string base64Encode(std::string_view s) { @@ -1539,19 +1503,20 @@ std::string base64Encode(std::string_view s) } } - if (nbits) res.push_back(base64Chars[data << (6 - nbits) & 0x3f]); - while (res.size() % 4) res.push_back('='); + if (nbits) + res.push_back(base64Chars[data << (6 - nbits) & 0x3f]); + while (res.size() % 4) + res.push_back('='); return res; } - std::string base64Decode(std::string_view s) { constexpr char npos = -1; constexpr std::array base64DecodeChars = [&]() { - std::array result{}; - for (auto& c : result) + std::array result{}; + for (auto & c : result) c = npos; for (int i = 0; i < 64; i++) result[base64Chars[i]] = i; @@ -1565,8 +1530,10 @@ std::string base64Decode(std::string_view s) unsigned int d = 0, bits = 0; for (char c : s) { - if (c == '=') break; - if (c == '\n') continue; + if (c == '=') + break; + if (c == '\n') + continue; char digit = base64DecodeChars[(unsigned char) c]; if (digit == npos) @@ -1583,7 +1550,6 @@ std::string base64Decode(std::string_view s) return res; } - std::string stripIndentation(std::string_view s) { size_t minIndent = 10000; @@ -1611,7 +1577,8 @@ std::string stripIndentation(std::string_view s) size_t pos = 0; while (pos < s.size()) { auto eol = s.find('\n', pos); - if (eol == s.npos) eol = s.size(); + if (eol == s.npos) + eol = s.size(); if (eol - pos > minIndent) res.append(s.substr(pos + minIndent, eol - pos - minIndent)); res.push_back('\n'); @@ -1621,12 +1588,10 @@ std::string stripIndentation(std::string_view s) return res; } - ////////////////////////////////////////////////////////////////////// static Sync> windowSize{{0, 0}}; - static void updateWindowSize() { struct winsize ws; @@ -1637,13 +1602,11 @@ static void updateWindowSize() } } - std::pair getWindowSize() { return *windowSize.lock(); } - /* We keep track of interrupt callbacks using integer tokens, so we can iterate safely without having to lock the data structure while executing arbitrary functions. @@ -1737,14 +1700,14 @@ rlim_t savedStackSize = 0; void setStackSize(size_t stackSize) { - #if __linux__ +#if __linux__ struct rlimit limit; if (getrlimit(RLIMIT_STACK, &limit) == 0 && limit.rlim_cur < stackSize) { savedStackSize = limit.rlim_cur; limit.rlim_cur = stackSize; setrlimit(RLIMIT_STACK, &limit); } - #endif +#endif } #if __linux__ @@ -1770,7 +1733,8 @@ void restoreMountNamespace() try { auto savedCwd = absPath("."); - if (fdSavedMountNamespace && setns(fdSavedMountNamespace.get(), CLONE_NEWNS) == -1) + if (fdSavedMountNamespace && + setns(fdSavedMountNamespace.get(), CLONE_NEWNS) == -1) throw SysError("restoring parent mount namespace"); if (chdir(savedCwd.c_str()) == -1) { throw SysError("restoring cwd"); @@ -1796,7 +1760,7 @@ void restoreProcessContext(bool restoreMounts) restoreMountNamespace(); } - #if __linux__ +#if __linux__ if (savedStackSize) { struct rlimit limit; if (getrlimit(RLIMIT_STACK, &limit) == 0) { @@ -1804,12 +1768,11 @@ void restoreProcessContext(bool restoreMounts) setrlimit(RLIMIT_STACK, &limit); } } - #endif +#endif } /* RAII helper to automatically deregister a callback. */ -struct InterruptCallbackImpl : InterruptCallback -{ +struct InterruptCallbackImpl : InterruptCallback { InterruptCallbacks::Token token; ~InterruptCallbackImpl() override { @@ -1818,7 +1781,8 @@ struct InterruptCallbackImpl : InterruptCallback } }; -std::unique_ptr createInterruptCallback(std::function callback) +std::unique_ptr +createInterruptCallback(std::function callback) { auto interruptCallbacks(_interruptCallbacks.lock()); auto token = interruptCallbacks->nextToken++; @@ -1830,21 +1794,21 @@ std::unique_ptr createInterruptCallback(std::function return std::unique_ptr(res.release()); } - AutoCloseFD createUnixDomainSocket() { - AutoCloseFD fdSocket = socket(PF_UNIX, SOCK_STREAM - #ifdef SOCK_CLOEXEC - | SOCK_CLOEXEC - #endif - , 0); + AutoCloseFD fdSocket = socket(PF_UNIX, + SOCK_STREAM +#ifdef SOCK_CLOEXEC + | SOCK_CLOEXEC +#endif + , + 0); if (!fdSocket) throw SysError("cannot create Unix domain socket"); closeOnExec(fdSocket.get()); return fdSocket; } - AutoCloseFD createUnixDomainSocket(const Path & path, mode_t mode) { auto fdSocket = nix::createUnixDomainSocket(); @@ -1860,7 +1824,6 @@ AutoCloseFD createUnixDomainSocket(const Path & path, mode_t mode) return fdSocket; } - void bind(int fd, const std::string & path) { unlink(path.c_str()); @@ -1891,7 +1854,6 @@ void bind(int fd, const std::string & path) } } - void connect(int fd, const std::string & path) { struct sockaddr_un addr; @@ -1920,13 +1882,11 @@ void connect(int fd, const std::string & path) } } - std::string showBytes(uint64_t bytes) { return fmt("%.2f MiB", bytes / (1024.0 * 1024.0)); } - // FIXME: move to libstore/build void commonChildInit(Pipe & logPipe) { diff --git a/src/libutil/util.hh b/src/libutil/util.hh index d3ed15b0b07b..aa89d7ee5dbf 100644 --- a/src/libutil/util.hh +++ b/src/libutil/util.hh @@ -31,11 +31,9 @@ namespace nix { struct Sink; struct Source; - /* The system for which Nix is compiled. */ extern const std::string nativeSystem; - /* Return an environment variable. */ std::optional getEnv(const std::string & key); @@ -48,9 +46,8 @@ void clearEnv(); /* Return an absolutized path, resolving paths relative to the specified directory, or the current directory otherwise. The path is also canonicalised. */ -Path absPath(Path path, - std::optional dir = {}, - bool resolveSymlinks = false); +Path absPath(Path path, std::optional dir = {}, + bool resolveSymlinks = false); /* Canonicalise a path by removing all `.' or `..' components and double or trailing slashes. Optionally resolves all symlink @@ -94,13 +91,14 @@ bool isLink(const Path & path); /* Read the contents of a directory. The entries `.' and `..' are removed. */ -struct DirEntry -{ +struct DirEntry { std::string name; ino_t ino; unsigned char type; // one of DT_* DirEntry(std::string name, ino_t ino, unsigned char type) - : name(std::move(name)), ino(ino), type(type) { } + : name(std::move(name)), ino(ino), type(type) + { + } }; typedef std::vector DirEntries; @@ -155,19 +153,15 @@ std::optional getSelfExe(); /* Create a directory and all its parents, if necessary. Returns the list of created directories, in order of creation. */ Paths createDirs(const Path & path); -inline Paths createDirs(PathView path) -{ - return createDirs(Path(path)); -} +inline Paths createDirs(PathView path) { return createDirs(Path(path)); } /* Create a symlink. */ void createSymlink(const Path & target, const Path & link, - std::optional mtime = {}); + std::optional mtime = {}); /* Atomically create or replace a symlink. */ void replaceSymlink(const Path & target, const Path & link, - std::optional mtime = {}); - + std::optional mtime = {}); /* Wrappers arount read()/write() that read/write exactly the requested number of bytes. */ @@ -176,22 +170,19 @@ void writeFull(int fd, std::string_view s, bool allowInterrupts = true); MakeError(EndOfFile, Error); - /* Read a file descriptor until EOF occurs. */ -std::string drainFD(int fd, bool block = true, const size_t reserveSize=0); +std::string drainFD(int fd, bool block = true, const size_t reserveSize = 0); void drainFD(int fd, Sink & sink, bool block = true); - /* Automatic cleanup of resources. */ - -class AutoDelete -{ +class AutoDelete { Path path; bool del; bool recursive; -public: + + public: AutoDelete(); AutoDelete(const Path & p, bool recursive = true); ~AutoDelete(); @@ -201,62 +192,54 @@ public: operator PathView() const { return path; } }; - -class AutoCloseFD -{ +class AutoCloseFD { int fd; -public: + + public: AutoCloseFD(); AutoCloseFD(int fd); AutoCloseFD(const AutoCloseFD & fd) = delete; - AutoCloseFD(AutoCloseFD&& fd); + AutoCloseFD(AutoCloseFD && fd); ~AutoCloseFD(); - AutoCloseFD& operator =(const AutoCloseFD & fd) = delete; - AutoCloseFD& operator =(AutoCloseFD&& fd); + AutoCloseFD & operator=(const AutoCloseFD & fd) = delete; + AutoCloseFD & operator=(AutoCloseFD && fd); int get() const; explicit operator bool() const; int release(); void close(); }; - /* Create a temporary directory. */ Path createTempDir(const Path & tmpRoot = "", const Path & prefix = "nix", - bool includePid = true, bool useGlobalCounter = true, mode_t mode = 0755); + bool includePid = true, bool useGlobalCounter = true, + mode_t mode = 0755); /* Create a temporary file, returning a file handle and its path. */ std::pair createTempFile(const Path & prefix = "nix"); - -class Pipe -{ -public: +class Pipe { + public: AutoCloseFD readSide, writeSide; void create(); void close(); }; - -struct DIRDeleter -{ - void operator()(DIR * dir) const { - closedir(dir); - } +struct DIRDeleter { + void operator()(DIR * dir) const { closedir(dir); } }; typedef std::unique_ptr AutoCloseDir; - -class Pid -{ +class Pid { pid_t pid = -1; bool separatePG = false; int killSignal = SIGKILL; -public: + + public: Pid(); Pid(pid_t pid); ~Pid(); - void operator =(pid_t pid); + void operator=(pid_t pid); operator pid_t(); int kill(); int wait(); @@ -266,33 +249,29 @@ public: pid_t release(); }; - /* Kill all processes running under the specified uid by sending them a SIGKILL. */ void killUser(uid_t uid); - /* Fork a process that runs the given function, and return the child pid to the caller. */ -struct ProcessOptions -{ +struct ProcessOptions { std::string errorPrefix = ""; bool dieWithParent = true; bool runExitHandlers = false; bool allowVfork = false; }; -pid_t startProcess(std::function fun, const ProcessOptions & options = ProcessOptions()); - +pid_t startProcess(std::function fun, + const ProcessOptions & options = ProcessOptions()); /* Run a program and return its stdout in a string (i.e., like the shell backtick operator). */ std::string runProgram(Path program, bool searchPath = false, - const Strings & args = Strings(), - const std::optional & input = {}); + const Strings & args = Strings(), + const std::optional & input = {}); -struct RunOptions -{ +struct RunOptions { Path program; bool searchPath = true; Strings args; @@ -310,11 +289,9 @@ std::pair runProgram(RunOptions && options); void runProgram2(const RunOptions & options); - /* Change the stack size. */ void setStackSize(size_t stackSize); - /* Restore the original inherited Unix process context (such as signal masks, stack size). */ void restoreProcessContext(bool restoreMounts = true); @@ -332,16 +309,14 @@ void restoreMountNamespace(); fail. */ void unshareFilesystem(); - -class ExecError : public Error -{ -public: +class ExecError : public Error { + public: int status; template - ExecError(int status, const Args & ... args) - : Error(args...), status(status) - { } + ExecError(int status, const Args &... args) : Error(args...), status(status) + { + } }; /* Convert a list of strings to a null-terminated vector of char @@ -356,7 +331,6 @@ void closeMostFDs(const std::set & exceptions); /* Set the close-on-exec flag for the given file descriptor. */ void closeOnExec(int fd); - /* User interruption. */ extern std::atomic _isInterrupted; @@ -375,13 +349,11 @@ void inline checkInterrupt() MakeError(Interrupted, BaseError); - MakeError(FormatError, Error); - /* String tokenizer. */ -template C tokenizeString(std::string_view s, std::string_view separators = " \t\n\r"); - +template +C tokenizeString(std::string_view s, std::string_view separators = " \t\n\r"); /* Concatenate the given strings with a separator between the elements. */ @@ -390,25 +362,27 @@ std::string concatStringsSep(const std::string_view sep, const C & ss) { size_t size = 0; // need a cast to string_view since this is also called with Symbols - for (const auto & s : ss) size += sep.size() + std::string_view(s).size(); + for (const auto & s : ss) + size += sep.size() + std::string_view(s).size(); std::string s; s.reserve(size); for (auto & i : ss) { - if (s.size() != 0) s += sep; + if (s.size() != 0) + s += sep; s += i; } return s; } -template -auto concatStrings(Parts && ... parts) - -> std::enable_if_t<(... && std::is_convertible_v), std::string> +template +auto concatStrings(Parts &&... parts) + -> std::enable_if_t<(... && std::is_convertible_v), + std::string> { - std::string_view views[sizeof...(parts)] = { parts... }; + std::string_view views[sizeof...(parts)] = {parts...}; return concatStringsSep({}, views); } - /* Add quotes around a collection of strings. */ template Strings quoteStrings(const C & c) { @@ -418,36 +392,27 @@ template Strings quoteStrings(const C & c) return res; } - /* Remove trailing whitespace from a string. FIXME: return std::string_view. */ std::string chomp(std::string_view s); - /* Remove whitespace from the start and end of a string. */ std::string trim(std::string_view s, std::string_view whitespace = " \n\r\t"); - /* Replace all occurrences of a string inside another string. */ -std::string replaceStrings( - std::string s, - std::string_view from, - std::string_view to); - +std::string replaceStrings(std::string s, std::string_view from, + std::string_view to); std::string rewriteStrings(std::string s, const StringMap & rewrites); - /* Convert the exit status of a child as returned by wait() into an error string. */ std::string statusToString(int status); bool statusOk(int status); - /* Parse a string into an integer. */ -template -std::optional string2Int(const std::string_view s) +template std::optional string2Int(const std::string_view s) { if (s.substr(0, 1) == "-" && !std::numeric_limits::is_signed) return std::nullopt; @@ -460,18 +425,22 @@ std::optional string2Int(const std::string_view s) /* Like string2Int(), but support an optional suffix 'K', 'M', 'G' or 'T' denoting a binary unit prefix. */ -template -N string2IntWithUnitPrefix(std::string_view s) +template N string2IntWithUnitPrefix(std::string_view s) { N multiplier = 1; if (!s.empty()) { char u = std::toupper(*s.rbegin()); if (std::isalpha(u)) { - if (u == 'K') multiplier = 1ULL << 10; - else if (u == 'M') multiplier = 1ULL << 20; - else if (u == 'G') multiplier = 1ULL << 30; - else if (u == 'T') multiplier = 1ULL << 40; - else throw UsageError("invalid unit specifier '%1%'", u); + if (u == 'K') + multiplier = 1ULL << 10; + else if (u == 'M') + multiplier = 1ULL << 20; + else if (u == 'G') + multiplier = 1ULL << 30; + else if (u == 'T') + multiplier = 1ULL << 40; + else + throw UsageError("invalid unit specifier '%1%'", u); s.remove_suffix(1); } } @@ -481,8 +450,7 @@ N string2IntWithUnitPrefix(std::string_view s) } /* Parse a string into a float. */ -template -std::optional string2Float(const std::string_view s) +template std::optional string2Float(const std::string_view s) { try { return boost::lexical_cast(s.data(), s.size()); @@ -491,29 +459,22 @@ std::optional string2Float(const std::string_view s) } } - /* Return true iff `s' starts with `prefix'. */ bool hasPrefix(std::string_view s, std::string_view prefix); - /* Return true iff `s' ends in `suffix'. */ bool hasSuffix(std::string_view s, std::string_view suffix); - /* Convert a string to lower case. */ std::string toLower(const std::string & s); - /* Escape a string as a shell word. */ std::string shellEscape(const std::string_view s); - /* Exception handling in destructors: print an error message, then ignore the exception. */ void ignoreException(); - - /* Tree formatting. */ constexpr char treeConn[] = "├───"; constexpr char treeLast[] = "└───"; @@ -529,125 +490,119 @@ bool shouldANSI(); some escape sequences (such as colour setting) are copied but not included in the character count. Also, tabs are expanded to spaces. */ -std::string filterANSIEscapes(const std::string & s, - bool filterAll = false, +std::string filterANSIEscapes( + const std::string & s, bool filterAll = false, unsigned int width = std::numeric_limits::max()); - /* Base64 encoding/decoding. */ std::string base64Encode(std::string_view s); std::string base64Decode(std::string_view s); - /* Remove common leading whitespace from the lines in the string 's'. For example, if every line is indented by at least 3 spaces, then we remove 3 spaces from the start of every line. */ std::string stripIndentation(std::string_view s); - /* Get a value for the specified key from an associate container. */ -template -const typename T::mapped_type * get(const T & map, const typename T::key_type & key) +template +const typename T::mapped_type * get(const T & map, + const typename T::key_type & key) { auto i = map.find(key); - if (i == map.end()) return nullptr; + if (i == map.end()) + return nullptr; return &i->second; } -template +template typename T::mapped_type * get(T & map, const typename T::key_type & key) { auto i = map.find(key); - if (i == map.end()) return nullptr; + if (i == map.end()) + return nullptr; return &i->second; } -/* Get a value for the specified key from an associate container, or a default value if the key isn't present. */ -template -const typename T::mapped_type & getOr(T & map, - const typename T::key_type & key, - const typename T::mapped_type & defaultValue) +/* Get a value for the specified key from an associate container, or a default + * value if the key isn't present. */ +template +const typename T::mapped_type & +getOr(T & map, const typename T::key_type & key, + const typename T::mapped_type & defaultValue) { auto i = map.find(key); - if (i == map.end()) return defaultValue; + if (i == map.end()) + return defaultValue; return i->second; } /* Remove and return the first item from a container. */ -template -std::optional remove_begin(T & c) +template std::optional remove_begin(T & c) { auto i = c.begin(); - if (i == c.end()) return {}; + if (i == c.end()) + return {}; auto v = std::move(*i); c.erase(i); return v; } - /* Remove and return the first item from a container. */ -template -std::optional pop(T & c) +template std::optional pop(T & c) { - if (c.empty()) return {}; + if (c.empty()) + return {}; auto v = std::move(c.front()); c.pop(); return v; } - -template -class Callback; - +template class Callback; /* Start a thread that handles various signals. Also block those signals on the current thread (and thus any threads created by it). */ void startSignalHandlerThread(); -struct InterruptCallback -{ - virtual ~InterruptCallback() { }; +struct InterruptCallback { + virtual ~InterruptCallback(){}; }; /* Register a function that gets called on SIGINT (in a non-signal context). */ -std::unique_ptr createInterruptCallback( - std::function callback); +std::unique_ptr +createInterruptCallback(std::function callback); void triggerInterrupt(); /* A RAII class that causes the current thread to receive SIGUSR1 when the signal handler thread receives SIGINT. That is, this allows SIGINT to be multiplexed to multiple threads. */ -struct ReceiveInterrupts -{ +struct ReceiveInterrupts { pthread_t target; std::unique_ptr callback; ReceiveInterrupts() - : target(pthread_self()) - , callback(createInterruptCallback([&]() { pthread_kill(target, SIGUSR1); })) - { } + : target(pthread_self()), callback(createInterruptCallback( + [&]() { pthread_kill(target, SIGUSR1); })) + { + } }; - - /* A RAII helper that increments a counter on construction and decrements it on destruction. */ -template -struct MaintainCount -{ +template struct MaintainCount { T & counter; long delta; - MaintainCount(T & counter, long delta = 1) : counter(counter), delta(delta) { counter += delta; } + MaintainCount(T & counter, long delta = 1) : counter(counter), delta(delta) + { + counter += delta; + } ~MaintainCount() { counter -= delta; } }; - /* Return the number of rows and columns of the terminal. */ std::pair getWindowSize(); - /* Used in various places. */ typedef std::function PathFilter; @@ -668,52 +623,54 @@ void bind(int fd, const std::string & path); /* Connect to a Unix domain socket. */ void connect(int fd, const std::string & path); - // A Rust/Python-like enumerate() iterator adapter. // Borrowed from http://reedbeta.com/blog/python-like-enumerate-in-cpp17. -template ())), - typename = decltype(std::end(std::declval()))> +template())), + typename = decltype(std::end(std::declval()))> constexpr auto enumerate(T && iterable) { - struct iterator - { + struct iterator { size_t i; TIter iter; - bool operator != (const iterator & other) const { return iter != other.iter; } - void operator ++ () { ++i; ++iter; } - auto operator * () const { return std::tie(i, *iter); } + bool operator!=(const iterator & other) const + { + return iter != other.iter; + } + void operator++() + { + ++i; + ++iter; + } + auto operator*() const { return std::tie(i, *iter); } }; - struct iterable_wrapper - { + struct iterable_wrapper { T iterable; - auto begin() { return iterator{ 0, std::begin(iterable) }; } - auto end() { return iterator{ 0, std::end(iterable) }; } + auto begin() { return iterator{0, std::begin(iterable)}; } + auto end() { return iterator{0, std::end(iterable)}; } }; - return iterable_wrapper{ std::forward(iterable) }; + return iterable_wrapper{std::forward(iterable)}; } - // C++17 std::visit boilerplate -template struct overloaded : Ts... { using Ts::operator()...; }; +template struct overloaded : Ts... { + using Ts::operator()...; +}; template overloaded(Ts...) -> overloaded; - std::string showBytes(uint64_t bytes); - /* Provide an addition operator between strings and string_views inexplicably omitted from the standard library. */ -inline std::string operator + (const std::string & s1, std::string_view s2) +inline std::string operator+(const std::string & s1, std::string_view s2) { auto s = s1; s.append(s2); return s; } -inline std::string operator + (std::string && s, std::string_view s2) +inline std::string operator+(std::string && s, std::string_view s2) { s.append(s2); return std::move(s); diff --git a/src/libutil/xml-writer.cc b/src/libutil/xml-writer.cc index 7993bee9af0e..d1ccc86d2f98 100644 --- a/src/libutil/xml-writer.cc +++ b/src/libutil/xml-writer.cc @@ -2,10 +2,8 @@ #include "xml-writer.hh" - namespace nix { - XMLWriter::XMLWriter(bool indent, std::ostream & output) : output(output), indent(indent) { @@ -13,84 +11,82 @@ XMLWriter::XMLWriter(bool indent, std::ostream & output) closed = false; } - -XMLWriter::~XMLWriter() -{ - close(); -} - +XMLWriter::~XMLWriter() { close(); } void XMLWriter::close() { - if (closed) return; - while (!pendingElems.empty()) closeElement(); + if (closed) + return; + while (!pendingElems.empty()) + closeElement(); closed = true; } - void XMLWriter::indent_(size_t depth) { - if (!indent) return; + if (!indent) + return; output << std::string(depth * 2, ' '); } - -void XMLWriter::openElement( - std::string_view name, - const XMLAttrs & attrs) +void XMLWriter::openElement(std::string_view name, const XMLAttrs & attrs) { assert(!closed); indent_(pendingElems.size()); output << "<" << name; writeAttrs(attrs); output << ">"; - if (indent) output << std::endl; + if (indent) + output << std::endl; pendingElems.push_back(std::string(name)); } - void XMLWriter::closeElement() { assert(!pendingElems.empty()); indent_(pendingElems.size() - 1); output << ""; - if (indent) output << std::endl; + if (indent) + output << std::endl; pendingElems.pop_back(); - if (pendingElems.empty()) closed = true; + if (pendingElems.empty()) + closed = true; } - -void XMLWriter::writeEmptyElement( - std::string_view name, - const XMLAttrs & attrs) +void XMLWriter::writeEmptyElement(std::string_view name, const XMLAttrs & attrs) { assert(!closed); indent_(pendingElems.size()); output << "<" << name; writeAttrs(attrs); output << " />"; - if (indent) output << std::endl; + if (indent) + output << std::endl; } - void XMLWriter::writeAttrs(const XMLAttrs & attrs) { for (auto & i : attrs) { output << " " << i.first << "=\""; for (size_t j = 0; j < i.second.size(); ++j) { char c = i.second[j]; - if (c == '"') output << """; - else if (c == '<') output << "<"; - else if (c == '>') output << ">"; - else if (c == '&') output << "&"; + if (c == '"') + output << """; + else if (c == '<') + output << "<"; + else if (c == '>') + output << ">"; + else if (c == '&') + output << "&"; /* Escape newlines to prevent attribute normalisation (see XML spec, section 3.3.3. */ - else if (c == '\n') output << " "; - else output << c; + else if (c == '\n') + output << " "; + else + output << c; } output << "\""; } } - } diff --git a/src/libutil/xml-writer.hh b/src/libutil/xml-writer.hh index 4c91adee629a..ed11c0532717 100644 --- a/src/libutil/xml-writer.hh +++ b/src/libutil/xml-writer.hh @@ -5,17 +5,12 @@ #include #include - namespace nix { - typedef std::map XMLAttrs; - -class XMLWriter -{ -private: - +class XMLWriter { + private: std::ostream & output; bool indent; @@ -23,43 +18,37 @@ private: std::list pendingElems; -public: - + public: XMLWriter(bool indent, std::ostream & output); ~XMLWriter(); void close(); void openElement(std::string_view name, - const XMLAttrs & attrs = XMLAttrs()); + const XMLAttrs & attrs = XMLAttrs()); void closeElement(); void writeEmptyElement(std::string_view name, - const XMLAttrs & attrs = XMLAttrs()); + const XMLAttrs & attrs = XMLAttrs()); -private: + private: void writeAttrs(const XMLAttrs & attrs); void indent_(size_t depth); }; - -class XMLOpenElement -{ -private: +class XMLOpenElement { + private: XMLWriter & writer; -public: + + public: XMLOpenElement(XMLWriter & writer, std::string_view name, - const XMLAttrs & attrs = XMLAttrs()) + const XMLAttrs & attrs = XMLAttrs()) : writer(writer) { writer.openElement(name, attrs); } - ~XMLOpenElement() - { - writer.closeElement(); - } + ~XMLOpenElement() { writer.closeElement(); } }; - } diff --git a/src/nix-build/nix-build.cc b/src/nix-build/nix-build.cc index 519855ea3b2d..9a0d74528190 100644 --- a/src/nix-build/nix-build.cc +++ b/src/nix-build/nix-build.cc @@ -27,7 +27,7 @@ using namespace nix; using namespace std::string_literals; -extern char * * environ __attribute__((weak)); +extern char ** environ __attribute__((weak)); /* Recreate the effect of the perl shellwords function, breaking up a * string into arguments like a shell word, including escapes @@ -38,10 +38,7 @@ static std::vector shellwords(const std::string & s) auto begin = s.cbegin(); std::vector res; std::string cur; - enum state { - sBegin, - sQuote - }; + enum state { sBegin, sQuote }; state st = sBegin; auto it = begin; for (; it != s.cend(); ++it) { @@ -56,24 +53,26 @@ static std::vector shellwords(const std::string & s) } } switch (*it) { - case '"': - cur.append(begin, it); - begin = it + 1; - st = st == sBegin ? sQuote : sBegin; - break; - case '\\': - /* perl shellwords mostly just treats the next char as part of the string with no special processing */ - cur.append(begin, it); - begin = ++it; - break; + case '"': + cur.append(begin, it); + begin = it + 1; + st = st == sBegin ? sQuote : sBegin; + break; + case '\\': + /* perl shellwords mostly just treats the next char as part of the + * string with no special processing */ + cur.append(begin, it); + begin = ++it; + break; } } cur.append(begin, it); - if (!cur.empty()) res.push_back(cur); + if (!cur.empty()) + res.push_back(cur); return res; } -static void main_nix_build(int argc, char * * argv) +static void main_nix_build(int argc, char ** argv) { auto dryRun = false; auto runEnv = std::regex_search(argv[0], std::regex("nix-shell$")); @@ -103,12 +102,26 @@ static void main_nix_build(int argc, char * * argv) std::string outLink = "./result"; // List of environment variables kept for --pure - std::set keepVars{ - "HOME", "XDG_RUNTIME_DIR", "USER", "LOGNAME", "DISPLAY", - "WAYLAND_DISPLAY", "WAYLAND_SOCKET", "PATH", "TERM", "IN_NIX_SHELL", - "NIX_SHELL_PRESERVE_PROMPT", "TZ", "PAGER", "NIX_BUILD_SHELL", "SHLVL", - "http_proxy", "https_proxy", "ftp_proxy", "all_proxy", "no_proxy" - }; + std::set keepVars{"HOME", + "XDG_RUNTIME_DIR", + "USER", + "LOGNAME", + "DISPLAY", + "WAYLAND_DISPLAY", + "WAYLAND_SOCKET", + "PATH", + "TERM", + "IN_NIX_SHELL", + "NIX_SHELL_PRESERVE_PROMPT", + "TZ", + "PAGER", + "NIX_BUILD_SHELL", + "SHLVL", + "http_proxy", + "https_proxy", + "ftp_proxy", + "all_proxy", + "no_proxy"}; Strings args; for (int i = 1; i < argc; ++i) @@ -130,20 +143,22 @@ static void main_nix_build(int argc, char * * argv) for (auto line : lines) { line = chomp(line); std::smatch match; - if (std::regex_match(line, match, std::regex("^#!\\s*nix-shell (.*)$"))) + if (std::regex_match(line, match, + std::regex("^#!\\s*nix-shell (.*)$"))) for (const auto & word : shellwords(match[1].str())) args.push_back(word); } } - } catch (SysError &) { } + } catch (SysError &) { + } } - struct MyArgs : LegacyArgs, MixEvalArgs - { + struct MyArgs : LegacyArgs, MixEvalArgs { using LegacyArgs::LegacyArgs; }; - MyArgs myArgs(myName, [&](Strings::iterator & arg, const Strings::iterator & end) { + MyArgs myArgs(myName, [&](Strings::iterator & arg, + const Strings::iterator & end) { if (*arg == "--help") { deletePath(tmpDir); showManPage(myName); @@ -196,8 +211,10 @@ static void main_nix_build(int argc, char * * argv) else if (*arg == "--expr" || *arg == "-E") fromArgs = true; - else if (*arg == "--pure") pure = true; - else if (*arg == "--impure") pure = false; + else if (*arg == "--pure") + pure = true; + else if (*arg == "--impure") + pure = false; else if (runEnv && (*arg == "--packages" || *arg == "-p")) packages = true; @@ -220,12 +237,19 @@ static void main_nix_build(int argc, char * * argv) if (std::regex_search(interpreter, std::regex("ruby"))) { // Hack for Ruby. Ruby also examines the shebang. It tries to - // read the shebang to understand which packages to read from. Since - // this is handled via nix-shell -p, we wrap our ruby script execution - // in ruby -e 'load' which ignores the shebangs. - envCommand = (format("exec %1% %2% -e 'load(ARGV.shift)' -- %3% %4%") % execArgs % interpreter % shellEscape(script) % joined.str()).str(); + // read the shebang to understand which packages to read from. + // Since this is handled via nix-shell -p, we wrap our ruby + // script execution in ruby -e 'load' which ignores the + // shebangs. + envCommand = + (format("exec %1% %2% -e 'load(ARGV.shift)' -- %3% %4%") % + execArgs % interpreter % shellEscape(script) % + joined.str()) + .str(); } else { - envCommand = (format("exec %1% %2% %3% %4%") % execArgs % interpreter % shellEscape(script) % joined.str()).str(); + envCommand = (format("exec %1% %2% %3% %4%") % execArgs % + interpreter % shellEscape(script) % joined.str()) + .str(); } } @@ -250,9 +274,11 @@ static void main_nix_build(int argc, char * * argv) throw UsageError("'-p' and '-E' are mutually exclusive"); auto store = openStore(); - auto evalStore = myArgs.evalStoreUrl ? openStore(*myArgs.evalStoreUrl) : store; + auto evalStore = + myArgs.evalStoreUrl ? openStore(*myArgs.evalStoreUrl) : store; - auto state = std::make_unique(myArgs.searchPath, evalStore, store); + auto state = + std::make_unique(myArgs.searchPath, evalStore, store); state->repair = repair; auto autoArgs = myArgs.getAutoArgs(*state); @@ -260,13 +286,15 @@ static void main_nix_build(int argc, char * * argv) if (runEnv) { auto newArgs = state->buildBindings(autoArgs->size() + 1); newArgs.alloc("inNixShell").mkBool(true); - for (auto & i : *autoArgs) newArgs.insert(i); + for (auto & i : *autoArgs) + newArgs.insert(i); autoArgs = newArgs.finish(); } if (packages) { std::ostringstream joined; - joined << "{...}@args: with import args; (pkgs.runCommandCC or pkgs.runCommand) \"shell\" { buildInputs = [ "; + joined << "{...}@args: with import args; (pkgs.runCommandCC " + "or pkgs.runCommand) \"shell\" { buildInputs = [ "; for (const auto & i : left) joined << '(' << i << ") "; joined << "]; } \"\""; @@ -292,25 +320,31 @@ static void main_nix_build(int argc, char * * argv) else for (auto i : left) { if (fromArgs) - exprs.push_back(state->parseExprFromString(std::move(i), absPath("."))); + exprs.push_back( + state->parseExprFromString(std::move(i), absPath("."))); else { auto absolute = i; try { absolute = canonPath(absPath(i), true); - } catch (Error & e) {}; + } catch (Error & e) { + }; auto [path, outputNames] = parsePathWithOutputs(absolute); if (evalStore->isStorePath(path) && hasSuffix(path, ".drv")) drvs.push_back(DrvInfo(*state, evalStore, absolute)); else /* If we're in a #! script, interpret filenames relative to the script. */ - exprs.push_back(state->parseExprFromFile(resolveExprPath(state->checkSourcePath(lookupFileArg(*state, - inShebang && !packages ? absPath(i, absPath(dirOf(script))) : i))))); + exprs.push_back(state->parseExprFromFile( + resolveExprPath(state->checkSourcePath(lookupFileArg( + *state, inShebang && !packages + ? absPath(i, absPath(dirOf(script))) + : i))))); } } /* Evaluate them into derivations. */ - if (attrPaths.empty()) attrPaths = {""}; + if (attrPaths.empty()) + attrPaths = {""}; for (auto e : exprs) { Value vRoot; @@ -330,11 +364,12 @@ static void main_nix_build(int argc, char * * argv) fetch binary cache data. */ uint64_t downloadSize, narSize; StorePathSet willBuild, willSubstitute, unknown; - store->queryMissing(paths, - willBuild, willSubstitute, unknown, downloadSize, narSize); + store->queryMissing(paths, willBuild, willSubstitute, unknown, + downloadSize, narSize); if (settings.printMissing) - printMissing(ref(store), willBuild, willSubstitute, unknown, downloadSize, narSize); + printMissing(ref(store), willBuild, willSubstitute, unknown, + downloadSize, narSize); if (!dryRun) store->buildPaths(paths, buildMode, evalStore); @@ -359,17 +394,19 @@ static void main_nix_build(int argc, char * * argv) if (!shell) { try { - auto expr = state->parseExprFromString("(import {}).bashInteractive", absPath(".")); + auto expr = state->parseExprFromString( + "(import {}).bashInteractive", absPath(".")); Value v; state->eval(expr, v); auto drv = getDerivation(*state, v, false); if (!drv) - throw Error("the 'bashInteractive' attribute in did not evaluate to a derivation"); + throw Error("the 'bashInteractive' attribute in " + "did not evaluate to a derivation"); auto bashDrv = drv->requireDrvPath(); - pathsToBuild.push_back(DerivedPath::Built { + pathsToBuild.push_back(DerivedPath::Built{ .drvPath = bashDrv, .outputs = {}, }); @@ -389,14 +426,13 @@ static void main_nix_build(int argc, char * * argv) // standard. const auto & inputDrv = inputDrv0; if (std::all_of(envExclude.cbegin(), envExclude.cend(), - [&](const std::string & exclude) { - return !std::regex_search(store->printStorePath(inputDrv), std::regex(exclude)); - })) - { - pathsToBuild.push_back(DerivedPath::Built { - .drvPath = inputDrv, - .outputs = inputOutputs - }); + [&](const std::string & exclude) { + return !std::regex_search( + store->printStorePath(inputDrv), + std::regex(exclude)); + })) { + pathsToBuild.push_back(DerivedPath::Built{ + .drvPath = inputDrv, .outputs = inputOutputs}); pathsToCopy.insert(inputDrv); } } @@ -407,11 +443,14 @@ static void main_nix_build(int argc, char * * argv) buildPaths(pathsToBuild); - if (dryRun) return; + if (dryRun) + return; if (shellDrv) { - auto shellDrvOutputs = store->queryPartialDerivationOutputMap(shellDrv.value()); - shell = store->printStorePath(shellDrvOutputs.at("out").value()) + "/bin/bash"; + auto shellDrvOutputs = + store->queryPartialDerivationOutputMap(shellDrv.value()); + shell = store->printStorePath(shellDrvOutputs.at("out").value()) + + "/bin/bash"; } if (settings.isExperimentalFeatureEnabled(Xp::CaDerivations)) { @@ -424,7 +463,8 @@ static void main_nix_build(int argc, char * * argv) auto env = getEnv(); auto tmp = getEnv("TMPDIR"); - if (!tmp) tmp = getEnv("XDG_RUNTIME_DIR").value_or("/tmp"); + if (!tmp) + tmp = getEnv("XDG_RUNTIME_DIR").value_or("/tmp"); if (pure) { decltype(env) newEnv; @@ -436,11 +476,13 @@ static void main_nix_build(int argc, char * * argv) env["__ETC_PROFILE_SOURCED"] = "1"; } - env["NIX_BUILD_TOP"] = env["TMPDIR"] = env["TEMPDIR"] = env["TMP"] = env["TEMP"] = *tmp; + env["NIX_BUILD_TOP"] = env["TMPDIR"] = env["TEMPDIR"] = env["TMP"] = + env["TEMP"] = *tmp; env["NIX_STORE"] = store->storeDir; env["NIX_BUILD_CORES"] = std::to_string(settings.buildCores); - auto passAsFile = tokenizeString(getOr(drv.env, "passAsFile", "")); + auto passAsFile = + tokenizeString(getOr(drv.env, "passAsFile", "")); bool keepTmp = false; int fileNr = 0; @@ -460,7 +502,8 @@ static void main_nix_build(int argc, char * * argv) if (env.count("__json")) { StorePathSet inputs; for (auto & [depDrvPath, wantedDepOutputs] : drv.inputDrvs) { - auto outputs = evalStore->queryPartialDerivationOutputMap(depDrvPath); + auto outputs = + evalStore->queryPartialDerivationOutputMap(depDrvPath); for (auto & i : wantedDepOutputs) { auto o = outputs.at(i); store->computeFSClosure(*o, inputs); @@ -469,7 +512,8 @@ static void main_nix_build(int argc, char * * argv) ParsedDerivation parsedDrv(drvInfo.requireDrvPath(), drv); - if (auto structAttrs = parsedDrv.prepareStructuredAttrs(*store, inputs)) { + if (auto structAttrs = + parsedDrv.prepareStructuredAttrs(*store, inputs)) { auto json = structAttrs.value(); structuredAttrsRC = writeStructuredAttrsShell(json); @@ -491,19 +535,21 @@ static void main_nix_build(int argc, char * * argv) lose the current $PATH directories. */ auto rcfile = (Path) tmpDir + "/rc"; std::string rc = fmt( - R"(_nix_shell_clean_tmpdir() { command rm -rf %1%; }; )"s + - (keepTmp ? - "trap _nix_shell_clean_tmpdir EXIT; " - "exitHooks+=(_nix_shell_clean_tmpdir); " - "failureHooks+=(_nix_shell_clean_tmpdir); ": - "_nix_shell_clean_tmpdir; ") + - (pure ? "" : "[ -n \"$PS1\" ] && [ -e ~/.bashrc ] && source ~/.bashrc;") + + R"(_nix_shell_clean_tmpdir() { command rm -rf %1%; }; )"s + + (keepTmp ? "trap _nix_shell_clean_tmpdir EXIT; " + "exitHooks+=(_nix_shell_clean_tmpdir); " + "failureHooks+=(_nix_shell_clean_tmpdir); " + : "_nix_shell_clean_tmpdir; ") + + (pure ? "" + : "[ -n \"$PS1\" ] && [ -e ~/.bashrc ] && source " + "~/.bashrc;") + "%2%" // always clear PATH. - // when nix-shell is run impure, we rehydrate it with the `p=$PATH` above + // when nix-shell is run impure, we rehydrate it with the + // `p=$PATH` above "unset PATH;" - "dontAddDisableDepTrack=1;\n" - + structuredAttrsRC + + "dontAddDisableDepTrack=1;\n" + + structuredAttrsRC + "\n[ -e $stdenv/setup ] && source $stdenv/setup; " "%3%" "PATH=%4%:\"$PATH\"; " @@ -511,19 +557,20 @@ static void main_nix_build(int argc, char * * argv) "BASH=%5%; " "set +e; " R"s([ -n "$PS1" -a -z "$NIX_SHELL_PRESERVE_PROMPT" ] && PS1='\n\[\033[1;32m\][nix-shell:\w]\$\[\033[0m\] '; )s" - "if [ \"$(type -t runHook)\" = function ]; then runHook shellHook; fi; " + "if [ \"$(type -t runHook)\" = function ]; then runHook " + "shellHook; fi; " "unset NIX_ENFORCE_PURITY; " "shopt -u nullglob; " "unset TZ; %6%" "shopt -s execfail;" "%7%", - shellEscape(tmpDir), - (pure ? "" : "p=$PATH; "), - (pure ? "" : "PATH=$PATH:$p; unset p; "), - shellEscape(dirOf(*shell)), - shellEscape(*shell), - (getenv("TZ") ? (std::string("export TZ=") + shellEscape(getenv("TZ")) + "; ") : ""), - envCommand); + shellEscape(tmpDir), (pure ? "" : "p=$PATH; "), + (pure ? "" : "PATH=$PATH:$p; unset p; "), + shellEscape(dirOf(*shell)), shellEscape(*shell), + (getenv("TZ") ? (std::string("export TZ=") + + shellEscape(getenv("TZ")) + "; ") + : ""), + envCommand); vomit("Sourcing nix-shell with file %s and contents:\n%s", rcfile, rc); writeFile(rcfile, rc); @@ -531,9 +578,8 @@ static void main_nix_build(int argc, char * * argv) for (auto & i : env) envStrs.push_back(i.first + "=" + i.second); - auto args = interactive - ? Strings{"bash", "--rcfile", rcfile} - : Strings{"bash", rcfile}; + auto args = interactive ? Strings{"bash", "--rcfile", rcfile} + : Strings{"bash", rcfile}; auto envPtrs = stringsToCharPtrs(envStrs); @@ -563,7 +609,8 @@ static void main_nix_build(int argc, char * * argv) auto outputName = drvInfo.queryOutputName(); if (outputName == "") - throw Error("derivation '%s' lacks an 'outputName' attribute", store->printStorePath(drvPath)); + throw Error("derivation '%s' lacks an 'outputName' attribute", + store->printStorePath(drvPath)); pathsToBuild.push_back(DerivedPath::Built{drvPath, {outputName}}); pathsToBuildOrdered.push_back({drvPath, {outputName}}); @@ -578,7 +625,8 @@ static void main_nix_build(int argc, char * * argv) buildPaths(pathsToBuild); - if (dryRun) return; + if (dryRun) + return; std::vector outPaths; @@ -588,7 +636,8 @@ static void main_nix_build(int argc, char * * argv) if (counter) drvPrefix += fmt("-%d", counter + 1); - auto builtOutputs = evalStore->queryPartialDerivationOutputMap(drvPath); + auto builtOutputs = + evalStore->queryPartialDerivationOutputMap(drvPath); auto maybeOutputPath = builtOutputs.at(outputName); assert(maybeOutputPath); @@ -596,7 +645,8 @@ static void main_nix_build(int argc, char * * argv) if (auto store2 = store.dynamic_pointer_cast()) { std::string symlink = drvPrefix; - if (outputName != "out") symlink += "-" + outputName; + if (outputName != "out") + symlink += "-" + outputName; store2->addPermRoot(outputPath, absPath(symlink)); } diff --git a/src/nix-channel/nix-channel.cc b/src/nix-channel/nix-channel.cc old mode 100755 new mode 100644 index cf52b03b4904..71e48e8ae4eb --- a/src/nix-channel/nix-channel.cc +++ b/src/nix-channel/nix-channel.cc @@ -19,10 +19,12 @@ static Path channelsList; // Reads the list of channels. static void readChannels() { - if (!pathExists(channelsList)) return; + if (!pathExists(channelsList)) + return; auto channelsFile = readFile(channelsList); - for (const auto & line : tokenizeString>(channelsFile, "\n")) { + for (const auto & line : + tokenizeString>(channelsFile, "\n")) { chomp(line); if (std::regex_search(line, std::regex("^\\s*\\#"))) continue; @@ -36,11 +38,13 @@ static void readChannels() // Writes the list of channels. static void writeChannels() { - auto channelsFD = AutoCloseFD{open(channelsList.c_str(), O_WRONLY | O_CLOEXEC | O_CREAT | O_TRUNC, 0644)}; + auto channelsFD = AutoCloseFD{open( + channelsList.c_str(), O_WRONLY | O_CLOEXEC | O_CREAT | O_TRUNC, 0644)}; if (!channelsFD) throw SysError("opening '%1%' for writing", channelsList); for (const auto & channel : channels) - writeFull(channelsFD.get(), channel.second + " " + channel.first + "\n"); + writeFull(channelsFD.get(), + channel.second + " " + channel.first + "\n"); } // Adds a channel. @@ -64,7 +68,8 @@ static void removeChannel(const std::string & name) channels.erase(name); writeChannels(); - runProgram(settings.nixBinDir + "/nix-env", true, { "--profile", profile, "--uninstall", name }); + runProgram(settings.nixBinDir + "/nix-env", true, + {"--profile", profile, "--uninstall", name}); } static Path nixDefExpr; @@ -78,8 +83,8 @@ static void update(const StringSet & channelNames) auto [fd, unpackChannelPath] = createTempFile(); writeFull(fd.get(), - #include "unpack-channel.nix.gen.hh" - ); +#include "unpack-channel.nix.gen.hh" + ); fd = -1; AutoDelete del(unpackChannelPath, false); @@ -104,39 +109,62 @@ static void update(const StringSet & channelNames) // no need to update this channel, reuse the existing store path Path symlink = profile + "/" + name; Path storepath = dirOf(readLink(symlink)); - exprs.push_back("f: rec { name = \"" + cname + "\"; type = \"derivation\"; outputs = [\"out\"]; system = \"builtin\"; outPath = builtins.storePath \"" + storepath + "\"; out = { inherit outPath; };}"); + exprs.push_back( + "f: rec { name = \"" + cname + + "\"; type = \"derivation\"; outputs = [\"out\"]; system = " + "\"builtin\"; outPath = builtins.storePath \"" + + storepath + "\"; out = { inherit outPath; };}"); } else { - // We want to download the url to a file to see if it's a tarball while also checking if we - // got redirected in the process, so that we can grab the various parts of a nix channel - // definition from a consistent location if the redirect changes mid-download. - auto result = fetchers::downloadFile(store, url, std::string(baseNameOf(url)), false); + // We want to download the url to a file to see if it's a tarball + // while also checking if we got redirected in the process, so that + // we can grab the various parts of a nix channel definition from a + // consistent location if the redirect changes mid-download. + auto result = fetchers::downloadFile( + store, url, std::string(baseNameOf(url)), false); auto filename = store->toRealPath(result.storePath); url = result.effectiveUrl; bool unpacked = false; - if (std::regex_search(filename, std::regex("\\.tar\\.(gz|bz2|xz)$"))) { - runProgram(settings.nixBinDir + "/nix-build", false, { "--no-out-link", "--expr", "import " + unpackChannelPath + - "{ name = \"" + cname + "\"; channelName = \"" + name + "\"; src = builtins.storePath \"" + filename + "\"; }" }); + if (std::regex_search(filename, + std::regex("\\.tar\\.(gz|bz2|xz)$"))) { + runProgram(settings.nixBinDir + "/nix-build", false, + {"--no-out-link", "--expr", + "import " + unpackChannelPath + "{ name = \"" + + cname + "\"; channelName = \"" + name + + "\"; src = builtins.storePath \"" + filename + + "\"; }"}); unpacked = true; } if (!unpacked) { // Download the channel tarball. try { - filename = store->toRealPath(fetchers::downloadFile(store, url + "/nixexprs.tar.xz", "nixexprs.tar.xz", false).storePath); + filename = store->toRealPath( + fetchers::downloadFile(store, url + "/nixexprs.tar.xz", + "nixexprs.tar.xz", false) + .storePath); } catch (FileTransferError & e) { - filename = store->toRealPath(fetchers::downloadFile(store, url + "/nixexprs.tar.bz2", "nixexprs.tar.bz2", false).storePath); + filename = store->toRealPath( + fetchers::downloadFile(store, url + "/nixexprs.tar.bz2", + "nixexprs.tar.bz2", false) + .storePath); } } - // Regardless of where it came from, add the expression representing this channel to accumulated expression - exprs.push_back("f: f { name = \"" + cname + "\"; channelName = \"" + name + "\"; src = builtins.storePath \"" + filename + "\"; " + extraAttrs + " }"); + // Regardless of where it came from, add the expression representing + // this channel to accumulated expression + exprs.push_back("f: f { name = \"" + cname + + "\"; channelName = \"" + name + + "\"; src = builtins.storePath \"" + filename + + "\"; " + extraAttrs + " }"); } } // Unpack the channel tarballs into the Nix store and install them // into the channels profile. std::cerr << "unpacking channels...\n"; - Strings envArgs{ "--profile", profile, "--file", unpackChannelPath, "--install", "--remove-all", "--from-expression" }; + Strings envArgs{"--profile", profile, "--file", + unpackChannelPath, "--install", "--remove-all", + "--from-expression"}; for (auto & expr : exprs) envArgs.push_back(std::move(expr)); envArgs.push_back("--quiet"); @@ -166,86 +194,83 @@ static int main_nix_channel(int argc, char ** argv) nixDefExpr = home + "/.nix-defexpr"; // Figure out the name of the channels profile. - profile = fmt("%s/profiles/per-user/%s/channels", settings.nixStateDir, getUserName()); - - enum { - cNone, - cAdd, - cRemove, - cList, - cUpdate, - cRollback - } cmd = cNone; + profile = fmt("%s/profiles/per-user/%s/channels", settings.nixStateDir, + getUserName()); + + enum { cNone, cAdd, cRemove, cList, cUpdate, cRollback } cmd = cNone; std::vector args; - parseCmdLine(argc, argv, [&](Strings::iterator & arg, const Strings::iterator & end) { - if (*arg == "--help") { - showManPage("nix-channel"); - } else if (*arg == "--version") { - printVersion("nix-channel"); - } else if (*arg == "--add") { - cmd = cAdd; - } else if (*arg == "--remove") { - cmd = cRemove; - } else if (*arg == "--list") { - cmd = cList; - } else if (*arg == "--update") { - cmd = cUpdate; - } else if (*arg == "--rollback") { - cmd = cRollback; - } else { - if (hasPrefix(*arg, "-")) - throw UsageError("unsupported argument '%s'", *arg); - args.push_back(std::move(*arg)); - } - return true; - }); + parseCmdLine( + argc, argv, + [&](Strings::iterator & arg, const Strings::iterator & end) { + if (*arg == "--help") { + showManPage("nix-channel"); + } else if (*arg == "--version") { + printVersion("nix-channel"); + } else if (*arg == "--add") { + cmd = cAdd; + } else if (*arg == "--remove") { + cmd = cRemove; + } else if (*arg == "--list") { + cmd = cList; + } else if (*arg == "--update") { + cmd = cUpdate; + } else if (*arg == "--rollback") { + cmd = cRollback; + } else { + if (hasPrefix(*arg, "-")) + throw UsageError("unsupported argument '%s'", *arg); + args.push_back(std::move(*arg)); + } + return true; + }); switch (cmd) { - case cNone: - throw UsageError("no command specified"); - case cAdd: - if (args.size() < 1 || args.size() > 2) - throw UsageError("'--add' requires one or two arguments"); - { + case cNone: + throw UsageError("no command specified"); + case cAdd: + if (args.size() < 1 || args.size() > 2) + throw UsageError("'--add' requires one or two arguments"); + { auto url = args[0]; std::string name; if (args.size() == 2) { name = args[1]; } else { name = baseNameOf(url); - name = std::regex_replace(name, std::regex("-unstable$"), ""); + name = + std::regex_replace(name, std::regex("-unstable$"), ""); name = std::regex_replace(name, std::regex("-stable$"), ""); } addChannel(url, name); - } - break; - case cRemove: - if (args.size() != 1) - throw UsageError("'--remove' requires one argument"); - removeChannel(args[0]); - break; - case cList: - if (!args.empty()) - throw UsageError("'--list' expects no arguments"); - readChannels(); - for (const auto & channel : channels) - std::cout << channel.first << ' ' << channel.second << '\n'; - break; - case cUpdate: - update(StringSet(args.begin(), args.end())); - break; - case cRollback: - if (args.size() > 1) - throw UsageError("'--rollback' has at most one argument"); - Strings envArgs{"--profile", profile}; - if (args.size() == 1) { - envArgs.push_back("--switch-generation"); - envArgs.push_back(args[0]); - } else { - envArgs.push_back("--rollback"); - } - runProgram(settings.nixBinDir + "/nix-env", false, envArgs); - break; + } + break; + case cRemove: + if (args.size() != 1) + throw UsageError("'--remove' requires one argument"); + removeChannel(args[0]); + break; + case cList: + if (!args.empty()) + throw UsageError("'--list' expects no arguments"); + readChannels(); + for (const auto & channel : channels) + std::cout << channel.first << ' ' << channel.second << '\n'; + break; + case cUpdate: + update(StringSet(args.begin(), args.end())); + break; + case cRollback: + if (args.size() > 1) + throw UsageError("'--rollback' has at most one argument"); + Strings envArgs{"--profile", profile}; + if (args.size() == 1) { + envArgs.push_back("--switch-generation"); + envArgs.push_back(args[0]); + } else { + envArgs.push_back("--rollback"); + } + runProgram(settings.nixBinDir + "/nix-env", false, envArgs); + break; } return 0; diff --git a/src/nix-collect-garbage/nix-collect-garbage.cc b/src/nix-collect-garbage/nix-collect-garbage.cc index e413faffe878..b7fe0e8494cb 100644 --- a/src/nix-collect-garbage/nix-collect-garbage.cc +++ b/src/nix-collect-garbage/nix-collect-garbage.cc @@ -14,14 +14,14 @@ using namespace nix; std::string deleteOlderThan; bool dryRun = false; - /* If `-d' was specified, remove all old generations of all profiles. * Of course, this makes rollbacks to before this point in time * impossible. */ void removeOldGenerations(std::string dir) { - if (access(dir.c_str(), R_OK) != 0) return; + if (access(dir.c_str(), R_OK) != 0) + return; bool canWrite = access(dir.c_str(), W_OK) == 0; @@ -36,11 +36,13 @@ void removeOldGenerations(std::string dir) try { link = readLink(path); } catch (SysError & e) { - if (e.errNo == ENOENT) continue; + if (e.errNo == ENOENT) + continue; throw; } if (link.find("link") != std::string::npos) { - printInfo(format("removing old generations of profile %1%") % path); + printInfo(format("removing old generations of profile %1%") % + path); if (deleteOlderThan != "") deleteGenerationsOlderThan(path, deleteOlderThan, dryRun); else @@ -52,33 +54,38 @@ void removeOldGenerations(std::string dir) } } -static int main_nix_collect_garbage(int argc, char * * argv) +static int main_nix_collect_garbage(int argc, char ** argv) { { bool removeOld = false; GCOptions options; - parseCmdLine(argc, argv, [&](Strings::iterator & arg, const Strings::iterator & end) { - if (*arg == "--help") - showManPage("nix-collect-garbage"); - else if (*arg == "--version") - printVersion("nix-collect-garbage"); - else if (*arg == "--delete-old" || *arg == "-d") removeOld = true; - else if (*arg == "--delete-older-than") { - removeOld = true; - deleteOlderThan = getArg(*arg, arg, end); - } - else if (*arg == "--dry-run") dryRun = true; - else if (*arg == "--max-freed") - options.maxFreed = std::max(getIntArg(*arg, arg, end, true), (int64_t) 0); - else - return false; - return true; - }); + parseCmdLine( + argc, argv, + [&](Strings::iterator & arg, const Strings::iterator & end) { + if (*arg == "--help") + showManPage("nix-collect-garbage"); + else if (*arg == "--version") + printVersion("nix-collect-garbage"); + else if (*arg == "--delete-old" || *arg == "-d") + removeOld = true; + else if (*arg == "--delete-older-than") { + removeOld = true; + deleteOlderThan = getArg(*arg, arg, end); + } else if (*arg == "--dry-run") + dryRun = true; + else if (*arg == "--max-freed") + options.maxFreed = std::max( + getIntArg(*arg, arg, end, true), (int64_t) 0); + else + return false; + return true; + }); auto profilesDir = settings.nixStateDir + "/profiles"; - if (removeOld) removeOldGenerations(profilesDir); + if (removeOld) + removeOldGenerations(profilesDir); // Run the actual garbage collector. if (!dryRun) { @@ -94,4 +101,5 @@ static int main_nix_collect_garbage(int argc, char * * argv) } } -static RegisterLegacyCommand r_nix_collect_garbage("nix-collect-garbage", main_nix_collect_garbage); +static RegisterLegacyCommand r_nix_collect_garbage("nix-collect-garbage", + main_nix_collect_garbage); diff --git a/src/nix-copy-closure/nix-copy-closure.cc b/src/nix-copy-closure/nix-copy-closure.cc old mode 100755 new mode 100644 index 841d50fd3077..09ed15d8987a --- a/src/nix-copy-closure/nix-copy-closure.cc +++ b/src/nix-copy-closure/nix-copy-closure.cc @@ -15,33 +15,40 @@ static int main_nix_copy_closure(int argc, char ** argv) std::string sshHost; PathSet storePaths; - parseCmdLine(argc, argv, [&](Strings::iterator & arg, const Strings::iterator & end) { - if (*arg == "--help") - showManPage("nix-copy-closure"); - else if (*arg == "--version") - printVersion("nix-copy-closure"); - else if (*arg == "--gzip" || *arg == "--bzip2" || *arg == "--xz") { - if (*arg != "--gzip") - printMsg(lvlError, format("Warning: '%1%' is not implemented, falling back to gzip") % *arg); - gzip = true; - } else if (*arg == "--from") - toMode = false; - else if (*arg == "--to") - toMode = true; - else if (*arg == "--include-outputs") - includeOutputs = true; - else if (*arg == "--show-progress") - printMsg(lvlError, "Warning: '--show-progress' is not implemented"); - else if (*arg == "--dry-run") - dryRun = true; - else if (*arg == "--use-substitutes" || *arg == "-s") - useSubstitutes = Substitute; - else if (sshHost.empty()) - sshHost = *arg; - else - storePaths.insert(*arg); - return true; - }); + parseCmdLine( + argc, argv, + [&](Strings::iterator & arg, const Strings::iterator & end) { + if (*arg == "--help") + showManPage("nix-copy-closure"); + else if (*arg == "--version") + printVersion("nix-copy-closure"); + else if (*arg == "--gzip" || *arg == "--bzip2" || + *arg == "--xz") { + if (*arg != "--gzip") + printMsg(lvlError, + format("Warning: '%1%' is not implemented, " + "falling back to gzip") % + *arg); + gzip = true; + } else if (*arg == "--from") + toMode = false; + else if (*arg == "--to") + toMode = true; + else if (*arg == "--include-outputs") + includeOutputs = true; + else if (*arg == "--show-progress") + printMsg(lvlError, + "Warning: '--show-progress' is not implemented"); + else if (*arg == "--dry-run") + dryRun = true; + else if (*arg == "--use-substitutes" || *arg == "-s") + useSubstitutes = Substitute; + else if (sshHost.empty()) + sshHost = *arg; + else + storePaths.insert(*arg); + return true; + }); if (sshHost.empty()) throw UsageError("no host name specified"); @@ -54,10 +61,12 @@ static int main_nix_copy_closure(int argc, char ** argv) for (auto & path : storePaths) storePaths2.insert(from->followLinksToStorePath(path)); - copyClosure(*from, *to, storePaths2, NoRepair, NoCheckSigs, useSubstitutes); + copyClosure(*from, *to, storePaths2, NoRepair, NoCheckSigs, + useSubstitutes); return 0; } } -static RegisterLegacyCommand r_nix_copy_closure("nix-copy-closure", main_nix_copy_closure); +static RegisterLegacyCommand r_nix_copy_closure("nix-copy-closure", + main_nix_copy_closure); diff --git a/src/nix-env/nix-env.cc b/src/nix-env/nix-env.cc index a69d3700d010..92884d250a5d 100644 --- a/src/nix-env/nix-env.cc +++ b/src/nix-env/nix-env.cc @@ -30,7 +30,6 @@ using namespace nix; using std::cout; - typedef enum { srcNixExprDrvs, srcNixExprs, @@ -40,19 +39,15 @@ typedef enum { srcUnknown } InstallSourceType; - -struct InstallSourceInfo -{ +struct InstallSourceInfo { InstallSourceType type; - Path nixExprPath; /* for srcNixExprDrvs, srcNixExprs */ - Path profile; /* for srcProfile */ + Path nixExprPath; /* for srcNixExprDrvs, srcNixExprs */ + Path profile; /* for srcProfile */ std::string systemFilter; /* for srcNixExprDrvs */ Bindings * autoArgs; }; - -struct Globals -{ +struct Globals { InstallSourceInfo instSource; Path profile; std::shared_ptr state; @@ -63,55 +58,52 @@ struct Globals bool prebuiltOnly; }; +typedef void (*Operation)(Globals & globals, Strings opFlags, Strings opArgs); -typedef void (* Operation) (Globals & globals, - Strings opFlags, Strings opArgs); - - -static std::string needArg(Strings::iterator & i, - Strings & args, const std::string & arg) +static std::string needArg(Strings::iterator & i, Strings & args, + const std::string & arg) { - if (i == args.end()) throw UsageError("'%1%' requires an argument", arg); + if (i == args.end()) + throw UsageError("'%1%' requires an argument", arg); return *i++; } - -static bool parseInstallSourceOptions(Globals & globals, - Strings::iterator & i, Strings & args, const std::string & arg) +static bool parseInstallSourceOptions(Globals & globals, Strings::iterator & i, + Strings & args, const std::string & arg) { if (arg == "--from-expression" || arg == "-E") globals.instSource.type = srcNixExprs; else if (arg == "--from-profile") { globals.instSource.type = srcProfile; globals.instSource.profile = needArg(i, args, arg); - } - else if (arg == "--attr" || arg == "-A") + } else if (arg == "--attr" || arg == "-A") globals.instSource.type = srcAttrPath; - else return false; + else + return false; return true; } - static bool isNixExpr(const Path & path, struct stat & st) { - return S_ISREG(st.st_mode) || (S_ISDIR(st.st_mode) && pathExists(path + "/default.nix")); + return S_ISREG(st.st_mode) || + (S_ISDIR(st.st_mode) && pathExists(path + "/default.nix")); } - static constexpr size_t maxAttrs = 1024; - -static void getAllExprs(EvalState & state, - const Path & path, StringSet & seen, BindingsBuilder & attrs) +static void getAllExprs(EvalState & state, const Path & path, StringSet & seen, + BindingsBuilder & attrs) { StringSet namesSorted; - for (auto & i : readDirectory(path)) namesSorted.insert(i.name); + for (auto & i : readDirectory(path)) + namesSorted.insert(i.name); for (auto & i : namesSorted) { /* Ignore the manifest.nix used by profiles. This is necessary to prevent it from showing up in channels (which are implemented using profiles). */ - if (i == "manifest.nix") continue; + if (i == "manifest.nix") + continue; Path path2 = path + "/" + i; @@ -119,7 +111,8 @@ static void getAllExprs(EvalState & state, if (stat(path2.c_str(), &st) == -1) continue; // ignore dangling symlinks in ~/.nix-defexpr - if (isNixExpr(path2, st) && (!S_ISREG(st.st_mode) || hasSuffix(path2, ".nix"))) { + if (isNixExpr(path2, st) && + (!S_ISREG(st.st_mode) || hasSuffix(path2, ".nix"))) { /* Strip off the `.nix' filename suffix (if applicable), otherwise the attribute cannot be selected with the `-A' option. Useful if you want to stick a Nix @@ -129,29 +122,33 @@ static void getAllExprs(EvalState & state, attrName = std::string(attrName, 0, attrName.size() - 4); if (!seen.insert(attrName).second) { std::string suggestionMessage = ""; - if (path2.find("channels") != std::string::npos && path.find("channels") != std::string::npos) { - suggestionMessage = fmt("\nsuggestion: remove '%s' from either the root channels or the user channels", attrName); + if (path2.find("channels") != std::string::npos && + path.find("channels") != std::string::npos) { + suggestionMessage = + fmt("\nsuggestion: remove '%s' from either the root " + "channels or the user channels", + attrName); } - printError("warning: name collision in input Nix expressions, skipping '%1%'" - "%2%", path2, suggestionMessage); + printError("warning: name collision in input Nix expressions, " + "skipping '%1%'" + "%2%", + path2, suggestionMessage); continue; } /* Load the expression on demand. */ auto vArg = state.allocValue(); vArg->mkString(path2); if (seen.size() == maxAttrs) - throw Error("too many Nix expressions in directory '%1%'", path); + throw Error("too many Nix expressions in directory '%1%'", + path); attrs.alloc(attrName).mkApp(&state.getBuiltin("import"), vArg); - } - else if (S_ISDIR(st.st_mode)) + } else if (S_ISDIR(st.st_mode)) /* `path2' is a directory (with no default.nix in it); recurse into it. */ getAllExprs(state, path2, seen, attrs); } } - - static void loadSourceExpr(EvalState & state, const Path & path, Value & v) { struct stat st; @@ -175,13 +172,13 @@ static void loadSourceExpr(EvalState & state, const Path & path, Value & v) v.mkAttrs(attrs); } - else throw Error("path '%s' is not a directory or a Nix expression", path); + else + throw Error("path '%s' is not a directory or a Nix expression", path); } - static void loadDerivations(EvalState & state, Path nixExprPath, - std::string systemFilter, Bindings & autoArgs, - const std::string & pathPrefix, DrvInfos & elems) + std::string systemFilter, Bindings & autoArgs, + const std::string & pathPrefix, DrvInfos & elems) { Value vRoot; loadSourceExpr(state, nixExprPath, vRoot); @@ -193,35 +190,33 @@ static void loadDerivations(EvalState & state, Path nixExprPath, /* Filter out all derivations not applicable to the current system. */ for (DrvInfos::iterator i = elems.begin(), j; i != elems.end(); i = j) { - j = i; j++; + j = i; + j++; if (systemFilter != "*" && i->querySystem() != systemFilter) elems.erase(i); } } - static long getPriority(EvalState & state, DrvInfo & drv) { return drv.queryMetaInt("priority", 0); } - static long comparePriorities(EvalState & state, DrvInfo & drv1, DrvInfo & drv2) { return getPriority(state, drv2) - getPriority(state, drv1); } - // FIXME: this function is rather slow since it checks a single path // at a time. static bool isPrebuilt(EvalState & state, DrvInfo & elem) { auto path = elem.queryOutPath(); - if (state.store->isValidPath(path)) return true; + if (state.store->isValidPath(path)) + return true; return state.store->querySubstitutablePaths({path}).count(path); } - static void checkSelectorUse(DrvNames & selectors) { /* Check that all selectors have been used. */ @@ -230,14 +225,15 @@ static void checkSelectorUse(DrvNames & selectors) throw Error("selector '%1%' matches no derivations", i.fullName); } - namespace { -std::set searchByPrefix(const DrvInfos & allElems, std::string_view prefix) { +std::set searchByPrefix(const DrvInfos & allElems, + std::string_view prefix) +{ constexpr std::size_t maxResults = 3; std::set result; for (const auto & drvInfo : allElems) { - const auto drvName = DrvName { drvInfo.queryName() }; + const auto drvName = DrvName{drvInfo.queryName()}; if (hasPrefix(drvName.name, prefix)) { result.emplace(drvName.name); @@ -249,15 +245,14 @@ std::set searchByPrefix(const DrvInfos & allElems, std::string_view return result; } -struct Match -{ +struct Match { DrvInfo drvInfo; std::size_t index; Match(DrvInfo drvInfo_, std::size_t index_) - : drvInfo{std::move(drvInfo_)} - , index{index_} - {} + : drvInfo{std::move(drvInfo_)}, index{index_} + { + } }; /* If a selector matches multiple derivations @@ -267,7 +262,8 @@ struct Match derivations, pick the one with the highest version. Finally, if there are still multiple derivations, arbitrarily pick the first one. */ -std::vector pickNewestOnly(EvalState & state, std::vector matches) { +std::vector pickNewestOnly(EvalState & state, std::vector matches) +{ /* Map from package names to derivations. */ std::map newest; StringSet multiple; @@ -275,7 +271,7 @@ std::vector pickNewestOnly(EvalState & state, std::vector matches) for (auto & match : matches) { auto & oneDrv = match.drvInfo; - const auto drvName = DrvName { oneDrv.queryName() }; + const auto drvName = DrvName{oneDrv.queryName()}; long comparison = 1; const auto itOther = newest.find(drvName.name); @@ -283,14 +279,15 @@ std::vector pickNewestOnly(EvalState & state, std::vector matches) if (itOther != newest.end()) { auto & newestDrv = itOther->second.drvInfo; - comparison = - oneDrv.querySystem() == newestDrv.querySystem() ? 0 : - oneDrv.querySystem() == settings.thisSystem ? 1 : - newestDrv.querySystem() == settings.thisSystem ? -1 : 0; + comparison = oneDrv.querySystem() == newestDrv.querySystem() ? 0 + : oneDrv.querySystem() == settings.thisSystem ? 1 + : newestDrv.querySystem() == settings.thisSystem ? -1 + : 0; if (comparison == 0) comparison = comparePriorities(state, oneDrv, newestDrv); if (comparison == 0) - comparison = compareVersions(drvName.version, DrvName { newestDrv.queryName() }.version); + comparison = compareVersions( + drvName.version, DrvName{newestDrv.queryName()}.version); } if (comparison > 0) { @@ -305,9 +302,9 @@ std::vector pickNewestOnly(EvalState & state, std::vector matches) matches.clear(); for (auto & [name, match] : newest) { if (multiple.find(name) != multiple.end()) - warn( - "there are multiple derivations named '%1%'; using the first one", - name); + warn("there are multiple derivations named '%1%'; using the first " + "one", + name); matches.push_back(match); } @@ -317,7 +314,7 @@ std::vector pickNewestOnly(EvalState & state, std::vector matches) } // end namespace static DrvInfos filterBySelector(EvalState & state, const DrvInfos & allElems, - const Strings & args, bool newestOnly) + const Strings & args, bool newestOnly) { DrvNames selectors = drvNamesFromArgs(args); if (selectors.empty()) @@ -329,7 +326,7 @@ static DrvInfos filterBySelector(EvalState & state, const DrvInfos & allElems, for (auto & selector : selectors) { std::vector matches; for (const auto & [index, drvInfo] : enumerate(allElems)) { - const auto drvName = DrvName { drvInfo.queryName() }; + const auto drvName = DrvName{drvInfo.queryName()}; if (selector.matches(drvName)) { ++selector.hits; matches.emplace_back(drvInfo, index); @@ -350,13 +347,16 @@ static DrvInfos filterBySelector(EvalState & state, const DrvInfos & allElems, const auto prefixHits = searchByPrefix(allElems, selector.name); if (prefixHits.empty()) { - throw Error("selector '%1%' matches no derivations", selector.fullName); + throw Error("selector '%1%' matches no derivations", + selector.fullName); } else { std::string suggestionMessage = ", maybe you meant:"; for (const auto & drvName : prefixHits) { suggestionMessage += fmt("\n%s", drvName); } - throw Error("selector '%1%' matches no derivations" + suggestionMessage, selector.fullName); + throw Error("selector '%1%' matches no derivations" + + suggestionMessage, + selector.fullName); } } } @@ -364,16 +364,14 @@ static DrvInfos filterBySelector(EvalState & state, const DrvInfos & allElems, return elems; } - static bool isPath(std::string_view s) { return s.find('/') != std::string_view::npos; } - -static void queryInstSources(EvalState & state, - InstallSourceInfo & instSource, const Strings & args, - DrvInfos & elems, bool newestOnly) +static void queryInstSources(EvalState & state, InstallSourceInfo & instSource, + const Strings & args, DrvInfos & elems, + bool newestOnly) { InstallSourceType type = instSource.type; if (type == srcUnknown && args.size() > 0 && isPath(args.front())) @@ -381,98 +379,97 @@ static void queryInstSources(EvalState & state, switch (type) { - /* Get the available user environment elements from the - derivations specified in a Nix expression, including only - those with names matching any of the names in `args'. */ - case srcUnknown: - case srcNixExprDrvs: { + /* Get the available user environment elements from the + derivations specified in a Nix expression, including only + those with names matching any of the names in `args'. */ + case srcUnknown: + case srcNixExprDrvs: { - /* Load the derivations from the (default or specified) - Nix expression. */ - DrvInfos allElems; - loadDerivations(state, instSource.nixExprPath, - instSource.systemFilter, *instSource.autoArgs, "", allElems); + /* Load the derivations from the (default or specified) + Nix expression. */ + DrvInfos allElems; + loadDerivations(state, instSource.nixExprPath, instSource.systemFilter, + *instSource.autoArgs, "", allElems); - elems = filterBySelector(state, allElems, args, newestOnly); + elems = filterBySelector(state, allElems, args, newestOnly); - break; - } - - /* Get the available user environment elements from the Nix - expressions specified on the command line; these should be - functions that take the default Nix expression file as - argument, e.g., if the file is `./foo.nix', then the - argument `x: x.bar' is equivalent to `(x: x.bar) - (import ./foo.nix)' = `(import ./foo.nix).bar'. */ - case srcNixExprs: { - - Value vArg; - loadSourceExpr(state, instSource.nixExprPath, vArg); - - for (auto & i : args) { - Expr * eFun = state.parseExprFromString(i, absPath(".")); - Value vFun, vTmp; - state.eval(eFun, vFun); - vTmp.mkApp(&vFun, &vArg); - getDerivations(state, vTmp, "", *instSource.autoArgs, elems, true); - } + break; + } - break; + /* Get the available user environment elements from the Nix + expressions specified on the command line; these should be + functions that take the default Nix expression file as + argument, e.g., if the file is `./foo.nix', then the + argument `x: x.bar' is equivalent to `(x: x.bar) + (import ./foo.nix)' = `(import ./foo.nix).bar'. */ + case srcNixExprs: { + + Value vArg; + loadSourceExpr(state, instSource.nixExprPath, vArg); + + for (auto & i : args) { + Expr * eFun = state.parseExprFromString(i, absPath(".")); + Value vFun, vTmp; + state.eval(eFun, vFun); + vTmp.mkApp(&vFun, &vArg); + getDerivations(state, vTmp, "", *instSource.autoArgs, elems, true); } - /* The available user environment elements are specified as a - list of store paths (which may or may not be - derivations). */ - case srcStorePaths: { + break; + } - for (auto & i : args) { - auto path = state.store->followLinksToStorePath(i); + /* The available user environment elements are specified as a + list of store paths (which may or may not be + derivations). */ + case srcStorePaths: { - std::string name(path.name()); + for (auto & i : args) { + auto path = state.store->followLinksToStorePath(i); - DrvInfo elem(state, "", nullptr); - elem.setName(name); + std::string name(path.name()); - if (path.isDerivation()) { - elem.setDrvPath(path); - auto outputs = state.store->queryDerivationOutputMap(path); - elem.setOutPath(outputs.at("out")); - if (name.size() >= drvExtension.size() && - std::string(name, name.size() - drvExtension.size()) == drvExtension) - name = name.substr(0, name.size() - drvExtension.size()); - } - else - elem.setOutPath(path); + DrvInfo elem(state, "", nullptr); + elem.setName(name); - elems.push_back(elem); - } + if (path.isDerivation()) { + elem.setDrvPath(path); + auto outputs = state.store->queryDerivationOutputMap(path); + elem.setOutPath(outputs.at("out")); + if (name.size() >= drvExtension.size() && + std::string(name, name.size() - drvExtension.size()) == + drvExtension) + name = name.substr(0, name.size() - drvExtension.size()); + } else + elem.setOutPath(path); - break; + elems.push_back(elem); } - /* Get the available user environment elements from another - user environment. These are then filtered as in the - `srcNixExprDrvs' case. */ - case srcProfile: { - elems = filterBySelector(state, - queryInstalled(state, instSource.profile), - args, newestOnly); - break; - } + break; + } - case srcAttrPath: { - Value vRoot; - loadSourceExpr(state, instSource.nixExprPath, vRoot); - for (auto & i : args) { - Value & v(*findAlongAttrPath(state, i, *instSource.autoArgs, vRoot).first); - getDerivations(state, v, "", *instSource.autoArgs, elems, true); - } - break; + /* Get the available user environment elements from another + user environment. These are then filtered as in the + `srcNixExprDrvs' case. */ + case srcProfile: { + elems = filterBySelector( + state, queryInstalled(state, instSource.profile), args, newestOnly); + break; + } + + case srcAttrPath: { + Value vRoot; + loadSourceExpr(state, instSource.nixExprPath, vRoot); + for (auto & i : args) { + Value & v(*findAlongAttrPath(state, i, *instSource.autoArgs, vRoot) + .first); + getDerivations(state, v, "", *instSource.autoArgs, elems, true); } + break; + } } } - static void printMissing(EvalState & state, DrvInfos & elems) { std::vector targets; @@ -485,21 +482,17 @@ static void printMissing(EvalState & state, DrvInfos & elems) printMissing(state.store, targets); } +static bool keep(DrvInfo & drv) { return drv.queryMetaBool("keep", false); } -static bool keep(DrvInfo & drv) -{ - return drv.queryMetaBool("keep", false); -} - - -static void installDerivations(Globals & globals, - const Strings & args, const Path & profile) +static void installDerivations(Globals & globals, const Strings & args, + const Path & profile) { debug(format("installing derivations")); /* Get the set of user environment elements to be installed. */ DrvInfos newElems, newElemsTmp; - queryInstSources(*globals.state, globals.instSource, args, newElemsTmp, true); + queryInstSources(*globals.state, globals.instSource, args, newElemsTmp, + true); /* If --prebuilt-only is given, filter out source-only packages. */ for (auto & i : newElemsTmp) @@ -517,7 +510,6 @@ static void installDerivations(Globals & globals, newNames.insert(DrvName(i.queryName()).name); } - while (true) { auto lockToken = optimisticLockProfile(profile); @@ -531,8 +523,7 @@ static void installDerivations(Globals & globals, for (auto & i : installedElems) { DrvName drvName(i.queryName()); if (!globals.preserveInstalled && - newNames.find(drvName.name) != newNames.end() && - !keep(i)) + newNames.find(drvName.name) != newNames.end() && !keep(i)) printInfo("replacing old '%s'", i.queryName()); else allElems.push_back(i); @@ -544,35 +535,36 @@ static void installDerivations(Globals & globals, printMissing(*globals.state, newElems); - if (globals.dryRun) return; + if (globals.dryRun) + return; - if (createUserEnv(*globals.state, allElems, - profile, settings.envKeepDerivations, lockToken)) break; + if (createUserEnv(*globals.state, allElems, profile, + settings.envKeepDerivations, lockToken)) + break; } } - static void opInstall(Globals & globals, Strings opFlags, Strings opArgs) { - for (Strings::iterator i = opFlags.begin(); i != opFlags.end(); ) { + for (Strings::iterator i = opFlags.begin(); i != opFlags.end();) { auto arg = *i++; - if (parseInstallSourceOptions(globals, i, opFlags, arg)) ; + if (parseInstallSourceOptions(globals, i, opFlags, arg)) + ; else if (arg == "--preserve-installed" || arg == "-P") globals.preserveInstalled = true; else if (arg == "--remove-all" || arg == "-r") globals.removeAll = true; - else throw UsageError("unknown flag '%1%'", arg); + else + throw UsageError("unknown flag '%1%'", arg); } installDerivations(globals, opArgs, globals.profile); } - typedef enum { utLt, utLeq, utEq, utAlways } UpgradeType; - -static void upgradeDerivations(Globals & globals, - const Strings & args, UpgradeType upgradeType) +static void upgradeDerivations(Globals & globals, const Strings & args, + UpgradeType upgradeType) { debug(format("upgrading derivations")); @@ -584,11 +576,13 @@ static void upgradeDerivations(Globals & globals, while (true) { auto lockToken = optimisticLockProfile(globals.profile); - DrvInfos installedElems = queryInstalled(*globals.state, globals.profile); + DrvInfos installedElems = + queryInstalled(*globals.state, globals.profile); /* Fetch all derivations from the input file. */ DrvInfos availElems; - queryInstSources(*globals.state, globals.instSource, args, availElems, false); + queryInstSources(*globals.state, globals.instSource, args, availElems, + false); /* Go through all installed derivations. */ DrvInfos newElems; @@ -616,18 +610,22 @@ static void upgradeDerivations(Globals & globals, continue; DrvName newName(j->queryName()); if (newName.name == drvName.name) { - int d = compareVersions(drvName.version, newName.version); + int d = + compareVersions(drvName.version, newName.version); if ((upgradeType == utLt && d < 0) || (upgradeType == utLeq && d <= 0) || (upgradeType == utEq && d == 0) || - upgradeType == utAlways) - { + upgradeType == utAlways) { long d2 = -1; if (bestElem != availElems.end()) { - d2 = comparePriorities(*globals.state, *bestElem, *j); - if (d2 == 0) d2 = compareVersions(bestVersion, newName.version); + d2 = comparePriorities(*globals.state, + *bestElem, *j); + if (d2 == 0) + d2 = compareVersions(bestVersion, + newName.version); } - if (d2 < 0 && (!globals.prebuiltOnly || isPrebuilt(*globals.state, *j))) { + if (d2 < 0 && (!globals.prebuiltOnly || + isPrebuilt(*globals.state, *j))) { bestElem = j; bestVersion = newName.version; } @@ -636,58 +634,66 @@ static void upgradeDerivations(Globals & globals, } if (bestElem != availElems.end() && - i.queryOutPath() != - bestElem->queryOutPath()) - { - const char * action = compareVersions(drvName.version, bestVersion) <= 0 - ? "upgrading" : "downgrading"; - printInfo("%1% '%2%' to '%3%'", - action, i.queryName(), bestElem->queryName()); + i.queryOutPath() != bestElem->queryOutPath()) { + const char * action = + compareVersions(drvName.version, bestVersion) <= 0 + ? "upgrading" + : "downgrading"; + printInfo("%1% '%2%' to '%3%'", action, i.queryName(), + bestElem->queryName()); newElems.push_back(*bestElem); - } else newElems.push_back(i); + } else + newElems.push_back(i); } catch (Error & e) { - e.addTrace(std::nullopt, "while trying to find an upgrade for '%s'", i.queryName()); + e.addTrace(std::nullopt, + "while trying to find an upgrade for '%s'", + i.queryName()); throw; } } printMissing(*globals.state, newElems); - if (globals.dryRun) return; + if (globals.dryRun) + return; - if (createUserEnv(*globals.state, newElems, - globals.profile, settings.envKeepDerivations, lockToken)) break; + if (createUserEnv(*globals.state, newElems, globals.profile, + settings.envKeepDerivations, lockToken)) + break; } } - static void opUpgrade(Globals & globals, Strings opFlags, Strings opArgs) { UpgradeType upgradeType = utLt; - for (Strings::iterator i = opFlags.begin(); i != opFlags.end(); ) { + for (Strings::iterator i = opFlags.begin(); i != opFlags.end();) { std::string arg = *i++; - if (parseInstallSourceOptions(globals, i, opFlags, arg)) ; - else if (arg == "--lt") upgradeType = utLt; - else if (arg == "--leq") upgradeType = utLeq; - else if (arg == "--eq") upgradeType = utEq; - else if (arg == "--always") upgradeType = utAlways; - else throw UsageError("unknown flag '%1%'", arg); + if (parseInstallSourceOptions(globals, i, opFlags, arg)) + ; + else if (arg == "--lt") + upgradeType = utLt; + else if (arg == "--leq") + upgradeType = utLeq; + else if (arg == "--eq") + upgradeType = utEq; + else if (arg == "--always") + upgradeType = utAlways; + else + throw UsageError("unknown flag '%1%'", arg); } upgradeDerivations(globals, opArgs, upgradeType); } - static void setMetaFlag(EvalState & state, DrvInfo & drv, - const std::string & name, const std::string & value) + const std::string & name, const std::string & value) { auto v = state.allocValue(); v->mkString(value); drv.setMeta(name, v); } - static void opSetFlag(Globals & globals, Strings opFlags, Strings opArgs) { if (opFlags.size() > 0) @@ -703,7 +709,8 @@ static void opSetFlag(Globals & globals, Strings opFlags, Strings opArgs) while (true) { std::string lockToken = optimisticLockProfile(globals.profile); - DrvInfos installedElems = queryInstalled(*globals.state, globals.profile); + DrvInfos installedElems = + queryInstalled(*globals.state, globals.profile); /* Update all matching derivations. */ for (auto & i : installedElems) { @@ -720,21 +727,24 @@ static void opSetFlag(Globals & globals, Strings opFlags, Strings opArgs) checkSelectorUse(selectors); /* Write the new user environment. */ - if (createUserEnv(*globals.state, installedElems, - globals.profile, settings.envKeepDerivations, lockToken)) break; + if (createUserEnv(*globals.state, installedElems, globals.profile, + settings.envKeepDerivations, lockToken)) + break; } } - static void opSet(Globals & globals, Strings opFlags, Strings opArgs) { auto store2 = globals.state->store.dynamic_pointer_cast(); - if (!store2) throw Error("--set is not supported for this Nix store"); + if (!store2) + throw Error("--set is not supported for this Nix store"); - for (Strings::iterator i = opFlags.begin(); i != opFlags.end(); ) { + for (Strings::iterator i = opFlags.begin(); i != opFlags.end();) { std::string arg = *i++; - if (parseInstallSourceOptions(globals, i, opFlags, arg)) ; - else throw UsageError("unknown flag '%1%'", arg); + if (parseInstallSourceOptions(globals, i, opFlags, arg)) + ; + else + throw UsageError("unknown flag '%1%'", arg); } DrvInfos elems; @@ -749,26 +759,24 @@ static void opSet(Globals & globals, Strings opFlags, Strings opArgs) drv.setName(globals.forceName); auto drvPath = drv.queryDrvPath(); - std::vector paths { - drvPath - ? (DerivedPath) (DerivedPath::Built { *drvPath }) - : (DerivedPath) (DerivedPath::Opaque { drv.queryOutPath() }), + std::vector paths{ + drvPath ? (DerivedPath) (DerivedPath::Built{*drvPath}) + : (DerivedPath) (DerivedPath::Opaque{drv.queryOutPath()}), }; printMissing(globals.state->store, paths); - if (globals.dryRun) return; - globals.state->store->buildPaths(paths, globals.state->repair ? bmRepair : bmNormal); + if (globals.dryRun) + return; + globals.state->store->buildPaths(paths, globals.state->repair ? bmRepair + : bmNormal); debug(format("switching to new user environment")); - Path generation = createGeneration( - ref(store2), - globals.profile, - drv.queryOutPath()); + Path generation = createGeneration(ref(store2), + globals.profile, drv.queryOutPath()); switchLink(globals.profile, generation); } - static void uninstallDerivations(Globals & globals, Strings & selectors, - Path & profile) + Path & profile) { while (true) { auto lockToken = optimisticLockProfile(profile); @@ -778,39 +786,41 @@ static void uninstallDerivations(Globals & globals, Strings & selectors, for (auto & selector : selectors) { DrvInfos::iterator split = workingElems.begin(); if (isPath(selector)) { - StorePath selectorStorePath = globals.state->store->followLinksToStorePath(selector); + StorePath selectorStorePath = + globals.state->store->followLinksToStorePath(selector); split = std::partition( workingElems.begin(), workingElems.end(), - [&selectorStorePath, globals](auto &elem) { + [&selectorStorePath, globals](auto & elem) { return selectorStorePath != elem.queryOutPath(); - } - ); + }); } else { DrvName selectorName(selector); - split = std::partition( - workingElems.begin(), workingElems.end(), - [&selectorName](auto &elem){ - DrvName elemName(elem.queryName()); - return !selectorName.matches(elemName); - } - ); + split = + std::partition(workingElems.begin(), workingElems.end(), + [&selectorName](auto & elem) { + DrvName elemName(elem.queryName()); + return !selectorName.matches(elemName); + }); } if (split == workingElems.end()) - warn("selector '%s' matched no installed derivations", selector); - for (auto removedElem = split; removedElem != workingElems.end(); removedElem++) { + warn("selector '%s' matched no installed derivations", + selector); + for (auto removedElem = split; removedElem != workingElems.end(); + removedElem++) { printInfo("uninstalling '%s'", removedElem->queryName()); } workingElems.erase(split, workingElems.end()); } - if (globals.dryRun) return; + if (globals.dryRun) + return; - if (createUserEnv(*globals.state, workingElems, - profile, settings.envKeepDerivations, lockToken)) break; + if (createUserEnv(*globals.state, workingElems, profile, + settings.envKeepDerivations, lockToken)) + break; } } - static void opUninstall(Globals & globals, Strings opFlags, Strings opArgs) { if (opFlags.size() > 0) @@ -818,26 +828,18 @@ static void opUninstall(Globals & globals, Strings opFlags, Strings opArgs) uninstallDerivations(globals, opArgs, globals.profile); } - -static bool cmpChars(char a, char b) -{ - return toupper(a) < toupper(b); -} - +static bool cmpChars(char a, char b) { return toupper(a) < toupper(b); } static bool cmpElemByName(const DrvInfo & a, const DrvInfo & b) { auto a_name = a.queryName(); auto b_name = b.queryName(); - return lexicographical_compare( - a_name.begin(), a_name.end(), - b_name.begin(), b_name.end(), cmpChars); + return lexicographical_compare(a_name.begin(), a_name.end(), b_name.begin(), + b_name.end(), cmpChars); } - typedef std::list Table; - void printTable(Table & table) { auto nrColumns = table.size() > 0 ? table.front().size() : 0; @@ -850,7 +852,8 @@ void printTable(Table & table) Strings::iterator j; size_t column; for (j = i.begin(), column = 0; j != i.end(); ++j, ++column) - if (j->size() > widths[column]) widths[column] = j->size(); + if (j->size() > widths[column]) + widths[column] = j->size(); } for (auto & i : table) { @@ -867,7 +870,6 @@ void printTable(Table & table) } } - /* This function compares the version of an element against the versions in the given set of elements. `cvLess' means that only lower versions are in the set, `cvEqual' means that at most an @@ -877,8 +879,9 @@ void printTable(Table & table) typedef enum { cvLess, cvEqual, cvGreater, cvUnavail } VersionDiff; -static VersionDiff compareVersionAgainstSet( - const DrvInfo & elem, const DrvInfos & elems, std::string & version) +static VersionDiff compareVersionAgainstSet(const DrvInfo & elem, + const DrvInfos & elems, + std::string & version) { DrvName name(elem.queryName()); @@ -892,14 +895,13 @@ static VersionDiff compareVersionAgainstSet( if (d < 0) { diff = cvGreater; version = name2.version; - } - else if (diff != cvGreater && d == 0) { + } else if (diff != cvGreater && d == 0) { diff = cvEqual; version = name2.version; - } - else if (diff != cvGreater && diff != cvEqual && d > 0) { + } else if (diff != cvGreater && diff != cvEqual && d > 0) { diff = cvLess; - if (version == "" || compareVersions(version, name2.version) < 0) + if (version == "" || + compareVersions(version, name2.version) < 0) version = name2.version; } } @@ -908,13 +910,14 @@ static VersionDiff compareVersionAgainstSet( return diff; } - -static void queryJSON(Globals & globals, std::vector & elems, bool printOutPath, bool printMeta) +static void queryJSON(Globals & globals, std::vector & elems, + bool printOutPath, bool printMeta) { JSONObject topObj(cout, true); for (auto & i : elems) { try { - if (i.hasFailed()) continue; + if (i.hasFailed()) + continue; JSONObject pkgObj = topObj.object(i.attrPath); @@ -930,7 +933,9 @@ static void queryJSON(Globals & globals, std::vector & elems, bool prin JSONObject outputObj = pkgObj.object("outputs"); for (auto & j : outputs) { if (j.second) - outputObj.attr(j.first, globals.state->store->printStorePath(*j.second)); + outputObj.attr( + j.first, + globals.state->store->printStorePath(*j.second)); else outputObj.attr(j.first, nullptr); } @@ -943,27 +948,34 @@ static void queryJSON(Globals & globals, std::vector & elems, bool prin auto placeholder = metaObj.placeholder(j); Value * v = i.queryMeta(j); if (!v) { - printError("derivation '%s' has invalid meta attribute '%s'", i.queryName(), j); + printError( + "derivation '%s' has invalid meta attribute '%s'", + i.queryName(), j); placeholder.write(nullptr); } else { PathSet context; - printValueAsJSON(*globals.state, true, *v, noPos, placeholder, context); + printValueAsJSON(*globals.state, true, *v, noPos, + placeholder, context); } } } } catch (AssertionError & e) { - printMsg(lvlTalkative, "skipping derivation named '%1%' which gives an assertion failure", i.queryName()); + printMsg(lvlTalkative, + "skipping derivation named '%1%' which gives an assertion " + "failure", + i.queryName()); } catch (Error & e) { - e.addTrace(std::nullopt, "while querying the derivation named '%1%'", i.queryName()); + e.addTrace(std::nullopt, + "while querying the derivation named '%1%'", + i.queryName()); throw; } } } - static void opQuery(Globals & globals, Strings opFlags, Strings opArgs) { - auto & store { *globals.state->store }; + auto & store{*globals.state->store}; Strings remaining; std::string attrPath; @@ -984,21 +996,34 @@ static void opQuery(Globals & globals, Strings opFlags, Strings opArgs) settings.readOnlyMode = true; /* makes evaluation a bit faster */ - for (Strings::iterator i = opFlags.begin(); i != opFlags.end(); ) { + for (Strings::iterator i = opFlags.begin(); i != opFlags.end();) { auto arg = *i++; - if (arg == "--status" || arg == "-s") printStatus = true; - else if (arg == "--no-name") printName = false; - else if (arg == "--system") printSystem = true; - else if (arg == "--description") printDescription = true; - else if (arg == "--compare-versions" || arg == "-c") compareVersions = true; - else if (arg == "--drv-path") printDrvPath = true; - else if (arg == "--out-path") printOutPath = true; - else if (arg == "--meta") printMeta = true; - else if (arg == "--installed") source = sInstalled; - else if (arg == "--available" || arg == "-a") source = sAvailable; - else if (arg == "--xml") xmlOutput = true; - else if (arg == "--json") jsonOutput = true; - else if (arg == "--attr-path" || arg == "-P") printAttrPath = true; + if (arg == "--status" || arg == "-s") + printStatus = true; + else if (arg == "--no-name") + printName = false; + else if (arg == "--system") + printSystem = true; + else if (arg == "--description") + printDescription = true; + else if (arg == "--compare-versions" || arg == "-c") + compareVersions = true; + else if (arg == "--drv-path") + printDrvPath = true; + else if (arg == "--out-path") + printOutPath = true; + else if (arg == "--meta") + printMeta = true; + else if (arg == "--installed") + source = sInstalled; + else if (arg == "--available" || arg == "-a") + source = sAvailable; + else if (arg == "--xml") + xmlOutput = true; + else if (arg == "--json") + jsonOutput = true; + else if (arg == "--attr-path" || arg == "-P") + printAttrPath = true; else if (arg == "--attr" || arg == "-A") attrPath = needArg(i, opFlags, arg); else @@ -1016,23 +1041,22 @@ static void opQuery(Globals & globals, Strings opFlags, Strings opArgs) if (source == sAvailable || compareVersions) loadDerivations(*globals.state, globals.instSource.nixExprPath, - globals.instSource.systemFilter, *globals.instSource.autoArgs, - attrPath, availElems); + globals.instSource.systemFilter, + *globals.instSource.autoArgs, attrPath, availElems); - DrvInfos elems_ = filterBySelector(*globals.state, - source == sInstalled ? installedElems : availElems, + DrvInfos elems_ = filterBySelector( + *globals.state, source == sInstalled ? installedElems : availElems, opArgs, false); DrvInfos & otherElems(source == sInstalled ? availElems : installedElems); - /* Sort them by name. */ /* !!! */ std::vector elems; - for (auto & i : elems_) elems.push_back(i); + for (auto & i : elems_) + elems.push_back(i); sort(elems.begin(), elems.end(), cmpElemByName); - /* We only need to know the installed paths when we are querying the status of the derivation. */ StorePathSet installed; /* installed paths */ @@ -1041,7 +1065,6 @@ static void opQuery(Globals & globals, Strings opFlags, Strings opArgs) for (auto & i : installedElems) installed.insert(i.queryOutPath()); - /* Query which paths have substitutes. */ StorePathSet validPaths; StorePathSet substitutablePaths; @@ -1051,14 +1074,16 @@ static void opQuery(Globals & globals, Strings opFlags, Strings opArgs) try { paths.insert(i.queryOutPath()); } catch (AssertionError & e) { - printMsg(lvlTalkative, "skipping derivation named '%s' which gives an assertion failure", i.queryName()); + printMsg(lvlTalkative, + "skipping derivation named '%s' which gives an " + "assertion failure", + i.queryName()); i.setFailed(); } validPaths = store.queryValidPaths(paths); substitutablePaths = store.querySubstitutablePaths(paths); } - /* Print the desired columns, or XML output. */ if (jsonOutput) { queryJSON(globals, elems, printOutPath, printMeta); @@ -1076,12 +1101,13 @@ static void opQuery(Globals & globals, Strings opFlags, Strings opArgs) for (auto & i : elems) { try { - if (i.hasFailed()) continue; + if (i.hasFailed()) + continue; - //Activity act(*logger, lvlDebug, format("outputting query result '%1%'") % i.attrPath); + // Activity act(*logger, lvlDebug, format("outputting query result + // '%1%'") % i.attrPath); - if (globals.prebuiltOnly && - !validPaths.count(i.queryOutPath()) && + if (globals.prebuiltOnly && !validPaths.count(i.queryOutPath()) && !substitutablePaths.count(i.queryOutPath())) continue; @@ -1101,10 +1127,9 @@ static void opQuery(Globals & globals, Strings opFlags, Strings opArgs) attrs["valid"] = isValid ? "1" : "0"; attrs["substitutable"] = hasSubs ? "1" : "0"; } else - columns.push_back( - (std::string) (isInstalled ? "I" : "-") - + (isValid ? "P" : "-") - + (hasSubs ? "S" : "-")); + columns.push_back((std::string)(isInstalled ? "I" : "-") + + (isValid ? "P" : "-") + + (hasSubs ? "S" : "-")); } if (xmlOutput) @@ -1127,15 +1152,25 @@ static void opQuery(Globals & globals, Strings opFlags, Strings opArgs) elements, or the set of installed elements. !!! This is O(N * M), should be O(N * lg M). */ std::string version; - VersionDiff diff = compareVersionAgainstSet(i, otherElems, version); + VersionDiff diff = + compareVersionAgainstSet(i, otherElems, version); char ch; switch (diff) { - case cvLess: ch = '>'; break; - case cvEqual: ch = '='; break; - case cvGreater: ch = '<'; break; - case cvUnavail: ch = '-'; break; - default: abort(); + case cvLess: + ch = '>'; + break; + case cvEqual: + ch = '='; + break; + case cvGreater: + ch = '<'; + break; + case cvUnavail: + ch = '-'; + break; + default: + abort(); } if (xmlOutput) { @@ -1152,17 +1187,19 @@ static void opQuery(Globals & globals, Strings opFlags, Strings opArgs) } if (xmlOutput) { - if (i.querySystem() != "") attrs["system"] = i.querySystem(); - } - else if (printSystem) + if (i.querySystem() != "") + attrs["system"] = i.querySystem(); + } else if (printSystem) columns.push_back(i.querySystem()); if (printDrvPath) { auto drvPath = i.queryDrvPath(); if (xmlOutput) { - if (drvPath) attrs["drvPath"] = store.printStorePath(*drvPath); + if (drvPath) + attrs["drvPath"] = store.printStorePath(*drvPath); } else - columns.push_back(drvPath ? store.printStorePath(*drvPath) : "-"); + columns.push_back(drvPath ? store.printStorePath(*drvPath) + : "-"); } if (xmlOutput) @@ -1172,8 +1209,12 @@ static void opQuery(Globals & globals, Strings opFlags, Strings opArgs) DrvInfo::Outputs outputs = i.queryOutputs(); std::string s; for (auto & j : outputs) { - if (!s.empty()) s += ';'; - if (j.first != "out") { s += j.first; s += "="; } + if (!s.empty()) + s += ';'; + if (j.first != "out") { + s += j.first; + s += "="; + } s += store.printStorePath(*j.second); } columns.push_back(s); @@ -1182,7 +1223,8 @@ static void opQuery(Globals & globals, Strings opFlags, Strings opArgs) if (printDescription) { auto descr = i.queryMetaString("description"); if (xmlOutput) { - if (descr != "") attrs["description"] = descr; + if (descr != "") + attrs["description"] = descr; } else columns.push_back(descr); } @@ -1204,9 +1246,9 @@ static void opQuery(Globals & globals, Strings opFlags, Strings opArgs) attrs2["name"] = j; Value * v = i.queryMeta(j); if (!v) - printError( - "derivation '%s' has invalid meta attribute '%s'", - i.queryName(), j); + printError("derivation '%s' has invalid meta " + "attribute '%s'", + i.queryName(), j); else { if (v->type() == nString) { attrs2["type"] = "string"; @@ -1214,11 +1256,13 @@ static void opQuery(Globals & globals, Strings opFlags, Strings opArgs) xml.writeEmptyElement("meta", attrs2); } else if (v->type() == nInt) { attrs2["type"] = "int"; - attrs2["value"] = (format("%1%") % v->integer).str(); + attrs2["value"] = + (format("%1%") % v->integer).str(); xml.writeEmptyElement("meta", attrs2); } else if (v->type() == nFloat) { attrs2["type"] = "float"; - attrs2["value"] = (format("%1%") % v->fpoint).str(); + attrs2["value"] = + (format("%1%") % v->fpoint).str(); xml.writeEmptyElement("meta", attrs2); } else if (v->type() == nBool) { attrs2["type"] = "bool"; @@ -1228,7 +1272,8 @@ static void opQuery(Globals & globals, Strings opFlags, Strings opArgs) attrs2["type"] = "strings"; XMLOpenElement m(xml, "meta", attrs2); for (auto elem : v->listItems()) { - if (elem->type() != nString) continue; + if (elem->type() != nString) + continue; XMLAttrs attrs3; attrs3["value"] = elem->string.s; xml.writeEmptyElement("string", attrs3); @@ -1237,14 +1282,16 @@ static void opQuery(Globals & globals, Strings opFlags, Strings opArgs) attrs2["type"] = "strings"; XMLOpenElement m(xml, "meta", attrs2); Bindings & attrs = *v->attrs; - for (auto &i : attrs) { + for (auto & i : attrs) { Attr & a(*attrs.find(i.name)); - if(a.value->type() != nString) continue; + if (a.value->type() != nString) + continue; XMLAttrs attrs3; - attrs3["type"] = globals.state->symbols[i.name]; + attrs3["type"] = + globals.state->symbols[i.name]; attrs3["value"] = a.value->string.s; xml.writeEmptyElement("string", attrs3); - } + } } } } @@ -1255,17 +1302,22 @@ static void opQuery(Globals & globals, Strings opFlags, Strings opArgs) cout.flush(); } catch (AssertionError & e) { - printMsg(lvlTalkative, "skipping derivation named '%1%' which gives an assertion failure", i.queryName()); + printMsg(lvlTalkative, + "skipping derivation named '%1%' which gives an assertion " + "failure", + i.queryName()); } catch (Error & e) { - e.addTrace(std::nullopt, "while querying the derivation named '%1%'", i.queryName()); + e.addTrace(std::nullopt, + "while querying the derivation named '%1%'", + i.queryName()); throw; } } - if (!xmlOutput) printTable(table); + if (!xmlOutput) + printTable(table); } - static void opSwitchProfile(Globals & globals, Strings opFlags, Strings opArgs) { if (opFlags.size() > 0) @@ -1279,8 +1331,8 @@ static void opSwitchProfile(Globals & globals, Strings opFlags, Strings opArgs) switchLink(profileLink, profile); } - -static void opSwitchGeneration(Globals & globals, Strings opFlags, Strings opArgs) +static void opSwitchGeneration(Globals & globals, Strings opFlags, + Strings opArgs) { if (opFlags.size() > 0) throw UsageError("unknown flag '%1%'", opFlags.front()); @@ -1293,7 +1345,6 @@ static void opSwitchGeneration(Globals & globals, Strings opFlags, Strings opArg throw UsageError("expected a generation number"); } - static void opRollback(Globals & globals, Strings opFlags, Strings opArgs) { if (opFlags.size() > 0) @@ -1304,8 +1355,8 @@ static void opRollback(Globals & globals, Strings opFlags, Strings opArgs) switchGeneration(globals.profile, {}, globals.dryRun); } - -static void opListGenerations(Globals & globals, Strings opFlags, Strings opArgs) +static void opListGenerations(Globals & globals, Strings opFlags, + Strings opArgs) { if (opFlags.size() > 0) throw UsageError("unknown flag '%1%'", opFlags.front()); @@ -1321,32 +1372,36 @@ static void opListGenerations(Globals & globals, Strings opFlags, Strings opArgs for (auto & i : gens) { tm t; - if (!localtime_r(&i.creationTime, &t)) throw Error("cannot convert time"); - cout << format("%|4| %|4|-%|02|-%|02| %|02|:%|02|:%|02| %||\n") - % i.number - % (t.tm_year + 1900) % (t.tm_mon + 1) % t.tm_mday - % t.tm_hour % t.tm_min % t.tm_sec - % (i.number == curGen ? "(current)" : ""); + if (!localtime_r(&i.creationTime, &t)) + throw Error("cannot convert time"); + cout << format("%|4| %|4|-%|02|-%|02| %|02|:%|02|:%|02| %||\n") % + i.number % (t.tm_year + 1900) % (t.tm_mon + 1) % t.tm_mday % + t.tm_hour % t.tm_min % t.tm_sec % + (i.number == curGen ? "(current)" : ""); } } - -static void opDeleteGenerations(Globals & globals, Strings opFlags, Strings opArgs) +static void opDeleteGenerations(Globals & globals, Strings opFlags, + Strings opArgs) { if (opFlags.size() > 0) throw UsageError("unknown flag '%1%'", opFlags.front()); if (opArgs.size() == 1 && opArgs.front() == "old") { deleteOldGenerations(globals.profile, globals.dryRun); - } else if (opArgs.size() == 1 && opArgs.front().find('d') != std::string::npos) { - deleteGenerationsOlderThan(globals.profile, opArgs.front(), globals.dryRun); - } else if (opArgs.size() == 1 && opArgs.front().find('+') != std::string::npos) { + } else if (opArgs.size() == 1 && + opArgs.front().find('d') != std::string::npos) { + deleteGenerationsOlderThan(globals.profile, opArgs.front(), + globals.dryRun); + } else if (opArgs.size() == 1 && + opArgs.front().find('+') != std::string::npos) { if (opArgs.front().size() < 2) throw Error("invalid number of generations '%1%'", opArgs.front()); auto str_max = opArgs.front().substr(1); auto max = string2Int(str_max); if (!max || *max == 0) - throw Error("invalid number of generations to keep '%1%'", opArgs.front()); + throw Error("invalid number of generations to keep '%1%'", + opArgs.front()); deleteGenerationsGreaterThan(globals.profile, *max, globals.dryRun); } else { std::set gens; @@ -1360,14 +1415,12 @@ static void opDeleteGenerations(Globals & globals, Strings opFlags, Strings opAr } } - static void opVersion(Globals & globals, Strings opFlags, Strings opArgs) { printVersion("nix-env"); } - -static int main_nix_env(int argc, char * * argv) +static int main_nix_env(int argc, char ** argv) { { Strings opFlags, opArgs; @@ -1384,14 +1437,16 @@ static int main_nix_env(int argc, char * * argv) if (!pathExists(globals.instSource.nixExprPath)) { try { createDirs(globals.instSource.nixExprPath); - replaceSymlink( - fmt("%s/profiles/per-user/%s/channels", settings.nixStateDir, getUserName()), - globals.instSource.nixExprPath + "/channels"); + replaceSymlink(fmt("%s/profiles/per-user/%s/channels", + settings.nixStateDir, getUserName()), + globals.instSource.nixExprPath + "/channels"); if (getuid() != 0) - replaceSymlink( - fmt("%s/profiles/per-user/root/channels", settings.nixStateDir), - globals.instSource.nixExprPath + "/channels_root"); - } catch (Error &) { } + replaceSymlink(fmt("%s/profiles/per-user/root/channels", + settings.nixStateDir), + globals.instSource.nixExprPath + + "/channels_root"); + } catch (Error &) { + } } globals.dryRun = false; @@ -1399,83 +1454,86 @@ static int main_nix_env(int argc, char * * argv) globals.removeAll = false; globals.prebuiltOnly = false; - struct MyArgs : LegacyArgs, MixEvalArgs - { + struct MyArgs : LegacyArgs, MixEvalArgs { using LegacyArgs::LegacyArgs; }; - MyArgs myArgs(std::string(baseNameOf(argv[0])), [&](Strings::iterator & arg, const Strings::iterator & end) { - Operation oldOp = op; - - if (*arg == "--help") - showManPage("nix-env"); - else if (*arg == "--version") - op = opVersion; - else if (*arg == "--install" || *arg == "-i") - op = opInstall; - else if (*arg == "--force-name") // undocumented flag for nix-install-package - globals.forceName = getArg(*arg, arg, end); - else if (*arg == "--uninstall" || *arg == "-e") - op = opUninstall; - else if (*arg == "--upgrade" || *arg == "-u") - op = opUpgrade; - else if (*arg == "--set-flag") - op = opSetFlag; - else if (*arg == "--set") - op = opSet; - else if (*arg == "--query" || *arg == "-q") - op = opQuery; - else if (*arg == "--profile" || *arg == "-p") - globals.profile = absPath(getArg(*arg, arg, end)); - else if (*arg == "--file" || *arg == "-f") - file = getArg(*arg, arg, end); - else if (*arg == "--switch-profile" || *arg == "-S") - op = opSwitchProfile; - else if (*arg == "--switch-generation" || *arg == "-G") - op = opSwitchGeneration; - else if (*arg == "--rollback") - op = opRollback; - else if (*arg == "--list-generations") - op = opListGenerations; - else if (*arg == "--delete-generations") - op = opDeleteGenerations; - else if (*arg == "--dry-run") { - printInfo("(dry run; not doing anything)"); - globals.dryRun = true; - } - else if (*arg == "--system-filter") - globals.instSource.systemFilter = getArg(*arg, arg, end); - else if (*arg == "--prebuilt-only" || *arg == "-b") - globals.prebuiltOnly = true; - else if (*arg == "--repair") - repair = Repair; - else if (*arg != "" && arg->at(0) == '-') { - opFlags.push_back(*arg); - /* FIXME: hacky */ - if (*arg == "--from-profile" || - (op == opQuery && (*arg == "--attr" || *arg == "-A"))) - opFlags.push_back(getArg(*arg, arg, end)); - } - else - opArgs.push_back(*arg); + MyArgs myArgs( + std::string(baseNameOf(argv[0])), + [&](Strings::iterator & arg, const Strings::iterator & end) { + Operation oldOp = op; + + if (*arg == "--help") + showManPage("nix-env"); + else if (*arg == "--version") + op = opVersion; + else if (*arg == "--install" || *arg == "-i") + op = opInstall; + else if (*arg == "--force-name") // undocumented flag for + // nix-install-package + globals.forceName = getArg(*arg, arg, end); + else if (*arg == "--uninstall" || *arg == "-e") + op = opUninstall; + else if (*arg == "--upgrade" || *arg == "-u") + op = opUpgrade; + else if (*arg == "--set-flag") + op = opSetFlag; + else if (*arg == "--set") + op = opSet; + else if (*arg == "--query" || *arg == "-q") + op = opQuery; + else if (*arg == "--profile" || *arg == "-p") + globals.profile = absPath(getArg(*arg, arg, end)); + else if (*arg == "--file" || *arg == "-f") + file = getArg(*arg, arg, end); + else if (*arg == "--switch-profile" || *arg == "-S") + op = opSwitchProfile; + else if (*arg == "--switch-generation" || *arg == "-G") + op = opSwitchGeneration; + else if (*arg == "--rollback") + op = opRollback; + else if (*arg == "--list-generations") + op = opListGenerations; + else if (*arg == "--delete-generations") + op = opDeleteGenerations; + else if (*arg == "--dry-run") { + printInfo("(dry run; not doing anything)"); + globals.dryRun = true; + } else if (*arg == "--system-filter") + globals.instSource.systemFilter = getArg(*arg, arg, end); + else if (*arg == "--prebuilt-only" || *arg == "-b") + globals.prebuiltOnly = true; + else if (*arg == "--repair") + repair = Repair; + else if (*arg != "" && arg->at(0) == '-') { + opFlags.push_back(*arg); + /* FIXME: hacky */ + if (*arg == "--from-profile" || + (op == opQuery && (*arg == "--attr" || *arg == "-A"))) + opFlags.push_back(getArg(*arg, arg, end)); + } else + opArgs.push_back(*arg); - if (oldOp && oldOp != op) - throw UsageError("only one operation may be specified"); + if (oldOp && oldOp != op) + throw UsageError("only one operation may be specified"); - return true; - }); + return true; + }); myArgs.parseCmdline(argvToStrings(argc, argv)); - if (!op) throw UsageError("no operation specified"); + if (!op) + throw UsageError("no operation specified"); auto store = openStore(); - globals.state = std::shared_ptr(new EvalState(myArgs.searchPath, store)); + globals.state = + std::shared_ptr(new EvalState(myArgs.searchPath, store)); globals.state->repair = repair; if (file != "") - globals.instSource.nixExprPath = lookupFileArg(*globals.state, file); + globals.instSource.nixExprPath = + lookupFileArg(*globals.state, file); globals.instSource.autoArgs = myArgs.getAutoArgs(*globals.state); diff --git a/src/nix-env/user-env.cc b/src/nix-env/user-env.cc index 4b1202be39d5..5b3c7a675064 100644 --- a/src/nix-env/user-env.cc +++ b/src/nix-env/user-env.cc @@ -10,15 +10,15 @@ #include "eval-inline.hh" #include "profiles.hh" - namespace nix { - DrvInfos queryInstalled(EvalState & state, const Path & userEnv) { DrvInfos elems; if (pathExists(userEnv + "/manifest.json")) - throw Error("profile '%s' is incompatible with 'nix-env'; please use 'nix profile' instead", userEnv); + throw Error("profile '%s' is incompatible with 'nix-env'; please use " + "'nix profile' instead", + userEnv); Path manifestFile = userEnv + "/manifest.nix"; if (pathExists(manifestFile)) { Value v; @@ -29,10 +29,8 @@ DrvInfos queryInstalled(EvalState & state, const Path & userEnv) return elems; } - -bool createUserEnv(EvalState & state, DrvInfos & elems, - const Path & profile, bool keepDerivations, - const std::string & lockToken) +bool createUserEnv(EvalState & state, DrvInfos & elems, const Path & profile, + bool keepDerivations, const std::string & lockToken) { /* Build the components in the user environment, if they don't exist already. */ @@ -42,9 +40,8 @@ bool createUserEnv(EvalState & state, DrvInfos & elems, drvsToBuild.push_back({*drvPath}); debug(format("building user environment dependencies")); - state.store->buildPaths( - toDerivedPaths(drvsToBuild), - state.repair ? bmRepair : bmNormal); + state.store->buildPaths(toDerivedPaths(drvsToBuild), + state.repair ? bmRepair : bmNormal); /* Construct the whole top level derivation. */ StorePathSet references; @@ -55,7 +52,8 @@ bool createUserEnv(EvalState & state, DrvInfos & elems, /* Create a pseudo-derivation containing the name, system, output paths, and optionally the derivation path, as well as the meta attributes. */ - std::optional drvPath = keepDerivations ? i.queryDrvPath() : std::nullopt; + std::optional drvPath = + keepDerivations ? i.queryDrvPath() : std::nullopt; DrvInfo::Outputs outputs = i.queryOutputs(true, true); StringSet metaNames = i.queryMetaNames(); @@ -66,9 +64,11 @@ bool createUserEnv(EvalState & state, DrvInfos & elems, auto system = i.querySystem(); if (!system.empty()) attrs.alloc(state.sSystem).mkString(system); - attrs.alloc(state.sOutPath).mkString(state.store->printStorePath(i.queryOutPath())); + attrs.alloc(state.sOutPath) + .mkString(state.store->printStorePath(i.queryOutPath())); if (drvPath) - attrs.alloc(state.sDrvPath).mkString(state.store->printStorePath(*drvPath)); + attrs.alloc(state.sDrvPath) + .mkString(state.store->printStorePath(*drvPath)); // Copy each output meant for installation. auto & vOutputs = attrs.alloc(state.sOutputs); @@ -76,7 +76,8 @@ bool createUserEnv(EvalState & state, DrvInfos & elems, for (const auto & [m, j] : enumerate(outputs)) { (vOutputs.listElems()[m] = state.allocValue())->mkString(j.first); auto outputAttrs = state.buildBindings(2); - outputAttrs.alloc(state.sOutPath).mkString(state.store->printStorePath(*j.second)); + outputAttrs.alloc(state.sOutPath) + .mkString(state.store->printStorePath(*j.second)); attrs.alloc(j.first).mkAttrs(outputAttrs); /* This is only necessary when installing store paths, e.g., @@ -91,7 +92,8 @@ bool createUserEnv(EvalState & state, DrvInfos & elems, auto meta = state.buildBindings(metaNames.size()); for (auto & j : metaNames) { Value * v = i.queryMeta(j); - if (!v) continue; + if (!v) + continue; meta.insert(state.symbols.create(j), v); } @@ -99,7 +101,8 @@ bool createUserEnv(EvalState & state, DrvInfos & elems, (manifest.listElems()[n++] = state.allocValue())->mkAttrs(attrs); - if (drvPath) references.insert(*drvPath); + if (drvPath) + references.insert(*drvPath); } /* Also write a copy of the list of user environment elements to @@ -107,21 +110,22 @@ bool createUserEnv(EvalState & state, DrvInfos & elems, environment. */ std::ostringstream str; manifest.print(state.symbols, str, true); - auto manifestFile = state.store->addTextToStore("env-manifest.nix", - str.str(), references); + auto manifestFile = + state.store->addTextToStore("env-manifest.nix", str.str(), references); /* Get the environment builder expression. */ Value envBuilder; state.eval(state.parseExprFromString( - #include "buildenv.nix.gen.hh" - , "/"), envBuilder); +#include "buildenv.nix.gen.hh" + , "/"), + envBuilder); /* Construct a Nix expression that calls the user environment builder with the manifest as argument. */ auto attrs = state.buildBindings(3); - attrs.alloc("manifest").mkString( - state.store->printStorePath(manifestFile), - {state.store->printStorePath(manifestFile)}); + attrs.alloc("manifest") + .mkString(state.store->printStorePath(manifestFile), + {state.store->printStorePath(manifestFile)}); attrs.insert(state.symbols.create("derivations"), &manifest); Value args; args.mkAttrs(attrs); @@ -134,17 +138,18 @@ bool createUserEnv(EvalState & state, DrvInfos & elems, state.forceValue(topLevel, [&]() { return topLevel.determinePos(noPos); }); PathSet context; Attr & aDrvPath(*topLevel.attrs->find(state.sDrvPath)); - auto topLevelDrv = state.coerceToStorePath(aDrvPath.pos, *aDrvPath.value, context); + auto topLevelDrv = + state.coerceToStorePath(aDrvPath.pos, *aDrvPath.value, context); Attr & aOutPath(*topLevel.attrs->find(state.sOutPath)); - auto topLevelOut = state.coerceToStorePath(aOutPath.pos, *aOutPath.value, context); + auto topLevelOut = + state.coerceToStorePath(aOutPath.pos, *aOutPath.value, context); /* Realise the resulting store expression. */ debug("building user environment"); std::vector topLevelDrvs; topLevelDrvs.push_back({topLevelDrv}); - state.store->buildPaths( - toDerivedPaths(topLevelDrvs), - state.repair ? bmRepair : bmNormal); + state.store->buildPaths(toDerivedPaths(topLevelDrvs), + state.repair ? bmRepair : bmNormal); /* Switch the current user environment to the output path. */ auto store2 = state.store.dynamic_pointer_cast(); @@ -155,17 +160,18 @@ bool createUserEnv(EvalState & state, DrvInfos & elems, Path lockTokenCur = optimisticLockProfile(profile); if (lockToken != lockTokenCur) { - printInfo("profile '%1%' changed while we were busy; restarting", profile); + printInfo("profile '%1%' changed while we were busy; restarting", + profile); return false; } debug(format("switching to new user environment")); - Path generation = createGeneration(ref(store2), profile, topLevelOut); + Path generation = + createGeneration(ref(store2), profile, topLevelOut); switchLink(profile, generation); } return true; } - } diff --git a/src/nix-env/user-env.hh b/src/nix-env/user-env.hh index 10646f713134..2972e5c4f723 100644 --- a/src/nix-env/user-env.hh +++ b/src/nix-env/user-env.hh @@ -6,8 +6,7 @@ namespace nix { DrvInfos queryInstalled(EvalState & state, const Path & userEnv); -bool createUserEnv(EvalState & state, DrvInfos & elems, - const Path & profile, bool keepDerivations, - const std::string & lockToken); +bool createUserEnv(EvalState & state, DrvInfos & elems, const Path & profile, + bool keepDerivations, const std::string & lockToken); } diff --git a/src/nix-instantiate/nix-instantiate.cc b/src/nix-instantiate/nix-instantiate.cc index d3144e131a23..212d7ad28545 100644 --- a/src/nix-instantiate/nix-instantiate.cc +++ b/src/nix-instantiate/nix-instantiate.cc @@ -15,20 +15,16 @@ #include #include - using namespace nix; - static Path gcRoot; static int rootNr = 0; - enum OutputKind { okPlain, okXML, okJSON }; - -void processExpr(EvalState & state, const Strings & attrPaths, - bool parseOnly, bool strict, Bindings & autoArgs, - bool evalOnly, OutputKind output, bool location, Expr * e) +void processExpr(EvalState & state, const Strings & attrPaths, bool parseOnly, + bool strict, Bindings & autoArgs, bool evalOnly, + OutputKind output, bool location, Expr * e) { if (parseOnly) { e->show(state.symbols, std::cout); @@ -51,11 +47,14 @@ void processExpr(EvalState & state, const Strings & attrPaths, else state.autoCallFunction(autoArgs, v, vRes); if (output == okXML) - printValueAsXML(state, strict, location, vRes, std::cout, context, noPos); + printValueAsXML(state, strict, location, vRes, std::cout, + context, noPos); else if (output == okJSON) - printValueAsJSON(state, strict, vRes, v.determinePos(noPos), std::cout, context); + printValueAsJSON(state, strict, vRes, v.determinePos(noPos), + std::cout, context); else { - if (strict) state.forceValueDeep(vRes); + if (strict) + state.forceValueDeep(vRes); vRes.print(state.symbols, std::cout); std::cout << std::endl; } @@ -69,25 +68,29 @@ void processExpr(EvalState & state, const Strings & attrPaths, /* What output do we want? */ std::string outputName = i.queryOutputName(); if (outputName == "") - throw Error("derivation '%1%' lacks an 'outputName' attribute", drvPathS); + throw Error( + "derivation '%1%' lacks an 'outputName' attribute", + drvPathS); if (gcRoot == "") printGCWarning(); else { Path rootName = absPath(gcRoot); - if (++rootNr > 1) rootName += "-" + std::to_string(rootNr); - auto store2 = state.store.dynamic_pointer_cast(); + if (++rootNr > 1) + rootName += "-" + std::to_string(rootNr); + auto store2 = + state.store.dynamic_pointer_cast(); if (store2) drvPathS = store2->addPermRoot(drvPath, rootName); } - std::cout << fmt("%s%s\n", drvPathS, (outputName != "out" ? "!" + outputName : "")); + std::cout << fmt("%s%s\n", drvPathS, + (outputName != "out" ? "!" + outputName : "")); } } } } - -static int main_nix_instantiate(int argc, char * * argv) +static int main_nix_instantiate(int argc, char ** argv) { { Strings files; @@ -103,52 +106,53 @@ static int main_nix_instantiate(int argc, char * * argv) bool wantsReadWrite = false; RepairFlag repair = NoRepair; - struct MyArgs : LegacyArgs, MixEvalArgs - { + struct MyArgs : LegacyArgs, MixEvalArgs { using LegacyArgs::LegacyArgs; }; - MyArgs myArgs(std::string(baseNameOf(argv[0])), [&](Strings::iterator & arg, const Strings::iterator & end) { - if (*arg == "--help") - showManPage("nix-instantiate"); - else if (*arg == "--version") - printVersion("nix-instantiate"); - else if (*arg == "-") - readStdin = true; - else if (*arg == "--expr" || *arg == "-E") - fromArgs = true; - else if (*arg == "--eval" || *arg == "--eval-only") - evalOnly = true; - else if (*arg == "--read-write-mode") - wantsReadWrite = true; - else if (*arg == "--parse" || *arg == "--parse-only") - parseOnly = evalOnly = true; - else if (*arg == "--find-file") - findFile = true; - else if (*arg == "--attr" || *arg == "-A") - attrPaths.push_back(getArg(*arg, arg, end)); - else if (*arg == "--add-root") - gcRoot = getArg(*arg, arg, end); - else if (*arg == "--indirect") - ; - else if (*arg == "--xml") - outputKind = okXML; - else if (*arg == "--json") - outputKind = okJSON; - else if (*arg == "--no-location") - xmlOutputSourceLocation = false; - else if (*arg == "--strict") - strict = true; - else if (*arg == "--repair") - repair = Repair; - else if (*arg == "--dry-run") - settings.readOnlyMode = true; - else if (*arg != "" && arg->at(0) == '-') - return false; - else - files.push_back(*arg); - return true; - }); + MyArgs myArgs( + std::string(baseNameOf(argv[0])), + [&](Strings::iterator & arg, const Strings::iterator & end) { + if (*arg == "--help") + showManPage("nix-instantiate"); + else if (*arg == "--version") + printVersion("nix-instantiate"); + else if (*arg == "-") + readStdin = true; + else if (*arg == "--expr" || *arg == "-E") + fromArgs = true; + else if (*arg == "--eval" || *arg == "--eval-only") + evalOnly = true; + else if (*arg == "--read-write-mode") + wantsReadWrite = true; + else if (*arg == "--parse" || *arg == "--parse-only") + parseOnly = evalOnly = true; + else if (*arg == "--find-file") + findFile = true; + else if (*arg == "--attr" || *arg == "-A") + attrPaths.push_back(getArg(*arg, arg, end)); + else if (*arg == "--add-root") + gcRoot = getArg(*arg, arg, end); + else if (*arg == "--indirect") + ; + else if (*arg == "--xml") + outputKind = okXML; + else if (*arg == "--json") + outputKind = okJSON; + else if (*arg == "--no-location") + xmlOutputSourceLocation = false; + else if (*arg == "--strict") + strict = true; + else if (*arg == "--repair") + repair = Repair; + else if (*arg == "--dry-run") + settings.readOnlyMode = true; + else if (*arg != "" && arg->at(0) == '-') + return false; + else + files.push_back(*arg); + return true; + }); myArgs.parseCmdline(argvToStrings(argc, argv)); @@ -156,19 +160,23 @@ static int main_nix_instantiate(int argc, char * * argv) settings.readOnlyMode = true; auto store = openStore(); - auto evalStore = myArgs.evalStoreUrl ? openStore(*myArgs.evalStoreUrl) : store; + auto evalStore = + myArgs.evalStoreUrl ? openStore(*myArgs.evalStoreUrl) : store; - auto state = std::make_unique(myArgs.searchPath, evalStore, store); + auto state = + std::make_unique(myArgs.searchPath, evalStore, store); state->repair = repair; Bindings & autoArgs = *myArgs.getAutoArgs(*state); - if (attrPaths.empty()) attrPaths = {""}; + if (attrPaths.empty()) + attrPaths = {""}; if (findFile) { for (auto & i : files) { Path p = state->findFile(i); - if (p == "") throw Error("unable to find '%1%'", i); + if (p == "") + throw Error("unable to find '%1%'", i); std::cout << p << std::endl; } return 0; @@ -177,16 +185,18 @@ static int main_nix_instantiate(int argc, char * * argv) if (readStdin) { Expr * e = state->parseStdin(); processExpr(*state, attrPaths, parseOnly, strict, autoArgs, - evalOnly, outputKind, xmlOutputSourceLocation, e); + evalOnly, outputKind, xmlOutputSourceLocation, e); } else if (files.empty() && !fromArgs) files.push_back("./default.nix"); for (auto & i : files) { - Expr * e = fromArgs - ? state->parseExprFromString(i, absPath(".")) - : state->parseExprFromFile(resolveExprPath(state->checkSourcePath(lookupFileArg(*state, i)))); + Expr * e = + fromArgs + ? state->parseExprFromString(i, absPath(".")) + : state->parseExprFromFile(resolveExprPath( + state->checkSourcePath(lookupFileArg(*state, i)))); processExpr(*state, attrPaths, parseOnly, strict, autoArgs, - evalOnly, outputKind, xmlOutputSourceLocation, e); + evalOnly, outputKind, xmlOutputSourceLocation, e); } state->printStats(); @@ -195,4 +205,5 @@ static int main_nix_instantiate(int argc, char * * argv) } } -static RegisterLegacyCommand r_nix_instantiate("nix-instantiate", main_nix_instantiate); +static RegisterLegacyCommand r_nix_instantiate("nix-instantiate", + main_nix_instantiate); diff --git a/src/nix-store/dotgraph.cc b/src/nix-store/dotgraph.cc index 577cadceb313..e55233f0400a 100644 --- a/src/nix-store/dotgraph.cc +++ b/src/nix-store/dotgraph.cc @@ -4,44 +4,37 @@ #include - using std::cout; namespace nix { - static std::string dotQuote(std::string_view s) { return "\"" + std::string(s) + "\""; } - static const std::string & nextColour() { static int n = 0; - static std::vector colours - { "black", "red", "green", "blue" - , "magenta", "burlywood" }; + static std::vector colours{"black", "red", "green", + "blue", "magenta", "burlywood"}; return colours[n++ % colours.size()]; } - static std::string makeEdge(std::string_view src, std::string_view dst) { - return fmt("%1% -> %2% [color = %3%];\n", - dotQuote(src), dotQuote(dst), dotQuote(nextColour())); + return fmt("%1% -> %2% [color = %3%];\n", dotQuote(src), dotQuote(dst), + dotQuote(nextColour())); } - static std::string makeNode(std::string_view id, std::string_view label, - std::string_view colour) + std::string_view colour) { return fmt("%1% [label = %2%, shape = box, " - "style = filled, fillcolor = %3%];\n", - dotQuote(id), dotQuote(label), dotQuote(colour)); + "style = filled, fillcolor = %3%];\n", + dotQuote(id), dotQuote(label), dotQuote(colour)); } - void printDotGraph(ref store, StorePathSet && roots) { StorePathSet workList(std::move(roots)); @@ -52,14 +45,16 @@ void printDotGraph(ref store, StorePathSet && roots) while (!workList.empty()) { auto path = std::move(workList.extract(workList.begin()).value()); - if (!doneSet.insert(path).second) continue; + if (!doneSet.insert(path).second) + continue; cout << makeNode(std::string(path.to_string()), path.name(), "#ff0000"); for (auto & p : store->queryPathInfo(path)->references) { if (p != path) { workList.insert(p); - cout << makeEdge(std::string(p.to_string()), std::string(path.to_string())); + cout << makeEdge(std::string(p.to_string()), + std::string(path.to_string())); } } } @@ -67,5 +62,4 @@ void printDotGraph(ref store, StorePathSet && roots) cout << "}\n"; } - } diff --git a/src/nix-store/graphml.cc b/src/nix-store/graphml.cc index 425d61e53eda..3a1dc67486c0 100644 --- a/src/nix-store/graphml.cc +++ b/src/nix-store/graphml.cc @@ -5,12 +5,10 @@ #include - using std::cout; namespace nix { - static inline std::string_view xmlQuote(std::string_view s) { // Luckily, store paths shouldn't contain any character that needs to be @@ -18,35 +16,29 @@ static inline std::string_view xmlQuote(std::string_view s) return s; } - static std::string symbolicName(std::string_view p) { return std::string(p.substr(0, p.find('-') + 1)); } - static std::string makeEdge(std::string_view src, std::string_view dst) { - return fmt(" \n", - xmlQuote(src), xmlQuote(dst)); + return fmt(" \n", xmlQuote(src), + xmlQuote(dst)); } - static std::string makeNode(const ValidPathInfo & info) { - return fmt( - " \n" - " %2%\n" - " %3%\n" - " %4%\n" - " \n", - info.path.to_string(), - info.narSize, - symbolicName(std::string(info.path.name())), - (info.path.isDerivation() ? "derivation" : "output-path")); + return fmt(" \n" + " %2%\n" + " %3%\n" + " %4%\n" + " \n", + info.path.to_string(), info.narSize, + symbolicName(std::string(info.path.name())), + (info.path.isDerivation() ? "derivation" : "output-path")); } - void printGraphML(ref store, StorePathSet && roots) { StorePathSet workList(std::move(roots)); @@ -56,7 +48,9 @@ void printGraphML(ref store, StorePathSet && roots) cout << "\n" << "\n" + << " " + "xsi:schemaLocation='http://graphml.graphdrawing.org/xmlns/1.0/" + "graphml.xsd'>\n" << "" << "" << "" @@ -66,7 +60,8 @@ void printGraphML(ref store, StorePathSet && roots) auto path = std::move(workList.extract(workList.begin()).value()); ret = doneSet.insert(path); - if (ret.second == false) continue; + if (ret.second == false) + continue; auto info = store->queryPathInfo(path); cout << makeNode(*info); @@ -77,12 +72,10 @@ void printGraphML(ref store, StorePathSet && roots) cout << makeEdge(path.to_string(), p.to_string()); } } - } cout << "\n"; cout << "\n"; } - } diff --git a/src/nix-store/nix-store.cc b/src/nix-store/nix-store.cc index b453ea1ca30d..777c7e103e7a 100644 --- a/src/nix-store/nix-store.cc +++ b/src/nix-store/nix-store.cc @@ -24,42 +24,38 @@ #include #include - namespace nix_store { - using namespace nix; using std::cin; using std::cout; - -typedef void (* Operation) (Strings opFlags, Strings opArgs); - +typedef void (*Operation)(Strings opFlags, Strings opArgs); static Path gcRoot; static int rootNr = 0; static bool noOutput = false; static std::shared_ptr store; - ref ensureLocalStore() { auto store2 = std::dynamic_pointer_cast(store); - if (!store2) throw Error("you don't have sufficient rights to use this command"); + if (!store2) + throw Error("you don't have sufficient rights to use this command"); return ref(store2); } - static StorePath useDeriver(const StorePath & path) { - if (path.isDerivation()) return path; + if (path.isDerivation()) + return path; auto info = store->queryPathInfo(path); if (!info->deriver) - throw Error("deriver of path '%s' is not known", store->printStorePath(path)); + throw Error("deriver of path '%s' is not known", + store->printStorePath(path)); return *info->deriver; } - /* Realise the given path. For a derivation that means build it; for other paths it means ensure their validity. */ static PathSet realisePath(StorePathWithOutputs path, bool build = true) @@ -67,19 +63,22 @@ static PathSet realisePath(StorePathWithOutputs path, bool build = true) auto store2 = std::dynamic_pointer_cast(store); if (path.path.isDerivation()) { - if (build) store->buildPaths({path.toDerivedPath()}); + if (build) + store->buildPaths({path.toDerivedPath()}); auto outputPaths = store->queryDerivationOutputMap(path.path); Derivation drv = store->derivationFromPath(path.path); rootNr++; if (path.outputs.empty()) - for (auto & i : drv.outputs) path.outputs.insert(i.first); + for (auto & i : drv.outputs) + path.outputs.insert(i.first); PathSet outputs; for (auto & j : path.outputs) { DerivationOutputs::iterator i = drv.outputs.find(j); if (i == drv.outputs.end()) - throw Error("derivation '%s' does not have an output named '%s'", + throw Error( + "derivation '%s' does not have an output named '%s'", store2->printStorePath(path.path), j); auto outPath = outputPaths.at(i->first); auto retPath = store->printStorePath(outPath); @@ -88,8 +87,10 @@ static PathSet realisePath(StorePathWithOutputs path, bool build = true) printGCWarning(); else { Path rootName = gcRoot; - if (rootNr > 1) rootName += "-" + std::to_string(rootNr); - if (i->first != "out") rootName += "-" + i->first; + if (rootNr > 1) + rootName += "-" + std::to_string(rootNr); + if (i->first != "out") + rootName += "-" + i->first; retPath = store2->addPermRoot(outPath, rootName); } } @@ -99,16 +100,19 @@ static PathSet realisePath(StorePathWithOutputs path, bool build = true) } else { - if (build) store->ensurePath(path.path); + if (build) + store->ensurePath(path.path); else if (!store->isValidPath(path.path)) - throw Error("path '%s' does not exist and cannot be created", store->printStorePath(path.path)); + throw Error("path '%s' does not exist and cannot be created", + store->printStorePath(path.path)); if (store2) { if (gcRoot == "") printGCWarning(); else { Path rootName = gcRoot; rootNr++; - if (rootNr > 1) rootName += "-" + std::to_string(rootNr); + if (rootNr > 1) + rootName += "-" + std::to_string(rootNr); return {store2->addPermRoot(path.path, rootName)}; } } @@ -116,7 +120,6 @@ static PathSet realisePath(StorePathWithOutputs path, bool build = true) } } - /* Realise the given paths. */ static void opRealise(Strings opFlags, Strings opArgs) { @@ -125,11 +128,16 @@ static void opRealise(Strings opFlags, Strings opArgs) bool ignoreUnknown = false; for (auto & i : opFlags) - if (i == "--dry-run") dryRun = true; - else if (i == "--repair") buildMode = bmRepair; - else if (i == "--check") buildMode = bmCheck; - else if (i == "--ignore-unknown") ignoreUnknown = true; - else throw UsageError("unknown flag '%1%'", i); + if (i == "--dry-run") + dryRun = true; + else if (i == "--repair") + buildMode = bmRepair; + else if (i == "--check") + buildMode = bmCheck; + else if (i == "--ignore-unknown") + ignoreUnknown = true; + else + throw UsageError("unknown flag '%1%'", i); std::vector paths; for (auto & i : opArgs) @@ -137,22 +145,24 @@ static void opRealise(Strings opFlags, Strings opArgs) uint64_t downloadSize, narSize; StorePathSet willBuild, willSubstitute, unknown; - store->queryMissing( - toDerivedPaths(paths), - willBuild, willSubstitute, unknown, downloadSize, narSize); + store->queryMissing(toDerivedPaths(paths), willBuild, willSubstitute, + unknown, downloadSize, narSize); if (ignoreUnknown) { std::vector paths2; for (auto & i : paths) - if (!unknown.count(i.path)) paths2.push_back(i); + if (!unknown.count(i.path)) + paths2.push_back(i); paths = std::move(paths2); unknown = StorePathSet(); } if (settings.printMissing) - printMissing(ref(store), willBuild, willSubstitute, unknown, downloadSize, narSize); + printMissing(ref(store), willBuild, willSubstitute, unknown, + downloadSize, narSize); - if (dryRun) return; + if (dryRun) + return; /* Build all paths at the same time to exploit parallelism. */ store->buildPaths(toDerivedPaths(paths), buildMode); @@ -166,17 +176,17 @@ static void opRealise(Strings opFlags, Strings opArgs) } } - /* Add files to the Nix store and print the resulting paths. */ static void opAdd(Strings opFlags, Strings opArgs) { - if (!opFlags.empty()) throw UsageError("unknown flag"); + if (!opFlags.empty()) + throw UsageError("unknown flag"); for (auto & i : opArgs) - cout << fmt("%s\n", store->printStorePath(store->addToStore(std::string(baseNameOf(i)), i))); + cout << fmt("%s\n", store->printStorePath(store->addToStore( + std::string(baseNameOf(i)), i))); } - /* Preload the output of a fixed-output derivation into the Nix store. */ static void opAddFixed(Strings opFlags, Strings opArgs) @@ -184,8 +194,10 @@ static void opAddFixed(Strings opFlags, Strings opArgs) auto method = FileIngestionMethod::Flat; for (auto & i : opFlags) - if (i == "--recursive") method = FileIngestionMethod::Recursive; - else throw UsageError("unknown flag '%1%'", i); + if (i == "--recursive") + method = FileIngestionMethod::Recursive; + else + throw UsageError("unknown flag '%1%'", i); if (opArgs.empty()) throw UsageError("first argument must be hash algorithm"); @@ -194,18 +206,23 @@ static void opAddFixed(Strings opFlags, Strings opArgs) opArgs.pop_front(); for (auto & i : opArgs) - std::cout << fmt("%s\n", store->printStorePath(store->addToStoreSlow(baseNameOf(i), i, method, hashAlgo).path)); + std::cout << fmt( + "%s\n", + store->printStorePath( + store->addToStoreSlow(baseNameOf(i), i, method, hashAlgo) + .path)); } - /* Hack to support caching in `nix-prefetch-url'. */ static void opPrintFixedPath(Strings opFlags, Strings opArgs) { auto recursive = FileIngestionMethod::Flat; for (auto i : opFlags) - if (i == "--recursive") recursive = FileIngestionMethod::Recursive; - else throw UsageError("unknown flag '%1%'", i); + if (i == "--recursive") + recursive = FileIngestionMethod::Recursive; + else + throw UsageError("unknown flag '%1%'", i); if (opArgs.size() != 3) throw UsageError("'--print-fixed-path' requires three arguments"); @@ -215,13 +232,15 @@ static void opPrintFixedPath(Strings opFlags, Strings opArgs) std::string hash = *i++; std::string name = *i++; - cout << fmt("%s\n", store->printStorePath(store->makeFixedOutputPath(recursive, Hash::parseAny(hash, hashAlgo), name))); + cout << fmt("%s\n", store->printStorePath(store->makeFixedOutputPath( + recursive, Hash::parseAny(hash, hashAlgo), name))); } - -static StorePathSet maybeUseOutputs(const StorePath & storePath, bool useOutput, bool forceRealise) +static StorePathSet maybeUseOutputs(const StorePath & storePath, bool useOutput, + bool forceRealise) { - if (forceRealise) realisePath({storePath}); + if (forceRealise) + realisePath({storePath}); if (useOutput && storePath.isDerivation()) { auto drv = store->derivationFromPath(storePath); StorePathSet outputs; @@ -229,20 +248,21 @@ static StorePathSet maybeUseOutputs(const StorePath & storePath, bool useOutput, return store->queryDerivationOutputs(storePath); for (auto & i : drv.outputsAndOptPaths(*store)) { if (!i.second.second) - throw UsageError("Cannot use output path of floating content-addressed derivation until we know what it is (e.g. by building it)"); + throw UsageError("Cannot use output path of floating " + "content-addressed derivation until we know " + "what it is (e.g. by building it)"); outputs.insert(*i.second.second); } return outputs; - } - else return {storePath}; + } else + return {storePath}; } - /* Some code to print a tree representation of a derivation dependency graph. Topological sorting is used to keep the tree relatively flat. */ -static void printTree(const StorePath & path, - const std::string & firstPad, const std::string & tailPad, StorePathSet & done) +static void printTree(const StorePath & path, const std::string & firstPad, + const std::string & tailPad, StorePathSet & done) { if (!done.insert(path).second) { cout << fmt("%s%s [...]\n", firstPad, store->printStorePath(path)); @@ -260,23 +280,33 @@ static void printTree(const StorePath & path, auto sorted = store->topoSortPaths(info->references); reverse(sorted.begin(), sorted.end()); - for (const auto &[n, i] : enumerate(sorted)) { + for (const auto & [n, i] : enumerate(sorted)) { bool last = n + 1 == sorted.size(); - printTree(i, - tailPad + (last ? treeLast : treeConn), - tailPad + (last ? treeNull : treeLine), - done); + printTree(i, tailPad + (last ? treeLast : treeConn), + tailPad + (last ? treeNull : treeLine), done); } } - /* Perform various sorts of queries. */ static void opQuery(Strings opFlags, Strings opArgs) { - enum QueryType - { qDefault, qOutputs, qRequisites, qReferences, qReferrers - , qReferrersClosure, qDeriver, qBinding, qHash, qSize - , qTree, qGraph, qGraphML, qResolve, qRoots }; + enum QueryType { + qDefault, + qOutputs, + qRequisites, + qReferences, + qReferrers, + qReferrersClosure, + qDeriver, + qBinding, + qHash, + qSize, + qTree, + qGraph, + qGraphML, + qResolve, + qRoots + }; QueryType query = qDefault; bool useOutput = false; bool includeOutputs = false; @@ -285,171 +315,200 @@ static void opQuery(Strings opFlags, Strings opArgs) for (auto & i : opFlags) { QueryType prev = query; - if (i == "--outputs") query = qOutputs; - else if (i == "--requisites" || i == "-R") query = qRequisites; - else if (i == "--references") query = qReferences; - else if (i == "--referrers" || i == "--referers") query = qReferrers; - else if (i == "--referrers-closure" || i == "--referers-closure") query = qReferrersClosure; - else if (i == "--deriver" || i == "-d") query = qDeriver; + if (i == "--outputs") + query = qOutputs; + else if (i == "--requisites" || i == "-R") + query = qRequisites; + else if (i == "--references") + query = qReferences; + else if (i == "--referrers" || i == "--referers") + query = qReferrers; + else if (i == "--referrers-closure" || i == "--referers-closure") + query = qReferrersClosure; + else if (i == "--deriver" || i == "-d") + query = qDeriver; else if (i == "--binding" || i == "-b") { if (opArgs.size() == 0) throw UsageError("expected binding name"); bindingName = opArgs.front(); opArgs.pop_front(); query = qBinding; - } - else if (i == "--hash") query = qHash; - else if (i == "--size") query = qSize; - else if (i == "--tree") query = qTree; - else if (i == "--graph") query = qGraph; - else if (i == "--graphml") query = qGraphML; - else if (i == "--resolve") query = qResolve; - else if (i == "--roots") query = qRoots; - else if (i == "--use-output" || i == "-u") useOutput = true; - else if (i == "--force-realise" || i == "--force-realize" || i == "-f") forceRealise = true; - else if (i == "--include-outputs") includeOutputs = true; - else throw UsageError("unknown flag '%1%'", i); + } else if (i == "--hash") + query = qHash; + else if (i == "--size") + query = qSize; + else if (i == "--tree") + query = qTree; + else if (i == "--graph") + query = qGraph; + else if (i == "--graphml") + query = qGraphML; + else if (i == "--resolve") + query = qResolve; + else if (i == "--roots") + query = qRoots; + else if (i == "--use-output" || i == "-u") + useOutput = true; + else if (i == "--force-realise" || i == "--force-realize" || i == "-f") + forceRealise = true; + else if (i == "--include-outputs") + includeOutputs = true; + else + throw UsageError("unknown flag '%1%'", i); if (prev != qDefault && prev != query) throw UsageError("query type '%1%' conflicts with earlier flag", i); } - if (query == qDefault) query = qOutputs; + if (query == qDefault) + query = qOutputs; RunPager pager; switch (query) { - case qOutputs: { - for (auto & i : opArgs) { - auto outputs = maybeUseOutputs(store->followLinksToStorePath(i), true, forceRealise); - for (auto & outputPath : outputs) - cout << fmt("%1%\n", store->printStorePath(outputPath)); - } - break; + case qOutputs: { + for (auto & i : opArgs) { + auto outputs = maybeUseOutputs(store->followLinksToStorePath(i), + true, forceRealise); + for (auto & outputPath : outputs) + cout << fmt("%1%\n", store->printStorePath(outputPath)); } + break; + } - case qRequisites: - case qReferences: - case qReferrers: - case qReferrersClosure: { - StorePathSet paths; - for (auto & i : opArgs) { - auto ps = maybeUseOutputs(store->followLinksToStorePath(i), useOutput, forceRealise); - for (auto & j : ps) { - if (query == qRequisites) store->computeFSClosure(j, paths, false, includeOutputs); - else if (query == qReferences) { - for (auto & p : store->queryPathInfo(j)->references) - paths.insert(p); - } - else if (query == qReferrers) { - StorePathSet tmp; - store->queryReferrers(j, tmp); - for (auto & i : tmp) - paths.insert(i); - } - else if (query == qReferrersClosure) store->computeFSClosure(j, paths, true); - } + case qRequisites: + case qReferences: + case qReferrers: + case qReferrersClosure: { + StorePathSet paths; + for (auto & i : opArgs) { + auto ps = maybeUseOutputs(store->followLinksToStorePath(i), + useOutput, forceRealise); + for (auto & j : ps) { + if (query == qRequisites) + store->computeFSClosure(j, paths, false, includeOutputs); + else if (query == qReferences) { + for (auto & p : store->queryPathInfo(j)->references) + paths.insert(p); + } else if (query == qReferrers) { + StorePathSet tmp; + store->queryReferrers(j, tmp); + for (auto & i : tmp) + paths.insert(i); + } else if (query == qReferrersClosure) + store->computeFSClosure(j, paths, true); } - auto sorted = store->topoSortPaths(paths); - for (StorePaths::reverse_iterator i = sorted.rbegin(); - i != sorted.rend(); ++i) - cout << fmt("%s\n", store->printStorePath(*i)); - break; } + auto sorted = store->topoSortPaths(paths); + for (StorePaths::reverse_iterator i = sorted.rbegin(); + i != sorted.rend(); ++i) + cout << fmt("%s\n", store->printStorePath(*i)); + break; + } - case qDeriver: - for (auto & i : opArgs) { - auto info = store->queryPathInfo(store->followLinksToStorePath(i)); - cout << fmt("%s\n", info->deriver ? store->printStorePath(*info->deriver) : "unknown-deriver"); - } - break; - - case qBinding: - for (auto & i : opArgs) { - auto path = useDeriver(store->followLinksToStorePath(i)); - Derivation drv = store->derivationFromPath(path); - StringPairs::iterator j = drv.env.find(bindingName); - if (j == drv.env.end()) - throw Error("derivation '%s' has no environment binding named '%s'", - store->printStorePath(path), bindingName); - cout << fmt("%s\n", j->second); + case qDeriver: + for (auto & i : opArgs) { + auto info = store->queryPathInfo(store->followLinksToStorePath(i)); + cout << fmt("%s\n", info->deriver + ? store->printStorePath(*info->deriver) + : "unknown-deriver"); + } + break; + + case qBinding: + for (auto & i : opArgs) { + auto path = useDeriver(store->followLinksToStorePath(i)); + Derivation drv = store->derivationFromPath(path); + StringPairs::iterator j = drv.env.find(bindingName); + if (j == drv.env.end()) + throw Error( + "derivation '%s' has no environment binding named '%s'", + store->printStorePath(path), bindingName); + cout << fmt("%s\n", j->second); + } + break; + + case qHash: + case qSize: + for (auto & i : opArgs) { + for (auto & j : maybeUseOutputs(store->followLinksToStorePath(i), + useOutput, forceRealise)) { + auto info = store->queryPathInfo(j); + if (query == qHash) { + assert(info->narHash.type == htSHA256); + cout << fmt("%s\n", info->narHash.to_string(Base32, true)); + } else if (query == qSize) + cout << fmt("%d\n", info->narSize); } - break; + } + break; - case qHash: - case qSize: - for (auto & i : opArgs) { - for (auto & j : maybeUseOutputs(store->followLinksToStorePath(i), useOutput, forceRealise)) { - auto info = store->queryPathInfo(j); - if (query == qHash) { - assert(info->narHash.type == htSHA256); - cout << fmt("%s\n", info->narHash.to_string(Base32, true)); - } else if (query == qSize) - cout << fmt("%d\n", info->narSize); - } - } - break; + case qTree: { + StorePathSet done; + for (auto & i : opArgs) + printTree(store->followLinksToStorePath(i), "", "", done); + break; + } - case qTree: { - StorePathSet done; - for (auto & i : opArgs) - printTree(store->followLinksToStorePath(i), "", "", done); - break; - } + case qGraph: { + StorePathSet roots; + for (auto & i : opArgs) + for (auto & j : maybeUseOutputs(store->followLinksToStorePath(i), + useOutput, forceRealise)) + roots.insert(j); + printDotGraph(ref(store), std::move(roots)); + break; + } - case qGraph: { - StorePathSet roots; - for (auto & i : opArgs) - for (auto & j : maybeUseOutputs(store->followLinksToStorePath(i), useOutput, forceRealise)) - roots.insert(j); - printDotGraph(ref(store), std::move(roots)); - break; - } + case qGraphML: { + StorePathSet roots; + for (auto & i : opArgs) + for (auto & j : maybeUseOutputs(store->followLinksToStorePath(i), + useOutput, forceRealise)) + roots.insert(j); + printGraphML(ref(store), std::move(roots)); + break; + } - case qGraphML: { - StorePathSet roots; - for (auto & i : opArgs) - for (auto & j : maybeUseOutputs(store->followLinksToStorePath(i), useOutput, forceRealise)) - roots.insert(j); - printGraphML(ref(store), std::move(roots)); - break; - } + case qResolve: { + for (auto & i : opArgs) + cout << fmt("%s\n", store->printStorePath( + store->followLinksToStorePath(i))); + break; + } - case qResolve: { - for (auto & i : opArgs) - cout << fmt("%s\n", store->printStorePath(store->followLinksToStorePath(i))); - break; - } + case qRoots: { + StorePathSet args; + for (auto & i : opArgs) + for (auto & p : maybeUseOutputs(store->followLinksToStorePath(i), + useOutput, forceRealise)) + args.insert(p); - case qRoots: { - StorePathSet args; - for (auto & i : opArgs) - for (auto & p : maybeUseOutputs(store->followLinksToStorePath(i), useOutput, forceRealise)) - args.insert(p); + StorePathSet referrers; + store->computeFSClosure(args, referrers, true, settings.gcKeepOutputs, + settings.gcKeepDerivations); - StorePathSet referrers; - store->computeFSClosure( - args, referrers, true, settings.gcKeepOutputs, settings.gcKeepDerivations); - - auto & gcStore = require(*store); - Roots roots = gcStore.findRoots(false); - for (auto & [target, links] : roots) - if (referrers.find(target) != referrers.end()) - for (auto & link : links) - cout << fmt("%1% -> %2%\n", link, gcStore.printStorePath(target)); - break; - } + auto & gcStore = require(*store); + Roots roots = gcStore.findRoots(false); + for (auto & [target, links] : roots) + if (referrers.find(target) != referrers.end()) + for (auto & link : links) + cout << fmt("%1% -> %2%\n", link, + gcStore.printStorePath(target)); + break; + } - default: - abort(); + default: + abort(); } } - static void opPrintEnv(Strings opFlags, Strings opArgs) { - if (!opFlags.empty()) throw UsageError("unknown flag"); - if (opArgs.size() != 1) throw UsageError("'--print-env' requires one derivation store path"); + if (!opFlags.empty()) + throw UsageError("unknown flag"); + if (opArgs.size() != 1) + throw UsageError("'--print-env' requires one derivation store path"); Path drvPath = opArgs.front(); Derivation drv = store->derivationFromPath(store->parseStorePath(drvPath)); @@ -457,24 +516,26 @@ static void opPrintEnv(Strings opFlags, Strings opArgs) /* Print each environment variable in the derivation in a format * that can be sourced by the shell. */ for (auto & i : drv.env) - cout << format("export %1%; %1%=%2%\n") % i.first % shellEscape(i.second); + cout << format("export %1%; %1%=%2%\n") % i.first % + shellEscape(i.second); /* Also output the arguments. This doesn't preserve whitespace in arguments. */ cout << "export _args; _args='"; bool first = true; for (auto & i : drv.args) { - if (!first) cout << ' '; + if (!first) + cout << ' '; first = false; cout << shellEscape(i); } cout << "'\n"; } - static void opReadLog(Strings opFlags, Strings opArgs) { - if (!opFlags.empty()) throw UsageError("unknown flag"); + if (!opFlags.empty()) + throw UsageError("unknown flag"); auto & logStore = require(*store); @@ -484,25 +545,26 @@ static void opReadLog(Strings opFlags, Strings opArgs) auto path = logStore.followLinksToStorePath(i); auto log = logStore.getBuildLog(path); if (!log) - throw Error("build log of derivation '%s' is not available", logStore.printStorePath(path)); + throw Error("build log of derivation '%s' is not available", + logStore.printStorePath(path)); std::cout << *log; } } - static void opDumpDB(Strings opFlags, Strings opArgs) { - if (!opFlags.empty()) throw UsageError("unknown flag"); + if (!opFlags.empty()) + throw UsageError("unknown flag"); if (!opArgs.empty()) { for (auto & i : opArgs) - cout << store->makeValidityRegistration({store->followLinksToStorePath(i)}, true, true); + cout << store->makeValidityRegistration( + {store->followLinksToStorePath(i)}, true, true); } else { for (auto & i : store->queryAllValidPaths()) cout << store->makeValidityRegistration({i}, true, true); } } - static void registerValidity(bool reregister, bool hashGiven, bool canonicalise) { ValidPathInfos infos; @@ -510,15 +572,19 @@ static void registerValidity(bool reregister, bool hashGiven, bool canonicalise) while (1) { // We use a dummy value because we'll set it below. FIXME be correct by // construction and avoid dummy value. - auto hashResultOpt = !hashGiven ? std::optional { {Hash::dummy, -1} } : std::nullopt; + auto hashResultOpt = !hashGiven + ? std::optional{{Hash::dummy, -1}} + : std::nullopt; auto info = decodeValidPathInfo(*store, cin, hashResultOpt); - if (!info) break; + if (!info) + break; if (!store->isValidPath(info->path) || reregister) { /* !!! races */ if (canonicalise) canonicalisePathMetaData(store->printStorePath(info->path), -1); if (!hashGiven) { - HashResult hash = hashPath(htSHA256, store->printStorePath(info->path)); + HashResult hash = + hashPath(htSHA256, store->printStorePath(info->path)); info->narHash = hash.first; info->narSize = hash.second; } @@ -529,39 +595,43 @@ static void registerValidity(bool reregister, bool hashGiven, bool canonicalise) ensureLocalStore()->registerValidPaths(infos); } - static void opLoadDB(Strings opFlags, Strings opArgs) { - if (!opFlags.empty()) throw UsageError("unknown flag"); + if (!opFlags.empty()) + throw UsageError("unknown flag"); if (!opArgs.empty()) throw UsageError("no arguments expected"); registerValidity(true, true, false); } - static void opRegisterValidity(Strings opFlags, Strings opArgs) { bool reregister = false; // !!! maybe this should be the default bool hashGiven = false; for (auto & i : opFlags) - if (i == "--reregister") reregister = true; - else if (i == "--hash-given") hashGiven = true; - else throw UsageError("unknown flag '%1%'", i); + if (i == "--reregister") + reregister = true; + else if (i == "--hash-given") + hashGiven = true; + else + throw UsageError("unknown flag '%1%'", i); - if (!opArgs.empty()) throw UsageError("no arguments expected"); + if (!opArgs.empty()) + throw UsageError("no arguments expected"); registerValidity(reregister, hashGiven, true); } - static void opCheckValidity(Strings opFlags, Strings opArgs) { bool printInvalid = false; for (auto & i : opFlags) - if (i == "--print-invalid") printInvalid = true; - else throw UsageError("unknown flag '%1%'", i); + if (i == "--print-invalid") + printInvalid = true; + else + throw UsageError("unknown flag '%1%'", i); for (auto & i : opArgs) { auto path = store->followLinksToStorePath(i); @@ -569,12 +639,12 @@ static void opCheckValidity(Strings opFlags, Strings opArgs) if (printInvalid) cout << fmt("%s\n", store->printStorePath(path)); else - throw Error("path '%s' is not valid", store->printStorePath(path)); + throw Error("path '%s' is not valid", + store->printStorePath(path)); } } } - static void opGC(Strings opFlags, Strings opArgs) { bool printRoots = false; @@ -585,14 +655,20 @@ static void opGC(Strings opFlags, Strings opArgs) /* Do what? */ for (auto i = opFlags.begin(); i != opFlags.end(); ++i) - if (*i == "--print-roots") printRoots = true; - else if (*i == "--print-live") options.action = GCOptions::gcReturnLive; - else if (*i == "--print-dead") options.action = GCOptions::gcReturnDead; + if (*i == "--print-roots") + printRoots = true; + else if (*i == "--print-live") + options.action = GCOptions::gcReturnLive; + else if (*i == "--print-dead") + options.action = GCOptions::gcReturnDead; else if (*i == "--max-freed") - options.maxFreed = std::max(getIntArg(*i, i, opFlags.end(), true), (int64_t) 0); - else throw UsageError("bad sub-operation '%1%' in GC", *i); + options.maxFreed = std::max( + getIntArg(*i, i, opFlags.end(), true), (int64_t) 0); + 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"); auto & gcStore = require(*store); @@ -604,7 +680,8 @@ static void opGC(Strings opFlags, Strings opArgs) for (auto & link : links) roots2.emplace(link, target); for (auto & [link, target] : roots2) - std::cout << link << " -> " << gcStore.printStorePath(target) << "\n"; + std::cout << link << " -> " << gcStore.printStorePath(target) + << "\n"; } else { @@ -617,7 +694,6 @@ static void opGC(Strings opFlags, Strings opArgs) } } - /* Remove paths from the Nix store if possible (i.e., if they do not have any remaining referrers and are not reachable from any GC roots). */ @@ -627,8 +703,10 @@ static void opDelete(Strings opFlags, Strings opArgs) options.action = GCOptions::gcDeleteSpecific; for (auto & i : opFlags) - if (i == "--ignore-liveness") options.ignoreLiveness = true; - else throw UsageError("unknown flag '%1%'", i); + if (i == "--ignore-liveness") + options.ignoreLiveness = true; + else + throw UsageError("unknown flag '%1%'", i); for (auto & i : opArgs) options.pathsToDelete.insert(store->followLinksToStorePath(i)); @@ -640,12 +718,13 @@ static void opDelete(Strings opFlags, Strings opArgs) gcStore.collectGarbage(options, results); } - /* Dump a path as a Nix archive. The archive is written to stdout */ static void opDump(Strings opFlags, Strings opArgs) { - if (!opFlags.empty()) throw UsageError("unknown flag"); - if (opArgs.size() != 1) throw UsageError("only one argument allowed"); + if (!opFlags.empty()) + throw UsageError("unknown flag"); + if (opArgs.size() != 1) + throw UsageError("only one argument allowed"); FdSink sink(STDOUT_FILENO); std::string path = *opArgs.begin(); @@ -653,18 +732,18 @@ static void opDump(Strings opFlags, Strings opArgs) sink.flush(); } - /* Restore a value from a Nix archive. The archive is read from stdin. */ static void opRestore(Strings opFlags, Strings opArgs) { - if (!opFlags.empty()) throw UsageError("unknown flag"); - if (opArgs.size() != 1) throw UsageError("only one argument allowed"); + if (!opFlags.empty()) + throw UsageError("unknown flag"); + if (opArgs.size() != 1) + throw UsageError("only one argument allowed"); FdSource source(STDIN_FILENO); restorePath(*opArgs.begin(), source); } - static void opExport(Strings opFlags, Strings opArgs) { for (auto & i : opFlags) @@ -680,13 +759,13 @@ static void opExport(Strings opFlags, Strings opArgs) sink.flush(); } - static void opImport(Strings opFlags, Strings opArgs) { for (auto & i : opFlags) throw UsageError("unknown flag '%1%'", i); - if (!opArgs.empty()) throw UsageError("no arguments expected"); + if (!opArgs.empty()) + throw UsageError("no arguments expected"); FdSource source(STDIN_FILENO); auto paths = store->importPaths(source, NoCheckSigs); @@ -695,18 +774,17 @@ static void opImport(Strings opFlags, Strings opArgs) cout << fmt("%s\n", store->printStorePath(i)) << std::flush; } - /* Initialise the Nix databases. */ static void opInit(Strings opFlags, Strings opArgs) { - if (!opFlags.empty()) throw UsageError("unknown flag"); + if (!opFlags.empty()) + throw UsageError("unknown flag"); if (!opArgs.empty()) throw UsageError("no arguments expected"); /* Doesn't do anything right now; database tables are initialised automatically. */ } - /* Verify the consistency of the Nix environment. */ static void opVerify(Strings opFlags, Strings opArgs) { @@ -717,9 +795,12 @@ static void opVerify(Strings opFlags, Strings opArgs) RepairFlag repair = NoRepair; for (auto & i : opFlags) - if (i == "--check-contents") checkContents = true; - else if (i == "--repair") repair = Repair; - else throw UsageError("unknown flag '%1%'", i); + if (i == "--check-contents") + checkContents = true; + else if (i == "--repair") + repair = Repair; + else + throw UsageError("unknown flag '%1%'", i); if (store->verifyStore(checkContents, repair)) { warn("not all store errors were fixed"); @@ -727,7 +808,6 @@ static void opVerify(Strings opFlags, Strings opArgs) } } - /* Verify whether the contents of the given store path have not changed. */ static void opVerifyPath(Strings opFlags, Strings opArgs) { @@ -738,16 +818,17 @@ static void opVerifyPath(Strings opFlags, Strings opArgs) for (auto & i : opArgs) { auto path = store->followLinksToStorePath(i); - printMsg(lvlTalkative, "checking path '%s'...", store->printStorePath(path)); + printMsg(lvlTalkative, "checking path '%s'...", + store->printStorePath(path)); auto info = store->queryPathInfo(path); HashSink sink(info->narHash.type); store->narFromPath(path, sink); auto current = sink.finish(); if (current.first != info->narHash) { printError("path '%s' was modified! expected hash '%s', got '%s'", - store->printStorePath(path), - info->narHash.to_string(Base32, true), - current.first.to_string(Base32, true)); + store->printStorePath(path), + info->narHash.to_string(Base32, true), + current.first.to_string(Base32, true)); status = 1; } } @@ -755,7 +836,6 @@ static void opVerifyPath(Strings opFlags, Strings opArgs) throw Exit(status); } - /* Repair the contents of the given path by redownloading it using a substituter (if available). */ static void opRepairPath(Strings opFlags, Strings opArgs) @@ -782,17 +862,21 @@ static void opServe(Strings opFlags, Strings opArgs) { bool writeAllowed = false; for (auto & i : opFlags) - if (i == "--write") writeAllowed = true; - else throw UsageError("unknown flag '%1%'", i); + if (i == "--write") + writeAllowed = true; + else + throw UsageError("unknown flag '%1%'", i); - if (!opArgs.empty()) throw UsageError("no arguments expected"); + if (!opArgs.empty()) + throw UsageError("no arguments expected"); FdSource in(STDIN_FILENO); FdSink out(STDOUT_FILENO); /* Exchange the greeting. */ unsigned int magic = readInt(in); - if (magic != SERVE_MAGIC_1) throw Error("protocol mismatch"); + if (magic != SERVE_MAGIC_1) + throw Error("protocol mismatch"); out << SERVE_MAGIC_2 << SERVE_PROTOCOL_VERSION; out.flush(); unsigned int clientVersion = readInt(in); @@ -828,163 +912,174 @@ static void opServe(Strings opFlags, Strings opArgs) switch (cmd) { - case cmdQueryValidPaths: { - bool lock = readInt(in); - bool substitute = readInt(in); - auto paths = worker_proto::read(*store, in, Phantom {}); - if (lock && writeAllowed) - for (auto & path : paths) - store->addTempRoot(path); - - if (substitute && writeAllowed) { - store->substitutePaths(paths); - } - - worker_proto::write(*store, out, store->queryValidPaths(paths)); - break; + case cmdQueryValidPaths: { + bool lock = readInt(in); + bool substitute = readInt(in); + auto paths = + worker_proto::read(*store, in, Phantom{}); + if (lock && writeAllowed) + for (auto & path : paths) + store->addTempRoot(path); + + if (substitute && writeAllowed) { + store->substitutePaths(paths); } - case cmdQueryPathInfos: { - auto paths = worker_proto::read(*store, in, Phantom {}); - // !!! Maybe we want a queryPathInfos? - for (auto & i : paths) { - try { - auto info = store->queryPathInfo(i); - out << store->printStorePath(info->path) - << (info->deriver ? store->printStorePath(*info->deriver) : ""); - worker_proto::write(*store, out, info->references); - // !!! Maybe we want compression? - out << info->narSize // downloadSize - << info->narSize; - if (GET_PROTOCOL_MINOR(clientVersion) >= 4) - out << info->narHash.to_string(Base32, true) - << renderContentAddress(info->ca) - << info->sigs; - } catch (InvalidPath &) { - } + worker_proto::write(*store, out, store->queryValidPaths(paths)); + break; + } + + case cmdQueryPathInfos: { + auto paths = + worker_proto::read(*store, in, Phantom{}); + // !!! Maybe we want a queryPathInfos? + for (auto & i : paths) { + try { + auto info = store->queryPathInfo(i); + out << store->printStorePath(info->path) + << (info->deriver + ? store->printStorePath(*info->deriver) + : ""); + worker_proto::write(*store, out, info->references); + // !!! Maybe we want compression? + out << info->narSize // downloadSize + << info->narSize; + if (GET_PROTOCOL_MINOR(clientVersion) >= 4) + out << info->narHash.to_string(Base32, true) + << renderContentAddress(info->ca) << info->sigs; + } catch (InvalidPath &) { } - out << ""; - break; } + out << ""; + break; + } - case cmdDumpStorePath: - store->narFromPath(store->parseStorePath(readString(in)), out); - break; + case cmdDumpStorePath: + store->narFromPath(store->parseStorePath(readString(in)), out); + break; - case cmdImportPaths: { - if (!writeAllowed) throw Error("importing paths is not allowed"); - store->importPaths(in, NoCheckSigs); // FIXME: should we skip sig checking? - out << 1; // indicate success - break; - } + case cmdImportPaths: { + if (!writeAllowed) + throw Error("importing paths is not allowed"); + store->importPaths( + in, NoCheckSigs); // FIXME: should we skip sig checking? + out << 1; // indicate success + break; + } - case cmdExportPaths: { - readInt(in); // obsolete - store->exportPaths(worker_proto::read(*store, in, Phantom {}), out); - break; - } + case cmdExportPaths: { + readInt(in); // obsolete + store->exportPaths( + worker_proto::read(*store, in, Phantom{}), out); + break; + } - case cmdBuildPaths: { + case cmdBuildPaths: { - if (!writeAllowed) throw Error("building paths is not allowed"); + if (!writeAllowed) + throw Error("building paths is not allowed"); - std::vector paths; - for (auto & s : readStrings(in)) - paths.push_back(parsePathWithOutputs(*store, s)); + std::vector paths; + for (auto & s : readStrings(in)) + paths.push_back(parsePathWithOutputs(*store, s)); - getBuildSettings(); + getBuildSettings(); - try { - MonitorFdHup monitor(in.fd); - store->buildPaths(toDerivedPaths(paths)); - out << 0; - } catch (Error & e) { - assert(e.status); - out << e.status << e.msg(); - } - break; + try { + MonitorFdHup monitor(in.fd); + store->buildPaths(toDerivedPaths(paths)); + out << 0; + } catch (Error & e) { + assert(e.status); + out << e.status << e.msg(); } + break; + } - case cmdBuildDerivation: { /* Used by hydra-queue-runner. */ - - if (!writeAllowed) throw Error("building paths is not allowed"); - - auto drvPath = store->parseStorePath(readString(in)); - BasicDerivation drv; - readDerivation(in, *store, drv, Derivation::nameFromPath(drvPath)); + case cmdBuildDerivation: { /* Used by hydra-queue-runner. */ - getBuildSettings(); + if (!writeAllowed) + throw Error("building paths is not allowed"); - MonitorFdHup monitor(in.fd); - auto status = store->buildDerivation(drvPath, drv); + auto drvPath = store->parseStorePath(readString(in)); + BasicDerivation drv; + readDerivation(in, *store, drv, Derivation::nameFromPath(drvPath)); - out << status.status << status.errorMsg; + getBuildSettings(); - if (GET_PROTOCOL_MINOR(clientVersion) >= 3) - out << status.timesBuilt << status.isNonDeterministic << status.startTime << status.stopTime; - if (GET_PROTOCOL_MINOR(clientVersion >= 6)) { - worker_proto::write(*store, out, status.builtOutputs); - } + MonitorFdHup monitor(in.fd); + auto status = store->buildDerivation(drvPath, drv); + out << status.status << status.errorMsg; - break; + if (GET_PROTOCOL_MINOR(clientVersion) >= 3) + out << status.timesBuilt << status.isNonDeterministic + << status.startTime << status.stopTime; + if (GET_PROTOCOL_MINOR(clientVersion >= 6)) { + worker_proto::write(*store, out, status.builtOutputs); } - case cmdQueryClosure: { - bool includeOutputs = readInt(in); - StorePathSet closure; - store->computeFSClosure(worker_proto::read(*store, in, Phantom {}), - closure, false, includeOutputs); - worker_proto::write(*store, out, closure); - break; - } + break; + } - case cmdAddToStoreNar: { - if (!writeAllowed) throw Error("importing paths is not allowed"); + case cmdQueryClosure: { + bool includeOutputs = readInt(in); + StorePathSet closure; + store->computeFSClosure( + worker_proto::read(*store, in, Phantom{}), + closure, false, includeOutputs); + worker_proto::write(*store, out, closure); + break; + } - auto path = readString(in); - auto deriver = readString(in); - ValidPathInfo info { - store->parseStorePath(path), - Hash::parseAny(readString(in), htSHA256), - }; - if (deriver != "") - info.deriver = store->parseStorePath(deriver); - info.references = worker_proto::read(*store, in, Phantom {}); - in >> info.registrationTime >> info.narSize >> info.ultimate; - info.sigs = readStrings(in); - info.ca = parseContentAddressOpt(readString(in)); + case cmdAddToStoreNar: { + if (!writeAllowed) + throw Error("importing paths is not allowed"); - if (info.narSize == 0) - throw Error("narInfo is too old and missing the narSize field"); + auto path = readString(in); + auto deriver = readString(in); + ValidPathInfo info{ + store->parseStorePath(path), + Hash::parseAny(readString(in), htSHA256), + }; + if (deriver != "") + info.deriver = store->parseStorePath(deriver); + info.references = + worker_proto::read(*store, in, Phantom{}); + in >> info.registrationTime >> info.narSize >> info.ultimate; + info.sigs = readStrings(in); + info.ca = parseContentAddressOpt(readString(in)); - SizedSource sizedSource(in, info.narSize); + if (info.narSize == 0) + throw Error("narInfo is too old and missing the narSize field"); - store->addToStore(info, sizedSource, NoRepair, NoCheckSigs); + SizedSource sizedSource(in, info.narSize); - // consume all the data that has been sent before continuing. - sizedSource.drainAll(); + store->addToStore(info, sizedSource, NoRepair, NoCheckSigs); - out << 1; // indicate success + // consume all the data that has been sent before continuing. + sizedSource.drainAll(); - break; - } + out << 1; // indicate success - default: - throw Error("unknown serve command %1%", cmd); + break; + } + + default: + throw Error("unknown serve command %1%", cmd); } out.flush(); } } - static void opGenerateBinaryCacheKey(Strings opFlags, Strings opArgs) { for (auto & i : opFlags) throw UsageError("unknown flag '%1%'", i); - if (opArgs.size() != 3) throw UsageError("three arguments expected"); + if (opArgs.size() != 3) + throw UsageError("three arguments expected"); auto i = opArgs.begin(); std::string keyName = *i++; std::string secretKeyFile = *i++; @@ -997,98 +1092,100 @@ static void opGenerateBinaryCacheKey(Strings opFlags, Strings opArgs) writeFile(secretKeyFile, secretKey.to_string()); } - static void opVersion(Strings opFlags, Strings opArgs) { printVersion("nix-store"); } - /* Scan the arguments; find the operation, set global flags, put all other flags in a list, and put all other arguments in another list. */ -static int main_nix_store(int argc, char * * argv) +static int main_nix_store(int argc, char ** argv) { { Strings opFlags, opArgs; Operation op = 0; - parseCmdLine(argc, argv, [&](Strings::iterator & arg, const Strings::iterator & end) { - Operation oldOp = op; - - if (*arg == "--help") - showManPage("nix-store"); - else if (*arg == "--version") - op = opVersion; - else if (*arg == "--realise" || *arg == "--realize" || *arg == "-r") - op = opRealise; - else if (*arg == "--add" || *arg == "-A") - op = opAdd; - else if (*arg == "--add-fixed") - op = opAddFixed; - else if (*arg == "--print-fixed-path") - op = opPrintFixedPath; - else if (*arg == "--delete") - op = opDelete; - else if (*arg == "--query" || *arg == "-q") - op = opQuery; - else if (*arg == "--print-env") - op = opPrintEnv; - else if (*arg == "--read-log" || *arg == "-l") - op = opReadLog; - else if (*arg == "--dump-db") - op = opDumpDB; - else if (*arg == "--load-db") - op = opLoadDB; - else if (*arg == "--register-validity") - op = opRegisterValidity; - else if (*arg == "--check-validity") - op = opCheckValidity; - else if (*arg == "--gc") - op = opGC; - else if (*arg == "--dump") - op = opDump; - else if (*arg == "--restore") - op = opRestore; - else if (*arg == "--export") - op = opExport; - else if (*arg == "--import") - op = opImport; - else if (*arg == "--init") - op = opInit; - else if (*arg == "--verify") - op = opVerify; - else if (*arg == "--verify-path") - op = opVerifyPath; - else if (*arg == "--repair-path") - op = opRepairPath; - else if (*arg == "--optimise" || *arg == "--optimize") - op = opOptimise; - else if (*arg == "--serve") - op = opServe; - else if (*arg == "--generate-binary-cache-key") - op = opGenerateBinaryCacheKey; - else if (*arg == "--add-root") - gcRoot = absPath(getArg(*arg, arg, end)); - else if (*arg == "--indirect") - ; - else if (*arg == "--no-output") - noOutput = true; - else if (*arg != "" && arg->at(0) == '-') { - opFlags.push_back(*arg); - if (*arg == "--max-freed" || *arg == "--max-links" || *arg == "--max-atime") /* !!! hack */ - opFlags.push_back(getArg(*arg, arg, end)); - } - else - opArgs.push_back(*arg); - - if (oldOp && oldOp != op) - throw UsageError("only one operation may be specified"); - - return true; - }); - - if (!op) throw UsageError("no operation specified"); + parseCmdLine( + argc, argv, + [&](Strings::iterator & arg, const Strings::iterator & end) { + Operation oldOp = op; + + if (*arg == "--help") + showManPage("nix-store"); + else if (*arg == "--version") + op = opVersion; + else if (*arg == "--realise" || *arg == "--realize" || + *arg == "-r") + op = opRealise; + else if (*arg == "--add" || *arg == "-A") + op = opAdd; + else if (*arg == "--add-fixed") + op = opAddFixed; + else if (*arg == "--print-fixed-path") + op = opPrintFixedPath; + else if (*arg == "--delete") + op = opDelete; + else if (*arg == "--query" || *arg == "-q") + op = opQuery; + else if (*arg == "--print-env") + op = opPrintEnv; + else if (*arg == "--read-log" || *arg == "-l") + op = opReadLog; + else if (*arg == "--dump-db") + op = opDumpDB; + else if (*arg == "--load-db") + op = opLoadDB; + else if (*arg == "--register-validity") + op = opRegisterValidity; + else if (*arg == "--check-validity") + op = opCheckValidity; + else if (*arg == "--gc") + op = opGC; + else if (*arg == "--dump") + op = opDump; + else if (*arg == "--restore") + op = opRestore; + else if (*arg == "--export") + op = opExport; + else if (*arg == "--import") + op = opImport; + else if (*arg == "--init") + op = opInit; + else if (*arg == "--verify") + op = opVerify; + else if (*arg == "--verify-path") + op = opVerifyPath; + else if (*arg == "--repair-path") + op = opRepairPath; + else if (*arg == "--optimise" || *arg == "--optimize") + op = opOptimise; + else if (*arg == "--serve") + op = opServe; + else if (*arg == "--generate-binary-cache-key") + op = opGenerateBinaryCacheKey; + else if (*arg == "--add-root") + gcRoot = absPath(getArg(*arg, arg, end)); + else if (*arg == "--indirect") + ; + else if (*arg == "--no-output") + noOutput = true; + else if (*arg != "" && arg->at(0) == '-') { + opFlags.push_back(*arg); + if (*arg == "--max-freed" || *arg == "--max-links" || + *arg == "--max-atime") /* !!! hack */ + opFlags.push_back(getArg(*arg, arg, end)); + } else + opArgs.push_back(*arg); + + if (oldOp && oldOp != op) + throw UsageError("only one operation may be specified"); + + return true; + }); + + if (!op) + throw UsageError("no operation specified"); if (op != opDump && op != opRestore) /* !!! hack */ store = openStore(); diff --git a/src/nix/add-to-store.cc b/src/nix/add-to-store.cc index 5168413d2de0..fef84875c550 100644 --- a/src/nix/add-to-store.cc +++ b/src/nix/add-to-store.cc @@ -5,8 +5,7 @@ using namespace nix; -struct CmdAddToStore : MixDryRun, StoreCommand -{ +struct CmdAddToStore : MixDryRun, StoreCommand { Path path; std::optional namePart; FileIngestionMethod ingestionMethod; @@ -19,7 +18,8 @@ struct CmdAddToStore : MixDryRun, StoreCommand addFlag({ .longName = "name", .shortName = 'n', - .description = "Override the name component of the store path. It defaults to the base name of *path*.", + .description = "Override the name component of the store path. It " + "defaults to the base name of *path*.", .labels = {"name"}, .handler = {&namePart}, }); @@ -27,7 +27,8 @@ struct CmdAddToStore : MixDryRun, StoreCommand void run(ref store) override { - if (!namePart) namePart = baseNameOf(path); + if (!namePart) + namePart = baseNameOf(path); StringSink sink; dumpPath(path, sink); @@ -41,15 +42,15 @@ struct CmdAddToStore : MixDryRun, StoreCommand hash = hsink.finish().first; } - ValidPathInfo info { + ValidPathInfo info{ store->makeFixedOutputPath(ingestionMethod, hash, *namePart), narHash, }; info.narSize = sink.s.size(); - info.ca = std::optional { FixedOutputHash { + info.ca = std::optional{FixedOutputHash{ .method = ingestionMethod, .hash = hash, - } }; + }}; if (!dryRun) { auto source = StringSource(sink.s); @@ -60,12 +61,8 @@ struct CmdAddToStore : MixDryRun, StoreCommand } }; -struct CmdAddFile : CmdAddToStore -{ - CmdAddFile() - { - ingestionMethod = FileIngestionMethod::Flat; - } +struct CmdAddFile : CmdAddToStore { + CmdAddFile() { ingestionMethod = FileIngestionMethod::Flat; } std::string description() override { @@ -75,28 +72,21 @@ struct CmdAddFile : CmdAddToStore std::string doc() override { return - #include "add-file.md" - ; +#include "add-file.md" + ; } }; -struct CmdAddPath : CmdAddToStore -{ - CmdAddPath() - { - ingestionMethod = FileIngestionMethod::Recursive; - } +struct CmdAddPath : CmdAddToStore { + CmdAddPath() { ingestionMethod = FileIngestionMethod::Recursive; } - std::string description() override - { - return "add a path to the Nix store"; - } + std::string description() override { return "add a path to the Nix store"; } std::string doc() override { return - #include "add-path.md" - ; +#include "add-path.md" + ; } }; diff --git a/src/nix/app.cc b/src/nix/app.cc index 821964f86257..17ba18d05731 100644 --- a/src/nix/app.cc +++ b/src/nix/app.cc @@ -8,29 +8,20 @@ namespace nix { -struct InstallableDerivedPath : Installable -{ +struct InstallableDerivedPath : Installable { ref store; const DerivedPath derivedPath; InstallableDerivedPath(ref store, const DerivedPath & derivedPath) - : store(store) - , derivedPath(derivedPath) + : store(store), derivedPath(derivedPath) { } - std::string what() const override { return derivedPath.to_string(*store); } - DerivedPaths toDerivedPaths() override - { - return {derivedPath}; - } + DerivedPaths toDerivedPaths() override { return {derivedPath}; } - std::optional getStorePath() override - { - return std::nullopt; - } + std::optional getStorePath() override { return std::nullopt; } }; /** @@ -42,18 +33,18 @@ StringPairs resolveRewrites(Store & store, const BuiltPaths dependencies) StringPairs res; for (auto & dep : dependencies) if (auto drvDep = std::get_if(&dep)) - for (auto & [ outputName, outputPath ] : drvDep->outputs) + for (auto & [outputName, outputPath] : drvDep->outputs) res.emplace( downstreamPlaceholder(store, drvDep->drvPath, outputName), - store.printStorePath(outputPath) - ); + store.printStorePath(outputPath)); return res; } /** * Resolve the given string assuming the given context. */ -std::string resolveString(Store & store, const std::string & toResolve, const BuiltPaths dependencies) +std::string resolveString(Store & store, const std::string & toResolve, + const BuiltPaths dependencies) { auto rewrites = resolveRewrites(store, dependencies); return rewriteStrings(toResolve, rewrites); @@ -66,18 +57,23 @@ UnresolvedApp Installable::toApp(EvalState & state) auto type = cursor->getAttr("type")->getString(); - std::string expected = !attrPath.empty() && state.symbols[attrPath[0]] == "apps" ? "app" : "derivation"; + std::string expected = + !attrPath.empty() && state.symbols[attrPath[0]] == "apps" + ? "app" + : "derivation"; if (type != expected) - throw Error("attribute '%s' should have type '%s'", cursor->getAttrPathStr(), expected); + throw Error("attribute '%s' should have type '%s'", + cursor->getAttrPathStr(), expected); if (type == "app") { - auto [program, context] = cursor->getAttr("program")->getStringWithContext(); + auto [program, context] = + cursor->getAttr("program")->getStringWithContext(); std::vector context2; for (auto & [path, name] : context) context2.push_back({path, {name}}); - return UnresolvedApp{App { + return UnresolvedApp{App{ .context = std::move(context2), .program = program, }}; @@ -90,22 +86,21 @@ UnresolvedApp Installable::toApp(EvalState & state) auto name = cursor->getAttr(state.sName)->getString(); auto aPname = cursor->maybeGetAttr("pname"); auto aMeta = cursor->maybeGetAttr(state.sMeta); - auto aMainProgram = aMeta ? aMeta->maybeGetAttr("mainProgram") : nullptr; - auto mainProgram = - aMainProgram - ? aMainProgram->getString() - : aPname - ? aPname->getString() - : DrvName(name).name; + auto aMainProgram = + aMeta ? aMeta->maybeGetAttr("mainProgram") : nullptr; + auto mainProgram = aMainProgram ? aMainProgram->getString() + : aPname ? aPname->getString() + : DrvName(name).name; auto program = outPath + "/bin/" + mainProgram; - return UnresolvedApp { App { - .context = { { drvPath, {outputName} } }, + return UnresolvedApp{App{ + .context = {{drvPath, {outputName}}}, .program = program, }}; } else - throw Error("attribute '%s' has unsupported type '%s'", cursor->getAttrPathStr(), type); + throw Error("attribute '%s' has unsupported type '%s'", + cursor->getAttrPathStr(), type); } // FIXME: move to libcmd @@ -116,10 +111,11 @@ App UnresolvedApp::resolve(ref evalStore, ref store) std::vector> installableContext; for (auto & ctxElt : unresolved.context) - installableContext.push_back( - std::make_shared(store, ctxElt.toDerivedPath())); + installableContext.push_back(std::make_shared( + store, ctxElt.toDerivedPath())); - auto builtContext = Installable::build(evalStore, store, Realise::Outputs, installableContext); + auto builtContext = Installable::build(evalStore, store, Realise::Outputs, + installableContext); res.program = resolveString(*store, unresolved.program, builtContext); if (!store->isInStore(res.program)) throw Error("app program '%s' is not in the Nix store", res.program); diff --git a/src/nix/build.cc b/src/nix/build.cc index 9c648d28ebc8..5acf73377b94 100644 --- a/src/nix/build.cc +++ b/src/nix/build.cc @@ -10,22 +10,20 @@ using namespace nix; -struct CmdBuild : InstallablesCommand, MixDryRun, MixJSON, MixProfile -{ +struct CmdBuild : InstallablesCommand, MixDryRun, MixJSON, MixProfile { Path outLink = "result"; bool printOutputPaths = false; BuildMode buildMode = bmNormal; CmdBuild() { - addFlag({ - .longName = "out-link", - .shortName = 'o', - .description = "Use *path* as prefix for the symlinks to the build results. It defaults to `result`.", - .labels = {"path"}, - .handler = {&outLink}, - .completer = completePath - }); + addFlag({.longName = "out-link", + .shortName = 'o', + .description = "Use *path* as prefix for the symlinks to the " + "build results. It defaults to `result`.", + .labels = {"path"}, + .handler = {&outLink}, + .completer = completePath}); addFlag({ .longName = "no-link", @@ -41,7 +39,8 @@ struct CmdBuild : InstallablesCommand, MixDryRun, MixJSON, MixProfile addFlag({ .longName = "rebuild", - .description = "Rebuild an already built package and compare the result to the existing store paths.", + .description = "Rebuild an already built package and compare the " + "result to the existing store paths.", .handler = {&buildMode, bmCheck}, }); } @@ -54,8 +53,8 @@ struct CmdBuild : InstallablesCommand, MixDryRun, MixJSON, MixProfile std::string doc() override { return - #include "build.md" - ; +#include "build.md" + ; } void run(ref store) override @@ -69,51 +68,62 @@ struct CmdBuild : InstallablesCommand, MixDryRun, MixJSON, MixProfile } printMissing(store, pathsToBuild, lvlError); if (json) - logger->cout("%s", derivedPathsToJSON(pathsToBuild, store).dump()); + logger->cout("%s", + derivedPathsToJSON(pathsToBuild, store).dump()); return; } auto buildables = Installable::build( - getEvalStore(), store, - Realise::Outputs, - installables, buildMode); + getEvalStore(), store, Realise::Outputs, installables, buildMode); - if (json) logger->cout("%s", derivedPathsWithHintsToJSON(buildables, store).dump()); + if (json) + logger->cout("%s", + derivedPathsWithHintsToJSON(buildables, store).dump()); if (outLink != "") if (auto store2 = store.dynamic_pointer_cast()) for (const auto & [_i, buildable] : enumerate(buildables)) { auto i = _i; - std::visit(overloaded { - [&](const BuiltPath::Opaque & bo) { - std::string symlink = outLink; - if (i) symlink += fmt("-%d", i); - store2->addPermRoot(bo.path, absPath(symlink)); - }, - [&](const BuiltPath::Built & bfd) { - for (auto & output : bfd.outputs) { + std::visit( + overloaded{ + [&](const BuiltPath::Opaque & bo) { std::string symlink = outLink; - if (i) symlink += fmt("-%d", i); - if (output.first != "out") symlink += fmt("-%s", output.first); - store2->addPermRoot(output.second, absPath(symlink)); - } + if (i) + symlink += fmt("-%d", i); + store2->addPermRoot(bo.path, absPath(symlink)); + }, + [&](const BuiltPath::Built & bfd) { + for (auto & output : bfd.outputs) { + std::string symlink = outLink; + if (i) + symlink += fmt("-%d", i); + if (output.first != "out") + symlink += fmt("-%s", output.first); + store2->addPermRoot(output.second, + absPath(symlink)); + } + }, }, - }, buildable.raw()); + buildable.raw()); } if (printOutputPaths) { stopProgressBar(); for (auto & buildable : buildables) { - std::visit(overloaded { - [&](const BuiltPath::Opaque & bo) { - std::cout << store->printStorePath(bo.path) << std::endl; - }, - [&](const BuiltPath::Built & bfd) { - for (auto & output : bfd.outputs) { - std::cout << store->printStorePath(output.second) << std::endl; - } - }, - }, buildable.raw()); + std::visit(overloaded{ + [&](const BuiltPath::Opaque & bo) { + std::cout << store->printStorePath(bo.path) + << std::endl; + }, + [&](const BuiltPath::Built & bfd) { + for (auto & output : bfd.outputs) { + std::cout << store->printStorePath( + output.second) + << std::endl; + } + }, + }, + buildable.raw()); } } diff --git a/src/nix/bundle.cc b/src/nix/bundle.cc index 2e48e4c74103..6a4c4c16d4f3 100644 --- a/src/nix/bundle.cc +++ b/src/nix/bundle.cc @@ -7,44 +7,43 @@ using namespace nix; -struct CmdBundle : InstallableCommand -{ +struct CmdBundle : InstallableCommand { std::string bundler = "github:NixOS/bundlers"; std::optional outLink; CmdBundle() { - addFlag({ - .longName = "bundler", - .description = fmt("Use a custom bundler instead of the default (`%s`).", bundler), - .labels = {"flake-url"}, - .handler = {&bundler}, - .completer = {[&](size_t, std::string_view prefix) { - completeFlakeRef(getStore(), prefix); - }} - }); - - addFlag({ - .longName = "out-link", - .shortName = 'o', - .description = "Override the name of the symlink to the build result. It defaults to the base name of the app.", - .labels = {"path"}, - .handler = {&outLink}, - .completer = completePath - }); - + addFlag({.longName = "bundler", + .description = + fmt("Use a custom bundler instead of the default (`%s`).", + bundler), + .labels = {"flake-url"}, + .handler = {&bundler}, + .completer = {[&](size_t, std::string_view prefix) { + completeFlakeRef(getStore(), prefix); + }}}); + + addFlag( + {.longName = "out-link", + .shortName = 'o', + .description = "Override the name of the symlink to the build " + "result. It defaults to the base name of the app.", + .labels = {"path"}, + .handler = {&outLink}, + .completer = completePath}); } std::string description() override { - return "bundle an application so that it works outside of the Nix store"; + return "bundle an application so that it works outside of the Nix " + "store"; } std::string doc() override { return - #include "bundle.md" - ; +#include "bundle.md" + ; } Category category() override { return catSecondary; } @@ -52,10 +51,8 @@ struct CmdBundle : InstallableCommand // FIXME: cut&paste from CmdRun. Strings getDefaultFlakeAttrPaths() override { - Strings res{ - "apps." + settings.thisSystem.get() + ".default", - "defaultApp." + settings.thisSystem.get() - }; + Strings res{"apps." + settings.thisSystem.get() + ".default", + "defaultApp." + settings.thisSystem.get()}; for (auto & s : SourceExprCommand::getDefaultFlakeAttrPaths()) res.push_back(s); return res; @@ -75,37 +72,46 @@ struct CmdBundle : InstallableCommand auto val = installable->toValue(*evalState).first; - auto [bundlerFlakeRef, bundlerName, outputsSpec] = parseFlakeRefWithFragmentAndOutputsSpec(bundler, absPath(".")); - const flake::LockFlags lockFlags{ .writeLockFile = false }; - InstallableFlake bundler{this, - evalState, std::move(bundlerFlakeRef), bundlerName, outputsSpec, + auto [bundlerFlakeRef, bundlerName, outputsSpec] = + parseFlakeRefWithFragmentAndOutputsSpec(bundler, absPath(".")); + const flake::LockFlags lockFlags{.writeLockFile = false}; + InstallableFlake bundler{ + this, + evalState, + std::move(bundlerFlakeRef), + bundlerName, + outputsSpec, {"bundlers." + settings.thisSystem.get() + ".default", - "defaultBundler." + settings.thisSystem.get() - }, + "defaultBundler." + settings.thisSystem.get()}, {"bundlers." + settings.thisSystem.get() + "."}, - lockFlags - }; + lockFlags}; auto vRes = evalState->allocValue(); - evalState->callFunction(*bundler.toValue(*evalState).first, *val, *vRes, noPos); + evalState->callFunction(*bundler.toValue(*evalState).first, *val, *vRes, + noPos); if (!evalState->isDerivation(*vRes)) - throw Error("the bundler '%s' does not produce a derivation", bundler.what()); + throw Error("the bundler '%s' does not produce a derivation", + bundler.what()); auto attr1 = vRes->attrs->get(evalState->sDrvPath); if (!attr1) - throw Error("the bundler '%s' does not produce a derivation", bundler.what()); + throw Error("the bundler '%s' does not produce a derivation", + bundler.what()); PathSet context2; - auto drvPath = evalState->coerceToStorePath(attr1->pos, *attr1->value, context2); + auto drvPath = + evalState->coerceToStorePath(attr1->pos, *attr1->value, context2); auto attr2 = vRes->attrs->get(evalState->sOutPath); if (!attr2) - throw Error("the bundler '%s' does not produce a derivation", bundler.what()); + throw Error("the bundler '%s' does not produce a derivation", + bundler.what()); - auto outPath = evalState->coerceToStorePath(attr2->pos, *attr2->value, context2); + auto outPath = + evalState->coerceToStorePath(attr2->pos, *attr2->value, context2); - store->buildPaths({ DerivedPath::Built { drvPath } }); + store->buildPaths({DerivedPath::Built{drvPath}}); auto outPathS = store->printStorePath(outPath); @@ -117,7 +123,8 @@ struct CmdBundle : InstallableCommand } // TODO: will crash if not a localFSStore? - store.dynamic_pointer_cast()->addPermRoot(outPath, absPath(*outLink)); + store.dynamic_pointer_cast()->addPermRoot( + outPath, absPath(*outLink)); } }; diff --git a/src/nix/cat.cc b/src/nix/cat.cc index 6420a0f79891..43b84568d72e 100644 --- a/src/nix/cat.cc +++ b/src/nix/cat.cc @@ -5,8 +5,7 @@ using namespace nix; -struct MixCat : virtual Args -{ +struct MixCat : virtual Args { std::string path; void cat(ref accessor) @@ -21,15 +20,11 @@ struct MixCat : virtual Args } }; -struct CmdCatStore : StoreCommand, MixCat -{ +struct CmdCatStore : StoreCommand, MixCat { CmdCatStore() { - expectArgs({ - .label = "path", - .handler = {&path}, - .completer = completePath - }); + expectArgs( + {.label = "path", .handler = {&path}, .completer = completePath}); } std::string description() override @@ -40,27 +35,20 @@ struct CmdCatStore : StoreCommand, MixCat std::string doc() override { return - #include "store-cat.md" - ; +#include "store-cat.md" + ; } - void run(ref store) override - { - cat(store->getFSAccessor()); - } + void run(ref store) override { cat(store->getFSAccessor()); } }; -struct CmdCatNar : StoreCommand, MixCat -{ +struct CmdCatNar : StoreCommand, MixCat { Path narPath; CmdCatNar() { - expectArgs({ - .label = "nar", - .handler = {&narPath}, - .completer = completePath - }); + expectArgs( + {.label = "nar", .handler = {&narPath}, .completer = completePath}); expectArg("path", &path); } @@ -72,8 +60,8 @@ struct CmdCatNar : StoreCommand, MixCat std::string doc() override { return - #include "nar-cat.md" - ; +#include "nar-cat.md" + ; } void run(ref store) override diff --git a/src/nix/copy.cc b/src/nix/copy.cc index 8730a9a5c339..71dc3fccb028 100644 --- a/src/nix/copy.cc +++ b/src/nix/copy.cc @@ -4,27 +4,27 @@ using namespace nix; -struct CmdCopy : virtual CopyCommand, virtual BuiltPathsCommand -{ +struct CmdCopy : virtual CopyCommand, virtual BuiltPathsCommand { CheckSigsFlag checkSigs = CheckSigs; SubstituteFlag substitute = NoSubstitute; using BuiltPathsCommand::run; - CmdCopy() - : BuiltPathsCommand(true) + CmdCopy() : BuiltPathsCommand(true) { addFlag({ .longName = "no-check-sigs", - .description = "Do not require that paths are signed by trusted keys.", + .description = + "Do not require that paths are signed by trusted keys.", .handler = {&checkSigs, NoCheckSigs}, }); addFlag({ .longName = "substitute-on-destination", .shortName = 's', - .description = "Whether to try substitutes on the destination store (only supported by SSH stores).", + .description = "Whether to try substitutes on the destination " + "store (only supported by SSH stores).", .handler = {&substitute, Substitute}, }); @@ -39,8 +39,8 @@ struct CmdCopy : virtual CopyCommand, virtual BuiltPathsCommand std::string doc() override { return - #include "copy.md" - ; +#include "copy.md" + ; } Category category() override { return catSecondary; } @@ -53,11 +53,12 @@ struct CmdCopy : virtual CopyCommand, virtual BuiltPathsCommand for (auto & builtPath : paths) { auto theseRealisations = builtPath.toRealisedPaths(*srcStore); - stuffToCopy.insert(theseRealisations.begin(), theseRealisations.end()); + stuffToCopy.insert(theseRealisations.begin(), + theseRealisations.end()); } - copyPaths( - *srcStore, *dstStore, stuffToCopy, NoRepair, checkSigs, substitute); + copyPaths(*srcStore, *dstStore, stuffToCopy, NoRepair, checkSigs, + substitute); } }; diff --git a/src/nix/daemon.cc b/src/nix/daemon.cc index 940923d3b56f..04a363b6c049 100644 --- a/src/nix/daemon.cc +++ b/src/nix/daemon.cc @@ -36,16 +36,19 @@ using namespace nix::daemon; #ifndef __linux__ #define SPLICE_F_MOVE 0 -static ssize_t splice(int fd_in, void *off_in, int fd_out, void *off_out, size_t len, unsigned int flags) +static ssize_t splice(int fd_in, void * off_in, int fd_out, void * off_out, + size_t len, unsigned int flags) { - // We ignore most parameters, we just have them for conformance with the linux syscall + // We ignore most parameters, we just have them for conformance with the + // linux syscall std::vector buf(8192); auto read_count = read(fd_in, buf.data(), buf.size()); if (read_count == -1) return read_count; auto write_count = decltype(read_count)(0); while (write_count < read_count) { - auto res = write(fd_out, buf.data() + write_count, read_count - write_count); + auto res = + write(fd_out, buf.data() + write_count, read_count - write_count); if (res == -1) return res; write_count += res; @@ -54,17 +57,16 @@ static ssize_t splice(int fd_in, void *off_in, int fd_out, void *off_out, size_t } #endif - static void sigChldHandler(int sigNo) { // Ensure we don't modify errno of whatever we've interrupted auto saved_errno = errno; // Reap all dead children. - while (waitpid(-1, 0, WNOHANG) > 0) ; + while (waitpid(-1, 0, WNOHANG) > 0) + ; errno = saved_errno; } - static void setSigChldAction(bool autoReap) { struct sigaction act, oact; @@ -75,8 +77,8 @@ static void setSigChldAction(bool autoReap) throw SysError("setting SIGCHLD handler"); } - -bool matchUser(const std::string & user, const std::string & group, const Strings & users) +bool matchUser(const std::string & user, const std::string & group, + const Strings & users) { if (find(users.begin(), users.end(), "*") != users.end()) return true; @@ -86,19 +88,20 @@ bool matchUser(const std::string & user, const std::string & group, const String for (auto & i : users) if (i.substr(0, 1) == "@") { - if (group == i.substr(1)) return true; + if (group == i.substr(1)) + return true; struct group * gr = getgrnam(i.c_str() + 1); - if (!gr) continue; - for (char * * mem = gr->gr_mem; *mem; mem++) - if (user == std::string(*mem)) return true; + if (!gr) + continue; + for (char ** mem = gr->gr_mem; *mem; mem++) + if (user == std::string(*mem)) + return true; } return false; } - -struct PeerInfo -{ +struct PeerInfo { bool pidKnown; pid_t pid; bool uidKnown; @@ -107,11 +110,10 @@ struct PeerInfo gid_t gid; }; - // Get the identity of the caller, if possible. static PeerInfo getPeerInfo(int remote) { - PeerInfo peer = { false, 0, false, 0, false, 0 }; + PeerInfo peer = {false, 0, false, 0, false, 0}; #if defined(SO_PEERCRED) @@ -119,7 +121,7 @@ static PeerInfo getPeerInfo(int remote) socklen_t credLen = sizeof(cred); if (getsockopt(remote, SOL_SOCKET, SO_PEERCRED, &cred, &credLen) == -1) throw SysError("getting peer credentials"); - peer = { true, cred.pid, true, cred.uid, true, cred.gid }; + peer = {true, cred.pid, true, cred.uid, true, cred.gid}; #elif defined(LOCAL_PEERCRED) @@ -131,17 +133,15 @@ static PeerInfo getPeerInfo(int remote) socklen_t credLen = sizeof(cred); if (getsockopt(remote, SOL_LOCAL, LOCAL_PEERCRED, &cred, &credLen) == -1) throw SysError("getting peer credentials"); - peer = { false, 0, true, cred.cr_uid, false, 0 }; + peer = {false, 0, true, cred.cr_uid, false, 0}; #endif return peer; } - #define SD_LISTEN_FDS_START 3 - static ref openUncachedStore() { Store::Params params; // FIXME: get params from somewhere @@ -150,7 +150,6 @@ static ref openUncachedStore() return openStore(settings.storeUri, params); } - static void daemonLoop() { if (chdir("/") == -1) @@ -161,7 +160,8 @@ static void daemonLoop() // Handle socket-based activation by systemd. auto listenFds = getEnv("LISTEN_FDS"); if (listenFds) { - if (getEnv("LISTEN_PID") != std::to_string(getpid()) || listenFds != "1") + if (getEnv("LISTEN_PID") != std::to_string(getpid()) || + listenFds != "1") throw Error("unexpected systemd environment variables"); fdSocket = SD_LISTEN_FDS_START; closeOnExec(fdSocket.get()); @@ -184,11 +184,13 @@ static void daemonLoop() struct sockaddr_un remoteAddr; socklen_t remoteAddrLen = sizeof(remoteAddr); - AutoCloseFD remote = accept(fdSocket.get(), - (struct sockaddr *) &remoteAddr, &remoteAddrLen); + AutoCloseFD remote = + accept(fdSocket.get(), (struct sockaddr *) &remoteAddr, + &remoteAddrLen); checkInterrupt(); if (!remote) { - if (errno == EINTR) continue; + if (errno == EINTR) + continue; throw SysError("accepting connection"); } @@ -209,12 +211,18 @@ static void daemonLoop() if (matchUser(user, group, trustedUsers)) trusted = Trusted; - if ((!trusted && !matchUser(user, group, allowedUsers)) || group == settings.buildUsersGroup) - throw Error("user '%1%' is not allowed to connect to the Nix daemon", user); + if ((!trusted && !matchUser(user, group, allowedUsers)) || + group == settings.buildUsersGroup) + throw Error( + "user '%1%' is not allowed to connect to the Nix daemon", + user); - printInfo(format((std::string) "accepted connection from pid %1%, user %2%" + (trusted ? " (trusted)" : "")) - % (peer.pidKnown ? std::to_string(peer.pid) : "") - % (peer.uidKnown ? user : "")); + printInfo( + format( + (std::string) "accepted connection from pid %1%, user %2%" + + (trusted ? " (trusted)" : "")) % + (peer.pidKnown ? std::to_string(peer.pid) : "") % + (peer.uidKnown ? user : "")); // Fork a child to handle the connection. ProcessOptions options; @@ -222,37 +230,41 @@ static void daemonLoop() options.dieWithParent = false; options.runExitHandlers = true; options.allowVfork = false; - startProcess([&]() { - fdSocket = -1; - - // Background the daemon. - if (setsid() == -1) - throw SysError("creating a new session"); - - // Restore normal handling of SIGCHLD. - setSigChldAction(false); - - // For debugging, stuff the pid into argv[1]. - if (peer.pidKnown && savedArgv[1]) { - auto processName = std::to_string(peer.pid); - strncpy(savedArgv[1], processName.c_str(), strlen(savedArgv[1])); - } - - // Handle the connection. - FdSource from(remote.get()); - FdSink to(remote.get()); - processConnection(openUncachedStore(), from, to, trusted, NotRecursive, [&](Store & store) { + startProcess( + [&]() { + fdSocket = -1; + + // Background the daemon. + if (setsid() == -1) + throw SysError("creating a new session"); + + // Restore normal handling of SIGCHLD. + setSigChldAction(false); + + // For debugging, stuff the pid into argv[1]. + if (peer.pidKnown && savedArgv[1]) { + auto processName = std::to_string(peer.pid); + strncpy(savedArgv[1], processName.c_str(), + strlen(savedArgv[1])); + } + + // Handle the connection. + FdSource from(remote.get()); + FdSink to(remote.get()); + processConnection(openUncachedStore(), from, to, trusted, + NotRecursive, [&](Store & store) { #if 0 /* Prevent users from doing something very dangerous. */ if (geteuid() == 0 && querySetting("build-users-group", "") == "") throw Error("if you run 'nix-daemon' as root, then you MUST set 'build-users-group'!"); #endif - store.createUser(user, peer.uid); - }); + store.createUser(user, peer.uid); + }); - exit(0); - }, options); + exit(0); + }, + options); } catch (Interrupted & e) { return; @@ -268,7 +280,8 @@ static void daemonLoop() static void runDaemon(bool stdio) { if (stdio) { - if (auto store = openUncachedStore().dynamic_pointer_cast()) { + if (auto store = + openUncachedStore().dynamic_pointer_cast()) { auto conn = store->openConnectionWrapper(); int from = conn->from.fd; int to = conn->to.fd; @@ -282,16 +295,20 @@ static void runDaemon(bool stdio) if (select(nfds, &fds, nullptr, nullptr, nullptr) == -1) throw SysError("waiting for data from client or server"); if (FD_ISSET(from, &fds)) { - auto res = splice(from, nullptr, STDOUT_FILENO, nullptr, SSIZE_MAX, SPLICE_F_MOVE); + auto res = splice(from, nullptr, STDOUT_FILENO, nullptr, + SSIZE_MAX, SPLICE_F_MOVE); if (res == -1) - throw SysError("splicing data from daemon socket to stdout"); + throw SysError( + "splicing data from daemon socket to stdout"); else if (res == 0) throw EndOfFile("unexpected EOF from daemon socket"); } if (FD_ISSET(STDIN_FILENO, &fds)) { - auto res = splice(STDIN_FILENO, nullptr, to, nullptr, SSIZE_MAX, SPLICE_F_MOVE); + auto res = splice(STDIN_FILENO, nullptr, to, nullptr, + SSIZE_MAX, SPLICE_F_MOVE); if (res == -1) - throw SysError("splicing data from stdin to daemon socket"); + throw SysError( + "splicing data from stdin to daemon socket"); else if (res == 0) return; } @@ -302,29 +319,33 @@ static void runDaemon(bool stdio) /* Auth hook is empty because in this mode we blindly trust the standard streams. Limiting access to those is explicitly not `nix-daemon`'s responsibility. */ - processConnection(openUncachedStore(), from, to, Trusted, NotRecursive, [&](Store & _){}); + processConnection(openUncachedStore(), from, to, Trusted, + NotRecursive, [&](Store & _) {}); } } else daemonLoop(); } -static int main_nix_daemon(int argc, char * * argv) +static int main_nix_daemon(int argc, char ** argv) { { auto stdio = false; - parseCmdLine(argc, argv, [&](Strings::iterator & arg, const Strings::iterator & end) { - if (*arg == "--daemon") - ; // ignored for backwards compatibility - else if (*arg == "--help") - showManPage("nix-daemon"); - else if (*arg == "--version") - printVersion("nix-daemon"); - else if (*arg == "--stdio") - stdio = true; - else return false; - return true; - }); + parseCmdLine( + argc, argv, + [&](Strings::iterator & arg, const Strings::iterator & end) { + if (*arg == "--daemon") + ; // ignored for backwards compatibility + else if (*arg == "--help") + showManPage("nix-daemon"); + else if (*arg == "--version") + printVersion("nix-daemon"); + else if (*arg == "--stdio") + stdio = true; + else + return false; + return true; + }); runDaemon(stdio); @@ -334,11 +355,11 @@ static int main_nix_daemon(int argc, char * * argv) static RegisterLegacyCommand r_nix_daemon("nix-daemon", main_nix_daemon); -struct CmdDaemon : StoreCommand -{ +struct CmdDaemon : StoreCommand { std::string description() override { - return "daemon to perform store operations on behalf of non-root clients"; + return "daemon to perform store operations on behalf of non-root " + "clients"; } Category category() override { return catUtility; } @@ -346,14 +367,11 @@ struct CmdDaemon : StoreCommand std::string doc() override { return - #include "daemon.md" - ; +#include "daemon.md" + ; } - void run(ref store) override - { - runDaemon(false); - } + void run(ref store) override { runDaemon(false); } }; static auto rCmdDaemon = registerCommand2({"daemon"}); diff --git a/src/nix/describe-stores.cc b/src/nix/describe-stores.cc index 1dd384c0e340..6e6919034be7 100644 --- a/src/nix/describe-stores.cc +++ b/src/nix/describe-stores.cc @@ -7,8 +7,7 @@ using namespace nix; -struct CmdDescribeStores : Command, MixJSON -{ +struct CmdDescribeStores : Command, MixJSON { std::string description() override { return "show registered store types and their available options"; @@ -31,14 +30,20 @@ struct CmdDescribeStores : Command, MixJSON std::cout << "## " << storeName << std::endl << std::endl; for (auto & [optionName, optionDesc] : storeConfig.items()) { std::cout << "### " << optionName << std::endl << std::endl; - std::cout << optionDesc["description"].get() << std::endl; - std::cout << "default: " << optionDesc["defaultValue"] << std::endl <() + << std::endl; + std::cout << "default: " << optionDesc["defaultValue"] + << std::endl + << std::endl; if (!optionDesc["aliases"].empty()) - std::cout << "aliases: " << optionDesc["aliases"] << std::endl << std::endl; + std::cout << "aliases: " << optionDesc["aliases"] + << std::endl + << std::endl; } } } } }; -static auto rDescribeStore = registerCommand("describe-stores"); +static auto rDescribeStore = + registerCommand("describe-stores"); diff --git a/src/nix/develop.cc b/src/nix/develop.cc index 2a3fc021325d..b105218ca3f8 100644 --- a/src/nix/develop.cc +++ b/src/nix/develop.cc @@ -13,30 +13,32 @@ using namespace nix; -struct DevelopSettings : Config -{ - Setting bashPrompt{this, "", "bash-prompt", +struct DevelopSettings : Config { + Setting bashPrompt{ + this, "", "bash-prompt", "The bash prompt (`PS1`) in `nix develop` shells."}; - Setting bashPromptPrefix{this, "", "bash-prompt-prefix", - "Prefix prepended to the `PS1` environment variable in `nix develop` shells."}; + Setting bashPromptPrefix{ + this, "", "bash-prompt-prefix", + "Prefix prepended to the `PS1` environment variable in `nix develop` " + "shells."}; - Setting bashPromptSuffix{this, "", "bash-prompt-suffix", - "Suffix appended to the `PS1` environment variable in `nix develop` shells."}; + Setting bashPromptSuffix{ + this, "", "bash-prompt-suffix", + "Suffix appended to the `PS1` environment variable in `nix develop` " + "shells."}; }; static DevelopSettings developSettings; static GlobalConfig::Register rDevelopSettings(&developSettings); -struct BuildEnvironment -{ - struct String - { +struct BuildEnvironment { + struct String { bool exported; std::string value; - bool operator == (const String & other) const + bool operator==(const String & other) const { return exported == other.exported && value == other.value; } @@ -62,7 +64,9 @@ struct BuildEnvironment for (auto & [name, info] : json["variables"].items()) { std::string type = info["type"]; if (type == "var" || type == "exported") - res.vars.insert({name, BuildEnvironment::String { .exported = type == "exported", .value = info["value"] }}); + res.vars.insert({name, BuildEnvironment::String{ + .exported = type == "exported", + .value = info["value"]}}); else if (type == "array") res.vars.insert({name, (Array) info["value"]}); else if (type == "associative") @@ -86,12 +90,10 @@ struct BuildEnvironment if (auto str = std::get_if(&value)) { info["type"] = str->exported ? "exported" : "var"; info["value"] = str->value; - } - else if (auto arr = std::get_if(&value)) { + } else if (auto arr = std::get_if(&value)) { info["type"] = "array"; info["value"] = *arr; - } - else if (auto arr = std::get_if(&value)) { + } else if (auto arr = std::get_if(&value)) { info["type"] = "associative"; info["value"] = *arr; } @@ -108,7 +110,8 @@ struct BuildEnvironment return json; } - void toBash(std::ostream & out, const std::set & ignoreVars) const + void toBash(std::ostream & out, + const std::set & ignoreVars) const { for (auto & [name, value] : vars) { if (!ignoreVars.count(name)) { @@ -116,17 +119,16 @@ struct BuildEnvironment out << fmt("%s=%s\n", name, shellEscape(str->value)); if (str->exported) out << fmt("export %s\n", name); - } - else if (auto arr = std::get_if(&value)) { + } else if (auto arr = std::get_if(&value)) { out << "declare -a " << name << "=("; for (auto & s : *arr) out << shellEscape(s) << " "; out << ")\n"; - } - else if (auto arr = std::get_if(&value)) { + } else if (auto arr = std::get_if(&value)) { out << "declare -A " << name << "=("; for (auto & [n, v] : *arr) - out << "[" << shellEscape(n) << "]=" << shellEscape(v) << " "; + out << "[" << shellEscape(n) << "]=" << shellEscape(v) + << " "; out << ")\n"; } } @@ -153,21 +155,21 @@ struct BuildEnvironment return *arr; } else if (auto assoc = std::get_if(&value)) { Array assocKeys; - std::for_each(assoc->begin(), assoc->end(), [&](auto & n) { assocKeys.push_back(n.first); }); + std::for_each(assoc->begin(), assoc->end(), + [&](auto & n) { assocKeys.push_back(n.first); }); return assocKeys; - } - else + } else throw Error("bash variable is not a string or array"); } - bool operator == (const BuildEnvironment & other) const + bool operator==(const BuildEnvironment & other) const { return vars == other.vars && bashFunctions == other.bashFunctions; } }; const static std::string getEnvSh = - #include "get-env.sh.gen.hh" +#include "get-env.sh.gen.hh" ; /* Given an existing derivation, return the shell environment as @@ -175,13 +177,16 @@ const static std::string getEnvSh = modified derivation with the same dependencies and nearly the same initial environment variables, that just writes the resulting environment to a file and exits. */ -static StorePath getDerivationEnvironment(ref store, ref evalStore, const StorePath & drvPath) +static StorePath getDerivationEnvironment(ref store, + ref evalStore, + const StorePath & drvPath) { auto drv = evalStore->derivationFromPath(drvPath); auto builder = baseNameOf(drv.builder); if (builder != "bash") - throw Error("'nix develop' only works on derivations that use 'bash' as their builder"); + throw Error("'nix develop' only works on derivations that use 'bash' " + "as their builder"); auto getEnvShPath = evalStore->addTextToStore("get-env.sh", getEnvSh, {}); @@ -199,12 +204,12 @@ static StorePath getDerivationEnvironment(ref store, ref evalStore drv.inputSrcs.insert(std::move(getEnvShPath)); if (settings.isExperimentalFeatureEnabled(Xp::CaDerivations)) { for (auto & output : drv.outputs) { - output.second = DerivationOutput::Deferred {}, + output.second = DerivationOutput::Deferred{}, drv.env[output.first] = hashPlaceholder(output.first); } } else { for (auto & output : drv.outputs) { - output.second = DerivationOutput::Deferred { }; + output.second = DerivationOutput::Deferred{}; drv.env[output.first] = ""; } auto hashesModulo = hashDerivationModulo(*evalStore, drv, true); @@ -212,7 +217,7 @@ static StorePath getDerivationEnvironment(ref store, ref evalStore for (auto & output : drv.outputs) { Hash h = hashesModulo.hashes.at(output.first); auto outPath = store->makeOutputPath(output.first, h, drv.name); - output.second = DerivationOutput::InputAddressed { + output.second = DerivationOutput::InputAddressed{ .path = outPath, }; drv.env[output.first] = store->printStorePath(outPath); @@ -224,7 +229,8 @@ static StorePath getDerivationEnvironment(ref store, ref evalStore /* Build the derivation. */ store->buildPaths({DerivedPath::Built{shellDrvPath}}, bmNormal, evalStore); - for (auto & [_0, optPath] : evalStore->queryPartialDerivationOutputMap(shellDrvPath)) { + for (auto & [_0, optPath] : + evalStore->queryPartialDerivationOutputMap(shellDrvPath)) { assert(optPath); auto & outPath = *optPath; assert(store->isValidPath(outPath)); @@ -236,24 +242,17 @@ static StorePath getDerivationEnvironment(ref store, ref evalStore throw Error("get-env.sh failed to produce an environment"); } -struct Common : InstallableCommand, MixProfile -{ +struct Common : InstallableCommand, MixProfile { std::set ignoreVars{ "BASHOPTS", "HOME", // FIXME: don't ignore in pure mode? - "NIX_BUILD_TOP", - "NIX_ENFORCE_PURITY", - "NIX_LOG_FD", - "NIX_REMOTE", - "PPID", - "SHELLOPTS", + "NIX_BUILD_TOP", "NIX_ENFORCE_PURITY", + "NIX_LOG_FD", "NIX_REMOTE", + "PPID", "SHELLOPTS", "SSL_CERT_FILE", // FIXME: only want to ignore /no-cert-file.crt - "TEMP", - "TEMPDIR", - "TERM", - "TMP", - "TMPDIR", - "TZ", + "TEMP", "TEMPDIR", + "TERM", "TMP", + "TMPDIR", "TZ", "UID", }; @@ -261,20 +260,18 @@ struct Common : InstallableCommand, MixProfile Common() { - addFlag({ - .longName = "redirect", - .description = "Redirect a store path to a mutable location.", - .labels = {"installable", "outputs-dir"}, - .handler = {[&](std::string installable, std::string outputsDir) { - redirects.push_back({installable, outputsDir}); - }} - }); + addFlag( + {.longName = "redirect", + .description = "Redirect a store path to a mutable location.", + .labels = {"installable", "outputs-dir"}, + .handler = {[&](std::string installable, std::string outputsDir) { + redirects.push_back({installable, outputsDir}); + }}}); } - std::string makeRcScript( - ref store, - const BuildEnvironment & buildEnvironment, - const Path & outputsDir = absPath(".") + "/outputs") + std::string + makeRcScript(ref store, const BuildEnvironment & buildEnvironment, + const Path & outputsDir = absPath(".") + "/outputs") { std::ostringstream out; @@ -300,11 +297,13 @@ struct Common : InstallableCommand, MixProfile // FIXME: properly unquote 'outputs'. StringMap rewrites; - for (auto & outputName : BuildEnvironment::getStrings(outputs->second)) { + for (auto & outputName : + BuildEnvironment::getStrings(outputs->second)) { auto from = buildEnvironment.vars.find(outputName); assert(from != buildEnvironment.vars.end()); // FIXME: unquote - rewrites.insert({BuildEnvironment::getString(from->second), outputsDir + "/" + outputName}); + rewrites.insert({BuildEnvironment::getString(from->second), + outputsDir + "/" + outputName}); } /* Substitute redirects. */ @@ -312,11 +311,14 @@ struct Common : InstallableCommand, MixProfile auto dir = absPath(dir_); auto installable = parseInstallable(store, installable_); auto builtPaths = Installable::toStorePaths( - getEvalStore(), store, Realise::Nothing, OperateOn::Output, {installable}); - for (auto & path: builtPaths) { + getEvalStore(), store, Realise::Nothing, OperateOn::Output, + {installable}); + for (auto & path : builtPaths) { auto from = store->printStorePath(path); if (script.find(from) == std::string::npos) - warn("'%s' (path '%s') is not used by this build environment", installable->what(), from); + warn("'%s' (path '%s') is not used by this build " + "environment", + installable->what(), from); else { printInfo("redirecting '%s' to '%s'", from, dir); rewrites.insert({from, dir}); @@ -354,8 +356,9 @@ struct Common : InstallableCommand, MixProfile auto drvs = Installable::toDerivations(store, {installable}); if (drvs.size() != 1) - throw Error("'%s' needs to evaluate to a single derivation, but it evaluated to %d derivations", - installable->what(), drvs.size()); + throw Error("'%s' needs to evaluate to a single derivation, " + "but it evaluated to %d derivations", + installable->what(), drvs.size()); auto & drvPath = *drvs.begin(); @@ -363,7 +366,8 @@ struct Common : InstallableCommand, MixProfile } } - std::pair getBuildEnvironment(ref store) + std::pair + getBuildEnvironment(ref store) { auto shellOutPath = getShellOutPath(store); @@ -373,31 +377,34 @@ struct Common : InstallableCommand, MixProfile debug("reading environment file '%s'", strPath); - return {BuildEnvironment::fromJSON(readFile(store->toRealPath(shellOutPath))), strPath}; + return {BuildEnvironment::fromJSON( + readFile(store->toRealPath(shellOutPath))), + strPath}; } }; -struct CmdDevelop : Common, MixEnvironment -{ +struct CmdDevelop : Common, MixEnvironment { std::vector command; std::optional phase; CmdDevelop() { - addFlag({ - .longName = "command", - .shortName = 'c', - .description = "Instead of starting an interactive shell, start the specified command and arguments.", - .labels = {"command", "args"}, - .handler = {[&](std::vector ss) { - if (ss.empty()) throw UsageError("--command requires at least one argument"); - command = ss; - }} - }); + addFlag({.longName = "command", + .shortName = 'c', + .description = "Instead of starting an interactive shell, " + "start the specified command and arguments.", + .labels = {"command", "args"}, + .handler = {[&](std::vector ss) { + if (ss.empty()) + throw UsageError( + "--command requires at least one argument"); + command = ss; + }}}); addFlag({ .longName = "phase", - .description = "The stdenv phase to run (e.g. `build` or `configure`).", + .description = + "The stdenv phase to run (e.g. `build` or `configure`).", .labels = {"phase-name"}, .handler = {&phase}, }); @@ -441,14 +448,15 @@ struct CmdDevelop : Common, MixEnvironment std::string description() override { - return "run a bash shell that provides the build environment of a derivation"; + return "run a bash shell that provides the build environment of a " + "derivation"; } std::string doc() override { return - #include "develop.md" - ; +#include "develop.md" + ; } void run(ref store) override @@ -466,7 +474,8 @@ struct CmdDevelop : Common, MixEnvironment if (phase) { if (!command.empty()) - throw UsageError("you cannot use both '--command' and '--phase'"); + throw UsageError( + "you cannot use both '--command' and '--phase'"); // FIXME: foundMakefile is set by buildPhase, need to get // rid of that. script += fmt("foundMakefile=1\n"); @@ -481,16 +490,20 @@ struct CmdDevelop : Common, MixEnvironment } else { - script = "[ -n \"$PS1\" ] && [ -e ~/.bashrc ] && source ~/.bashrc;\n" + script; + script = + "[ -n \"$PS1\" ] && [ -e ~/.bashrc ] && source ~/.bashrc;\n" + + script; if (developSettings.bashPrompt != "") script += fmt("[ -n \"$PS1\" ] && PS1=%s;\n", - shellEscape(developSettings.bashPrompt.get())); + shellEscape(developSettings.bashPrompt.get())); if (developSettings.bashPromptPrefix != "") - script += fmt("[ -n \"$PS1\" ] && PS1=%s\"$PS1\";\n", - shellEscape(developSettings.bashPromptPrefix.get())); + script += + fmt("[ -n \"$PS1\" ] && PS1=%s\"$PS1\";\n", + shellEscape(developSettings.bashPromptPrefix.get())); if (developSettings.bashPromptSuffix != "") - script += fmt("[ -n \"$PS1\" ] && PS1+=%s;\n", - shellEscape(developSettings.bashPromptSuffix.get())); + script += + fmt("[ -n \"$PS1\" ] && PS1+=%s;\n", + shellEscape(developSettings.bashPromptSuffix.get())); } writeFull(rcFileFd.get(), script); @@ -509,18 +522,16 @@ struct CmdDevelop : Common, MixEnvironment nixpkgsLockFlags.inputUpdates = {}; auto bashInstallable = std::make_shared( - this, - state, - installable->nixpkgsFlakeRef(), - "bashInteractive", - DefaultOutputs(), - Strings{}, + this, state, installable->nixpkgsFlakeRef(), "bashInteractive", + DefaultOutputs(), Strings{}, Strings{"legacyPackages." + settings.thisSystem.get() + "."}, nixpkgsLockFlags); bool found = false; - for (auto & path : Installable::toStorePaths(getEvalStore(), store, Realise::Outputs, OperateOn::Output, {bashInstallable})) { + for (auto & path : Installable::toStorePaths( + getEvalStore(), store, Realise::Outputs, OperateOn::Output, + {bashInstallable})) { auto s = store->printStorePath(path) + "/bin/bash"; if (pathExists(s)) { shell = s; @@ -530,23 +541,28 @@ struct CmdDevelop : Common, MixEnvironment } if (!found) - throw Error("package 'nixpkgs#bashInteractive' does not provide a 'bin/bash'"); + throw Error("package 'nixpkgs#bashInteractive' does not " + "provide a 'bin/bash'"); } catch (Error &) { ignoreException(); } - // If running a phase or single command, don't want an interactive shell running after - // Ctrl-C, so don't pass --rcfile - auto args = phase || !command.empty() ? Strings{std::string(baseNameOf(shell)), rcFilePath} - : Strings{std::string(baseNameOf(shell)), "--rcfile", rcFilePath}; + // If running a phase or single command, don't want an interactive shell + // running after Ctrl-C, so don't pass --rcfile + auto args = phase || !command.empty() + ? Strings{std::string(baseNameOf(shell)), rcFilePath} + : Strings{std::string(baseNameOf(shell)), "--rcfile", + rcFilePath}; // Need to chdir since phases assume in flake directory if (phase) { // chdir if installable is a flake of type git+file or path - auto installableFlake = std::dynamic_pointer_cast(installable); + auto installableFlake = + std::dynamic_pointer_cast(installable); if (installableFlake) { - auto sourcePath = installableFlake->getLockedFlake()->flake.resolvedRef.input.getSourcePath(); + auto sourcePath = installableFlake->getLockedFlake() + ->flake.resolvedRef.input.getSourcePath(); if (sourcePath) { if (chdir(sourcePath->c_str()) == -1) { throw SysError("chdir to '%s' failed", *sourcePath); @@ -559,18 +575,18 @@ struct CmdDevelop : Common, MixEnvironment } }; -struct CmdPrintDevEnv : Common, MixJSON -{ +struct CmdPrintDevEnv : Common, MixJSON { std::string description() override { - return "print shell code that can be sourced by bash to reproduce the build environment of a derivation"; + return "print shell code that can be sourced by bash to reproduce the " + "build environment of a derivation"; } std::string doc() override { return - #include "print-dev-env.md" - ; +#include "print-dev-env.md" + ; } Category category() override { return catUtility; } @@ -581,10 +597,8 @@ struct CmdPrintDevEnv : Common, MixJSON stopProgressBar(); - logger->writeToStdout( - json - ? buildEnvironment.toJSON() - : makeRcScript(store, buildEnvironment)); + logger->writeToStdout(json ? buildEnvironment.toJSON() + : makeRcScript(store, buildEnvironment)); } }; diff --git a/src/nix/diff-closures.cc b/src/nix/diff-closures.cc index 0621d662c653..a4024ee373fe 100644 --- a/src/nix/diff-closures.cc +++ b/src/nix/diff-closures.cc @@ -8,13 +8,13 @@ namespace nix { -struct Info -{ +struct Info { std::string outputName; }; // name -> version -> store paths -typedef std::map>> GroupedPaths; +typedef std::map>> + GroupedPaths; GroupedPaths getClosureInfo(ref store, const StorePath & toplevel) { @@ -37,7 +37,8 @@ GroupedPaths getClosureInfo(ref store, const StorePath & toplevel) } DrvName drvName(name); - groupedPaths[drvName.name][drvName.version].emplace(path, Info { .outputName = outputName }); + groupedPaths[drvName.name][drvName.version].emplace( + path, Info{.outputName = outputName}); } return groupedPaths; @@ -45,38 +46,39 @@ GroupedPaths getClosureInfo(ref store, const StorePath & toplevel) std::string showVersions(const std::set & versions) { - if (versions.empty()) return "∅"; + if (versions.empty()) + return "∅"; std::set versions2; for (auto & version : versions) versions2.insert(version.empty() ? "ε" : version); return concatStringsSep(", ", versions2); } -void printClosureDiff( - ref store, - const StorePath & beforePath, - const StorePath & afterPath, - std::string_view indent) +void printClosureDiff(ref store, const StorePath & beforePath, + const StorePath & afterPath, std::string_view indent) { auto beforeClosure = getClosureInfo(store, beforePath); auto afterClosure = getClosureInfo(store, afterPath); std::set allNames; - for (auto & [name, _] : beforeClosure) allNames.insert(name); - for (auto & [name, _] : afterClosure) allNames.insert(name); + for (auto & [name, _] : beforeClosure) + allNames.insert(name); + for (auto & [name, _] : afterClosure) + allNames.insert(name); for (auto & name : allNames) { auto & beforeVersions = beforeClosure[name]; auto & afterVersions = afterClosure[name]; - auto totalSize = [&](const std::map> & versions) - { - uint64_t sum = 0; - for (auto & [_, paths] : versions) - for (auto & [path, _] : paths) - sum += store->queryPathInfo(path)->narSize; - return sum; - }; + auto totalSize = + [&](const std::map> & + versions) { + uint64_t sum = 0; + for (auto & [_, paths] : versions) + for (auto & [path, _] : paths) + sum += store->queryPathInfo(path)->narSize; + return sum; + }; auto beforeSize = totalSize(beforeVersions); auto afterSize = totalSize(afterVersions); @@ -85,19 +87,27 @@ void printClosureDiff( std::set removed, unchanged; for (auto & [version, _] : beforeVersions) - if (!afterVersions.count(version)) removed.insert(version); else unchanged.insert(version); + if (!afterVersions.count(version)) + removed.insert(version); + else + unchanged.insert(version); std::set added; for (auto & [version, _] : afterVersions) - if (!beforeVersions.count(version)) added.insert(version); + if (!beforeVersions.count(version)) + added.insert(version); if (showDelta || !removed.empty() || !added.empty()) { std::vector items; if (!removed.empty() || !added.empty()) - items.push_back(fmt("%s → %s", showVersions(removed), showVersions(added))); + items.push_back( + fmt("%s → %s", showVersions(removed), showVersions(added))); if (showDelta) - items.push_back(fmt("%s%+.1f KiB" ANSI_NORMAL, sizeDelta > 0 ? ANSI_RED : ANSI_GREEN, sizeDelta / 1024.0)); - std::cout << fmt("%s%s: %s\n", indent, name, concatStringsSep(", ", items)); + items.push_back(fmt("%s%+.1f KiB" ANSI_NORMAL, + sizeDelta > 0 ? ANSI_RED : ANSI_GREEN, + sizeDelta / 1024.0)); + std::cout << fmt("%s%s: %s\n", indent, name, + concatStringsSep(", ", items)); } } } @@ -106,8 +116,7 @@ void printClosureDiff( using namespace nix; -struct CmdDiffClosures : SourceExprCommand -{ +struct CmdDiffClosures : SourceExprCommand { std::string _before, _after; CmdDiffClosures() @@ -118,24 +127,28 @@ struct CmdDiffClosures : SourceExprCommand std::string description() override { - return "show what packages and versions were added and removed between two closures"; + return "show what packages and versions were added and removed between " + "two closures"; } std::string doc() override { return - #include "diff-closures.md" - ; +#include "diff-closures.md" + ; } void run(ref store) override { auto before = parseInstallable(store, _before); - auto beforePath = Installable::toStorePath(getEvalStore(), store, Realise::Outputs, operateOn, before); + auto beforePath = Installable::toStorePath( + getEvalStore(), store, Realise::Outputs, operateOn, before); auto after = parseInstallable(store, _after); - auto afterPath = Installable::toStorePath(getEvalStore(), store, Realise::Outputs, operateOn, after); + auto afterPath = Installable::toStorePath( + getEvalStore(), store, Realise::Outputs, operateOn, after); printClosureDiff(store, beforePath, afterPath, ""); } }; -static auto rCmdDiffClosures = registerCommand2({"store", "diff-closures"}); +static auto rCmdDiffClosures = + registerCommand2({"store", "diff-closures"}); diff --git a/src/nix/doctor.cc b/src/nix/doctor.cc index ea87e3d87449..e65c701a3809 100644 --- a/src/nix/doctor.cc +++ b/src/nix/doctor.cc @@ -23,25 +23,27 @@ std::string formatProtocol(unsigned int proto) return "unknown"; } -bool checkPass(const std::string & msg) { +bool checkPass(const std::string & msg) +{ notice(ANSI_GREEN "[PASS] " ANSI_NORMAL + msg); return true; } -bool checkFail(const std::string & msg) { +bool checkFail(const std::string & msg) +{ notice(ANSI_RED "[FAIL] " ANSI_NORMAL + msg); return false; } } -struct CmdDoctor : StoreCommand -{ +struct CmdDoctor : StoreCommand { bool success = true; std::string description() override { - return "check your system for potential problems and print a PASS or FAIL for each check"; + return "check your system for potential problems and print a PASS or " + "FAIL for each check"; } Category category() override { return catNixInstallation; } @@ -64,7 +66,8 @@ struct CmdDoctor : StoreCommand { PathSet dirs; - for (auto & dir : tokenizeString(getEnv("PATH").value_or(""), ":")) + for (auto & dir : + tokenizeString(getEnv("PATH").value_or(""), ":")) if (pathExists(dir + "/nix-env")) dirs.insert(dirOf(canonPath(dir + "/nix-env", true))); @@ -83,25 +86,32 @@ struct CmdDoctor : StoreCommand { PathSet dirs; - for (auto & dir : tokenizeString(getEnv("PATH").value_or(""), ":")) { + for (auto & dir : + tokenizeString(getEnv("PATH").value_or(""), ":")) { Path profileDir = dirOf(dir); try { Path userEnv = canonPath(profileDir, true); - if (store->isStorePath(userEnv) && hasSuffix(userEnv, "user-environment")) { - while (profileDir.find("/profiles/") == std::string::npos && isLink(profileDir)) - profileDir = absPath(readLink(profileDir), dirOf(profileDir)); + if (store->isStorePath(userEnv) && + hasSuffix(userEnv, "user-environment")) { + while (profileDir.find("/profiles/") == std::string::npos && + isLink(profileDir)) + profileDir = + absPath(readLink(profileDir), dirOf(profileDir)); if (profileDir.find("/profiles/") == std::string::npos) dirs.insert(dir); } - } catch (SysError &) {} + } catch (SysError &) { + } } if (!dirs.empty()) { std::stringstream ss; - ss << "Found profiles outside of " << settings.nixStateDir << "/profiles.\n" - << "The generation this profile points to might not have a gcroot and could be\n" + ss << "Found profiles outside of " << settings.nixStateDir + << "/profiles.\n" + << "The generation this profile points to might not have a " + "gcroot and could be\n" << "garbage collected, resulting in broken symlinks.\n\n"; for (auto & dir : dirs) ss << " " << dir << "\n"; @@ -114,14 +124,17 @@ struct CmdDoctor : StoreCommand bool checkStoreProtocol(unsigned int storeProto) { - unsigned int clientProto = GET_PROTOCOL_MAJOR(SERVE_PROTOCOL_VERSION) == GET_PROTOCOL_MAJOR(storeProto) - ? SERVE_PROTOCOL_VERSION - : PROTOCOL_VERSION; + unsigned int clientProto = GET_PROTOCOL_MAJOR(SERVE_PROTOCOL_VERSION) == + GET_PROTOCOL_MAJOR(storeProto) + ? SERVE_PROTOCOL_VERSION + : PROTOCOL_VERSION; if (clientProto != storeProto) { std::stringstream ss; - ss << "Warning: protocol version of this client does not match the store.\n" - << "While this is not necessarily a problem it's recommended to keep the client in\n" + ss << "Warning: protocol version of this client does not match the " + "store.\n" + << "While this is not necessarily a problem it's recommended to " + "keep the client in\n" << "sync with the daemon.\n\n" << "Client protocol: " << formatProtocol(clientProto) << "\n" << "Store protocol: " << formatProtocol(storeProto) << "\n\n"; diff --git a/src/nix/dump-path.cc b/src/nix/dump-path.cc index c4edc894b896..baf9ff210051 100644 --- a/src/nix/dump-path.cc +++ b/src/nix/dump-path.cc @@ -4,8 +4,7 @@ using namespace nix; -struct CmdDumpPath : StorePathCommand -{ +struct CmdDumpPath : StorePathCommand { std::string description() override { return "serialise a store path to stdout in NAR format"; @@ -14,8 +13,8 @@ struct CmdDumpPath : StorePathCommand std::string doc() override { return - #include "store-dump-path.md" - ; +#include "store-dump-path.md" + ; } void run(ref store, const StorePath & storePath) override @@ -28,17 +27,13 @@ struct CmdDumpPath : StorePathCommand static auto rDumpPath = registerCommand2({"store", "dump-path"}); -struct CmdDumpPath2 : Command -{ +struct CmdDumpPath2 : Command { Path path; CmdDumpPath2() { - expectArgs({ - .label = "path", - .handler = {&path}, - .completer = completePath - }); + expectArgs( + {.label = "path", .handler = {&path}, .completer = completePath}); } std::string description() override @@ -49,8 +44,8 @@ struct CmdDumpPath2 : Command std::string doc() override { return - #include "nar-dump-path.md" - ; +#include "nar-dump-path.md" + ; } void run() override diff --git a/src/nix/edit.cc b/src/nix/edit.cc index 76a134b1f67c..48ecff9ceb7d 100644 --- a/src/nix/edit.cc +++ b/src/nix/edit.cc @@ -8,8 +8,7 @@ using namespace nix; -struct CmdEdit : InstallableCommand -{ +struct CmdEdit : InstallableCommand { std::string description() override { return "open the Nix expression of a Nix package in $EDITOR"; @@ -18,8 +17,8 @@ struct CmdEdit : InstallableCommand std::string doc() override { return - #include "edit.md" - ; +#include "edit.md" + ; } Category category() override { return catSecondary; } @@ -34,7 +33,8 @@ struct CmdEdit : InstallableCommand try { return findPackageFilename(*state, *v, installable->what()); } catch (NoPositionInfo &) { - throw Error("cannot find position information for '%s", installable->what()); + throw Error("cannot find position information for '%s", + installable->what()); } }(); @@ -47,7 +47,8 @@ struct CmdEdit : InstallableCommand execvp(args.front().c_str(), stringsToCharPtrs(args).data()); std::string command; - for (const auto &arg : args) command += " '" + arg + "'"; + for (const auto & arg : args) + command += " '" + arg + "'"; throw SysError("cannot run command%s", command); } }; diff --git a/src/nix/eval.cc b/src/nix/eval.cc index 967dc851907f..54bd0aadbeeb 100644 --- a/src/nix/eval.cc +++ b/src/nix/eval.cc @@ -10,8 +10,7 @@ using namespace nix; -struct CmdEval : MixJSON, InstallableCommand -{ +struct CmdEval : MixJSON, InstallableCommand { bool raw = false; std::optional apply; std::optional writeTo; @@ -39,16 +38,13 @@ struct CmdEval : MixJSON, InstallableCommand }); } - std::string description() override - { - return "evaluate a Nix expression"; - } + std::string description() override { return "evaluate a Nix expression"; } std::string doc() override { return - #include "eval.md" - ; +#include "eval.md" + ; } Category category() override { return catSecondary; } @@ -65,7 +61,8 @@ struct CmdEval : MixJSON, InstallableCommand if (apply) { auto vApply = state->allocValue(); - state->eval(state->parseExprFromString(*apply, absPath(".")), *vApply); + state->eval(state->parseExprFromString(*apply, absPath(".")), + *vApply); auto vRes = state->allocValue(); state->callFunction(*vApply, *v, *vRes, noPos); v = vRes; @@ -77,10 +74,10 @@ struct CmdEval : MixJSON, InstallableCommand if (pathExists(*writeTo)) throw Error("path '%s' already exists", *writeTo); - std::function recurse; + std::function + recurse; - recurse = [&](Value & v, const PosIdx pos, const Path & path) - { + recurse = [&](Value & v, const PosIdx pos, const Path & path) { state->forceValue(v, pos); if (v.type() == nString) // FIXME: disallow strings with contexts? @@ -93,17 +90,20 @@ struct CmdEval : MixJSON, InstallableCommand try { if (name == "." || name == "..") throw Error("invalid file name '%s'", name); - recurse(*attr.value, attr.pos, concatStrings(path, "/", name)); + recurse(*attr.value, attr.pos, + concatStrings(path, "/", name)); } catch (Error & e) { e.addTrace( state->positions[attr.pos], - hintfmt("while evaluating the attribute '%s'", name)); + hintfmt("while evaluating the attribute '%s'", + name)); throw; } } - } - else - throw TypeError("value at '%s' is not a string or an attribute set", state->positions[pos]); + } else + throw TypeError( + "value at '%s' is not a string or an attribute set", + state->positions[pos]); }; recurse(*v, pos, *writeTo); diff --git a/src/nix/flake.cc b/src/nix/flake.cc index 8370b8dcf02f..8d10ecf37149 100644 --- a/src/nix/flake.cc +++ b/src/nix/flake.cc @@ -22,27 +22,23 @@ using namespace nix; using namespace nix::flake; -class FlakeCommand : virtual Args, public MixFlakeOptions -{ +class FlakeCommand : virtual Args, public MixFlakeOptions { std::string flakeUrl = "."; -public: - + public: FlakeCommand() { - expectArgs({ - .label = "flake-url", - .optional = true, - .handler = {&flakeUrl}, - .completer = {[&](size_t, std::string_view prefix) { - completeFlakeRef(getStore(), prefix); - }} - }); + expectArgs({.label = "flake-url", + .optional = true, + .handler = {&flakeUrl}, + .completer = {[&](size_t, std::string_view prefix) { + completeFlakeRef(getStore(), prefix); + }}}); } FlakeRef getFlakeRef() { - return parseFlakeRef(flakeUrl, absPath(".")); //FIXME + return parseFlakeRef(flakeUrl, absPath(".")); // FIXME } LockedFlake lockFlake() @@ -56,12 +52,8 @@ class FlakeCommand : virtual Args, public MixFlakeOptions } }; -struct CmdFlakeUpdate : FlakeCommand -{ - std::string description() override - { - return "update flake lock file"; - } +struct CmdFlakeUpdate : FlakeCommand { + std::string description() override { return "update flake lock file"; } CmdFlakeUpdate() { @@ -75,8 +67,8 @@ struct CmdFlakeUpdate : FlakeCommand std::string doc() override { return - #include "flake-update.md" - ; +#include "flake-update.md" + ; } void run(nix::ref store) override @@ -91,8 +83,7 @@ struct CmdFlakeUpdate : FlakeCommand } }; -struct CmdFlakeLock : FlakeCommand -{ +struct CmdFlakeLock : FlakeCommand { std::string description() override { return "create missing lock file entries"; @@ -107,8 +98,8 @@ struct CmdFlakeLock : FlakeCommand std::string doc() override { return - #include "flake-lock.md" - ; +#include "flake-lock.md" + ; } void run(nix::ref store) override @@ -122,8 +113,11 @@ struct CmdFlakeLock : FlakeCommand } }; -static void enumerateOutputs(EvalState & state, Value & vFlake, - std::function callback) +static void +enumerateOutputs(EvalState & state, Value & vFlake, + std::function + callback) { auto pos = vFlake.determinePos(noPos); state.forceAttrs(vFlake, pos); @@ -147,18 +141,14 @@ static void enumerateOutputs(EvalState & state, Value & vFlake, } } -struct CmdFlakeMetadata : FlakeCommand, MixJSON -{ - std::string description() override - { - return "show flake metadata"; - } +struct CmdFlakeMetadata : FlakeCommand, MixJSON { + std::string description() override { return "show flake metadata"; } std::string doc() override { return - #include "flake-metadata.md" - ; +#include "flake-metadata.md" + ; } void run(nix::ref store) override @@ -174,7 +164,8 @@ struct CmdFlakeMetadata : FlakeCommand, MixJSON j["original"] = fetchers::attrsToJSON(flake.originalRef.toAttrs()); j["resolvedUrl"] = flake.resolvedRef.to_string(); j["resolved"] = fetchers::attrsToJSON(flake.resolvedRef.toAttrs()); - j["url"] = flake.lockedRef.to_string(); // FIXME: rename to lockedUrl + j["url"] = + flake.lockedRef.to_string(); // FIXME: rename to lockedUrl j["locked"] = fetchers::attrsToJSON(flake.lockedRef.toAttrs()); if (auto rev = flake.lockedRef.input.getRev()) j["revision"] = rev->to_string(Base16, false); @@ -186,27 +177,21 @@ struct CmdFlakeMetadata : FlakeCommand, MixJSON j["locks"] = lockedFlake.lockFile.toJSON(); logger->cout("%s", j.dump()); } else { - logger->cout( - ANSI_BOLD "Resolved URL:" ANSI_NORMAL " %s", - flake.resolvedRef.to_string()); - logger->cout( - ANSI_BOLD "Locked URL:" ANSI_NORMAL " %s", - flake.lockedRef.to_string()); + logger->cout(ANSI_BOLD "Resolved URL:" ANSI_NORMAL " %s", + flake.resolvedRef.to_string()); + logger->cout(ANSI_BOLD "Locked URL:" ANSI_NORMAL " %s", + flake.lockedRef.to_string()); if (flake.description) - logger->cout( - ANSI_BOLD "Description:" ANSI_NORMAL " %s", - *flake.description); - logger->cout( - ANSI_BOLD "Path:" ANSI_NORMAL " %s", - store->printStorePath(flake.sourceInfo->storePath)); + logger->cout(ANSI_BOLD "Description:" ANSI_NORMAL " %s", + *flake.description); + logger->cout(ANSI_BOLD "Path:" ANSI_NORMAL " %s", + store->printStorePath(flake.sourceInfo->storePath)); if (auto rev = flake.lockedRef.input.getRev()) - logger->cout( - ANSI_BOLD "Revision:" ANSI_NORMAL " %s", - rev->to_string(Base16, false)); + logger->cout(ANSI_BOLD "Revision:" ANSI_NORMAL " %s", + rev->to_string(Base16, false)); if (auto revCount = flake.lockedRef.input.getRevCount()) - logger->cout( - ANSI_BOLD "Revisions:" ANSI_NORMAL " %s", - *revCount); + logger->cout(ANSI_BOLD "Revisions:" ANSI_NORMAL " %s", + *revCount); if (auto lastModified = flake.lockedRef.input.getLastModified()) logger->cout( ANSI_BOLD "Last modified:" ANSI_NORMAL " %s", @@ -216,25 +201,30 @@ struct CmdFlakeMetadata : FlakeCommand, MixJSON std::unordered_set> visited; - std::function recurse; + std::function + recurse; - recurse = [&](const Node & node, const std::string & prefix) - { + recurse = [&](const Node & node, const std::string & prefix) { for (const auto & [i, input] : enumerate(node.inputs)) { bool last = i + 1 == node.inputs.size(); if (auto lockedNode = std::get_if<0>(&input.second)) { logger->cout("%s" ANSI_BOLD "%s" ANSI_NORMAL ": %s", - prefix + (last ? treeLast : treeConn), input.first, - *lockedNode ? (*lockedNode)->lockedRef : flake.lockedRef); + prefix + (last ? treeLast : treeConn), + input.first, + *lockedNode ? (*lockedNode)->lockedRef + : flake.lockedRef); bool firstVisit = visited.insert(*lockedNode).second; - if (firstVisit) recurse(**lockedNode, prefix + (last ? treeNull : treeLine)); + if (firstVisit) + recurse(**lockedNode, + prefix + (last ? treeNull : treeLine)); } else if (auto follows = std::get_if<1>(&input.second)) { - logger->cout("%s" ANSI_BOLD "%s" ANSI_NORMAL " follows input '%s'", - prefix + (last ? treeLast : treeConn), input.first, - printInputPath(*follows)); + logger->cout("%s" ANSI_BOLD "%s" ANSI_NORMAL + " follows input '%s'", + prefix + (last ? treeLast : treeConn), + input.first, printInputPath(*follows)); } } }; @@ -245,8 +235,7 @@ struct CmdFlakeMetadata : FlakeCommand, MixJSON } }; -struct CmdFlakeInfo : CmdFlakeMetadata -{ +struct CmdFlakeInfo : CmdFlakeMetadata { void run(nix::ref store) override { warn("'nix flake info' is a deprecated alias for 'nix flake metadata'"); @@ -254,17 +243,14 @@ struct CmdFlakeInfo : CmdFlakeMetadata } }; -struct CmdFlakeCheck : FlakeCommand -{ +struct CmdFlakeCheck : FlakeCommand { bool build = true; CmdFlakeCheck() { - addFlag({ - .longName = "no-build", - .description = "Do not build checks.", - .handler = {&build, false} - }); + addFlag({.longName = "no-build", + .description = "Do not build checks.", + .handler = {&build, false}}); } std::string description() override @@ -275,8 +261,8 @@ struct CmdFlakeCheck : FlakeCommand std::string doc() override { return - #include "flake-check.md" - ; +#include "flake-check.md" + ; } void run(nix::ref store) override @@ -299,41 +285,43 @@ struct CmdFlakeCheck : FlakeCommand if (settings.keepGoing) { ignoreException(); hasErrors = true; - } - else + } else throw; } }; // FIXME: rewrite to use EvalCache. - auto resolve = [&] (PosIdx p) { - return state->positions[p]; - }; + auto resolve = [&](PosIdx p) { return state->positions[p]; }; - auto argHasName = [&] (Symbol arg, std::string_view expected) { + auto argHasName = [&](Symbol arg, std::string_view expected) { std::string_view name = state->symbols[arg]; - return - name == expected - || name == "_" - || (hasPrefix(name, "_") && name.substr(1) == expected); + return name == expected || name == "_" || + (hasPrefix(name, "_") && name.substr(1) == expected); }; - auto checkSystemName = [&](const std::string & system, const PosIdx pos) { + auto checkSystemName = [&](const std::string & system, + const PosIdx pos) { // FIXME: what's the format of "system"? if (system.find('-') == std::string::npos) - reportError(Error("'%s' is not a valid system type, at %s", system, resolve(pos))); + reportError(Error("'%s' is not a valid system type, at %s", + system, resolve(pos))); }; - auto checkDerivation = [&](const std::string & attrPath, Value & v, const PosIdx pos) -> std::optional { + auto checkDerivation = + [&](const std::string & attrPath, Value & v, + const PosIdx pos) -> std::optional { try { auto drvInfo = getDerivation(*state, v, false); if (!drvInfo) - throw Error("flake attribute '%s' is not a derivation", attrPath); + throw Error("flake attribute '%s' is not a derivation", + attrPath); // FIXME: check meta attributes return drvInfo->queryDrvPath(); } catch (Error & e) { - e.addTrace(resolve(pos), hintfmt("while checking the derivation '%s'", attrPath)); + e.addTrace( + resolve(pos), + hintfmt("while checking the derivation '%s'", attrPath)); reportError(e); } return std::nullopt; @@ -341,48 +329,57 @@ struct CmdFlakeCheck : FlakeCommand std::vector drvPaths; - auto checkApp = [&](const std::string & attrPath, Value & v, const PosIdx pos) { + auto checkApp = [&](const std::string & attrPath, Value & v, + const PosIdx pos) { try { - #if 0 +#if 0 // FIXME auto app = App(*state, v); for (auto & i : app.context) { auto [drvPathS, outputName] = decodeContext(i); store->parseStorePath(drvPathS); } - #endif +#endif } catch (Error & e) { - e.addTrace(resolve(pos), hintfmt("while checking the app definition '%s'", attrPath)); + e.addTrace(resolve(pos), + hintfmt("while checking the app definition '%s'", + attrPath)); reportError(e); } }; - auto checkOverlay = [&](const std::string & attrPath, Value & v, const PosIdx pos) { + auto checkOverlay = [&](const std::string & attrPath, Value & v, + const PosIdx pos) { try { state->forceValue(v, pos); - if (!v.isLambda() - || v.lambda.fun->hasFormals() - || !argHasName(v.lambda.fun->arg, "final")) - throw Error("overlay does not take an argument named 'final'"); + if (!v.isLambda() || v.lambda.fun->hasFormals() || + !argHasName(v.lambda.fun->arg, "final")) + throw Error( + "overlay does not take an argument named 'final'"); auto body = dynamic_cast(v.lambda.fun->body); - if (!body - || body->hasFormals() - || !argHasName(body->arg, "prev")) - throw Error("overlay does not take an argument named 'prev'"); + if (!body || body->hasFormals() || + !argHasName(body->arg, "prev")) + throw Error( + "overlay does not take an argument named 'prev'"); // FIXME: if we have a 'nixpkgs' input, use it to // evaluate the overlay. } catch (Error & e) { - e.addTrace(resolve(pos), hintfmt("while checking the overlay '%s'", attrPath)); + e.addTrace( + resolve(pos), + hintfmt("while checking the overlay '%s'", attrPath)); reportError(e); } }; - auto checkModule = [&](const std::string & attrPath, Value & v, const PosIdx pos) { + auto checkModule = [&](const std::string & attrPath, Value & v, + const PosIdx pos) { try { state->forceValue(v, pos); if (v.isLambda()) { - if (!v.lambda.fun->hasFormals() || !v.lambda.fun->formals->ellipsis) - throw Error("module must match an open attribute set ('{ config, ... }')"); + if (!v.lambda.fun->hasFormals() || + !v.lambda.fun->formals->ellipsis) + throw Error("module must match an open attribute set " + "('{ config, ... }')"); } else if (v.type() == nAttrs) { for (auto & attr : *v.attrs) try { @@ -390,102 +387,136 @@ struct CmdFlakeCheck : FlakeCommand } catch (Error & e) { e.addTrace( state->positions[attr.pos], - hintfmt("while evaluating the option '%s'", state->symbols[attr.name])); + hintfmt("while evaluating the option '%s'", + state->symbols[attr.name])); throw; } } else - throw Error("module must be a function or an attribute set"); + throw Error( + "module must be a function or an attribute set"); // FIXME: if we have a 'nixpkgs' input, use it to // check the module. } catch (Error & e) { - e.addTrace(resolve(pos), hintfmt("while checking the NixOS module '%s'", attrPath)); + e.addTrace( + resolve(pos), + hintfmt("while checking the NixOS module '%s'", attrPath)); reportError(e); } }; - std::function checkHydraJobs; + std::function + checkHydraJobs; - checkHydraJobs = [&](const std::string & attrPath, Value & v, const PosIdx pos) { + checkHydraJobs = [&](const std::string & attrPath, Value & v, + const PosIdx pos) { try { state->forceAttrs(v, pos); if (state->isDerivation(v)) - throw Error("jobset should not be a derivation at top-level"); + throw Error( + "jobset should not be a derivation at top-level"); for (auto & attr : *v.attrs) { state->forceAttrs(*attr.value, attr.pos); - auto attrPath2 = concatStrings(attrPath, ".", state->symbols[attr.name]); + auto attrPath2 = + concatStrings(attrPath, ".", state->symbols[attr.name]); if (state->isDerivation(*attr.value)) { Activity act(*logger, lvlChatty, actUnknown, - fmt("checking Hydra job '%s'", attrPath2)); + fmt("checking Hydra job '%s'", attrPath2)); checkDerivation(attrPath2, *attr.value, attr.pos); } else checkHydraJobs(attrPath2, *attr.value, attr.pos); } } catch (Error & e) { - e.addTrace(resolve(pos), hintfmt("while checking the Hydra jobset '%s'", attrPath)); + e.addTrace( + resolve(pos), + hintfmt("while checking the Hydra jobset '%s'", attrPath)); reportError(e); } }; - auto checkNixOSConfiguration = [&](const std::string & attrPath, Value & v, const PosIdx pos) { + auto checkNixOSConfiguration = [&](const std::string & attrPath, + Value & v, const PosIdx pos) { try { - Activity act(*logger, lvlChatty, actUnknown, + Activity act( + *logger, lvlChatty, actUnknown, fmt("checking NixOS configuration '%s'", attrPath)); Bindings & bindings(*state->allocBindings(0)); - auto vToplevel = findAlongAttrPath(*state, "config.system.build.toplevel", bindings, v).first; + auto vToplevel = + findAlongAttrPath(*state, "config.system.build.toplevel", + bindings, v) + .first; state->forceAttrs(*vToplevel, pos); if (!state->isDerivation(*vToplevel)) - throw Error("attribute 'config.system.build.toplevel' is not a derivation"); + throw Error("attribute 'config.system.build.toplevel' is " + "not a derivation"); } catch (Error & e) { - e.addTrace(resolve(pos), hintfmt("while checking the NixOS configuration '%s'", attrPath)); + e.addTrace( + resolve(pos), + hintfmt("while checking the NixOS configuration '%s'", + attrPath)); reportError(e); } }; - auto checkTemplate = [&](const std::string & attrPath, Value & v, const PosIdx pos) { + auto checkTemplate = [&](const std::string & attrPath, Value & v, + const PosIdx pos) { try { Activity act(*logger, lvlChatty, actUnknown, - fmt("checking template '%s'", attrPath)); + fmt("checking template '%s'", attrPath)); state->forceAttrs(v, pos); if (auto attr = v.attrs->get(state->symbols.create("path"))) { if (attr->name == state->symbols.create("path")) { PathSet context; - auto path = state->coerceToPath(attr->pos, *attr->value, context); + auto path = state->coerceToPath(attr->pos, *attr->value, + context); if (!store->isInStore(path)) - throw Error("template '%s' has a bad 'path' attribute"); + throw Error( + "template '%s' has a bad 'path' attribute"); // TODO: recursively check the flake in 'path'. } } else - throw Error("template '%s' lacks attribute 'path'", attrPath); + throw Error("template '%s' lacks attribute 'path'", + attrPath); - if (auto attr = v.attrs->get(state->symbols.create("description"))) + if (auto attr = + v.attrs->get(state->symbols.create("description"))) state->forceStringNoCtx(*attr->value, attr->pos); else - throw Error("template '%s' lacks attribute 'description'", attrPath); + throw Error("template '%s' lacks attribute 'description'", + attrPath); for (auto & attr : *v.attrs) { std::string_view name(state->symbols[attr.name]); - if (name != "path" && name != "description" && name != "welcomeText") - throw Error("template '%s' has unsupported attribute '%s'", attrPath, name); + if (name != "path" && name != "description" && + name != "welcomeText") + throw Error( + "template '%s' has unsupported attribute '%s'", + attrPath, name); } } catch (Error & e) { - e.addTrace(resolve(pos), hintfmt("while checking the template '%s'", attrPath)); + e.addTrace( + resolve(pos), + hintfmt("while checking the template '%s'", attrPath)); reportError(e); } }; - auto checkBundler = [&](const std::string & attrPath, Value & v, const PosIdx pos) { + auto checkBundler = [&](const std::string & attrPath, Value & v, + const PosIdx pos) { try { state->forceValue(v, pos); if (!v.isLambda()) throw Error("bundler must be a function"); // TODO: check types of inputs/outputs? } catch (Error & e) { - e.addTrace(resolve(pos), hintfmt("while checking the template '%s'", attrPath)); + e.addTrace( + resolve(pos), + hintfmt("while checking the template '%s'", attrPath)); reportError(e); } }; @@ -496,41 +527,51 @@ struct CmdFlakeCheck : FlakeCommand auto vFlake = state->allocValue(); flake::callFlake(*state, flake, *vFlake); - enumerateOutputs(*state, - *vFlake, - [&](const std::string & name, Value & vOutput, const PosIdx pos) { + enumerateOutputs( + *state, *vFlake, + [&](const std::string & name, Value & vOutput, + const PosIdx pos) { Activity act(*logger, lvlChatty, actUnknown, - fmt("checking flake output '%s'", name)); + fmt("checking flake output '%s'", name)); try { - evalSettings.enableImportFromDerivation.setDefault(name != "hydraJobs"); + evalSettings.enableImportFromDerivation.setDefault( + name != "hydraJobs"); state->forceValue(vOutput, pos); std::string_view replacement = - name == "defaultPackage" ? "packages..default" : - name == "defaultApp" ? "apps..default" : - name == "defaultTemplate" ? "templates.default" : - name == "defaultBundler" ? "bundlers..default" : - name == "overlay" ? "overlays.default" : - name == "devShell" ? "devShells..default" : - name == "nixosModule" ? "nixosModules.default" : - ""; + name == "defaultPackage" + ? "packages..default" + : name == "defaultApp" ? "apps..default" + : name == "defaultTemplate" ? "templates.default" + : name == "defaultBundler" + ? "bundlers..default" + : name == "overlay" ? "overlays.default" + : name == "devShell" ? "devShells..default" + : name == "nixosModule" ? "nixosModules.default" + : ""; if (replacement != "") - warn("flake output attribute '%s' is deprecated; use '%s' instead", name, replacement); + warn("flake output attribute '%s' is deprecated; " + "use '%s' instead", + name, replacement); if (name == "checks") { state->forceAttrs(vOutput, pos); for (auto & attr : *vOutput.attrs) { - const auto & attr_name = state->symbols[attr.name]; + const auto & attr_name = + state->symbols[attr.name]; checkSystemName(attr_name, attr.pos); state->forceAttrs(*attr.value, attr.pos); for (auto & attr2 : *attr.value->attrs) { auto drvPath = checkDerivation( - fmt("%s.%s.%s", name, attr_name, state->symbols[attr2.name]), + fmt("%s.%s.%s", name, attr_name, + state->symbols[attr2.name]), *attr2.value, attr2.pos); - if (drvPath && attr_name == settings.thisSystem.get()) - drvPaths.push_back(DerivedPath::Built{*drvPath}); + if (drvPath && + attr_name == settings.thisSystem.get()) + drvPaths.push_back( + DerivedPath::Built{*drvPath}); } } } @@ -538,23 +579,25 @@ struct CmdFlakeCheck : FlakeCommand else if (name == "formatter") { state->forceAttrs(vOutput, pos); for (auto & attr : *vOutput.attrs) { - const auto & attr_name = state->symbols[attr.name]; + const auto & attr_name = + state->symbols[attr.name]; checkSystemName(attr_name, attr.pos); - checkApp( - fmt("%s.%s", name, attr_name), - *attr.value, attr.pos); + checkApp(fmt("%s.%s", name, attr_name), + *attr.value, attr.pos); } } else if (name == "packages" || name == "devShells") { state->forceAttrs(vOutput, pos); for (auto & attr : *vOutput.attrs) { - const auto & attr_name = state->symbols[attr.name]; + const auto & attr_name = + state->symbols[attr.name]; checkSystemName(attr_name, attr.pos); state->forceAttrs(*attr.value, attr.pos); for (auto & attr2 : *attr.value->attrs) checkDerivation( - fmt("%s.%s.%s", name, attr_name, state->symbols[attr2.name]), + fmt("%s.%s.%s", name, attr_name, + state->symbols[attr2.name]), *attr2.value, attr2.pos); } } @@ -562,42 +605,45 @@ struct CmdFlakeCheck : FlakeCommand else if (name == "apps") { state->forceAttrs(vOutput, pos); for (auto & attr : *vOutput.attrs) { - const auto & attr_name = state->symbols[attr.name]; + const auto & attr_name = + state->symbols[attr.name]; checkSystemName(attr_name, attr.pos); state->forceAttrs(*attr.value, attr.pos); for (auto & attr2 : *attr.value->attrs) - checkApp( - fmt("%s.%s.%s", name, attr_name, state->symbols[attr2.name]), - *attr2.value, attr2.pos); + checkApp(fmt("%s.%s.%s", name, attr_name, + state->symbols[attr2.name]), + *attr2.value, attr2.pos); } } - else if (name == "defaultPackage" || name == "devShell") { + else if (name == "defaultPackage" || + name == "devShell") { state->forceAttrs(vOutput, pos); for (auto & attr : *vOutput.attrs) { - const auto & attr_name = state->symbols[attr.name]; + const auto & attr_name = + state->symbols[attr.name]; checkSystemName(attr_name, attr.pos); - checkDerivation( - fmt("%s.%s", name, attr_name), - *attr.value, attr.pos); + checkDerivation(fmt("%s.%s", name, attr_name), + *attr.value, attr.pos); } } else if (name == "defaultApp") { state->forceAttrs(vOutput, pos); for (auto & attr : *vOutput.attrs) { - const auto & attr_name = state->symbols[attr.name]; + const auto & attr_name = + state->symbols[attr.name]; checkSystemName(attr_name, attr.pos); - checkApp( - fmt("%s.%s", name, attr_name), - *attr.value, attr.pos); + checkApp(fmt("%s.%s", name, attr_name), + *attr.value, attr.pos); } } else if (name == "legacyPackages") { state->forceAttrs(vOutput, pos); for (auto & attr : *vOutput.attrs) { - checkSystemName(state->symbols[attr.name], attr.pos); + checkSystemName(state->symbols[attr.name], + attr.pos); // FIXME: do getDerivations? } } @@ -608,8 +654,9 @@ struct CmdFlakeCheck : FlakeCommand else if (name == "overlays") { state->forceAttrs(vOutput, pos); for (auto & attr : *vOutput.attrs) - checkOverlay(fmt("%s.%s", name, state->symbols[attr.name]), - *attr.value, attr.pos); + checkOverlay(fmt("%s.%s", name, + state->symbols[attr.name]), + *attr.value, attr.pos); } else if (name == "nixosModule") @@ -618,14 +665,17 @@ struct CmdFlakeCheck : FlakeCommand else if (name == "nixosModules") { state->forceAttrs(vOutput, pos); for (auto & attr : *vOutput.attrs) - checkModule(fmt("%s.%s", name, state->symbols[attr.name]), - *attr.value, attr.pos); + checkModule(fmt("%s.%s", name, + state->symbols[attr.name]), + *attr.value, attr.pos); } else if (name == "nixosConfigurations") { state->forceAttrs(vOutput, pos); for (auto & attr : *vOutput.attrs) - checkNixOSConfiguration(fmt("%s.%s", name, state->symbols[attr.name]), + checkNixOSConfiguration( + fmt("%s.%s", name, + state->symbols[attr.name]), *attr.value, attr.pos); } @@ -638,30 +688,33 @@ struct CmdFlakeCheck : FlakeCommand else if (name == "templates") { state->forceAttrs(vOutput, pos); for (auto & attr : *vOutput.attrs) - checkTemplate(fmt("%s.%s", name, state->symbols[attr.name]), - *attr.value, attr.pos); + checkTemplate(fmt("%s.%s", name, + state->symbols[attr.name]), + *attr.value, attr.pos); } else if (name == "defaultBundler") { state->forceAttrs(vOutput, pos); for (auto & attr : *vOutput.attrs) { - const auto & attr_name = state->symbols[attr.name]; + const auto & attr_name = + state->symbols[attr.name]; checkSystemName(attr_name, attr.pos); - checkBundler( - fmt("%s.%s", name, attr_name), - *attr.value, attr.pos); + checkBundler(fmt("%s.%s", name, attr_name), + *attr.value, attr.pos); } } else if (name == "bundlers") { state->forceAttrs(vOutput, pos); for (auto & attr : *vOutput.attrs) { - const auto & attr_name = state->symbols[attr.name]; + const auto & attr_name = + state->symbols[attr.name]; checkSystemName(attr_name, attr.pos); state->forceAttrs(*attr.value, attr.pos); for (auto & attr2 : *attr.value->attrs) { checkBundler( - fmt("%s.%s.%s", name, attr_name, state->symbols[attr2.name]), + fmt("%s.%s.%s", name, attr_name, + state->symbols[attr2.name]), *attr2.value, attr2.pos); } } @@ -671,7 +724,9 @@ struct CmdFlakeCheck : FlakeCommand warn("unknown flake output '%s'", name); } catch (Error & e) { - e.addTrace(resolve(pos), hintfmt("while checking flake output '%s'", name)); + e.addTrace( + resolve(pos), + hintfmt("while checking flake output '%s'", name)); reportError(e); } }); @@ -687,32 +742,28 @@ struct CmdFlakeCheck : FlakeCommand }; static Strings defaultTemplateAttrPathsPrefixes{"templates."}; -static Strings defaultTemplateAttrPaths = {"templates.default", "defaultTemplate"}; +static Strings defaultTemplateAttrPaths = {"templates.default", + "defaultTemplate"}; -struct CmdFlakeInitCommon : virtual Args, EvalCommand -{ +struct CmdFlakeInitCommon : virtual Args, EvalCommand { std::string templateUrl = "templates"; Path destDir; - const LockFlags lockFlags{ .writeLockFile = false }; + const LockFlags lockFlags{.writeLockFile = false}; CmdFlakeInitCommon() { - addFlag({ - .longName = "template", - .shortName = 't', - .description = "The template to use.", - .labels = {"template"}, - .handler = {&templateUrl}, - .completer = {[&](size_t, std::string_view prefix) { - completeFlakeRefWithFragment( - getEvalState(), - lockFlags, - defaultTemplateAttrPathsPrefixes, - defaultTemplateAttrPaths, - prefix); - }} - }); + addFlag({.longName = "template", + .shortName = 't', + .description = "The template to use.", + .labels = {"template"}, + .handler = {&templateUrl}, + .completer = {[&](size_t, std::string_view prefix) { + completeFlakeRefWithFragment( + getEvalState(), lockFlags, + defaultTemplateAttrPathsPrefixes, + defaultTemplateAttrPaths, prefix); + }}}); } void run(nix::ref store) override @@ -721,13 +772,13 @@ struct CmdFlakeInitCommon : virtual Args, EvalCommand auto evalState = getEvalState(); - auto [templateFlakeRef, templateName] = parseFlakeRefWithFragment(templateUrl, absPath(".")); + auto [templateFlakeRef, templateName] = + parseFlakeRefWithFragment(templateUrl, absPath(".")); - auto installable = InstallableFlake(nullptr, - evalState, std::move(templateFlakeRef), templateName, DefaultOutputs(), - defaultTemplateAttrPaths, - defaultTemplateAttrPathsPrefixes, - lockFlags); + auto installable = InstallableFlake( + nullptr, evalState, std::move(templateFlakeRef), templateName, + DefaultOutputs(), defaultTemplateAttrPaths, + defaultTemplateAttrPathsPrefixes, lockFlags); auto cursor = installable.getCursor(*evalState); @@ -743,8 +794,7 @@ struct CmdFlakeInitCommon : virtual Args, EvalCommand std::vector files; std::function copyDir; - copyDir = [&](const Path & from, const Path & to) - { + copyDir = [&](const Path & from, const Path & to) { createDirs(to); for (auto & entry : readDirectory(from)) { @@ -758,19 +808,21 @@ struct CmdFlakeInitCommon : virtual Args, EvalCommand if (pathExists(to2)) { auto contents2 = readFile(to2); if (contents != contents2) - throw Error("refusing to overwrite existing file '%s'", to2); + throw Error( + "refusing to overwrite existing file '%s'", + to2); } else writeFile(to2, contents); - } - else if (S_ISLNK(st.st_mode)) { + } else if (S_ISLNK(st.st_mode)) { auto target = readLink(from2); if (pathExists(to2)) { if (readLink(to2) != target) - throw Error("refusing to overwrite existing symlink '%s'", to2); + throw Error( + "refusing to overwrite existing symlink '%s'", + to2); } else - createSymlink(target, to2); - } - else + createSymlink(target, to2); + } else throw Error("file '%s' has unsupported type", from2); files.push_back(to2); notice("wrote: %s", to2); @@ -780,8 +832,10 @@ struct CmdFlakeInitCommon : virtual Args, EvalCommand copyDir(templateDir, flakeDir); if (pathExists(flakeDir + "/.git")) { - Strings args = { "-C", flakeDir, "add", "--intent-to-add", "--force", "--" }; - for (auto & s : files) args.push_back(s); + Strings args = {"-C", flakeDir, "add", "--intent-to-add", + "--force", "--"}; + for (auto & s : files) + args.push_back(s); runProgram("git", true, args); } auto welcomeText = cursor->maybeGetAttr("welcomeText"); @@ -792,8 +846,7 @@ struct CmdFlakeInitCommon : virtual Args, EvalCommand } }; -struct CmdFlakeInit : CmdFlakeInitCommon -{ +struct CmdFlakeInit : CmdFlakeInitCommon { std::string description() override { return "create a flake in the current directory from a template"; @@ -802,18 +855,14 @@ struct CmdFlakeInit : CmdFlakeInitCommon std::string doc() override { return - #include "flake-init.md" - ; +#include "flake-init.md" + ; } - CmdFlakeInit() - { - destDir = "."; - } + CmdFlakeInit() { destDir = "."; } }; -struct CmdFlakeNew : CmdFlakeInitCommon -{ +struct CmdFlakeNew : CmdFlakeInitCommon { std::string description() override { return "create a flake in the specified directory from a template"; @@ -822,45 +871,37 @@ struct CmdFlakeNew : CmdFlakeInitCommon std::string doc() override { return - #include "flake-new.md" - ; +#include "flake-new.md" + ; } CmdFlakeNew() { - expectArgs({ - .label = "dest-dir", - .handler = {&destDir}, - .completer = completePath - }); + expectArgs({.label = "dest-dir", + .handler = {&destDir}, + .completer = completePath}); } }; -struct CmdFlakeClone : FlakeCommand -{ +struct CmdFlakeClone : FlakeCommand { Path destDir; - std::string description() override - { - return "clone flake repository"; - } + std::string description() override { return "clone flake repository"; } std::string doc() override { return - #include "flake-clone.md" - ; +#include "flake-clone.md" + ; } CmdFlakeClone() { - addFlag({ - .longName = "dest", - .shortName = 'f', - .description = "Clone the flake to path *dest*.", - .labels = {"path"}, - .handler = {&destDir} - }); + addFlag({.longName = "dest", + .shortName = 'f', + .description = "Clone the flake to path *dest*.", + .labels = {"path"}, + .handler = {&destDir}}); } void run(nix::ref store) override @@ -872,18 +913,15 @@ struct CmdFlakeClone : FlakeCommand } }; -struct CmdFlakeArchive : FlakeCommand, MixJSON, MixDryRun -{ +struct CmdFlakeArchive : FlakeCommand, MixJSON, MixDryRun { std::string dstUri; CmdFlakeArchive() { - addFlag({ - .longName = "to", - .description = "URI of the destination Nix store", - .labels = {"store-uri"}, - .handler = {&dstUri} - }); + addFlag({.longName = "to", + .description = "URI of the destination Nix store", + .labels = {"store-uri"}, + .handler = {&dstUri}}); } std::string description() override @@ -894,36 +932,44 @@ struct CmdFlakeArchive : FlakeCommand, MixJSON, MixDryRun std::string doc() override { return - #include "flake-archive.md" - ; +#include "flake-archive.md" + ; } void run(nix::ref store) override { auto flake = lockFlake(); - auto jsonRoot = json ? std::optional(std::cout) : std::nullopt; + auto jsonRoot = + json ? std::optional(std::cout) : std::nullopt; StorePathSet sources; sources.insert(flake.flake.sourceInfo->storePath); if (jsonRoot) - jsonRoot->attr("path", store->printStorePath(flake.flake.sourceInfo->storePath)); + jsonRoot->attr("path", store->printStorePath( + flake.flake.sourceInfo->storePath)); // FIXME: use graph output, handle cycles. - std::function & jsonObj)> traverse; - traverse = [&](const Node & node, std::optional & jsonObj) - { - auto jsonObj2 = jsonObj ? jsonObj->object("inputs") : std::optional(); + std::function & jsonObj)> + traverse; + traverse = [&](const Node & node, std::optional & jsonObj) { + auto jsonObj2 = jsonObj ? jsonObj->object("inputs") + : std::optional(); for (auto & [inputName, input] : node.inputs) { if (auto inputNode = std::get_if<0>(&input)) { - auto jsonObj3 = jsonObj2 ? jsonObj2->object(inputName) : std::optional(); + auto jsonObj3 = jsonObj2 ? jsonObj2->object(inputName) + : std::optional(); auto storePath = - dryRun - ? (*inputNode)->lockedRef.input.computeStorePath(*store) - : (*inputNode)->lockedRef.input.fetch(store).first.storePath; + dryRun ? (*inputNode) + ->lockedRef.input.computeStorePath(*store) + : (*inputNode) + ->lockedRef.input.fetch(store) + .first.storePath; if (jsonObj3) - jsonObj3->attr("path", store->printStorePath(storePath)); + jsonObj3->attr("path", + store->printStorePath(storePath)); sources.insert(std::move(storePath)); traverse(**inputNode, jsonObj3); } @@ -933,23 +979,22 @@ struct CmdFlakeArchive : FlakeCommand, MixJSON, MixDryRun traverse(*flake.lockFile.root, jsonRoot); if (!dryRun && !dstUri.empty()) { - ref dstStore = dstUri.empty() ? openStore() : openStore(dstUri); + ref dstStore = + dstUri.empty() ? openStore() : openStore(dstUri); copyPaths(*store, *dstStore, sources); } } }; -struct CmdFlakeShow : FlakeCommand, MixJSON -{ +struct CmdFlakeShow : FlakeCommand, MixJSON { bool showLegacy = false; CmdFlakeShow() { - addFlag({ - .longName = "legacy", - .description = "Show the contents of the `legacyPackages` output.", - .handler = {&showLegacy, true} - }); + addFlag( + {.longName = "legacy", + .description = "Show the contents of the `legacyPackages` output.", + .handler = {&showLegacy, true}}); } std::string description() override @@ -960,8 +1005,8 @@ struct CmdFlakeShow : FlakeCommand, MixJSON std::string doc() override { return - #include "flake-show.md" - ; +#include "flake-show.md" + ; } void run(nix::ref store) override @@ -971,29 +1016,26 @@ struct CmdFlakeShow : FlakeCommand, MixJSON auto state = getEvalState(); auto flake = std::make_shared(lockFlake()); - std::function & attrPath, - const std::string & headerPrefix, - const std::string & nextPrefix)> visit; - - visit = [&]( - eval_cache::AttrCursor & visitor, - const std::vector & attrPath, - const std::string & headerPrefix, - const std::string & nextPrefix) - -> nlohmann::json - { + std::function & attrPath, + const std::string & headerPrefix, + const std::string & nextPrefix)> + visit; + + visit = [&](eval_cache::AttrCursor & visitor, + const std::vector & attrPath, + const std::string & headerPrefix, + const std::string & nextPrefix) -> nlohmann::json { auto j = nlohmann::json::object(); auto attrPathS = state->symbols.resolve(attrPath); - Activity act(*logger, lvlInfo, actUnknown, + Activity act( + *logger, lvlInfo, actUnknown, fmt("evaluating '%s'", concatStringsSep(".", attrPathS))); try { - auto recurse = [&]() - { + auto recurse = [&]() { if (!json) logger->cout("%s", headerPrefix); auto attrs = visitor.getAttrs(); @@ -1003,20 +1045,25 @@ struct CmdFlakeShow : FlakeCommand, MixJSON auto visitor2 = visitor.getAttr(attrName); auto attrPath2(attrPath); attrPath2.push_back(attr); - auto j2 = visit(*visitor2, attrPath2, - fmt(ANSI_GREEN "%s%s" ANSI_NORMAL ANSI_BOLD "%s" ANSI_NORMAL, nextPrefix, last ? treeLast : treeConn, attrName), - nextPrefix + (last ? treeNull : treeLine)); - if (json) j.emplace(attrName, std::move(j2)); + auto j2 = + visit(*visitor2, attrPath2, + fmt(ANSI_GREEN "%s%s" ANSI_NORMAL ANSI_BOLD + "%s" ANSI_NORMAL, + nextPrefix, last ? treeLast : treeConn, + attrName), + nextPrefix + (last ? treeNull : treeLine)); + if (json) + j.emplace(attrName, std::move(j2)); } }; - auto showDerivation = [&]() - { + auto showDerivation = [&]() { auto name = visitor.getAttr(state->sName)->getString(); if (json) { std::optional description; if (auto aMeta = visitor.maybeGetAttr(state->sMeta)) { - if (auto aDescription = aMeta->maybeGetAttr(state->sDescription)) + if (auto aDescription = + aMeta->maybeGetAttr(state->sDescription)) description = aDescription->getString(); } j.emplace("type", "derivation"); @@ -1024,42 +1071,47 @@ struct CmdFlakeShow : FlakeCommand, MixJSON if (description) j.emplace("description", *description); } else { - logger->cout("%s: %s '%s'", - headerPrefix, - attrPath.size() == 2 && attrPathS[0] == "devShell" ? "development environment" : - attrPath.size() >= 2 && attrPathS[0] == "devShells" ? "development environment" : - attrPath.size() == 3 && attrPathS[0] == "checks" ? "derivation" : - attrPath.size() >= 1 && attrPathS[0] == "hydraJobs" ? "derivation" : - "package", + logger->cout( + "%s: %s '%s'", headerPrefix, + attrPath.size() == 2 && attrPathS[0] == "devShell" + ? "development environment" + : attrPath.size() >= 2 && + attrPathS[0] == "devShells" + ? "development environment" + : attrPath.size() == 3 && attrPathS[0] == "checks" + ? "derivation" + : attrPath.size() >= 1 && + attrPathS[0] == "hydraJobs" + ? "derivation" + : "package", name); } }; - if (attrPath.size() == 0 - || (attrPath.size() == 1 && ( - attrPathS[0] == "defaultPackage" - || attrPathS[0] == "devShell" - || attrPathS[0] == "formatter" - || attrPathS[0] == "nixosConfigurations" - || attrPathS[0] == "nixosModules" - || attrPathS[0] == "defaultApp" - || attrPathS[0] == "templates" - || attrPathS[0] == "overlays")) - || ((attrPath.size() == 1 || attrPath.size() == 2) - && (attrPathS[0] == "checks" - || attrPathS[0] == "packages" - || attrPathS[0] == "devShells" - || attrPathS[0] == "apps")) - ) - { + if (attrPath.size() == 0 || + (attrPath.size() == 1 && + (attrPathS[0] == "defaultPackage" || + attrPathS[0] == "devShell" || + attrPathS[0] == "formatter" || + attrPathS[0] == "nixosConfigurations" || + attrPathS[0] == "nixosModules" || + attrPathS[0] == "defaultApp" || + attrPathS[0] == "templates" || + attrPathS[0] == "overlays")) || + ((attrPath.size() == 1 || attrPath.size() == 2) && + (attrPathS[0] == "checks" || attrPathS[0] == "packages" || + attrPathS[0] == "devShells" || attrPathS[0] == "apps"))) { recurse(); } - else if ( - (attrPath.size() == 2 && (attrPathS[0] == "defaultPackage" || attrPathS[0] == "devShell" || attrPathS[0] == "formatter")) - || (attrPath.size() == 3 && (attrPathS[0] == "checks" || attrPathS[0] == "packages" || attrPathS[0] == "devShells")) - ) - { + else if ((attrPath.size() == 2 && + (attrPathS[0] == "defaultPackage" || + attrPathS[0] == "devShell" || + attrPathS[0] == "formatter")) || + (attrPath.size() == 3 && + (attrPathS[0] == "checks" || + attrPathS[0] == "packages" || + attrPathS[0] == "devShells"))) { if (visitor.isDerivation()) showDerivation(); else @@ -1073,14 +1125,20 @@ struct CmdFlakeShow : FlakeCommand, MixJSON recurse(); } - else if (attrPath.size() > 0 && attrPathS[0] == "legacyPackages") { + else if (attrPath.size() > 0 && + attrPathS[0] == "legacyPackages") { if (attrPath.size() == 1) recurse(); - else if (!showLegacy){ + else if (!showLegacy) { if (!json) - logger->cout(fmt("%s " ANSI_WARNING "omitted" ANSI_NORMAL " (use '--legacy' to show)", headerPrefix)); + logger->cout(fmt("%s " ANSI_WARNING + "omitted" ANSI_NORMAL + " (use '--legacy' to show)", + headerPrefix)); else { - logger->warn(fmt("%s omitted (use '--legacy' to show)", concatStringsSep(".", attrPathS))); + logger->warn( + fmt("%s omitted (use '--legacy' to show)", + concatStringsSep(".", attrPathS))); } } else { if (visitor.isDerivation()) @@ -1091,10 +1149,9 @@ struct CmdFlakeShow : FlakeCommand, MixJSON } } - else if ( - (attrPath.size() == 2 && attrPathS[0] == "defaultApp") || - (attrPath.size() == 3 && attrPathS[0] == "apps")) - { + else if ((attrPath.size() == 2 && + attrPathS[0] == "defaultApp") || + (attrPath.size() == 3 && attrPathS[0] == "apps")) { auto aType = visitor.maybeGetAttr("type"); if (!aType || aType->getString() != "app") throw EvalError("not an app definition"); @@ -1105,31 +1162,44 @@ struct CmdFlakeShow : FlakeCommand, MixJSON } } - else if ( - (attrPath.size() == 1 && attrPathS[0] == "defaultTemplate") || - (attrPath.size() == 2 && attrPathS[0] == "templates")) - { - auto description = visitor.getAttr("description")->getString(); + else if ((attrPath.size() == 1 && + attrPathS[0] == "defaultTemplate") || + (attrPath.size() == 2 && + attrPathS[0] == "templates")) { + auto description = + visitor.getAttr("description")->getString(); if (json) { j.emplace("type", "template"); j.emplace("description", description); } else { - logger->cout("%s: template: " ANSI_BOLD "%s" ANSI_NORMAL, headerPrefix, description); + logger->cout("%s: template: " ANSI_BOLD + "%s" ANSI_NORMAL, + headerPrefix, description); } } else { auto [type, description] = - (attrPath.size() == 1 && attrPathS[0] == "overlay") - || (attrPath.size() == 2 && attrPathS[0] == "overlays") ? std::make_pair("nixpkgs-overlay", "Nixpkgs overlay") : - attrPath.size() == 2 && attrPathS[0] == "nixosConfigurations" ? std::make_pair("nixos-configuration", "NixOS configuration") : - (attrPath.size() == 1 && attrPathS[0] == "nixosModule") - || (attrPath.size() == 2 && attrPathS[0] == "nixosModules") ? std::make_pair("nixos-module", "NixOS module") : - std::make_pair("unknown", "unknown"); + (attrPath.size() == 1 && attrPathS[0] == "overlay") || + (attrPath.size() == 2 && + attrPathS[0] == "overlays") + ? std::make_pair("nixpkgs-overlay", + "Nixpkgs overlay") + : attrPath.size() == 2 && + attrPathS[0] == "nixosConfigurations" + ? std::make_pair("nixos-configuration", + "NixOS configuration") + : (attrPath.size() == 1 && + attrPathS[0] == "nixosModule") || + (attrPath.size() == 2 && + attrPathS[0] == "nixosModules") + ? std::make_pair("nixos-module", "NixOS module") + : std::make_pair("unknown", "unknown"); if (json) { j.emplace("type", type); } else { - logger->cout("%s: " ANSI_WARNING "%s" ANSI_NORMAL, headerPrefix, description); + logger->cout("%s: " ANSI_WARNING "%s" ANSI_NORMAL, + headerPrefix, description); } } } catch (EvalError & e) { @@ -1142,28 +1212,28 @@ struct CmdFlakeShow : FlakeCommand, MixJSON auto cache = openEvalCache(*state, flake); - auto j = visit(*cache->getRoot(), {}, fmt(ANSI_BOLD "%s" ANSI_NORMAL, flake->flake.lockedRef), ""); + auto j = + visit(*cache->getRoot(), {}, + fmt(ANSI_BOLD "%s" ANSI_NORMAL, flake->flake.lockedRef), ""); if (json) logger->cout("%s", j.dump()); } }; -struct CmdFlakePrefetch : FlakeCommand, MixJSON -{ - CmdFlakePrefetch() - { - } +struct CmdFlakePrefetch : FlakeCommand, MixJSON { + CmdFlakePrefetch() {} std::string description() override { - return "download the source tree denoted by a flake reference into the Nix store"; + return "download the source tree denoted by a flake reference into the " + "Nix store"; } std::string doc() override { return - #include "flake-prefetch.md" - ; +#include "flake-prefetch.md" + ; } void run(ref store) override @@ -1180,42 +1250,37 @@ struct CmdFlakePrefetch : FlakeCommand, MixJSON logger->cout(res.dump()); } else { notice("Downloaded '%s' to '%s' (hash '%s').", - lockedRef.to_string(), - store->printStorePath(tree.storePath), - hash.to_string(SRI, true)); + lockedRef.to_string(), store->printStorePath(tree.storePath), + hash.to_string(SRI, true)); } } }; -struct CmdFlake : NixMultiCommand -{ +struct CmdFlake : NixMultiCommand { CmdFlake() : MultiCommand({ - {"update", []() { return make_ref(); }}, - {"lock", []() { return make_ref(); }}, - {"metadata", []() { return make_ref(); }}, - {"info", []() { return make_ref(); }}, - {"check", []() { return make_ref(); }}, - {"init", []() { return make_ref(); }}, - {"new", []() { return make_ref(); }}, - {"clone", []() { return make_ref(); }}, - {"archive", []() { return make_ref(); }}, - {"show", []() { return make_ref(); }}, - {"prefetch", []() { return make_ref(); }}, - }) + {"update", []() { return make_ref(); }}, + {"lock", []() { return make_ref(); }}, + {"metadata", []() { return make_ref(); }}, + {"info", []() { return make_ref(); }}, + {"check", []() { return make_ref(); }}, + {"init", []() { return make_ref(); }}, + {"new", []() { return make_ref(); }}, + {"clone", []() { return make_ref(); }}, + {"archive", []() { return make_ref(); }}, + {"show", []() { return make_ref(); }}, + {"prefetch", []() { return make_ref(); }}, + }) { } - std::string description() override - { - return "manage Nix flakes"; - } + std::string description() override { return "manage Nix flakes"; } std::string doc() override { return - #include "flake.md" - ; +#include "flake.md" + ; } void run() override diff --git a/src/nix/fmt.cc b/src/nix/fmt.cc index 6f6a4a632273..f42fd185f3bf 100644 --- a/src/nix/fmt.cc +++ b/src/nix/fmt.cc @@ -8,19 +8,22 @@ struct CmdFmt : SourceExprCommand { CmdFmt() { expectArgs({.label = "args", .handler = {&args}}); } - std::string description() override { + std::string description() override + { return "reformat your code in the standard style"; } - std::string doc() override { + std::string doc() override + { return - #include "fmt.md" - ; +#include "fmt.md" + ; } Category category() override { return catSecondary; } - Strings getDefaultFlakeAttrPaths() override { + Strings getDefaultFlakeAttrPaths() override + { return Strings{"formatter." + settings.thisSystem.get()}; } @@ -41,8 +44,9 @@ struct CmdFmt : SourceExprCommand { // Format the current flake out of the box programArgs.push_back("."); } else { - // User wants more power, let them decide which paths to include/exclude - for (auto &i : args) { + // User wants more power, let them decide which paths to + // include/exclude + for (auto & i : args) { programArgs.push_back(i); } } diff --git a/src/nix/hash.cc b/src/nix/hash.cc index 60d9593a772a..ef1c8fcd03be 100644 --- a/src/nix/hash.cc +++ b/src/nix/hash.cc @@ -8,8 +8,7 @@ using namespace nix; -struct CmdHashBase : Command -{ +struct CmdHashBase : Command { FileIngestionMethod mode; Base base = SRI; bool truncate = false; @@ -45,29 +44,27 @@ struct CmdHashBase : Command addFlag(Flag::mkHashTypeFlag("type", &ht)); - #if 0 +#if 0 addFlag({ .longName = "modulo", .description = "Compute the hash modulo the specified string.", .labels = {"modulus"}, .handler = {&modulus}, }); - #endif\ +#endif - expectArgs({ - .label = "paths", - .handler = {&paths}, - .completer = completePath - }); + expectArgs( + {.label = "paths", .handler = {&paths}, .completer = completePath}); } std::string description() override { switch (mode) { case FileIngestionMethod::Flat: - return "print cryptographic hash of a regular file"; + return "print cryptographic hash of a regular file"; case FileIngestionMethod::Recursive: - return "print cryptographic hash of the NAR serialisation of a path"; + return "print cryptographic hash of the NAR serialisation of a " + "path"; default: assert(false); }; @@ -93,14 +90,14 @@ struct CmdHashBase : Command } Hash h = hashSink->finish().first; - if (truncate && h.hashSize > 20) h = compressHash(h, 20); + if (truncate && h.hashSize > 20) + h = compressHash(h, 20); logger->cout(h.to_string(base, base == SRI)); } } }; -struct CmdToBase : Command -{ +struct CmdToBase : Command { Base base; std::optional ht; std::vector args; @@ -114,10 +111,10 @@ struct CmdToBase : Command std::string description() override { return fmt("convert a hash to %s representation", - base == Base16 ? "base-16" : - base == Base32 ? "base-32" : - base == Base64 ? "base-64" : - "SRI"); + base == Base16 ? "base-16" + : base == Base32 ? "base-32" + : base == Base64 ? "base-64" + : "SRI"); } void run() override @@ -127,18 +124,25 @@ struct CmdToBase : Command } }; -struct CmdHash : NixMultiCommand -{ +struct CmdHash : NixMultiCommand { CmdHash() : MultiCommand({ - {"file", []() { return make_ref(FileIngestionMethod::Flat);; }}, - {"path", []() { return make_ref(FileIngestionMethod::Recursive); }}, - {"to-base16", []() { return make_ref(Base16); }}, - {"to-base32", []() { return make_ref(Base32); }}, - {"to-base64", []() { return make_ref(Base64); }}, - {"to-sri", []() { return make_ref(SRI); }}, + {"file", + []() { + return make_ref(FileIngestionMethod::Flat); + ; + }}, + {"path", + []() { + return make_ref(FileIngestionMethod::Recursive); + }}, + {"to-base16", []() { return make_ref(Base16); }}, + {"to-base32", []() { return make_ref(Base32); }}, + {"to-base64", []() { return make_ref(Base64); }}, + {"to-sri", []() { return make_ref(SRI); }}, }) - { } + { + } std::string description() override { @@ -159,7 +163,7 @@ struct CmdHash : NixMultiCommand static auto rCmdHash = registerCommand("hash"); /* Legacy nix-hash command. */ -static int compatNixHash(int argc, char * * argv) +static int compatNixHash(int argc, char ** argv) { HashType ht = htMD5; bool flat = false; @@ -168,29 +172,35 @@ static int compatNixHash(int argc, char * * argv) enum { opHash, opTo32, opTo16 } op = opHash; std::vector ss; - parseCmdLine(argc, argv, [&](Strings::iterator & arg, const Strings::iterator & end) { - if (*arg == "--help") - showManPage("nix-hash"); - else if (*arg == "--version") - printVersion("nix-hash"); - else if (*arg == "--flat") flat = true; - else if (*arg == "--base32") base32 = true; - else if (*arg == "--truncate") truncate = true; - else if (*arg == "--type") { - std::string s = getArg(*arg, arg, end); - ht = parseHashType(s); - } - else if (*arg == "--to-base16") op = opTo16; - else if (*arg == "--to-base32") op = opTo32; - else if (*arg != "" && arg->at(0) == '-') - return false; - else - ss.push_back(*arg); - return true; - }); + parseCmdLine(argc, argv, + [&](Strings::iterator & arg, const Strings::iterator & end) { + if (*arg == "--help") + showManPage("nix-hash"); + else if (*arg == "--version") + printVersion("nix-hash"); + else if (*arg == "--flat") + flat = true; + else if (*arg == "--base32") + base32 = true; + else if (*arg == "--truncate") + truncate = true; + else if (*arg == "--type") { + std::string s = getArg(*arg, arg, end); + ht = parseHashType(s); + } else if (*arg == "--to-base16") + op = opTo16; + else if (*arg == "--to-base32") + op = opTo32; + else if (*arg != "" && arg->at(0) == '-') + return false; + else + ss.push_back(*arg); + return true; + }); if (op == opHash) { - CmdHashBase cmd(flat ? FileIngestionMethod::Flat : FileIngestionMethod::Recursive); + CmdHashBase cmd(flat ? FileIngestionMethod::Flat + : FileIngestionMethod::Recursive); cmd.ht = ht; cmd.base = base32 ? Base32 : Base16; cmd.truncate = truncate; diff --git a/src/nix/log.cc b/src/nix/log.cc index 72d02ef11bb7..a9c3eac114f7 100644 --- a/src/nix/log.cc +++ b/src/nix/log.cc @@ -7,18 +7,18 @@ using namespace nix; -struct CmdLog : InstallableCommand -{ +struct CmdLog : InstallableCommand { std::string description() override { - return "show the build log of the specified packages or paths, if available"; + return "show the build log of the specified packages or paths, if " + "available"; } std::string doc() override { return - #include "log.md" - ; +#include "log.md" + ; } Category category() override { return catSecondary; } @@ -37,22 +37,28 @@ struct CmdLog : InstallableCommand for (auto & sub : subs) { auto * logSubP = dynamic_cast(&*sub); if (!logSubP) { - printInfo("Skipped '%s' which does not support retrieving build logs", sub->getUri()); + printInfo( + "Skipped '%s' which does not support retrieving build logs", + sub->getUri()); continue; } auto & logSub = *logSubP; - auto log = std::visit(overloaded { - [&](const DerivedPath::Opaque & bo) { - return logSub.getBuildLog(bo.path); - }, - [&](const DerivedPath::Built & bfd) { - return logSub.getBuildLog(bfd.drvPath); - }, - }, b.raw()); - if (!log) continue; + auto log = + std::visit(overloaded{ + [&](const DerivedPath::Opaque & bo) { + return logSub.getBuildLog(bo.path); + }, + [&](const DerivedPath::Built & bfd) { + return logSub.getBuildLog(bfd.drvPath); + }, + }, + b.raw()); + if (!log) + continue; stopProgressBar(); - printInfo("got build log for '%s' from '%s'", installable->what(), logSub.getUri()); + printInfo("got build log for '%s' from '%s'", installable->what(), + logSub.getUri()); std::cout << *log; return; } diff --git a/src/nix/ls.cc b/src/nix/ls.cc index 07554994b3d8..5e6c4284515a 100644 --- a/src/nix/ls.cc +++ b/src/nix/ls.cc @@ -7,8 +7,7 @@ using namespace nix; -struct MixLs : virtual Args, MixJSON -{ +struct MixLs : virtual Args, MixJSON { std::string path; bool recursive = false; @@ -41,16 +40,18 @@ struct MixLs : virtual Args, MixJSON void listText(ref accessor) { - std::function doPath; + std::function + doPath; auto showFile = [&](const Path & curPath, const std::string & relPath) { if (verbose) { auto st = accessor->stat(curPath); std::string tp = - st.type == FSAccessor::Type::tRegular ? - (st.isExecutable ? "-r-xr-xr-x" : "-r--r--r--") : - st.type == FSAccessor::Type::tSymlink ? "lrwxrwxrwx" : - "dr-xr-xr-x"; + st.type == FSAccessor::Type::tRegular + ? (st.isExecutable ? "-r-xr-xr-x" : "-r--r--r--") + : st.type == FSAccessor::Type::tSymlink ? "lrwxrwxrwx" + : "dr-xr-xr-x"; auto line = fmt("%s %20d %s", tp, st.fileSize, relPath); if (st.type == FSAccessor::Type::tSymlink) line += " -> " + accessor->readLink(curPath); @@ -68,8 +69,7 @@ struct MixLs : virtual Args, MixJSON }; doPath = [&](const FSAccessor::Stat & st, const Path & curPath, - const std::string & relPath, bool showDirectory) - { + const std::string & relPath, bool showDirectory) { if (st.type == FSAccessor::Type::tDirectory && !showDirectory) { auto names = accessor->readDirectory(curPath); for (auto & name : names) @@ -82,13 +82,16 @@ struct MixLs : virtual Args, MixJSON if (st.type == FSAccessor::Type::tMissing) throw Error("path '%1%' does not exist", path); doPath(st, path, - st.type == FSAccessor::Type::tDirectory ? "." : std::string(baseNameOf(path)), - showDirectory); + st.type == FSAccessor::Type::tDirectory + ? "." + : std::string(baseNameOf(path)), + showDirectory); } void list(ref accessor) { - if (path == "/") path = ""; + if (path == "/") + path = ""; if (json) { JSONPlaceholder jsonRoot(std::cout); @@ -100,15 +103,11 @@ struct MixLs : virtual Args, MixJSON } }; -struct CmdLsStore : StoreCommand, MixLs -{ +struct CmdLsStore : StoreCommand, MixLs { CmdLsStore() { - expectArgs({ - .label = "path", - .handler = {&path}, - .completer = completePath - }); + expectArgs( + {.label = "path", .handler = {&path}, .completer = completePath}); } std::string description() override @@ -119,35 +118,28 @@ struct CmdLsStore : StoreCommand, MixLs std::string doc() override { return - #include "store-ls.md" - ; +#include "store-ls.md" + ; } - void run(ref store) override - { - list(store->getFSAccessor()); - } + void run(ref store) override { list(store->getFSAccessor()); } }; -struct CmdLsNar : Command, MixLs -{ +struct CmdLsNar : Command, MixLs { Path narPath; CmdLsNar() { - expectArgs({ - .label = "nar", - .handler = {&narPath}, - .completer = completePath - }); + expectArgs( + {.label = "nar", .handler = {&narPath}, .completer = completePath}); expectArg("path", &path); } std::string doc() override { return - #include "nar-ls.md" - ; +#include "nar-ls.md" + ; } std::string description() override @@ -155,10 +147,7 @@ struct CmdLsNar : Command, MixLs return "show information about a path inside a NAR file"; } - void run() override - { - list(makeNarAccessor(readFile(narPath))); - } + void run() override { list(makeNarAccessor(readFile(narPath))); } }; static auto rCmdLsStore = registerCommand2({"store", "ls"}); diff --git a/src/nix/main.cc b/src/nix/main.cc index 17c92ebc6128..1bb17d863a1b 100644 --- a/src/nix/main.cc +++ b/src/nix/main.cc @@ -22,7 +22,7 @@ extern std::string chrootHelperName; -void chrootHelper(int argc, char * * argv); +void chrootHelper(int argc, char ** argv); namespace nix { @@ -37,14 +37,18 @@ static bool haveInternet() Finally free([&]() { freeifaddrs(addrs); }); for (auto i = addrs; i; i = i->ifa_next) { - if (!i->ifa_addr) continue; + if (!i->ifa_addr) + continue; if (i->ifa_addr->sa_family == AF_INET) { - if (ntohl(((sockaddr_in *) i->ifa_addr)->sin_addr.s_addr) != INADDR_LOOPBACK) { + if (ntohl(((sockaddr_in *) i->ifa_addr)->sin_addr.s_addr) != + INADDR_LOOPBACK) { return true; } } else if (i->ifa_addr->sa_family == AF_INET6) { - if (!IN6_IS_ADDR_LOOPBACK(&((sockaddr_in6 *) i->ifa_addr)->sin6_addr) && - !IN6_IS_ADDR_LINKLOCAL(&((sockaddr_in6 *) i->ifa_addr)->sin6_addr)) + if (!IN6_IS_ADDR_LOOPBACK( + &((sockaddr_in6 *) i->ifa_addr)->sin6_addr) && + !IN6_IS_ADDR_LINKLOCAL( + &((sockaddr_in6 *) i->ifa_addr)->sin6_addr)) return true; } } @@ -53,23 +57,26 @@ static bool haveInternet() } std::string programPath; -char * * savedArgv; +char ** savedArgv; -struct HelpRequested { }; +struct HelpRequested { +}; -struct NixArgs : virtual MultiCommand, virtual MixCommonArgs -{ +struct NixArgs : virtual MultiCommand, virtual MixCommonArgs { bool useNet = true; bool refresh = false; bool showVersion = false; - NixArgs() : MultiCommand(RegisterCommand::getCommandsFor({})), MixCommonArgs("nix") + NixArgs() + : MultiCommand(RegisterCommand::getCommandsFor({})), + MixCommonArgs("nix") { categories.clear(); categories[Command::catDefault] = "Main commands"; categories[catSecondary] = "Infrequently used commands"; categories[catUtility] = "Utility/scripting commands"; - categories[catNixInstallation] = "Commands for upgrading or troubleshooting your Nix installation"; + categories[catNixInstallation] = + "Commands for upgrading or troubleshooting your Nix installation"; addFlag({ .longName = "help", @@ -82,7 +89,7 @@ struct NixArgs : virtual MultiCommand, virtual MixCommonArgs .shortName = 'L', .description = "Print full build logs on standard error.", .category = loggingCategory, - .handler = {[&]() {setLogFormat(LogFormat::barWithLogs); }}, + .handler = {[&]() { setLogFormat(LogFormat::barWithLogs); }}, }); addFlag({ @@ -94,13 +101,15 @@ struct NixArgs : virtual MultiCommand, virtual MixCommonArgs addFlag({ .longName = "offline", .aliases = {"no-net"}, // FIXME: remove - .description = "Disable substituters and consider all previously downloaded files up-to-date.", + .description = "Disable substituters and consider all previously " + "downloaded files up-to-date.", .handler = {[&]() { useNet = false; }}, }); addFlag({ .longName = "refresh", - .description = "Consider all previously downloaded files out-of-date.", + .description = + "Consider all previously downloaded files out-of-date.", .handler = {[&]() { refresh = true; }}, }); } @@ -129,14 +138,17 @@ struct NixArgs : virtual MultiCommand, virtual MixCommonArgs bool aliasUsed = false; - Strings::iterator rewriteArgs(Strings & args, Strings::iterator pos) override + Strings::iterator rewriteArgs(Strings & args, + Strings::iterator pos) override { - if (aliasUsed || command || pos == args.end()) return pos; + if (aliasUsed || command || pos == args.end()) + return pos; auto arg = *pos; auto i = aliases.find(arg); - if (i == aliases.end()) return pos; - warn("'%s' is a deprecated alias for '%s'", - arg, concatStringsSep(" ", i->second)); + if (i == aliases.end()) + return pos; + warn("'%s' is a deprecated alias for '%s'", arg, + concatStringsSep(" ", i->second)); pos = args.erase(pos); for (auto j = i->second.rbegin(); j != i->second.rend(); ++j) pos = args.insert(pos, *j); @@ -146,14 +158,15 @@ struct NixArgs : virtual MultiCommand, virtual MixCommonArgs std::string description() override { - return "a tool for reproducible and declarative configuration management"; + return "a tool for reproducible and declarative configuration " + "management"; } std::string doc() override { return - #include "nix.md" - ; +#include "nix.md" + ; } // Plugins may add new subcommands. @@ -165,9 +178,12 @@ struct NixArgs : virtual MultiCommand, virtual MixCommonArgs /* Render the help for the specified subcommand to stdout using lowdown. */ -static void showHelp(std::vector subcommand, MultiCommand & toplevel) +static void showHelp(std::vector subcommand, + MultiCommand & toplevel) { - auto mdName = subcommand.empty() ? "nix" : fmt("nix3-%s", concatStringsSep("-", subcommand)); + auto mdName = subcommand.empty() + ? "nix" + : fmt("nix3-%s", concatStringsSep("-", subcommand)); evalSettings.restrictEval = false; evalSettings.pureEval = false; @@ -175,26 +191,28 @@ static void showHelp(std::vector subcommand, MultiCommand & topleve auto vGenerateManpage = state.allocValue(); state.eval(state.parseExprFromString( - #include "generate-manpage.nix.gen.hh" - , "/"), *vGenerateManpage); +#include "generate-manpage.nix.gen.hh" + , "/"), + *vGenerateManpage); auto vUtils = state.allocValue(); - state.cacheFile( - "/utils.nix", "/utils.nix", - state.parseExprFromString( - #include "utils.nix.gen.hh" - , "/"), - *vUtils); + state.cacheFile("/utils.nix", "/utils.nix", + state.parseExprFromString( +#include "utils.nix.gen.hh" + , "/"), + *vUtils); auto attrs = state.buildBindings(16); attrs.alloc("command").mkString(toplevel.toJSON().dump()); auto vRes = state.allocValue(); - state.callFunction(*vGenerateManpage, state.allocValue()->mkAttrs(attrs), *vRes, noPos); + state.callFunction(*vGenerateManpage, state.allocValue()->mkAttrs(attrs), + *vRes, noPos); auto attr = vRes->attrs->get(state.symbols.create(mdName + ".md")); if (!attr) - throw UsageError("Nix has no subcommand '%s'", concatStringsSep("", subcommand)); + throw UsageError("Nix has no subcommand '%s'", + concatStringsSep("", subcommand)); auto markdown = state.forceString(*attr->value); @@ -202,8 +220,7 @@ static void showHelp(std::vector subcommand, MultiCommand & topleve std::cout << renderMarkdownToTerminal(markdown) << "\n"; } -struct CmdHelp : Command -{ +struct CmdHelp : Command { std::vector subcommand; CmdHelp() @@ -222,22 +239,23 @@ struct CmdHelp : Command std::string doc() override { return - #include "help.md" - ; +#include "help.md" + ; } void run() override { assert(parent); MultiCommand * toplevel = parent; - while (toplevel->parent) toplevel = toplevel->parent; + while (toplevel->parent) + toplevel = toplevel->parent; showHelp(subcommand, *toplevel); } }; static auto rCmdHelp = registerCommand("help"); -void mainWrapped(int argc, char * * argv) +void mainWrapped(int argc, char ** argv) { savedArgv = argv; @@ -251,15 +269,16 @@ void mainWrapped(int argc, char * * argv) initNix(); initGC(); - #if __linux__ +#if __linux__ if (getuid() == 0) { try { saveMountNamespace(); if (unshare(CLONE_NEWNS) == -1) throw SysError("setting up a private mount namespace"); - } catch (Error & e) { } + } catch (Error & e) { + } } - #endif +#endif Finally f([] { logger->stop(); }); @@ -268,12 +287,14 @@ void mainWrapped(int argc, char * * argv) if (argc > 0 && std::string_view(argv[0]) == "__build-remote") { programName = "build-remote"; - argv++; argc--; + argv++; + argc--; } { auto legacy = (*RegisterLegacyCommand::commands)[programName]; - if (legacy) return legacy(argc, argv); + if (legacy) + return legacy(argc, argv); } evalSettings.pureEval = true; @@ -301,9 +322,11 @@ void mainWrapped(int argc, char * * argv) auto builtins = state.baseEnv.values[0]->attrs; for (auto & builtin : *builtins) { auto b = nlohmann::json::object(); - if (!builtin.value->isPrimOp()) continue; + if (!builtin.value->isPrimOp()) + continue; auto primOp = builtin.value->primOp; - if (!primOp->doc) continue; + if (!primOp->doc) + continue; b["arity"] = primOp->arity; b["args"] = primOp->args; b["doc"] = trim(stripIndentation(primOp->doc)); @@ -313,16 +336,18 @@ void mainWrapped(int argc, char * * argv) return; } - Finally printCompletions([&]() - { + Finally printCompletions([&]() { if (completions) { switch (completionType) { case ctNormal: - std::cout << "normal\n"; break; + std::cout << "normal\n"; + break; case ctFilenames: - std::cout << "filenames\n"; break; + std::cout << "filenames\n"; + break; case ctAttrs: - std::cout << "attrs\n"; break; + std::cout << "attrs\n"; + break; } for (auto & s : *completions) std::cout << s.completion << "\t" << s.description << "\n"; @@ -337,17 +362,20 @@ void mainWrapped(int argc, char * * argv) while (command) { if (command && command->command) { subcommand.push_back(command->command->first); - command = dynamic_cast(&*command->command->second); + command = + dynamic_cast(&*command->command->second); } else break; } showHelp(subcommand, args); return; } catch (UsageError &) { - if (!completions) throw; + if (!completions) + throw; } - if (completions) return; + if (completions) + return; if (args.showVersion) { printVersion(programName); @@ -357,13 +385,13 @@ void mainWrapped(int argc, char * * argv) if (!args.command) throw UsageError("no subcommand specified"); - if (args.command->first != "repl" - && args.command->first != "doctor" - && args.command->first != "upgrade-nix") + if (args.command->first != "repl" && args.command->first != "doctor" && + args.command->first != "upgrade-nix") settings.requireExperimentalFeature(Xp::NixCommand); if (args.useNet && !haveInternet()) { - warn("you don't have Internet access; disabling some network-dependent features"); + warn("you don't have Internet access; disabling some network-dependent " + "features"); args.useNet = false; } @@ -385,7 +413,8 @@ void mainWrapped(int argc, char * * argv) settings.ttlPositiveNarInfoCache = 0; } - if (args.command->second->forceImpureByDefault() && !evalSettings.pureEval.overridden) { + if (args.command->second->forceImpureByDefault() && + !evalSettings.pureEval.overridden) { evalSettings.pureEval = false; } args.command->second->prepare(); @@ -394,13 +423,12 @@ void mainWrapped(int argc, char * * argv) } -int main(int argc, char * * argv) +int main(int argc, char ** argv) { // Increase the default stack size for the evaluator and for // libstdc++'s std::regex. nix::setStackSize(64 * 1024 * 1024); - return nix::handleExceptions(argv[0], [&]() { - nix::mainWrapped(argc, argv); - }); + return nix::handleExceptions(argv[0], + [&]() { nix::mainWrapped(argc, argv); }); } diff --git a/src/nix/make-content-addressed.cc b/src/nix/make-content-addressed.cc index 34860c38f289..c095117a9f01 100644 --- a/src/nix/make-content-addressed.cc +++ b/src/nix/make-content-addressed.cc @@ -6,12 +6,10 @@ using namespace nix; -struct CmdMakeContentAddressed : virtual CopyCommand, virtual StorePathsCommand, MixJSON -{ - CmdMakeContentAddressed() - { - realiseMode = Realise::Outputs; - } +struct CmdMakeContentAddressed : virtual CopyCommand, + virtual StorePathsCommand, + MixJSON { + CmdMakeContentAddressed() { realiseMode = Realise::Outputs; } std::string description() override { @@ -21,15 +19,16 @@ struct CmdMakeContentAddressed : virtual CopyCommand, virtual StorePathsCommand, std::string doc() override { return - #include "make-content-addressed.md" - ; +#include "make-content-addressed.md" + ; } void run(ref srcStore, StorePaths && storePaths) override { auto dstStore = dstUri.empty() ? openStore() : openStore(dstUri); - auto remappings = makeContentAddressed(*srcStore, *dstStore, + auto remappings = makeContentAddressed( + *srcStore, *dstStore, StorePathSet(storePaths.begin(), storePaths.end())); if (json) { @@ -38,18 +37,20 @@ struct CmdMakeContentAddressed : virtual CopyCommand, virtual StorePathsCommand, for (auto & path : storePaths) { auto i = remappings.find(path); assert(i != remappings.end()); - jsonRewrites.attr(srcStore->printStorePath(path), srcStore->printStorePath(i->second)); + jsonRewrites.attr(srcStore->printStorePath(path), + srcStore->printStorePath(i->second)); } } else { for (auto & path : storePaths) { auto i = remappings.find(path); assert(i != remappings.end()); - notice("rewrote '%s' to '%s'", - srcStore->printStorePath(path), - srcStore->printStorePath(i->second)); + notice("rewrote '%s' to '%s'", srcStore->printStorePath(path), + srcStore->printStorePath(i->second)); } } } }; -static auto rCmdMakeContentAddressed = registerCommand2({"store", "make-content-addressed"}); +static auto rCmdMakeContentAddressed = + registerCommand2( + {"store", "make-content-addressed"}); diff --git a/src/nix/nar.cc b/src/nix/nar.cc index dbb043d9b9d8..6904efe9f180 100644 --- a/src/nix/nar.cc +++ b/src/nix/nar.cc @@ -2,21 +2,16 @@ using namespace nix; -struct CmdNar : NixMultiCommand -{ - CmdNar() : MultiCommand(RegisterCommand::getCommandsFor({"nar"})) - { } +struct CmdNar : NixMultiCommand { + CmdNar() : MultiCommand(RegisterCommand::getCommandsFor({"nar"})) {} - std::string description() override - { - return "create or inspect NAR files"; - } + std::string description() override { return "create or inspect NAR files"; } std::string doc() override { return - #include "nar.md" - ; +#include "nar.md" + ; } Category category() override { return catUtility; } diff --git a/src/nix/optimise-store.cc b/src/nix/optimise-store.cc index 985006e5a544..a4fa1361b194 100644 --- a/src/nix/optimise-store.cc +++ b/src/nix/optimise-store.cc @@ -6,8 +6,7 @@ using namespace nix; -struct CmdOptimiseStore : StoreCommand -{ +struct CmdOptimiseStore : StoreCommand { std::string description() override { return "replace identical files in the store by hard links"; @@ -16,14 +15,12 @@ struct CmdOptimiseStore : StoreCommand std::string doc() override { return - #include "optimise-store.md" - ; +#include "optimise-store.md" + ; } - void run(ref store) override - { - store->optimiseStore(); - } + void run(ref store) override { store->optimiseStore(); } }; -static auto rCmdOptimiseStore = registerCommand2({"store", "optimise"}); +static auto rCmdOptimiseStore = + registerCommand2({"store", "optimise"}); diff --git a/src/nix/path-info.cc b/src/nix/path-info.cc index d690fe59494b..8a1e4a757dd5 100644 --- a/src/nix/path-info.cc +++ b/src/nix/path-info.cc @@ -9,8 +9,7 @@ using namespace nix; -struct CmdPathInfo : StorePathsCommand, MixJSON -{ +struct CmdPathInfo : StorePathsCommand, MixJSON { bool showSize = false; bool showClosureSize = false; bool humanReadable = false; @@ -21,21 +20,24 @@ struct CmdPathInfo : StorePathsCommand, MixJSON addFlag({ .longName = "size", .shortName = 's', - .description = "Print the size of the NAR serialisation of each path.", + .description = + "Print the size of the NAR serialisation of each path.", .handler = {&showSize, true}, }); addFlag({ .longName = "closure-size", .shortName = 'S', - .description = "Print the sum of the sizes of the NAR serialisations of the closure of each path.", + .description = "Print the sum of the sizes of the NAR " + "serialisations of the closure of each path.", .handler = {&showClosureSize, true}, }); addFlag({ .longName = "human-readable", .shortName = 'h', - .description = "With `-s` and `-S`, print sizes in a human-friendly format such as `5.67G`.", + .description = "With `-s` and `-S`, print sizes in a " + "human-friendly format such as `5.67G`.", .handler = {&humanReadable, true}, }); @@ -54,8 +56,8 @@ struct CmdPathInfo : StorePathsCommand, MixJSON std::string doc() override { return - #include "path-info.md" - ; +#include "path-info.md" + ; } Category category() override { return catSecondary; } @@ -67,9 +69,8 @@ struct CmdPathInfo : StorePathsCommand, MixJSON return; } - static const std::array idents{{ - ' ', 'K', 'M', 'G', 'T', 'P', 'E', 'Z', 'Y' - }}; + static const std::array idents{ + {' ', 'K', 'M', 'G', 'T', 'P', 'E', 'Z', 'Y'}}; size_t power = 0; double res = value; while (res > 1024 && power < idents.size()) { @@ -83,14 +84,16 @@ struct CmdPathInfo : StorePathsCommand, MixJSON { size_t pathLen = 0; for (auto & storePath : storePaths) - pathLen = std::max(pathLen, store->printStorePath(storePath).size()); + pathLen = + std::max(pathLen, store->printStorePath(storePath).size()); if (json) { JSONPlaceholder jsonRoot(std::cout); - store->pathInfoToJSON(jsonRoot, + store->pathInfoToJSON( + jsonRoot, // FIXME: preserve order? - StorePathSet(storePaths.begin(), storePaths.end()), - true, showClosureSize, SRI, AllowInvalid); + StorePathSet(storePaths.begin(), storePaths.end()), true, + showClosureSize, SRI, AllowInvalid); } else { @@ -102,7 +105,9 @@ struct CmdPathInfo : StorePathsCommand, MixJSON std::cout << storePathS; if (showSize || showClosureSize || showSigs) - std::cout << std::string(std::max(0, (int) pathLen - (int) storePathS.size()), ' '); + std::cout << std::string( + std::max(0, (int) pathLen - (int) storePathS.size()), + ' '); if (showSize) printSize(info->narSize); @@ -113,15 +118,17 @@ struct CmdPathInfo : StorePathsCommand, MixJSON if (showSigs) { std::cout << '\t'; Strings ss; - if (info->ultimate) ss.push_back("ultimate"); - if (info->ca) ss.push_back("ca:" + renderContentAddress(*info->ca)); - for (auto & sig : info->sigs) ss.push_back(sig); + if (info->ultimate) + ss.push_back("ultimate"); + if (info->ca) + ss.push_back("ca:" + renderContentAddress(*info->ca)); + for (auto & sig : info->sigs) + ss.push_back(sig); std::cout << concatStringsSep(" ", ss); } std::cout << std::endl; } - } } }; diff --git a/src/nix/ping-store.cc b/src/nix/ping-store.cc index 3c3b7bb45eea..54682dbc8839 100644 --- a/src/nix/ping-store.cc +++ b/src/nix/ping-store.cc @@ -4,8 +4,7 @@ using namespace nix; -struct CmdPingStore : StoreCommand -{ +struct CmdPingStore : StoreCommand { std::string description() override { return "test whether a store can be accessed"; @@ -14,8 +13,8 @@ struct CmdPingStore : StoreCommand std::string doc() override { return - #include "ping-store.md" - ; +#include "ping-store.md" + ; } void run(ref store) override diff --git a/src/nix/prefetch.cc b/src/nix/prefetch.cc index ce3288dc1bc2..528b6c833ee9 100644 --- a/src/nix/prefetch.cc +++ b/src/nix/prefetch.cc @@ -18,16 +18,21 @@ using namespace nix; mirrors defined in Nixpkgs. */ std::string resolveMirrorUrl(EvalState & state, const std::string & url) { - if (url.substr(0, 9) != "mirror://") return url; + if (url.substr(0, 9) != "mirror://") + return url; std::string s(url, 9); auto p = s.find('/'); - if (p == std::string::npos) throw Error("invalid mirror URL '%s'", url); + if (p == std::string::npos) + throw Error("invalid mirror URL '%s'", url); std::string mirrorName(s, 0, p); Value vMirrors; // FIXME: use nixpkgs flake - state.eval(state.parseExprFromString("import ", "."), vMirrors); + state.eval( + state.parseExprFromString( + "import ", "."), + vMirrors); state.forceAttrs(vMirrors, noPos); auto mirrorList = vMirrors.attrs->find(state.symbols.create(mirrorName)); @@ -42,16 +47,14 @@ std::string resolveMirrorUrl(EvalState & state, const std::string & url) return mirror + (hasSuffix(mirror, "/") ? "" : "/") + s.substr(p + 1); } -std::tuple prefetchFile( - ref store, - std::string_view url, - std::optional name, - HashType hashType, - std::optional expectedHash, - bool unpack, - bool executable) +std::tuple prefetchFile(ref store, std::string_view url, + std::optional name, + HashType hashType, + std::optional expectedHash, + bool unpack, bool executable) { - auto ingestionMethod = unpack || executable ? FileIngestionMethod::Recursive : FileIngestionMethod::Flat; + auto ingestionMethod = unpack || executable ? FileIngestionMethod::Recursive + : FileIngestionMethod::Flat; /* Figure out a name in the Nix store. */ if (!name) { @@ -67,7 +70,8 @@ std::tuple prefetchFile( the store. */ if (expectedHash) { hashType = expectedHash->type; - storePath = store->makeFixedOutputPath(ingestionMethod, *expectedHash, *name); + storePath = + store->makeFixedOutputPath(ingestionMethod, *expectedHash, *name); if (store->isValidPath(*storePath)) hash = expectedHash; else @@ -85,8 +89,10 @@ std::tuple prefetchFile( if (executable) mode = 0700; - AutoCloseFD fd = open(tmpFile.c_str(), O_WRONLY | O_CREAT | O_EXCL, mode); - if (!fd) throw SysError("creating temporary file '%s'", tmpFile); + AutoCloseFD fd = + open(tmpFile.c_str(), O_WRONLY | O_CREAT | O_EXCL, mode); + if (!fd) + throw SysError("creating temporary file '%s'", tmpFile); FdSink sink(fd.get()); @@ -98,7 +104,7 @@ std::tuple prefetchFile( /* Optionally unpack the file. */ if (unpack) { Activity act(*logger, lvlChatty, actUnknown, - fmt("unpacking '%s'", url)); + fmt("unpacking '%s'", url)); Path unpacked = (Path) tmpDir + "/unpacked"; createDirs(unpacked); unpackTarfile(tmpFile, unpacked); @@ -113,9 +119,10 @@ std::tuple prefetchFile( } Activity act(*logger, lvlChatty, actUnknown, - fmt("adding '%s' to the store", url)); + fmt("adding '%s' to the store", url)); - auto info = store->addToStoreSlow(*name, tmpFile, ingestionMethod, hashType, expectedHash); + auto info = store->addToStoreSlow(*name, tmpFile, ingestionMethod, + hashType, expectedHash); storePath = info.path; assert(info.ca); hash = getContentAddressHash(*info.ca); @@ -124,7 +131,7 @@ std::tuple prefetchFile( return {storePath.value(), hash.value()}; } -static int main_nix_prefetch_url(int argc, char * * argv) +static int main_nix_prefetch_url(int argc, char ** argv) { { HashType ht = htSHA256; @@ -136,38 +143,37 @@ static int main_nix_prefetch_url(int argc, char * * argv) bool executable = false; std::optional name; - struct MyArgs : LegacyArgs, MixEvalArgs - { + struct MyArgs : LegacyArgs, MixEvalArgs { using LegacyArgs::LegacyArgs; }; - MyArgs myArgs(std::string(baseNameOf(argv[0])), [&](Strings::iterator & arg, const Strings::iterator & end) { - if (*arg == "--help") - showManPage("nix-prefetch-url"); - else if (*arg == "--version") - printVersion("nix-prefetch-url"); - else if (*arg == "--type") { - auto s = getArg(*arg, arg, end); - ht = parseHashType(s); - } - else if (*arg == "--print-path") - printPath = true; - else if (*arg == "--attr" || *arg == "-A") { - fromExpr = true; - attrPath = getArg(*arg, arg, end); - } - else if (*arg == "--unpack") - unpack = true; - else if (*arg == "--executable") - executable = true; - else if (*arg == "--name") - name = getArg(*arg, arg, end); - else if (*arg != "" && arg->at(0) == '-') - return false; - else - args.push_back(*arg); - return true; - }); + MyArgs myArgs( + std::string(baseNameOf(argv[0])), + [&](Strings::iterator & arg, const Strings::iterator & end) { + if (*arg == "--help") + showManPage("nix-prefetch-url"); + else if (*arg == "--version") + printVersion("nix-prefetch-url"); + else if (*arg == "--type") { + auto s = getArg(*arg, arg, end); + ht = parseHashType(s); + } else if (*arg == "--print-path") + printPath = true; + else if (*arg == "--attr" || *arg == "-A") { + fromExpr = true; + attrPath = getArg(*arg, arg, end); + } else if (*arg == "--unpack") + unpack = true; + else if (*arg == "--executable") + executable = true; + else if (*arg == "--name") + name = getArg(*arg, arg, end); + else if (*arg != "" && arg->at(0) == '-') + return false; + else + args.push_back(*arg); + return true; + }); myArgs.parseCmdline(argvToStrings(argc, argv)); @@ -177,7 +183,7 @@ static int main_nix_prefetch_url(int argc, char * * argv) Finally f([]() { stopProgressBar(); }); if (isatty(STDERR_FILENO)) - startProgressBar(); + startProgressBar(); auto store = openStore(); auto state = std::make_unique(myArgs.searchPath, store); @@ -192,10 +198,12 @@ static int main_nix_prefetch_url(int argc, char * * argv) throw UsageError("you must specify a URL"); url = args[0]; } else { - Path path = resolveExprPath(lookupFileArg(*state, args.empty() ? "." : args[0])); + Path path = resolveExprPath( + lookupFileArg(*state, args.empty() ? "." : args[0])); Value vRoot; state->evalFile(path, vRoot); - Value & v(*findAlongAttrPath(*state, attrPath, autoArgs, vRoot).first); + Value & v( + *findAlongAttrPath(*state, attrPath, autoArgs, vRoot).first); state->forceAttrs(v, noPos); /* Extract the URL. */ @@ -226,8 +234,9 @@ static int main_nix_prefetch_url(int argc, char * * argv) if (args.size() == 2) expectedHash = Hash::parseAny(args[1], ht); - auto [storePath, hash] = prefetchFile( - store, resolveMirrorUrl(*state, url), name, ht, expectedHash, unpack, executable); + auto [storePath, hash] = + prefetchFile(store, resolveMirrorUrl(*state, url), name, ht, + expectedHash, unpack, executable); stopProgressBar(); @@ -242,10 +251,10 @@ static int main_nix_prefetch_url(int argc, char * * argv) } } -static RegisterLegacyCommand r_nix_prefetch_url("nix-prefetch-url", main_nix_prefetch_url); +static RegisterLegacyCommand r_nix_prefetch_url("nix-prefetch-url", + main_nix_prefetch_url); -struct CmdStorePrefetchFile : StoreCommand, MixJSON -{ +struct CmdStorePrefetchFile : StoreCommand, MixJSON { std::string url; bool executable = false; std::optional name; @@ -254,21 +263,19 @@ struct CmdStorePrefetchFile : StoreCommand, MixJSON CmdStorePrefetchFile() { - addFlag({ - .longName = "name", - .description = "Override the name component of the resulting store path. It defaults to the base name of *url*.", - .labels = {"name"}, - .handler = {&name} - }); - - addFlag({ - .longName = "expected-hash", - .description = "The expected hash of the file.", - .labels = {"hash"}, - .handler = {[&](std::string s) { - expectedHash = Hash::parseAny(s, hashType); - }} - }); + addFlag({.longName = "name", + .description = + "Override the name component of the resulting store path. " + "It defaults to the base name of *url*.", + .labels = {"name"}, + .handler = {&name}}); + + addFlag({.longName = "expected-hash", + .description = "The expected hash of the file.", + .labels = {"hash"}, + .handler = {[&](std::string s) { + expectedHash = Hash::parseAny(s, hashType); + }}}); addFlag(Flag::mkHashTypeFlag("hash-type", &hashType)); @@ -291,12 +298,13 @@ struct CmdStorePrefetchFile : StoreCommand, MixJSON std::string doc() override { return - #include "store-prefetch-file.md" - ; +#include "store-prefetch-file.md" + ; } void run(ref store) override { - auto [storePath, hash] = prefetchFile(store, url, name, hashType, expectedHash, false, executable); + auto [storePath, hash] = prefetchFile(store, url, name, hashType, + expectedHash, false, executable); if (json) { auto res = nlohmann::json::object(); @@ -304,12 +312,11 @@ struct CmdStorePrefetchFile : StoreCommand, MixJSON res["hash"] = hash.to_string(SRI, true); logger->cout(res.dump()); } else { - notice("Downloaded '%s' to '%s' (hash '%s').", - url, - store->printStorePath(storePath), - hash.to_string(SRI, true)); + notice("Downloaded '%s' to '%s' (hash '%s').", url, + store->printStorePath(storePath), hash.to_string(SRI, true)); } } }; -static auto rCmdStorePrefetchFile = registerCommand2({"store", "prefetch-file"}); +static auto rCmdStorePrefetchFile = + registerCommand2({"store", "prefetch-file"}); diff --git a/src/nix/profile.cc b/src/nix/profile.cc index 3814e7d5a6fd..eb9bd42a4ad5 100644 --- a/src/nix/profile.cc +++ b/src/nix/profile.cc @@ -16,24 +16,22 @@ using namespace nix; -struct ProfileElementSource -{ +struct ProfileElementSource { FlakeRef originalRef; // FIXME: record original attrpath. FlakeRef resolvedRef; std::string attrPath; OutputsSpec outputs; - bool operator < (const ProfileElementSource & other) const + bool operator<(const ProfileElementSource & other) const { - return - std::tuple(originalRef.to_string(), attrPath, outputs) < - std::tuple(other.originalRef.to_string(), other.attrPath, other.outputs); + return std::tuple(originalRef.to_string(), attrPath, outputs) < + std::tuple(other.originalRef.to_string(), other.attrPath, + other.outputs); } }; -struct ProfileElement -{ +struct ProfileElement { StorePathSet storePaths; std::optional source; bool active = true; @@ -42,7 +40,8 @@ struct ProfileElement std::string describe() const { if (source) - return fmt("%s#%s%s", source->originalRef, source->attrPath, printOutputsSpec(source->outputs)); + return fmt("%s#%s%s", source->originalRef, source->attrPath, + printOutputsSpec(source->outputs)); StringSet names; for (auto & path : storePaths) names.insert(DrvName(path.name()).name); @@ -57,36 +56,35 @@ struct ProfileElement return showVersions(versions); } - bool operator < (const ProfileElement & other) const + bool operator<(const ProfileElement & other) const { - return std::tuple(describe(), storePaths) < std::tuple(other.describe(), other.storePaths); + return std::tuple(describe(), storePaths) < + std::tuple(other.describe(), other.storePaths); } - void updateStorePaths( - ref evalStore, - ref store, - const BuiltPaths & builtPaths) + void updateStorePaths(ref evalStore, ref store, + const BuiltPaths & builtPaths) { storePaths.clear(); for (auto & buildable : builtPaths) { - std::visit(overloaded { - [&](const BuiltPath::Opaque & bo) { - storePaths.insert(bo.path); - }, - [&](const BuiltPath::Built & bfd) { - for (auto & output : bfd.outputs) - storePaths.insert(output.second); - }, - }, buildable.raw()); + std::visit(overloaded{ + [&](const BuiltPath::Opaque & bo) { + storePaths.insert(bo.path); + }, + [&](const BuiltPath::Built & bfd) { + for (auto & output : bfd.outputs) + storePaths.insert(output.second); + }, + }, + buildable.raw()); } } }; -struct ProfileManifest -{ +struct ProfileManifest { std::vector elements; - ProfileManifest() { } + ProfileManifest() {} ProfileManifest(EvalState & state, const Path & profile) { @@ -99,33 +97,32 @@ struct ProfileManifest std::string sUrl; std::string sOriginalUrl; switch (version) { - case 1: - sUrl = "uri"; - sOriginalUrl = "originalUri"; - break; - case 2: - sUrl = "url"; - sOriginalUrl = "originalUrl"; - break; - default: - throw Error("profile manifest '%s' has unsupported version %d", manifestPath, version); + case 1: + sUrl = "uri"; + sOriginalUrl = "originalUri"; + break; + case 2: + sUrl = "url"; + sOriginalUrl = "originalUrl"; + break; + default: + throw Error("profile manifest '%s' has unsupported version %d", + manifestPath, version); } for (auto & e : json["elements"]) { ProfileElement element; for (auto & p : e["storePaths"]) - element.storePaths.insert(state.store->parseStorePath((std::string) p)); + element.storePaths.insert( + state.store->parseStorePath((std::string) p)); element.active = e["active"]; - if(e.contains("priority")) { + if (e.contains("priority")) { element.priority = e["priority"]; } if (e.value(sUrl, "") != "") { - element.source = ProfileElementSource { - parseFlakeRef(e[sOriginalUrl]), - parseFlakeRef(e[sUrl]), - e["attrPath"], - e["outputs"].get() - }; + element.source = ProfileElementSource{ + parseFlakeRef(e[sOriginalUrl]), parseFlakeRef(e[sUrl]), + e["attrPath"], e["outputs"].get()}; } elements.emplace_back(std::move(element)); } @@ -134,9 +131,11 @@ struct ProfileManifest else if (pathExists(profile + "/manifest.nix")) { // FIXME: needed because of pure mode; ugly. state.allowPath(state.store->followLinksToStore(profile)); - state.allowPath(state.store->followLinksToStore(profile + "/manifest.nix")); + state.allowPath( + state.store->followLinksToStore(profile + "/manifest.nix")); - auto drvInfos = queryInstalled(state, state.store->followLinksToStore(profile)); + auto drvInfos = + queryInstalled(state, state.store->followLinksToStore(profile)); for (auto & drvInfo : drvInfos) { ProfileElement element; @@ -181,7 +180,8 @@ struct ProfileManifest for (auto & element : elements) { for (auto & path : element.storePaths) { if (element.active) - pkgs.emplace_back(store->printStorePath(path), true, element.priority); + pkgs.emplace_back(store->printStorePath(path), true, + element.priority); references.insert(path); } } @@ -196,13 +196,15 @@ struct ProfileManifest auto narHash = hashString(htSHA256, sink.s); - ValidPathInfo info { - store->makeFixedOutputPath(FileIngestionMethod::Recursive, narHash, "profile", references), + ValidPathInfo info{ + store->makeFixedOutputPath(FileIngestionMethod::Recursive, narHash, + "profile", references), narHash, }; info.references = std::move(references); info.narSize = sink.s.size(); - info.ca = FixedOutputHash { .method = FileIngestionMethod::Recursive, .hash = info.narHash }; + info.ca = FixedOutputHash{.method = FileIngestionMethod::Recursive, + .hash = info.narHash}; StringSource source(sink.s); store->addToStore(info, source); @@ -210,7 +212,8 @@ struct ProfileManifest return std::move(info.path); } - static void printDiff(const ProfileManifest & prev, const ProfileManifest & cur, std::string_view indent) + static void printDiff(const ProfileManifest & prev, + const ProfileManifest & cur, std::string_view indent) { auto prevElems = prev.elements; std::sort(prevElems.begin(), prevElems.end()); @@ -224,21 +227,24 @@ struct ProfileManifest bool changes = false; while (i != prevElems.end() || j != curElems.end()) { - if (j != curElems.end() && (i == prevElems.end() || i->describe() > j->describe())) { - std::cout << fmt("%s%s: ∅ -> %s\n", indent, j->describe(), j->versions()); + if (j != curElems.end() && + (i == prevElems.end() || i->describe() > j->describe())) { + std::cout << fmt("%s%s: ∅ -> %s\n", indent, j->describe(), + j->versions()); changes = true; ++j; - } - else if (i != prevElems.end() && (j == curElems.end() || i->describe() < j->describe())) { - std::cout << fmt("%s%s: %s -> ∅\n", indent, i->describe(), i->versions()); + } else if (i != prevElems.end() && + (j == curElems.end() || i->describe() < j->describe())) { + std::cout << fmt("%s%s: %s -> ∅\n", indent, i->describe(), + i->versions()); changes = true; ++i; - } - else { + } else { auto v1 = i->versions(); auto v2 = j->versions(); if (v1 != v2) { - std::cout << fmt("%s%s: %s -> %s\n", indent, i->describe(), v1, v2); + std::cout << fmt("%s%s: %s -> %s\n", indent, i->describe(), + v1, v2); changes = true; } ++i; @@ -251,9 +257,9 @@ struct ProfileManifest } }; -static std::map -builtPathsPerInstallable( - const std::vector, BuiltPath>> & builtPaths) +static std::map builtPathsPerInstallable( + const std::vector, BuiltPath>> & + builtPaths) { std::map res; for (auto & [installable, builtPath] : builtPaths) @@ -261,11 +267,11 @@ builtPathsPerInstallable( return res; } -struct CmdProfileInstall : InstallablesCommand, MixDefaultProfile -{ +struct CmdProfileInstall : InstallablesCommand, MixDefaultProfile { std::optional priority; - CmdProfileInstall() { + CmdProfileInstall() + { addFlag({ .longName = "priority", .description = "The priority of the package to install.", @@ -282,43 +288,41 @@ struct CmdProfileInstall : InstallablesCommand, MixDefaultProfile std::string doc() override { return - #include "profile-install.md" - ; +#include "profile-install.md" + ; } void run(ref store) override { ProfileManifest manifest(*getEvalState(), *profile); - auto builtPaths = builtPathsPerInstallable( - Installable::build2( - getEvalStore(), store, Realise::Outputs, installables, bmNormal)); + auto builtPaths = builtPathsPerInstallable(Installable::build2( + getEvalStore(), store, Realise::Outputs, installables, bmNormal)); for (auto & installable : installables) { ProfileElement element; - - - if (auto installable2 = std::dynamic_pointer_cast(installable)) { + if (auto installable2 = + std::dynamic_pointer_cast(installable)) { // FIXME: make build() return this? - auto [attrPath, resolvedRef, drv] = installable2->toDerivation(); - element.source = ProfileElementSource { - installable2->flakeRef, - resolvedRef, - attrPath, - installable2->outputsSpec - }; - - if(drv.priority) { + auto [attrPath, resolvedRef, drv] = + installable2->toDerivation(); + element.source = + ProfileElementSource{installable2->flakeRef, resolvedRef, + attrPath, installable2->outputsSpec}; + + if (drv.priority) { element.priority = *drv.priority; } } - if(priority) { // if --priority was specified we want to override the priority of the installable + if (priority) { // if --priority was specified we want to override + // the priority of the installable element.priority = *priority; }; - element.updateStorePaths(getEvalStore(), store, builtPaths[installable.get()]); + element.updateStorePaths(getEvalStore(), store, + builtPaths[installable.get()]); manifest.elements.push_back(std::move(element)); } @@ -327,20 +331,15 @@ struct CmdProfileInstall : InstallablesCommand, MixDefaultProfile } }; -class MixProfileElementMatchers : virtual Args -{ +class MixProfileElementMatchers : virtual Args { std::vector _matchers; -public: - - MixProfileElementMatchers() - { - expectArgs("elements", &_matchers); - } + public: + MixProfileElementMatchers() { expectArgs("elements", &_matchers); } struct RegexPattern { std::string pattern; - std::regex reg; + std::regex reg; }; typedef std::variant Matcher; @@ -354,22 +353,27 @@ class MixProfileElementMatchers : virtual Args else if (store->isStorePath(s)) res.push_back(s); else - res.push_back(RegexPattern{s,std::regex(s, std::regex::extended | std::regex::icase)}); + res.push_back(RegexPattern{ + s, + std::regex(s, std::regex::extended | std::regex::icase)}); } return res; } - bool matches(const Store & store, const ProfileElement & element, size_t pos, const std::vector & matchers) + bool matches(const Store & store, const ProfileElement & element, + size_t pos, const std::vector & matchers) { for (auto & matcher : matchers) { if (auto n = std::get_if(&matcher)) { - if (*n == pos) return true; + if (*n == pos) + return true; } else if (auto path = std::get_if(&matcher)) { - if (element.storePaths.count(store.parseStorePath(*path))) return true; + if (element.storePaths.count(store.parseStorePath(*path))) + return true; } else if (auto regex = std::get_if(&matcher)) { - if (element.source - && std::regex_match(element.source->attrPath, regex->reg)) + if (element.source && + std::regex_match(element.source->attrPath, regex->reg)) return true; } } @@ -378,8 +382,9 @@ class MixProfileElementMatchers : virtual Args } }; -struct CmdProfileRemove : virtual EvalCommand, MixDefaultProfile, MixProfileElementMatchers -{ +struct CmdProfileRemove : virtual EvalCommand, + MixDefaultProfile, + MixProfileElementMatchers { std::string description() override { return "remove packages from a profile"; @@ -388,8 +393,8 @@ struct CmdProfileRemove : virtual EvalCommand, MixDefaultProfile, MixProfileElem std::string doc() override { return - #include "profile-remove.md" - ; +#include "profile-remove.md" + ; } void run(ref store) override @@ -409,29 +414,31 @@ struct CmdProfileRemove : virtual EvalCommand, MixDefaultProfile, MixProfileElem } } - auto removedCount = oldManifest.elements.size() - newManifest.elements.size(); - printInfo("removed %d packages, kept %d packages", - removedCount, - newManifest.elements.size()); + auto removedCount = + oldManifest.elements.size() - newManifest.elements.size(); + printInfo("removed %d packages, kept %d packages", removedCount, + newManifest.elements.size()); if (removedCount == 0) { - for (auto matcher: matchers) { - if (const size_t * index = std::get_if(&matcher)){ + for (auto matcher : matchers) { + if (const size_t * index = std::get_if(&matcher)) { warn("'%d' is not a valid index", *index); - } else if (const Path * path = std::get_if(&matcher)){ + } else if (const Path * path = std::get_if(&matcher)) { warn("'%s' does not match any paths", *path); - } else if (const RegexPattern * regex = std::get_if(&matcher)){ + } else if (const RegexPattern * regex = + std::get_if(&matcher)) { warn("'%s' does not match any packages", regex->pattern); } } - warn ("Use 'nix profile list' to see the current profile."); + warn("Use 'nix profile list' to see the current profile."); } updateProfile(newManifest.build(store)); } }; -struct CmdProfileUpgrade : virtual SourceExprCommand, MixDefaultProfile, MixProfileElementMatchers -{ +struct CmdProfileUpgrade : virtual SourceExprCommand, + MixDefaultProfile, + MixProfileElementMatchers { std::string description() override { return "upgrade packages using their most recent flake"; @@ -440,8 +447,8 @@ struct CmdProfileUpgrade : virtual SourceExprCommand, MixDefaultProfile, MixProf std::string doc() override { return - #include "profile-upgrade.md" - ; +#include "profile-upgrade.md" + ; } void run(ref store) override @@ -457,38 +464,32 @@ struct CmdProfileUpgrade : virtual SourceExprCommand, MixDefaultProfile, MixProf for (size_t i = 0; i < manifest.elements.size(); ++i) { auto & element(manifest.elements[i]); - if (element.source - && !element.source->originalRef.input.isLocked() - && matches(*store, element, i, matchers)) - { + if (element.source && + !element.source->originalRef.input.isLocked() && + matches(*store, element, i, matchers)) { upgradedCount++; - Activity act(*logger, lvlChatty, actUnknown, + Activity act( + *logger, lvlChatty, actUnknown, fmt("checking '%s' for updates", element.source->attrPath)); auto installable = std::make_shared( - this, - getEvalState(), - FlakeRef(element.source->originalRef), - "", - element.source->outputs, - Strings{element.source->attrPath}, - Strings{}, - lockFlags); + this, getEvalState(), FlakeRef(element.source->originalRef), + "", element.source->outputs, + Strings{element.source->attrPath}, Strings{}, lockFlags); auto [attrPath, resolvedRef, drv] = installable->toDerivation(); - if (element.source->resolvedRef == resolvedRef) continue; + if (element.source->resolvedRef == resolvedRef) + continue; printInfo("upgrading '%s' from flake '%s' to '%s'", - element.source->attrPath, element.source->resolvedRef, resolvedRef); + element.source->attrPath, element.source->resolvedRef, + resolvedRef); - element.source = ProfileElementSource { - installable->flakeRef, - resolvedRef, - attrPath, - installable->outputsSpec - }; + element.source = + ProfileElementSource{installable->flakeRef, resolvedRef, + attrPath, installable->outputsSpec}; installables.push_back(installable); indices.push_back(i); @@ -497,43 +498,42 @@ struct CmdProfileUpgrade : virtual SourceExprCommand, MixDefaultProfile, MixProf if (upgradedCount == 0) { for (auto & matcher : matchers) { - if (const size_t * index = std::get_if(&matcher)){ + if (const size_t * index = std::get_if(&matcher)) { warn("'%d' is not a valid index", *index); - } else if (const Path * path = std::get_if(&matcher)){ + } else if (const Path * path = std::get_if(&matcher)) { warn("'%s' does not match any paths", *path); - } else if (const RegexPattern * regex = std::get_if(&matcher)){ + } else if (const RegexPattern * regex = + std::get_if(&matcher)) { warn("'%s' does not match any packages", regex->pattern); } } - warn ("Use 'nix profile list' to see the current profile."); + warn("Use 'nix profile list' to see the current profile."); } - auto builtPaths = builtPathsPerInstallable( - Installable::build2( - getEvalStore(), store, Realise::Outputs, installables, bmNormal)); + auto builtPaths = builtPathsPerInstallable(Installable::build2( + getEvalStore(), store, Realise::Outputs, installables, bmNormal)); for (size_t i = 0; i < installables.size(); ++i) { auto & installable = installables.at(i); auto & element = manifest.elements[indices.at(i)]; - element.updateStorePaths(getEvalStore(), store, builtPaths[installable.get()]); + element.updateStorePaths(getEvalStore(), store, + builtPaths[installable.get()]); } updateProfile(manifest.build(store)); } }; -struct CmdProfileList : virtual EvalCommand, virtual StoreCommand, MixDefaultProfile -{ - std::string description() override - { - return "list installed packages"; - } +struct CmdProfileList : virtual EvalCommand, + virtual StoreCommand, + MixDefaultProfile { + std::string description() override { return "list installed packages"; } std::string doc() override { return - #include "profile-list.md" - ; +#include "profile-list.md" + ; } void run(ref store) override @@ -542,16 +542,23 @@ struct CmdProfileList : virtual EvalCommand, virtual StoreCommand, MixDefaultPro for (size_t i = 0; i < manifest.elements.size(); ++i) { auto & element(manifest.elements[i]); - logger->cout("%d %s %s %s", i, - element.source ? element.source->originalRef.to_string() + "#" + element.source->attrPath + printOutputsSpec(element.source->outputs) : "-", - element.source ? element.source->resolvedRef.to_string() + "#" + element.source->attrPath + printOutputsSpec(element.source->outputs) : "-", - concatStringsSep(" ", store->printStorePathSet(element.storePaths))); + logger->cout( + "%d %s %s %s", i, + element.source ? element.source->originalRef.to_string() + "#" + + element.source->attrPath + + printOutputsSpec(element.source->outputs) + : "-", + element.source ? element.source->resolvedRef.to_string() + "#" + + element.source->attrPath + + printOutputsSpec(element.source->outputs) + : "-", + concatStringsSep(" ", + store->printStorePathSet(element.storePaths))); } } }; -struct CmdProfileDiffClosures : virtual StoreCommand, MixDefaultProfile -{ +struct CmdProfileDiffClosures : virtual StoreCommand, MixDefaultProfile { std::string description() override { return "show the closure difference between each version of a profile"; @@ -560,8 +567,8 @@ struct CmdProfileDiffClosures : virtual StoreCommand, MixDefaultProfile std::string doc() override { return - #include "profile-diff-closures.md" - ; +#include "profile-diff-closures.md" + ; } void run(ref store) override @@ -573,13 +580,14 @@ struct CmdProfileDiffClosures : virtual StoreCommand, MixDefaultProfile for (auto & gen : gens) { if (prevGen) { - if (!first) std::cout << "\n"; + if (!first) + std::cout << "\n"; first = false; - std::cout << fmt("Version %d -> %d:\n", prevGen->number, gen.number); + std::cout << fmt("Version %d -> %d:\n", prevGen->number, + gen.number); printClosureDiff(store, - store->followLinksToStorePath(prevGen->path), - store->followLinksToStorePath(gen.path), - " "); + store->followLinksToStorePath(prevGen->path), + store->followLinksToStorePath(gen.path), " "); } prevGen = gen; @@ -587,8 +595,9 @@ struct CmdProfileDiffClosures : virtual StoreCommand, MixDefaultProfile } }; -struct CmdProfileHistory : virtual StoreCommand, EvalCommand, MixDefaultProfile -{ +struct CmdProfileHistory : virtual StoreCommand, + EvalCommand, + MixDefaultProfile { std::string description() override { return "show all versions of a profile"; @@ -597,8 +606,8 @@ struct CmdProfileHistory : virtual StoreCommand, EvalCommand, MixDefaultProfile std::string doc() override { return - #include "profile-history.md" - ; +#include "profile-history.md" + ; } void run(ref store) override @@ -611,27 +620,25 @@ struct CmdProfileHistory : virtual StoreCommand, EvalCommand, MixDefaultProfile for (auto & gen : gens) { ProfileManifest manifest(*getEvalState(), gen.path); - if (!first) std::cout << "\n"; + if (!first) + std::cout << "\n"; first = false; - std::cout << fmt("Version %s%d" ANSI_NORMAL " (%s)%s:\n", - gen.number == curGen ? ANSI_GREEN : ANSI_BOLD, - gen.number, + std::cout << fmt( + "Version %s%d" ANSI_NORMAL " (%s)%s:\n", + gen.number == curGen ? ANSI_GREEN : ANSI_BOLD, gen.number, std::put_time(std::gmtime(&gen.creationTime), "%Y-%m-%d"), prevGen ? fmt(" <- %d", prevGen->first.number) : ""); ProfileManifest::printDiff( - prevGen ? prevGen->second : ProfileManifest(), - manifest, - " "); + prevGen ? prevGen->second : ProfileManifest(), manifest, " "); prevGen = {gen, std::move(manifest)}; } } }; -struct CmdProfileRollback : virtual StoreCommand, MixDefaultProfile, MixDryRun -{ +struct CmdProfileRollback : virtual StoreCommand, MixDefaultProfile, MixDryRun { std::optional version; CmdProfileRollback() @@ -646,14 +653,15 @@ struct CmdProfileRollback : virtual StoreCommand, MixDefaultProfile, MixDryRun std::string description() override { - return "roll back to the previous version or a specified version of a profile"; + return "roll back to the previous version or a specified version of a " + "profile"; } std::string doc() override { return - #include "profile-rollback.md" - ; +#include "profile-rollback.md" + ; } void run(ref store) override @@ -662,8 +670,9 @@ struct CmdProfileRollback : virtual StoreCommand, MixDefaultProfile, MixDryRun } }; -struct CmdProfileWipeHistory : virtual StoreCommand, MixDefaultProfile, MixDryRun -{ +struct CmdProfileWipeHistory : virtual StoreCommand, + MixDefaultProfile, + MixDryRun { std::optional minAge; CmdProfileWipeHistory() @@ -687,8 +696,8 @@ struct CmdProfileWipeHistory : virtual StoreCommand, MixDefaultProfile, MixDryRu std::string doc() override { return - #include "profile-wipe-history.md" - ; +#include "profile-wipe-history.md" + ; } void run(ref store) override @@ -700,31 +709,30 @@ struct CmdProfileWipeHistory : virtual StoreCommand, MixDefaultProfile, MixDryRu } }; -struct CmdProfile : NixMultiCommand -{ +struct CmdProfile : NixMultiCommand { CmdProfile() : MultiCommand({ {"install", []() { return make_ref(); }}, {"remove", []() { return make_ref(); }}, {"upgrade", []() { return make_ref(); }}, {"list", []() { return make_ref(); }}, - {"diff-closures", []() { return make_ref(); }}, + {"diff-closures", + []() { return make_ref(); }}, {"history", []() { return make_ref(); }}, {"rollback", []() { return make_ref(); }}, - {"wipe-history", []() { return make_ref(); }}, + {"wipe-history", + []() { return make_ref(); }}, }) - { } - - std::string description() override { - return "manage Nix profiles"; } + std::string description() override { return "manage Nix profiles"; } + std::string doc() override { return - #include "profile.md" - ; +#include "profile.md" + ; } void run() override diff --git a/src/nix/realisation.cc b/src/nix/realisation.cc index c9a7157cdcd6..3589addd1cbf 100644 --- a/src/nix/realisation.cc +++ b/src/nix/realisation.cc @@ -5,10 +5,11 @@ using namespace nix; -struct CmdRealisation : virtual NixMultiCommand -{ - CmdRealisation() : MultiCommand(RegisterCommand::getCommandsFor({"realisation"})) - { } +struct CmdRealisation : virtual NixMultiCommand { + CmdRealisation() + : MultiCommand(RegisterCommand::getCommandsFor({"realisation"})) + { + } std::string description() override { @@ -28,8 +29,7 @@ struct CmdRealisation : virtual NixMultiCommand static auto rCmdRealisation = registerCommand("realisation"); -struct CmdRealisationInfo : BuiltPathsCommand, MixJSON -{ +struct CmdRealisationInfo : BuiltPathsCommand, MixJSON { std::string description() override { return "query information about one or several realisations"; @@ -38,7 +38,7 @@ struct CmdRealisationInfo : BuiltPathsCommand, MixJSON std::string doc() override { return - #include "realisation/info.md" +#include "realisation/info.md" ; } @@ -51,7 +51,8 @@ struct CmdRealisationInfo : BuiltPathsCommand, MixJSON for (auto & builtPath : paths) { auto theseRealisations = builtPath.toRealisedPaths(*store); - realisations.insert(theseRealisations.begin(), theseRealisations.end()); + realisations.insert(theseRealisations.begin(), + theseRealisations.end()); } if (json) { @@ -61,18 +62,17 @@ struct CmdRealisationInfo : BuiltPathsCommand, MixJSON if (auto realisation = std::get_if(&path.raw)) currentPath = realisation->toJSON(); else - currentPath["opaquePath"] = store->printStorePath(path.path()); + currentPath["opaquePath"] = + store->printStorePath(path.path()); res.push_back(currentPath); } std::cout << res.dump(); - } - else { + } else { for (auto & path : realisations) { if (auto realisation = std::get_if(&path.raw)) { - std::cout << - realisation->id.to_string() << " " << - store->printStorePath(realisation->outPath); + std::cout << realisation->id.to_string() << " " + << store->printStorePath(realisation->outPath); } else std::cout << store->printStorePath(path.path()); @@ -82,4 +82,5 @@ struct CmdRealisationInfo : BuiltPathsCommand, MixJSON } }; -static auto rCmdRealisationInfo = registerCommand2({"realisation", "info"}); +static auto rCmdRealisationInfo = + registerCommand2({"realisation", "info"}); diff --git a/src/nix/registry.cc b/src/nix/registry.cc index c496f94f82a4..e8a36325d2d7 100644 --- a/src/nix/registry.cc +++ b/src/nix/registry.cc @@ -10,15 +10,12 @@ using namespace nix; using namespace nix::flake; - -class RegistryCommand : virtual Args -{ +class RegistryCommand : virtual Args { std::string registry_path; std::shared_ptr registry; -public: - + public: RegistryCommand() { addFlag({ @@ -31,7 +28,8 @@ class RegistryCommand : virtual Args std::shared_ptr getRegistry() { - if (registry) return registry; + if (registry) + return registry; if (registry_path.empty()) { registry = fetchers::getUserRegistry(); } else { @@ -50,18 +48,14 @@ class RegistryCommand : virtual Args } }; -struct CmdRegistryList : StoreCommand -{ - std::string description() override - { - return "list available Nix flakes"; - } +struct CmdRegistryList : StoreCommand { + std::string description() override { return "list available Nix flakes"; } std::string doc() override { return - #include "registry-list.md" - ; +#include "registry-list.md" + ; } void run(nix::ref store) override @@ -73,11 +67,12 @@ struct CmdRegistryList : StoreCommand for (auto & registry : registries) { for (auto & entry : registry->entries) { // FIXME: format nicely - logger->cout("%s %s %s", - registry->type == Registry::Flag ? "flags " : - registry->type == Registry::User ? "user " : - registry->type == Registry::System ? "system" : - "global", + logger->cout( + "%s %s %s", + registry->type == Registry::Flag ? "flags " + : registry->type == Registry::User ? "user " + : registry->type == Registry::System ? "system" + : "global", entry.from.toURLString(), entry.to.toURLString(attrsToQuery(entry.extraAttrs))); } @@ -85,8 +80,7 @@ struct CmdRegistryList : StoreCommand } }; -struct CmdRegistryAdd : MixEvalArgs, Command, RegistryCommand -{ +struct CmdRegistryAdd : MixEvalArgs, Command, RegistryCommand { std::string fromUrl, toUrl; std::string description() override @@ -97,8 +91,8 @@ struct CmdRegistryAdd : MixEvalArgs, Command, RegistryCommand std::string doc() override { return - #include "registry-add.md" - ; +#include "registry-add.md" + ; } CmdRegistryAdd() @@ -113,15 +107,15 @@ struct CmdRegistryAdd : MixEvalArgs, Command, RegistryCommand auto toRef = parseFlakeRef(toUrl); auto registry = getRegistry(); fetchers::Attrs extraAttrs; - if (toRef.subdir != "") extraAttrs["dir"] = toRef.subdir; + if (toRef.subdir != "") + extraAttrs["dir"] = toRef.subdir; registry->remove(fromRef.input); registry->add(fromRef.input, toRef.input, extraAttrs); registry->write(getRegistryPath()); } }; -struct CmdRegistryRemove : RegistryCommand, Command -{ +struct CmdRegistryRemove : RegistryCommand, Command { std::string url; std::string description() override @@ -132,14 +126,11 @@ struct CmdRegistryRemove : RegistryCommand, Command std::string doc() override { return - #include "registry-remove.md" - ; +#include "registry-remove.md" + ; } - CmdRegistryRemove() - { - expectArg("url", &url); - } + CmdRegistryRemove() { expectArg("url", &url); } void run() override { @@ -149,36 +140,34 @@ struct CmdRegistryRemove : RegistryCommand, Command } }; -struct CmdRegistryPin : RegistryCommand, EvalCommand -{ +struct CmdRegistryPin : RegistryCommand, EvalCommand { std::string url; std::string locked; std::string description() override { - return "pin a flake to its current version or to the current version of a flake URL"; + return "pin a flake to its current version or to the current version " + "of a flake URL"; } std::string doc() override { return - #include "registry-pin.md" - ; +#include "registry-pin.md" + ; } CmdRegistryPin() { expectArg("url", &url); - expectArgs({ - .label = "locked", - .optional = true, - .handler = {&locked}, - .completer = {[&](size_t, std::string_view prefix) { - completeFlakeRef(getStore(), prefix); - }} - }); + expectArgs({.label = "locked", + .optional = true, + .handler = {&locked}, + .completer = {[&](size_t, std::string_view prefix) { + completeFlakeRef(getStore(), prefix); + }}}); } void run(nix::ref store) override @@ -192,34 +181,31 @@ struct CmdRegistryPin : RegistryCommand, EvalCommand registry->remove(ref.input); auto [tree, resolved] = locked_ref.resolve(store).input.fetch(store); fetchers::Attrs extraAttrs; - if (ref.subdir != "") extraAttrs["dir"] = ref.subdir; + if (ref.subdir != "") + extraAttrs["dir"] = ref.subdir; registry->add(ref.input, resolved, extraAttrs); registry->write(getRegistryPath()); } }; -struct CmdRegistry : virtual NixMultiCommand -{ +struct CmdRegistry : virtual NixMultiCommand { CmdRegistry() : MultiCommand({ - {"list", []() { return make_ref(); }}, - {"add", []() { return make_ref(); }}, - {"remove", []() { return make_ref(); }}, - {"pin", []() { return make_ref(); }}, - }) + {"list", []() { return make_ref(); }}, + {"add", []() { return make_ref(); }}, + {"remove", []() { return make_ref(); }}, + {"pin", []() { return make_ref(); }}, + }) { } - std::string description() override - { - return "manage the flake registry"; - } + std::string description() override { return "manage the flake registry"; } std::string doc() override { return - #include "registry.md" - ; +#include "registry.md" + ; } Category category() override { return catSecondary; } diff --git a/src/nix/run.cc b/src/nix/run.cc index 45d2dfd0d295..580f8efa7b8b 100644 --- a/src/nix/run.cc +++ b/src/nix/run.cc @@ -22,9 +22,8 @@ std::string chrootHelperName = "__run_in_chroot"; namespace nix { -void runProgramInStore(ref store, - const std::string & program, - const Strings & args) +void runProgramInStore(ref store, const std::string & program, + const Strings & args) { stopProgressBar(); @@ -41,13 +40,18 @@ void runProgramInStore(ref store, auto store2 = store.dynamic_pointer_cast(); if (!store2) - throw Error("store '%s' is not a local store so it does not support command execution", store->getUri()); + throw Error("store '%s' is not a local store so it does not support " + "command execution", + store->getUri()); if (store->storeDir != store2->getRealStoreDir()) { - Strings helperArgs = { chrootHelperName, store->storeDir, store2->getRealStoreDir(), program }; - for (auto & arg : args) helperArgs.push_back(arg); + Strings helperArgs = {chrootHelperName, store->storeDir, + store2->getRealStoreDir(), program}; + for (auto & arg : args) + helperArgs.push_back(arg); - execv(getSelfExe().value_or("nix").c_str(), stringsToCharPtrs(helperArgs).data()); + execv(getSelfExe().value_or("nix").c_str(), + stringsToCharPtrs(helperArgs).data()); throw SysError("could not execute chroot helper"); } @@ -59,25 +63,26 @@ void runProgramInStore(ref store, } -struct CmdShell : InstallablesCommand, MixEnvironment -{ +struct CmdShell : InstallablesCommand, MixEnvironment { using InstallablesCommand::run; - std::vector command = { getEnv("SHELL").value_or("bash") }; + std::vector command = {getEnv("SHELL").value_or("bash")}; CmdShell() { - addFlag({ - .longName = "command", - .shortName = 'c', - .description = "Command and arguments to be executed, defaulting to `$SHELL`", - .labels = {"command", "args"}, - .handler = {[&](std::vector ss) { - if (ss.empty()) throw UsageError("--command requires at least one argument"); - command = ss; - }} - }); + addFlag( + {.longName = "command", + .shortName = 'c', + .description = + "Command and arguments to be executed, defaulting to `$SHELL`", + .labels = {"command", "args"}, + .handler = {[&](std::vector ss) { + if (ss.empty()) + throw UsageError( + "--command requires at least one argument"); + command = ss; + }}}); } std::string description() override @@ -88,33 +93,39 @@ struct CmdShell : InstallablesCommand, MixEnvironment std::string doc() override { return - #include "shell.md" - ; +#include "shell.md" + ; } void run(ref store) override { - auto outPaths = Installable::toStorePaths(getEvalStore(), store, Realise::Outputs, OperateOn::Output, installables); + auto outPaths = + Installable::toStorePaths(getEvalStore(), store, Realise::Outputs, + OperateOn::Output, installables); auto accessor = store->getFSAccessor(); std::unordered_set done; std::queue todo; - for (auto & path : outPaths) todo.push(path); + for (auto & path : outPaths) + todo.push(path); setEnviron(); - auto unixPath = tokenizeString(getEnv("PATH").value_or(""), ":"); + auto unixPath = + tokenizeString(getEnv("PATH").value_or(""), ":"); while (!todo.empty()) { auto path = todo.front(); todo.pop(); - if (!done.insert(path).second) continue; + if (!done.insert(path).second) + continue; if (true) unixPath.push_front(store->printStorePath(path) + "/bin"); - auto propPath = store->printStorePath(path) + "/nix-support/propagated-user-env-packages"; + auto propPath = store->printStorePath(path) + + "/nix-support/propagated-user-env-packages"; if (accessor->stat(propPath).type == FSAccessor::tRegular) { for (auto & p : tokenizeString(readFile(propPath))) todo.push(store->parseStorePath(p)); @@ -124,7 +135,8 @@ struct CmdShell : InstallablesCommand, MixEnvironment setenv("PATH", concatStringsSep(":", unixPath).c_str(), 1); Strings args; - for (auto & arg : command) args.push_back(arg); + for (auto & arg : command) + args.push_back(arg); runProgramInStore(store, *command.begin(), args); } @@ -132,31 +144,24 @@ struct CmdShell : InstallablesCommand, MixEnvironment static auto rCmdShell = registerCommand("shell"); -struct CmdRun : InstallableCommand -{ +struct CmdRun : InstallableCommand { using InstallableCommand::run; std::vector args; CmdRun() { - expectArgs({ - .label = "args", - .handler = {&args}, - .completer = completePath - }); + expectArgs( + {.label = "args", .handler = {&args}, .completer = completePath}); } - std::string description() override - { - return "run a Nix application"; - } + std::string description() override { return "run a Nix application"; } std::string doc() override { return - #include "run.md" - ; +#include "run.md" + ; } Strings getDefaultFlakeAttrPaths() override @@ -186,7 +191,8 @@ struct CmdRun : InstallableCommand auto app = installable->toApp(*state).resolve(getEvalStore(), store); Strings allArgs{app.program}; - for (auto & i : args) allArgs.push_back(i); + for (auto & i : args) + allArgs.push_back(i); runProgramInStore(store, app.program, allArgs); } @@ -194,7 +200,7 @@ struct CmdRun : InstallableCommand static auto rCmdRun = registerCommand("run"); -void chrootHelper(int argc, char * * argv) +void chrootHelper(int argc, char ** argv) { int p = 1; std::string storeDir = argv[p++]; @@ -228,25 +234,29 @@ void chrootHelper(int argc, char * * argv) createDirs(tmpDir + storeDir); - if (mount(realStoreDir.c_str(), (tmpDir + storeDir).c_str(), "", MS_BIND, 0) == -1) + if (mount(realStoreDir.c_str(), (tmpDir + storeDir).c_str(), "", + MS_BIND, 0) == -1) throw SysError("mounting '%s' on '%s'", realStoreDir, storeDir); for (auto entry : readDirectory("/")) { auto src = "/" + entry.name; Path dst = tmpDir + "/" + entry.name; - if (pathExists(dst)) continue; + if (pathExists(dst)) + continue; auto st = lstat(src); if (S_ISDIR(st.st_mode)) { if (mkdir(dst.c_str(), 0700) == -1) throw SysError("creating directory '%s'", dst); - if (mount(src.c_str(), dst.c_str(), "", MS_BIND | MS_REC, 0) == -1) + if (mount(src.c_str(), dst.c_str(), "", MS_BIND | MS_REC, 0) == + -1) throw SysError("mounting '%s' on '%s'", src, dst); } else if (S_ISLNK(st.st_mode)) createSymlink(readLink(src), dst); } char * cwd = getcwd(0, 0); - if (!cwd) throw SysError("getting current directory"); + if (!cwd) + throw SysError("getting current directory"); Finally freeCwd([&]() { free(cwd); }); if (chroot(tmpDir.c_str()) == -1) @@ -254,9 +264,9 @@ void chrootHelper(int argc, char * * argv) if (chdir(cwd) == -1) throw SysError("chdir to '%s' in chroot", cwd); - } else - if (mount(realStoreDir.c_str(), storeDir.c_str(), "", MS_BIND, 0) == -1) - throw SysError("mounting '%s' on '%s'", realStoreDir, storeDir); + } else if (mount(realStoreDir.c_str(), storeDir.c_str(), "", MS_BIND, 0) == + -1) + throw SysError("mounting '%s' on '%s'", realStoreDir, storeDir); writeFile("/proc/self/setgroups", "deny"); writeFile("/proc/self/uid_map", fmt("%d %d %d", uid, uid, 1)); @@ -267,6 +277,8 @@ void chrootHelper(int argc, char * * argv) throw SysError("unable to exec '%s'", cmd); #else - throw Error("mounting the Nix store on '%s' is not supported on this platform", storeDir); + throw Error( + "mounting the Nix store on '%s' is not supported on this platform", + storeDir); #endif } diff --git a/src/nix/run.hh b/src/nix/run.hh index 6180a87dd5a2..b4d3f9b7b355 100644 --- a/src/nix/run.hh +++ b/src/nix/run.hh @@ -4,8 +4,7 @@ namespace nix { -void runProgramInStore(ref store, - const std::string & program, - const Strings & args); +void runProgramInStore(ref store, const std::string & program, + const Strings & args); } diff --git a/src/nix/search.cc b/src/nix/search.cc index bdd45cbed957..6694c7d1c1d8 100644 --- a/src/nix/search.cc +++ b/src/nix/search.cc @@ -21,43 +21,36 @@ std::string wrap(std::string prefix, std::string s) return concatStrings(prefix, s, ANSI_NORMAL); } -struct CmdSearch : InstallableCommand, MixJSON -{ +struct CmdSearch : InstallableCommand, MixJSON { std::vector res; std::vector excludeRes; CmdSearch() { expectArgs("regex", &res); - addFlag(Flag { + addFlag(Flag{ .longName = "exclude", .shortName = 'e', - .description = "Hide packages whose attribute path, name or description contain *regex*.", + .description = "Hide packages whose attribute path, name or " + "description contain *regex*.", .labels = {"regex"}, - .handler = {[this](std::string s) { - excludeRes.push_back(s); - }}, + .handler = {[this](std::string s) { excludeRes.push_back(s); }}, }); } - std::string description() override - { - return "search for packages"; - } + std::string description() override { return "search for packages"; } std::string doc() override { return - #include "search.md" - ; +#include "search.md" + ; } Strings getDefaultFlakeAttrPaths() override { - return { - "packages." + settings.thisSystem.get() + ".", - "legacyPackages." + settings.thisSystem.get() + "." - }; + return {"packages." + settings.thisSystem.get() + ".", + "legacyPackages." + settings.thisSystem.get() + "."}; } void run(ref store) override @@ -66,8 +59,9 @@ struct CmdSearch : InstallableCommand, MixJSON evalSettings.enableImportFromDerivation.setDefault(false); // Empty search string should match all packages - // Use "^" here instead of ".*" due to differences in resulting highlighting - // (see #1893 -- libc++ claims empty search string is not in POSIX grammar) + // Use "^" here instead of ".*" due to differences in resulting + // highlighting (see #1893 -- libc++ claims empty search string is not + // in POSIX grammar) if (res.empty()) res.push_back("^"); @@ -77,10 +71,12 @@ struct CmdSearch : InstallableCommand, MixJSON excludeRegexes.reserve(excludeRes.size()); for (auto & re : res) - regexes.push_back(std::regex(re, std::regex::extended | std::regex::icase)); + regexes.push_back( + std::regex(re, std::regex::extended | std::regex::icase)); for (auto & re : excludeRes) - excludeRegexes.emplace_back(re, std::regex::extended | std::regex::icase); + excludeRegexes.emplace_back(re, std::regex::extended | + std::regex::icase); auto state = getEvalState(); @@ -88,17 +84,20 @@ struct CmdSearch : InstallableCommand, MixJSON uint64_t results = 0; - std::function & attrPath, bool initialRecurse)> visit; + std::function & attrPath, + bool initialRecurse)> + visit; - visit = [&](eval_cache::AttrCursor & cursor, const std::vector & attrPath, bool initialRecurse) - { + visit = [&](eval_cache::AttrCursor & cursor, + const std::vector & attrPath, bool initialRecurse) { auto attrPathS = state->symbols.resolve(attrPath); - Activity act(*logger, lvlInfo, actUnknown, + Activity act( + *logger, lvlInfo, actUnknown, fmt("evaluating '%s'", concatStringsSep(".", attrPathS))); try { - auto recurse = [&]() - { + auto recurse = [&]() { for (const auto & attr : cursor.getAttrs()) { auto cursor2 = cursor.getAttr(state->symbols[attr]); auto attrPath2(attrPath); @@ -111,9 +110,13 @@ struct CmdSearch : InstallableCommand, MixJSON DrvName name(cursor.getAttr(state->sName)->getString()); auto aMeta = cursor.maybeGetAttr(state->sMeta); - auto aDescription = aMeta ? aMeta->maybeGetAttr(state->sDescription) : nullptr; - auto description = aDescription ? aDescription->getString() : ""; - std::replace(description.begin(), description.end(), '\n', ' '); + auto aDescription = + aMeta ? aMeta->maybeGetAttr(state->sDescription) + : nullptr; + auto description = + aDescription ? aDescription->getString() : ""; + std::replace(description.begin(), description.end(), '\n', + ' '); auto attrPath2 = concatStringsSep(".", attrPathS); std::vector attrPathMatches; @@ -122,16 +125,16 @@ struct CmdSearch : InstallableCommand, MixJSON bool found = false; for (auto & regex : excludeRegexes) { - if ( - std::regex_search(attrPath2, regex) - || std::regex_search(name.name, regex) - || std::regex_search(description, regex)) + if (std::regex_search(attrPath2, regex) || + std::regex_search(name.name, regex) || + std::regex_search(description, regex)) return; } for (auto & regex : regexes) { found = false; - auto addAll = [&found](std::sregex_iterator it, std::vector & vec) { + auto addAll = [&found](std::sregex_iterator it, + std::vector & vec) { const auto end = std::sregex_iterator(); while (it != end) { vec.push_back(*it++); @@ -139,16 +142,21 @@ struct CmdSearch : InstallableCommand, MixJSON } }; - addAll(std::sregex_iterator(attrPath2.begin(), attrPath2.end(), regex), attrPathMatches); - addAll(std::sregex_iterator(name.name.begin(), name.name.end(), regex), nameMatches); - addAll(std::sregex_iterator(description.begin(), description.end(), regex), descriptionMatches); + addAll(std::sregex_iterator(attrPath2.begin(), + attrPath2.end(), regex), + attrPathMatches); + addAll(std::sregex_iterator(name.name.begin(), + name.name.end(), regex), + nameMatches); + addAll(std::sregex_iterator(description.begin(), + description.end(), regex), + descriptionMatches); if (!found) break; } - if (found) - { + if (found) { results++; if (json) { auto jsonElem = jsonOut->object(attrPath2); @@ -156,30 +164,39 @@ struct CmdSearch : InstallableCommand, MixJSON jsonElem.attr("version", name.version); jsonElem.attr("description", description); } else { - auto name2 = hiliteMatches(name.name, nameMatches, ANSI_GREEN, "\e[0;2m"); - if (results > 1) logger->cout(""); + auto name2 = hiliteMatches(name.name, nameMatches, + ANSI_GREEN, "\e[0;2m"); + if (results > 1) + logger->cout(""); logger->cout( "* %s%s", - wrap("\e[0;1m", hiliteMatches(attrPath2, attrPathMatches, ANSI_GREEN, "\e[0;1m")), - name.version != "" ? " (" + name.version + ")" : ""); + wrap("\e[0;1m", + hiliteMatches(attrPath2, attrPathMatches, + ANSI_GREEN, "\e[0;1m")), + name.version != "" ? " (" + name.version + ")" + : ""); if (description != "") logger->cout( - " %s", hiliteMatches(description, descriptionMatches, ANSI_GREEN, ANSI_NORMAL)); + " %s", hiliteMatches( + description, descriptionMatches, + ANSI_GREEN, ANSI_NORMAL)); } } } - else if ( - attrPath.size() == 0 - || (attrPathS[0] == "legacyPackages" && attrPath.size() <= 2) - || (attrPathS[0] == "packages" && attrPath.size() <= 2)) + else if (attrPath.size() == 0 || + (attrPathS[0] == "legacyPackages" && + attrPath.size() <= 2) || + (attrPathS[0] == "packages" && attrPath.size() <= 2)) recurse(); else if (initialRecurse) recurse(); - else if (attrPathS[0] == "legacyPackages" && attrPath.size() > 2) { - auto attr = cursor.maybeGetAttr(state->sRecurseForDerivations); + else if (attrPathS[0] == "legacyPackages" && + attrPath.size() > 2) { + auto attr = + cursor.maybeGetAttr(state->sRecurseForDerivations); if (attr && attr->getBool()) recurse(); } diff --git a/src/nix/show-config.cc b/src/nix/show-config.cc index 29944e7488a7..ed4c89192f07 100644 --- a/src/nix/show-config.cc +++ b/src/nix/show-config.cc @@ -7,12 +7,8 @@ using namespace nix; -struct CmdShowConfig : Command, MixJSON -{ - std::string description() override - { - return "show the Nix configuration"; - } +struct CmdShowConfig : Command, MixJSON { + std::string description() override { return "show the Nix configuration"; } Category category() override { return catUtility; } diff --git a/src/nix/show-derivation.cc b/src/nix/show-derivation.cc index fb46b4dbf34f..ff9487c40a46 100644 --- a/src/nix/show-derivation.cc +++ b/src/nix/show-derivation.cc @@ -10,18 +10,16 @@ using namespace nix; -struct CmdShowDerivation : InstallablesCommand -{ +struct CmdShowDerivation : InstallablesCommand { bool recursive = false; CmdShowDerivation() { - addFlag({ - .longName = "recursive", - .shortName = 'r', - .description = "Include the dependencies of the specified derivations.", - .handler = {&recursive, true} - }); + addFlag({.longName = "recursive", + .shortName = 'r', + .description = + "Include the dependencies of the specified derivations.", + .handler = {&recursive, true}}); } std::string description() override @@ -32,8 +30,8 @@ struct CmdShowDerivation : InstallablesCommand std::string doc() override { return - #include "show-derivation.md" - ; +#include "show-derivation.md" + ; } Category category() override { return catUtility; } @@ -50,76 +48,96 @@ struct CmdShowDerivation : InstallablesCommand { - JSONObject jsonRoot(std::cout, true); - - for (auto & drvPath : drvPaths) { - if (!drvPath.isDerivation()) continue; - - auto drvObj(jsonRoot.object(store->printStorePath(drvPath))); - - auto drv = store->readDerivation(drvPath); - - { - auto outputsObj(drvObj.object("outputs")); - for (auto & [_outputName, output] : drv.outputs) { - auto & outputName = _outputName; // work around clang bug - auto outputObj { outputsObj.object(outputName) }; - std::visit(overloaded { - [&](const DerivationOutput::InputAddressed & doi) { - outputObj.attr("path", store->printStorePath(doi.path)); - }, - [&](const DerivationOutput::CAFixed & dof) { - outputObj.attr("path", store->printStorePath(dof.path(*store, drv.name, outputName))); - outputObj.attr("hashAlgo", dof.hash.printMethodAlgo()); - outputObj.attr("hash", dof.hash.hash.to_string(Base16, false)); - }, - [&](const DerivationOutput::CAFloating & dof) { - outputObj.attr("hashAlgo", makeFileIngestionPrefix(dof.method) + printHashType(dof.hashType)); - }, - [&](const DerivationOutput::Deferred &) {}, - [&](const DerivationOutput::Impure & doi) { - outputObj.attr("hashAlgo", makeFileIngestionPrefix(doi.method) + printHashType(doi.hashType)); - outputObj.attr("impure", true); - }, - }, output.raw()); + JSONObject jsonRoot(std::cout, true); + + for (auto & drvPath : drvPaths) { + if (!drvPath.isDerivation()) + continue; + + auto drvObj(jsonRoot.object(store->printStorePath(drvPath))); + + auto drv = store->readDerivation(drvPath); + + { + auto outputsObj(drvObj.object("outputs")); + for (auto & [_outputName, output] : drv.outputs) { + auto & outputName = + _outputName; // work around clang bug + auto outputObj{outputsObj.object(outputName)}; + std::visit( + overloaded{ + [&](const DerivationOutput::InputAddressed & + doi) { + outputObj.attr( + "path", + store->printStorePath(doi.path)); + }, + [&](const DerivationOutput::CAFixed & dof) { + outputObj.attr( + "path", + store->printStorePath(dof.path( + *store, drv.name, outputName))); + outputObj.attr("hashAlgo", + dof.hash.printMethodAlgo()); + outputObj.attr( + "hash", + dof.hash.hash.to_string(Base16, false)); + }, + [&](const DerivationOutput::CAFloating & dof) { + outputObj.attr( + "hashAlgo", + makeFileIngestionPrefix(dof.method) + + printHashType(dof.hashType)); + }, + [&](const DerivationOutput::Deferred &) {}, + [&](const DerivationOutput::Impure & doi) { + outputObj.attr( + "hashAlgo", + makeFileIngestionPrefix(doi.method) + + printHashType(doi.hashType)); + outputObj.attr("impure", true); + }, + }, + output.raw()); + } } - } - { - auto inputsList(drvObj.list("inputSrcs")); - for (auto & input : drv.inputSrcs) - inputsList.elem(store->printStorePath(input)); - } + { + auto inputsList(drvObj.list("inputSrcs")); + for (auto & input : drv.inputSrcs) + inputsList.elem(store->printStorePath(input)); + } - { - auto inputDrvsObj(drvObj.object("inputDrvs")); - for (auto & input : drv.inputDrvs) { - auto inputList(inputDrvsObj.list(store->printStorePath(input.first))); - for (auto & outputId : input.second) - inputList.elem(outputId); + { + auto inputDrvsObj(drvObj.object("inputDrvs")); + for (auto & input : drv.inputDrvs) { + auto inputList(inputDrvsObj.list( + store->printStorePath(input.first))); + for (auto & outputId : input.second) + inputList.elem(outputId); + } } - } - drvObj.attr("system", drv.platform); - drvObj.attr("builder", drv.builder); + drvObj.attr("system", drv.platform); + drvObj.attr("builder", drv.builder); - { - auto argsList(drvObj.list("args")); - for (auto & arg : drv.args) - argsList.elem(arg); - } + { + auto argsList(drvObj.list("args")); + for (auto & arg : drv.args) + argsList.elem(arg); + } - { - auto envObj(drvObj.object("env")); - for (auto & var : drv.env) - envObj.attr(var.first, var.second); + { + auto envObj(drvObj.object("env")); + for (auto & var : drv.env) + envObj.attr(var.first, var.second); + } } } - } - std::cout << "\n"; } }; -static auto rCmdShowDerivation = registerCommand("show-derivation"); +static auto rCmdShowDerivation = + registerCommand("show-derivation"); diff --git a/src/nix/sigs.cc b/src/nix/sigs.cc index 3d659d6d2410..c3083d4e3b1c 100644 --- a/src/nix/sigs.cc +++ b/src/nix/sigs.cc @@ -7,8 +7,7 @@ using namespace nix; -struct CmdCopySigs : StorePathsCommand -{ +struct CmdCopySigs : StorePathsCommand { Strings substituterUris; CmdCopySigs() @@ -30,7 +29,8 @@ struct CmdCopySigs : StorePathsCommand void run(ref store, StorePaths && storePaths) override { if (substituterUris.empty()) - throw UsageError("you must specify at least one substituter using '-s'"); + throw UsageError( + "you must specify at least one substituter using '-s'"); // FIXME: factor out commonality with MixVerify. std::vector> substituters; @@ -42,10 +42,11 @@ struct CmdCopySigs : StorePathsCommand std::string doneLabel = "done"; std::atomic added{0}; - //logger->setExpected(doneLabel, storePaths.size()); + // logger->setExpected(doneLabel, storePaths.size()); auto doPath = [&](const Path & storePathS) { - //Activity act(*logger, lvlInfo, format("getting signatures for '%s'") % storePath); + // Activity act(*logger, lvlInfo, format("getting signatures for + // '%s'") % storePath); checkInterrupt(); @@ -78,7 +79,7 @@ struct CmdCopySigs : StorePathsCommand added += newSigs.size(); } - //logger->incProgress(doneLabel); + // logger->incProgress(doneLabel); }; for (auto & storePath : storePaths) @@ -90,28 +91,23 @@ struct CmdCopySigs : StorePathsCommand } }; -static auto rCmdCopySigs = registerCommand2({"store", "copy-sigs"}); +static auto rCmdCopySigs = + registerCommand2({"store", "copy-sigs"}); -struct CmdSign : StorePathsCommand -{ +struct CmdSign : StorePathsCommand { Path secretKeyFile; CmdSign() { - addFlag({ - .longName = "key-file", - .shortName = 'k', - .description = "File containing the secret signing key.", - .labels = {"file"}, - .handler = {&secretKeyFile}, - .completer = completePath - }); + addFlag({.longName = "key-file", + .shortName = 'k', + .description = "File containing the secret signing key.", + .labels = {"file"}, + .handler = {&secretKeyFile}, + .completer = completePath}); } - std::string description() override - { - return "sign store paths"; - } + std::string description() override { return "sign store paths"; } void run(ref store, StorePaths && storePaths) override { @@ -142,15 +138,15 @@ struct CmdSign : StorePathsCommand static auto rCmdSign = registerCommand2({"store", "sign"}); -struct CmdKeyGenerateSecret : Command -{ +struct CmdKeyGenerateSecret : Command { std::optional keyName; CmdKeyGenerateSecret() { addFlag({ .longName = "key-name", - .description = "Identifier of the key (e.g. `cache.example.org-1`).", + .description = + "Identifier of the key (e.g. `cache.example.org-1`).", .labels = {"name"}, .handler = {&keyName}, }); @@ -164,8 +160,8 @@ struct CmdKeyGenerateSecret : Command std::string doc() override { return - #include "key-generate-secret.md" - ; +#include "key-generate-secret.md" + ; } void run() override @@ -177,18 +173,18 @@ struct CmdKeyGenerateSecret : Command } }; -struct CmdKeyConvertSecretToPublic : Command -{ +struct CmdKeyConvertSecretToPublic : Command { std::string description() override { - return "generate a public key for verifying store paths from a secret key read from standard input"; + return "generate a public key for verifying store paths from a secret " + "key read from standard input"; } std::string doc() override { return - #include "key-convert-secret-to-public.md" - ; +#include "key-convert-secret-to-public.md" + ; } void run() override @@ -198,13 +194,14 @@ struct CmdKeyConvertSecretToPublic : Command } }; -struct CmdKey : NixMultiCommand -{ +struct CmdKey : NixMultiCommand { CmdKey() : MultiCommand({ - {"generate-secret", []() { return make_ref(); }}, - {"convert-secret-to-public", []() { return make_ref(); }}, - }) + {"generate-secret", + []() { return make_ref(); }}, + {"convert-secret-to-public", + []() { return make_ref(); }}, + }) { } diff --git a/src/nix/store-copy-log.cc b/src/nix/store-copy-log.cc index 2e288f743917..5aae73e34418 100644 --- a/src/nix/store-copy-log.cc +++ b/src/nix/store-copy-log.cc @@ -10,8 +10,7 @@ using namespace nix; -struct CmdCopyLog : virtual CopyCommand, virtual InstallablesCommand -{ +struct CmdCopyLog : virtual CopyCommand, virtual InstallablesCommand { std::string description() override { return "copy build logs between Nix stores"; @@ -20,8 +19,8 @@ struct CmdCopyLog : virtual CopyCommand, virtual InstallablesCommand std::string doc() override { return - #include "store-copy-log.md" - ; +#include "store-copy-log.md" + ; } Category category() override { return catUtility; } @@ -43,7 +42,8 @@ struct CmdCopyLog : virtual CopyCommand, virtual InstallablesCommand if (auto log = srcLogStore.getBuildLog(drvPath)) dstLogStore.addBuildLog(drvPath, *log); else - throw Error("build log for '%s' is not available", srcStore->printStorePath(drvPath)); + throw Error("build log for '%s' is not available", + srcStore->printStorePath(drvPath)); } } }; diff --git a/src/nix/store-delete.cc b/src/nix/store-delete.cc index ca43f1530eef..85f0e3d61f90 100644 --- a/src/nix/store-delete.cc +++ b/src/nix/store-delete.cc @@ -7,17 +7,16 @@ using namespace nix; -struct CmdStoreDelete : StorePathsCommand -{ - GCOptions options { .action = GCOptions::gcDeleteSpecific }; +struct CmdStoreDelete : StorePathsCommand { + GCOptions options{.action = GCOptions::gcDeleteSpecific}; CmdStoreDelete() { - addFlag({ - .longName = "ignore-liveness", - .description = "Do not check whether the paths are reachable from a root.", - .handler = {&options.ignoreLiveness, true} - }); + addFlag( + {.longName = "ignore-liveness", + .description = + "Do not check whether the paths are reachable from a root.", + .handler = {&options.ignoreLiveness, true}}); } std::string description() override @@ -28,8 +27,8 @@ struct CmdStoreDelete : StorePathsCommand std::string doc() override { return - #include "store-delete.md" - ; +#include "store-delete.md" + ; } void run(ref store, std::vector && storePaths) override @@ -45,4 +44,5 @@ struct CmdStoreDelete : StorePathsCommand } }; -static auto rCmdStoreDelete = registerCommand2({"store", "delete"}); +static auto rCmdStoreDelete = + registerCommand2({"store", "delete"}); diff --git a/src/nix/store-gc.cc b/src/nix/store-gc.cc index 8b9b5d1642a8..fff059d672d0 100644 --- a/src/nix/store-gc.cc +++ b/src/nix/store-gc.cc @@ -7,18 +7,15 @@ using namespace nix; -struct CmdStoreGC : StoreCommand, MixDryRun -{ +struct CmdStoreGC : StoreCommand, MixDryRun { GCOptions options; CmdStoreGC() { - addFlag({ - .longName = "max", - .description = "Stop after freeing *n* bytes of disk space.", - .labels = {"n"}, - .handler = {&options.maxFreed} - }); + addFlag({.longName = "max", + .description = "Stop after freeing *n* bytes of disk space.", + .labels = {"n"}, + .handler = {&options.maxFreed}}); } std::string description() override @@ -29,15 +26,16 @@ struct CmdStoreGC : StoreCommand, MixDryRun std::string doc() override { return - #include "store-gc.md" - ; +#include "store-gc.md" + ; } void run(ref store) override { auto & gcStore = require(*store); - options.action = dryRun ? GCOptions::gcReturnDead : GCOptions::gcDeleteDead; + options.action = + dryRun ? GCOptions::gcReturnDead : GCOptions::gcDeleteDead; GCResults results; PrintFreed freed(options.action == GCOptions::gcDeleteDead, results); gcStore.collectGarbage(options, results); diff --git a/src/nix/store-repair.cc b/src/nix/store-repair.cc index 8fcb3639a43f..cf83e7634445 100644 --- a/src/nix/store-repair.cc +++ b/src/nix/store-repair.cc @@ -3,18 +3,14 @@ using namespace nix; -struct CmdStoreRepair : StorePathsCommand -{ - std::string description() override - { - return "repair store paths"; - } +struct CmdStoreRepair : StorePathsCommand { + std::string description() override { return "repair store paths"; } std::string doc() override { return - #include "store-repair.md" - ; +#include "store-repair.md" + ; } void run(ref store, std::vector && storePaths) override @@ -24,4 +20,5 @@ struct CmdStoreRepair : StorePathsCommand } }; -static auto rStoreRepair = registerCommand2({"store", "repair"}); +static auto rStoreRepair = + registerCommand2({"store", "repair"}); diff --git a/src/nix/store.cc b/src/nix/store.cc index 44e53c7c7586..a9b27816e4a9 100644 --- a/src/nix/store.cc +++ b/src/nix/store.cc @@ -2,15 +2,10 @@ using namespace nix; -struct CmdStore : virtual NixMultiCommand -{ - CmdStore() : MultiCommand(RegisterCommand::getCommandsFor({"store"})) - { } +struct CmdStore : virtual NixMultiCommand { + CmdStore() : MultiCommand(RegisterCommand::getCommandsFor({"store"})) {} - std::string description() override - { - return "manipulate a Nix store"; - } + std::string description() override { return "manipulate a Nix store"; } Category category() override { return catUtility; } diff --git a/src/nix/upgrade-nix.cc b/src/nix/upgrade-nix.cc index 2d2453395f3a..fb5b55582efe 100644 --- a/src/nix/upgrade-nix.cc +++ b/src/nix/upgrade-nix.cc @@ -9,27 +9,25 @@ using namespace nix; -struct CmdUpgradeNix : MixDryRun, StoreCommand -{ +struct CmdUpgradeNix : MixDryRun, StoreCommand { Path profileDir; - std::string storePathsUrl = "https://github.com/NixOS/nixpkgs/raw/master/nixos/modules/installer/tools/nix-fallback-paths.nix"; + std::string storePathsUrl = + "https://github.com/NixOS/nixpkgs/raw/master/nixos/modules/installer/" + "tools/nix-fallback-paths.nix"; CmdUpgradeNix() { - addFlag({ - .longName = "profile", - .shortName = 'p', - .description = "The path to the Nix profile to upgrade.", - .labels = {"profile-dir"}, - .handler = {&profileDir} - }); - - addFlag({ - .longName = "nix-store-paths-url", - .description = "The URL of the file that contains the store paths of the latest Nix release.", - .labels = {"url"}, - .handler = {&storePathsUrl} - }); + addFlag({.longName = "profile", + .shortName = 'p', + .description = "The path to the Nix profile to upgrade.", + .labels = {"profile-dir"}, + .handler = {&profileDir}}); + + addFlag({.longName = "nix-store-paths-url", + .description = "The URL of the file that contains the store " + "paths of the latest Nix release.", + .labels = {"url"}, + .handler = {&storePathsUrl}}); } std::string description() override @@ -40,8 +38,8 @@ struct CmdUpgradeNix : MixDryRun, StoreCommand std::string doc() override { return - #include "upgrade-nix.md" - ; +#include "upgrade-nix.md" + ; } Category category() override { return catNixInstallation; } @@ -66,12 +64,16 @@ struct CmdUpgradeNix : MixDryRun, StoreCommand } { - Activity act(*logger, lvlInfo, actUnknown, fmt("downloading '%s'...", store->printStorePath(storePath))); + Activity act( + *logger, lvlInfo, actUnknown, + fmt("downloading '%s'...", store->printStorePath(storePath))); store->ensurePath(storePath); } { - Activity act(*logger, lvlInfo, actUnknown, fmt("verifying that '%s' works...", store->printStorePath(storePath))); + Activity act(*logger, lvlInfo, actUnknown, + fmt("verifying that '%s' works...", + store->printStorePath(storePath))); auto program = store->printStorePath(storePath) + "/bin/nix-env"; auto s = runProgram(program, false, {"--version"}); if (s.find("Nix") == std::string::npos) @@ -82,9 +84,11 @@ struct CmdUpgradeNix : MixDryRun, StoreCommand { Activity act(*logger, lvlInfo, actUnknown, - fmt("installing '%s' into profile '%s'...", store->printStorePath(storePath), profileDir)); + fmt("installing '%s' into profile '%s'...", + store->printStorePath(storePath), profileDir)); runProgram(settings.nixBinDir + "/nix-env", false, - {"--profile", profileDir, "-i", store->printStorePath(storePath), "--no-sandbox"}); + {"--profile", profileDir, "-i", + store->printStorePath(storePath), "--no-sandbox"}); } printInfo(ANSI_GREEN "upgrade to version %s done" ANSI_NORMAL, version); @@ -95,14 +99,16 @@ struct CmdUpgradeNix : MixDryRun, StoreCommand { Path where; - for (auto & dir : tokenizeString(getEnv("PATH").value_or(""), ":")) + for (auto & dir : + tokenizeString(getEnv("PATH").value_or(""), ":")) if (pathExists(dir + "/nix-env")) { where = dir; break; } if (where == "") - throw Error("couldn't figure out how Nix is installed, so I can't upgrade it"); + throw Error("couldn't figure out how Nix is installed, so I can't " + "upgrade it"); printInfo("found Nix in '%s'", where); @@ -112,7 +118,8 @@ struct CmdUpgradeNix : MixDryRun, StoreCommand Path profileDir = dirOf(where); // Resolve profile to /nix/var/nix/profiles/ link. - while (canonPath(profileDir).find("/profiles/") == std::string::npos && isLink(profileDir)) + while (canonPath(profileDir).find("/profiles/") == std::string::npos && + isLink(profileDir)) profileDir = readLink(profileDir); printInfo("found profile '%s'", profileDir); @@ -121,7 +128,9 @@ struct CmdUpgradeNix : MixDryRun, StoreCommand if (baseNameOf(where) != "bin" || !hasSuffix(userEnv, "user-environment")) - throw Error("directory '%s' does not appear to be part of a Nix profile", where); + throw Error( + "directory '%s' does not appear to be part of a Nix profile", + where); if (!store->isValidPath(store->parseStorePath(userEnv))) throw Error("directory '%s' is not in the Nix store", userEnv); @@ -132,7 +141,8 @@ struct CmdUpgradeNix : MixDryRun, StoreCommand /* Return the store path of the latest stable Nix. */ StorePath getLatestNix(ref store) { - Activity act(*logger, lvlInfo, actUnknown, "querying latest Nix version"); + Activity act(*logger, lvlInfo, actUnknown, + "querying latest Nix version"); // FIXME: use nixos.org? auto req = FileTransferRequest(storePathsUrl); @@ -142,7 +152,8 @@ struct CmdUpgradeNix : MixDryRun, StoreCommand auto v = state->allocValue(); state->eval(state->parseExprFromString(res.data, "/no-such-path"), *v); Bindings & bindings(*state->allocBindings(0)); - auto v2 = findAlongAttrPath(*state, settings.thisSystem, bindings, *v).first; + auto v2 = + findAlongAttrPath(*state, settings.thisSystem, bindings, *v).first; return store->parseStorePath(state->forceString(*v2)); } diff --git a/src/nix/verify.cc b/src/nix/verify.cc index e92df1303144..e7e21ad7fbc5 100644 --- a/src/nix/verify.cc +++ b/src/nix/verify.cc @@ -9,8 +9,7 @@ using namespace nix; -struct CmdVerify : StorePathsCommand -{ +struct CmdVerify : StorePathsCommand { bool noContents = false; bool noTrust = false; Strings substituterUris; @@ -30,21 +29,20 @@ struct CmdVerify : StorePathsCommand .handler = {&noTrust, true}, }); - addFlag({ - .longName = "substituter", - .shortName = 's', - .description = "Use signatures from the specified store.", - .labels = {"store-uri"}, - .handler = {[&](std::string s) { substituterUris.push_back(s); }} - }); - - addFlag({ - .longName = "sigs-needed", - .shortName = 'n', - .description = "Require that each path has at least *n* valid signatures.", - .labels = {"n"}, - .handler = {&sigsNeeded} - }); + addFlag({.longName = "substituter", + .shortName = 's', + .description = "Use signatures from the specified store.", + .labels = {"store-uri"}, + .handler = { + [&](std::string s) { substituterUris.push_back(s); }}}); + + addFlag( + {.longName = "sigs-needed", + .shortName = 'n', + .description = + "Require that each path has at least *n* valid signatures.", + .labels = {"n"}, + .handler = {&sigsNeeded}}); } std::string description() override @@ -55,8 +53,8 @@ struct CmdVerify : StorePathsCommand std::string doc() override { return - #include "verify.md" - ; +#include "verify.md" + ; } void run(ref store, StorePaths && storePaths) override @@ -88,12 +86,15 @@ struct CmdVerify : StorePathsCommand MaintainCount> mcActive(active); update(); - auto info = store->queryPathInfo(store->parseStorePath(storePath)); + auto info = + store->queryPathInfo(store->parseStorePath(storePath)); // Note: info->path can be different from storePath // for binary cache stores when using --all (since we // can't enumerate names efficiently). - Activity act2(*logger, lvlInfo, actUnknown, fmt("checking '%s'", store->printStorePath(info->path))); + Activity act2( + *logger, lvlInfo, actUnknown, + fmt("checking '%s'", store->printStorePath(info->path))); if (!noContents) { @@ -105,11 +106,13 @@ struct CmdVerify : StorePathsCommand if (hash.first != info->narHash) { corrupted++; - act2.result(resCorruptedPath, store->printStorePath(info->path)); - printError("path '%s' was modified! expected hash '%s', got '%s'", - store->printStorePath(info->path), - info->narHash.to_string(Base32, true), - hash.first.to_string(Base32, true)); + act2.result(resCorruptedPath, + store->printStorePath(info->path)); + printError("path '%s' was modified! expected hash " + "'%s', got '%s'", + store->printStorePath(info->path), + info->narHash.to_string(Base32, true), + hash.first.to_string(Base32, true)); } } @@ -123,26 +126,33 @@ struct CmdVerify : StorePathsCommand else { StringSet sigsSeen; - size_t actualSigsNeeded = std::max(sigsNeeded, (size_t) 1); + size_t actualSigsNeeded = + std::max(sigsNeeded, (size_t) 1); size_t validSigs = 0; auto doSigs = [&](StringSet sigs) { for (auto sig : sigs) { - if (!sigsSeen.insert(sig).second) continue; - if (validSigs < ValidPathInfo::maxSigs && info->checkSignature(*store, publicKeys, sig)) + if (!sigsSeen.insert(sig).second) + continue; + if (validSigs < ValidPathInfo::maxSigs && + info->checkSignature(*store, publicKeys, + sig)) validSigs++; } }; - if (info->isContentAddressed(*store)) validSigs = ValidPathInfo::maxSigs; + if (info->isContentAddressed(*store)) + validSigs = ValidPathInfo::maxSigs; doSigs(info->sigs); for (auto & store2 : substituters) { - if (validSigs >= actualSigsNeeded) break; + if (validSigs >= actualSigsNeeded) + break; try { auto info2 = store2->queryPathInfo(info->path); - if (info2->isContentAddressed(*store)) validSigs = ValidPathInfo::maxSigs; + if (info2->isContentAddressed(*store)) + validSigs = ValidPathInfo::maxSigs; doSigs(info2->sigs); } catch (InvalidPath &) { } catch (Error & e) { @@ -156,10 +166,11 @@ struct CmdVerify : StorePathsCommand if (!good) { untrusted++; - act2.result(resUntrustedPath, store->printStorePath(info->path)); - printError("path '%s' is untrusted", store->printStorePath(info->path)); + act2.result(resUntrustedPath, + store->printStorePath(info->path)); + printError("path '%s' is untrusted", + store->printStorePath(info->path)); } - } done++; @@ -177,10 +188,8 @@ struct CmdVerify : StorePathsCommand pool.process(); - throw Exit( - (corrupted ? 1 : 0) | - (untrusted ? 2 : 0) | - (failed ? 4 : 0)); + throw Exit((corrupted ? 1 : 0) | (untrusted ? 2 : 0) | + (failed ? 4 : 0)); } }; diff --git a/src/nix/why-depends.cc b/src/nix/why-depends.cc index 1d9ab28baa05..ac330c74bc18 100644 --- a/src/nix/why-depends.cc +++ b/src/nix/why-depends.cc @@ -9,14 +9,10 @@ using namespace nix; static std::string hilite(const std::string & s, size_t pos, size_t len, - const std::string & colour = ANSI_RED) + const std::string & colour = ANSI_RED) { - return - std::string(s, 0, pos) - + colour - + std::string(s, pos, len) - + ANSI_NORMAL - + std::string(s, pos + len); + return std::string(s, 0, pos) + colour + std::string(s, pos, len) + + ANSI_NORMAL + std::string(s, pos + len); } static std::string filterPrintable(const std::string & s) @@ -27,40 +23,38 @@ static std::string filterPrintable(const std::string & s) return res; } -struct CmdWhyDepends : SourceExprCommand -{ +struct CmdWhyDepends : SourceExprCommand { std::string _package, _dependency; bool all = false; bool precise = false; CmdWhyDepends() { - expectArgs({ - .label = "package", - .handler = {&_package}, - .completer = {[&](size_t, std::string_view prefix) { - completeInstallable(prefix); - }} - }); - - expectArgs({ - .label = "dependency", - .handler = {&_dependency}, - .completer = {[&](size_t, std::string_view prefix) { - completeInstallable(prefix); - }} - }); + expectArgs({.label = "package", + .handler = {&_package}, + .completer = {[&](size_t, std::string_view prefix) { + completeInstallable(prefix); + }}}); + + expectArgs({.label = "dependency", + .handler = {&_dependency}, + .completer = {[&](size_t, std::string_view prefix) { + completeInstallable(prefix); + }}}); addFlag({ .longName = "all", .shortName = 'a', - .description = "Show all edges in the dependency graph leading from *package* to *dependency*, rather than just a shortest path.", + .description = + "Show all edges in the dependency graph leading from *package* " + "to *dependency*, rather than just a shortest path.", .handler = {&all, true}, }); addFlag({ .longName = "precise", - .description = "For each edge in the dependency graph, show the files in the parent that cause the dependency.", + .description = "For each edge in the dependency graph, show the " + "files in the parent that cause the dependency.", .handler = {&precise, true}, }); } @@ -73,8 +67,8 @@ struct CmdWhyDepends : SourceExprCommand std::string doc() override { return - #include "why-depends.md" - ; +#include "why-depends.md" + ; } Category category() override { return catSecondary; } @@ -82,9 +76,11 @@ struct CmdWhyDepends : SourceExprCommand void run(ref store) override { auto package = parseInstallable(store, _package); - auto packagePath = Installable::toStorePath(getEvalStore(), store, Realise::Outputs, operateOn, package); + auto packagePath = Installable::toStorePath( + getEvalStore(), store, Realise::Outputs, operateOn, package); auto dependency = parseInstallable(store, _dependency); - auto dependencyPath = Installable::toStorePath(getEvalStore(), store, Realise::Derivation, operateOn, dependency); + auto dependencyPath = Installable::toStorePath( + getEvalStore(), store, Realise::Derivation, operateOn, dependency); auto dependencyPathHash = dependencyPath.hashPart(); StorePathSet closure; @@ -92,8 +88,8 @@ struct CmdWhyDepends : SourceExprCommand if (!closure.count(dependencyPath)) { printError("'%s' does not depend on '%s'", - store->printStorePath(packagePath), - store->printStorePath(dependencyPath)); + store->printStorePath(packagePath), + store->printStorePath(dependencyPath)); return; } @@ -103,8 +99,7 @@ struct CmdWhyDepends : SourceExprCommand auto const inf = std::numeric_limits::max(); - struct Node - { + struct Node { StorePath path; StorePathSet refs; StorePathSet rrefs; @@ -117,11 +112,10 @@ struct CmdWhyDepends : SourceExprCommand std::map graph; for (auto & path : closure) - graph.emplace(path, Node { - .path = path, - .refs = store->queryPathInfo(path)->references, - .dist = path == dependencyPath ? 0 : inf - }); + graph.emplace(path, + Node{.path = path, + .refs = store->queryPathInfo(path)->references, + .dist = path == dependencyPath ? 0 : inf}); // Transpose the graph. for (auto & node : graph) @@ -149,7 +143,6 @@ struct CmdWhyDepends : SourceExprCommand queue.push(&node2); } } - } } @@ -157,28 +150,31 @@ struct CmdWhyDepends : SourceExprCommand closure (i.e., that have a non-infinite distance to 'dependency'). Print every edge on a path between `package` and `dependency`. */ - std::function printNode; + std::function + printNode; - struct BailOut { }; + struct BailOut { + }; - printNode = [&](Node & node, const std::string & firstPad, const std::string & tailPad) { + printNode = [&](Node & node, const std::string & firstPad, + const std::string & tailPad) { auto pathS = store->printStorePath(node.path); assert(node.dist != inf); if (precise) { - logger->cout("%s%s%s%s" ANSI_NORMAL, - firstPad, - node.visited ? "\e[38;5;244m" : "", - firstPad != "" ? "→ " : "", - pathS); + logger->cout("%s%s%s%s" ANSI_NORMAL, firstPad, + node.visited ? "\e[38;5;244m" : "", + firstPad != "" ? "→ " : "", pathS); } - if (node.path == dependencyPath && !all - && packagePath != dependencyPath) + if (node.path == dependencyPath && !all && + packagePath != dependencyPath) throw BailOut(); - if (node.visited) return; - if (precise) node.visited = true; + if (node.visited) + return; + if (precise) + node.visited = true; /* Sort the references by distance to `dependency` to ensure that the shortest path is printed first. */ @@ -186,9 +182,11 @@ struct CmdWhyDepends : SourceExprCommand std::set hashes; for (auto & ref : node.refs) { - if (ref == node.path && packagePath != dependencyPath) continue; + if (ref == node.path && packagePath != dependencyPath) + continue; auto & node2 = graph.at(ref); - if (node2.dist == inf) continue; + if (node2.dist == inf) + continue; refs.emplace(node2.dist, &node2); hashes.insert(std::string(node2.path.hashPart())); } @@ -222,12 +220,13 @@ struct CmdWhyDepends : SourceExprCommand if (pos != std::string::npos) { size_t margin = 32; auto pos2 = pos >= margin ? pos - margin : 0; - hits[hash].emplace_back(fmt("%s: …%s…\n", - p2, - hilite(filterPrintable( - std::string(contents, pos2, pos - pos2 + hash.size() + margin)), - pos - pos2, StorePath::HashLen, - getColour(hash)))); + hits[hash].emplace_back(fmt( + "%s: …%s…\n", p2, + hilite(filterPrintable(std::string( + contents, pos2, + pos - pos2 + hash.size() + margin)), + pos - pos2, StorePath::HashLen, + getColour(hash)))); } } } @@ -238,15 +237,18 @@ struct CmdWhyDepends : SourceExprCommand for (auto & hash : hashes) { auto pos = target.find(hash); if (pos != std::string::npos) - hits[hash].emplace_back(fmt("%s -> %s\n", p2, - hilite(target, pos, StorePath::HashLen, getColour(hash)))); + hits[hash].emplace_back( + fmt("%s -> %s\n", p2, + hilite(target, pos, StorePath::HashLen, + getColour(hash)))); } } }; // FIXME: should use scanForReferences(). - if (precise) visitPath(pathS); + if (precise) + visitPath(pathS); for (auto & ref : refs) { std::string hash(ref.second->path.hashPart()); @@ -256,34 +258,35 @@ struct CmdWhyDepends : SourceExprCommand for (auto & hit : hits[hash]) { bool first = hit == *hits[hash].begin(); std::cout << tailPad - << (first ? (last ? treeLast : treeConn) : (last ? treeNull : treeLine)) + << (first ? (last ? treeLast : treeConn) + : (last ? treeNull : treeLine)) << hit; - if (!all) break; + if (!all) + break; } if (!precise) { auto pathS = store->printStorePath(ref.second->path); - logger->cout("%s%s%s%s" ANSI_NORMAL, - firstPad, - ref.second->visited ? "\e[38;5;244m" : "", - last ? treeLast : treeConn, - pathS); + logger->cout("%s%s%s%s" ANSI_NORMAL, firstPad, + ref.second->visited ? "\e[38;5;244m" : "", + last ? treeLast : treeConn, pathS); node.visited = true; } - printNode(*ref.second, - tailPad + (last ? treeNull : treeLine), - tailPad + (last ? treeNull : treeLine)); + printNode(*ref.second, tailPad + (last ? treeNull : treeLine), + tailPad + (last ? treeNull : treeLine)); } }; RunPager pager; try { if (!precise) { - logger->cout("%s", store->printStorePath(graph.at(packagePath).path)); + logger->cout("%s", + store->printStorePath(graph.at(packagePath).path)); } printNode(graph.at(packagePath), "", ""); - } catch (BailOut & ) { } + } catch (BailOut &) { + } } }; diff --git a/src/resolve-system-dependencies/resolve-system-dependencies.cc b/src/resolve-system-dependencies/resolve-system-dependencies.cc index c6023eb03874..fbd143f7bd22 100644 --- a/src/resolve-system-dependencies/resolve-system-dependencies.cc +++ b/src/resolve-system-dependencies/resolve-system-dependencies.cc @@ -48,31 +48,37 @@ std::set runResolver(const Path & filename) return {}; } - char* obj = (char*) mmap(NULL, st.st_size, PROT_READ, MAP_SHARED, fd.get(), 0); + char * obj = + (char *) mmap(NULL, st.st_size, PROT_READ, MAP_SHARED, fd.get(), 0); if (!obj) throw SysError("mmapping '%s'", filename); ptrdiff_t mach64_offset = 0; - uint32_t magic = ((mach_header_64*) obj)->magic; + uint32_t magic = ((mach_header_64 *) obj)->magic; if (magic == FAT_CIGAM || magic == FAT_MAGIC) { bool should_swap = magic == FAT_CIGAM; - uint32_t narches = DO_SWAP(should_swap, ((fat_header *) obj)->nfat_arch); + uint32_t narches = + DO_SWAP(should_swap, ((fat_header *) obj)->nfat_arch); for (uint32_t i = 0; i < narches; i++) { - fat_arch* arch = (fat_arch*) (obj + sizeof(fat_header) + sizeof(fat_arch) * i); + fat_arch * arch = + (fat_arch *) (obj + sizeof(fat_header) + sizeof(fat_arch) * i); if (DO_SWAP(should_swap, arch->cputype) == CPU_TYPE_X86_64) { mach64_offset = (ptrdiff_t) DO_SWAP(should_swap, arch->offset); break; } } if (mach64_offset == 0) { - printError("could not find any mach64 blobs in file '%1%', continuing...", filename); + printError( + "could not find any mach64 blobs in file '%1%', continuing...", + filename); return {}; } } else if (magic == MH_MAGIC_64 || magic == MH_CIGAM_64) { mach64_offset = 0; } else { - printError("Object file has unknown magic number '%1%', skipping it...", magic); + printError("Object file has unknown magic number '%1%', skipping it...", + magic); return {}; } @@ -84,12 +90,13 @@ std::set runResolver(const Path & filename) std::set libs; for (uint32_t i = 0; i < DO_SWAP(should_swap, m_header->ncmds); i++) { load_command * cmd = (load_command *) (obj + cmd_offset); - switch(DO_SWAP(should_swap, cmd->cmd)) { - case LC_LOAD_UPWARD_DYLIB: - case LC_LOAD_DYLIB: - case LC_REEXPORT_DYLIB: - libs.insert(std::string((char *) cmd + ((dylib_command*) cmd)->dylib.name.offset)); - break; + switch (DO_SWAP(should_swap, cmd->cmd)) { + case LC_LOAD_UPWARD_DYLIB: + case LC_LOAD_DYLIB: + case LC_REEXPORT_DYLIB: + libs.insert(std::string( + (char *) cmd + ((dylib_command *) cmd)->dylib.name.offset)); + break; } cmd_offset += DO_SWAP(should_swap, cmd->cmdsize); } @@ -97,23 +104,20 @@ std::set runResolver(const Path & filename) return libs; } -bool isSymlink(const Path & path) -{ - return S_ISLNK(lstat(path).st_mode); -} +bool isSymlink(const Path & path) { return S_ISLNK(lstat(path).st_mode); } Path resolveSymlink(const Path & path) { auto target = readLink(path); - return hasPrefix(target, "/") - ? target - : concatStrings(dirOf(path), "/", target); + return hasPrefix(target, "/") ? target + : concatStrings(dirOf(path), "/", target); } std::set resolveTree(const Path & path, PathSet & deps) { std::set results; - if (!deps.insert(path).second) return {}; + if (!deps.insert(path).second) + return {}; for (auto & lib : runResolver(path)) { results.insert(lib); for (auto & p : resolveTree(lib, deps)) { @@ -125,7 +129,8 @@ std::set resolveTree(const Path & path, PathSet & deps) std::set getPath(const Path & path) { - if (hasPrefix(path, "/dev")) return {}; + if (hasPrefix(path, "/dev")) + return {}; Path cacheFile = resolveCacheFile(path); if (pathExists(cacheFile)) @@ -157,13 +162,12 @@ int main(int argc, char ** argv) uname(&_uname); - auto cacheParentDir = (format("%1%/dependency-maps") % settings.nixStateDir).str(); + auto cacheParentDir = + (format("%1%/dependency-maps") % settings.nixStateDir).str(); - cacheDir = (format("%1%/%2%-%3%-%4%") - % cacheParentDir - % _uname.machine - % _uname.sysname - % _uname.release).str(); + cacheDir = (format("%1%/%2%-%3%-%4%") % cacheParentDir % + _uname.machine % _uname.sysname % _uname.release) + .str(); mkdir(cacheParentDir.c_str(), 0755); mkdir(cacheDir.c_str(), 0755); @@ -175,8 +179,10 @@ int main(int argc, char ** argv) if (std::string(argv[1]) == "--test") impurePaths.insert(argv[2]); else { - auto drv = store->derivationFromPath(store->parseStorePath(argv[1])); - impurePaths = tokenizeString(getOr(drv.env, "__impureHostDeps", "")); + auto drv = + store->derivationFromPath(store->parseStorePath(argv[1])); + impurePaths = tokenizeString( + getOr(drv.env, "__impureHostDeps", "")); impurePaths.insert("/usr/lib/libSystem.dylib"); } diff --git a/tests/plugins/plugintest.cc b/tests/plugins/plugintest.cc index 04b7910216bd..b7325ddc0d61 100644 --- a/tests/plugins/plugintest.cc +++ b/tests/plugins/plugintest.cc @@ -3,17 +3,17 @@ using namespace nix; -struct MySettings : Config -{ +struct MySettings : Config { Setting settingSet{this, false, "setting-set", - "Whether the plugin-defined setting was set"}; + "Whether the plugin-defined setting was set"}; }; MySettings mySettings; static GlobalConfig::Register rs(&mySettings); -static void prim_anotherNull (EvalState & state, const PosIdx pos, Value ** args, Value & v) +static void prim_anotherNull(EvalState & state, const PosIdx pos, Value ** args, + Value & v) { if (mySettings.settingSet) v.mkNull();