mirror of
https://github.com/privatevoid-net/nix-super.git
synced 2024-11-10 08:16:15 +02:00
Merge remote-tracking branch 'nixos/master'
This commit is contained in:
commit
4aaaa3cdfd
62 changed files with 2836 additions and 967 deletions
28
.github/PULL_REQUEST_TEMPLATE.md
vendored
Normal file
28
.github/PULL_REQUEST_TEMPLATE.md
vendored
Normal file
|
@ -0,0 +1,28 @@
|
||||||
|
# Motivation
|
||||||
|
<!-- Briefly explain what the change is about and why it is desirable. -->
|
||||||
|
|
||||||
|
# Context
|
||||||
|
<!-- Provide context. Reference open issues if available. -->
|
||||||
|
|
||||||
|
<!-- Non-trivial change: Briefly outline the implementation strategy. -->
|
||||||
|
|
||||||
|
<!-- Invasive change: Discuss alternative designs or approaches you considered. -->
|
||||||
|
|
||||||
|
<!-- Large change: Provide instructions to reviewers how to read the diff. -->
|
||||||
|
|
||||||
|
# Checklist for maintainers
|
||||||
|
|
||||||
|
<!-- Contributors: please leave this as is -->
|
||||||
|
|
||||||
|
Maintainers: tick if completed or explain if not relevant
|
||||||
|
|
||||||
|
- [ ] agreed on idea
|
||||||
|
- [ ] agreed on implementation strategy
|
||||||
|
- [ ] tests, as appropriate
|
||||||
|
- functional tests - `tests/**.sh`
|
||||||
|
- unit tests - `src/*/tests`
|
||||||
|
- integration tests
|
||||||
|
- [ ] documentation in the manual
|
||||||
|
- [ ] code and comments are self-explanatory
|
||||||
|
- [ ] commit message explains why the change was made
|
||||||
|
- [ ] new feature or bug fix: updated release notes
|
2
.github/workflows/backport.yml
vendored
2
.github/workflows/backport.yml
vendored
|
@ -21,7 +21,7 @@ jobs:
|
||||||
fetch-depth: 0
|
fetch-depth: 0
|
||||||
- name: Create backport PRs
|
- name: Create backport PRs
|
||||||
# should be kept in sync with `version`
|
# should be kept in sync with `version`
|
||||||
uses: zeebe-io/backport-action@v1.0.1
|
uses: zeebe-io/backport-action@v1.1.0
|
||||||
with:
|
with:
|
||||||
# Config README: https://github.com/zeebe-io/backport-action#backport-action
|
# Config README: https://github.com/zeebe-io/backport-action#backport-action
|
||||||
github_token: ${{ secrets.GITHUB_TOKEN }}
|
github_token: ${{ secrets.GITHUB_TOKEN }}
|
||||||
|
|
77
boehmgc-coroutine-sp-fallback.diff
Normal file
77
boehmgc-coroutine-sp-fallback.diff
Normal file
|
@ -0,0 +1,77 @@
|
||||||
|
diff --git a/darwin_stop_world.c b/darwin_stop_world.c
|
||||||
|
index 3dbaa3fb..36a1d1f7 100644
|
||||||
|
--- a/darwin_stop_world.c
|
||||||
|
+++ b/darwin_stop_world.c
|
||||||
|
@@ -352,6 +352,7 @@ GC_INNER void GC_push_all_stacks(void)
|
||||||
|
int nthreads = 0;
|
||||||
|
word total_size = 0;
|
||||||
|
mach_msg_type_number_t listcount = (mach_msg_type_number_t)THREAD_TABLE_SZ;
|
||||||
|
+ size_t stack_limit;
|
||||||
|
if (!EXPECT(GC_thr_initialized, TRUE))
|
||||||
|
GC_thr_init();
|
||||||
|
|
||||||
|
@@ -407,6 +408,19 @@ GC_INNER void GC_push_all_stacks(void)
|
||||||
|
GC_push_all_stack_sections(lo, hi, p->traced_stack_sect);
|
||||||
|
}
|
||||||
|
if (altstack_lo) {
|
||||||
|
+ // When a thread goes into a coroutine, we lose its original sp until
|
||||||
|
+ // control flow returns to the thread.
|
||||||
|
+ // While in the coroutine, the sp points outside the thread stack,
|
||||||
|
+ // so we can detect this and push the entire thread stack instead,
|
||||||
|
+ // as an approximation.
|
||||||
|
+ // We assume that the coroutine has similarly added its entire stack.
|
||||||
|
+ // This could be made accurate by cooperating with the application
|
||||||
|
+ // via new functions and/or callbacks.
|
||||||
|
+ stack_limit = pthread_get_stacksize_np(p->id);
|
||||||
|
+ if (altstack_lo >= altstack_hi || altstack_lo < altstack_hi - stack_limit) { // sp outside stack
|
||||||
|
+ altstack_lo = altstack_hi - stack_limit;
|
||||||
|
+ }
|
||||||
|
+
|
||||||
|
total_size += altstack_hi - altstack_lo;
|
||||||
|
GC_push_all_stack(altstack_lo, altstack_hi);
|
||||||
|
}
|
||||||
|
diff --git a/pthread_stop_world.c b/pthread_stop_world.c
|
||||||
|
index b5d71e62..aed7b0bf 100644
|
||||||
|
--- a/pthread_stop_world.c
|
||||||
|
+++ b/pthread_stop_world.c
|
||||||
|
@@ -768,6 +768,8 @@ STATIC void GC_restart_handler(int sig)
|
||||||
|
/* world is stopped. Should not fail if it isn't. */
|
||||||
|
GC_INNER void GC_push_all_stacks(void)
|
||||||
|
{
|
||||||
|
+ size_t stack_limit;
|
||||||
|
+ pthread_attr_t pattr;
|
||||||
|
GC_bool found_me = FALSE;
|
||||||
|
size_t nthreads = 0;
|
||||||
|
int i;
|
||||||
|
@@ -851,6 +853,31 @@ GC_INNER void GC_push_all_stacks(void)
|
||||||
|
hi = p->altstack + p->altstack_size;
|
||||||
|
/* FIXME: Need to scan the normal stack too, but how ? */
|
||||||
|
/* FIXME: Assume stack grows down */
|
||||||
|
+ } else {
|
||||||
|
+ if (pthread_getattr_np(p->id, &pattr)) {
|
||||||
|
+ ABORT("GC_push_all_stacks: pthread_getattr_np failed!");
|
||||||
|
+ }
|
||||||
|
+ if (pthread_attr_getstacksize(&pattr, &stack_limit)) {
|
||||||
|
+ ABORT("GC_push_all_stacks: pthread_attr_getstacksize failed!");
|
||||||
|
+ }
|
||||||
|
+ if (pthread_attr_destroy(&pattr)) {
|
||||||
|
+ ABORT("GC_push_all_stacks: pthread_attr_destroy failed!");
|
||||||
|
+ }
|
||||||
|
+ // When a thread goes into a coroutine, we lose its original sp until
|
||||||
|
+ // control flow returns to the thread.
|
||||||
|
+ // While in the coroutine, the sp points outside the thread stack,
|
||||||
|
+ // so we can detect this and push the entire thread stack instead,
|
||||||
|
+ // as an approximation.
|
||||||
|
+ // We assume that the coroutine has similarly added its entire stack.
|
||||||
|
+ // This could be made accurate by cooperating with the application
|
||||||
|
+ // via new functions and/or callbacks.
|
||||||
|
+ #ifndef STACK_GROWS_UP
|
||||||
|
+ if (lo >= hi || lo < hi - stack_limit) { // sp outside stack
|
||||||
|
+ lo = hi - stack_limit;
|
||||||
|
+ }
|
||||||
|
+ #else
|
||||||
|
+ #error "STACK_GROWS_UP not supported in boost_coroutine2 (as of june 2021), so we don't support it in Nix."
|
||||||
|
+ #endif
|
||||||
|
}
|
||||||
|
GC_push_all_stack_sections(lo, hi, traced_stack_sect);
|
||||||
|
# ifdef STACK_GROWS_UP
|
|
@ -274,6 +274,12 @@ fi
|
||||||
PKG_CHECK_MODULES([GTEST], [gtest_main])
|
PKG_CHECK_MODULES([GTEST], [gtest_main])
|
||||||
|
|
||||||
|
|
||||||
|
# Look for rapidcheck.
|
||||||
|
# No pkg-config yet, https://github.com/emil-e/rapidcheck/issues/302
|
||||||
|
AC_CHECK_HEADERS([rapidcheck/gtest.h], [], [], [#include <gtest/gtest.h>])
|
||||||
|
AC_CHECK_LIB([rapidcheck], [])
|
||||||
|
|
||||||
|
|
||||||
# Look for nlohmann/json.
|
# Look for nlohmann/json.
|
||||||
PKG_CHECK_MODULES([NLOHMANN_JSON], [nlohmann_json >= 3.9])
|
PKG_CHECK_MODULES([NLOHMANN_JSON], [nlohmann_json >= 3.9])
|
||||||
|
|
||||||
|
|
|
@ -66,11 +66,11 @@ The operation `--realise` essentially “builds” the specified store
|
||||||
paths. Realisation is a somewhat overloaded term:
|
paths. Realisation is a somewhat overloaded term:
|
||||||
|
|
||||||
- If the store path is a *derivation*, realisation ensures that the
|
- If the store path is a *derivation*, realisation ensures that the
|
||||||
output paths of the derivation are [valid](../glossary.md) (i.e.,
|
output paths of the derivation are [valid] (i.e.,
|
||||||
the output path and its closure exist in the file system). This
|
the output path and its closure exist in the file system). This
|
||||||
can be done in several ways. First, it is possible that the
|
can be done in several ways. First, it is possible that the
|
||||||
outputs are already valid, in which case we are done
|
outputs are already valid, in which case we are done
|
||||||
immediately. Otherwise, there may be [substitutes](../glossary.md)
|
immediately. Otherwise, there may be [substitutes]
|
||||||
that produce the outputs (e.g., by downloading them). Finally, the
|
that produce the outputs (e.g., by downloading them). Finally, the
|
||||||
outputs can be produced by running the build task described
|
outputs can be produced by running the build task described
|
||||||
by the derivation.
|
by the derivation.
|
||||||
|
@ -82,6 +82,9 @@ paths. Realisation is a somewhat overloaded term:
|
||||||
produced through substitutes. If there are no (successful)
|
produced through substitutes. If there are no (successful)
|
||||||
substitutes, realisation fails.
|
substitutes, realisation fails.
|
||||||
|
|
||||||
|
[valid]: ../glossary.md#gloss-validity
|
||||||
|
[substitutes]: ../glossary.md#gloss-substitute
|
||||||
|
|
||||||
The output path of each derivation is printed on standard output. (For
|
The output path of each derivation is printed on standard output. (For
|
||||||
non-derivations argument, the argument itself is printed.)
|
non-derivations argument, the argument itself is printed.)
|
||||||
|
|
||||||
|
@ -295,8 +298,8 @@ error: cannot delete path `/nix/store/zq0h41l75vlb4z45kzgjjmsjxvcv1qk7-mesa-6.4'
|
||||||
|
|
||||||
## Description
|
## Description
|
||||||
|
|
||||||
The operation `--query` displays various bits of information about the
|
The operation `--query` displays information about [store path]s.
|
||||||
store paths . The queries are described below. At most one query can be
|
The queries are described below. At most one query can be
|
||||||
specified. The default query is `--outputs`.
|
specified. The default query is `--outputs`.
|
||||||
|
|
||||||
The paths *paths* may also be symlinks from outside of the Nix store, to
|
The paths *paths* may also be symlinks from outside of the Nix store, to
|
||||||
|
@ -316,12 +319,12 @@ symlink.
|
||||||
## Queries
|
## Queries
|
||||||
|
|
||||||
- `--outputs`\
|
- `--outputs`\
|
||||||
Prints out the [output paths](../glossary.md) of the store
|
Prints out the [output path]s of the store
|
||||||
derivations *paths*. These are the paths that will be produced when
|
derivations *paths*. These are the paths that will be produced when
|
||||||
the derivation is built.
|
the derivation is built.
|
||||||
|
|
||||||
- `--requisites`; `-R`\
|
- `--requisites`; `-R`\
|
||||||
Prints out the [closure](../glossary.md) of the store path *paths*.
|
Prints out the [closure] of the given *paths*.
|
||||||
|
|
||||||
This query has one option:
|
This query has one option:
|
||||||
|
|
||||||
|
@ -338,10 +341,12 @@ symlink.
|
||||||
derivation and specifying the option `--include-outputs`.
|
derivation and specifying the option `--include-outputs`.
|
||||||
|
|
||||||
- `--references`\
|
- `--references`\
|
||||||
Prints the set of [references](../glossary.md) of the store paths
|
Prints the set of [references]s of the store paths
|
||||||
*paths*, that is, their immediate dependencies. (For *all*
|
*paths*, that is, their immediate dependencies. (For *all*
|
||||||
dependencies, use `--requisites`.)
|
dependencies, use `--requisites`.)
|
||||||
|
|
||||||
|
[reference]: ../glossary.md#gloss-reference
|
||||||
|
|
||||||
- `--referrers`\
|
- `--referrers`\
|
||||||
Prints the set of *referrers* of the store paths *paths*, that is,
|
Prints the set of *referrers* of the store paths *paths*, that is,
|
||||||
the store paths currently existing in the Nix store that refer to
|
the store paths currently existing in the Nix store that refer to
|
||||||
|
@ -356,11 +361,13 @@ symlink.
|
||||||
in the Nix store that are dependent on *paths*.
|
in the Nix store that are dependent on *paths*.
|
||||||
|
|
||||||
- `--deriver`; `-d`\
|
- `--deriver`; `-d`\
|
||||||
Prints the [deriver](../glossary.md) of the store paths *paths*. If
|
Prints the [deriver] of the store paths *paths*. If
|
||||||
the path has no deriver (e.g., if it is a source file), or if the
|
the path has no deriver (e.g., if it is a source file), or if the
|
||||||
deriver is not known (e.g., in the case of a binary-only
|
deriver is not known (e.g., in the case of a binary-only
|
||||||
deployment), the string `unknown-deriver` is printed.
|
deployment), the string `unknown-deriver` is printed.
|
||||||
|
|
||||||
|
[deriver]: ../glossary.md#gloss-deriver
|
||||||
|
|
||||||
- `--graph`\
|
- `--graph`\
|
||||||
Prints the references graph of the store paths *paths* in the format
|
Prints the references graph of the store paths *paths* in the format
|
||||||
of the `dot` tool of AT\&T's [Graphviz
|
of the `dot` tool of AT\&T's [Graphviz
|
||||||
|
|
|
@ -92,7 +92,8 @@ $ nix develop
|
||||||
|
|
||||||
The unit-tests for each Nix library (`libexpr`, `libstore`, etc..) are defined
|
The unit-tests for each Nix library (`libexpr`, `libstore`, etc..) are defined
|
||||||
under `src/{library_name}/tests` using the
|
under `src/{library_name}/tests` using the
|
||||||
[googletest](https://google.github.io/googletest/) framework.
|
[googletest](https://google.github.io/googletest/) and
|
||||||
|
[rapidcheck](https://github.com/emil-e/rapidcheck) frameworks.
|
||||||
|
|
||||||
You can run the whole testsuite with `make check`, or the tests for a specific component with `make libfoo-tests_RUN`. Finer-grained filtering is also possible using the [--gtest_filter](https://google.github.io/googletest/advanced.html#running-a-subset-of-the-tests) command-line option.
|
You can run the whole testsuite with `make check`, or the tests for a specific component with `make libfoo-tests_RUN`. Finer-grained filtering is also possible using the [--gtest_filter](https://google.github.io/googletest/advanced.html#running-a-subset-of-the-tests) command-line option.
|
||||||
|
|
||||||
|
|
|
@ -19,6 +19,17 @@
|
||||||
|
|
||||||
[store derivation]: #gloss-store-derivation
|
[store derivation]: #gloss-store-derivation
|
||||||
|
|
||||||
|
- [realise]{#gloss-realise}, realisation\
|
||||||
|
Ensure a [store path] is [valid][validity].
|
||||||
|
|
||||||
|
This means either running the `builder` executable as specified in the corresponding [derivation] or fetching a pre-built [store object] from a [substituter].
|
||||||
|
|
||||||
|
See [`nix-build`](./command-ref/nix-build.md) and [`nix-store --realise`](./command-ref/nix-store.md#operation---realise).
|
||||||
|
|
||||||
|
See [`nix build`](./command-ref/new-cli/nix3-build.md) (experimental).
|
||||||
|
|
||||||
|
[realise]: #gloss-realise
|
||||||
|
|
||||||
- [content-addressed derivation]{#gloss-content-addressed-derivation}\
|
- [content-addressed derivation]{#gloss-content-addressed-derivation}\
|
||||||
A derivation which has the
|
A derivation which has the
|
||||||
[`__contentAddressed`](./language/advanced-attributes.md#adv-attr-__contentAddressed)
|
[`__contentAddressed`](./language/advanced-attributes.md#adv-attr-__contentAddressed)
|
||||||
|
@ -101,6 +112,8 @@
|
||||||
copy store objects it doesn't have. For details, see the
|
copy store objects it doesn't have. For details, see the
|
||||||
[`substituters` option](./command-ref/conf-file.md#conf-substituters).
|
[`substituters` option](./command-ref/conf-file.md#conf-substituters).
|
||||||
|
|
||||||
|
[substituter]: #gloss-substituter
|
||||||
|
|
||||||
- [purity]{#gloss-purity}\
|
- [purity]{#gloss-purity}\
|
||||||
The assumption that equal Nix derivations when run always produce
|
The assumption that equal Nix derivations when run always produce
|
||||||
the same output. This cannot be guaranteed in general (e.g., a
|
the same output. This cannot be guaranteed in general (e.g., a
|
||||||
|
@ -143,19 +156,25 @@
|
||||||
to path `Q`, then `Q` is in the closure of `P`. Further, if `Q`
|
to path `Q`, then `Q` is in the closure of `P`. Further, if `Q`
|
||||||
references `R` then `R` is also in the closure of `P`.
|
references `R` then `R` is also in the closure of `P`.
|
||||||
|
|
||||||
|
[closure]: #gloss-closure
|
||||||
|
|
||||||
- [output path]{#gloss-output-path}\
|
- [output path]{#gloss-output-path}\
|
||||||
A [store path] produced by a [derivation].
|
A [store path] produced by a [derivation].
|
||||||
|
|
||||||
[output path]: #gloss-output-path
|
[output path]: #gloss-output-path
|
||||||
|
|
||||||
- [deriver]{#gloss-deriver}\
|
- [deriver]{#gloss-deriver}\
|
||||||
The deriver of an *output path* is the store
|
The [store derivation] that produced an [output path].
|
||||||
derivation that built it.
|
|
||||||
|
|
||||||
- [validity]{#gloss-validity}\
|
- [validity]{#gloss-validity}\
|
||||||
A store path is considered *valid* if it exists in the file system,
|
A store path is valid if all [store object]s in its [closure] can be read from the [store].
|
||||||
is listed in the Nix database as being valid, and if all paths in
|
|
||||||
its closure are also valid.
|
For a local store, this means:
|
||||||
|
- The store path leads to an existing [store object] in that [store].
|
||||||
|
- The store path is listed in the Nix database as being valid.
|
||||||
|
- All paths in the store path's [closure] are valid.
|
||||||
|
|
||||||
|
[validity]: #gloss-validity
|
||||||
|
|
||||||
- [user environment]{#gloss-user-env}\
|
- [user environment]{#gloss-user-env}\
|
||||||
An automatically generated store object that consists of a set of
|
An automatically generated store object that consists of a set of
|
||||||
|
|
|
@ -191,12 +191,12 @@ This is an incomplete overview of language features, by example.
|
||||||
<tr>
|
<tr>
|
||||||
<td>
|
<td>
|
||||||
|
|
||||||
<nixpkgs>
|
`<nixpkgs>`
|
||||||
|
|
||||||
</td>
|
</td>
|
||||||
<td>
|
<td>
|
||||||
|
|
||||||
Search path. Value determined by [`$NIX_PATH` environment variable](../command-ref/env-common.md#env-NIX_PATH).
|
Search path for Nix files. Value determined by [`$NIX_PATH` environment variable](../command-ref/env-common.md#env-NIX_PATH).
|
||||||
|
|
||||||
</td>
|
</td>
|
||||||
</tr>
|
</tr>
|
||||||
|
|
|
@ -24,7 +24,7 @@
|
||||||
| [Equality] | *expr* `==` *expr* | none | 11 |
|
| [Equality] | *expr* `==` *expr* | none | 11 |
|
||||||
| Inequality | *expr* `!=` *expr* | none | 11 |
|
| Inequality | *expr* `!=` *expr* | none | 11 |
|
||||||
| Logical conjunction (`AND`) | *bool* `&&` *bool* | left | 12 |
|
| Logical conjunction (`AND`) | *bool* `&&` *bool* | left | 12 |
|
||||||
| Logical disjunction (`OR`) | *bool* `||` *bool* | left | 13 |
|
| Logical disjunction (`OR`) | *bool* `\|\|` *bool* | left | 13 |
|
||||||
| [Logical implication] | *bool* `->` *bool* | none | 14 |
|
| [Logical implication] | *bool* `->` *bool* | none | 14 |
|
||||||
|
|
||||||
[string]: ./values.md#type-string
|
[string]: ./values.md#type-string
|
||||||
|
@ -120,12 +120,12 @@ The result is a string.
|
||||||
|
|
||||||
## Update
|
## Update
|
||||||
|
|
||||||
> *attrset1* + *attrset2*
|
> *attrset1* // *attrset2*
|
||||||
|
|
||||||
Update [attribute set] *attrset1* with names and values from *attrset2*.
|
Update [attribute set] *attrset1* with names and values from *attrset2*.
|
||||||
|
|
||||||
The returned attribute set will have of all the attributes in *e1* and *e2*.
|
The returned attribute set will have of all the attributes in *attrset1* and *attrset2*.
|
||||||
If an attribute name is present in both, the attribute value from the former is taken.
|
If an attribute name is present in both, the attribute value from the latter is taken.
|
||||||
|
|
||||||
[Update]: #update
|
[Update]: #update
|
||||||
|
|
||||||
|
|
|
@ -18,6 +18,10 @@
|
||||||
* Instead of "antiquotation", the more common term [string interpolation](../language/string-interpolation.md) is now used consistently.
|
* Instead of "antiquotation", the more common term [string interpolation](../language/string-interpolation.md) is now used consistently.
|
||||||
Historical release notes were not changed.
|
Historical release notes were not changed.
|
||||||
|
|
||||||
|
* Error traces have been reworked to provide detailed explanations and more
|
||||||
|
accurate error locations. A short excerpt of the trace is now shown by
|
||||||
|
default when an error occurs.
|
||||||
|
|
||||||
* Allow explicitly selecting outputs in a store derivation installable, just like we can do with other sorts of installables.
|
* Allow explicitly selecting outputs in a store derivation installable, just like we can do with other sorts of installables.
|
||||||
For example,
|
For example,
|
||||||
```shell-session
|
```shell-session
|
||||||
|
|
|
@ -1,2 +1,10 @@
|
||||||
# Release X.Y (202?-??-??)
|
# Release X.Y (202?-??-??)
|
||||||
|
|
||||||
|
* A new function `builtins.readFileType` is available. It is similar to
|
||||||
|
`builtins.readDir` but acts on a single file or directory.
|
||||||
|
|
||||||
|
* The `builtins.readDir` function has been optimized when encountering not-yet-known
|
||||||
|
file types from POSIX's `readdir`. In such cases the type of each file is/was
|
||||||
|
discovered by making multiple syscalls. This change makes these operations
|
||||||
|
lazy such that these lookups will only be performed if the attribute is used.
|
||||||
|
This optimization affects a minority of filesystems and operating systems.
|
||||||
|
|
19
flake.nix
19
flake.nix
|
@ -82,7 +82,9 @@
|
||||||
});
|
});
|
||||||
|
|
||||||
configureFlags =
|
configureFlags =
|
||||||
lib.optionals stdenv.isLinux [
|
[
|
||||||
|
"CXXFLAGS=-I${lib.getDev rapidcheck}/extras/gtest/include"
|
||||||
|
] ++ lib.optionals stdenv.isLinux [
|
||||||
"--with-boost=${boost}/lib"
|
"--with-boost=${boost}/lib"
|
||||||
"--with-sandbox-shell=${sh}/bin/busybox"
|
"--with-sandbox-shell=${sh}/bin/busybox"
|
||||||
]
|
]
|
||||||
|
@ -116,6 +118,7 @@
|
||||||
boost
|
boost
|
||||||
lowdown-nix
|
lowdown-nix
|
||||||
gtest
|
gtest
|
||||||
|
rapidcheck
|
||||||
]
|
]
|
||||||
++ lib.optionals stdenv.isLinux [libseccomp]
|
++ lib.optionals stdenv.isLinux [libseccomp]
|
||||||
++ lib.optional (stdenv.isLinux || stdenv.isDarwin) libsodium
|
++ lib.optional (stdenv.isLinux || stdenv.isDarwin) libsodium
|
||||||
|
@ -128,9 +131,14 @@
|
||||||
});
|
});
|
||||||
|
|
||||||
propagatedDeps =
|
propagatedDeps =
|
||||||
[ (boehmgc.override {
|
[ ((boehmgc.override {
|
||||||
enableLargeConfig = true;
|
enableLargeConfig = true;
|
||||||
|
}).overrideAttrs(o: {
|
||||||
|
patches = (o.patches or []) ++ [
|
||||||
|
./boehmgc-coroutine-sp-fallback.diff
|
||||||
|
];
|
||||||
})
|
})
|
||||||
|
)
|
||||||
nlohmann_json
|
nlohmann_json
|
||||||
];
|
];
|
||||||
};
|
};
|
||||||
|
@ -650,6 +658,7 @@
|
||||||
inherit system crossSystem;
|
inherit system crossSystem;
|
||||||
overlays = [ self.overlays.default ];
|
overlays = [ self.overlays.default ];
|
||||||
};
|
};
|
||||||
|
inherit (nixpkgsCross) lib;
|
||||||
in with commonDeps { pkgs = nixpkgsCross; }; nixpkgsCross.stdenv.mkDerivation {
|
in with commonDeps { pkgs = nixpkgsCross; }; nixpkgsCross.stdenv.mkDerivation {
|
||||||
name = "nix-super-${version}";
|
name = "nix-super-${version}";
|
||||||
|
|
||||||
|
@ -662,7 +671,11 @@
|
||||||
nativeBuildInputs = nativeBuildDeps;
|
nativeBuildInputs = nativeBuildDeps;
|
||||||
buildInputs = buildDeps ++ propagatedDeps;
|
buildInputs = buildDeps ++ propagatedDeps;
|
||||||
|
|
||||||
configureFlags = [ "--sysconfdir=/etc" "--disable-doc-gen" ];
|
configureFlags = [
|
||||||
|
"CXXFLAGS=-I${lib.getDev nixpkgsCross.rapidcheck}/extras/gtest/include"
|
||||||
|
"--sysconfdir=/etc"
|
||||||
|
"--disable-doc-gen"
|
||||||
|
];
|
||||||
|
|
||||||
enableParallelBuilding = true;
|
enableParallelBuilding = true;
|
||||||
|
|
||||||
|
|
|
@ -630,7 +630,7 @@ ref<eval_cache::EvalCache> openEvalCache(
|
||||||
auto vFlake = state.allocValue();
|
auto vFlake = state.allocValue();
|
||||||
flake::callFlake(state, *lockedFlake, *vFlake);
|
flake::callFlake(state, *lockedFlake, *vFlake);
|
||||||
|
|
||||||
state.forceAttrs(*vFlake, noPos);
|
state.forceAttrs(*vFlake, noPos, "while parsing cached flake data");
|
||||||
|
|
||||||
auto aOutputs = vFlake->attrs->get(state.symbols.create("outputs"));
|
auto aOutputs = vFlake->attrs->get(state.symbols.create("outputs"));
|
||||||
assert(aOutputs);
|
assert(aOutputs);
|
||||||
|
@ -694,7 +694,7 @@ DerivedPathsWithInfo InstallableFlake::toDerivedPaths()
|
||||||
|
|
||||||
else if (v.type() == nString) {
|
else if (v.type() == nString) {
|
||||||
PathSet context;
|
PathSet context;
|
||||||
auto s = state->forceString(v, context, noPos);
|
auto s = state->forceString(v, context, noPos, fmt("while evaluating the flake output attribute '%s'", attrPath));
|
||||||
auto storePath = state->store->maybeParseStorePath(s);
|
auto storePath = state->store->maybeParseStorePath(s);
|
||||||
if (storePath && context.count(std::string(s))) {
|
if (storePath && context.count(std::string(s))) {
|
||||||
return {{
|
return {{
|
||||||
|
|
|
@ -397,7 +397,7 @@ StringSet NixRepl::completePrefix(const std::string & prefix)
|
||||||
Expr * e = parseString(expr);
|
Expr * e = parseString(expr);
|
||||||
Value v;
|
Value v;
|
||||||
e->eval(*state, *env, v);
|
e->eval(*state, *env, v);
|
||||||
state->forceAttrs(v, noPos);
|
state->forceAttrs(v, noPos, "while evaluating an attrset for the purpose of completion (this error should not be displayed; file an issue?)");
|
||||||
|
|
||||||
for (auto & i : *v.attrs) {
|
for (auto & i : *v.attrs) {
|
||||||
std::string_view name = state->symbols[i.name];
|
std::string_view name = state->symbols[i.name];
|
||||||
|
@ -590,7 +590,7 @@ bool NixRepl::processLine(std::string line)
|
||||||
const auto [path, line] = [&] () -> std::pair<Path, uint32_t> {
|
const auto [path, line] = [&] () -> std::pair<Path, uint32_t> {
|
||||||
if (v.type() == nPath || v.type() == nString) {
|
if (v.type() == nPath || v.type() == nString) {
|
||||||
PathSet context;
|
PathSet context;
|
||||||
auto path = state->coerceToPath(noPos, v, context);
|
auto path = state->coerceToPath(noPos, v, context, "while evaluating the filename to edit");
|
||||||
return {path, 0};
|
return {path, 0};
|
||||||
} else if (v.isLambda()) {
|
} else if (v.isLambda()) {
|
||||||
auto pos = state->positions[v.lambda.fun->pos];
|
auto pos = state->positions[v.lambda.fun->pos];
|
||||||
|
@ -839,7 +839,7 @@ void NixRepl::loadFiles()
|
||||||
|
|
||||||
void NixRepl::addAttrsToScope(Value & attrs)
|
void NixRepl::addAttrsToScope(Value & attrs)
|
||||||
{
|
{
|
||||||
state->forceAttrs(attrs, [&]() { return attrs.determinePos(noPos); });
|
state->forceAttrs(attrs, [&]() { return attrs.determinePos(noPos); }, "while evaluating an attribute set to be merged in the global scope");
|
||||||
if (displ + attrs.attrs->size() >= envSize)
|
if (displ + attrs.attrs->size() >= envSize)
|
||||||
throw Error("environment full; cannot add more variables");
|
throw Error("environment full; cannot add more variables");
|
||||||
|
|
||||||
|
@ -944,7 +944,7 @@ std::ostream & NixRepl::printValue(std::ostream & str, Value & v, unsigned int m
|
||||||
Bindings::iterator i = v.attrs->find(state->sDrvPath);
|
Bindings::iterator i = v.attrs->find(state->sDrvPath);
|
||||||
PathSet context;
|
PathSet context;
|
||||||
if (i != v.attrs->end())
|
if (i != v.attrs->end())
|
||||||
str << state->store->printStorePath(state->coerceToStorePath(i->pos, *i->value, context));
|
str << state->store->printStorePath(state->coerceToStorePath(i->pos, *i->value, context, "while evaluating the drvPath of a derivation"));
|
||||||
else
|
else
|
||||||
str << "???";
|
str << "???";
|
||||||
str << "»";
|
str << "»";
|
||||||
|
|
|
@ -118,7 +118,7 @@ std::pair<std::string, uint32_t> findPackageFilename(EvalState & state, Value &
|
||||||
|
|
||||||
// FIXME: is it possible to extract the Pos object instead of doing this
|
// FIXME: is it possible to extract the Pos object instead of doing this
|
||||||
// toString + parsing?
|
// toString + parsing?
|
||||||
auto pos = state.forceString(*v2);
|
auto pos = state.forceString(*v2, noPos, "while evaluating the 'meta.position' attribute of a derivation");
|
||||||
|
|
||||||
auto colon = pos.rfind(':');
|
auto colon = pos.rfind(':');
|
||||||
if (colon == std::string::npos)
|
if (colon == std::string::npos)
|
||||||
|
|
|
@ -385,7 +385,7 @@ Value & AttrCursor::getValue()
|
||||||
if (!_value) {
|
if (!_value) {
|
||||||
if (parent) {
|
if (parent) {
|
||||||
auto & vParent = parent->first->getValue();
|
auto & vParent = parent->first->getValue();
|
||||||
root->state.forceAttrs(vParent, noPos);
|
root->state.forceAttrs(vParent, noPos, "while searching for an attribute");
|
||||||
auto attr = vParent.attrs->get(parent->second);
|
auto attr = vParent.attrs->get(parent->second);
|
||||||
if (!attr)
|
if (!attr)
|
||||||
throw Error("attribute '%s' is unexpectedly missing", getAttrPathStr());
|
throw Error("attribute '%s' is unexpectedly missing", getAttrPathStr());
|
||||||
|
@ -571,14 +571,14 @@ std::string AttrCursor::getString()
|
||||||
debug("using cached string attribute '%s'", getAttrPathStr());
|
debug("using cached string attribute '%s'", getAttrPathStr());
|
||||||
return s->first;
|
return s->first;
|
||||||
} else
|
} else
|
||||||
root->state.debugThrowLastTrace(TypeError("'%s' is not a string", getAttrPathStr()));
|
root->state.error("'%s' is not a string", getAttrPathStr()).debugThrow<TypeError>();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
auto & v = forceValue();
|
auto & v = forceValue();
|
||||||
|
|
||||||
if (v.type() != nString && v.type() != nPath)
|
if (v.type() != nString && v.type() != nPath)
|
||||||
root->state.debugThrowLastTrace(TypeError("'%s' is not a string but %s", getAttrPathStr(), showType(v.type())));
|
root->state.error("'%s' is not a string but %s", getAttrPathStr()).debugThrow<TypeError>();
|
||||||
|
|
||||||
return v.type() == nString ? v.string.s : v.path;
|
return v.type() == nString ? v.string.s : v.path;
|
||||||
}
|
}
|
||||||
|
@ -613,7 +613,7 @@ string_t AttrCursor::getStringWithContext()
|
||||||
return *s;
|
return *s;
|
||||||
}
|
}
|
||||||
} else
|
} else
|
||||||
root->state.debugThrowLastTrace(TypeError("'%s' is not a string", getAttrPathStr()));
|
root->state.error("'%s' is not a string", getAttrPathStr()).debugThrow<TypeError>();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -624,7 +624,7 @@ string_t AttrCursor::getStringWithContext()
|
||||||
else if (v.type() == nPath)
|
else if (v.type() == nPath)
|
||||||
return {v.path, {}};
|
return {v.path, {}};
|
||||||
else
|
else
|
||||||
root->state.debugThrowLastTrace(TypeError("'%s' is not a string but %s", getAttrPathStr(), showType(v.type())));
|
root->state.error("'%s' is not a string but %s", getAttrPathStr()).debugThrow<TypeError>();
|
||||||
}
|
}
|
||||||
|
|
||||||
bool AttrCursor::getBool()
|
bool AttrCursor::getBool()
|
||||||
|
@ -637,14 +637,14 @@ bool AttrCursor::getBool()
|
||||||
debug("using cached Boolean attribute '%s'", getAttrPathStr());
|
debug("using cached Boolean attribute '%s'", getAttrPathStr());
|
||||||
return *b;
|
return *b;
|
||||||
} else
|
} else
|
||||||
root->state.debugThrowLastTrace(TypeError("'%s' is not a Boolean", getAttrPathStr()));
|
root->state.error("'%s' is not a Boolean", getAttrPathStr()).debugThrow<TypeError>();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
auto & v = forceValue();
|
auto & v = forceValue();
|
||||||
|
|
||||||
if (v.type() != nBool)
|
if (v.type() != nBool)
|
||||||
root->state.debugThrowLastTrace(TypeError("'%s' is not a Boolean", getAttrPathStr()));
|
root->state.error("'%s' is not a Boolean", getAttrPathStr()).debugThrow<TypeError>();
|
||||||
|
|
||||||
return v.boolean;
|
return v.boolean;
|
||||||
}
|
}
|
||||||
|
@ -696,7 +696,7 @@ std::vector<std::string> AttrCursor::getListOfStrings()
|
||||||
std::vector<std::string> res;
|
std::vector<std::string> res;
|
||||||
|
|
||||||
for (auto & elem : v.listItems())
|
for (auto & elem : v.listItems())
|
||||||
res.push_back(std::string(root->state.forceStringNoCtx(*elem)));
|
res.push_back(std::string(root->state.forceStringNoCtx(*elem, noPos, "while evaluating an attribute for caching")));
|
||||||
|
|
||||||
if (root->db)
|
if (root->db)
|
||||||
cachedValue = {root->db->setListOfStrings(getKey(), res), res};
|
cachedValue = {root->db->setListOfStrings(getKey(), res), res};
|
||||||
|
@ -714,14 +714,14 @@ std::vector<Symbol> AttrCursor::getAttrs()
|
||||||
debug("using cached attrset attribute '%s'", getAttrPathStr());
|
debug("using cached attrset attribute '%s'", getAttrPathStr());
|
||||||
return *attrs;
|
return *attrs;
|
||||||
} else
|
} else
|
||||||
root->state.debugThrowLastTrace(TypeError("'%s' is not an attribute set", getAttrPathStr()));
|
root->state.error("'%s' is not an attribute set", getAttrPathStr()).debugThrow<TypeError>();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
auto & v = forceValue();
|
auto & v = forceValue();
|
||||||
|
|
||||||
if (v.type() != nAttrs)
|
if (v.type() != nAttrs)
|
||||||
root->state.debugThrowLastTrace(TypeError("'%s' is not an attribute set", getAttrPathStr()));
|
root->state.error("'%s' is not an attribute set", getAttrPathStr()).debugThrow<TypeError>();
|
||||||
|
|
||||||
std::vector<Symbol> attrs;
|
std::vector<Symbol> attrs;
|
||||||
for (auto & attr : *getValue().attrs)
|
for (auto & attr : *getValue().attrs)
|
||||||
|
|
|
@ -103,33 +103,36 @@ void EvalState::forceValue(Value & v, Callable getPos)
|
||||||
else if (v.isApp())
|
else if (v.isApp())
|
||||||
callFunction(*v.app.left, *v.app.right, v, noPos);
|
callFunction(*v.app.left, *v.app.right, v, noPos);
|
||||||
else if (v.isBlackhole())
|
else if (v.isBlackhole())
|
||||||
throwEvalError(getPos(), "infinite recursion encountered");
|
error("infinite recursion encountered").atPos(getPos()).template debugThrow<EvalError>();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
[[gnu::always_inline]]
|
[[gnu::always_inline]]
|
||||||
inline void EvalState::forceAttrs(Value & v, const PosIdx pos)
|
inline void EvalState::forceAttrs(Value & v, const PosIdx pos, std::string_view errorCtx)
|
||||||
{
|
{
|
||||||
forceAttrs(v, [&]() { return pos; });
|
forceAttrs(v, [&]() { return pos; }, errorCtx);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
template <typename Callable>
|
template <typename Callable>
|
||||||
[[gnu::always_inline]]
|
[[gnu::always_inline]]
|
||||||
inline void EvalState::forceAttrs(Value & v, Callable getPos)
|
inline void EvalState::forceAttrs(Value & v, Callable getPos, std::string_view errorCtx)
|
||||||
{
|
{
|
||||||
forceValue(v, getPos);
|
forceValue(v, noPos);
|
||||||
if (v.type() != nAttrs)
|
if (v.type() != nAttrs) {
|
||||||
throwTypeError(getPos(), "value is %1% while a set was expected", v);
|
PosIdx pos = getPos();
|
||||||
|
error("value is %1% while a set was expected", showType(v)).withTrace(pos, errorCtx).debugThrow<TypeError>();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
[[gnu::always_inline]]
|
[[gnu::always_inline]]
|
||||||
inline void EvalState::forceList(Value & v, const PosIdx pos)
|
inline void EvalState::forceList(Value & v, const PosIdx pos, std::string_view errorCtx)
|
||||||
{
|
{
|
||||||
forceValue(v, pos);
|
forceValue(v, noPos);
|
||||||
if (!v.isList())
|
if (!v.isList()) {
|
||||||
throwTypeError(pos, "value is %1% while a list was expected", v);
|
error("value is %1% while a list was expected", showType(v)).withTrace(pos, errorCtx).debugThrow<TypeError>();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -11,7 +11,9 @@
|
||||||
|
|
||||||
#include <algorithm>
|
#include <algorithm>
|
||||||
#include <chrono>
|
#include <chrono>
|
||||||
|
#include <iostream>
|
||||||
#include <cstring>
|
#include <cstring>
|
||||||
|
#include <optional>
|
||||||
#include <unistd.h>
|
#include <unistd.h>
|
||||||
#include <sys/time.h>
|
#include <sys/time.h>
|
||||||
#include <sys/resource.h>
|
#include <sys/resource.h>
|
||||||
|
@ -318,7 +320,7 @@ static Symbol getName(const AttrName & name, EvalState & state, Env & env)
|
||||||
} else {
|
} else {
|
||||||
Value nameValue;
|
Value nameValue;
|
||||||
name.expr->eval(state, env, nameValue);
|
name.expr->eval(state, env, nameValue);
|
||||||
state.forceStringNoCtx(nameValue);
|
state.forceStringNoCtx(nameValue, noPos, "while evaluating an attribute name");
|
||||||
return state.symbols.create(nameValue.string.s);
|
return state.symbols.create(nameValue.string.s);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -414,6 +416,44 @@ static Strings parseNixPath(const std::string & s)
|
||||||
return res;
|
return res;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
ErrorBuilder & ErrorBuilder::atPos(PosIdx pos)
|
||||||
|
{
|
||||||
|
info.errPos = state.positions[pos];
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
|
||||||
|
ErrorBuilder & ErrorBuilder::withTrace(PosIdx pos, const std::string_view text)
|
||||||
|
{
|
||||||
|
info.traces.push_front(Trace{ .pos = state.positions[pos], .hint = hintformat(std::string(text)), .frame = false });
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
|
||||||
|
ErrorBuilder & ErrorBuilder::withFrameTrace(PosIdx pos, const std::string_view text)
|
||||||
|
{
|
||||||
|
info.traces.push_front(Trace{ .pos = state.positions[pos], .hint = hintformat(std::string(text)), .frame = true });
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
|
||||||
|
ErrorBuilder & ErrorBuilder::withSuggestions(Suggestions & s)
|
||||||
|
{
|
||||||
|
info.suggestions = s;
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
|
||||||
|
ErrorBuilder & ErrorBuilder::withFrame(const Env & env, const Expr & expr)
|
||||||
|
{
|
||||||
|
// NOTE: This is abusing side-effects.
|
||||||
|
// TODO: check compatibility with nested debugger calls.
|
||||||
|
state.debugTraces.push_front(DebugTrace {
|
||||||
|
.pos = nullptr,
|
||||||
|
.expr = expr,
|
||||||
|
.env = env,
|
||||||
|
.hint = hintformat("Fake frame for debugging purposes"),
|
||||||
|
.isError = true
|
||||||
|
});
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
EvalState::EvalState(
|
EvalState::EvalState(
|
||||||
const Strings & _searchPath,
|
const Strings & _searchPath,
|
||||||
|
@ -646,25 +686,7 @@ void EvalState::addConstant(const std::string & name, Value * v)
|
||||||
Value * EvalState::addPrimOp(const std::string & name,
|
Value * EvalState::addPrimOp(const std::string & name,
|
||||||
size_t arity, PrimOpFun primOp)
|
size_t arity, PrimOpFun primOp)
|
||||||
{
|
{
|
||||||
auto name2 = name.substr(0, 2) == "__" ? name.substr(2) : name;
|
return addPrimOp(PrimOp { .fun = primOp, .arity = arity, .name = name });
|
||||||
auto sym = symbols.create(name2);
|
|
||||||
|
|
||||||
/* Hack to make constants lazy: turn them into a application of
|
|
||||||
the primop to a dummy value. */
|
|
||||||
if (arity == 0) {
|
|
||||||
auto vPrimOp = allocValue();
|
|
||||||
vPrimOp->mkPrimOp(new PrimOp { .fun = primOp, .arity = 1, .name = name2 });
|
|
||||||
Value v;
|
|
||||||
v.mkApp(vPrimOp, vPrimOp);
|
|
||||||
return addConstant(name, v);
|
|
||||||
}
|
|
||||||
|
|
||||||
Value * v = allocValue();
|
|
||||||
v->mkPrimOp(new PrimOp { .fun = primOp, .arity = arity, .name = name2 });
|
|
||||||
staticBaseEnv->vars.emplace_back(symbols.create(name), baseEnvDispl);
|
|
||||||
baseEnv.values[baseEnvDispl++] = v;
|
|
||||||
baseEnv.values[0]->attrs->push_back(Attr(sym, v));
|
|
||||||
return v;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@ -842,176 +864,14 @@ void EvalState::runDebugRepl(const Error * error, const Env & env, const Expr &
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Every "format" object (even temporary) takes up a few hundred bytes
|
|
||||||
of stack space, which is a real killer in the recursive
|
|
||||||
evaluator. So here are some helper functions for throwing
|
|
||||||
exceptions. */
|
|
||||||
void EvalState::throwEvalError(const PosIdx pos, const char * s, Env & env, Expr & expr)
|
|
||||||
{
|
|
||||||
debugThrow(EvalError({
|
|
||||||
.msg = hintfmt(s),
|
|
||||||
.errPos = positions[pos]
|
|
||||||
}), env, expr);
|
|
||||||
}
|
|
||||||
|
|
||||||
void EvalState::throwEvalError(const PosIdx pos, const char * s)
|
|
||||||
{
|
|
||||||
debugThrowLastTrace(EvalError({
|
|
||||||
.msg = hintfmt(s),
|
|
||||||
.errPos = positions[pos]
|
|
||||||
}));
|
|
||||||
}
|
|
||||||
|
|
||||||
void EvalState::throwEvalError(const char * s, const std::string & s2)
|
|
||||||
{
|
|
||||||
debugThrowLastTrace(EvalError(s, s2));
|
|
||||||
}
|
|
||||||
|
|
||||||
void EvalState::throwEvalError(const PosIdx pos, const Suggestions & suggestions, const char * s,
|
|
||||||
const std::string & s2, Env & env, Expr & expr)
|
|
||||||
{
|
|
||||||
debugThrow(EvalError(ErrorInfo{
|
|
||||||
.msg = hintfmt(s, s2),
|
|
||||||
.errPos = positions[pos],
|
|
||||||
.suggestions = suggestions,
|
|
||||||
}), env, expr);
|
|
||||||
}
|
|
||||||
|
|
||||||
void EvalState::throwEvalError(const PosIdx pos, const char * s, const std::string & s2)
|
|
||||||
{
|
|
||||||
debugThrowLastTrace(EvalError({
|
|
||||||
.msg = hintfmt(s, s2),
|
|
||||||
.errPos = positions[pos]
|
|
||||||
}));
|
|
||||||
}
|
|
||||||
|
|
||||||
void EvalState::throwEvalError(const PosIdx pos, const char * s, const std::string & s2, Env & env, Expr & expr)
|
|
||||||
{
|
|
||||||
debugThrow(EvalError({
|
|
||||||
.msg = hintfmt(s, s2),
|
|
||||||
.errPos = positions[pos]
|
|
||||||
}), env, expr);
|
|
||||||
}
|
|
||||||
|
|
||||||
void EvalState::throwEvalError(const char * s, const std::string & s2,
|
|
||||||
const std::string & s3)
|
|
||||||
{
|
|
||||||
debugThrowLastTrace(EvalError({
|
|
||||||
.msg = hintfmt(s, s2, s3),
|
|
||||||
.errPos = positions[noPos]
|
|
||||||
}));
|
|
||||||
}
|
|
||||||
|
|
||||||
void EvalState::throwEvalError(const PosIdx pos, const char * s, const std::string & s2,
|
|
||||||
const std::string & s3)
|
|
||||||
{
|
|
||||||
debugThrowLastTrace(EvalError({
|
|
||||||
.msg = hintfmt(s, s2, s3),
|
|
||||||
.errPos = positions[pos]
|
|
||||||
}));
|
|
||||||
}
|
|
||||||
|
|
||||||
void EvalState::throwEvalError(const PosIdx pos, const char * s, const std::string & s2,
|
|
||||||
const std::string & s3, Env & env, Expr & expr)
|
|
||||||
{
|
|
||||||
debugThrow(EvalError({
|
|
||||||
.msg = hintfmt(s, s2, s3),
|
|
||||||
.errPos = positions[pos]
|
|
||||||
}), env, expr);
|
|
||||||
}
|
|
||||||
|
|
||||||
void EvalState::throwEvalError(const PosIdx p1, const char * s, const Symbol sym, const PosIdx p2, Env & env, Expr & expr)
|
|
||||||
{
|
|
||||||
// p1 is where the error occurred; p2 is a position mentioned in the message.
|
|
||||||
debugThrow(EvalError({
|
|
||||||
.msg = hintfmt(s, symbols[sym], positions[p2]),
|
|
||||||
.errPos = positions[p1]
|
|
||||||
}), env, expr);
|
|
||||||
}
|
|
||||||
|
|
||||||
void EvalState::throwTypeError(const PosIdx pos, const char * s, const Value & v)
|
|
||||||
{
|
|
||||||
debugThrowLastTrace(TypeError({
|
|
||||||
.msg = hintfmt(s, showType(v)),
|
|
||||||
.errPos = positions[pos]
|
|
||||||
}));
|
|
||||||
}
|
|
||||||
|
|
||||||
void EvalState::throwTypeError(const PosIdx pos, const char * s, const Value & v, Env & env, Expr & expr)
|
|
||||||
{
|
|
||||||
debugThrow(TypeError({
|
|
||||||
.msg = hintfmt(s, showType(v)),
|
|
||||||
.errPos = positions[pos]
|
|
||||||
}), env, expr);
|
|
||||||
}
|
|
||||||
|
|
||||||
void EvalState::throwTypeError(const PosIdx pos, const char * s)
|
|
||||||
{
|
|
||||||
debugThrowLastTrace(TypeError({
|
|
||||||
.msg = hintfmt(s),
|
|
||||||
.errPos = positions[pos]
|
|
||||||
}));
|
|
||||||
}
|
|
||||||
|
|
||||||
void EvalState::throwTypeError(const PosIdx pos, const char * s, const ExprLambda & fun,
|
|
||||||
const Symbol s2, Env & env, Expr &expr)
|
|
||||||
{
|
|
||||||
debugThrow(TypeError({
|
|
||||||
.msg = hintfmt(s, fun.showNamePos(*this), symbols[s2]),
|
|
||||||
.errPos = positions[pos]
|
|
||||||
}), env, expr);
|
|
||||||
}
|
|
||||||
|
|
||||||
void EvalState::throwTypeError(const PosIdx pos, const Suggestions & suggestions, const char * s,
|
|
||||||
const ExprLambda & fun, const Symbol s2, Env & env, Expr &expr)
|
|
||||||
{
|
|
||||||
debugThrow(TypeError(ErrorInfo {
|
|
||||||
.msg = hintfmt(s, fun.showNamePos(*this), symbols[s2]),
|
|
||||||
.errPos = positions[pos],
|
|
||||||
.suggestions = suggestions,
|
|
||||||
}), env, expr);
|
|
||||||
}
|
|
||||||
|
|
||||||
void EvalState::throwTypeError(const char * s, const Value & v, Env & env, Expr &expr)
|
|
||||||
{
|
|
||||||
debugThrow(TypeError({
|
|
||||||
.msg = hintfmt(s, showType(v)),
|
|
||||||
.errPos = positions[expr.getPos()],
|
|
||||||
}), env, expr);
|
|
||||||
}
|
|
||||||
|
|
||||||
void EvalState::throwAssertionError(const PosIdx pos, const char * s, const std::string & s1, Env & env, Expr &expr)
|
|
||||||
{
|
|
||||||
debugThrow(AssertionError({
|
|
||||||
.msg = hintfmt(s, s1),
|
|
||||||
.errPos = positions[pos]
|
|
||||||
}), env, expr);
|
|
||||||
}
|
|
||||||
|
|
||||||
void EvalState::throwUndefinedVarError(const PosIdx pos, const char * s, const std::string & s1, Env & env, Expr &expr)
|
|
||||||
{
|
|
||||||
debugThrow(UndefinedVarError({
|
|
||||||
.msg = hintfmt(s, s1),
|
|
||||||
.errPos = positions[pos]
|
|
||||||
}), env, expr);
|
|
||||||
}
|
|
||||||
|
|
||||||
void EvalState::throwMissingArgumentError(const PosIdx pos, const char * s, const std::string & s1, Env & env, Expr &expr)
|
|
||||||
{
|
|
||||||
debugThrow(MissingArgumentError({
|
|
||||||
.msg = hintfmt(s, s1),
|
|
||||||
.errPos = positions[pos]
|
|
||||||
}), env, expr);
|
|
||||||
}
|
|
||||||
|
|
||||||
void EvalState::addErrorTrace(Error & e, const char * s, const std::string & s2) const
|
void EvalState::addErrorTrace(Error & e, const char * s, const std::string & s2) const
|
||||||
{
|
{
|
||||||
e.addTrace(nullptr, s, s2);
|
e.addTrace(nullptr, s, s2);
|
||||||
}
|
}
|
||||||
|
|
||||||
void EvalState::addErrorTrace(Error & e, const PosIdx pos, const char * s, const std::string & s2) const
|
void EvalState::addErrorTrace(Error & e, const PosIdx pos, const char * s, const std::string & s2, bool frame) const
|
||||||
{
|
{
|
||||||
e.addTrace(positions[pos], s, s2);
|
e.addTrace(positions[pos], hintfmt(s, s2), frame);
|
||||||
}
|
}
|
||||||
|
|
||||||
static std::unique_ptr<DebugTraceStacker> makeDebugTraceStacker(
|
static std::unique_ptr<DebugTraceStacker> makeDebugTraceStacker(
|
||||||
|
@ -1088,7 +948,7 @@ inline Value * EvalState::lookupVar(Env * env, const ExprVar & var, bool noEval)
|
||||||
if (env->type == Env::HasWithExpr) {
|
if (env->type == Env::HasWithExpr) {
|
||||||
if (noEval) return 0;
|
if (noEval) return 0;
|
||||||
Value * v = allocValue();
|
Value * v = allocValue();
|
||||||
evalAttrs(*env->up, (Expr *) env->values[0], *v);
|
evalAttrs(*env->up, (Expr *) env->values[0], *v, noPos, "<borked>");
|
||||||
env->values[0] = v;
|
env->values[0] = v;
|
||||||
env->type = Env::HasWithAttrs;
|
env->type = Env::HasWithAttrs;
|
||||||
}
|
}
|
||||||
|
@ -1098,7 +958,7 @@ inline Value * EvalState::lookupVar(Env * env, const ExprVar & var, bool noEval)
|
||||||
return j->value;
|
return j->value;
|
||||||
}
|
}
|
||||||
if (!env->prevWith)
|
if (!env->prevWith)
|
||||||
throwUndefinedVarError(var.pos, "undefined variable '%1%'", symbols[var.name], *env, const_cast<ExprVar&>(var));
|
error("undefined variable '%1%'", symbols[var.name]).atPos(var.pos).withFrame(*env, var).debugThrow<UndefinedVarError>();
|
||||||
for (size_t l = env->prevWith; l; --l, env = env->up) ;
|
for (size_t l = env->prevWith; l; --l, env = env->up) ;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1248,7 +1108,7 @@ void EvalState::cacheFile(
|
||||||
// computation.
|
// computation.
|
||||||
if (mustBeTrivial &&
|
if (mustBeTrivial &&
|
||||||
!(dynamic_cast<ExprAttrs *>(e)))
|
!(dynamic_cast<ExprAttrs *>(e)))
|
||||||
throw EvalError("file '%s' must be an attribute set", path);
|
error("file '%s' must be an attribute set", path).debugThrow<EvalError>();
|
||||||
eval(e, v);
|
eval(e, v);
|
||||||
} catch (Error & e) {
|
} catch (Error & e) {
|
||||||
addErrorTrace(e, "while evaluating the file '%1%':", resolvedPath);
|
addErrorTrace(e, "while evaluating the file '%1%':", resolvedPath);
|
||||||
|
@ -1266,31 +1126,31 @@ void EvalState::eval(Expr * e, Value & v)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
inline bool EvalState::evalBool(Env & env, Expr * e)
|
inline bool EvalState::evalBool(Env & env, Expr * e, const PosIdx pos, std::string_view errorCtx)
|
||||||
{
|
{
|
||||||
|
try {
|
||||||
Value v;
|
Value v;
|
||||||
e->eval(*this, env, v);
|
e->eval(*this, env, v);
|
||||||
if (v.type() != nBool)
|
if (v.type() != nBool)
|
||||||
throwTypeError(noPos, "value is %1% while a Boolean was expected", v, env, *e);
|
error("value is %1% while a Boolean was expected", showType(v)).withFrame(env, *e).debugThrow<TypeError>();
|
||||||
return v.boolean;
|
return v.boolean;
|
||||||
|
} catch (Error & e) {
|
||||||
|
e.addTrace(positions[pos], errorCtx);
|
||||||
|
throw;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
inline bool EvalState::evalBool(Env & env, Expr * e, const PosIdx pos)
|
inline void EvalState::evalAttrs(Env & env, Expr * e, Value & v, const PosIdx pos, std::string_view errorCtx)
|
||||||
{
|
|
||||||
Value v;
|
|
||||||
e->eval(*this, env, v);
|
|
||||||
if (v.type() != nBool)
|
|
||||||
throwTypeError(pos, "value is %1% while a Boolean was expected", v, env, *e);
|
|
||||||
return v.boolean;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
inline void EvalState::evalAttrs(Env & env, Expr * e, Value & v)
|
|
||||||
{
|
{
|
||||||
|
try {
|
||||||
e->eval(*this, env, v);
|
e->eval(*this, env, v);
|
||||||
if (v.type() != nAttrs)
|
if (v.type() != nAttrs)
|
||||||
throwTypeError(noPos, "value is %1% while a set was expected", v, env, *e);
|
error("value is %1% while a set was expected", showType(v)).withFrame(env, *e).debugThrow<TypeError>();
|
||||||
|
} catch (Error & e) {
|
||||||
|
e.addTrace(positions[pos], errorCtx);
|
||||||
|
throw;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@ -1363,7 +1223,7 @@ void ExprAttrs::eval(EvalState & state, Env & env, Value & v)
|
||||||
Hence we need __overrides.) */
|
Hence we need __overrides.) */
|
||||||
if (hasOverrides) {
|
if (hasOverrides) {
|
||||||
Value * vOverrides = (*v.attrs)[overrides->second.displ].value;
|
Value * vOverrides = (*v.attrs)[overrides->second.displ].value;
|
||||||
state.forceAttrs(*vOverrides, [&]() { return vOverrides->determinePos(noPos); });
|
state.forceAttrs(*vOverrides, [&]() { return vOverrides->determinePos(noPos); }, "while evaluating the `__overrides` attribute");
|
||||||
Bindings * newBnds = state.allocBindings(v.attrs->capacity() + vOverrides->attrs->size());
|
Bindings * newBnds = state.allocBindings(v.attrs->capacity() + vOverrides->attrs->size());
|
||||||
for (auto & i : *v.attrs)
|
for (auto & i : *v.attrs)
|
||||||
newBnds->push_back(i);
|
newBnds->push_back(i);
|
||||||
|
@ -1391,11 +1251,11 @@ void ExprAttrs::eval(EvalState & state, Env & env, Value & v)
|
||||||
state.forceValue(nameVal, i.pos);
|
state.forceValue(nameVal, i.pos);
|
||||||
if (nameVal.type() == nNull)
|
if (nameVal.type() == nNull)
|
||||||
continue;
|
continue;
|
||||||
state.forceStringNoCtx(nameVal);
|
state.forceStringNoCtx(nameVal, i.pos, "while evaluating the name of a dynamic attribute");
|
||||||
auto nameSym = state.symbols.create(nameVal.string.s);
|
auto nameSym = state.symbols.create(nameVal.string.s);
|
||||||
Bindings::iterator j = v.attrs->find(nameSym);
|
Bindings::iterator j = v.attrs->find(nameSym);
|
||||||
if (j != v.attrs->end())
|
if (j != v.attrs->end())
|
||||||
state.throwEvalError(i.pos, "dynamic attribute '%1%' already defined at %2%", nameSym, j->pos, env, *this);
|
state.error("dynamic attribute '%1%' already defined at %2%", state.symbols[nameSym], state.positions[j->pos]).atPos(i.pos).withFrame(env, *this).debugThrow<EvalError>();
|
||||||
|
|
||||||
i.valueExpr->setName(nameSym);
|
i.valueExpr->setName(nameSym);
|
||||||
/* Keep sorted order so find can catch duplicates */
|
/* Keep sorted order so find can catch duplicates */
|
||||||
|
@ -1492,15 +1352,14 @@ void ExprSelect::eval(EvalState & state, Env & env, Value & v)
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
state.forceAttrs(*vAttrs, pos);
|
state.forceAttrs(*vAttrs, pos, "while selecting an attribute");
|
||||||
if ((j = vAttrs->attrs->find(name)) == vAttrs->attrs->end()) {
|
if ((j = vAttrs->attrs->find(name)) == vAttrs->attrs->end()) {
|
||||||
std::set<std::string> allAttrNames;
|
std::set<std::string> allAttrNames;
|
||||||
for (auto & attr : *vAttrs->attrs)
|
for (auto & attr : *vAttrs->attrs)
|
||||||
allAttrNames.insert(state.symbols[attr.name]);
|
allAttrNames.insert(state.symbols[attr.name]);
|
||||||
state.throwEvalError(
|
auto suggestions = Suggestions::bestMatches(allAttrNames, state.symbols[name]);
|
||||||
pos,
|
state.error("attribute '%1%' missing", state.symbols[name])
|
||||||
Suggestions::bestMatches(allAttrNames, state.symbols[name]),
|
.atPos(pos).withSuggestions(suggestions).withFrame(env, *this).debugThrow<EvalError>();
|
||||||
"attribute '%1%' missing", state.symbols[name], env, *this);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
vAttrs = j->value;
|
vAttrs = j->value;
|
||||||
|
@ -1595,7 +1454,12 @@ void EvalState::callFunction(Value & fun, size_t nrArgs, Value * * args, Value &
|
||||||
if (!lambda.hasFormals())
|
if (!lambda.hasFormals())
|
||||||
env2.values[displ++] = args[0];
|
env2.values[displ++] = args[0];
|
||||||
else {
|
else {
|
||||||
forceAttrs(*args[0], pos);
|
try {
|
||||||
|
forceAttrs(*args[0], lambda.pos, "while evaluating the value passed for the lambda argument");
|
||||||
|
} catch (Error & e) {
|
||||||
|
if (pos) e.addTrace(positions[pos], "from call site");
|
||||||
|
throw;
|
||||||
|
}
|
||||||
|
|
||||||
if (lambda.arg)
|
if (lambda.arg)
|
||||||
env2.values[displ++] = args[0];
|
env2.values[displ++] = args[0];
|
||||||
|
@ -1607,8 +1471,15 @@ void EvalState::callFunction(Value & fun, size_t nrArgs, Value * * args, Value &
|
||||||
for (auto & i : lambda.formals->formals) {
|
for (auto & i : lambda.formals->formals) {
|
||||||
auto j = args[0]->attrs->get(i.name);
|
auto j = args[0]->attrs->get(i.name);
|
||||||
if (!j) {
|
if (!j) {
|
||||||
if (!i.def) throwTypeError(pos, "%1% called without required argument '%2%'",
|
if (!i.def) {
|
||||||
lambda, i.name, *fun.lambda.env, lambda);
|
error("function '%1%' called without required argument '%2%'",
|
||||||
|
(lambda.name ? std::string(symbols[lambda.name]) : "anonymous lambda"),
|
||||||
|
symbols[i.name])
|
||||||
|
.atPos(lambda.pos)
|
||||||
|
.withTrace(pos, "from call site")
|
||||||
|
.withFrame(*fun.lambda.env, lambda)
|
||||||
|
.debugThrow<TypeError>();
|
||||||
|
}
|
||||||
env2.values[displ++] = i.def->maybeThunk(*this, env2);
|
env2.values[displ++] = i.def->maybeThunk(*this, env2);
|
||||||
} else {
|
} else {
|
||||||
attrsUsed++;
|
attrsUsed++;
|
||||||
|
@ -1626,11 +1497,15 @@ void EvalState::callFunction(Value & fun, size_t nrArgs, Value * * args, Value &
|
||||||
std::set<std::string> formalNames;
|
std::set<std::string> formalNames;
|
||||||
for (auto & formal : lambda.formals->formals)
|
for (auto & formal : lambda.formals->formals)
|
||||||
formalNames.insert(symbols[formal.name]);
|
formalNames.insert(symbols[formal.name]);
|
||||||
throwTypeError(
|
auto suggestions = Suggestions::bestMatches(formalNames, symbols[i.name]);
|
||||||
pos,
|
error("function '%1%' called with unexpected argument '%2%'",
|
||||||
Suggestions::bestMatches(formalNames, symbols[i.name]),
|
(lambda.name ? std::string(symbols[lambda.name]) : "anonymous lambda"),
|
||||||
"%1% called with unexpected argument '%2%'",
|
symbols[i.name])
|
||||||
lambda, i.name, *fun.lambda.env, lambda);
|
.atPos(lambda.pos)
|
||||||
|
.withTrace(pos, "from call site")
|
||||||
|
.withSuggestions(suggestions)
|
||||||
|
.withFrame(*fun.lambda.env, lambda)
|
||||||
|
.debugThrow<TypeError>();
|
||||||
}
|
}
|
||||||
abort(); // can't happen
|
abort(); // can't happen
|
||||||
}
|
}
|
||||||
|
@ -1653,11 +1528,15 @@ void EvalState::callFunction(Value & fun, size_t nrArgs, Value * * args, Value &
|
||||||
lambda.body->eval(*this, env2, vCur);
|
lambda.body->eval(*this, env2, vCur);
|
||||||
} catch (Error & e) {
|
} catch (Error & e) {
|
||||||
if (loggerSettings.showTrace.get()) {
|
if (loggerSettings.showTrace.get()) {
|
||||||
addErrorTrace(e, lambda.pos, "while calling %s",
|
addErrorTrace(
|
||||||
(lambda.name
|
e,
|
||||||
|
lambda.pos,
|
||||||
|
"while calling %s",
|
||||||
|
lambda.name
|
||||||
? concatStrings("'", symbols[lambda.name], "'")
|
? concatStrings("'", symbols[lambda.name], "'")
|
||||||
: "anonymous lambda"));
|
: "anonymous lambda",
|
||||||
addErrorTrace(e, pos, "while evaluating call site%s", "");
|
true);
|
||||||
|
if (pos) addErrorTrace(e, pos, "from call site%s", "", true);
|
||||||
}
|
}
|
||||||
throw;
|
throw;
|
||||||
}
|
}
|
||||||
|
@ -1676,9 +1555,17 @@ void EvalState::callFunction(Value & fun, size_t nrArgs, Value * * args, Value &
|
||||||
return;
|
return;
|
||||||
} else {
|
} else {
|
||||||
/* We have all the arguments, so call the primop. */
|
/* We have all the arguments, so call the primop. */
|
||||||
|
auto name = vCur.primOp->name;
|
||||||
|
|
||||||
nrPrimOpCalls++;
|
nrPrimOpCalls++;
|
||||||
if (countCalls) primOpCalls[vCur.primOp->name]++;
|
if (countCalls) primOpCalls[name]++;
|
||||||
vCur.primOp->fun(*this, pos, args, vCur);
|
|
||||||
|
try {
|
||||||
|
vCur.primOp->fun(*this, noPos, args, vCur);
|
||||||
|
} catch (Error & e) {
|
||||||
|
addErrorTrace(e, pos, "while calling the '%1%' builtin", name);
|
||||||
|
throw;
|
||||||
|
}
|
||||||
|
|
||||||
nrArgs -= argsLeft;
|
nrArgs -= argsLeft;
|
||||||
args += argsLeft;
|
args += argsLeft;
|
||||||
|
@ -1713,9 +1600,20 @@ void EvalState::callFunction(Value & fun, size_t nrArgs, Value * * args, Value &
|
||||||
for (size_t i = 0; i < argsLeft; ++i)
|
for (size_t i = 0; i < argsLeft; ++i)
|
||||||
vArgs[argsDone + i] = args[i];
|
vArgs[argsDone + i] = args[i];
|
||||||
|
|
||||||
|
auto name = primOp->primOp->name;
|
||||||
nrPrimOpCalls++;
|
nrPrimOpCalls++;
|
||||||
if (countCalls) primOpCalls[primOp->primOp->name]++;
|
if (countCalls) primOpCalls[name]++;
|
||||||
primOp->primOp->fun(*this, pos, vArgs, vCur);
|
|
||||||
|
try {
|
||||||
|
// TODO:
|
||||||
|
// 1. Unify this and above code. Heavily redundant.
|
||||||
|
// 2. Create a fake env (arg1, arg2, etc.) and a fake expr (arg1: arg2: etc: builtins.name arg1 arg2 etc)
|
||||||
|
// so the debugger allows to inspect the wrong parameters passed to the builtin.
|
||||||
|
primOp->primOp->fun(*this, noPos, vArgs, vCur);
|
||||||
|
} catch (Error & e) {
|
||||||
|
addErrorTrace(e, pos, "while calling the '%1%' builtin", name);
|
||||||
|
throw;
|
||||||
|
}
|
||||||
|
|
||||||
nrArgs -= argsLeft;
|
nrArgs -= argsLeft;
|
||||||
args += argsLeft;
|
args += argsLeft;
|
||||||
|
@ -1728,14 +1626,18 @@ void EvalState::callFunction(Value & fun, size_t nrArgs, Value * * args, Value &
|
||||||
heap-allocate a copy and use that instead. */
|
heap-allocate a copy and use that instead. */
|
||||||
Value * args2[] = {allocValue(), args[0]};
|
Value * args2[] = {allocValue(), args[0]};
|
||||||
*args2[0] = vCur;
|
*args2[0] = vCur;
|
||||||
/* !!! Should we use the attr pos here? */
|
try {
|
||||||
callFunction(*functor->value, 2, args2, vCur, pos);
|
callFunction(*functor->value, 2, args2, vCur, functor->pos);
|
||||||
|
} catch (Error & e) {
|
||||||
|
e.addTrace(positions[pos], "while calling a functor (an attribute set with a '__functor' attribute)");
|
||||||
|
throw;
|
||||||
|
}
|
||||||
nrArgs--;
|
nrArgs--;
|
||||||
args++;
|
args++;
|
||||||
}
|
}
|
||||||
|
|
||||||
else
|
else
|
||||||
throwTypeError(pos, "attempt to call something which is not a function but %1%", vCur);
|
error("attempt to call something which is not a function but %1%", showType(vCur)).atPos(pos).debugThrow<TypeError>();
|
||||||
}
|
}
|
||||||
|
|
||||||
vRes = vCur;
|
vRes = vCur;
|
||||||
|
@ -1799,13 +1701,12 @@ void EvalState::autoCallFunction(Bindings & args, Value & fun, Value & res)
|
||||||
if (j != args.end()) {
|
if (j != args.end()) {
|
||||||
attrs.insert(*j);
|
attrs.insert(*j);
|
||||||
} else if (!i.def) {
|
} else if (!i.def) {
|
||||||
throwMissingArgumentError(i.pos, R"(cannot evaluate a function that has an argument without a value ('%1%')
|
error(R"(cannot evaluate a function that has an argument without a value ('%1%')
|
||||||
|
|
||||||
Nix attempted to evaluate a function as a top level expression; in
|
Nix attempted to evaluate a function as a top level expression; in
|
||||||
this case it must have its arguments supplied either by default
|
this case it must have its arguments supplied either by default
|
||||||
values, or passed explicitly with '--arg' or '--argstr'. See
|
values, or passed explicitly with '--arg' or '--argstr'. See
|
||||||
https://nixos.org/manual/nix/stable/language/constructs.html#functions.)", symbols[i.name],
|
https://nixos.org/manual/nix/stable/language/constructs.html#functions.)", symbols[i.name])
|
||||||
*fun.lambda.env, *fun.lambda.fun);
|
.atPos(i.pos).withFrame(*fun.lambda.env, *fun.lambda.fun).debugThrow<MissingArgumentError>();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1828,16 +1729,17 @@ void ExprWith::eval(EvalState & state, Env & env, Value & v)
|
||||||
|
|
||||||
void ExprIf::eval(EvalState & state, Env & env, Value & v)
|
void ExprIf::eval(EvalState & state, Env & env, Value & v)
|
||||||
{
|
{
|
||||||
(state.evalBool(env, cond, pos) ? then : else_)->eval(state, env, v);
|
// We cheat in the parser, and pass the position of the condition as the position of the if itself.
|
||||||
|
(state.evalBool(env, cond, pos, "while evaluating a branch condition") ? then : else_)->eval(state, env, v);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
void ExprAssert::eval(EvalState & state, Env & env, Value & v)
|
void ExprAssert::eval(EvalState & state, Env & env, Value & v)
|
||||||
{
|
{
|
||||||
if (!state.evalBool(env, cond, pos)) {
|
if (!state.evalBool(env, cond, pos, "in the condition of the assert statement")) {
|
||||||
std::ostringstream out;
|
std::ostringstream out;
|
||||||
cond->show(state.symbols, out);
|
cond->show(state.symbols, out);
|
||||||
state.throwAssertionError(pos, "assertion '%1%' failed", out.str(), env, *this);
|
state.error("assertion '%1%' failed", out.str()).atPos(pos).withFrame(env, *this).debugThrow<AssertionError>();
|
||||||
}
|
}
|
||||||
body->eval(state, env, v);
|
body->eval(state, env, v);
|
||||||
}
|
}
|
||||||
|
@ -1845,7 +1747,7 @@ void ExprAssert::eval(EvalState & state, Env & env, Value & v)
|
||||||
|
|
||||||
void ExprOpNot::eval(EvalState & state, Env & env, Value & v)
|
void ExprOpNot::eval(EvalState & state, Env & env, Value & v)
|
||||||
{
|
{
|
||||||
v.mkBool(!state.evalBool(env, e));
|
v.mkBool(!state.evalBool(env, e, noPos, "in the argument of the not operator")); // XXX: FIXME: !
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@ -1853,7 +1755,7 @@ void ExprOpEq::eval(EvalState & state, Env & env, Value & v)
|
||||||
{
|
{
|
||||||
Value v1; e1->eval(state, env, v1);
|
Value v1; e1->eval(state, env, v1);
|
||||||
Value v2; e2->eval(state, env, v2);
|
Value v2; e2->eval(state, env, v2);
|
||||||
v.mkBool(state.eqValues(v1, v2));
|
v.mkBool(state.eqValues(v1, v2, pos, "while testing two values for equality"));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@ -1861,33 +1763,33 @@ void ExprOpNEq::eval(EvalState & state, Env & env, Value & v)
|
||||||
{
|
{
|
||||||
Value v1; e1->eval(state, env, v1);
|
Value v1; e1->eval(state, env, v1);
|
||||||
Value v2; e2->eval(state, env, v2);
|
Value v2; e2->eval(state, env, v2);
|
||||||
v.mkBool(!state.eqValues(v1, v2));
|
v.mkBool(!state.eqValues(v1, v2, pos, "while testing two values for inequality"));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
void ExprOpAnd::eval(EvalState & state, Env & env, Value & v)
|
void ExprOpAnd::eval(EvalState & state, Env & env, Value & v)
|
||||||
{
|
{
|
||||||
v.mkBool(state.evalBool(env, e1, pos) && state.evalBool(env, e2, pos));
|
v.mkBool(state.evalBool(env, e1, pos, "in the left operand of the AND (&&) operator") && state.evalBool(env, e2, pos, "in the right operand of the AND (&&) operator"));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
void ExprOpOr::eval(EvalState & state, Env & env, Value & v)
|
void ExprOpOr::eval(EvalState & state, Env & env, Value & v)
|
||||||
{
|
{
|
||||||
v.mkBool(state.evalBool(env, e1, pos) || state.evalBool(env, e2, pos));
|
v.mkBool(state.evalBool(env, e1, pos, "in the left operand of the OR (||) operator") || state.evalBool(env, e2, pos, "in the right operand of the OR (||) operator"));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
void ExprOpImpl::eval(EvalState & state, Env & env, Value & v)
|
void ExprOpImpl::eval(EvalState & state, Env & env, Value & v)
|
||||||
{
|
{
|
||||||
v.mkBool(!state.evalBool(env, e1, pos) || state.evalBool(env, e2, pos));
|
v.mkBool(!state.evalBool(env, e1, pos, "in the left operand of the IMPL (->) operator") || state.evalBool(env, e2, pos, "in the right operand of the IMPL (->) operator"));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
void ExprOpUpdate::eval(EvalState & state, Env & env, Value & v)
|
void ExprOpUpdate::eval(EvalState & state, Env & env, Value & v)
|
||||||
{
|
{
|
||||||
Value v1, v2;
|
Value v1, v2;
|
||||||
state.evalAttrs(env, e1, v1);
|
state.evalAttrs(env, e1, v1, pos, "in the left operand of the update (//) operator");
|
||||||
state.evalAttrs(env, e2, v2);
|
state.evalAttrs(env, e2, v2, pos, "in the right operand of the update (//) operator");
|
||||||
|
|
||||||
state.nrOpUpdates++;
|
state.nrOpUpdates++;
|
||||||
|
|
||||||
|
@ -1926,18 +1828,18 @@ void ExprOpConcatLists::eval(EvalState & state, Env & env, Value & v)
|
||||||
Value v1; e1->eval(state, env, v1);
|
Value v1; e1->eval(state, env, v1);
|
||||||
Value v2; e2->eval(state, env, v2);
|
Value v2; e2->eval(state, env, v2);
|
||||||
Value * lists[2] = { &v1, &v2 };
|
Value * lists[2] = { &v1, &v2 };
|
||||||
state.concatLists(v, 2, lists, pos);
|
state.concatLists(v, 2, lists, pos, "while evaluating one of the elements to concatenate");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
void EvalState::concatLists(Value & v, size_t nrLists, Value * * lists, const PosIdx pos)
|
void EvalState::concatLists(Value & v, size_t nrLists, Value * * lists, const PosIdx pos, std::string_view errorCtx)
|
||||||
{
|
{
|
||||||
nrListConcats++;
|
nrListConcats++;
|
||||||
|
|
||||||
Value * nonEmpty = 0;
|
Value * nonEmpty = 0;
|
||||||
size_t len = 0;
|
size_t len = 0;
|
||||||
for (size_t n = 0; n < nrLists; ++n) {
|
for (size_t n = 0; n < nrLists; ++n) {
|
||||||
forceList(*lists[n], pos);
|
forceList(*lists[n], pos, errorCtx);
|
||||||
auto l = lists[n]->listSize();
|
auto l = lists[n]->listSize();
|
||||||
len += l;
|
len += l;
|
||||||
if (l) nonEmpty = lists[n];
|
if (l) nonEmpty = lists[n];
|
||||||
|
@ -2014,20 +1916,22 @@ void ExprConcatStrings::eval(EvalState & state, Env & env, Value & v)
|
||||||
nf = n;
|
nf = n;
|
||||||
nf += vTmp.fpoint;
|
nf += vTmp.fpoint;
|
||||||
} else
|
} else
|
||||||
state.throwEvalError(i_pos, "cannot add %1% to an integer", showType(vTmp), env, *this);
|
state.error("cannot add %1% to an integer", showType(vTmp)).atPos(i_pos).withFrame(env, *this).debugThrow<EvalError>();
|
||||||
} else if (firstType == nFloat) {
|
} else if (firstType == nFloat) {
|
||||||
if (vTmp.type() == nInt) {
|
if (vTmp.type() == nInt) {
|
||||||
nf += vTmp.integer;
|
nf += vTmp.integer;
|
||||||
} else if (vTmp.type() == nFloat) {
|
} else if (vTmp.type() == nFloat) {
|
||||||
nf += vTmp.fpoint;
|
nf += vTmp.fpoint;
|
||||||
} else
|
} else
|
||||||
state.throwEvalError(i_pos, "cannot add %1% to a float", showType(vTmp), env, *this);
|
state.error("cannot add %1% to a float", showType(vTmp)).atPos(i_pos).withFrame(env, *this).debugThrow<EvalError>();
|
||||||
} else {
|
} else {
|
||||||
if (s.empty()) s.reserve(es->size());
|
if (s.empty()) s.reserve(es->size());
|
||||||
/* skip canonization of first path, which would only be not
|
/* skip canonization of first path, which would only be not
|
||||||
canonized in the first place if it's coming from a ./${foo} type
|
canonized in the first place if it's coming from a ./${foo} type
|
||||||
path */
|
path */
|
||||||
auto part = state.coerceToString(i_pos, vTmp, context, false, firstType == nString, !first);
|
auto part = state.coerceToString(i_pos, vTmp, context,
|
||||||
|
"while evaluating a path segment",
|
||||||
|
false, firstType == nString, !first);
|
||||||
sSize += part->size();
|
sSize += part->size();
|
||||||
s.emplace_back(std::move(part));
|
s.emplace_back(std::move(part));
|
||||||
}
|
}
|
||||||
|
@ -2041,7 +1945,7 @@ void ExprConcatStrings::eval(EvalState & state, Env & env, Value & v)
|
||||||
v.mkFloat(nf);
|
v.mkFloat(nf);
|
||||||
else if (firstType == nPath) {
|
else if (firstType == nPath) {
|
||||||
if (!context.empty())
|
if (!context.empty())
|
||||||
state.throwEvalError(pos, "a string that refers to a store path cannot be appended to a path", env, *this);
|
state.error("a string that refers to a store path cannot be appended to a path").atPos(pos).withFrame(env, *this).debugThrow<EvalError>();
|
||||||
v.mkPath(canonPath(str()));
|
v.mkPath(canonPath(str()));
|
||||||
} else
|
} else
|
||||||
v.mkStringMove(c_str(), context);
|
v.mkStringMove(c_str(), context);
|
||||||
|
@ -2091,33 +1995,47 @@ void EvalState::forceValueDeep(Value & v)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
NixInt EvalState::forceInt(Value & v, const PosIdx pos)
|
NixInt EvalState::forceInt(Value & v, const PosIdx pos, std::string_view errorCtx)
|
||||||
{
|
{
|
||||||
|
try {
|
||||||
forceValue(v, pos);
|
forceValue(v, pos);
|
||||||
if (v.type() != nInt)
|
if (v.type() != nInt)
|
||||||
throwTypeError(pos, "value is %1% while an integer was expected", v);
|
error("value is %1% while an integer was expected", showType(v)).debugThrow<TypeError>();
|
||||||
|
|
||||||
return v.integer;
|
return v.integer;
|
||||||
|
} catch (Error & e) {
|
||||||
|
e.addTrace(positions[pos], errorCtx);
|
||||||
|
throw;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
NixFloat EvalState::forceFloat(Value & v, const PosIdx pos)
|
NixFloat EvalState::forceFloat(Value & v, const PosIdx pos, std::string_view errorCtx)
|
||||||
{
|
{
|
||||||
|
try {
|
||||||
forceValue(v, pos);
|
forceValue(v, pos);
|
||||||
if (v.type() == nInt)
|
if (v.type() == nInt)
|
||||||
return v.integer;
|
return v.integer;
|
||||||
else if (v.type() != nFloat)
|
else if (v.type() != nFloat)
|
||||||
throwTypeError(pos, "value is %1% while a float was expected", v);
|
error("value is %1% while a float was expected", showType(v)).debugThrow<TypeError>();
|
||||||
return v.fpoint;
|
return v.fpoint;
|
||||||
|
} catch (Error & e) {
|
||||||
|
e.addTrace(positions[pos], errorCtx);
|
||||||
|
throw;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
bool EvalState::forceBool(Value & v, const PosIdx pos)
|
bool EvalState::forceBool(Value & v, const PosIdx pos, std::string_view errorCtx)
|
||||||
{
|
{
|
||||||
|
try {
|
||||||
forceValue(v, pos);
|
forceValue(v, pos);
|
||||||
if (v.type() != nBool)
|
if (v.type() != nBool)
|
||||||
throwTypeError(pos, "value is %1% while a Boolean was expected", v);
|
error("value is %1% while a Boolean was expected", showType(v)).debugThrow<TypeError>();
|
||||||
return v.boolean;
|
return v.boolean;
|
||||||
|
} catch (Error & e) {
|
||||||
|
e.addTrace(positions[pos], errorCtx);
|
||||||
|
throw;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@ -2127,21 +2045,30 @@ bool EvalState::isFunctor(Value & fun)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
void EvalState::forceFunction(Value & v, const PosIdx pos)
|
void EvalState::forceFunction(Value & v, const PosIdx pos, std::string_view errorCtx)
|
||||||
{
|
{
|
||||||
|
try {
|
||||||
forceValue(v, pos);
|
forceValue(v, pos);
|
||||||
if (v.type() != nFunction && !isFunctor(v))
|
if (v.type() != nFunction && !isFunctor(v))
|
||||||
throwTypeError(pos, "value is %1% while a function was expected", v);
|
error("value is %1% while a function was expected", showType(v)).debugThrow<TypeError>();
|
||||||
|
} catch (Error & e) {
|
||||||
|
e.addTrace(positions[pos], errorCtx);
|
||||||
|
throw;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
std::string_view EvalState::forceString(Value & v, const PosIdx pos)
|
std::string_view EvalState::forceString(Value & v, const PosIdx pos, std::string_view errorCtx)
|
||||||
{
|
{
|
||||||
|
try {
|
||||||
forceValue(v, pos);
|
forceValue(v, pos);
|
||||||
if (v.type() != nString) {
|
if (v.type() != nString)
|
||||||
throwTypeError(pos, "value is %1% while a string was expected", v);
|
error("value is %1% while a string was expected", showType(v)).debugThrow<TypeError>();
|
||||||
}
|
|
||||||
return v.string.s;
|
return v.string.s;
|
||||||
|
} catch (Error & e) {
|
||||||
|
e.addTrace(positions[pos], errorCtx);
|
||||||
|
throw;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@ -2164,24 +2091,19 @@ NixStringContext Value::getContext(const Store & store)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
std::string_view EvalState::forceString(Value & v, PathSet & context, const PosIdx pos)
|
std::string_view EvalState::forceString(Value & v, PathSet & context, const PosIdx pos, std::string_view errorCtx)
|
||||||
{
|
{
|
||||||
auto s = forceString(v, pos);
|
auto s = forceString(v, pos, errorCtx);
|
||||||
copyContext(v, context);
|
copyContext(v, context);
|
||||||
return s;
|
return s;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
std::string_view EvalState::forceStringNoCtx(Value & v, const PosIdx pos)
|
std::string_view EvalState::forceStringNoCtx(Value & v, const PosIdx pos, std::string_view errorCtx)
|
||||||
{
|
{
|
||||||
auto s = forceString(v, pos);
|
auto s = forceString(v, pos, errorCtx);
|
||||||
if (v.string.context) {
|
if (v.string.context) {
|
||||||
if (pos)
|
error("the string '%1%' is not allowed to refer to a store path (such as '%2%')", v.string.s, v.string.context[0]).withTrace(pos, errorCtx).debugThrow<EvalError>();
|
||||||
throwEvalError(pos, "the string '%1%' is not allowed to refer to a store path (such as '%2%')",
|
|
||||||
v.string.s, v.string.context[0]);
|
|
||||||
else
|
|
||||||
throwEvalError("the string '%1%' is not allowed to refer to a store path (such as '%2%')",
|
|
||||||
v.string.s, v.string.context[0]);
|
|
||||||
}
|
}
|
||||||
return s;
|
return s;
|
||||||
}
|
}
|
||||||
|
@ -2205,14 +2127,16 @@ std::optional<std::string> EvalState::tryAttrsToString(const PosIdx pos, Value &
|
||||||
if (i != v.attrs->end()) {
|
if (i != v.attrs->end()) {
|
||||||
Value v1;
|
Value v1;
|
||||||
callFunction(*i->value, v, v1, pos);
|
callFunction(*i->value, v, v1, pos);
|
||||||
return coerceToString(pos, v1, context, coerceMore, copyToStore).toOwned();
|
return coerceToString(pos, v1, context,
|
||||||
|
"while evaluating the result of the `__toString` attribute",
|
||||||
|
coerceMore, copyToStore).toOwned();
|
||||||
}
|
}
|
||||||
|
|
||||||
return {};
|
return {};
|
||||||
}
|
}
|
||||||
|
|
||||||
BackedStringView EvalState::coerceToString(const PosIdx pos, Value & v, PathSet & context,
|
BackedStringView EvalState::coerceToString(const PosIdx pos, Value &v, PathSet &context,
|
||||||
bool coerceMore, bool copyToStore, bool canonicalizePath)
|
std::string_view errorCtx, bool coerceMore, bool copyToStore, bool canonicalizePath)
|
||||||
{
|
{
|
||||||
forceValue(v, pos);
|
forceValue(v, pos);
|
||||||
|
|
||||||
|
@ -2235,13 +2159,23 @@ BackedStringView EvalState::coerceToString(const PosIdx pos, Value & v, PathSet
|
||||||
if (maybeString)
|
if (maybeString)
|
||||||
return std::move(*maybeString);
|
return std::move(*maybeString);
|
||||||
auto i = v.attrs->find(sOutPath);
|
auto i = v.attrs->find(sOutPath);
|
||||||
if (i == v.attrs->end())
|
if (i == v.attrs->end()) {
|
||||||
throwTypeError(pos, "cannot coerce a set to a string");
|
error("cannot coerce %1% to a string", showType(v))
|
||||||
return coerceToString(pos, *i->value, context, coerceMore, copyToStore);
|
.withTrace(pos, errorCtx)
|
||||||
|
.debugThrow<TypeError>();
|
||||||
|
}
|
||||||
|
return coerceToString(pos, *i->value, context, errorCtx,
|
||||||
|
coerceMore, copyToStore, canonicalizePath);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (v.type() == nExternal)
|
if (v.type() == nExternal) {
|
||||||
|
try {
|
||||||
return v.external->coerceToString(positions[pos], context, coerceMore, copyToStore);
|
return v.external->coerceToString(positions[pos], context, coerceMore, copyToStore);
|
||||||
|
} catch (Error & e) {
|
||||||
|
e.addTrace(nullptr, errorCtx);
|
||||||
|
throw;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if (coerceMore) {
|
if (coerceMore) {
|
||||||
/* Note that `false' is represented as an empty string for
|
/* Note that `false' is represented as an empty string for
|
||||||
|
@ -2255,7 +2189,14 @@ BackedStringView EvalState::coerceToString(const PosIdx pos, Value & v, PathSet
|
||||||
if (v.isList()) {
|
if (v.isList()) {
|
||||||
std::string result;
|
std::string result;
|
||||||
for (auto [n, v2] : enumerate(v.listItems())) {
|
for (auto [n, v2] : enumerate(v.listItems())) {
|
||||||
result += *coerceToString(pos, *v2, context, coerceMore, copyToStore);
|
try {
|
||||||
|
result += *coerceToString(noPos, *v2, context,
|
||||||
|
"while evaluating one element of the list",
|
||||||
|
coerceMore, copyToStore, canonicalizePath);
|
||||||
|
} catch (Error & e) {
|
||||||
|
e.addTrace(positions[pos], errorCtx);
|
||||||
|
throw;
|
||||||
|
}
|
||||||
if (n < v.listSize() - 1
|
if (n < v.listSize() - 1
|
||||||
/* !!! not quite correct */
|
/* !!! not quite correct */
|
||||||
&& (!v2->isList() || v2->listSize() != 0))
|
&& (!v2->isList() || v2->listSize() != 0))
|
||||||
|
@ -2265,14 +2206,16 @@ BackedStringView EvalState::coerceToString(const PosIdx pos, Value & v, PathSet
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
throwTypeError(pos, "cannot coerce %1% to a string", v);
|
error("cannot coerce %1% to a string", showType(v))
|
||||||
|
.withTrace(pos, errorCtx)
|
||||||
|
.debugThrow<TypeError>();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
StorePath EvalState::copyPathToStore(PathSet & context, const Path & path)
|
StorePath EvalState::copyPathToStore(PathSet & context, const Path & path)
|
||||||
{
|
{
|
||||||
if (nix::isDerivation(path))
|
if (nix::isDerivation(path))
|
||||||
throwEvalError("file names are not allowed to end in '%1%'", drvExtension);
|
error("file names are not allowed to end in '%1%'", drvExtension).debugThrow<EvalError>();
|
||||||
|
|
||||||
auto dstPath = [&]() -> StorePath
|
auto dstPath = [&]() -> StorePath
|
||||||
{
|
{
|
||||||
|
@ -2293,28 +2236,25 @@ StorePath EvalState::copyPathToStore(PathSet & context, const Path & path)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
Path EvalState::coerceToPath(const PosIdx pos, Value & v, PathSet & context)
|
Path EvalState::coerceToPath(const PosIdx pos, Value & v, PathSet & context, std::string_view errorCtx)
|
||||||
{
|
{
|
||||||
auto path = coerceToString(pos, v, context, false, false).toOwned();
|
auto path = coerceToString(pos, v, context, errorCtx, false, false, true).toOwned();
|
||||||
if (path == "" || path[0] != '/')
|
if (path == "" || path[0] != '/')
|
||||||
throwEvalError(pos, "string '%1%' doesn't represent an absolute path", path);
|
error("string '%1%' doesn't represent an absolute path", path).withTrace(pos, errorCtx).debugThrow<EvalError>();
|
||||||
return path;
|
return path;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
StorePath EvalState::coerceToStorePath(const PosIdx pos, Value & v, PathSet & context)
|
StorePath EvalState::coerceToStorePath(const PosIdx pos, Value & v, PathSet & context, std::string_view errorCtx)
|
||||||
{
|
{
|
||||||
auto path = coerceToString(pos, v, context, false, false).toOwned();
|
auto path = coerceToString(pos, v, context, errorCtx, false, false, true).toOwned();
|
||||||
if (auto storePath = store->maybeParseStorePath(path))
|
if (auto storePath = store->maybeParseStorePath(path))
|
||||||
return *storePath;
|
return *storePath;
|
||||||
throw EvalError({
|
error("path '%1%' is not in the Nix store", path).withTrace(pos, errorCtx).debugThrow<EvalError>();
|
||||||
.msg = hintfmt("path '%1%' is not in the Nix store", path),
|
|
||||||
.errPos = positions[pos]
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
bool EvalState::eqValues(Value & v1, Value & v2)
|
bool EvalState::eqValues(Value & v1, Value & v2, const PosIdx pos, std::string_view errorCtx)
|
||||||
{
|
{
|
||||||
forceValue(v1, noPos);
|
forceValue(v1, noPos);
|
||||||
forceValue(v2, noPos);
|
forceValue(v2, noPos);
|
||||||
|
@ -2334,7 +2274,6 @@ bool EvalState::eqValues(Value & v1, Value & v2)
|
||||||
if (v1.type() != v2.type()) return false;
|
if (v1.type() != v2.type()) return false;
|
||||||
|
|
||||||
switch (v1.type()) {
|
switch (v1.type()) {
|
||||||
|
|
||||||
case nInt:
|
case nInt:
|
||||||
return v1.integer == v2.integer;
|
return v1.integer == v2.integer;
|
||||||
|
|
||||||
|
@ -2353,7 +2292,7 @@ bool EvalState::eqValues(Value & v1, Value & v2)
|
||||||
case nList:
|
case nList:
|
||||||
if (v1.listSize() != v2.listSize()) return false;
|
if (v1.listSize() != v2.listSize()) return false;
|
||||||
for (size_t n = 0; n < v1.listSize(); ++n)
|
for (size_t n = 0; n < v1.listSize(); ++n)
|
||||||
if (!eqValues(*v1.listElems()[n], *v2.listElems()[n])) return false;
|
if (!eqValues(*v1.listElems()[n], *v2.listElems()[n], pos, errorCtx)) return false;
|
||||||
return true;
|
return true;
|
||||||
|
|
||||||
case nAttrs: {
|
case nAttrs: {
|
||||||
|
@ -2363,7 +2302,7 @@ bool EvalState::eqValues(Value & v1, Value & v2)
|
||||||
Bindings::iterator i = v1.attrs->find(sOutPath);
|
Bindings::iterator i = v1.attrs->find(sOutPath);
|
||||||
Bindings::iterator j = v2.attrs->find(sOutPath);
|
Bindings::iterator j = v2.attrs->find(sOutPath);
|
||||||
if (i != v1.attrs->end() && j != v2.attrs->end())
|
if (i != v1.attrs->end() && j != v2.attrs->end())
|
||||||
return eqValues(*i->value, *j->value);
|
return eqValues(*i->value, *j->value, pos, errorCtx);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (v1.attrs->size() != v2.attrs->size()) return false;
|
if (v1.attrs->size() != v2.attrs->size()) return false;
|
||||||
|
@ -2371,7 +2310,7 @@ bool EvalState::eqValues(Value & v1, Value & v2)
|
||||||
/* Otherwise, compare the attributes one by one. */
|
/* Otherwise, compare the attributes one by one. */
|
||||||
Bindings::iterator i, j;
|
Bindings::iterator i, j;
|
||||||
for (i = v1.attrs->begin(), j = v2.attrs->begin(); i != v1.attrs->end(); ++i, ++j)
|
for (i = v1.attrs->begin(), j = v2.attrs->begin(); i != v1.attrs->end(); ++i, ++j)
|
||||||
if (i->name != j->name || !eqValues(*i->value, *j->value))
|
if (i->name != j->name || !eqValues(*i->value, *j->value, pos, errorCtx))
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
|
@ -2388,9 +2327,7 @@ bool EvalState::eqValues(Value & v1, Value & v2)
|
||||||
return v1.fpoint == v2.fpoint;
|
return v1.fpoint == v2.fpoint;
|
||||||
|
|
||||||
default:
|
default:
|
||||||
throwEvalError("cannot compare %1% with %2%",
|
error("cannot compare %1% with %2%", showType(v1), showType(v2)).withTrace(pos, errorCtx).debugThrow<EvalError>();
|
||||||
showType(v1),
|
|
||||||
showType(v2));
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -2517,8 +2454,7 @@ void EvalState::printStats()
|
||||||
std::string ExternalValueBase::coerceToString(const Pos & pos, PathSet & context, bool copyMore, bool copyToStore) const
|
std::string ExternalValueBase::coerceToString(const Pos & pos, PathSet & context, bool copyMore, bool copyToStore) const
|
||||||
{
|
{
|
||||||
throw TypeError({
|
throw TypeError({
|
||||||
.msg = hintfmt("cannot coerce %1% to a string", showType()),
|
.msg = hintfmt("cannot coerce %1% to a string", showType())
|
||||||
.errPos = pos
|
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -86,6 +86,43 @@ struct DebugTrace {
|
||||||
|
|
||||||
void debugError(Error * e, Env & env, Expr & expr);
|
void debugError(Error * e, Env & env, Expr & expr);
|
||||||
|
|
||||||
|
class ErrorBuilder
|
||||||
|
{
|
||||||
|
private:
|
||||||
|
EvalState & state;
|
||||||
|
ErrorInfo info;
|
||||||
|
|
||||||
|
ErrorBuilder(EvalState & s, ErrorInfo && i): state(s), info(i) { }
|
||||||
|
|
||||||
|
public:
|
||||||
|
template<typename... Args>
|
||||||
|
[[nodiscard, gnu::noinline]]
|
||||||
|
static ErrorBuilder * create(EvalState & s, const Args & ... args)
|
||||||
|
{
|
||||||
|
return new ErrorBuilder(s, ErrorInfo { .msg = hintfmt(args...) });
|
||||||
|
}
|
||||||
|
|
||||||
|
[[nodiscard, gnu::noinline]]
|
||||||
|
ErrorBuilder & atPos(PosIdx pos);
|
||||||
|
|
||||||
|
[[nodiscard, gnu::noinline]]
|
||||||
|
ErrorBuilder & withTrace(PosIdx pos, const std::string_view text);
|
||||||
|
|
||||||
|
[[nodiscard, gnu::noinline]]
|
||||||
|
ErrorBuilder & withFrameTrace(PosIdx pos, const std::string_view text);
|
||||||
|
|
||||||
|
[[nodiscard, gnu::noinline]]
|
||||||
|
ErrorBuilder & withSuggestions(Suggestions & s);
|
||||||
|
|
||||||
|
[[nodiscard, gnu::noinline]]
|
||||||
|
ErrorBuilder & withFrame(const Env & e, const Expr & ex);
|
||||||
|
|
||||||
|
template<class ErrorType>
|
||||||
|
[[gnu::noinline, gnu::noreturn]]
|
||||||
|
void debugThrow();
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
class EvalState : public std::enable_shared_from_this<EvalState>
|
class EvalState : public std::enable_shared_from_this<EvalState>
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
|
@ -145,29 +182,38 @@ public:
|
||||||
|
|
||||||
template<class E>
|
template<class E>
|
||||||
[[gnu::noinline, gnu::noreturn]]
|
[[gnu::noinline, gnu::noreturn]]
|
||||||
void debugThrow(E && error, const Env & env, const Expr & expr)
|
void debugThrowLastTrace(E && error)
|
||||||
{
|
{
|
||||||
if (debugRepl)
|
debugThrow(error, nullptr, nullptr);
|
||||||
runDebugRepl(&error, env, expr);
|
|
||||||
|
|
||||||
throw std::move(error);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
template<class E>
|
template<class E>
|
||||||
[[gnu::noinline, gnu::noreturn]]
|
[[gnu::noinline, gnu::noreturn]]
|
||||||
void debugThrowLastTrace(E && e)
|
void debugThrow(E && error, const Env * env, const Expr * expr)
|
||||||
{
|
{
|
||||||
// Call this in the situation where Expr and Env are inaccessible.
|
if (debugRepl && ((env && expr) || !debugTraces.empty())) {
|
||||||
// The debugger will start in the last context that's in the
|
if (!env || !expr) {
|
||||||
// DebugTrace stack.
|
|
||||||
if (debugRepl && !debugTraces.empty()) {
|
|
||||||
const DebugTrace & last = debugTraces.front();
|
const DebugTrace & last = debugTraces.front();
|
||||||
runDebugRepl(&e, last.env, last.expr);
|
env = &last.env;
|
||||||
|
expr = &last.expr;
|
||||||
|
}
|
||||||
|
runDebugRepl(&error, *env, *expr);
|
||||||
}
|
}
|
||||||
|
|
||||||
throw std::move(e);
|
throw std::move(error);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// This is dangerous, but gets in line with the idea that error creation and
|
||||||
|
// throwing should not allocate on the stack of hot functions.
|
||||||
|
// as long as errors are immediately thrown, it works.
|
||||||
|
ErrorBuilder * errorBuilder;
|
||||||
|
|
||||||
|
template<typename... Args>
|
||||||
|
[[nodiscard, gnu::noinline]]
|
||||||
|
ErrorBuilder & error(const Args & ... args) {
|
||||||
|
errorBuilder = ErrorBuilder::create(*this, args...);
|
||||||
|
return *errorBuilder;
|
||||||
|
}
|
||||||
|
|
||||||
private:
|
private:
|
||||||
SrcToStore srcToStore;
|
SrcToStore srcToStore;
|
||||||
|
@ -282,8 +328,8 @@ public:
|
||||||
/* Evaluation the expression, then verify that it has the expected
|
/* Evaluation the expression, then verify that it has the expected
|
||||||
type. */
|
type. */
|
||||||
inline bool evalBool(Env & env, Expr * e);
|
inline bool evalBool(Env & env, Expr * e);
|
||||||
inline bool evalBool(Env & env, Expr * e, const PosIdx pos);
|
inline bool evalBool(Env & env, Expr * e, const PosIdx pos, std::string_view errorCtx);
|
||||||
inline void evalAttrs(Env & env, Expr * e, Value & v);
|
inline void evalAttrs(Env & env, Expr * e, Value & v, const PosIdx pos, std::string_view errorCtx);
|
||||||
|
|
||||||
/* If `v' is a thunk, enter it and overwrite `v' with the result
|
/* If `v' is a thunk, enter it and overwrite `v' with the result
|
||||||
of the evaluation of the thunk. If `v' is a delayed function
|
of the evaluation of the thunk. If `v' is a delayed function
|
||||||
|
@ -299,89 +345,25 @@ public:
|
||||||
void forceValueDeep(Value & v);
|
void forceValueDeep(Value & v);
|
||||||
|
|
||||||
/* Force `v', and then verify that it has the expected type. */
|
/* Force `v', and then verify that it has the expected type. */
|
||||||
NixInt forceInt(Value & v, const PosIdx pos);
|
NixInt forceInt(Value & v, const PosIdx pos, std::string_view errorCtx);
|
||||||
NixFloat forceFloat(Value & v, const PosIdx pos);
|
NixFloat forceFloat(Value & v, const PosIdx pos, std::string_view errorCtx);
|
||||||
bool forceBool(Value & v, const PosIdx pos);
|
bool forceBool(Value & v, const PosIdx pos, std::string_view errorCtx);
|
||||||
|
|
||||||
void forceAttrs(Value & v, const PosIdx pos);
|
void forceAttrs(Value & v, const PosIdx pos, std::string_view errorCtx);
|
||||||
|
|
||||||
template <typename Callable>
|
template <typename Callable>
|
||||||
inline void forceAttrs(Value & v, Callable getPos);
|
inline void forceAttrs(Value & v, Callable getPos, std::string_view errorCtx);
|
||||||
|
|
||||||
inline void forceList(Value & v, const PosIdx pos);
|
inline void forceList(Value & v, const PosIdx pos, std::string_view errorCtx);
|
||||||
void forceFunction(Value & v, const PosIdx pos); // either lambda or primop
|
void forceFunction(Value & v, const PosIdx pos, std::string_view errorCtx); // either lambda or primop
|
||||||
std::string_view forceString(Value & v, const PosIdx pos = noPos);
|
std::string_view forceString(Value & v, const PosIdx pos, std::string_view errorCtx);
|
||||||
std::string_view forceString(Value & v, PathSet & context, const PosIdx pos = noPos);
|
std::string_view forceString(Value & v, PathSet & context, const PosIdx pos, std::string_view errorCtx);
|
||||||
std::string_view forceStringNoCtx(Value & v, const PosIdx pos = noPos);
|
std::string_view forceStringNoCtx(Value & v, const PosIdx pos, std::string_view errorCtx);
|
||||||
|
|
||||||
[[gnu::noinline, gnu::noreturn]]
|
|
||||||
void throwEvalError(const PosIdx pos, const char * s);
|
|
||||||
[[gnu::noinline, gnu::noreturn]]
|
|
||||||
void throwEvalError(const PosIdx pos, const char * s,
|
|
||||||
Env & env, Expr & expr);
|
|
||||||
[[gnu::noinline, gnu::noreturn]]
|
|
||||||
void throwEvalError(const char * s, const std::string & s2);
|
|
||||||
[[gnu::noinline, gnu::noreturn]]
|
|
||||||
void throwEvalError(const PosIdx pos, const char * s, const std::string & s2);
|
|
||||||
[[gnu::noinline, gnu::noreturn]]
|
|
||||||
void throwEvalError(const char * s, const std::string & s2,
|
|
||||||
Env & env, Expr & expr);
|
|
||||||
[[gnu::noinline, gnu::noreturn]]
|
|
||||||
void throwEvalError(const PosIdx pos, const char * s, const std::string & s2,
|
|
||||||
Env & env, Expr & expr);
|
|
||||||
[[gnu::noinline, gnu::noreturn]]
|
|
||||||
void throwEvalError(const char * s, const std::string & s2, const std::string & s3,
|
|
||||||
Env & env, Expr & expr);
|
|
||||||
[[gnu::noinline, gnu::noreturn]]
|
|
||||||
void throwEvalError(const PosIdx pos, const char * s, const std::string & s2, const std::string & s3,
|
|
||||||
Env & env, Expr & expr);
|
|
||||||
[[gnu::noinline, gnu::noreturn]]
|
|
||||||
void throwEvalError(const PosIdx pos, const char * s, const std::string & s2, const std::string & s3);
|
|
||||||
[[gnu::noinline, gnu::noreturn]]
|
|
||||||
void throwEvalError(const char * s, const std::string & s2, const std::string & s3);
|
|
||||||
[[gnu::noinline, gnu::noreturn]]
|
|
||||||
void throwEvalError(const PosIdx pos, const Suggestions & suggestions, const char * s, const std::string & s2,
|
|
||||||
Env & env, Expr & expr);
|
|
||||||
[[gnu::noinline, gnu::noreturn]]
|
|
||||||
void throwEvalError(const PosIdx p1, const char * s, const Symbol sym, const PosIdx p2,
|
|
||||||
Env & env, Expr & expr);
|
|
||||||
|
|
||||||
[[gnu::noinline, gnu::noreturn]]
|
|
||||||
void throwTypeError(const PosIdx pos, const char * s, const Value & v);
|
|
||||||
[[gnu::noinline, gnu::noreturn]]
|
|
||||||
void throwTypeError(const PosIdx pos, const char * s, const Value & v,
|
|
||||||
Env & env, Expr & expr);
|
|
||||||
[[gnu::noinline, gnu::noreturn]]
|
|
||||||
void throwTypeError(const PosIdx pos, const char * s);
|
|
||||||
[[gnu::noinline, gnu::noreturn]]
|
|
||||||
void throwTypeError(const PosIdx pos, const char * s,
|
|
||||||
Env & env, Expr & expr);
|
|
||||||
[[gnu::noinline, gnu::noreturn]]
|
|
||||||
void throwTypeError(const PosIdx pos, const char * s, const ExprLambda & fun, const Symbol s2,
|
|
||||||
Env & env, Expr & expr);
|
|
||||||
[[gnu::noinline, gnu::noreturn]]
|
|
||||||
void throwTypeError(const PosIdx pos, const Suggestions & suggestions, const char * s, const ExprLambda & fun, const Symbol s2,
|
|
||||||
Env & env, Expr & expr);
|
|
||||||
[[gnu::noinline, gnu::noreturn]]
|
|
||||||
void throwTypeError(const char * s, const Value & v,
|
|
||||||
Env & env, Expr & expr);
|
|
||||||
|
|
||||||
[[gnu::noinline, gnu::noreturn]]
|
|
||||||
void throwAssertionError(const PosIdx pos, const char * s, const std::string & s1,
|
|
||||||
Env & env, Expr & expr);
|
|
||||||
|
|
||||||
[[gnu::noinline, gnu::noreturn]]
|
|
||||||
void throwUndefinedVarError(const PosIdx pos, const char * s, const std::string & s1,
|
|
||||||
Env & env, Expr & expr);
|
|
||||||
|
|
||||||
[[gnu::noinline, gnu::noreturn]]
|
|
||||||
void throwMissingArgumentError(const PosIdx pos, const char * s, const std::string & s1,
|
|
||||||
Env & env, Expr & expr);
|
|
||||||
|
|
||||||
[[gnu::noinline]]
|
[[gnu::noinline]]
|
||||||
void addErrorTrace(Error & e, const char * s, const std::string & s2) const;
|
void addErrorTrace(Error & e, const char * s, const std::string & s2) const;
|
||||||
[[gnu::noinline]]
|
[[gnu::noinline]]
|
||||||
void addErrorTrace(Error & e, const PosIdx pos, const char * s, const std::string & s2) const;
|
void addErrorTrace(Error & e, const PosIdx pos, const char * s, const std::string & s2, bool frame = false) const;
|
||||||
|
|
||||||
public:
|
public:
|
||||||
/* Return true iff the value `v' denotes a derivation (i.e. a
|
/* Return true iff the value `v' denotes a derivation (i.e. a
|
||||||
|
@ -396,6 +378,7 @@ public:
|
||||||
booleans and lists to a string. If `copyToStore' is set,
|
booleans and lists to a string. If `copyToStore' is set,
|
||||||
referenced paths are copied to the Nix store as a side effect. */
|
referenced paths are copied to the Nix store as a side effect. */
|
||||||
BackedStringView coerceToString(const PosIdx pos, Value & v, PathSet & context,
|
BackedStringView coerceToString(const PosIdx pos, Value & v, PathSet & context,
|
||||||
|
std::string_view errorCtx,
|
||||||
bool coerceMore = false, bool copyToStore = true,
|
bool coerceMore = false, bool copyToStore = true,
|
||||||
bool canonicalizePath = true);
|
bool canonicalizePath = true);
|
||||||
|
|
||||||
|
@ -404,10 +387,10 @@ public:
|
||||||
/* Path coercion. Converts strings, paths and derivations to a
|
/* Path coercion. Converts strings, paths and derivations to a
|
||||||
path. The result is guaranteed to be a canonicalised, absolute
|
path. The result is guaranteed to be a canonicalised, absolute
|
||||||
path. Nothing is copied to the store. */
|
path. Nothing is copied to the store. */
|
||||||
Path coerceToPath(const PosIdx pos, Value & v, PathSet & context);
|
Path coerceToPath(const PosIdx pos, Value & v, PathSet & context, std::string_view errorCtx);
|
||||||
|
|
||||||
/* Like coerceToPath, but the result must be a store path. */
|
/* Like coerceToPath, but the result must be a store path. */
|
||||||
StorePath coerceToStorePath(const PosIdx pos, Value & v, PathSet & context);
|
StorePath coerceToStorePath(const PosIdx pos, Value & v, PathSet & context, std::string_view errorCtx);
|
||||||
|
|
||||||
public:
|
public:
|
||||||
|
|
||||||
|
@ -467,7 +450,7 @@ public:
|
||||||
|
|
||||||
/* Do a deep equality test between two values. That is, list
|
/* Do a deep equality test between two values. That is, list
|
||||||
elements and attributes are compared recursively. */
|
elements and attributes are compared recursively. */
|
||||||
bool eqValues(Value & v1, Value & v2);
|
bool eqValues(Value & v1, Value & v2, const PosIdx pos, std::string_view errorCtx);
|
||||||
|
|
||||||
bool isFunctor(Value & fun);
|
bool isFunctor(Value & fun);
|
||||||
|
|
||||||
|
@ -502,7 +485,7 @@ public:
|
||||||
void mkThunk_(Value & v, Expr * expr);
|
void mkThunk_(Value & v, Expr * expr);
|
||||||
void mkPos(Value & v, PosIdx pos);
|
void mkPos(Value & v, PosIdx pos);
|
||||||
|
|
||||||
void concatLists(Value & v, size_t nrLists, Value * * lists, const PosIdx pos);
|
void concatLists(Value & v, size_t nrLists, Value * * lists, const PosIdx pos, std::string_view errorCtx);
|
||||||
|
|
||||||
/* Print statistics. */
|
/* Print statistics. */
|
||||||
void printStats();
|
void printStats();
|
||||||
|
@ -665,6 +648,13 @@ extern EvalSettings evalSettings;
|
||||||
|
|
||||||
static const std::string corepkgsPrefix{"/__corepkgs__/"};
|
static const std::string corepkgsPrefix{"/__corepkgs__/"};
|
||||||
|
|
||||||
|
template<class ErrorType>
|
||||||
|
void ErrorBuilder::debugThrow()
|
||||||
|
{
|
||||||
|
// NOTE: We always use the -LastTrace version as we push the new trace in withFrame()
|
||||||
|
state.debugThrowLastTrace(ErrorType(info));
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#include "eval-inline.hh"
|
#include "eval-inline.hh"
|
||||||
|
|
|
@ -259,28 +259,28 @@ static Flake getFlake(
|
||||||
if (setting.value->type() == nString)
|
if (setting.value->type() == nString)
|
||||||
flake.config.settings.emplace(
|
flake.config.settings.emplace(
|
||||||
state.symbols[setting.name],
|
state.symbols[setting.name],
|
||||||
std::string(state.forceStringNoCtx(*setting.value, setting.pos)));
|
std::string(state.forceStringNoCtx(*setting.value, setting.pos, "")));
|
||||||
else if (setting.value->type() == nPath) {
|
else if (setting.value->type() == nPath) {
|
||||||
PathSet emptyContext = {};
|
PathSet emptyContext = {};
|
||||||
flake.config.settings.emplace(
|
flake.config.settings.emplace(
|
||||||
state.symbols[setting.name],
|
state.symbols[setting.name],
|
||||||
state.coerceToString(setting.pos, *setting.value, emptyContext, false, true, true) .toOwned());
|
state.coerceToString(setting.pos, *setting.value, emptyContext, "", false, true, true) .toOwned());
|
||||||
}
|
}
|
||||||
else if (setting.value->type() == nInt)
|
else if (setting.value->type() == nInt)
|
||||||
flake.config.settings.emplace(
|
flake.config.settings.emplace(
|
||||||
state.symbols[setting.name],
|
state.symbols[setting.name],
|
||||||
state.forceInt(*setting.value, setting.pos));
|
state.forceInt(*setting.value, setting.pos, ""));
|
||||||
else if (setting.value->type() == nBool)
|
else if (setting.value->type() == nBool)
|
||||||
flake.config.settings.emplace(
|
flake.config.settings.emplace(
|
||||||
state.symbols[setting.name],
|
state.symbols[setting.name],
|
||||||
Explicit<bool> { state.forceBool(*setting.value, setting.pos) });
|
Explicit<bool> { state.forceBool(*setting.value, setting.pos, "") });
|
||||||
else if (setting.value->type() == nList) {
|
else if (setting.value->type() == nList) {
|
||||||
std::vector<std::string> ss;
|
std::vector<std::string> ss;
|
||||||
for (auto elem : setting.value->listItems()) {
|
for (auto elem : setting.value->listItems()) {
|
||||||
if (elem->type() != nString)
|
if (elem->type() != nString)
|
||||||
throw TypeError("list element in flake configuration setting '%s' is %s while a string is expected",
|
throw TypeError("list element in flake configuration setting '%s' is %s while a string is expected",
|
||||||
state.symbols[setting.name], showType(*setting.value));
|
state.symbols[setting.name], showType(*setting.value));
|
||||||
ss.emplace_back(state.forceStringNoCtx(*elem, setting.pos));
|
ss.emplace_back(state.forceStringNoCtx(*elem, setting.pos, ""));
|
||||||
}
|
}
|
||||||
flake.config.settings.emplace(state.symbols[setting.name], ss);
|
flake.config.settings.emplace(state.symbols[setting.name], ss);
|
||||||
}
|
}
|
||||||
|
@ -741,7 +741,7 @@ void callFlake(EvalState & state,
|
||||||
|
|
||||||
static void prim_getFlake(EvalState & state, const PosIdx pos, Value * * args, Value & v)
|
static void prim_getFlake(EvalState & state, const PosIdx pos, Value * * args, Value & v)
|
||||||
{
|
{
|
||||||
std::string flakeRefS(state.forceStringNoCtx(*args[0], pos));
|
std::string flakeRefS(state.forceStringNoCtx(*args[0], pos, "while evaluating the argument passed to builtins.getFlake"));
|
||||||
auto flakeRef = parseFlakeRef(flakeRefS, {}, true);
|
auto flakeRef = parseFlakeRef(flakeRefS, {}, true);
|
||||||
if (evalSettings.pureEval && !flakeRef.input.isLocked())
|
if (evalSettings.pureEval && !flakeRef.input.isLocked())
|
||||||
throw Error("cannot call 'getFlake' on unlocked flake reference '%s', at %s (use --impure to override)", flakeRefS, state.positions[pos]);
|
throw Error("cannot call 'getFlake' on unlocked flake reference '%s', at %s (use --impure to override)", flakeRefS, state.positions[pos]);
|
||||||
|
|
|
@ -51,7 +51,7 @@ std::string DrvInfo::queryName() const
|
||||||
if (name == "" && attrs) {
|
if (name == "" && attrs) {
|
||||||
auto i = attrs->find(state->sName);
|
auto i = attrs->find(state->sName);
|
||||||
if (i == attrs->end()) throw TypeError("derivation name missing");
|
if (i == attrs->end()) throw TypeError("derivation name missing");
|
||||||
name = state->forceStringNoCtx(*i->value);
|
name = state->forceStringNoCtx(*i->value, noPos, "while evaluating the 'name' attribute of a derivation");
|
||||||
}
|
}
|
||||||
return name;
|
return name;
|
||||||
}
|
}
|
||||||
|
@ -61,7 +61,7 @@ std::string DrvInfo::querySystem() const
|
||||||
{
|
{
|
||||||
if (system == "" && attrs) {
|
if (system == "" && attrs) {
|
||||||
auto i = attrs->find(state->sSystem);
|
auto i = attrs->find(state->sSystem);
|
||||||
system = i == attrs->end() ? "unknown" : state->forceStringNoCtx(*i->value, i->pos);
|
system = i == attrs->end() ? "unknown" : state->forceStringNoCtx(*i->value, i->pos, "while evaluating the 'system' attribute of a derivation");
|
||||||
}
|
}
|
||||||
return system;
|
return system;
|
||||||
}
|
}
|
||||||
|
@ -75,7 +75,7 @@ std::optional<StorePath> DrvInfo::queryDrvPath() const
|
||||||
if (i == attrs->end())
|
if (i == attrs->end())
|
||||||
drvPath = {std::nullopt};
|
drvPath = {std::nullopt};
|
||||||
else
|
else
|
||||||
drvPath = {state->coerceToStorePath(i->pos, *i->value, context)};
|
drvPath = {state->coerceToStorePath(i->pos, *i->value, context, "while evaluating the 'drvPath' attribute of a derivation")};
|
||||||
}
|
}
|
||||||
return drvPath.value_or(std::nullopt);
|
return drvPath.value_or(std::nullopt);
|
||||||
}
|
}
|
||||||
|
@ -95,7 +95,7 @@ StorePath DrvInfo::queryOutPath() const
|
||||||
Bindings::iterator i = attrs->find(state->sOutPath);
|
Bindings::iterator i = attrs->find(state->sOutPath);
|
||||||
PathSet context;
|
PathSet context;
|
||||||
if (i != attrs->end())
|
if (i != attrs->end())
|
||||||
outPath = state->coerceToStorePath(i->pos, *i->value, context);
|
outPath = state->coerceToStorePath(i->pos, *i->value, context, "while evaluating the output path of a derivation");
|
||||||
}
|
}
|
||||||
if (!outPath)
|
if (!outPath)
|
||||||
throw UnimplementedError("CA derivations are not yet supported");
|
throw UnimplementedError("CA derivations are not yet supported");
|
||||||
|
@ -109,23 +109,23 @@ DrvInfo::Outputs DrvInfo::queryOutputs(bool withPaths, bool onlyOutputsToInstall
|
||||||
/* Get the ‘outputs’ list. */
|
/* Get the ‘outputs’ list. */
|
||||||
Bindings::iterator i;
|
Bindings::iterator i;
|
||||||
if (attrs && (i = attrs->find(state->sOutputs)) != attrs->end()) {
|
if (attrs && (i = attrs->find(state->sOutputs)) != attrs->end()) {
|
||||||
state->forceList(*i->value, i->pos);
|
state->forceList(*i->value, i->pos, "while evaluating the 'outputs' attribute of a derivation");
|
||||||
|
|
||||||
/* For each output... */
|
/* For each output... */
|
||||||
for (auto elem : i->value->listItems()) {
|
for (auto elem : i->value->listItems()) {
|
||||||
std::string output(state->forceStringNoCtx(*elem, i->pos));
|
std::string output(state->forceStringNoCtx(*elem, i->pos, "while evaluating the name of an output of a derivation"));
|
||||||
|
|
||||||
if (withPaths) {
|
if (withPaths) {
|
||||||
/* Evaluate the corresponding set. */
|
/* Evaluate the corresponding set. */
|
||||||
Bindings::iterator out = attrs->find(state->symbols.create(output));
|
Bindings::iterator out = attrs->find(state->symbols.create(output));
|
||||||
if (out == attrs->end()) continue; // FIXME: throw error?
|
if (out == attrs->end()) continue; // FIXME: throw error?
|
||||||
state->forceAttrs(*out->value, i->pos);
|
state->forceAttrs(*out->value, i->pos, "while evaluating an output of a derivation");
|
||||||
|
|
||||||
/* And evaluate its ‘outPath’ attribute. */
|
/* And evaluate its ‘outPath’ attribute. */
|
||||||
Bindings::iterator outPath = out->value->attrs->find(state->sOutPath);
|
Bindings::iterator outPath = out->value->attrs->find(state->sOutPath);
|
||||||
if (outPath == out->value->attrs->end()) continue; // FIXME: throw error?
|
if (outPath == out->value->attrs->end()) continue; // FIXME: throw error?
|
||||||
PathSet context;
|
PathSet context;
|
||||||
outputs.emplace(output, state->coerceToStorePath(outPath->pos, *outPath->value, context));
|
outputs.emplace(output, state->coerceToStorePath(outPath->pos, *outPath->value, context, "while evaluating an output path of a derivation"));
|
||||||
} else
|
} else
|
||||||
outputs.emplace(output, std::nullopt);
|
outputs.emplace(output, std::nullopt);
|
||||||
}
|
}
|
||||||
|
@ -137,7 +137,7 @@ DrvInfo::Outputs DrvInfo::queryOutputs(bool withPaths, bool onlyOutputsToInstall
|
||||||
return outputs;
|
return outputs;
|
||||||
|
|
||||||
Bindings::iterator i;
|
Bindings::iterator i;
|
||||||
if (attrs && (i = attrs->find(state->sOutputSpecified)) != attrs->end() && state->forceBool(*i->value, i->pos)) {
|
if (attrs && (i = attrs->find(state->sOutputSpecified)) != attrs->end() && state->forceBool(*i->value, i->pos, "while evaluating the 'outputSpecified' attribute of a derivation")) {
|
||||||
Outputs result;
|
Outputs result;
|
||||||
auto out = outputs.find(queryOutputName());
|
auto out = outputs.find(queryOutputName());
|
||||||
if (out == outputs.end())
|
if (out == outputs.end())
|
||||||
|
@ -169,7 +169,7 @@ std::string DrvInfo::queryOutputName() const
|
||||||
{
|
{
|
||||||
if (outputName == "" && attrs) {
|
if (outputName == "" && attrs) {
|
||||||
Bindings::iterator i = attrs->find(state->sOutputName);
|
Bindings::iterator i = attrs->find(state->sOutputName);
|
||||||
outputName = i != attrs->end() ? state->forceStringNoCtx(*i->value) : "";
|
outputName = i != attrs->end() ? state->forceStringNoCtx(*i->value, noPos, "while evaluating the output name of a derivation") : "";
|
||||||
}
|
}
|
||||||
return outputName;
|
return outputName;
|
||||||
}
|
}
|
||||||
|
@ -181,7 +181,7 @@ Bindings * DrvInfo::getMeta()
|
||||||
if (!attrs) return 0;
|
if (!attrs) return 0;
|
||||||
Bindings::iterator a = attrs->find(state->sMeta);
|
Bindings::iterator a = attrs->find(state->sMeta);
|
||||||
if (a == attrs->end()) return 0;
|
if (a == attrs->end()) return 0;
|
||||||
state->forceAttrs(*a->value, a->pos);
|
state->forceAttrs(*a->value, a->pos, "while evaluating the 'meta' attribute of a derivation");
|
||||||
meta = a->value->attrs;
|
meta = a->value->attrs;
|
||||||
return meta;
|
return meta;
|
||||||
}
|
}
|
||||||
|
@ -382,7 +382,7 @@ static void getDerivations(EvalState & state, Value & vIn,
|
||||||
`recurseForDerivations = true' attribute. */
|
`recurseForDerivations = true' attribute. */
|
||||||
if (i->value->type() == nAttrs) {
|
if (i->value->type() == nAttrs) {
|
||||||
Bindings::iterator j = i->value->attrs->find(state.sRecurseForDerivations);
|
Bindings::iterator j = i->value->attrs->find(state.sRecurseForDerivations);
|
||||||
if (j != i->value->attrs->end() && state.forceBool(*j->value, j->pos))
|
if (j != i->value->attrs->end() && state.forceBool(*j->value, j->pos, "while evaluating the attribute `recurseForDerivations`"))
|
||||||
getDerivations(state, *i->value, pathPrefix2, autoArgs, drvs, done, ignoreAssertionFailures);
|
getDerivations(state, *i->value, pathPrefix2, autoArgs, drvs, done, ignoreAssertionFailures);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -8,7 +8,6 @@
|
||||||
#include "error.hh"
|
#include "error.hh"
|
||||||
#include "chunked-vector.hh"
|
#include "chunked-vector.hh"
|
||||||
|
|
||||||
|
|
||||||
namespace nix {
|
namespace nix {
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -401,22 +401,22 @@ expr_op
|
||||||
| '-' expr_op %prec NEGATE { $$ = new ExprCall(CUR_POS, new ExprVar(data->symbols.create("__sub")), {new ExprInt(0), $2}); }
|
| '-' expr_op %prec NEGATE { $$ = new ExprCall(CUR_POS, new ExprVar(data->symbols.create("__sub")), {new ExprInt(0), $2}); }
|
||||||
| expr_op EQ expr_op { $$ = new ExprOpEq($1, $3); }
|
| expr_op EQ expr_op { $$ = new ExprOpEq($1, $3); }
|
||||||
| expr_op NEQ expr_op { $$ = new ExprOpNEq($1, $3); }
|
| expr_op NEQ expr_op { $$ = new ExprOpNEq($1, $3); }
|
||||||
| expr_op '<' expr_op { $$ = new ExprCall(CUR_POS, new ExprVar(data->symbols.create("__lessThan")), {$1, $3}); }
|
| expr_op '<' expr_op { $$ = new ExprCall(makeCurPos(@2, data), new ExprVar(data->symbols.create("__lessThan")), {$1, $3}); }
|
||||||
| expr_op LEQ expr_op { $$ = new ExprOpNot(new ExprCall(CUR_POS, new ExprVar(data->symbols.create("__lessThan")), {$3, $1})); }
|
| expr_op LEQ expr_op { $$ = new ExprOpNot(new ExprCall(makeCurPos(@2, data), new ExprVar(data->symbols.create("__lessThan")), {$3, $1})); }
|
||||||
| expr_op '>' expr_op { $$ = new ExprCall(CUR_POS, new ExprVar(data->symbols.create("__lessThan")), {$3, $1}); }
|
| expr_op '>' expr_op { $$ = new ExprCall(makeCurPos(@2, data), new ExprVar(data->symbols.create("__lessThan")), {$3, $1}); }
|
||||||
| expr_op GEQ expr_op { $$ = new ExprOpNot(new ExprCall(CUR_POS, new ExprVar(data->symbols.create("__lessThan")), {$1, $3})); }
|
| expr_op GEQ expr_op { $$ = new ExprOpNot(new ExprCall(makeCurPos(@2, data), new ExprVar(data->symbols.create("__lessThan")), {$1, $3})); }
|
||||||
| expr_op AND expr_op { $$ = new ExprOpAnd(CUR_POS, $1, $3); }
|
| expr_op AND expr_op { $$ = new ExprOpAnd(makeCurPos(@2, data), $1, $3); }
|
||||||
| expr_op OR expr_op { $$ = new ExprOpOr(CUR_POS, $1, $3); }
|
| expr_op OR expr_op { $$ = new ExprOpOr(makeCurPos(@2, data), $1, $3); }
|
||||||
| expr_op IMPL expr_op { $$ = new ExprOpImpl(CUR_POS, $1, $3); }
|
| expr_op IMPL expr_op { $$ = new ExprOpImpl(makeCurPos(@2, data), $1, $3); }
|
||||||
| expr_op UPDATE expr_op { $$ = new ExprOpUpdate(CUR_POS, $1, $3); }
|
| expr_op UPDATE expr_op { $$ = new ExprOpUpdate(makeCurPos(@2, data), $1, $3); }
|
||||||
| expr_op '?' attrpath { $$ = new ExprOpHasAttr($1, *$3); }
|
| expr_op '?' attrpath { $$ = new ExprOpHasAttr($1, *$3); }
|
||||||
| expr_op '+' expr_op
|
| expr_op '+' expr_op
|
||||||
{ $$ = new ExprConcatStrings(CUR_POS, false, new std::vector<std::pair<PosIdx, Expr *>>({{makeCurPos(@1, data), $1}, {makeCurPos(@3, data), $3}})); }
|
{ $$ = new ExprConcatStrings(makeCurPos(@2, data), false, new std::vector<std::pair<PosIdx, Expr *> >({{makeCurPos(@1, data), $1}, {makeCurPos(@3, data), $3}})); }
|
||||||
| expr_op '-' expr_op { $$ = new ExprCall(CUR_POS, new ExprVar(data->symbols.create("__sub")), {$1, $3}); }
|
| expr_op '-' expr_op { $$ = new ExprCall(makeCurPos(@2, data), new ExprVar(data->symbols.create("__sub")), {$1, $3}); }
|
||||||
| expr_op '*' expr_op { $$ = new ExprCall(CUR_POS, new ExprVar(data->symbols.create("__mul")), {$1, $3}); }
|
| expr_op '*' expr_op { $$ = new ExprCall(makeCurPos(@2, data), new ExprVar(data->symbols.create("__mul")), {$1, $3}); }
|
||||||
| expr_op '/' expr_op { $$ = new ExprCall(CUR_POS, new ExprVar(data->symbols.create("__div")), {$1, $3}); }
|
| expr_op '/' expr_op { $$ = new ExprCall(makeCurPos(@2, data), new ExprVar(data->symbols.create("__div")), {$1, $3}); }
|
||||||
| expr_op CONCAT expr_op { $$ = new ExprOpConcatLists(CUR_POS, $1, $3); }
|
| expr_op CONCAT expr_op { $$ = new ExprOpConcatLists(makeCurPos(@2, data), $1, $3); }
|
||||||
| expr_op '$' expr_op { $$ = new ExprCall(CUR_POS, $1, {$3}); }
|
| expr_op '$' expr_op { $$ = new ExprCall(makeCurPos(@2, data), $1, {$3}); }
|
||||||
| expr_app
|
| expr_app
|
||||||
;
|
;
|
||||||
|
|
||||||
|
@ -784,13 +784,13 @@ Path EvalState::findFile(SearchPath & searchPath, const std::string_view path, c
|
||||||
if (hasPrefix(path, "nix/"))
|
if (hasPrefix(path, "nix/"))
|
||||||
return concatStrings(corepkgsPrefix, path.substr(4));
|
return concatStrings(corepkgsPrefix, path.substr(4));
|
||||||
|
|
||||||
debugThrowLastTrace(ThrownError({
|
debugThrow(ThrownError({
|
||||||
.msg = hintfmt(evalSettings.pureEval
|
.msg = hintfmt(evalSettings.pureEval
|
||||||
? "cannot look up '<%s>' in pure evaluation mode (use '--impure' to override)"
|
? "cannot look up '<%s>' in pure evaluation mode (use '--impure' to override)"
|
||||||
: "file '%s' was not found in the Nix search path (add it using $NIX_PATH or -I)",
|
: "file '%s' was not found in the Nix search path (add it using $NIX_PATH or -I)",
|
||||||
path),
|
path),
|
||||||
.errPos = positions[pos]
|
.errPos = positions[pos]
|
||||||
}));
|
}), 0, 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
File diff suppressed because it is too large
Load diff
|
@ -8,7 +8,7 @@ namespace nix {
|
||||||
static void prim_unsafeDiscardStringContext(EvalState & state, const PosIdx pos, Value * * args, Value & v)
|
static void prim_unsafeDiscardStringContext(EvalState & state, const PosIdx pos, Value * * args, Value & v)
|
||||||
{
|
{
|
||||||
PathSet context;
|
PathSet context;
|
||||||
auto s = state.coerceToString(pos, *args[0], context);
|
auto s = state.coerceToString(pos, *args[0], context, "while evaluating the argument passed to builtins.unsafeDiscardStringContext");
|
||||||
v.mkString(*s);
|
v.mkString(*s);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -18,7 +18,7 @@ static RegisterPrimOp primop_unsafeDiscardStringContext("__unsafeDiscardStringCo
|
||||||
static void prim_hasContext(EvalState & state, const PosIdx pos, Value * * args, Value & v)
|
static void prim_hasContext(EvalState & state, const PosIdx pos, Value * * args, Value & v)
|
||||||
{
|
{
|
||||||
PathSet context;
|
PathSet context;
|
||||||
state.forceString(*args[0], context, pos);
|
state.forceString(*args[0], context, pos, "while evaluating the argument passed to builtins.hasContext");
|
||||||
v.mkBool(!context.empty());
|
v.mkBool(!context.empty());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -34,7 +34,7 @@ static RegisterPrimOp primop_hasContext("__hasContext", 1, prim_hasContext);
|
||||||
static void prim_unsafeDiscardOutputDependency(EvalState & state, const PosIdx pos, Value * * args, Value & v)
|
static void prim_unsafeDiscardOutputDependency(EvalState & state, const PosIdx pos, Value * * args, Value & v)
|
||||||
{
|
{
|
||||||
PathSet context;
|
PathSet context;
|
||||||
auto s = state.coerceToString(pos, *args[0], context);
|
auto s = state.coerceToString(pos, *args[0], context, "while evaluating the argument passed to builtins.unsafeDiscardOutputDependency");
|
||||||
|
|
||||||
PathSet context2;
|
PathSet context2;
|
||||||
for (auto && p : context) {
|
for (auto && p : context) {
|
||||||
|
@ -80,18 +80,16 @@ static void prim_getContext(EvalState & state, const PosIdx pos, Value * * args,
|
||||||
Strings outputs;
|
Strings outputs;
|
||||||
};
|
};
|
||||||
PathSet context;
|
PathSet context;
|
||||||
state.forceString(*args[0], context, pos);
|
state.forceString(*args[0], context, pos, "while evaluating the argument passed to builtins.getContext");
|
||||||
auto contextInfos = std::map<StorePath, ContextInfo>();
|
auto contextInfos = std::map<StorePath, ContextInfo>();
|
||||||
for (const auto & p : context) {
|
for (const auto & p : context) {
|
||||||
Path drv;
|
|
||||||
std::string output;
|
|
||||||
NixStringContextElem ctx = NixStringContextElem::parse(*state.store, p);
|
NixStringContextElem ctx = NixStringContextElem::parse(*state.store, p);
|
||||||
std::visit(overloaded {
|
std::visit(overloaded {
|
||||||
[&](NixStringContextElem::DrvDeep & d) {
|
[&](NixStringContextElem::DrvDeep & d) {
|
||||||
contextInfos[d.drvPath].allOutputs = true;
|
contextInfos[d.drvPath].allOutputs = true;
|
||||||
},
|
},
|
||||||
[&](NixStringContextElem::Built & b) {
|
[&](NixStringContextElem::Built & b) {
|
||||||
contextInfos[b.drvPath].outputs.emplace_back(std::move(output));
|
contextInfos[b.drvPath].outputs.emplace_back(std::move(b.output));
|
||||||
},
|
},
|
||||||
[&](NixStringContextElem::Opaque & o) {
|
[&](NixStringContextElem::Opaque & o) {
|
||||||
contextInfos[o.path].path = true;
|
contextInfos[o.path].path = true;
|
||||||
|
@ -132,9 +130,9 @@ static RegisterPrimOp primop_getContext("__getContext", 1, prim_getContext);
|
||||||
static void prim_appendContext(EvalState & state, const PosIdx pos, Value * * args, Value & v)
|
static void prim_appendContext(EvalState & state, const PosIdx pos, Value * * args, Value & v)
|
||||||
{
|
{
|
||||||
PathSet context;
|
PathSet context;
|
||||||
auto orig = state.forceString(*args[0], context, pos);
|
auto orig = state.forceString(*args[0], context, noPos, "while evaluating the first argument passed to builtins.appendContext");
|
||||||
|
|
||||||
state.forceAttrs(*args[1], pos);
|
state.forceAttrs(*args[1], pos, "while evaluating the second argument passed to builtins.appendContext");
|
||||||
|
|
||||||
auto sPath = state.symbols.create("path");
|
auto sPath = state.symbols.create("path");
|
||||||
auto sAllOutputs = state.symbols.create("allOutputs");
|
auto sAllOutputs = state.symbols.create("allOutputs");
|
||||||
|
@ -142,24 +140,24 @@ static void prim_appendContext(EvalState & state, const PosIdx pos, Value * * ar
|
||||||
const auto & name = state.symbols[i.name];
|
const auto & name = state.symbols[i.name];
|
||||||
if (!state.store->isStorePath(name))
|
if (!state.store->isStorePath(name))
|
||||||
throw EvalError({
|
throw EvalError({
|
||||||
.msg = hintfmt("Context key '%s' is not a store path", name),
|
.msg = hintfmt("context key '%s' is not a store path", name),
|
||||||
.errPos = state.positions[i.pos]
|
.errPos = state.positions[i.pos]
|
||||||
});
|
});
|
||||||
if (!settings.readOnlyMode)
|
if (!settings.readOnlyMode)
|
||||||
state.store->ensurePath(state.store->parseStorePath(name));
|
state.store->ensurePath(state.store->parseStorePath(name));
|
||||||
state.forceAttrs(*i.value, i.pos);
|
state.forceAttrs(*i.value, i.pos, "while evaluating the value of a string context");
|
||||||
auto iter = i.value->attrs->find(sPath);
|
auto iter = i.value->attrs->find(sPath);
|
||||||
if (iter != i.value->attrs->end()) {
|
if (iter != i.value->attrs->end()) {
|
||||||
if (state.forceBool(*iter->value, iter->pos))
|
if (state.forceBool(*iter->value, iter->pos, "while evaluating the `path` attribute of a string context"))
|
||||||
context.emplace(name);
|
context.emplace(name);
|
||||||
}
|
}
|
||||||
|
|
||||||
iter = i.value->attrs->find(sAllOutputs);
|
iter = i.value->attrs->find(sAllOutputs);
|
||||||
if (iter != i.value->attrs->end()) {
|
if (iter != i.value->attrs->end()) {
|
||||||
if (state.forceBool(*iter->value, iter->pos)) {
|
if (state.forceBool(*iter->value, iter->pos, "while evaluating the `allOutputs` attribute of a string context")) {
|
||||||
if (!isDerivation(name)) {
|
if (!isDerivation(name)) {
|
||||||
throw EvalError({
|
throw EvalError({
|
||||||
.msg = hintfmt("Tried to add all-outputs context of %s, which is not a derivation, to a string", name),
|
.msg = hintfmt("tried to add all-outputs context of %s, which is not a derivation, to a string", name),
|
||||||
.errPos = state.positions[i.pos]
|
.errPos = state.positions[i.pos]
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
@ -169,15 +167,15 @@ static void prim_appendContext(EvalState & state, const PosIdx pos, Value * * ar
|
||||||
|
|
||||||
iter = i.value->attrs->find(state.sOutputs);
|
iter = i.value->attrs->find(state.sOutputs);
|
||||||
if (iter != i.value->attrs->end()) {
|
if (iter != i.value->attrs->end()) {
|
||||||
state.forceList(*iter->value, iter->pos);
|
state.forceList(*iter->value, iter->pos, "while evaluating the `outputs` attribute of a string context");
|
||||||
if (iter->value->listSize() && !isDerivation(name)) {
|
if (iter->value->listSize() && !isDerivation(name)) {
|
||||||
throw EvalError({
|
throw EvalError({
|
||||||
.msg = hintfmt("Tried to add derivation output context of %s, which is not a derivation, to a string", name),
|
.msg = hintfmt("tried to add derivation output context of %s, which is not a derivation, to a string", name),
|
||||||
.errPos = state.positions[i.pos]
|
.errPos = state.positions[i.pos]
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
for (auto elem : iter->value->listItems()) {
|
for (auto elem : iter->value->listItems()) {
|
||||||
auto outputName = state.forceStringNoCtx(*elem, iter->pos);
|
auto outputName = state.forceStringNoCtx(*elem, iter->pos, "while evaluating an output name within a string context");
|
||||||
context.insert(concatStrings("!", outputName, "!", name));
|
context.insert(concatStrings("!", outputName, "!", name));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -7,7 +7,7 @@ namespace nix {
|
||||||
|
|
||||||
static void prim_fetchClosure(EvalState & state, const PosIdx pos, Value * * args, Value & v)
|
static void prim_fetchClosure(EvalState & state, const PosIdx pos, Value * * args, Value & v)
|
||||||
{
|
{
|
||||||
state.forceAttrs(*args[0], pos);
|
state.forceAttrs(*args[0], pos, "while evaluating the argument passed to builtins.fetchClosure");
|
||||||
|
|
||||||
std::optional<std::string> fromStoreUrl;
|
std::optional<std::string> fromStoreUrl;
|
||||||
std::optional<StorePath> fromPath;
|
std::optional<StorePath> fromPath;
|
||||||
|
@ -19,7 +19,8 @@ static void prim_fetchClosure(EvalState & state, const PosIdx pos, Value * * arg
|
||||||
|
|
||||||
if (attrName == "fromPath") {
|
if (attrName == "fromPath") {
|
||||||
PathSet context;
|
PathSet context;
|
||||||
fromPath = state.coerceToStorePath(attr.pos, *attr.value, context);
|
fromPath = state.coerceToStorePath(attr.pos, *attr.value, context,
|
||||||
|
"while evaluating the 'fromPath' attribute passed to builtins.fetchClosure");
|
||||||
}
|
}
|
||||||
|
|
||||||
else if (attrName == "toPath") {
|
else if (attrName == "toPath") {
|
||||||
|
@ -27,12 +28,14 @@ static void prim_fetchClosure(EvalState & state, const PosIdx pos, Value * * arg
|
||||||
toCA = true;
|
toCA = true;
|
||||||
if (attr.value->type() != nString || attr.value->string.s != std::string("")) {
|
if (attr.value->type() != nString || attr.value->string.s != std::string("")) {
|
||||||
PathSet context;
|
PathSet context;
|
||||||
toPath = state.coerceToStorePath(attr.pos, *attr.value, context);
|
toPath = state.coerceToStorePath(attr.pos, *attr.value, context,
|
||||||
|
"while evaluating the 'toPath' attribute passed to builtins.fetchClosure");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
else if (attrName == "fromStore")
|
else if (attrName == "fromStore")
|
||||||
fromStoreUrl = state.forceStringNoCtx(*attr.value, attr.pos);
|
fromStoreUrl = state.forceStringNoCtx(*attr.value, attr.pos,
|
||||||
|
"while evaluating the 'fromStore' attribute passed to builtins.fetchClosure");
|
||||||
|
|
||||||
else
|
else
|
||||||
throw Error({
|
throw Error({
|
||||||
|
|
|
@ -19,23 +19,23 @@ static void prim_fetchMercurial(EvalState & state, const PosIdx pos, Value * * a
|
||||||
|
|
||||||
if (args[0]->type() == nAttrs) {
|
if (args[0]->type() == nAttrs) {
|
||||||
|
|
||||||
state.forceAttrs(*args[0], pos);
|
|
||||||
|
|
||||||
for (auto & attr : *args[0]->attrs) {
|
for (auto & attr : *args[0]->attrs) {
|
||||||
std::string_view n(state.symbols[attr.name]);
|
std::string_view n(state.symbols[attr.name]);
|
||||||
if (n == "url")
|
if (n == "url")
|
||||||
url = state.coerceToString(attr.pos, *attr.value, context, false, false).toOwned();
|
url = state.coerceToString(attr.pos, *attr.value, context,
|
||||||
|
"while evaluating the `url` attribute passed to builtins.fetchMercurial",
|
||||||
|
false, false).toOwned();
|
||||||
else if (n == "rev") {
|
else if (n == "rev") {
|
||||||
// Ugly: unlike fetchGit, here the "rev" attribute can
|
// Ugly: unlike fetchGit, here the "rev" attribute can
|
||||||
// be both a revision or a branch/tag name.
|
// be both a revision or a branch/tag name.
|
||||||
auto value = state.forceStringNoCtx(*attr.value, attr.pos);
|
auto value = state.forceStringNoCtx(*attr.value, attr.pos, "while evaluating the `rev` attribute passed to builtins.fetchMercurial");
|
||||||
if (std::regex_match(value.begin(), value.end(), revRegex))
|
if (std::regex_match(value.begin(), value.end(), revRegex))
|
||||||
rev = Hash::parseAny(value, htSHA1);
|
rev = Hash::parseAny(value, htSHA1);
|
||||||
else
|
else
|
||||||
ref = value;
|
ref = value;
|
||||||
}
|
}
|
||||||
else if (n == "name")
|
else if (n == "name")
|
||||||
name = state.forceStringNoCtx(*attr.value, attr.pos);
|
name = state.forceStringNoCtx(*attr.value, attr.pos, "while evaluating the `name` attribute passed to builtins.fetchMercurial");
|
||||||
else
|
else
|
||||||
throw EvalError({
|
throw EvalError({
|
||||||
.msg = hintfmt("unsupported argument '%s' to 'fetchMercurial'", state.symbols[attr.name]),
|
.msg = hintfmt("unsupported argument '%s' to 'fetchMercurial'", state.symbols[attr.name]),
|
||||||
|
@ -50,7 +50,9 @@ static void prim_fetchMercurial(EvalState & state, const PosIdx pos, Value * * a
|
||||||
});
|
});
|
||||||
|
|
||||||
} else
|
} else
|
||||||
url = state.coerceToString(pos, *args[0], context, false, false).toOwned();
|
url = state.coerceToString(pos, *args[0], context,
|
||||||
|
"while evaluating the first argument passed to builtins.fetchMercurial",
|
||||||
|
false, false).toOwned();
|
||||||
|
|
||||||
// FIXME: git externals probably can be used to bypass the URI
|
// FIXME: git externals probably can be used to bypass the URI
|
||||||
// whitelist. Ah well.
|
// whitelist. Ah well.
|
||||||
|
|
|
@ -102,7 +102,7 @@ static void fetchTree(
|
||||||
state.forceValue(*args[0], pos);
|
state.forceValue(*args[0], pos);
|
||||||
|
|
||||||
if (args[0]->type() == nAttrs) {
|
if (args[0]->type() == nAttrs) {
|
||||||
state.forceAttrs(*args[0], pos);
|
state.forceAttrs(*args[0], pos, "while evaluating the argument passed to builtins.fetchTree");
|
||||||
|
|
||||||
fetchers::Attrs attrs;
|
fetchers::Attrs attrs;
|
||||||
|
|
||||||
|
@ -112,7 +112,7 @@ static void fetchTree(
|
||||||
.msg = hintfmt("unexpected attribute 'type'"),
|
.msg = hintfmt("unexpected attribute 'type'"),
|
||||||
.errPos = state.positions[pos]
|
.errPos = state.positions[pos]
|
||||||
}));
|
}));
|
||||||
type = state.forceStringNoCtx(*aType->value, aType->pos);
|
type = state.forceStringNoCtx(*aType->value, aType->pos, "while evaluating the `type` attribute passed to builtins.fetchTree");
|
||||||
} else if (!type)
|
} else if (!type)
|
||||||
state.debugThrowLastTrace(EvalError({
|
state.debugThrowLastTrace(EvalError({
|
||||||
.msg = hintfmt("attribute 'type' is missing in call to 'fetchTree'"),
|
.msg = hintfmt("attribute 'type' is missing in call to 'fetchTree'"),
|
||||||
|
@ -125,7 +125,7 @@ static void fetchTree(
|
||||||
if (attr.name == state.sType) continue;
|
if (attr.name == state.sType) continue;
|
||||||
state.forceValue(*attr.value, attr.pos);
|
state.forceValue(*attr.value, attr.pos);
|
||||||
if (attr.value->type() == nPath || attr.value->type() == nString) {
|
if (attr.value->type() == nPath || attr.value->type() == nString) {
|
||||||
auto s = state.coerceToString(attr.pos, *attr.value, context, false, false).toOwned();
|
auto s = state.coerceToString(attr.pos, *attr.value, context, "", false, false).toOwned();
|
||||||
attrs.emplace(state.symbols[attr.name],
|
attrs.emplace(state.symbols[attr.name],
|
||||||
state.symbols[attr.name] == "url"
|
state.symbols[attr.name] == "url"
|
||||||
? type == "git"
|
? type == "git"
|
||||||
|
@ -151,7 +151,9 @@ static void fetchTree(
|
||||||
|
|
||||||
input = fetchers::Input::fromAttrs(std::move(attrs));
|
input = fetchers::Input::fromAttrs(std::move(attrs));
|
||||||
} else {
|
} else {
|
||||||
auto url = state.coerceToString(pos, *args[0], context, false, false).toOwned();
|
auto url = state.coerceToString(pos, *args[0], context,
|
||||||
|
"while evaluating the first argument passed to the fetcher",
|
||||||
|
false, false).toOwned();
|
||||||
|
|
||||||
if (type == "git") {
|
if (type == "git") {
|
||||||
fetchers::Attrs attrs;
|
fetchers::Attrs attrs;
|
||||||
|
@ -195,16 +197,14 @@ static void fetch(EvalState & state, const PosIdx pos, Value * * args, Value & v
|
||||||
|
|
||||||
if (args[0]->type() == nAttrs) {
|
if (args[0]->type() == nAttrs) {
|
||||||
|
|
||||||
state.forceAttrs(*args[0], pos);
|
|
||||||
|
|
||||||
for (auto & attr : *args[0]->attrs) {
|
for (auto & attr : *args[0]->attrs) {
|
||||||
std::string_view n(state.symbols[attr.name]);
|
std::string_view n(state.symbols[attr.name]);
|
||||||
if (n == "url")
|
if (n == "url")
|
||||||
url = state.forceStringNoCtx(*attr.value, attr.pos);
|
url = state.forceStringNoCtx(*attr.value, attr.pos, "while evaluating the url we should fetch");
|
||||||
else if (n == "sha256")
|
else if (n == "sha256")
|
||||||
expectedHash = newHashAllowEmpty(state.forceStringNoCtx(*attr.value, attr.pos), htSHA256);
|
expectedHash = newHashAllowEmpty(state.forceStringNoCtx(*attr.value, attr.pos, "while evaluating the sha256 of the content we should fetch"), htSHA256);
|
||||||
else if (n == "name")
|
else if (n == "name")
|
||||||
name = state.forceStringNoCtx(*attr.value, attr.pos);
|
name = state.forceStringNoCtx(*attr.value, attr.pos, "while evaluating the name of the content we should fetch");
|
||||||
else
|
else
|
||||||
state.debugThrowLastTrace(EvalError({
|
state.debugThrowLastTrace(EvalError({
|
||||||
.msg = hintfmt("unsupported argument '%s' to '%s'", n, who),
|
.msg = hintfmt("unsupported argument '%s' to '%s'", n, who),
|
||||||
|
@ -218,7 +218,7 @@ static void fetch(EvalState & state, const PosIdx pos, Value * * args, Value & v
|
||||||
.errPos = state.positions[pos]
|
.errPos = state.positions[pos]
|
||||||
}));
|
}));
|
||||||
} else
|
} else
|
||||||
url = state.forceStringNoCtx(*args[0], pos);
|
url = state.forceStringNoCtx(*args[0], pos, "while evaluating the url we should fetch");
|
||||||
|
|
||||||
if (who == "fetchTarball")
|
if (who == "fetchTarball")
|
||||||
url = evalSettings.resolvePseudoUrl(*url);
|
url = evalSettings.resolvePseudoUrl(*url);
|
||||||
|
|
|
@ -7,7 +7,7 @@ namespace nix {
|
||||||
|
|
||||||
static void prim_fromTOML(EvalState & state, const PosIdx pos, Value * * args, Value & val)
|
static void prim_fromTOML(EvalState & state, const PosIdx pos, Value * * args, Value & val)
|
||||||
{
|
{
|
||||||
auto toml = state.forceStringNoCtx(*args[0], pos);
|
auto toml = state.forceStringNoCtx(*args[0], pos, "while evaluating the argument passed to builtins.fromTOML");
|
||||||
|
|
||||||
std::istringstream tomlStream(std::string{toml});
|
std::istringstream tomlStream(std::string{toml});
|
||||||
|
|
||||||
|
|
1298
src/libexpr/tests/error_traces.cc
Normal file
1298
src/libexpr/tests/error_traces.cc
Normal file
File diff suppressed because it is too large
Load diff
|
@ -823,4 +823,10 @@ namespace nix {
|
||||||
for (const auto [n, elem] : enumerate(v.listItems()))
|
for (const auto [n, elem] : enumerate(v.listItems()))
|
||||||
ASSERT_THAT(*elem, IsStringEq(expected[n]));
|
ASSERT_THAT(*elem, IsStringEq(expected[n]));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
TEST_F(PrimOpTest, genericClosure_not_strict) {
|
||||||
|
// Operator should not be used when startSet is empty
|
||||||
|
auto v = eval("builtins.genericClosure { startSet = []; }");
|
||||||
|
ASSERT_THAT(v, IsListOfSize(0));
|
||||||
|
}
|
||||||
} /* namespace nix */
|
} /* namespace nix */
|
||||||
|
|
|
@ -407,8 +407,6 @@ int handleExceptions(const std::string & programName, std::function<void()> fun)
|
||||||
return 1;
|
return 1;
|
||||||
} catch (BaseError & e) {
|
} catch (BaseError & e) {
|
||||||
logError(e.info());
|
logError(e.info());
|
||||||
if (e.hasTrace() && !loggerSettings.showTrace.get())
|
|
||||||
printError("(use '--show-trace' to show detailed location information)");
|
|
||||||
return e.status;
|
return e.status;
|
||||||
} catch (std::bad_alloc & e) {
|
} catch (std::bad_alloc & e) {
|
||||||
printError(error + "out of memory");
|
printError(error + "out of memory");
|
||||||
|
|
|
@ -13,6 +13,7 @@
|
||||||
|
|
||||||
namespace nix {
|
namespace nix {
|
||||||
|
|
||||||
|
class Store;
|
||||||
|
|
||||||
/* Abstract syntax of derivations. */
|
/* Abstract syntax of derivations. */
|
||||||
|
|
||||||
|
|
|
@ -1,8 +1,10 @@
|
||||||
#include "util.hh"
|
|
||||||
#include "outputs-spec.hh"
|
|
||||||
#include "nlohmann/json.hpp"
|
|
||||||
|
|
||||||
#include <regex>
|
#include <regex>
|
||||||
|
#include <nlohmann/json.hpp>
|
||||||
|
|
||||||
|
#include "util.hh"
|
||||||
|
#include "regex-combinators.hh"
|
||||||
|
#include "outputs-spec.hh"
|
||||||
|
#include "path-regex.hh"
|
||||||
|
|
||||||
namespace nix {
|
namespace nix {
|
||||||
|
|
||||||
|
@ -18,11 +20,14 @@ bool OutputsSpec::contains(const std::string & outputName) const
|
||||||
}, raw());
|
}, raw());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static std::string outputSpecRegexStr =
|
||||||
|
regex::either(
|
||||||
|
regex::group(R"(\*)"),
|
||||||
|
regex::group(regex::list(nameRegexStr)));
|
||||||
|
|
||||||
std::optional<OutputsSpec> OutputsSpec::parseOpt(std::string_view s)
|
std::optional<OutputsSpec> OutputsSpec::parseOpt(std::string_view s)
|
||||||
{
|
{
|
||||||
// See checkName() for valid output name characters.
|
static std::regex regex(std::string { outputSpecRegexStr });
|
||||||
static std::regex regex(R"((\*)|([a-zA-Z\+\-\._\?=]+(,[a-zA-Z\+\-\._\?=]+)*))");
|
|
||||||
|
|
||||||
std::smatch match;
|
std::smatch match;
|
||||||
std::string s2 { s }; // until some improves std::regex
|
std::string s2 { s }; // until some improves std::regex
|
||||||
|
|
7
src/libstore/path-regex.hh
Normal file
7
src/libstore/path-regex.hh
Normal file
|
@ -0,0 +1,7 @@
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
namespace nix {
|
||||||
|
|
||||||
|
static constexpr std::string_view nameRegexStr = R"([0-9a-zA-Z\+\-\._\?=]+)";
|
||||||
|
|
||||||
|
}
|
|
@ -8,8 +8,10 @@ static void checkName(std::string_view path, std::string_view name)
|
||||||
{
|
{
|
||||||
if (name.empty())
|
if (name.empty())
|
||||||
throw BadStorePath("store path '%s' has an empty name", path);
|
throw BadStorePath("store path '%s' has an empty name", path);
|
||||||
if (name.size() > 211)
|
if (name.size() > StorePath::MaxPathLen)
|
||||||
throw BadStorePath("store path '%s' has a name longer than 211 characters", path);
|
throw BadStorePath("store path '%s' has a name longer than '%d characters",
|
||||||
|
StorePath::MaxPathLen, path);
|
||||||
|
// See nameRegexStr for the definition
|
||||||
for (auto c : name)
|
for (auto c : name)
|
||||||
if (!((c >= '0' && c <= '9')
|
if (!((c >= '0' && c <= '9')
|
||||||
|| (c >= 'a' && c <= 'z')
|
|| (c >= 'a' && c <= 'z')
|
||||||
|
|
|
@ -5,7 +5,6 @@
|
||||||
|
|
||||||
namespace nix {
|
namespace nix {
|
||||||
|
|
||||||
class Store;
|
|
||||||
struct Hash;
|
struct Hash;
|
||||||
|
|
||||||
class StorePath
|
class StorePath
|
||||||
|
@ -17,6 +16,8 @@ public:
|
||||||
/* Size of the hash part of store paths, in base-32 characters. */
|
/* Size of the hash part of store paths, in base-32 characters. */
|
||||||
constexpr static size_t HashLen = 32; // i.e. 160 bits
|
constexpr static size_t HashLen = 32; // i.e. 160 bits
|
||||||
|
|
||||||
|
constexpr static size_t MaxPathLen = 211;
|
||||||
|
|
||||||
StorePath() = delete;
|
StorePath() = delete;
|
||||||
|
|
||||||
StorePath(std::string_view baseName);
|
StorePath(std::string_view baseName);
|
||||||
|
|
|
@ -7,6 +7,8 @@
|
||||||
|
|
||||||
namespace nix {
|
namespace nix {
|
||||||
|
|
||||||
|
class Store;
|
||||||
|
|
||||||
struct DrvOutput {
|
struct DrvOutput {
|
||||||
// The hash modulo of the derivation
|
// The hash modulo of the derivation
|
||||||
Hash drvHash;
|
Hash drvHash;
|
||||||
|
|
23
src/libstore/tests/libstoretests.hh
Normal file
23
src/libstore/tests/libstoretests.hh
Normal file
|
@ -0,0 +1,23 @@
|
||||||
|
#include <gtest/gtest.h>
|
||||||
|
#include <gmock/gmock.h>
|
||||||
|
|
||||||
|
#include "store-api.hh"
|
||||||
|
|
||||||
|
namespace nix {
|
||||||
|
|
||||||
|
class LibStoreTest : public ::testing::Test {
|
||||||
|
public:
|
||||||
|
static void SetUpTestSuite() {
|
||||||
|
initLibStore();
|
||||||
|
}
|
||||||
|
|
||||||
|
protected:
|
||||||
|
LibStoreTest()
|
||||||
|
: store(openStore("dummy://"))
|
||||||
|
{ }
|
||||||
|
|
||||||
|
ref<Store> store;
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
} /* namespace nix */
|
|
@ -12,4 +12,4 @@ libstore-tests_CXXFLAGS += -I src/libstore -I src/libutil
|
||||||
|
|
||||||
libstore-tests_LIBS = libstore libutil
|
libstore-tests_LIBS = libstore libutil
|
||||||
|
|
||||||
libstore-tests_LDFLAGS := $(GTEST_LIBS)
|
libstore-tests_LDFLAGS := -lrapidcheck $(GTEST_LIBS)
|
||||||
|
|
|
@ -47,6 +47,13 @@ TEST(OutputsSpec, names_underscore) {
|
||||||
ASSERT_EQ(expected.to_string(), str);
|
ASSERT_EQ(expected.to_string(), str);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
TEST(OutputsSpec, names_numberic) {
|
||||||
|
std::string_view str = "01";
|
||||||
|
OutputsSpec expected = OutputsSpec::Names { "01" };
|
||||||
|
ASSERT_EQ(OutputsSpec::parse(str), expected);
|
||||||
|
ASSERT_EQ(expected.to_string(), str);
|
||||||
|
}
|
||||||
|
|
||||||
TEST(OutputsSpec, names_out_bin) {
|
TEST(OutputsSpec, names_out_bin) {
|
||||||
OutputsSpec expected = OutputsSpec::Names { "out", "bin" };
|
OutputsSpec expected = OutputsSpec::Names { "out", "bin" };
|
||||||
ASSERT_EQ(OutputsSpec::parse("out,bin"), expected);
|
ASSERT_EQ(OutputsSpec::parse("out,bin"), expected);
|
||||||
|
|
144
src/libstore/tests/path.cc
Normal file
144
src/libstore/tests/path.cc
Normal file
|
@ -0,0 +1,144 @@
|
||||||
|
#include <regex>
|
||||||
|
|
||||||
|
#include <nlohmann/json.hpp>
|
||||||
|
#include <gtest/gtest.h>
|
||||||
|
#include <rapidcheck/gtest.h>
|
||||||
|
|
||||||
|
#include "path-regex.hh"
|
||||||
|
#include "store-api.hh"
|
||||||
|
|
||||||
|
#include "libstoretests.hh"
|
||||||
|
|
||||||
|
namespace nix {
|
||||||
|
|
||||||
|
#define STORE_DIR "/nix/store/"
|
||||||
|
#define HASH_PART "g1w7hy3qg1w7hy3qg1w7hy3qg1w7hy3q"
|
||||||
|
|
||||||
|
class StorePathTest : public LibStoreTest
|
||||||
|
{
|
||||||
|
};
|
||||||
|
|
||||||
|
static std::regex nameRegex { std::string { nameRegexStr } };
|
||||||
|
|
||||||
|
#define TEST_DONT_PARSE(NAME, STR) \
|
||||||
|
TEST_F(StorePathTest, bad_ ## NAME) { \
|
||||||
|
std::string_view str = \
|
||||||
|
STORE_DIR HASH_PART "-" STR; \
|
||||||
|
ASSERT_THROW( \
|
||||||
|
store->parseStorePath(str), \
|
||||||
|
BadStorePath); \
|
||||||
|
std::string name { STR }; \
|
||||||
|
EXPECT_FALSE(std::regex_match(name, nameRegex)); \
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_DONT_PARSE(empty, "")
|
||||||
|
TEST_DONT_PARSE(garbage, "&*()")
|
||||||
|
TEST_DONT_PARSE(double_star, "**")
|
||||||
|
TEST_DONT_PARSE(star_first, "*,foo")
|
||||||
|
TEST_DONT_PARSE(star_second, "foo,*")
|
||||||
|
TEST_DONT_PARSE(bang, "foo!o")
|
||||||
|
|
||||||
|
#undef TEST_DONT_PARSE
|
||||||
|
|
||||||
|
#define TEST_DO_PARSE(NAME, STR) \
|
||||||
|
TEST_F(StorePathTest, good_ ## NAME) { \
|
||||||
|
std::string_view str = \
|
||||||
|
STORE_DIR HASH_PART "-" STR; \
|
||||||
|
auto p = store->parseStorePath(str); \
|
||||||
|
std::string name { p.name() }; \
|
||||||
|
EXPECT_TRUE(std::regex_match(name, nameRegex)); \
|
||||||
|
}
|
||||||
|
|
||||||
|
// 0-9 a-z A-Z + - . _ ? =
|
||||||
|
|
||||||
|
TEST_DO_PARSE(numbers, "02345")
|
||||||
|
TEST_DO_PARSE(lower_case, "foo")
|
||||||
|
TEST_DO_PARSE(upper_case, "FOO")
|
||||||
|
TEST_DO_PARSE(plus, "foo+bar")
|
||||||
|
TEST_DO_PARSE(dash, "foo-dev")
|
||||||
|
TEST_DO_PARSE(underscore, "foo_bar")
|
||||||
|
TEST_DO_PARSE(period, "foo.txt")
|
||||||
|
TEST_DO_PARSE(question_mark, "foo?why")
|
||||||
|
TEST_DO_PARSE(equals_sign, "foo=foo")
|
||||||
|
|
||||||
|
#undef TEST_DO_PARSE
|
||||||
|
|
||||||
|
// For rapidcheck
|
||||||
|
void showValue(const StorePath & p, std::ostream & os) {
|
||||||
|
os << p.to_string();
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
namespace rc {
|
||||||
|
using namespace nix;
|
||||||
|
|
||||||
|
template<>
|
||||||
|
struct Arbitrary<StorePath> {
|
||||||
|
static Gen<StorePath> arbitrary();
|
||||||
|
};
|
||||||
|
|
||||||
|
Gen<StorePath> Arbitrary<StorePath>::arbitrary()
|
||||||
|
{
|
||||||
|
auto len = *gen::inRange<size_t>(1, StorePath::MaxPathLen);
|
||||||
|
|
||||||
|
std::string pre { HASH_PART "-" };
|
||||||
|
pre.reserve(pre.size() + len);
|
||||||
|
|
||||||
|
for (size_t c = 0; c < len; ++c) {
|
||||||
|
switch (auto i = *gen::inRange<uint8_t>(0, 10 + 2 * 26 + 6)) {
|
||||||
|
case 0 ... 9:
|
||||||
|
pre += '0' + i;
|
||||||
|
case 10 ... 35:
|
||||||
|
pre += 'A' + (i - 10);
|
||||||
|
break;
|
||||||
|
case 36 ... 61:
|
||||||
|
pre += 'a' + (i - 36);
|
||||||
|
break;
|
||||||
|
case 62:
|
||||||
|
pre += '+';
|
||||||
|
break;
|
||||||
|
case 63:
|
||||||
|
pre += '-';
|
||||||
|
break;
|
||||||
|
case 64:
|
||||||
|
pre += '.';
|
||||||
|
break;
|
||||||
|
case 65:
|
||||||
|
pre += '_';
|
||||||
|
break;
|
||||||
|
case 66:
|
||||||
|
pre += '?';
|
||||||
|
break;
|
||||||
|
case 67:
|
||||||
|
pre += '=';
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
assert(false);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return gen::just(StorePath { pre });
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace rc
|
||||||
|
|
||||||
|
namespace nix {
|
||||||
|
|
||||||
|
RC_GTEST_FIXTURE_PROP(
|
||||||
|
StorePathTest,
|
||||||
|
prop_regex_accept,
|
||||||
|
(const StorePath & p))
|
||||||
|
{
|
||||||
|
RC_ASSERT(std::regex_match(std::string { p.name() }, nameRegex));
|
||||||
|
}
|
||||||
|
|
||||||
|
RC_GTEST_FIXTURE_PROP(
|
||||||
|
StorePathTest,
|
||||||
|
prop_round_rip,
|
||||||
|
(const StorePath & p))
|
||||||
|
{
|
||||||
|
RC_ASSERT(p == store->parseStorePath(store->printStorePath(p)));
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -9,9 +9,9 @@ namespace nix {
|
||||||
|
|
||||||
const std::string nativeSystem = SYSTEM;
|
const std::string nativeSystem = SYSTEM;
|
||||||
|
|
||||||
void BaseError::addTrace(std::shared_ptr<AbstractPos> && e, hintformat hint)
|
void BaseError::addTrace(std::shared_ptr<AbstractPos> && e, hintformat hint, bool frame)
|
||||||
{
|
{
|
||||||
err.traces.push_front(Trace { .pos = std::move(e), .hint = hint });
|
err.traces.push_front(Trace { .pos = std::move(e), .hint = hint, .frame = frame });
|
||||||
}
|
}
|
||||||
|
|
||||||
// c++ std::exception descendants must have a 'const char* what()' function.
|
// c++ std::exception descendants must have a 'const char* what()' function.
|
||||||
|
@ -200,13 +200,125 @@ std::ostream & showErrorInfo(std::ostream & out, const ErrorInfo & einfo, bool s
|
||||||
|
|
||||||
auto noSource = ANSI_ITALIC " (source not available)" ANSI_NORMAL "\n";
|
auto noSource = ANSI_ITALIC " (source not available)" ANSI_NORMAL "\n";
|
||||||
|
|
||||||
// traces
|
/*
|
||||||
if (showTrace && !einfo.traces.empty()) {
|
* Traces
|
||||||
|
* ------
|
||||||
|
*
|
||||||
|
* The semantics of traces is a bit weird. We have only one option to
|
||||||
|
* print them and to make them verbose (--show-trace). In the code they
|
||||||
|
* are always collected, but they are not printed by default. The code
|
||||||
|
* also collects more traces when the option is on. This means that there
|
||||||
|
* is no way to print the simplified traces at all.
|
||||||
|
*
|
||||||
|
* I (layus) designed the code to attach positions to a restricted set of
|
||||||
|
* messages. This means that we have a lot of traces with no position at
|
||||||
|
* all, including most of the base error messages. For example "type
|
||||||
|
* error: found a string while a set was expected" has no position, but
|
||||||
|
* will come with several traces detailing it's precise relation to the
|
||||||
|
* closest know position. This makes erroring without printing traces
|
||||||
|
* quite useless.
|
||||||
|
*
|
||||||
|
* This is why I introduced the idea to always print a few traces on
|
||||||
|
* error. The number 3 is quite arbitrary, and was selected so as not to
|
||||||
|
* clutter the console on error. For the same reason, a trace with an
|
||||||
|
* error position takes more space, and counts as two traces towards the
|
||||||
|
* limit.
|
||||||
|
*
|
||||||
|
* The rest is truncated, unless --show-trace is passed. This preserves
|
||||||
|
* the same bad semantics of --show-trace to both show the trace and
|
||||||
|
* augment it with new data. Not too sure what is the best course of
|
||||||
|
* action.
|
||||||
|
*
|
||||||
|
* The issue is that it is fundamentally hard to provide a trace for a
|
||||||
|
* lazy language. The trace will only cover the current spine of the
|
||||||
|
* evaluation, missing things that have been evaluated before. For
|
||||||
|
* example, most type errors are hard to inspect because there is not
|
||||||
|
* trace for the faulty value. These errors should really print the faulty
|
||||||
|
* value itself.
|
||||||
|
*
|
||||||
|
* In function calls, the --show-trace flag triggers extra traces for each
|
||||||
|
* function invocation. These work as scopes, allowing to follow the
|
||||||
|
* current spine of the evaluation graph. Without that flag, the error
|
||||||
|
* trace should restrict itself to a restricted prefix of that trace,
|
||||||
|
* until the first scope. If we ever get to such a precise error
|
||||||
|
* reporting, there would be no need to add an arbitrary limit here. We
|
||||||
|
* could always print the full trace, and it would just be small without
|
||||||
|
* the flag.
|
||||||
|
*
|
||||||
|
* One idea I had is for XxxError.addTrace() to perform nothing if one
|
||||||
|
* scope has already been traced. Alternatively, we could stop here when
|
||||||
|
* we encounter such a scope instead of after an arbitrary number of
|
||||||
|
* traces. This however requires to augment traces with the notion of
|
||||||
|
* "scope".
|
||||||
|
*
|
||||||
|
* This is particularly visible in code like evalAttrs(...) where we have
|
||||||
|
* to make a decision between the two following options.
|
||||||
|
*
|
||||||
|
* ``` long traces
|
||||||
|
* inline void EvalState::evalAttrs(Env & env, Expr * e, Value & v, const Pos & pos, std::string_view errorCtx)
|
||||||
|
* {
|
||||||
|
* try {
|
||||||
|
* e->eval(*this, env, v);
|
||||||
|
* if (v.type() != nAttrs)
|
||||||
|
* throwTypeError("value is %1% while a set was expected", v);
|
||||||
|
* } catch (Error & e) {
|
||||||
|
* e.addTrace(pos, errorCtx);
|
||||||
|
* throw;
|
||||||
|
* }
|
||||||
|
* }
|
||||||
|
* ```
|
||||||
|
*
|
||||||
|
* ``` short traces
|
||||||
|
* inline void EvalState::evalAttrs(Env & env, Expr * e, Value & v, const Pos & pos, std::string_view errorCtx)
|
||||||
|
* {
|
||||||
|
* e->eval(*this, env, v);
|
||||||
|
* try {
|
||||||
|
* if (v.type() != nAttrs)
|
||||||
|
* throwTypeError("value is %1% while a set was expected", v);
|
||||||
|
* } catch (Error & e) {
|
||||||
|
* e.addTrace(pos, errorCtx);
|
||||||
|
* throw;
|
||||||
|
* }
|
||||||
|
* }
|
||||||
|
* ```
|
||||||
|
*
|
||||||
|
* The second example can be rewritten more concisely, but kept in this
|
||||||
|
* form to highlight the symmetry. The first option adds more information,
|
||||||
|
* because whatever caused an error down the line, in the generic eval
|
||||||
|
* function, will get annotated with the code location that uses and
|
||||||
|
* required it. The second option is less verbose, but does not provide
|
||||||
|
* any context at all as to where and why a failing value was required.
|
||||||
|
*
|
||||||
|
* Scopes would fix that, by adding context only when --show-trace is
|
||||||
|
* passed, and keeping the trace terse otherwise.
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
|
||||||
|
// Enough indent to align with with the `... `
|
||||||
|
// prepended to each element of the trace
|
||||||
|
auto ellipsisIndent = " ";
|
||||||
|
|
||||||
|
bool frameOnly = false;
|
||||||
|
if (!einfo.traces.empty()) {
|
||||||
|
size_t count = 0;
|
||||||
for (const auto & trace : einfo.traces) {
|
for (const auto & trace : einfo.traces) {
|
||||||
|
if (!showTrace && count > 3) {
|
||||||
|
oss << "\n" << ANSI_WARNING "(stack trace truncated; use '--show-trace' to show the full trace)" ANSI_NORMAL << "\n";
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (trace.hint.str().empty()) continue;
|
||||||
|
if (frameOnly && !trace.frame) continue;
|
||||||
|
|
||||||
|
count++;
|
||||||
|
frameOnly = trace.frame;
|
||||||
|
|
||||||
oss << "\n" << "… " << trace.hint.str() << "\n";
|
oss << "\n" << "… " << trace.hint.str() << "\n";
|
||||||
|
|
||||||
if (trace.pos) {
|
if (trace.pos) {
|
||||||
oss << "\n" << ANSI_BLUE << "at " ANSI_WARNING << *trace.pos << ANSI_NORMAL << ":";
|
count++;
|
||||||
|
|
||||||
|
oss << "\n" << ellipsisIndent << ANSI_BLUE << "at " ANSI_WARNING << *trace.pos << ANSI_NORMAL << ":";
|
||||||
|
|
||||||
if (auto loc = trace.pos->getCodeLines()) {
|
if (auto loc = trace.pos->getCodeLines()) {
|
||||||
oss << "\n";
|
oss << "\n";
|
||||||
|
|
|
@ -74,6 +74,8 @@ struct AbstractPos
|
||||||
virtual void print(std::ostream & out) const = 0;
|
virtual void print(std::ostream & out) const = 0;
|
||||||
|
|
||||||
std::optional<LinesOfCode> getCodeLines() const;
|
std::optional<LinesOfCode> getCodeLines() const;
|
||||||
|
|
||||||
|
virtual ~AbstractPos() = default;
|
||||||
};
|
};
|
||||||
|
|
||||||
std::ostream & operator << (std::ostream & str, const AbstractPos & pos);
|
std::ostream & operator << (std::ostream & str, const AbstractPos & pos);
|
||||||
|
@ -86,6 +88,7 @@ void printCodeLines(std::ostream & out,
|
||||||
struct Trace {
|
struct Trace {
|
||||||
std::shared_ptr<AbstractPos> pos;
|
std::shared_ptr<AbstractPos> pos;
|
||||||
hintformat hint;
|
hintformat hint;
|
||||||
|
bool frame;
|
||||||
};
|
};
|
||||||
|
|
||||||
struct ErrorInfo {
|
struct ErrorInfo {
|
||||||
|
@ -114,6 +117,8 @@ protected:
|
||||||
public:
|
public:
|
||||||
unsigned int status = 1; // exit status
|
unsigned int status = 1; // exit status
|
||||||
|
|
||||||
|
BaseError(const BaseError &) = default;
|
||||||
|
|
||||||
template<typename... Args>
|
template<typename... Args>
|
||||||
BaseError(unsigned int status, const Args & ... args)
|
BaseError(unsigned int status, const Args & ... args)
|
||||||
: err { .level = lvlError, .msg = hintfmt(args...) }
|
: err { .level = lvlError, .msg = hintfmt(args...) }
|
||||||
|
@ -152,15 +157,22 @@ public:
|
||||||
const std::string & msg() const { return calcWhat(); }
|
const std::string & msg() const { return calcWhat(); }
|
||||||
const ErrorInfo & info() const { calcWhat(); return err; }
|
const ErrorInfo & info() const { calcWhat(); return err; }
|
||||||
|
|
||||||
template<typename... Args>
|
void pushTrace(Trace trace)
|
||||||
void addTrace(std::shared_ptr<AbstractPos> && e, const std::string & fs, const Args & ... args)
|
|
||||||
{
|
{
|
||||||
addTrace(std::move(e), hintfmt(fs, args...));
|
err.traces.push_front(trace);
|
||||||
}
|
}
|
||||||
|
|
||||||
void addTrace(std::shared_ptr<AbstractPos> && e, hintformat hint);
|
template<typename... Args>
|
||||||
|
void addTrace(std::shared_ptr<AbstractPos> && e, std::string_view fs, const Args & ... args)
|
||||||
|
{
|
||||||
|
addTrace(std::move(e), hintfmt(std::string(fs), args...));
|
||||||
|
}
|
||||||
|
|
||||||
|
void addTrace(std::shared_ptr<AbstractPos> && e, hintformat hint, bool frame = false);
|
||||||
|
|
||||||
bool hasTrace() const { return !err.traces.empty(); }
|
bool hasTrace() const { return !err.traces.empty(); }
|
||||||
|
|
||||||
|
const ErrorInfo & info() { return err; };
|
||||||
};
|
};
|
||||||
|
|
||||||
#define MakeError(newClass, superClass) \
|
#define MakeError(newClass, superClass) \
|
||||||
|
|
30
src/libutil/regex-combinators.hh
Normal file
30
src/libutil/regex-combinators.hh
Normal file
|
@ -0,0 +1,30 @@
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <string_view>
|
||||||
|
|
||||||
|
namespace nix::regex {
|
||||||
|
|
||||||
|
// TODO use constexpr string building like
|
||||||
|
// https://github.com/akrzemi1/static_string/blob/master/include/ak_toolkit/static_string.hpp
|
||||||
|
|
||||||
|
static inline std::string either(std::string_view a, std::string_view b)
|
||||||
|
{
|
||||||
|
return std::string { a } + "|" + b;
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline std::string group(std::string_view a)
|
||||||
|
{
|
||||||
|
return std::string { "(" } + a + ")";
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline std::string many(std::string_view a)
|
||||||
|
{
|
||||||
|
return std::string { "(?:" } + a + ")*";
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline std::string list(std::string_view a)
|
||||||
|
{
|
||||||
|
return std::string { a } + many(group("," + a));
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -134,9 +134,9 @@ bool createUserEnv(EvalState & state, DrvInfos & elems,
|
||||||
state.forceValue(topLevel, [&]() { return topLevel.determinePos(noPos); });
|
state.forceValue(topLevel, [&]() { return topLevel.determinePos(noPos); });
|
||||||
PathSet context;
|
PathSet context;
|
||||||
Attr & aDrvPath(*topLevel.attrs->find(state.sDrvPath));
|
Attr & aDrvPath(*topLevel.attrs->find(state.sDrvPath));
|
||||||
auto topLevelDrv = state.coerceToStorePath(aDrvPath.pos, *aDrvPath.value, context);
|
auto topLevelDrv = state.coerceToStorePath(aDrvPath.pos, *aDrvPath.value, context, "");
|
||||||
Attr & aOutPath(*topLevel.attrs->find(state.sOutPath));
|
Attr & aOutPath(*topLevel.attrs->find(state.sOutPath));
|
||||||
auto topLevelOut = state.coerceToStorePath(aOutPath.pos, *aOutPath.value, context);
|
auto topLevelOut = state.coerceToStorePath(aOutPath.pos, *aOutPath.value, context, "");
|
||||||
|
|
||||||
/* Realise the resulting store expression. */
|
/* Realise the resulting store expression. */
|
||||||
debug("building user environment");
|
debug("building user environment");
|
||||||
|
|
|
@ -97,13 +97,13 @@ struct CmdBundle : InstallableCommand
|
||||||
throw Error("the bundler '%s' does not produce a derivation", bundler.what());
|
throw Error("the bundler '%s' does not produce a derivation", bundler.what());
|
||||||
|
|
||||||
PathSet context2;
|
PathSet context2;
|
||||||
auto drvPath = evalState->coerceToStorePath(attr1->pos, *attr1->value, context2);
|
auto drvPath = evalState->coerceToStorePath(attr1->pos, *attr1->value, context2, "");
|
||||||
|
|
||||||
auto attr2 = vRes->attrs->get(evalState->sOutPath);
|
auto attr2 = vRes->attrs->get(evalState->sOutPath);
|
||||||
if (!attr2)
|
if (!attr2)
|
||||||
throw Error("the bundler '%s' does not produce a derivation", bundler.what());
|
throw Error("the bundler '%s' does not produce a derivation", bundler.what());
|
||||||
|
|
||||||
auto outPath = evalState->coerceToStorePath(attr2->pos, *attr2->value, context2);
|
auto outPath = evalState->coerceToStorePath(attr2->pos, *attr2->value, context2, "");
|
||||||
|
|
||||||
store->buildPaths({
|
store->buildPaths({
|
||||||
DerivedPath::Built {
|
DerivedPath::Built {
|
||||||
|
@ -118,7 +118,7 @@ struct CmdBundle : InstallableCommand
|
||||||
auto * attr = vRes->attrs->get(evalState->sName);
|
auto * attr = vRes->attrs->get(evalState->sName);
|
||||||
if (!attr)
|
if (!attr)
|
||||||
throw Error("attribute 'name' missing");
|
throw Error("attribute 'name' missing");
|
||||||
outLink = evalState->forceStringNoCtx(*attr->value, attr->pos);
|
outLink = evalState->forceStringNoCtx(*attr->value, attr->pos, "");
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO: will crash if not a localFSStore?
|
// TODO: will crash if not a localFSStore?
|
||||||
|
|
|
@ -112,7 +112,7 @@ struct CmdEval : MixJSON, InstallableCommand
|
||||||
|
|
||||||
else if (raw) {
|
else if (raw) {
|
||||||
stopProgressBar();
|
stopProgressBar();
|
||||||
std::cout << *state->coerceToString(noPos, *v, context);
|
std::cout << *state->coerceToString(noPos, *v, context, "while generating the eval command output");
|
||||||
}
|
}
|
||||||
|
|
||||||
else if (json) {
|
else if (json) {
|
||||||
|
|
|
@ -126,12 +126,12 @@ static void enumerateOutputs(EvalState & state, Value & vFlake,
|
||||||
std::function<void(const std::string & name, Value & vProvide, const PosIdx pos)> callback)
|
std::function<void(const std::string & name, Value & vProvide, const PosIdx pos)> callback)
|
||||||
{
|
{
|
||||||
auto pos = vFlake.determinePos(noPos);
|
auto pos = vFlake.determinePos(noPos);
|
||||||
state.forceAttrs(vFlake, pos);
|
state.forceAttrs(vFlake, pos, "while evaluating a flake to get its outputs");
|
||||||
|
|
||||||
auto aOutputs = vFlake.attrs->get(state.symbols.create("outputs"));
|
auto aOutputs = vFlake.attrs->get(state.symbols.create("outputs"));
|
||||||
assert(aOutputs);
|
assert(aOutputs);
|
||||||
|
|
||||||
state.forceAttrs(*aOutputs->value, pos);
|
state.forceAttrs(*aOutputs->value, pos, "while evaluating the outputs of a flake");
|
||||||
|
|
||||||
auto sHydraJobs = state.symbols.create("hydraJobs");
|
auto sHydraJobs = state.symbols.create("hydraJobs");
|
||||||
|
|
||||||
|
@ -391,13 +391,13 @@ struct CmdFlakeCheck : FlakeCommand
|
||||||
|
|
||||||
checkHydraJobs = [&](const std::string & attrPath, Value & v, const PosIdx pos) {
|
checkHydraJobs = [&](const std::string & attrPath, Value & v, const PosIdx pos) {
|
||||||
try {
|
try {
|
||||||
state->forceAttrs(v, pos);
|
state->forceAttrs(v, pos, "");
|
||||||
|
|
||||||
if (state->isDerivation(v))
|
if (state->isDerivation(v))
|
||||||
throw Error("jobset should not be a derivation at top-level");
|
throw Error("jobset should not be a derivation at top-level");
|
||||||
|
|
||||||
for (auto & attr : *v.attrs) {
|
for (auto & attr : *v.attrs) {
|
||||||
state->forceAttrs(*attr.value, attr.pos);
|
state->forceAttrs(*attr.value, attr.pos, "");
|
||||||
auto attrPath2 = concatStrings(attrPath, ".", state->symbols[attr.name]);
|
auto attrPath2 = concatStrings(attrPath, ".", state->symbols[attr.name]);
|
||||||
if (state->isDerivation(*attr.value)) {
|
if (state->isDerivation(*attr.value)) {
|
||||||
Activity act(*logger, lvlChatty, actUnknown,
|
Activity act(*logger, lvlChatty, actUnknown,
|
||||||
|
@ -419,7 +419,7 @@ struct CmdFlakeCheck : FlakeCommand
|
||||||
fmt("checking NixOS configuration '%s'", attrPath));
|
fmt("checking NixOS configuration '%s'", attrPath));
|
||||||
Bindings & bindings(*state->allocBindings(0));
|
Bindings & bindings(*state->allocBindings(0));
|
||||||
auto vToplevel = findAlongAttrPath(*state, "config.system.build.toplevel", bindings, v).first;
|
auto vToplevel = findAlongAttrPath(*state, "config.system.build.toplevel", bindings, v).first;
|
||||||
state->forceAttrs(*vToplevel, pos);
|
state->forceValue(*vToplevel, pos);
|
||||||
if (!state->isDerivation(*vToplevel))
|
if (!state->isDerivation(*vToplevel))
|
||||||
throw Error("attribute 'config.system.build.toplevel' is not a derivation");
|
throw Error("attribute 'config.system.build.toplevel' is not a derivation");
|
||||||
} catch (Error & e) {
|
} catch (Error & e) {
|
||||||
|
@ -433,12 +433,12 @@ struct CmdFlakeCheck : FlakeCommand
|
||||||
Activity act(*logger, lvlChatty, actUnknown,
|
Activity act(*logger, lvlChatty, actUnknown,
|
||||||
fmt("checking template '%s'", attrPath));
|
fmt("checking template '%s'", attrPath));
|
||||||
|
|
||||||
state->forceAttrs(v, pos);
|
state->forceAttrs(v, pos, "");
|
||||||
|
|
||||||
if (auto attr = v.attrs->get(state->symbols.create("path"))) {
|
if (auto attr = v.attrs->get(state->symbols.create("path"))) {
|
||||||
if (attr->name == state->symbols.create("path")) {
|
if (attr->name == state->symbols.create("path")) {
|
||||||
PathSet context;
|
PathSet context;
|
||||||
auto path = state->coerceToPath(attr->pos, *attr->value, context);
|
auto path = state->coerceToPath(attr->pos, *attr->value, context, "");
|
||||||
if (!store->isInStore(path))
|
if (!store->isInStore(path))
|
||||||
throw Error("template '%s' has a bad 'path' attribute");
|
throw Error("template '%s' has a bad 'path' attribute");
|
||||||
// TODO: recursively check the flake in 'path'.
|
// TODO: recursively check the flake in 'path'.
|
||||||
|
@ -447,7 +447,7 @@ struct CmdFlakeCheck : FlakeCommand
|
||||||
throw Error("template '%s' lacks attribute 'path'", attrPath);
|
throw Error("template '%s' lacks attribute 'path'", attrPath);
|
||||||
|
|
||||||
if (auto attr = v.attrs->get(state->symbols.create("description")))
|
if (auto attr = v.attrs->get(state->symbols.create("description")))
|
||||||
state->forceStringNoCtx(*attr->value, attr->pos);
|
state->forceStringNoCtx(*attr->value, attr->pos, "");
|
||||||
else
|
else
|
||||||
throw Error("template '%s' lacks attribute 'description'", attrPath);
|
throw Error("template '%s' lacks attribute 'description'", attrPath);
|
||||||
|
|
||||||
|
@ -504,11 +504,11 @@ struct CmdFlakeCheck : FlakeCommand
|
||||||
warn("flake output attribute '%s' is deprecated; use '%s' instead", name, replacement);
|
warn("flake output attribute '%s' is deprecated; use '%s' instead", name, replacement);
|
||||||
|
|
||||||
if (name == "checks") {
|
if (name == "checks") {
|
||||||
state->forceAttrs(vOutput, pos);
|
state->forceAttrs(vOutput, pos, "");
|
||||||
for (auto & attr : *vOutput.attrs) {
|
for (auto & attr : *vOutput.attrs) {
|
||||||
const auto & attr_name = state->symbols[attr.name];
|
const auto & attr_name = state->symbols[attr.name];
|
||||||
checkSystemName(attr_name, attr.pos);
|
checkSystemName(attr_name, attr.pos);
|
||||||
state->forceAttrs(*attr.value, attr.pos);
|
state->forceAttrs(*attr.value, attr.pos, "");
|
||||||
for (auto & attr2 : *attr.value->attrs) {
|
for (auto & attr2 : *attr.value->attrs) {
|
||||||
auto drvPath = checkDerivation(
|
auto drvPath = checkDerivation(
|
||||||
fmt("%s.%s.%s", name, attr_name, state->symbols[attr2.name]),
|
fmt("%s.%s.%s", name, attr_name, state->symbols[attr2.name]),
|
||||||
|
@ -524,7 +524,7 @@ struct CmdFlakeCheck : FlakeCommand
|
||||||
}
|
}
|
||||||
|
|
||||||
else if (name == "formatter") {
|
else if (name == "formatter") {
|
||||||
state->forceAttrs(vOutput, pos);
|
state->forceAttrs(vOutput, pos, "");
|
||||||
for (auto & attr : *vOutput.attrs) {
|
for (auto & attr : *vOutput.attrs) {
|
||||||
const auto & attr_name = state->symbols[attr.name];
|
const auto & attr_name = state->symbols[attr.name];
|
||||||
checkSystemName(attr_name, attr.pos);
|
checkSystemName(attr_name, attr.pos);
|
||||||
|
@ -535,11 +535,11 @@ struct CmdFlakeCheck : FlakeCommand
|
||||||
}
|
}
|
||||||
|
|
||||||
else if (name == "packages" || name == "devShells") {
|
else if (name == "packages" || name == "devShells") {
|
||||||
state->forceAttrs(vOutput, pos);
|
state->forceAttrs(vOutput, pos, "");
|
||||||
for (auto & attr : *vOutput.attrs) {
|
for (auto & attr : *vOutput.attrs) {
|
||||||
const auto & attr_name = state->symbols[attr.name];
|
const auto & attr_name = state->symbols[attr.name];
|
||||||
checkSystemName(attr_name, attr.pos);
|
checkSystemName(attr_name, attr.pos);
|
||||||
state->forceAttrs(*attr.value, attr.pos);
|
state->forceAttrs(*attr.value, attr.pos, "");
|
||||||
for (auto & attr2 : *attr.value->attrs)
|
for (auto & attr2 : *attr.value->attrs)
|
||||||
checkDerivation(
|
checkDerivation(
|
||||||
fmt("%s.%s.%s", name, attr_name, state->symbols[attr2.name]),
|
fmt("%s.%s.%s", name, attr_name, state->symbols[attr2.name]),
|
||||||
|
@ -548,11 +548,11 @@ struct CmdFlakeCheck : FlakeCommand
|
||||||
}
|
}
|
||||||
|
|
||||||
else if (name == "apps") {
|
else if (name == "apps") {
|
||||||
state->forceAttrs(vOutput, pos);
|
state->forceAttrs(vOutput, pos, "");
|
||||||
for (auto & attr : *vOutput.attrs) {
|
for (auto & attr : *vOutput.attrs) {
|
||||||
const auto & attr_name = state->symbols[attr.name];
|
const auto & attr_name = state->symbols[attr.name];
|
||||||
checkSystemName(attr_name, attr.pos);
|
checkSystemName(attr_name, attr.pos);
|
||||||
state->forceAttrs(*attr.value, attr.pos);
|
state->forceAttrs(*attr.value, attr.pos, "");
|
||||||
for (auto & attr2 : *attr.value->attrs)
|
for (auto & attr2 : *attr.value->attrs)
|
||||||
checkApp(
|
checkApp(
|
||||||
fmt("%s.%s.%s", name, attr_name, state->symbols[attr2.name]),
|
fmt("%s.%s.%s", name, attr_name, state->symbols[attr2.name]),
|
||||||
|
@ -561,7 +561,7 @@ struct CmdFlakeCheck : FlakeCommand
|
||||||
}
|
}
|
||||||
|
|
||||||
else if (name == "defaultPackage" || name == "devShell") {
|
else if (name == "defaultPackage" || name == "devShell") {
|
||||||
state->forceAttrs(vOutput, pos);
|
state->forceAttrs(vOutput, pos, "");
|
||||||
for (auto & attr : *vOutput.attrs) {
|
for (auto & attr : *vOutput.attrs) {
|
||||||
const auto & attr_name = state->symbols[attr.name];
|
const auto & attr_name = state->symbols[attr.name];
|
||||||
checkSystemName(attr_name, attr.pos);
|
checkSystemName(attr_name, attr.pos);
|
||||||
|
@ -572,7 +572,7 @@ struct CmdFlakeCheck : FlakeCommand
|
||||||
}
|
}
|
||||||
|
|
||||||
else if (name == "defaultApp") {
|
else if (name == "defaultApp") {
|
||||||
state->forceAttrs(vOutput, pos);
|
state->forceAttrs(vOutput, pos, "");
|
||||||
for (auto & attr : *vOutput.attrs) {
|
for (auto & attr : *vOutput.attrs) {
|
||||||
const auto & attr_name = state->symbols[attr.name];
|
const auto & attr_name = state->symbols[attr.name];
|
||||||
checkSystemName(attr_name, attr.pos);
|
checkSystemName(attr_name, attr.pos);
|
||||||
|
@ -583,7 +583,7 @@ struct CmdFlakeCheck : FlakeCommand
|
||||||
}
|
}
|
||||||
|
|
||||||
else if (name == "legacyPackages") {
|
else if (name == "legacyPackages") {
|
||||||
state->forceAttrs(vOutput, pos);
|
state->forceAttrs(vOutput, pos, "");
|
||||||
for (auto & attr : *vOutput.attrs) {
|
for (auto & attr : *vOutput.attrs) {
|
||||||
checkSystemName(state->symbols[attr.name], attr.pos);
|
checkSystemName(state->symbols[attr.name], attr.pos);
|
||||||
// FIXME: do getDerivations?
|
// FIXME: do getDerivations?
|
||||||
|
@ -594,7 +594,7 @@ struct CmdFlakeCheck : FlakeCommand
|
||||||
checkOverlay(name, vOutput, pos);
|
checkOverlay(name, vOutput, pos);
|
||||||
|
|
||||||
else if (name == "overlays") {
|
else if (name == "overlays") {
|
||||||
state->forceAttrs(vOutput, pos);
|
state->forceAttrs(vOutput, pos, "");
|
||||||
for (auto & attr : *vOutput.attrs)
|
for (auto & attr : *vOutput.attrs)
|
||||||
checkOverlay(fmt("%s.%s", name, state->symbols[attr.name]),
|
checkOverlay(fmt("%s.%s", name, state->symbols[attr.name]),
|
||||||
*attr.value, attr.pos);
|
*attr.value, attr.pos);
|
||||||
|
@ -604,14 +604,14 @@ struct CmdFlakeCheck : FlakeCommand
|
||||||
checkModule(name, vOutput, pos);
|
checkModule(name, vOutput, pos);
|
||||||
|
|
||||||
else if (name == "nixosModules") {
|
else if (name == "nixosModules") {
|
||||||
state->forceAttrs(vOutput, pos);
|
state->forceAttrs(vOutput, pos, "");
|
||||||
for (auto & attr : *vOutput.attrs)
|
for (auto & attr : *vOutput.attrs)
|
||||||
checkModule(fmt("%s.%s", name, state->symbols[attr.name]),
|
checkModule(fmt("%s.%s", name, state->symbols[attr.name]),
|
||||||
*attr.value, attr.pos);
|
*attr.value, attr.pos);
|
||||||
}
|
}
|
||||||
|
|
||||||
else if (name == "nixosConfigurations") {
|
else if (name == "nixosConfigurations") {
|
||||||
state->forceAttrs(vOutput, pos);
|
state->forceAttrs(vOutput, pos, "");
|
||||||
for (auto & attr : *vOutput.attrs)
|
for (auto & attr : *vOutput.attrs)
|
||||||
checkNixOSConfiguration(fmt("%s.%s", name, state->symbols[attr.name]),
|
checkNixOSConfiguration(fmt("%s.%s", name, state->symbols[attr.name]),
|
||||||
*attr.value, attr.pos);
|
*attr.value, attr.pos);
|
||||||
|
@ -624,14 +624,14 @@ struct CmdFlakeCheck : FlakeCommand
|
||||||
checkTemplate(name, vOutput, pos);
|
checkTemplate(name, vOutput, pos);
|
||||||
|
|
||||||
else if (name == "templates") {
|
else if (name == "templates") {
|
||||||
state->forceAttrs(vOutput, pos);
|
state->forceAttrs(vOutput, pos, "");
|
||||||
for (auto & attr : *vOutput.attrs)
|
for (auto & attr : *vOutput.attrs)
|
||||||
checkTemplate(fmt("%s.%s", name, state->symbols[attr.name]),
|
checkTemplate(fmt("%s.%s", name, state->symbols[attr.name]),
|
||||||
*attr.value, attr.pos);
|
*attr.value, attr.pos);
|
||||||
}
|
}
|
||||||
|
|
||||||
else if (name == "defaultBundler") {
|
else if (name == "defaultBundler") {
|
||||||
state->forceAttrs(vOutput, pos);
|
state->forceAttrs(vOutput, pos, "");
|
||||||
for (auto & attr : *vOutput.attrs) {
|
for (auto & attr : *vOutput.attrs) {
|
||||||
const auto & attr_name = state->symbols[attr.name];
|
const auto & attr_name = state->symbols[attr.name];
|
||||||
checkSystemName(attr_name, attr.pos);
|
checkSystemName(attr_name, attr.pos);
|
||||||
|
@ -642,11 +642,11 @@ struct CmdFlakeCheck : FlakeCommand
|
||||||
}
|
}
|
||||||
|
|
||||||
else if (name == "bundlers") {
|
else if (name == "bundlers") {
|
||||||
state->forceAttrs(vOutput, pos);
|
state->forceAttrs(vOutput, pos, "");
|
||||||
for (auto & attr : *vOutput.attrs) {
|
for (auto & attr : *vOutput.attrs) {
|
||||||
const auto & attr_name = state->symbols[attr.name];
|
const auto & attr_name = state->symbols[attr.name];
|
||||||
checkSystemName(attr_name, attr.pos);
|
checkSystemName(attr_name, attr.pos);
|
||||||
state->forceAttrs(*attr.value, attr.pos);
|
state->forceAttrs(*attr.value, attr.pos, "");
|
||||||
for (auto & attr2 : *attr.value->attrs) {
|
for (auto & attr2 : *attr.value->attrs) {
|
||||||
checkBundler(
|
checkBundler(
|
||||||
fmt("%s.%s.%s", name, attr_name, state->symbols[attr2.name]),
|
fmt("%s.%s.%s", name, attr_name, state->symbols[attr2.name]),
|
||||||
|
|
|
@ -199,7 +199,7 @@ static void showHelp(std::vector<std::string> subcommand, MultiCommand & topleve
|
||||||
if (!attr)
|
if (!attr)
|
||||||
throw UsageError("Nix has no subcommand '%s'", concatStringsSep("", subcommand));
|
throw UsageError("Nix has no subcommand '%s'", concatStringsSep("", subcommand));
|
||||||
|
|
||||||
auto markdown = state.forceString(*attr->value);
|
auto markdown = state.forceString(*attr->value, noPos, "while evaluating the lowdown help text");
|
||||||
|
|
||||||
RunPager pager;
|
RunPager pager;
|
||||||
std::cout << renderMarkdownToTerminal(markdown) << "\n";
|
std::cout << renderMarkdownToTerminal(markdown) << "\n";
|
||||||
|
|
|
@ -28,17 +28,17 @@ std::string resolveMirrorUrl(EvalState & state, const std::string & url)
|
||||||
Value vMirrors;
|
Value vMirrors;
|
||||||
// FIXME: use nixpkgs flake
|
// FIXME: use nixpkgs flake
|
||||||
state.eval(state.parseExprFromString("import <nixpkgs/pkgs/build-support/fetchurl/mirrors.nix>", "."), vMirrors);
|
state.eval(state.parseExprFromString("import <nixpkgs/pkgs/build-support/fetchurl/mirrors.nix>", "."), vMirrors);
|
||||||
state.forceAttrs(vMirrors, noPos);
|
state.forceAttrs(vMirrors, noPos, "while evaluating the set of all mirrors");
|
||||||
|
|
||||||
auto mirrorList = vMirrors.attrs->find(state.symbols.create(mirrorName));
|
auto mirrorList = vMirrors.attrs->find(state.symbols.create(mirrorName));
|
||||||
if (mirrorList == vMirrors.attrs->end())
|
if (mirrorList == vMirrors.attrs->end())
|
||||||
throw Error("unknown mirror name '%s'", mirrorName);
|
throw Error("unknown mirror name '%s'", mirrorName);
|
||||||
state.forceList(*mirrorList->value, noPos);
|
state.forceList(*mirrorList->value, noPos, "while evaluating one mirror configuration");
|
||||||
|
|
||||||
if (mirrorList->value->listSize() < 1)
|
if (mirrorList->value->listSize() < 1)
|
||||||
throw Error("mirror URL '%s' did not expand to anything", url);
|
throw Error("mirror URL '%s' did not expand to anything", url);
|
||||||
|
|
||||||
std::string mirror(state.forceString(*mirrorList->value->listElems()[0]));
|
std::string mirror(state.forceString(*mirrorList->value->listElems()[0], noPos, "while evaluating the first available mirror"));
|
||||||
return mirror + (hasSuffix(mirror, "/") ? "" : "/") + s.substr(p + 1);
|
return mirror + (hasSuffix(mirror, "/") ? "" : "/") + s.substr(p + 1);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -196,29 +196,29 @@ static int main_nix_prefetch_url(int argc, char * * argv)
|
||||||
Value vRoot;
|
Value vRoot;
|
||||||
state->evalFile(path, vRoot);
|
state->evalFile(path, vRoot);
|
||||||
Value & v(*findAlongAttrPath(*state, attrPath, autoArgs, vRoot).first);
|
Value & v(*findAlongAttrPath(*state, attrPath, autoArgs, vRoot).first);
|
||||||
state->forceAttrs(v, noPos);
|
state->forceAttrs(v, noPos, "while evaluating the source attribute to prefetch");
|
||||||
|
|
||||||
/* Extract the URL. */
|
/* Extract the URL. */
|
||||||
auto * attr = v.attrs->get(state->symbols.create("urls"));
|
auto * attr = v.attrs->get(state->symbols.create("urls"));
|
||||||
if (!attr)
|
if (!attr)
|
||||||
throw Error("attribute 'urls' missing");
|
throw Error("attribute 'urls' missing");
|
||||||
state->forceList(*attr->value, noPos);
|
state->forceList(*attr->value, noPos, "while evaluating the urls to prefetch");
|
||||||
if (attr->value->listSize() < 1)
|
if (attr->value->listSize() < 1)
|
||||||
throw Error("'urls' list is empty");
|
throw Error("'urls' list is empty");
|
||||||
url = state->forceString(*attr->value->listElems()[0]);
|
url = state->forceString(*attr->value->listElems()[0], noPos, "while evaluating the first url from the urls list");
|
||||||
|
|
||||||
/* Extract the hash mode. */
|
/* Extract the hash mode. */
|
||||||
auto attr2 = v.attrs->get(state->symbols.create("outputHashMode"));
|
auto attr2 = v.attrs->get(state->symbols.create("outputHashMode"));
|
||||||
if (!attr2)
|
if (!attr2)
|
||||||
printInfo("warning: this does not look like a fetchurl call");
|
printInfo("warning: this does not look like a fetchurl call");
|
||||||
else
|
else
|
||||||
unpack = state->forceString(*attr2->value) == "recursive";
|
unpack = state->forceString(*attr2->value, noPos, "while evaluating the outputHashMode of the source to prefetch") == "recursive";
|
||||||
|
|
||||||
/* Extract the name. */
|
/* Extract the name. */
|
||||||
if (!name) {
|
if (!name) {
|
||||||
auto attr3 = v.attrs->get(state->symbols.create("name"));
|
auto attr3 = v.attrs->get(state->symbols.create("name"));
|
||||||
if (!attr3)
|
if (!attr3)
|
||||||
name = state->forceString(*attr3->value);
|
name = state->forceString(*attr3->value, noPos, "while evaluating the name of the source to prefetch");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -9,15 +9,44 @@ using namespace nix;
|
||||||
|
|
||||||
struct CmdShowConfig : Command, MixJSON
|
struct CmdShowConfig : Command, MixJSON
|
||||||
{
|
{
|
||||||
|
std::optional<std::string> name;
|
||||||
|
|
||||||
|
CmdShowConfig() {
|
||||||
|
expectArgs({
|
||||||
|
.label = {"name"},
|
||||||
|
.optional = true,
|
||||||
|
.handler = {&name},
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
std::string description() override
|
std::string description() override
|
||||||
{
|
{
|
||||||
return "show the Nix configuration";
|
return "show the Nix configuration or the value of a specific setting";
|
||||||
}
|
}
|
||||||
|
|
||||||
Category category() override { return catUtility; }
|
Category category() override { return catUtility; }
|
||||||
|
|
||||||
void run() override
|
void run() override
|
||||||
{
|
{
|
||||||
|
if (name) {
|
||||||
|
if (json) {
|
||||||
|
throw UsageError("'--json' is not supported when specifying a setting name");
|
||||||
|
}
|
||||||
|
|
||||||
|
std::map<std::string, Config::SettingInfo> settings;
|
||||||
|
globalConfig.getSettings(settings);
|
||||||
|
auto setting = settings.find(*name);
|
||||||
|
|
||||||
|
if (setting == settings.end()) {
|
||||||
|
throw Error("could not find setting '%1%'", *name);
|
||||||
|
} else {
|
||||||
|
const auto & value = setting->second.value;
|
||||||
|
logger->cout("%s", value);
|
||||||
|
}
|
||||||
|
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
if (json) {
|
if (json) {
|
||||||
// FIXME: use appropriate JSON types (bool, ints, etc).
|
// FIXME: use appropriate JSON types (bool, ints, etc).
|
||||||
logger->cout("%s", globalConfig.toJSON().dump());
|
logger->cout("%s", globalConfig.toJSON().dump());
|
||||||
|
|
|
@ -144,7 +144,7 @@ struct CmdUpgradeNix : MixDryRun, StoreCommand
|
||||||
Bindings & bindings(*state->allocBindings(0));
|
Bindings & bindings(*state->allocBindings(0));
|
||||||
auto v2 = findAlongAttrPath(*state, settings.thisSystem, bindings, *v).first;
|
auto v2 = findAlongAttrPath(*state, settings.thisSystem, bindings, *v).first;
|
||||||
|
|
||||||
return store->parseStorePath(state->forceString(*v2));
|
return store->parseStorePath(state->forceString(*v2, noPos, "while evaluating the path tho latest nix version"));
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
@ -51,3 +51,8 @@ exp_features=$(nix show-config | grep '^experimental-features' | cut -d '=' -f 2
|
||||||
[[ $prev != $exp_cores ]]
|
[[ $prev != $exp_cores ]]
|
||||||
[[ $exp_cores == "4242" ]]
|
[[ $exp_cores == "4242" ]]
|
||||||
[[ $exp_features == "flakes nix-command" ]]
|
[[ $exp_features == "flakes nix-command" ]]
|
||||||
|
|
||||||
|
# Test that it's possible to retrieve a single setting's value
|
||||||
|
val=$(nix show-config | grep '^warn-dirty' | cut -d '=' -f 2 | xargs)
|
||||||
|
val2=$(nix show-config warn-dirty)
|
||||||
|
[[ $val == $val2 ]]
|
||||||
|
|
|
@ -4,7 +4,7 @@ clearStore
|
||||||
clearProfiles
|
clearProfiles
|
||||||
|
|
||||||
checkRef() {
|
checkRef() {
|
||||||
nix-store -q --references $TEST_ROOT/result | grep -q "$1" || fail "missing reference $1"
|
nix-store -q --references $TEST_ROOT/result | grep -q "$1"'$' || fail "missing reference $1"
|
||||||
}
|
}
|
||||||
|
|
||||||
# Test the export of the runtime dependency graph.
|
# Test the export of the runtime dependency graph.
|
||||||
|
|
|
@ -1 +1 @@
|
||||||
true
|
[ true true true true true true ]
|
||||||
|
|
|
@ -18,7 +18,24 @@ let
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
legit-context = builtins.getContext "${path}${drv.outPath}${drv.foo.outPath}${drv.drvPath}";
|
combo-path = "${path}${drv.outPath}${drv.foo.outPath}${drv.drvPath}";
|
||||||
|
legit-context = builtins.getContext combo-path;
|
||||||
|
|
||||||
constructed-context = builtins.getContext (builtins.appendContext "" desired-context);
|
reconstructed-path = builtins.appendContext
|
||||||
in legit-context == constructed-context
|
(builtins.unsafeDiscardStringContext combo-path)
|
||||||
|
desired-context;
|
||||||
|
|
||||||
|
# Eta rule for strings with context.
|
||||||
|
etaRule = str:
|
||||||
|
str == builtins.appendContext
|
||||||
|
(builtins.unsafeDiscardStringContext str)
|
||||||
|
(builtins.getContext str);
|
||||||
|
|
||||||
|
in [
|
||||||
|
(legit-context == desired-context)
|
||||||
|
(reconstructed-path == combo-path)
|
||||||
|
(etaRule "foo")
|
||||||
|
(etaRule drv.drvPath)
|
||||||
|
(etaRule drv.foo.outPath)
|
||||||
|
(etaRule (builtins.unsafeDiscardOutputDependency drv.drvPath))
|
||||||
|
]
|
||||||
|
|
|
@ -1 +1 @@
|
||||||
{ bar = "regular"; foo = "directory"; }
|
{ bar = "regular"; foo = "directory"; ldir = "symlink"; linked = "symlink"; }
|
||||||
|
|
1
tests/lang/eval-okay-readFileType.exp
Normal file
1
tests/lang/eval-okay-readFileType.exp
Normal file
|
@ -0,0 +1 @@
|
||||||
|
{ bar = "regular"; foo = "directory"; ldir = "symlink"; linked = "symlink"; }
|
6
tests/lang/eval-okay-readFileType.nix
Normal file
6
tests/lang/eval-okay-readFileType.nix
Normal file
|
@ -0,0 +1,6 @@
|
||||||
|
{
|
||||||
|
bar = builtins.readFileType ./readDir/bar;
|
||||||
|
foo = builtins.readFileType ./readDir/foo;
|
||||||
|
linked = builtins.readFileType ./readDir/linked;
|
||||||
|
ldir = builtins.readFileType ./readDir/ldir;
|
||||||
|
}
|
1
tests/lang/readDir/ldir
Symbolic link
1
tests/lang/readDir/ldir
Symbolic link
|
@ -0,0 +1 @@
|
||||||
|
foo
|
1
tests/lang/readDir/linked
Symbolic link
1
tests/lang/readDir/linked
Symbolic link
|
@ -0,0 +1 @@
|
||||||
|
foo/git-hates-directories
|
Loading…
Reference in a new issue