mirror of
https://github.com/NixOS/nix
synced 2025-06-28 01:11:15 +02:00
Merge remote-tracking branch 'upstream/master' into fix-and-ci-static-builds
This commit is contained in:
commit
39de73550d
193 changed files with 3689 additions and 2107 deletions
15
src/libutil/abstract-setting-to-json.hh
Normal file
15
src/libutil/abstract-setting-to-json.hh
Normal file
|
@ -0,0 +1,15 @@
|
|||
#pragma once
|
||||
|
||||
#include <nlohmann/json.hpp>
|
||||
#include "config.hh"
|
||||
|
||||
namespace nix {
|
||||
template<typename T>
|
||||
std::map<std::string, nlohmann::json> BaseSetting<T>::toJSONObject()
|
||||
{
|
||||
auto obj = AbstractSetting::toJSONObject();
|
||||
obj.emplace("value", value);
|
||||
obj.emplace("defaultValue", defaultValue);
|
||||
return obj;
|
||||
}
|
||||
}
|
|
@ -27,11 +27,13 @@ struct ArchiveSettings : Config
|
|||
#endif
|
||||
"use-case-hack",
|
||||
"Whether to enable a Darwin-specific hack for dealing with file name collisions."};
|
||||
Setting<bool> preallocateContents{this, true, "preallocate-contents",
|
||||
"Whether to preallocate files when writing objects with known size."};
|
||||
};
|
||||
|
||||
static ArchiveSettings archiveSettings;
|
||||
|
||||
static GlobalConfig::Register r1(&archiveSettings);
|
||||
static GlobalConfig::Register rArchiveSettings(&archiveSettings);
|
||||
|
||||
const std::string narVersionMagic1 = "nix-archive-1";
|
||||
|
||||
|
@ -66,9 +68,7 @@ static void dump(const Path & path, Sink & sink, PathFilter & filter)
|
|||
{
|
||||
checkInterrupt();
|
||||
|
||||
struct stat st;
|
||||
if (lstat(path.c_str(), &st))
|
||||
throw SysError("getting attributes of path '%1%'", path);
|
||||
auto st = lstat(path);
|
||||
|
||||
sink << "(";
|
||||
|
||||
|
@ -325,6 +325,9 @@ struct RestoreSink : ParseSink
|
|||
|
||||
void preallocateContents(uint64_t len)
|
||||
{
|
||||
if (!archiveSettings.preallocateContents)
|
||||
return;
|
||||
|
||||
#if HAVE_POSIX_FALLOCATE
|
||||
if (len) {
|
||||
errno = posix_fallocate(fd.get(), 0, len);
|
||||
|
|
|
@ -277,7 +277,7 @@ Args::Flag Args::Flag::mkHashTypeOptFlag(std::string && longName, std::optional<
|
|||
};
|
||||
}
|
||||
|
||||
static void completePath(std::string_view prefix, bool onlyDirs)
|
||||
static void _completePath(std::string_view prefix, bool onlyDirs)
|
||||
{
|
||||
pathCompletions = true;
|
||||
glob_t globbuf;
|
||||
|
@ -300,12 +300,12 @@ static void completePath(std::string_view prefix, bool onlyDirs)
|
|||
|
||||
void completePath(size_t, std::string_view prefix)
|
||||
{
|
||||
completePath(prefix, false);
|
||||
_completePath(prefix, false);
|
||||
}
|
||||
|
||||
void completeDir(size_t, std::string_view prefix)
|
||||
{
|
||||
completePath(prefix, true);
|
||||
_completePath(prefix, true);
|
||||
}
|
||||
|
||||
Strings argvToStrings(int argc, char * * argv)
|
||||
|
|
|
@ -192,7 +192,7 @@ public:
|
|||
{
|
||||
expectArgs({
|
||||
.label = label,
|
||||
.optional = true,
|
||||
.optional = optional,
|
||||
.handler = {dest}
|
||||
});
|
||||
}
|
||||
|
|
46
src/libutil/callback.hh
Normal file
46
src/libutil/callback.hh
Normal file
|
@ -0,0 +1,46 @@
|
|||
#pragma once
|
||||
|
||||
#include <future>
|
||||
#include <functional>
|
||||
|
||||
namespace nix {
|
||||
|
||||
/* A callback is a wrapper around a lambda that accepts a valid of
|
||||
type T or an exception. (We abuse std::future<T> to pass the value or
|
||||
exception.) */
|
||||
template<typename T>
|
||||
class Callback
|
||||
{
|
||||
std::function<void(std::future<T>)> fun;
|
||||
std::atomic_flag done = ATOMIC_FLAG_INIT;
|
||||
|
||||
public:
|
||||
|
||||
Callback(std::function<void(std::future<T>)> fun) : fun(fun) { }
|
||||
|
||||
Callback(Callback && callback) : fun(std::move(callback.fun))
|
||||
{
|
||||
auto prev = callback.done.test_and_set();
|
||||
if (prev) done.test_and_set();
|
||||
}
|
||||
|
||||
void operator()(T && t) noexcept
|
||||
{
|
||||
auto prev = done.test_and_set();
|
||||
assert(!prev);
|
||||
std::promise<T> promise;
|
||||
promise.set_value(std::move(t));
|
||||
fun(promise.get_future());
|
||||
}
|
||||
|
||||
void rethrow(const std::exception_ptr & exc = std::current_exception()) noexcept
|
||||
{
|
||||
auto prev = done.test_and_set();
|
||||
assert(!prev);
|
||||
std::promise<T> promise;
|
||||
promise.set_exception(exc);
|
||||
fun(promise.get_future());
|
||||
}
|
||||
};
|
||||
|
||||
}
|
|
@ -1,5 +1,6 @@
|
|||
#include "config.hh"
|
||||
#include "args.hh"
|
||||
#include "abstract-setting-to-json.hh"
|
||||
|
||||
#include <nlohmann/json.hpp>
|
||||
|
||||
|
@ -137,11 +138,7 @@ nlohmann::json Config::toJSON()
|
|||
auto res = nlohmann::json::object();
|
||||
for (auto & s : _settings)
|
||||
if (!s.second.isAlias) {
|
||||
auto obj = nlohmann::json::object();
|
||||
obj.emplace("description", s.second.setting->description);
|
||||
obj.emplace("aliases", s.second.setting->aliases);
|
||||
obj.emplace("value", s.second.setting->toJSON());
|
||||
res.emplace(s.first, obj);
|
||||
res.emplace(s.first, s.second.setting->toJSON());
|
||||
}
|
||||
return res;
|
||||
}
|
||||
|
@ -168,19 +165,21 @@ void AbstractSetting::setDefault(const std::string & str)
|
|||
|
||||
nlohmann::json AbstractSetting::toJSON()
|
||||
{
|
||||
return to_string();
|
||||
return nlohmann::json(toJSONObject());
|
||||
}
|
||||
|
||||
std::map<std::string, nlohmann::json> AbstractSetting::toJSONObject()
|
||||
{
|
||||
std::map<std::string, nlohmann::json> obj;
|
||||
obj.emplace("description", description);
|
||||
obj.emplace("aliases", aliases);
|
||||
return obj;
|
||||
}
|
||||
|
||||
void AbstractSetting::convertToArg(Args & args, const std::string & category)
|
||||
{
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
nlohmann::json BaseSetting<T>::toJSON()
|
||||
{
|
||||
return value;
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
void BaseSetting<T>::convertToArg(Args & args, const std::string & category)
|
||||
{
|
||||
|
@ -259,11 +258,6 @@ template<> std::string BaseSetting<Strings>::to_string() const
|
|||
return concatStringsSep(" ", value);
|
||||
}
|
||||
|
||||
template<> nlohmann::json BaseSetting<Strings>::toJSON()
|
||||
{
|
||||
return value;
|
||||
}
|
||||
|
||||
template<> void BaseSetting<StringSet>::set(const std::string & str)
|
||||
{
|
||||
value = tokenizeString<StringSet>(str);
|
||||
|
@ -274,9 +268,24 @@ template<> std::string BaseSetting<StringSet>::to_string() const
|
|||
return concatStringsSep(" ", value);
|
||||
}
|
||||
|
||||
template<> nlohmann::json BaseSetting<StringSet>::toJSON()
|
||||
template<> void BaseSetting<StringMap>::set(const std::string & str)
|
||||
{
|
||||
return value;
|
||||
auto kvpairs = tokenizeString<Strings>(str);
|
||||
for (auto & s : kvpairs)
|
||||
{
|
||||
auto eq = s.find_first_of('=');
|
||||
if (std::string::npos != eq)
|
||||
value.emplace(std::string(s, 0, eq), std::string(s, eq + 1));
|
||||
// else ignored
|
||||
}
|
||||
}
|
||||
|
||||
template<> std::string BaseSetting<StringMap>::to_string() const
|
||||
{
|
||||
Strings kvstrs;
|
||||
std::transform(value.begin(), value.end(), back_inserter(kvstrs),
|
||||
[&](auto kvpair){ return kvpair.first + "=" + kvpair.second; });
|
||||
return concatStringsSep(" ", kvstrs);
|
||||
}
|
||||
|
||||
template class BaseSetting<int>;
|
||||
|
@ -289,6 +298,7 @@ template class BaseSetting<bool>;
|
|||
template class BaseSetting<std::string>;
|
||||
template class BaseSetting<Strings>;
|
||||
template class BaseSetting<StringSet>;
|
||||
template class BaseSetting<StringMap>;
|
||||
|
||||
void PathSetting::set(const std::string & str)
|
||||
{
|
||||
|
|
|
@ -206,7 +206,9 @@ protected:
|
|||
|
||||
virtual std::string to_string() const = 0;
|
||||
|
||||
virtual nlohmann::json toJSON();
|
||||
nlohmann::json toJSON();
|
||||
|
||||
virtual std::map<std::string, nlohmann::json> toJSONObject();
|
||||
|
||||
virtual void convertToArg(Args & args, const std::string & category);
|
||||
|
||||
|
@ -220,6 +222,7 @@ class BaseSetting : public AbstractSetting
|
|||
protected:
|
||||
|
||||
T value;
|
||||
const T defaultValue;
|
||||
|
||||
public:
|
||||
|
||||
|
@ -229,6 +232,7 @@ public:
|
|||
const std::set<std::string> & aliases = {})
|
||||
: AbstractSetting(name, description, aliases)
|
||||
, value(def)
|
||||
, defaultValue(def)
|
||||
{ }
|
||||
|
||||
operator const T &() const { return value; }
|
||||
|
@ -251,7 +255,7 @@ public:
|
|||
|
||||
void convertToArg(Args & args, const std::string & category) override;
|
||||
|
||||
nlohmann::json toJSON() override;
|
||||
std::map<std::string, nlohmann::json> toJSONObject() override;
|
||||
};
|
||||
|
||||
template<typename T>
|
||||
|
|
|
@ -11,13 +11,13 @@ const std::string nativeSystem = SYSTEM;
|
|||
|
||||
BaseError & BaseError::addTrace(std::optional<ErrPos> e, hintformat hint)
|
||||
{
|
||||
err.traces.push_front(Trace { .pos = e, .hint = hint});
|
||||
err.traces.push_front(Trace { .pos = e, .hint = hint });
|
||||
return *this;
|
||||
}
|
||||
|
||||
// c++ std::exception descendants must have a 'const char* what()' function.
|
||||
// This stringifies the error and caches it for use by what(), or similarly by msg().
|
||||
const string& BaseError::calcWhat() const
|
||||
const string & BaseError::calcWhat() const
|
||||
{
|
||||
if (what_.has_value())
|
||||
return *what_;
|
||||
|
@ -34,12 +34,12 @@ const string& BaseError::calcWhat() const
|
|||
|
||||
std::optional<string> ErrorInfo::programName = std::nullopt;
|
||||
|
||||
std::ostream& operator<<(std::ostream &os, const hintformat &hf)
|
||||
std::ostream & operator<<(std::ostream & os, const hintformat & hf)
|
||||
{
|
||||
return os << hf.str();
|
||||
}
|
||||
|
||||
string showErrPos(const ErrPos &errPos)
|
||||
string showErrPos(const ErrPos & errPos)
|
||||
{
|
||||
if (errPos.line > 0) {
|
||||
if (errPos.column > 0) {
|
||||
|
@ -53,7 +53,7 @@ string showErrPos(const ErrPos &errPos)
|
|||
}
|
||||
}
|
||||
|
||||
std::optional<LinesOfCode> getCodeLines(const ErrPos &errPos)
|
||||
std::optional<LinesOfCode> getCodeLines(const ErrPos & errPos)
|
||||
{
|
||||
if (errPos.line <= 0)
|
||||
return std::nullopt;
|
||||
|
@ -92,13 +92,13 @@ std::optional<LinesOfCode> getCodeLines(const ErrPos &errPos)
|
|||
return loc;
|
||||
}
|
||||
}
|
||||
catch (EndOfFile &eof) {
|
||||
catch (EndOfFile & eof) {
|
||||
if (loc.errLineOfCode.has_value())
|
||||
return loc;
|
||||
else
|
||||
return std::nullopt;
|
||||
}
|
||||
catch (std::exception &e) {
|
||||
catch (std::exception & e) {
|
||||
printError("error reading nix file: %s\n%s", errPos.file, e.what());
|
||||
return std::nullopt;
|
||||
}
|
||||
|
@ -137,10 +137,10 @@ std::optional<LinesOfCode> getCodeLines(const ErrPos &errPos)
|
|||
}
|
||||
|
||||
// print lines of code to the ostream, indicating the error column.
|
||||
void printCodeLines(std::ostream &out,
|
||||
const string &prefix,
|
||||
const ErrPos &errPos,
|
||||
const LinesOfCode &loc)
|
||||
void printCodeLines(std::ostream & out,
|
||||
const string & prefix,
|
||||
const ErrPos & errPos,
|
||||
const LinesOfCode & loc)
|
||||
{
|
||||
// previous line of code.
|
||||
if (loc.prevLineOfCode.has_value()) {
|
||||
|
@ -186,7 +186,7 @@ void printCodeLines(std::ostream &out,
|
|||
}
|
||||
}
|
||||
|
||||
void printAtPos(const string &prefix, const ErrPos &pos, std::ostream &out)
|
||||
void printAtPos(const string & prefix, const ErrPos & pos, std::ostream & out)
|
||||
{
|
||||
if (pos)
|
||||
{
|
||||
|
@ -212,7 +212,7 @@ void printAtPos(const string &prefix, const ErrPos &pos, std::ostream &out)
|
|||
}
|
||||
}
|
||||
|
||||
std::ostream& showErrorInfo(std::ostream &out, const ErrorInfo &einfo, bool showTrace)
|
||||
std::ostream & showErrorInfo(std::ostream & out, const ErrorInfo & einfo, bool showTrace)
|
||||
{
|
||||
auto errwidth = std::max<size_t>(getWindowSize().second, 20);
|
||||
string prefix = "";
|
||||
|
|
|
@ -107,7 +107,7 @@ struct Trace {
|
|||
struct ErrorInfo {
|
||||
Verbosity level;
|
||||
string name;
|
||||
string description;
|
||||
string description; // FIXME: remove? it seems to be barely used
|
||||
std::optional<hintformat> hint;
|
||||
std::optional<ErrPos> errPos;
|
||||
std::list<Trace> traces;
|
||||
|
@ -169,7 +169,7 @@ public:
|
|||
#endif
|
||||
|
||||
const string & msg() const { return calcWhat(); }
|
||||
const ErrorInfo & info() { calcWhat(); return err; }
|
||||
const ErrorInfo & info() const { calcWhat(); return err; }
|
||||
|
||||
template<typename... Args>
|
||||
BaseError & addTrace(std::optional<ErrPos> e, const string &fs, const Args & ... args)
|
||||
|
|
|
@ -1,7 +1,6 @@
|
|||
#pragma once
|
||||
|
||||
#include <boost/format.hpp>
|
||||
#include <boost/algorithm/string/replace.hpp>
|
||||
#include <string>
|
||||
#include "ansicolor.hh"
|
||||
|
||||
|
@ -77,11 +76,11 @@ template <class T>
|
|||
struct yellowtxt
|
||||
{
|
||||
yellowtxt(const T &s) : value(s) {}
|
||||
const T &value;
|
||||
const T & value;
|
||||
};
|
||||
|
||||
template <class T>
|
||||
std::ostream& operator<<(std::ostream &out, const yellowtxt<T> &y)
|
||||
std::ostream & operator<<(std::ostream & out, const yellowtxt<T> & y)
|
||||
{
|
||||
return out << ANSI_YELLOW << y.value << ANSI_NORMAL;
|
||||
}
|
||||
|
@ -89,12 +88,12 @@ std::ostream& operator<<(std::ostream &out, const yellowtxt<T> &y)
|
|||
template <class T>
|
||||
struct normaltxt
|
||||
{
|
||||
normaltxt(const T &s) : value(s) {}
|
||||
const T &value;
|
||||
normaltxt(const T & s) : value(s) {}
|
||||
const T & value;
|
||||
};
|
||||
|
||||
template <class T>
|
||||
std::ostream& operator<<(std::ostream &out, const normaltxt<T> &y)
|
||||
std::ostream & operator<<(std::ostream & out, const normaltxt<T> & y)
|
||||
{
|
||||
return out << ANSI_NORMAL << y.value;
|
||||
}
|
||||
|
@ -102,26 +101,30 @@ std::ostream& operator<<(std::ostream &out, const normaltxt<T> &y)
|
|||
class hintformat
|
||||
{
|
||||
public:
|
||||
hintformat(const string &format) :fmt(format)
|
||||
hintformat(const string & format) : fmt(format)
|
||||
{
|
||||
fmt.exceptions(boost::io::all_error_bits ^
|
||||
fmt.exceptions(boost::io::all_error_bits ^
|
||||
boost::io::too_many_args_bit ^
|
||||
boost::io::too_few_args_bit);
|
||||
}
|
||||
|
||||
hintformat(const hintformat &hf)
|
||||
: fmt(hf.fmt)
|
||||
{}
|
||||
hintformat(const hintformat & hf)
|
||||
: fmt(hf.fmt)
|
||||
{ }
|
||||
|
||||
hintformat(format && fmt)
|
||||
: fmt(std::move(fmt))
|
||||
{ }
|
||||
|
||||
template<class T>
|
||||
hintformat& operator%(const T &value)
|
||||
hintformat & operator%(const T & value)
|
||||
{
|
||||
fmt % yellowtxt(value);
|
||||
return *this;
|
||||
}
|
||||
|
||||
template<class T>
|
||||
hintformat& operator%(const normaltxt<T> &value)
|
||||
hintformat & operator%(const normaltxt<T> & value)
|
||||
{
|
||||
fmt % value.value;
|
||||
return *this;
|
||||
|
@ -136,7 +139,7 @@ private:
|
|||
format fmt;
|
||||
};
|
||||
|
||||
std::ostream& operator<<(std::ostream &os, const hintformat &hf);
|
||||
std::ostream & operator<<(std::ostream & os, const hintformat & hf);
|
||||
|
||||
template<typename... Args>
|
||||
inline hintformat hintfmt(const std::string & fs, const Args & ... args)
|
||||
|
|
|
@ -10,7 +10,7 @@ namespace nix {
|
|||
|
||||
LoggerSettings loggerSettings;
|
||||
|
||||
static GlobalConfig::Register r1(&loggerSettings);
|
||||
static GlobalConfig::Register rLoggerSettings(&loggerSettings);
|
||||
|
||||
static thread_local ActivityId curActivity = 0;
|
||||
|
||||
|
|
|
@ -93,7 +93,7 @@ void Source::operator () (unsigned char * data, size_t len)
|
|||
}
|
||||
|
||||
|
||||
std::string Source::drain()
|
||||
void Source::drainInto(Sink & sink)
|
||||
{
|
||||
std::string s;
|
||||
std::vector<unsigned char> buf(8192);
|
||||
|
@ -101,12 +101,19 @@ std::string Source::drain()
|
|||
size_t n;
|
||||
try {
|
||||
n = read(buf.data(), buf.size());
|
||||
s.append((char *) buf.data(), n);
|
||||
sink(buf.data(), n);
|
||||
} catch (EndOfFile &) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
return s;
|
||||
}
|
||||
|
||||
|
||||
std::string Source::drain()
|
||||
{
|
||||
StringSink s;
|
||||
drainInto(s);
|
||||
return *s.s;
|
||||
}
|
||||
|
||||
|
||||
|
@ -259,6 +266,24 @@ Sink & operator << (Sink & sink, const StringSet & s)
|
|||
return sink;
|
||||
}
|
||||
|
||||
Sink & operator << (Sink & sink, const Error & ex)
|
||||
{
|
||||
auto info = ex.info();
|
||||
sink
|
||||
<< "Error"
|
||||
<< info.level
|
||||
<< info.name
|
||||
<< info.description
|
||||
<< (info.hint ? info.hint->str() : "")
|
||||
<< 0 // FIXME: info.errPos
|
||||
<< info.traces.size();
|
||||
for (auto & trace : info.traces) {
|
||||
sink << 0; // FIXME: trace.pos
|
||||
sink << trace.hint.str();
|
||||
}
|
||||
return sink;
|
||||
}
|
||||
|
||||
|
||||
void readPadding(size_t len, Source & source)
|
||||
{
|
||||
|
@ -312,6 +337,30 @@ template Paths readStrings(Source & source);
|
|||
template PathSet readStrings(Source & source);
|
||||
|
||||
|
||||
Error readError(Source & source)
|
||||
{
|
||||
auto type = readString(source);
|
||||
assert(type == "Error");
|
||||
ErrorInfo info;
|
||||
info.level = (Verbosity) readInt(source);
|
||||
info.name = readString(source);
|
||||
info.description = readString(source);
|
||||
auto hint = readString(source);
|
||||
if (hint != "") info.hint = hintformat(std::move(format("%s") % hint));
|
||||
auto havePos = readNum<size_t>(source);
|
||||
assert(havePos == 0);
|
||||
auto nrTraces = readNum<size_t>(source);
|
||||
for (size_t i = 0; i < nrTraces; ++i) {
|
||||
havePos = readNum<size_t>(source);
|
||||
assert(havePos == 0);
|
||||
info.traces.push_back(Trace {
|
||||
.hint = hintformat(std::move(format("%s") % readString(source)))
|
||||
});
|
||||
}
|
||||
return Error(std::move(info));
|
||||
}
|
||||
|
||||
|
||||
void StringSink::operator () (const unsigned char * data, size_t len)
|
||||
{
|
||||
static bool warned = false;
|
||||
|
|
|
@ -22,6 +22,12 @@ struct Sink
|
|||
}
|
||||
};
|
||||
|
||||
/* Just throws away data. */
|
||||
struct NullSink : Sink
|
||||
{
|
||||
void operator () (const unsigned char * data, size_t len) override
|
||||
{ }
|
||||
};
|
||||
|
||||
/* A buffered abstract sink. Warning: a BufferedSink should not be
|
||||
used from multiple threads concurrently. */
|
||||
|
@ -63,6 +69,8 @@ struct Source
|
|||
|
||||
virtual bool good() { return true; }
|
||||
|
||||
void drainInto(Sink & sink);
|
||||
|
||||
std::string drain();
|
||||
};
|
||||
|
||||
|
@ -313,6 +321,7 @@ inline Sink & operator << (Sink & sink, uint64_t n)
|
|||
Sink & operator << (Sink & sink, const string & s);
|
||||
Sink & operator << (Sink & sink, const Strings & s);
|
||||
Sink & operator << (Sink & sink, const StringSet & s);
|
||||
Sink & operator << (Sink & in, const Error & ex);
|
||||
|
||||
|
||||
MakeError(SerialisationError, Error);
|
||||
|
@ -334,7 +343,7 @@ T readNum(Source & source)
|
|||
((uint64_t) buf[6] << 48) |
|
||||
((uint64_t) buf[7] << 56);
|
||||
|
||||
if (n > std::numeric_limits<T>::max())
|
||||
if (n > (uint64_t)std::numeric_limits<T>::max())
|
||||
throw SerialisationError("serialised integer %d is too large for type '%s'", n, typeid(T).name());
|
||||
|
||||
return (T) n;
|
||||
|
@ -374,6 +383,8 @@ Source & operator >> (Source & in, bool & b)
|
|||
return in;
|
||||
}
|
||||
|
||||
Error readError(Source & source);
|
||||
|
||||
|
||||
/* An adapter that converts a std::basic_istream into a source. */
|
||||
struct StreamToSourceAdapter : Source
|
||||
|
@ -398,4 +409,93 @@ struct StreamToSourceAdapter : Source
|
|||
};
|
||||
|
||||
|
||||
/* A source that reads a distinct format of concatenated chunks back into its
|
||||
logical form, in order to guarantee a known state to the original stream,
|
||||
even in the event of errors.
|
||||
|
||||
Use with FramedSink, which also allows the logical stream to be terminated
|
||||
in the event of an exception.
|
||||
*/
|
||||
struct FramedSource : Source
|
||||
{
|
||||
Source & from;
|
||||
bool eof = false;
|
||||
std::vector<unsigned char> pending;
|
||||
size_t pos = 0;
|
||||
|
||||
FramedSource(Source & from) : from(from)
|
||||
{ }
|
||||
|
||||
~FramedSource()
|
||||
{
|
||||
if (!eof) {
|
||||
while (true) {
|
||||
auto n = readInt(from);
|
||||
if (!n) break;
|
||||
std::vector<unsigned char> data(n);
|
||||
from(data.data(), n);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
size_t read(unsigned char * data, size_t len) override
|
||||
{
|
||||
if (eof) throw EndOfFile("reached end of FramedSource");
|
||||
|
||||
if (pos >= pending.size()) {
|
||||
size_t len = readInt(from);
|
||||
if (!len) {
|
||||
eof = true;
|
||||
return 0;
|
||||
}
|
||||
pending = std::vector<unsigned char>(len);
|
||||
pos = 0;
|
||||
from(pending.data(), len);
|
||||
}
|
||||
|
||||
auto n = std::min(len, pending.size() - pos);
|
||||
memcpy(data, pending.data() + pos, n);
|
||||
pos += n;
|
||||
return n;
|
||||
}
|
||||
};
|
||||
|
||||
/* Write as chunks in the format expected by FramedSource.
|
||||
|
||||
The exception_ptr reference can be used to terminate the stream when you
|
||||
detect that an error has occurred on the remote end.
|
||||
*/
|
||||
struct FramedSink : nix::BufferedSink
|
||||
{
|
||||
BufferedSink & to;
|
||||
std::exception_ptr & ex;
|
||||
|
||||
FramedSink(BufferedSink & to, std::exception_ptr & ex) : to(to), ex(ex)
|
||||
{ }
|
||||
|
||||
~FramedSink()
|
||||
{
|
||||
try {
|
||||
to << 0;
|
||||
to.flush();
|
||||
} catch (...) {
|
||||
ignoreException();
|
||||
}
|
||||
}
|
||||
|
||||
void write(const unsigned char * data, size_t len) override
|
||||
{
|
||||
/* Don't send more data if the remote has
|
||||
encountered an error. */
|
||||
if (ex) {
|
||||
auto ex2 = ex;
|
||||
ex = nullptr;
|
||||
std::rethrow_exception(ex2);
|
||||
}
|
||||
to << len;
|
||||
to(data, len);
|
||||
};
|
||||
};
|
||||
|
||||
|
||||
}
|
||||
|
|
|
@ -9,7 +9,7 @@ namespace nix {
|
|||
|
||||
// If `separator` is found, we return the portion of the string before the
|
||||
// separator, and modify the string argument to contain only the part after the
|
||||
// separator. Otherwise, wer return `std::nullopt`, and we leave the argument
|
||||
// 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) {
|
||||
auto sepInstance = string.find(separator);
|
||||
|
|
|
@ -161,7 +161,7 @@ namespace nix {
|
|||
Setting<std::string> setting{&config, "", "name-of-the-setting", "description"};
|
||||
setting.assign("value");
|
||||
|
||||
ASSERT_EQ(config.toJSON().dump(), R"#({"name-of-the-setting":{"aliases":[],"description":"description\n","value":"value"}})#");
|
||||
ASSERT_EQ(config.toJSON().dump(), R"#({"name-of-the-setting":{"aliases":[],"defaultValue":"","description":"description\n","value":"value"}})#");
|
||||
}
|
||||
|
||||
TEST(Config, setSettingAlias) {
|
||||
|
|
|
@ -1,3 +1,5 @@
|
|||
#pragma once
|
||||
|
||||
#include "error.hh"
|
||||
|
||||
namespace nix {
|
||||
|
|
|
@ -25,6 +25,8 @@ typedef string Path;
|
|||
typedef list<Path> Paths;
|
||||
typedef set<Path> PathSet;
|
||||
|
||||
typedef vector<std::pair<string, string>> Headers;
|
||||
|
||||
/* Helper class to run code at startup. */
|
||||
template<typename T>
|
||||
struct OnStartup
|
||||
|
|
44
src/libutil/url-parts.hh
Normal file
44
src/libutil/url-parts.hh
Normal file
|
@ -0,0 +1,44 @@
|
|||
#pragma once
|
||||
|
||||
#include <string>
|
||||
#include <regex>
|
||||
|
||||
namespace nix {
|
||||
|
||||
// URI stuff.
|
||||
const static std::string pctEncoded = "(?:%[0-9a-fA-F][0-9a-fA-F])";
|
||||
const static std::string schemeRegex = "(?:[a-z][a-z0-9+.-]*)";
|
||||
const static std::string ipv6AddressRegex = "(?:\\[[0-9a-fA-F:]+\\])";
|
||||
const static std::string unreservedRegex = "(?:[a-zA-Z0-9-._~])";
|
||||
const static std::string subdelimsRegex = "(?:[!$&'\"()*+,;=])";
|
||||
const static std::string hostnameRegex = "(?:(?:" + unreservedRegex + "|" + pctEncoded + "|" + subdelimsRegex + ")*)";
|
||||
const static std::string hostRegex = "(?:" + ipv6AddressRegex + "|" + hostnameRegex + ")";
|
||||
const static std::string userRegex = "(?:(?:" + unreservedRegex + "|" + pctEncoded + "|" + subdelimsRegex + "|:)*)";
|
||||
const static std::string authorityRegex = "(?:" + userRegex + "@)?" + hostRegex + "(?::[0-9]+)?";
|
||||
const static std::string pcharRegex = "(?:" + unreservedRegex + "|" + pctEncoded + "|" + subdelimsRegex + "|[:@])";
|
||||
const static std::string queryRegex = "(?:" + pcharRegex + "|[/? \"])*";
|
||||
const static std::string segmentRegex = "(?:" + pcharRegex + "+)";
|
||||
const static std::string absPathRegex = "(?:(?:/" + segmentRegex + ")*/?)";
|
||||
const static std::string pathRegex = "(?:" + segmentRegex + "(?:/" + segmentRegex + ")*/?)";
|
||||
|
||||
// A Git ref (i.e. branch or tag name).
|
||||
const static std::string refRegexS = "[a-zA-Z0-9][a-zA-Z0-9_.-]*"; // FIXME: check
|
||||
extern std::regex refRegex;
|
||||
|
||||
// Instead of defining what a good Git Ref is, we define what a bad Git Ref is
|
||||
// This is because of the definition of a ref in refs.c in https://github.com/git/git
|
||||
// See tests/fetchGitRefs.sh for the full definition
|
||||
const static std::string badGitRefRegexS = "//|^[./]|/\\.|\\.\\.|[[:cntrl:][:space:]:?^~\[]|\\\\|\\*|\\.lock$|\\.lock/|@\\{|[/.]$|^@$|^$";
|
||||
extern std::regex badGitRefRegex;
|
||||
|
||||
// A Git revision (a SHA-1 commit hash).
|
||||
const static std::string revRegexS = "[0-9a-fA-F]{40}";
|
||||
extern std::regex revRegex;
|
||||
|
||||
// A ref or revision, or a ref followed by a revision.
|
||||
const static std::string refAndOrRevRegex = "(?:(" + revRegexS + ")|(?:(" + refRegexS + ")(?:/(" + revRegexS + "))?))";
|
||||
|
||||
const static std::string flakeIdRegexS = "[a-zA-Z][a-zA-Z0-9_-]*";
|
||||
extern std::regex flakeIdRegex;
|
||||
|
||||
}
|
|
@ -1,4 +1,5 @@
|
|||
#include "url.hh"
|
||||
#include "url-parts.hh"
|
||||
#include "util.hh"
|
||||
|
||||
namespace nix {
|
||||
|
|
|
@ -2,8 +2,6 @@
|
|||
|
||||
#include "error.hh"
|
||||
|
||||
#include <regex>
|
||||
|
||||
namespace nix {
|
||||
|
||||
struct ParsedURL
|
||||
|
@ -29,40 +27,4 @@ std::map<std::string, std::string> decodeQuery(const std::string & query);
|
|||
|
||||
ParsedURL parseURL(const std::string & url);
|
||||
|
||||
// URI stuff.
|
||||
const static std::string pctEncoded = "(?:%[0-9a-fA-F][0-9a-fA-F])";
|
||||
const static std::string schemeRegex = "(?:[a-z+]+)";
|
||||
const static std::string ipv6AddressRegex = "(?:\\[[0-9a-fA-F:]+\\])";
|
||||
const static std::string unreservedRegex = "(?:[a-zA-Z0-9-._~])";
|
||||
const static std::string subdelimsRegex = "(?:[!$&'\"()*+,;=])";
|
||||
const static std::string hostnameRegex = "(?:(?:" + unreservedRegex + "|" + pctEncoded + "|" + subdelimsRegex + ")*)";
|
||||
const static std::string hostRegex = "(?:" + ipv6AddressRegex + "|" + hostnameRegex + ")";
|
||||
const static std::string userRegex = "(?:(?:" + unreservedRegex + "|" + pctEncoded + "|" + subdelimsRegex + "|:)*)";
|
||||
const static std::string authorityRegex = "(?:" + userRegex + "@)?" + hostRegex + "(?::[0-9]+)?";
|
||||
const static std::string pcharRegex = "(?:" + unreservedRegex + "|" + pctEncoded + "|" + subdelimsRegex + "|[:@])";
|
||||
const static std::string queryRegex = "(?:" + pcharRegex + "|[/? \"])*";
|
||||
const static std::string segmentRegex = "(?:" + pcharRegex + "+)";
|
||||
const static std::string absPathRegex = "(?:(?:/" + segmentRegex + ")*/?)";
|
||||
const static std::string pathRegex = "(?:" + segmentRegex + "(?:/" + segmentRegex + ")*/?)";
|
||||
|
||||
// A Git ref (i.e. branch or tag name).
|
||||
const static std::string refRegexS = "[a-zA-Z0-9][a-zA-Z0-9_.-]*"; // FIXME: check
|
||||
extern std::regex refRegex;
|
||||
|
||||
// Instead of defining what a good Git Ref is, we define what a bad Git Ref is
|
||||
// This is because of the definition of a ref in refs.c in https://github.com/git/git
|
||||
// See tests/fetchGitRefs.sh for the full definition
|
||||
const static std::string badGitRefRegexS = "//|^[./]|/\\.|\\.\\.|[[:cntrl:][:space:]:?^~\[]|\\\\|\\*|\\.lock$|\\.lock/|@\\{|[/.]$|^@$|^$";
|
||||
extern std::regex badGitRefRegex;
|
||||
|
||||
// A Git revision (a SHA-1 commit hash).
|
||||
const static std::string revRegexS = "[0-9a-fA-F]{40}";
|
||||
extern std::regex revRegex;
|
||||
|
||||
// A ref or revision, or a ref followed by a revision.
|
||||
const static std::string refAndOrRevRegex = "(?:(" + revRegexS + ")|(?:(" + refRegexS + ")(?:/(" + revRegexS + "))?))";
|
||||
|
||||
const static std::string flakeIdRegexS = "[a-zA-Z][a-zA-Z0-9_-]*";
|
||||
extern std::regex flakeIdRegex;
|
||||
|
||||
}
|
||||
|
|
|
@ -325,7 +325,12 @@ void writeFile(const Path & path, const string & s, mode_t mode)
|
|||
AutoCloseFD fd = open(path.c_str(), O_WRONLY | O_TRUNC | O_CREAT | O_CLOEXEC, mode);
|
||||
if (!fd)
|
||||
throw SysError("opening file '%1%'", path);
|
||||
writeFull(fd.get(), s);
|
||||
try {
|
||||
writeFull(fd.get(), s);
|
||||
} catch (Error & e) {
|
||||
e.addTrace({}, "writing file '%1%'", path);
|
||||
throw;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
@ -337,11 +342,16 @@ void writeFile(const Path & path, Source & source, mode_t mode)
|
|||
|
||||
std::vector<unsigned char> buf(64 * 1024);
|
||||
|
||||
while (true) {
|
||||
try {
|
||||
auto n = source.read(buf.data(), buf.size());
|
||||
writeFull(fd.get(), (unsigned char *) buf.data(), n);
|
||||
} catch (EndOfFile &) { break; }
|
||||
try {
|
||||
while (true) {
|
||||
try {
|
||||
auto n = source.read(buf.data(), buf.size());
|
||||
writeFull(fd.get(), (unsigned char *) buf.data(), n);
|
||||
} catch (EndOfFile &) { break; }
|
||||
}
|
||||
} catch (Error & e) {
|
||||
e.addTrace({}, "writing file '%1%'", path);
|
||||
throw;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -511,22 +521,24 @@ std::string getUserName()
|
|||
}
|
||||
|
||||
|
||||
static Path getHome2()
|
||||
Path getHome()
|
||||
{
|
||||
auto homeDir = getEnv("HOME");
|
||||
if (!homeDir) {
|
||||
std::vector<char> buf(16384);
|
||||
struct passwd pwbuf;
|
||||
struct passwd * pw;
|
||||
if (getpwuid_r(geteuid(), &pwbuf, buf.data(), buf.size(), &pw) != 0
|
||||
|| !pw || !pw->pw_dir || !pw->pw_dir[0])
|
||||
throw Error("cannot determine user's home directory");
|
||||
homeDir = pw->pw_dir;
|
||||
}
|
||||
return *homeDir;
|
||||
};
|
||||
|
||||
Path getHome() { return getHome2(); }
|
||||
static Path homeDir = []()
|
||||
{
|
||||
auto homeDir = getEnv("HOME");
|
||||
if (!homeDir) {
|
||||
std::vector<char> buf(16384);
|
||||
struct passwd pwbuf;
|
||||
struct passwd * pw;
|
||||
if (getpwuid_r(geteuid(), &pwbuf, buf.data(), buf.size(), &pw) != 0
|
||||
|| !pw || !pw->pw_dir || !pw->pw_dir[0])
|
||||
throw Error("cannot determine user's home directory");
|
||||
homeDir = pw->pw_dir;
|
||||
}
|
||||
return *homeDir;
|
||||
}();
|
||||
return homeDir;
|
||||
}
|
||||
|
||||
|
||||
Path getCacheDir()
|
||||
|
@ -1641,4 +1653,11 @@ AutoCloseFD createUnixDomainSocket(const Path & path, mode_t mode)
|
|||
return fdSocket;
|
||||
}
|
||||
|
||||
|
||||
string showBytes(uint64_t bytes)
|
||||
{
|
||||
return fmt("%.2f MiB", bytes / (1024.0 * 1024.0));
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
|
|
@ -12,13 +12,9 @@
|
|||
#include <signal.h>
|
||||
|
||||
#include <functional>
|
||||
#include <limits>
|
||||
#include <cstdio>
|
||||
#include <map>
|
||||
#include <sstream>
|
||||
#include <optional>
|
||||
#include <future>
|
||||
#include <iterator>
|
||||
|
||||
#ifndef HAVE_STRUCT_DIRENT_D_TYPE
|
||||
#define DT_UNKNOWN 0
|
||||
|
@ -480,43 +476,8 @@ std::optional<typename T::mapped_type> get(const T & map, const typename T::key_
|
|||
}
|
||||
|
||||
|
||||
/* A callback is a wrapper around a lambda that accepts a valid of
|
||||
type T or an exception. (We abuse std::future<T> to pass the value or
|
||||
exception.) */
|
||||
template<typename T>
|
||||
class Callback
|
||||
{
|
||||
std::function<void(std::future<T>)> fun;
|
||||
std::atomic_flag done = ATOMIC_FLAG_INIT;
|
||||
|
||||
public:
|
||||
|
||||
Callback(std::function<void(std::future<T>)> fun) : fun(fun) { }
|
||||
|
||||
Callback(Callback && callback) : fun(std::move(callback.fun))
|
||||
{
|
||||
auto prev = callback.done.test_and_set();
|
||||
if (prev) done.test_and_set();
|
||||
}
|
||||
|
||||
void operator()(T && t) noexcept
|
||||
{
|
||||
auto prev = done.test_and_set();
|
||||
assert(!prev);
|
||||
std::promise<T> promise;
|
||||
promise.set_value(std::move(t));
|
||||
fun(promise.get_future());
|
||||
}
|
||||
|
||||
void rethrow(const std::exception_ptr & exc = std::current_exception()) noexcept
|
||||
{
|
||||
auto prev = done.test_and_set();
|
||||
assert(!prev);
|
||||
std::promise<T> promise;
|
||||
promise.set_exception(exc);
|
||||
fun(promise.get_future());
|
||||
}
|
||||
};
|
||||
class Callback;
|
||||
|
||||
|
||||
/* Start a thread that handles various signals. Also block those signals
|
||||
|
@ -612,4 +573,7 @@ template<class... Ts> struct overloaded : Ts... { using Ts::operator()...; };
|
|||
template<class... Ts> overloaded(Ts...) -> overloaded<Ts...>;
|
||||
|
||||
|
||||
std::string showBytes(uint64_t bytes);
|
||||
|
||||
|
||||
}
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue