Merge remote-tracking branch 'me/more-rust-ffi' into no-stringly-typed-derivation-output
This commit is contained in:
commit
5b4cd84bc2
|
@ -31,7 +31,7 @@ readonly NIX_FIRST_BUILD_UID="30001"
|
||||||
readonly NIX_ROOT="/nix"
|
readonly NIX_ROOT="/nix"
|
||||||
readonly NIX_EXTRA_CONF=${NIX_EXTRA_CONF:-}
|
readonly NIX_EXTRA_CONF=${NIX_EXTRA_CONF:-}
|
||||||
|
|
||||||
readonly PROFILE_TARGETS=("/etc/bashrc" "/etc/profile.d/nix.sh" "/etc/zshrc")
|
readonly PROFILE_TARGETS=("/etc/bashrc" "/etc/profile.d/nix.sh" "/etc/zshenv")
|
||||||
readonly PROFILE_BACKUP_SUFFIX=".backup-before-nix"
|
readonly PROFILE_BACKUP_SUFFIX=".backup-before-nix"
|
||||||
readonly PROFILE_NIX_FILE="$NIX_ROOT/var/nix/profiles/default/etc/profile.d/nix-daemon.sh"
|
readonly PROFILE_NIX_FILE="$NIX_ROOT/var/nix/profiles/default/etc/profile.d/nix-daemon.sh"
|
||||||
|
|
||||||
|
|
|
@ -36,7 +36,9 @@ tarball="$tmpDir/$(basename "$tmpDir/nix-@nixVersion@-$system.tar.xz")"
|
||||||
|
|
||||||
require_util curl "download the binary tarball"
|
require_util curl "download the binary tarball"
|
||||||
require_util tar "unpack the binary tarball"
|
require_util tar "unpack the binary tarball"
|
||||||
require_util xz "unpack the binary tarball"
|
if [ "$(uname -s)" != "Darwin" ]; then
|
||||||
|
require_util xz "unpack the binary tarball"
|
||||||
|
fi
|
||||||
|
|
||||||
echo "downloading Nix @nixVersion@ binary tarball for $system from '$url' to '$tmpDir'..."
|
echo "downloading Nix @nixVersion@ binary tarball for $system from '$url' to '$tmpDir'..."
|
||||||
curl -L "$url" -o "$tarball" || oops "failed to download '$url'"
|
curl -L "$url" -o "$tarball" || oops "failed to download '$url'"
|
||||||
|
|
|
@ -1039,7 +1039,7 @@ static void prim_toFile(EvalState & state, const Pos & pos, Value * * args, Valu
|
||||||
|
|
||||||
|
|
||||||
static void addPath(EvalState & state, const Pos & pos, const string & name, const Path & path_,
|
static void addPath(EvalState & state, const Pos & pos, const string & name, const Path & path_,
|
||||||
Value * filterFun, FileIngestionMethod recursive, const Hash & expectedHash, Value & v)
|
Value * filterFun, FileIngestionMethod method, const Hash & expectedHash, Value & v)
|
||||||
{
|
{
|
||||||
const auto path = evalSettings.pureEval && expectedHash ?
|
const auto path = evalSettings.pureEval && expectedHash ?
|
||||||
path_ :
|
path_ :
|
||||||
|
@ -1070,12 +1070,12 @@ static void addPath(EvalState & state, const Pos & pos, const string & name, con
|
||||||
|
|
||||||
std::optional<StorePath> expectedStorePath;
|
std::optional<StorePath> expectedStorePath;
|
||||||
if (expectedHash)
|
if (expectedHash)
|
||||||
expectedStorePath = state.store->makeFixedOutputPath(recursive, expectedHash, name);
|
expectedStorePath = state.store->makeFixedOutputPath(method, expectedHash, name);
|
||||||
Path dstPath;
|
Path dstPath;
|
||||||
if (!expectedHash || !state.store->isValidPath(*expectedStorePath)) {
|
if (!expectedHash || !state.store->isValidPath(*expectedStorePath)) {
|
||||||
dstPath = state.store->printStorePath(settings.readOnlyMode
|
dstPath = state.store->printStorePath(settings.readOnlyMode
|
||||||
? state.store->computeStorePathForPath(name, path, recursive, htSHA256, filter).first
|
? state.store->computeStorePathForPath(name, path, method, htSHA256, filter).first
|
||||||
: state.store->addToStore(name, path, recursive, htSHA256, filter, state.repair));
|
: state.store->addToStore(name, path, method, htSHA256, filter, state.repair));
|
||||||
if (expectedHash && expectedStorePath != state.store->parseStorePath(dstPath))
|
if (expectedHash && expectedStorePath != state.store->parseStorePath(dstPath))
|
||||||
throw Error("store path mismatch in (possibly filtered) path added from '%s'", path);
|
throw Error("store path mismatch in (possibly filtered) path added from '%s'", path);
|
||||||
} else
|
} else
|
||||||
|
@ -1105,7 +1105,7 @@ static void prim_path(EvalState & state, const Pos & pos, Value * * args, Value
|
||||||
Path path;
|
Path path;
|
||||||
string name;
|
string name;
|
||||||
Value * filterFun = nullptr;
|
Value * filterFun = nullptr;
|
||||||
auto recursive = FileIngestionMethod::Recursive;
|
auto method = FileIngestionMethod::Recursive;
|
||||||
Hash expectedHash;
|
Hash expectedHash;
|
||||||
|
|
||||||
for (auto & attr : *args[0]->attrs) {
|
for (auto & attr : *args[0]->attrs) {
|
||||||
|
@ -1121,7 +1121,7 @@ static void prim_path(EvalState & state, const Pos & pos, Value * * args, Value
|
||||||
state.forceValue(*attr.value, pos);
|
state.forceValue(*attr.value, pos);
|
||||||
filterFun = attr.value;
|
filterFun = attr.value;
|
||||||
} else if (n == "recursive")
|
} else if (n == "recursive")
|
||||||
recursive = FileIngestionMethod { state.forceBool(*attr.value, *attr.pos) };
|
method = FileIngestionMethod { state.forceBool(*attr.value, *attr.pos) };
|
||||||
else if (n == "sha256")
|
else if (n == "sha256")
|
||||||
expectedHash = Hash(state.forceStringNoCtx(*attr.value, *attr.pos), htSHA256);
|
expectedHash = Hash(state.forceStringNoCtx(*attr.value, *attr.pos), htSHA256);
|
||||||
else
|
else
|
||||||
|
@ -1132,7 +1132,7 @@ static void prim_path(EvalState & state, const Pos & pos, Value * * args, Value
|
||||||
if (name.empty())
|
if (name.empty())
|
||||||
name = baseNameOf(path);
|
name = baseNameOf(path);
|
||||||
|
|
||||||
addPath(state, pos, name, path, filterFun, recursive, expectedHash, v);
|
addPath(state, pos, name, path, filterFun, method, expectedHash, v);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -327,7 +327,7 @@ void BinaryCacheStore::queryPathInfoUncached(const StorePath & storePath,
|
||||||
}
|
}
|
||||||
|
|
||||||
StorePath BinaryCacheStore::addToStore(const string & name, const Path & srcPath,
|
StorePath BinaryCacheStore::addToStore(const string & name, const Path & srcPath,
|
||||||
FileIngestionMethod recursive, HashType hashAlgo, PathFilter & filter, RepairFlag repair)
|
FileIngestionMethod method, HashType hashAlgo, PathFilter & filter, RepairFlag repair)
|
||||||
{
|
{
|
||||||
// FIXME: some cut&paste from LocalStore::addToStore().
|
// FIXME: some cut&paste from LocalStore::addToStore().
|
||||||
|
|
||||||
|
@ -336,7 +336,7 @@ StorePath BinaryCacheStore::addToStore(const string & name, const Path & srcPath
|
||||||
small files. */
|
small files. */
|
||||||
StringSink sink;
|
StringSink sink;
|
||||||
Hash h;
|
Hash h;
|
||||||
if (recursive == FileIngestionMethod::Recursive) {
|
if (method == FileIngestionMethod::Recursive) {
|
||||||
dumpPath(srcPath, sink, filter);
|
dumpPath(srcPath, sink, filter);
|
||||||
h = hashString(hashAlgo, *sink.s);
|
h = hashString(hashAlgo, *sink.s);
|
||||||
} else {
|
} else {
|
||||||
|
@ -345,7 +345,7 @@ StorePath BinaryCacheStore::addToStore(const string & name, const Path & srcPath
|
||||||
h = hashString(hashAlgo, s);
|
h = hashString(hashAlgo, s);
|
||||||
}
|
}
|
||||||
|
|
||||||
ValidPathInfo info(makeFixedOutputPath(recursive, h, name));
|
ValidPathInfo info(makeFixedOutputPath(method, h, name));
|
||||||
|
|
||||||
addToStore(info, sink.s, repair, CheckSigs, nullptr);
|
addToStore(info, sink.s, repair, CheckSigs, nullptr);
|
||||||
|
|
||||||
|
|
|
@ -79,7 +79,7 @@ public:
|
||||||
std::shared_ptr<FSAccessor> accessor) override;
|
std::shared_ptr<FSAccessor> accessor) override;
|
||||||
|
|
||||||
StorePath addToStore(const string & name, const Path & srcPath,
|
StorePath addToStore(const string & name, const Path & srcPath,
|
||||||
FileIngestionMethod recursive, HashType hashAlgo,
|
FileIngestionMethod method, HashType hashAlgo,
|
||||||
PathFilter & filter, RepairFlag repair) override;
|
PathFilter & filter, RepairFlag repair) override;
|
||||||
|
|
||||||
StorePath addTextToStore(const string & name, const string & s,
|
StorePath addTextToStore(const string & name, const string & s,
|
||||||
|
|
|
@ -2734,7 +2734,7 @@ struct RestrictedStore : public LocalFSStore
|
||||||
{ throw Error("queryPathFromHashPart"); }
|
{ throw Error("queryPathFromHashPart"); }
|
||||||
|
|
||||||
StorePath addToStore(const string & name, const Path & srcPath,
|
StorePath addToStore(const string & name, const Path & srcPath,
|
||||||
FileIngestionMethod recursive = FileIngestionMethod::Recursive, HashType hashAlgo = htSHA256,
|
FileIngestionMethod method = FileIngestionMethod::Recursive, HashType hashAlgo = htSHA256,
|
||||||
PathFilter & filter = defaultPathFilter, RepairFlag repair = NoRepair) override
|
PathFilter & filter = defaultPathFilter, RepairFlag repair = NoRepair) override
|
||||||
{ throw Error("addToStore"); }
|
{ throw Error("addToStore"); }
|
||||||
|
|
||||||
|
@ -2747,9 +2747,9 @@ struct RestrictedStore : public LocalFSStore
|
||||||
}
|
}
|
||||||
|
|
||||||
StorePath addToStoreFromDump(const string & dump, const string & name,
|
StorePath addToStoreFromDump(const string & dump, const string & name,
|
||||||
FileIngestionMethod recursive = FileIngestionMethod::Recursive, HashType hashAlgo = htSHA256, RepairFlag repair = NoRepair) override
|
FileIngestionMethod method = FileIngestionMethod::Recursive, HashType hashAlgo = htSHA256, RepairFlag repair = NoRepair) override
|
||||||
{
|
{
|
||||||
auto path = next->addToStoreFromDump(dump, name, recursive, hashAlgo, repair);
|
auto path = next->addToStoreFromDump(dump, name, method, hashAlgo, repair);
|
||||||
goal.addDependency(path);
|
goal.addDependency(path);
|
||||||
return path;
|
return path;
|
||||||
}
|
}
|
||||||
|
|
|
@ -37,6 +37,7 @@ struct DerivationOutput
|
||||||
DerivationOutput(const DerivationOutput &) = default;
|
DerivationOutput(const DerivationOutput &) = default;
|
||||||
DerivationOutput(DerivationOutput &&) = default;
|
DerivationOutput(DerivationOutput &&) = default;
|
||||||
DerivationOutput & operator = (const DerivationOutput &) = default;
|
DerivationOutput & operator = (const DerivationOutput &) = default;
|
||||||
|
void parseHashInfo(FileIngestionMethod & recursive, Hash & hash) const;
|
||||||
};
|
};
|
||||||
|
|
||||||
typedef std::map<string, DerivationOutput> DerivationOutputs;
|
typedef std::map<string, DerivationOutput> DerivationOutputs;
|
||||||
|
|
|
@ -1045,11 +1045,11 @@ void LocalStore::addToStore(const ValidPathInfo & info, Source & source,
|
||||||
|
|
||||||
|
|
||||||
StorePath LocalStore::addToStoreFromDump(const string & dump, const string & name,
|
StorePath LocalStore::addToStoreFromDump(const string & dump, const string & name,
|
||||||
FileIngestionMethod recursive, HashType hashAlgo, RepairFlag repair)
|
FileIngestionMethod method, HashType hashAlgo, RepairFlag repair)
|
||||||
{
|
{
|
||||||
Hash h = hashString(hashAlgo, dump);
|
Hash h = hashString(hashAlgo, dump);
|
||||||
|
|
||||||
auto dstPath = makeFixedOutputPath(recursive, h, name);
|
auto dstPath = makeFixedOutputPath(method, h, name);
|
||||||
|
|
||||||
addTempRoot(dstPath);
|
addTempRoot(dstPath);
|
||||||
|
|
||||||
|
@ -1069,7 +1069,7 @@ StorePath LocalStore::addToStoreFromDump(const string & dump, const string & nam
|
||||||
|
|
||||||
autoGC();
|
autoGC();
|
||||||
|
|
||||||
if (recursive == FileIngestionMethod::Recursive) {
|
if (method == FileIngestionMethod::Recursive) {
|
||||||
StringSource source(dump);
|
StringSource source(dump);
|
||||||
restorePath(realPath, source);
|
restorePath(realPath, source);
|
||||||
} else
|
} else
|
||||||
|
@ -1082,7 +1082,7 @@ StorePath LocalStore::addToStoreFromDump(const string & dump, const string & nam
|
||||||
above (if called with recursive == true and hashAlgo ==
|
above (if called with recursive == true and hashAlgo ==
|
||||||
sha256); otherwise, compute it here. */
|
sha256); otherwise, compute it here. */
|
||||||
HashResult hash;
|
HashResult hash;
|
||||||
if (recursive == FileIngestionMethod::Recursive) {
|
if (method == FileIngestionMethod::Recursive) {
|
||||||
hash.first = hashAlgo == htSHA256 ? h : hashString(htSHA256, dump);
|
hash.first = hashAlgo == htSHA256 ? h : hashString(htSHA256, dump);
|
||||||
hash.second = dump.size();
|
hash.second = dump.size();
|
||||||
} else
|
} else
|
||||||
|
@ -1093,7 +1093,7 @@ StorePath LocalStore::addToStoreFromDump(const string & dump, const string & nam
|
||||||
ValidPathInfo info(dstPath.clone());
|
ValidPathInfo info(dstPath.clone());
|
||||||
info.narHash = hash.first;
|
info.narHash = hash.first;
|
||||||
info.narSize = hash.second;
|
info.narSize = hash.second;
|
||||||
info.ca = makeFixedOutputCA(recursive, h);
|
info.ca = makeFixedOutputCA(method, h);
|
||||||
registerValidPath(info);
|
registerValidPath(info);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1105,7 +1105,7 @@ StorePath LocalStore::addToStoreFromDump(const string & dump, const string & nam
|
||||||
|
|
||||||
|
|
||||||
StorePath LocalStore::addToStore(const string & name, const Path & _srcPath,
|
StorePath LocalStore::addToStore(const string & name, const Path & _srcPath,
|
||||||
FileIngestionMethod recursive, HashType hashAlgo, PathFilter & filter, RepairFlag repair)
|
FileIngestionMethod method, HashType hashAlgo, PathFilter & filter, RepairFlag repair)
|
||||||
{
|
{
|
||||||
Path srcPath(absPath(_srcPath));
|
Path srcPath(absPath(_srcPath));
|
||||||
|
|
||||||
|
@ -1113,12 +1113,12 @@ StorePath LocalStore::addToStore(const string & name, const Path & _srcPath,
|
||||||
method for very large paths, but `copyPath' is mainly used for
|
method for very large paths, but `copyPath' is mainly used for
|
||||||
small files. */
|
small files. */
|
||||||
StringSink sink;
|
StringSink sink;
|
||||||
if (recursive == FileIngestionMethod::Recursive)
|
if (method == FileIngestionMethod::Recursive)
|
||||||
dumpPath(srcPath, sink, filter);
|
dumpPath(srcPath, sink, filter);
|
||||||
else
|
else
|
||||||
sink.s = make_ref<std::string>(readFile(srcPath));
|
sink.s = make_ref<std::string>(readFile(srcPath));
|
||||||
|
|
||||||
return addToStoreFromDump(*sink.s, name, recursive, hashAlgo, repair);
|
return addToStoreFromDump(*sink.s, name, method, hashAlgo, repair);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -201,12 +201,12 @@ StorePath Store::makeTextPath(std::string_view name, const Hash & hash,
|
||||||
|
|
||||||
|
|
||||||
std::pair<StorePath, Hash> Store::computeStorePathForPath(std::string_view name,
|
std::pair<StorePath, Hash> Store::computeStorePathForPath(std::string_view name,
|
||||||
const Path & srcPath, FileIngestionMethod recursive, HashType hashAlgo, PathFilter & filter) const
|
const Path & srcPath, FileIngestionMethod method, HashType hashAlgo, PathFilter & filter) const
|
||||||
{
|
{
|
||||||
Hash h = recursive == FileIngestionMethod::Recursive
|
Hash h = method == FileIngestionMethod::Recursive
|
||||||
? hashPath(hashAlgo, srcPath, filter).first
|
? hashPath(hashAlgo, srcPath, filter).first
|
||||||
: hashFile(hashAlgo, srcPath);
|
: hashFile(hashAlgo, srcPath);
|
||||||
return std::make_pair(makeFixedOutputPath(recursive, h, name), h);
|
return std::make_pair(makeFixedOutputPath(method, h, name), h);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -65,60 +65,63 @@ void Config::getSettings(std::map<std::string, SettingInfo> & res, bool override
|
||||||
res.emplace(opt.first, SettingInfo{opt.second.setting->to_string(), opt.second.setting->description});
|
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) {
|
||||||
|
unsigned int pos = 0;
|
||||||
|
|
||||||
|
while (pos < contents.size()) {
|
||||||
|
string line;
|
||||||
|
while (pos < contents.size() && contents[pos] != '\n')
|
||||||
|
line += contents[pos++];
|
||||||
|
pos++;
|
||||||
|
|
||||||
|
string::size_type hash = line.find('#');
|
||||||
|
if (hash != string::npos)
|
||||||
|
line = string(line, 0, hash);
|
||||||
|
|
||||||
|
vector<string> tokens = tokenizeString<vector<string> >(line);
|
||||||
|
if (tokens.empty()) continue;
|
||||||
|
|
||||||
|
if (tokens.size() < 2)
|
||||||
|
throw UsageError("illegal configuration line '%1%' in '%2%'", line, path);
|
||||||
|
|
||||||
|
auto include = false;
|
||||||
|
auto ignoreMissing = false;
|
||||||
|
if (tokens[0] == "include")
|
||||||
|
include = true;
|
||||||
|
else if (tokens[0] == "!include") {
|
||||||
|
include = true;
|
||||||
|
ignoreMissing = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
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)) {
|
||||||
|
applyConfigFile(p);
|
||||||
|
} else if (!ignoreMissing) {
|
||||||
|
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);
|
||||||
|
|
||||||
|
string name = tokens[0];
|
||||||
|
|
||||||
|
vector<string>::iterator i = tokens.begin();
|
||||||
|
advance(i, 2);
|
||||||
|
|
||||||
|
set(name, concatStringsSep(" ", Strings(i, tokens.end()))); // FIXME: slow
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
void AbstractConfig::applyConfigFile(const Path & path)
|
void AbstractConfig::applyConfigFile(const Path & path)
|
||||||
{
|
{
|
||||||
try {
|
try {
|
||||||
string contents = readFile(path);
|
string contents = readFile(path);
|
||||||
|
applyConfig(contents, path);
|
||||||
unsigned int pos = 0;
|
|
||||||
|
|
||||||
while (pos < contents.size()) {
|
|
||||||
string line;
|
|
||||||
while (pos < contents.size() && contents[pos] != '\n')
|
|
||||||
line += contents[pos++];
|
|
||||||
pos++;
|
|
||||||
|
|
||||||
string::size_type hash = line.find('#');
|
|
||||||
if (hash != string::npos)
|
|
||||||
line = string(line, 0, hash);
|
|
||||||
|
|
||||||
vector<string> tokens = tokenizeString<vector<string> >(line);
|
|
||||||
if (tokens.empty()) continue;
|
|
||||||
|
|
||||||
if (tokens.size() < 2)
|
|
||||||
throw UsageError("illegal configuration line '%1%' in '%2%'", line, path);
|
|
||||||
|
|
||||||
auto include = false;
|
|
||||||
auto ignoreMissing = false;
|
|
||||||
if (tokens[0] == "include")
|
|
||||||
include = true;
|
|
||||||
else if (tokens[0] == "!include") {
|
|
||||||
include = true;
|
|
||||||
ignoreMissing = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
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)) {
|
|
||||||
applyConfigFile(p);
|
|
||||||
} else if (!ignoreMissing) {
|
|
||||||
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);
|
|
||||||
|
|
||||||
string name = tokens[0];
|
|
||||||
|
|
||||||
vector<string>::iterator i = tokens.begin();
|
|
||||||
advance(i, 2);
|
|
||||||
|
|
||||||
set(name, concatStringsSep(" ", Strings(i, tokens.end()))); // FIXME: slow
|
|
||||||
};
|
|
||||||
} catch (SysError &) { }
|
} catch (SysError &) { }
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -7,6 +7,38 @@
|
||||||
|
|
||||||
namespace nix {
|
namespace nix {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The Config class provides Nix runtime configurations.
|
||||||
|
*
|
||||||
|
* What is a Configuration?
|
||||||
|
* A collection of uniquely named Settings.
|
||||||
|
*
|
||||||
|
* What is a Setting?
|
||||||
|
* Each property that you can set in a configuration corresponds to a
|
||||||
|
* `Setting`. A setting records value and description of a property
|
||||||
|
* with a default and optional aliases.
|
||||||
|
*
|
||||||
|
* A valid configuration consists of settings that are registered to a
|
||||||
|
* `Config` object instance:
|
||||||
|
*
|
||||||
|
* Config config;
|
||||||
|
* Setting<std::string> 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
|
||||||
|
* "x86_64-linux", it's description is "the current system". All of the
|
||||||
|
* registered settings can then be accessed as shown below:
|
||||||
|
*
|
||||||
|
* std::map<std::string, Config::SettingInfo> settings;
|
||||||
|
* config.getSettings(settings);
|
||||||
|
* config["system"].description == "the current system"
|
||||||
|
* config["system"].value == "x86_64-linux"
|
||||||
|
*
|
||||||
|
*
|
||||||
|
* The above retrieves all currently known settings from the `Config` object
|
||||||
|
* and adds them to the `settings` map.
|
||||||
|
*/
|
||||||
|
|
||||||
class Args;
|
class Args;
|
||||||
class AbstractSetting;
|
class AbstractSetting;
|
||||||
class JSONPlaceholder;
|
class JSONPlaceholder;
|
||||||
|
@ -23,6 +55,10 @@ protected:
|
||||||
|
|
||||||
public:
|
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) = 0;
|
||||||
|
|
||||||
struct SettingInfo
|
struct SettingInfo
|
||||||
|
@ -31,18 +67,52 @@ public:
|
||||||
std::string description;
|
std::string description;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Adds the currently known settings to the given result map `res`.
|
||||||
|
* - res: map to store settings in
|
||||||
|
* - overridenOnly: when set to true only overridden settings will be added to `res`
|
||||||
|
*/
|
||||||
virtual void getSettings(std::map<std::string, SettingInfo> & res, bool overridenOnly = false) = 0;
|
virtual void getSettings(std::map<std::string, SettingInfo> & res, bool overridenOnly = 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 = "<unknown>");
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Applies a nix configuration file
|
||||||
|
* - path: the location of the config file to apply
|
||||||
|
*/
|
||||||
void applyConfigFile(const Path & path);
|
void applyConfigFile(const Path & path);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Resets the `overridden` flag of all Settings
|
||||||
|
*/
|
||||||
virtual void resetOverriden() = 0;
|
virtual void resetOverriden() = 0;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Outputs all settings to JSON
|
||||||
|
* - out: JSONObject to write the configuration to
|
||||||
|
*/
|
||||||
virtual void toJSON(JSONObject & out) = 0;
|
virtual void toJSON(JSONObject & out) = 0;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Converts settings to `Args` to be used on the command line interface
|
||||||
|
* - args: args to write to
|
||||||
|
* - category: category of the settings
|
||||||
|
*/
|
||||||
virtual void convertToArgs(Args & args, const std::string & category) = 0;
|
virtual void convertToArgs(Args & args, const std::string & category) = 0;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Logs a warning for each unregistered setting
|
||||||
|
*/
|
||||||
void warnUnknownSettings();
|
void warnUnknownSettings();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Re-applies all previously attempted changes to unknown settings
|
||||||
|
*/
|
||||||
void reapplyUnknownSettings();
|
void reapplyUnknownSettings();
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
264
src/libutil/tests/config.cc
Normal file
264
src/libutil/tests/config.cc
Normal file
|
@ -0,0 +1,264 @@
|
||||||
|
#include "json.hh"
|
||||||
|
#include "config.hh"
|
||||||
|
#include "args.hh"
|
||||||
|
|
||||||
|
#include <sstream>
|
||||||
|
#include <gtest/gtest.h>
|
||||||
|
|
||||||
|
namespace nix {
|
||||||
|
|
||||||
|
/* ----------------------------------------------------------------------------
|
||||||
|
* Config
|
||||||
|
* --------------------------------------------------------------------------*/
|
||||||
|
|
||||||
|
TEST(Config, setUndefinedSetting) {
|
||||||
|
Config config;
|
||||||
|
ASSERT_EQ(config.set("undefined-key", "value"), false);
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST(Config, setDefinedSetting) {
|
||||||
|
Config config;
|
||||||
|
std::string value;
|
||||||
|
Setting<std::string> 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<std::string, Config::SettingInfo> settings;
|
||||||
|
Setting<std::string> foo{&config, value, "name-of-the-setting", "description"};
|
||||||
|
|
||||||
|
config.getSettings(settings, /* overridenOnly = */ 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");
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST(Config, getDefinedOverridenSettingNotSet) {
|
||||||
|
Config config;
|
||||||
|
std::string value;
|
||||||
|
std::map<std::string, Config::SettingInfo> settings;
|
||||||
|
Setting<std::string> foo{&config, value, "name-of-the-setting", "description"};
|
||||||
|
|
||||||
|
config.getSettings(settings, /* overridenOnly = */ true);
|
||||||
|
const auto e = settings.find("name-of-the-setting");
|
||||||
|
ASSERT_EQ(e, settings.end());
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST(Config, getDefinedSettingSet1) {
|
||||||
|
Config config;
|
||||||
|
std::string value;
|
||||||
|
std::map<std::string, Config::SettingInfo> settings;
|
||||||
|
Setting<std::string> setting{&config, value, "name-of-the-setting", "description"};
|
||||||
|
|
||||||
|
setting.assign("value");
|
||||||
|
|
||||||
|
config.getSettings(settings, /* overridenOnly = */ 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");
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST(Config, getDefinedSettingSet2) {
|
||||||
|
Config config;
|
||||||
|
std::map<std::string, Config::SettingInfo> settings;
|
||||||
|
Setting<std::string> setting{&config, "", "name-of-the-setting", "description"};
|
||||||
|
|
||||||
|
ASSERT_TRUE(config.set("name-of-the-setting", "value"));
|
||||||
|
|
||||||
|
config.getSettings(settings, /* overridenOnly = */ 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");
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST(Config, addSetting) {
|
||||||
|
class TestSetting : public AbstractSetting {
|
||||||
|
public:
|
||||||
|
TestSetting() : AbstractSetting("test", "test", {}) {}
|
||||||
|
void set(const std::string & value) {}
|
||||||
|
std::string to_string() const { return {}; }
|
||||||
|
};
|
||||||
|
|
||||||
|
Config config;
|
||||||
|
TestSetting setting;
|
||||||
|
|
||||||
|
ASSERT_FALSE(config.set("test", "value"));
|
||||||
|
config.addSetting(&setting);
|
||||||
|
ASSERT_TRUE(config.set("test", "value"));
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST(Config, withInitialValue) {
|
||||||
|
const StringMap initials = {
|
||||||
|
{ "key", "value" },
|
||||||
|
};
|
||||||
|
Config config(initials);
|
||||||
|
|
||||||
|
{
|
||||||
|
std::map<std::string, Config::SettingInfo> settings;
|
||||||
|
config.getSettings(settings, /* overridenOnly = */ false);
|
||||||
|
ASSERT_EQ(settings.find("key"), settings.end());
|
||||||
|
}
|
||||||
|
|
||||||
|
Setting<std::string> setting{&config, "default-value", "key", "description"};
|
||||||
|
|
||||||
|
{
|
||||||
|
std::map<std::string, Config::SettingInfo> settings;
|
||||||
|
config.getSettings(settings, /* overridenOnly = */ false);
|
||||||
|
ASSERT_EQ(settings["key"].value, "value");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST(Config, resetOverriden) {
|
||||||
|
Config config;
|
||||||
|
config.resetOverriden();
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST(Config, resetOverridenWithSetting) {
|
||||||
|
Config config;
|
||||||
|
Setting<std::string> setting{&config, "", "name-of-the-setting", "description"};
|
||||||
|
|
||||||
|
{
|
||||||
|
std::map<std::string, Config::SettingInfo> settings;
|
||||||
|
|
||||||
|
setting.set("foo");
|
||||||
|
ASSERT_EQ(setting.get(), "foo");
|
||||||
|
config.getSettings(settings, /* overridenOnly = */ true);
|
||||||
|
ASSERT_TRUE(settings.empty());
|
||||||
|
}
|
||||||
|
|
||||||
|
{
|
||||||
|
std::map<std::string, Config::SettingInfo> settings;
|
||||||
|
|
||||||
|
setting.override("bar");
|
||||||
|
ASSERT_TRUE(setting.overriden);
|
||||||
|
ASSERT_EQ(setting.get(), "bar");
|
||||||
|
config.getSettings(settings, /* overridenOnly = */ true);
|
||||||
|
ASSERT_FALSE(settings.empty());
|
||||||
|
}
|
||||||
|
|
||||||
|
{
|
||||||
|
std::map<std::string, Config::SettingInfo> settings;
|
||||||
|
|
||||||
|
config.resetOverriden();
|
||||||
|
ASSERT_FALSE(setting.overriden);
|
||||||
|
config.getSettings(settings, /* overridenOnly = */ true);
|
||||||
|
ASSERT_TRUE(settings.empty());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST(Config, toJSONOnEmptyConfig) {
|
||||||
|
std::stringstream out;
|
||||||
|
{ // Scoped to force the destructor of JSONObject to write the final `}`
|
||||||
|
JSONObject obj(out);
|
||||||
|
Config config;
|
||||||
|
config.toJSON(obj);
|
||||||
|
}
|
||||||
|
|
||||||
|
ASSERT_EQ(out.str(), "{}");
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST(Config, toJSONOnNonEmptyConfig) {
|
||||||
|
std::stringstream out;
|
||||||
|
{ // Scoped to force the destructor of JSONObject to write the final `}`
|
||||||
|
JSONObject obj(out);
|
||||||
|
|
||||||
|
Config config;
|
||||||
|
std::map<std::string, Config::SettingInfo> settings;
|
||||||
|
Setting<std::string> setting{&config, "", "name-of-the-setting", "description"};
|
||||||
|
setting.assign("value");
|
||||||
|
|
||||||
|
config.toJSON(obj);
|
||||||
|
}
|
||||||
|
ASSERT_EQ(out.str(), R"#({"name-of-the-setting":{"description":"description","value":"value"}})#");
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST(Config, setSettingAlias) {
|
||||||
|
Config config;
|
||||||
|
Setting<std::string> 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");
|
||||||
|
}
|
||||||
|
|
||||||
|
/* 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<std::string> setting{&config, "default", "name-of-the-setting", "description"};
|
||||||
|
ASSERT_EQ(setting.get(), "default");
|
||||||
|
config.reapplyUnknownSettings();
|
||||||
|
ASSERT_EQ(setting.get(), "unknownvalue");
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST(Config, applyConfigEmpty) {
|
||||||
|
Config config;
|
||||||
|
std::map<std::string, Config::SettingInfo> settings;
|
||||||
|
config.applyConfig("");
|
||||||
|
config.getSettings(settings);
|
||||||
|
ASSERT_TRUE(settings.empty());
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST(Config, applyConfigEmptyWithComment) {
|
||||||
|
Config config;
|
||||||
|
std::map<std::string, Config::SettingInfo> settings;
|
||||||
|
config.applyConfig("# just a comment");
|
||||||
|
config.getSettings(settings);
|
||||||
|
ASSERT_TRUE(settings.empty());
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST(Config, applyConfigAssignment) {
|
||||||
|
Config config;
|
||||||
|
std::map<std::string, Config::SettingInfo> settings;
|
||||||
|
Setting<std::string> 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, applyConfigWithReassignedSetting) {
|
||||||
|
Config config;
|
||||||
|
std::map<std::string, Config::SettingInfo> settings;
|
||||||
|
Setting<std::string> 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<std::string, Config::SettingInfo> settings;
|
||||||
|
Setting<std::string> 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);
|
||||||
|
}
|
||||||
|
}
|
80
src/libutil/tests/hash.cc
Normal file
80
src/libutil/tests/hash.cc
Normal file
|
@ -0,0 +1,80 @@
|
||||||
|
#include "hash.hh"
|
||||||
|
#include <gtest/gtest.h>
|
||||||
|
|
||||||
|
namespace nix {
|
||||||
|
|
||||||
|
/* ----------------------------------------------------------------------------
|
||||||
|
* 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), "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), "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),"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),"sha1:84983e441c3bd26ebaae4aa1f95129e5e54670f1");
|
||||||
|
}
|
||||||
|
|
||||||
|
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),
|
||||||
|
"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),
|
||||||
|
"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),
|
||||||
|
"sha512:ddaf35a193617abacc417349ae20413112e6fa4e89a9"
|
||||||
|
"7ea20a9eeee64b55d39a2192992a274fc1a836ba3c23a3feebbd"
|
||||||
|
"454d4423643ce80e2a9ac94fa54ca49f");
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST(hashString, testKnownSHA512Hashes2) {
|
||||||
|
// values taken from: https://tools.ietf.org/html/rfc4634
|
||||||
|
auto s = "abcdefghbcdefghicdefghijdefghijkefghijklfghijklmghijklmnhijklmnoijklmnopjklmnopqklmnopqrlmnopqrsmnopqrstnopqrstu";
|
||||||
|
|
||||||
|
auto hash = hashString(HashType::htSHA512, s);
|
||||||
|
ASSERT_EQ(hash.to_string(Base::Base16),
|
||||||
|
"sha512:8e959b75dae313da8cf4f72814fc143f8f7779c6eb9f7fa1"
|
||||||
|
"7299aeadb6889018501d289e4900f7e4331b99dec4b5433a"
|
||||||
|
"c7d329eeb6dd26545e96e55b874be909");
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST(hashString, hashingWithUnknownAlgoExits) {
|
||||||
|
auto s = "unknown";
|
||||||
|
ASSERT_DEATH(hashString(HashType::htUnknown, s), "");
|
||||||
|
}
|
||||||
|
}
|
193
src/libutil/tests/json.cc
Normal file
193
src/libutil/tests/json.cc
Normal file
|
@ -0,0 +1,193 @@
|
||||||
|
#include "json.hh"
|
||||||
|
#include <gtest/gtest.h>
|
||||||
|
#include <sstream>
|
||||||
|
|
||||||
|
namespace nix {
|
||||||
|
|
||||||
|
/* ----------------------------------------------------------------------------
|
||||||
|
* toJSON
|
||||||
|
* --------------------------------------------------------------------------*/
|
||||||
|
|
||||||
|
TEST(toJSON, quotesCharPtr) {
|
||||||
|
const char* input = "test";
|
||||||
|
std::stringstream out;
|
||||||
|
toJSON(out, input);
|
||||||
|
|
||||||
|
ASSERT_EQ(out.str(), "\"test\"");
|
||||||
|
}
|
||||||
|
|
||||||
|
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(), "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);
|
||||||
|
|
||||||
|
ASSERT_EQ(out.str(), "1.024");
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST(toJSON, convertsDouble) {
|
||||||
|
const double input = 1.024;
|
||||||
|
std::stringstream out;
|
||||||
|
toJSON(out, input);
|
||||||
|
|
||||||
|
ASSERT_EQ(out.str(), "1.024");
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST(toJSON, convertsBool) {
|
||||||
|
auto input = false;
|
||||||
|
std::stringstream out;
|
||||||
|
toJSON(out, input);
|
||||||
|
|
||||||
|
ASSERT_EQ(out.str(), "false");
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST(toJSON, quotesTab) {
|
||||||
|
std::stringstream out;
|
||||||
|
toJSON(out, "\t");
|
||||||
|
|
||||||
|
ASSERT_EQ(out.str(), "\"\\t\"");
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST(toJSON, quotesNewline) {
|
||||||
|
std::stringstream out;
|
||||||
|
toJSON(out, "\n");
|
||||||
|
|
||||||
|
ASSERT_EQ(out.str(), "\"\\n\"");
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST(toJSON, quotesCreturn) {
|
||||||
|
std::stringstream out;
|
||||||
|
toJSON(out, "\r");
|
||||||
|
|
||||||
|
ASSERT_EQ(out.str(), "\"\\r\"");
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST(toJSON, quotesCreturnNewLine) {
|
||||||
|
std::stringstream out;
|
||||||
|
toJSON(out, "\r\n");
|
||||||
|
|
||||||
|
ASSERT_EQ(out.str(), "\"\\r\\n\"");
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST(toJSON, quotesDoublequotes) {
|
||||||
|
std::stringstream out;
|
||||||
|
toJSON(out, "\"");
|
||||||
|
|
||||||
|
ASSERT_EQ(out.str(), "\"\\\"\"");
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST(toJSON, substringEscape) {
|
||||||
|
std::stringstream out;
|
||||||
|
const char *s = "foo\t";
|
||||||
|
toJSON(out, s+3, s + strlen(s));
|
||||||
|
|
||||||
|
ASSERT_EQ(out.str(), "\"\\t\"");
|
||||||
|
}
|
||||||
|
|
||||||
|
/* ----------------------------------------------------------------------------
|
||||||
|
* JSONObject
|
||||||
|
* --------------------------------------------------------------------------*/
|
||||||
|
|
||||||
|
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, 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, objectWithPlaceholderAndObject) {
|
||||||
|
std::stringstream out;
|
||||||
|
{
|
||||||
|
JSONObject t(out);
|
||||||
|
auto l = t.placeholder("object");
|
||||||
|
l.object().attr("key", "value");
|
||||||
|
}
|
||||||
|
|
||||||
|
ASSERT_EQ(out.str(), R"#({"object":{"key":"value"}})#");
|
||||||
|
}
|
||||||
|
|
||||||
|
/* ----------------------------------------------------------------------------
|
||||||
|
* JSONList
|
||||||
|
* --------------------------------------------------------------------------*/
|
||||||
|
|
||||||
|
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"])#");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
105
src/libutil/tests/xml-writer.cc
Normal file
105
src/libutil/tests/xml-writer.cc
Normal file
|
@ -0,0 +1,105 @@
|
||||||
|
#include "xml-writer.hh"
|
||||||
|
#include <gtest/gtest.h>
|
||||||
|
#include <sstream>
|
||||||
|
|
||||||
|
namespace nix {
|
||||||
|
|
||||||
|
/* ----------------------------------------------------------------------------
|
||||||
|
* XMLWriter
|
||||||
|
* --------------------------------------------------------------------------*/
|
||||||
|
|
||||||
|
TEST(XMLWriter, emptyObject) {
|
||||||
|
std::stringstream out;
|
||||||
|
{
|
||||||
|
XMLWriter t(false, out);
|
||||||
|
}
|
||||||
|
|
||||||
|
ASSERT_EQ(out.str(), "<?xml version='1.0' encoding='utf-8'?>\n");
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST(XMLWriter, objectWithEmptyElement) {
|
||||||
|
std::stringstream out;
|
||||||
|
{
|
||||||
|
XMLWriter t(false, out);
|
||||||
|
t.openElement("foobar");
|
||||||
|
}
|
||||||
|
|
||||||
|
ASSERT_EQ(out.str(), "<?xml version='1.0' encoding='utf-8'?>\n<foobar></foobar>");
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST(XMLWriter, objectWithElementWithAttrs) {
|
||||||
|
std::stringstream out;
|
||||||
|
{
|
||||||
|
XMLWriter t(false, out);
|
||||||
|
XMLAttrs attrs = {
|
||||||
|
{ "foo", "bar" }
|
||||||
|
};
|
||||||
|
t.openElement("foobar", attrs);
|
||||||
|
}
|
||||||
|
|
||||||
|
ASSERT_EQ(out.str(), "<?xml version='1.0' encoding='utf-8'?>\n<foobar foo=\"bar\"></foobar>");
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST(XMLWriter, objectWithElementWithEmptyAttrs) {
|
||||||
|
std::stringstream out;
|
||||||
|
{
|
||||||
|
XMLWriter t(false, out);
|
||||||
|
XMLAttrs attrs = {};
|
||||||
|
t.openElement("foobar", attrs);
|
||||||
|
}
|
||||||
|
|
||||||
|
ASSERT_EQ(out.str(), "<?xml version='1.0' encoding='utf-8'?>\n<foobar></foobar>");
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST(XMLWriter, objectWithElementWithAttrsEscaping) {
|
||||||
|
std::stringstream out;
|
||||||
|
{
|
||||||
|
XMLWriter t(false, out);
|
||||||
|
XMLAttrs attrs = {
|
||||||
|
{ "<key>", "<value>" }
|
||||||
|
};
|
||||||
|
t.openElement("foobar", attrs);
|
||||||
|
}
|
||||||
|
|
||||||
|
// XXX: While "<value>" is escaped, "<key>" isn't which I think is a bug.
|
||||||
|
ASSERT_EQ(out.str(), "<?xml version='1.0' encoding='utf-8'?>\n<foobar <key>=\"<value>\"></foobar>");
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST(XMLWriter, objectWithElementWithAttrsIndented) {
|
||||||
|
std::stringstream out;
|
||||||
|
{
|
||||||
|
XMLWriter t(true, out);
|
||||||
|
XMLAttrs attrs = {
|
||||||
|
{ "foo", "bar" }
|
||||||
|
};
|
||||||
|
t.openElement("foobar", attrs);
|
||||||
|
}
|
||||||
|
|
||||||
|
ASSERT_EQ(out.str(), "<?xml version='1.0' encoding='utf-8'?>\n<foobar foo=\"bar\">\n</foobar>\n");
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST(XMLWriter, writeEmptyElement) {
|
||||||
|
std::stringstream out;
|
||||||
|
{
|
||||||
|
XMLWriter t(false, out);
|
||||||
|
t.writeEmptyElement("foobar");
|
||||||
|
}
|
||||||
|
|
||||||
|
ASSERT_EQ(out.str(), "<?xml version='1.0' encoding='utf-8'?>\n<foobar />");
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST(XMLWriter, writeEmptyElementWithAttributes) {
|
||||||
|
std::stringstream out;
|
||||||
|
{
|
||||||
|
XMLWriter t(false, out);
|
||||||
|
XMLAttrs attrs = {
|
||||||
|
{ "foo", "bar" }
|
||||||
|
};
|
||||||
|
t.writeEmptyElement("foobar", attrs);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
ASSERT_EQ(out.str(), "<?xml version='1.0' encoding='utf-8'?>\n<foobar foo=\"bar\" />");
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -31,21 +31,21 @@ void StoreCommand::run()
|
||||||
run(getStore());
|
run(getStore());
|
||||||
}
|
}
|
||||||
|
|
||||||
StorePathsCommand::StorePathsCommand(FileIngestionMethod recursive)
|
StorePathsCommand::StorePathsCommand(bool recursive)
|
||||||
: recursive(recursive)
|
: recursive(recursive)
|
||||||
{
|
{
|
||||||
if (recursive == FileIngestionMethod::Recursive)
|
if (recursive)
|
||||||
addFlag({
|
addFlag({
|
||||||
.longName = "no-recursive",
|
.longName = "no-recursive",
|
||||||
.description = "apply operation to specified paths only",
|
.description = "apply operation to specified paths only",
|
||||||
.handler = {&this->recursive, FileIngestionMethod::Flat},
|
.handler = {&this->recursive, false},
|
||||||
});
|
});
|
||||||
else
|
else
|
||||||
addFlag({
|
addFlag({
|
||||||
.longName = "recursive",
|
.longName = "recursive",
|
||||||
.shortName = 'r',
|
.shortName = 'r',
|
||||||
.description = "apply operation to closure of the specified paths",
|
.description = "apply operation to closure of the specified paths",
|
||||||
.handler = {&this->recursive, FileIngestionMethod::Recursive},
|
.handler = {&this->recursive, true},
|
||||||
});
|
});
|
||||||
|
|
||||||
mkFlag(0, "all", "apply operation to the entire store", &all);
|
mkFlag(0, "all", "apply operation to the entire store", &all);
|
||||||
|
@ -66,7 +66,7 @@ void StorePathsCommand::run(ref<Store> store)
|
||||||
for (auto & p : toStorePaths(store, realiseMode, installables))
|
for (auto & p : toStorePaths(store, realiseMode, installables))
|
||||||
storePaths.push_back(p.clone());
|
storePaths.push_back(p.clone());
|
||||||
|
|
||||||
if (recursive == FileIngestionMethod::Recursive) {
|
if (recursive) {
|
||||||
StorePathSet closure;
|
StorePathSet closure;
|
||||||
store->computeFSClosure(storePathsToSet(storePaths), closure, false, false);
|
store->computeFSClosure(storePathsToSet(storePaths), closure, false, false);
|
||||||
storePaths.clear();
|
storePaths.clear();
|
||||||
|
|
|
@ -92,7 +92,7 @@ struct StorePathsCommand : public InstallablesCommand
|
||||||
{
|
{
|
||||||
private:
|
private:
|
||||||
|
|
||||||
FileIngestionMethod recursive = FileIngestionMethod::Flat;
|
bool recursive = false;
|
||||||
bool all = false;
|
bool all = false;
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
|
@ -101,7 +101,7 @@ protected:
|
||||||
|
|
||||||
public:
|
public:
|
||||||
|
|
||||||
StorePathsCommand(FileIngestionMethod recursive = FileIngestionMethod::Flat);
|
StorePathsCommand(bool recursive = false);
|
||||||
|
|
||||||
using StoreCommand::run;
|
using StoreCommand::run;
|
||||||
|
|
||||||
|
|
|
@ -17,7 +17,7 @@ struct CmdCopy : StorePathsCommand
|
||||||
SubstituteFlag substitute = NoSubstitute;
|
SubstituteFlag substitute = NoSubstitute;
|
||||||
|
|
||||||
CmdCopy()
|
CmdCopy()
|
||||||
: StorePathsCommand(FileIngestionMethod::Recursive)
|
: StorePathsCommand(true)
|
||||||
{
|
{
|
||||||
addFlag({
|
addFlag({
|
||||||
.longName = "from",
|
.longName = "from",
|
||||||
|
|
|
@ -9,15 +9,14 @@ using namespace nix;
|
||||||
|
|
||||||
struct CmdHash : Command
|
struct CmdHash : Command
|
||||||
{
|
{
|
||||||
enum Mode { mFile, mPath };
|
FileIngestionMethod mode;
|
||||||
Mode mode;
|
|
||||||
Base base = SRI;
|
Base base = SRI;
|
||||||
bool truncate = false;
|
bool truncate = false;
|
||||||
HashType ht = htSHA256;
|
HashType ht = htSHA256;
|
||||||
std::vector<std::string> paths;
|
std::vector<std::string> paths;
|
||||||
std::optional<std::string> modulus;
|
std::optional<std::string> modulus;
|
||||||
|
|
||||||
CmdHash(Mode mode) : mode(mode)
|
CmdHash(FileIngestionMethod mode) : mode(mode)
|
||||||
{
|
{
|
||||||
mkFlag(0, "sri", "print hash in SRI format", &base, SRI);
|
mkFlag(0, "sri", "print hash in SRI format", &base, SRI);
|
||||||
mkFlag(0, "base64", "print hash in base-64", &base, Base64);
|
mkFlag(0, "base64", "print hash in base-64", &base, Base64);
|
||||||
|
@ -36,9 +35,14 @@ struct CmdHash : Command
|
||||||
|
|
||||||
std::string description() override
|
std::string description() override
|
||||||
{
|
{
|
||||||
return mode == mFile
|
const char* d;
|
||||||
? "print cryptographic hash of a regular file"
|
switch (mode) {
|
||||||
: "print cryptographic hash of the NAR serialisation of a path";
|
case FileIngestionMethod::Flat:
|
||||||
|
d = "print cryptographic hash of a regular file";
|
||||||
|
case FileIngestionMethod::Recursive:
|
||||||
|
d = "print cryptographic hash of the NAR serialisation of a path";
|
||||||
|
};
|
||||||
|
return d;
|
||||||
}
|
}
|
||||||
|
|
||||||
Category category() override { return catUtility; }
|
Category category() override { return catUtility; }
|
||||||
|
@ -53,10 +57,14 @@ struct CmdHash : Command
|
||||||
else
|
else
|
||||||
hashSink = std::make_unique<HashSink>(ht);
|
hashSink = std::make_unique<HashSink>(ht);
|
||||||
|
|
||||||
if (mode == mFile)
|
switch (mode) {
|
||||||
|
case FileIngestionMethod::Flat:
|
||||||
readFile(path, *hashSink);
|
readFile(path, *hashSink);
|
||||||
else
|
break;
|
||||||
|
case FileIngestionMethod::Recursive:
|
||||||
dumpPath(path, *hashSink);
|
dumpPath(path, *hashSink);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
Hash h = hashSink->finish().first;
|
Hash h = hashSink->finish().first;
|
||||||
if (truncate && h.hashSize > 20) h = compressHash(h, 20);
|
if (truncate && h.hashSize > 20) h = compressHash(h, 20);
|
||||||
|
@ -65,8 +73,8 @@ struct CmdHash : Command
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
static RegisterCommand r1("hash-file", [](){ return make_ref<CmdHash>(CmdHash::mFile); });
|
static RegisterCommand r1("hash-file", [](){ return make_ref<CmdHash>(FileIngestionMethod::Flat); });
|
||||||
static RegisterCommand r2("hash-path", [](){ return make_ref<CmdHash>(CmdHash::mPath); });
|
static RegisterCommand r2("hash-path", [](){ return make_ref<CmdHash>(FileIngestionMethod::Recursive); });
|
||||||
|
|
||||||
struct CmdToBase : Command
|
struct CmdToBase : Command
|
||||||
{
|
{
|
||||||
|
@ -137,7 +145,7 @@ static int compatNixHash(int argc, char * * argv)
|
||||||
});
|
});
|
||||||
|
|
||||||
if (op == opHash) {
|
if (op == opHash) {
|
||||||
CmdHash cmd(flat ? CmdHash::mFile : CmdHash::mPath);
|
CmdHash cmd(flat ? FileIngestionMethod::Flat : FileIngestionMethod::Recursive);
|
||||||
cmd.ht = ht;
|
cmd.ht = ht;
|
||||||
cmd.base = base32 ? Base32 : Base16;
|
cmd.base = base32 ? Base32 : Base16;
|
||||||
cmd.truncate = truncate;
|
cmd.truncate = truncate;
|
||||||
|
|
Loading…
Reference in a new issue