Compare commits

...

20 Commits

Author SHA1 Message Date
Eelco Dolstra
c8633ce4f8 Add nix nario command
This replaces `nix-store --export` and `nix-store --import`.
2025-09-12 16:09:01 +02:00
Jörg Thalheim
377b60ee9b Merge pull request #13926 from NaN-git/opt_boost-unordered
replace all occurences of std::unordered_* by equivalents from boost
2025-09-12 11:46:42 +02:00
Jörg Thalheim
429812cdb8 Merge pull request #13966 from juhp/patch-1
meson: add soversion to libraries (#13960)
2025-09-12 08:25:12 +02:00
Jörg Thalheim
f6802a8ccf Merge pull request #13963 from xokdvium/fix-no-gc
libexpr: Fix build without Boehm
2025-09-12 08:23:19 +02:00
Jens Petersen
bdbc739d6e meson: add soversion to libraries (#13960) 2025-09-12 14:06:39 +08:00
Sergei Zimmerman
c0b35c71cd libexpr: Fix build without Boehm
This should have been placed under the ifdef.
2025-09-12 04:02:07 +03:00
Sergei Zimmerman
ef5fedbc0d Merge pull request #13936 from xokdvium/empty-list-bindings
libexpr: Make constant Values global constants, move out of EvalState
2025-09-10 23:12:20 +00:00
Sergei Zimmerman
5db4b0699c libexpr: Make constant Values global constants, move out of EvalState
These constant Values have no business being in the EvalState in the
first place. The ultimate goal is to get rid of the ugly `getBuiltins`
and its relience (in `createBaseEnv`) on these global constants is getting in the way.

Same idea as in f017f9ddd3.

Co-authored-by: eldritch horrors <pennae@lix.systems>
2025-09-11 01:53:41 +03:00
Sergei Zimmerman
462b9ac49c libexpr: Make Value::isa and Value::getStorage private methods
This was always intended to be the case, but accidentally left
in the public interface.
2025-09-11 01:52:17 +03:00
Sergei Zimmerman
4df1a3ca76 libexpr: Make emptyBindings a global constant
This object is always constant and will never get modified.
Having it as a global (constant) static is much easier and
unclutters the EvalState.

Same idea as in f017f9ddd3.

Co-authored-by: eldritch horrors <pennae@lix.systems>
2025-09-11 01:51:48 +03:00
Philipp Otterbein
9dbc2cae4f hashmaps with string keys: add transparent lookups 2025-09-10 23:04:44 +02:00
Philipp Otterbein
9f2b6a1b94 replace more std::unordered_* types by faster boost hash maps 2025-09-10 23:04:44 +02:00
Philipp Otterbein
4f8c50fb77 libexpr: replace std::unordered_* types by faster boost hash maps 2025-09-10 23:04:44 +02:00
John Ericson
c0fd9146d6 Move exportPaths() / importPaths() out of the Store class (#13959) 2025-09-10 15:39:20 -04:00
Eelco Dolstra
3898a7343a Merge pull request #13961 from NyCodeGHG/push-prlsssvmxwvl
meson: link to libatomic on powerpc-linux
2025-09-10 19:22:54 +02:00
Marie Ramlow
37eec84bc1 meson: link to libatomic on powerpc-linux
Like 32-bit Arm, 32-bit PowerPC also needs linking against libatomic
because it doesn't support some atomic instructions in hardware.
2025-09-10 18:50:35 +02:00
Eelco Dolstra
fe5b669534 Move exportPaths() / importPaths() out of the Store class 2025-09-10 14:22:46 +02:00
Jörg Thalheim
5e46df973f Merge pull request #13957 from NixOS/drop-old-serve-protocol
Remove support for serve protocol version < 5
2025-09-10 13:50:12 +02:00
Eelco Dolstra
9df99e0658 Remove ServeProto::Command::ExportPaths
This seems to have been unused since the build-remote.pl removal in February 2017 (27dc76c1a5).
2025-09-10 10:57:15 +02:00
Eelco Dolstra
fa048e4383 Remove support for serve protocol < 5
This was introduced in August 2018 (2825e05d21).
2025-09-10 10:57:15 +02:00
66 changed files with 632 additions and 330 deletions

View File

@@ -3,6 +3,6 @@
# This is needed for std::atomic on some platforms
# We did not manage to test this reliably on all platforms, so we hardcode
# it for now.
if host_machine.cpu_family() == 'arm'
if host_machine.cpu_family() in [ 'arm', 'ppc' ]
deps_other += cxx.find_library('atomic')
endif

View File

@@ -95,6 +95,7 @@ this_library = library(
'nixcmd',
sources,
config_priv_h,
soversion : 0,
dependencies : deps_public + deps_private + deps_other,
include_directories : include_dirs,
link_args : linker_export_flags,

View File

@@ -50,6 +50,7 @@ subdir('nix-meson-build-support/windows-version')
this_library = library(
'nixexprc',
sources,
soversion : 0,
dependencies : deps_public + deps_private + deps_other,
include_directories : include_dirs,
link_args : linker_export_flags,

View File

@@ -16,7 +16,7 @@
#include "nix_api_util_internal.h"
#if NIX_USE_BOEHMGC
# include <mutex>
# include <boost/unordered/concurrent_flat_map.hpp>
#endif
/**
@@ -207,28 +207,20 @@ void nix_state_free(EvalState * state)
}
#if NIX_USE_BOEHMGC
std::unordered_map<
boost::concurrent_flat_map<
const void *,
unsigned int,
std::hash<const void *>,
std::equal_to<const void *>,
traceable_allocator<std::pair<const void * const, unsigned int>>>
nix_refcounts;
std::mutex nix_refcount_lock;
nix_refcounts{};
nix_err nix_gc_incref(nix_c_context * context, const void * p)
{
if (context)
context->last_err_code = NIX_OK;
try {
std::scoped_lock lock(nix_refcount_lock);
auto f = nix_refcounts.find(p);
if (f != nix_refcounts.end()) {
f->second++;
} else {
nix_refcounts[p] = 1;
}
nix_refcounts.insert_or_visit({p, 1}, [](auto & kv) { kv.second++; });
}
NIXC_CATCH_ERRS
}
@@ -239,12 +231,12 @@ nix_err nix_gc_decref(nix_c_context * context, const void * p)
if (context)
context->last_err_code = NIX_OK;
try {
std::scoped_lock lock(nix_refcount_lock);
auto f = nix_refcounts.find(p);
if (f != nix_refcounts.end()) {
if (--f->second == 0)
nix_refcounts.erase(f);
} else
bool fail = true;
nix_refcounts.erase_if(p, [&](auto & kv) {
fail = false;
return !--kv.second;
});
if (fail)
throw std::runtime_error("nix_gc_decref: object was not referenced");
}
NIXC_CATCH_ERRS

View File

@@ -44,6 +44,7 @@ subdir('nix-meson-build-support/windows-version')
this_library = library(
'nix-expr-test-support',
sources,
soversion : 0,
dependencies : deps_public + deps_private + deps_other,
include_directories : include_dirs,
# TODO: Remove `-lrapidcheck` when https://github.com/emil-e/rapidcheck/pull/326

View File

@@ -110,8 +110,8 @@ std::pair<SourcePath, uint32_t> findPackageFilename(EvalState & state, Value & v
{
Value * v2;
try {
auto dummyArgs = state.allocBindings(0);
v2 = findAlongAttrPath(state, "meta.position", *dummyArgs, v).first;
auto & dummyArgs = Bindings::emptyBindings;
v2 = findAlongAttrPath(state, "meta.position", dummyArgs, v).first;
} catch (Error &) {
throw NoPositionInfo("package '%s' has no source location information", what);
}

View File

@@ -5,13 +5,15 @@
namespace nix {
Bindings Bindings::emptyBindings;
/* Allocate a new array of attributes for an attribute set with a specific
capacity. The space is implicitly reserved after the Bindings
structure. */
Bindings * EvalState::allocBindings(size_t capacity)
{
if (capacity == 0)
return &emptyBindings;
return &Bindings::emptyBindings;
if (capacity > std::numeric_limits<Bindings::size_t>::max())
throw Error("attribute set of size %d is too big", capacity);
nrAttrsets++;

View File

@@ -24,6 +24,10 @@
#endif
namespace nix {
#if NIX_USE_BOEHMGC
/*
* Ensure that Boehm satisfies our alignment requirements. This is the default configuration [^]
* and this assertion should never break for any platform. Let's assert it just in case.
@@ -35,9 +39,6 @@
*/
static_assert(sizeof(void *) * 2 == GC_GRANULE_BYTES, "Boehm GC must use GC_GRANULE_WORDS = 2");
namespace nix {
#if NIX_USE_BOEHMGC
/* Called when the Boehm GC runs out of memory. */
static void * oomHandler(size_t requested)
{

View File

@@ -202,7 +202,6 @@ EvalState::EvalState(
, settings{settings}
, symbols(StaticEvalSymbols::staticSymbolTable())
, repair(NoRepair)
, emptyBindings(Bindings())
, storeFS(makeMountedSourceAccessor({
{CanonPath::root, makeEmptySourceAccessor()},
/* In the pure eval case, we can simply require
@@ -285,10 +284,6 @@ EvalState::EvalState(
static_assert(sizeof(Env) <= 16, "environment must be <= 16 bytes");
vEmptyList.mkList(buildList(0));
vNull.mkNull();
vTrue.mkBool(true);
vFalse.mkBool(false);
vStringRegular.mkStringNoCopy("regular");
vStringDirectory.mkStringNoCopy("directory");
vStringSymlink.mkStringNoCopy("symlink");
@@ -895,7 +890,7 @@ ListBuilder::ListBuilder(EvalState & state, size_t size)
Value * EvalState::getBool(bool b)
{
return b ? &vTrue : &vFalse;
return b ? &Value::vTrue : &Value::vFalse;
}
unsigned long nrThunks = 0;
@@ -1090,7 +1085,9 @@ void EvalState::evalFile(const SourcePath & path, Value & v, bool mustBeTrivial)
void EvalState::resetFileCache()
{
fileEvalCache.clear();
fileEvalCache.rehash(0);
fileParseCache.clear();
fileParseCache.rehash(0);
inputCache->clear();
}
@@ -1301,7 +1298,7 @@ void ExprList::eval(EvalState & state, Env & env, Value & v)
Value * ExprList::maybeThunk(EvalState & state, Env & env)
{
if (elems.empty()) {
return &state.vEmptyList;
return &Value::vEmptyList;
}
return Expr::maybeThunk(state, env);
}
@@ -2375,10 +2372,9 @@ StorePath EvalState::copyPathToStore(NixStringContext & context, const SourcePat
if (nix::isDerivation(path.path.abs()))
error<EvalError>("file names are not allowed to end in '%1%'", drvExtension).debugThrow();
auto dstPathCached = get(*srcToStore.lock(), path);
auto dstPath = dstPathCached ? *dstPathCached : [&]() {
auto dstPath = fetchToStore(
std::optional<StorePath> dstPath;
if (!srcToStore.cvisit(path, [&dstPath](const auto & kv) { dstPath.emplace(kv.second); })) {
dstPath.emplace(fetchToStore(
fetchSettings,
*store,
path.resolveSymlinks(SymlinkResolution::Ancestors),
@@ -2386,15 +2382,14 @@ StorePath EvalState::copyPathToStore(NixStringContext & context, const SourcePat
path.baseName(),
ContentAddressMethod::Raw::NixArchive,
nullptr,
repair);
allowPath(dstPath);
srcToStore.lock()->try_emplace(path, dstPath);
printMsg(lvlChatty, "copied source '%1%' -> '%2%'", path, store->printStorePath(dstPath));
return dstPath;
}();
repair));
allowPath(*dstPath);
srcToStore.try_emplace(path, *dstPath);
printMsg(lvlChatty, "copied source '%1%' -> '%2%'", path, store->printStorePath(*dstPath));
}
context.insert(NixStringContextElem::Opaque{.path = dstPath});
return dstPath;
context.insert(NixStringContextElem::Opaque{.path = *dstPath});
return *dstPath;
}
SourcePath EvalState::coerceToPath(const PosIdx pos, Value & v, NixStringContext & context, std::string_view errorCtx)

View File

@@ -54,6 +54,12 @@ public:
typedef uint32_t size_t;
PosIdx pos;
/**
* An instance of bindings objects with 0 attributes.
* This object must never be modified.
*/
static Bindings emptyBindings;
private:
size_t size_ = 0;
Attr attrs[0];

View File

@@ -20,6 +20,8 @@
// For `NIX_USE_BOEHMGC`, and if that's set, `GC_THREADS`
#include "nix/expr/config.hh"
#include <boost/unordered/concurrent_flat_map.hpp>
#include <boost/unordered/unordered_flat_map.hpp>
#include <map>
#include <optional>
#include <functional>
@@ -162,7 +164,7 @@ typedef std::
map<std::string, Value *, std::less<std::string>, traceable_allocator<std::pair<const std::string, Value *>>>
ValMap;
typedef std::unordered_map<PosIdx, DocComment> DocCommentMap;
typedef boost::unordered_flat_map<PosIdx, DocComment, std::hash<PosIdx>> DocCommentMap;
struct Env
{
@@ -313,34 +315,6 @@ public:
*/
RepairFlag repair;
Bindings emptyBindings;
/**
* Empty list constant.
*/
Value vEmptyList;
/**
* `null` constant.
*
* This is _not_ a singleton. Pointer equality is _not_ sufficient.
*/
Value vNull;
/**
* `true` constant.
*
* This is _not_ a singleton. Pointer equality is _not_ sufficient.
*/
Value vTrue;
/**
* `true` constant.
*
* This is _not_ a singleton. Pointer equality is _not_ sufficient.
*/
Value vFalse;
/** `"regular"` */
Value vStringRegular;
/** `"directory"` */
@@ -395,7 +369,7 @@ public:
bool inDebugger = false;
int trylevel;
std::list<DebugTrace> debugTraces;
std::map<const Expr *, const std::shared_ptr<const StaticEnv>> exprEnvs;
boost::unordered_flat_map<const Expr *, const std::shared_ptr<const StaticEnv>> exprEnvs;
const std::shared_ptr<const StaticEnv> getStaticEnv(const Expr & expr) const
{
@@ -438,12 +412,12 @@ private:
/* Cache for calls to addToStore(); maps source paths to the store
paths. */
Sync<std::unordered_map<SourcePath, StorePath>> srcToStore;
boost::concurrent_flat_map<SourcePath, StorePath, std::hash<SourcePath>> srcToStore;
/**
* A cache from path names to parse trees.
*/
typedef std::unordered_map<
typedef boost::unordered_flat_map<
SourcePath,
Expr *,
std::hash<SourcePath>,
@@ -455,7 +429,7 @@ private:
/**
* A cache from path names to values.
*/
typedef std::unordered_map<
typedef boost::unordered_flat_map<
SourcePath,
Value,
std::hash<SourcePath>,
@@ -468,11 +442,12 @@ private:
* Associate source positions of certain AST nodes with their preceding doc comment, if they have one.
* Grouped by file.
*/
std::unordered_map<SourcePath, DocCommentMap> positionToDocComment;
boost::unordered_flat_map<SourcePath, DocCommentMap, std::hash<SourcePath>> positionToDocComment;
LookupPath lookupPath;
std::map<std::string, std::optional<SourcePath>> lookupPathResolved;
boost::unordered_flat_map<std::string, std::optional<SourcePath>, StringViewHash, std::equal_to<>>
lookupPathResolved;
/**
* Cache used by prim_match().
@@ -746,11 +721,11 @@ public:
/**
* Internal primops not exposed to the user.
*/
std::unordered_map<
boost::unordered_flat_map<
std::string,
Value *,
std::hash<std::string>,
std::equal_to<std::string>,
StringViewHash,
std::equal_to<>,
traceable_allocator<std::pair<const std::string, Value *>>>
internalPrimOps;
@@ -1017,10 +992,10 @@ private:
bool countCalls;
typedef std::map<std::string, size_t> PrimOpCalls;
typedef boost::unordered_flat_map<std::string, size_t, StringViewHash, std::equal_to<>> PrimOpCalls;
PrimOpCalls primOpCalls;
typedef std::map<ExprLambda *, size_t> FunctionCalls;
typedef boost::unordered_flat_map<ExprLambda *, size_t> FunctionCalls;
FunctionCalls functionCalls;
/** Evaluation/call profiler. */
@@ -1028,7 +1003,7 @@ private:
void incrFunctionCall(ExprLambda * fun);
typedef std::map<PosIdx, size_t> AttrSelects;
typedef boost::unordered_flat_map<PosIdx, size_t, std::hash<PosIdx>> AttrSelects;
AttrSelects attrSelects;
friend struct ExprOpUpdate;

View File

@@ -71,7 +71,7 @@ struct LexerState
/**
* @brief Maps some positions to a DocComment, where the comment is relevant to the location.
*/
std::unordered_map<PosIdx, DocComment> & positionToDocComment;
DocCommentMap & positionToDocComment;
PosTable & positions;
PosTable::Origin origin;

View File

@@ -12,6 +12,7 @@
#include "nix/expr/print-options.hh"
#include "nix/util/checked-arithmetic.hh"
#include <boost/unordered/unordered_flat_map_fwd.hpp>
#include <nlohmann/json_fwd.hpp>
namespace nix {
@@ -833,6 +834,35 @@ struct Value : public ValueStorage<sizeof(void *)>
{
friend std::string showType(const Value & v);
/**
* Empty list constant.
*
* This is _not_ a singleton. Pointer equality is _not_ sufficient.
*/
static Value vEmptyList;
/**
* `null` constant.
*
* This is _not_ a singleton. Pointer equality is _not_ sufficient.
*/
static Value vNull;
/**
* `true` constant.
*
* This is _not_ a singleton. Pointer equality is _not_ sufficient.
*/
static Value vTrue;
/**
* `true` constant.
*
* This is _not_ a singleton. Pointer equality is _not_ sufficient.
*/
static Value vFalse;
private:
template<InternalType... discriminator>
bool isa() const noexcept
{
@@ -1166,7 +1196,7 @@ void Value::mkBlackhole()
}
typedef std::vector<Value *, traceable_allocator<Value *>> ValueVector;
typedef std::unordered_map<
typedef boost::unordered_flat_map<
Symbol,
Value *,
std::hash<Symbol>,

View File

@@ -163,6 +163,7 @@ sources = files(
'search-path.cc',
'value-to-json.cc',
'value-to-xml.cc',
'value.cc',
'value/context.cc',
)
@@ -180,6 +181,7 @@ this_library = library(
parser_tab,
lexer_tab,
generated_headers,
soversion : 0,
dependencies : deps_public + deps_private + deps_other,
include_directories : include_dirs,
link_args : linker_export_flags,

View File

@@ -57,7 +57,7 @@
namespace nix {
typedef std::unordered_map<PosIdx, DocComment> DocCommentMap;
typedef boost::unordered_flat_map<PosIdx, DocComment, std::hash<PosIdx>> DocCommentMap;
Expr * parseExprFromBuf(
char * text,

View File

@@ -18,6 +18,8 @@
#include "nix/util/sort.hh"
#include <boost/container/small_vector.hpp>
#include <boost/unordered/concurrent_flat_map.hpp>
#include <boost/unordered/unordered_flat_map.hpp>
#include <nlohmann/json.hpp>
#include <sys/types.h>
@@ -1075,11 +1077,11 @@ static void prim_tryEval(EvalState & state, const PosIdx pos, Value ** args, Val
try {
state.forceValue(*args[0], pos);
attrs.insert(state.s.value, args[0]);
attrs.insert(state.symbols.create("success"), &state.vTrue);
attrs.insert(state.symbols.create("success"), &Value::vTrue);
} catch (AssertionError & e) {
// `value = false;` is unfortunate but removing it is a breaking change.
attrs.insert(state.s.value, &state.vFalse);
attrs.insert(state.symbols.create("success"), &state.vFalse);
attrs.insert(state.s.value, &Value::vFalse);
attrs.insert(state.symbols.create("success"), &Value::vFalse);
}
// restore the debugRepl pointer if we saved it earlier.
@@ -1750,7 +1752,7 @@ static void derivationStrictInternal(EvalState & state, std::string_view drvName
read them later. */
{
auto h = hashDerivationModulo(*state.store, drv, false);
drvHashes.lock()->insert_or_assign(drvPath, h);
drvHashes.insert_or_assign(drvPath, std::move(h));
}
auto result = state.buildBindings(1 + drv.outputs.size());
@@ -3326,14 +3328,14 @@ static void prim_functionArgs(EvalState & state, const PosIdx pos, Value ** args
{
state.forceValue(*args[0], pos);
if (args[0]->isPrimOpApp() || args[0]->isPrimOp()) {
v.mkAttrs(&state.emptyBindings);
v.mkAttrs(&Bindings::emptyBindings);
return;
}
if (!args[0]->isLambda())
state.error<TypeError>("'functionArgs' requires a function").atPos(pos).debugThrow();
if (!args[0]->lambda().fun->hasFormals()) {
v.mkAttrs(&state.emptyBindings);
v.mkAttrs(&Bindings::emptyBindings);
return;
}
@@ -4027,7 +4029,7 @@ static void prim_groupBy(EvalState & state, const PosIdx pos, Value ** args, Val
auto name = state.forceStringNoCtx(
res, pos, "while evaluating the return value of the grouping function passed to builtins.groupBy");
auto sym = state.symbols.create(name);
auto vector = attrs.try_emplace(sym, ValueVector()).first;
auto vector = attrs.try_emplace<ValueVector>(sym, {}).first;
vector->second.push_back(vElem);
}
@@ -4562,27 +4564,21 @@ static RegisterPrimOp primop_convertHash({
struct RegexCache
{
struct State
{
std::unordered_map<std::string, std::regex, StringViewHash, std::equal_to<>> cache;
};
Sync<State> state_;
boost::concurrent_flat_map<std::string, std::regex, StringViewHash, std::equal_to<>> cache;
std::regex get(std::string_view re)
{
auto state(state_.lock());
auto it = state->cache.find(re);
if (it != state->cache.end())
return it->second;
std::regex regex;
/* No std::regex constructor overload from std::string_view, but can be constructed
from a pointer + size or an iterator range. */
return state->cache
.emplace(
std::piecewise_construct,
std::forward_as_tuple(re),
std::forward_as_tuple(/*s=*/re.data(), /*count=*/re.size(), std::regex::extended))
.first->second;
cache.try_emplace_and_cvisit(
re,
/*s=*/re.data(),
/*count=*/re.size(),
std::regex::extended,
[&regex](const auto & kv) { regex = kv.second; },
[&regex](const auto & kv) { regex = kv.second; });
return regex;
}
};
@@ -4613,7 +4609,7 @@ void prim_match(EvalState & state, const PosIdx pos, Value ** args, Value & v)
auto list = state.buildList(match.size() - 1);
for (const auto & [i, v2] : enumerate(list))
if (!match[i + 1].matched)
v2 = &state.vNull;
v2 = &Value::vNull;
else
v2 = mkString(state, match[i + 1]);
v.mkList(list);
@@ -4705,7 +4701,7 @@ void prim_split(EvalState & state, const PosIdx pos, Value ** args, Value & v)
auto list2 = state.buildList(slen);
for (const auto & [si, v2] : enumerate(list2)) {
if (!match[si + 1].matched)
v2 = &state.vNull;
v2 = &Value::vNull;
else
v2 = mkString(state, match[si + 1]);
}
@@ -4826,7 +4822,7 @@ static void prim_replaceStrings(EvalState & state, const PosIdx pos, Value ** ar
from.emplace_back(state.forceString(
*elem, pos, "while evaluating one of the strings to replace passed to builtins.replaceStrings"));
std::unordered_map<size_t, std::string_view> cache;
boost::unordered_flat_map<size_t, std::string_view> cache;
auto to = args[1]->listView();
NixStringContext context;
@@ -5059,7 +5055,7 @@ void EvalState::createBaseEnv(const EvalSettings & evalSettings)
addConstant(
"null",
&vNull,
&Value::vNull,
{
.type = nNull,
.doc = R"(

View File

@@ -1,5 +1,4 @@
#include <limits>
#include <unordered_set>
#include <sstream>
#include "nix/expr/print.hh"
@@ -10,6 +9,8 @@
#include "nix/util/english.hh"
#include "nix/expr/eval.hh"
#include <boost/unordered/unordered_flat_set.hpp>
namespace nix {
void printElided(
@@ -81,7 +82,7 @@ std::ostream & printLiteralBool(std::ostream & str, bool boolean)
// For example `or' doesn't need to be quoted.
bool isReservedKeyword(const std::string_view str)
{
static const std::unordered_set<std::string_view> reservedKeywords = {
static const boost::unordered_flat_set<std::string_view> reservedKeywords = {
"if", "then", "else", "assert", "with", "let", "in", "rec", "inherit"};
return reservedKeywords.contains(str);
}

29
src/libexpr/value.cc Normal file
View File

@@ -0,0 +1,29 @@
#include "nix/expr/value.hh"
namespace nix {
Value Value::vEmptyList = []() {
Value res;
res.setStorage(List{.size = 0, .elems = nullptr});
return res;
}();
Value Value::vNull = []() {
Value res;
res.mkNull();
return res;
}();
Value Value::vTrue = []() {
Value res;
res.mkBool(true);
return res;
}();
Value Value::vFalse = []() {
Value res;
res.mkBool(false);
return res;
}();
} // namespace nix

View File

@@ -53,6 +53,7 @@ subdir('nix-meson-build-support/windows-version')
this_library = library(
'nixfetchersc',
sources,
soversion : 0,
dependencies : deps_public + deps_private + deps_other,
include_directories : include_dirs,
link_args : linker_export_flags,

View File

@@ -1,5 +1,7 @@
#include "nix/fetchers/filtering-source-accessor.hh"
#include <boost/unordered/unordered_flat_set.hpp>
namespace nix {
std::optional<std::filesystem::path> FilteringSourceAccessor::getPhysicalPath(const CanonPath & path)
@@ -57,12 +59,12 @@ void FilteringSourceAccessor::checkAccess(const CanonPath & path)
struct AllowListSourceAccessorImpl : AllowListSourceAccessor
{
std::set<CanonPath> allowedPrefixes;
std::unordered_set<CanonPath> allowedPaths;
boost::unordered_flat_set<CanonPath, std::hash<CanonPath>> allowedPaths;
AllowListSourceAccessorImpl(
ref<SourceAccessor> next,
std::set<CanonPath> && allowedPrefixes,
std::unordered_set<CanonPath> && allowedPaths,
boost::unordered_flat_set<CanonPath, std::hash<CanonPath>> && allowedPaths,
MakeNotAllowedError && makeNotAllowedError)
: AllowListSourceAccessor(SourcePath(next), std::move(makeNotAllowedError))
, allowedPrefixes(std::move(allowedPrefixes))
@@ -84,7 +86,7 @@ struct AllowListSourceAccessorImpl : AllowListSourceAccessor
ref<AllowListSourceAccessor> AllowListSourceAccessor::create(
ref<SourceAccessor> next,
std::set<CanonPath> && allowedPrefixes,
std::unordered_set<CanonPath> && allowedPaths,
boost::unordered_flat_set<CanonPath, std::hash<CanonPath>> && allowedPaths,
MakeNotAllowedError && makeNotAllowedError)
{
return make_ref<AllowListSourceAccessorImpl>(

View File

@@ -30,8 +30,9 @@
#include <git2/sys/mempack.h>
#include <git2/tree.h>
#include <boost/unordered/unordered_flat_map.hpp>
#include <boost/unordered/unordered_flat_set.hpp>
#include <iostream>
#include <unordered_set>
#include <queue>
#include <regex>
#include <span>
@@ -315,7 +316,7 @@ struct GitRepoImpl : GitRepo, std::enable_shared_from_this<GitRepoImpl>
uint64_t getRevCount(const Hash & rev) override
{
std::unordered_set<git_oid> done;
boost::unordered_flat_set<git_oid, std::hash<git_oid>> done;
std::queue<Commit> todo;
todo.push(peelObject<Commit>(lookupObject(*this, hashToOID(rev)).get(), GIT_OBJECT_COMMIT));
@@ -569,7 +570,7 @@ struct GitRepoImpl : GitRepo, std::enable_shared_from_this<GitRepoImpl>
void verifyCommit(const Hash & rev, const std::vector<fetchers::PublicKey> & publicKeys) override
{
// Map of SSH key types to their internal OpenSSH representations
static const std::unordered_map<std::string_view, std::string_view> keyTypeMap = {
static const boost::unordered_flat_map<std::string_view, std::string_view> keyTypeMap = {
{"ssh-dsa", "ssh-dsa"},
{"ssh-ecdsa", "ssh-ecdsa"},
{"ssh-ecdsa-sk", "sk-ecdsa-sha2-nistp256@openssh.com"},
@@ -816,7 +817,7 @@ struct GitSourceAccessor : SourceAccessor
return toHash(*git_tree_entry_id(entry));
}
std::unordered_map<CanonPath, TreeEntry> lookupCache;
boost::unordered_flat_map<CanonPath, TreeEntry, std::hash<CanonPath>> lookupCache;
/* Recursively look up 'path' relative to the root. */
git_tree_entry * lookup(State & state, const CanonPath & path)
@@ -1253,7 +1254,7 @@ GitRepoImpl::getAccessor(const WorkdirInfo & wd, bool exportIgnore, MakeNotAllow
makeFSSourceAccessor(path),
std::set<CanonPath>{wd.files},
// Always allow access to the root, but not its children.
std::unordered_set<CanonPath>{CanonPath::root},
boost::unordered_flat_set<CanonPath, std::hash<CanonPath>>{CanonPath::root},
std::move(makeNotAllowedError))
.cast<SourceAccessor>();
if (exportIgnore)

View File

@@ -2,7 +2,7 @@
#include "nix/util/source-path.hh"
#include <unordered_set>
#include <boost/unordered/unordered_flat_set_fwd.hpp>
namespace nix {
@@ -72,7 +72,7 @@ struct AllowListSourceAccessor : public FilteringSourceAccessor
static ref<AllowListSourceAccessor> create(
ref<SourceAccessor> next,
std::set<CanonPath> && allowedPrefixes,
std::unordered_set<CanonPath> && allowedPaths,
boost::unordered_flat_set<CanonPath, std::hash<CanonPath>> && allowedPaths,
MakeNotAllowedError && makeNotAllowedError);
using FilteringSourceAccessor::FilteringSourceAccessor;

View File

@@ -61,6 +61,7 @@ subdir('nix-meson-build-support/windows-version')
this_library = library(
'nixfetchers',
sources,
soversion : 0,
dependencies : deps_public + deps_private + deps_other,
include_directories : include_dirs,
link_args : linker_export_flags,

View File

@@ -53,6 +53,7 @@ subdir('nix-meson-build-support/windows-version')
this_library = library(
'nixflakec',
sources,
soversion : 0,
dependencies : deps_public + deps_private + deps_other,
include_directories : include_dirs,
link_args : linker_export_flags,

View File

@@ -1,5 +1,3 @@
#include <unordered_set>
#include "nix/fetchers/fetch-settings.hh"
#include "nix/flake/settings.hh"
#include "nix/flake/lockfile.hh"
@@ -9,6 +7,7 @@
#include <algorithm>
#include <iomanip>
#include <boost/unordered/unordered_flat_set.hpp>
#include <iterator>
#include <nlohmann/json.hpp>
@@ -162,7 +161,7 @@ std::pair<nlohmann::json, LockFile::KeyMap> LockFile::toJSON() const
{
nlohmann::json nodes;
KeyMap nodeKeys;
std::unordered_set<std::string> keys;
boost::unordered_flat_set<std::string> keys;
std::function<std::string(const std::string & key, ref<const Node> node)> dumpNode;

View File

@@ -58,6 +58,7 @@ this_library = library(
'nixflake',
sources,
generated_headers,
soversion : 0,
dependencies : deps_public + deps_private + deps_other,
include_directories : include_dirs,
link_args : linker_export_flags,

View File

@@ -45,6 +45,7 @@ subdir('nix-meson-build-support/windows-version')
this_library = library(
'nixmainc',
sources,
soversion : 0,
dependencies : deps_public + deps_private + deps_other,
include_directories : include_dirs,
link_args : linker_export_flags,

View File

@@ -77,6 +77,7 @@ this_library = library(
'nixmain',
sources,
config_priv_h,
soversion : 0,
dependencies : deps_public + deps_private + deps_other,
include_directories : include_dirs,
link_args : linker_export_flags,

View File

@@ -46,6 +46,7 @@ subdir('nix-meson-build-support/windows-version')
this_library = library(
'nixstorec',
sources,
soversion : 0,
dependencies : deps_public + deps_private + deps_other,
include_directories : include_dirs,
link_args : linker_export_flags,

View File

@@ -44,6 +44,7 @@ subdir('nix-meson-build-support/windows-version')
this_library = library(
'nix-store-test-support',
sources,
soversion : 0,
dependencies : deps_public + deps_private + deps_other,
include_directories : include_dirs,
# TODO: Remove `-lrapidcheck` when https://github.com/emil-e/rapidcheck/pull/326

View File

@@ -20,9 +20,9 @@ struct ServeProtoTest : VersionedProtoTest<ServeProto, serveProtoDir>
{
/**
* For serializers that don't care about the minimum version, we
* used the oldest one: 1.0.
* used the oldest one: 2.5.
*/
ServeProto::Version defaultVersion = 2 << 8 | 0;
ServeProto::Version defaultVersion = 2 << 8 | 5;
};
VERSIONED_CHARACTERIZATION_TEST(

View File

@@ -11,6 +11,7 @@
#include "nix/util/json-utils.hh"
#include <boost/container/small_vector.hpp>
#include <boost/unordered/concurrent_flat_map.hpp>
#include <nlohmann/json.hpp>
namespace nix {
@@ -834,7 +835,7 @@ DerivationType BasicDerivation::type() const
throw Error("can't mix derivation output types");
}
Sync<DrvHashes> drvHashes;
DrvHashes drvHashes;
/* pathDerivationModulo and hashDerivationModulo are mutually recursive
*/
@@ -844,16 +845,13 @@ Sync<DrvHashes> drvHashes;
*/
static const DrvHash pathDerivationModulo(Store & store, const StorePath & drvPath)
{
{
auto hashes = drvHashes.lock();
auto h = hashes->find(drvPath);
if (h != hashes->end()) {
return h->second;
}
std::optional<DrvHash> hash;
if (drvHashes.cvisit(drvPath, [&hash](const auto & kv) { hash.emplace(kv.second); })) {
return *hash;
}
auto h = hashDerivationModulo(store, store.readInvalidDerivation(drvPath), false);
// Cache it
drvHashes.lock()->insert_or_assign(drvPath, h);
drvHashes.insert_or_assign(drvPath, h);
return h;
}

View File

@@ -1,94 +1,116 @@
#include "nix/store/export-import.hh"
#include "nix/util/serialise.hh"
#include "nix/store/store-api.hh"
#include "nix/util/archive.hh"
#include "nix/store/common-protocol.hh"
#include "nix/store/common-protocol-impl.hh"
#include <algorithm>
namespace nix {
void Store::exportPaths(const StorePathSet & paths, Sink & sink)
static const uint32_t exportMagicV1 = 0x4558494e;
void exportPaths(Store & store, const StorePathSet & paths, Sink & sink, unsigned int version)
{
auto sorted = topoSortPaths(paths);
auto sorted = store.topoSortPaths(paths);
std::reverse(sorted.begin(), sorted.end());
for (auto & path : sorted) {
sink << 1;
exportPath(path, sink);
auto dumpNar = [&](const ValidPathInfo & info) {
HashSink hashSink(HashAlgorithm::SHA256);
TeeSink teeSink(sink, hashSink);
store.narFromPath(info.path, teeSink);
/* Refuse to export paths that have changed. This prevents
filesystem corruption from spreading to other machines.
Don't complain if the stored hash is zero (unknown). */
Hash hash = hashSink.currentHash().hash;
if (hash != info.narHash && info.narHash != Hash(info.narHash.algo))
throw Error(
"hash of path '%s' has changed from '%s' to '%s'!",
store.printStorePath(info.path),
info.narHash.to_string(HashFormat::Nix32, true),
hash.to_string(HashFormat::Nix32, true));
};
switch (version) {
case 1:
for (auto & path : sorted) {
sink << 1;
auto info = store.queryPathInfo(path);
dumpNar(*info);
sink << exportMagicV1 << store.printStorePath(path);
CommonProto::write(store, CommonProto::WriteConn{.to = sink}, info->references);
sink << (info->deriver ? store.printStorePath(*info->deriver) : "") << 0;
}
sink << 0;
break;
default:
throw Error("unsupported nario version %d", version);
}
sink << 0;
}
void Store::exportPath(const StorePath & path, Sink & sink)
{
auto info = queryPathInfo(path);
HashSink hashSink(HashAlgorithm::SHA256);
TeeSink teeSink(sink, hashSink);
narFromPath(path, teeSink);
/* Refuse to export paths that have changed. This prevents
filesystem corruption from spreading to other machines.
Don't complain if the stored hash is zero (unknown). */
Hash hash = hashSink.currentHash().hash;
if (hash != info->narHash && info->narHash != Hash(info->narHash.algo))
throw Error(
"hash of path '%s' has changed from '%s' to '%s'!",
printStorePath(path),
info->narHash.to_string(HashFormat::Nix32, true),
hash.to_string(HashFormat::Nix32, true));
teeSink << exportMagic << printStorePath(path);
CommonProto::write(*this, CommonProto::WriteConn{.to = teeSink}, info->references);
teeSink << (info->deriver ? printStorePath(*info->deriver) : "") << 0;
}
StorePaths Store::importPaths(Source & source, CheckSigsFlag checkSigs)
StorePaths importPaths(Store & store, Source & source, CheckSigsFlag checkSigs)
{
StorePaths res;
while (true) {
auto n = readNum<uint64_t>(source);
if (n == 0)
break;
if (n != 1)
throw Error("input doesn't look like something created by 'nix-store --export'");
/* Extract the NAR from the source. */
StringSink saved;
TeeSource tee{source, saved};
NullFileSystemObjectSink ether;
parseDump(ether, tee);
auto version = readNum<uint64_t>(source);
uint32_t magic = readInt(source);
if (magic != exportMagic)
throw Error("Nix archive cannot be imported; wrong format");
/* Note: nario version 1 lacks an explicit header. The first
integer denotes whether a store path follows or not. So look
for 0 or 1. */
switch (version) {
auto path = parseStorePath(readString(source));
case 0:
/* Empty version 1 nario, nothing to do. */
break;
// Activity act(*logger, lvlInfo, "importing path '%s'", info.path);
case 1:
/* Non-empty version 1 nario. */
while (true) {
/* Extract the NAR from the source. */
StringSink saved;
TeeSource tee{source, saved};
NullFileSystemObjectSink ether;
parseDump(ether, tee);
auto references = CommonProto::Serialise<StorePathSet>::read(*this, CommonProto::ReadConn{.from = source});
auto deriver = readString(source);
auto narHash = hashString(HashAlgorithm::SHA256, saved.s);
uint32_t magic = readInt(source);
if (magic != exportMagicV1)
throw Error("nario cannot be imported; wrong format");
ValidPathInfo info{path, narHash};
if (deriver != "")
info.deriver = parseStorePath(deriver);
info.references = references;
info.narSize = saved.s.size();
auto path = store.parseStorePath(readString(source));
// Ignore optional legacy signature.
if (readInt(source) == 1)
readString(source);
auto references = CommonProto::Serialise<StorePathSet>::read(store, CommonProto::ReadConn{.from = source});
auto deriver = readString(source);
auto narHash = hashString(HashAlgorithm::SHA256, saved.s);
// Can't use underlying source, which would have been exhausted
auto source = StringSource(saved.s);
addToStore(info, source, NoRepair, checkSigs);
ValidPathInfo info{path, narHash};
if (deriver != "")
info.deriver = store.parseStorePath(deriver);
info.references = references;
info.narSize = saved.s.size();
res.push_back(info.path);
// Ignore optional legacy signature.
if (readInt(source) == 1)
readString(source);
// Can't use underlying source, which would have been exhausted.
auto source2 = StringSource(saved.s);
store.addToStore(info, source2, NoRepair, checkSigs);
res.push_back(info.path);
auto n = readNum<uint64_t>(source);
if (n == 0)
break;
if (n != 1)
throw Error("input doesn't look like a nario");
}
break;
default:
throw Error("input doesn't look like a nario");
}
return res;

View File

@@ -1,6 +1,7 @@
#include "nix/store/derivations.hh"
#include "nix/store/globals.hh"
#include "nix/store/local-store.hh"
#include "nix/store/path.hh"
#include "nix/util/finally.hh"
#include "nix/util/unix-domain-socket.hh"
#include "nix/util/signals.hh"
@@ -13,14 +14,10 @@
# include "nix/util/processes.hh"
#endif
#include <boost/unordered/unordered_flat_map.hpp>
#include <boost/unordered/unordered_flat_set.hpp>
#include <boost/regex.hpp>
#include <functional>
#include <queue>
#include <algorithm>
#include <random>
#include <climits>
#include <errno.h>
#include <fcntl.h>
#include <sys/stat.h>
@@ -314,7 +311,12 @@ Roots LocalStore::findRoots(bool censor)
/**
* Key is a mere string because cannot has path with macOS's libc++
*/
typedef std::unordered_map<std::string, std::unordered_set<std::string>> UncheckedRoots;
typedef boost::unordered_flat_map<
std::string,
boost::unordered_flat_set<std::string, StringViewHash, std::equal_to<>>,
StringViewHash,
std::equal_to<>>
UncheckedRoots;
static void readProcLink(const std::filesystem::path & file, UncheckedRoots & roots)
{
@@ -328,7 +330,7 @@ static void readProcLink(const std::filesystem::path & file, UncheckedRoots & ro
throw;
}
if (buf.is_absolute())
roots[buf.string()].emplace(file.string());
roots[buf].emplace(file.string());
}
static std::string quoteRegexChars(const std::string & raw)
@@ -463,13 +465,13 @@ void LocalStore::collectGarbage(const GCOptions & options, GCResults & results)
bool gcKeepOutputs = settings.gcKeepOutputs;
bool gcKeepDerivations = settings.gcKeepDerivations;
std::unordered_set<StorePath> roots, dead, alive;
boost::unordered_flat_set<StorePath, std::hash<StorePath>> roots, dead, alive;
struct Shared
{
// The temp roots only store the hash part to make it easier to
// ignore suffixes like '.lock', '.chroot' and '.check'.
std::unordered_set<std::string> tempRoots;
boost::unordered_flat_set<std::string, StringViewHash, std::equal_to<>> tempRoots;
// Hash part of the store path currently being deleted, if
// any.
@@ -578,9 +580,9 @@ void LocalStore::collectGarbage(const GCOptions & options, GCResults & results)
auto storePath = maybeParseStorePath(path);
if (storePath) {
debug("got new GC root '%s'", path);
auto hashPart = std::string(storePath->hashPart());
auto hashPart = storePath->hashPart();
auto shared(_shared.lock());
shared->tempRoots.insert(hashPart);
shared->tempRoots.emplace(hashPart);
/* If this path is currently being
deleted, then we have to wait until
deletion is finished to ensure that
@@ -632,7 +634,7 @@ void LocalStore::collectGarbage(const GCOptions & options, GCResults & results)
Roots tempRoots;
findTempRoots(tempRoots, true);
for (auto & root : tempRoots) {
_shared.lock()->tempRoots.insert(std::string(root.first.hashPart()));
_shared.lock()->tempRoots.emplace(root.first.hashPart());
roots.insert(root.first);
}
@@ -672,7 +674,7 @@ void LocalStore::collectGarbage(const GCOptions & options, GCResults & results)
}
};
std::unordered_map<StorePath, StorePathSet> referrersCache;
boost::unordered_flat_map<StorePath, StorePathSet, std::hash<StorePath>> referrersCache;
/* Helper function that visits all paths reachable from `start`
via the referrers edges and optionally derivers and derivation
@@ -739,7 +741,7 @@ void LocalStore::collectGarbage(const GCOptions & options, GCResults & results)
return;
{
auto hashPart = std::string(path->hashPart());
auto hashPart = path->hashPart();
auto shared(_shared.lock());
if (shared->tempRoots.count(hashPart)) {
debug("cannot delete '%s' because it's a temporary root", printStorePath(*path));

View File

@@ -11,7 +11,7 @@
#include "nix/util/sync.hh"
#include "nix/util/variant-wrapper.hh"
#include <map>
#include <boost/unordered/concurrent_flat_map_fwd.hpp>
#include <variant>
namespace nix {
@@ -507,13 +507,23 @@ DrvHash hashDerivationModulo(Store & store, const Derivation & drv, bool maskOut
*/
std::map<std::string, Hash> staticOutputHashes(Store & store, const Derivation & drv);
struct DrvHashFct
{
using is_avalanching = std::true_type;
std::size_t operator()(const StorePath & path) const noexcept
{
return std::hash<std::string_view>{}(path.to_string());
}
};
/**
* Memoisation of hashDerivationModulo().
*/
typedef std::map<StorePath, DrvHash> DrvHashes;
typedef boost::concurrent_flat_map<StorePath, DrvHash, DrvHashFct> DrvHashes;
// FIXME: global, though at least thread-safe.
extern Sync<DrvHashes> drvHashes;
extern DrvHashes drvHashes;
struct Source;
struct Sink;

View File

@@ -0,0 +1,19 @@
#pragma once
#include "nix/store/store-api.hh"
namespace nix {
/**
* Export multiple paths in the format expected by `nix-store
* --import`. The paths will be sorted topologically.
*/
void exportPaths(Store & store, const StorePathSet & paths, Sink & sink, unsigned int version);
/**
* Import a sequence of NAR dumps created by `exportPaths()` into the
* Nix store.
*/
StorePaths importPaths(Store & store, Source & source, CheckSigsFlag checkSigs = CheckSigs);
} // namespace nix

View File

@@ -1,13 +1,17 @@
#pragma once
///@file
#include <unordered_set>
#include "nix/store/store-api.hh"
#include <boost/unordered/unordered_flat_map.hpp>
#include <boost/unordered/unordered_flat_set.hpp>
namespace nix {
typedef std::unordered_map<StorePath, std::unordered_set<std::string>> Roots;
typedef boost::unordered_flat_map<
StorePath,
boost::unordered_flat_set<std::string, StringViewHash, std::equal_to<>>,
std::hash<StorePath>>
Roots;
struct GCOptions
{

View File

@@ -11,7 +11,7 @@
#include <chrono>
#include <future>
#include <string>
#include <unordered_set>
#include <boost/unordered/unordered_flat_set.hpp>
namespace nix {
@@ -442,7 +442,7 @@ private:
std::pair<std::filesystem::path, AutoCloseFD> createTempDirInStore();
typedef std::unordered_set<ino_t> InodeHash;
typedef boost::unordered_flat_set<ino_t> InodeHash;
InodeHash loadInodeHash();
Strings readDirectoryIgnoringInodes(const Path & path, const InodeHash & inodeHash);

View File

@@ -34,6 +34,7 @@ headers = [ config_pub_h ] + files(
'derived-path-map.hh',
'derived-path.hh',
'downstream-placeholder.hh',
'export-import.hh',
'filetransfer.hh',
'gc-store.hh',
'globals.hh',

View File

@@ -82,8 +82,6 @@ struct ServeProto::BasicClientConnection
BuildResult getBuildDerivationResponse(const StoreDirConfig & store);
void narFromPath(const StoreDirConfig & store, const StorePath & path, std::function<void(Source &)> fun);
void importPaths(const StoreDirConfig & store, std::function<void(Sink &)> fun);
};
struct ServeProto::BasicServerConnection

View File

@@ -108,8 +108,6 @@ enum struct ServeProto::Command : uint64_t {
QueryValidPaths = 1,
QueryPathInfos = 2,
DumpStorePath = 3,
ImportPaths = 4,
ExportPaths = 5,
BuildPaths = 6,
QueryClosure = 7,
BuildDerivation = 8,

View File

@@ -48,11 +48,6 @@ enum CheckSigsFlag : bool { NoCheckSigs = false, CheckSigs = true };
enum SubstituteFlag : bool { NoSubstitute = false, Substitute = true };
/**
* Magic header of exportPath() output (obsolete).
*/
const uint32_t exportMagic = 0x4558494e;
enum BuildMode : uint8_t { bmNormal, bmRepair, bmCheck };
enum TrustedFlag : bool { NotTrusted = false, Trusted = true };
@@ -804,21 +799,6 @@ public:
*/
StorePaths topoSortPaths(const StorePathSet & paths);
/**
* Export multiple paths in the format expected by nix-store
* --import.
*/
void exportPaths(const StorePathSet & paths, Sink & sink);
void exportPath(const StorePath & path, Sink & sink);
/**
* Import a sequence of NAR dumps created by exportPaths() into the
* Nix store. Optionally, the contents of the NARs are preloaded
* into the specified FS accessor to speed up subsequent access.
*/
StorePaths importPaths(Source & source, CheckSigsFlag checkSigs = CheckSigs);
struct Stats
{
std::atomic<uint64_t> narInfoRead{0};

View File

@@ -105,9 +105,6 @@ std::map<StorePath, UnkeyedValidPathInfo> LegacySSHStore::queryPathInfosUncached
{
auto conn(connections->get());
/* No longer support missing NAR hash */
assert(GET_PROTOCOL_MINOR(conn->remoteVersion) >= 4);
debug(
"querying remote host '%s' for info on '%s'",
config->authority.host,
@@ -152,40 +149,21 @@ void LegacySSHStore::addToStore(const ValidPathInfo & info, Source & source, Rep
auto conn(connections->get());
if (GET_PROTOCOL_MINOR(conn->remoteVersion) >= 5) {
conn->to << ServeProto::Command::AddToStoreNar << printStorePath(info.path)
<< (info.deriver ? printStorePath(*info.deriver) : "")
<< info.narHash.to_string(HashFormat::Base16, false);
ServeProto::write(*this, *conn, info.references);
conn->to << info.registrationTime << info.narSize << info.ultimate << info.sigs
<< renderContentAddress(info.ca);
try {
copyNAR(source, conn->to);
} catch (...) {
conn->good = false;
throw;
}
conn->to.flush();
if (readInt(conn->from) != 1)
throw Error(
"failed to add path '%s' to remote host '%s'", printStorePath(info.path), config->authority.host);
} else {
conn->importPaths(*this, [&](Sink & sink) {
try {
copyNAR(source, sink);
} catch (...) {
conn->good = false;
throw;
}
sink << exportMagic << printStorePath(info.path);
ServeProto::write(*this, *conn, info.references);
sink << (info.deriver ? printStorePath(*info.deriver) : "") << 0 << 0;
});
conn->to << ServeProto::Command::AddToStoreNar << printStorePath(info.path)
<< (info.deriver ? printStorePath(*info.deriver) : "")
<< info.narHash.to_string(HashFormat::Base16, false);
ServeProto::write(*this, *conn, info.references);
conn->to << info.registrationTime << info.narSize << info.ultimate << info.sigs << renderContentAddress(info.ca);
try {
copyNAR(source, conn->to);
} catch (...) {
conn->good = false;
throw;
}
conn->to.flush();
if (readInt(conn->from) != 1)
throw Error("failed to add path '%s' to remote host '%s'", printStorePath(info.path), config->authority.host);
}
void LegacySSHStore::narFromPath(const StorePath & path, Sink & sink)

View File

@@ -363,6 +363,7 @@ this_library = library(
generated_headers,
sources,
config_priv_h,
soversion : 0,
dependencies : deps_public + deps_private + deps_other,
include_directories : include_dirs,
link_args : linker_export_flags,

View File

@@ -1,5 +1,3 @@
#include <unordered_set>
#include "nix/store/derivations.hh"
#include "nix/store/parsed-derivations.hh"
#include "nix/store/derivation-options.hh"
@@ -13,6 +11,8 @@
#include "nix/store/filetransfer.hh"
#include "nix/util/strings.hh"
#include <boost/unordered/unordered_flat_set.hpp>
namespace nix {
void Store::computeFSClosure(
@@ -106,7 +106,7 @@ MissingPaths Store::queryMissing(const std::vector<DerivedPath> & targets)
struct State
{
std::unordered_set<std::string> done;
boost::unordered_flat_set<std::string> done;
MissingPaths res;
};

View File

@@ -15,7 +15,7 @@ ServeProto::Version ServeProto::BasicClientConnection::handshake(
if (magic != SERVE_MAGIC_2)
throw Error("'nix-store --serve' protocol mismatch from '%s'", host);
auto remoteVersion = readInt(from);
if (GET_PROTOCOL_MAJOR(remoteVersion) != 0x200)
if (GET_PROTOCOL_MAJOR(remoteVersion) != 0x200 || GET_PROTOCOL_MINOR(remoteVersion) < 5)
throw Error("unsupported 'nix-store --serve' protocol version on '%s'", host);
return std::min(remoteVersion, localVersion);
}
@@ -93,14 +93,4 @@ void ServeProto::BasicClientConnection::narFromPath(
fun(from);
}
void ServeProto::BasicClientConnection::importPaths(const StoreDirConfig & store, std::function<void(Sink &)> fun)
{
to << ServeProto::Command::ImportPaths;
fun(to);
to.flush();
if (readInt(from) != 1)
throw Error("remote machine failed to import closure");
}
} // namespace nix

View File

@@ -53,6 +53,7 @@ this_library = library(
'nixutilc',
sources,
config_priv_h,
soversion : 0,
dependencies : deps_public + deps_private + deps_other,
include_directories : include_dirs,
link_args : linker_export_flags,

View File

@@ -41,6 +41,7 @@ subdir('nix-meson-build-support/windows-version')
this_library = library(
'nix-util-test-support',
sources,
soversion : 0,
dependencies : deps_public + deps_private + deps_other,
include_directories : include_dirs,
# TODO: Remove `-lrapidcheck` when https://github.com/emil-e/rapidcheck/pull/326

View File

@@ -258,7 +258,7 @@ public:
*/
std::string makeRelative(const CanonPath & path) const;
friend class std::hash<CanonPath>;
friend struct std::hash<CanonPath>;
};
std::ostream & operator<<(std::ostream & stream, const CanonPath & path);
@@ -268,6 +268,8 @@ std::ostream & operator<<(std::ostream & stream, const CanonPath & path);
template<>
struct std::hash<nix::CanonPath>
{
using is_avalanching = std::true_type;
std::size_t operator()(const nix::CanonPath & s) const noexcept
{
return std::hash<std::string>{}(s.path);

View File

@@ -4,10 +4,10 @@
#include "nix/util/file-system.hh"
#include "nix/util/finally.hh"
#include <boost/unordered/unordered_flat_set.hpp>
#include <chrono>
#include <cmath>
#include <regex>
#include <unordered_set>
#include <thread>
#include <dirent.h>
@@ -76,7 +76,7 @@ static CgroupStats destroyCgroup(const std::filesystem::path & cgroup, bool retu
int round = 1;
std::unordered_set<pid_t> pidsShown;
boost::unordered_flat_set<pid_t> pidsShown;
while (true) {
auto pids = tokenizeString<std::vector<std::string>>(readFile(procsFile));

View File

@@ -197,6 +197,7 @@ subdir('nix-meson-build-support/windows-version')
this_library = library(
'nixutil',
sources,
soversion : 0,
dependencies : deps_public + deps_private + deps_other,
include_directories : include_dirs,
link_args : linker_export_flags,

View File

@@ -104,7 +104,7 @@ std::optional<struct stat> PosixSourceAccessor::cachedLstat(const CanonPath & pa
if (cache.size() >= 16384)
cache.clear();
cache.emplace(absPath, st);
cache.emplace(std::move(absPath), st);
return st;
}

View File

@@ -71,7 +71,7 @@ struct CmdShell : InstallablesCommand, MixEnvironment
auto outPaths =
Installable::toStorePaths(getEvalStore(), store, Realise::Outputs, OperateOn::Output, installables);
std::unordered_set<StorePath> done;
boost::unordered_flat_set<StorePath, std::hash<StorePath>> done;
std::queue<StorePath> todo;
for (auto & path : outPaths)
todo.push(path);

View File

@@ -522,7 +522,7 @@ struct CmdFlakeCheck : FlakeCommand
auto checkNixOSConfiguration = [&](const std::string & attrPath, Value & v, const PosIdx pos) {
try {
Activity act(*logger, lvlInfo, actUnknown, fmt("checking NixOS configuration '%s'", attrPath));
Bindings & bindings(*state->allocBindings(0));
Bindings & bindings = Bindings::emptyBindings;
auto vToplevel = findAlongAttrPath(*state, "config.system.build.toplevel", bindings, v).first;
state->forceValue(*vToplevel, pos);
if (!state->isDerivation(*vToplevel))

View File

@@ -87,6 +87,7 @@ nix_sources = [ config_priv_h ] + files(
'make-content-addressed.cc',
'man-pages.cc',
'nar.cc',
'nario.cc',
'optimise-store.cc',
'path-from-hash-part.cc',
'path-info.cc',

28
src/nix/nario-export.md Normal file
View File

@@ -0,0 +1,28 @@
R""(
# Examples
* Export the closure of building `nixpkgs#hello`:
```console
# nix nario export --format 1 -r nixpkgs#hello > dump
```
It can be imported in another store:
```console
# nix nario import < dump
```
# Description
This command prints on standard output a serialization of the specified store paths in `nario` format. This serialization can be imported into another store using `nix nario import`.
References of a path are not exported by default; use `-r` to export a complete closure.
Paths are exported in topographically sorted order (i.e. if path `X` refers to `Y`, then `Y` appears before `X`).
You must specify the desired `nario` version. Currently the following versions are supported:
* `1`: This version is compatible with the legacy `nix-store --export` and `nix-store --import` commands.
)""

15
src/nix/nario-import.md Normal file
View File

@@ -0,0 +1,15 @@
R""(
# Examples
* Import store paths from the file named `dump`:
```console
# nix nario import < dump
```
# Description
This command reads from standard input a serialization of store paths produced by `nix nario export` and adds them to the Nix store.
)""

18
src/nix/nario-list.md Normal file
View File

@@ -0,0 +1,18 @@
R""(
# Examples
* List the contents of a nario file:
```console
# nix nario list < dump
/nix/store/4y1jj6cwvslmfh1bzkhbvhx77az6yf00-xgcc-14.2.1.20250322-libgcc: 201856 bytes
/nix/store/d8hnbm5hvbg2vza50garppb63y724i94-libunistring-1.3: 2070240 bytes
```
# Description
This command lists the contents of a nario file read from standard input.
)""

189
src/nix/nario.cc Normal file
View File

@@ -0,0 +1,189 @@
#include "nix/cmd/command.hh"
#include "nix/main/shared.hh"
#include "nix/store/store-api.hh"
#include "nix/store/export-import.hh"
#include "nix/util/callback.hh"
#include "nix/util/fs-sink.hh"
#include "nix/util/archive.hh"
using namespace nix;
struct CmdNario : NixMultiCommand
{
CmdNario()
: NixMultiCommand("nario", RegisterCommand::getCommandsFor({"nario"}))
{
}
std::string description() override
{
return "operations for manipulating nario files";
}
Category category() override
{
return catUtility;
}
};
static auto rCmdNario = registerCommand<CmdNario>("nario");
struct CmdNarioExport : StorePathsCommand
{
unsigned int version = 0;
CmdNarioExport()
{
addFlag({
.longName = "format",
.description = "Version of the nario format to use. Must be `1`.",
.labels = {"nario-format"},
.handler = {&version},
});
}
std::string description() override
{
return "serialize store paths to standard output in nario format";
}
std::string doc() override
{
return
#include "nario-export.md"
;
}
void run(ref<Store> store, StorePaths && storePaths) override
{
if (!version)
throw UsageError("`nix nario export` requires `--format` argument");
FdSink sink(getStandardOutput());
exportPaths(*store, StorePathSet(storePaths.begin(), storePaths.end()), sink, version);
}
};
static auto rCmdNarioExport = registerCommand2<CmdNarioExport>({"nario", "export"});
struct CmdNarioImport : StoreCommand
{
std::string description() override
{
return "import store paths from a nario file on standard input";
}
std::string doc() override
{
return
#include "nario-import.md"
;
}
void run(ref<Store> store) override
{
FdSource source(getStandardInput());
importPaths(*store, source, NoCheckSigs); // FIXME
}
};
static auto rCmdNarioImport = registerCommand2<CmdNarioImport>({"nario", "import"});
struct CmdNarioList : Command
{
std::string description() override
{
return "list the contents of a nario file";
}
std::string doc() override
{
return
#include "nario-list.md"
;
}
void run() override
{
struct Config : StoreConfig
{
Config(const Params & params)
: StoreConfig(params)
{
}
ref<Store> openStore() const override
{
abort();
}
};
struct ListingStore : Store
{
ListingStore(ref<const Config> config)
: Store{*config}
{
}
void queryPathInfoUncached(
const StorePath & path, Callback<std::shared_ptr<const ValidPathInfo>> callback) noexcept override
{
callback(nullptr);
}
std::optional<TrustedFlag> isTrustedClient() override
{
return Trusted;
}
std::optional<StorePath> queryPathFromHashPart(const std::string & hashPart) override
{
return std::nullopt;
}
void
addToStore(const ValidPathInfo & info, Source & source, RepairFlag repair, CheckSigsFlag checkSigs) override
{
logger->cout(fmt("%s: %d bytes", printStorePath(info.path), info.narSize));
// Discard the NAR.
NullFileSystemObjectSink parseSink;
parseDump(parseSink, source);
}
StorePath addToStoreFromDump(
Source & dump,
std::string_view name,
FileSerialisationMethod dumpMethod,
ContentAddressMethod hashMethod,
HashAlgorithm hashAlgo,
const StorePathSet & references,
RepairFlag repair) override
{
unsupported("addToStoreFromDump");
}
void narFromPath(const StorePath & path, Sink & sink) override
{
unsupported("narFromPath");
}
void queryRealisationUncached(
const DrvOutput &, Callback<std::shared_ptr<const Realisation>> callback) noexcept override
{
callback(nullptr);
}
ref<SourceAccessor> getFSAccessor(bool requireValidPath) override
{
return makeEmptySourceAccessor();
}
};
FdSource source(getStandardInput());
auto config = make_ref<Config>(StoreConfig::Params());
ListingStore lister(config);
importPaths(lister, source, NoCheckSigs);
}
};
static auto rCmdNarioList = registerCommand2<CmdNarioList>({"nario", "list"});

View File

@@ -158,7 +158,7 @@ static void loadSourceExpr(EvalState & state, const SourcePath & path, Value & v
directory). */
else if (st.type == SourceAccessor::tDirectory) {
auto attrs = state.buildBindings(maxAttrs);
attrs.insert(state.symbols.create("_combineChannels"), &state.vEmptyList);
attrs.insert(state.symbols.create("_combineChannels"), &Value::vEmptyList);
StringSet seen;
getAllExprs(state, path, seen, attrs);
v.mkAttrs(attrs);

View File

@@ -24,7 +24,7 @@ PackageInfos queryInstalled(EvalState & state, const Path & userEnv)
if (pathExists(manifestFile)) {
Value v;
state.evalFile(state.rootPath(CanonPath(manifestFile)).resolveSymlinks(), v);
Bindings & bindings(*state.allocBindings(0));
Bindings & bindings = Bindings::emptyBindings;
getDerivations(state, v, "", bindings, elems, false);
}
return elems;

View File

@@ -14,6 +14,7 @@
#include "nix/util/posix-source-accessor.hh"
#include "nix/store/globals.hh"
#include "nix/store/path-with-outputs.hh"
#include "nix/store/export-import.hh"
#include "man-pages.hh"
@@ -774,7 +775,7 @@ static void opExport(Strings opFlags, Strings opArgs)
paths.insert(store->followLinksToStorePath(i));
FdSink sink(getStandardOutput());
store->exportPaths(paths, sink);
exportPaths(*store, paths, sink, 1);
sink.flush();
}
@@ -787,7 +788,7 @@ static void opImport(Strings opFlags, Strings opArgs)
throw UsageError("no arguments expected");
FdSource source(STDIN_FILENO);
auto paths = store->importPaths(source, NoCheckSigs);
auto paths = importPaths(*store, source, NoCheckSigs);
for (auto & i : paths)
cout << fmt("%s\n", store->printStorePath(i)) << std::flush;
@@ -985,20 +986,6 @@ static void opServe(Strings opFlags, Strings opArgs)
store->narFromPath(store->parseStorePath(readString(in)), out);
break;
case ServeProto::Command::ImportPaths: {
if (!writeAllowed)
throw Error("importing paths is not allowed");
store->importPaths(in, NoCheckSigs); // FIXME: should we skip sig checking?
out << 1; // indicate success
break;
}
case ServeProto::Command::ExportPaths: {
readInt(in); // obsolete
store->exportPaths(ServeProto::Serialise<StorePathSet>::read(*store, rconn), out);
break;
}
case ServeProto::Command::BuildPaths: {
if (!writeAllowed)

View File

@@ -162,7 +162,7 @@ struct CmdUpgradeNix : MixDryRun, StoreCommand
auto state = std::make_unique<EvalState>(LookupPath{}, store, fetchSettings, evalSettings);
auto v = state->allocValue();
state->eval(state->parseExprFromString(res.data, state->rootPath(CanonPath("/no-such-path"))), *v);
Bindings & bindings(*state->allocBindings(0));
Bindings & bindings = Bindings::emptyBindings;
auto v2 = findAlongAttrPath(*state, settings.thisSystem, bindings, *v).first;
return store->parseStorePath(

View File

@@ -11,6 +11,7 @@
#include "nix/store/globals.hh"
#include "nix/store/store-open.hh"
#include "nix/util/posix-source-accessor.hh"
#include "nix/store/export-import.hh"
#include <sodium.h>
#include <nlohmann/json.hpp>
@@ -233,7 +234,7 @@ StoreWrapper::exportPaths(int fd, ...)
StorePathSet paths;
for (int n = 2; n < items; ++n) paths.insert(THIS->store->parseStorePath(SvPV_nolen(ST(n))));
FdSink sink(fd);
THIS->store->exportPaths(paths, sink);
exportPaths(*THIS->store, paths, sink, 1);
} catch (Error & e) {
croak("%s", e.what());
}
@@ -244,7 +245,7 @@ StoreWrapper::importPaths(int fd, int dontCheckSigs)
PPCODE:
try {
FdSource source(fd);
THIS->store->importPaths(source, dontCheckSigs ? NoCheckSigs : CheckSigs);
importPaths(*THIS->store, source, dontCheckSigs ? NoCheckSigs : CheckSigs);
} catch (Error & e) {
croak("%s", e.what());
}

View File

@@ -9,9 +9,14 @@ clearStore
outPath=$(nix-build dependencies.nix --no-out-link)
nix-store --export $outPath > $TEST_ROOT/exp
nix nario export --format 1 "$outPath" > $TEST_ROOT/exp2
cmp "$TEST_ROOT/exp" "$TEST_ROOT/exp2"
nix-store --export $(nix-store -qR $outPath) > $TEST_ROOT/exp_all
nix nario export --format 1 -r "$outPath" > $TEST_ROOT/exp_all2
cmp "$TEST_ROOT/exp_all" "$TEST_ROOT/exp_all2"
if nix-store --export $outPath >/dev/full ; then
echo "exporting to a bad file descriptor should fail"
exit 1
@@ -38,3 +43,13 @@ clearStore
# Regression test: the derivers in exp_all2 are empty, which shouldn't
# cause a failure.
nix-store --import < $TEST_ROOT/exp_all2
# Test `nix nario import` on files created by `nix-store --export`.
clearStore
nix nario import < $TEST_ROOT/exp_all
nix path-info "$outPath"
# Test `nix nario list`.
nix nario list < $TEST_ROOT/exp_all | grepQuiet "dependencies-input-0: .* bytes"