mirror of
https://github.com/NixOS/nix.git
synced 2025-11-09 03:56:01 +01:00
Merge branch 'master' into fzakaria/shellcheck-functional-db-migration
This commit is contained in:
commit
697c704f28
9 changed files with 572 additions and 23 deletions
16
doc/manual/rl-next/c-api-lazy-accessors.md
Normal file
16
doc/manual/rl-next/c-api-lazy-accessors.md
Normal file
|
|
@ -0,0 +1,16 @@
|
||||||
|
---
|
||||||
|
synopsis: "C API: Add lazy attribute and list item accessors"
|
||||||
|
prs: [14030]
|
||||||
|
---
|
||||||
|
|
||||||
|
The C API now includes lazy accessor functions for retrieving values from lists and attribute sets without forcing evaluation:
|
||||||
|
|
||||||
|
- `nix_get_list_byidx_lazy()` - Get a list element without forcing its evaluation
|
||||||
|
- `nix_get_attr_byname_lazy()` - Get an attribute value by name without forcing evaluation
|
||||||
|
- `nix_get_attr_byidx_lazy()` - Get an attribute by index without forcing evaluation
|
||||||
|
|
||||||
|
These functions are useful when forwarding unevaluated sub-values to other lists, attribute sets, or function calls. They allow more efficient handling of Nix values by deferring evaluation until actually needed.
|
||||||
|
|
||||||
|
Additionally, bounds checking has been improved for all `_byidx` functions to properly validate indices before access, preventing potential out-of-bounds errors.
|
||||||
|
|
||||||
|
The documentation for `NIX_ERR_KEY` error handling has also been clarified to specify when this error code is returned.
|
||||||
|
|
@ -106,7 +106,6 @@
|
||||||
enable = true;
|
enable = true;
|
||||||
excludes = [
|
excludes = [
|
||||||
# We haven't linted these files yet
|
# We haven't linted these files yet
|
||||||
''^scripts/install-systemd-multi-user\.sh$''
|
|
||||||
''^tests/functional/dump-db\.sh$''
|
''^tests/functional/dump-db\.sh$''
|
||||||
''^tests/functional/dyn-drv/eval-outputOf\.sh$''
|
''^tests/functional/dyn-drv/eval-outputOf\.sh$''
|
||||||
''^tests/functional/dyn-drv/old-daemon-error-hack\.sh$''
|
''^tests/functional/dyn-drv/old-daemon-error-hack\.sh$''
|
||||||
|
|
|
||||||
|
|
@ -39,7 +39,7 @@ create_systemd_proxy_env() {
|
||||||
vars="http_proxy https_proxy ftp_proxy all_proxy no_proxy HTTP_PROXY HTTPS_PROXY FTP_PROXY ALL_PROXY NO_PROXY"
|
vars="http_proxy https_proxy ftp_proxy all_proxy no_proxy HTTP_PROXY HTTPS_PROXY FTP_PROXY ALL_PROXY NO_PROXY"
|
||||||
for v in $vars; do
|
for v in $vars; do
|
||||||
if [ "x${!v:-}" != "x" ]; then
|
if [ "x${!v:-}" != "x" ]; then
|
||||||
echo "Environment=${v}=$(escape_systemd_env ${!v})"
|
echo "Environment=${v}=$(escape_systemd_env "${!v}")"
|
||||||
fi
|
fi
|
||||||
done
|
done
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -326,6 +326,10 @@ nix_value * nix_get_list_byidx(nix_c_context * context, const nix_value * value,
|
||||||
try {
|
try {
|
||||||
auto & v = check_value_in(value);
|
auto & v = check_value_in(value);
|
||||||
assert(v.type() == nix::nList);
|
assert(v.type() == nix::nList);
|
||||||
|
if (ix >= v.listSize()) {
|
||||||
|
nix_set_err_msg(context, NIX_ERR_KEY, "list index out of bounds");
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
auto * p = v.listView()[ix];
|
auto * p = v.listView()[ix];
|
||||||
nix_gc_incref(nullptr, p);
|
nix_gc_incref(nullptr, p);
|
||||||
if (p != nullptr)
|
if (p != nullptr)
|
||||||
|
|
@ -335,6 +339,26 @@ nix_value * nix_get_list_byidx(nix_c_context * context, const nix_value * value,
|
||||||
NIXC_CATCH_ERRS_NULL
|
NIXC_CATCH_ERRS_NULL
|
||||||
}
|
}
|
||||||
|
|
||||||
|
nix_value *
|
||||||
|
nix_get_list_byidx_lazy(nix_c_context * context, const nix_value * value, EvalState * state, unsigned int ix)
|
||||||
|
{
|
||||||
|
if (context)
|
||||||
|
context->last_err_code = NIX_OK;
|
||||||
|
try {
|
||||||
|
auto & v = check_value_in(value);
|
||||||
|
assert(v.type() == nix::nList);
|
||||||
|
if (ix >= v.listSize()) {
|
||||||
|
nix_set_err_msg(context, NIX_ERR_KEY, "list index out of bounds");
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
auto * p = v.listView()[ix];
|
||||||
|
nix_gc_incref(nullptr, p);
|
||||||
|
// Note: intentionally NOT calling forceValue() to keep the element lazy
|
||||||
|
return as_nix_value_ptr(p);
|
||||||
|
}
|
||||||
|
NIXC_CATCH_ERRS_NULL
|
||||||
|
}
|
||||||
|
|
||||||
nix_value * nix_get_attr_byname(nix_c_context * context, const nix_value * value, EvalState * state, const char * name)
|
nix_value * nix_get_attr_byname(nix_c_context * context, const nix_value * value, EvalState * state, const char * name)
|
||||||
{
|
{
|
||||||
if (context)
|
if (context)
|
||||||
|
|
@ -355,6 +379,27 @@ nix_value * nix_get_attr_byname(nix_c_context * context, const nix_value * value
|
||||||
NIXC_CATCH_ERRS_NULL
|
NIXC_CATCH_ERRS_NULL
|
||||||
}
|
}
|
||||||
|
|
||||||
|
nix_value *
|
||||||
|
nix_get_attr_byname_lazy(nix_c_context * context, const nix_value * value, EvalState * state, const char * name)
|
||||||
|
{
|
||||||
|
if (context)
|
||||||
|
context->last_err_code = NIX_OK;
|
||||||
|
try {
|
||||||
|
auto & v = check_value_in(value);
|
||||||
|
assert(v.type() == nix::nAttrs);
|
||||||
|
nix::Symbol s = state->state.symbols.create(name);
|
||||||
|
auto attr = v.attrs()->get(s);
|
||||||
|
if (attr) {
|
||||||
|
nix_gc_incref(nullptr, attr->value);
|
||||||
|
// Note: intentionally NOT calling forceValue() to keep the attribute lazy
|
||||||
|
return as_nix_value_ptr(attr->value);
|
||||||
|
}
|
||||||
|
nix_set_err_msg(context, NIX_ERR_KEY, "missing attribute");
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
NIXC_CATCH_ERRS_NULL
|
||||||
|
}
|
||||||
|
|
||||||
bool nix_has_attr_byname(nix_c_context * context, const nix_value * value, EvalState * state, const char * name)
|
bool nix_has_attr_byname(nix_c_context * context, const nix_value * value, EvalState * state, const char * name)
|
||||||
{
|
{
|
||||||
if (context)
|
if (context)
|
||||||
|
|
@ -389,6 +434,10 @@ nix_get_attr_byidx(nix_c_context * context, nix_value * value, EvalState * state
|
||||||
try {
|
try {
|
||||||
auto & v = check_value_in(value);
|
auto & v = check_value_in(value);
|
||||||
collapse_attrset_layer_chain_if_needed(v, state);
|
collapse_attrset_layer_chain_if_needed(v, state);
|
||||||
|
if (i >= v.attrs()->size()) {
|
||||||
|
nix_set_err_msg(context, NIX_ERR_KEY, "attribute index out of bounds");
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
const nix::Attr & a = (*v.attrs())[i];
|
const nix::Attr & a = (*v.attrs())[i];
|
||||||
*name = state->state.symbols[a.name].c_str();
|
*name = state->state.symbols[a.name].c_str();
|
||||||
nix_gc_incref(nullptr, a.value);
|
nix_gc_incref(nullptr, a.value);
|
||||||
|
|
@ -398,6 +447,27 @@ nix_get_attr_byidx(nix_c_context * context, nix_value * value, EvalState * state
|
||||||
NIXC_CATCH_ERRS_NULL
|
NIXC_CATCH_ERRS_NULL
|
||||||
}
|
}
|
||||||
|
|
||||||
|
nix_value * nix_get_attr_byidx_lazy(
|
||||||
|
nix_c_context * context, nix_value * value, EvalState * state, unsigned int i, const char ** name)
|
||||||
|
{
|
||||||
|
if (context)
|
||||||
|
context->last_err_code = NIX_OK;
|
||||||
|
try {
|
||||||
|
auto & v = check_value_in(value);
|
||||||
|
collapse_attrset_layer_chain_if_needed(v, state);
|
||||||
|
if (i >= v.attrs()->size()) {
|
||||||
|
nix_set_err_msg(context, NIX_ERR_KEY, "attribute index out of bounds (Nix C API contract violation)");
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
const nix::Attr & a = (*v.attrs())[i];
|
||||||
|
*name = state->state.symbols[a.name].c_str();
|
||||||
|
nix_gc_incref(nullptr, a.value);
|
||||||
|
// Note: intentionally NOT calling forceValue() to keep the attribute lazy
|
||||||
|
return as_nix_value_ptr(a.value);
|
||||||
|
}
|
||||||
|
NIXC_CATCH_ERRS_NULL
|
||||||
|
}
|
||||||
|
|
||||||
const char * nix_get_attr_name_byidx(nix_c_context * context, nix_value * value, EvalState * state, unsigned int i)
|
const char * nix_get_attr_name_byidx(nix_c_context * context, nix_value * value, EvalState * state, unsigned int i)
|
||||||
{
|
{
|
||||||
if (context)
|
if (context)
|
||||||
|
|
@ -405,6 +475,10 @@ const char * nix_get_attr_name_byidx(nix_c_context * context, nix_value * value,
|
||||||
try {
|
try {
|
||||||
auto & v = check_value_in(value);
|
auto & v = check_value_in(value);
|
||||||
collapse_attrset_layer_chain_if_needed(v, state);
|
collapse_attrset_layer_chain_if_needed(v, state);
|
||||||
|
if (i >= v.attrs()->size()) {
|
||||||
|
nix_set_err_msg(context, NIX_ERR_KEY, "attribute index out of bounds (Nix C API contract violation)");
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
const nix::Attr & a = (*v.attrs())[i];
|
const nix::Attr & a = (*v.attrs())[i];
|
||||||
return state->state.symbols[a.name].c_str();
|
return state->state.symbols[a.name].c_str();
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -265,10 +265,25 @@ ExternalValue * nix_get_external(nix_c_context * context, nix_value * value);
|
||||||
*/
|
*/
|
||||||
nix_value * nix_get_list_byidx(nix_c_context * context, const nix_value * value, EvalState * state, unsigned int ix);
|
nix_value * nix_get_list_byidx(nix_c_context * context, const nix_value * value, EvalState * state, unsigned int ix);
|
||||||
|
|
||||||
/** @brief Get an attr by name
|
/** @brief Get the ix'th element of a list without forcing evaluation of the element
|
||||||
|
*
|
||||||
|
* Returns the list element without forcing its evaluation, allowing access to lazy values.
|
||||||
|
* The list value itself must already be evaluated.
|
||||||
*
|
*
|
||||||
* Owned by the GC. Use nix_gc_decref when you're done with the pointer
|
* Owned by the GC. Use nix_gc_decref when you're done with the pointer
|
||||||
* @param[out] context Optional, stores error information
|
* @param[out] context Optional, stores error information
|
||||||
|
* @param[in] value Nix value to inspect (must be an evaluated list)
|
||||||
|
* @param[in] state nix evaluator state
|
||||||
|
* @param[in] ix list element to get
|
||||||
|
* @return value, NULL in case of errors
|
||||||
|
*/
|
||||||
|
nix_value *
|
||||||
|
nix_get_list_byidx_lazy(nix_c_context * context, const nix_value * value, EvalState * state, unsigned int ix);
|
||||||
|
|
||||||
|
/** @brief Get an attr by name
|
||||||
|
*
|
||||||
|
* Use nix_gc_decref when you're done with the pointer
|
||||||
|
* @param[out] context Optional, stores error information
|
||||||
* @param[in] value Nix value to inspect
|
* @param[in] value Nix value to inspect
|
||||||
* @param[in] state nix evaluator state
|
* @param[in] state nix evaluator state
|
||||||
* @param[in] name attribute name
|
* @param[in] name attribute name
|
||||||
|
|
@ -276,6 +291,21 @@ nix_value * nix_get_list_byidx(nix_c_context * context, const nix_value * value,
|
||||||
*/
|
*/
|
||||||
nix_value * nix_get_attr_byname(nix_c_context * context, const nix_value * value, EvalState * state, const char * name);
|
nix_value * nix_get_attr_byname(nix_c_context * context, const nix_value * value, EvalState * state, const char * name);
|
||||||
|
|
||||||
|
/** @brief Get an attribute value by attribute name, without forcing evaluation of the attribute's value
|
||||||
|
*
|
||||||
|
* Returns the attribute value without forcing its evaluation, allowing access to lazy values.
|
||||||
|
* The attribute set value itself must already be evaluated.
|
||||||
|
*
|
||||||
|
* Use nix_gc_decref when you're done with the pointer
|
||||||
|
* @param[out] context Optional, stores error information
|
||||||
|
* @param[in] value Nix value to inspect (must be an evaluated attribute set)
|
||||||
|
* @param[in] state nix evaluator state
|
||||||
|
* @param[in] name attribute name
|
||||||
|
* @return value, NULL in case of errors
|
||||||
|
*/
|
||||||
|
nix_value *
|
||||||
|
nix_get_attr_byname_lazy(nix_c_context * context, const nix_value * value, EvalState * state, const char * name);
|
||||||
|
|
||||||
/** @brief Check if an attribute name exists on a value
|
/** @brief Check if an attribute name exists on a value
|
||||||
* @param[out] context Optional, stores error information
|
* @param[out] context Optional, stores error information
|
||||||
* @param[in] value Nix value to inspect
|
* @param[in] value Nix value to inspect
|
||||||
|
|
@ -285,11 +315,21 @@ nix_value * nix_get_attr_byname(nix_c_context * context, const nix_value * value
|
||||||
*/
|
*/
|
||||||
bool nix_has_attr_byname(nix_c_context * context, const nix_value * value, EvalState * state, const char * name);
|
bool nix_has_attr_byname(nix_c_context * context, const nix_value * value, EvalState * state, const char * name);
|
||||||
|
|
||||||
/** @brief Get an attribute by index in the sorted bindings
|
/** @brief Get an attribute by index
|
||||||
*
|
*
|
||||||
* Also gives you the name.
|
* Also gives you the name.
|
||||||
*
|
*
|
||||||
* Owned by the GC. Use nix_gc_decref when you're done with the pointer
|
* Attributes are returned in an unspecified order which is NOT suitable for
|
||||||
|
* reproducible operations. In Nix's domain, reproducibility is paramount. The caller
|
||||||
|
* is responsible for sorting the attributes or storing them in an ordered map to
|
||||||
|
* ensure deterministic behavior in your application.
|
||||||
|
*
|
||||||
|
* @note When Nix does sort attributes, which it does for virtually all intermediate
|
||||||
|
* operations and outputs, it uses byte-wise lexicographic order (equivalent to
|
||||||
|
* lexicographic order by Unicode scalar value for valid UTF-8). We recommend
|
||||||
|
* applying this same ordering for consistency.
|
||||||
|
*
|
||||||
|
* Use nix_gc_decref when you're done with the pointer
|
||||||
* @param[out] context Optional, stores error information
|
* @param[out] context Optional, stores error information
|
||||||
* @param[in] value Nix value to inspect
|
* @param[in] value Nix value to inspect
|
||||||
* @param[in] state nix evaluator state
|
* @param[in] state nix evaluator state
|
||||||
|
|
@ -300,9 +340,47 @@ bool nix_has_attr_byname(nix_c_context * context, const nix_value * value, EvalS
|
||||||
nix_value *
|
nix_value *
|
||||||
nix_get_attr_byidx(nix_c_context * context, nix_value * value, EvalState * state, unsigned int i, const char ** name);
|
nix_get_attr_byidx(nix_c_context * context, nix_value * value, EvalState * state, unsigned int i, const char ** name);
|
||||||
|
|
||||||
/** @brief Get an attribute name by index in the sorted bindings
|
/** @brief Get an attribute by index, without forcing evaluation of the attribute's value
|
||||||
*
|
*
|
||||||
* Useful when you want the name but want to avoid evaluation.
|
* Also gives you the name.
|
||||||
|
*
|
||||||
|
* Returns the attribute value without forcing its evaluation, allowing access to lazy values.
|
||||||
|
* The attribute set value itself must already have been evaluated.
|
||||||
|
*
|
||||||
|
* Attributes are returned in an unspecified order which is NOT suitable for
|
||||||
|
* reproducible operations. In Nix's domain, reproducibility is paramount. The caller
|
||||||
|
* is responsible for sorting the attributes or storing them in an ordered map to
|
||||||
|
* ensure deterministic behavior in your application.
|
||||||
|
*
|
||||||
|
* @note When Nix does sort attributes, which it does for virtually all intermediate
|
||||||
|
* operations and outputs, it uses byte-wise lexicographic order (equivalent to
|
||||||
|
* lexicographic order by Unicode scalar value for valid UTF-8). We recommend
|
||||||
|
* applying this same ordering for consistency.
|
||||||
|
*
|
||||||
|
* Use nix_gc_decref when you're done with the pointer
|
||||||
|
* @param[out] context Optional, stores error information
|
||||||
|
* @param[in] value Nix value to inspect (must be an evaluated attribute set)
|
||||||
|
* @param[in] state nix evaluator state
|
||||||
|
* @param[in] i attribute index
|
||||||
|
* @param[out] name will store a pointer to the attribute name
|
||||||
|
* @return value, NULL in case of errors
|
||||||
|
*/
|
||||||
|
nix_value * nix_get_attr_byidx_lazy(
|
||||||
|
nix_c_context * context, nix_value * value, EvalState * state, unsigned int i, const char ** name);
|
||||||
|
|
||||||
|
/** @brief Get an attribute name by index
|
||||||
|
*
|
||||||
|
* Returns the attribute name without forcing evaluation of the attribute's value.
|
||||||
|
*
|
||||||
|
* Attributes are returned in an unspecified order which is NOT suitable for
|
||||||
|
* reproducible operations. In Nix's domain, reproducibility is paramount. The caller
|
||||||
|
* is responsible for sorting the attributes or storing them in an ordered map to
|
||||||
|
* ensure deterministic behavior in your application.
|
||||||
|
*
|
||||||
|
* @note When Nix does sort attributes, which it does for virtually all intermediate
|
||||||
|
* operations and outputs, it uses byte-wise lexicographic order (equivalent to
|
||||||
|
* lexicographic order by Unicode scalar value for valid UTF-8). We recommend
|
||||||
|
* applying this same ordering for consistency.
|
||||||
*
|
*
|
||||||
* Owned by the nix EvalState
|
* Owned by the nix EvalState
|
||||||
* @param[out] context Optional, stores error information
|
* @param[out] context Optional, stores error information
|
||||||
|
|
|
||||||
|
|
@ -423,6 +423,55 @@ TEST_F(nix_api_expr_test, nix_expr_primop_bad_return_thunk)
|
||||||
ASSERT_THAT(nix_err_msg(nullptr, ctx, nullptr), testing::HasSubstr("badReturnThunk"));
|
ASSERT_THAT(nix_err_msg(nullptr, ctx, nullptr), testing::HasSubstr("badReturnThunk"));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static void primop_with_nix_err_key(
|
||||||
|
void * user_data, nix_c_context * context, EvalState * state, nix_value ** args, nix_value * ret)
|
||||||
|
{
|
||||||
|
nix_set_err_msg(context, NIX_ERR_KEY, "Test error from primop");
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_F(nix_api_expr_test, nix_expr_primop_nix_err_key_conversion)
|
||||||
|
{
|
||||||
|
// Test that NIX_ERR_KEY from a custom primop gets converted to a generic EvalError
|
||||||
|
//
|
||||||
|
// RATIONALE: NIX_ERR_KEY must not be propagated from custom primops because it would
|
||||||
|
// create semantic confusion. NIX_ERR_KEY indicates missing keys/indices in C API functions
|
||||||
|
// (like nix_get_attr_byname, nix_get_list_byidx). If custom primops could return NIX_ERR_KEY,
|
||||||
|
// an evaluation error would be indistinguishable from an actual missing attribute.
|
||||||
|
//
|
||||||
|
// For example, if nix_get_attr_byname returned NIX_ERR_KEY when the attribute is present
|
||||||
|
// but the value evaluation fails, callers expecting NIX_ERR_KEY to mean "missing attribute"
|
||||||
|
// would incorrectly handle evaluation failures as missing attributes. In places where
|
||||||
|
// missing attributes are tolerated (like optional attributes), this would cause the
|
||||||
|
// program to continue after swallowing the error, leading to silent failures.
|
||||||
|
PrimOp * primop = nix_alloc_primop(
|
||||||
|
ctx, primop_with_nix_err_key, 1, "testErrorPrimop", nullptr, "a test primop that sets NIX_ERR_KEY", nullptr);
|
||||||
|
assert_ctx_ok();
|
||||||
|
nix_value * primopValue = nix_alloc_value(ctx, state);
|
||||||
|
assert_ctx_ok();
|
||||||
|
nix_init_primop(ctx, primopValue, primop);
|
||||||
|
assert_ctx_ok();
|
||||||
|
|
||||||
|
nix_value * arg = nix_alloc_value(ctx, state);
|
||||||
|
assert_ctx_ok();
|
||||||
|
nix_init_int(ctx, arg, 42);
|
||||||
|
assert_ctx_ok();
|
||||||
|
|
||||||
|
nix_value * result = nix_alloc_value(ctx, state);
|
||||||
|
assert_ctx_ok();
|
||||||
|
nix_value_call(ctx, state, primopValue, arg, result);
|
||||||
|
|
||||||
|
// Verify that NIX_ERR_KEY gets converted to NIX_ERR_NIX_ERROR (generic evaluation error)
|
||||||
|
ASSERT_EQ(nix_err_code(ctx), NIX_ERR_NIX_ERROR);
|
||||||
|
ASSERT_THAT(nix_err_msg(nullptr, ctx, nullptr), testing::HasSubstr("Error from custom function"));
|
||||||
|
ASSERT_THAT(nix_err_msg(nullptr, ctx, nullptr), testing::HasSubstr("Test error from primop"));
|
||||||
|
ASSERT_THAT(nix_err_msg(nullptr, ctx, nullptr), testing::HasSubstr("testErrorPrimop"));
|
||||||
|
|
||||||
|
// Clean up
|
||||||
|
nix_gc_decref(ctx, primopValue);
|
||||||
|
nix_gc_decref(ctx, arg);
|
||||||
|
nix_gc_decref(ctx, result);
|
||||||
|
}
|
||||||
|
|
||||||
TEST_F(nix_api_expr_test, nix_value_call_multi_no_args)
|
TEST_F(nix_api_expr_test, nix_value_call_multi_no_args)
|
||||||
{
|
{
|
||||||
nix_value * n = nix_alloc_value(ctx, state);
|
nix_value * n = nix_alloc_value(ctx, state);
|
||||||
|
|
|
||||||
|
|
@ -162,6 +162,114 @@ TEST_F(nix_api_expr_test, nix_build_and_init_list)
|
||||||
nix_gc_decref(ctx, intValue);
|
nix_gc_decref(ctx, intValue);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
TEST_F(nix_api_expr_test, nix_get_list_byidx_large_indices)
|
||||||
|
{
|
||||||
|
// Create a small list to test extremely large out-of-bounds access
|
||||||
|
ListBuilder * builder = nix_make_list_builder(ctx, state, 2);
|
||||||
|
nix_value * intValue = nix_alloc_value(ctx, state);
|
||||||
|
nix_init_int(ctx, intValue, 42);
|
||||||
|
nix_list_builder_insert(ctx, builder, 0, intValue);
|
||||||
|
nix_list_builder_insert(ctx, builder, 1, intValue);
|
||||||
|
nix_make_list(ctx, builder, value);
|
||||||
|
nix_list_builder_free(builder);
|
||||||
|
|
||||||
|
// Test extremely large indices that would definitely crash without bounds checking
|
||||||
|
ASSERT_EQ(nullptr, nix_get_list_byidx(ctx, value, state, 1000000));
|
||||||
|
ASSERT_EQ(NIX_ERR_KEY, nix_err_code(ctx));
|
||||||
|
ASSERT_EQ(nullptr, nix_get_list_byidx(ctx, value, state, UINT_MAX / 2));
|
||||||
|
ASSERT_EQ(NIX_ERR_KEY, nix_err_code(ctx));
|
||||||
|
ASSERT_EQ(nullptr, nix_get_list_byidx(ctx, value, state, UINT_MAX / 2 + 1000000));
|
||||||
|
ASSERT_EQ(NIX_ERR_KEY, nix_err_code(ctx));
|
||||||
|
|
||||||
|
// Clean up
|
||||||
|
nix_gc_decref(ctx, intValue);
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_F(nix_api_expr_test, nix_get_list_byidx_lazy)
|
||||||
|
{
|
||||||
|
// Create a list with a throwing lazy element, an already-evaluated int, and a lazy function call
|
||||||
|
|
||||||
|
// 1. Throwing lazy element - create a function application thunk that will throw when forced
|
||||||
|
nix_value * throwingFn = nix_alloc_value(ctx, state);
|
||||||
|
nix_value * throwingValue = nix_alloc_value(ctx, state);
|
||||||
|
|
||||||
|
nix_expr_eval_from_string(
|
||||||
|
ctx,
|
||||||
|
state,
|
||||||
|
R"(
|
||||||
|
_: throw "This should not be evaluated by the lazy accessor"
|
||||||
|
)",
|
||||||
|
"<test>",
|
||||||
|
throwingFn);
|
||||||
|
assert_ctx_ok();
|
||||||
|
|
||||||
|
nix_init_apply(ctx, throwingValue, throwingFn, throwingFn);
|
||||||
|
assert_ctx_ok();
|
||||||
|
|
||||||
|
// 2. Already evaluated int (not lazy)
|
||||||
|
nix_value * intValue = nix_alloc_value(ctx, state);
|
||||||
|
nix_init_int(ctx, intValue, 42);
|
||||||
|
assert_ctx_ok();
|
||||||
|
|
||||||
|
// 3. Lazy function application that would compute increment 5 = 6
|
||||||
|
nix_value * lazyApply = nix_alloc_value(ctx, state);
|
||||||
|
nix_value * incrementFn = nix_alloc_value(ctx, state);
|
||||||
|
nix_value * argFive = nix_alloc_value(ctx, state);
|
||||||
|
|
||||||
|
nix_expr_eval_from_string(ctx, state, "x: x + 1", "<test>", incrementFn);
|
||||||
|
assert_ctx_ok();
|
||||||
|
nix_init_int(ctx, argFive, 5);
|
||||||
|
|
||||||
|
// Create a lazy application: (x: x + 1) 5
|
||||||
|
nix_init_apply(ctx, lazyApply, incrementFn, argFive);
|
||||||
|
assert_ctx_ok();
|
||||||
|
|
||||||
|
ListBuilder * builder = nix_make_list_builder(ctx, state, 3);
|
||||||
|
nix_list_builder_insert(ctx, builder, 0, throwingValue);
|
||||||
|
nix_list_builder_insert(ctx, builder, 1, intValue);
|
||||||
|
nix_list_builder_insert(ctx, builder, 2, lazyApply);
|
||||||
|
nix_make_list(ctx, builder, value);
|
||||||
|
nix_list_builder_free(builder);
|
||||||
|
|
||||||
|
// Test 1: Lazy accessor should return the throwing element without forcing evaluation
|
||||||
|
nix_value * lazyThrowingElement = nix_get_list_byidx_lazy(ctx, value, state, 0);
|
||||||
|
assert_ctx_ok();
|
||||||
|
ASSERT_NE(nullptr, lazyThrowingElement);
|
||||||
|
|
||||||
|
// Verify the element is still lazy by checking that forcing it throws
|
||||||
|
nix_value_force(ctx, state, lazyThrowingElement);
|
||||||
|
assert_ctx_err();
|
||||||
|
ASSERT_THAT(
|
||||||
|
nix_err_msg(nullptr, ctx, nullptr), testing::HasSubstr("This should not be evaluated by the lazy accessor"));
|
||||||
|
|
||||||
|
// Test 2: Lazy accessor should return the already-evaluated int
|
||||||
|
nix_value * intElement = nix_get_list_byidx_lazy(ctx, value, state, 1);
|
||||||
|
assert_ctx_ok();
|
||||||
|
ASSERT_NE(nullptr, intElement);
|
||||||
|
ASSERT_EQ(42, nix_get_int(ctx, intElement));
|
||||||
|
|
||||||
|
// Test 3: Lazy accessor should return the lazy function application without forcing
|
||||||
|
nix_value * lazyFunctionElement = nix_get_list_byidx_lazy(ctx, value, state, 2);
|
||||||
|
assert_ctx_ok();
|
||||||
|
ASSERT_NE(nullptr, lazyFunctionElement);
|
||||||
|
|
||||||
|
// Force the lazy function application - should compute 5 + 1 = 6
|
||||||
|
nix_value_force(ctx, state, lazyFunctionElement);
|
||||||
|
assert_ctx_ok();
|
||||||
|
ASSERT_EQ(6, nix_get_int(ctx, lazyFunctionElement));
|
||||||
|
|
||||||
|
// Clean up
|
||||||
|
nix_gc_decref(ctx, throwingFn);
|
||||||
|
nix_gc_decref(ctx, throwingValue);
|
||||||
|
nix_gc_decref(ctx, intValue);
|
||||||
|
nix_gc_decref(ctx, lazyApply);
|
||||||
|
nix_gc_decref(ctx, incrementFn);
|
||||||
|
nix_gc_decref(ctx, argFive);
|
||||||
|
nix_gc_decref(ctx, lazyThrowingElement);
|
||||||
|
nix_gc_decref(ctx, intElement);
|
||||||
|
nix_gc_decref(ctx, lazyFunctionElement);
|
||||||
|
}
|
||||||
|
|
||||||
TEST_F(nix_api_expr_test, nix_build_and_init_attr_invalid)
|
TEST_F(nix_api_expr_test, nix_build_and_init_attr_invalid)
|
||||||
{
|
{
|
||||||
ASSERT_EQ(nullptr, nix_get_attr_byname(ctx, nullptr, state, 0));
|
ASSERT_EQ(nullptr, nix_get_attr_byname(ctx, nullptr, state, 0));
|
||||||
|
|
@ -244,6 +352,225 @@ TEST_F(nix_api_expr_test, nix_build_and_init_attr)
|
||||||
free(out_name);
|
free(out_name);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
TEST_F(nix_api_expr_test, nix_get_attr_byidx_large_indices)
|
||||||
|
{
|
||||||
|
// Create a small attribute set to test extremely large out-of-bounds access
|
||||||
|
const char ** out_name = (const char **) malloc(sizeof(char *));
|
||||||
|
BindingsBuilder * builder = nix_make_bindings_builder(ctx, state, 2);
|
||||||
|
nix_value * intValue = nix_alloc_value(ctx, state);
|
||||||
|
nix_init_int(ctx, intValue, 42);
|
||||||
|
nix_bindings_builder_insert(ctx, builder, "test", intValue);
|
||||||
|
nix_make_attrs(ctx, value, builder);
|
||||||
|
nix_bindings_builder_free(builder);
|
||||||
|
|
||||||
|
// Test extremely large indices that would definitely crash without bounds checking
|
||||||
|
ASSERT_EQ(nullptr, nix_get_attr_byidx(ctx, value, state, 1000000, out_name));
|
||||||
|
ASSERT_EQ(NIX_ERR_KEY, nix_err_code(ctx));
|
||||||
|
ASSERT_EQ(nullptr, nix_get_attr_byidx(ctx, value, state, UINT_MAX / 2, out_name));
|
||||||
|
ASSERT_EQ(NIX_ERR_KEY, nix_err_code(ctx));
|
||||||
|
ASSERT_EQ(nullptr, nix_get_attr_byidx(ctx, value, state, UINT_MAX / 2 + 1000000, out_name));
|
||||||
|
ASSERT_EQ(NIX_ERR_KEY, nix_err_code(ctx));
|
||||||
|
|
||||||
|
// Test nix_get_attr_name_byidx with large indices too
|
||||||
|
ASSERT_EQ(nullptr, nix_get_attr_name_byidx(ctx, value, state, 1000000));
|
||||||
|
ASSERT_EQ(NIX_ERR_KEY, nix_err_code(ctx));
|
||||||
|
ASSERT_EQ(nullptr, nix_get_attr_name_byidx(ctx, value, state, UINT_MAX / 2));
|
||||||
|
ASSERT_EQ(NIX_ERR_KEY, nix_err_code(ctx));
|
||||||
|
ASSERT_EQ(nullptr, nix_get_attr_name_byidx(ctx, value, state, UINT_MAX / 2 + 1000000));
|
||||||
|
ASSERT_EQ(NIX_ERR_KEY, nix_err_code(ctx));
|
||||||
|
|
||||||
|
// Clean up
|
||||||
|
nix_gc_decref(ctx, intValue);
|
||||||
|
free(out_name);
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_F(nix_api_expr_test, nix_get_attr_byname_lazy)
|
||||||
|
{
|
||||||
|
// Create an attribute set with a throwing lazy attribute, an already-evaluated int, and a lazy function call
|
||||||
|
|
||||||
|
// 1. Throwing lazy element - create a function application thunk that will throw when forced
|
||||||
|
nix_value * throwingFn = nix_alloc_value(ctx, state);
|
||||||
|
nix_value * throwingValue = nix_alloc_value(ctx, state);
|
||||||
|
|
||||||
|
nix_expr_eval_from_string(
|
||||||
|
ctx,
|
||||||
|
state,
|
||||||
|
R"(
|
||||||
|
_: throw "This should not be evaluated by the lazy accessor"
|
||||||
|
)",
|
||||||
|
"<test>",
|
||||||
|
throwingFn);
|
||||||
|
assert_ctx_ok();
|
||||||
|
|
||||||
|
nix_init_apply(ctx, throwingValue, throwingFn, throwingFn);
|
||||||
|
assert_ctx_ok();
|
||||||
|
|
||||||
|
// 2. Already evaluated int (not lazy)
|
||||||
|
nix_value * intValue = nix_alloc_value(ctx, state);
|
||||||
|
nix_init_int(ctx, intValue, 42);
|
||||||
|
assert_ctx_ok();
|
||||||
|
|
||||||
|
// 3. Lazy function application that would compute increment 7 = 8
|
||||||
|
nix_value * lazyApply = nix_alloc_value(ctx, state);
|
||||||
|
nix_value * incrementFn = nix_alloc_value(ctx, state);
|
||||||
|
nix_value * argSeven = nix_alloc_value(ctx, state);
|
||||||
|
|
||||||
|
nix_expr_eval_from_string(ctx, state, "x: x + 1", "<test>", incrementFn);
|
||||||
|
assert_ctx_ok();
|
||||||
|
nix_init_int(ctx, argSeven, 7);
|
||||||
|
|
||||||
|
// Create a lazy application: (x: x + 1) 7
|
||||||
|
nix_init_apply(ctx, lazyApply, incrementFn, argSeven);
|
||||||
|
assert_ctx_ok();
|
||||||
|
|
||||||
|
BindingsBuilder * builder = nix_make_bindings_builder(ctx, state, 3);
|
||||||
|
nix_bindings_builder_insert(ctx, builder, "throwing", throwingValue);
|
||||||
|
nix_bindings_builder_insert(ctx, builder, "normal", intValue);
|
||||||
|
nix_bindings_builder_insert(ctx, builder, "lazy", lazyApply);
|
||||||
|
nix_make_attrs(ctx, value, builder);
|
||||||
|
nix_bindings_builder_free(builder);
|
||||||
|
|
||||||
|
// Test 1: Lazy accessor should return the throwing attribute without forcing evaluation
|
||||||
|
nix_value * lazyThrowingAttr = nix_get_attr_byname_lazy(ctx, value, state, "throwing");
|
||||||
|
assert_ctx_ok();
|
||||||
|
ASSERT_NE(nullptr, lazyThrowingAttr);
|
||||||
|
|
||||||
|
// Verify the attribute is still lazy by checking that forcing it throws
|
||||||
|
nix_value_force(ctx, state, lazyThrowingAttr);
|
||||||
|
assert_ctx_err();
|
||||||
|
ASSERT_THAT(
|
||||||
|
nix_err_msg(nullptr, ctx, nullptr), testing::HasSubstr("This should not be evaluated by the lazy accessor"));
|
||||||
|
|
||||||
|
// Test 2: Lazy accessor should return the already-evaluated int
|
||||||
|
nix_value * intAttr = nix_get_attr_byname_lazy(ctx, value, state, "normal");
|
||||||
|
assert_ctx_ok();
|
||||||
|
ASSERT_NE(nullptr, intAttr);
|
||||||
|
ASSERT_EQ(42, nix_get_int(ctx, intAttr));
|
||||||
|
|
||||||
|
// Test 3: Lazy accessor should return the lazy function application without forcing
|
||||||
|
nix_value * lazyFunctionAttr = nix_get_attr_byname_lazy(ctx, value, state, "lazy");
|
||||||
|
assert_ctx_ok();
|
||||||
|
ASSERT_NE(nullptr, lazyFunctionAttr);
|
||||||
|
|
||||||
|
// Force the lazy function application - should compute 7 + 1 = 8
|
||||||
|
nix_value_force(ctx, state, lazyFunctionAttr);
|
||||||
|
assert_ctx_ok();
|
||||||
|
ASSERT_EQ(8, nix_get_int(ctx, lazyFunctionAttr));
|
||||||
|
|
||||||
|
// Test 4: Missing attribute should return NULL with NIX_ERR_KEY
|
||||||
|
nix_value * missingAttr = nix_get_attr_byname_lazy(ctx, value, state, "nonexistent");
|
||||||
|
ASSERT_EQ(nullptr, missingAttr);
|
||||||
|
ASSERT_EQ(NIX_ERR_KEY, nix_err_code(ctx));
|
||||||
|
|
||||||
|
// Clean up
|
||||||
|
nix_gc_decref(ctx, throwingFn);
|
||||||
|
nix_gc_decref(ctx, throwingValue);
|
||||||
|
nix_gc_decref(ctx, intValue);
|
||||||
|
nix_gc_decref(ctx, lazyApply);
|
||||||
|
nix_gc_decref(ctx, incrementFn);
|
||||||
|
nix_gc_decref(ctx, argSeven);
|
||||||
|
nix_gc_decref(ctx, lazyThrowingAttr);
|
||||||
|
nix_gc_decref(ctx, intAttr);
|
||||||
|
nix_gc_decref(ctx, lazyFunctionAttr);
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_F(nix_api_expr_test, nix_get_attr_byidx_lazy)
|
||||||
|
{
|
||||||
|
// Create an attribute set with a throwing lazy attribute, an already-evaluated int, and a lazy function call
|
||||||
|
|
||||||
|
// 1. Throwing lazy element - create a function application thunk that will throw when forced
|
||||||
|
nix_value * throwingFn = nix_alloc_value(ctx, state);
|
||||||
|
nix_value * throwingValue = nix_alloc_value(ctx, state);
|
||||||
|
|
||||||
|
nix_expr_eval_from_string(
|
||||||
|
ctx,
|
||||||
|
state,
|
||||||
|
R"(
|
||||||
|
_: throw "This should not be evaluated by the lazy accessor"
|
||||||
|
)",
|
||||||
|
"<test>",
|
||||||
|
throwingFn);
|
||||||
|
assert_ctx_ok();
|
||||||
|
|
||||||
|
nix_init_apply(ctx, throwingValue, throwingFn, throwingFn);
|
||||||
|
assert_ctx_ok();
|
||||||
|
|
||||||
|
// 2. Already evaluated int (not lazy)
|
||||||
|
nix_value * intValue = nix_alloc_value(ctx, state);
|
||||||
|
nix_init_int(ctx, intValue, 99);
|
||||||
|
assert_ctx_ok();
|
||||||
|
|
||||||
|
// 3. Lazy function application that would compute increment 10 = 11
|
||||||
|
nix_value * lazyApply = nix_alloc_value(ctx, state);
|
||||||
|
nix_value * incrementFn = nix_alloc_value(ctx, state);
|
||||||
|
nix_value * argTen = nix_alloc_value(ctx, state);
|
||||||
|
|
||||||
|
nix_expr_eval_from_string(ctx, state, "x: x + 1", "<test>", incrementFn);
|
||||||
|
assert_ctx_ok();
|
||||||
|
nix_init_int(ctx, argTen, 10);
|
||||||
|
|
||||||
|
// Create a lazy application: (x: x + 1) 10
|
||||||
|
nix_init_apply(ctx, lazyApply, incrementFn, argTen);
|
||||||
|
assert_ctx_ok();
|
||||||
|
|
||||||
|
BindingsBuilder * builder = nix_make_bindings_builder(ctx, state, 3);
|
||||||
|
nix_bindings_builder_insert(ctx, builder, "a_throwing", throwingValue);
|
||||||
|
nix_bindings_builder_insert(ctx, builder, "b_normal", intValue);
|
||||||
|
nix_bindings_builder_insert(ctx, builder, "c_lazy", lazyApply);
|
||||||
|
nix_make_attrs(ctx, value, builder);
|
||||||
|
nix_bindings_builder_free(builder);
|
||||||
|
|
||||||
|
// Proper usage: first get the size and gather all attributes into a map
|
||||||
|
unsigned int attrCount = nix_get_attrs_size(ctx, value);
|
||||||
|
assert_ctx_ok();
|
||||||
|
ASSERT_EQ(3u, attrCount);
|
||||||
|
|
||||||
|
// Gather all attributes into a map (proper contract usage)
|
||||||
|
std::map<std::string, nix_value *> attrMap;
|
||||||
|
const char * name;
|
||||||
|
|
||||||
|
for (unsigned int i = 0; i < attrCount; i++) {
|
||||||
|
nix_value * attr = nix_get_attr_byidx_lazy(ctx, value, state, i, &name);
|
||||||
|
assert_ctx_ok();
|
||||||
|
ASSERT_NE(nullptr, attr);
|
||||||
|
attrMap[std::string(name)] = attr;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Now test the gathered attributes
|
||||||
|
ASSERT_EQ(3u, attrMap.size());
|
||||||
|
ASSERT_TRUE(attrMap.count("a_throwing"));
|
||||||
|
ASSERT_TRUE(attrMap.count("b_normal"));
|
||||||
|
ASSERT_TRUE(attrMap.count("c_lazy"));
|
||||||
|
|
||||||
|
// Test 1: Throwing attribute should be lazy
|
||||||
|
nix_value * throwingAttr = attrMap["a_throwing"];
|
||||||
|
nix_value_force(ctx, state, throwingAttr);
|
||||||
|
assert_ctx_err();
|
||||||
|
ASSERT_THAT(
|
||||||
|
nix_err_msg(nullptr, ctx, nullptr), testing::HasSubstr("This should not be evaluated by the lazy accessor"));
|
||||||
|
|
||||||
|
// Test 2: Normal attribute should be already evaluated
|
||||||
|
nix_value * normalAttr = attrMap["b_normal"];
|
||||||
|
ASSERT_EQ(99, nix_get_int(ctx, normalAttr));
|
||||||
|
|
||||||
|
// Test 3: Lazy function should compute when forced
|
||||||
|
nix_value * lazyAttr = attrMap["c_lazy"];
|
||||||
|
nix_value_force(ctx, state, lazyAttr);
|
||||||
|
assert_ctx_ok();
|
||||||
|
ASSERT_EQ(11, nix_get_int(ctx, lazyAttr));
|
||||||
|
|
||||||
|
// Clean up
|
||||||
|
nix_gc_decref(ctx, throwingFn);
|
||||||
|
nix_gc_decref(ctx, throwingValue);
|
||||||
|
nix_gc_decref(ctx, intValue);
|
||||||
|
nix_gc_decref(ctx, lazyApply);
|
||||||
|
nix_gc_decref(ctx, incrementFn);
|
||||||
|
nix_gc_decref(ctx, argTen);
|
||||||
|
for (auto & pair : attrMap) {
|
||||||
|
nix_gc_decref(ctx, pair.second);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
TEST_F(nix_api_expr_test, nix_value_init)
|
TEST_F(nix_api_expr_test, nix_value_init)
|
||||||
{
|
{
|
||||||
// Setup
|
// Setup
|
||||||
|
|
|
||||||
|
|
@ -53,7 +53,7 @@ extern "C" {
|
||||||
* - NIX_OK: No error occurred (0)
|
* - NIX_OK: No error occurred (0)
|
||||||
* - NIX_ERR_UNKNOWN: An unknown error occurred (-1)
|
* - NIX_ERR_UNKNOWN: An unknown error occurred (-1)
|
||||||
* - NIX_ERR_OVERFLOW: An overflow error occurred (-2)
|
* - NIX_ERR_OVERFLOW: An overflow error occurred (-2)
|
||||||
* - NIX_ERR_KEY: A key error occurred (-3)
|
* - NIX_ERR_KEY: A key/index access error occurred in C API functions (-3)
|
||||||
* - NIX_ERR_NIX_ERROR: A generic Nix error occurred (-4)
|
* - NIX_ERR_NIX_ERROR: A generic Nix error occurred (-4)
|
||||||
*/
|
*/
|
||||||
enum nix_err {
|
enum nix_err {
|
||||||
|
|
@ -83,10 +83,21 @@ enum nix_err {
|
||||||
NIX_ERR_OVERFLOW = -2,
|
NIX_ERR_OVERFLOW = -2,
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief A key error occurred.
|
* @brief A key/index access error occurred in C API functions.
|
||||||
*
|
*
|
||||||
* This error code is returned when a key error occurred during the function
|
* This error code is returned when accessing a key, index, or identifier that
|
||||||
* execution.
|
* does not exist in C API functions. Common scenarios include:
|
||||||
|
* - Setting keys that don't exist (nix_setting_get, nix_setting_set)
|
||||||
|
* - List indices that are out of bounds (nix_get_list_byidx*)
|
||||||
|
* - Attribute names that don't exist (nix_get_attr_byname*)
|
||||||
|
* - Attribute indices that are out of bounds (nix_get_attr_byidx*, nix_get_attr_name_byidx)
|
||||||
|
*
|
||||||
|
* This error typically indicates incorrect usage or assumptions about data structure
|
||||||
|
* contents, rather than internal Nix evaluation errors.
|
||||||
|
*
|
||||||
|
* @note This error code should ONLY be returned by C API functions themselves,
|
||||||
|
* not by underlying Nix evaluation. For example, evaluating `{}.foo` in Nix
|
||||||
|
* will throw a normal error (NIX_ERR_NIX_ERROR), not NIX_ERR_KEY.
|
||||||
*/
|
*/
|
||||||
NIX_ERR_KEY = -3,
|
NIX_ERR_KEY = -3,
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -108,8 +108,6 @@ struct CmdWhyDepends : SourceExprCommand, MixOperateOnOptions
|
||||||
auto dependencyPath = *optDependencyPath;
|
auto dependencyPath = *optDependencyPath;
|
||||||
auto dependencyPathHash = dependencyPath.hashPart();
|
auto dependencyPathHash = dependencyPath.hashPart();
|
||||||
|
|
||||||
auto accessor = store->getFSAccessor();
|
|
||||||
|
|
||||||
auto const inf = std::numeric_limits<size_t>::max();
|
auto const inf = std::numeric_limits<size_t>::max();
|
||||||
|
|
||||||
struct Node
|
struct Node
|
||||||
|
|
@ -172,8 +170,6 @@ struct CmdWhyDepends : SourceExprCommand, MixOperateOnOptions
|
||||||
{};
|
{};
|
||||||
|
|
||||||
printNode = [&](Node & node, const std::string & firstPad, const std::string & tailPad) {
|
printNode = [&](Node & node, const std::string & firstPad, const std::string & tailPad) {
|
||||||
CanonPath pathS(node.path.to_string());
|
|
||||||
|
|
||||||
assert(node.dist != inf);
|
assert(node.dist != inf);
|
||||||
if (precise) {
|
if (precise) {
|
||||||
logger->cout(
|
logger->cout(
|
||||||
|
|
@ -181,7 +177,7 @@ struct CmdWhyDepends : SourceExprCommand, MixOperateOnOptions
|
||||||
firstPad,
|
firstPad,
|
||||||
node.visited ? "\e[38;5;244m" : "",
|
node.visited ? "\e[38;5;244m" : "",
|
||||||
firstPad != "" ? "→ " : "",
|
firstPad != "" ? "→ " : "",
|
||||||
pathS.abs());
|
store->printStorePath(node.path));
|
||||||
}
|
}
|
||||||
|
|
||||||
if (node.path == dependencyPath && !all && packagePath != dependencyPath)
|
if (node.path == dependencyPath && !all && packagePath != dependencyPath)
|
||||||
|
|
@ -211,13 +207,13 @@ struct CmdWhyDepends : SourceExprCommand, MixOperateOnOptions
|
||||||
contain the reference. */
|
contain the reference. */
|
||||||
std::map<std::string, Strings> hits;
|
std::map<std::string, Strings> hits;
|
||||||
|
|
||||||
std::function<void(const CanonPath &)> visitPath;
|
auto accessor = store->getFSAccessor(node.path);
|
||||||
|
|
||||||
visitPath = [&](const CanonPath & p) {
|
auto visitPath = [&](this auto && recur, const CanonPath & p) -> void {
|
||||||
auto st = accessor->maybeLstat(p);
|
auto st = accessor->maybeLstat(p);
|
||||||
assert(st);
|
assert(st);
|
||||||
|
|
||||||
auto p2 = p == pathS ? "/" : p.abs().substr(pathS.abs().size() + 1);
|
auto p2 = p.isRoot() ? p.abs() : p.rel();
|
||||||
|
|
||||||
auto getColour = [&](const std::string & hash) {
|
auto getColour = [&](const std::string & hash) {
|
||||||
return hash == dependencyPathHash ? ANSI_GREEN : ANSI_BLUE;
|
return hash == dependencyPathHash ? ANSI_GREEN : ANSI_BLUE;
|
||||||
|
|
@ -226,7 +222,7 @@ struct CmdWhyDepends : SourceExprCommand, MixOperateOnOptions
|
||||||
if (st->type == SourceAccessor::Type::tDirectory) {
|
if (st->type == SourceAccessor::Type::tDirectory) {
|
||||||
auto names = accessor->readDirectory(p);
|
auto names = accessor->readDirectory(p);
|
||||||
for (auto & [name, type] : names)
|
for (auto & [name, type] : names)
|
||||||
visitPath(p / name);
|
recur(p / name);
|
||||||
}
|
}
|
||||||
|
|
||||||
else if (st->type == SourceAccessor::Type::tRegular) {
|
else if (st->type == SourceAccessor::Type::tRegular) {
|
||||||
|
|
@ -264,7 +260,7 @@ struct CmdWhyDepends : SourceExprCommand, MixOperateOnOptions
|
||||||
// FIXME: should use scanForReferences().
|
// FIXME: should use scanForReferences().
|
||||||
|
|
||||||
if (precise)
|
if (precise)
|
||||||
visitPath(pathS);
|
visitPath(CanonPath::root);
|
||||||
|
|
||||||
for (auto & ref : refs) {
|
for (auto & ref : refs) {
|
||||||
std::string hash(ref.second->path.hashPart());
|
std::string hash(ref.second->path.hashPart());
|
||||||
|
|
@ -280,13 +276,12 @@ struct CmdWhyDepends : SourceExprCommand, MixOperateOnOptions
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!precise) {
|
if (!precise) {
|
||||||
auto pathS = store->printStorePath(ref.second->path);
|
|
||||||
logger->cout(
|
logger->cout(
|
||||||
"%s%s%s%s" ANSI_NORMAL,
|
"%s%s%s%s" ANSI_NORMAL,
|
||||||
firstPad,
|
firstPad,
|
||||||
ref.second->visited ? "\e[38;5;244m" : "",
|
ref.second->visited ? "\e[38;5;244m" : "",
|
||||||
last ? treeLast : treeConn,
|
last ? treeLast : treeConn,
|
||||||
pathS);
|
store->printStorePath(ref.second->path));
|
||||||
node.visited = true;
|
node.visited = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue