aboutsummaryrefslogtreecommitdiff
path: root/src/libutil
diff options
context:
space:
mode:
Diffstat (limited to 'src/libutil')
-rw-r--r--src/libutil/apply-config-options.hh53
-rw-r--r--src/libutil/args.cc5
-rw-r--r--src/libutil/args.hh2
-rw-r--r--src/libutil/backed-string-view.hh69
-rw-r--r--src/libutil/config-impl.hh22
-rw-r--r--src/libutil/config.cc97
-rw-r--r--src/libutil/config.hh19
-rw-r--r--src/libutil/error.cc1
-rw-r--r--src/libutil/error.hh5
-rw-r--r--src/libutil/file-system.cc30
-rw-r--r--src/libutil/file-system.hh17
-rw-r--r--src/libutil/finally.hh1
-rw-r--r--src/libutil/fmt.cc24
-rw-r--r--src/libutil/fmt.hh25
-rw-r--r--src/libutil/hash.cc4
-rw-r--r--src/libutil/meson.build5
-rw-r--r--src/libutil/notifying-counter.hh99
-rw-r--r--src/libutil/processes.cc1
-rw-r--r--src/libutil/regex.cc3
-rw-r--r--src/libutil/result.hh24
-rw-r--r--src/libutil/serialise.cc48
-rw-r--r--src/libutil/serialise.hh63
-rw-r--r--src/libutil/signals.cc1
-rw-r--r--src/libutil/source-path.cc1
-rw-r--r--src/libutil/strings.hh25
-rw-r--r--src/libutil/thread-pool.hh1
-rw-r--r--src/libutil/types.hh72
-rw-r--r--src/libutil/url.hh1
-rw-r--r--src/libutil/users.cc2
-rw-r--r--src/libutil/variant-wrapper.hh2
30 files changed, 512 insertions, 210 deletions
diff --git a/src/libutil/apply-config-options.hh b/src/libutil/apply-config-options.hh
new file mode 100644
index 000000000..533904a69
--- /dev/null
+++ b/src/libutil/apply-config-options.hh
@@ -0,0 +1,53 @@
+#pragma once
+/**
+ * @file
+ * @brief Options for applying `Config` settings.
+ */
+
+#include <types.hh>
+
+namespace nix {
+
+/**
+ * Options for applying `Config` settings.
+ */
+struct ApplyConfigOptions
+{
+ /**
+ * The configuration file being loaded.
+ *
+ * If set, relative paths are allowed and interpreted as relative to the
+ * directory of this path.
+ */
+ std::optional<Path> path = std::nullopt;
+
+ /**
+ * If set, tilde paths (like `~/.config/repl.nix`) are allowed and the
+ * tilde is substituted for this directory.
+ */
+ std::optional<Path> home = std::nullopt;
+
+ /**
+ * Is the configuration being loaded from the `$NIX_CONFIG` environment
+ * variable?
+ *
+ * Used for formatting error messages.
+ */
+ bool fromEnvVar = false;
+
+ /**
+ * Display the `relative` path field, with a reasonable default if none is
+ * available.
+ */
+ std::string relativeDisplay() const {
+ if (path) {
+ return *path;
+ } else if (fromEnvVar) {
+ return "$NIX_CONFIG";
+ } else {
+ return "<unknown>";
+ }
+ }
+};
+
+}
diff --git a/src/libutil/args.cc b/src/libutil/args.cc
index 1342e7c6a..edcab23ac 100644
--- a/src/libutil/args.cc
+++ b/src/libutil/args.cc
@@ -1,10 +1,11 @@
#include "args.hh"
#include "args/root.hh"
#include "hash.hh"
-#include "json-utils.hh"
+#include "strings.hh"
+#include "json-utils.hh" // IWYU pragma: keep (instances)
#include "environment-variables.hh"
-#include "experimental-features-json.hh"
+#include "experimental-features-json.hh" // IWYU pragma: keep (instances)
#include "logging.hh"
#include <glob.h>
diff --git a/src/libutil/args.hh b/src/libutil/args.hh
index 5fdbaba7e..e2bac6415 100644
--- a/src/libutil/args.hh
+++ b/src/libutil/args.hh
@@ -3,6 +3,8 @@
#include "experimental-features.hh"
#include "types.hh"
+#include "ref.hh"
+
#include <functional>
#include <map>
#include <memory>
diff --git a/src/libutil/backed-string-view.hh b/src/libutil/backed-string-view.hh
new file mode 100644
index 000000000..96136331c
--- /dev/null
+++ b/src/libutil/backed-string-view.hh
@@ -0,0 +1,69 @@
+#pragma once
+/// @file String view that can be either owned or borrowed.
+#include <variant>
+#include <string>
+#include <string_view>
+
+/**
+ * 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
+ * newly allocated storage for values.
+ *
+ * We do not define implicit conversions, even with ref qualifiers,
+ * since those can easily become ambiguous to the reader and can degrade
+ * into copying behaviour we want to avoid.
+ */
+class BackedStringView {
+private:
+ std::variant<std::string, std::string_view> 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:
+ std::string_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) {}
+ template<size_t N>
+ BackedStringView(const char (& lit)[N]): data(std::string_view(lit)) {}
+
+ BackedStringView(const BackedStringView &) = delete;
+ BackedStringView & operator=(const BackedStringView &) = delete;
+
+ /**
+ * We only want move operations defined since the sole purpose of
+ * this type is to avoid copies.
+ */
+ BackedStringView(BackedStringView && other) = default;
+ BackedStringView & operator=(BackedStringView && other) = default;
+
+ bool isOwned() const
+ {
+ return std::holds_alternative<std::string>(data);
+ }
+
+ std::string toOwned() &&
+ {
+ return isOwned()
+ ? std::move(std::get<std::string>(data))
+ : std::string(std::get<std::string_view>(data));
+ }
+
+ std::string_view operator*() const
+ {
+ return isOwned()
+ ? std::get<std::string>(data)
+ : std::get<std::string_view>(data);
+ }
+ Ptr operator->() const { return Ptr(**this); }
+};
diff --git a/src/libutil/config-impl.hh b/src/libutil/config-impl.hh
index 024018e00..748107b6e 100644
--- a/src/libutil/config-impl.hh
+++ b/src/libutil/config-impl.hh
@@ -51,14 +51,14 @@ bool BaseSetting<T>::isAppendable()
return trait::appendable;
}
-template<> void BaseSetting<Strings>::appendOrSet(Strings newValue, bool append);
-template<> void BaseSetting<StringSet>::appendOrSet(StringSet newValue, bool append);
-template<> void BaseSetting<StringMap>::appendOrSet(StringMap newValue, bool append);
-template<> void BaseSetting<ExperimentalFeatures>::appendOrSet(ExperimentalFeatures newValue, bool append);
-template<> void BaseSetting<DeprecatedFeatures>::appendOrSet(DeprecatedFeatures newValue, bool append);
+template<> void BaseSetting<Strings>::appendOrSet(Strings newValue, bool append, const ApplyConfigOptions & options);
+template<> void BaseSetting<StringSet>::appendOrSet(StringSet newValue, bool append, const ApplyConfigOptions & options);
+template<> void BaseSetting<StringMap>::appendOrSet(StringMap newValue, bool append, const ApplyConfigOptions & options);
+template<> void BaseSetting<ExperimentalFeatures>::appendOrSet(ExperimentalFeatures newValue, bool append, const ApplyConfigOptions & options);
+template<> void BaseSetting<DeprecatedFeatures>::appendOrSet(DeprecatedFeatures newValue, bool append, const ApplyConfigOptions & options);
template<typename T>
-void BaseSetting<T>::appendOrSet(T newValue, bool append)
+void BaseSetting<T>::appendOrSet(T newValue, bool append, const ApplyConfigOptions & options)
{
static_assert(
!trait::appendable,
@@ -69,14 +69,14 @@ void BaseSetting<T>::appendOrSet(T newValue, bool append)
}
template<typename T>
-void BaseSetting<T>::set(const std::string & str, bool append)
+void BaseSetting<T>::set(const std::string & str, bool append, const ApplyConfigOptions & options)
{
if (experimentalFeatureSettings.isEnabled(experimentalFeature)) {
- auto parsed = parse(str);
+ auto parsed = parse(str, options);
if (deprecated && (append || parsed != value)) {
warn("deprecated setting '%s' found (set to '%s')", name, str);
}
- appendOrSet(std::move(parsed), append);
+ appendOrSet(std::move(parsed), append, options);
} else {
assert(experimentalFeature);
warn("Ignoring setting '%s' because experimental feature '%s' is not enabled",
@@ -111,7 +111,7 @@ void BaseSetting<T>::convertToArg(Args & args, const std::string & category)
}
#define DECLARE_CONFIG_SERIALISER(TY) \
- template<> TY BaseSetting< TY >::parse(const std::string & str) const; \
+ template<> TY BaseSetting< TY >::parse(const std::string & str, const ApplyConfigOptions & options) const; \
template<> std::string BaseSetting< TY >::to_string() const;
DECLARE_CONFIG_SERIALISER(std::string)
@@ -124,7 +124,7 @@ DECLARE_CONFIG_SERIALISER(ExperimentalFeatures)
DECLARE_CONFIG_SERIALISER(DeprecatedFeatures)
template<typename T>
-T BaseSetting<T>::parse(const std::string & str) const
+T BaseSetting<T>::parse(const std::string & str, const ApplyConfigOptions & options) const
{
static_assert(std::is_integral<T>::value, "Integer required.");
diff --git a/src/libutil/config.cc b/src/libutil/config.cc
index 8e20f1321..778da1413 100644
--- a/src/libutil/config.cc
+++ b/src/libutil/config.cc
@@ -1,4 +1,5 @@
#include "config.hh"
+#include "apply-config-options.hh"
#include "args.hh"
#include "abstract-setting-to-json.hh"
#include "experimental-features.hh"
@@ -17,7 +18,7 @@ Config::Config(StringMap initials)
: AbstractConfig(std::move(initials))
{ }
-bool Config::set(const std::string & name, const std::string & value)
+bool Config::set(const std::string & name, const std::string & value, const ApplyConfigOptions & options)
{
bool append = false;
auto i = _settings.find(name);
@@ -30,7 +31,7 @@ bool Config::set(const std::string & name, const std::string & value)
} else
return false;
}
- i->second.setting->set(value, append);
+ i->second.setting->set(value, append, options);
i->second.setting->overridden = true;
return true;
}
@@ -91,7 +92,7 @@ void Config::getSettings(std::map<std::string, SettingInfo> & res, bool overridd
}
-static void applyConfigInner(const std::string & contents, const std::string & path, std::vector<std::pair<std::string, std::string>> & parsedContents) {
+static void applyConfigInner(const std::string & contents, const ApplyConfigOptions & options, std::vector<std::pair<std::string, std::string>> & parsedContents) {
unsigned int pos = 0;
while (pos < contents.size()) {
@@ -107,7 +108,7 @@ static void applyConfigInner(const std::string & contents, const std::string & p
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, options.relativeDisplay());
auto include = false;
auto ignoreMissing = false;
@@ -119,24 +120,32 @@ static void applyConfigInner(const std::string & contents, const std::string & p
}
if (include) {
- if (tokens.size() != 2)
- throw UsageError("illegal configuration line '%1%' in '%2%'", line, path);
- auto p = absPath(tokens[1], dirOf(path));
- if (pathExists(p)) {
+ if (tokens.size() != 2) {
+ throw UsageError("illegal configuration line '%1%' in '%2%'", line, options.relativeDisplay());
+ }
+ if (!options.path) {
+ throw UsageError("can only include configuration '%1%' from files", tokens[1]);
+ }
+ auto pathToInclude = absPath(tildePath(tokens[1], options.home), dirOf(*options.path));
+ if (pathExists(pathToInclude)) {
+ auto includeOptions = ApplyConfigOptions {
+ .path = pathToInclude,
+ .home = options.home,
+ };
try {
- std::string includedContents = readFile(path);
- applyConfigInner(includedContents, p, parsedContents);
+ std::string includedContents = readFile(pathToInclude);
+ applyConfigInner(includedContents, includeOptions, parsedContents);
} catch (SysError &) {
// TODO: Do we actually want to ignore this? Or is it better to fail?
}
} else if (!ignoreMissing) {
- throw Error("file '%1%' included from '%2%' not found", p, path);
+ throw Error("file '%1%' included from '%2%' not found", pathToInclude, *options.path);
}
continue;
}
if (tokens[1] != "=")
- throw UsageError("illegal configuration line '%1%' in '%2%'", line, path);
+ throw UsageError("illegal configuration line '%1%' in '%2%'", line, options.relativeDisplay());
std::string name = std::move(tokens[0]);
@@ -150,20 +159,20 @@ static void applyConfigInner(const std::string & contents, const std::string & p
};
}
-void AbstractConfig::applyConfig(const std::string & contents, const std::string & path) {
+void AbstractConfig::applyConfig(const std::string & contents, const ApplyConfigOptions & options) {
std::vector<std::pair<std::string, std::string>> parsedContents;
- applyConfigInner(contents, path, parsedContents);
+ applyConfigInner(contents, options, parsedContents);
// First apply experimental-feature related settings
for (const auto & [name, value] : parsedContents)
if (name == "experimental-features" || name == "extra-experimental-features")
- set(name, value);
+ set(name, value, options);
// Then apply other settings
for (const auto & [name, value] : parsedContents)
if (name != "experimental-features" && name != "extra-experimental-features")
- set(name, value);
+ set(name, value, options);
}
void Config::resetOverridden()
@@ -241,7 +250,7 @@ void AbstractSetting::convertToArg(Args & args, const std::string & category)
bool AbstractSetting::isOverridden() const { return overridden; }
-template<> std::string BaseSetting<std::string>::parse(const std::string & str) const
+template<> std::string BaseSetting<std::string>::parse(const std::string & str, const ApplyConfigOptions & options) const
{
return str;
}
@@ -251,7 +260,7 @@ template<> std::string BaseSetting<std::string>::to_string() const
return value;
}
-template<> std::optional<std::string> BaseSetting<std::optional<std::string>>::parse(const std::string & str) const
+template<> std::optional<std::string> BaseSetting<std::optional<std::string>>::parse(const std::string & str, const ApplyConfigOptions & options) const
{
if (str == "")
return std::nullopt;
@@ -264,7 +273,7 @@ template<> std::string BaseSetting<std::optional<std::string>>::to_string() cons
return value ? *value : "";
}
-template<> bool BaseSetting<bool>::parse(const std::string & str) const
+template<> bool BaseSetting<bool>::parse(const std::string & str, const ApplyConfigOptions & options) const
{
if (str == "true" || str == "yes" || str == "1")
return true;
@@ -297,12 +306,12 @@ template<> void BaseSetting<bool>::convertToArg(Args & args, const std::string &
});
}
-template<> Strings BaseSetting<Strings>::parse(const std::string & str) const
+template<> Strings BaseSetting<Strings>::parse(const std::string & str, const ApplyConfigOptions & options) const
{
return tokenizeString<Strings>(str);
}
-template<> void BaseSetting<Strings>::appendOrSet(Strings newValue, bool append)
+template<> void BaseSetting<Strings>::appendOrSet(Strings newValue, bool append, const ApplyConfigOptions & options)
{
if (!append) value.clear();
value.insert(value.end(), std::make_move_iterator(newValue.begin()),
@@ -314,12 +323,12 @@ template<> std::string BaseSetting<Strings>::to_string() const
return concatStringsSep(" ", value);
}
-template<> StringSet BaseSetting<StringSet>::parse(const std::string & str) const
+template<> StringSet BaseSetting<StringSet>::parse(const std::string & str, const ApplyConfigOptions & options) const
{
return tokenizeString<StringSet>(str);
}
-template<> void BaseSetting<StringSet>::appendOrSet(StringSet newValue, bool append)
+template<> void BaseSetting<StringSet>::appendOrSet(StringSet newValue, bool append, const ApplyConfigOptions & options)
{
if (!append) value.clear();
value.insert(std::make_move_iterator(newValue.begin()), std::make_move_iterator(newValue.end()));
@@ -330,7 +339,7 @@ template<> std::string BaseSetting<StringSet>::to_string() const
return concatStringsSep(" ", value);
}
-template<> ExperimentalFeatures BaseSetting<ExperimentalFeatures>::parse(const std::string & str) const
+template<> ExperimentalFeatures BaseSetting<ExperimentalFeatures>::parse(const std::string & str, const ApplyConfigOptions & options) const
{
ExperimentalFeatures res{};
for (auto & s : tokenizeString<StringSet>(str)) {
@@ -342,7 +351,7 @@ template<> ExperimentalFeatures BaseSetting<ExperimentalFeatures>::parse(const s
return res;
}
-template<> void BaseSetting<ExperimentalFeatures>::appendOrSet(ExperimentalFeatures newValue, bool append)
+template<> void BaseSetting<ExperimentalFeatures>::appendOrSet(ExperimentalFeatures newValue, bool append, const ApplyConfigOptions & options)
{
if (append)
value = value | newValue;
@@ -359,7 +368,7 @@ template<> std::string BaseSetting<ExperimentalFeatures>::to_string() const
return concatStringsSep(" ", stringifiedXpFeatures);
}
-template<> DeprecatedFeatures BaseSetting<DeprecatedFeatures>::parse(const std::string & str) const
+template<> DeprecatedFeatures BaseSetting<DeprecatedFeatures>::parse(const std::string & str, const ApplyConfigOptions & options) const
{
DeprecatedFeatures res{};
for (auto & s : tokenizeString<StringSet>(str)) {
@@ -371,7 +380,7 @@ template<> DeprecatedFeatures BaseSetting<DeprecatedFeatures>::parse(const std::
return res;
}
-template<> void BaseSetting<DeprecatedFeatures>::appendOrSet(DeprecatedFeatures newValue, bool append)
+template<> void BaseSetting<DeprecatedFeatures>::appendOrSet(DeprecatedFeatures newValue, bool append, const ApplyConfigOptions & options)
{
if (append)
value = value | newValue;
@@ -388,7 +397,7 @@ template<> std::string BaseSetting<DeprecatedFeatures>::to_string() const
return concatStringsSep(" ", stringifiedDpFeatures);
}
-template<> StringMap BaseSetting<StringMap>::parse(const std::string & str) const
+template<> StringMap BaseSetting<StringMap>::parse(const std::string & str, const ApplyConfigOptions & options) const
{
StringMap res;
for (const auto & s : tokenizeString<Strings>(str)) {
@@ -399,7 +408,7 @@ template<> StringMap BaseSetting<StringMap>::parse(const std::string & str) cons
return res;
}
-template<> void BaseSetting<StringMap>::appendOrSet(StringMap newValue, bool append)
+template<> void BaseSetting<StringMap>::appendOrSet(StringMap newValue, bool append, const ApplyConfigOptions & options)
{
if (!append) value.clear();
value.insert(std::make_move_iterator(newValue.begin()), std::make_move_iterator(newValue.end()));
@@ -426,34 +435,40 @@ template class BaseSetting<StringMap>;
template class BaseSetting<ExperimentalFeatures>;
template class BaseSetting<DeprecatedFeatures>;
-static Path parsePath(const AbstractSetting & s, const std::string & str)
+static Path parsePath(const AbstractSetting & s, const std::string & str, const ApplyConfigOptions & options)
{
- if (str == "")
+ if (str == "") {
throw UsageError("setting '%s' is a path and paths cannot be empty", s.name);
- else
- return canonPath(str);
+ } else {
+ auto tildeResolvedPath = tildePath(str, options.home);
+ if (options.path) {
+ return absPath(tildeResolvedPath, dirOf(*options.path));
+ } else {
+ return canonPath(tildeResolvedPath);
+ }
+ }
}
-template<> Path PathsSetting<Path>::parse(const std::string & str) const
+template<> Path PathsSetting<Path>::parse(const std::string & str, const ApplyConfigOptions & options) const
{
- return parsePath(*this, str);
+ return parsePath(*this, str, options);
}
-template<> std::optional<Path> PathsSetting<std::optional<Path>>::parse(const std::string & str) const
+template<> std::optional<Path> PathsSetting<std::optional<Path>>::parse(const std::string & str, const ApplyConfigOptions & options) const
{
if (str == "")
return std::nullopt;
else
- return parsePath(*this, str);
+ return parsePath(*this, str, options);
}
-template<> Paths PathsSetting<Paths>::parse(const std::string & str) const
+template<> Paths PathsSetting<Paths>::parse(const std::string & str, const ApplyConfigOptions & options) const
{
auto strings = tokenizeString<Strings>(str);
Paths parsed;
for (auto str : strings) {
- parsed.push_back(canonPath(str));
+ parsed.push_back(parsePath(*this, str, options));
}
return parsed;
@@ -464,10 +479,10 @@ template class PathsSetting<std::optional<Path>>;
template class PathsSetting<Paths>;
-bool GlobalConfig::set(const std::string & name, const std::string & value)
+bool GlobalConfig::set(const std::string & name, const std::string & value, const ApplyConfigOptions & options)
{
for (auto & config : *configRegistrations)
- if (config->set(name, value)) return true;
+ if (config->set(name, value, options)) return true;
unknownSettings.emplace(name, value);
diff --git a/src/libutil/config.hh b/src/libutil/config.hh
index dbca4b406..59cc281c5 100644
--- a/src/libutil/config.hh
+++ b/src/libutil/config.hh
@@ -10,6 +10,7 @@
#include "types.hh"
#include "experimental-features.hh"
#include "deprecated-features.hh"
+#include "apply-config-options.hh"
namespace nix {
@@ -61,7 +62,7 @@ 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;
+ virtual bool set(const std::string & name, const std::string & value, const ApplyConfigOptions & options = {}) = 0;
struct SettingInfo
{
@@ -81,7 +82,7 @@ public:
* - contents: configuration contents to be parsed and applied
* - path: location of the configuration file
*/
- void applyConfig(const std::string & contents, const std::string & path = "<unknown>");
+ void applyConfig(const std::string & contents, const ApplyConfigOptions & options = {});
/**
* Resets the `overridden` flag of all Settings
@@ -155,7 +156,7 @@ public:
Config(StringMap initials = {});
- bool set(const std::string & name, const std::string & value) override;
+ bool set(const std::string & name, const std::string & value, const ApplyConfigOptions & options = {}) override;
void addSetting(AbstractSetting * setting);
@@ -200,7 +201,7 @@ protected:
virtual ~AbstractSetting();
- virtual void set(const std::string & value, bool append = false) = 0;
+ virtual void set(const std::string & value, bool append = false, const ApplyConfigOptions & options = {}) = 0;
/**
* Whether the type is appendable; i.e. whether the `append`
@@ -237,7 +238,7 @@ protected:
*
* Used by `set()`.
*/
- virtual T parse(const std::string & str) const;
+ virtual T parse(const std::string & str, const ApplyConfigOptions & options) const;
/**
* Append or overwrite `value` with `newValue`.
@@ -247,7 +248,7 @@ protected:
*
* @param append Whether to append or overwrite.
*/
- virtual void appendOrSet(T newValue, bool append);
+ virtual void appendOrSet(T newValue, bool append, const ApplyConfigOptions & options);
public:
@@ -284,7 +285,7 @@ public:
* Uses `parse()` to get the value from `str`, and `appendOrSet()`
* to set it.
*/
- void set(const std::string & str, bool append = false) override final;
+ void set(const std::string & str, bool append = false, const ApplyConfigOptions & options = {}) override final;
/**
* C++ trick; This is template-specialized to compile-time indicate whether
@@ -373,7 +374,7 @@ public:
options->addSetting(this);
}
- T parse(const std::string & str) const override;
+ T parse(const std::string & str, const ApplyConfigOptions & options) const override;
void operator =(const T & v) { this->assign(v); }
};
@@ -384,7 +385,7 @@ struct GlobalConfig : public AbstractConfig
typedef std::vector<Config*> ConfigRegistrations;
static ConfigRegistrations * configRegistrations;
- bool set(const std::string & name, const std::string & value) override;
+ bool set(const std::string & name, const std::string & value, const ApplyConfigOptions & options = {}) override;
void getSettings(std::map<std::string, SettingInfo> & res, bool overriddenOnly = false) override;
diff --git a/src/libutil/error.cc b/src/libutil/error.cc
index e5d6a9fa8..a7cbfbfd0 100644
--- a/src/libutil/error.cc
+++ b/src/libutil/error.cc
@@ -3,6 +3,7 @@
#include "logging.hh"
#include "position.hh"
#include "terminal.hh"
+#include "strings.hh"
#include <iostream>
#include <optional>
diff --git a/src/libutil/error.hh b/src/libutil/error.hh
index 06dfba0df..73c1ccadd 100644
--- a/src/libutil/error.hh
+++ b/src/libutil/error.hh
@@ -16,16 +16,12 @@
*/
#include "suggestions.hh"
-#include "ref.hh"
-#include "types.hh"
#include "fmt.hh"
#include <cstring>
#include <list>
#include <memory>
-#include <map>
#include <optional>
-#include <compare>
#include <sys/types.h>
#include <sys/stat.h>
@@ -173,6 +169,7 @@ public:
};
#define MakeError(newClass, superClass) \
+ /* NOLINTNEXTLINE(bugprone-macro-parentheses) */ \
class newClass : public superClass \
{ \
public: \
diff --git a/src/libutil/file-system.cc b/src/libutil/file-system.cc
index 631cf076b..1d3eba58f 100644
--- a/src/libutil/file-system.cc
+++ b/src/libutil/file-system.cc
@@ -10,6 +10,7 @@
#include "logging.hh"
#include "serialise.hh"
#include "signals.hh"
+#include "strings.hh"
#include "types.hh"
#include "users.hh"
@@ -17,15 +18,19 @@ namespace fs = std::filesystem;
namespace nix {
+Path getCwd() {
+ char buf[PATH_MAX];
+ if (!getcwd(buf, sizeof(buf))) {
+ throw SysError("cannot get cwd");
+ }
+ return Path(buf);
+}
+
Path absPath(Path path, std::optional<PathView> dir, bool resolveSymlinks)
{
if (path.empty() || path[0] != '/') {
if (!dir) {
- char buf[PATH_MAX];
- if (!getcwd(buf, sizeof(buf))) {
- throw SysError("cannot get cwd");
- }
- path = concatStrings(buf, "/", path);
+ path = concatStrings(getCwd(), "/", path);
} else {
path = concatStrings(*dir, "/", path);
}
@@ -117,6 +122,21 @@ Path realPath(Path const & path)
return ret;
}
+Path tildePath(Path const & path, const std::optional<Path> & home)
+{
+ if (path.starts_with("~/")) {
+ if (home) {
+ return *home + "/" + path.substr(2);
+ } else {
+ throw UsageError("`~` path not allowed: %1%", path);
+ }
+ } else if (path.starts_with('~')) {
+ throw UsageError("`~` paths must start with `~/`: %1%", path);
+ } else {
+ return path;
+ }
+}
+
void chmodPath(const Path & path, mode_t mode)
{
if (chmod(path.c_str(), mode) == -1)
diff --git a/src/libutil/file-system.hh b/src/libutil/file-system.hh
index 9fe931556..2547c63b5 100644
--- a/src/libutil/file-system.hh
+++ b/src/libutil/file-system.hh
@@ -30,6 +30,13 @@ struct Sink;
struct Source;
/**
+ * Get the current working directory.
+ *
+ * Throw an error if the current directory cannot get got.
+ */
+Path getCwd();
+
+/**
* @return An absolutized path, resolving paths relative to the
* specified directory, or the current directory otherwise. The path
* is also canonicalised.
@@ -63,6 +70,16 @@ Path canonPath(PathView path, bool resolveSymlinks = false);
Path realPath(Path const & path);
/**
+ * Resolve a tilde path like `~/puppy.nix` into an absolute path.
+ *
+ * If `home` is given, it's substituted for `~/` at the start of the input
+ * `path`. Otherwise, an error is thrown.
+ *
+ * If the path starts with `~` but not `~/`, an error is thrown.
+ */
+Path tildePath(Path const & path, const std::optional<Path> & home = std::nullopt);
+
+/**
* Change the permissions of a path
* Not called `chmod` as it shadows and could be confused with
* `int chmod(char *, mode_t)`, which does not handle errors
diff --git a/src/libutil/finally.hh b/src/libutil/finally.hh
index dc51d7b1e..5e62dfa3b 100644
--- a/src/libutil/finally.hh
+++ b/src/libutil/finally.hh
@@ -22,6 +22,7 @@ public:
Finally(Finally &&other) : fun(std::move(other.fun)) {
other.movedFrom = true;
}
+ // NOLINTNEXTLINE(bugprone-exception-escape): the noexcept is declared properly here, the analysis seems broken
~Finally() noexcept(noexcept(fun()))
{
try {
diff --git a/src/libutil/fmt.cc b/src/libutil/fmt.cc
new file mode 100644
index 000000000..bff5af020
--- /dev/null
+++ b/src/libutil/fmt.cc
@@ -0,0 +1,24 @@
+#include "fmt.hh" // IWYU pragma: keep
+// Darwin and FreeBSD stdenv do not define _GNU_SOURCE but do have _Unwind_Backtrace.
+#if __APPLE__ || __FreeBSD__
+#define BOOST_STACKTRACE_GNU_SOURCE_NOT_REQUIRED
+#endif
+#include <boost/stacktrace/stacktrace.hpp>
+
+template class boost::basic_format<char>;
+
+namespace nix {
+
+// Explicit instantiation saves about 30 cpu-seconds of compile time
+template HintFmt::HintFmt(const std::string &, const Uncolored<std::string> &s);
+template HintFmt::HintFmt(const std::string &, const std::string &s);
+template HintFmt::HintFmt(const std::string &, const uint64_t &, const char * const &);
+
+HintFmt::HintFmt(const std::string & literal) : HintFmt("%s", Uncolored(literal)) {}
+
+void printStackTrace()
+{
+ std::cerr << boost::stacktrace::stacktrace() << std::endl;
+}
+
+}
diff --git a/src/libutil/fmt.hh b/src/libutil/fmt.hh
index d015f7e5f..ee3e1e2e7 100644
--- a/src/libutil/fmt.hh
+++ b/src/libutil/fmt.hh
@@ -3,17 +3,17 @@
#include <iostream>
#include <string>
-#include <optional>
#include <boost/format.hpp>
-// Darwin and FreeBSD stdenv do not define _GNU_SOURCE but do have _Unwind_Backtrace.
-#if __APPLE__ || __FreeBSD__
-#define BOOST_STACKTRACE_GNU_SOURCE_NOT_REQUIRED
-#endif
-#include <boost/stacktrace.hpp>
#include "ansicolor.hh"
+// Explicit instantiation in fmt.cc
+extern template class boost::basic_format<char>;
+
namespace nix {
+/** Prints a C++ stack trace to stderr using boost stacktrace */
+void printStackTrace();
+
/**
* Values wrapped in this struct are printed in magenta.
*
@@ -157,7 +157,9 @@ public:
* Format the given string literally, without interpolating format
* placeholders.
*/
- HintFmt(const std::string & literal) : HintFmt("%s", Uncolored(literal)) {}
+ // Moved out of line because it was instantiating the template below in
+ // every file in the project.
+ HintFmt(const std::string & literal);
/**
* Interpolate the given arguments into the format string.
@@ -172,14 +174,14 @@ public:
std::cerr << "HintFmt received incorrect number of format args. Original format string: '";
std::cerr << format << "'; number of arguments: " << sizeof...(args) << "\n";
// And regardless of the coredump give me a damn stacktrace.
- std::cerr << boost::stacktrace::stacktrace() << std::endl;
+ printStackTrace();
abort();
}
} catch (boost::io::format_error & ex) {
// Same thing, but for anything that happens in the member initializers.
std::cerr << "HintFmt received incorrect format string. Original format string: '";
std::cerr << format << "'; number of arguments: " << sizeof...(args) << "\n";
- std::cerr << boost::stacktrace::stacktrace() << std::endl;
+ printStackTrace();
abort();
}
@@ -193,6 +195,11 @@ public:
}
};
+// Explicit instantiations in fmt.cc
+extern template HintFmt::HintFmt(const std::string &, const Uncolored<std::string> &s);
+extern template HintFmt::HintFmt(const std::string &, const std::string &s);
+extern template HintFmt::HintFmt(const std::string &, const uint64_t &, const char * const &);
+
std::ostream & operator<<(std::ostream & os, const HintFmt & hf);
}
diff --git a/src/libutil/hash.cc b/src/libutil/hash.cc
index 925f71f80..d383e9802 100644
--- a/src/libutil/hash.cc
+++ b/src/libutil/hash.cc
@@ -7,8 +7,10 @@
#include "args.hh"
#include "hash.hh"
#include "archive.hh"
+#include "charptr-cast.hh"
#include "logging.hh"
#include "split.hh"
+#include "strings.hh"
#include <sys/types.h>
#include <sys/stat.h>
@@ -129,7 +131,7 @@ std::string Hash::to_string(Base base, bool includeType) const
break;
case Base::Base64:
case Base::SRI:
- s += base64Encode(std::string_view(reinterpret_cast<const char *>(hash), hashSize));
+ s += base64Encode(std::string_view(charptr_cast<const char *>(hash), hashSize));
break;
}
return s;
diff --git a/src/libutil/meson.build b/src/libutil/meson.build
index 3e10e4b63..a3f21de59 100644
--- a/src/libutil/meson.build
+++ b/src/libutil/meson.build
@@ -17,6 +17,7 @@ libutil_sources = files(
'experimental-features.cc',
'file-descriptor.cc',
'file-system.cc',
+ 'fmt.cc',
'git.cc',
'hash.cc',
'hilite.cc',
@@ -46,11 +47,13 @@ libutil_sources = files(
)
libutil_headers = files(
+ 'apply-config-options.hh',
'abstract-setting-to-json.hh',
'ansicolor.hh',
'archive.hh',
'args/root.hh',
'args.hh',
+ 'backed-string-view.hh',
'box_ptr.hh',
'canon-path.hh',
'cgroup.hh',
@@ -92,6 +95,7 @@ libutil_headers = files(
'monitor-fd.hh',
'mount.hh',
'namespaces.hh',
+ 'notifying-counter.hh',
'pool.hh',
'position.hh',
'print-elided.hh',
@@ -101,6 +105,7 @@ libutil_headers = files(
'regex-combinators.hh',
'regex.hh',
'repair-flag.hh',
+ 'result.hh',
'serialise.hh',
'shlex.hh',
'signals.hh',
diff --git a/src/libutil/notifying-counter.hh b/src/libutil/notifying-counter.hh
new file mode 100644
index 000000000..dc58aac91
--- /dev/null
+++ b/src/libutil/notifying-counter.hh
@@ -0,0 +1,99 @@
+#pragma once
+/// @file
+
+#include <cassert>
+#include <functional>
+#include <memory>
+
+namespace nix {
+
+template<std::integral T>
+class NotifyingCounter
+{
+private:
+ T counter;
+ std::function<void()> notify;
+
+public:
+ class Bump
+ {
+ friend class NotifyingCounter;
+
+ struct SubOnFree
+ {
+ T delta;
+
+ void operator()(NotifyingCounter * c) const
+ {
+ c->add(-delta);
+ }
+ };
+
+ // lightly misuse unique_ptr to get RAII types with destructor callbacks
+ std::unique_ptr<NotifyingCounter<T>, SubOnFree> at;
+
+ Bump(NotifyingCounter<T> & at, T delta) : at(&at, {delta}) {}
+
+ public:
+ Bump() = default;
+ Bump(decltype(nullptr)) {}
+
+ T delta() const
+ {
+ return at ? at.get_deleter().delta : 0;
+ }
+
+ void reset()
+ {
+ at.reset();
+ }
+ };
+
+ explicit NotifyingCounter(std::function<void()> notify, T initial = 0)
+ : counter(initial)
+ , notify(std::move(notify))
+ {
+ assert(this->notify);
+ }
+
+ // bumps hold pointers to this, so we should neither copy nor move.
+ NotifyingCounter(const NotifyingCounter &) = delete;
+ NotifyingCounter & operator=(const NotifyingCounter &) = delete;
+ NotifyingCounter(NotifyingCounter &&) = delete;
+ NotifyingCounter & operator=(NotifyingCounter &&) = delete;
+
+ T get() const
+ {
+ return counter;
+ }
+
+ operator T() const
+ {
+ return counter;
+ }
+
+ void add(T delta)
+ {
+ counter += delta;
+ notify();
+ }
+
+ NotifyingCounter & operator+=(T delta)
+ {
+ add(delta);
+ return *this;
+ }
+
+ NotifyingCounter & operator++(int)
+ {
+ return *this += 1;
+ }
+
+ Bump addTemporarily(T delta)
+ {
+ add(delta);
+ return Bump{*this, delta};
+ }
+};
+
+}
diff --git a/src/libutil/processes.cc b/src/libutil/processes.cc
index 61e1ad556..eec592221 100644
--- a/src/libutil/processes.cc
+++ b/src/libutil/processes.cc
@@ -3,6 +3,7 @@
#include "finally.hh"
#include "logging.hh"
#include "processes.hh"
+#include "strings.hh"
#include "serialise.hh"
#include "signals.hh"
diff --git a/src/libutil/regex.cc b/src/libutil/regex.cc
index a9e6c6bee..a12d13550 100644
--- a/src/libutil/regex.cc
+++ b/src/libutil/regex.cc
@@ -1,6 +1,9 @@
#include <string>
#include <regex>
+// Declared as extern in precompiled-headers.hh
+template class std::basic_regex<char>;
+
namespace nix::regex {
std::string quoteRegexChars(const std::string & raw)
{
diff --git a/src/libutil/result.hh b/src/libutil/result.hh
new file mode 100644
index 000000000..b01766fe4
--- /dev/null
+++ b/src/libutil/result.hh
@@ -0,0 +1,24 @@
+#pragma once
+/// @file
+
+#include <boost/outcome/std_outcome.hpp>
+#include <boost/outcome/std_result.hpp>
+#include <boost/outcome/success_failure.hpp>
+#include <exception>
+
+namespace nix {
+
+template<typename T, typename E = std::exception_ptr>
+using Result = boost::outcome_v2::std_result<T, E>;
+
+template<typename T, typename D, typename E = std::exception_ptr>
+using Outcome = boost::outcome_v2::std_outcome<T, D, E>;
+
+namespace result {
+
+using boost::outcome_v2::success;
+using boost::outcome_v2::failure;
+
+}
+
+}
diff --git a/src/libutil/serialise.cc b/src/libutil/serialise.cc
index f1db05b0b..f509fedff 100644
--- a/src/libutil/serialise.cc
+++ b/src/libutil/serialise.cc
@@ -1,4 +1,5 @@
#include "serialise.hh"
+#include "charptr-cast.hh"
#include "signals.hh"
#include <cstring>
@@ -8,6 +9,46 @@
namespace nix {
+namespace {
+/**
+ * Convert a little-endian integer to host order.
+ */
+template<typename T>
+T readLittleEndian(unsigned char * p)
+{
+ T x = 0;
+ for (size_t i = 0; i < sizeof(x); ++i, ++p) {
+ x |= ((T) *p) << (i * 8);
+ }
+ return x;
+}
+}
+
+template<typename T>
+T readNum(Source & source)
+{
+ unsigned char buf[8];
+ source(charptr_cast<char *>(buf), sizeof(buf));
+
+ auto n = readLittleEndian<uint64_t>(buf);
+
+ if (n > (uint64_t) std::numeric_limits<T>::max())
+ throw SerialisationError("serialised integer %d is too large for type '%s'", n, typeid(T).name());
+
+ return (T) n;
+}
+
+template bool readNum<bool>(Source & source);
+
+template unsigned char readNum<unsigned char>(Source & source);
+
+template unsigned int readNum<unsigned int>(Source & source);
+
+template unsigned long readNum<unsigned long>(Source & source);
+template long readNum<long>(Source & source);
+
+template unsigned long long readNum<unsigned long long>(Source & source);
+template long long readNum<long long>(Source & source);
void BufferedSink::operator () (std::string_view data)
{
@@ -126,7 +167,7 @@ size_t FdSource::readUnbuffered(char * data, size_t len)
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(std::string(*endOfFileError)); }
+ if (n == 0) { _good = false; throw EndOfFile(endOfFileError()); }
read += n;
return n;
}
@@ -137,6 +178,11 @@ bool FdSource::good()
return _good;
}
+std::string FdSource::endOfFileError() const
+{
+ return specialEndOfFileError.has_value() ? *specialEndOfFileError : "unexpected end-of-file";
+}
+
size_t StringSource::read(char * data, size_t len)
{
diff --git a/src/libutil/serialise.hh b/src/libutil/serialise.hh
index 6c637bd35..612658b2d 100644
--- a/src/libutil/serialise.hh
+++ b/src/libutil/serialise.hh
@@ -1,16 +1,13 @@
#pragma once
///@file
-#include <concepts>
#include <memory>
+#include "charptr-cast.hh"
#include "generator.hh"
-#include "strings.hh"
#include "types.hh"
#include "file-descriptor.hh"
-namespace boost::context { struct stack_context; }
-
namespace nix {
@@ -154,7 +151,10 @@ struct FdSource : BufferedSource
{
int fd;
size_t read = 0;
- BackedStringView endOfFileError{"unexpected end-of-file"};
+ /** Defaults to "unexpected end-of-file" */
+ std::optional<std::string> specialEndOfFileError;
+
+ std::string endOfFileError() const;
FdSource() : fd(-1) { }
FdSource(int fd) : fd(fd) { }
@@ -387,7 +387,7 @@ struct SerializingTransform
buf[5] = (n >> 40) & 0xff;
buf[6] = (n >> 48) & 0xff;
buf[7] = (unsigned char) (n >> 56) & 0xff;
- return {reinterpret_cast<const char *>(buf.begin()), 8};
+ return {charptr_cast<const char *>(buf.begin()), 8};
}
static Bytes padding(size_t unpadded)
@@ -419,6 +419,9 @@ struct SerializingTransform
void writePadding(size_t len, Sink & sink);
+// NOLINTBEGIN(cppcoreguidelines-avoid-capturing-lambda-coroutines):
+// These coroutines do their entire job before the semicolon and are not
+// retained, so they live long enough.
inline Sink & operator<<(Sink & sink, uint64_t u)
{
return sink << [&]() -> WireFormatGenerator { co_yield u; }();
@@ -443,23 +446,12 @@ inline Sink & operator<<(Sink & sink, const Error & ex)
{
return sink << [&]() -> WireFormatGenerator { co_yield ex; }();
}
+// NOLINTEND(cppcoreguidelines-avoid-capturing-lambda-coroutines)
MakeError(SerialisationError, Error);
template<typename T>
-T readNum(Source & source)
-{
- unsigned char buf[8];
- source((char *) buf, sizeof(buf));
-
- auto n = readLittleEndian<uint64_t>(buf);
-
- if (n > (uint64_t) std::numeric_limits<T>::max())
- throw SerialisationError("serialised integer %d is too large for type '%s'", n, typeid(T).name());
-
- return (T) n;
-}
-
+T readNum(Source & source);
inline unsigned int readInt(Source & source)
{
@@ -542,13 +534,17 @@ struct FramedSource : Source
~FramedSource()
{
- if (!eof) {
- while (true) {
- auto n = readInt(from);
- if (!n) break;
- std::vector<char> data(n);
- from(data.data(), n);
+ try {
+ if (!eof) {
+ while (true) {
+ auto n = readInt(from);
+ if (!n) break;
+ std::vector<char> data(n);
+ from(data.data(), n);
+ }
}
+ } catch (...) {
+ ignoreException();
}
}
@@ -612,23 +608,6 @@ struct FramedSink : nix::BufferedSink
};
};
-/**
- * Stack allocation strategy for sinkToSource.
- * Mutable to avoid a boehm gc dependency in libutil.
- *
- * boost::context doesn't provide a virtual class, so we define our own.
- */
-struct StackAllocator {
- virtual boost::context::stack_context allocate() = 0;
- virtual void deallocate(boost::context::stack_context sctx) = 0;
-
- /**
- * The stack allocator to use in sinkToSource and potentially elsewhere.
- * It is reassigned by the initGC() method in libexpr.
- */
- static StackAllocator *defaultAllocator;
-};
-
/* Disabling GC when entering a coroutine (without the boehm patch).
mutable to avoid boehm gc dependency in libutil.
*/
diff --git a/src/libutil/signals.cc b/src/libutil/signals.cc
index a94c2802a..04a697d01 100644
--- a/src/libutil/signals.cc
+++ b/src/libutil/signals.cc
@@ -3,6 +3,7 @@
#include "sync.hh"
#include "terminal.hh"
+#include <map>
#include <thread>
namespace nix {
diff --git a/src/libutil/source-path.cc b/src/libutil/source-path.cc
index cfaac20c0..782005ef1 100644
--- a/src/libutil/source-path.cc
+++ b/src/libutil/source-path.cc
@@ -1,4 +1,5 @@
#include "source-path.hh"
+#include "strings.hh"
namespace nix {
diff --git a/src/libutil/strings.hh b/src/libutil/strings.hh
index 7330e2063..782807b61 100644
--- a/src/libutil/strings.hh
+++ b/src/libutil/strings.hh
@@ -165,19 +165,6 @@ std::optional<N> string2Float(const std::string_view s);
/**
- * Convert a little-endian integer to host order.
- */
-template<typename T>
-T readLittleEndian(unsigned char * p)
-{
- T x = 0;
- for (size_t i = 0; i < sizeof(x); ++i, ++p) {
- x |= ((T) *p) << (i * 8);
- }
- return x;
-}
-
-/**
* Convert a string to lower case.
*/
std::string toLower(const std::string & s);
@@ -213,8 +200,18 @@ std::string showBytes(uint64_t bytes);
/**
- * Provide an addition operator between strings and string_views
+ * Provide an addition operator between `std::string` and `std::string_view`
* inexplicably omitted from the standard library.
+ *
+ * > The reason for this is given in n3512 string_ref: a non-owning reference
+ * to a string, revision 2 by Jeffrey Yasskin:
+ * >
+ * > > I also omitted operator+(basic_string, basic_string_ref) because LLVM
+ * > > returns a lightweight object from this overload and only performs the
+ * > > concatenation lazily. If we define this overload, we'll have a hard time
+ * > > introducing that lightweight concatenation later.
+ *
+ * See: https://stackoverflow.com/a/47735624
*/
inline std::string operator + (const std::string & s1, std::string_view s2)
{
diff --git a/src/libutil/thread-pool.hh b/src/libutil/thread-pool.hh
index 3db7ce88f..380e1a2d2 100644
--- a/src/libutil/thread-pool.hh
+++ b/src/libutil/thread-pool.hh
@@ -4,6 +4,7 @@
#include "error.hh"
#include "sync.hh"
+#include <map>
#include <queue>
#include <functional>
#include <thread>
diff --git a/src/libutil/types.hh b/src/libutil/types.hh
index 13cb062fb..66c41fe59 100644
--- a/src/libutil/types.hh
+++ b/src/libutil/types.hh
@@ -1,17 +1,15 @@
#pragma once
///@file
-#include "ref.hh"
-
#include <list>
#include <optional>
#include <set>
#include <string>
-#include <limits>
+#include <string_view>
#include <map>
-#include <variant>
#include <vector>
#include <span>
+#include <stdint.h> // IWYU pragma: keep (this is used literally everywhere)
namespace nix {
@@ -166,70 +164,4 @@ constexpr auto enumerate(T && iterable)
template<class... Ts> struct overloaded : Ts... { using Ts::operator()...; };
template<class... Ts> overloaded(Ts...) -> overloaded<Ts...>;
-
-
-/**
- * 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
- * newly allocated storage for values.
- *
- * We do not define implicit conversions, even with ref qualifiers,
- * since those can easily become ambiguous to the reader and can degrade
- * into copying behaviour we want to avoid.
- */
-class BackedStringView {
-private:
- std::variant<std::string, std::string_view> 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:
- std::string_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) {}
- template<size_t N>
- BackedStringView(const char (& lit)[N]): data(std::string_view(lit)) {}
-
- BackedStringView(const BackedStringView &) = delete;
- BackedStringView & operator=(const BackedStringView &) = delete;
-
- /**
- * We only want move operations defined since the sole purpose of
- * this type is to avoid copies.
- */
- BackedStringView(BackedStringView && other) = default;
- BackedStringView & operator=(BackedStringView && other) = default;
-
- bool isOwned() const
- {
- return std::holds_alternative<std::string>(data);
- }
-
- std::string toOwned() &&
- {
- return isOwned()
- ? std::move(std::get<std::string>(data))
- : std::string(std::get<std::string_view>(data));
- }
-
- std::string_view operator*() const
- {
- return isOwned()
- ? std::get<std::string>(data)
- : std::get<std::string_view>(data);
- }
- Ptr operator->() const { return Ptr(**this); }
-};
-
}
diff --git a/src/libutil/url.hh b/src/libutil/url.hh
index d2413ec0e..a821301ba 100644
--- a/src/libutil/url.hh
+++ b/src/libutil/url.hh
@@ -2,6 +2,7 @@
///@file
#include "error.hh"
+#include <map>
namespace nix {
diff --git a/src/libutil/users.cc b/src/libutil/users.cc
index a9a8a7353..ce36bad9b 100644
--- a/src/libutil/users.cc
+++ b/src/libutil/users.cc
@@ -36,7 +36,7 @@ Path getHome()
std::optional<std::string> unownedUserHomeDir = {};
auto homeDir = getEnv("HOME");
if (homeDir) {
- // Only use $HOME if doesn't exist or is owned by the current user.
+ // Only use `$HOME` if it exists and is owned by the current user.
struct stat st;
int result = stat(homeDir->c_str(), &st);
if (result != 0) {
diff --git a/src/libutil/variant-wrapper.hh b/src/libutil/variant-wrapper.hh
index cedcb999c..a809cd2a4 100644
--- a/src/libutil/variant-wrapper.hh
+++ b/src/libutil/variant-wrapper.hh
@@ -8,6 +8,7 @@
* Force the default versions of all constructors (copy, move, copy
* assignment).
*/
+// NOLINTBEGIN(bugprone-macro-parentheses)
#define FORCE_DEFAULT_CONSTRUCTORS(CLASS_NAME) \
CLASS_NAME(const CLASS_NAME &) = default; \
CLASS_NAME(CLASS_NAME &) = default; \
@@ -15,6 +16,7 @@
\
CLASS_NAME & operator =(const CLASS_NAME &) = default; \
CLASS_NAME & operator =(CLASS_NAME &) = default;
+// NOLINTEND(bugprone-macro-parentheses)
/**
* Make a wrapper constructor. All args are forwarded to the