1
1
Fork 0
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:
Eelco Dolstra 2025-05-16 12:48:44 +02:00
commit c20642ac7b
354 changed files with 6768 additions and 3808 deletions

View file

@ -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);

View file

@ -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;
}

View file

@ -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;

View file

@ -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);

View file

@ -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) {

View file

@ -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."

View file

@ -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)

View file

@ -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("cant 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

View file

@ -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());

View file

@ -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

View file

@ -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];
}

View file

@ -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;

View file

@ -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;

View file

@ -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)

View file

@ -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_;
};
}

View file

@ -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
{

View file

@ -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 {};

View file

@ -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();

View file

@ -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);
}

View file

@ -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});
}
};
}

View file

@ -35,7 +35,7 @@ public:
) const;
static Suggestions bestMatches (
const std::set<std::string> & allMatches,
const StringSet & allMatches,
std::string_view query
);

View file

@ -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. */

View file

@ -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;

View file

@ -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);
/**

View file

@ -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)

View file

@ -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;
}

View file

@ -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',

View file

@ -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;
}

View file

@ -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, "/"));
}
}

View file

@ -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> &);
/**

View 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));
}
}

View file

@ -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;

View file

@ -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");

View file

@ -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++;
}

View file

@ -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

View file

@ -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)

View file

@ -9,8 +9,6 @@
namespace nix {
namespace fs { using namespace std::filesystem; }
std::string getUserName()
{
auto pw = getpwuid(geteuid());

View file

@ -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);

View file

@ -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);

View file

@ -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.

View file

@ -25,6 +25,7 @@ inline void setInterruptThrown()
static inline bool isInterrupted()
{
/* Do nothing for now */
return false;
}
inline void checkInterrupt()