mirror of
https://github.com/NixOS/nix.git
synced 2025-12-01 06:31:00 +01:00
Merge remote-tracking branch 'origin/2.29-maintenance' into detsys-main
This commit is contained in:
commit
c20642ac7b
354 changed files with 6768 additions and 3808 deletions
|
|
@ -593,7 +593,7 @@ MultiCommand::MultiCommand(std::string_view commandName, const Commands & comman
|
|||
assert(!command);
|
||||
auto i = commands.find(s);
|
||||
if (i == commands.end()) {
|
||||
std::set<std::string> commandNames;
|
||||
StringSet commandNames;
|
||||
for (auto & [name, _] : commands)
|
||||
commandNames.insert(name);
|
||||
auto suggestions = Suggestions::bestMatches(commandNames, s);
|
||||
|
|
|
|||
|
|
@ -3,7 +3,8 @@
|
|||
#include "util-config-private.hh"
|
||||
|
||||
#if HAVE_LIBCPUID
|
||||
#include <libcpuid/libcpuid.h>
|
||||
# include <libcpuid/libcpuid.h>
|
||||
# include <map>
|
||||
#endif
|
||||
|
||||
namespace nix {
|
||||
|
|
@ -12,61 +13,21 @@ namespace nix {
|
|||
|
||||
StringSet computeLevels() {
|
||||
StringSet levels;
|
||||
struct cpu_id_t data;
|
||||
|
||||
if (!cpuid_present())
|
||||
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" },
|
||||
};
|
||||
|
||||
if (cpu_identify(NULL, &data) < 0)
|
||||
return levels;
|
||||
|
||||
cpu_raw_data_t raw;
|
||||
cpu_id_t data;
|
||||
|
||||
if (cpuid_get_raw_data(&raw) < 0)
|
||||
return levels;
|
||||
|
||||
if (cpu_identify(&raw, &data) < 0)
|
||||
return levels;
|
||||
|
||||
if (!(data.flags[CPU_FEATURE_CMOV] &&
|
||||
data.flags[CPU_FEATURE_CX8] &&
|
||||
data.flags[CPU_FEATURE_FPU] &&
|
||||
data.flags[CPU_FEATURE_FXSR] &&
|
||||
data.flags[CPU_FEATURE_MMX] &&
|
||||
data.flags[CPU_FEATURE_SSE] &&
|
||||
data.flags[CPU_FEATURE_SSE2]))
|
||||
return levels;
|
||||
|
||||
levels.insert("x86_64-v1");
|
||||
|
||||
if (!(data.flags[CPU_FEATURE_CX16] &&
|
||||
data.flags[CPU_FEATURE_LAHF_LM] &&
|
||||
data.flags[CPU_FEATURE_POPCNT] &&
|
||||
// SSE3
|
||||
data.flags[CPU_FEATURE_PNI] &&
|
||||
data.flags[CPU_FEATURE_SSSE3] &&
|
||||
data.flags[CPU_FEATURE_SSE4_1] &&
|
||||
data.flags[CPU_FEATURE_SSE4_2]))
|
||||
return levels;
|
||||
|
||||
levels.insert("x86_64-v2");
|
||||
|
||||
if (!(data.flags[CPU_FEATURE_AVX] &&
|
||||
data.flags[CPU_FEATURE_AVX2] &&
|
||||
data.flags[CPU_FEATURE_F16C] &&
|
||||
data.flags[CPU_FEATURE_FMA3] &&
|
||||
// LZCNT
|
||||
data.flags[CPU_FEATURE_ABM] &&
|
||||
data.flags[CPU_FEATURE_MOVBE]))
|
||||
return levels;
|
||||
|
||||
levels.insert("x86_64-v3");
|
||||
|
||||
if (!(data.flags[CPU_FEATURE_AVX512F] &&
|
||||
data.flags[CPU_FEATURE_AVX512BW] &&
|
||||
data.flags[CPU_FEATURE_AVX512CD] &&
|
||||
data.flags[CPU_FEATURE_AVX512DQ] &&
|
||||
data.flags[CPU_FEATURE_AVX512VL]))
|
||||
return levels;
|
||||
|
||||
levels.insert("x86_64-v4");
|
||||
for (auto & [level, levelString] : feature_strings)
|
||||
if (data.feature_level >= level)
|
||||
levels.insert(levelString);
|
||||
|
||||
return levels;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -6,7 +6,7 @@ namespace nix {
|
|||
|
||||
bool GlobalConfig::set(const std::string & name, const std::string & value)
|
||||
{
|
||||
for (auto & config : *configRegistrations)
|
||||
for (auto & config : configRegistrations())
|
||||
if (config->set(name, value))
|
||||
return true;
|
||||
|
||||
|
|
@ -17,20 +17,20 @@ bool GlobalConfig::set(const std::string & name, const std::string & value)
|
|||
|
||||
void GlobalConfig::getSettings(std::map<std::string, SettingInfo> & res, bool overriddenOnly)
|
||||
{
|
||||
for (auto & config : *configRegistrations)
|
||||
for (auto & config : configRegistrations())
|
||||
config->getSettings(res, overriddenOnly);
|
||||
}
|
||||
|
||||
void GlobalConfig::resetOverridden()
|
||||
{
|
||||
for (auto & config : *configRegistrations)
|
||||
for (auto & config : configRegistrations())
|
||||
config->resetOverridden();
|
||||
}
|
||||
|
||||
nlohmann::json GlobalConfig::toJSON()
|
||||
{
|
||||
auto res = nlohmann::json::object();
|
||||
for (const auto & config : *configRegistrations)
|
||||
for (const auto & config : configRegistrations())
|
||||
res.update(config->toJSON());
|
||||
return res;
|
||||
}
|
||||
|
|
@ -47,19 +47,15 @@ std::string GlobalConfig::toKeyValue()
|
|||
|
||||
void GlobalConfig::convertToArgs(Args & args, const std::string & category)
|
||||
{
|
||||
for (auto & config : *configRegistrations)
|
||||
for (auto & config : configRegistrations())
|
||||
config->convertToArgs(args, category);
|
||||
}
|
||||
|
||||
GlobalConfig globalConfig;
|
||||
|
||||
GlobalConfig::ConfigRegistrations * GlobalConfig::configRegistrations;
|
||||
|
||||
GlobalConfig::Register::Register(Config * config)
|
||||
{
|
||||
if (!configRegistrations)
|
||||
configRegistrations = new ConfigRegistrations;
|
||||
configRegistrations->emplace_back(config);
|
||||
configRegistrations().emplace_back(config);
|
||||
}
|
||||
|
||||
ExperimentalFeatureSettings experimentalFeatureSettings;
|
||||
|
|
|
|||
|
|
@ -220,7 +220,7 @@ void Config::convertToArgs(Args & args, const std::string & category)
|
|||
AbstractSetting::AbstractSetting(
|
||||
const std::string & name,
|
||||
const std::string & description,
|
||||
const std::set<std::string> & aliases,
|
||||
const StringSet & aliases,
|
||||
std::optional<ExperimentalFeature> experimentalFeature)
|
||||
: name(name)
|
||||
, description(stripIndentation(description))
|
||||
|
|
@ -428,7 +428,7 @@ PathSetting::PathSetting(Config * options,
|
|||
const Path & def,
|
||||
const std::string & name,
|
||||
const std::string & description,
|
||||
const std::set<std::string> & aliases)
|
||||
const StringSet & aliases)
|
||||
: BaseSetting<Path>(def, true, name, description, aliases)
|
||||
{
|
||||
options->addSetting(this);
|
||||
|
|
@ -444,7 +444,7 @@ OptionalPathSetting::OptionalPathSetting(Config * options,
|
|||
const std::optional<Path> & def,
|
||||
const std::string & name,
|
||||
const std::string & description,
|
||||
const std::set<std::string> & aliases)
|
||||
const StringSet & aliases)
|
||||
: BaseSetting<std::optional<Path>>(def, true, name, description, aliases)
|
||||
{
|
||||
options->addSetting(this);
|
||||
|
|
|
|||
|
|
@ -57,7 +57,7 @@ size_t savedStackSize = 0;
|
|||
void setStackSize(size_t stackSize)
|
||||
{
|
||||
struct rlimit limit;
|
||||
if (getrlimit(RLIMIT_STACK, &limit) == 0 && limit.rlim_cur < stackSize) {
|
||||
if (getrlimit(RLIMIT_STACK, &limit) == 0 && static_cast<size_t>(limit.rlim_cur) < stackSize) {
|
||||
savedStackSize = limit.rlim_cur;
|
||||
limit.rlim_cur = std::min(static_cast<rlim_t>(stackSize), limit.rlim_max);
|
||||
if (setrlimit(RLIMIT_STACK, &limit) != 0) {
|
||||
|
|
|
|||
|
|
@ -6,10 +6,6 @@
|
|||
|
||||
namespace nix {
|
||||
|
||||
namespace fs {
|
||||
using namespace std::filesystem;
|
||||
}
|
||||
|
||||
constexpr static const OsStringView path_var_separator{
|
||||
&ExecutablePath::separator,
|
||||
1,
|
||||
|
|
@ -28,7 +24,7 @@ ExecutablePath ExecutablePath::parse(const OsString & path)
|
|||
auto strings = path.empty() ? (std::list<OsString>{})
|
||||
: basicSplitString<std::list<OsString>, OsChar>(path, path_var_separator);
|
||||
|
||||
std::vector<fs::path> ret;
|
||||
std::vector<std::filesystem::path> ret;
|
||||
ret.reserve(strings.size());
|
||||
|
||||
std::transform(
|
||||
|
|
@ -36,7 +32,7 @@ ExecutablePath ExecutablePath::parse(const OsString & path)
|
|||
std::make_move_iterator(strings.end()),
|
||||
std::back_inserter(ret),
|
||||
[](OsString && str) {
|
||||
return fs::path{
|
||||
return std::filesystem::path{
|
||||
str.empty()
|
||||
// "A zero-length prefix is a legacy feature that
|
||||
// indicates the current working directory. It
|
||||
|
|
@ -62,13 +58,13 @@ OsString ExecutablePath::render() const
|
|||
return basicConcatStringsSep(path_var_separator, path2);
|
||||
}
|
||||
|
||||
std::optional<fs::path>
|
||||
ExecutablePath::findName(const OsString & exe, std::function<bool(const fs::path &)> isExecutable) const
|
||||
std::optional<std::filesystem::path>
|
||||
ExecutablePath::findName(const OsString & exe, std::function<bool(const std::filesystem::path &)> isExecutable) const
|
||||
{
|
||||
// "If the pathname being sought contains a <slash>, the search
|
||||
// through the path prefixes shall not be performed."
|
||||
// https://pubs.opengroup.org/onlinepubs/9699919799/basedefs/V1_chap08.html#tag_08_03
|
||||
assert(OsPathTrait<fs::path::value_type>::rfindPathSep(exe) == exe.npos);
|
||||
assert(OsPathTrait<std::filesystem::path::value_type>::rfindPathSep(exe) == exe.npos);
|
||||
|
||||
for (auto & dir : directories) {
|
||||
auto candidate = dir / exe;
|
||||
|
|
@ -79,7 +75,8 @@ ExecutablePath::findName(const OsString & exe, std::function<bool(const fs::path
|
|||
return std::nullopt;
|
||||
}
|
||||
|
||||
fs::path ExecutablePath::findPath(const fs::path & exe, std::function<bool(const fs::path &)> isExecutable) const
|
||||
std::filesystem::path ExecutablePath::findPath(
|
||||
const std::filesystem::path & exe, std::function<bool(const std::filesystem::path &)> isExecutable) const
|
||||
{
|
||||
// "If the pathname being sought contains a <slash>, the search
|
||||
// through the path prefixes shall not be performed."
|
||||
|
|
|
|||
|
|
@ -348,7 +348,7 @@ nlohmann::json documentExperimentalFeatures()
|
|||
return (nlohmann::json) res;
|
||||
}
|
||||
|
||||
std::set<ExperimentalFeature> parseFeatures(const std::set<std::string> & rawFeatures)
|
||||
std::set<ExperimentalFeature> parseFeatures(const StringSet & rawFeatures)
|
||||
{
|
||||
std::set<ExperimentalFeature> res;
|
||||
for (auto & rawFeature : rawFeatures)
|
||||
|
|
|
|||
|
|
@ -21,6 +21,8 @@
|
|||
#include <sys/time.h>
|
||||
#include <unistd.h>
|
||||
|
||||
#include <boost/iostreams/device/mapped_file.hpp>
|
||||
|
||||
#ifdef _WIN32
|
||||
# include <io.h>
|
||||
#endif
|
||||
|
|
@ -31,21 +33,37 @@
|
|||
|
||||
namespace nix {
|
||||
|
||||
namespace fs {
|
||||
using namespace std::filesystem;
|
||||
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 filesystem_error and throw SysError**
|
||||
// Adapt the error message as needed for SysError
|
||||
throw SysError("cannot read directory %s", p);
|
||||
}
|
||||
}
|
||||
|
||||
bool symlink_exists(const std::filesystem::path & path) {
|
||||
try {
|
||||
return std::filesystem::exists(std::filesystem::symlink_status(path));
|
||||
} catch (const std::filesystem::filesystem_error & e) {
|
||||
throw SysError("cannot check existence of %1%", path);
|
||||
}
|
||||
}
|
||||
DirectoryIterator& DirectoryIterator::operator++() {
|
||||
// **Attempt to increment the underlying iterator**
|
||||
std::error_code ec;
|
||||
it_.increment(ec);
|
||||
if (ec) {
|
||||
// Try to get path info if possible, might fail if iterator is bad
|
||||
try {
|
||||
if (it_ != std::filesystem::directory_iterator{}) {
|
||||
throw SysError("cannot read directory past %s: %s", it_->path(), ec.message());
|
||||
}
|
||||
} catch (...) {
|
||||
throw SysError("cannot read directory");
|
||||
}
|
||||
}
|
||||
return *this;
|
||||
}
|
||||
|
||||
bool isAbsolute(PathView path)
|
||||
{
|
||||
return fs::path { path }.is_absolute();
|
||||
return std::filesystem::path { path }.is_absolute();
|
||||
}
|
||||
|
||||
|
||||
|
|
@ -94,7 +112,7 @@ Path canonPath(PathView path, bool resolveSymlinks)
|
|||
throw Error("not an absolute path: '%1%'", path);
|
||||
|
||||
// For Windows
|
||||
auto rootName = fs::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,
|
||||
|
|
@ -109,7 +127,7 @@ Path canonPath(PathView path, bool resolveSymlinks)
|
|||
path,
|
||||
[&followCount, &temp, maxFollow, resolveSymlinks]
|
||||
(std::string & result, std::string_view & remaining) {
|
||||
if (resolveSymlinks && fs::is_symlink(result)) {
|
||||
if (resolveSymlinks && std::filesystem::is_symlink(result)) {
|
||||
if (++followCount >= maxFollow)
|
||||
throw Error("infinite symlink recursion in path '%1%'", remaining);
|
||||
remaining = (temp = concatStrings(readLink(result), remaining));
|
||||
|
|
@ -138,7 +156,7 @@ Path dirOf(const PathView path)
|
|||
Path::size_type pos = OsPathTrait<char>::rfindPathSep(path);
|
||||
if (pos == path.npos)
|
||||
return ".";
|
||||
return fs::path{path}.parent_path().string();
|
||||
return std::filesystem::path{path}.parent_path().string();
|
||||
}
|
||||
|
||||
|
||||
|
|
@ -161,16 +179,18 @@ std::string_view baseNameOf(std::string_view path)
|
|||
}
|
||||
|
||||
|
||||
bool isInDir(std::string_view path, std::string_view dir)
|
||||
bool isInDir(const std::filesystem::path & path, const std::filesystem::path & dir)
|
||||
{
|
||||
return path.substr(0, 1) == "/"
|
||||
&& path.substr(0, dir.size()) == dir
|
||||
&& path.size() >= dir.size() + 2
|
||||
&& path[dir.size()] == '/';
|
||||
/* Note that while the standard doesn't guarantee this, the
|
||||
`lexically_*` functions should do no IO and not throw. */
|
||||
auto rel = path.lexically_relative(dir);
|
||||
/* Method from
|
||||
https://stackoverflow.com/questions/62503197/check-if-path-contains-another-in-c++ */
|
||||
return !rel.empty() && rel.native()[0] != OS_STR('.');
|
||||
}
|
||||
|
||||
|
||||
bool isDirOrInDir(std::string_view path, std::string_view dir)
|
||||
bool isDirOrInDir(const std::filesystem::path & path, const std::filesystem::path & dir)
|
||||
{
|
||||
return path == dir || isInDir(path, dir);
|
||||
}
|
||||
|
|
@ -213,9 +233,9 @@ std::optional<struct stat> maybeLstat(const Path & path)
|
|||
}
|
||||
|
||||
|
||||
bool pathExists(const Path & path)
|
||||
bool pathExists(const std::filesystem::path & path)
|
||||
{
|
||||
return maybeLstat(path).has_value();
|
||||
return maybeLstat(path.string()).has_value();
|
||||
}
|
||||
|
||||
bool pathAccessible(const std::filesystem::path & path)
|
||||
|
|
@ -233,7 +253,7 @@ bool pathAccessible(const std::filesystem::path & path)
|
|||
Path readLink(const Path & path)
|
||||
{
|
||||
checkInterrupt();
|
||||
return fs::read_symlink(path).string();
|
||||
return std::filesystem::read_symlink(path).string();
|
||||
}
|
||||
|
||||
|
||||
|
|
@ -255,9 +275,22 @@ std::string readFile(const std::filesystem::path & path)
|
|||
return readFile(os_string_to_string(PathViewNG { path }));
|
||||
}
|
||||
|
||||
|
||||
void readFile(const Path & path, Sink & sink)
|
||||
void readFile(const Path & path, Sink & sink, bool memory_map)
|
||||
{
|
||||
// Memory-map the file for faster processing where possible.
|
||||
if (memory_map) {
|
||||
try {
|
||||
boost::iostreams::mapped_file_source mmap(path);
|
||||
if (mmap.is_open()) {
|
||||
sink({mmap.data(), mmap.size()});
|
||||
return;
|
||||
}
|
||||
} catch (const boost::exception & e) {
|
||||
}
|
||||
debug("memory-mapping failed for path: %s", path);
|
||||
}
|
||||
|
||||
// Stream the file instead if memory-mapping fails or is disabled.
|
||||
AutoCloseFD fd = toDescriptor(open(path.c_str(), O_RDONLY
|
||||
// TODO
|
||||
#ifndef _WIN32
|
||||
|
|
@ -351,17 +384,17 @@ void recursiveSync(const Path & path)
|
|||
|
||||
/* Otherwise, perform a depth-first traversal of the directory and
|
||||
fsync all the files. */
|
||||
std::deque<fs::path> dirsToEnumerate;
|
||||
std::deque<std::filesystem::path> dirsToEnumerate;
|
||||
dirsToEnumerate.push_back(path);
|
||||
std::vector<fs::path> dirsToFsync;
|
||||
std::vector<std::filesystem::path> dirsToFsync;
|
||||
while (!dirsToEnumerate.empty()) {
|
||||
auto currentDir = dirsToEnumerate.back();
|
||||
dirsToEnumerate.pop_back();
|
||||
for (auto & entry : std::filesystem::directory_iterator(currentDir)) {
|
||||
for (auto & entry : DirectoryIterator(currentDir)) {
|
||||
auto st = entry.symlink_status();
|
||||
if (fs::is_directory(st)) {
|
||||
if (std::filesystem::is_directory(st)) {
|
||||
dirsToEnumerate.emplace_back(entry.path());
|
||||
} else if (fs::is_regular_file(st)) {
|
||||
} else if (std::filesystem::is_regular_file(st)) {
|
||||
AutoCloseFD fd = toDescriptor(open(entry.path().string().c_str(), O_RDONLY, 0));
|
||||
if (!fd)
|
||||
throw SysError("opening file '%1%'", entry.path());
|
||||
|
|
@ -381,7 +414,7 @@ void recursiveSync(const Path & path)
|
|||
}
|
||||
|
||||
|
||||
static void _deletePath(Descriptor parentfd, const fs::path & path, uint64_t & bytesFreed)
|
||||
static void _deletePath(Descriptor parentfd, const std::filesystem::path & path, uint64_t & bytesFreed)
|
||||
{
|
||||
#ifndef _WIN32
|
||||
checkInterrupt();
|
||||
|
|
@ -455,7 +488,7 @@ static void _deletePath(Descriptor parentfd, const fs::path & path, uint64_t & b
|
|||
#endif
|
||||
}
|
||||
|
||||
static void _deletePath(const fs::path & path, uint64_t & bytesFreed)
|
||||
static void _deletePath(const std::filesystem::path & path, uint64_t & bytesFreed)
|
||||
{
|
||||
Path dir = dirOf(path.string());
|
||||
if (dir == "")
|
||||
|
|
@ -471,7 +504,7 @@ static void _deletePath(const fs::path & path, uint64_t & bytesFreed)
|
|||
}
|
||||
|
||||
|
||||
void deletePath(const fs::path & path)
|
||||
void deletePath(const std::filesystem::path & path)
|
||||
{
|
||||
uint64_t dummy;
|
||||
deletePath(path, dummy);
|
||||
|
|
@ -487,17 +520,17 @@ void createDir(const Path & path, mode_t mode)
|
|||
throw SysError("creating directory '%1%'", path);
|
||||
}
|
||||
|
||||
void createDirs(const fs::path & path)
|
||||
void createDirs(const std::filesystem::path & path)
|
||||
{
|
||||
try {
|
||||
fs::create_directories(path);
|
||||
} catch (fs::filesystem_error & e) {
|
||||
std::filesystem::create_directories(path);
|
||||
} catch (std::filesystem::filesystem_error & e) {
|
||||
throw SysError("creating directory '%1%'", path.string());
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void deletePath(const fs::path & path, uint64_t & bytesFreed)
|
||||
void deletePath(const std::filesystem::path & path, uint64_t & bytesFreed)
|
||||
{
|
||||
//Activity act(*logger, lvlDebug, "recursively deleting path '%1%'", path);
|
||||
bytesFreed = 0;
|
||||
|
|
@ -522,7 +555,7 @@ AutoDelete::~AutoDelete()
|
|||
if (recursive)
|
||||
deletePath(_path);
|
||||
else {
|
||||
fs::remove(_path);
|
||||
std::filesystem::remove(_path);
|
||||
}
|
||||
}
|
||||
} catch (...) {
|
||||
|
|
@ -535,7 +568,7 @@ void AutoDelete::cancel()
|
|||
del = false;
|
||||
}
|
||||
|
||||
void AutoDelete::reset(const fs::path & p, bool recursive) {
|
||||
void AutoDelete::reset(const std::filesystem::path & p, bool recursive) {
|
||||
_path = p;
|
||||
this->recursive = recursive;
|
||||
del = true;
|
||||
|
|
@ -611,30 +644,30 @@ std::pair<AutoCloseFD, Path> createTempFile(const Path & prefix)
|
|||
void createSymlink(const Path & target, const Path & link)
|
||||
{
|
||||
try {
|
||||
fs::create_symlink(target, link);
|
||||
} catch (fs::filesystem_error & e) {
|
||||
std::filesystem::create_symlink(target, link);
|
||||
} catch (std::filesystem::filesystem_error & e) {
|
||||
throw SysError("creating symlink '%1%' -> '%2%'", link, target);
|
||||
}
|
||||
}
|
||||
|
||||
void replaceSymlink(const fs::path & target, const fs::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() / fs::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 {
|
||||
fs::create_symlink(target, tmp);
|
||||
} catch (fs::filesystem_error & e) {
|
||||
std::filesystem::create_symlink(target, tmp);
|
||||
} catch (std::filesystem::filesystem_error & e) {
|
||||
if (e.code() == std::errc::file_exists) continue;
|
||||
throw SysError("creating symlink '%1%' -> '%2%'", tmp, target);
|
||||
throw SysError("creating symlink %1% -> %2%", tmp, target);
|
||||
}
|
||||
|
||||
try {
|
||||
fs::rename(tmp, link);
|
||||
} catch (fs::filesystem_error & e) {
|
||||
std::filesystem::rename(tmp, link);
|
||||
} catch (std::filesystem::filesystem_error & e) {
|
||||
if (e.code() == std::errc::file_exists) continue;
|
||||
throw SysError("renaming '%1%' to '%2%'", tmp, link);
|
||||
throw SysError("renaming %1% to %2%", tmp, link);
|
||||
}
|
||||
|
||||
|
||||
|
|
@ -642,26 +675,25 @@ void replaceSymlink(const fs::path & target, const fs::path & link)
|
|||
}
|
||||
}
|
||||
|
||||
void setWriteTime(const fs::path & path, const struct stat & st)
|
||||
void setWriteTime(const std::filesystem::path & path, const struct stat & st)
|
||||
{
|
||||
setWriteTime(path, st.st_atime, st.st_mtime, S_ISLNK(st.st_mode));
|
||||
}
|
||||
|
||||
void copyFile(const fs::path & from, const fs::path & to, bool andDelete)
|
||||
void copyFile(const std::filesystem::path & from, const std::filesystem::path & to, bool andDelete)
|
||||
{
|
||||
auto fromStatus = fs::symlink_status(from);
|
||||
auto fromStatus =std::filesystem::symlink_status(from);
|
||||
|
||||
// Mark the directory as writable so that we can delete its children
|
||||
if (andDelete && fs::is_directory(fromStatus)) {
|
||||
fs::permissions(from, fs::perms::owner_write, fs::perm_options::add | fs::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 (fs::is_symlink(fromStatus) || fs::is_regular_file(fromStatus)) {
|
||||
fs::copy(from, to, fs::copy_options::copy_symlinks | fs::copy_options::overwrite_existing);
|
||||
} else if (fs::is_directory(fromStatus)) {
|
||||
fs::create_directory(to);
|
||||
for (auto & entry : fs::directory_iterator(from)) {
|
||||
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)) {
|
||||
copyFile(entry, to / entry.path().filename(), andDelete);
|
||||
}
|
||||
} else {
|
||||
|
|
@ -670,9 +702,9 @@ void copyFile(const fs::path & from, const fs::path & to, bool andDelete)
|
|||
|
||||
setWriteTime(to, lstat(from.string().c_str()));
|
||||
if (andDelete) {
|
||||
if (!fs::is_symlink(fromStatus))
|
||||
fs::permissions(from, fs::perms::owner_write, fs::perm_options::add | fs::perm_options::nofollow);
|
||||
fs::remove(from);
|
||||
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::remove(from);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -680,18 +712,18 @@ void moveFile(const Path & oldName, const Path & newName)
|
|||
{
|
||||
try {
|
||||
std::filesystem::rename(oldName, newName);
|
||||
} catch (fs::filesystem_error & e) {
|
||||
auto oldPath = fs::path(oldName);
|
||||
auto newPath = fs::path(newName);
|
||||
} catch (std::filesystem::filesystem_error & e) {
|
||||
auto oldPath = std::filesystem::path(oldName);
|
||||
auto newPath = std::filesystem::path(newName);
|
||||
// For the move to be as atomic as possible, copy to a temporary
|
||||
// directory
|
||||
fs::path temp = createTempDir(
|
||||
std::filesystem::path temp = createTempDir(
|
||||
os_string_to_string(PathViewNG { newPath.parent_path() }),
|
||||
"rename-tmp");
|
||||
Finally removeTemp = [&]() { fs::remove(temp); };
|
||||
Finally removeTemp = [&]() { std::filesystem::remove(temp); };
|
||||
auto tempCopyTarget = temp / "copy-target";
|
||||
if (e.code().value() == EXDEV) {
|
||||
fs::remove(newPath);
|
||||
std::filesystem::remove(newPath);
|
||||
warn("can’t rename %s as %s, copying instead", oldName, newName);
|
||||
copyFile(oldPath, tempCopyTarget, true);
|
||||
std::filesystem::rename(
|
||||
|
|
@ -703,7 +735,7 @@ void moveFile(const Path & oldName, const Path & newName)
|
|||
|
||||
//////////////////////////////////////////////////////////////////////
|
||||
|
||||
bool isExecutableFileAmbient(const fs::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.
|
||||
|
|
@ -727,9 +759,23 @@ std::filesystem::path makeParentCanonical(const std::filesystem::path & rawPath)
|
|||
return parent;
|
||||
}
|
||||
return std::filesystem::canonical(parent) / path.filename();
|
||||
} catch (fs::filesystem_error & e) {
|
||||
} catch (std::filesystem::filesystem_error & e) {
|
||||
throw SysError("canonicalising parent path of '%1%'", path);
|
||||
}
|
||||
}
|
||||
|
||||
bool chmodIfNeeded(const std::filesystem::path & path, mode_t mode, mode_t mask)
|
||||
{
|
||||
auto pathString = path.string();
|
||||
auto prevMode = lstat(pathString).st_mode;
|
||||
|
||||
if (((prevMode ^ mode) & mask) == 0)
|
||||
return false;
|
||||
|
||||
if (chmod(pathString.c_str(), mode) != 0)
|
||||
throw SysError("could not set permissions on '%s' to %o", pathString, mode);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
} // namespace nix
|
||||
|
|
|
|||
|
|
@ -34,9 +34,9 @@ static size_t regularHashSize(HashAlgorithm type) {
|
|||
}
|
||||
|
||||
|
||||
const std::set<std::string> hashAlgorithms = {"blake3", "md5", "sha1", "sha256", "sha512" };
|
||||
const StringSet hashAlgorithms = {"blake3", "md5", "sha1", "sha256", "sha512" };
|
||||
|
||||
const std::set<std::string> hashFormats = {"base64", "nix32", "base16", "sri" };
|
||||
const StringSet hashFormats = {"base64", "nix32", "base16", "sri" };
|
||||
|
||||
Hash::Hash(HashAlgorithm algo, const ExperimentalFeatureSettings & xpSettings) : algo(algo)
|
||||
{
|
||||
|
|
@ -309,11 +309,31 @@ static void start(HashAlgorithm ha, Ctx & ctx)
|
|||
else if (ha == HashAlgorithm::SHA512) SHA512_Init(&ctx.sha512);
|
||||
}
|
||||
|
||||
// BLAKE3 data size threshold beyond which parallel hashing with TBB is likely faster.
|
||||
//
|
||||
// NOTE: This threshold is based on the recommended rule-of-thumb from the official BLAKE3 documentation for typical
|
||||
// x86_64 hardware as of 2025. In the future it may make sense to allow the user to tune this through nix.conf.
|
||||
const size_t blake3TbbThreshold = 128000;
|
||||
|
||||
// Decide which BLAKE3 update strategy to use based on some heuristics. Currently this just checks the data size but in
|
||||
// the future it might also take into consideration available system resources or the presence of a shared-memory
|
||||
// capable GPU for a heterogenous compute implementation.
|
||||
void blake3_hasher_update_with_heuristics(blake3_hasher * blake3, std::string_view data)
|
||||
{
|
||||
#ifdef BLAKE3_USE_TBB
|
||||
if (data.size() >= blake3TbbThreshold) {
|
||||
blake3_hasher_update_tbb(blake3, data.data(), data.size());
|
||||
} else
|
||||
#endif
|
||||
{
|
||||
blake3_hasher_update(blake3, data.data(), data.size());
|
||||
}
|
||||
}
|
||||
|
||||
static void update(HashAlgorithm ha, Ctx & ctx,
|
||||
std::string_view data)
|
||||
{
|
||||
if (ha == HashAlgorithm::BLAKE3) blake3_hasher_update(&ctx.blake3, 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());
|
||||
|
|
|
|||
|
|
@ -179,7 +179,7 @@ public:
|
|||
using ptr = std::shared_ptr<Flag>;
|
||||
|
||||
std::string longName;
|
||||
std::set<std::string> aliases;
|
||||
StringSet aliases;
|
||||
char shortName = 0;
|
||||
std::string description;
|
||||
std::string category;
|
||||
|
|
@ -263,7 +263,7 @@ protected:
|
|||
virtual Strings::iterator rewriteArgs(Strings & args, Strings::iterator pos)
|
||||
{ return pos; }
|
||||
|
||||
std::set<std::string> hiddenCategories;
|
||||
StringSet hiddenCategories;
|
||||
|
||||
/**
|
||||
* Called after all command line flags before the first non-flag
|
||||
|
|
|
|||
|
|
@ -45,9 +45,10 @@ public:
|
|||
addChunk();
|
||||
}
|
||||
|
||||
uint32_t size() const { return size_; }
|
||||
uint32_t size() const noexcept { return size_; }
|
||||
|
||||
std::pair<T &, uint32_t> add(T value)
|
||||
template <typename... Args>
|
||||
std::pair<T &, uint32_t> add(Args &&... args)
|
||||
{
|
||||
const auto idx = size_++;
|
||||
auto & chunk = [&] () -> auto & {
|
||||
|
|
@ -55,11 +56,16 @@ public:
|
|||
return back;
|
||||
return addChunk();
|
||||
}();
|
||||
auto & result = chunk.emplace_back(std::move(value));
|
||||
auto & result = chunk.emplace_back(std::forward<Args>(args)...);
|
||||
return {result, idx};
|
||||
}
|
||||
|
||||
const T & operator[](uint32_t idx) const
|
||||
/**
|
||||
* Unchecked subscript operator.
|
||||
* @pre add must have been called at least idx + 1 times.
|
||||
* @throws nothing
|
||||
*/
|
||||
const T & operator[](uint32_t idx) const noexcept
|
||||
{
|
||||
return chunks[idx / ChunkSize][idx % ChunkSize];
|
||||
}
|
||||
|
|
|
|||
|
|
@ -8,7 +8,12 @@ namespace nix {
|
|||
struct GlobalConfig : public AbstractConfig
|
||||
{
|
||||
typedef std::vector<Config *> ConfigRegistrations;
|
||||
static ConfigRegistrations * configRegistrations;
|
||||
|
||||
static ConfigRegistrations & configRegistrations()
|
||||
{
|
||||
static ConfigRegistrations configRegistrations;
|
||||
return configRegistrations;
|
||||
}
|
||||
|
||||
bool set(const std::string & name, const std::string & value) override;
|
||||
|
||||
|
|
|
|||
|
|
@ -179,7 +179,7 @@ public:
|
|||
|
||||
const std::string name;
|
||||
const std::string description;
|
||||
const std::set<std::string> aliases;
|
||||
const StringSet aliases;
|
||||
|
||||
int created = 123;
|
||||
|
||||
|
|
@ -192,7 +192,7 @@ protected:
|
|||
AbstractSetting(
|
||||
const std::string & name,
|
||||
const std::string & description,
|
||||
const std::set<std::string> & aliases,
|
||||
const StringSet & aliases,
|
||||
std::optional<ExperimentalFeature> experimentalFeature = std::nullopt);
|
||||
|
||||
virtual ~AbstractSetting();
|
||||
|
|
@ -251,7 +251,7 @@ public:
|
|||
const bool documentDefault,
|
||||
const std::string & name,
|
||||
const std::string & description,
|
||||
const std::set<std::string> & aliases = {},
|
||||
const StringSet & aliases = {},
|
||||
std::optional<ExperimentalFeature> experimentalFeature = std::nullopt)
|
||||
: AbstractSetting(name, description, aliases, experimentalFeature)
|
||||
, value(def)
|
||||
|
|
@ -323,7 +323,7 @@ public:
|
|||
const T & def,
|
||||
const std::string & name,
|
||||
const std::string & description,
|
||||
const std::set<std::string> & aliases = {},
|
||||
const StringSet & aliases = {},
|
||||
const bool documentDefault = true,
|
||||
std::optional<ExperimentalFeature> experimentalFeature = std::nullopt)
|
||||
: BaseSetting<T>(def, documentDefault, name, description, aliases, std::move(experimentalFeature))
|
||||
|
|
@ -349,7 +349,7 @@ public:
|
|||
const Path & def,
|
||||
const std::string & name,
|
||||
const std::string & description,
|
||||
const std::set<std::string> & aliases = {});
|
||||
const StringSet & aliases = {});
|
||||
|
||||
Path parse(const std::string & str) const override;
|
||||
|
||||
|
|
@ -371,7 +371,7 @@ public:
|
|||
const std::optional<Path> & def,
|
||||
const std::string & name,
|
||||
const std::string & description,
|
||||
const std::set<std::string> & aliases = {});
|
||||
const StringSet & aliases = {});
|
||||
|
||||
std::optional<Path> parse(const std::string & str) const override;
|
||||
|
||||
|
|
|
|||
|
|
@ -76,7 +76,7 @@ std::ostream & operator<<(
|
|||
* Parse a set of strings to the corresponding set of experimental
|
||||
* features, ignoring (but warning for) any unknown feature.
|
||||
*/
|
||||
std::set<ExperimentalFeature> parseFeatures(const std::set<std::string> &);
|
||||
std::set<ExperimentalFeature> parseFeatures(const StringSet &);
|
||||
|
||||
/**
|
||||
* An experimental feature was required for some (experimental)
|
||||
|
|
|
|||
|
|
@ -2,7 +2,7 @@
|
|||
/**
|
||||
* @file
|
||||
*
|
||||
* Utiltities for working with the file sytem and file paths.
|
||||
* Utilities for working with the file system and file paths.
|
||||
*/
|
||||
|
||||
#include "nix/util/types.hh"
|
||||
|
|
@ -105,13 +105,13 @@ std::string_view baseNameOf(std::string_view path);
|
|||
* Check whether 'path' is a descendant of 'dir'. Both paths must be
|
||||
* canonicalized.
|
||||
*/
|
||||
bool isInDir(std::string_view path, std::string_view dir);
|
||||
bool isInDir(const std::filesystem::path & path, const std::filesystem::path & dir);
|
||||
|
||||
/**
|
||||
* Check whether 'path' is equal to 'dir' or a descendant of
|
||||
* 'dir'. Both paths must be canonicalized.
|
||||
*/
|
||||
bool isDirOrInDir(std::string_view path, std::string_view dir);
|
||||
bool isDirOrInDir(const std::filesystem::path & path, const std::filesystem::path & dir);
|
||||
|
||||
/**
|
||||
* Get status of `path`.
|
||||
|
|
@ -126,26 +126,8 @@ std::optional<struct stat> maybeLstat(const Path & path);
|
|||
|
||||
/**
|
||||
* @return true iff the given path exists.
|
||||
*
|
||||
* In the process of being deprecated for `fs::symlink_exists`.
|
||||
*/
|
||||
bool pathExists(const Path & path);
|
||||
|
||||
namespace fs {
|
||||
|
||||
/**
|
||||
* TODO: we may actually want to use pathExists instead of this function
|
||||
* ```
|
||||
* symlink_exists(p) = std::filesystem::exists(std::filesystem::symlink_status(p))
|
||||
* ```
|
||||
* Missing convenience analogous to
|
||||
* ```
|
||||
* std::filesystem::exists(p) = std::filesystem::exists(std::filesystem::status(p))
|
||||
* ```
|
||||
*/
|
||||
bool symlink_exists(const std::filesystem::path & path);
|
||||
|
||||
} // namespace fs
|
||||
bool pathExists(const std::filesystem::path & path);
|
||||
|
||||
/**
|
||||
* Canonicalize a path except for the last component.
|
||||
|
|
@ -191,7 +173,7 @@ Descriptor openDirectory(const std::filesystem::path & path);
|
|||
*/
|
||||
std::string readFile(const Path & path);
|
||||
std::string readFile(const std::filesystem::path & path);
|
||||
void readFile(const Path & path, Sink & sink);
|
||||
void readFile(const Path & path, Sink & sink, bool memory_map = true);
|
||||
|
||||
/**
|
||||
* Write a string to a file.
|
||||
|
|
@ -360,4 +342,80 @@ typedef std::function<bool(const Path & path)> PathFilter;
|
|||
|
||||
extern PathFilter defaultPathFilter;
|
||||
|
||||
/**
|
||||
* Change permissions of a file only if necessary.
|
||||
*
|
||||
* @details
|
||||
* Skip chmod call if the directory already has the requested permissions.
|
||||
* This is to avoid failing when the executing user lacks permissions to change the
|
||||
* directory's permissions even if it would be no-op.
|
||||
*
|
||||
* @param path Path to the file to change the permissions for.
|
||||
* @param mode New file mode.
|
||||
* @param mask Used for checking if the file already has requested permissions.
|
||||
*
|
||||
* @return true if permissions changed, false otherwise.
|
||||
*/
|
||||
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 {
|
||||
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&;
|
||||
|
||||
// Default constructor (represents end iterator)
|
||||
DirectoryIterator() noexcept = default;
|
||||
|
||||
// Constructor taking a path
|
||||
explicit DirectoryIterator(const std::filesystem::path& p);
|
||||
|
||||
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 {
|
||||
return &(*it_);
|
||||
}
|
||||
|
||||
|
||||
DirectoryIterator& operator++();
|
||||
|
||||
// Postfix increment operator
|
||||
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 {
|
||||
return a.it_ == b.it_;
|
||||
}
|
||||
|
||||
// Inequality comparison
|
||||
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{}; }
|
||||
|
||||
|
||||
private:
|
||||
std::filesystem::directory_iterator it_;
|
||||
};
|
||||
|
||||
}
|
||||
|
|
|
|||
|
|
@ -22,7 +22,7 @@ const int sha1HashSize = 20;
|
|||
const int sha256HashSize = 32;
|
||||
const int sha512HashSize = 64;
|
||||
|
||||
extern const std::set<std::string> hashAlgorithms;
|
||||
extern const StringSet hashAlgorithms;
|
||||
|
||||
extern const std::string nix32Chars;
|
||||
|
||||
|
|
@ -42,7 +42,7 @@ enum struct HashFormat : int {
|
|||
SRI
|
||||
};
|
||||
|
||||
extern const std::set<std::string> hashFormats;
|
||||
extern const StringSet hashFormats;
|
||||
|
||||
struct Hash
|
||||
{
|
||||
|
|
|
|||
|
|
@ -4,6 +4,7 @@
|
|||
#include <nlohmann/json.hpp>
|
||||
#include <list>
|
||||
|
||||
#include "nix/util/error.hh"
|
||||
#include "nix/util/types.hh"
|
||||
|
||||
namespace nix {
|
||||
|
|
@ -35,7 +36,26 @@ const nlohmann::json * getNullable(const nlohmann::json & value);
|
|||
const nlohmann::json::object_t & getObject(const nlohmann::json & value);
|
||||
const nlohmann::json::array_t & getArray(const nlohmann::json & value);
|
||||
const nlohmann::json::string_t & getString(const nlohmann::json & value);
|
||||
const nlohmann::json::number_integer_t & getInteger(const nlohmann::json & value);
|
||||
const nlohmann::json::number_unsigned_t & getUnsigned(const nlohmann::json & value);
|
||||
|
||||
template<typename T>
|
||||
auto getInteger(const nlohmann::json & value) -> std::enable_if_t<std::is_signed_v<T> && std::is_integral_v<T>, T>
|
||||
{
|
||||
if (auto ptr = value.get_ptr<const nlohmann::json::number_unsigned_t *>()) {
|
||||
if (*ptr <= std::make_unsigned_t<T>(std::numeric_limits<T>::max())) {
|
||||
return *ptr;
|
||||
}
|
||||
} else if (auto ptr = value.get_ptr<const nlohmann::json::number_integer_t *>()) {
|
||||
if (*ptr >= std::numeric_limits<T>::min() && *ptr <= std::numeric_limits<T>::max()) {
|
||||
return *ptr;
|
||||
}
|
||||
} else {
|
||||
auto typeName = value.is_number_float() ? "floating point number" : value.type_name();
|
||||
throw Error("Expected JSON value to be an integral number but it is of type '%s': %s", typeName, value.dump());
|
||||
}
|
||||
throw Error("Out of range: JSON value '%s' cannot be casted to %d-bit integer", value.dump(), 8 * sizeof(T));
|
||||
}
|
||||
|
||||
const nlohmann::json::boolean_t & getBoolean(const nlohmann::json & value);
|
||||
Strings getStringList(const nlohmann::json & value);
|
||||
StringMap getStringMap(const nlohmann::json & value);
|
||||
|
|
@ -70,8 +90,8 @@ struct json_avoids_null<std::vector<T>> : std::true_type {};
|
|||
template<typename T>
|
||||
struct json_avoids_null<std::list<T>> : std::true_type {};
|
||||
|
||||
template<typename T>
|
||||
struct json_avoids_null<std::set<T>> : std::true_type {};
|
||||
template<typename T, typename Compare>
|
||||
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 {};
|
||||
|
|
|
|||
|
|
@ -11,7 +11,7 @@ namespace nix {
|
|||
/**
|
||||
* A simple least-recently used cache. Not thread-safe.
|
||||
*/
|
||||
template<typename Key, typename Value>
|
||||
template<typename Key, typename Value, typename Compare = std::less<>>
|
||||
class LRUCache
|
||||
{
|
||||
private:
|
||||
|
|
@ -22,24 +22,32 @@ private:
|
|||
// and LRU.
|
||||
struct LRUIterator;
|
||||
|
||||
using Data = std::map<Key, std::pair<LRUIterator, Value>>;
|
||||
using Data = std::map<Key, std::pair<LRUIterator, Value>, Compare>;
|
||||
using LRU = std::list<typename Data::iterator>;
|
||||
|
||||
struct LRUIterator { typename LRU::iterator it; };
|
||||
struct LRUIterator
|
||||
{
|
||||
typename LRU::iterator it;
|
||||
};
|
||||
|
||||
Data data;
|
||||
LRU lru;
|
||||
|
||||
public:
|
||||
|
||||
LRUCache(size_t capacity) : capacity(capacity) { }
|
||||
LRUCache(size_t capacity)
|
||||
: capacity(capacity)
|
||||
{
|
||||
}
|
||||
|
||||
/**
|
||||
* Insert or upsert an item in the cache.
|
||||
*/
|
||||
void upsert(const Key & key, const Value & value)
|
||||
template<typename K>
|
||||
void upsert(const K & key, const Value & value)
|
||||
{
|
||||
if (capacity == 0) return;
|
||||
if (capacity == 0)
|
||||
return;
|
||||
|
||||
erase(key);
|
||||
|
||||
|
|
@ -61,10 +69,12 @@ public:
|
|||
i->second.first.it = j;
|
||||
}
|
||||
|
||||
bool erase(const Key & key)
|
||||
template<typename K>
|
||||
bool erase(const K & key)
|
||||
{
|
||||
auto i = data.find(key);
|
||||
if (i == data.end()) return false;
|
||||
if (i == data.end())
|
||||
return false;
|
||||
lru.erase(i->second.first.it);
|
||||
data.erase(i);
|
||||
return true;
|
||||
|
|
@ -74,27 +84,33 @@ public:
|
|||
* Look up an item in the cache. If it exists, it becomes the most
|
||||
* recently used item.
|
||||
* */
|
||||
std::optional<Value> get(const Key & key)
|
||||
template<typename K>
|
||||
std::optional<Value> get(const K & key)
|
||||
{
|
||||
auto i = data.find(key);
|
||||
if (i == data.end()) return {};
|
||||
if (i == data.end())
|
||||
return {};
|
||||
|
||||
/**
|
||||
* Move this item to the back of the LRU list.
|
||||
*
|
||||
* Think of std::list iterators as stable pointers to the list node,
|
||||
* which never get invalidated. Thus, we can reuse the same lru list
|
||||
* element and just splice it to the back of the list without the need
|
||||
* to update its value in the key -> list iterator map.
|
||||
*/
|
||||
lru.erase(i->second.first.it);
|
||||
auto j = lru.insert(lru.end(), i);
|
||||
i->second.first.it = j;
|
||||
auto & [it, value] = i->second;
|
||||
lru.splice(/*pos=*/lru.end(), /*other=*/lru, it.it);
|
||||
|
||||
return i->second.second;
|
||||
return value;
|
||||
}
|
||||
|
||||
size_t size() const
|
||||
size_t size() const noexcept
|
||||
{
|
||||
return data.size();
|
||||
}
|
||||
|
||||
void clear()
|
||||
void clear() noexcept
|
||||
{
|
||||
data.clear();
|
||||
lru.clear();
|
||||
|
|
|
|||
|
|
@ -220,4 +220,10 @@ ref<SourceAccessor> makeFSSourceAccessor(std::filesystem::path root);
|
|||
*/
|
||||
ref<SourceAccessor> makeUnionSourceAccessor(std::vector<ref<SourceAccessor>> && accessors);
|
||||
|
||||
/**
|
||||
* Creates a new source accessor which is confined to the subdirectory
|
||||
* of the given source accessor.
|
||||
*/
|
||||
ref<SourceAccessor> projectSubdirSourceAccessor(ref<SourceAccessor>, CanonPath subdirectory);
|
||||
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,5 +1,7 @@
|
|||
#pragma once
|
||||
|
||||
#include "nix/util/types.hh"
|
||||
|
||||
#include <list>
|
||||
#include <set>
|
||||
#include <string_view>
|
||||
|
|
@ -30,7 +32,7 @@ template<class C>
|
|||
C tokenizeString(std::string_view s, std::string_view separators = " \t\n\r");
|
||||
|
||||
extern template std::list<std::string> tokenizeString(std::string_view s, std::string_view separators);
|
||||
extern template std::set<std::string> tokenizeString(std::string_view s, std::string_view separators);
|
||||
extern template StringSet tokenizeString(std::string_view s, std::string_view separators);
|
||||
extern template std::vector<std::string> tokenizeString(std::string_view s, std::string_view separators);
|
||||
|
||||
/**
|
||||
|
|
@ -44,7 +46,7 @@ template<typename C>
|
|||
C splitString(std::string_view s, std::string_view separators);
|
||||
|
||||
extern template std::list<std::string> splitString(std::string_view s, std::string_view separators);
|
||||
extern template std::set<std::string> splitString(std::string_view s, std::string_view separators);
|
||||
extern template StringSet splitString(std::string_view s, std::string_view separators);
|
||||
extern template std::vector<std::string> splitString(std::string_view s, std::string_view separators);
|
||||
|
||||
/**
|
||||
|
|
@ -54,7 +56,7 @@ template<class C>
|
|||
std::string concatStringsSep(const std::string_view sep, const C & ss);
|
||||
|
||||
extern template std::string concatStringsSep(std::string_view, const std::list<std::string> &);
|
||||
extern template std::string concatStringsSep(std::string_view, const std::set<std::string> &);
|
||||
extern template std::string concatStringsSep(std::string_view, const StringSet &);
|
||||
extern template std::string concatStringsSep(std::string_view, const std::vector<std::string> &);
|
||||
extern template std::string concatStringsSep(std::string_view, const boost::container::small_vector<std::string, 64> &);
|
||||
|
||||
|
|
@ -85,7 +87,7 @@ template<class C>
|
|||
dropEmptyInitThenConcatStringsSep(const std::string_view sep, const C & ss);
|
||||
|
||||
extern template std::string dropEmptyInitThenConcatStringsSep(std::string_view, const std::list<std::string> &);
|
||||
extern template std::string dropEmptyInitThenConcatStringsSep(std::string_view, const std::set<std::string> &);
|
||||
extern template std::string dropEmptyInitThenConcatStringsSep(std::string_view, const StringSet &);
|
||||
extern template std::string dropEmptyInitThenConcatStringsSep(std::string_view, const std::vector<std::string> &);
|
||||
|
||||
/**
|
||||
|
|
@ -95,4 +97,39 @@ extern template std::string dropEmptyInitThenConcatStringsSep(std::string_view,
|
|||
* Arguments that need to be passed to ssh with spaces in them.
|
||||
*/
|
||||
std::list<std::string> shellSplitString(std::string_view s);
|
||||
|
||||
/**
|
||||
* Hash implementation that can be used for zero-copy heterogenous lookup from
|
||||
* P1690R1[1] in unordered containers.
|
||||
*
|
||||
* [1]: https://www.open-std.org/jtc1/sc22/wg21/docs/papers/2019/p1690r1.html
|
||||
*/
|
||||
struct StringViewHash
|
||||
{
|
||||
private:
|
||||
using HashType = std::hash<std::string_view>;
|
||||
|
||||
public:
|
||||
using is_transparent = void;
|
||||
|
||||
auto operator()(const char * str) const
|
||||
{
|
||||
/* This has a slight overhead due to an implicit strlen, but there isn't
|
||||
a good way around it because the hash value of all overloads must be
|
||||
consistent. Delegating to string_view is the solution initially proposed
|
||||
in P0919R3. */
|
||||
return HashType{}(std::string_view{str});
|
||||
}
|
||||
|
||||
auto operator()(std::string_view str) const
|
||||
{
|
||||
return HashType{}(str);
|
||||
}
|
||||
|
||||
auto operator()(const std::string & str) const
|
||||
{
|
||||
return HashType{}(std::string_view{str});
|
||||
}
|
||||
};
|
||||
|
||||
}
|
||||
|
|
|
|||
|
|
@ -35,7 +35,7 @@ public:
|
|||
) const;
|
||||
|
||||
static Suggestions bestMatches (
|
||||
const std::set<std::string> & allMatches,
|
||||
const StringSet & allMatches,
|
||||
std::string_view query
|
||||
);
|
||||
|
||||
|
|
|
|||
|
|
@ -5,13 +5,13 @@
|
|||
|
||||
namespace nix {
|
||||
|
||||
template<typename T>
|
||||
std::vector<T> topoSort(std::set<T> items,
|
||||
std::function<std::set<T>(const T &)> getChildren,
|
||||
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> sorted;
|
||||
std::set<T> visited, parents;
|
||||
decltype(items) visited, parents;
|
||||
|
||||
std::function<void(const T & path, const T * parent)> dfsVisit;
|
||||
|
||||
|
|
@ -21,7 +21,7 @@ std::vector<T> topoSort(std::set<T> items,
|
|||
if (!visited.insert(path).second) return;
|
||||
parents.insert(path);
|
||||
|
||||
std::set<T> references = getChildren(path);
|
||||
auto references = getChildren(path);
|
||||
|
||||
for (auto & i : references)
|
||||
/* Don't traverse into items that don't exist in our starting set. */
|
||||
|
|
|
|||
|
|
@ -12,17 +12,35 @@
|
|||
namespace nix {
|
||||
|
||||
typedef std::list<std::string> Strings;
|
||||
typedef std::set<std::string> StringSet;
|
||||
typedef std::map<std::string, std::string> StringMap;
|
||||
typedef std::map<std::string, std::string> StringPairs;
|
||||
|
||||
/**
|
||||
* Alias to ordered set container with transparent comparator.
|
||||
*
|
||||
* Used instead of std::set<std::string> to use C++14 N3657 [1]
|
||||
* heterogenous lookup consistently across the whole codebase.
|
||||
* Transparent comparators get rid of creation of unnecessary
|
||||
* temporary variables when looking up keys by `std::string_view`
|
||||
* or C-style `const char *` strings.
|
||||
*
|
||||
* [1]: https://www.open-std.org/jtc1/sc22/wg21/docs/papers/2013/n3657.htm
|
||||
*/
|
||||
using StringSet = std::set<std::string, std::less<>>;
|
||||
|
||||
/**
|
||||
* Paths are just strings.
|
||||
*/
|
||||
typedef std::string Path;
|
||||
typedef std::string_view PathView;
|
||||
typedef std::list<Path> Paths;
|
||||
typedef std::set<Path> PathSet;
|
||||
|
||||
/**
|
||||
* Alias to an ordered set of `Path`s. Uses transparent comparator.
|
||||
*
|
||||
* @see StringSet
|
||||
*/
|
||||
using PathSet = std::set<Path, std::less<>>;
|
||||
|
||||
typedef std::vector<std::pair<std::string, std::string>> Headers;
|
||||
|
||||
|
|
|
|||
|
|
@ -152,8 +152,13 @@ std::string toLower(std::string s);
|
|||
|
||||
/**
|
||||
* Escape a string as a shell word.
|
||||
*
|
||||
* This always adds single quotes, even if escaping is not strictly necessary.
|
||||
* So both
|
||||
* - `"hello world"` -> `"'hello world'"`, which needs escaping because of the space
|
||||
* - `"echo"` -> `"'echo'"`, which doesn't need escaping
|
||||
*/
|
||||
std::string shellEscape(const std::string_view s);
|
||||
std::string escapeShellArgAlways(const std::string_view s);
|
||||
|
||||
|
||||
/**
|
||||
|
|
|
|||
|
|
@ -92,9 +92,18 @@ const nlohmann::json::string_t & getString(const nlohmann::json & value)
|
|||
return ensureType(value, nlohmann::json::value_t::string).get_ref<const nlohmann::json::string_t &>();
|
||||
}
|
||||
|
||||
const nlohmann::json::number_integer_t & getInteger(const nlohmann::json & value)
|
||||
const nlohmann::json::number_unsigned_t & getUnsigned(const nlohmann::json & value)
|
||||
{
|
||||
return ensureType(value, nlohmann::json::value_t::number_integer).get_ref<const nlohmann::json::number_integer_t &>();
|
||||
if (auto ptr = value.get<const nlohmann::json::number_unsigned_t *>()) {
|
||||
return *ptr;
|
||||
}
|
||||
const char * typeName = value.type_name();
|
||||
if (typeName == nlohmann::json(0).type_name()) {
|
||||
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());
|
||||
}
|
||||
|
||||
const nlohmann::json::boolean_t & getBoolean(const nlohmann::json & value)
|
||||
|
|
|
|||
|
|
@ -65,7 +65,7 @@ static CgroupStats destroyCgroup(const std::filesystem::path & cgroup, bool retu
|
|||
|
||||
/* Otherwise, manually kill every process in the subcgroups and
|
||||
this cgroup. */
|
||||
for (auto & entry : std::filesystem::directory_iterator{cgroup}) {
|
||||
for (auto & entry : DirectoryIterator{cgroup}) {
|
||||
checkInterrupt();
|
||||
if (entry.symlink_status().type() != std::filesystem::file_type::directory) continue;
|
||||
destroyCgroup(cgroup / entry.path().filename(), false);
|
||||
|
|
@ -134,7 +134,7 @@ static CgroupStats destroyCgroup(const std::filesystem::path & cgroup, bool retu
|
|||
}
|
||||
|
||||
if (rmdir(cgroup.c_str()) == -1)
|
||||
throw SysError("deleting cgroup '%s'", cgroup);
|
||||
throw SysError("deleting cgroup %s", cgroup);
|
||||
|
||||
return stats;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -50,13 +50,14 @@ endif
|
|||
|
||||
blake3 = dependency(
|
||||
'libblake3',
|
||||
version: '>= 1.5.5',
|
||||
version: '>= 1.8.2',
|
||||
method : 'pkg-config',
|
||||
)
|
||||
deps_private += blake3
|
||||
|
||||
boost = dependency(
|
||||
'boost',
|
||||
modules : ['context', 'coroutine'],
|
||||
modules : ['context', 'coroutine', 'iostreams'],
|
||||
include_type: 'system',
|
||||
)
|
||||
# boost is a public dependency, but not a pkg-config dependency unfortunately, so we
|
||||
|
|
@ -91,7 +92,7 @@ cpuid_required = get_option('cpuid')
|
|||
if host_machine.cpu_family() != 'x86_64' and cpuid_required.enabled()
|
||||
warning('Force-enabling seccomp on non-x86_64 does not make sense')
|
||||
endif
|
||||
cpuid = dependency('libcpuid', 'cpuid', required : cpuid_required)
|
||||
cpuid = dependency('libcpuid', 'cpuid', version : '>= 0.7.0', required : cpuid_required)
|
||||
configdata.set('HAVE_LIBCPUID', cpuid.found().to_int())
|
||||
deps_private += cpuid
|
||||
|
||||
|
|
@ -142,6 +143,7 @@ sources = [config_priv_h] + files(
|
|||
'signature/signer.cc',
|
||||
'source-accessor.cc',
|
||||
'source-path.cc',
|
||||
'subdir-source-accessor.cc',
|
||||
'strings.cc',
|
||||
'suggestions.cc',
|
||||
'tarfile.cc',
|
||||
|
|
|
|||
|
|
@ -138,42 +138,38 @@ SourceAccessor::DirEntries PosixSourceAccessor::readDirectory(const CanonPath &
|
|||
{
|
||||
assertNoSymlinks(path);
|
||||
DirEntries res;
|
||||
try {
|
||||
for (auto & entry : std::filesystem::directory_iterator{makeAbsPath(path)}) {
|
||||
checkInterrupt();
|
||||
auto type = [&]() -> std::optional<Type> {
|
||||
std::filesystem::file_type nativeType;
|
||||
try {
|
||||
nativeType = entry.symlink_status().type();
|
||||
} catch (std::filesystem::filesystem_error & e) {
|
||||
// We cannot always stat the child. (Ideally there is no
|
||||
// stat because the native directory entry has the type
|
||||
// already, but this isn't always the case.)
|
||||
if (e.code() == std::errc::permission_denied || e.code() == std::errc::operation_not_permitted)
|
||||
return std::nullopt;
|
||||
else throw;
|
||||
}
|
||||
for (auto & entry : DirectoryIterator{makeAbsPath(path)}) {
|
||||
checkInterrupt();
|
||||
auto type = [&]() -> std::optional<Type> {
|
||||
std::filesystem::file_type nativeType;
|
||||
try {
|
||||
nativeType = entry.symlink_status().type();
|
||||
} catch (std::filesystem::filesystem_error & e) {
|
||||
// We cannot always stat the child. (Ideally there is no
|
||||
// stat because the native directory entry has the type
|
||||
// already, but this isn't always the case.)
|
||||
if (e.code() == std::errc::permission_denied || e.code() == std::errc::operation_not_permitted)
|
||||
return std::nullopt;
|
||||
else throw;
|
||||
}
|
||||
|
||||
// cannot exhaustively enumerate because implementation-specific
|
||||
// additional file types are allowed.
|
||||
// cannot exhaustively enumerate because implementation-specific
|
||||
// additional file types are allowed.
|
||||
#pragma GCC diagnostic push
|
||||
#pragma GCC diagnostic ignored "-Wswitch-enum"
|
||||
switch (nativeType) {
|
||||
case std::filesystem::file_type::regular: return Type::tRegular; break;
|
||||
case std::filesystem::file_type::symlink: return Type::tSymlink; break;
|
||||
case std::filesystem::file_type::directory: return Type::tDirectory; break;
|
||||
case std::filesystem::file_type::character: return Type::tChar; break;
|
||||
case std::filesystem::file_type::block: return Type::tBlock; break;
|
||||
case std::filesystem::file_type::fifo: return Type::tFifo; break;
|
||||
case std::filesystem::file_type::socket: return Type::tSocket; break;
|
||||
default: return tUnknown;
|
||||
}
|
||||
#pragma GCC diagnostic pop
|
||||
}();
|
||||
res.emplace(entry.path().filename().string(), type);
|
||||
switch (nativeType) {
|
||||
case std::filesystem::file_type::regular: return Type::tRegular; break;
|
||||
case std::filesystem::file_type::symlink: return Type::tSymlink; break;
|
||||
case std::filesystem::file_type::directory: return Type::tDirectory; break;
|
||||
case std::filesystem::file_type::character: return Type::tChar; break;
|
||||
case std::filesystem::file_type::block: return Type::tBlock; break;
|
||||
case std::filesystem::file_type::fifo: return Type::tFifo; break;
|
||||
case std::filesystem::file_type::socket: return Type::tSocket; break;
|
||||
default: return tUnknown;
|
||||
}
|
||||
} catch (std::filesystem::filesystem_error & e) {
|
||||
throw SysError("reading directory %1%", showPath(path));
|
||||
#pragma GCC diagnostic pop
|
||||
}();
|
||||
res.emplace(entry.path().filename().string(), type);
|
||||
}
|
||||
return res;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -114,9 +114,11 @@ CanonPath SourceAccessor::resolveSymlinks(
|
|||
if (!linksAllowed--)
|
||||
throw Error("infinite symlink recursion in path '%s'", showPath(path));
|
||||
auto target = readLink(res);
|
||||
res.pop();
|
||||
if (isAbsolute(target))
|
||||
if (isAbsolute(target)) {
|
||||
res = CanonPath::root;
|
||||
} else {
|
||||
res.pop();
|
||||
}
|
||||
todo.splice(todo.begin(), tokenizeString<std::list<std::string>>(target, "/"));
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -26,18 +26,18 @@ __attribute__((no_sanitize("undefined"))) std::string_view toView(const std::ost
|
|||
}
|
||||
|
||||
template std::list<std::string> tokenizeString(std::string_view s, std::string_view separators);
|
||||
template std::set<std::string> tokenizeString(std::string_view s, std::string_view separators);
|
||||
template StringSet tokenizeString(std::string_view s, std::string_view separators);
|
||||
template std::vector<std::string> tokenizeString(std::string_view s, std::string_view separators);
|
||||
|
||||
template std::list<std::string> splitString(std::string_view s, std::string_view separators);
|
||||
template std::set<std::string> splitString(std::string_view s, std::string_view separators);
|
||||
template StringSet splitString(std::string_view s, std::string_view separators);
|
||||
template std::vector<std::string> splitString(std::string_view s, std::string_view separators);
|
||||
|
||||
template std::list<OsString>
|
||||
basicSplitString(std::basic_string_view<OsChar> s, std::basic_string_view<OsChar> separators);
|
||||
|
||||
template std::string concatStringsSep(std::string_view, const std::list<std::string> &);
|
||||
template std::string concatStringsSep(std::string_view, const std::set<std::string> &);
|
||||
template std::string concatStringsSep(std::string_view, const StringSet &);
|
||||
template std::string concatStringsSep(std::string_view, const std::vector<std::string> &);
|
||||
template std::string concatStringsSep(std::string_view, const boost::container::small_vector<std::string, 64> &);
|
||||
|
||||
|
|
@ -49,7 +49,7 @@ typedef std::string_view strings_4[4];
|
|||
template std::string concatStringsSep(std::string_view, const strings_4 &);
|
||||
|
||||
template std::string dropEmptyInitThenConcatStringsSep(std::string_view, const std::list<std::string> &);
|
||||
template std::string dropEmptyInitThenConcatStringsSep(std::string_view, const std::set<std::string> &);
|
||||
template std::string dropEmptyInitThenConcatStringsSep(std::string_view, const StringSet &);
|
||||
template std::string dropEmptyInitThenConcatStringsSep(std::string_view, const std::vector<std::string> &);
|
||||
|
||||
/**
|
||||
|
|
|
|||
59
src/libutil/subdir-source-accessor.cc
Normal file
59
src/libutil/subdir-source-accessor.cc
Normal file
|
|
@ -0,0 +1,59 @@
|
|||
#include "nix/util/source-accessor.hh"
|
||||
|
||||
namespace nix {
|
||||
|
||||
struct SubdirSourceAccessor : SourceAccessor
|
||||
{
|
||||
ref<SourceAccessor> parent;
|
||||
|
||||
CanonPath subdirectory;
|
||||
|
||||
SubdirSourceAccessor(ref<SourceAccessor> && parent, CanonPath && subdirectory)
|
||||
: parent(std::move(parent))
|
||||
, subdirectory(std::move(subdirectory))
|
||||
{
|
||||
displayPrefix.clear();
|
||||
}
|
||||
|
||||
std::string readFile(const CanonPath & path) override
|
||||
{
|
||||
return parent->readFile(subdirectory / path);
|
||||
}
|
||||
|
||||
void readFile(const CanonPath & path, Sink & sink, std::function<void(uint64_t)> sizeCallback) override
|
||||
{
|
||||
return parent->readFile(subdirectory / path, sink, sizeCallback);
|
||||
}
|
||||
|
||||
bool pathExists(const CanonPath & path) override
|
||||
{
|
||||
return parent->pathExists(subdirectory / path);
|
||||
}
|
||||
|
||||
std::optional<Stat> maybeLstat(const CanonPath & path) override
|
||||
{
|
||||
return parent->maybeLstat(subdirectory / path);
|
||||
}
|
||||
|
||||
DirEntries readDirectory(const CanonPath & path) override
|
||||
{
|
||||
return parent->readDirectory(subdirectory / path);
|
||||
}
|
||||
|
||||
std::string readLink(const CanonPath & path) override
|
||||
{
|
||||
return parent->readLink(subdirectory / path);
|
||||
}
|
||||
|
||||
std::string showPath(const CanonPath & path) override
|
||||
{
|
||||
return displayPrefix + parent->showPath(subdirectory / path) + displaySuffix;
|
||||
}
|
||||
};
|
||||
|
||||
ref<SourceAccessor> projectSubdirSourceAccessor(ref<SourceAccessor> parent, CanonPath subdirectory)
|
||||
{
|
||||
return make_ref<SubdirSourceAccessor>(std::move(parent), std::move(subdirectory));
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -38,7 +38,7 @@ int levenshteinDistance(std::string_view first, std::string_view second)
|
|||
}
|
||||
|
||||
Suggestions Suggestions::bestMatches (
|
||||
const std::set<std::string> & allMatches,
|
||||
const StringSet & allMatches,
|
||||
std::string_view query)
|
||||
{
|
||||
std::set<Suggestion> res;
|
||||
|
|
|
|||
|
|
@ -8,10 +8,6 @@
|
|||
|
||||
namespace nix {
|
||||
|
||||
namespace fs {
|
||||
using namespace std::filesystem;
|
||||
}
|
||||
|
||||
namespace {
|
||||
|
||||
int callback_open(struct archive *, void * self)
|
||||
|
|
@ -127,7 +123,7 @@ TarArchive::~TarArchive()
|
|||
archive_read_free(this->archive);
|
||||
}
|
||||
|
||||
static void extract_archive(TarArchive & archive, const fs::path & destDir)
|
||||
static void extract_archive(TarArchive & archive, const std::filesystem::path & destDir)
|
||||
{
|
||||
int flags = ARCHIVE_EXTRACT_TIME | ARCHIVE_EXTRACT_SECURE_SYMLINKS | ARCHIVE_EXTRACT_SECURE_NODOTDOT;
|
||||
|
||||
|
|
@ -162,7 +158,7 @@ static void extract_archive(TarArchive & archive, const fs::path & destDir)
|
|||
archive.close();
|
||||
}
|
||||
|
||||
void unpackTarfile(Source & source, const fs::path & destDir)
|
||||
void unpackTarfile(Source & source, const std::filesystem::path & destDir)
|
||||
{
|
||||
auto archive = TarArchive(source);
|
||||
|
||||
|
|
@ -170,7 +166,7 @@ void unpackTarfile(Source & source, const fs::path & destDir)
|
|||
extract_archive(archive, destDir);
|
||||
}
|
||||
|
||||
void unpackTarfile(const fs::path & tarFile, const fs::path & destDir)
|
||||
void unpackTarfile(const std::filesystem::path & tarFile, const std::filesystem::path & destDir)
|
||||
{
|
||||
auto archive = TarArchive(tarFile);
|
||||
|
||||
|
|
@ -182,6 +178,10 @@ time_t unpackTarfileToSink(TarArchive & archive, ExtendedFileSystemObjectSink &
|
|||
{
|
||||
time_t lastModified = 0;
|
||||
|
||||
/* Only allocate the buffer once. Use the heap because 131 KiB is a bit too
|
||||
much for the stack. */
|
||||
std::vector<unsigned char> buf(128 * 1024);
|
||||
|
||||
for (;;) {
|
||||
// FIXME: merge with extract_archive
|
||||
struct archive_entry * entry;
|
||||
|
|
@ -216,7 +216,6 @@ time_t unpackTarfileToSink(TarArchive & archive, ExtendedFileSystemObjectSink &
|
|||
crf.isExecutable();
|
||||
|
||||
while (true) {
|
||||
std::vector<unsigned char> buf(128 * 1024);
|
||||
auto n = archive_read_data(archive.archive, buf.data(), buf.size());
|
||||
if (n < 0)
|
||||
checkLibArchive(archive.archive, n, "cannot read file from tarball: %s");
|
||||
|
|
|
|||
|
|
@ -95,10 +95,19 @@ std::string filterANSIEscapes(std::string_view s, bool filterAll, unsigned int w
|
|||
} else if (i != s.end() && *i == ']') {
|
||||
// OSC
|
||||
e += *i++;
|
||||
// eat ESC
|
||||
while (i != s.end() && *i != '\e') e += *i++;
|
||||
// eat backslash
|
||||
if (i != s.end() && *i == '\\') e += last = *i++;
|
||||
// https://gist.github.com/egmontkob/eb114294efbcd5adb1944c9f3cb5feda defines
|
||||
// two forms of a URI separator:
|
||||
// 1. ESC '\' (standard)
|
||||
// 2. BEL ('\a') (xterm-style, used by gcc)
|
||||
|
||||
// eat ESC or BEL
|
||||
while (i != s.end() && *i != '\e' && *i != '\a') e += *i++;
|
||||
if (i != s.end()) {
|
||||
char v = *i;
|
||||
e += *i++;
|
||||
// eat backslash after ESC
|
||||
if (i != s.end() && v == '\e' && *i == '\\') e += last = *i++;
|
||||
}
|
||||
} else {
|
||||
if (i != s.end() && *i >= 0x40 && *i <= 0x5f) e += *i++;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -191,7 +191,7 @@ void unix::closeExtraFDs()
|
|||
|
||||
#ifdef __linux__
|
||||
try {
|
||||
for (auto & s : std::filesystem::directory_iterator{"/proc/self/fd"}) {
|
||||
for (auto & s : DirectoryIterator{"/proc/self/fd"}) {
|
||||
checkInterrupt();
|
||||
auto fd = std::stoi(s.path().filename());
|
||||
if (fd > MAX_KEPT_FD) {
|
||||
|
|
@ -201,7 +201,6 @@ void unix::closeExtraFDs()
|
|||
}
|
||||
return;
|
||||
} catch (SysError &) {
|
||||
} catch (std::filesystem::filesystem_error &) {
|
||||
}
|
||||
#endif
|
||||
|
||||
|
|
|
|||
|
|
@ -23,7 +23,8 @@ Descriptor openDirectory(const std::filesystem::path & path)
|
|||
return open(path.c_str(), O_RDONLY | O_DIRECTORY);
|
||||
}
|
||||
|
||||
void setWriteTime(const fs::path & path, time_t accessedTime, time_t modificationTime, std::optional<bool> optIsSymlink)
|
||||
void setWriteTime(
|
||||
const std::filesystem::path & path, time_t accessedTime, time_t modificationTime, std::optional<bool> optIsSymlink)
|
||||
{
|
||||
// Would be nice to use std::filesystem unconditionally, but
|
||||
// doesn't support access time just modification time.
|
||||
|
|
@ -57,7 +58,7 @@ void setWriteTime(const fs::path & path, time_t accessedTime, time_t modificatio
|
|||
if (lutimes(path.c_str(), times) == -1)
|
||||
throw SysError("changing modification time of %s", path);
|
||||
# else
|
||||
bool isSymlink = optIsSymlink ? *optIsSymlink : fs::is_symlink(path);
|
||||
bool isSymlink = optIsSymlink ? *optIsSymlink : std::filesystem::is_symlink(path);
|
||||
|
||||
if (!isSymlink) {
|
||||
if (utimes(path.c_str(), times) == -1)
|
||||
|
|
|
|||
|
|
@ -9,8 +9,6 @@
|
|||
|
||||
namespace nix {
|
||||
|
||||
namespace fs { using namespace std::filesystem; }
|
||||
|
||||
std::string getUserName()
|
||||
{
|
||||
auto pw = getpwuid(geteuid());
|
||||
|
|
|
|||
|
|
@ -171,7 +171,7 @@ std::string toLower(std::string s)
|
|||
}
|
||||
|
||||
|
||||
std::string shellEscape(const std::string_view s)
|
||||
std::string escapeShellArgAlways(const std::string_view s)
|
||||
{
|
||||
std::string r;
|
||||
r.reserve(s.size() + 2);
|
||||
|
|
|
|||
|
|
@ -13,8 +13,10 @@ std::optional<OsString> getEnvOs(const OsString & key)
|
|||
return std::nullopt;
|
||||
}
|
||||
|
||||
// Allocate a buffer to hold the environment variable value
|
||||
std::wstring value{bufferSize, L'\0'};
|
||||
/* Allocate a buffer to hold the environment variable value.
|
||||
WARNING: Do not even think about using uniform initialization here,
|
||||
we DONT want to call the initializer list ctor accidentally. */
|
||||
std::wstring value(bufferSize, L'\0');
|
||||
|
||||
// Retrieve the environment variable value
|
||||
DWORD resultSize = GetEnvironmentVariableW(key.c_str(), &value[0], bufferSize);
|
||||
|
|
|
|||
|
|
@ -3,13 +3,10 @@
|
|||
#ifdef _WIN32
|
||||
namespace nix {
|
||||
|
||||
namespace fs {
|
||||
using namespace std::filesystem;
|
||||
}
|
||||
|
||||
void setWriteTime(const fs::path & path, time_t accessedTime, time_t modificationTime, std::optional<bool> optIsSymlink)
|
||||
void setWriteTime(
|
||||
const std::filesystem::path & path, time_t accessedTime, time_t modificationTime, std::optional<bool> optIsSymlink)
|
||||
{
|
||||
// FIXME use `fs::last_write_time`.
|
||||
// FIXME use `std::filesystem::last_write_time`.
|
||||
//
|
||||
// Would be nice to use std::filesystem unconditionally, but
|
||||
// doesn't support access time just modification time.
|
||||
|
|
|
|||
|
|
@ -25,6 +25,7 @@ inline void setInterruptThrown()
|
|||
static inline bool isInterrupted()
|
||||
{
|
||||
/* Do nothing for now */
|
||||
return false;
|
||||
}
|
||||
|
||||
inline void checkInterrupt()
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue