mirror of
https://github.com/NixOS/nix.git
synced 2025-11-16 07:22:43 +01:00
Apply clang-format universally.
* It is tough to contribute to a project that doesn't use a formatter, * It is extra hard to contribute to a project which has configured the formatter, but ignores it for some files * Code formatting makes it harder to hide obscure / weird bugs by accident or on purpose, Let's rip the bandaid off? Note that PRs currently in flight should be able to be merged relatively easily by applying `clang-format` to their tip prior to merge.
This commit is contained in:
parent
41bf87ec70
commit
e4f62e4608
587 changed files with 23258 additions and 23135 deletions
|
|
@ -16,12 +16,13 @@ namespace nix {
|
|||
|
||||
struct ArchiveSettings : Config
|
||||
{
|
||||
Setting<bool> useCaseHack{this,
|
||||
#ifdef __APPLE__
|
||||
true,
|
||||
#else
|
||||
false,
|
||||
#endif
|
||||
Setting<bool> useCaseHack{
|
||||
this,
|
||||
#ifdef __APPLE__
|
||||
true,
|
||||
#else
|
||||
false,
|
||||
#endif
|
||||
"use-case-hack",
|
||||
"Whether to enable a macOS-specific hack for dealing with file name case collisions."};
|
||||
};
|
||||
|
|
@ -32,18 +33,12 @@ static GlobalConfig::Register rArchiveSettings(&archiveSettings);
|
|||
|
||||
PathFilter defaultPathFilter = [](const Path &) { return true; };
|
||||
|
||||
|
||||
void SourceAccessor::dumpPath(
|
||||
const CanonPath & path,
|
||||
Sink & sink,
|
||||
PathFilter & filter)
|
||||
void SourceAccessor::dumpPath(const CanonPath & path, Sink & sink, PathFilter & filter)
|
||||
{
|
||||
auto dumpContents = [&](const CanonPath & path)
|
||||
{
|
||||
auto dumpContents = [&](const CanonPath & path) {
|
||||
sink << "contents";
|
||||
std::optional<uint64_t> size;
|
||||
readFile(path, sink, [&](uint64_t _size)
|
||||
{
|
||||
readFile(path, sink, [&](uint64_t _size) {
|
||||
size = _size;
|
||||
sink << _size;
|
||||
});
|
||||
|
|
@ -82,9 +77,8 @@ void SourceAccessor::dumpPath(
|
|||
name.erase(pos);
|
||||
}
|
||||
if (!unhacked.emplace(name, i.first).second)
|
||||
throw Error("file name collision between '%s' and '%s'",
|
||||
(path / unhacked[name]),
|
||||
(path / i.first));
|
||||
throw Error(
|
||||
"file name collision between '%s' and '%s'", (path / unhacked[name]), (path / i.first));
|
||||
} else
|
||||
unhacked.emplace(i.first, i.first);
|
||||
|
||||
|
|
@ -99,7 +93,8 @@ void SourceAccessor::dumpPath(
|
|||
else if (st.type == tSymlink)
|
||||
sink << "type" << "symlink" << "target" << readLink(path);
|
||||
|
||||
else throw Error("file '%s' has an unsupported type", path);
|
||||
else
|
||||
throw Error("file '%s' has an unsupported type", path);
|
||||
|
||||
sink << ")";
|
||||
};
|
||||
|
|
@ -108,7 +103,6 @@ void SourceAccessor::dumpPath(
|
|||
dump(path);
|
||||
}
|
||||
|
||||
|
||||
time_t dumpPathAndGetMtime(const Path & path, Sink & sink, PathFilter & filter)
|
||||
{
|
||||
auto path2 = PosixSourceAccessor::createAtRoot(path);
|
||||
|
|
@ -121,20 +115,17 @@ void dumpPath(const Path & path, Sink & sink, PathFilter & filter)
|
|||
dumpPathAndGetMtime(path, sink, filter);
|
||||
}
|
||||
|
||||
|
||||
void dumpString(std::string_view s, Sink & sink)
|
||||
{
|
||||
sink << narVersionMagic1 << "(" << "type" << "regular" << "contents" << s << ")";
|
||||
}
|
||||
|
||||
|
||||
template<typename... Args>
|
||||
static SerialisationError badArchive(std::string_view s, const Args & ... args)
|
||||
static SerialisationError badArchive(std::string_view s, const Args &... args)
|
||||
{
|
||||
return SerialisationError("bad archive: " + s, args...);
|
||||
}
|
||||
|
||||
|
||||
static void parseContents(CreateRegularFileSink & sink, Source & source)
|
||||
{
|
||||
uint64_t size = readLongLong(source);
|
||||
|
|
@ -147,7 +138,8 @@ static void parseContents(CreateRegularFileSink & sink, Source & source)
|
|||
while (left) {
|
||||
checkInterrupt();
|
||||
auto n = buf.size();
|
||||
if ((uint64_t)n > left) n = left;
|
||||
if ((uint64_t) n > left)
|
||||
n = left;
|
||||
source(buf.data(), n);
|
||||
sink({buf.data(), n});
|
||||
left -= n;
|
||||
|
|
@ -156,16 +148,14 @@ static void parseContents(CreateRegularFileSink & sink, Source & source)
|
|||
readPadding(size, source);
|
||||
}
|
||||
|
||||
|
||||
struct CaseInsensitiveCompare
|
||||
{
|
||||
bool operator() (const std::string & a, const std::string & b) const
|
||||
bool operator()(const std::string & a, const std::string & b) const
|
||||
{
|
||||
return strcasecmp(a.c_str(), b.c_str()) < 0;
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
static void parse(FileSystemObjectSink & sink, Source & source, const CanonPath & path)
|
||||
{
|
||||
auto getString = [&]() {
|
||||
|
|
@ -191,7 +181,8 @@ static void parse(FileSystemObjectSink & sink, Source & source, const CanonPath
|
|||
|
||||
if (tag == "executable") {
|
||||
auto s2 = getString();
|
||||
if (s2 != "") throw badArchive("executable marker has non-empty value");
|
||||
if (s2 != "")
|
||||
throw badArchive("executable marker has non-empty value");
|
||||
crf.isExecutable();
|
||||
tag = getString();
|
||||
}
|
||||
|
|
@ -213,7 +204,8 @@ static void parse(FileSystemObjectSink & sink, Source & source, const CanonPath
|
|||
while (1) {
|
||||
auto tag = getString();
|
||||
|
||||
if (tag == ")") break;
|
||||
if (tag == ")")
|
||||
break;
|
||||
|
||||
if (tag != "entry")
|
||||
throw badArchive("expected tag 'entry' or ')', got '%s'", tag);
|
||||
|
|
@ -223,7 +215,8 @@ static void parse(FileSystemObjectSink & sink, Source & source, const CanonPath
|
|||
expectTag("name");
|
||||
|
||||
auto name = getString();
|
||||
if (name.empty() || name == "." || name == ".." || name.find('/') != std::string::npos || name.find((char) 0) != std::string::npos)
|
||||
if (name.empty() || name == "." || name == ".." || name.find('/') != std::string::npos
|
||||
|| name.find((char) 0) != std::string::npos)
|
||||
throw badArchive("NAR contains invalid file name '%1%'", name);
|
||||
if (name <= prevName)
|
||||
throw badArchive("NAR directory is not sorted");
|
||||
|
|
@ -236,7 +229,10 @@ static void parse(FileSystemObjectSink & sink, Source & source, const CanonPath
|
|||
name += std::to_string(++i->second);
|
||||
auto j = names.find(name);
|
||||
if (j != names.end())
|
||||
throw badArchive("NAR contains file name '%s' that collides with case-hacked file name '%s'", prevName, j->first);
|
||||
throw badArchive(
|
||||
"NAR contains file name '%s' that collides with case-hacked file name '%s'",
|
||||
prevName,
|
||||
j->first);
|
||||
} else
|
||||
names[name] = 0;
|
||||
}
|
||||
|
|
@ -258,10 +254,10 @@ static void parse(FileSystemObjectSink & sink, Source & source, const CanonPath
|
|||
expectTag(")");
|
||||
}
|
||||
|
||||
else throw badArchive("unknown file type '%s'", type);
|
||||
else
|
||||
throw badArchive("unknown file type '%s'", type);
|
||||
}
|
||||
|
||||
|
||||
void parseDump(FileSystemObjectSink & sink, Source & source)
|
||||
{
|
||||
std::string version;
|
||||
|
|
@ -276,7 +272,6 @@ void parseDump(FileSystemObjectSink & sink, Source & source)
|
|||
parse(sink, source, CanonPath::root);
|
||||
}
|
||||
|
||||
|
||||
void restorePath(const std::filesystem::path & path, Source & source, bool startFsync)
|
||||
{
|
||||
RestoreSink sink{startFsync};
|
||||
|
|
@ -284,7 +279,6 @@ void restorePath(const std::filesystem::path & path, Source & source, bool start
|
|||
parseDump(sink, source);
|
||||
}
|
||||
|
||||
|
||||
void copyNAR(Source & source, Sink & sink)
|
||||
{
|
||||
// FIXME: if 'source' is the output of dumpPath() followed by EOF,
|
||||
|
|
@ -292,10 +286,9 @@ void copyNAR(Source & source, Sink & sink)
|
|||
|
||||
NullFileSystemObjectSink parseSink; /* just parse the NAR */
|
||||
|
||||
TeeSource wrapper { source, sink };
|
||||
TeeSource wrapper{source, sink};
|
||||
|
||||
parseDump(parseSink, wrapper);
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
} // namespace nix
|
||||
|
|
|
|||
|
|
@ -10,7 +10,7 @@
|
|||
#include <string>
|
||||
#include <regex>
|
||||
#ifndef _WIN32
|
||||
# include <glob.h>
|
||||
# include <glob.h>
|
||||
#endif
|
||||
|
||||
namespace nix {
|
||||
|
|
@ -24,14 +24,16 @@ void Args::addFlag(Flag && flag_)
|
|||
longFlags[flag->longName] = flag;
|
||||
for (auto & alias : flag->aliases)
|
||||
longFlags[alias] = flag;
|
||||
if (flag->shortName) shortFlags[flag->shortName] = flag;
|
||||
if (flag->shortName)
|
||||
shortFlags[flag->shortName] = flag;
|
||||
}
|
||||
|
||||
void Args::removeFlag(const std::string & longName)
|
||||
{
|
||||
auto flag = longFlags.find(longName);
|
||||
assert(flag != longFlags.end());
|
||||
if (flag->second->shortName) shortFlags.erase(flag->second->shortName);
|
||||
if (flag->second->shortName)
|
||||
shortFlags.erase(flag->second->shortName);
|
||||
longFlags.erase(flag);
|
||||
}
|
||||
|
||||
|
|
@ -51,10 +53,7 @@ void Completions::add(std::string completion, std::string description)
|
|||
if (needs_ellipsis)
|
||||
description.append(" [...]");
|
||||
}
|
||||
completions.insert(Completion {
|
||||
.completion = completion,
|
||||
.description = description
|
||||
});
|
||||
completions.insert(Completion{.completion = completion, .description = description});
|
||||
}
|
||||
|
||||
auto Completion::operator<=>(const Completion & other) const noexcept = default;
|
||||
|
|
@ -74,7 +73,8 @@ RootArgs & Args::getRoot()
|
|||
|
||||
std::optional<std::string> RootArgs::needsCompletion(std::string_view s)
|
||||
{
|
||||
if (!completions) return {};
|
||||
if (!completions)
|
||||
return {};
|
||||
auto i = s.find(completionMarker);
|
||||
if (i != std::string::npos)
|
||||
return std::string(s.begin(), i);
|
||||
|
|
@ -86,7 +86,8 @@ std::optional<std::string> RootArgs::needsCompletion(std::string_view s)
|
|||
*
|
||||
* Except we can't recursively reference the Parser typedef, so we have to write a class.
|
||||
*/
|
||||
struct Parser {
|
||||
struct Parser
|
||||
{
|
||||
std::string_view remaining;
|
||||
|
||||
/**
|
||||
|
|
@ -94,12 +95,14 @@ struct Parser {
|
|||
*/
|
||||
virtual void operator()(std::shared_ptr<Parser> & state, Strings & r) = 0;
|
||||
|
||||
Parser(std::string_view s) : remaining(s) {};
|
||||
Parser(std::string_view s)
|
||||
: remaining(s) {};
|
||||
|
||||
virtual ~Parser() { };
|
||||
virtual ~Parser() {};
|
||||
};
|
||||
|
||||
struct ParseQuoted : public Parser {
|
||||
struct ParseQuoted : public Parser
|
||||
{
|
||||
/**
|
||||
* @brief Accumulated string
|
||||
*
|
||||
|
|
@ -107,13 +110,14 @@ struct ParseQuoted : public Parser {
|
|||
*/
|
||||
std::string acc;
|
||||
|
||||
ParseQuoted(std::string_view s) : Parser(s) {};
|
||||
ParseQuoted(std::string_view s)
|
||||
: Parser(s) {};
|
||||
|
||||
virtual void operator()(std::shared_ptr<Parser> & state, Strings & r) override;
|
||||
};
|
||||
|
||||
|
||||
struct ParseUnquoted : public Parser {
|
||||
struct ParseUnquoted : public Parser
|
||||
{
|
||||
/**
|
||||
* @brief Accumulated string
|
||||
*
|
||||
|
|
@ -122,9 +126,11 @@ struct ParseUnquoted : public Parser {
|
|||
*/
|
||||
std::string acc;
|
||||
|
||||
ParseUnquoted(std::string_view s) : Parser(s) {};
|
||||
ParseUnquoted(std::string_view s)
|
||||
: Parser(s) {};
|
||||
|
||||
virtual void operator()(std::shared_ptr<Parser> & state, Strings & r) override {
|
||||
virtual void operator()(std::shared_ptr<Parser> & state, Strings & r) override
|
||||
{
|
||||
if (remaining.empty()) {
|
||||
if (!acc.empty())
|
||||
r.push_back(acc);
|
||||
|
|
@ -132,111 +138,116 @@ struct ParseUnquoted : public Parser {
|
|||
return;
|
||||
}
|
||||
switch (remaining[0]) {
|
||||
case ' ': case '\t': case '\n': case '\r':
|
||||
if (!acc.empty())
|
||||
r.push_back(acc);
|
||||
state = std::make_shared<ParseUnquoted>(ParseUnquoted(remaining.substr(1)));
|
||||
case ' ':
|
||||
case '\t':
|
||||
case '\n':
|
||||
case '\r':
|
||||
if (!acc.empty())
|
||||
r.push_back(acc);
|
||||
state = std::make_shared<ParseUnquoted>(ParseUnquoted(remaining.substr(1)));
|
||||
return;
|
||||
case '`':
|
||||
if (remaining.size() > 1 && remaining[1] == '`') {
|
||||
state = std::make_shared<ParseQuoted>(ParseQuoted(remaining.substr(2)));
|
||||
return;
|
||||
case '`':
|
||||
if (remaining.size() > 1 && remaining[1] == '`') {
|
||||
state = std::make_shared<ParseQuoted>(ParseQuoted(remaining.substr(2)));
|
||||
return;
|
||||
}
|
||||
else
|
||||
throw Error("single backtick is not a supported syntax in the nix shebang.");
|
||||
} else
|
||||
throw Error("single backtick is not a supported syntax in the nix shebang.");
|
||||
|
||||
// reserved characters
|
||||
// meaning to be determined, or may be reserved indefinitely so that
|
||||
// #!nix syntax looks unambiguous
|
||||
case '$':
|
||||
case '*':
|
||||
case '~':
|
||||
case '<':
|
||||
case '>':
|
||||
case '|':
|
||||
case ';':
|
||||
case '(':
|
||||
case ')':
|
||||
case '[':
|
||||
case ']':
|
||||
case '{':
|
||||
case '}':
|
||||
case '\'':
|
||||
case '"':
|
||||
case '\\':
|
||||
throw Error("unsupported unquoted character in nix shebang: " + std::string(1, remaining[0]) + ". Use double backticks to escape?");
|
||||
// reserved characters
|
||||
// meaning to be determined, or may be reserved indefinitely so that
|
||||
// #!nix syntax looks unambiguous
|
||||
case '$':
|
||||
case '*':
|
||||
case '~':
|
||||
case '<':
|
||||
case '>':
|
||||
case '|':
|
||||
case ';':
|
||||
case '(':
|
||||
case ')':
|
||||
case '[':
|
||||
case ']':
|
||||
case '{':
|
||||
case '}':
|
||||
case '\'':
|
||||
case '"':
|
||||
case '\\':
|
||||
throw Error(
|
||||
"unsupported unquoted character in nix shebang: " + std::string(1, remaining[0])
|
||||
+ ". Use double backticks to escape?");
|
||||
|
||||
case '#':
|
||||
if (acc.empty()) {
|
||||
throw Error ("unquoted nix shebang argument cannot start with #. Use double backticks to escape?");
|
||||
} else {
|
||||
acc += remaining[0];
|
||||
remaining = remaining.substr(1);
|
||||
return;
|
||||
}
|
||||
|
||||
default:
|
||||
case '#':
|
||||
if (acc.empty()) {
|
||||
throw Error("unquoted nix shebang argument cannot start with #. Use double backticks to escape?");
|
||||
} else {
|
||||
acc += remaining[0];
|
||||
remaining = remaining.substr(1);
|
||||
return;
|
||||
}
|
||||
|
||||
default:
|
||||
acc += remaining[0];
|
||||
remaining = remaining.substr(1);
|
||||
return;
|
||||
}
|
||||
assert(false);
|
||||
}
|
||||
};
|
||||
|
||||
void ParseQuoted::operator()(std::shared_ptr<Parser> &state, Strings & r) {
|
||||
void ParseQuoted::operator()(std::shared_ptr<Parser> & state, Strings & r)
|
||||
{
|
||||
if (remaining.empty()) {
|
||||
throw Error("unterminated quoted string in nix shebang");
|
||||
}
|
||||
switch (remaining[0]) {
|
||||
case ' ':
|
||||
if ((remaining.size() == 3 && remaining[1] == '`' && remaining[2] == '`')
|
||||
|| (remaining.size() > 3 && remaining[1] == '`' && remaining[2] == '`' && remaining[3] != '`')) {
|
||||
// exactly two backticks mark the end of a quoted string, but a preceding space is ignored if present.
|
||||
state = std::make_shared<ParseUnquoted>(ParseUnquoted(remaining.substr(3)));
|
||||
r.push_back(acc);
|
||||
return;
|
||||
}
|
||||
else {
|
||||
// just a normal space
|
||||
acc += remaining[0];
|
||||
remaining = remaining.substr(1);
|
||||
return;
|
||||
}
|
||||
case '`':
|
||||
// exactly two backticks mark the end of a quoted string
|
||||
if ((remaining.size() == 2 && remaining[1] == '`')
|
||||
|| (remaining.size() > 2 && remaining[1] == '`' && remaining[2] != '`')) {
|
||||
state = std::make_shared<ParseUnquoted>(ParseUnquoted(remaining.substr(2)));
|
||||
r.push_back(acc);
|
||||
return;
|
||||
}
|
||||
|
||||
// a sequence of at least 3 backticks is one escape-backtick which is ignored, followed by any number of backticks, which are verbatim
|
||||
else if (remaining.size() >= 3 && remaining[1] == '`' && remaining[2] == '`') {
|
||||
// ignore "escape" backtick
|
||||
remaining = remaining.substr(1);
|
||||
// add the rest
|
||||
while (remaining.size() > 0 && remaining[0] == '`') {
|
||||
acc += '`';
|
||||
remaining = remaining.substr(1);
|
||||
}
|
||||
return;
|
||||
}
|
||||
else {
|
||||
acc += remaining[0];
|
||||
remaining = remaining.substr(1);
|
||||
return;
|
||||
}
|
||||
default:
|
||||
case ' ':
|
||||
if ((remaining.size() == 3 && remaining[1] == '`' && remaining[2] == '`')
|
||||
|| (remaining.size() > 3 && remaining[1] == '`' && remaining[2] == '`' && remaining[3] != '`')) {
|
||||
// exactly two backticks mark the end of a quoted string, but a preceding space is ignored if present.
|
||||
state = std::make_shared<ParseUnquoted>(ParseUnquoted(remaining.substr(3)));
|
||||
r.push_back(acc);
|
||||
return;
|
||||
} else {
|
||||
// just a normal space
|
||||
acc += remaining[0];
|
||||
remaining = remaining.substr(1);
|
||||
return;
|
||||
}
|
||||
case '`':
|
||||
// exactly two backticks mark the end of a quoted string
|
||||
if ((remaining.size() == 2 && remaining[1] == '`')
|
||||
|| (remaining.size() > 2 && remaining[1] == '`' && remaining[2] != '`')) {
|
||||
state = std::make_shared<ParseUnquoted>(ParseUnquoted(remaining.substr(2)));
|
||||
r.push_back(acc);
|
||||
return;
|
||||
}
|
||||
|
||||
// a sequence of at least 3 backticks is one escape-backtick which is ignored, followed by any number of
|
||||
// backticks, which are verbatim
|
||||
else if (remaining.size() >= 3 && remaining[1] == '`' && remaining[2] == '`') {
|
||||
// ignore "escape" backtick
|
||||
remaining = remaining.substr(1);
|
||||
// add the rest
|
||||
while (remaining.size() > 0 && remaining[0] == '`') {
|
||||
acc += '`';
|
||||
remaining = remaining.substr(1);
|
||||
}
|
||||
return;
|
||||
} else {
|
||||
acc += remaining[0];
|
||||
remaining = remaining.substr(1);
|
||||
return;
|
||||
}
|
||||
default:
|
||||
acc += remaining[0];
|
||||
remaining = remaining.substr(1);
|
||||
return;
|
||||
}
|
||||
assert(false);
|
||||
}
|
||||
|
||||
Strings parseShebangContent(std::string_view s) {
|
||||
Strings parseShebangContent(std::string_view s)
|
||||
{
|
||||
Strings result;
|
||||
std::shared_ptr<Parser> parserState(std::make_shared<ParseUnquoted>(ParseUnquoted(s)));
|
||||
|
||||
|
|
@ -268,22 +279,22 @@ void RootArgs::parseCmdline(const Strings & _cmdline, bool allowShebang)
|
|||
// if we have at least one argument, it's the name of an
|
||||
// executable file, and it starts with "#!".
|
||||
Strings savedArgs;
|
||||
if (allowShebang){
|
||||
if (allowShebang) {
|
||||
auto script = *cmdline.begin();
|
||||
try {
|
||||
std::ifstream stream(script);
|
||||
char shebang[3]={0,0,0};
|
||||
stream.get(shebang,3);
|
||||
if (strncmp(shebang,"#!",2) == 0){
|
||||
for (auto pos = std::next(cmdline.begin()); pos != cmdline.end();pos++)
|
||||
char shebang[3] = {0, 0, 0};
|
||||
stream.get(shebang, 3);
|
||||
if (strncmp(shebang, "#!", 2) == 0) {
|
||||
for (auto pos = std::next(cmdline.begin()); pos != cmdline.end(); pos++)
|
||||
savedArgs.push_back(*pos);
|
||||
cmdline.clear();
|
||||
|
||||
std::string line;
|
||||
std::getline(stream,line);
|
||||
std::getline(stream, line);
|
||||
static const std::string commentChars("#/\\%@*-(");
|
||||
std::string shebangContent;
|
||||
while (std::getline(stream,line) && !line.empty() && commentChars.find(line[0]) != std::string::npos){
|
||||
while (std::getline(stream, line) && !line.empty() && commentChars.find(line[0]) != std::string::npos) {
|
||||
line = chomp(line);
|
||||
|
||||
std::smatch match;
|
||||
|
|
@ -297,12 +308,13 @@ void RootArgs::parseCmdline(const Strings & _cmdline, bool allowShebang)
|
|||
}
|
||||
cmdline.push_back(script);
|
||||
commandBaseDir = dirOf(script);
|
||||
for (auto pos = savedArgs.begin(); pos != savedArgs.end();pos++)
|
||||
for (auto pos = savedArgs.begin(); pos != savedArgs.end(); pos++)
|
||||
cmdline.push_back(*pos);
|
||||
}
|
||||
} catch (SystemError &) { }
|
||||
} catch (SystemError &) {
|
||||
}
|
||||
}
|
||||
for (auto pos = cmdline.begin(); pos != cmdline.end(); ) {
|
||||
for (auto pos = cmdline.begin(); pos != cmdline.end();) {
|
||||
|
||||
auto arg = *pos;
|
||||
|
||||
|
|
@ -310,7 +322,8 @@ void RootArgs::parseCmdline(const Strings & _cmdline, bool allowShebang)
|
|||
`-j3` -> `-j 3`). */
|
||||
if (!dashDash && arg.length() > 2 && arg[0] == '-' && arg[1] != '-' && isalpha(arg[1])) {
|
||||
*pos = (std::string) "-" + arg[1];
|
||||
auto next = pos; ++next;
|
||||
auto next = pos;
|
||||
++next;
|
||||
for (unsigned int j = 2; j < arg.length(); j++)
|
||||
if (isalpha(arg[j]))
|
||||
cmdline.insert(next, (std::string) "-" + arg[j]);
|
||||
|
|
@ -324,12 +337,10 @@ void RootArgs::parseCmdline(const Strings & _cmdline, bool allowShebang)
|
|||
if (!dashDash && arg == "--") {
|
||||
dashDash = true;
|
||||
++pos;
|
||||
}
|
||||
else if (!dashDash && std::string(arg, 0, 1) == "-") {
|
||||
} else if (!dashDash && std::string(arg, 0, 1) == "-") {
|
||||
if (!processFlag(pos, cmdline.end()))
|
||||
throw UsageError("unrecognised flag '%1%'", arg);
|
||||
}
|
||||
else {
|
||||
} else {
|
||||
pos = rewriteArgs(cmdline, pos);
|
||||
pendingArgs.push_back(*pos++);
|
||||
if (processArgs(pendingArgs, false))
|
||||
|
|
@ -377,12 +388,12 @@ bool Args::processFlag(Strings::iterator & pos, Strings::iterator end)
|
|||
|
||||
std::vector<std::string> args;
|
||||
bool anyCompleted = false;
|
||||
for (size_t n = 0 ; n < flag.handler.arity; ++n) {
|
||||
for (size_t n = 0; n < flag.handler.arity; ++n) {
|
||||
if (pos == end) {
|
||||
if (flag.handler.arity == ArityAny || anyCompleted) break;
|
||||
if (flag.handler.arity == ArityAny || anyCompleted)
|
||||
break;
|
||||
throw UsageError(
|
||||
"flag '%s' requires %d argument(s), but only %d were given",
|
||||
name, flag.handler.arity, n);
|
||||
"flag '%s' requires %d argument(s), but only %d were given", name, flag.handler.arity, n);
|
||||
}
|
||||
if (auto prefix = rootArgs.needsCompletion(*pos)) {
|
||||
anyCompleted = true;
|
||||
|
|
@ -404,9 +415,7 @@ bool Args::processFlag(Strings::iterator & pos, Strings::iterator end)
|
|||
if (std::string(*pos, 0, 2) == "--") {
|
||||
if (auto prefix = rootArgs.needsCompletion(*pos)) {
|
||||
for (auto & [name, flag] : longFlags) {
|
||||
if (!hiddenCategories.count(flag->category)
|
||||
&& hasPrefix(name, std::string(*prefix, 2)))
|
||||
{
|
||||
if (!hiddenCategories.count(flag->category) && hasPrefix(name, std::string(*prefix, 2))) {
|
||||
if (auto & f = flag->experimentalFeature)
|
||||
rootArgs.flagExperimentalFeatures.insert(*f);
|
||||
rootArgs.completions->add("--" + name, flag->description);
|
||||
|
|
@ -415,14 +424,16 @@ bool Args::processFlag(Strings::iterator & pos, Strings::iterator end)
|
|||
return false;
|
||||
}
|
||||
auto i = longFlags.find(std::string(*pos, 2));
|
||||
if (i == longFlags.end()) return false;
|
||||
if (i == longFlags.end())
|
||||
return false;
|
||||
return process("--" + i->first, *i->second);
|
||||
}
|
||||
|
||||
if (std::string(*pos, 0, 1) == "-" && pos->size() == 2) {
|
||||
auto c = (*pos)[1];
|
||||
auto i = shortFlags.find(c);
|
||||
if (i == shortFlags.end()) return false;
|
||||
if (i == shortFlags.end())
|
||||
return false;
|
||||
return process(std::string("-") + c, *i->second);
|
||||
}
|
||||
|
||||
|
|
@ -452,12 +463,11 @@ bool Args::processArgs(const Strings & args, bool finish)
|
|||
|
||||
bool res = false;
|
||||
|
||||
if ((exp.handler.arity == ArityAny && finish) ||
|
||||
(exp.handler.arity != ArityAny && args.size() == exp.handler.arity))
|
||||
{
|
||||
if ((exp.handler.arity == ArityAny && finish)
|
||||
|| (exp.handler.arity != ArityAny && args.size() == exp.handler.arity)) {
|
||||
std::vector<std::string> ss;
|
||||
bool anyCompleted = false;
|
||||
for (const auto &[n, s] : enumerate(args)) {
|
||||
for (const auto & [n, s] : enumerate(args)) {
|
||||
if (auto prefix = rootArgs.needsCompletion(s)) {
|
||||
anyCompleted = true;
|
||||
ss.push_back(*prefix);
|
||||
|
|
@ -479,11 +489,7 @@ bool Args::processArgs(const Strings & args, bool finish)
|
|||
except that it will only adjust the next and prev pointers of the list
|
||||
elements, meaning the actual contents don't move in memory. This is
|
||||
critical to prevent invalidating internal pointers! */
|
||||
processedArgs.splice(
|
||||
processedArgs.end(),
|
||||
expectedArgs,
|
||||
expectedArgs.begin(),
|
||||
++expectedArgs.begin());
|
||||
processedArgs.splice(processedArgs.end(), expectedArgs, expectedArgs.begin(), ++expectedArgs.begin());
|
||||
|
||||
res = true;
|
||||
}
|
||||
|
|
@ -501,7 +507,8 @@ nlohmann::json Args::toJSON()
|
|||
for (auto & [name, flag] : longFlags) {
|
||||
auto j = nlohmann::json::object();
|
||||
j["hiddenCategory"] = hiddenCategories.count(flag->category) > 0;
|
||||
if (flag->aliases.count(name)) continue;
|
||||
if (flag->aliases.count(name))
|
||||
continue;
|
||||
if (flag->shortName)
|
||||
j["shortName"] = std::string(1, flag->shortName);
|
||||
if (flag->description != "")
|
||||
|
|
@ -531,32 +538,34 @@ nlohmann::json Args::toJSON()
|
|||
res["flags"] = std::move(flags);
|
||||
res["args"] = std::move(args);
|
||||
auto s = doc();
|
||||
if (s != "") res.emplace("doc", stripIndentation(s));
|
||||
if (s != "")
|
||||
res.emplace("doc", stripIndentation(s));
|
||||
return res;
|
||||
}
|
||||
|
||||
static void _completePath(AddCompletions & completions, std::string_view prefix, bool onlyDirs)
|
||||
{
|
||||
completions.setType(Completions::Type::Filenames);
|
||||
#ifndef _WIN32 // TODO implement globbing completions on Windows
|
||||
#ifndef _WIN32 // TODO implement globbing completions on Windows
|
||||
glob_t globbuf;
|
||||
int flags = GLOB_NOESCAPE;
|
||||
#ifdef GLOB_ONLYDIR
|
||||
# ifdef GLOB_ONLYDIR
|
||||
if (onlyDirs)
|
||||
flags |= GLOB_ONLYDIR;
|
||||
#endif
|
||||
# endif
|
||||
// using expandTilde here instead of GLOB_TILDE(_CHECK) so that ~<Tab> expands to /home/user/
|
||||
if (glob((expandTilde(prefix) + "*").c_str(), flags, nullptr, &globbuf) == 0) {
|
||||
for (size_t i = 0; i < globbuf.gl_pathc; ++i) {
|
||||
if (onlyDirs) {
|
||||
auto st = stat(globbuf.gl_pathv[i]);
|
||||
if (!S_ISDIR(st.st_mode)) continue;
|
||||
if (!S_ISDIR(st.st_mode))
|
||||
continue;
|
||||
}
|
||||
completions.add(globbuf.gl_pathv[i]);
|
||||
}
|
||||
}
|
||||
globfree(&globbuf);
|
||||
#endif
|
||||
#endif
|
||||
}
|
||||
|
||||
void Args::completePath(AddCompletions & completions, size_t, std::string_view prefix)
|
||||
|
|
@ -569,53 +578,56 @@ void Args::completeDir(AddCompletions & completions, size_t, std::string_view pr
|
|||
_completePath(completions, prefix, true);
|
||||
}
|
||||
|
||||
Strings argvToStrings(int argc, char * * argv)
|
||||
Strings argvToStrings(int argc, char ** argv)
|
||||
{
|
||||
Strings args;
|
||||
argc--; argv++;
|
||||
while (argc--) args.push_back(*argv++);
|
||||
argc--;
|
||||
argv++;
|
||||
while (argc--)
|
||||
args.push_back(*argv++);
|
||||
return args;
|
||||
}
|
||||
|
||||
std::optional<ExperimentalFeature> Command::experimentalFeature ()
|
||||
std::optional<ExperimentalFeature> Command::experimentalFeature()
|
||||
{
|
||||
return { Xp::NixCommand };
|
||||
return {Xp::NixCommand};
|
||||
}
|
||||
|
||||
MultiCommand::MultiCommand(std::string_view commandName, const Commands & commands_)
|
||||
: commands(commands_)
|
||||
, commandName(commandName)
|
||||
{
|
||||
expectArgs({
|
||||
.label = "subcommand",
|
||||
.optional = true,
|
||||
.handler = {[=,this](std::string s) {
|
||||
assert(!command);
|
||||
auto i = commands.find(s);
|
||||
if (i == commands.end()) {
|
||||
StringSet commandNames;
|
||||
for (auto & [name, _] : commands)
|
||||
commandNames.insert(name);
|
||||
auto suggestions = Suggestions::bestMatches(commandNames, s);
|
||||
throw UsageError(suggestions, "'%s' is not a recognised command", s);
|
||||
}
|
||||
command = {s, i->second()};
|
||||
command->second->parent = this;
|
||||
}},
|
||||
.completer = {[&](AddCompletions & completions, size_t, std::string_view prefix) {
|
||||
for (auto & [name, command] : commands)
|
||||
if (hasPrefix(name, prefix))
|
||||
completions.add(name);
|
||||
}}
|
||||
});
|
||||
expectArgs(
|
||||
{.label = "subcommand",
|
||||
.optional = true,
|
||||
.handler = {[=, this](std::string s) {
|
||||
assert(!command);
|
||||
auto i = commands.find(s);
|
||||
if (i == commands.end()) {
|
||||
StringSet commandNames;
|
||||
for (auto & [name, _] : commands)
|
||||
commandNames.insert(name);
|
||||
auto suggestions = Suggestions::bestMatches(commandNames, s);
|
||||
throw UsageError(suggestions, "'%s' is not a recognised command", s);
|
||||
}
|
||||
command = {s, i->second()};
|
||||
command->second->parent = this;
|
||||
}},
|
||||
.completer = {[&](AddCompletions & completions, size_t, std::string_view prefix) {
|
||||
for (auto & [name, command] : commands)
|
||||
if (hasPrefix(name, prefix))
|
||||
completions.add(name);
|
||||
}}});
|
||||
|
||||
categories[Command::catDefault] = "Available commands";
|
||||
}
|
||||
|
||||
bool MultiCommand::processFlag(Strings::iterator & pos, Strings::iterator end)
|
||||
{
|
||||
if (Args::processFlag(pos, end)) return true;
|
||||
if (command && command->second->processFlag(pos, end)) return true;
|
||||
if (Args::processFlag(pos, end))
|
||||
return true;
|
||||
if (command && command->second->processFlag(pos, end))
|
||||
return true;
|
||||
return false;
|
||||
}
|
||||
|
||||
|
|
@ -652,14 +664,15 @@ Strings::iterator MultiCommand::rewriteArgs(Strings & args, Strings::iterator po
|
|||
if (command)
|
||||
return command->second->rewriteArgs(args, pos);
|
||||
|
||||
if (aliasUsed || pos == args.end()) return pos;
|
||||
if (aliasUsed || pos == args.end())
|
||||
return pos;
|
||||
auto arg = *pos;
|
||||
auto i = aliases.find(arg);
|
||||
if (i == aliases.end()) return pos;
|
||||
if (i == aliases.end())
|
||||
return pos;
|
||||
auto & info = i->second;
|
||||
if (info.status == AliasStatus::Deprecated) {
|
||||
warn("'%s' is a deprecated alias for '%s'",
|
||||
arg, concatStringsSep(" ", info.replacement));
|
||||
warn("'%s' is a deprecated alias for '%s'", arg, concatStringsSep(" ", info.replacement));
|
||||
}
|
||||
pos = args.erase(pos);
|
||||
for (auto j = info.replacement.rbegin(); j != info.replacement.rend(); ++j)
|
||||
|
|
@ -668,4 +681,4 @@ Strings::iterator MultiCommand::rewriteArgs(Strings & args, Strings::iterator po
|
|||
return pos;
|
||||
}
|
||||
|
||||
}
|
||||
} // namespace nix
|
||||
|
|
|
|||
|
|
@ -9,19 +9,18 @@ CanonPath CanonPath::root = CanonPath("/");
|
|||
|
||||
static std::string absPathPure(std::string_view path)
|
||||
{
|
||||
return canonPathInner<UnixPathTrait>(path, [](auto &, auto &){});
|
||||
return canonPathInner<UnixPathTrait>(path, [](auto &, auto &) {});
|
||||
}
|
||||
|
||||
CanonPath::CanonPath(std::string_view raw)
|
||||
: path(absPathPure(concatStrings("/", raw)))
|
||||
{ }
|
||||
{
|
||||
}
|
||||
|
||||
CanonPath::CanonPath(std::string_view raw, const CanonPath & root)
|
||||
: path(absPathPure(
|
||||
raw.size() > 0 && raw[0] == '/'
|
||||
? raw
|
||||
: concatStrings(root.abs(), "/", raw)))
|
||||
{ }
|
||||
: path(absPathPure(raw.size() > 0 && raw[0] == '/' ? raw : concatStrings(root.abs(), "/", raw)))
|
||||
{
|
||||
}
|
||||
|
||||
CanonPath::CanonPath(const std::vector<std::string> & elems)
|
||||
: path("/")
|
||||
|
|
@ -32,7 +31,8 @@ CanonPath::CanonPath(const std::vector<std::string> & elems)
|
|||
|
||||
std::optional<CanonPath> CanonPath::parent() const
|
||||
{
|
||||
if (isRoot()) return std::nullopt;
|
||||
if (isRoot())
|
||||
return std::nullopt;
|
||||
return CanonPath(unchecked_t(), path.substr(0, std::max((size_t) 1, path.rfind('/'))));
|
||||
}
|
||||
|
||||
|
|
@ -45,30 +45,31 @@ void CanonPath::pop()
|
|||
bool CanonPath::isWithin(const CanonPath & parent) const
|
||||
{
|
||||
return !(
|
||||
path.size() < parent.path.size()
|
||||
|| path.substr(0, parent.path.size()) != parent.path
|
||||
|| (parent.path.size() > 1 && path.size() > parent.path.size()
|
||||
&& path[parent.path.size()] != '/'));
|
||||
path.size() < parent.path.size() || path.substr(0, parent.path.size()) != parent.path
|
||||
|| (parent.path.size() > 1 && path.size() > parent.path.size() && path[parent.path.size()] != '/'));
|
||||
}
|
||||
|
||||
CanonPath CanonPath::removePrefix(const CanonPath & prefix) const
|
||||
{
|
||||
assert(isWithin(prefix));
|
||||
if (prefix.isRoot()) return *this;
|
||||
if (path.size() == prefix.path.size()) return root;
|
||||
if (prefix.isRoot())
|
||||
return *this;
|
||||
if (path.size() == prefix.path.size())
|
||||
return root;
|
||||
return CanonPath(unchecked_t(), path.substr(prefix.path.size()));
|
||||
}
|
||||
|
||||
void CanonPath::extend(const CanonPath & x)
|
||||
{
|
||||
if (x.isRoot()) return;
|
||||
if (x.isRoot())
|
||||
return;
|
||||
if (isRoot())
|
||||
path += x.rel();
|
||||
else
|
||||
path += x.abs();
|
||||
}
|
||||
|
||||
CanonPath CanonPath::operator / (const CanonPath & x) const
|
||||
CanonPath CanonPath::operator/(const CanonPath & x) const
|
||||
{
|
||||
auto res = *this;
|
||||
res.extend(x);
|
||||
|
|
@ -79,11 +80,12 @@ void CanonPath::push(std::string_view c)
|
|||
{
|
||||
assert(c.find('/') == c.npos);
|
||||
assert(c != "." && c != "..");
|
||||
if (!isRoot()) path += '/';
|
||||
if (!isRoot())
|
||||
path += '/';
|
||||
path += c;
|
||||
}
|
||||
|
||||
CanonPath CanonPath::operator / (std::string_view c) const
|
||||
CanonPath CanonPath::operator/(std::string_view c) const
|
||||
{
|
||||
auto res = *this;
|
||||
res.push(c);
|
||||
|
|
@ -111,7 +113,7 @@ bool CanonPath::isAllowed(const std::set<CanonPath> & allowed) const
|
|||
return false;
|
||||
}
|
||||
|
||||
std::ostream & operator << (std::ostream & stream, const CanonPath & path)
|
||||
std::ostream & operator<<(std::ostream & stream, const CanonPath & path)
|
||||
{
|
||||
stream << path.abs();
|
||||
return stream;
|
||||
|
|
@ -122,7 +124,8 @@ std::string CanonPath::makeRelative(const CanonPath & path) const
|
|||
auto p1 = begin();
|
||||
auto p2 = path.begin();
|
||||
|
||||
for (; p1 != end() && p2 != path.end() && *p1 == *p2; ++p1, ++p2) ;
|
||||
for (; p1 != end() && p2 != path.end() && *p1 == *p2; ++p1, ++p2)
|
||||
;
|
||||
|
||||
if (p1 == end() && p2 == path.end())
|
||||
return ".";
|
||||
|
|
@ -132,15 +135,17 @@ std::string CanonPath::makeRelative(const CanonPath & path) const
|
|||
std::string res;
|
||||
while (p1 != end()) {
|
||||
++p1;
|
||||
if (!res.empty()) res += '/';
|
||||
if (!res.empty())
|
||||
res += '/';
|
||||
res += "..";
|
||||
}
|
||||
if (p2 != path.end()) {
|
||||
if (!res.empty()) res += '/';
|
||||
if (!res.empty())
|
||||
res += '/';
|
||||
res += p2.remaining;
|
||||
}
|
||||
return res;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
} // namespace nix
|
||||
|
|
|
|||
|
|
@ -39,12 +39,15 @@ struct ArchiveDecompressionSource : Source
|
|||
std::unique_ptr<TarArchive> archive = 0;
|
||||
Source & src;
|
||||
std::optional<std::string> compressionMethod;
|
||||
|
||||
ArchiveDecompressionSource(Source & src, std::optional<std::string> compressionMethod = std::nullopt)
|
||||
: src(src)
|
||||
, compressionMethod(std::move(compressionMethod))
|
||||
{
|
||||
}
|
||||
|
||||
~ArchiveDecompressionSource() override {}
|
||||
|
||||
size_t read(char * data, size_t len) override
|
||||
{
|
||||
struct archive_entry * ae;
|
||||
|
|
@ -139,16 +142,19 @@ private:
|
|||
struct NoneSink : CompressionSink
|
||||
{
|
||||
Sink & nextSink;
|
||||
|
||||
NoneSink(Sink & nextSink, int level = COMPRESSION_LEVEL_DEFAULT)
|
||||
: nextSink(nextSink)
|
||||
{
|
||||
if (level != COMPRESSION_LEVEL_DEFAULT)
|
||||
warn("requested compression level '%d' not supported by compression method 'none'", level);
|
||||
}
|
||||
|
||||
void finish() override
|
||||
{
|
||||
flush();
|
||||
}
|
||||
|
||||
void writeUnbuffered(std::string_view data) override
|
||||
{
|
||||
nextSink(data);
|
||||
|
|
@ -307,4 +313,4 @@ std::string compress(const std::string & method, std::string_view in, const bool
|
|||
return std::move(ssink.s);
|
||||
}
|
||||
|
||||
}
|
||||
} // namespace nix
|
||||
|
|
|
|||
|
|
@ -11,15 +11,16 @@ namespace nix {
|
|||
|
||||
#if HAVE_LIBCPUID
|
||||
|
||||
StringSet computeLevels() {
|
||||
StringSet computeLevels()
|
||||
{
|
||||
StringSet levels;
|
||||
struct cpu_id_t data;
|
||||
|
||||
const std::map<cpu_feature_level_t, std::string> feature_strings = {
|
||||
{ FEATURE_LEVEL_X86_64_V1, "x86_64-v1" },
|
||||
{ FEATURE_LEVEL_X86_64_V2, "x86_64-v2" },
|
||||
{ FEATURE_LEVEL_X86_64_V3, "x86_64-v3" },
|
||||
{ FEATURE_LEVEL_X86_64_V4, "x86_64-v4" },
|
||||
{FEATURE_LEVEL_X86_64_V1, "x86_64-v1"},
|
||||
{FEATURE_LEVEL_X86_64_V2, "x86_64-v2"},
|
||||
{FEATURE_LEVEL_X86_64_V3, "x86_64-v3"},
|
||||
{FEATURE_LEVEL_X86_64_V4, "x86_64-v4"},
|
||||
};
|
||||
|
||||
if (cpu_identify(NULL, &data) < 0)
|
||||
|
|
@ -34,10 +35,11 @@ StringSet computeLevels() {
|
|||
|
||||
#else
|
||||
|
||||
StringSet computeLevels() {
|
||||
StringSet computeLevels()
|
||||
{
|
||||
return StringSet{};
|
||||
}
|
||||
|
||||
#endif // HAVE_LIBCPUID
|
||||
|
||||
}
|
||||
} // namespace nix
|
||||
|
|
|
|||
|
|
@ -62,4 +62,4 @@ ExperimentalFeatureSettings experimentalFeatureSettings;
|
|||
|
||||
static GlobalConfig::Register rSettings(&experimentalFeatureSettings);
|
||||
|
||||
}
|
||||
} // namespace nix
|
||||
|
|
|
|||
|
|
@ -16,7 +16,8 @@ namespace nix {
|
|||
|
||||
Config::Config(StringMap initials)
|
||||
: AbstractConfig(std::move(initials))
|
||||
{ }
|
||||
{
|
||||
}
|
||||
|
||||
bool Config::set(const std::string & name, const std::string & value)
|
||||
{
|
||||
|
|
@ -54,8 +55,7 @@ void Config::addSetting(AbstractSetting * setting)
|
|||
for (auto & alias : setting->aliases) {
|
||||
if (auto i = unknownSettings.find(alias); i != unknownSettings.end()) {
|
||||
if (set)
|
||||
warn("setting '%s' is set, but it's an alias of '%s' which is also set",
|
||||
alias, setting->name);
|
||||
warn("setting '%s' is set, but it's an alias of '%s' which is also set", alias, setting->name);
|
||||
else {
|
||||
setting->set(std::move(i->second));
|
||||
setting->overridden = true;
|
||||
|
|
@ -68,7 +68,8 @@ void Config::addSetting(AbstractSetting * setting)
|
|||
|
||||
AbstractConfig::AbstractConfig(StringMap initials)
|
||||
: unknownSettings(std::move(initials))
|
||||
{ }
|
||||
{
|
||||
}
|
||||
|
||||
void AbstractConfig::warnUnknownSettings()
|
||||
{
|
||||
|
|
@ -87,21 +88,24 @@ void AbstractConfig::reapplyUnknownSettings()
|
|||
void Config::getSettings(std::map<std::string, SettingInfo> & res, bool overriddenOnly)
|
||||
{
|
||||
for (const auto & opt : _settings)
|
||||
if (!opt.second.isAlias
|
||||
&& (!overriddenOnly || opt.second.setting->overridden)
|
||||
if (!opt.second.isAlias && (!overriddenOnly || opt.second.setting->overridden)
|
||||
&& experimentalFeatureSettings.isEnabled(opt.second.setting->experimentalFeature))
|
||||
res.emplace(opt.first, SettingInfo{opt.second.setting->to_string(), opt.second.setting->description});
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Parse configuration in `contents`, and also the configuration files included from there, with their location specified relative to `path`.
|
||||
* Parse configuration in `contents`, and also the configuration files included from there, with their location
|
||||
* specified relative to `path`.
|
||||
*
|
||||
* `contents` and `path` represent the file that is being parsed.
|
||||
* The result is only an intermediate list of key-value pairs of strings.
|
||||
* More parsing according to the settings-specific semantics is being done by `loadConfFile` in `libstore/globals.cc`.
|
||||
*/
|
||||
static void parseConfigFiles(const std::string & contents, const std::string & path, std::vector<std::pair<std::string, std::string>> & parsedContents) {
|
||||
*/
|
||||
static void parseConfigFiles(
|
||||
const std::string & contents,
|
||||
const std::string & path,
|
||||
std::vector<std::pair<std::string, std::string>> & parsedContents)
|
||||
{
|
||||
unsigned int pos = 0;
|
||||
|
||||
while (pos < contents.size()) {
|
||||
|
|
@ -114,7 +118,8 @@ static void parseConfigFiles(const std::string & contents, const std::string & p
|
|||
line = std::string(line, 0, hash);
|
||||
|
||||
auto tokens = tokenizeString<std::vector<std::string>>(line);
|
||||
if (tokens.empty()) continue;
|
||||
if (tokens.empty())
|
||||
continue;
|
||||
|
||||
if (tokens.size() < 2)
|
||||
throw UsageError("syntax error in configuration line '%1%' in '%2%'", line, path);
|
||||
|
|
@ -160,7 +165,8 @@ static void parseConfigFiles(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 std::string & path)
|
||||
{
|
||||
std::vector<std::pair<std::string, std::string>> parsedContents;
|
||||
|
||||
parseConfigFiles(contents, path, parsedContents);
|
||||
|
|
@ -176,8 +182,7 @@ void AbstractConfig::applyConfig(const std::string & contents, const std::string
|
|||
// but at the time of writing it's not worth building that for just one thing
|
||||
for (const auto & [name, value] : parsedContents) {
|
||||
if (name != "experimental-features" && name != "extra-experimental-features") {
|
||||
if ((name == "nix-path" || name == "extra-nix-path")
|
||||
&& getEnv("NIX_PATH").has_value()) {
|
||||
if ((name == "nix-path" || name == "extra-nix-path") && getEnv("NIX_PATH").has_value()) {
|
||||
continue;
|
||||
}
|
||||
set(name, value);
|
||||
|
|
@ -253,37 +258,42 @@ std::map<std::string, nlohmann::json> AbstractSetting::toJSONObject() const
|
|||
return obj;
|
||||
}
|
||||
|
||||
void AbstractSetting::convertToArg(Args & args, const std::string & category)
|
||||
void AbstractSetting::convertToArg(Args & args, const std::string & category) {}
|
||||
|
||||
bool AbstractSetting::isOverridden() const
|
||||
{
|
||||
return overridden;
|
||||
}
|
||||
|
||||
|
||||
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
|
||||
{
|
||||
return str;
|
||||
}
|
||||
|
||||
template<> std::string BaseSetting<std::string>::to_string() const
|
||||
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
|
||||
{
|
||||
if (str == "")
|
||||
return std::nullopt;
|
||||
else
|
||||
return { str };
|
||||
return {str};
|
||||
}
|
||||
|
||||
template<> std::string BaseSetting<std::optional<std::string>>::to_string() const
|
||||
template<>
|
||||
std::string BaseSetting<std::optional<std::string>>::to_string() const
|
||||
{
|
||||
return value ? *value : "";
|
||||
}
|
||||
|
||||
template<> bool BaseSetting<bool>::parse(const std::string & str) const
|
||||
template<>
|
||||
bool BaseSetting<bool>::parse(const std::string & str) const
|
||||
{
|
||||
if (str == "true" || str == "yes" || str == "1")
|
||||
return true;
|
||||
|
|
@ -293,12 +303,14 @@ template<> bool BaseSetting<bool>::parse(const std::string & str) const
|
|||
throw UsageError("Boolean setting '%s' has invalid value '%s'", name, str);
|
||||
}
|
||||
|
||||
template<> std::string BaseSetting<bool>::to_string() const
|
||||
template<>
|
||||
std::string BaseSetting<bool>::to_string() const
|
||||
{
|
||||
return value ? "true" : "false";
|
||||
}
|
||||
|
||||
template<> void BaseSetting<bool>::convertToArg(Args & args, const std::string & category)
|
||||
template<>
|
||||
void BaseSetting<bool>::convertToArg(Args & args, const std::string & category)
|
||||
{
|
||||
args.addFlag({
|
||||
.longName = name,
|
||||
|
|
@ -318,40 +330,48 @@ 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
|
||||
{
|
||||
return tokenizeString<Strings>(str);
|
||||
}
|
||||
|
||||
template<> void BaseSetting<Strings>::appendOrSet(Strings newValue, bool append)
|
||||
template<>
|
||||
void BaseSetting<Strings>::appendOrSet(Strings newValue, bool append)
|
||||
{
|
||||
if (!append) value.clear();
|
||||
value.insert(value.end(), std::make_move_iterator(newValue.begin()),
|
||||
std::make_move_iterator(newValue.end()));
|
||||
if (!append)
|
||||
value.clear();
|
||||
value.insert(value.end(), std::make_move_iterator(newValue.begin()), std::make_move_iterator(newValue.end()));
|
||||
}
|
||||
|
||||
template<> std::string BaseSetting<Strings>::to_string() const
|
||||
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
|
||||
{
|
||||
return tokenizeString<StringSet>(str);
|
||||
}
|
||||
|
||||
template<> void BaseSetting<StringSet>::appendOrSet(StringSet newValue, bool append)
|
||||
template<>
|
||||
void BaseSetting<StringSet>::appendOrSet(StringSet newValue, bool append)
|
||||
{
|
||||
if (!append) value.clear();
|
||||
if (!append)
|
||||
value.clear();
|
||||
value.insert(std::make_move_iterator(newValue.begin()), std::make_move_iterator(newValue.end()));
|
||||
}
|
||||
|
||||
template<> std::string BaseSetting<StringSet>::to_string() const
|
||||
template<>
|
||||
std::string BaseSetting<StringSet>::to_string() const
|
||||
{
|
||||
return concatStringsSep(" ", value);
|
||||
}
|
||||
|
||||
template<> std::set<ExperimentalFeature> BaseSetting<std::set<ExperimentalFeature>>::parse(const std::string & str) const
|
||||
template<>
|
||||
std::set<ExperimentalFeature> BaseSetting<std::set<ExperimentalFeature>>::parse(const std::string & str) const
|
||||
{
|
||||
std::set<ExperimentalFeature> res;
|
||||
for (auto & s : tokenizeString<StringSet>(str)) {
|
||||
|
|
@ -365,13 +385,16 @@ template<> std::set<ExperimentalFeature> BaseSetting<std::set<ExperimentalFeatur
|
|||
return res;
|
||||
}
|
||||
|
||||
template<> void BaseSetting<std::set<ExperimentalFeature>>::appendOrSet(std::set<ExperimentalFeature> newValue, bool append)
|
||||
template<>
|
||||
void BaseSetting<std::set<ExperimentalFeature>>::appendOrSet(std::set<ExperimentalFeature> newValue, bool append)
|
||||
{
|
||||
if (!append) value.clear();
|
||||
if (!append)
|
||||
value.clear();
|
||||
value.insert(std::make_move_iterator(newValue.begin()), std::make_move_iterator(newValue.end()));
|
||||
}
|
||||
|
||||
template<> std::string BaseSetting<std::set<ExperimentalFeature>>::to_string() const
|
||||
template<>
|
||||
std::string BaseSetting<std::set<ExperimentalFeature>>::to_string() const
|
||||
{
|
||||
StringSet stringifiedXpFeatures;
|
||||
for (const auto & feature : value)
|
||||
|
|
@ -379,7 +402,8 @@ template<> std::string BaseSetting<std::set<ExperimentalFeature>>::to_string() c
|
|||
return concatStringsSep(" ", stringifiedXpFeatures);
|
||||
}
|
||||
|
||||
template<> StringMap BaseSetting<StringMap>::parse(const std::string & str) const
|
||||
template<>
|
||||
StringMap BaseSetting<StringMap>::parse(const std::string & str) const
|
||||
{
|
||||
StringMap res;
|
||||
for (const auto & s : tokenizeString<Strings>(str)) {
|
||||
|
|
@ -390,17 +414,23 @@ 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)
|
||||
{
|
||||
if (!append) value.clear();
|
||||
if (!append)
|
||||
value.clear();
|
||||
value.insert(std::make_move_iterator(newValue.begin()), std::make_move_iterator(newValue.end()));
|
||||
}
|
||||
|
||||
template<> std::string BaseSetting<StringMap>::to_string() const
|
||||
template<>
|
||||
std::string BaseSetting<StringMap>::to_string() const
|
||||
{
|
||||
return std::transform_reduce(value.cbegin(), value.cend(), std::string{},
|
||||
[](const auto & l, const auto &r) { return l + " " + r; },
|
||||
[](const auto & kvpair){ return kvpair.first + "=" + kvpair.second; });
|
||||
return std::transform_reduce(
|
||||
value.cbegin(),
|
||||
value.cend(),
|
||||
std::string{},
|
||||
[](const auto & l, const auto & r) { return l + " " + r; },
|
||||
[](const auto & kvpair) { return kvpair.first + "=" + kvpair.second; });
|
||||
}
|
||||
|
||||
template class BaseSetting<int>;
|
||||
|
|
@ -424,7 +454,8 @@ static Path parsePath(const AbstractSetting & s, const std::string & str)
|
|||
return canonPath(str);
|
||||
}
|
||||
|
||||
PathSetting::PathSetting(Config * options,
|
||||
PathSetting::PathSetting(
|
||||
Config * options,
|
||||
const Path & def,
|
||||
const std::string & name,
|
||||
const std::string & description,
|
||||
|
|
@ -439,8 +470,8 @@ Path PathSetting::parse(const std::string & str) const
|
|||
return parsePath(*this, str);
|
||||
}
|
||||
|
||||
|
||||
OptionalPathSetting::OptionalPathSetting(Config * options,
|
||||
OptionalPathSetting::OptionalPathSetting(
|
||||
Config * options,
|
||||
const std::optional<Path> & def,
|
||||
const std::string & name,
|
||||
const std::string & description,
|
||||
|
|
@ -450,7 +481,6 @@ OptionalPathSetting::OptionalPathSetting(Config * options,
|
|||
options->addSetting(this);
|
||||
}
|
||||
|
||||
|
||||
std::optional<Path> OptionalPathSetting::parse(const std::string & str) const
|
||||
{
|
||||
if (str == "")
|
||||
|
|
@ -459,7 +489,7 @@ std::optional<Path> OptionalPathSetting::parse(const std::string & str) const
|
|||
return parsePath(*this, str);
|
||||
}
|
||||
|
||||
void OptionalPathSetting::operator =(const std::optional<Path> & v)
|
||||
void OptionalPathSetting::operator=(const std::optional<Path> & v)
|
||||
{
|
||||
this->assign(v);
|
||||
}
|
||||
|
|
@ -483,7 +513,8 @@ bool ExperimentalFeatureSettings::isEnabled(const std::optional<ExperimentalFeat
|
|||
|
||||
void ExperimentalFeatureSettings::require(const std::optional<ExperimentalFeature> & feature) const
|
||||
{
|
||||
if (feature) require(*feature);
|
||||
if (feature)
|
||||
require(*feature);
|
||||
}
|
||||
|
||||
}
|
||||
} // namespace nix
|
||||
|
|
|
|||
|
|
@ -10,28 +10,29 @@
|
|||
#include <math.h>
|
||||
|
||||
#ifdef __APPLE__
|
||||
# include <mach-o/dyld.h>
|
||||
# include <mach-o/dyld.h>
|
||||
#endif
|
||||
|
||||
#ifdef __linux__
|
||||
# include <mutex>
|
||||
# include "nix/util/cgroup.hh"
|
||||
# include "nix/util/linux-namespaces.hh"
|
||||
# include <mutex>
|
||||
# include "nix/util/cgroup.hh"
|
||||
# include "nix/util/linux-namespaces.hh"
|
||||
#endif
|
||||
|
||||
#ifdef __FreeBSD__
|
||||
# include <sys/param.h>
|
||||
# include <sys/sysctl.h>
|
||||
# include <sys/param.h>
|
||||
# include <sys/sysctl.h>
|
||||
#endif
|
||||
|
||||
namespace nix {
|
||||
|
||||
unsigned int getMaxCPU()
|
||||
{
|
||||
#ifdef __linux__
|
||||
#ifdef __linux__
|
||||
try {
|
||||
auto cgroupFS = getCgroupFS();
|
||||
if (!cgroupFS) return 0;
|
||||
if (!cgroupFS)
|
||||
return 0;
|
||||
|
||||
auto cpuFile = *cgroupFS + "/" + getCurrentCgroup() + "/cpu.max";
|
||||
|
||||
|
|
@ -45,17 +46,17 @@ unsigned int getMaxCPU()
|
|||
auto quota = cpuMaxParts[0];
|
||||
auto period = cpuMaxParts[1];
|
||||
if (quota != "max")
|
||||
return std::ceil(std::stoi(quota) / std::stof(period));
|
||||
} catch (Error &) { ignoreExceptionInDestructor(lvlDebug); }
|
||||
#endif
|
||||
return std::ceil(std::stoi(quota) / std::stof(period));
|
||||
} catch (Error &) {
|
||||
ignoreExceptionInDestructor(lvlDebug);
|
||||
}
|
||||
#endif
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
//////////////////////////////////////////////////////////////////////
|
||||
|
||||
|
||||
#ifndef _WIN32
|
||||
size_t savedStackSize = 0;
|
||||
|
||||
|
|
@ -73,9 +74,8 @@ void setStackSize(size_t stackSize)
|
|||
savedStackSize,
|
||||
stackSize,
|
||||
limit.rlim_max,
|
||||
std::strerror(errno)
|
||||
).str()
|
||||
);
|
||||
std::strerror(errno))
|
||||
.str());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -83,16 +83,16 @@ void setStackSize(size_t stackSize)
|
|||
|
||||
void restoreProcessContext(bool restoreMounts)
|
||||
{
|
||||
#ifndef _WIN32
|
||||
#ifndef _WIN32
|
||||
unix::restoreSignals();
|
||||
#endif
|
||||
#endif
|
||||
if (restoreMounts) {
|
||||
#ifdef __linux__
|
||||
#ifdef __linux__
|
||||
restoreMountNamespace();
|
||||
#endif
|
||||
#endif
|
||||
}
|
||||
|
||||
#ifndef _WIN32
|
||||
#ifndef _WIN32
|
||||
if (savedStackSize) {
|
||||
struct rlimit limit;
|
||||
if (getrlimit(RLIMIT_STACK, &limit) == 0) {
|
||||
|
|
@ -100,27 +100,24 @@ void restoreProcessContext(bool restoreMounts)
|
|||
setrlimit(RLIMIT_STACK, &limit);
|
||||
}
|
||||
}
|
||||
#endif
|
||||
#endif
|
||||
}
|
||||
|
||||
|
||||
//////////////////////////////////////////////////////////////////////
|
||||
|
||||
|
||||
std::optional<Path> getSelfExe()
|
||||
{
|
||||
static auto cached = []() -> std::optional<Path>
|
||||
{
|
||||
#if defined(__linux__) || defined(__GNU__)
|
||||
static auto cached = []() -> std::optional<Path> {
|
||||
#if defined(__linux__) || defined(__GNU__)
|
||||
return readLink("/proc/self/exe");
|
||||
#elif defined(__APPLE__)
|
||||
#elif defined(__APPLE__)
|
||||
char buf[1024];
|
||||
uint32_t size = sizeof(buf);
|
||||
if (_NSGetExecutablePath(buf, &size) == 0)
|
||||
return buf;
|
||||
else
|
||||
return std::nullopt;
|
||||
#elif defined(__FreeBSD__)
|
||||
#elif defined(__FreeBSD__)
|
||||
int sysctlName[] = {
|
||||
CTL_KERN,
|
||||
KERN_PROC,
|
||||
|
|
@ -129,7 +126,7 @@ std::optional<Path> getSelfExe()
|
|||
};
|
||||
size_t pathLen = 0;
|
||||
if (sysctl(sysctlName, sizeof(sysctlName) / sizeof(sysctlName[0]), nullptr, &pathLen, nullptr, 0) < 0) {
|
||||
return std::nullopt;
|
||||
return std::nullopt;
|
||||
}
|
||||
|
||||
std::vector<char> path(pathLen);
|
||||
|
|
@ -138,11 +135,11 @@ std::optional<Path> getSelfExe()
|
|||
}
|
||||
|
||||
return Path(path.begin(), path.end());
|
||||
#else
|
||||
#else
|
||||
return std::nullopt;
|
||||
#endif
|
||||
#endif
|
||||
}();
|
||||
return cached;
|
||||
}
|
||||
|
||||
}
|
||||
} // namespace nix
|
||||
|
|
|
|||
|
|
@ -2,11 +2,8 @@
|
|||
|
||||
namespace nix {
|
||||
|
||||
std::ostream & pluralize(
|
||||
std::ostream & output,
|
||||
unsigned int count,
|
||||
const std::string_view single,
|
||||
const std::string_view plural)
|
||||
std::ostream &
|
||||
pluralize(std::ostream & output, unsigned int count, const std::string_view single, const std::string_view plural)
|
||||
{
|
||||
if (count == 1)
|
||||
output << "1 " << single;
|
||||
|
|
@ -15,4 +12,4 @@ std::ostream & pluralize(
|
|||
return output;
|
||||
}
|
||||
|
||||
}
|
||||
} // namespace nix
|
||||
|
|
|
|||
|
|
@ -48,4 +48,4 @@ void replaceEnv(const StringMap & newEnv)
|
|||
setEnv(newEnvVar.first.c_str(), newEnvVar.second.c_str());
|
||||
}
|
||||
|
||||
}
|
||||
} // namespace nix
|
||||
|
|
|
|||
|
|
@ -15,13 +15,14 @@ namespace nix {
|
|||
|
||||
void BaseError::addTrace(std::shared_ptr<const Pos> && e, HintFmt hint, TracePrint print)
|
||||
{
|
||||
err.traces.push_front(Trace { .pos = std::move(e), .hint = hint, .print = print });
|
||||
err.traces.push_front(Trace{.pos = std::move(e), .hint = hint, .print = print});
|
||||
}
|
||||
|
||||
void throwExceptionSelfCheck()
|
||||
{
|
||||
// This is meant to be caught in initLibUtil()
|
||||
throw Error("C++ exception handling is broken. This would appear to be a problem with the way Nix was compiled and/or linked and/or loaded.");
|
||||
throw Error(
|
||||
"C++ exception handling is broken. This would appear to be a problem with the way Nix was compiled and/or linked and/or loaded.");
|
||||
}
|
||||
|
||||
// c++ std::exception descendants must have a 'const char* what()' function.
|
||||
|
|
@ -40,7 +41,7 @@ const std::string & BaseError::calcWhat() const
|
|||
|
||||
std::optional<std::string> ErrorInfo::programName = std::nullopt;
|
||||
|
||||
std::ostream & operator <<(std::ostream & os, const HintFmt & hf)
|
||||
std::ostream & operator<<(std::ostream & os, const HintFmt & hf)
|
||||
{
|
||||
return os << hf.str();
|
||||
}
|
||||
|
|
@ -48,7 +49,7 @@ std::ostream & operator <<(std::ostream & os, const HintFmt & hf)
|
|||
/**
|
||||
* An arbitrarily defined value comparison for the purpose of using traces in the key of a sorted container.
|
||||
*/
|
||||
inline std::strong_ordering operator<=>(const Trace& lhs, const Trace& rhs)
|
||||
inline std::strong_ordering operator<=>(const Trace & lhs, const Trace & rhs)
|
||||
{
|
||||
// `std::shared_ptr` does not have value semantics for its comparison
|
||||
// functions, so we need to check for nulls and compare the dereferenced
|
||||
|
|
@ -66,27 +67,16 @@ inline std::strong_ordering operator<=>(const Trace& lhs, const Trace& rhs)
|
|||
}
|
||||
|
||||
// print lines of code to the ostream, indicating the error column.
|
||||
void printCodeLines(std::ostream & out,
|
||||
const std::string & prefix,
|
||||
const Pos & errPos,
|
||||
const LinesOfCode & loc)
|
||||
void printCodeLines(std::ostream & out, const std::string & prefix, const Pos & errPos, const LinesOfCode & loc)
|
||||
{
|
||||
// previous line of code.
|
||||
if (loc.prevLineOfCode.has_value()) {
|
||||
out << std::endl
|
||||
<< fmt("%1% %|2$5d|| %3%",
|
||||
prefix,
|
||||
(errPos.line - 1),
|
||||
*loc.prevLineOfCode);
|
||||
out << std::endl << fmt("%1% %|2$5d|| %3%", prefix, (errPos.line - 1), *loc.prevLineOfCode);
|
||||
}
|
||||
|
||||
if (loc.errLineOfCode.has_value()) {
|
||||
// line of code containing the error.
|
||||
out << std::endl
|
||||
<< fmt("%1% %|2$5d|| %3%",
|
||||
prefix,
|
||||
(errPos.line),
|
||||
*loc.errLineOfCode);
|
||||
out << std::endl << fmt("%1% %|2$5d|| %3%", prefix, (errPos.line), *loc.errLineOfCode);
|
||||
// error arrows for the column range.
|
||||
if (errPos.column > 0) {
|
||||
int start = errPos.column;
|
||||
|
|
@ -97,21 +87,13 @@ void printCodeLines(std::ostream & out,
|
|||
|
||||
std::string arrows("^");
|
||||
|
||||
out << std::endl
|
||||
<< fmt("%1% |%2%" ANSI_RED "%3%" ANSI_NORMAL,
|
||||
prefix,
|
||||
spaces,
|
||||
arrows);
|
||||
out << std::endl << fmt("%1% |%2%" ANSI_RED "%3%" ANSI_NORMAL, prefix, spaces, arrows);
|
||||
}
|
||||
}
|
||||
|
||||
// next line of code.
|
||||
if (loc.nextLineOfCode.has_value()) {
|
||||
out << std::endl
|
||||
<< fmt("%1% %|2$5d|| %3%",
|
||||
prefix,
|
||||
(errPos.line + 1),
|
||||
*loc.nextLineOfCode);
|
||||
out << std::endl << fmt("%1% %|2$5d|| %3%", prefix, (errPos.line + 1), *loc.nextLineOfCode);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -122,10 +104,12 @@ static std::string indent(std::string_view indentFirst, std::string_view indentR
|
|||
|
||||
while (!s.empty()) {
|
||||
auto end = s.find('\n');
|
||||
if (!first) res += "\n";
|
||||
if (!first)
|
||||
res += "\n";
|
||||
res += chomp(std::string(first ? indentFirst : indentRest) + std::string(s.substr(0, end)));
|
||||
first = false;
|
||||
if (end == s.npos) break;
|
||||
if (end == s.npos)
|
||||
break;
|
||||
s = s.substr(end + 1);
|
||||
}
|
||||
|
||||
|
|
@ -146,7 +130,8 @@ static bool printUnknownLocations = getEnv("_NIX_EVAL_SHOW_UNKNOWN_LOCATIONS").h
|
|||
*
|
||||
* @return true if a position was printed.
|
||||
*/
|
||||
static bool printPosMaybe(std::ostream & oss, std::string_view indent, const std::shared_ptr<const Pos> & pos) {
|
||||
static bool printPosMaybe(std::ostream & oss, std::string_view indent, const std::shared_ptr<const Pos> & pos)
|
||||
{
|
||||
bool hasPos = pos && *pos;
|
||||
if (hasPos) {
|
||||
oss << indent << ANSI_BLUE << "at " ANSI_WARNING << *pos << ANSI_NORMAL << ":";
|
||||
|
|
@ -161,11 +146,7 @@ static bool printPosMaybe(std::ostream & oss, std::string_view indent, const std
|
|||
return hasPos;
|
||||
}
|
||||
|
||||
static void printTrace(
|
||||
std::ostream & output,
|
||||
const std::string_view & indent,
|
||||
size_t & count,
|
||||
const Trace & trace)
|
||||
static void printTrace(std::ostream & output, const std::string_view & indent, size_t & count, const Trace & trace)
|
||||
{
|
||||
output << "\n" << "… " << trace.hint.str() << "\n";
|
||||
|
||||
|
|
@ -188,7 +169,8 @@ void printSkippedTracesMaybe(
|
|||
printTrace(output, indent, count, trace);
|
||||
}
|
||||
} else {
|
||||
output << "\n" << ANSI_WARNING "(" << skippedTraces.size() << " duplicate frames omitted)" ANSI_NORMAL << "\n";
|
||||
output << "\n"
|
||||
<< ANSI_WARNING "(" << skippedTraces.size() << " duplicate frames omitted)" ANSI_NORMAL << "\n";
|
||||
// Clear the set of "seen" traces after printing a chunk of
|
||||
// `duplicate frames omitted`.
|
||||
//
|
||||
|
|
@ -228,43 +210,43 @@ std::ostream & showErrorInfo(std::ostream & out, const ErrorInfo & einfo, bool s
|
|||
{
|
||||
std::string prefix;
|
||||
switch (einfo.level) {
|
||||
case Verbosity::lvlError: {
|
||||
prefix = ANSI_RED "error";
|
||||
break;
|
||||
}
|
||||
case Verbosity::lvlNotice: {
|
||||
prefix = ANSI_RED "note";
|
||||
break;
|
||||
}
|
||||
case Verbosity::lvlWarn: {
|
||||
if (einfo.isFromExpr)
|
||||
prefix = ANSI_WARNING "evaluation warning";
|
||||
else
|
||||
prefix = ANSI_WARNING "warning";
|
||||
break;
|
||||
}
|
||||
case Verbosity::lvlInfo: {
|
||||
prefix = ANSI_GREEN "info";
|
||||
break;
|
||||
}
|
||||
case Verbosity::lvlTalkative: {
|
||||
prefix = ANSI_GREEN "talk";
|
||||
break;
|
||||
}
|
||||
case Verbosity::lvlChatty: {
|
||||
prefix = ANSI_GREEN "chat";
|
||||
break;
|
||||
}
|
||||
case Verbosity::lvlVomit: {
|
||||
prefix = ANSI_GREEN "vomit";
|
||||
break;
|
||||
}
|
||||
case Verbosity::lvlDebug: {
|
||||
prefix = ANSI_WARNING "debug";
|
||||
break;
|
||||
}
|
||||
default:
|
||||
assert(false);
|
||||
case Verbosity::lvlError: {
|
||||
prefix = ANSI_RED "error";
|
||||
break;
|
||||
}
|
||||
case Verbosity::lvlNotice: {
|
||||
prefix = ANSI_RED "note";
|
||||
break;
|
||||
}
|
||||
case Verbosity::lvlWarn: {
|
||||
if (einfo.isFromExpr)
|
||||
prefix = ANSI_WARNING "evaluation warning";
|
||||
else
|
||||
prefix = ANSI_WARNING "warning";
|
||||
break;
|
||||
}
|
||||
case Verbosity::lvlInfo: {
|
||||
prefix = ANSI_GREEN "info";
|
||||
break;
|
||||
}
|
||||
case Verbosity::lvlTalkative: {
|
||||
prefix = ANSI_GREEN "talk";
|
||||
break;
|
||||
}
|
||||
case Verbosity::lvlChatty: {
|
||||
prefix = ANSI_GREEN "chat";
|
||||
break;
|
||||
}
|
||||
case Verbosity::lvlVomit: {
|
||||
prefix = ANSI_GREEN "vomit";
|
||||
break;
|
||||
}
|
||||
case Verbosity::lvlDebug: {
|
||||
prefix = ANSI_WARNING "debug";
|
||||
break;
|
||||
}
|
||||
default:
|
||||
assert(false);
|
||||
}
|
||||
|
||||
// FIXME: show the program name as part of the trace?
|
||||
|
|
@ -383,7 +365,8 @@ std::ostream & showErrorInfo(std::ostream & out, const ErrorInfo & einfo, bool s
|
|||
bool truncate = false;
|
||||
|
||||
for (const auto & trace : einfo.traces) {
|
||||
if (trace.hint.str().empty()) continue;
|
||||
if (trace.hint.str().empty())
|
||||
continue;
|
||||
|
||||
if (!showTrace && count > 3) {
|
||||
truncate = true;
|
||||
|
|
@ -406,11 +389,13 @@ std::ostream & showErrorInfo(std::ostream & out, const ErrorInfo & einfo, bool s
|
|||
}
|
||||
}
|
||||
|
||||
|
||||
printSkippedTracesMaybe(oss, ellipsisIndent, count, skippedTraces, tracesSeen);
|
||||
|
||||
if (truncate) {
|
||||
oss << "\n" << ANSI_WARNING "(stack trace truncated; use '--show-trace' to show the full, detailed trace)" ANSI_NORMAL << "\n";
|
||||
oss << "\n"
|
||||
<< ANSI_WARNING
|
||||
"(stack trace truncated; use '--show-trace' to show the full, detailed trace)" ANSI_NORMAL
|
||||
<< "\n";
|
||||
}
|
||||
|
||||
oss << "\n" << prefix;
|
||||
|
|
@ -422,9 +407,7 @@ std::ostream & showErrorInfo(std::ostream & out, const ErrorInfo & einfo, bool s
|
|||
|
||||
auto suggestions = einfo.suggestions.trim();
|
||||
if (!suggestions.suggestions.empty()) {
|
||||
oss << "Did you mean " <<
|
||||
suggestions.trim() <<
|
||||
"?" << std::endl;
|
||||
oss << "Did you mean " << suggestions.trim() << "?" << std::endl;
|
||||
}
|
||||
|
||||
out << indent(prefix, std::string(filterANSIEscapes(prefix, true).size(), ' '), chomp(oss.str()));
|
||||
|
|
@ -440,7 +423,8 @@ static void writeErr(std::string_view buf)
|
|||
while (!buf.empty()) {
|
||||
auto n = write(STDERR_FILENO, buf.data(), buf.size());
|
||||
if (n < 0) {
|
||||
if (errno == EINTR) continue;
|
||||
if (errno == EINTR)
|
||||
continue;
|
||||
abort();
|
||||
}
|
||||
buf = buf.substr(n);
|
||||
|
|
@ -449,7 +433,7 @@ static void writeErr(std::string_view buf)
|
|||
|
||||
void panic(std::string_view msg)
|
||||
{
|
||||
writeErr("\n\n" ANSI_RED "terminating due to unexpected unrecoverable internal error: " ANSI_NORMAL );
|
||||
writeErr("\n\n" ANSI_RED "terminating due to unexpected unrecoverable internal error: " ANSI_NORMAL);
|
||||
writeErr(msg);
|
||||
writeErr("\n");
|
||||
abort();
|
||||
|
|
@ -464,4 +448,4 @@ void panic(const char * file, int line, const char * func)
|
|||
panic(std::string_view(buf, std::min(static_cast<int>(sizeof(buf)), n)));
|
||||
}
|
||||
|
||||
}
|
||||
} // namespace nix
|
||||
|
|
|
|||
|
|
@ -4,4 +4,4 @@ namespace nix {
|
|||
|
||||
Exit::~Exit() {}
|
||||
|
||||
}
|
||||
} // namespace nix
|
||||
|
|
|
|||
|
|
@ -317,7 +317,7 @@ constexpr std::array<ExperimentalFeatureDetails, numXpFeatures> xpFeatureDetails
|
|||
static_assert(
|
||||
[]() constexpr {
|
||||
for (auto [index, feature] : enumerate(xpFeatureDetails))
|
||||
if (index != (size_t)feature.tag)
|
||||
if (index != (size_t) feature.tag)
|
||||
return false;
|
||||
return true;
|
||||
}(),
|
||||
|
|
@ -342,8 +342,8 @@ const std::optional<ExperimentalFeature> parseExperimentalFeature(const std::str
|
|||
|
||||
std::string_view showExperimentalFeature(const ExperimentalFeature tag)
|
||||
{
|
||||
assert((size_t)tag < xpFeatureDetails.size());
|
||||
return xpFeatureDetails[(size_t)tag].name;
|
||||
assert((size_t) tag < xpFeatureDetails.size());
|
||||
return xpFeatureDetails[(size_t) tag].name;
|
||||
}
|
||||
|
||||
nlohmann::json documentExperimentalFeatures()
|
||||
|
|
@ -352,7 +352,8 @@ nlohmann::json documentExperimentalFeatures()
|
|||
for (auto & xpFeature : xpFeatureDetails) {
|
||||
std::stringstream docOss;
|
||||
docOss << stripIndentation(xpFeature.description);
|
||||
docOss << fmt("\nRefer to [%1% tracking issue](%2%) for feature tracking.", xpFeature.name, xpFeature.trackingUrl);
|
||||
docOss << fmt(
|
||||
"\nRefer to [%1% tracking issue](%2%) for feature tracking.", xpFeature.name, xpFeature.trackingUrl);
|
||||
res[std::string{xpFeature.name}] = trim(docOss.str());
|
||||
}
|
||||
return (nlohmann::json) res;
|
||||
|
|
@ -368,11 +369,14 @@ std::set<ExperimentalFeature> parseFeatures(const StringSet & rawFeatures)
|
|||
}
|
||||
|
||||
MissingExperimentalFeature::MissingExperimentalFeature(ExperimentalFeature feature)
|
||||
: Error("experimental Nix feature '%1%' is disabled; add '--extra-experimental-features %1%' to enable it", showExperimentalFeature(feature))
|
||||
: Error(
|
||||
"experimental Nix feature '%1%' is disabled; add '--extra-experimental-features %1%' to enable it",
|
||||
showExperimentalFeature(feature))
|
||||
, missingFeature(feature)
|
||||
{}
|
||||
{
|
||||
}
|
||||
|
||||
std::ostream & operator <<(std::ostream & str, const ExperimentalFeature & feature)
|
||||
std::ostream & operator<<(std::ostream & str, const ExperimentalFeature & feature)
|
||||
{
|
||||
return str << showExperimentalFeature(feature);
|
||||
}
|
||||
|
|
@ -393,4 +397,4 @@ void from_json(const nlohmann::json & j, ExperimentalFeature & feature)
|
|||
throw Error("Unknown experimental feature '%s' in JSON input", input);
|
||||
}
|
||||
|
||||
}
|
||||
} // namespace nix
|
||||
|
|
|
|||
|
|
@ -25,7 +25,6 @@ FileSerialisationMethod parseFileSerialisationMethod(std::string_view input)
|
|||
throw UsageError("Unknown file serialiation method '%s', expect `flat` or `nar`", input);
|
||||
}
|
||||
|
||||
|
||||
FileIngestionMethod parseFileIngestionMethod(std::string_view input)
|
||||
{
|
||||
if (input == "git") {
|
||||
|
|
@ -39,7 +38,6 @@ FileIngestionMethod parseFileIngestionMethod(std::string_view input)
|
|||
}
|
||||
}
|
||||
|
||||
|
||||
std::string_view renderFileSerialisationMethod(FileSerialisationMethod method)
|
||||
{
|
||||
switch (method) {
|
||||
|
|
@ -52,14 +50,12 @@ std::string_view renderFileSerialisationMethod(FileSerialisationMethod method)
|
|||
}
|
||||
}
|
||||
|
||||
|
||||
std::string_view renderFileIngestionMethod(FileIngestionMethod method)
|
||||
{
|
||||
switch (method) {
|
||||
case FileIngestionMethod::Flat:
|
||||
case FileIngestionMethod::NixArchive:
|
||||
return renderFileSerialisationMethod(
|
||||
static_cast<FileSerialisationMethod>(method));
|
||||
return renderFileSerialisationMethod(static_cast<FileSerialisationMethod>(method));
|
||||
case FileIngestionMethod::Git:
|
||||
return "git";
|
||||
default:
|
||||
|
|
@ -67,12 +63,7 @@ std::string_view renderFileIngestionMethod(FileIngestionMethod method)
|
|||
}
|
||||
}
|
||||
|
||||
|
||||
void dumpPath(
|
||||
const SourcePath & path,
|
||||
Sink & sink,
|
||||
FileSerialisationMethod method,
|
||||
PathFilter & filter)
|
||||
void dumpPath(const SourcePath & path, Sink & sink, FileSerialisationMethod method, PathFilter & filter)
|
||||
{
|
||||
switch (method) {
|
||||
case FileSerialisationMethod::Flat:
|
||||
|
|
@ -84,12 +75,7 @@ void dumpPath(
|
|||
}
|
||||
}
|
||||
|
||||
|
||||
void restorePath(
|
||||
const Path & path,
|
||||
Source & source,
|
||||
FileSerialisationMethod method,
|
||||
bool startFsync)
|
||||
void restorePath(const Path & path, Source & source, FileSerialisationMethod method, bool startFsync)
|
||||
{
|
||||
switch (method) {
|
||||
case FileSerialisationMethod::Flat:
|
||||
|
|
@ -101,22 +87,15 @@ void restorePath(
|
|||
}
|
||||
}
|
||||
|
||||
|
||||
HashResult hashPath(
|
||||
const SourcePath & path,
|
||||
FileSerialisationMethod method, HashAlgorithm ha,
|
||||
PathFilter & filter)
|
||||
HashResult hashPath(const SourcePath & path, FileSerialisationMethod method, HashAlgorithm ha, PathFilter & filter)
|
||||
{
|
||||
HashSink sink { ha };
|
||||
HashSink sink{ha};
|
||||
dumpPath(path, sink, method, filter);
|
||||
return sink.finish();
|
||||
}
|
||||
|
||||
|
||||
std::pair<Hash, std::optional<uint64_t>> hashPath(
|
||||
const SourcePath & path,
|
||||
FileIngestionMethod method, HashAlgorithm ht,
|
||||
PathFilter & filter)
|
||||
std::pair<Hash, std::optional<uint64_t>>
|
||||
hashPath(const SourcePath & path, FileIngestionMethod method, HashAlgorithm ht, PathFilter & filter)
|
||||
{
|
||||
switch (method) {
|
||||
case FileIngestionMethod::Flat:
|
||||
|
|
@ -130,4 +109,4 @@ std::pair<Hash, std::optional<uint64_t>> hashPath(
|
|||
assert(false);
|
||||
}
|
||||
|
||||
}
|
||||
} // namespace nix
|
||||
|
|
|
|||
|
|
@ -4,9 +4,9 @@
|
|||
#include <fcntl.h>
|
||||
#include <unistd.h>
|
||||
#ifdef _WIN32
|
||||
# include <winnt.h>
|
||||
# include <fileapi.h>
|
||||
# include "nix/util/windows-error.hh"
|
||||
# include <winnt.h>
|
||||
# include <fileapi.h>
|
||||
# include "nix/util/windows-error.hh"
|
||||
#endif
|
||||
|
||||
namespace nix {
|
||||
|
|
@ -17,7 +17,6 @@ void writeLine(Descriptor fd, std::string s)
|
|||
writeFull(fd, s);
|
||||
}
|
||||
|
||||
|
||||
std::string drainFD(Descriptor fd, bool block, const size_t reserveSize)
|
||||
{
|
||||
// the parser needs two extra bytes to append terminating characters, other users will
|
||||
|
|
@ -33,24 +32,27 @@ std::string drainFD(Descriptor fd, bool block, const size_t reserveSize)
|
|||
return std::move(sink.s);
|
||||
}
|
||||
|
||||
|
||||
//////////////////////////////////////////////////////////////////////
|
||||
|
||||
AutoCloseFD::AutoCloseFD()
|
||||
: fd{INVALID_DESCRIPTOR}
|
||||
{
|
||||
}
|
||||
|
||||
AutoCloseFD::AutoCloseFD() : fd{INVALID_DESCRIPTOR} {}
|
||||
|
||||
|
||||
AutoCloseFD::AutoCloseFD(Descriptor fd) : fd{fd} {}
|
||||
AutoCloseFD::AutoCloseFD(Descriptor fd)
|
||||
: fd{fd}
|
||||
{
|
||||
}
|
||||
|
||||
// NOTE: This can be noexcept since we are just copying a value and resetting
|
||||
// the file descriptor in the rhs.
|
||||
AutoCloseFD::AutoCloseFD(AutoCloseFD && that) noexcept : fd{that.fd}
|
||||
AutoCloseFD::AutoCloseFD(AutoCloseFD && that) noexcept
|
||||
: fd{that.fd}
|
||||
{
|
||||
that.fd = INVALID_DESCRIPTOR;
|
||||
}
|
||||
|
||||
|
||||
AutoCloseFD & AutoCloseFD::operator =(AutoCloseFD && that)
|
||||
AutoCloseFD & AutoCloseFD::operator=(AutoCloseFD && that)
|
||||
{
|
||||
close();
|
||||
fd = that.fd;
|
||||
|
|
@ -58,7 +60,6 @@ AutoCloseFD & AutoCloseFD::operator =(AutoCloseFD && that)
|
|||
return *this;
|
||||
}
|
||||
|
||||
|
||||
AutoCloseFD::~AutoCloseFD()
|
||||
{
|
||||
try {
|
||||
|
|
@ -68,23 +69,21 @@ AutoCloseFD::~AutoCloseFD()
|
|||
}
|
||||
}
|
||||
|
||||
|
||||
Descriptor AutoCloseFD::get() const
|
||||
{
|
||||
return fd;
|
||||
}
|
||||
|
||||
|
||||
void AutoCloseFD::close()
|
||||
{
|
||||
if (fd != INVALID_DESCRIPTOR) {
|
||||
if(
|
||||
if (
|
||||
#ifdef _WIN32
|
||||
::CloseHandle(fd)
|
||||
::CloseHandle(fd)
|
||||
#else
|
||||
::close(fd)
|
||||
::close(fd)
|
||||
#endif
|
||||
== -1)
|
||||
== -1)
|
||||
/* This should never happen. */
|
||||
throw NativeSysError("closing file descriptor %1%", fd);
|
||||
fd = INVALID_DESCRIPTOR;
|
||||
|
|
@ -109,25 +108,21 @@ void AutoCloseFD::fsync() const
|
|||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
void AutoCloseFD::startFsync() const
|
||||
{
|
||||
#ifdef __linux__
|
||||
if (fd != -1) {
|
||||
/* Ignore failure, since fsync must be run later anyway. This is just a performance optimization. */
|
||||
::sync_file_range(fd, 0, 0, SYNC_FILE_RANGE_WRITE);
|
||||
}
|
||||
if (fd != -1) {
|
||||
/* Ignore failure, since fsync must be run later anyway. This is just a performance optimization. */
|
||||
::sync_file_range(fd, 0, 0, SYNC_FILE_RANGE_WRITE);
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
|
||||
AutoCloseFD::operator bool() const
|
||||
{
|
||||
return fd != INVALID_DESCRIPTOR;
|
||||
}
|
||||
|
||||
|
||||
Descriptor AutoCloseFD::release()
|
||||
{
|
||||
Descriptor oldFD = fd;
|
||||
|
|
@ -135,14 +130,12 @@ Descriptor AutoCloseFD::release()
|
|||
return oldFD;
|
||||
}
|
||||
|
||||
|
||||
//////////////////////////////////////////////////////////////////////
|
||||
|
||||
|
||||
void Pipe::close()
|
||||
{
|
||||
readSide.close();
|
||||
writeSide.close();
|
||||
}
|
||||
|
||||
}
|
||||
} // namespace nix
|
||||
|
|
|
|||
|
|
@ -24,28 +24,30 @@
|
|||
#include <boost/iostreams/device/mapped_file.hpp>
|
||||
|
||||
#ifdef __FreeBSD__
|
||||
# include <sys/param.h>
|
||||
# include <sys/mount.h>
|
||||
# include <sys/param.h>
|
||||
# include <sys/mount.h>
|
||||
#endif
|
||||
|
||||
#ifdef _WIN32
|
||||
# include <io.h>
|
||||
# include <io.h>
|
||||
#endif
|
||||
|
||||
namespace nix {
|
||||
|
||||
DirectoryIterator::DirectoryIterator(const std::filesystem::path& p) {
|
||||
DirectoryIterator::DirectoryIterator(const std::filesystem::path & p)
|
||||
{
|
||||
try {
|
||||
// **Attempt to create the underlying directory_iterator**
|
||||
it_ = std::filesystem::directory_iterator(p);
|
||||
} catch (const std::filesystem::filesystem_error& e) {
|
||||
} catch (const std::filesystem::filesystem_error & e) {
|
||||
// **Catch filesystem_error and throw SysError**
|
||||
// Adapt the error message as needed for SysError
|
||||
throw SysError("cannot read directory %s", p);
|
||||
}
|
||||
}
|
||||
|
||||
DirectoryIterator& DirectoryIterator::operator++() {
|
||||
DirectoryIterator & DirectoryIterator::operator++()
|
||||
{
|
||||
// **Attempt to increment the underlying iterator**
|
||||
std::error_code ec;
|
||||
it_.increment(ec);
|
||||
|
|
@ -64,10 +66,9 @@ DirectoryIterator& DirectoryIterator::operator++() {
|
|||
|
||||
bool isAbsolute(PathView path)
|
||||
{
|
||||
return std::filesystem::path { path }.is_absolute();
|
||||
return std::filesystem::path{path}.is_absolute();
|
||||
}
|
||||
|
||||
|
||||
Path absPath(PathView path, std::optional<PathView> dir, bool resolveSymlinks)
|
||||
{
|
||||
std::string scratch;
|
||||
|
|
@ -82,7 +83,7 @@ Path absPath(PathView path, std::optional<PathView> dir, bool resolveSymlinks)
|
|||
#ifdef __GNU__
|
||||
/* GNU (aka. GNU/Hurd) doesn't have any limitation on path
|
||||
lengths and doesn't define `PATH_MAX'. */
|
||||
char *buf = getcwd(NULL, 0);
|
||||
char * buf = getcwd(NULL, 0);
|
||||
if (buf == NULL)
|
||||
#else
|
||||
char buf[PATH_MAX];
|
||||
|
|
@ -113,7 +114,7 @@ Path canonPath(PathView path, bool resolveSymlinks)
|
|||
throw Error("not an absolute path: '%1%'", path);
|
||||
|
||||
// For Windows
|
||||
auto rootName = std::filesystem::path { path }.root_name();
|
||||
auto rootName = std::filesystem::path{path}.root_name();
|
||||
|
||||
/* This just exists because we cannot set the target of `remaining`
|
||||
(the callback parameter) directly to a newly-constructed string,
|
||||
|
|
@ -125,9 +126,7 @@ Path canonPath(PathView path, bool resolveSymlinks)
|
|||
unsigned int followCount = 0, maxFollow = 1024;
|
||||
|
||||
auto ret = canonPathInner<OsPathTrait<char>>(
|
||||
path,
|
||||
[&followCount, &temp, maxFollow, resolveSymlinks]
|
||||
(std::string & result, std::string_view & remaining) {
|
||||
path, [&followCount, &temp, maxFollow, resolveSymlinks](std::string & result, std::string_view & remaining) {
|
||||
if (resolveSymlinks && std::filesystem::is_symlink(result)) {
|
||||
if (++followCount >= maxFollow)
|
||||
throw Error("infinite symlink recursion in path '%1%'", remaining);
|
||||
|
|
@ -151,7 +150,6 @@ Path canonPath(PathView path, bool resolveSymlinks)
|
|||
return ret;
|
||||
}
|
||||
|
||||
|
||||
Path dirOf(const PathView path)
|
||||
{
|
||||
Path::size_type pos = OsPathTrait<char>::rfindPathSep(path);
|
||||
|
|
@ -160,7 +158,6 @@ Path dirOf(const PathView path)
|
|||
return std::filesystem::path{path}.parent_path().string();
|
||||
}
|
||||
|
||||
|
||||
std::string_view baseNameOf(std::string_view path)
|
||||
{
|
||||
if (path.empty())
|
||||
|
|
@ -179,7 +176,6 @@ std::string_view baseNameOf(std::string_view path)
|
|||
return path.substr(pos, last - pos + 1);
|
||||
}
|
||||
|
||||
|
||||
bool isInDir(const std::filesystem::path & path, const std::filesystem::path & dir)
|
||||
{
|
||||
/* Note that while the standard doesn't guarantee this, the
|
||||
|
|
@ -190,13 +186,11 @@ bool isInDir(const std::filesystem::path & path, const std::filesystem::path & d
|
|||
return !rel.empty() && rel.native()[0] != OS_STR('.');
|
||||
}
|
||||
|
||||
|
||||
bool isDirOrInDir(const std::filesystem::path & path, const std::filesystem::path & dir)
|
||||
{
|
||||
return path == dir || isInDir(path, dir);
|
||||
}
|
||||
|
||||
|
||||
struct stat stat(const Path & path)
|
||||
{
|
||||
struct stat st;
|
||||
|
|
@ -206,9 +200,9 @@ struct stat stat(const Path & path)
|
|||
}
|
||||
|
||||
#ifdef _WIN32
|
||||
# define STAT stat
|
||||
# define STAT stat
|
||||
#else
|
||||
# define STAT lstat
|
||||
# define STAT lstat
|
||||
#endif
|
||||
|
||||
struct stat lstat(const Path & path)
|
||||
|
|
@ -219,12 +213,10 @@ struct stat lstat(const Path & path)
|
|||
return st;
|
||||
}
|
||||
|
||||
|
||||
std::optional<struct stat> maybeLstat(const Path & path)
|
||||
{
|
||||
std::optional<struct stat> st{std::in_place};
|
||||
if (STAT(path.c_str(), &*st))
|
||||
{
|
||||
if (STAT(path.c_str(), &*st)) {
|
||||
if (errno == ENOENT || errno == ENOTDIR)
|
||||
st.reset();
|
||||
else
|
||||
|
|
@ -233,7 +225,6 @@ std::optional<struct stat> maybeLstat(const Path & path)
|
|||
return st;
|
||||
}
|
||||
|
||||
|
||||
bool pathExists(const std::filesystem::path & path)
|
||||
{
|
||||
return maybeLstat(path.string()).has_value();
|
||||
|
|
@ -245,27 +236,28 @@ bool pathAccessible(const std::filesystem::path & path)
|
|||
return pathExists(path.string());
|
||||
} catch (SysError & e) {
|
||||
// swallow EPERM
|
||||
if (e.errNo == EPERM) return false;
|
||||
if (e.errNo == EPERM)
|
||||
return false;
|
||||
throw;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Path readLink(const Path & path)
|
||||
{
|
||||
checkInterrupt();
|
||||
return std::filesystem::read_symlink(path).string();
|
||||
}
|
||||
|
||||
|
||||
std::string readFile(const Path & path)
|
||||
{
|
||||
AutoCloseFD fd = toDescriptor(open(path.c_str(), O_RDONLY
|
||||
AutoCloseFD fd = toDescriptor(open(
|
||||
path.c_str(),
|
||||
O_RDONLY
|
||||
// TODO
|
||||
#ifndef _WIN32
|
||||
| O_CLOEXEC
|
||||
| O_CLOEXEC
|
||||
#endif
|
||||
));
|
||||
));
|
||||
if (!fd)
|
||||
throw SysError("opening file '%1%'", path);
|
||||
return readFile(fd.get());
|
||||
|
|
@ -273,7 +265,7 @@ std::string readFile(const Path & path)
|
|||
|
||||
std::string readFile(const std::filesystem::path & path)
|
||||
{
|
||||
return readFile(os_string_to_string(PathViewNG { path }));
|
||||
return readFile(os_string_to_string(PathViewNG{path}));
|
||||
}
|
||||
|
||||
void readFile(const Path & path, Sink & sink, bool memory_map)
|
||||
|
|
@ -292,26 +284,30 @@ void readFile(const Path & path, Sink & sink, bool memory_map)
|
|||
}
|
||||
|
||||
// Stream the file instead if memory-mapping fails or is disabled.
|
||||
AutoCloseFD fd = toDescriptor(open(path.c_str(), O_RDONLY
|
||||
AutoCloseFD fd = toDescriptor(open(
|
||||
path.c_str(),
|
||||
O_RDONLY
|
||||
// TODO
|
||||
#ifndef _WIN32
|
||||
| O_CLOEXEC
|
||||
| O_CLOEXEC
|
||||
#endif
|
||||
));
|
||||
));
|
||||
if (!fd)
|
||||
throw SysError("opening file '%s'", path);
|
||||
drainFD(fd.get(), sink);
|
||||
}
|
||||
|
||||
|
||||
void writeFile(const Path & path, std::string_view s, mode_t mode, FsSync sync)
|
||||
{
|
||||
AutoCloseFD fd = toDescriptor(open(path.c_str(), O_WRONLY | O_TRUNC | O_CREAT
|
||||
AutoCloseFD fd = toDescriptor(open(
|
||||
path.c_str(),
|
||||
O_WRONLY | O_TRUNC | O_CREAT
|
||||
// TODO
|
||||
#ifndef _WIN32
|
||||
| O_CLOEXEC
|
||||
| O_CLOEXEC
|
||||
#endif
|
||||
, mode));
|
||||
,
|
||||
mode));
|
||||
if (!fd)
|
||||
throw SysError("opening file '%1%'", path);
|
||||
|
||||
|
|
@ -338,12 +334,15 @@ void writeFile(AutoCloseFD & fd, const Path & origPath, std::string_view s, mode
|
|||
|
||||
void writeFile(const Path & path, Source & source, mode_t mode, FsSync sync)
|
||||
{
|
||||
AutoCloseFD fd = toDescriptor(open(path.c_str(), O_WRONLY | O_TRUNC | O_CREAT
|
||||
AutoCloseFD fd = toDescriptor(open(
|
||||
path.c_str(),
|
||||
O_WRONLY | O_TRUNC | O_CREAT
|
||||
// TODO
|
||||
#ifndef _WIN32
|
||||
| O_CLOEXEC
|
||||
| O_CLOEXEC
|
||||
#endif
|
||||
, mode));
|
||||
,
|
||||
mode));
|
||||
if (!fd)
|
||||
throw SysError("opening file '%1%'", path);
|
||||
|
||||
|
|
@ -354,7 +353,9 @@ void writeFile(const Path & path, Source & source, mode_t mode, FsSync sync)
|
|||
try {
|
||||
auto n = source.read(buf.data(), buf.size());
|
||||
writeFull(fd.get(), {buf.data(), n});
|
||||
} catch (EndOfFile &) { break; }
|
||||
} catch (EndOfFile &) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
} catch (Error & e) {
|
||||
e.addTrace({}, "writing file '%1%'", path);
|
||||
|
|
@ -377,11 +378,11 @@ void syncParent(const Path & path)
|
|||
}
|
||||
|
||||
#ifdef __FreeBSD__
|
||||
#define MOUNTEDPATHS_PARAM , std::set<Path> &mountedPaths
|
||||
#define MOUNTEDPATHS_ARG , mountedPaths
|
||||
# define MOUNTEDPATHS_PARAM , std::set<Path> & mountedPaths
|
||||
# define MOUNTEDPATHS_ARG , mountedPaths
|
||||
#else
|
||||
#define MOUNTEDPATHS_PARAM
|
||||
#define MOUNTEDPATHS_ARG
|
||||
# define MOUNTEDPATHS_PARAM
|
||||
# define MOUNTEDPATHS_ARG
|
||||
#endif
|
||||
|
||||
void recursiveSync(const Path & path)
|
||||
|
|
@ -428,27 +429,30 @@ void recursiveSync(const Path & path)
|
|||
}
|
||||
}
|
||||
|
||||
|
||||
static void _deletePath(Descriptor parentfd, const std::filesystem::path & path, uint64_t & bytesFreed, std::exception_ptr & ex MOUNTEDPATHS_PARAM)
|
||||
static void _deletePath(
|
||||
Descriptor parentfd,
|
||||
const std::filesystem::path & path,
|
||||
uint64_t & bytesFreed,
|
||||
std::exception_ptr & ex MOUNTEDPATHS_PARAM)
|
||||
{
|
||||
#ifndef _WIN32
|
||||
checkInterrupt();
|
||||
|
||||
#ifdef __FreeBSD__
|
||||
# ifdef __FreeBSD__
|
||||
// In case of emergency (unmount fails for some reason) not recurse into mountpoints.
|
||||
// This prevents us from tearing up the nullfs-mounted nix store.
|
||||
if (mountedPaths.find(path) != mountedPaths.end()) {
|
||||
return;
|
||||
}
|
||||
#endif
|
||||
# endif
|
||||
|
||||
std::string name(path.filename());
|
||||
assert(name != "." && name != ".." && !name.empty());
|
||||
|
||||
struct stat st;
|
||||
if (fstatat(parentfd, name.c_str(), &st,
|
||||
AT_SYMLINK_NOFOLLOW) == -1) {
|
||||
if (errno == ENOENT) return;
|
||||
if (fstatat(parentfd, name.c_str(), &st, AT_SYMLINK_NOFOLLOW) == -1) {
|
||||
if (errno == ENOENT)
|
||||
return;
|
||||
throw SysError("getting status of %1%", path);
|
||||
}
|
||||
|
||||
|
|
@ -456,23 +460,23 @@ static void _deletePath(Descriptor parentfd, const std::filesystem::path & path,
|
|||
/* We are about to delete a file. Will it likely free space? */
|
||||
|
||||
switch (st.st_nlink) {
|
||||
/* Yes: last link. */
|
||||
case 1:
|
||||
bytesFreed += st.st_size;
|
||||
break;
|
||||
/* Maybe: yes, if 'auto-optimise-store' or manual optimisation
|
||||
was performed. Instead of checking for real let's assume
|
||||
it's an optimised file and space will be freed.
|
||||
/* Yes: last link. */
|
||||
case 1:
|
||||
bytesFreed += st.st_size;
|
||||
break;
|
||||
/* Maybe: yes, if 'auto-optimise-store' or manual optimisation
|
||||
was performed. Instead of checking for real let's assume
|
||||
it's an optimised file and space will be freed.
|
||||
|
||||
In worst case we will double count on freed space for files
|
||||
with exactly two hardlinks for unoptimised packages.
|
||||
*/
|
||||
case 2:
|
||||
bytesFreed += st.st_size;
|
||||
break;
|
||||
/* No: 3+ links. */
|
||||
default:
|
||||
break;
|
||||
In worst case we will double count on freed space for files
|
||||
with exactly two hardlinks for unoptimised packages.
|
||||
*/
|
||||
case 2:
|
||||
bytesFreed += st.st_size;
|
||||
break;
|
||||
/* No: 3+ links. */
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -495,15 +499,18 @@ static void _deletePath(Descriptor parentfd, const std::filesystem::path & path,
|
|||
while (errno = 0, dirent = readdir(dir.get())) { /* sic */
|
||||
checkInterrupt();
|
||||
std::string childName = dirent->d_name;
|
||||
if (childName == "." || childName == "..") continue;
|
||||
if (childName == "." || childName == "..")
|
||||
continue;
|
||||
_deletePath(dirfd(dir.get()), path / childName, bytesFreed, ex MOUNTEDPATHS_ARG);
|
||||
}
|
||||
if (errno) throw SysError("reading directory %1%", path);
|
||||
if (errno)
|
||||
throw SysError("reading directory %1%", path);
|
||||
}
|
||||
|
||||
int flags = S_ISDIR(st.st_mode) ? AT_REMOVEDIR : 0;
|
||||
if (unlinkat(parentfd, name.c_str(), flags) == -1) {
|
||||
if (errno == ENOENT) return;
|
||||
if (errno == ENOENT)
|
||||
return;
|
||||
try {
|
||||
throw SysError("cannot unlink %1%", path);
|
||||
} catch (...) {
|
||||
|
|
@ -526,7 +533,8 @@ static void _deletePath(const std::filesystem::path & path, uint64_t & bytesFree
|
|||
|
||||
AutoCloseFD dirfd = toDescriptor(open(path.parent_path().string().c_str(), O_RDONLY));
|
||||
if (!dirfd) {
|
||||
if (errno == ENOENT) return;
|
||||
if (errno == ENOENT)
|
||||
return;
|
||||
throw SysError("opening directory %s", path.parent_path());
|
||||
}
|
||||
|
||||
|
|
@ -538,7 +546,6 @@ static void _deletePath(const std::filesystem::path & path, uint64_t & bytesFree
|
|||
std::rethrow_exception(ex);
|
||||
}
|
||||
|
||||
|
||||
void deletePath(const std::filesystem::path & path)
|
||||
{
|
||||
uint64_t dummy;
|
||||
|
|
@ -547,30 +554,32 @@ void deletePath(const std::filesystem::path & path)
|
|||
|
||||
void createDir(const Path & path, mode_t mode)
|
||||
{
|
||||
if (mkdir(path.c_str()
|
||||
if (mkdir(
|
||||
path.c_str()
|
||||
#ifndef _WIN32
|
||||
, mode
|
||||
,
|
||||
mode
|
||||
#endif
|
||||
) == -1)
|
||||
)
|
||||
== -1)
|
||||
throw SysError("creating directory '%1%'", path);
|
||||
}
|
||||
|
||||
void createDirs(const std::filesystem::path & path)
|
||||
{
|
||||
try {
|
||||
std::filesystem::create_directories(path);
|
||||
std::filesystem::create_directories(path);
|
||||
} catch (std::filesystem::filesystem_error & e) {
|
||||
throw SysError("creating directory '%1%'", path.string());
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void deletePath(const std::filesystem::path & path, uint64_t & bytesFreed)
|
||||
{
|
||||
//Activity act(*logger, lvlDebug, "recursively deleting path '%1%'", path);
|
||||
// Activity act(*logger, lvlDebug, "recursively deleting path '%1%'", path);
|
||||
#ifdef __FreeBSD__
|
||||
std::set<Path> mountedPaths;
|
||||
struct statfs *mntbuf;
|
||||
struct statfs * mntbuf;
|
||||
int count;
|
||||
if ((count = getmntinfo(&mntbuf, MNT_WAIT)) < 0) {
|
||||
throw SysError("getmntinfo");
|
||||
|
|
@ -584,12 +593,15 @@ void deletePath(const std::filesystem::path & path, uint64_t & bytesFreed)
|
|||
_deletePath(path, bytesFreed MOUNTEDPATHS_ARG);
|
||||
}
|
||||
|
||||
|
||||
//////////////////////////////////////////////////////////////////////
|
||||
|
||||
AutoDelete::AutoDelete() : del{false} {}
|
||||
AutoDelete::AutoDelete()
|
||||
: del{false}
|
||||
{
|
||||
}
|
||||
|
||||
AutoDelete::AutoDelete(const std::filesystem::path & p, bool recursive) : _path(p)
|
||||
AutoDelete::AutoDelete(const std::filesystem::path & p, bool recursive)
|
||||
: _path(p)
|
||||
{
|
||||
del = true;
|
||||
this->recursive = recursive;
|
||||
|
|
@ -615,7 +627,8 @@ void AutoDelete::cancel()
|
|||
del = false;
|
||||
}
|
||||
|
||||
void AutoDelete::reset(const std::filesystem::path & p, bool recursive) {
|
||||
void AutoDelete::reset(const std::filesystem::path & p, bool recursive)
|
||||
{
|
||||
_path = p;
|
||||
this->recursive = recursive;
|
||||
del = true;
|
||||
|
|
@ -624,9 +637,16 @@ void AutoDelete::reset(const std::filesystem::path & p, bool recursive) {
|
|||
//////////////////////////////////////////////////////////////////////
|
||||
|
||||
#ifdef __FreeBSD__
|
||||
AutoUnmount::AutoUnmount() : del{false} {}
|
||||
AutoUnmount::AutoUnmount()
|
||||
: del{false}
|
||||
{
|
||||
}
|
||||
|
||||
AutoUnmount::AutoUnmount(Path &p) : path(p), del(true) {}
|
||||
AutoUnmount::AutoUnmount(Path & p)
|
||||
: path(p)
|
||||
, del(true)
|
||||
{
|
||||
}
|
||||
|
||||
AutoUnmount::~AutoUnmount()
|
||||
{
|
||||
|
|
@ -649,7 +669,8 @@ void AutoUnmount::cancel()
|
|||
|
||||
//////////////////////////////////////////////////////////////////////
|
||||
|
||||
std::string defaultTempDir() {
|
||||
std::string defaultTempDir()
|
||||
{
|
||||
return getEnvNonEmpty("TMPDIR").value_or("/tmp");
|
||||
}
|
||||
|
||||
|
|
@ -658,11 +679,14 @@ Path createTempDir(const Path & tmpRoot, const Path & prefix, mode_t mode)
|
|||
while (1) {
|
||||
checkInterrupt();
|
||||
Path tmpDir = makeTempPath(tmpRoot, prefix);
|
||||
if (mkdir(tmpDir.c_str()
|
||||
if (mkdir(
|
||||
tmpDir.c_str()
|
||||
#ifndef _WIN32 // TODO abstract mkdir perms for Windows
|
||||
, mode
|
||||
,
|
||||
mode
|
||||
#endif
|
||||
) == 0) {
|
||||
)
|
||||
== 0) {
|
||||
#ifdef __FreeBSD__
|
||||
/* Explicitly set the group of the directory. This is to
|
||||
work around around problems caused by BSD's group
|
||||
|
|
@ -682,7 +706,6 @@ Path createTempDir(const Path & tmpRoot, const Path & prefix, mode_t mode)
|
|||
}
|
||||
}
|
||||
|
||||
|
||||
std::pair<AutoCloseFD, Path> createTempFile(const Path & prefix)
|
||||
{
|
||||
Path tmpl(defaultTempDir() + "/" + prefix + ".XXXXXX");
|
||||
|
|
@ -717,24 +740,25 @@ void createSymlink(const Path & target, const Path & link)
|
|||
void replaceSymlink(const std::filesystem::path & target, const std::filesystem::path & link)
|
||||
{
|
||||
for (unsigned int n = 0; true; n++) {
|
||||
auto tmp = link.parent_path() /std::filesystem::path{fmt(".%d_%s", n, link.filename().string())};
|
||||
auto tmp = link.parent_path() / std::filesystem::path{fmt(".%d_%s", n, link.filename().string())};
|
||||
tmp = tmp.lexically_normal();
|
||||
|
||||
try {
|
||||
std::filesystem::create_symlink(target, tmp);
|
||||
} catch (std::filesystem::filesystem_error & e) {
|
||||
if (e.code() == std::errc::file_exists) continue;
|
||||
if (e.code() == std::errc::file_exists)
|
||||
continue;
|
||||
throw SysError("creating symlink %1% -> %2%", tmp, target);
|
||||
}
|
||||
|
||||
try {
|
||||
std::filesystem::rename(tmp, link);
|
||||
} catch (std::filesystem::filesystem_error & e) {
|
||||
if (e.code() == std::errc::file_exists) continue;
|
||||
if (e.code() == std::errc::file_exists)
|
||||
continue;
|
||||
throw SysError("renaming %1% to %2%", tmp, link);
|
||||
}
|
||||
|
||||
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
|
@ -746,15 +770,19 @@ void setWriteTime(const std::filesystem::path & path, const struct stat & st)
|
|||
|
||||
void copyFile(const std::filesystem::path & from, const std::filesystem::path & to, bool andDelete)
|
||||
{
|
||||
auto fromStatus =std::filesystem::symlink_status(from);
|
||||
auto fromStatus = std::filesystem::symlink_status(from);
|
||||
|
||||
// Mark the directory as writable so that we can delete its children
|
||||
if (andDelete &&std::filesystem::is_directory(fromStatus)) {
|
||||
std::filesystem::permissions(from, std::filesystem::perms::owner_write, std::filesystem::perm_options::add | std::filesystem::perm_options::nofollow);
|
||||
if (andDelete && std::filesystem::is_directory(fromStatus)) {
|
||||
std::filesystem::permissions(
|
||||
from,
|
||||
std::filesystem::perms::owner_write,
|
||||
std::filesystem::perm_options::add | std::filesystem::perm_options::nofollow);
|
||||
}
|
||||
|
||||
if (std::filesystem::is_symlink(fromStatus) ||std::filesystem::is_regular_file(fromStatus)) {
|
||||
std::filesystem::copy(from, to, std::filesystem::copy_options::copy_symlinks | std::filesystem::copy_options::overwrite_existing);
|
||||
if (std::filesystem::is_symlink(fromStatus) || std::filesystem::is_regular_file(fromStatus)) {
|
||||
std::filesystem::copy(
|
||||
from, to, std::filesystem::copy_options::copy_symlinks | std::filesystem::copy_options::overwrite_existing);
|
||||
} else if (std::filesystem::is_directory(fromStatus)) {
|
||||
std::filesystem::create_directory(to);
|
||||
for (auto & entry : DirectoryIterator(from)) {
|
||||
|
|
@ -767,7 +795,10 @@ void copyFile(const std::filesystem::path & from, const std::filesystem::path &
|
|||
setWriteTime(to, lstat(from.string().c_str()));
|
||||
if (andDelete) {
|
||||
if (!std::filesystem::is_symlink(fromStatus))
|
||||
std::filesystem::permissions(from, std::filesystem::perms::owner_write, std::filesystem::perm_options::add | std::filesystem::perm_options::nofollow);
|
||||
std::filesystem::permissions(
|
||||
from,
|
||||
std::filesystem::perms::owner_write,
|
||||
std::filesystem::perm_options::add | std::filesystem::perm_options::nofollow);
|
||||
std::filesystem::remove(from);
|
||||
}
|
||||
}
|
||||
|
|
@ -781,9 +812,8 @@ void moveFile(const Path & oldName, const Path & newName)
|
|||
auto newPath = std::filesystem::path(newName);
|
||||
// For the move to be as atomic as possible, copy to a temporary
|
||||
// directory
|
||||
std::filesystem::path temp = createTempDir(
|
||||
os_string_to_string(PathViewNG { newPath.parent_path() }),
|
||||
"rename-tmp");
|
||||
std::filesystem::path temp =
|
||||
createTempDir(os_string_to_string(PathViewNG{newPath.parent_path()}), "rename-tmp");
|
||||
Finally removeTemp = [&]() { std::filesystem::remove(temp); };
|
||||
auto tempCopyTarget = temp / "copy-target";
|
||||
if (e.code().value() == EXDEV) {
|
||||
|
|
@ -791,31 +821,34 @@ void moveFile(const Path & oldName, const Path & newName)
|
|||
warn("can’t rename %s as %s, copying instead", oldName, newName);
|
||||
copyFile(oldPath, tempCopyTarget, true);
|
||||
std::filesystem::rename(
|
||||
os_string_to_string(PathViewNG { tempCopyTarget }),
|
||||
os_string_to_string(PathViewNG { newPath }));
|
||||
os_string_to_string(PathViewNG{tempCopyTarget}), os_string_to_string(PathViewNG{newPath}));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//////////////////////////////////////////////////////////////////////
|
||||
|
||||
bool isExecutableFileAmbient(const std::filesystem::path & exe) {
|
||||
bool isExecutableFileAmbient(const std::filesystem::path & exe)
|
||||
{
|
||||
// Check file type, because directory being executable means
|
||||
// something completely different.
|
||||
// `is_regular_file` follows symlinks before checking.
|
||||
return std::filesystem::is_regular_file(exe)
|
||||
&& access(exe.string().c_str(),
|
||||
&& access(
|
||||
exe.string().c_str(),
|
||||
#ifdef WIN32
|
||||
0 // TODO do better
|
||||
0 // TODO do better
|
||||
#else
|
||||
X_OK
|
||||
X_OK
|
||||
#endif
|
||||
) == 0;
|
||||
)
|
||||
== 0;
|
||||
}
|
||||
|
||||
std::filesystem::path makeParentCanonical(const std::filesystem::path & rawPath)
|
||||
{
|
||||
std::filesystem::path path(absPath(rawPath));;
|
||||
std::filesystem::path path(absPath(rawPath));
|
||||
;
|
||||
try {
|
||||
auto parent = path.parent_path();
|
||||
if (parent == path) {
|
||||
|
|
|
|||
|
|
@ -48,5 +48,5 @@ void AutoRemoveJail::reset(int j)
|
|||
|
||||
//////////////////////////////////////////////////////////////////////
|
||||
|
||||
}
|
||||
} // namespace nix
|
||||
#endif
|
||||
|
|
|
|||
|
|
@ -17,4 +17,4 @@ public:
|
|||
void reset(int j);
|
||||
};
|
||||
|
||||
}
|
||||
} // namespace nix
|
||||
|
|
|
|||
|
|
@ -5,47 +5,38 @@
|
|||
#include "nix/util/fs-sink.hh"
|
||||
|
||||
#ifdef _WIN32
|
||||
# include <fileapi.h>
|
||||
# include "nix/util/file-path.hh"
|
||||
# include "nix/util/windows-error.hh"
|
||||
# include <fileapi.h>
|
||||
# include "nix/util/file-path.hh"
|
||||
# include "nix/util/windows-error.hh"
|
||||
#endif
|
||||
|
||||
#include "util-config-private.hh"
|
||||
|
||||
namespace nix {
|
||||
|
||||
void copyRecursive(
|
||||
SourceAccessor & accessor, const CanonPath & from,
|
||||
FileSystemObjectSink & sink, const CanonPath & to)
|
||||
void copyRecursive(SourceAccessor & accessor, const CanonPath & from, FileSystemObjectSink & sink, const CanonPath & to)
|
||||
{
|
||||
auto stat = accessor.lstat(from);
|
||||
|
||||
switch (stat.type) {
|
||||
case SourceAccessor::tSymlink:
|
||||
{
|
||||
case SourceAccessor::tSymlink: {
|
||||
sink.createSymlink(to, accessor.readLink(from));
|
||||
break;
|
||||
}
|
||||
|
||||
case SourceAccessor::tRegular:
|
||||
{
|
||||
case SourceAccessor::tRegular: {
|
||||
sink.createRegularFile(to, [&](CreateRegularFileSink & crf) {
|
||||
if (stat.isExecutable)
|
||||
crf.isExecutable();
|
||||
accessor.readFile(from, crf, [&](uint64_t size) {
|
||||
crf.preallocateContents(size);
|
||||
});
|
||||
accessor.readFile(from, crf, [&](uint64_t size) { crf.preallocateContents(size); });
|
||||
});
|
||||
break;
|
||||
}
|
||||
|
||||
case SourceAccessor::tDirectory:
|
||||
{
|
||||
case SourceAccessor::tDirectory: {
|
||||
sink.createDirectory(to);
|
||||
for (auto & [name, _] : accessor.readDirectory(from)) {
|
||||
copyRecursive(
|
||||
accessor, from / name,
|
||||
sink, to / name);
|
||||
copyRecursive(accessor, from / name, sink, to / name);
|
||||
break;
|
||||
}
|
||||
break;
|
||||
|
|
@ -61,11 +52,10 @@ void copyRecursive(
|
|||
}
|
||||
}
|
||||
|
||||
|
||||
struct RestoreSinkSettings : Config
|
||||
{
|
||||
Setting<bool> preallocateContents{this, false, "preallocate-contents",
|
||||
"Whether to preallocate files when writing objects with known size."};
|
||||
Setting<bool> preallocateContents{
|
||||
this, false, "preallocate-contents", "Whether to preallocate files when writing objects with known size."};
|
||||
};
|
||||
|
||||
static RestoreSinkSettings restoreSinkSettings;
|
||||
|
|
@ -87,7 +77,8 @@ void RestoreSink::createDirectory(const CanonPath & path)
|
|||
throw Error("path '%s' already exists", p.string());
|
||||
};
|
||||
|
||||
struct RestoreRegularFile : CreateRegularFileSink {
|
||||
struct RestoreRegularFile : CreateRegularFileSink
|
||||
{
|
||||
AutoCloseFD fd;
|
||||
bool startFsync = false;
|
||||
|
||||
|
|
@ -101,7 +92,7 @@ struct RestoreRegularFile : CreateRegularFileSink {
|
|||
fd.startFsync();
|
||||
}
|
||||
|
||||
void operator () (std::string_view data) override;
|
||||
void operator()(std::string_view data) override;
|
||||
void isExecutable() override;
|
||||
void preallocateContents(uint64_t size) override;
|
||||
};
|
||||
|
|
@ -114,12 +105,20 @@ void RestoreSink::createRegularFile(const CanonPath & path, std::function<void(C
|
|||
crf.startFsync = startFsync;
|
||||
crf.fd =
|
||||
#ifdef _WIN32
|
||||
CreateFileW(p.c_str(), GENERIC_READ | GENERIC_WRITE, FILE_SHARE_READ | FILE_SHARE_WRITE, NULL, CREATE_NEW, FILE_ATTRIBUTE_NORMAL, NULL)
|
||||
CreateFileW(
|
||||
p.c_str(),
|
||||
GENERIC_READ | GENERIC_WRITE,
|
||||
FILE_SHARE_READ | FILE_SHARE_WRITE,
|
||||
NULL,
|
||||
CREATE_NEW,
|
||||
FILE_ATTRIBUTE_NORMAL,
|
||||
NULL)
|
||||
#else
|
||||
open(p.c_str(), O_CREAT | O_EXCL | O_WRONLY | O_CLOEXEC, 0666)
|
||||
#endif
|
||||
;
|
||||
if (!crf.fd) throw NativeSysError("creating file '%1%'", p);
|
||||
if (!crf.fd)
|
||||
throw NativeSysError("creating file '%1%'", p);
|
||||
func(crf);
|
||||
}
|
||||
|
||||
|
|
@ -154,7 +153,7 @@ void RestoreRegularFile::preallocateContents(uint64_t len)
|
|||
#endif
|
||||
}
|
||||
|
||||
void RestoreRegularFile::operator () (std::string_view data)
|
||||
void RestoreRegularFile::operator()(std::string_view data)
|
||||
{
|
||||
writeFull(fd.get(), data);
|
||||
}
|
||||
|
|
@ -165,32 +164,42 @@ void RestoreSink::createSymlink(const CanonPath & path, const std::string & targ
|
|||
nix::createSymlink(target, p.string());
|
||||
}
|
||||
|
||||
|
||||
void RegularFileSink::createRegularFile(const CanonPath & path, std::function<void(CreateRegularFileSink &)> func)
|
||||
{
|
||||
struct CRF : CreateRegularFileSink {
|
||||
struct CRF : CreateRegularFileSink
|
||||
{
|
||||
RegularFileSink & back;
|
||||
CRF(RegularFileSink & back) : back(back) {}
|
||||
void operator () (std::string_view data) override
|
||||
|
||||
CRF(RegularFileSink & back)
|
||||
: back(back)
|
||||
{
|
||||
}
|
||||
|
||||
void operator()(std::string_view data) override
|
||||
{
|
||||
back.sink(data);
|
||||
}
|
||||
|
||||
void isExecutable() override {}
|
||||
} crf { *this };
|
||||
} crf{*this};
|
||||
|
||||
func(crf);
|
||||
}
|
||||
|
||||
|
||||
void NullFileSystemObjectSink::createRegularFile(const CanonPath & path, std::function<void(CreateRegularFileSink &)> func)
|
||||
void NullFileSystemObjectSink::createRegularFile(
|
||||
const CanonPath & path, std::function<void(CreateRegularFileSink &)> func)
|
||||
{
|
||||
struct : CreateRegularFileSink {
|
||||
void operator () (std::string_view data) override {}
|
||||
struct : CreateRegularFileSink
|
||||
{
|
||||
void operator()(std::string_view data) override {}
|
||||
|
||||
void isExecutable() override {}
|
||||
} crf;
|
||||
|
||||
// Even though `NullFileSystemObjectSink` doesn't do anything, it's important
|
||||
// that we call the function, to e.g. advance the parser using this
|
||||
// sink.
|
||||
func(crf);
|
||||
}
|
||||
|
||||
}
|
||||
} // namespace nix
|
||||
|
|
|
|||
|
|
@ -17,32 +17,31 @@ namespace nix::git {
|
|||
using namespace nix;
|
||||
using namespace std::string_literals;
|
||||
|
||||
std::optional<Mode> decodeMode(RawMode m) {
|
||||
std::optional<Mode> decodeMode(RawMode m)
|
||||
{
|
||||
switch (m) {
|
||||
case (RawMode) Mode::Directory:
|
||||
case (RawMode) Mode::Executable:
|
||||
case (RawMode) Mode::Regular:
|
||||
case (RawMode) Mode::Symlink:
|
||||
return (Mode) m;
|
||||
default:
|
||||
return std::nullopt;
|
||||
case (RawMode) Mode::Directory:
|
||||
case (RawMode) Mode::Executable:
|
||||
case (RawMode) Mode::Regular:
|
||||
case (RawMode) Mode::Symlink:
|
||||
return (Mode) m;
|
||||
default:
|
||||
return std::nullopt;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
static std::string getStringUntil(Source & source, char byte)
|
||||
{
|
||||
std::string s;
|
||||
char n[1] = { 0 };
|
||||
source(std::string_view { n, 1 });
|
||||
char n[1] = {0};
|
||||
source(std::string_view{n, 1});
|
||||
while (*n != byte) {
|
||||
s += *n;
|
||||
source(std::string_view { n, 1 });
|
||||
source(std::string_view{n, 1});
|
||||
}
|
||||
return s;
|
||||
}
|
||||
|
||||
|
||||
static std::string getString(Source & source, int n)
|
||||
{
|
||||
std::string v;
|
||||
|
|
@ -75,7 +74,7 @@ void parseBlob(
|
|||
|
||||
while (left) {
|
||||
checkInterrupt();
|
||||
buf.resize(std::min((unsigned long long)buf.capacity(), left));
|
||||
buf.resize(std::min((unsigned long long) buf.capacity(), left));
|
||||
source(buf);
|
||||
crf(buf);
|
||||
left -= buf.size();
|
||||
|
|
@ -93,16 +92,13 @@ void parseBlob(
|
|||
doRegularFile(true);
|
||||
break;
|
||||
|
||||
case BlobMode::Symlink:
|
||||
{
|
||||
case BlobMode::Symlink: {
|
||||
std::string target;
|
||||
target.resize(size, '0');
|
||||
target.reserve(size);
|
||||
for (size_t n = 0; n < target.size();) {
|
||||
checkInterrupt();
|
||||
n += source.read(
|
||||
const_cast<char *>(target.c_str()) + n,
|
||||
target.size() - n);
|
||||
n += source.read(const_cast<char *>(target.c_str()) + n, target.size() - n);
|
||||
}
|
||||
|
||||
sink.createSymlink(sinkPath, target);
|
||||
|
|
@ -147,16 +143,16 @@ void parseTree(
|
|||
Hash hash(HashAlgorithm::SHA1);
|
||||
std::copy(hashs.begin(), hashs.end(), hash.hash);
|
||||
|
||||
hook(CanonPath{name}, TreeEntry {
|
||||
.mode = mode,
|
||||
.hash = hash,
|
||||
});
|
||||
hook(
|
||||
CanonPath{name},
|
||||
TreeEntry{
|
||||
.mode = mode,
|
||||
.hash = hash,
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
ObjectType parseObjectType(
|
||||
Source & source,
|
||||
const ExperimentalFeatureSettings & xpSettings)
|
||||
ObjectType parseObjectType(Source & source, const ExperimentalFeatureSettings & xpSettings)
|
||||
{
|
||||
xpSettings.require(Xp::GitHashing);
|
||||
|
||||
|
|
@ -166,7 +162,8 @@ ObjectType parseObjectType(
|
|||
return ObjectType::Blob;
|
||||
} else if (type == "tree ") {
|
||||
return ObjectType::Tree;
|
||||
} else throw Error("input doesn't look like a Git object");
|
||||
} else
|
||||
throw Error("input doesn't look like a Git object");
|
||||
}
|
||||
|
||||
void parse(
|
||||
|
|
@ -193,23 +190,26 @@ void parse(
|
|||
};
|
||||
}
|
||||
|
||||
|
||||
std::optional<Mode> convertMode(SourceAccessor::Type type)
|
||||
{
|
||||
switch (type) {
|
||||
case SourceAccessor::tSymlink: return Mode::Symlink;
|
||||
case SourceAccessor::tRegular: return Mode::Regular;
|
||||
case SourceAccessor::tDirectory: return Mode::Directory;
|
||||
case SourceAccessor::tSymlink:
|
||||
return Mode::Symlink;
|
||||
case SourceAccessor::tRegular:
|
||||
return Mode::Regular;
|
||||
case SourceAccessor::tDirectory:
|
||||
return Mode::Directory;
|
||||
case SourceAccessor::tChar:
|
||||
case SourceAccessor::tBlock:
|
||||
case SourceAccessor::tSocket:
|
||||
case SourceAccessor::tFifo: return std::nullopt;
|
||||
case SourceAccessor::tFifo:
|
||||
return std::nullopt;
|
||||
case SourceAccessor::tUnknown:
|
||||
default: unreachable();
|
||||
default:
|
||||
unreachable();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void restore(FileSystemObjectSink & sink, Source & source, std::function<RestoreHook> hook)
|
||||
{
|
||||
parse(sink, CanonPath::root, source, BlobMode::Regular, [&](CanonPath name, TreeEntry entry) {
|
||||
|
|
@ -217,35 +217,30 @@ void restore(FileSystemObjectSink & sink, Source & source, std::function<Restore
|
|||
auto stat = accessor->lstat(from);
|
||||
auto gotOpt = convertMode(stat.type);
|
||||
if (!gotOpt)
|
||||
throw Error("file '%s' (git hash %s) has an unsupported type",
|
||||
throw Error(
|
||||
"file '%s' (git hash %s) has an unsupported type",
|
||||
from,
|
||||
entry.hash.to_string(HashFormat::Base16, false));
|
||||
auto & got = *gotOpt;
|
||||
if (got != entry.mode)
|
||||
throw Error("git mode of file '%s' (git hash %s) is %o but expected %o",
|
||||
throw Error(
|
||||
"git mode of file '%s' (git hash %s) is %o but expected %o",
|
||||
from,
|
||||
entry.hash.to_string(HashFormat::Base16, false),
|
||||
(RawMode) got,
|
||||
(RawMode) entry.mode);
|
||||
copyRecursive(
|
||||
*accessor, from,
|
||||
sink, name);
|
||||
copyRecursive(*accessor, from, sink, name);
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
void dumpBlobPrefix(
|
||||
uint64_t size, Sink & sink,
|
||||
const ExperimentalFeatureSettings & xpSettings)
|
||||
void dumpBlobPrefix(uint64_t size, Sink & sink, const ExperimentalFeatureSettings & xpSettings)
|
||||
{
|
||||
xpSettings.require(Xp::GitHashing);
|
||||
auto s = fmt("blob %d\0"s, std::to_string(size));
|
||||
sink(s);
|
||||
}
|
||||
|
||||
|
||||
void dumpTree(const Tree & entries, Sink & sink,
|
||||
const ExperimentalFeatureSettings & xpSettings)
|
||||
void dumpTree(const Tree & entries, Sink & sink, const ExperimentalFeatureSettings & xpSettings)
|
||||
{
|
||||
xpSettings.require(Xp::GitHashing);
|
||||
|
||||
|
|
@ -270,7 +265,6 @@ void dumpTree(const Tree & entries, Sink & sink,
|
|||
sink(v1);
|
||||
}
|
||||
|
||||
|
||||
Mode dump(
|
||||
const SourcePath & path,
|
||||
Sink & sink,
|
||||
|
|
@ -281,22 +275,17 @@ Mode dump(
|
|||
auto st = path.lstat();
|
||||
|
||||
switch (st.type) {
|
||||
case SourceAccessor::tRegular:
|
||||
{
|
||||
path.readFile(sink, [&](uint64_t size) {
|
||||
dumpBlobPrefix(size, sink, xpSettings);
|
||||
});
|
||||
return st.isExecutable
|
||||
? Mode::Executable
|
||||
: Mode::Regular;
|
||||
case SourceAccessor::tRegular: {
|
||||
path.readFile(sink, [&](uint64_t size) { dumpBlobPrefix(size, sink, xpSettings); });
|
||||
return st.isExecutable ? Mode::Executable : Mode::Regular;
|
||||
}
|
||||
|
||||
case SourceAccessor::tDirectory:
|
||||
{
|
||||
case SourceAccessor::tDirectory: {
|
||||
Tree entries;
|
||||
for (auto & [name, _] : path.readDirectory()) {
|
||||
auto child = path / name;
|
||||
if (!filter(child.path.abs())) continue;
|
||||
if (!filter(child.path.abs()))
|
||||
continue;
|
||||
|
||||
auto entry = hook(child);
|
||||
|
||||
|
|
@ -310,8 +299,7 @@ Mode dump(
|
|||
return Mode::Directory;
|
||||
}
|
||||
|
||||
case SourceAccessor::tSymlink:
|
||||
{
|
||||
case SourceAccessor::tSymlink: {
|
||||
auto target = path.readLink();
|
||||
dumpBlobPrefix(target.size(), sink, xpSettings);
|
||||
sink(target);
|
||||
|
|
@ -328,11 +316,7 @@ Mode dump(
|
|||
}
|
||||
}
|
||||
|
||||
|
||||
TreeEntry dumpHash(
|
||||
HashAlgorithm ha,
|
||||
const SourcePath & path,
|
||||
PathFilter & filter)
|
||||
TreeEntry dumpHash(HashAlgorithm ha, const SourcePath & path, PathFilter & filter)
|
||||
{
|
||||
std::function<DumpHook> hook;
|
||||
hook = [&](const SourcePath & path) -> TreeEntry {
|
||||
|
|
@ -348,7 +332,6 @@ TreeEntry dumpHash(
|
|||
return hook(path);
|
||||
}
|
||||
|
||||
|
||||
std::optional<LsRemoteRefLine> parseLsRemoteLine(std::string_view line)
|
||||
{
|
||||
const static std::regex line_regex("^(ref: *)?([^\\s]+)(?:\\t+(.*))?$");
|
||||
|
|
@ -356,13 +339,10 @@ std::optional<LsRemoteRefLine> parseLsRemoteLine(std::string_view line)
|
|||
if (!std::regex_match(line.cbegin(), line.cend(), match, line_regex))
|
||||
return std::nullopt;
|
||||
|
||||
return LsRemoteRefLine {
|
||||
.kind = match[1].length() == 0
|
||||
? LsRemoteRefLine::Kind::Object
|
||||
: LsRemoteRefLine::Kind::Symbolic,
|
||||
return LsRemoteRefLine{
|
||||
.kind = match[1].length() == 0 ? LsRemoteRefLine::Kind::Object : LsRemoteRefLine::Kind::Symbolic,
|
||||
.target = match[2],
|
||||
.reference = match[3].length() == 0 ? std::nullopt : std::optional<std::string>{ match[3] }
|
||||
};
|
||||
.reference = match[3].length() == 0 ? std::nullopt : std::optional<std::string>{match[3]}};
|
||||
}
|
||||
|
||||
}
|
||||
} // namespace nix::git
|
||||
|
|
|
|||
|
|
@ -20,23 +20,29 @@
|
|||
|
||||
namespace nix {
|
||||
|
||||
static size_t regularHashSize(HashAlgorithm type) {
|
||||
static size_t regularHashSize(HashAlgorithm type)
|
||||
{
|
||||
switch (type) {
|
||||
case HashAlgorithm::BLAKE3: return blake3HashSize;
|
||||
case HashAlgorithm::MD5: return md5HashSize;
|
||||
case HashAlgorithm::SHA1: return sha1HashSize;
|
||||
case HashAlgorithm::SHA256: return sha256HashSize;
|
||||
case HashAlgorithm::SHA512: return sha512HashSize;
|
||||
case HashAlgorithm::BLAKE3:
|
||||
return blake3HashSize;
|
||||
case HashAlgorithm::MD5:
|
||||
return md5HashSize;
|
||||
case HashAlgorithm::SHA1:
|
||||
return sha1HashSize;
|
||||
case HashAlgorithm::SHA256:
|
||||
return sha256HashSize;
|
||||
case HashAlgorithm::SHA512:
|
||||
return sha512HashSize;
|
||||
}
|
||||
unreachable();
|
||||
}
|
||||
|
||||
const StringSet hashAlgorithms = {"blake3", "md5", "sha1", "sha256", "sha512"};
|
||||
|
||||
const StringSet hashAlgorithms = {"blake3", "md5", "sha1", "sha256", "sha512" };
|
||||
const StringSet hashFormats = {"base64", "nix32", "base16", "sri"};
|
||||
|
||||
const StringSet hashFormats = {"base64", "nix32", "base16", "sri" };
|
||||
|
||||
Hash::Hash(HashAlgorithm algo, const ExperimentalFeatureSettings & xpSettings) : algo(algo)
|
||||
Hash::Hash(HashAlgorithm algo, const ExperimentalFeatureSettings & xpSettings)
|
||||
: algo(algo)
|
||||
{
|
||||
if (algo == HashAlgorithm::BLAKE3) {
|
||||
xpSettings.require(Xp::BLAKE3Hashes);
|
||||
|
|
@ -46,30 +52,31 @@ Hash::Hash(HashAlgorithm algo, const ExperimentalFeatureSettings & xpSettings) :
|
|||
memset(hash, 0, maxHashSize);
|
||||
}
|
||||
|
||||
|
||||
bool Hash::operator == (const Hash & h2) const noexcept
|
||||
bool Hash::operator==(const Hash & h2) const noexcept
|
||||
{
|
||||
if (hashSize != h2.hashSize) return false;
|
||||
if (hashSize != h2.hashSize)
|
||||
return false;
|
||||
for (unsigned int i = 0; i < hashSize; i++)
|
||||
if (hash[i] != h2.hash[i]) return false;
|
||||
if (hash[i] != h2.hash[i])
|
||||
return false;
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
std::strong_ordering Hash::operator <=> (const Hash & h) const noexcept
|
||||
std::strong_ordering Hash::operator<=>(const Hash & h) const noexcept
|
||||
{
|
||||
if (auto cmp = hashSize <=> h.hashSize; cmp != 0) return cmp;
|
||||
if (auto cmp = hashSize <=> h.hashSize; cmp != 0)
|
||||
return cmp;
|
||||
for (unsigned int i = 0; i < hashSize; i++) {
|
||||
if (auto cmp = hash[i] <=> h.hash[i]; cmp != 0) return cmp;
|
||||
if (auto cmp = hash[i] <=> h.hash[i]; cmp != 0)
|
||||
return cmp;
|
||||
}
|
||||
if (auto cmp = algo <=> h.algo; cmp != 0) return cmp;
|
||||
if (auto cmp = algo <=> h.algo; cmp != 0)
|
||||
return cmp;
|
||||
return std::strong_ordering::equivalent;
|
||||
}
|
||||
|
||||
|
||||
const std::string base16Chars = "0123456789abcdef";
|
||||
|
||||
|
||||
static std::string printHash16(const Hash & hash)
|
||||
{
|
||||
std::string buf;
|
||||
|
|
@ -81,11 +88,9 @@ static std::string printHash16(const Hash & hash)
|
|||
return buf;
|
||||
}
|
||||
|
||||
|
||||
// omitted: E O U T
|
||||
const std::string nix32Chars = "0123456789abcdfghijklmnpqrsvwxyz";
|
||||
|
||||
|
||||
static std::string printHash32(const Hash & hash)
|
||||
{
|
||||
assert(hash.hashSize);
|
||||
|
|
@ -99,23 +104,19 @@ static std::string printHash32(const Hash & hash)
|
|||
unsigned int b = n * 5;
|
||||
unsigned int i = b / 8;
|
||||
unsigned int j = b % 8;
|
||||
unsigned char c =
|
||||
(hash.hash[i] >> j)
|
||||
| (i >= hash.hashSize - 1 ? 0 : hash.hash[i + 1] << (8 - j));
|
||||
unsigned char c = (hash.hash[i] >> j) | (i >= hash.hashSize - 1 ? 0 : hash.hash[i + 1] << (8 - j));
|
||||
s.push_back(nix32Chars[c & 0x1f]);
|
||||
}
|
||||
|
||||
return s;
|
||||
}
|
||||
|
||||
|
||||
std::string printHash16or32(const Hash & hash)
|
||||
{
|
||||
assert(static_cast<char>(hash.algo));
|
||||
return hash.to_string(hash.algo == HashAlgorithm::MD5 ? HashFormat::Base16 : HashFormat::Nix32, false);
|
||||
}
|
||||
|
||||
|
||||
std::string Hash::to_string(HashFormat hashFormat, bool includeAlgo) const
|
||||
{
|
||||
std::string s;
|
||||
|
|
@ -215,16 +216,17 @@ Hash::Hash(std::string_view rest, HashAlgorithm algo, bool isSRI)
|
|||
if (!isSRI && rest.size() == base16Len()) {
|
||||
|
||||
auto parseHexDigit = [&](char c) {
|
||||
if (c >= '0' && c <= '9') return c - '0';
|
||||
if (c >= 'A' && c <= 'F') return c - 'A' + 10;
|
||||
if (c >= 'a' && c <= 'f') return c - 'a' + 10;
|
||||
if (c >= '0' && c <= '9')
|
||||
return c - '0';
|
||||
if (c >= 'A' && c <= 'F')
|
||||
return c - 'A' + 10;
|
||||
if (c >= 'a' && c <= 'f')
|
||||
return c - 'a' + 10;
|
||||
throw BadHash("invalid base-16 hash '%s'", rest);
|
||||
};
|
||||
|
||||
for (unsigned int i = 0; i < hashSize; i++) {
|
||||
hash[i] =
|
||||
parseHexDigit(rest[i * 2]) << 4
|
||||
| parseHexDigit(rest[i * 2 + 1]);
|
||||
hash[i] = parseHexDigit(rest[i * 2]) << 4 | parseHexDigit(rest[i * 2 + 1]);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -234,7 +236,8 @@ Hash::Hash(std::string_view rest, HashAlgorithm algo, bool isSRI)
|
|||
char c = rest[rest.size() - n - 1];
|
||||
unsigned char digit;
|
||||
for (digit = 0; digit < nix32Chars.size(); ++digit) /* !!! slow */
|
||||
if (nix32Chars[digit] == c) break;
|
||||
if (nix32Chars[digit] == c)
|
||||
break;
|
||||
if (digit >= 32)
|
||||
throw BadHash("invalid base-32 hash '%s'", rest);
|
||||
unsigned int b = n * 5;
|
||||
|
|
@ -287,7 +290,6 @@ Hash newHashAllowEmpty(std::string_view hashStr, std::optional<HashAlgorithm> ha
|
|||
return Hash::parseAny(hashStr, ha);
|
||||
}
|
||||
|
||||
|
||||
union Ctx
|
||||
{
|
||||
blake3_hasher blake3;
|
||||
|
|
@ -297,14 +299,18 @@ union Ctx
|
|||
SHA512_CTX sha512;
|
||||
};
|
||||
|
||||
|
||||
static void start(HashAlgorithm ha, Ctx & ctx)
|
||||
{
|
||||
if (ha == HashAlgorithm::BLAKE3) blake3_hasher_init(&ctx.blake3);
|
||||
else if (ha == HashAlgorithm::MD5) MD5_Init(&ctx.md5);
|
||||
else if (ha == HashAlgorithm::SHA1) SHA1_Init(&ctx.sha1);
|
||||
else if (ha == HashAlgorithm::SHA256) SHA256_Init(&ctx.sha256);
|
||||
else if (ha == HashAlgorithm::SHA512) SHA512_Init(&ctx.sha512);
|
||||
if (ha == HashAlgorithm::BLAKE3)
|
||||
blake3_hasher_init(&ctx.blake3);
|
||||
else if (ha == HashAlgorithm::MD5)
|
||||
MD5_Init(&ctx.md5);
|
||||
else if (ha == HashAlgorithm::SHA1)
|
||||
SHA1_Init(&ctx.sha1);
|
||||
else if (ha == HashAlgorithm::SHA256)
|
||||
SHA256_Init(&ctx.sha256);
|
||||
else if (ha == HashAlgorithm::SHA512)
|
||||
SHA512_Init(&ctx.sha512);
|
||||
}
|
||||
|
||||
// BLAKE3 data size threshold beyond which parallel hashing with TBB is likely faster.
|
||||
|
|
@ -328,28 +334,35 @@ void blake3_hasher_update_with_heuristics(blake3_hasher * blake3, std::string_vi
|
|||
}
|
||||
}
|
||||
|
||||
static void update(HashAlgorithm ha, Ctx & ctx,
|
||||
std::string_view data)
|
||||
static void update(HashAlgorithm ha, Ctx & ctx, std::string_view data)
|
||||
{
|
||||
if (ha == HashAlgorithm::BLAKE3) blake3_hasher_update_with_heuristics(&ctx.blake3, data);
|
||||
else if (ha == HashAlgorithm::MD5) MD5_Update(&ctx.md5, data.data(), data.size());
|
||||
else if (ha == HashAlgorithm::SHA1) SHA1_Update(&ctx.sha1, data.data(), data.size());
|
||||
else if (ha == HashAlgorithm::SHA256) SHA256_Update(&ctx.sha256, data.data(), data.size());
|
||||
else if (ha == HashAlgorithm::SHA512) SHA512_Update(&ctx.sha512, data.data(), data.size());
|
||||
if (ha == HashAlgorithm::BLAKE3)
|
||||
blake3_hasher_update_with_heuristics(&ctx.blake3, data);
|
||||
else if (ha == HashAlgorithm::MD5)
|
||||
MD5_Update(&ctx.md5, data.data(), data.size());
|
||||
else if (ha == HashAlgorithm::SHA1)
|
||||
SHA1_Update(&ctx.sha1, data.data(), data.size());
|
||||
else if (ha == HashAlgorithm::SHA256)
|
||||
SHA256_Update(&ctx.sha256, data.data(), data.size());
|
||||
else if (ha == HashAlgorithm::SHA512)
|
||||
SHA512_Update(&ctx.sha512, data.data(), data.size());
|
||||
}
|
||||
|
||||
|
||||
static void finish(HashAlgorithm ha, Ctx & ctx, unsigned char * hash)
|
||||
{
|
||||
if (ha == HashAlgorithm::BLAKE3) blake3_hasher_finalize(&ctx.blake3, hash, BLAKE3_OUT_LEN);
|
||||
else if (ha == HashAlgorithm::MD5) MD5_Final(hash, &ctx.md5);
|
||||
else if (ha == HashAlgorithm::SHA1) SHA1_Final(hash, &ctx.sha1);
|
||||
else if (ha == HashAlgorithm::SHA256) SHA256_Final(hash, &ctx.sha256);
|
||||
else if (ha == HashAlgorithm::SHA512) SHA512_Final(hash, &ctx.sha512);
|
||||
if (ha == HashAlgorithm::BLAKE3)
|
||||
blake3_hasher_finalize(&ctx.blake3, hash, BLAKE3_OUT_LEN);
|
||||
else if (ha == HashAlgorithm::MD5)
|
||||
MD5_Final(hash, &ctx.md5);
|
||||
else if (ha == HashAlgorithm::SHA1)
|
||||
SHA1_Final(hash, &ctx.sha1);
|
||||
else if (ha == HashAlgorithm::SHA256)
|
||||
SHA256_Final(hash, &ctx.sha256);
|
||||
else if (ha == HashAlgorithm::SHA512)
|
||||
SHA512_Final(hash, &ctx.sha512);
|
||||
}
|
||||
|
||||
Hash hashString(
|
||||
HashAlgorithm ha, std::string_view s, const ExperimentalFeatureSettings & xpSettings)
|
||||
Hash hashString(HashAlgorithm ha, std::string_view s, const ExperimentalFeatureSettings & xpSettings)
|
||||
{
|
||||
Ctx ctx;
|
||||
Hash hash(ha, xpSettings);
|
||||
|
|
@ -366,8 +379,8 @@ Hash hashFile(HashAlgorithm ha, const Path & path)
|
|||
return sink.finish().first;
|
||||
}
|
||||
|
||||
|
||||
HashSink::HashSink(HashAlgorithm ha) : ha(ha)
|
||||
HashSink::HashSink(HashAlgorithm ha)
|
||||
: ha(ha)
|
||||
{
|
||||
ctx = new Ctx;
|
||||
bytes = 0;
|
||||
|
|
@ -403,7 +416,6 @@ HashResult HashSink::currentHash()
|
|||
return HashResult(hash, bytes);
|
||||
}
|
||||
|
||||
|
||||
Hash compressHash(const Hash & hash, unsigned int newSize)
|
||||
{
|
||||
Hash h(hash.algo);
|
||||
|
|
@ -413,17 +425,20 @@ Hash compressHash(const Hash & hash, unsigned int newSize)
|
|||
return h;
|
||||
}
|
||||
|
||||
|
||||
std::optional<HashFormat> parseHashFormatOpt(std::string_view hashFormatName)
|
||||
{
|
||||
if (hashFormatName == "base16") return HashFormat::Base16;
|
||||
if (hashFormatName == "nix32") return HashFormat::Nix32;
|
||||
if (hashFormatName == "base16")
|
||||
return HashFormat::Base16;
|
||||
if (hashFormatName == "nix32")
|
||||
return HashFormat::Nix32;
|
||||
if (hashFormatName == "base32") {
|
||||
warn(R"("base32" is a deprecated alias for hash format "nix32".)");
|
||||
return HashFormat::Nix32;
|
||||
}
|
||||
if (hashFormatName == "base64") return HashFormat::Base64;
|
||||
if (hashFormatName == "sri") return HashFormat::SRI;
|
||||
if (hashFormatName == "base64")
|
||||
return HashFormat::Base64;
|
||||
if (hashFormatName == "sri")
|
||||
return HashFormat::SRI;
|
||||
return std::nullopt;
|
||||
}
|
||||
|
||||
|
|
@ -455,11 +470,16 @@ std::string_view printHashFormat(HashFormat HashFormat)
|
|||
|
||||
std::optional<HashAlgorithm> parseHashAlgoOpt(std::string_view s)
|
||||
{
|
||||
if (s == "blake3") return HashAlgorithm::BLAKE3;
|
||||
if (s == "md5") return HashAlgorithm::MD5;
|
||||
if (s == "sha1") return HashAlgorithm::SHA1;
|
||||
if (s == "sha256") return HashAlgorithm::SHA256;
|
||||
if (s == "sha512") return HashAlgorithm::SHA512;
|
||||
if (s == "blake3")
|
||||
return HashAlgorithm::BLAKE3;
|
||||
if (s == "md5")
|
||||
return HashAlgorithm::MD5;
|
||||
if (s == "sha1")
|
||||
return HashAlgorithm::SHA1;
|
||||
if (s == "sha256")
|
||||
return HashAlgorithm::SHA256;
|
||||
if (s == "sha512")
|
||||
return HashAlgorithm::SHA512;
|
||||
return std::nullopt;
|
||||
}
|
||||
|
||||
|
|
@ -475,11 +495,16 @@ HashAlgorithm parseHashAlgo(std::string_view s)
|
|||
std::string_view printHashAlgo(HashAlgorithm ha)
|
||||
{
|
||||
switch (ha) {
|
||||
case HashAlgorithm::BLAKE3: return "blake3";
|
||||
case HashAlgorithm::MD5: return "md5";
|
||||
case HashAlgorithm::SHA1: return "sha1";
|
||||
case HashAlgorithm::SHA256: return "sha256";
|
||||
case HashAlgorithm::SHA512: return "sha512";
|
||||
case HashAlgorithm::BLAKE3:
|
||||
return "blake3";
|
||||
case HashAlgorithm::MD5:
|
||||
return "md5";
|
||||
case HashAlgorithm::SHA1:
|
||||
return "sha1";
|
||||
case HashAlgorithm::SHA256:
|
||||
return "sha256";
|
||||
case HashAlgorithm::SHA512:
|
||||
return "sha512";
|
||||
default:
|
||||
// illegal hash type enum value internally, as opposed to external input
|
||||
// which should be validated with nice error message.
|
||||
|
|
@ -487,4 +512,4 @@ std::string_view printHashAlgo(HashAlgorithm ha)
|
|||
}
|
||||
}
|
||||
|
||||
}
|
||||
} // namespace nix
|
||||
|
|
|
|||
|
|
@ -2,19 +2,15 @@
|
|||
|
||||
namespace nix {
|
||||
|
||||
std::string hiliteMatches(
|
||||
std::string_view s,
|
||||
std::vector<std::smatch> matches,
|
||||
std::string_view prefix,
|
||||
std::string_view postfix)
|
||||
std::string
|
||||
hiliteMatches(std::string_view s, std::vector<std::smatch> matches, std::string_view prefix, std::string_view postfix)
|
||||
{
|
||||
// Avoid extra work on zero matches
|
||||
if (matches.size() == 0)
|
||||
return std::string(s);
|
||||
|
||||
std::sort(matches.begin(), matches.end(), [](const auto & a, const auto & b) {
|
||||
return a.position() < b.position();
|
||||
});
|
||||
std::sort(
|
||||
matches.begin(), matches.end(), [](const auto & a, const auto & b) { return a.position() < b.position(); });
|
||||
|
||||
std::string out;
|
||||
ssize_t last_end = 0;
|
||||
|
|
@ -41,4 +37,4 @@ std::string hiliteMatches(
|
|||
return out;
|
||||
}
|
||||
|
||||
}
|
||||
} // namespace nix
|
||||
|
|
|
|||
|
|
@ -15,4 +15,4 @@ std::map<std::string, nlohmann::json> BaseSetting<T>::toJSONObject() const
|
|||
obj.emplace("documentDefault", documentDefault);
|
||||
return obj;
|
||||
}
|
||||
}
|
||||
} // namespace nix
|
||||
|
|
|
|||
|
|
@ -1,4 +1,5 @@
|
|||
#pragma once
|
||||
|
||||
/**
|
||||
* @file
|
||||
*
|
||||
|
|
@ -18,4 +19,4 @@ namespace nix {
|
|||
#define ANSI_MAGENTA "\e[35;1m"
|
||||
#define ANSI_CYAN "\e[36;1m"
|
||||
|
||||
}
|
||||
} // namespace nix
|
||||
|
|
|
|||
|
|
@ -5,10 +5,8 @@
|
|||
#include "nix/util/serialise.hh"
|
||||
#include "nix/util/fs-sink.hh"
|
||||
|
||||
|
||||
namespace nix {
|
||||
|
||||
|
||||
/**
|
||||
* dumpPath creates a Nix archive of the specified path.
|
||||
*
|
||||
|
|
@ -57,14 +55,12 @@ namespace nix {
|
|||
* `+` denotes string concatenation.
|
||||
* ```
|
||||
*/
|
||||
void dumpPath(const Path & path, Sink & sink,
|
||||
PathFilter & filter = defaultPathFilter);
|
||||
void dumpPath(const Path & path, Sink & sink, PathFilter & filter = defaultPathFilter);
|
||||
|
||||
/**
|
||||
* Same as dumpPath(), but returns the last modified date of the path.
|
||||
*/
|
||||
time_t dumpPathAndGetMtime(const Path & path, Sink & sink,
|
||||
PathFilter & filter = defaultPathFilter);
|
||||
time_t dumpPathAndGetMtime(const Path & path, Sink & sink, PathFilter & filter = defaultPathFilter);
|
||||
|
||||
/**
|
||||
* Dump an archive with a single file with these contents.
|
||||
|
|
@ -82,10 +78,8 @@ void restorePath(const std::filesystem::path & path, Source & source, bool start
|
|||
*/
|
||||
void copyNAR(Source & source, Sink & sink);
|
||||
|
||||
|
||||
inline constexpr std::string_view narVersionMagic1 = "nix-archive-1";
|
||||
|
||||
inline constexpr std::string_view caseHackSuffix = "~nix~case~hack~";
|
||||
|
||||
|
||||
}
|
||||
} // namespace nix
|
||||
|
|
|
|||
|
|
@ -31,18 +31,28 @@ public:
|
|||
|
||||
/**
|
||||
* Return a short one-line description of the command.
|
||||
*/
|
||||
virtual std::string description() { return ""; }
|
||||
*/
|
||||
virtual std::string description()
|
||||
{
|
||||
return "";
|
||||
}
|
||||
|
||||
virtual bool forceImpureByDefault() { return false; }
|
||||
virtual bool forceImpureByDefault()
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return documentation about this command, in Markdown format.
|
||||
*/
|
||||
virtual std::string doc() { return ""; }
|
||||
virtual std::string doc()
|
||||
{
|
||||
return "";
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Get the [base directory](https://nixos.org/manual/nix/unstable/glossary#gloss-base-directory) for the command.
|
||||
* @brief Get the [base directory](https://nixos.org/manual/nix/unstable/glossary#gloss-base-directory) for the
|
||||
* command.
|
||||
*
|
||||
* @return Generally the working directory, but in case of a shebang
|
||||
* interpreter, returns the directory of the script.
|
||||
|
|
@ -78,73 +88,79 @@ protected:
|
|||
Handler(std::function<void(std::vector<std::string>)> && fun)
|
||||
: fun(std::move(fun))
|
||||
, arity(ArityAny)
|
||||
{ }
|
||||
{
|
||||
}
|
||||
|
||||
Handler(std::function<void()> && handler)
|
||||
: fun([handler{std::move(handler)}](std::vector<std::string>) { handler(); })
|
||||
, arity(0)
|
||||
{ }
|
||||
{
|
||||
}
|
||||
|
||||
Handler(std::function<void(std::string)> && handler)
|
||||
: fun([handler{std::move(handler)}](std::vector<std::string> ss) {
|
||||
handler(std::move(ss[0]));
|
||||
})
|
||||
: fun([handler{std::move(handler)}](std::vector<std::string> ss) { handler(std::move(ss[0])); })
|
||||
, arity(1)
|
||||
{ }
|
||||
{
|
||||
}
|
||||
|
||||
Handler(std::function<void(std::string, std::string)> && handler)
|
||||
: fun([handler{std::move(handler)}](std::vector<std::string> ss) {
|
||||
handler(std::move(ss[0]), std::move(ss[1]));
|
||||
})
|
||||
})
|
||||
, arity(2)
|
||||
{ }
|
||||
{
|
||||
}
|
||||
|
||||
Handler(std::vector<std::string> * dest)
|
||||
: fun([dest](std::vector<std::string> ss) { *dest = ss; })
|
||||
, arity(ArityAny)
|
||||
{ }
|
||||
{
|
||||
}
|
||||
|
||||
Handler(std::string * dest)
|
||||
: fun([dest](std::vector<std::string> ss) { *dest = ss[0]; })
|
||||
, arity(1)
|
||||
{ }
|
||||
{
|
||||
}
|
||||
|
||||
Handler(std::optional<std::string> * dest)
|
||||
: fun([dest](std::vector<std::string> ss) { *dest = ss[0]; })
|
||||
, arity(1)
|
||||
{ }
|
||||
{
|
||||
}
|
||||
|
||||
Handler(std::filesystem::path * dest)
|
||||
: fun([dest](std::vector<std::string> ss) { *dest = ss[0]; })
|
||||
, arity(1)
|
||||
{ }
|
||||
{
|
||||
}
|
||||
|
||||
Handler(std::optional<std::filesystem::path> * dest)
|
||||
: fun([dest](std::vector<std::string> ss) { *dest = ss[0]; })
|
||||
, arity(1)
|
||||
{ }
|
||||
{
|
||||
}
|
||||
|
||||
template<class T>
|
||||
Handler(T * dest, const T & val)
|
||||
: fun([dest, val](std::vector<std::string> ss) { *dest = val; })
|
||||
, arity(0)
|
||||
{ }
|
||||
{
|
||||
}
|
||||
|
||||
template<class I>
|
||||
Handler(I * dest)
|
||||
: fun([dest](std::vector<std::string> ss) {
|
||||
*dest = string2IntWithUnitPrefix<I>(ss[0]);
|
||||
})
|
||||
: fun([dest](std::vector<std::string> ss) { *dest = string2IntWithUnitPrefix<I>(ss[0]); })
|
||||
, arity(1)
|
||||
{ }
|
||||
{
|
||||
}
|
||||
|
||||
template<class I>
|
||||
Handler(std::optional<I> * dest)
|
||||
: fun([dest](std::vector<std::string> ss) {
|
||||
*dest = string2IntWithUnitPrefix<I>(ss[0]);
|
||||
})
|
||||
: fun([dest](std::vector<std::string> ss) { *dest = string2IntWithUnitPrefix<I>(ss[0]); })
|
||||
, arity(1)
|
||||
{ }
|
||||
{
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
|
|
@ -248,8 +264,8 @@ protected:
|
|||
* This list is used to extend the lifetime of the argument forms.
|
||||
* If this is not done, some closures that reference the command
|
||||
* itself will segfault.
|
||||
*/
|
||||
std::list<ExpectedArg> processedArgs;
|
||||
*/
|
||||
std::list<ExpectedArg> processedArgs;
|
||||
|
||||
/**
|
||||
* Process some positional arguments
|
||||
|
|
@ -261,7 +277,9 @@ protected:
|
|||
virtual bool processArgs(const Strings & args, bool finish);
|
||||
|
||||
virtual Strings::iterator rewriteArgs(Strings & args, Strings::iterator pos)
|
||||
{ return pos; }
|
||||
{
|
||||
return pos;
|
||||
}
|
||||
|
||||
StringSet hiddenCategories;
|
||||
|
||||
|
|
@ -287,11 +305,7 @@ public:
|
|||
*/
|
||||
void expectArg(const std::string & label, std::string * dest, bool optional = false)
|
||||
{
|
||||
expectArgs({
|
||||
.label = label,
|
||||
.optional = optional,
|
||||
.handler = {dest}
|
||||
});
|
||||
expectArgs({.label = label, .optional = optional, .handler = {dest}});
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
@ -299,11 +313,7 @@ public:
|
|||
*/
|
||||
void expectArg(const std::string & label, std::filesystem::path * dest, bool optional = false)
|
||||
{
|
||||
expectArgs({
|
||||
.label = label,
|
||||
.optional = optional,
|
||||
.handler = {dest}
|
||||
});
|
||||
expectArgs({.label = label, .optional = optional, .handler = {dest}});
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
@ -311,10 +321,7 @@ public:
|
|||
*/
|
||||
void expectArgs(const std::string & label, std::vector<std::string> * dest)
|
||||
{
|
||||
expectArgs({
|
||||
.label = label,
|
||||
.handler = {dest}
|
||||
});
|
||||
expectArgs({.label = label, .handler = {dest}});
|
||||
}
|
||||
|
||||
static CompleterFun completePath;
|
||||
|
|
@ -364,7 +371,10 @@ struct Command : virtual public Args
|
|||
|
||||
virtual std::optional<ExperimentalFeature> experimentalFeature();
|
||||
|
||||
virtual Category category() { return catDefault; }
|
||||
virtual Category category()
|
||||
{
|
||||
return catDefault;
|
||||
}
|
||||
};
|
||||
|
||||
using Commands = std::map<std::string, std::function<ref<Command>()>>;
|
||||
|
|
@ -401,7 +411,8 @@ public:
|
|||
};
|
||||
|
||||
/** An alias, except for the original syntax, which is in the map key. */
|
||||
struct AliasInfo {
|
||||
struct AliasInfo
|
||||
{
|
||||
AliasStatus status;
|
||||
std::vector<std::string> replacement;
|
||||
};
|
||||
|
|
@ -419,9 +430,10 @@ protected:
|
|||
bool aliasUsed = false;
|
||||
};
|
||||
|
||||
Strings argvToStrings(int argc, char * * argv);
|
||||
Strings argvToStrings(int argc, char ** argv);
|
||||
|
||||
struct Completion {
|
||||
struct Completion
|
||||
{
|
||||
std::string completion;
|
||||
std::string description;
|
||||
|
||||
|
|
@ -465,4 +477,4 @@ public:
|
|||
|
||||
Strings parseShebangContent(std::string_view s);
|
||||
|
||||
}
|
||||
} // namespace nix
|
||||
|
|
|
|||
|
|
@ -57,7 +57,8 @@ protected:
|
|||
/**
|
||||
* A pointer to the completion and its two arguments; a thunk;
|
||||
*/
|
||||
struct DeferredCompletion {
|
||||
struct DeferredCompletion
|
||||
{
|
||||
const CompleterClosure & completer;
|
||||
size_t n;
|
||||
std::string prefix;
|
||||
|
|
@ -82,4 +83,4 @@ private:
|
|||
std::optional<std::string> needsCompletion(std::string_view s);
|
||||
};
|
||||
|
||||
}
|
||||
} // namespace nix
|
||||
|
|
|
|||
|
|
@ -20,14 +20,18 @@ class Callback
|
|||
|
||||
public:
|
||||
|
||||
Callback(std::function<void(std::future<T>)> fun) : fun(fun) { }
|
||||
Callback(std::function<void(std::future<T>)> fun)
|
||||
: fun(fun)
|
||||
{
|
||||
}
|
||||
|
||||
// NOTE: std::function is noexcept move-constructible since C++20.
|
||||
Callback(Callback && callback) noexcept(std::is_nothrow_move_constructible_v<decltype(fun)>)
|
||||
: fun(std::move(callback.fun))
|
||||
{
|
||||
auto prev = callback.done.test_and_set();
|
||||
if (prev) done.test_and_set();
|
||||
if (prev)
|
||||
done.test_and_set();
|
||||
}
|
||||
|
||||
void operator()(T && t) noexcept
|
||||
|
|
@ -49,4 +53,4 @@ public:
|
|||
}
|
||||
};
|
||||
|
||||
}
|
||||
} // namespace nix
|
||||
|
|
|
|||
|
|
@ -51,13 +51,16 @@ public:
|
|||
|
||||
explicit CanonPath(const char * raw)
|
||||
: CanonPath(std::string_view(raw))
|
||||
{ }
|
||||
{
|
||||
}
|
||||
|
||||
struct unchecked_t { };
|
||||
struct unchecked_t
|
||||
{};
|
||||
|
||||
CanonPath(unchecked_t _, std::string path)
|
||||
: path(std::move(path))
|
||||
{ }
|
||||
{
|
||||
}
|
||||
|
||||
/**
|
||||
* Construct a canon path from a vector of elements.
|
||||
|
|
@ -74,13 +77,19 @@ public:
|
|||
CanonPath(std::string_view raw, const CanonPath & root);
|
||||
|
||||
bool isRoot() const
|
||||
{ return path.size() <= 1; }
|
||||
{
|
||||
return path.size() <= 1;
|
||||
}
|
||||
|
||||
explicit operator std::string_view() const
|
||||
{ return path; }
|
||||
{
|
||||
return path;
|
||||
}
|
||||
|
||||
const std::string & abs() const
|
||||
{ return path; }
|
||||
{
|
||||
return path;
|
||||
}
|
||||
|
||||
/**
|
||||
* Like abs(), but return an empty string if this path is
|
||||
|
|
@ -93,10 +102,14 @@ public:
|
|||
}
|
||||
|
||||
const char * c_str() const
|
||||
{ return path.c_str(); }
|
||||
{
|
||||
return path.c_str();
|
||||
}
|
||||
|
||||
std::string_view rel() const
|
||||
{ return ((std::string_view) path).substr(1); }
|
||||
{
|
||||
return ((std::string_view) path).substr(1);
|
||||
}
|
||||
|
||||
const char * rel_c_str() const
|
||||
{
|
||||
|
|
@ -113,18 +126,25 @@ public:
|
|||
Iterator(std::string_view remaining)
|
||||
: remaining(remaining)
|
||||
, slash(remaining.find('/'))
|
||||
{ }
|
||||
{
|
||||
}
|
||||
|
||||
bool operator != (const Iterator & x) const
|
||||
{ return remaining.data() != x.remaining.data(); }
|
||||
bool operator!=(const Iterator & x) const
|
||||
{
|
||||
return remaining.data() != x.remaining.data();
|
||||
}
|
||||
|
||||
bool operator == (const Iterator & x) const
|
||||
{ return !(*this != x); }
|
||||
bool operator==(const Iterator & x) const
|
||||
{
|
||||
return !(*this != x);
|
||||
}
|
||||
|
||||
const std::string_view operator * () const
|
||||
{ return remaining.substr(0, slash); }
|
||||
const std::string_view operator*() const
|
||||
{
|
||||
return remaining.substr(0, slash);
|
||||
}
|
||||
|
||||
void operator ++ ()
|
||||
void operator++()
|
||||
{
|
||||
if (slash == remaining.npos)
|
||||
remaining = remaining.substr(remaining.size());
|
||||
|
|
@ -135,8 +155,15 @@ public:
|
|||
}
|
||||
};
|
||||
|
||||
Iterator begin() const { return Iterator(rel()); }
|
||||
Iterator end() const { return Iterator(rel().substr(path.size() - 1)); }
|
||||
Iterator begin() const
|
||||
{
|
||||
return Iterator(rel());
|
||||
}
|
||||
|
||||
Iterator end() const
|
||||
{
|
||||
return Iterator(rel().substr(path.size() - 1));
|
||||
}
|
||||
|
||||
std::optional<CanonPath> parent() const;
|
||||
|
||||
|
|
@ -147,21 +174,27 @@ public:
|
|||
|
||||
std::optional<std::string_view> dirOf() const
|
||||
{
|
||||
if (isRoot()) return std::nullopt;
|
||||
if (isRoot())
|
||||
return std::nullopt;
|
||||
return ((std::string_view) path).substr(0, path.rfind('/'));
|
||||
}
|
||||
|
||||
std::optional<std::string_view> baseName() const
|
||||
{
|
||||
if (isRoot()) return std::nullopt;
|
||||
if (isRoot())
|
||||
return std::nullopt;
|
||||
return ((std::string_view) path).substr(path.rfind('/') + 1);
|
||||
}
|
||||
|
||||
bool operator == (const CanonPath & x) const
|
||||
{ return path == x.path; }
|
||||
bool operator==(const CanonPath & x) const
|
||||
{
|
||||
return path == x.path;
|
||||
}
|
||||
|
||||
bool operator != (const CanonPath & x) const
|
||||
{ return path != x.path; }
|
||||
bool operator!=(const CanonPath & x) const
|
||||
{
|
||||
return path != x.path;
|
||||
}
|
||||
|
||||
/**
|
||||
* Compare paths lexicographically except that path separators
|
||||
|
|
@ -169,16 +202,19 @@ public:
|
|||
* a directory is always followed directly by its children. For
|
||||
* instance, 'foo' < 'foo/bar' < 'foo!'.
|
||||
*/
|
||||
auto operator <=> (const CanonPath & x) const
|
||||
auto operator<=>(const CanonPath & x) const
|
||||
{
|
||||
auto i = path.begin();
|
||||
auto j = x.path.begin();
|
||||
for ( ; i != path.end() && j != x.path.end(); ++i, ++j) {
|
||||
for (; i != path.end() && j != x.path.end(); ++i, ++j) {
|
||||
auto c_i = *i;
|
||||
if (c_i == '/') c_i = 0;
|
||||
if (c_i == '/')
|
||||
c_i = 0;
|
||||
auto c_j = *j;
|
||||
if (c_j == '/') c_j = 0;
|
||||
if (auto cmp = c_i <=> c_j; cmp != 0) return cmp;
|
||||
if (c_j == '/')
|
||||
c_j = 0;
|
||||
if (auto cmp = c_i <=> c_j; cmp != 0)
|
||||
return cmp;
|
||||
}
|
||||
return (i != path.end()) <=> (j != x.path.end());
|
||||
}
|
||||
|
|
@ -199,14 +235,14 @@ public:
|
|||
/**
|
||||
* Concatenate two paths.
|
||||
*/
|
||||
CanonPath operator / (const CanonPath & x) const;
|
||||
CanonPath operator/(const CanonPath & x) const;
|
||||
|
||||
/**
|
||||
* Add a path component to this one. It must not contain any slashes.
|
||||
*/
|
||||
void push(std::string_view c);
|
||||
|
||||
CanonPath operator / (std::string_view c) const;
|
||||
CanonPath operator/(std::string_view c) const;
|
||||
|
||||
/**
|
||||
* Check whether access to this path is allowed, which is the case
|
||||
|
|
@ -225,14 +261,14 @@ public:
|
|||
friend class std::hash<CanonPath>;
|
||||
};
|
||||
|
||||
std::ostream & operator << (std::ostream & stream, const CanonPath & path);
|
||||
std::ostream & operator<<(std::ostream & stream, const CanonPath & path);
|
||||
|
||||
}
|
||||
} // namespace nix
|
||||
|
||||
template<>
|
||||
struct std::hash<nix::CanonPath>
|
||||
{
|
||||
std::size_t operator ()(const nix::CanonPath & s) const noexcept
|
||||
std::size_t operator()(const nix::CanonPath & s) const noexcept
|
||||
{
|
||||
return std::hash<std::string>{}(s.path);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -32,15 +32,18 @@ struct Checked
|
|||
T value;
|
||||
|
||||
Checked() = default;
|
||||
|
||||
explicit Checked(T const value)
|
||||
: value{value}
|
||||
{
|
||||
}
|
||||
|
||||
Checked(Checked<T> const & other) = default;
|
||||
Checked(Checked<T> && other) = default;
|
||||
Checked<T> & operator=(Checked<T> const & other) = default;
|
||||
|
||||
std::strong_ordering operator<=>(Checked<T> const & other) const = default;
|
||||
|
||||
std::strong_ordering operator<=>(T const & other) const
|
||||
{
|
||||
return value <=> other;
|
||||
|
|
@ -68,6 +71,7 @@ struct Checked
|
|||
, overflowed_{overflowed ? OverflowKind::Overflow : OverflowKind::NoOverflow}
|
||||
{
|
||||
}
|
||||
|
||||
Result(T value, OverflowKind overflowed)
|
||||
: value{value}
|
||||
, overflowed_{overflowed}
|
||||
|
|
@ -116,6 +120,7 @@ struct Checked
|
|||
{
|
||||
return (*this) + other.value;
|
||||
}
|
||||
|
||||
Result operator+(T const other) const
|
||||
{
|
||||
T result;
|
||||
|
|
@ -127,6 +132,7 @@ struct Checked
|
|||
{
|
||||
return (*this) - other.value;
|
||||
}
|
||||
|
||||
Result operator-(T const other) const
|
||||
{
|
||||
T result;
|
||||
|
|
@ -138,6 +144,7 @@ struct Checked
|
|||
{
|
||||
return (*this) * other.value;
|
||||
}
|
||||
|
||||
Result operator*(T const other) const
|
||||
{
|
||||
T result;
|
||||
|
|
@ -149,6 +156,7 @@ struct Checked
|
|||
{
|
||||
return (*this) / other.value;
|
||||
}
|
||||
|
||||
/**
|
||||
* Performs a checked division.
|
||||
*
|
||||
|
|
@ -181,4 +189,4 @@ std::ostream & operator<<(std::ostream & ios, Checked<T> v)
|
|||
return ios;
|
||||
}
|
||||
|
||||
}
|
||||
} // namespace nix::checked
|
||||
|
|
|
|||
|
|
@ -20,7 +20,8 @@ namespace nix {
|
|||
* references to its elements.
|
||||
*/
|
||||
template<typename T, size_t ChunkSize>
|
||||
class ChunkedVector {
|
||||
class ChunkedVector
|
||||
{
|
||||
private:
|
||||
uint32_t size_ = 0;
|
||||
std::vector<std::vector<T>> chunks;
|
||||
|
|
@ -45,13 +46,16 @@ public:
|
|||
addChunk();
|
||||
}
|
||||
|
||||
uint32_t size() const noexcept { return size_; }
|
||||
uint32_t size() const noexcept
|
||||
{
|
||||
return size_;
|
||||
}
|
||||
|
||||
template <typename... Args>
|
||||
template<typename... Args>
|
||||
std::pair<T &, uint32_t> add(Args &&... args)
|
||||
{
|
||||
const auto idx = size_++;
|
||||
auto & chunk = [&] () -> auto & {
|
||||
auto & chunk = [&]() -> auto & {
|
||||
if (auto & back = chunks.back(); back.size() < ChunkSize)
|
||||
return back;
|
||||
return addChunk();
|
||||
|
|
@ -78,4 +82,4 @@ public:
|
|||
fn(e);
|
||||
}
|
||||
};
|
||||
}
|
||||
} // namespace nix
|
||||
|
|
|
|||
|
|
@ -13,11 +13,7 @@ template<typename T>
|
|||
using GetEdgesAsync = std::function<void(const T &, std::function<void(std::promise<set<T>> &)>)>;
|
||||
|
||||
template<typename T>
|
||||
void computeClosure(
|
||||
const set<T> startElts,
|
||||
set<T> & res,
|
||||
GetEdgesAsync<T> getEdgesAsync
|
||||
)
|
||||
void computeClosure(const set<T> startElts, set<T> & res, GetEdgesAsync<T> getEdgesAsync)
|
||||
{
|
||||
struct State
|
||||
{
|
||||
|
|
@ -35,8 +31,10 @@ void computeClosure(
|
|||
enqueue = [&](const T & current) -> void {
|
||||
{
|
||||
auto state(state_.lock());
|
||||
if (state->exc) return;
|
||||
if (!state->res.insert(current).second) return;
|
||||
if (state->exc)
|
||||
return;
|
||||
if (!state->res.insert(current).second)
|
||||
return;
|
||||
state->pending++;
|
||||
}
|
||||
|
||||
|
|
@ -48,13 +46,16 @@ void computeClosure(
|
|||
{
|
||||
auto state(state_.lock());
|
||||
assert(state->pending);
|
||||
if (!--state->pending) done.notify_one();
|
||||
if (!--state->pending)
|
||||
done.notify_one();
|
||||
}
|
||||
} catch (...) {
|
||||
auto state(state_.lock());
|
||||
if (!state->exc) state->exc = std::current_exception();
|
||||
if (!state->exc)
|
||||
state->exc = std::current_exception();
|
||||
assert(state->pending);
|
||||
if (!--state->pending) done.notify_one();
|
||||
if (!--state->pending)
|
||||
done.notify_one();
|
||||
};
|
||||
});
|
||||
};
|
||||
|
|
@ -64,9 +65,11 @@ void computeClosure(
|
|||
|
||||
{
|
||||
auto state(state_.lock());
|
||||
while (state->pending) state.wait(done);
|
||||
if (state->exc) std::rethrow_exception(state->exc);
|
||||
while (state->pending)
|
||||
state.wait(done);
|
||||
if (state->exc)
|
||||
std::rethrow_exception(state->exc);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
} // namespace nix
|
||||
|
|
|
|||
|
|
@ -1,13 +1,14 @@
|
|||
#pragma once
|
||||
///@file
|
||||
|
||||
#define GENERATE_ONE_CMP(PRE, RET, QUAL, COMPARATOR, MY_TYPE, ...) \
|
||||
PRE RET QUAL operator COMPARATOR(const MY_TYPE & other) const noexcept { \
|
||||
__VA_OPT__(const MY_TYPE * me = this;) \
|
||||
auto fields1 = std::tie( __VA_ARGS__ ); \
|
||||
__VA_OPT__(me = &other;) \
|
||||
auto fields2 = std::tie( __VA_ARGS__ ); \
|
||||
return fields1 COMPARATOR fields2; \
|
||||
#define GENERATE_ONE_CMP(PRE, RET, QUAL, COMPARATOR, MY_TYPE, ...) \
|
||||
PRE RET QUAL operator COMPARATOR(const MY_TYPE & other) const noexcept \
|
||||
{ \
|
||||
__VA_OPT__(const MY_TYPE * me = this;) \
|
||||
auto fields1 = std::tie(__VA_ARGS__); \
|
||||
__VA_OPT__(me = &other;) \
|
||||
auto fields2 = std::tie(__VA_ARGS__); \
|
||||
return fields1 COMPARATOR fields2; \
|
||||
}
|
||||
#define GENERATE_EQUAL(prefix, qualification, my_type, args...) \
|
||||
GENERATE_ONE_CMP(prefix, bool, qualification, ==, my_type, args)
|
||||
|
|
@ -36,8 +37,8 @@
|
|||
* ```
|
||||
*/
|
||||
#define GENERATE_CMP(args...) \
|
||||
GENERATE_EQUAL(,,args) \
|
||||
GENERATE_SPACESHIP(,auto,,args)
|
||||
GENERATE_EQUAL(, , args) \
|
||||
GENERATE_SPACESHIP(, auto, , args)
|
||||
|
||||
/**
|
||||
* @param prefix This is for something before each declaration like
|
||||
|
|
@ -46,5 +47,5 @@
|
|||
* @param my_type the type are defining operators for.
|
||||
*/
|
||||
#define GENERATE_CMP_EXT(prefix, ret, my_type, args...) \
|
||||
GENERATE_EQUAL(prefix, my_type ::, my_type, args) \
|
||||
GENERATE_EQUAL(prefix, my_type ::, my_type, args) \
|
||||
GENERATE_SPACESHIP(prefix, ret, my_type ::, my_type, args)
|
||||
|
|
|
|||
|
|
@ -29,4 +29,4 @@ MakeError(UnknownCompressionMethod, Error);
|
|||
|
||||
MakeError(CompressionError, Error);
|
||||
|
||||
}
|
||||
} // namespace nix
|
||||
|
|
|
|||
|
|
@ -35,4 +35,4 @@ struct GlobalConfig : public AbstractConfig
|
|||
|
||||
extern GlobalConfig globalConfig;
|
||||
|
||||
}
|
||||
} // namespace nix
|
||||
|
|
|
|||
|
|
@ -17,19 +17,26 @@
|
|||
|
||||
namespace nix {
|
||||
|
||||
template<> struct BaseSetting<Strings>::trait
|
||||
template<>
|
||||
struct BaseSetting<Strings>::trait
|
||||
{
|
||||
static constexpr bool appendable = true;
|
||||
};
|
||||
template<> struct BaseSetting<StringSet>::trait
|
||||
|
||||
template<>
|
||||
struct BaseSetting<StringSet>::trait
|
||||
{
|
||||
static constexpr bool appendable = true;
|
||||
};
|
||||
template<> struct BaseSetting<StringMap>::trait
|
||||
|
||||
template<>
|
||||
struct BaseSetting<StringMap>::trait
|
||||
{
|
||||
static constexpr bool appendable = true;
|
||||
};
|
||||
template<> struct BaseSetting<std::set<ExperimentalFeature>>::trait
|
||||
|
||||
template<>
|
||||
struct BaseSetting<std::set<ExperimentalFeature>>::trait
|
||||
{
|
||||
static constexpr bool appendable = true;
|
||||
};
|
||||
|
|
@ -46,17 +53,19 @@ 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<std::set<ExperimentalFeature>>::appendOrSet(std::set<ExperimentalFeature> newValue, bool append);
|
||||
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<std::set<ExperimentalFeature>>::appendOrSet(std::set<ExperimentalFeature> newValue, bool append);
|
||||
|
||||
template<typename T>
|
||||
void BaseSetting<T>::appendOrSet(T newValue, bool append)
|
||||
{
|
||||
static_assert(
|
||||
!trait::appendable,
|
||||
"using default `appendOrSet` implementation with an appendable type");
|
||||
static_assert(!trait::appendable, "using default `appendOrSet` implementation with an appendable type");
|
||||
assert(!append);
|
||||
|
||||
value = std::move(newValue);
|
||||
|
|
@ -69,13 +78,15 @@ void BaseSetting<T>::set(const std::string & str, bool append)
|
|||
appendOrSet(parse(str), append);
|
||||
else {
|
||||
assert(experimentalFeature);
|
||||
warn("Ignoring setting '%s' because experimental feature '%s' is not enabled",
|
||||
warn(
|
||||
"Ignoring setting '%s' because experimental feature '%s' is not enabled",
|
||||
name,
|
||||
showExperimentalFeature(*experimentalFeature));
|
||||
}
|
||||
}
|
||||
|
||||
template<> void BaseSetting<bool>::convertToArg(Args & args, const std::string & category);
|
||||
template<>
|
||||
void BaseSetting<bool>::convertToArg(Args & args, const std::string & category);
|
||||
|
||||
template<typename T>
|
||||
void BaseSetting<T>::convertToArg(Args & args, const std::string & category)
|
||||
|
|
@ -86,7 +97,10 @@ void BaseSetting<T>::convertToArg(Args & args, const std::string & category)
|
|||
.description = fmt("Set the `%s` setting.", name),
|
||||
.category = category,
|
||||
.labels = {"value"},
|
||||
.handler = {[this](std::string s) { overridden = true; set(s); }},
|
||||
.handler = {[this](std::string s) {
|
||||
overridden = true;
|
||||
set(s);
|
||||
}},
|
||||
.experimentalFeature = experimentalFeature,
|
||||
});
|
||||
|
||||
|
|
@ -97,14 +111,19 @@ void BaseSetting<T>::convertToArg(Args & args, const std::string & category)
|
|||
.description = fmt("Append to the `%s` setting.", name),
|
||||
.category = category,
|
||||
.labels = {"value"},
|
||||
.handler = {[this](std::string s) { overridden = true; set(s, true); }},
|
||||
.handler = {[this](std::string s) {
|
||||
overridden = true;
|
||||
set(s, true);
|
||||
}},
|
||||
.experimentalFeature = experimentalFeature,
|
||||
});
|
||||
}
|
||||
|
||||
#define DECLARE_CONFIG_SERIALISER(TY) \
|
||||
template<> TY BaseSetting< TY >::parse(const std::string & str) const; \
|
||||
template<> std::string BaseSetting< TY >::to_string() const;
|
||||
#define DECLARE_CONFIG_SERIALISER(TY) \
|
||||
template<> \
|
||||
TY BaseSetting<TY>::parse(const std::string & str) const; \
|
||||
template<> \
|
||||
std::string BaseSetting<TY>::to_string() const;
|
||||
|
||||
DECLARE_CONFIG_SERIALISER(std::string)
|
||||
DECLARE_CONFIG_SERIALISER(std::optional<std::string>)
|
||||
|
|
@ -134,4 +153,4 @@ std::string BaseSetting<T>::to_string() const
|
|||
return std::to_string(value);
|
||||
}
|
||||
|
||||
}
|
||||
} // namespace nix
|
||||
|
|
|
|||
|
|
@ -247,7 +247,8 @@ protected:
|
|||
|
||||
public:
|
||||
|
||||
BaseSetting(const T & def,
|
||||
BaseSetting(
|
||||
const T & def,
|
||||
const bool documentDefault,
|
||||
const std::string & name,
|
||||
const std::string & description,
|
||||
|
|
@ -257,21 +258,58 @@ public:
|
|||
, value(def)
|
||||
, defaultValue(def)
|
||||
, documentDefault(documentDefault)
|
||||
{ }
|
||||
{
|
||||
}
|
||||
|
||||
operator const T &() const
|
||||
{
|
||||
return value;
|
||||
}
|
||||
|
||||
operator T &()
|
||||
{
|
||||
return value;
|
||||
}
|
||||
|
||||
const T & get() const
|
||||
{
|
||||
return value;
|
||||
}
|
||||
|
||||
T & get()
|
||||
{
|
||||
return value;
|
||||
}
|
||||
|
||||
operator const T &() const { return value; }
|
||||
operator T &() { return value; }
|
||||
const T & get() const { return value; }
|
||||
T & get() { return value; }
|
||||
template<typename U>
|
||||
bool operator ==(const U & v2) const { return value == v2; }
|
||||
bool operator==(const U & v2) const
|
||||
{
|
||||
return value == v2;
|
||||
}
|
||||
|
||||
template<typename U>
|
||||
bool operator !=(const U & v2) const { return value != v2; }
|
||||
bool operator!=(const U & v2) const
|
||||
{
|
||||
return value != v2;
|
||||
}
|
||||
|
||||
template<typename U>
|
||||
void operator =(const U & v) { assign(v); }
|
||||
virtual void assign(const T & v) { value = v; }
|
||||
void operator=(const U & v)
|
||||
{
|
||||
assign(v);
|
||||
}
|
||||
|
||||
virtual void assign(const T & v)
|
||||
{
|
||||
value = v;
|
||||
}
|
||||
|
||||
template<typename U>
|
||||
void setDefault(const U & v) { if (!overridden) value = v; }
|
||||
void setDefault(const U & v)
|
||||
{
|
||||
if (!overridden)
|
||||
value = v;
|
||||
}
|
||||
|
||||
/**
|
||||
* Require any experimental feature the setting depends on
|
||||
|
|
@ -307,19 +345,23 @@ public:
|
|||
};
|
||||
|
||||
template<typename T>
|
||||
std::ostream & operator <<(std::ostream & str, const BaseSetting<T> & opt)
|
||||
std::ostream & operator<<(std::ostream & str, const BaseSetting<T> & opt)
|
||||
{
|
||||
return str << static_cast<const T &>(opt);
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
bool operator ==(const T & v1, const BaseSetting<T> & v2) { return v1 == static_cast<const T &>(v2); }
|
||||
bool operator==(const T & v1, const BaseSetting<T> & v2)
|
||||
{
|
||||
return v1 == static_cast<const T &>(v2);
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
class Setting : public BaseSetting<T>
|
||||
{
|
||||
public:
|
||||
Setting(Config * options,
|
||||
Setting(
|
||||
Config * options,
|
||||
const T & def,
|
||||
const std::string & name,
|
||||
const std::string & description,
|
||||
|
|
@ -331,7 +373,10 @@ public:
|
|||
options->addSetting(this);
|
||||
}
|
||||
|
||||
void operator =(const T & v) { this->assign(v); }
|
||||
void operator=(const T & v)
|
||||
{
|
||||
this->assign(v);
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
|
|
@ -345,7 +390,8 @@ class PathSetting : public BaseSetting<Path>
|
|||
{
|
||||
public:
|
||||
|
||||
PathSetting(Config * options,
|
||||
PathSetting(
|
||||
Config * options,
|
||||
const Path & def,
|
||||
const std::string & name,
|
||||
const std::string & description,
|
||||
|
|
@ -353,9 +399,15 @@ public:
|
|||
|
||||
Path parse(const std::string & str) const override;
|
||||
|
||||
Path operator +(const char * p) const { return value + p; }
|
||||
Path operator+(const char * p) const
|
||||
{
|
||||
return value + p;
|
||||
}
|
||||
|
||||
void operator =(const Path & v) { this->assign(v); }
|
||||
void operator=(const Path & v)
|
||||
{
|
||||
this->assign(v);
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
|
|
@ -367,7 +419,8 @@ class OptionalPathSetting : public BaseSetting<std::optional<Path>>
|
|||
{
|
||||
public:
|
||||
|
||||
OptionalPathSetting(Config * options,
|
||||
OptionalPathSetting(
|
||||
Config * options,
|
||||
const std::optional<Path> & def,
|
||||
const std::string & name,
|
||||
const std::string & description,
|
||||
|
|
@ -375,14 +428,16 @@ public:
|
|||
|
||||
std::optional<Path> parse(const std::string & str) const override;
|
||||
|
||||
void operator =(const std::optional<Path> & v);
|
||||
void operator=(const std::optional<Path> & v);
|
||||
};
|
||||
|
||||
|
||||
struct ExperimentalFeatureSettings : Config {
|
||||
struct ExperimentalFeatureSettings : Config
|
||||
{
|
||||
|
||||
Setting<std::set<ExperimentalFeature>> experimentalFeatures{
|
||||
this, {}, "experimental-features",
|
||||
this,
|
||||
{},
|
||||
"experimental-features",
|
||||
R"(
|
||||
Experimental features that are enabled.
|
||||
|
||||
|
|
@ -426,4 +481,4 @@ struct ExperimentalFeatureSettings : Config {
|
|||
// FIXME: don't use a global variable.
|
||||
extern ExperimentalFeatureSettings experimentalFeatureSettings;
|
||||
|
||||
}
|
||||
} // namespace nix
|
||||
|
|
|
|||
|
|
@ -4,7 +4,7 @@
|
|||
#include <optional>
|
||||
|
||||
#ifndef _WIN32
|
||||
# include <sys/resource.h>
|
||||
# include <sys/resource.h>
|
||||
#endif
|
||||
|
||||
#include "nix/util/types.hh"
|
||||
|
|
@ -38,4 +38,4 @@ void restoreProcessContext(bool restoreMounts = true);
|
|||
*/
|
||||
std::optional<Path> getSelfExe();
|
||||
|
||||
}
|
||||
} // namespace nix
|
||||
|
|
|
|||
|
|
@ -9,10 +9,7 @@ namespace nix {
|
|||
*
|
||||
* If `count == 1`, prints `1 {single}` to `output`, otherwise prints `{count} {plural}`.
|
||||
*/
|
||||
std::ostream & pluralize(
|
||||
std::ostream & output,
|
||||
unsigned int count,
|
||||
const std::string_view single,
|
||||
const std::string_view plural);
|
||||
std::ostream &
|
||||
pluralize(std::ostream & output, unsigned int count, const std::string_view single, const std::string_view plural);
|
||||
|
||||
}
|
||||
} // namespace nix
|
||||
|
|
|
|||
|
|
@ -66,4 +66,4 @@ void clearEnv();
|
|||
*/
|
||||
void replaceEnv(const StringMap & newEnv);
|
||||
|
||||
}
|
||||
} // namespace nix
|
||||
|
|
|
|||
|
|
@ -29,22 +29,13 @@
|
|||
|
||||
namespace nix {
|
||||
|
||||
|
||||
typedef enum {
|
||||
lvlError = 0,
|
||||
lvlWarn,
|
||||
lvlNotice,
|
||||
lvlInfo,
|
||||
lvlTalkative,
|
||||
lvlChatty,
|
||||
lvlDebug,
|
||||
lvlVomit
|
||||
} Verbosity;
|
||||
typedef enum { lvlError = 0, lvlWarn, lvlNotice, lvlInfo, lvlTalkative, lvlChatty, lvlDebug, lvlVomit } Verbosity;
|
||||
|
||||
/**
|
||||
* The lines of code surrounding an error.
|
||||
*/
|
||||
struct LinesOfCode {
|
||||
struct LinesOfCode
|
||||
{
|
||||
std::optional<std::string> prevLineOfCode;
|
||||
std::optional<std::string> errLineOfCode;
|
||||
std::optional<std::string> nextLineOfCode;
|
||||
|
|
@ -60,10 +51,7 @@ struct LinesOfCode {
|
|||
4feb7d9f71? */
|
||||
struct Pos;
|
||||
|
||||
void printCodeLines(std::ostream & out,
|
||||
const std::string & prefix,
|
||||
const Pos & errPos,
|
||||
const LinesOfCode & loc);
|
||||
void printCodeLines(std::ostream & out, const std::string & prefix, const Pos & errPos, const LinesOfCode & loc);
|
||||
|
||||
/**
|
||||
* When a stack frame is printed.
|
||||
|
|
@ -77,15 +65,17 @@ enum struct TracePrint {
|
|||
Always,
|
||||
};
|
||||
|
||||
struct Trace {
|
||||
struct Trace
|
||||
{
|
||||
std::shared_ptr<const Pos> pos;
|
||||
HintFmt hint;
|
||||
TracePrint print = TracePrint::Default;
|
||||
};
|
||||
|
||||
inline std::strong_ordering operator<=>(const Trace& lhs, const Trace& rhs);
|
||||
inline std::strong_ordering operator<=>(const Trace & lhs, const Trace & rhs);
|
||||
|
||||
struct ErrorInfo {
|
||||
struct ErrorInfo
|
||||
{
|
||||
Verbosity level;
|
||||
HintFmt msg;
|
||||
std::shared_ptr<const Pos> pos;
|
||||
|
|
@ -128,51 +118,71 @@ protected:
|
|||
|
||||
public:
|
||||
BaseError(const BaseError &) = default;
|
||||
BaseError& operator=(const BaseError &) = default;
|
||||
BaseError& operator=(BaseError &&) = default;
|
||||
BaseError & operator=(const BaseError &) = default;
|
||||
BaseError & operator=(BaseError &&) = default;
|
||||
|
||||
template<typename... Args>
|
||||
BaseError(unsigned int status, const Args & ... args)
|
||||
: err { .level = lvlError, .msg = HintFmt(args...), .status = status }
|
||||
{ }
|
||||
BaseError(unsigned int status, const Args &... args)
|
||||
: err{.level = lvlError, .msg = HintFmt(args...), .status = status}
|
||||
{
|
||||
}
|
||||
|
||||
template<typename... Args>
|
||||
explicit BaseError(const std::string & fs, const Args & ... args)
|
||||
: err { .level = lvlError, .msg = HintFmt(fs, args...) }
|
||||
{ }
|
||||
explicit BaseError(const std::string & fs, const Args &... args)
|
||||
: err{.level = lvlError, .msg = HintFmt(fs, args...)}
|
||||
{
|
||||
}
|
||||
|
||||
template<typename... Args>
|
||||
BaseError(const Suggestions & sug, const Args & ... args)
|
||||
: err { .level = lvlError, .msg = HintFmt(args...), .suggestions = sug }
|
||||
{ }
|
||||
BaseError(const Suggestions & sug, const Args &... args)
|
||||
: err{.level = lvlError, .msg = HintFmt(args...), .suggestions = sug}
|
||||
{
|
||||
}
|
||||
|
||||
BaseError(HintFmt hint)
|
||||
: err { .level = lvlError, .msg = hint }
|
||||
{ }
|
||||
: err{.level = lvlError, .msg = hint}
|
||||
{
|
||||
}
|
||||
|
||||
BaseError(ErrorInfo && e)
|
||||
: err(std::move(e))
|
||||
{ }
|
||||
{
|
||||
}
|
||||
|
||||
BaseError(const ErrorInfo & e)
|
||||
: err(e)
|
||||
{ }
|
||||
{
|
||||
}
|
||||
|
||||
/** The error message without "error: " prefixed to it. */
|
||||
std::string message() {
|
||||
std::string message()
|
||||
{
|
||||
return err.msg.str();
|
||||
}
|
||||
|
||||
const char * what() const noexcept override { return calcWhat().c_str(); }
|
||||
const std::string & msg() const { return calcWhat(); }
|
||||
const ErrorInfo & info() const { calcWhat(); return err; }
|
||||
const char * what() const noexcept override
|
||||
{
|
||||
return calcWhat().c_str();
|
||||
}
|
||||
|
||||
const std::string & msg() const
|
||||
{
|
||||
return calcWhat();
|
||||
}
|
||||
|
||||
const ErrorInfo & info() const
|
||||
{
|
||||
calcWhat();
|
||||
return err;
|
||||
}
|
||||
|
||||
void withExitStatus(unsigned int status)
|
||||
{
|
||||
err.status = status;
|
||||
}
|
||||
|
||||
void atPos(std::shared_ptr<const Pos> pos) {
|
||||
void atPos(std::shared_ptr<const Pos> pos)
|
||||
{
|
||||
err.pos = pos;
|
||||
}
|
||||
|
||||
|
|
@ -182,23 +192,29 @@ public:
|
|||
}
|
||||
|
||||
template<typename... Args>
|
||||
void addTrace(std::shared_ptr<const Pos> && e, std::string_view fs, const Args & ... args)
|
||||
void addTrace(std::shared_ptr<const Pos> && e, std::string_view fs, const Args &... args)
|
||||
{
|
||||
addTrace(std::move(e), HintFmt(std::string(fs), args...));
|
||||
}
|
||||
|
||||
void addTrace(std::shared_ptr<const Pos> && e, HintFmt hint, TracePrint print = TracePrint::Default);
|
||||
|
||||
bool hasTrace() const { return !err.traces.empty(); }
|
||||
bool hasTrace() const
|
||||
{
|
||||
return !err.traces.empty();
|
||||
}
|
||||
|
||||
const ErrorInfo & info() { return err; };
|
||||
const ErrorInfo & info()
|
||||
{
|
||||
return err;
|
||||
};
|
||||
};
|
||||
|
||||
#define MakeError(newClass, superClass) \
|
||||
class newClass : public superClass \
|
||||
{ \
|
||||
public: \
|
||||
using superClass::superClass; \
|
||||
class newClass : public superClass \
|
||||
{ \
|
||||
public: \
|
||||
using superClass::superClass; \
|
||||
}
|
||||
|
||||
MakeError(Error, BaseError);
|
||||
|
|
@ -236,8 +252,9 @@ public:
|
|||
* will be used to try to add additional information to the message.
|
||||
*/
|
||||
template<typename... Args>
|
||||
SysError(int errNo, const Args & ... args)
|
||||
: SystemError(""), errNo(errNo)
|
||||
SysError(int errNo, const Args &... args)
|
||||
: SystemError("")
|
||||
, errNo(errNo)
|
||||
{
|
||||
auto hf = HintFmt(args...);
|
||||
err.msg = HintFmt("%1%: %2%", Uncolored(hf.str()), strerror(errNo));
|
||||
|
|
@ -250,15 +267,15 @@ public:
|
|||
* calling this constructor!
|
||||
*/
|
||||
template<typename... Args>
|
||||
SysError(const Args & ... args)
|
||||
: SysError(errno, args ...)
|
||||
SysError(const Args &... args)
|
||||
: SysError(errno, args...)
|
||||
{
|
||||
}
|
||||
};
|
||||
|
||||
#ifdef _WIN32
|
||||
namespace windows {
|
||||
class WinError;
|
||||
class WinError;
|
||||
}
|
||||
#endif
|
||||
|
||||
|
|
@ -301,4 +318,4 @@ void panic(const char * file, int line, const char * func);
|
|||
*/
|
||||
#define unreachable() (::nix::panic(__FILE__, __LINE__, __func__))
|
||||
|
||||
}
|
||||
} // namespace nix
|
||||
|
|
|
|||
|
|
@ -12,4 +12,4 @@ namespace nix {
|
|||
*/
|
||||
int execvpe(const OsChar * file0, const OsChar * const argv[], const OsChar * const envp[]);
|
||||
|
||||
}
|
||||
} // namespace nix
|
||||
|
|
|
|||
|
|
@ -11,9 +11,18 @@ class Exit : public std::exception
|
|||
{
|
||||
public:
|
||||
int status;
|
||||
Exit() : status(0) { }
|
||||
explicit Exit(int status) : status(status) { }
|
||||
|
||||
Exit()
|
||||
: status(0)
|
||||
{
|
||||
}
|
||||
|
||||
explicit Exit(int status)
|
||||
: status(status)
|
||||
{
|
||||
}
|
||||
|
||||
virtual ~Exit();
|
||||
};
|
||||
|
||||
}
|
||||
} // namespace nix
|
||||
|
|
|
|||
|
|
@ -15,8 +15,7 @@ namespace nix {
|
|||
* their string representation and documentation in the corresponding
|
||||
* `.cc` file as well.
|
||||
*/
|
||||
enum struct ExperimentalFeature
|
||||
{
|
||||
enum struct ExperimentalFeature {
|
||||
CaDerivations,
|
||||
ImpureDerivations,
|
||||
Flakes,
|
||||
|
|
@ -49,8 +48,7 @@ using Xp = ExperimentalFeature;
|
|||
* Parse an experimental feature (enum value) from its name. Experimental
|
||||
* feature flag names are hyphenated and do not contain spaces.
|
||||
*/
|
||||
const std::optional<ExperimentalFeature> parseExperimentalFeature(
|
||||
const std::string_view & name);
|
||||
const std::optional<ExperimentalFeature> parseExperimentalFeature(const std::string_view & name);
|
||||
|
||||
/**
|
||||
* Show the name of an experimental feature. This is the opposite of
|
||||
|
|
@ -68,9 +66,7 @@ nlohmann::json documentExperimentalFeatures();
|
|||
/**
|
||||
* Shorthand for `str << showExperimentalFeature(feature)`.
|
||||
*/
|
||||
std::ostream & operator<<(
|
||||
std::ostream & str,
|
||||
const ExperimentalFeature & feature);
|
||||
std::ostream & operator<<(std::ostream & str, const ExperimentalFeature & feature);
|
||||
|
||||
/**
|
||||
* Parse a set of strings to the corresponding set of experimental
|
||||
|
|
@ -100,4 +96,4 @@ public:
|
|||
void to_json(nlohmann::json &, const ExperimentalFeature &);
|
||||
void from_json(const nlohmann::json &, ExperimentalFeature &);
|
||||
|
||||
}
|
||||
} // namespace nix
|
||||
|
|
|
|||
|
|
@ -57,22 +57,14 @@ std::string_view renderFileSerialisationMethod(FileSerialisationMethod method);
|
|||
* Dump a serialization of the given file system object.
|
||||
*/
|
||||
void dumpPath(
|
||||
const SourcePath & path,
|
||||
Sink & sink,
|
||||
FileSerialisationMethod method,
|
||||
PathFilter & filter = defaultPathFilter);
|
||||
const SourcePath & path, Sink & sink, FileSerialisationMethod method, PathFilter & filter = defaultPathFilter);
|
||||
|
||||
/**
|
||||
* Restore a serialisation of the given file system object.
|
||||
*
|
||||
* \todo use an arbitrary `FileSystemObjectSink`.
|
||||
*/
|
||||
void restorePath(
|
||||
const Path & path,
|
||||
Source & source,
|
||||
FileSerialisationMethod method,
|
||||
bool startFsync = false);
|
||||
|
||||
void restorePath(const Path & path, Source & source, FileSerialisationMethod method, bool startFsync = false);
|
||||
|
||||
/**
|
||||
* Compute the hash of the given file system object according to the
|
||||
|
|
@ -85,9 +77,7 @@ void restorePath(
|
|||
* ```
|
||||
*/
|
||||
HashResult hashPath(
|
||||
const SourcePath & path,
|
||||
FileSerialisationMethod method, HashAlgorithm ha,
|
||||
PathFilter & filter = defaultPathFilter);
|
||||
const SourcePath & path, FileSerialisationMethod method, HashAlgorithm ha, PathFilter & filter = defaultPathFilter);
|
||||
|
||||
/**
|
||||
* An enumeration of the ways we can ingest file system
|
||||
|
|
@ -153,8 +143,6 @@ std::string_view renderFileIngestionMethod(FileIngestionMethod method);
|
|||
* useful defined for a merkle format.
|
||||
*/
|
||||
std::pair<Hash, std::optional<uint64_t>> hashPath(
|
||||
const SourcePath & path,
|
||||
FileIngestionMethod method, HashAlgorithm ha,
|
||||
PathFilter & filter = defaultPathFilter);
|
||||
const SourcePath & path, FileIngestionMethod method, HashAlgorithm ha, PathFilter & filter = defaultPathFilter);
|
||||
|
||||
}
|
||||
} // namespace nix
|
||||
|
|
|
|||
|
|
@ -5,8 +5,8 @@
|
|||
#include "nix/util/error.hh"
|
||||
|
||||
#ifdef _WIN32
|
||||
# define WIN32_LEAN_AND_MEAN
|
||||
# include <windows.h>
|
||||
# define WIN32_LEAN_AND_MEAN
|
||||
# include <windows.h>
|
||||
#endif
|
||||
|
||||
namespace nix {
|
||||
|
|
@ -93,18 +93,19 @@ void writeLine(Descriptor fd, std::string s);
|
|||
/**
|
||||
* Read a file descriptor until EOF occurs.
|
||||
*/
|
||||
std::string drainFD(Descriptor fd, bool block = true, const size_t reserveSize=0);
|
||||
std::string drainFD(Descriptor fd, bool block = true, const size_t reserveSize = 0);
|
||||
|
||||
/**
|
||||
* The Windows version is always blocking.
|
||||
*/
|
||||
void drainFD(
|
||||
Descriptor fd
|
||||
, Sink & sink
|
||||
Descriptor fd,
|
||||
Sink & sink
|
||||
#ifndef _WIN32
|
||||
, bool block = true
|
||||
,
|
||||
bool block = true
|
||||
#endif
|
||||
);
|
||||
);
|
||||
|
||||
/**
|
||||
* Get [Standard Input](https://en.wikipedia.org/wiki/Standard_streams#Standard_input_(stdin))
|
||||
|
|
@ -155,10 +156,10 @@ public:
|
|||
AutoCloseFD();
|
||||
AutoCloseFD(Descriptor fd);
|
||||
AutoCloseFD(const AutoCloseFD & fd) = delete;
|
||||
AutoCloseFD(AutoCloseFD&& fd) noexcept;
|
||||
AutoCloseFD(AutoCloseFD && fd) noexcept;
|
||||
~AutoCloseFD();
|
||||
AutoCloseFD& operator =(const AutoCloseFD & fd) = delete;
|
||||
AutoCloseFD& operator =(AutoCloseFD&& fd);
|
||||
AutoCloseFD & operator=(const AutoCloseFD & fd) = delete;
|
||||
AutoCloseFD & operator=(AutoCloseFD && fd);
|
||||
Descriptor get() const;
|
||||
explicit operator bool() const;
|
||||
Descriptor release();
|
||||
|
|
@ -213,4 +214,4 @@ std::wstring handleToFileName(Descriptor handle);
|
|||
|
||||
MakeError(EndOfFile, Error);
|
||||
|
||||
}
|
||||
} // namespace nix
|
||||
|
|
|
|||
|
|
@ -42,7 +42,6 @@ struct UnixPathTrait
|
|||
}
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Windows-style path primitives.
|
||||
*
|
||||
|
|
@ -75,22 +74,17 @@ struct WindowsPathTrait
|
|||
{
|
||||
size_t p1 = path.find('/', from);
|
||||
size_t p2 = path.find(preferredSep, from);
|
||||
return p1 == String::npos ? p2 :
|
||||
p2 == String::npos ? p1 :
|
||||
std::min(p1, p2);
|
||||
return p1 == String::npos ? p2 : p2 == String::npos ? p1 : std::min(p1, p2);
|
||||
}
|
||||
|
||||
static size_t rfindPathSep(StringView path, size_t from = String::npos)
|
||||
{
|
||||
size_t p1 = path.rfind('/', from);
|
||||
size_t p2 = path.rfind(preferredSep, from);
|
||||
return p1 == String::npos ? p2 :
|
||||
p2 == String::npos ? p1 :
|
||||
std::max(p1, p2);
|
||||
return p1 == String::npos ? p2 : p2 == String::npos ? p1 : std::max(p1, p2);
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
template<typename CharT>
|
||||
using OsPathTrait =
|
||||
#ifdef _WIN32
|
||||
|
|
@ -100,7 +94,6 @@ using OsPathTrait =
|
|||
#endif
|
||||
;
|
||||
|
||||
|
||||
/**
|
||||
* Core pure path canonicalization algorithm.
|
||||
*
|
||||
|
|
@ -116,9 +109,7 @@ using OsPathTrait =
|
|||
* "result" points to a symlink.
|
||||
*/
|
||||
template<class PathDict>
|
||||
typename PathDict::String canonPathInner(
|
||||
typename PathDict::StringView remaining,
|
||||
auto && hookComponent)
|
||||
typename PathDict::String canonPathInner(typename PathDict::StringView remaining, auto && hookComponent)
|
||||
{
|
||||
assert(remaining != "");
|
||||
|
||||
|
|
@ -131,7 +122,8 @@ typename PathDict::String canonPathInner(
|
|||
while (!remaining.empty() && PathDict::isPathSep(remaining[0]))
|
||||
remaining.remove_prefix(1);
|
||||
|
||||
if (remaining.empty()) break;
|
||||
if (remaining.empty())
|
||||
break;
|
||||
|
||||
auto nextComp = ({
|
||||
auto nextPathSep = PathDict::findPathSep(remaining);
|
||||
|
|
@ -143,9 +135,9 @@ typename PathDict::String canonPathInner(
|
|||
remaining.remove_prefix(1);
|
||||
|
||||
/* If `..', delete the last component. */
|
||||
else if (nextComp == "..")
|
||||
{
|
||||
if (!result.empty()) result.erase(PathDict::rfindPathSep(result));
|
||||
else if (nextComp == "..") {
|
||||
if (!result.empty())
|
||||
result.erase(PathDict::rfindPathSep(result));
|
||||
remaining.remove_prefix(2);
|
||||
}
|
||||
|
||||
|
|
@ -165,9 +157,9 @@ typename PathDict::String canonPathInner(
|
|||
}
|
||||
|
||||
if (result.empty())
|
||||
result = typename PathDict::String { PathDict::preferredSep };
|
||||
result = typename PathDict::String{PathDict::preferredSep};
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
}
|
||||
} // namespace nix
|
||||
|
|
|
|||
|
|
@ -30,18 +30,27 @@ struct PathViewNG : OsStringView
|
|||
|
||||
PathViewNG(const std::filesystem::path & path)
|
||||
: OsStringView{path.native()}
|
||||
{ }
|
||||
{
|
||||
}
|
||||
|
||||
PathViewNG(const OsString & path)
|
||||
: OsStringView{path}
|
||||
{ }
|
||||
{
|
||||
}
|
||||
|
||||
const string_view & native() const { return *this; }
|
||||
string_view & native() { return *this; }
|
||||
const string_view & native() const
|
||||
{
|
||||
return *this;
|
||||
}
|
||||
|
||||
string_view & native()
|
||||
{
|
||||
return *this;
|
||||
}
|
||||
};
|
||||
|
||||
std::optional<std::filesystem::path> maybePath(PathView path);
|
||||
|
||||
std::filesystem::path pathNG(PathView path);
|
||||
|
||||
}
|
||||
} // namespace nix
|
||||
|
|
|
|||
|
|
@ -14,7 +14,7 @@
|
|||
#include <dirent.h>
|
||||
#include <unistd.h>
|
||||
#ifdef _WIN32
|
||||
# include <windef.h>
|
||||
# include <windef.h>
|
||||
#endif
|
||||
|
||||
#include <functional>
|
||||
|
|
@ -28,7 +28,7 @@
|
|||
* @todo get rid of this, and stop using `stat` when we want `lstat` too.
|
||||
*/
|
||||
#ifndef S_ISLNK
|
||||
# define S_ISLNK(m) false
|
||||
# define S_ISLNK(m) false
|
||||
#endif
|
||||
|
||||
namespace nix {
|
||||
|
|
@ -48,19 +48,14 @@ bool isAbsolute(PathView path);
|
|||
*
|
||||
* In the process of being deprecated for `std::filesystem::absolute`.
|
||||
*/
|
||||
Path absPath(PathView path,
|
||||
std::optional<PathView> dir = {},
|
||||
bool resolveSymlinks = false);
|
||||
Path absPath(PathView path, std::optional<PathView> dir = {}, bool resolveSymlinks = false);
|
||||
|
||||
inline Path absPath(const Path & path,
|
||||
std::optional<PathView> dir = {},
|
||||
bool resolveSymlinks = false)
|
||||
inline Path absPath(const Path & path, std::optional<PathView> dir = {}, bool resolveSymlinks = false)
|
||||
{
|
||||
return absPath(PathView{path}, dir, resolveSymlinks);
|
||||
}
|
||||
|
||||
std::filesystem::path absPath(const std::filesystem::path & path,
|
||||
bool resolveSymlinks = false);
|
||||
std::filesystem::path absPath(const std::filesystem::path & path, bool resolveSymlinks = false);
|
||||
|
||||
/**
|
||||
* Canonicalise a path by removing all `.` or `..` components and
|
||||
|
|
@ -176,19 +171,22 @@ enum struct FsSync { Yes, No };
|
|||
*/
|
||||
void writeFile(const Path & path, std::string_view s, mode_t mode = 0666, FsSync sync = FsSync::No);
|
||||
|
||||
static inline void writeFile(const std::filesystem::path & path, std::string_view s, mode_t mode = 0666, FsSync sync = FsSync::No)
|
||||
static inline void
|
||||
writeFile(const std::filesystem::path & path, std::string_view s, mode_t mode = 0666, FsSync sync = FsSync::No)
|
||||
{
|
||||
return writeFile(path.string(), s, mode, sync);
|
||||
}
|
||||
|
||||
void writeFile(const Path & path, Source & source, mode_t mode = 0666, FsSync sync = FsSync::No);
|
||||
|
||||
static inline void writeFile(const std::filesystem::path & path, Source & source, mode_t mode = 0666, FsSync sync = FsSync::No)
|
||||
static inline void
|
||||
writeFile(const std::filesystem::path & path, Source & source, mode_t mode = 0666, FsSync sync = FsSync::No)
|
||||
{
|
||||
return writeFile(path.string(), source, mode, sync);
|
||||
}
|
||||
|
||||
void writeFile(AutoCloseFD & fd, const Path & origPath, std::string_view s, mode_t mode = 0666, FsSync sync = FsSync::No);
|
||||
void writeFile(
|
||||
AutoCloseFD & fd, const Path & origPath, std::string_view s, mode_t mode = 0666, FsSync sync = FsSync::No);
|
||||
|
||||
/**
|
||||
* Flush a path's parent directory to disk.
|
||||
|
|
@ -295,29 +293,41 @@ public:
|
|||
|
||||
void reset(const std::filesystem::path & p, bool recursive = true);
|
||||
|
||||
const std::filesystem::path & path() const { return _path; }
|
||||
PathViewNG view() const { return _path; }
|
||||
const std::filesystem::path & path() const
|
||||
{
|
||||
return _path;
|
||||
}
|
||||
|
||||
operator const std::filesystem::path & () const { return _path; }
|
||||
operator PathViewNG () const { return _path; }
|
||||
PathViewNG view() const
|
||||
{
|
||||
return _path;
|
||||
}
|
||||
|
||||
operator const std::filesystem::path &() const
|
||||
{
|
||||
return _path;
|
||||
}
|
||||
|
||||
operator PathViewNG() const
|
||||
{
|
||||
return _path;
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
struct DIRDeleter
|
||||
{
|
||||
void operator()(DIR * dir) const {
|
||||
void operator()(DIR * dir) const
|
||||
{
|
||||
closedir(dir);
|
||||
}
|
||||
};
|
||||
|
||||
typedef std::unique_ptr<DIR, DIRDeleter> AutoCloseDir;
|
||||
|
||||
|
||||
/**
|
||||
* Create a temporary directory.
|
||||
*/
|
||||
Path createTempDir(const Path & tmpRoot = "", const Path & prefix = "nix",
|
||||
mode_t mode = 0755);
|
||||
Path createTempDir(const Path & tmpRoot = "", const Path & prefix = "nix", mode_t mode = 0755);
|
||||
|
||||
/**
|
||||
* Create a temporary file, returning a file handle and its path.
|
||||
|
|
@ -367,59 +377,71 @@ extern PathFilter defaultPathFilter;
|
|||
bool chmodIfNeeded(const std::filesystem::path & path, mode_t mode, mode_t mask = S_IRWXU | S_IRWXG | S_IRWXO);
|
||||
|
||||
/**
|
||||
* @brief A directory iterator that can be used to iterate over the
|
||||
* contents of a directory. It is similar to std::filesystem::directory_iterator
|
||||
* but throws NixError on failure instead of std::filesystem::filesystem_error.
|
||||
*/
|
||||
class DirectoryIterator {
|
||||
* @brief A directory iterator that can be used to iterate over the
|
||||
* contents of a directory. It is similar to std::filesystem::directory_iterator
|
||||
* but throws NixError on failure instead of std::filesystem::filesystem_error.
|
||||
*/
|
||||
class DirectoryIterator
|
||||
{
|
||||
public:
|
||||
// --- Iterator Traits ---
|
||||
using iterator_category = std::input_iterator_tag;
|
||||
using value_type = std::filesystem::directory_entry;
|
||||
using difference_type = std::ptrdiff_t;
|
||||
using pointer = const std::filesystem::directory_entry*;
|
||||
using reference = const std::filesystem::directory_entry&;
|
||||
using value_type = std::filesystem::directory_entry;
|
||||
using difference_type = std::ptrdiff_t;
|
||||
using pointer = const std::filesystem::directory_entry *;
|
||||
using reference = const std::filesystem::directory_entry &;
|
||||
|
||||
// Default constructor (represents end iterator)
|
||||
DirectoryIterator() noexcept = default;
|
||||
|
||||
// Constructor taking a path
|
||||
explicit DirectoryIterator(const std::filesystem::path& p);
|
||||
explicit DirectoryIterator(const std::filesystem::path & p);
|
||||
|
||||
reference operator*() const {
|
||||
reference operator*() const
|
||||
{
|
||||
// Accessing the value itself doesn't typically throw filesystem_error
|
||||
// after successful construction/increment, but underlying operations might.
|
||||
// If directory_entry methods called via -> could throw, add try-catch there.
|
||||
return *it_;
|
||||
}
|
||||
|
||||
pointer operator->() const {
|
||||
pointer operator->() const
|
||||
{
|
||||
return &(*it_);
|
||||
}
|
||||
|
||||
|
||||
DirectoryIterator& operator++();
|
||||
DirectoryIterator & operator++();
|
||||
|
||||
// Postfix increment operator
|
||||
DirectoryIterator operator++(int) {
|
||||
DirectoryIterator operator++(int)
|
||||
{
|
||||
DirectoryIterator temp = *this;
|
||||
++(*this); // Uses the prefix increment's try-catch logic
|
||||
return temp;
|
||||
}
|
||||
|
||||
// Equality comparison
|
||||
friend bool operator==(const DirectoryIterator& a, const DirectoryIterator& b) noexcept {
|
||||
friend bool operator==(const DirectoryIterator & a, const DirectoryIterator & b) noexcept
|
||||
{
|
||||
return a.it_ == b.it_;
|
||||
}
|
||||
|
||||
// Inequality comparison
|
||||
friend bool operator!=(const DirectoryIterator& a, const DirectoryIterator& b) noexcept {
|
||||
friend bool operator!=(const DirectoryIterator & a, const DirectoryIterator & b) noexcept
|
||||
{
|
||||
return !(a == b);
|
||||
}
|
||||
|
||||
// Allow direct use in range-based for loops if iterating over an instance
|
||||
DirectoryIterator begin() const { return *this; }
|
||||
DirectoryIterator end() const { return DirectoryIterator{}; }
|
||||
DirectoryIterator begin() const
|
||||
{
|
||||
return *this;
|
||||
}
|
||||
|
||||
DirectoryIterator end() const
|
||||
{
|
||||
return DirectoryIterator{};
|
||||
}
|
||||
|
||||
|
||||
private:
|
||||
|
|
@ -432,11 +454,11 @@ class AutoUnmount
|
|||
Path path;
|
||||
bool del;
|
||||
public:
|
||||
AutoUnmount(Path&);
|
||||
AutoUnmount(Path &);
|
||||
AutoUnmount();
|
||||
~AutoUnmount();
|
||||
void cancel();
|
||||
};
|
||||
#endif
|
||||
|
||||
}
|
||||
} // namespace nix
|
||||
|
|
|
|||
|
|
@ -16,10 +16,15 @@ private:
|
|||
bool movedFrom = false;
|
||||
|
||||
public:
|
||||
Finally(Fn fun) : fun(std::move(fun)) { }
|
||||
Finally(Fn fun)
|
||||
: fun(std::move(fun))
|
||||
{
|
||||
}
|
||||
|
||||
// Copying Finallys is definitely not a good idea and will cause them to be
|
||||
// called twice.
|
||||
Finally(Finally &other) = delete;
|
||||
Finally(Finally & other) = delete;
|
||||
|
||||
// NOTE: Move constructor can be nothrow if the callable type is itself nothrow
|
||||
// move-constructible.
|
||||
Finally(Finally && other) noexcept(std::is_nothrow_move_constructible_v<Fn>)
|
||||
|
|
@ -27,6 +32,7 @@ public:
|
|||
{
|
||||
other.movedFrom = true;
|
||||
}
|
||||
|
||||
~Finally() noexcept(false)
|
||||
{
|
||||
try {
|
||||
|
|
|
|||
|
|
@ -5,7 +5,6 @@
|
|||
#include <string>
|
||||
#include "nix/util/ansicolor.hh"
|
||||
|
||||
|
||||
namespace nix {
|
||||
|
||||
/**
|
||||
|
|
@ -22,10 +21,11 @@ namespace nix {
|
|||
*/
|
||||
template<class F>
|
||||
inline void formatHelper(F & f)
|
||||
{ }
|
||||
{
|
||||
}
|
||||
|
||||
template<class F, typename T, typename... Args>
|
||||
inline void formatHelper(F & f, const T & x, const Args & ... args)
|
||||
inline void formatHelper(F & f, const T & x, const Args &... args)
|
||||
{
|
||||
// Interpolate one argument and then recurse.
|
||||
formatHelper(f % x, args...);
|
||||
|
|
@ -36,10 +36,7 @@ inline void formatHelper(F & f, const T & x, const Args & ... args)
|
|||
*/
|
||||
inline void setExceptions(boost::format & fmt)
|
||||
{
|
||||
fmt.exceptions(
|
||||
boost::io::all_error_bits ^
|
||||
boost::io::too_many_args_bit ^
|
||||
boost::io::too_few_args_bit);
|
||||
fmt.exceptions(boost::io::all_error_bits ^ boost::io::too_many_args_bit ^ boost::io::too_few_args_bit);
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
@ -80,7 +77,7 @@ inline std::string fmt(const char * s)
|
|||
}
|
||||
|
||||
template<typename... Args>
|
||||
inline std::string fmt(const std::string & fs, const Args & ... args)
|
||||
inline std::string fmt(const std::string & fs, const Args &... args)
|
||||
{
|
||||
boost::format f(fs);
|
||||
setExceptions(f);
|
||||
|
|
@ -95,14 +92,18 @@ inline std::string fmt(const std::string & fs, const Args & ... args)
|
|||
* either wrap the argument in `Uncolored` or add a specialization of
|
||||
* `HintFmt::operator%`.
|
||||
*/
|
||||
template <class T>
|
||||
template<class T>
|
||||
struct Magenta
|
||||
{
|
||||
Magenta(const T &s) : value(s) {}
|
||||
Magenta(const T & s)
|
||||
: value(s)
|
||||
{
|
||||
}
|
||||
|
||||
const T & value;
|
||||
};
|
||||
|
||||
template <class T>
|
||||
template<class T>
|
||||
std::ostream & operator<<(std::ostream & out, const Magenta<T> & y)
|
||||
{
|
||||
return out << ANSI_WARNING << y.value << ANSI_NORMAL;
|
||||
|
|
@ -115,14 +116,18 @@ std::ostream & operator<<(std::ostream & out, const Magenta<T> & y)
|
|||
*
|
||||
* By default, arguments to `HintFmt` are printed in magenta (see `Magenta`).
|
||||
*/
|
||||
template <class T>
|
||||
template<class T>
|
||||
struct Uncolored
|
||||
{
|
||||
Uncolored(const T & s) : value(s) {}
|
||||
Uncolored(const T & s)
|
||||
: value(s)
|
||||
{
|
||||
}
|
||||
|
||||
const T & value;
|
||||
};
|
||||
|
||||
template <class T>
|
||||
template<class T>
|
||||
std::ostream & operator<<(std::ostream & out, const Uncolored<T> & y)
|
||||
{
|
||||
return out << ANSI_NORMAL << y.value;
|
||||
|
|
@ -144,9 +149,11 @@ public:
|
|||
*/
|
||||
HintFmt(const std::string & literal)
|
||||
: HintFmt("%s", Uncolored(literal))
|
||||
{ }
|
||||
{
|
||||
}
|
||||
|
||||
static HintFmt fromFormatString(const std::string & format) {
|
||||
static HintFmt fromFormatString(const std::string & format)
|
||||
{
|
||||
return HintFmt(boost::format(format));
|
||||
}
|
||||
|
||||
|
|
@ -154,16 +161,18 @@ public:
|
|||
* Interpolate the given arguments into the format string.
|
||||
*/
|
||||
template<typename... Args>
|
||||
HintFmt(const std::string & format, const Args & ... args)
|
||||
HintFmt(const std::string & format, const Args &... args)
|
||||
: HintFmt(boost::format(format), args...)
|
||||
{ }
|
||||
{
|
||||
}
|
||||
|
||||
HintFmt(const HintFmt & hf)
|
||||
: fmt(hf.fmt)
|
||||
{ }
|
||||
{
|
||||
}
|
||||
|
||||
template<typename... Args>
|
||||
HintFmt(boost::format && fmt, const Args & ... args)
|
||||
HintFmt(boost::format && fmt, const Args &... args)
|
||||
: fmt(std::move(fmt))
|
||||
{
|
||||
setExceptions(fmt);
|
||||
|
|
@ -194,4 +203,4 @@ public:
|
|||
|
||||
std::ostream & operator<<(std::ostream & os, const HintFmt & hf);
|
||||
|
||||
}
|
||||
} // namespace nix
|
||||
|
|
|
|||
|
|
@ -19,10 +19,9 @@ struct CreateRegularFileSink : Sink
|
|||
/**
|
||||
* An optimization. By default, do nothing.
|
||||
*/
|
||||
virtual void preallocateContents(uint64_t size) { };
|
||||
virtual void preallocateContents(uint64_t size) {};
|
||||
};
|
||||
|
||||
|
||||
struct FileSystemObjectSink
|
||||
{
|
||||
virtual ~FileSystemObjectSink() = default;
|
||||
|
|
@ -33,9 +32,7 @@ struct FileSystemObjectSink
|
|||
* This function in general is no re-entrant. Only one file can be
|
||||
* written at a time.
|
||||
*/
|
||||
virtual void createRegularFile(
|
||||
const CanonPath & path,
|
||||
std::function<void(CreateRegularFileSink &)>) = 0;
|
||||
virtual void createRegularFile(const CanonPath & path, std::function<void(CreateRegularFileSink &)>) = 0;
|
||||
|
||||
virtual void createSymlink(const CanonPath & path, const std::string & target) = 0;
|
||||
};
|
||||
|
|
@ -57,19 +54,18 @@ struct ExtendedFileSystemObjectSink : virtual FileSystemObjectSink
|
|||
* Recursively copy file system objects from the source into the sink.
|
||||
*/
|
||||
void copyRecursive(
|
||||
SourceAccessor & accessor, const CanonPath & sourcePath,
|
||||
FileSystemObjectSink & sink, const CanonPath & destPath);
|
||||
SourceAccessor & accessor, const CanonPath & sourcePath, FileSystemObjectSink & sink, const CanonPath & destPath);
|
||||
|
||||
/**
|
||||
* Ignore everything and do nothing
|
||||
*/
|
||||
struct NullFileSystemObjectSink : FileSystemObjectSink
|
||||
{
|
||||
void createDirectory(const CanonPath & path) override { }
|
||||
void createSymlink(const CanonPath & path, const std::string & target) override { }
|
||||
void createRegularFile(
|
||||
const CanonPath & path,
|
||||
std::function<void(CreateRegularFileSink &)>) override;
|
||||
void createDirectory(const CanonPath & path) override {}
|
||||
|
||||
void createSymlink(const CanonPath & path, const std::string & target) override {}
|
||||
|
||||
void createRegularFile(const CanonPath & path, std::function<void(CreateRegularFileSink &)>) override;
|
||||
};
|
||||
|
||||
/**
|
||||
|
|
@ -82,13 +78,12 @@ struct RestoreSink : FileSystemObjectSink
|
|||
|
||||
explicit RestoreSink(bool startFsync)
|
||||
: startFsync{startFsync}
|
||||
{ }
|
||||
{
|
||||
}
|
||||
|
||||
void createDirectory(const CanonPath & path) override;
|
||||
|
||||
void createRegularFile(
|
||||
const CanonPath & path,
|
||||
std::function<void(CreateRegularFileSink &)>) override;
|
||||
void createRegularFile(const CanonPath & path, std::function<void(CreateRegularFileSink &)>) override;
|
||||
|
||||
void createSymlink(const CanonPath & path, const std::string & target) override;
|
||||
};
|
||||
|
|
@ -103,7 +98,10 @@ struct RegularFileSink : FileSystemObjectSink
|
|||
bool regular = true;
|
||||
Sink & sink;
|
||||
|
||||
RegularFileSink(Sink & sink) : sink(sink) { }
|
||||
RegularFileSink(Sink & sink)
|
||||
: sink(sink)
|
||||
{
|
||||
}
|
||||
|
||||
void createDirectory(const CanonPath & path) override
|
||||
{
|
||||
|
|
@ -115,9 +113,7 @@ struct RegularFileSink : FileSystemObjectSink
|
|||
regular = false;
|
||||
}
|
||||
|
||||
void createRegularFile(
|
||||
const CanonPath & path,
|
||||
std::function<void(CreateRegularFileSink &)>) override;
|
||||
void createRegularFile(const CanonPath & path, std::function<void(CreateRegularFileSink &)>) override;
|
||||
};
|
||||
|
||||
}
|
||||
} // namespace nix
|
||||
|
|
|
|||
|
|
@ -16,8 +16,8 @@ namespace nix::git {
|
|||
enum struct ObjectType {
|
||||
Blob,
|
||||
Tree,
|
||||
//Commit,
|
||||
//Tag,
|
||||
// Commit,
|
||||
// Tag,
|
||||
};
|
||||
|
||||
using RawMode = uint32_t;
|
||||
|
|
@ -39,8 +39,8 @@ struct TreeEntry
|
|||
Mode mode;
|
||||
Hash hash;
|
||||
|
||||
bool operator ==(const TreeEntry &) const = default;
|
||||
auto operator <=>(const TreeEntry &) const = default;
|
||||
bool operator==(const TreeEntry &) const = default;
|
||||
auto operator<=>(const TreeEntry &) const = default;
|
||||
};
|
||||
|
||||
/**
|
||||
|
|
@ -72,9 +72,8 @@ using SinkHook = void(const CanonPath & name, TreeEntry entry);
|
|||
*
|
||||
* @throws if prefix not recognized
|
||||
*/
|
||||
ObjectType parseObjectType(
|
||||
Source & source,
|
||||
const ExperimentalFeatureSettings & xpSettings = experimentalFeatureSettings);
|
||||
ObjectType
|
||||
parseObjectType(Source & source, const ExperimentalFeatureSettings & xpSettings = experimentalFeatureSettings);
|
||||
|
||||
/**
|
||||
* These 3 modes are represented by blob objects.
|
||||
|
|
@ -82,21 +81,22 @@ ObjectType parseObjectType(
|
|||
* Sometimes we need this information to disambiguate how a blob is
|
||||
* being used to better match our own "file system object" data model.
|
||||
*/
|
||||
enum struct BlobMode : RawMode
|
||||
{
|
||||
enum struct BlobMode : RawMode {
|
||||
Regular = static_cast<RawMode>(Mode::Regular),
|
||||
Executable = static_cast<RawMode>(Mode::Executable),
|
||||
Symlink = static_cast<RawMode>(Mode::Symlink),
|
||||
};
|
||||
|
||||
void parseBlob(
|
||||
FileSystemObjectSink & sink, const CanonPath & sinkPath,
|
||||
FileSystemObjectSink & sink,
|
||||
const CanonPath & sinkPath,
|
||||
Source & source,
|
||||
BlobMode blobMode,
|
||||
const ExperimentalFeatureSettings & xpSettings = experimentalFeatureSettings);
|
||||
|
||||
void parseTree(
|
||||
FileSystemObjectSink & sink, const CanonPath & sinkPath,
|
||||
FileSystemObjectSink & sink,
|
||||
const CanonPath & sinkPath,
|
||||
Source & source,
|
||||
std::function<SinkHook> hook,
|
||||
const ExperimentalFeatureSettings & xpSettings = experimentalFeatureSettings);
|
||||
|
|
@ -109,7 +109,8 @@ void parseTree(
|
|||
* a blob, this is ignored.
|
||||
*/
|
||||
void parse(
|
||||
FileSystemObjectSink & sink, const CanonPath & sinkPath,
|
||||
FileSystemObjectSink & sink,
|
||||
const CanonPath & sinkPath,
|
||||
Source & source,
|
||||
BlobMode rootModeIfBlob,
|
||||
std::function<SinkHook> hook,
|
||||
|
|
@ -139,15 +140,13 @@ void restore(FileSystemObjectSink & sink, Source & source, std::function<Restore
|
|||
* @param xpSettings for testing purposes
|
||||
*/
|
||||
void dumpBlobPrefix(
|
||||
uint64_t size, Sink & sink,
|
||||
const ExperimentalFeatureSettings & xpSettings = experimentalFeatureSettings);
|
||||
uint64_t size, Sink & sink, const ExperimentalFeatureSettings & xpSettings = experimentalFeatureSettings);
|
||||
|
||||
/**
|
||||
* Dumps a representation of a git tree to a sink
|
||||
*/
|
||||
void dumpTree(
|
||||
const Tree & entries, Sink & sink,
|
||||
const ExperimentalFeatureSettings & xpSettings = experimentalFeatureSettings);
|
||||
const Tree & entries, Sink & sink, const ExperimentalFeatureSettings & xpSettings = experimentalFeatureSettings);
|
||||
|
||||
/**
|
||||
* Callback for processing a child with `dump`
|
||||
|
|
@ -172,10 +171,7 @@ Mode dump(
|
|||
*
|
||||
* A smaller wrapper around `dump`.
|
||||
*/
|
||||
TreeEntry dumpHash(
|
||||
HashAlgorithm ha,
|
||||
const SourcePath & path,
|
||||
PathFilter & filter = defaultPathFilter);
|
||||
TreeEntry dumpHash(HashAlgorithm ha, const SourcePath & path, PathFilter & filter = defaultPathFilter);
|
||||
|
||||
/**
|
||||
* A line from the output of `git ls-remote --symref`.
|
||||
|
|
@ -196,11 +192,9 @@ TreeEntry dumpHash(
|
|||
* ```
|
||||
* where {target} is a commit id and {reference} is mandatory
|
||||
*/
|
||||
struct LsRemoteRefLine {
|
||||
enum struct Kind {
|
||||
Symbolic,
|
||||
Object
|
||||
};
|
||||
struct LsRemoteRefLine
|
||||
{
|
||||
enum struct Kind { Symbolic, Object };
|
||||
Kind kind;
|
||||
std::string target;
|
||||
std::optional<std::string> reference;
|
||||
|
|
@ -211,4 +205,4 @@ struct LsRemoteRefLine {
|
|||
*/
|
||||
std::optional<LsRemoteRefLine> parseLsRemoteLine(std::string_view line);
|
||||
|
||||
}
|
||||
} // namespace nix::git
|
||||
|
|
|
|||
|
|
@ -8,10 +8,8 @@
|
|||
|
||||
namespace nix {
|
||||
|
||||
|
||||
MakeError(BadHash, Error);
|
||||
|
||||
|
||||
enum struct HashAlgorithm : char { MD5 = 42, SHA1, SHA256, SHA512, BLAKE3 };
|
||||
|
||||
const int blake3HashSize = 32;
|
||||
|
|
@ -89,12 +87,12 @@ public:
|
|||
/**
|
||||
* Check whether two hashes are equal.
|
||||
*/
|
||||
bool operator == (const Hash & h2) const noexcept;
|
||||
bool operator==(const Hash & h2) const noexcept;
|
||||
|
||||
/**
|
||||
* Compare how two hashes are ordered.
|
||||
*/
|
||||
std::strong_ordering operator <=> (const Hash & h2) const noexcept;
|
||||
std::strong_ordering operator<=>(const Hash & h2) const noexcept;
|
||||
|
||||
/**
|
||||
* Returns the length of a base-16 representation of this hash.
|
||||
|
|
@ -158,7 +156,8 @@ std::string printHash16or32(const Hash & hash);
|
|||
/**
|
||||
* Compute the hash of the given string.
|
||||
*/
|
||||
Hash hashString(HashAlgorithm ha, std::string_view s, const ExperimentalFeatureSettings & xpSettings = experimentalFeatureSettings);
|
||||
Hash hashString(
|
||||
HashAlgorithm ha, std::string_view s, const ExperimentalFeatureSettings & xpSettings = experimentalFeatureSettings);
|
||||
|
||||
/**
|
||||
* Compute the hash of the given file, hashing its contents directly.
|
||||
|
|
@ -210,7 +209,6 @@ std::optional<HashAlgorithm> parseHashAlgoOpt(std::string_view s);
|
|||
*/
|
||||
std::string_view printHashAlgo(HashAlgorithm ha);
|
||||
|
||||
|
||||
union Ctx;
|
||||
|
||||
struct AbstractHashSink : virtual Sink
|
||||
|
|
@ -234,5 +232,4 @@ public:
|
|||
HashResult currentHash();
|
||||
};
|
||||
|
||||
|
||||
}
|
||||
} // namespace nix
|
||||
|
|
|
|||
|
|
@ -14,10 +14,7 @@ namespace nix {
|
|||
* If some matches overlap, then their union will be wrapped rather
|
||||
* than the individual matches.
|
||||
*/
|
||||
std::string hiliteMatches(
|
||||
std::string_view s,
|
||||
std::vector<std::smatch> matches,
|
||||
std::string_view prefix,
|
||||
std::string_view postfix);
|
||||
std::string
|
||||
hiliteMatches(std::string_view s, std::vector<std::smatch> matches, std::string_view prefix, std::string_view postfix);
|
||||
|
||||
}
|
||||
} // namespace nix
|
||||
|
|
|
|||
|
|
@ -4,12 +4,13 @@
|
|||
#include <nlohmann/json_fwd.hpp>
|
||||
|
||||
// Following https://github.com/nlohmann/json#how-can-i-use-get-for-non-default-constructiblenon-copyable-types
|
||||
#define JSON_IMPL(TYPE) \
|
||||
namespace nlohmann { \
|
||||
using namespace nix; \
|
||||
template <> \
|
||||
struct adl_serializer<TYPE> { \
|
||||
static TYPE from_json(const json & json); \
|
||||
static void to_json(json & json, TYPE t); \
|
||||
}; \
|
||||
#define JSON_IMPL(TYPE) \
|
||||
namespace nlohmann { \
|
||||
using namespace nix; \
|
||||
template<> \
|
||||
struct adl_serializer<TYPE> \
|
||||
{ \
|
||||
static TYPE from_json(const json & json); \
|
||||
static void to_json(json & json, TYPE t); \
|
||||
}; \
|
||||
}
|
||||
|
|
|
|||
|
|
@ -21,9 +21,7 @@ nlohmann::json * get(nlohmann::json & map, const std::string & key);
|
|||
*
|
||||
* Use instead of nlohmann::json::at() to avoid ugly exceptions.
|
||||
*/
|
||||
const nlohmann::json & valueAt(
|
||||
const nlohmann::json::object_t & map,
|
||||
const std::string & key);
|
||||
const nlohmann::json & valueAt(const nlohmann::json::object_t & map, const std::string & key);
|
||||
|
||||
std::optional<nlohmann::json> optionalValueAt(const nlohmann::json::object_t & value, const std::string & key);
|
||||
std::optional<nlohmann::json> nullableValueAt(const nlohmann::json::object_t & value, const std::string & key);
|
||||
|
|
@ -73,36 +71,45 @@ struct json_avoids_null;
|
|||
* Handle numbers in default impl
|
||||
*/
|
||||
template<typename T>
|
||||
struct json_avoids_null : std::bool_constant<std::is_integral<T>::value> {};
|
||||
struct json_avoids_null : std::bool_constant<std::is_integral<T>::value>
|
||||
{};
|
||||
|
||||
template<>
|
||||
struct json_avoids_null<std::nullptr_t> : std::false_type {};
|
||||
struct json_avoids_null<std::nullptr_t> : std::false_type
|
||||
{};
|
||||
|
||||
template<>
|
||||
struct json_avoids_null<bool> : std::true_type {};
|
||||
struct json_avoids_null<bool> : std::true_type
|
||||
{};
|
||||
|
||||
template<>
|
||||
struct json_avoids_null<std::string> : std::true_type {};
|
||||
struct json_avoids_null<std::string> : std::true_type
|
||||
{};
|
||||
|
||||
template<typename T>
|
||||
struct json_avoids_null<std::vector<T>> : std::true_type {};
|
||||
struct json_avoids_null<std::vector<T>> : std::true_type
|
||||
{};
|
||||
|
||||
template<typename T>
|
||||
struct json_avoids_null<std::list<T>> : std::true_type {};
|
||||
struct json_avoids_null<std::list<T>> : std::true_type
|
||||
{};
|
||||
|
||||
template<typename T, typename Compare>
|
||||
struct json_avoids_null<std::set<T, Compare>> : std::true_type {};
|
||||
struct json_avoids_null<std::set<T, Compare>> : std::true_type
|
||||
{};
|
||||
|
||||
template<typename K, typename V>
|
||||
struct json_avoids_null<std::map<K, V>> : std::true_type {};
|
||||
struct json_avoids_null<std::map<K, V>> : std::true_type
|
||||
{};
|
||||
|
||||
/**
|
||||
* `ExperimentalFeature` is always rendered as a string.
|
||||
*/
|
||||
template<>
|
||||
struct json_avoids_null<ExperimentalFeature> : std::true_type {};
|
||||
struct json_avoids_null<ExperimentalFeature> : std::true_type
|
||||
{};
|
||||
|
||||
}
|
||||
} // namespace nix
|
||||
|
||||
namespace nlohmann {
|
||||
|
||||
|
|
@ -123,12 +130,8 @@ struct adl_serializer<std::optional<T>>
|
|||
*/
|
||||
static void from_json(const json & json, std::optional<T> & t)
|
||||
{
|
||||
static_assert(
|
||||
nix::json_avoids_null<T>::value,
|
||||
"null is already in use for underlying type's JSON");
|
||||
t = json.is_null()
|
||||
? std::nullopt
|
||||
: std::make_optional(json.template get<T>());
|
||||
static_assert(nix::json_avoids_null<T>::value, "null is already in use for underlying type's JSON");
|
||||
t = json.is_null() ? std::nullopt : std::make_optional(json.template get<T>());
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
@ -137,9 +140,7 @@ struct adl_serializer<std::optional<T>>
|
|||
*/
|
||||
static void to_json(json & json, const std::optional<T> & t)
|
||||
{
|
||||
static_assert(
|
||||
nix::json_avoids_null<T>::value,
|
||||
"null is already in use for underlying type's JSON");
|
||||
static_assert(nix::json_avoids_null<T>::value, "null is already in use for underlying type's JSON");
|
||||
if (t)
|
||||
json = *t;
|
||||
else
|
||||
|
|
@ -147,4 +148,4 @@ struct adl_serializer<std::optional<T>>
|
|||
}
|
||||
};
|
||||
|
||||
}
|
||||
} // namespace nlohmann
|
||||
|
|
|
|||
|
|
@ -46,14 +46,18 @@ typedef uint64_t ActivityId;
|
|||
struct LoggerSettings : Config
|
||||
{
|
||||
Setting<bool> showTrace{
|
||||
this, false, "show-trace",
|
||||
this,
|
||||
false,
|
||||
"show-trace",
|
||||
R"(
|
||||
Whether Nix should print out a stack trace in case of Nix
|
||||
expression evaluation errors.
|
||||
)"};
|
||||
|
||||
Setting<Path> jsonLogPath{
|
||||
this, "", "json-log-path",
|
||||
this,
|
||||
"",
|
||||
"json-log-path",
|
||||
R"(
|
||||
A file or unix socket to which JSON records of Nix's log output are
|
||||
written, in the same format as `--log-format internal-json`
|
||||
|
|
@ -75,23 +79,40 @@ public:
|
|||
{
|
||||
// FIXME: use std::variant.
|
||||
enum { tInt = 0, tString = 1 } type;
|
||||
|
||||
uint64_t i = 0;
|
||||
std::string s;
|
||||
Field(const std::string & s) : type(tString), s(s) { }
|
||||
Field(const char * s) : type(tString), s(s) { }
|
||||
Field(const uint64_t & i) : type(tInt), i(i) { }
|
||||
|
||||
Field(const std::string & s)
|
||||
: type(tString)
|
||||
, s(s)
|
||||
{
|
||||
}
|
||||
|
||||
Field(const char * s)
|
||||
: type(tString)
|
||||
, s(s)
|
||||
{
|
||||
}
|
||||
|
||||
Field(const uint64_t & i)
|
||||
: type(tInt)
|
||||
, i(i)
|
||||
{
|
||||
}
|
||||
};
|
||||
|
||||
typedef std::vector<Field> Fields;
|
||||
|
||||
virtual ~Logger() { }
|
||||
virtual ~Logger() {}
|
||||
|
||||
virtual void stop() { };
|
||||
virtual void stop() {};
|
||||
|
||||
/**
|
||||
* Guard object to resume the logger when done.
|
||||
*/
|
||||
struct Suspension {
|
||||
struct Suspension
|
||||
{
|
||||
Finally<std::function<void()>> _finalize;
|
||||
};
|
||||
|
||||
|
|
@ -99,11 +120,14 @@ public:
|
|||
|
||||
std::optional<Suspension> suspendIf(bool cond);
|
||||
|
||||
virtual void pause() { };
|
||||
virtual void resume() { };
|
||||
virtual void pause() {};
|
||||
virtual void resume() {};
|
||||
|
||||
// Whether the logger prints the whole build log
|
||||
virtual bool isVerbose() { return false; }
|
||||
virtual bool isVerbose()
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
virtual void log(Verbosity lvl, std::string_view s) = 0;
|
||||
|
||||
|
|
@ -122,26 +146,32 @@ public:
|
|||
|
||||
virtual void warn(const std::string & msg);
|
||||
|
||||
virtual void startActivity(ActivityId act, Verbosity lvl, ActivityType type,
|
||||
const std::string & s, const Fields & fields, ActivityId parent) { };
|
||||
virtual void startActivity(
|
||||
ActivityId act,
|
||||
Verbosity lvl,
|
||||
ActivityType type,
|
||||
const std::string & s,
|
||||
const Fields & fields,
|
||||
ActivityId parent) {};
|
||||
|
||||
virtual void stopActivity(ActivityId act) { };
|
||||
virtual void stopActivity(ActivityId act) {};
|
||||
|
||||
virtual void result(ActivityId act, ResultType type, const Fields & fields) { };
|
||||
virtual void result(ActivityId act, ResultType type, const Fields & fields) {};
|
||||
|
||||
virtual void writeToStdout(std::string_view s);
|
||||
|
||||
template<typename... Args>
|
||||
inline void cout(const Args & ... args)
|
||||
inline void cout(const Args &... args)
|
||||
{
|
||||
writeToStdout(fmt(args...));
|
||||
}
|
||||
|
||||
virtual std::optional<char> ask(std::string_view s)
|
||||
{ return {}; }
|
||||
{
|
||||
return {};
|
||||
}
|
||||
|
||||
virtual void setPrintBuildLogs(bool printBuildLogs)
|
||||
{ }
|
||||
virtual void setPrintBuildLogs(bool printBuildLogs) {}
|
||||
};
|
||||
|
||||
/**
|
||||
|
|
@ -151,8 +181,10 @@ public:
|
|||
*/
|
||||
struct nop
|
||||
{
|
||||
template<typename... T> nop(T...)
|
||||
{ }
|
||||
template<typename... T>
|
||||
nop(T...)
|
||||
{
|
||||
}
|
||||
};
|
||||
|
||||
ActivityId getCurActivity();
|
||||
|
|
@ -164,25 +196,34 @@ struct Activity
|
|||
|
||||
const ActivityId id;
|
||||
|
||||
Activity(Logger & logger, Verbosity lvl, ActivityType type, const std::string & s = "",
|
||||
const Logger::Fields & fields = {}, ActivityId parent = getCurActivity());
|
||||
Activity(
|
||||
Logger & logger,
|
||||
Verbosity lvl,
|
||||
ActivityType type,
|
||||
const std::string & s = "",
|
||||
const Logger::Fields & fields = {},
|
||||
ActivityId parent = getCurActivity());
|
||||
|
||||
Activity(Logger & logger, ActivityType type,
|
||||
const Logger::Fields & fields = {}, ActivityId parent = getCurActivity())
|
||||
: Activity(logger, lvlError, type, "", fields, parent) { };
|
||||
Activity(
|
||||
Logger & logger, ActivityType type, const Logger::Fields & fields = {}, ActivityId parent = getCurActivity())
|
||||
: Activity(logger, lvlError, type, "", fields, parent) {};
|
||||
|
||||
Activity(const Activity & act) = delete;
|
||||
|
||||
~Activity();
|
||||
|
||||
void progress(uint64_t done = 0, uint64_t expected = 0, uint64_t running = 0, uint64_t failed = 0) const
|
||||
{ result(resProgress, done, expected, running, failed); }
|
||||
{
|
||||
result(resProgress, done, expected, running, failed);
|
||||
}
|
||||
|
||||
void setExpected(ActivityType type2, uint64_t expected) const
|
||||
{ result(resSetExpected, type2, expected); }
|
||||
{
|
||||
result(resSetExpected, type2, expected);
|
||||
}
|
||||
|
||||
template<typename... Args>
|
||||
void result(ResultType type, const Args & ... args) const
|
||||
void result(ResultType type, const Args &... args) const
|
||||
{
|
||||
Logger::Fields fields;
|
||||
nop{(fields.emplace_back(Logger::Field(args)), 1)...};
|
||||
|
|
@ -200,8 +241,17 @@ struct Activity
|
|||
struct PushActivity
|
||||
{
|
||||
const ActivityId prevAct;
|
||||
PushActivity(ActivityId act) : prevAct(getCurActivity()) { setCurActivity(act); }
|
||||
~PushActivity() { setCurActivity(prevAct); }
|
||||
|
||||
PushActivity(ActivityId act)
|
||||
: prevAct(getCurActivity())
|
||||
{
|
||||
setCurActivity(act);
|
||||
}
|
||||
|
||||
~PushActivity()
|
||||
{
|
||||
setCurActivity(prevAct);
|
||||
}
|
||||
};
|
||||
|
||||
extern std::unique_ptr<Logger> logger;
|
||||
|
|
@ -213,9 +263,8 @@ std::unique_ptr<Logger> makeSimpleLogger(bool printBuildLogs = true);
|
|||
* list of loggers in `extraLoggers`. Only `mainLogger` is used for
|
||||
* writing to stdout and getting user input.
|
||||
*/
|
||||
std::unique_ptr<Logger> makeTeeLogger(
|
||||
std::unique_ptr<Logger> mainLogger,
|
||||
std::vector<std::unique_ptr<Logger>> && extraLoggers);
|
||||
std::unique_ptr<Logger>
|
||||
makeTeeLogger(std::unique_ptr<Logger> mainLogger, std::vector<std::unique_ptr<Logger>> && extraLoggers);
|
||||
|
||||
std::unique_ptr<Logger> makeJSONLogger(Descriptor fd, bool includeNixPrefix = true);
|
||||
|
||||
|
|
@ -231,16 +280,20 @@ std::optional<nlohmann::json> parseJSONMessage(const std::string & msg, std::str
|
|||
/**
|
||||
* @param source A noun phrase describing the source of the message, e.g. "the builder".
|
||||
*/
|
||||
bool handleJSONLogMessage(nlohmann::json & json,
|
||||
const Activity & act, std::map<ActivityId, Activity> & activities,
|
||||
bool handleJSONLogMessage(
|
||||
nlohmann::json & json,
|
||||
const Activity & act,
|
||||
std::map<ActivityId, Activity> & activities,
|
||||
std::string_view source,
|
||||
bool trusted);
|
||||
|
||||
/**
|
||||
* @param source A noun phrase describing the source of the message, e.g. "the builder".
|
||||
*/
|
||||
bool handleJSONLogMessage(const std::string & msg,
|
||||
const Activity & act, std::map<ActivityId, Activity> & activities,
|
||||
bool handleJSONLogMessage(
|
||||
const std::string & msg,
|
||||
const Activity & act,
|
||||
std::map<ActivityId, Activity> & activities,
|
||||
std::string_view source,
|
||||
bool trusted);
|
||||
|
||||
|
|
@ -255,11 +308,11 @@ extern Verbosity verbosity;
|
|||
* intervention or that need more explanation. Use the 'print' macros for more
|
||||
* lightweight status messages.
|
||||
*/
|
||||
#define logErrorInfo(level, errorInfo...) \
|
||||
do { \
|
||||
if ((level) <= nix::verbosity) { \
|
||||
logger->logEI((level), errorInfo); \
|
||||
} \
|
||||
#define logErrorInfo(level, errorInfo...) \
|
||||
do { \
|
||||
if ((level) <= nix::verbosity) { \
|
||||
logger->logEI((level), errorInfo); \
|
||||
} \
|
||||
} while (0)
|
||||
|
||||
#define logError(errorInfo...) logErrorInfo(lvlError, errorInfo)
|
||||
|
|
@ -271,11 +324,11 @@ extern Verbosity verbosity;
|
|||
* arguments are evaluated lazily.
|
||||
*/
|
||||
#define printMsgUsing(loggerParam, level, args...) \
|
||||
do { \
|
||||
auto __lvl = level; \
|
||||
if (__lvl <= nix::verbosity) { \
|
||||
loggerParam->log(__lvl, fmt(args)); \
|
||||
} \
|
||||
do { \
|
||||
auto __lvl = level; \
|
||||
if (__lvl <= nix::verbosity) { \
|
||||
loggerParam->log(__lvl, fmt(args)); \
|
||||
} \
|
||||
} while (0)
|
||||
#define printMsg(level, args...) printMsgUsing(logger, level, args)
|
||||
|
||||
|
|
@ -290,7 +343,7 @@ extern Verbosity verbosity;
|
|||
* if verbosity >= lvlWarn, print a message with a yellow 'warning:' prefix.
|
||||
*/
|
||||
template<typename... Args>
|
||||
inline void warn(const std::string & fs, const Args & ... args)
|
||||
inline void warn(const std::string & fs, const Args &... args)
|
||||
{
|
||||
boost::format f(fs);
|
||||
formatHelper(f, args...);
|
||||
|
|
@ -305,4 +358,4 @@ inline void warn(const std::string & fs, const Args & ... args)
|
|||
|
||||
void writeToStderr(std::string_view s);
|
||||
|
||||
}
|
||||
} // namespace nix
|
||||
|
|
|
|||
|
|
@ -141,4 +141,4 @@ public:
|
|||
}
|
||||
};
|
||||
|
||||
}
|
||||
} // namespace nix
|
||||
|
|
|
|||
|
|
@ -14,33 +14,37 @@ struct MemorySourceAccessor : virtual SourceAccessor
|
|||
* `MemorySourceAccessor`, this has a side benefit of nicely
|
||||
* defining what a "file system object" is in Nix.
|
||||
*/
|
||||
struct File {
|
||||
bool operator == (const File &) const noexcept;
|
||||
std::strong_ordering operator <=> (const File &) const noexcept;
|
||||
struct File
|
||||
{
|
||||
bool operator==(const File &) const noexcept;
|
||||
std::strong_ordering operator<=>(const File &) const noexcept;
|
||||
|
||||
struct Regular {
|
||||
struct Regular
|
||||
{
|
||||
bool executable = false;
|
||||
std::string contents;
|
||||
|
||||
bool operator == (const Regular &) const = default;
|
||||
auto operator <=> (const Regular &) const = default;
|
||||
bool operator==(const Regular &) const = default;
|
||||
auto operator<=>(const Regular &) const = default;
|
||||
};
|
||||
|
||||
struct Directory {
|
||||
struct Directory
|
||||
{
|
||||
using Name = std::string;
|
||||
|
||||
std::map<Name, File, std::less<>> contents;
|
||||
|
||||
bool operator == (const Directory &) const noexcept;
|
||||
bool operator==(const Directory &) const noexcept;
|
||||
// TODO libc++ 16 (used by darwin) missing `std::map::operator <=>`, can't do yet.
|
||||
bool operator < (const Directory &) const noexcept;
|
||||
bool operator<(const Directory &) const noexcept;
|
||||
};
|
||||
|
||||
struct Symlink {
|
||||
struct Symlink
|
||||
{
|
||||
std::string target;
|
||||
|
||||
bool operator == (const Symlink &) const = default;
|
||||
auto operator <=> (const Symlink &) const = default;
|
||||
bool operator==(const Symlink &) const = default;
|
||||
auto operator<=>(const Symlink &) const = default;
|
||||
};
|
||||
|
||||
using Raw = std::variant<Regular, Directory, Symlink>;
|
||||
|
|
@ -51,10 +55,12 @@ struct MemorySourceAccessor : virtual SourceAccessor
|
|||
Stat lstat() const;
|
||||
};
|
||||
|
||||
File root { File::Directory {} };
|
||||
File root{File::Directory{}};
|
||||
|
||||
bool operator == (const MemorySourceAccessor &) const noexcept = default;
|
||||
bool operator < (const MemorySourceAccessor & other) const noexcept {
|
||||
bool operator==(const MemorySourceAccessor &) const noexcept = default;
|
||||
|
||||
bool operator<(const MemorySourceAccessor & other) const noexcept
|
||||
{
|
||||
return root < other.root;
|
||||
}
|
||||
|
||||
|
|
@ -80,19 +86,18 @@ struct MemorySourceAccessor : virtual SourceAccessor
|
|||
SourcePath addFile(CanonPath path, std::string && contents);
|
||||
};
|
||||
|
||||
|
||||
inline bool MemorySourceAccessor::File::Directory::operator == (
|
||||
inline bool MemorySourceAccessor::File::Directory::operator==(
|
||||
const MemorySourceAccessor::File::Directory &) const noexcept = default;
|
||||
inline bool MemorySourceAccessor::File::Directory::operator < (
|
||||
const MemorySourceAccessor::File::Directory & other) const noexcept
|
||||
|
||||
inline bool
|
||||
MemorySourceAccessor::File::Directory::operator<(const MemorySourceAccessor::File::Directory & other) const noexcept
|
||||
{
|
||||
return contents < other.contents;
|
||||
}
|
||||
|
||||
inline bool MemorySourceAccessor::File::operator == (
|
||||
const MemorySourceAccessor::File &) const noexcept = default;
|
||||
inline std::strong_ordering MemorySourceAccessor::File::operator <=> (
|
||||
const MemorySourceAccessor::File &) const noexcept = default;
|
||||
inline bool MemorySourceAccessor::File::operator==(const MemorySourceAccessor::File &) const noexcept = default;
|
||||
inline std::strong_ordering
|
||||
MemorySourceAccessor::File::operator<=>(const MemorySourceAccessor::File &) const noexcept = default;
|
||||
|
||||
/**
|
||||
* Write to a `MemorySourceAccessor` at the given path
|
||||
|
|
@ -101,15 +106,16 @@ struct MemorySink : FileSystemObjectSink
|
|||
{
|
||||
MemorySourceAccessor & dst;
|
||||
|
||||
MemorySink(MemorySourceAccessor & dst) : dst(dst) { }
|
||||
MemorySink(MemorySourceAccessor & dst)
|
||||
: dst(dst)
|
||||
{
|
||||
}
|
||||
|
||||
void createDirectory(const CanonPath & path) override;
|
||||
|
||||
void createRegularFile(
|
||||
const CanonPath & path,
|
||||
std::function<void(CreateRegularFileSink &)>) override;
|
||||
void createRegularFile(const CanonPath & path, std::function<void(CreateRegularFileSink &)>) override;
|
||||
|
||||
void createSymlink(const CanonPath & path, const std::string & target) override;
|
||||
};
|
||||
|
||||
}
|
||||
} // namespace nix
|
||||
|
|
|
|||
|
|
@ -79,4 +79,4 @@ struct MuxablePipePollState
|
|||
std::function<void(Descriptor fd)> handleEOF);
|
||||
};
|
||||
|
||||
}
|
||||
} // namespace nix
|
||||
|
|
|
|||
|
|
@ -49,4 +49,4 @@ OsString string_to_os_string(std::string_view s);
|
|||
# define OS_STR(s) L##s
|
||||
#endif
|
||||
|
||||
}
|
||||
} // namespace nix
|
||||
|
|
|
|||
|
|
@ -29,7 +29,7 @@ namespace nix {
|
|||
* Here, the Connection object referenced by ‘conn’ is automatically
|
||||
* returned to the pool when ‘conn’ goes out of scope.
|
||||
*/
|
||||
template <class R>
|
||||
template<class R>
|
||||
class Pool
|
||||
{
|
||||
public:
|
||||
|
|
@ -63,7 +63,8 @@ private:
|
|||
|
||||
public:
|
||||
|
||||
Pool(size_t max = std::numeric_limits<size_t>::max(),
|
||||
Pool(
|
||||
size_t max = std::numeric_limits<size_t>::max(),
|
||||
const Factory & factory = []() { return make_ref<R>(); },
|
||||
const Validator & validator = [](ref<R> r) { return true; })
|
||||
: factory(factory)
|
||||
|
|
@ -106,7 +107,11 @@ public:
|
|||
|
||||
friend Pool;
|
||||
|
||||
Handle(Pool & pool, std::shared_ptr<R> r) : pool(pool), r(r) { }
|
||||
Handle(Pool & pool, std::shared_ptr<R> r)
|
||||
: pool(pool)
|
||||
, r(r)
|
||||
{
|
||||
}
|
||||
|
||||
public:
|
||||
// NOTE: Copying std::shared_ptr and calling a .reset() on it is always noexcept.
|
||||
|
|
@ -123,7 +128,8 @@ public:
|
|||
|
||||
~Handle()
|
||||
{
|
||||
if (!r) return;
|
||||
if (!r)
|
||||
return;
|
||||
{
|
||||
auto state_(pool.state.lock());
|
||||
if (!bad)
|
||||
|
|
@ -134,10 +140,20 @@ public:
|
|||
pool.wakeup.notify_one();
|
||||
}
|
||||
|
||||
R * operator -> () { return &*r; }
|
||||
R & operator * () { return *r; }
|
||||
R * operator->()
|
||||
{
|
||||
return &*r;
|
||||
}
|
||||
|
||||
void markBad() { bad = true; }
|
||||
R & operator*()
|
||||
{
|
||||
return *r;
|
||||
}
|
||||
|
||||
void markBad()
|
||||
{
|
||||
bad = true;
|
||||
}
|
||||
};
|
||||
|
||||
Handle get()
|
||||
|
|
@ -197,4 +213,4 @@ public:
|
|||
}
|
||||
};
|
||||
|
||||
}
|
||||
} // namespace nix
|
||||
|
|
|
|||
|
|
@ -49,7 +49,7 @@ public:
|
|||
|
||||
inline PosIdx noPos = {};
|
||||
|
||||
}
|
||||
} // namespace nix
|
||||
|
||||
namespace std {
|
||||
|
||||
|
|
|
|||
|
|
@ -113,4 +113,4 @@ public:
|
|||
}
|
||||
};
|
||||
|
||||
}
|
||||
} // namespace nix
|
||||
|
|
|
|||
|
|
@ -21,30 +21,53 @@ struct Pos
|
|||
uint32_t line = 0;
|
||||
uint32_t column = 0;
|
||||
|
||||
struct Stdin {
|
||||
struct Stdin
|
||||
{
|
||||
ref<std::string> source;
|
||||
|
||||
bool operator==(const Stdin & rhs) const noexcept
|
||||
{ return *source == *rhs.source; }
|
||||
{
|
||||
return *source == *rhs.source;
|
||||
}
|
||||
|
||||
std::strong_ordering operator<=>(const Stdin & rhs) const noexcept
|
||||
{ return *source <=> *rhs.source; }
|
||||
{
|
||||
return *source <=> *rhs.source;
|
||||
}
|
||||
};
|
||||
struct String {
|
||||
|
||||
struct String
|
||||
{
|
||||
ref<std::string> source;
|
||||
|
||||
bool operator==(const String & rhs) const noexcept
|
||||
{ return *source == *rhs.source; }
|
||||
{
|
||||
return *source == *rhs.source;
|
||||
}
|
||||
|
||||
std::strong_ordering operator<=>(const String & rhs) const noexcept
|
||||
{ return *source <=> *rhs.source; }
|
||||
{
|
||||
return *source <=> *rhs.source;
|
||||
}
|
||||
};
|
||||
|
||||
typedef std::variant<std::monostate, Stdin, String, SourcePath> Origin;
|
||||
|
||||
Origin origin = std::monostate();
|
||||
|
||||
Pos() { }
|
||||
Pos(uint32_t line, uint32_t column, Origin origin)
|
||||
: line(line), column(column), origin(origin) { }
|
||||
Pos() {}
|
||||
|
||||
explicit operator bool() const { return line > 0; }
|
||||
Pos(uint32_t line, uint32_t column, Origin origin)
|
||||
: line(line)
|
||||
, column(column)
|
||||
, origin(origin)
|
||||
{
|
||||
}
|
||||
|
||||
explicit operator bool() const
|
||||
{
|
||||
return line > 0;
|
||||
}
|
||||
|
||||
operator std::shared_ptr<const Pos>() const;
|
||||
|
||||
|
|
@ -67,39 +90,60 @@ struct Pos
|
|||
*/
|
||||
std::optional<SourcePath> getSourcePath() const;
|
||||
|
||||
struct LinesIterator {
|
||||
struct LinesIterator
|
||||
{
|
||||
using difference_type = size_t;
|
||||
using value_type = std::string_view;
|
||||
using reference = const std::string_view &;
|
||||
using pointer = const std::string_view *;
|
||||
using iterator_category = std::input_iterator_tag;
|
||||
|
||||
LinesIterator(): pastEnd(true) {}
|
||||
explicit LinesIterator(std::string_view input): input(input), pastEnd(input.empty()) {
|
||||
LinesIterator()
|
||||
: pastEnd(true)
|
||||
{
|
||||
}
|
||||
|
||||
explicit LinesIterator(std::string_view input)
|
||||
: input(input)
|
||||
, pastEnd(input.empty())
|
||||
{
|
||||
if (!pastEnd)
|
||||
bump(true);
|
||||
}
|
||||
|
||||
LinesIterator & operator++() {
|
||||
LinesIterator & operator++()
|
||||
{
|
||||
bump(false);
|
||||
return *this;
|
||||
}
|
||||
LinesIterator operator++(int) {
|
||||
|
||||
LinesIterator operator++(int)
|
||||
{
|
||||
auto result = *this;
|
||||
++*this;
|
||||
return result;
|
||||
}
|
||||
|
||||
reference operator*() const { return curLine; }
|
||||
pointer operator->() const { return &curLine; }
|
||||
reference operator*() const
|
||||
{
|
||||
return curLine;
|
||||
}
|
||||
|
||||
bool operator!=(const LinesIterator & other) const {
|
||||
pointer operator->() const
|
||||
{
|
||||
return &curLine;
|
||||
}
|
||||
|
||||
bool operator!=(const LinesIterator & other) const
|
||||
{
|
||||
return !(*this == other);
|
||||
}
|
||||
bool operator==(const LinesIterator & other) const {
|
||||
|
||||
bool operator==(const LinesIterator & other) const
|
||||
{
|
||||
return (pastEnd && other.pastEnd)
|
||||
|| (std::forward_as_tuple(input.size(), input.data())
|
||||
== std::forward_as_tuple(other.input.size(), other.input.data()));
|
||||
|| (std::forward_as_tuple(input.size(), input.data())
|
||||
== std::forward_as_tuple(other.input.size(), other.input.data()));
|
||||
}
|
||||
|
||||
private:
|
||||
|
|
@ -112,4 +156,4 @@ struct Pos
|
|||
|
||||
std::ostream & operator<<(std::ostream & str, const Pos & pos);
|
||||
|
||||
}
|
||||
} // namespace nix
|
||||
|
|
|
|||
|
|
@ -27,10 +27,7 @@ struct PosixSourceAccessor : virtual SourceAccessor
|
|||
*/
|
||||
time_t mtime = 0;
|
||||
|
||||
void readFile(
|
||||
const CanonPath & path,
|
||||
Sink & sink,
|
||||
std::function<void(uint64_t)> sizeCallback) override;
|
||||
void readFile(const CanonPath & path, Sink & sink, std::function<void(uint64_t)> sizeCallback) override;
|
||||
|
||||
bool pathExists(const CanonPath & path) override;
|
||||
|
||||
|
|
@ -81,4 +78,4 @@ private:
|
|||
std::filesystem::path makeAbsPath(const CanonPath & path);
|
||||
};
|
||||
|
||||
}
|
||||
} // namespace nix
|
||||
|
|
|
|||
|
|
@ -37,11 +37,11 @@ public:
|
|||
Pid();
|
||||
#ifndef _WIN32
|
||||
Pid(pid_t pid);
|
||||
void operator =(pid_t pid);
|
||||
void operator=(pid_t pid);
|
||||
operator pid_t();
|
||||
#else
|
||||
Pid(AutoCloseFD pid);
|
||||
void operator =(AutoCloseFD pid);
|
||||
void operator=(AutoCloseFD pid);
|
||||
#endif
|
||||
~Pid();
|
||||
int kill();
|
||||
|
|
@ -55,7 +55,6 @@ public:
|
|||
#endif
|
||||
};
|
||||
|
||||
|
||||
#ifndef _WIN32
|
||||
/**
|
||||
* Kill all processes running under the specified uid by sending them
|
||||
|
|
@ -64,7 +63,6 @@ public:
|
|||
void killUser(uid_t uid);
|
||||
#endif
|
||||
|
||||
|
||||
/**
|
||||
* Fork a process that runs the given function, and return the child
|
||||
* pid to the caller.
|
||||
|
|
@ -89,9 +87,12 @@ pid_t startProcess(std::function<void()> fun, const ProcessOptions & options = P
|
|||
* Run a program and return its stdout in a string (i.e., like the
|
||||
* shell backtick operator).
|
||||
*/
|
||||
std::string runProgram(Path program, bool lookupPath = false,
|
||||
std::string runProgram(
|
||||
Path program,
|
||||
bool lookupPath = false,
|
||||
const Strings & args = Strings(),
|
||||
const std::optional<std::string> & input = {}, bool isInteractive = false);
|
||||
const std::optional<std::string> & input = {},
|
||||
bool isInteractive = false);
|
||||
|
||||
struct RunOptions
|
||||
{
|
||||
|
|
@ -115,16 +116,17 @@ std::pair<int, std::string> runProgram(RunOptions && options);
|
|||
|
||||
void runProgram2(const RunOptions & options);
|
||||
|
||||
|
||||
class ExecError : public Error
|
||||
{
|
||||
public:
|
||||
int status;
|
||||
|
||||
template<typename... Args>
|
||||
ExecError(int status, const Args & ... args)
|
||||
: Error(args...), status(status)
|
||||
{ }
|
||||
ExecError(int status, const Args &... args)
|
||||
: Error(args...)
|
||||
, status(status)
|
||||
{
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
|
|
@ -135,4 +137,4 @@ std::string statusToString(int status);
|
|||
|
||||
bool statusOk(int status);
|
||||
|
||||
}
|
||||
} // namespace nix
|
||||
|
|
|
|||
|
|
@ -32,17 +32,17 @@ public:
|
|||
throw std::invalid_argument("null pointer cast to ref");
|
||||
}
|
||||
|
||||
T* operator ->() const
|
||||
T * operator->() const
|
||||
{
|
||||
return &*p;
|
||||
}
|
||||
|
||||
T& operator *() const
|
||||
T & operator*() const
|
||||
{
|
||||
return *p;
|
||||
}
|
||||
|
||||
operator std::shared_ptr<T> () const
|
||||
operator std::shared_ptr<T>() const
|
||||
{
|
||||
return p;
|
||||
}
|
||||
|
|
@ -65,22 +65,22 @@ public:
|
|||
}
|
||||
|
||||
template<typename T2>
|
||||
operator ref<T2> () const
|
||||
operator ref<T2>() const
|
||||
{
|
||||
return ref<T2>((std::shared_ptr<T2>) p);
|
||||
}
|
||||
|
||||
bool operator == (const ref<T> & other) const
|
||||
bool operator==(const ref<T> & other) const
|
||||
{
|
||||
return p == other.p;
|
||||
}
|
||||
|
||||
bool operator != (const ref<T> & other) const
|
||||
bool operator!=(const ref<T> & other) const
|
||||
{
|
||||
return p != other.p;
|
||||
}
|
||||
|
||||
auto operator <=> (const ref<T> & other) const
|
||||
auto operator<=>(const ref<T> & other) const
|
||||
{
|
||||
return p <=> other.p;
|
||||
}
|
||||
|
|
@ -88,17 +88,14 @@ public:
|
|||
private:
|
||||
|
||||
template<typename T2, typename... Args>
|
||||
friend ref<T2>
|
||||
make_ref(Args&&... args);
|
||||
|
||||
friend ref<T2> make_ref(Args &&... args);
|
||||
};
|
||||
|
||||
template<typename T, typename... Args>
|
||||
inline ref<T>
|
||||
make_ref(Args&&... args)
|
||||
inline ref<T> make_ref(Args &&... args)
|
||||
{
|
||||
auto p = std::make_shared<T>(std::forward<Args>(args)...);
|
||||
return ref<T>(p);
|
||||
}
|
||||
|
||||
}
|
||||
} // namespace nix
|
||||
|
|
|
|||
|
|
@ -14,13 +14,17 @@ class RefScanSink : public Sink
|
|||
|
||||
public:
|
||||
|
||||
RefScanSink(StringSet && hashes) : hashes(hashes)
|
||||
{ }
|
||||
RefScanSink(StringSet && hashes)
|
||||
: hashes(hashes)
|
||||
{
|
||||
}
|
||||
|
||||
StringSet & getResult()
|
||||
{ return seen; }
|
||||
{
|
||||
return seen;
|
||||
}
|
||||
|
||||
void operator () (std::string_view data) override;
|
||||
void operator()(std::string_view data) override;
|
||||
};
|
||||
|
||||
struct RewritingSink : Sink
|
||||
|
|
@ -36,7 +40,7 @@ struct RewritingSink : Sink
|
|||
RewritingSink(const std::string & from, const std::string & to, Sink & nextSink);
|
||||
RewritingSink(const StringMap & rewrites, Sink & nextSink);
|
||||
|
||||
void operator () (std::string_view data) override;
|
||||
void operator()(std::string_view data) override;
|
||||
|
||||
void flush();
|
||||
};
|
||||
|
|
@ -48,9 +52,9 @@ struct HashModuloSink : AbstractHashSink
|
|||
|
||||
HashModuloSink(HashAlgorithm ha, const std::string & modulus);
|
||||
|
||||
void operator () (std::string_view data) override;
|
||||
void operator()(std::string_view data) override;
|
||||
|
||||
HashResult finish() override;
|
||||
};
|
||||
|
||||
}
|
||||
} // namespace nix
|
||||
|
|
|
|||
|
|
@ -31,4 +31,4 @@ static inline std::string list(std::string_view a)
|
|||
return ss.str();
|
||||
}
|
||||
|
||||
}
|
||||
} // namespace nix::regex
|
||||
|
|
|
|||
|
|
@ -1,4 +1,5 @@
|
|||
#pragma once
|
||||
|
||||
///@file
|
||||
|
||||
namespace nix {
|
||||
|
|
|
|||
|
|
@ -8,19 +8,25 @@
|
|||
#include "nix/util/util.hh"
|
||||
#include "nix/util/file-descriptor.hh"
|
||||
|
||||
namespace boost::context { struct stack_context; }
|
||||
namespace boost::context {
|
||||
struct stack_context;
|
||||
}
|
||||
|
||||
namespace nix {
|
||||
|
||||
|
||||
/**
|
||||
* Abstract destination of binary data.
|
||||
*/
|
||||
struct Sink
|
||||
{
|
||||
virtual ~Sink() { }
|
||||
virtual void operator () (std::string_view data) = 0;
|
||||
virtual bool good() { return true; }
|
||||
virtual ~Sink() {}
|
||||
|
||||
virtual void operator()(std::string_view data) = 0;
|
||||
|
||||
virtual bool good()
|
||||
{
|
||||
return true;
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
|
|
@ -28,17 +34,14 @@ struct Sink
|
|||
*/
|
||||
struct NullSink : Sink
|
||||
{
|
||||
void operator () (std::string_view data) override
|
||||
{ }
|
||||
void operator()(std::string_view data) override {}
|
||||
};
|
||||
|
||||
|
||||
struct FinishSink : virtual Sink
|
||||
{
|
||||
virtual void finish() = 0;
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* A buffered abstract sink. Warning: a BufferedSink should not be
|
||||
* used from multiple threads concurrently.
|
||||
|
|
@ -49,9 +52,13 @@ struct BufferedSink : virtual Sink
|
|||
std::unique_ptr<char[]> buffer;
|
||||
|
||||
BufferedSink(size_t bufSize = 32 * 1024)
|
||||
: bufSize(bufSize), bufPos(0), buffer(nullptr) { }
|
||||
: bufSize(bufSize)
|
||||
, bufPos(0)
|
||||
, buffer(nullptr)
|
||||
{
|
||||
}
|
||||
|
||||
void operator () (std::string_view data) override;
|
||||
void operator()(std::string_view data) override;
|
||||
|
||||
void flush();
|
||||
|
||||
|
|
@ -60,21 +67,20 @@ protected:
|
|||
virtual void writeUnbuffered(std::string_view data) = 0;
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Abstract source of binary data.
|
||||
*/
|
||||
struct Source
|
||||
{
|
||||
virtual ~Source() { }
|
||||
virtual ~Source() {}
|
||||
|
||||
/**
|
||||
* Store exactly ‘len’ bytes in the buffer pointed to by ‘data’.
|
||||
* It blocks until all the requested data is available, or throws
|
||||
* an error if it is not going to be available.
|
||||
*/
|
||||
void operator () (char * data, size_t len);
|
||||
void operator () (std::string_view data);
|
||||
void operator()(char * data, size_t len);
|
||||
void operator()(std::string_view data);
|
||||
|
||||
/**
|
||||
* Store up to ‘len’ in the buffer pointed to by ‘data’, and
|
||||
|
|
@ -83,14 +89,16 @@ struct Source
|
|||
*/
|
||||
virtual size_t read(char * data, size_t len) = 0;
|
||||
|
||||
virtual bool good() { return true; }
|
||||
virtual bool good()
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
void drainInto(Sink & sink);
|
||||
|
||||
std::string drain();
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* A buffered abstract source. Warning: a BufferedSource should not be
|
||||
* used from multiple threads concurrently.
|
||||
|
|
@ -101,7 +109,12 @@ struct BufferedSource : Source
|
|||
std::unique_ptr<char[]> buffer;
|
||||
|
||||
BufferedSource(size_t bufSize = 32 * 1024)
|
||||
: bufSize(bufSize), bufPosIn(0), bufPosOut(0), buffer(nullptr) { }
|
||||
: bufSize(bufSize)
|
||||
, bufPosIn(0)
|
||||
, bufPosOut(0)
|
||||
, buffer(nullptr)
|
||||
{
|
||||
}
|
||||
|
||||
size_t read(char * data, size_t len) override;
|
||||
|
||||
|
|
@ -117,7 +130,6 @@ protected:
|
|||
virtual size_t readUnbuffered(char * data, size_t len) = 0;
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* A sink that writes data to a file descriptor.
|
||||
*/
|
||||
|
|
@ -126,9 +138,17 @@ struct FdSink : BufferedSink
|
|||
Descriptor fd;
|
||||
size_t written = 0;
|
||||
|
||||
FdSink() : fd(INVALID_DESCRIPTOR) { }
|
||||
FdSink(Descriptor fd) : fd(fd) { }
|
||||
FdSink(FdSink&&) = default;
|
||||
FdSink()
|
||||
: fd(INVALID_DESCRIPTOR)
|
||||
{
|
||||
}
|
||||
|
||||
FdSink(Descriptor fd)
|
||||
: fd(fd)
|
||||
{
|
||||
}
|
||||
|
||||
FdSink(FdSink &&) = default;
|
||||
|
||||
FdSink & operator=(FdSink && s)
|
||||
{
|
||||
|
|
@ -149,7 +169,6 @@ private:
|
|||
bool _good = true;
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* A source that reads data from a file descriptor.
|
||||
*/
|
||||
|
|
@ -159,8 +178,16 @@ struct FdSource : BufferedSource
|
|||
size_t read = 0;
|
||||
BackedStringView endOfFileError{"unexpected end-of-file"};
|
||||
|
||||
FdSource() : fd(INVALID_DESCRIPTOR) { }
|
||||
FdSource(Descriptor fd) : fd(fd) { }
|
||||
FdSource()
|
||||
: fd(INVALID_DESCRIPTOR)
|
||||
{
|
||||
}
|
||||
|
||||
FdSource(Descriptor fd)
|
||||
: fd(fd)
|
||||
{
|
||||
}
|
||||
|
||||
FdSource(FdSource &&) = default;
|
||||
|
||||
FdSource & operator=(FdSource && s) = default;
|
||||
|
|
@ -179,22 +206,24 @@ private:
|
|||
bool _good = true;
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* A sink that writes data to a string.
|
||||
*/
|
||||
struct StringSink : Sink
|
||||
{
|
||||
std::string s;
|
||||
StringSink() { }
|
||||
|
||||
StringSink() {}
|
||||
|
||||
explicit StringSink(const size_t reservedSize)
|
||||
{
|
||||
s.reserve(reservedSize);
|
||||
s.reserve(reservedSize);
|
||||
};
|
||||
StringSink(std::string && s) : s(std::move(s)) { };
|
||||
void operator () (std::string_view data) override;
|
||||
};
|
||||
|
||||
StringSink(std::string && s)
|
||||
: s(std::move(s)) {};
|
||||
void operator()(std::string_view data) override;
|
||||
};
|
||||
|
||||
/**
|
||||
* A source that reads data from a string.
|
||||
|
|
@ -208,28 +237,41 @@ struct StringSource : Source
|
|||
// from std::string -> std::string_view occurs when the string is passed
|
||||
// by rvalue.
|
||||
StringSource(std::string &&) = delete;
|
||||
StringSource(std::string_view s) : s(s), pos(0) { }
|
||||
StringSource(const std::string& str): StringSource(std::string_view(str)) {}
|
||||
|
||||
StringSource(std::string_view s)
|
||||
: s(s)
|
||||
, pos(0)
|
||||
{
|
||||
}
|
||||
|
||||
StringSource(const std::string & str)
|
||||
: StringSource(std::string_view(str))
|
||||
{
|
||||
}
|
||||
|
||||
size_t read(char * data, size_t len) override;
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* A sink that writes all incoming data to two other sinks.
|
||||
*/
|
||||
struct TeeSink : Sink
|
||||
{
|
||||
Sink & sink1, & sink2;
|
||||
TeeSink(Sink & sink1, Sink & sink2) : sink1(sink1), sink2(sink2) { }
|
||||
virtual void operator () (std::string_view data) override
|
||||
Sink &sink1, &sink2;
|
||||
|
||||
TeeSink(Sink & sink1, Sink & sink2)
|
||||
: sink1(sink1)
|
||||
, sink2(sink2)
|
||||
{
|
||||
}
|
||||
|
||||
virtual void operator()(std::string_view data) override
|
||||
{
|
||||
sink1(data);
|
||||
sink2(data);
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Adapter class of a Source that saves all data read to a sink.
|
||||
*/
|
||||
|
|
@ -237,8 +279,13 @@ struct TeeSource : Source
|
|||
{
|
||||
Source & orig;
|
||||
Sink & sink;
|
||||
|
||||
TeeSource(Source & orig, Sink & sink)
|
||||
: orig(orig), sink(sink) { }
|
||||
: orig(orig)
|
||||
, sink(sink)
|
||||
{
|
||||
}
|
||||
|
||||
size_t read(char * data, size_t len) override
|
||||
{
|
||||
size_t n = orig.read(data, len);
|
||||
|
|
@ -254,8 +301,13 @@ struct SizedSource : Source
|
|||
{
|
||||
Source & orig;
|
||||
size_t remain;
|
||||
|
||||
SizedSource(Source & orig, size_t size)
|
||||
: orig(orig), remain(size) { }
|
||||
: orig(orig)
|
||||
, remain(size)
|
||||
{
|
||||
}
|
||||
|
||||
size_t read(char * data, size_t len) override
|
||||
{
|
||||
if (this->remain <= 0) {
|
||||
|
|
@ -289,7 +341,7 @@ struct LengthSink : Sink
|
|||
{
|
||||
uint64_t length = 0;
|
||||
|
||||
void operator () (std::string_view data) override
|
||||
void operator()(std::string_view data) override
|
||||
{
|
||||
length += data.size();
|
||||
}
|
||||
|
|
@ -302,8 +354,10 @@ struct LengthSource : Source
|
|||
{
|
||||
Source & next;
|
||||
|
||||
LengthSource(Source & next) : next(next)
|
||||
{ }
|
||||
LengthSource(Source & next)
|
||||
: next(next)
|
||||
{
|
||||
}
|
||||
|
||||
uint64_t total = 0;
|
||||
|
||||
|
|
@ -324,15 +378,17 @@ struct LambdaSink : Sink
|
|||
|
||||
lambda_t lambda;
|
||||
|
||||
LambdaSink(const lambda_t & lambda) : lambda(lambda) { }
|
||||
LambdaSink(const lambda_t & lambda)
|
||||
: lambda(lambda)
|
||||
{
|
||||
}
|
||||
|
||||
void operator () (std::string_view data) override
|
||||
void operator()(std::string_view data) override
|
||||
{
|
||||
lambda(data);
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Convert a function into a source.
|
||||
*/
|
||||
|
|
@ -342,7 +398,10 @@ struct LambdaSource : Source
|
|||
|
||||
lambda_t lambda;
|
||||
|
||||
LambdaSource(const lambda_t & lambda) : lambda(lambda) { }
|
||||
LambdaSource(const lambda_t & lambda)
|
||||
: lambda(lambda)
|
||||
{
|
||||
}
|
||||
|
||||
size_t read(char * data, size_t len) override
|
||||
{
|
||||
|
|
@ -356,11 +415,14 @@ struct LambdaSource : Source
|
|||
*/
|
||||
struct ChainSource : Source
|
||||
{
|
||||
Source & source1, & source2;
|
||||
Source &source1, &source2;
|
||||
bool useSecond = false;
|
||||
|
||||
ChainSource(Source & s1, Source & s2)
|
||||
: source1(s1), source2(s2)
|
||||
{ }
|
||||
: source1(s1)
|
||||
, source2(s2)
|
||||
{
|
||||
}
|
||||
|
||||
size_t read(char * data, size_t len) override;
|
||||
};
|
||||
|
|
@ -372,16 +434,12 @@ std::unique_ptr<FinishSink> sourceToSink(std::function<void(Source &)> fun);
|
|||
* Source executes the function as a coroutine.
|
||||
*/
|
||||
std::unique_ptr<Source> sinkToSource(
|
||||
std::function<void(Sink &)> fun,
|
||||
std::function<void()> eof = []() {
|
||||
throw EndOfFile("coroutine has finished");
|
||||
});
|
||||
|
||||
std::function<void(Sink &)> fun, std::function<void()> eof = []() { throw EndOfFile("coroutine has finished"); });
|
||||
|
||||
void writePadding(size_t len, Sink & sink);
|
||||
void writeString(std::string_view s, Sink & sink);
|
||||
|
||||
inline Sink & operator << (Sink & sink, uint64_t n)
|
||||
inline Sink & operator<<(Sink & sink, uint64_t n)
|
||||
{
|
||||
unsigned char buf[8];
|
||||
buf[0] = n & 0xff;
|
||||
|
|
@ -396,15 +454,13 @@ inline Sink & operator << (Sink & sink, uint64_t n)
|
|||
return sink;
|
||||
}
|
||||
|
||||
Sink & operator << (Sink & in, const Error & ex);
|
||||
Sink & operator << (Sink & sink, std::string_view s);
|
||||
Sink & operator << (Sink & sink, const Strings & s);
|
||||
Sink & operator << (Sink & sink, const StringSet & s);
|
||||
|
||||
Sink & operator<<(Sink & in, const Error & ex);
|
||||
Sink & operator<<(Sink & sink, std::string_view s);
|
||||
Sink & operator<<(Sink & sink, const Strings & s);
|
||||
Sink & operator<<(Sink & sink, const StringSet & s);
|
||||
|
||||
MakeError(SerialisationError, Error);
|
||||
|
||||
|
||||
template<typename T>
|
||||
T readNum(Source & source)
|
||||
{
|
||||
|
|
@ -419,35 +475,33 @@ T readNum(Source & source)
|
|||
return (T) n;
|
||||
}
|
||||
|
||||
|
||||
inline unsigned int readInt(Source & source)
|
||||
{
|
||||
return readNum<unsigned int>(source);
|
||||
}
|
||||
|
||||
|
||||
inline uint64_t readLongLong(Source & source)
|
||||
{
|
||||
return readNum<uint64_t>(source);
|
||||
}
|
||||
|
||||
|
||||
void readPadding(size_t len, Source & source);
|
||||
size_t readString(char * buf, size_t max, Source & source);
|
||||
std::string readString(Source & source, size_t max = std::numeric_limits<size_t>::max());
|
||||
template<class T> T readStrings(Source & source);
|
||||
template<class T>
|
||||
T readStrings(Source & source);
|
||||
|
||||
Source & operator >> (Source & in, std::string & s);
|
||||
Source & operator>>(Source & in, std::string & s);
|
||||
|
||||
template<typename T>
|
||||
Source & operator >> (Source & in, T & n)
|
||||
Source & operator>>(Source & in, T & n)
|
||||
{
|
||||
n = readNum<T>(in);
|
||||
return in;
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
Source & operator >> (Source & in, bool & b)
|
||||
Source & operator>>(Source & in, bool & b)
|
||||
{
|
||||
b = readNum<uint64_t>(in);
|
||||
return in;
|
||||
|
|
@ -455,7 +509,6 @@ Source & operator >> (Source & in, bool & b)
|
|||
|
||||
Error readError(Source & source);
|
||||
|
||||
|
||||
/**
|
||||
* An adapter that converts a std::basic_istream into a source.
|
||||
*/
|
||||
|
|
@ -465,7 +518,8 @@ struct StreamToSourceAdapter : Source
|
|||
|
||||
StreamToSourceAdapter(std::shared_ptr<std::basic_istream<char>> istream)
|
||||
: istream(istream)
|
||||
{ }
|
||||
{
|
||||
}
|
||||
|
||||
size_t read(char * data, size_t len) override
|
||||
{
|
||||
|
|
@ -480,7 +534,6 @@ struct StreamToSourceAdapter : Source
|
|||
}
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* A source that reads a distinct format of concatenated chunks back into its
|
||||
* logical form, in order to guarantee a known state to the original stream,
|
||||
|
|
@ -496,8 +549,10 @@ struct FramedSource : Source
|
|||
std::vector<char> pending;
|
||||
size_t pos = 0;
|
||||
|
||||
FramedSource(Source & from) : from(from)
|
||||
{ }
|
||||
FramedSource(Source & from)
|
||||
: from(from)
|
||||
{
|
||||
}
|
||||
|
||||
~FramedSource()
|
||||
{
|
||||
|
|
@ -505,7 +560,8 @@ struct FramedSource : Source
|
|||
if (!eof) {
|
||||
while (true) {
|
||||
auto n = readInt(from);
|
||||
if (!n) break;
|
||||
if (!n)
|
||||
break;
|
||||
std::vector<char> data(n);
|
||||
from(data.data(), n);
|
||||
}
|
||||
|
|
@ -517,7 +573,8 @@ struct FramedSource : Source
|
|||
|
||||
size_t read(char * data, size_t len) override
|
||||
{
|
||||
if (eof) throw EndOfFile("reached end of FramedSource");
|
||||
if (eof)
|
||||
throw EndOfFile("reached end of FramedSource");
|
||||
|
||||
if (pos >= pending.size()) {
|
||||
size_t len = readInt(from);
|
||||
|
|
@ -549,8 +606,10 @@ struct FramedSink : nix::BufferedSink
|
|||
std::function<void()> checkError;
|
||||
|
||||
FramedSink(BufferedSink & to, std::function<void()> && checkError)
|
||||
: to(to), checkError(checkError)
|
||||
{ }
|
||||
: to(to)
|
||||
, checkError(checkError)
|
||||
{
|
||||
}
|
||||
|
||||
~FramedSink()
|
||||
{
|
||||
|
|
@ -572,4 +631,4 @@ struct FramedSink : nix::BufferedSink
|
|||
};
|
||||
};
|
||||
|
||||
}
|
||||
} // namespace nix
|
||||
|
|
|
|||
|
|
@ -41,10 +41,9 @@ inline void checkInterrupt();
|
|||
*/
|
||||
MakeError(Interrupted, BaseError);
|
||||
|
||||
|
||||
struct InterruptCallback
|
||||
{
|
||||
virtual ~InterruptCallback() { };
|
||||
virtual ~InterruptCallback() {};
|
||||
};
|
||||
|
||||
/**
|
||||
|
|
@ -53,8 +52,7 @@ struct InterruptCallback
|
|||
*
|
||||
* @note Does nothing on Windows
|
||||
*/
|
||||
std::unique_ptr<InterruptCallback> createInterruptCallback(
|
||||
std::function<void()> callback);
|
||||
std::unique_ptr<InterruptCallback> createInterruptCallback(std::function<void()> callback);
|
||||
|
||||
/**
|
||||
* A RAII class that causes the current thread to receive SIGUSR1 when
|
||||
|
|
@ -65,6 +63,6 @@ std::unique_ptr<InterruptCallback> createInterruptCallback(
|
|||
*/
|
||||
struct ReceiveInterrupts;
|
||||
|
||||
}
|
||||
} // namespace nix
|
||||
|
||||
#include "nix/util/signals-impl.hh"
|
||||
|
|
|
|||
|
|
@ -15,7 +15,8 @@ namespace nix {
|
|||
* <name>:<key/signature-in-Base64>
|
||||
* ```
|
||||
*/
|
||||
struct BorrowedCryptoValue {
|
||||
struct BorrowedCryptoValue
|
||||
{
|
||||
std::string_view name;
|
||||
std::string_view payload;
|
||||
|
||||
|
|
@ -45,7 +46,10 @@ protected:
|
|||
Key(std::string_view s, bool sensitiveValue);
|
||||
|
||||
Key(std::string_view name, std::string && key)
|
||||
: name(name), key(std::move(key)) { }
|
||||
: name(name)
|
||||
, key(std::move(key))
|
||||
{
|
||||
}
|
||||
};
|
||||
|
||||
struct PublicKey;
|
||||
|
|
@ -65,7 +69,9 @@ struct SecretKey : Key
|
|||
|
||||
private:
|
||||
SecretKey(std::string_view name, std::string && key)
|
||||
: Key(name, std::move(key)) { }
|
||||
: Key(name, std::move(key))
|
||||
{
|
||||
}
|
||||
};
|
||||
|
||||
struct PublicKey : Key
|
||||
|
|
@ -89,7 +95,9 @@ struct PublicKey : Key
|
|||
|
||||
private:
|
||||
PublicKey(std::string_view name, std::string && key)
|
||||
: Key(name, std::move(key)) { }
|
||||
: Key(name, std::move(key))
|
||||
{
|
||||
}
|
||||
friend struct SecretKey;
|
||||
};
|
||||
|
||||
|
|
@ -104,4 +112,4 @@ typedef std::map<std::string, PublicKey> PublicKeys;
|
|||
*/
|
||||
bool verifyDetached(std::string_view data, std::string_view sig, const PublicKeys & publicKeys);
|
||||
|
||||
}
|
||||
} // namespace nix
|
||||
|
|
|
|||
|
|
@ -37,7 +37,7 @@ struct Signer
|
|||
virtual const PublicKey & getPublicKey() = 0;
|
||||
};
|
||||
|
||||
using Signers = std::map<std::string, Signer*>;
|
||||
using Signers = std::map<std::string, Signer *>;
|
||||
|
||||
/**
|
||||
* Local signer
|
||||
|
|
@ -58,4 +58,4 @@ private:
|
|||
PublicKey publicKey;
|
||||
};
|
||||
|
||||
}
|
||||
} // namespace nix
|
||||
|
|
|
|||
|
|
@ -296,4 +296,4 @@ void peeksort(Iter begin, Iter end, Comparator comp = {})
|
|||
peeksortImpl(peeksortImpl, begin, end, /*leftRunEnd=*/begin, /*rightRunBegin=*/end);
|
||||
}
|
||||
|
||||
}
|
||||
} // namespace nix
|
||||
|
|
|
|||
|
|
@ -46,8 +46,7 @@ struct SourceAccessor : std::enable_shared_from_this<SourceAccessor>
|
|||
|
||||
SourceAccessor();
|
||||
|
||||
virtual ~SourceAccessor()
|
||||
{ }
|
||||
virtual ~SourceAccessor() {}
|
||||
|
||||
/**
|
||||
* Return the contents of a file as a string.
|
||||
|
|
@ -72,24 +71,28 @@ struct SourceAccessor : std::enable_shared_from_this<SourceAccessor>
|
|||
* @note subclasses of `SourceAccessor` need to implement at least
|
||||
* one of the `readFile()` variants.
|
||||
*/
|
||||
virtual void readFile(
|
||||
const CanonPath & path,
|
||||
Sink & sink,
|
||||
std::function<void(uint64_t)> sizeCallback = [](uint64_t size){});
|
||||
virtual void
|
||||
readFile(const CanonPath & path, Sink & sink, std::function<void(uint64_t)> sizeCallback = [](uint64_t size) {});
|
||||
|
||||
virtual bool pathExists(const CanonPath & path);
|
||||
|
||||
enum Type {
|
||||
tRegular, tSymlink, tDirectory,
|
||||
/**
|
||||
Any other node types that may be encountered on the file system, such as device nodes, sockets, named pipe, and possibly even more exotic things.
|
||||
tRegular,
|
||||
tSymlink,
|
||||
tDirectory,
|
||||
/**
|
||||
Any other node types that may be encountered on the file system, such as device nodes, sockets, named pipe,
|
||||
and possibly even more exotic things.
|
||||
|
||||
Responsible for `"unknown"` from `builtins.readFileType "/dev/null"`.
|
||||
Responsible for `"unknown"` from `builtins.readFileType "/dev/null"`.
|
||||
|
||||
Unlike `DT_UNKNOWN`, this must not be used for deferring the lookup of types.
|
||||
*/
|
||||
tChar, tBlock, tSocket, tFifo,
|
||||
tUnknown
|
||||
Unlike `DT_UNKNOWN`, this must not be used for deferring the lookup of types.
|
||||
*/
|
||||
tChar,
|
||||
tBlock,
|
||||
tSocket,
|
||||
tFifo,
|
||||
tUnknown
|
||||
};
|
||||
|
||||
struct Stat
|
||||
|
|
@ -133,15 +136,10 @@ struct SourceAccessor : std::enable_shared_from_this<SourceAccessor>
|
|||
|
||||
virtual std::string readLink(const CanonPath & path) = 0;
|
||||
|
||||
virtual void dumpPath(
|
||||
const CanonPath & path,
|
||||
Sink & sink,
|
||||
PathFilter & filter = defaultPathFilter);
|
||||
virtual void dumpPath(const CanonPath & path, Sink & sink, PathFilter & filter = defaultPathFilter);
|
||||
|
||||
Hash hashPath(
|
||||
const CanonPath & path,
|
||||
PathFilter & filter = defaultPathFilter,
|
||||
HashAlgorithm ha = HashAlgorithm::SHA256);
|
||||
Hash
|
||||
hashPath(const CanonPath & path, PathFilter & filter = defaultPathFilter, HashAlgorithm ha = HashAlgorithm::SHA256);
|
||||
|
||||
/**
|
||||
* Return a corresponding path in the root filesystem, if
|
||||
|
|
@ -149,14 +147,16 @@ struct SourceAccessor : std::enable_shared_from_this<SourceAccessor>
|
|||
* materialized in the root filesystem.
|
||||
*/
|
||||
virtual std::optional<std::filesystem::path> getPhysicalPath(const CanonPath & path)
|
||||
{ return std::nullopt; }
|
||||
{
|
||||
return std::nullopt;
|
||||
}
|
||||
|
||||
bool operator == (const SourceAccessor & x) const
|
||||
bool operator==(const SourceAccessor & x) const
|
||||
{
|
||||
return number == x.number;
|
||||
}
|
||||
|
||||
auto operator <=> (const SourceAccessor & x) const
|
||||
auto operator<=>(const SourceAccessor & x) const
|
||||
{
|
||||
return number <=> x.number;
|
||||
}
|
||||
|
|
@ -172,9 +172,7 @@ struct SourceAccessor : std::enable_shared_from_this<SourceAccessor>
|
|||
* @param mode might only be a temporary solution for this.
|
||||
* See the discussion in https://github.com/NixOS/nix/pull/9985.
|
||||
*/
|
||||
CanonPath resolveSymlinks(
|
||||
const CanonPath & path,
|
||||
SymlinkResolution mode = SymlinkResolution::Full);
|
||||
CanonPath resolveSymlinks(const CanonPath & path, SymlinkResolution mode = SymlinkResolution::Full);
|
||||
|
||||
/**
|
||||
* A string that uniquely represents the contents of this
|
||||
|
|
@ -187,7 +185,9 @@ struct SourceAccessor : std::enable_shared_from_this<SourceAccessor>
|
|||
* tree, if available.
|
||||
*/
|
||||
virtual std::optional<time_t> getLastModified()
|
||||
{ return std::nullopt; }
|
||||
{
|
||||
return std::nullopt;
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
|
|
@ -228,4 +228,4 @@ ref<SourceAccessor> makeUnionSourceAccessor(std::vector<ref<SourceAccessor>> &&
|
|||
*/
|
||||
ref<SourceAccessor> projectSubdirSourceAccessor(ref<SourceAccessor>, CanonPath subdirectory);
|
||||
|
||||
}
|
||||
} // namespace nix
|
||||
|
|
|
|||
|
|
@ -26,7 +26,8 @@ struct SourcePath
|
|||
SourcePath(ref<SourceAccessor> accessor, CanonPath path = CanonPath::root)
|
||||
: accessor(std::move(accessor))
|
||||
, path(std::move(path))
|
||||
{ }
|
||||
{
|
||||
}
|
||||
|
||||
std::string_view baseName() const;
|
||||
|
||||
|
|
@ -42,15 +43,15 @@ struct SourcePath
|
|||
*/
|
||||
std::string readFile() const;
|
||||
|
||||
void readFile(
|
||||
Sink & sink,
|
||||
std::function<void(uint64_t)> sizeCallback = [](uint64_t size){}) const
|
||||
{ return accessor->readFile(path, sink, sizeCallback); }
|
||||
void readFile(Sink & sink, std::function<void(uint64_t)> sizeCallback = [](uint64_t size) {}) const
|
||||
{
|
||||
return accessor->readFile(path, sink, sizeCallback);
|
||||
}
|
||||
|
||||
/**
|
||||
* Return whether this `SourcePath` denotes a file (of any type)
|
||||
* that exists
|
||||
*/
|
||||
*/
|
||||
bool pathExists() const;
|
||||
|
||||
/**
|
||||
|
|
@ -80,9 +81,7 @@ struct SourcePath
|
|||
/**
|
||||
* Dump this `SourcePath` to `sink` as a NAR archive.
|
||||
*/
|
||||
void dumpPath(
|
||||
Sink & sink,
|
||||
PathFilter & filter = defaultPathFilter) const;
|
||||
void dumpPath(Sink & sink, PathFilter & filter = defaultPathFilter) const;
|
||||
|
||||
/**
|
||||
* Return the location of this path in the "real" filesystem, if
|
||||
|
|
@ -95,14 +94,14 @@ struct SourcePath
|
|||
/**
|
||||
* Append a `CanonPath` to this path.
|
||||
*/
|
||||
SourcePath operator / (const CanonPath & x) const;
|
||||
SourcePath operator/(const CanonPath & x) const;
|
||||
|
||||
/**
|
||||
* Append a single component `c` to this path. `c` must not
|
||||
* contain a slash. A slash is implicitly added between this path
|
||||
* and `c`.
|
||||
*/
|
||||
SourcePath operator / (std::string_view c) const;
|
||||
SourcePath operator/(std::string_view c) const;
|
||||
|
||||
bool operator==(const SourcePath & x) const noexcept;
|
||||
std::strong_ordering operator<=>(const SourcePath & x) const noexcept;
|
||||
|
|
@ -110,8 +109,7 @@ struct SourcePath
|
|||
/**
|
||||
* Convenience wrapper around `SourceAccessor::resolveSymlinks()`.
|
||||
*/
|
||||
SourcePath resolveSymlinks(
|
||||
SymlinkResolution mode = SymlinkResolution::Full) const
|
||||
SourcePath resolveSymlinks(SymlinkResolution mode = SymlinkResolution::Full) const
|
||||
{
|
||||
return {accessor, accessor->resolveSymlinks(path, mode)};
|
||||
}
|
||||
|
|
@ -119,9 +117,9 @@ struct SourcePath
|
|||
friend class std::hash<nix::SourcePath>;
|
||||
};
|
||||
|
||||
std::ostream & operator << (std::ostream & str, const SourcePath & path);
|
||||
std::ostream & operator<<(std::ostream & str, const SourcePath & path);
|
||||
|
||||
}
|
||||
} // namespace nix
|
||||
|
||||
template<>
|
||||
struct std::hash<nix::SourcePath>
|
||||
|
|
|
|||
|
|
@ -14,23 +14,25 @@ namespace nix {
|
|||
* separator. Otherwise, we return `std::nullopt`, and we leave the argument
|
||||
* string alone.
|
||||
*/
|
||||
static inline std::optional<std::string_view> splitPrefixTo(std::string_view & string, char separator) {
|
||||
static inline std::optional<std::string_view> splitPrefixTo(std::string_view & string, char separator)
|
||||
{
|
||||
auto sepInstance = string.find(separator);
|
||||
|
||||
if (sepInstance != std::string_view::npos) {
|
||||
auto prefix = string.substr(0, sepInstance);
|
||||
string.remove_prefix(sepInstance+1);
|
||||
string.remove_prefix(sepInstance + 1);
|
||||
return prefix;
|
||||
}
|
||||
|
||||
return std::nullopt;
|
||||
}
|
||||
|
||||
static inline bool splitPrefix(std::string_view & string, std::string_view prefix) {
|
||||
static inline bool splitPrefix(std::string_view & string, std::string_view prefix)
|
||||
{
|
||||
bool res = hasPrefix(string, prefix);
|
||||
if (res)
|
||||
string.remove_prefix(prefix.length());
|
||||
return res;
|
||||
}
|
||||
|
||||
}
|
||||
} // namespace nix
|
||||
|
|
|
|||
|
|
@ -132,4 +132,4 @@ public:
|
|||
}
|
||||
};
|
||||
|
||||
}
|
||||
} // namespace nix
|
||||
|
|
|
|||
|
|
@ -11,7 +11,8 @@ int levenshteinDistance(std::string_view first, std::string_view second);
|
|||
/**
|
||||
* A potential suggestion for the cli interface.
|
||||
*/
|
||||
class Suggestion {
|
||||
class Suggestion
|
||||
{
|
||||
public:
|
||||
/// The smaller the better
|
||||
int distance;
|
||||
|
|
@ -19,27 +20,22 @@ public:
|
|||
|
||||
std::string to_string() const;
|
||||
|
||||
bool operator ==(const Suggestion &) const = default;
|
||||
auto operator <=>(const Suggestion &) const = default;
|
||||
bool operator==(const Suggestion &) const = default;
|
||||
auto operator<=>(const Suggestion &) const = default;
|
||||
};
|
||||
|
||||
class Suggestions {
|
||||
class Suggestions
|
||||
{
|
||||
public:
|
||||
std::set<Suggestion> suggestions;
|
||||
|
||||
std::string to_string() const;
|
||||
|
||||
Suggestions trim(
|
||||
int limit = 5,
|
||||
int maxDistance = 2
|
||||
) const;
|
||||
Suggestions trim(int limit = 5, int maxDistance = 2) const;
|
||||
|
||||
static Suggestions bestMatches (
|
||||
const StringSet & allMatches,
|
||||
std::string_view query
|
||||
);
|
||||
static Suggestions bestMatches(const StringSet & allMatches, std::string_view query);
|
||||
|
||||
Suggestions& operator+=(const Suggestions & other);
|
||||
Suggestions & operator+=(const Suggestions & other);
|
||||
};
|
||||
|
||||
std::ostream & operator<<(std::ostream & str, const Suggestion &);
|
||||
|
|
@ -49,18 +45,19 @@ std::ostream & operator<<(std::ostream & str, const Suggestions &);
|
|||
* Either a value of type `T`, or some suggestions
|
||||
*/
|
||||
template<typename T>
|
||||
class OrSuggestions {
|
||||
class OrSuggestions
|
||||
{
|
||||
public:
|
||||
using Raw = std::variant<T, Suggestions>;
|
||||
|
||||
Raw raw;
|
||||
|
||||
T* operator ->()
|
||||
T * operator->()
|
||||
{
|
||||
return &**this;
|
||||
}
|
||||
|
||||
T& operator *()
|
||||
T & operator*()
|
||||
{
|
||||
return std::get<T>(raw);
|
||||
}
|
||||
|
|
@ -100,7 +97,6 @@ public:
|
|||
else
|
||||
return noSuggestions;
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
}
|
||||
} // namespace nix
|
||||
|
|
|
|||
|
|
@ -36,10 +36,22 @@ private:
|
|||
|
||||
public:
|
||||
|
||||
SyncBase() { }
|
||||
SyncBase(const T & data) : data(data) { }
|
||||
SyncBase(T && data) noexcept : data(std::move(data)) { }
|
||||
SyncBase(SyncBase && other) noexcept : data(std::move(*other.lock())) { }
|
||||
SyncBase() {}
|
||||
|
||||
SyncBase(const T & data)
|
||||
: data(data)
|
||||
{
|
||||
}
|
||||
|
||||
SyncBase(T && data) noexcept
|
||||
: data(std::move(data))
|
||||
{
|
||||
}
|
||||
|
||||
SyncBase(SyncBase && other) noexcept
|
||||
: data(std::move(*other.lock()))
|
||||
{
|
||||
}
|
||||
|
||||
template<class L>
|
||||
class Lock
|
||||
|
|
@ -48,11 +60,22 @@ public:
|
|||
SyncBase * s;
|
||||
L lk;
|
||||
friend SyncBase;
|
||||
Lock(SyncBase * s) : s(s), lk(s->mutex) { }
|
||||
|
||||
Lock(SyncBase * s)
|
||||
: s(s)
|
||||
, lk(s->mutex)
|
||||
{
|
||||
}
|
||||
public:
|
||||
Lock(Lock && l) : s(l.s) { unreachable(); }
|
||||
Lock(Lock && l)
|
||||
: s(l.s)
|
||||
{
|
||||
unreachable();
|
||||
}
|
||||
|
||||
Lock(const Lock & l) = delete;
|
||||
~Lock() { }
|
||||
|
||||
~Lock() {}
|
||||
|
||||
void wait(std::condition_variable & cv)
|
||||
{
|
||||
|
|
@ -61,25 +84,22 @@ public:
|
|||
}
|
||||
|
||||
template<class Rep, class Period>
|
||||
std::cv_status wait_for(std::condition_variable & cv,
|
||||
const std::chrono::duration<Rep, Period> & duration)
|
||||
std::cv_status wait_for(std::condition_variable & cv, const std::chrono::duration<Rep, Period> & duration)
|
||||
{
|
||||
assert(s);
|
||||
return cv.wait_for(lk, duration);
|
||||
}
|
||||
|
||||
template<class Rep, class Period, class Predicate>
|
||||
bool wait_for(std::condition_variable & cv,
|
||||
const std::chrono::duration<Rep, Period> & duration,
|
||||
Predicate pred)
|
||||
bool wait_for(std::condition_variable & cv, const std::chrono::duration<Rep, Period> & duration, Predicate pred)
|
||||
{
|
||||
assert(s);
|
||||
return cv.wait_for(lk, duration, pred);
|
||||
}
|
||||
|
||||
template<class Clock, class Duration>
|
||||
std::cv_status wait_until(std::condition_variable & cv,
|
||||
const std::chrono::time_point<Clock, Duration> & duration)
|
||||
std::cv_status
|
||||
wait_until(std::condition_variable & cv, const std::chrono::time_point<Clock, Duration> & duration)
|
||||
{
|
||||
assert(s);
|
||||
return cv.wait_until(lk, duration);
|
||||
|
|
@ -88,32 +108,53 @@ public:
|
|||
|
||||
struct WriteLock : Lock<WL>
|
||||
{
|
||||
T * operator -> () { return &WriteLock::s->data; }
|
||||
T & operator * () { return WriteLock::s->data; }
|
||||
T * operator->()
|
||||
{
|
||||
return &WriteLock::s->data;
|
||||
}
|
||||
|
||||
T & operator*()
|
||||
{
|
||||
return WriteLock::s->data;
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Acquire write (exclusive) access to the inner value.
|
||||
*/
|
||||
WriteLock lock() { return WriteLock(this); }
|
||||
WriteLock lock()
|
||||
{
|
||||
return WriteLock(this);
|
||||
}
|
||||
|
||||
struct ReadLock : Lock<RL>
|
||||
{
|
||||
const T * operator -> () { return &ReadLock::s->data; }
|
||||
const T & operator * () { return ReadLock::s->data; }
|
||||
const T * operator->()
|
||||
{
|
||||
return &ReadLock::s->data;
|
||||
}
|
||||
|
||||
const T & operator*()
|
||||
{
|
||||
return ReadLock::s->data;
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Acquire read access to the inner value. When using
|
||||
* `std::shared_mutex`, this will use a shared lock.
|
||||
*/
|
||||
ReadLock readLock() const { return ReadLock(const_cast<SyncBase *>(this)); }
|
||||
ReadLock readLock() const
|
||||
{
|
||||
return ReadLock(const_cast<SyncBase *>(this));
|
||||
}
|
||||
};
|
||||
|
||||
template<class T>
|
||||
using Sync = SyncBase<T, std::mutex, std::unique_lock<std::mutex>, std::unique_lock<std::mutex>>;
|
||||
|
||||
template<class T>
|
||||
using SharedSync = SyncBase<T, std::shared_mutex, std::unique_lock<std::shared_mutex>, std::shared_lock<std::shared_mutex>>;
|
||||
using SharedSync =
|
||||
SyncBase<T, std::shared_mutex, std::unique_lock<std::shared_mutex>, std::shared_lock<std::shared_mutex>>;
|
||||
|
||||
}
|
||||
} // namespace nix
|
||||
|
|
|
|||
|
|
@ -43,4 +43,4 @@ void unpackTarfile(const std::filesystem::path & tarFile, const std::filesystem:
|
|||
|
||||
time_t unpackTarfileToSink(TarArchive & archive, ExtendedFileSystemObjectSink & parseSink);
|
||||
|
||||
}
|
||||
} // namespace nix
|
||||
|
|
|
|||
|
|
@ -18,9 +18,8 @@ bool isTTY();
|
|||
* included in the character count. Also, tabs are expanded to
|
||||
* spaces.
|
||||
*/
|
||||
std::string filterANSIEscapes(std::string_view s,
|
||||
bool filterAll = false,
|
||||
unsigned int width = std::numeric_limits<unsigned int>::max());
|
||||
std::string filterANSIEscapes(
|
||||
std::string_view s, bool filterAll = false, unsigned int width = std::numeric_limits<unsigned int>::max());
|
||||
|
||||
/**
|
||||
* Recalculate the window size, updating a global variable.
|
||||
|
|
@ -37,4 +36,4 @@ void updateWindowSize();
|
|||
*/
|
||||
std::pair<unsigned short, unsigned short> getWindowSize();
|
||||
|
||||
}
|
||||
} // namespace nix
|
||||
|
|
|
|||
|
|
@ -87,7 +87,8 @@ void processGraph(
|
|||
std::function<std::set<T>(const T &)> getEdges,
|
||||
std::function<void(const T &)> processNode)
|
||||
{
|
||||
struct Graph {
|
||||
struct Graph
|
||||
{
|
||||
std::set<T> left;
|
||||
std::map<T, std::set<T>> refs, rrefs;
|
||||
};
|
||||
|
|
@ -101,7 +102,6 @@ void processGraph(
|
|||
ThreadPool pool;
|
||||
|
||||
worker = [&](const T & node) {
|
||||
|
||||
{
|
||||
auto graph(graph_.lock());
|
||||
auto i = graph->refs.find(node);
|
||||
|
|
@ -110,22 +110,21 @@ void processGraph(
|
|||
goto doWork;
|
||||
}
|
||||
|
||||
getRefs:
|
||||
{
|
||||
auto refs = getEdges(node);
|
||||
refs.erase(node);
|
||||
getRefs: {
|
||||
auto refs = getEdges(node);
|
||||
refs.erase(node);
|
||||
|
||||
{
|
||||
auto graph(graph_.lock());
|
||||
for (auto & ref : refs)
|
||||
if (graph->left.count(ref)) {
|
||||
graph->refs[node].insert(ref);
|
||||
graph->rrefs[ref].insert(node);
|
||||
}
|
||||
if (graph->refs[node].empty())
|
||||
goto doWork;
|
||||
}
|
||||
{
|
||||
auto graph(graph_.lock());
|
||||
for (auto & ref : refs)
|
||||
if (graph->left.count(ref)) {
|
||||
graph->refs[node].insert(ref);
|
||||
graph->rrefs[ref].insert(node);
|
||||
}
|
||||
if (graph->refs[node].empty())
|
||||
goto doWork;
|
||||
}
|
||||
}
|
||||
|
||||
return;
|
||||
|
||||
|
|
@ -167,4 +166,4 @@ void processGraph(
|
|||
throw Error("graph processing incomplete (cyclic reference?)");
|
||||
}
|
||||
|
||||
}
|
||||
} // namespace nix
|
||||
|
|
|
|||
|
|
@ -6,9 +6,10 @@
|
|||
namespace nix {
|
||||
|
||||
template<typename T, typename Compare>
|
||||
std::vector<T> topoSort(std::set<T, Compare> items,
|
||||
std::function<std::set<T, Compare>(const T &)> getChildren,
|
||||
std::function<Error(const T &, const T &)> makeCycleError)
|
||||
std::vector<T> topoSort(
|
||||
std::set<T, Compare> items,
|
||||
std::function<std::set<T, Compare>(const T &)> getChildren,
|
||||
std::function<Error(const T &, const T &)> makeCycleError)
|
||||
{
|
||||
std::vector<T> sorted;
|
||||
decltype(items) visited, parents;
|
||||
|
|
@ -16,9 +17,11 @@ std::vector<T> topoSort(std::set<T, Compare> items,
|
|||
std::function<void(const T & path, const T * parent)> dfsVisit;
|
||||
|
||||
dfsVisit = [&](const T & path, const T * parent) {
|
||||
if (parents.count(path)) throw makeCycleError(path, *parent);
|
||||
if (parents.count(path))
|
||||
throw makeCycleError(path, *parent);
|
||||
|
||||
if (!visited.insert(path).second) return;
|
||||
if (!visited.insert(path).second)
|
||||
return;
|
||||
parents.insert(path);
|
||||
|
||||
auto references = getChildren(path);
|
||||
|
|
@ -40,4 +43,4 @@ std::vector<T> topoSort(std::set<T, Compare> items,
|
|||
return sorted;
|
||||
}
|
||||
|
||||
}
|
||||
} // namespace nix
|
||||
|
|
|
|||
|
|
@ -1,7 +1,6 @@
|
|||
#pragma once
|
||||
///@file
|
||||
|
||||
|
||||
#include <list>
|
||||
#include <set>
|
||||
#include <string>
|
||||
|
|
@ -67,7 +66,10 @@ typedef std::vector<std::pair<std::string, std::string>> Headers;
|
|||
template<typename T>
|
||||
struct OnStartup
|
||||
{
|
||||
OnStartup(T && t) { t(); }
|
||||
OnStartup(T && t)
|
||||
{
|
||||
t();
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
|
|
@ -75,18 +77,18 @@ struct OnStartup
|
|||
* cast to a bool in Attr.
|
||||
*/
|
||||
template<typename T>
|
||||
struct Explicit {
|
||||
struct Explicit
|
||||
{
|
||||
T t;
|
||||
|
||||
bool operator ==(const Explicit<T> & other) const = default;
|
||||
bool operator==(const Explicit<T> & other) const = default;
|
||||
|
||||
bool operator <(const Explicit<T> & other) const
|
||||
bool operator<(const Explicit<T> & other) const
|
||||
{
|
||||
return t < other.t;
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* This wants to be a little bit like rust's Cow type.
|
||||
* Some parts of the evaluator benefit greatly from being able to reuse
|
||||
|
|
@ -97,7 +99,8 @@ struct Explicit {
|
|||
* since those can easily become ambiguous to the reader and can degrade
|
||||
* into copying behaviour we want to avoid.
|
||||
*/
|
||||
class BackedStringView {
|
||||
class BackedStringView
|
||||
{
|
||||
private:
|
||||
std::variant<std::string, std::string_view> data;
|
||||
|
||||
|
|
@ -106,19 +109,38 @@ private:
|
|||
* a pointer. Without this we'd need to store the view object
|
||||
* even when we already own a string.
|
||||
*/
|
||||
class Ptr {
|
||||
class Ptr
|
||||
{
|
||||
private:
|
||||
std::string_view view;
|
||||
public:
|
||||
Ptr(std::string_view view): view(view) {}
|
||||
const std::string_view * operator->() const { return &view; }
|
||||
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) {}
|
||||
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 char (&lit)[N])
|
||||
: data(std::string_view(lit))
|
||||
{
|
||||
}
|
||||
|
||||
BackedStringView(const BackedStringView &) = delete;
|
||||
BackedStringView & operator=(const BackedStringView &) = delete;
|
||||
|
|
@ -137,18 +159,18 @@ public:
|
|||
|
||||
std::string toOwned() &&
|
||||
{
|
||||
return isOwned()
|
||||
? std::move(std::get<std::string>(data))
|
||||
: std::string(std::get<std::string_view>(data));
|
||||
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);
|
||||
return isOwned() ? std::get<std::string>(data) : std::get<std::string_view>(data);
|
||||
}
|
||||
|
||||
Ptr operator->() const
|
||||
{
|
||||
return Ptr(**this);
|
||||
}
|
||||
Ptr operator->() const { return Ptr(**this); }
|
||||
};
|
||||
|
||||
}
|
||||
} // namespace nix
|
||||
|
|
|
|||
|
|
@ -87,4 +87,4 @@ void connect(Socket fd, const std::filesystem::path & path);
|
|||
*/
|
||||
AutoCloseFD connect(const std::filesystem::path & path);
|
||||
|
||||
}
|
||||
} // namespace nix
|
||||
|
|
|
|||
|
|
@ -33,7 +33,8 @@ extern std::regex refRegex;
|
|||
/// Instead of defining what a good Git Ref is, we define what a bad Git Ref is
|
||||
/// This is because of the definition of a ref in refs.c in https://github.com/git/git
|
||||
/// See tests/functional/fetchGitRefs.sh for the full definition
|
||||
const static std::string badGitRefRegexS = "//|^[./]|/\\.|\\.\\.|[[:cntrl:][:space:]:?^~\[]|\\\\|\\*|\\.lock$|\\.lock/|@\\{|[/.]$|^@$|^$";
|
||||
const static std::string badGitRefRegexS =
|
||||
"//|^[./]|/\\.|\\.\\.|[[:cntrl:][:space:]:?^~\[]|\\\\|\\*|\\.lock$|\\.lock/|@\\{|[/.]$|^@$|^$";
|
||||
extern std::regex badGitRefRegex;
|
||||
|
||||
/// A Git revision (a SHA-1 commit hash).
|
||||
|
|
@ -43,4 +44,4 @@ extern std::regex revRegex;
|
|||
/// A ref or revision, or a ref followed by a revision.
|
||||
const static std::string refAndOrRevRegex = "(?:(" + revRegexS + ")|(?:(" + refRegexS + ")(?:/(" + revRegexS + "))?))";
|
||||
|
||||
}
|
||||
} // namespace nix
|
||||
|
|
|
|||
|
|
@ -15,7 +15,7 @@ struct ParsedURL
|
|||
|
||||
std::string to_string() const;
|
||||
|
||||
bool operator ==(const ParsedURL & other) const noexcept;
|
||||
bool operator==(const ParsedURL & other) const noexcept;
|
||||
|
||||
/**
|
||||
* Remove `.` and `..` path elements.
|
||||
|
|
@ -23,12 +23,12 @@ struct ParsedURL
|
|||
ParsedURL canonicalise();
|
||||
};
|
||||
|
||||
std::ostream & operator << (std::ostream & os, const ParsedURL & url);
|
||||
std::ostream & operator<<(std::ostream & os, const ParsedURL & url);
|
||||
|
||||
MakeError(BadURL, Error);
|
||||
|
||||
std::string percentDecode(std::string_view in);
|
||||
std::string percentEncode(std::string_view s, std::string_view keep="");
|
||||
std::string percentEncode(std::string_view s, std::string_view keep = "");
|
||||
|
||||
StringMap decodeQuery(const std::string & query);
|
||||
|
||||
|
|
@ -44,7 +44,8 @@ ParsedURL parseURL(const std::string & url);
|
|||
* For example git uses `git+https` to designate remotes using a Git
|
||||
* protocol over http.
|
||||
*/
|
||||
struct ParsedUrlScheme {
|
||||
struct ParsedUrlScheme
|
||||
{
|
||||
std::optional<std::string_view> application;
|
||||
std::string_view transport;
|
||||
};
|
||||
|
|
@ -65,4 +66,4 @@ std::string fixGitURL(const std::string & url);
|
|||
*/
|
||||
bool isValidSchemeName(std::string_view scheme);
|
||||
|
||||
}
|
||||
} // namespace nix
|
||||
|
|
|
|||
|
|
@ -4,7 +4,7 @@
|
|||
#include "nix/util/types.hh"
|
||||
|
||||
#ifndef _WIN32
|
||||
# include <sys/types.h>
|
||||
# include <sys/types.h>
|
||||
#endif
|
||||
|
||||
namespace nix {
|
||||
|
|
@ -59,7 +59,6 @@ Path createNixStateDir();
|
|||
*/
|
||||
std::string expandTilde(std::string_view path);
|
||||
|
||||
|
||||
/**
|
||||
* Is the current user UID 0 on Unix?
|
||||
*
|
||||
|
|
@ -67,4 +66,4 @@ std::string expandTilde(std::string_view path);
|
|||
*/
|
||||
bool isRootUser();
|
||||
|
||||
}
|
||||
} // namespace nix
|
||||
|
|
|
|||
|
|
@ -5,7 +5,6 @@
|
|||
#include "nix/util/error.hh"
|
||||
#include "nix/util/logging.hh"
|
||||
|
||||
|
||||
#include <functional>
|
||||
#include <map>
|
||||
#include <sstream>
|
||||
|
|
@ -24,10 +23,8 @@ void initLibUtil();
|
|||
*/
|
||||
std::vector<char *> stringsToCharPtrs(const Strings & ss);
|
||||
|
||||
|
||||
MakeError(FormatError, Error);
|
||||
|
||||
|
||||
template<class... Parts>
|
||||
auto concatStrings(Parts &&... parts)
|
||||
-> std::enable_if_t<(... && std::is_convertible_v<Parts, std::string_view>), std::string>
|
||||
|
|
@ -36,11 +33,11 @@ auto concatStrings(Parts &&... parts)
|
|||
return concatStringsSep({}, views);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Add quotes around a collection of strings.
|
||||
*/
|
||||
template<class C> Strings quoteStrings(const C & c)
|
||||
template<class C>
|
||||
Strings quoteStrings(const C & c)
|
||||
{
|
||||
Strings res;
|
||||
for (auto & s : c)
|
||||
|
|
@ -55,25 +52,18 @@ template<class C> Strings quoteStrings(const C & c)
|
|||
*/
|
||||
std::string chomp(std::string_view s);
|
||||
|
||||
|
||||
/**
|
||||
* Remove whitespace from the start and end of a string.
|
||||
*/
|
||||
std::string trim(std::string_view s, std::string_view whitespace = " \n\r\t");
|
||||
|
||||
|
||||
/**
|
||||
* Replace all occurrences of a string inside another string.
|
||||
*/
|
||||
std::string replaceStrings(
|
||||
std::string s,
|
||||
std::string_view from,
|
||||
std::string_view to);
|
||||
|
||||
std::string replaceStrings(std::string s, std::string_view from, std::string_view to);
|
||||
|
||||
std::string rewriteStrings(std::string s, const StringMap & rewrites);
|
||||
|
||||
|
||||
/**
|
||||
* Parse a string into an integer.
|
||||
*/
|
||||
|
|
@ -91,11 +81,16 @@ N string2IntWithUnitPrefix(std::string_view s)
|
|||
if (!s.empty()) {
|
||||
char u = std::toupper(*s.rbegin());
|
||||
if (std::isalpha(u)) {
|
||||
if (u == 'K') multiplier = 1ULL << 10;
|
||||
else if (u == 'M') multiplier = 1ULL << 20;
|
||||
else if (u == 'G') multiplier = 1ULL << 30;
|
||||
else if (u == 'T') multiplier = 1ULL << 40;
|
||||
else throw UsageError("invalid unit specifier '%1%'", u);
|
||||
if (u == 'K')
|
||||
multiplier = 1ULL << 10;
|
||||
else if (u == 'M')
|
||||
multiplier = 1ULL << 20;
|
||||
else if (u == 'G')
|
||||
multiplier = 1ULL << 30;
|
||||
else if (u == 'T')
|
||||
multiplier = 1ULL << 40;
|
||||
else
|
||||
throw UsageError("invalid unit specifier '%1%'", u);
|
||||
s.remove_suffix(1);
|
||||
}
|
||||
}
|
||||
|
|
@ -117,7 +112,6 @@ std::string renderSize(uint64_t value, bool align = false);
|
|||
template<class N>
|
||||
std::optional<N> string2Float(const std::string_view s);
|
||||
|
||||
|
||||
/**
|
||||
* Convert a little-endian integer to host order.
|
||||
*/
|
||||
|
|
@ -131,25 +125,21 @@ T readLittleEndian(unsigned char * p)
|
|||
return x;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @return true iff `s` starts with `prefix`.
|
||||
*/
|
||||
bool hasPrefix(std::string_view s, std::string_view prefix);
|
||||
|
||||
|
||||
/**
|
||||
* @return true iff `s` ends in `suffix`.
|
||||
*/
|
||||
bool hasSuffix(std::string_view s, std::string_view suffix);
|
||||
|
||||
|
||||
/**
|
||||
* Convert a string to lower case.
|
||||
*/
|
||||
std::string toLower(std::string s);
|
||||
|
||||
|
||||
/**
|
||||
* Escape a string as a shell word.
|
||||
*
|
||||
|
|
@ -160,7 +150,6 @@ std::string toLower(std::string s);
|
|||
*/
|
||||
std::string escapeShellArgAlways(const std::string_view s);
|
||||
|
||||
|
||||
/**
|
||||
* Exception handling in destructors: print an error message, then
|
||||
* ignore the exception.
|
||||
|
|
@ -182,8 +171,6 @@ void ignoreExceptionInDestructor(Verbosity lvl = lvlError);
|
|||
*/
|
||||
void ignoreExceptionExceptInterrupt(Verbosity lvl = lvlError);
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* Tree formatting.
|
||||
*/
|
||||
|
|
@ -192,7 +179,6 @@ constexpr char treeLast[] = "└───";
|
|||
constexpr char treeLine[] = "│ ";
|
||||
constexpr char treeNull[] = " ";
|
||||
|
||||
|
||||
/**
|
||||
* Encode arbitrary bytes as Base64.
|
||||
*/
|
||||
|
|
@ -203,7 +189,6 @@ std::string base64Encode(std::string_view s);
|
|||
*/
|
||||
std::string base64Decode(std::string_view s);
|
||||
|
||||
|
||||
/**
|
||||
* Remove common leading whitespace from the lines in the string
|
||||
* 's'. For example, if every line is indented by at least 3 spaces,
|
||||
|
|
@ -211,7 +196,6 @@ std::string base64Decode(std::string_view s);
|
|||
*/
|
||||
std::string stripIndentation(std::string_view s);
|
||||
|
||||
|
||||
/**
|
||||
* Get the prefix of 's' up to and excluding the next line break (LF
|
||||
* optionally preceded by CR), and the remainder following the line
|
||||
|
|
@ -219,66 +203,67 @@ std::string stripIndentation(std::string_view s);
|
|||
*/
|
||||
std::pair<std::string_view, std::string_view> getLine(std::string_view s);
|
||||
|
||||
|
||||
/**
|
||||
* Get a value for the specified key from an associate container.
|
||||
*/
|
||||
template <class T>
|
||||
template<class T>
|
||||
const typename T::mapped_type * get(const T & map, const typename T::key_type & key)
|
||||
{
|
||||
auto i = map.find(key);
|
||||
if (i == map.end()) return nullptr;
|
||||
if (i == map.end())
|
||||
return nullptr;
|
||||
return &i->second;
|
||||
}
|
||||
|
||||
template <class T>
|
||||
template<class T>
|
||||
typename T::mapped_type * get(T & map, const typename T::key_type & key)
|
||||
{
|
||||
auto i = map.find(key);
|
||||
if (i == map.end()) return nullptr;
|
||||
if (i == map.end())
|
||||
return nullptr;
|
||||
return &i->second;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get a value for the specified key from an associate container, or a default value if the key isn't present.
|
||||
*/
|
||||
template <class T>
|
||||
const typename T::mapped_type & getOr(T & map,
|
||||
const typename T::key_type & key,
|
||||
const typename T::mapped_type & defaultValue)
|
||||
template<class T>
|
||||
const typename T::mapped_type &
|
||||
getOr(T & map, const typename T::key_type & key, const typename T::mapped_type & defaultValue)
|
||||
{
|
||||
auto i = map.find(key);
|
||||
if (i == map.end()) return defaultValue;
|
||||
if (i == map.end())
|
||||
return defaultValue;
|
||||
return i->second;
|
||||
}
|
||||
|
||||
/**
|
||||
* Remove and return the first item from a container.
|
||||
*/
|
||||
template <class T>
|
||||
template<class T>
|
||||
std::optional<typename T::value_type> remove_begin(T & c)
|
||||
{
|
||||
auto i = c.begin();
|
||||
if (i == c.end()) return {};
|
||||
if (i == c.end())
|
||||
return {};
|
||||
auto v = std::move(*i);
|
||||
c.erase(i);
|
||||
return v;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Remove and return the first item from a container.
|
||||
*/
|
||||
template <class T>
|
||||
template<class T>
|
||||
std::optional<typename T::value_type> pop(T & c)
|
||||
{
|
||||
if (c.empty()) return {};
|
||||
if (c.empty())
|
||||
return {};
|
||||
auto v = std::move(c.front());
|
||||
c.pop();
|
||||
return v;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Append items to a container. TODO: remove this once we can use
|
||||
* C++23's `append_range()`.
|
||||
|
|
@ -289,11 +274,9 @@ void append(C & c, std::initializer_list<T> l)
|
|||
c.insert(c.end(), l.begin(), l.end());
|
||||
}
|
||||
|
||||
|
||||
template<typename T>
|
||||
class Callback;
|
||||
|
||||
|
||||
/**
|
||||
* A RAII helper that increments a counter on construction and
|
||||
* decrements it on destruction.
|
||||
|
|
@ -303,56 +286,89 @@ struct MaintainCount
|
|||
{
|
||||
T & counter;
|
||||
long delta;
|
||||
MaintainCount(T & counter, long delta = 1) : counter(counter), delta(delta) { counter += delta; }
|
||||
~MaintainCount() { counter -= delta; }
|
||||
};
|
||||
|
||||
MaintainCount(T & counter, long delta = 1)
|
||||
: counter(counter)
|
||||
, delta(delta)
|
||||
{
|
||||
counter += delta;
|
||||
}
|
||||
|
||||
~MaintainCount()
|
||||
{
|
||||
counter -= delta;
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* A Rust/Python-like enumerate() iterator adapter.
|
||||
*
|
||||
* Borrowed from http://reedbeta.com/blog/python-like-enumerate-in-cpp17.
|
||||
*/
|
||||
template <typename T,
|
||||
typename TIter = decltype(std::begin(std::declval<T>())),
|
||||
typename = decltype(std::end(std::declval<T>()))>
|
||||
template<
|
||||
typename T,
|
||||
typename TIter = decltype(std::begin(std::declval<T>())),
|
||||
typename = decltype(std::end(std::declval<T>()))>
|
||||
constexpr auto enumerate(T && iterable)
|
||||
{
|
||||
struct iterator
|
||||
{
|
||||
size_t i;
|
||||
TIter iter;
|
||||
constexpr bool operator != (const iterator & other) const { return iter != other.iter; }
|
||||
constexpr void operator ++ () { ++i; ++iter; }
|
||||
constexpr auto operator * () const { return std::tie(i, *iter); }
|
||||
|
||||
constexpr bool operator!=(const iterator & other) const
|
||||
{
|
||||
return iter != other.iter;
|
||||
}
|
||||
|
||||
constexpr void operator++()
|
||||
{
|
||||
++i;
|
||||
++iter;
|
||||
}
|
||||
|
||||
constexpr auto operator*() const
|
||||
{
|
||||
return std::tie(i, *iter);
|
||||
}
|
||||
};
|
||||
|
||||
struct iterable_wrapper
|
||||
{
|
||||
T iterable;
|
||||
constexpr auto begin() { return iterator{ 0, std::begin(iterable) }; }
|
||||
constexpr auto end() { return iterator{ 0, std::end(iterable) }; }
|
||||
|
||||
constexpr auto begin()
|
||||
{
|
||||
return iterator{0, std::begin(iterable)};
|
||||
}
|
||||
|
||||
constexpr auto end()
|
||||
{
|
||||
return iterator{0, std::end(iterable)};
|
||||
}
|
||||
};
|
||||
|
||||
return iterable_wrapper{ std::forward<T>(iterable) };
|
||||
return iterable_wrapper{std::forward<T>(iterable)};
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* C++17 std::visit boilerplate
|
||||
*/
|
||||
template<class... Ts> struct overloaded : Ts... { using Ts::operator()...; };
|
||||
template<class... Ts> overloaded(Ts...) -> overloaded<Ts...>;
|
||||
|
||||
template<class... Ts>
|
||||
struct overloaded : Ts...
|
||||
{
|
||||
using Ts::operator()...;
|
||||
};
|
||||
template<class... Ts>
|
||||
overloaded(Ts...) -> overloaded<Ts...>;
|
||||
|
||||
std::string showBytes(uint64_t bytes);
|
||||
|
||||
|
||||
/**
|
||||
* Provide an addition operator between strings and string_views
|
||||
* inexplicably omitted from the standard library.
|
||||
*/
|
||||
inline std::string operator + (const std::string & s1, std::string_view s2)
|
||||
inline std::string operator+(const std::string & s1, std::string_view s2)
|
||||
{
|
||||
std::string s;
|
||||
s.reserve(s1.size() + s2.size());
|
||||
|
|
@ -361,13 +377,13 @@ inline std::string operator + (const std::string & s1, std::string_view s2)
|
|||
return s;
|
||||
}
|
||||
|
||||
inline std::string operator + (std::string && s, std::string_view s2)
|
||||
inline std::string operator+(std::string && s, std::string_view s2)
|
||||
{
|
||||
s.append(s2);
|
||||
return std::move(s);
|
||||
}
|
||||
|
||||
inline std::string operator + (std::string_view s1, const char * s2)
|
||||
inline std::string operator+(std::string_view s1, const char * s2)
|
||||
{
|
||||
auto s2Size = strlen(s2);
|
||||
std::string s;
|
||||
|
|
@ -377,4 +393,4 @@ inline std::string operator + (std::string_view s1, const char * s2)
|
|||
return s;
|
||||
}
|
||||
|
||||
}
|
||||
} // namespace nix
|
||||
|
|
|
|||
|
|
@ -8,13 +8,13 @@
|
|||
* Force the default versions of all constructors (copy, move, copy
|
||||
* assignment).
|
||||
*/
|
||||
#define FORCE_DEFAULT_CONSTRUCTORS(CLASS_NAME) \
|
||||
CLASS_NAME(const CLASS_NAME &) = default; \
|
||||
CLASS_NAME(CLASS_NAME &) = default; \
|
||||
CLASS_NAME(CLASS_NAME &&) = default; \
|
||||
\
|
||||
CLASS_NAME & operator =(const CLASS_NAME &) = default; \
|
||||
CLASS_NAME & operator =(CLASS_NAME &) = default;
|
||||
#define FORCE_DEFAULT_CONSTRUCTORS(CLASS_NAME) \
|
||||
CLASS_NAME(const CLASS_NAME &) = default; \
|
||||
CLASS_NAME(CLASS_NAME &) = default; \
|
||||
CLASS_NAME(CLASS_NAME &&) = default; \
|
||||
\
|
||||
CLASS_NAME & operator=(const CLASS_NAME &) = default; \
|
||||
CLASS_NAME & operator=(CLASS_NAME &) = default;
|
||||
|
||||
/**
|
||||
* Make a wrapper constructor. All args are forwarded to the
|
||||
|
|
@ -22,9 +22,10 @@
|
|||
*
|
||||
* The moral equivalent of `using Raw::Raw;`
|
||||
*/
|
||||
#define MAKE_WRAPPER_CONSTRUCTOR(CLASS_NAME) \
|
||||
FORCE_DEFAULT_CONSTRUCTORS(CLASS_NAME) \
|
||||
\
|
||||
CLASS_NAME(auto &&... arg) \
|
||||
#define MAKE_WRAPPER_CONSTRUCTOR(CLASS_NAME) \
|
||||
FORCE_DEFAULT_CONSTRUCTORS(CLASS_NAME) \
|
||||
\
|
||||
CLASS_NAME(auto &&... arg) \
|
||||
: raw(std::forward<decltype(arg)>(arg)...) \
|
||||
{ }
|
||||
{ \
|
||||
}
|
||||
|
|
|
|||
|
|
@ -6,13 +6,10 @@
|
|||
#include <list>
|
||||
#include <map>
|
||||
|
||||
|
||||
namespace nix {
|
||||
|
||||
|
||||
typedef std::map<std::string, std::string, std::less<>> XMLAttrs;
|
||||
|
||||
|
||||
class XMLWriter
|
||||
{
|
||||
private:
|
||||
|
|
@ -31,12 +28,10 @@ public:
|
|||
|
||||
void close();
|
||||
|
||||
void openElement(std::string_view name,
|
||||
const XMLAttrs & attrs = XMLAttrs());
|
||||
void openElement(std::string_view name, const XMLAttrs & attrs = XMLAttrs());
|
||||
void closeElement();
|
||||
|
||||
void writeEmptyElement(std::string_view name,
|
||||
const XMLAttrs & attrs = XMLAttrs());
|
||||
void writeEmptyElement(std::string_view name, const XMLAttrs & attrs = XMLAttrs());
|
||||
|
||||
private:
|
||||
void writeAttrs(const XMLAttrs & attrs);
|
||||
|
|
@ -44,23 +39,21 @@ private:
|
|||
void indent_(size_t depth);
|
||||
};
|
||||
|
||||
|
||||
class XMLOpenElement
|
||||
{
|
||||
private:
|
||||
XMLWriter & writer;
|
||||
public:
|
||||
XMLOpenElement(XMLWriter & writer, std::string_view name,
|
||||
const XMLAttrs & attrs = XMLAttrs())
|
||||
XMLOpenElement(XMLWriter & writer, std::string_view name, const XMLAttrs & attrs = XMLAttrs())
|
||||
: writer(writer)
|
||||
{
|
||||
writer.openElement(name, attrs);
|
||||
}
|
||||
|
||||
~XMLOpenElement()
|
||||
{
|
||||
writer.closeElement();
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
}
|
||||
} // namespace nix
|
||||
|
|
|
|||
|
|
@ -10,20 +10,20 @@ namespace nix {
|
|||
const nlohmann::json * get(const nlohmann::json & map, const std::string & key)
|
||||
{
|
||||
auto i = map.find(key);
|
||||
if (i == map.end()) return nullptr;
|
||||
if (i == map.end())
|
||||
return nullptr;
|
||||
return &*i;
|
||||
}
|
||||
|
||||
nlohmann::json * get(nlohmann::json & map, const std::string & key)
|
||||
{
|
||||
auto i = map.find(key);
|
||||
if (i == map.end()) return nullptr;
|
||||
if (i == map.end())
|
||||
return nullptr;
|
||||
return &*i;
|
||||
}
|
||||
|
||||
const nlohmann::json & valueAt(
|
||||
const nlohmann::json::object_t & map,
|
||||
const std::string & key)
|
||||
const nlohmann::json & valueAt(const nlohmann::json::object_t & map, const std::string & key)
|
||||
{
|
||||
if (!map.contains(key))
|
||||
throw Error("Expected JSON object to contain key '%s' but it doesn't: %s", key, nlohmann::json(map).dump());
|
||||
|
|
@ -36,7 +36,7 @@ std::optional<nlohmann::json> optionalValueAt(const nlohmann::json::object_t & m
|
|||
if (!map.contains(key))
|
||||
return std::nullopt;
|
||||
|
||||
return std::optional { map.at(key) };
|
||||
return std::optional{map.at(key)};
|
||||
}
|
||||
|
||||
std::optional<nlohmann::json> nullableValueAt(const nlohmann::json::object_t & map, const std::string & key)
|
||||
|
|
@ -46,7 +46,7 @@ std::optional<nlohmann::json> nullableValueAt(const nlohmann::json::object_t & m
|
|||
if (value.is_null())
|
||||
return std::nullopt;
|
||||
|
||||
return std::optional { std::move(value) };
|
||||
return std::optional{std::move(value)};
|
||||
}
|
||||
|
||||
const nlohmann::json * getNullable(const nlohmann::json & value)
|
||||
|
|
@ -63,16 +63,14 @@ const nlohmann::json * getNullable(const nlohmann::json & value)
|
|||
* functions. It is too cumbersome and easy to forget to expect regular
|
||||
* JSON code to use it directly.
|
||||
*/
|
||||
static const nlohmann::json & ensureType(
|
||||
const nlohmann::json & value,
|
||||
nlohmann::json::value_type expectedType
|
||||
)
|
||||
static const nlohmann::json & ensureType(const nlohmann::json & value, nlohmann::json::value_type expectedType)
|
||||
{
|
||||
if (value.type() != expectedType)
|
||||
throw Error(
|
||||
"Expected JSON value to be of type '%s' but it is of type '%s': %s",
|
||||
nlohmann::json(expectedType).type_name(),
|
||||
value.type_name(), value.dump());
|
||||
value.type_name(),
|
||||
value.dump());
|
||||
|
||||
return value;
|
||||
}
|
||||
|
|
@ -102,8 +100,7 @@ const nlohmann::json::number_unsigned_t & getUnsigned(const nlohmann::json & val
|
|||
typeName = value.is_number_float() ? "floating point number" : "signed integral number";
|
||||
}
|
||||
throw Error(
|
||||
"Expected JSON value to be an unsigned integral number but it is of type '%s': %s",
|
||||
typeName, value.dump());
|
||||
"Expected JSON value to be an unsigned integral number but it is of type '%s': %s", typeName, value.dump());
|
||||
}
|
||||
|
||||
const nlohmann::json::boolean_t & getBoolean(const nlohmann::json & value)
|
||||
|
|
@ -146,4 +143,4 @@ StringSet getStringSet(const nlohmann::json & value)
|
|||
|
||||
return stringSet;
|
||||
}
|
||||
}
|
||||
} // namespace nix
|
||||
|
|
|
|||
|
|
@ -19,7 +19,8 @@ std::optional<Path> getCgroupFS()
|
|||
{
|
||||
static auto res = [&]() -> std::optional<Path> {
|
||||
auto fp = fopen("/proc/mounts", "r");
|
||||
if (!fp) return std::nullopt;
|
||||
if (!fp)
|
||||
return std::nullopt;
|
||||
Finally delFP = [&]() { fclose(fp); };
|
||||
while (auto ent = getmntent(fp))
|
||||
if (std::string_view(ent->mnt_type) == "cgroup2")
|
||||
|
|
@ -50,7 +51,8 @@ StringMap getCgroups(const Path & cgroupFile)
|
|||
|
||||
static CgroupStats destroyCgroup(const std::filesystem::path & cgroup, bool returnStats)
|
||||
{
|
||||
if (!pathExists(cgroup)) return {};
|
||||
if (!pathExists(cgroup))
|
||||
return {};
|
||||
|
||||
auto procsFile = cgroup / "cgroup.procs";
|
||||
|
||||
|
|
@ -67,7 +69,8 @@ static CgroupStats destroyCgroup(const std::filesystem::path & cgroup, bool retu
|
|||
this cgroup. */
|
||||
for (auto & entry : DirectoryIterator{cgroup}) {
|
||||
checkInterrupt();
|
||||
if (entry.symlink_status().type() != std::filesystem::file_type::directory) continue;
|
||||
if (entry.symlink_status().type() != std::filesystem::file_type::directory)
|
||||
continue;
|
||||
destroyCgroup(cgroup / entry.path().filename(), false);
|
||||
}
|
||||
|
||||
|
|
@ -78,7 +81,8 @@ static CgroupStats destroyCgroup(const std::filesystem::path & cgroup, bool retu
|
|||
while (true) {
|
||||
auto pids = tokenizeString<std::vector<std::string>>(readFile(procsFile));
|
||||
|
||||
if (pids.empty()) break;
|
||||
if (pids.empty())
|
||||
break;
|
||||
|
||||
if (round > 20)
|
||||
throw Error("cannot kill cgroup '%s'", cgroup);
|
||||
|
|
@ -93,8 +97,7 @@ static CgroupStats destroyCgroup(const std::filesystem::path & cgroup, bool retu
|
|||
try {
|
||||
auto cmdline = readFile(fmt("/proc/%d/cmdline", pid));
|
||||
using namespace std::string_literals;
|
||||
warn("killing stray builder process %d (%s)...",
|
||||
pid, trim(replaceStrings(cmdline, "\0"s, " ")));
|
||||
warn("killing stray builder process %d (%s)...", pid, trim(replaceStrings(cmdline, "\0"s, " ")));
|
||||
} catch (SystemError &) {
|
||||
}
|
||||
}
|
||||
|
|
@ -120,17 +123,18 @@ static CgroupStats destroyCgroup(const std::filesystem::path & cgroup, bool retu
|
|||
std::string_view userPrefix = "user_usec ";
|
||||
if (hasPrefix(line, userPrefix)) {
|
||||
auto n = string2Int<uint64_t>(line.substr(userPrefix.size()));
|
||||
if (n) stats.cpuUser = std::chrono::microseconds(*n);
|
||||
if (n)
|
||||
stats.cpuUser = std::chrono::microseconds(*n);
|
||||
}
|
||||
|
||||
std::string_view systemPrefix = "system_usec ";
|
||||
if (hasPrefix(line, systemPrefix)) {
|
||||
auto n = string2Int<uint64_t>(line.substr(systemPrefix.size()));
|
||||
if (n) stats.cpuSystem = std::chrono::microseconds(*n);
|
||||
if (n)
|
||||
stats.cpuSystem = std::chrono::microseconds(*n);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
if (rmdir(cgroup.c_str()) == -1)
|
||||
|
|
@ -163,4 +167,4 @@ std::string getRootCgroup()
|
|||
return rootCgroup;
|
||||
}
|
||||
|
||||
}
|
||||
} // namespace nix
|
||||
|
|
|
|||
|
|
@ -34,4 +34,4 @@ std::string getCurrentCgroup();
|
|||
*/
|
||||
std::string getRootCgroup();
|
||||
|
||||
}
|
||||
} // namespace nix
|
||||
|
|
|
|||
|
|
@ -32,4 +32,4 @@ bool userNamespacesSupported();
|
|||
|
||||
bool mountAndPidNamespacesSupported();
|
||||
|
||||
}
|
||||
} // namespace nix
|
||||
|
|
|
|||
|
|
@ -16,36 +16,27 @@ namespace nix {
|
|||
|
||||
bool userNamespacesSupported()
|
||||
{
|
||||
static auto res = [&]() -> bool
|
||||
{
|
||||
static auto res = [&]() -> bool {
|
||||
if (!pathExists("/proc/self/ns/user")) {
|
||||
debug("'/proc/self/ns/user' does not exist; your kernel was likely built without CONFIG_USER_NS=y");
|
||||
return false;
|
||||
}
|
||||
|
||||
Path maxUserNamespaces = "/proc/sys/user/max_user_namespaces";
|
||||
if (!pathExists(maxUserNamespaces) ||
|
||||
trim(readFile(maxUserNamespaces)) == "0")
|
||||
{
|
||||
if (!pathExists(maxUserNamespaces) || trim(readFile(maxUserNamespaces)) == "0") {
|
||||
debug("user namespaces appear to be disabled; check '/proc/sys/user/max_user_namespaces'");
|
||||
return false;
|
||||
}
|
||||
|
||||
Path procSysKernelUnprivilegedUsernsClone = "/proc/sys/kernel/unprivileged_userns_clone";
|
||||
if (pathExists(procSysKernelUnprivilegedUsernsClone)
|
||||
&& trim(readFile(procSysKernelUnprivilegedUsernsClone)) == "0")
|
||||
{
|
||||
&& trim(readFile(procSysKernelUnprivilegedUsernsClone)) == "0") {
|
||||
debug("user namespaces appear to be disabled; check '/proc/sys/kernel/unprivileged_userns_clone'");
|
||||
return false;
|
||||
}
|
||||
|
||||
try {
|
||||
Pid pid = startProcess([&]()
|
||||
{
|
||||
_exit(0);
|
||||
}, {
|
||||
.cloneFlags = CLONE_NEWUSER
|
||||
});
|
||||
Pid pid = startProcess([&]() { _exit(0); }, {.cloneFlags = CLONE_NEWUSER});
|
||||
|
||||
auto r = pid.wait();
|
||||
assert(!r);
|
||||
|
|
@ -61,27 +52,25 @@ bool userNamespacesSupported()
|
|||
|
||||
bool mountAndPidNamespacesSupported()
|
||||
{
|
||||
static auto res = [&]() -> bool
|
||||
{
|
||||
static auto res = [&]() -> bool {
|
||||
try {
|
||||
|
||||
Pid pid = startProcess([&]()
|
||||
{
|
||||
/* Make sure we don't remount the parent's /proc. */
|
||||
if (mount(0, "/", 0, MS_PRIVATE | MS_REC, 0) == -1)
|
||||
_exit(1);
|
||||
Pid pid = startProcess(
|
||||
[&]() {
|
||||
/* Make sure we don't remount the parent's /proc. */
|
||||
if (mount(0, "/", 0, MS_PRIVATE | MS_REC, 0) == -1)
|
||||
_exit(1);
|
||||
|
||||
/* Test whether we can remount /proc. The kernel disallows
|
||||
this if /proc is not fully visible, i.e. if there are
|
||||
filesystems mounted on top of files inside /proc. See
|
||||
https://lore.kernel.org/lkml/87tvsrjai0.fsf@xmission.com/T/. */
|
||||
if (mount("none", "/proc", "proc", 0, 0) == -1)
|
||||
_exit(2);
|
||||
/* Test whether we can remount /proc. The kernel disallows
|
||||
this if /proc is not fully visible, i.e. if there are
|
||||
filesystems mounted on top of files inside /proc. See
|
||||
https://lore.kernel.org/lkml/87tvsrjai0.fsf@xmission.com/T/. */
|
||||
if (mount("none", "/proc", "proc", 0, 0) == -1)
|
||||
_exit(2);
|
||||
|
||||
_exit(0);
|
||||
}, {
|
||||
.cloneFlags = CLONE_NEWNS | CLONE_NEWPID | (userNamespacesSupported() ? CLONE_NEWUSER : 0)
|
||||
});
|
||||
_exit(0);
|
||||
},
|
||||
{.cloneFlags = CLONE_NEWNS | CLONE_NEWPID | (userNamespacesSupported() ? CLONE_NEWUSER : 0)});
|
||||
|
||||
if (pid.wait()) {
|
||||
debug("PID namespaces do not work on this system: cannot remount /proc");
|
||||
|
|
@ -98,7 +87,6 @@ bool mountAndPidNamespacesSupported()
|
|||
return res;
|
||||
}
|
||||
|
||||
|
||||
//////////////////////////////////////////////////////////////////////
|
||||
|
||||
static AutoCloseFD fdSavedMountNamespace;
|
||||
|
|
@ -144,4 +132,4 @@ void tryUnshareFilesystem()
|
|||
throw SysError("unsharing filesystem state");
|
||||
}
|
||||
|
||||
}
|
||||
} // namespace nix
|
||||
|
|
|
|||
Some files were not shown because too many files have changed in this diff Show more
Loading…
Add table
Add a link
Reference in a new issue