mirror of
https://github.com/privatevoid-net/nix-super.git
synced 2024-11-25 15:26:17 +02:00
Merge remote-tracking branch 'origin/master' into handle-missing-gc-socket
This commit is contained in:
commit
302625e83b
68 changed files with 1721 additions and 556 deletions
14
.github/workflows/ci.yml
vendored
14
.github/workflows/ci.yml
vendored
|
@ -20,12 +20,12 @@ jobs:
|
|||
- uses: actions/checkout@v4
|
||||
with:
|
||||
fetch-depth: 0
|
||||
- uses: cachix/install-nix-action@v24
|
||||
- uses: cachix/install-nix-action@v25
|
||||
with:
|
||||
# The sandbox would otherwise be disabled by default on Darwin
|
||||
extra_nix_config: "sandbox = true"
|
||||
- run: echo CACHIX_NAME="$(echo $GITHUB_REPOSITORY-install-tests | tr "[A-Z]/" "[a-z]-")" >> $GITHUB_ENV
|
||||
- uses: cachix/cachix-action@v13
|
||||
- uses: cachix/cachix-action@v14
|
||||
if: needs.check_secrets.outputs.cachix == 'true'
|
||||
with:
|
||||
name: '${{ env.CACHIX_NAME }}'
|
||||
|
@ -62,10 +62,10 @@ jobs:
|
|||
with:
|
||||
fetch-depth: 0
|
||||
- run: echo CACHIX_NAME="$(echo $GITHUB_REPOSITORY-install-tests | tr "[A-Z]/" "[a-z]-")" >> $GITHUB_ENV
|
||||
- uses: cachix/install-nix-action@v24
|
||||
- uses: cachix/install-nix-action@v25
|
||||
with:
|
||||
install_url: https://releases.nixos.org/nix/nix-2.13.3/install
|
||||
- uses: cachix/cachix-action@v13
|
||||
- uses: cachix/cachix-action@v14
|
||||
with:
|
||||
name: '${{ env.CACHIX_NAME }}'
|
||||
signingKey: '${{ secrets.CACHIX_SIGNING_KEY }}'
|
||||
|
@ -84,7 +84,7 @@ jobs:
|
|||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- run: echo CACHIX_NAME="$(echo $GITHUB_REPOSITORY-install-tests | tr "[A-Z]/" "[a-z]-")" >> $GITHUB_ENV
|
||||
- uses: cachix/install-nix-action@v24
|
||||
- uses: cachix/install-nix-action@v25
|
||||
with:
|
||||
install_url: '${{needs.installer.outputs.installerURL}}'
|
||||
install_options: "--tarball-url-prefix https://${{ env.CACHIX_NAME }}.cachix.org/serve"
|
||||
|
@ -114,12 +114,12 @@ jobs:
|
|||
- uses: actions/checkout@v4
|
||||
with:
|
||||
fetch-depth: 0
|
||||
- uses: cachix/install-nix-action@v24
|
||||
- uses: cachix/install-nix-action@v25
|
||||
with:
|
||||
install_url: https://releases.nixos.org/nix/nix-2.13.3/install
|
||||
- run: echo CACHIX_NAME="$(echo $GITHUB_REPOSITORY-install-tests | tr "[A-Z]/" "[a-z]-")" >> $GITHUB_ENV
|
||||
- run: echo NIX_VERSION="$(nix --experimental-features 'nix-command flakes' eval .\#default.version | tr -d \")" >> $GITHUB_ENV
|
||||
- uses: cachix/cachix-action@v13
|
||||
- uses: cachix/cachix-action@v14
|
||||
if: needs.check_secrets.outputs.cachix == 'true'
|
||||
with:
|
||||
name: '${{ env.CACHIX_NAME }}'
|
||||
|
|
|
@ -29,7 +29,6 @@ LOWDOWN_LIBS = @LOWDOWN_LIBS@
|
|||
OPENSSL_LIBS = @OPENSSL_LIBS@
|
||||
PACKAGE_NAME = @PACKAGE_NAME@
|
||||
PACKAGE_VERSION = @PACKAGE_VERSION@
|
||||
RAPIDCHECK_HEADERS = @RAPIDCHECK_HEADERS@
|
||||
SHELL = @bash@
|
||||
SODIUM_LIBS = @SODIUM_LIBS@
|
||||
SQLITE3_LIBS = @SQLITE3_LIBS@
|
||||
|
|
21
configure.ac
21
configure.ac
|
@ -353,27 +353,8 @@ AS_IF([test "$ENABLE_UNIT_TESTS" == "yes"],[
|
|||
# Look for gtest.
|
||||
PKG_CHECK_MODULES([GTEST], [gtest_main])
|
||||
|
||||
|
||||
# Look for rapidcheck.
|
||||
AC_ARG_VAR([RAPIDCHECK_HEADERS], [include path of gtest headers shipped by RAPIDCHECK])
|
||||
# No pkg-config yet, https://github.com/emil-e/rapidcheck/issues/302
|
||||
AC_LANG_PUSH(C++)
|
||||
AC_SUBST(RAPIDCHECK_HEADERS)
|
||||
[CXXFLAGS="-I $RAPIDCHECK_HEADERS $CXXFLAGS"]
|
||||
[LIBS="-lrapidcheck -lgtest $LIBS"]
|
||||
AC_CHECK_HEADERS([rapidcheck/gtest.h], [], [], [#include <gtest/gtest.h>])
|
||||
dnl AC_CHECK_LIB doesn't work for C++ libs with mangled symbols
|
||||
AC_LINK_IFELSE([
|
||||
AC_LANG_PROGRAM([[
|
||||
#include <gtest/gtest.h>
|
||||
#include <rapidcheck/gtest.h>
|
||||
]], [[
|
||||
return RUN_ALL_TESTS();
|
||||
]])
|
||||
],
|
||||
[],
|
||||
[AC_MSG_ERROR([librapidcheck is not found.])])
|
||||
AC_LANG_POP(C++)
|
||||
PKG_CHECK_MODULES([RAPIDCHECK], [rapidcheck rapidcheck_gtest])
|
||||
|
||||
])
|
||||
|
||||
|
|
|
@ -81,7 +81,7 @@ EXPAND_ONLY_PREDEF = YES
|
|||
# RECURSIVE has no effect here.
|
||||
# This tag requires that the tag SEARCH_INCLUDES is set to YES.
|
||||
|
||||
INCLUDE_PATH = @RAPIDCHECK_HEADERS@
|
||||
INCLUDE_PATH =
|
||||
|
||||
# If the MACRO_EXPANSION and EXPAND_ONLY_PREDEF tags are set to YES then this
|
||||
# tag can be used to specify a list of macro names that should be expanded. The
|
||||
|
|
|
@ -3,4 +3,6 @@ synopsis: "`nix profile` now allows referring to elements by human-readable name
|
|||
prs: 8678
|
||||
---
|
||||
|
||||
[`nix profile`](@docroot@/command-ref/new-cli/nix3-profile.md) now uses names to refer to installed packages when running [`list`](@docroot@/command-ref/new-cli/nix3-profile-list.md), [`remove`](@docroot@/command-ref/new-cli/nix3-profile-remove.md) or [`upgrade`](@docroot@/command-ref/new-cli/nix3-profile-upgrade.md) as opposed to indices. Indices are deprecated and will be removed in a future version.
|
||||
[`nix profile`](@docroot@/command-ref/new-cli/nix3-profile.md) now uses names to refer to installed packages when running [`list`](@docroot@/command-ref/new-cli/nix3-profile-list.md), [`remove`](@docroot@/command-ref/new-cli/nix3-profile-remove.md) or [`upgrade`](@docroot@/command-ref/new-cli/nix3-profile-upgrade.md) as opposed to indices. Profile element names are generated when a package is installed and remain the same until the package is removed.
|
||||
|
||||
**Warning**: The `manifest.nix` file used to record the contents of profiles has changed. Nix will automatically upgrade profiles to the new version when you modify the profile. After that, the profile can no longer be used by older versions of Nix.
|
||||
|
|
|
@ -35,13 +35,50 @@ standard input.
|
|||
|
||||
- `--parse`\
|
||||
Just parse the input files, and print their abstract syntax trees on
|
||||
standard output in ATerm format.
|
||||
standard output as a Nix expression.
|
||||
|
||||
- `--eval`\
|
||||
Just parse and evaluate the input files, and print the resulting
|
||||
values on standard output. No instantiation of store derivations
|
||||
takes place.
|
||||
|
||||
> **Warning**
|
||||
>
|
||||
> This option produces ambiguous output which is not suitable for machine
|
||||
> consumption. For example, these two Nix expressions print the same result
|
||||
> despite having different types:
|
||||
>
|
||||
> ```console
|
||||
> $ nix-instantiate --eval --expr '{ a = {}; }'
|
||||
> { a = <CODE>; }
|
||||
> $ nix-instantiate --eval --expr '{ a = <CODE>; }'
|
||||
> { a = <CODE>; }
|
||||
> ```
|
||||
>
|
||||
> For human-readable output, `nix eval` (experimental) is more informative:
|
||||
>
|
||||
> ```console
|
||||
> $ nix-instantiate --eval --expr 'a: a'
|
||||
> <LAMBDA>
|
||||
> $ nix eval --expr 'a: a'
|
||||
> «lambda @ «string»:1:1»
|
||||
> ```
|
||||
>
|
||||
> For machine-readable output, the `--xml` option produces unambiguous
|
||||
> output:
|
||||
>
|
||||
> ```console
|
||||
> $ nix-instantiate --eval --xml --expr '{ foo = <CODE>; }'
|
||||
> <?xml version='1.0' encoding='utf-8'?>
|
||||
> <expr>
|
||||
> <attrs>
|
||||
> <attr column="3" line="1" name="foo">
|
||||
> <unevaluated />
|
||||
> </attr>
|
||||
> </attrs>
|
||||
> </expr>
|
||||
> ```
|
||||
|
||||
- `--find-file`\
|
||||
Look up the given files in Nix’s search path (as specified by the
|
||||
`NIX_PATH` environment variable). If found, print the corresponding
|
||||
|
@ -61,11 +98,11 @@ standard input.
|
|||
|
||||
- `--json`\
|
||||
When used with `--eval`, print the resulting value as an JSON
|
||||
representation of the abstract syntax tree rather than as an ATerm.
|
||||
representation of the abstract syntax tree rather than as a Nix expression.
|
||||
|
||||
- `--xml`\
|
||||
When used with `--eval`, print the resulting value as an XML
|
||||
representation of the abstract syntax tree rather than as an ATerm.
|
||||
representation of the abstract syntax tree rather than as a Nix expression.
|
||||
The schema is the same as that used by the [`toXML`
|
||||
built-in](../language/builtins.md).
|
||||
|
||||
|
@ -133,28 +170,29 @@ $ nix-instantiate --eval --xml --expr '1 + 2'
|
|||
The difference between non-strict and strict evaluation:
|
||||
|
||||
```console
|
||||
$ nix-instantiate --eval --xml --expr 'rec { x = "foo"; y = x; }'
|
||||
...
|
||||
<attr name="x">
|
||||
<string value="foo" />
|
||||
</attr>
|
||||
<attr name="y">
|
||||
<unevaluated />
|
||||
</attr>
|
||||
...
|
||||
$ nix-instantiate --eval --xml --expr '{ x = {}; }'
|
||||
<?xml version='1.0' encoding='utf-8'?>
|
||||
<expr>
|
||||
<attrs>
|
||||
<attr column="3" line="1" name="x">
|
||||
<unevaluated />
|
||||
</attr>
|
||||
</attrs>
|
||||
</expr>
|
||||
```
|
||||
|
||||
Note that `y` is left unevaluated (the XML representation doesn’t
|
||||
attempt to show non-normal forms).
|
||||
|
||||
```console
|
||||
$ nix-instantiate --eval --xml --strict --expr 'rec { x = "foo"; y = x; }'
|
||||
...
|
||||
<attr name="x">
|
||||
<string value="foo" />
|
||||
</attr>
|
||||
<attr name="y">
|
||||
<string value="foo" />
|
||||
</attr>
|
||||
...
|
||||
$ nix-instantiate --eval --xml --strict --expr '{ x = {}; }'
|
||||
<?xml version='1.0' encoding='utf-8'?>
|
||||
<expr>
|
||||
<attrs>
|
||||
<attr column="3" line="1" name="x">
|
||||
<attrs>
|
||||
</attrs>
|
||||
</attr>
|
||||
</attrs>
|
||||
</expr>
|
||||
```
|
||||
|
|
|
@ -51,11 +51,14 @@ To install it in `$(pwd)/outputs` and test it:
|
|||
|
||||
```console
|
||||
[nix-shell]$ make install
|
||||
[nix-shell]$ make installcheck -j $NIX_BUILD_CORES
|
||||
[nix-shell]$ make installcheck check -j $NIX_BUILD_CORES
|
||||
[nix-shell]$ nix --version
|
||||
nix (Nix) 2.12
|
||||
```
|
||||
|
||||
For more information on running and filtering tests, see
|
||||
[`testing.md`](./testing.md).
|
||||
|
||||
To build a release version of Nix for the current operating system and CPU architecture:
|
||||
|
||||
```console
|
||||
|
|
|
@ -77,7 +77,7 @@ there is no risk of any build-system wildcards for the library accidentally pick
|
|||
### Running tests
|
||||
|
||||
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, or the `GTEST_FILTER` environment variable.
|
||||
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, or the `GTEST_FILTER` environment variable, e.g. `GTEST_FILTER='ErrorTraceTest.*' make check`.
|
||||
|
||||
### Characterisation testing { #characaterisation-testing-unit }
|
||||
|
||||
|
|
|
@ -3,10 +3,10 @@
|
|||
- [derivation]{#gloss-derivation}
|
||||
|
||||
A description of a build task. The result of a derivation is a
|
||||
store object. Derivations are typically specified in Nix expressions
|
||||
store object. Derivations declared in Nix expressions are specified
|
||||
using the [`derivation` primitive](./language/derivations.md). These are
|
||||
translated into low-level *store derivations* (implicitly by
|
||||
`nix-env` and `nix-build`, or explicitly by `nix-instantiate`).
|
||||
`nix-build`, or explicitly by `nix-instantiate`).
|
||||
|
||||
[derivation]: #gloss-derivation
|
||||
|
||||
|
@ -14,6 +14,7 @@
|
|||
|
||||
A [derivation] represented as a `.drv` file in the [store].
|
||||
It has a [store path], like any [store object].
|
||||
It is the [instantiated][instantiate] form of a derivation.
|
||||
|
||||
Example: `/nix/store/g946hcz4c8mdvq2g8vxx42z51qb71rvp-git-2.38.1.drv`
|
||||
|
||||
|
@ -23,9 +24,9 @@
|
|||
|
||||
- [instantiate]{#gloss-instantiate}, instantiation
|
||||
|
||||
Translate a [derivation] into a [store derivation].
|
||||
Save an evaluated [derivation] as a [store derivation] in the Nix [store].
|
||||
|
||||
See [`nix-instantiate`](./command-ref/nix-instantiate.md).
|
||||
See [`nix-instantiate`](./command-ref/nix-instantiate.md), which produces a store derivation from a Nix expression that evaluates to a derivation.
|
||||
|
||||
[instantiate]: #gloss-instantiate
|
||||
|
||||
|
@ -66,7 +67,7 @@
|
|||
|
||||
From the perspective of the location where Nix is invoked, the Nix store can be referred to _local_ or _remote_.
|
||||
Only a [local store]{#gloss-local-store} exposes a location in the file system of the machine where Nix is invoked that allows access to store objects, typically `/nix/store`.
|
||||
Local stores can be used for building [derivations](#derivation).
|
||||
Local stores can be used for building [derivations](#gloss-derivation).
|
||||
See [Local Store](@docroot@/command-ref/new-cli/nix3-help-stores.md#local-store) for details.
|
||||
|
||||
[store]: #gloss-store
|
||||
|
@ -168,9 +169,10 @@
|
|||
|
||||
A high-level description of software packages and compositions
|
||||
thereof. Deploying software using Nix entails writing Nix
|
||||
expressions for your packages. Nix expressions are translated to
|
||||
derivations that are stored in the Nix store. These derivations can
|
||||
then be built.
|
||||
expressions for your packages. Nix expressions specify [derivations][derivation],
|
||||
which are [instantiated][instantiate] into the Nix store as [store derivations][store derivation].
|
||||
These derivations can then be [realised][realise] to produce
|
||||
[outputs][output].
|
||||
|
||||
- [reference]{#gloss-reference}
|
||||
|
||||
|
@ -222,6 +224,9 @@
|
|||
|
||||
The [store derivation] that produced an [output path].
|
||||
|
||||
The deriver for an output path can be queried with the `--deriver` option to
|
||||
[`nix-store --query`](@docroot@/command-ref/nix-store/query.md).
|
||||
|
||||
- [validity]{#gloss-validity}
|
||||
|
||||
A store path is valid if all [store object]s in its [closure] can be read from the [store].
|
||||
|
|
|
@ -34,11 +34,11 @@
|
|||
},
|
||||
"nixpkgs": {
|
||||
"locked": {
|
||||
"lastModified": 1704018918,
|
||||
"narHash": "sha256-erjg/HrpC9liEfm7oLqb8GXCqsxaFwIIPqCsknW5aFY=",
|
||||
"lastModified": 1705033721,
|
||||
"narHash": "sha256-K5eJHmL1/kev6WuqyqqbS1cdNnSidIZ3jeqJ7GbrYnQ=",
|
||||
"owner": "NixOS",
|
||||
"repo": "nixpkgs",
|
||||
"rev": "2c9c58e98243930f8cb70387934daa4bc8b00373",
|
||||
"rev": "a1982c92d8980a0114372973cbdfe0a307f1bdea",
|
||||
"type": "github"
|
||||
},
|
||||
"original": {
|
||||
|
|
|
@ -236,7 +236,6 @@ in {
|
|||
openssl
|
||||
sqlite
|
||||
xz
|
||||
] ++ lib.optionals (!stdenv.hostPlatform.isWindows) [
|
||||
({ inherit readline editline; }.${readlineFlavor})
|
||||
] ++ lib.optionals enableMarkdown [
|
||||
lowdown
|
||||
|
@ -310,7 +309,7 @@ in {
|
|||
] ++ lib.optional (doBuild && stdenv.isLinux && !(stdenv.hostPlatform.isStatic && stdenv.system == "aarch64-linux"))
|
||||
"LDFLAGS=-fuse-ld=gold"
|
||||
++ lib.optional (doBuild && stdenv.hostPlatform.isStatic) "--enable-embedded-sandbox-shell"
|
||||
++ lib.optional buildUnitTests "RAPIDCHECK_HEADERS=${lib.getDev rapidcheck}/extras/gtest/include";
|
||||
;
|
||||
|
||||
enableParallelBuilding = true;
|
||||
|
||||
|
|
|
@ -93,9 +93,17 @@ struct NixRepl
|
|||
void evalString(std::string s, Value & v);
|
||||
void loadDebugTraceEnv(DebugTrace & dt);
|
||||
|
||||
typedef std::set<Value *> ValuesSeen;
|
||||
std::ostream & printValue(std::ostream & str, Value & v, unsigned int maxDepth);
|
||||
std::ostream & printValue(std::ostream & str, Value & v, unsigned int maxDepth, ValuesSeen & seen);
|
||||
void printValue(std::ostream & str,
|
||||
Value & v,
|
||||
unsigned int maxDepth = std::numeric_limits<unsigned int>::max())
|
||||
{
|
||||
::nix::printValue(*state, str, v, PrintOptions {
|
||||
.ansiColors = true,
|
||||
.force = true,
|
||||
.derivationPaths = true,
|
||||
.maxDepth = maxDepth
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
std::string removeWhitespace(std::string s)
|
||||
|
@ -246,7 +254,7 @@ void NixRepl::mainLoop()
|
|||
rl_readline_name = "nix-repl";
|
||||
try {
|
||||
createDirs(dirOf(historyFile));
|
||||
} catch (SysError & e) {
|
||||
} catch (SystemError & e) {
|
||||
logWarning(e.info());
|
||||
}
|
||||
#ifndef USE_READLINE
|
||||
|
@ -708,7 +716,8 @@ bool NixRepl::processLine(std::string line)
|
|||
else if (command == ":p" || command == ":print") {
|
||||
Value v;
|
||||
evalString(arg, v);
|
||||
printValue(std::cout, v, 1000000000) << std::endl;
|
||||
printValue(std::cout, v);
|
||||
std::cout << std::endl;
|
||||
}
|
||||
|
||||
else if (command == ":q" || command == ":quit") {
|
||||
|
@ -770,7 +779,8 @@ bool NixRepl::processLine(std::string line)
|
|||
} else {
|
||||
Value v;
|
||||
evalString(line, v);
|
||||
printValue(std::cout, v, 1) << std::endl;
|
||||
printValue(std::cout, v, 1);
|
||||
std::cout << std::endl;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -892,144 +902,6 @@ void NixRepl::evalString(std::string s, Value & v)
|
|||
}
|
||||
|
||||
|
||||
std::ostream & NixRepl::printValue(std::ostream & str, Value & v, unsigned int maxDepth)
|
||||
{
|
||||
ValuesSeen seen;
|
||||
return printValue(str, v, maxDepth, seen);
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
// FIXME: lot of cut&paste from Nix's eval.cc.
|
||||
std::ostream & NixRepl::printValue(std::ostream & str, Value & v, unsigned int maxDepth, ValuesSeen & seen)
|
||||
{
|
||||
str.flush();
|
||||
checkInterrupt();
|
||||
|
||||
state->forceValue(v, v.determinePos(noPos));
|
||||
|
||||
switch (v.type()) {
|
||||
|
||||
case nInt:
|
||||
str << ANSI_CYAN << v.integer << ANSI_NORMAL;
|
||||
break;
|
||||
|
||||
case nBool:
|
||||
str << ANSI_CYAN;
|
||||
printLiteralBool(str, v.boolean);
|
||||
str << ANSI_NORMAL;
|
||||
break;
|
||||
|
||||
case nString:
|
||||
str << ANSI_WARNING;
|
||||
printLiteralString(str, v.string_view());
|
||||
str << ANSI_NORMAL;
|
||||
break;
|
||||
|
||||
case nPath:
|
||||
str << ANSI_GREEN << v.path().to_string() << ANSI_NORMAL; // !!! escaping?
|
||||
break;
|
||||
|
||||
case nNull:
|
||||
str << ANSI_CYAN "null" ANSI_NORMAL;
|
||||
break;
|
||||
|
||||
case nAttrs: {
|
||||
seen.insert(&v);
|
||||
|
||||
bool isDrv = state->isDerivation(v);
|
||||
|
||||
if (isDrv) {
|
||||
str << "«derivation ";
|
||||
Bindings::iterator i = v.attrs->find(state->sDrvPath);
|
||||
NixStringContext context;
|
||||
if (i != v.attrs->end())
|
||||
str << state->store->printStorePath(state->coerceToStorePath(i->pos, *i->value, context, "while evaluating the drvPath of a derivation"));
|
||||
else
|
||||
str << "???";
|
||||
str << "»";
|
||||
}
|
||||
|
||||
else if (maxDepth > 0) {
|
||||
str << "{ ";
|
||||
|
||||
typedef std::map<std::string, Value *> Sorted;
|
||||
Sorted sorted;
|
||||
for (auto & i : *v.attrs)
|
||||
sorted.emplace(state->symbols[i.name], i.value);
|
||||
|
||||
for (auto & i : sorted) {
|
||||
printAttributeName(str, i.first);
|
||||
str << " = ";
|
||||
if (seen.count(i.second))
|
||||
str << "«repeated»";
|
||||
else
|
||||
try {
|
||||
printValue(str, *i.second, maxDepth - 1, seen);
|
||||
} catch (AssertionError & e) {
|
||||
str << ANSI_RED "«error: " << e.msg() << "»" ANSI_NORMAL;
|
||||
}
|
||||
str << "; ";
|
||||
}
|
||||
|
||||
str << "}";
|
||||
} else
|
||||
str << "{ ... }";
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
case nList:
|
||||
seen.insert(&v);
|
||||
|
||||
str << "[ ";
|
||||
if (maxDepth > 0)
|
||||
for (auto elem : v.listItems()) {
|
||||
if (seen.count(elem))
|
||||
str << "«repeated»";
|
||||
else
|
||||
try {
|
||||
printValue(str, *elem, maxDepth - 1, seen);
|
||||
} catch (AssertionError & e) {
|
||||
str << ANSI_RED "«error: " << e.msg() << "»" ANSI_NORMAL;
|
||||
}
|
||||
str << " ";
|
||||
}
|
||||
else
|
||||
str << "... ";
|
||||
str << "]";
|
||||
break;
|
||||
|
||||
case nFunction:
|
||||
if (v.isLambda()) {
|
||||
std::ostringstream s;
|
||||
s << state->positions[v.lambda.fun->pos];
|
||||
str << ANSI_BLUE "«lambda @ " << filterANSIEscapes(s.str()) << "»" ANSI_NORMAL;
|
||||
} else if (v.isPrimOp()) {
|
||||
str << ANSI_MAGENTA "«primop»" ANSI_NORMAL;
|
||||
} else if (v.isPrimOpApp()) {
|
||||
str << ANSI_BLUE "«primop-app»" ANSI_NORMAL;
|
||||
} else {
|
||||
abort();
|
||||
}
|
||||
break;
|
||||
|
||||
case nFloat:
|
||||
str << v.fpoint;
|
||||
break;
|
||||
|
||||
case nThunk:
|
||||
case nExternal:
|
||||
default:
|
||||
str << ANSI_RED "«unknown»" ANSI_NORMAL;
|
||||
break;
|
||||
}
|
||||
|
||||
return str;
|
||||
}
|
||||
|
||||
|
||||
std::unique_ptr<AbstractNixRepl> AbstractNixRepl::create(
|
||||
const SearchPath & searchPath, nix::ref<Store> store, ref<EvalState> state,
|
||||
std::function<AnnotatedValues()> getValues)
|
||||
|
|
|
@ -105,117 +105,23 @@ RootValue allocRootValue(Value * v)
|
|||
#endif
|
||||
}
|
||||
|
||||
void Value::print(const SymbolTable &symbols, std::ostream &str,
|
||||
std::set<const void *> *seen, int depth) const
|
||||
|
||||
{
|
||||
checkInterrupt();
|
||||
|
||||
if (depth <= 0) {
|
||||
str << "«too deep»";
|
||||
return;
|
||||
}
|
||||
switch (internalType) {
|
||||
case tInt:
|
||||
str << integer;
|
||||
break;
|
||||
case tBool:
|
||||
printLiteralBool(str, boolean);
|
||||
break;
|
||||
case tString:
|
||||
printLiteralString(str, string_view());
|
||||
break;
|
||||
case tPath:
|
||||
str << path().to_string(); // !!! escaping?
|
||||
break;
|
||||
case tNull:
|
||||
str << "null";
|
||||
break;
|
||||
case tAttrs: {
|
||||
if (seen && !attrs->empty() && !seen->insert(attrs).second)
|
||||
str << "«repeated»";
|
||||
else {
|
||||
str << "{ ";
|
||||
for (auto & i : attrs->lexicographicOrder(symbols)) {
|
||||
str << symbols[i->name] << " = ";
|
||||
i->value->print(symbols, str, seen, depth - 1);
|
||||
str << "; ";
|
||||
}
|
||||
str << "}";
|
||||
}
|
||||
break;
|
||||
}
|
||||
case tList1:
|
||||
case tList2:
|
||||
case tListN:
|
||||
if (seen && listSize() && !seen->insert(listElems()).second)
|
||||
str << "«repeated»";
|
||||
else {
|
||||
str << "[ ";
|
||||
for (auto v2 : listItems()) {
|
||||
if (v2)
|
||||
v2->print(symbols, str, seen, depth - 1);
|
||||
else
|
||||
str << "(nullptr)";
|
||||
str << " ";
|
||||
}
|
||||
str << "]";
|
||||
}
|
||||
break;
|
||||
case tThunk:
|
||||
case tApp:
|
||||
if (!isBlackhole()) {
|
||||
str << "<CODE>";
|
||||
} else {
|
||||
// Although we know for sure that it's going to be an infinite recursion
|
||||
// when this value is accessed _in the current context_, it's likely
|
||||
// that the user will misinterpret a simpler «infinite recursion» output
|
||||
// as a definitive statement about the value, while in fact it may be
|
||||
// a valid value after `builtins.trace` and perhaps some other steps
|
||||
// have completed.
|
||||
str << "«potential infinite recursion»";
|
||||
}
|
||||
break;
|
||||
case tLambda:
|
||||
str << "<LAMBDA>";
|
||||
break;
|
||||
case tPrimOp:
|
||||
str << "<PRIMOP>";
|
||||
break;
|
||||
case tPrimOpApp:
|
||||
str << "<PRIMOP-APP>";
|
||||
break;
|
||||
case tExternal:
|
||||
str << *external;
|
||||
break;
|
||||
case tFloat:
|
||||
str << fpoint;
|
||||
break;
|
||||
default:
|
||||
printError("Nix evaluator internal error: Value::print(): invalid value type %1%", internalType);
|
||||
abort();
|
||||
}
|
||||
}
|
||||
|
||||
void Value::print(const SymbolTable &symbols, std::ostream &str,
|
||||
bool showRepeated, int depth) const {
|
||||
std::set<const void *> seen;
|
||||
print(symbols, str, showRepeated ? nullptr : &seen, depth);
|
||||
}
|
||||
|
||||
// Pretty print types for assertion errors
|
||||
std::ostream & operator << (std::ostream & os, const ValueType t) {
|
||||
os << showType(t);
|
||||
return os;
|
||||
}
|
||||
|
||||
std::string printValue(const EvalState & state, const Value & v)
|
||||
std::string printValue(EvalState & state, Value & v)
|
||||
{
|
||||
std::ostringstream out;
|
||||
v.print(state.symbols, out);
|
||||
v.print(state, out);
|
||||
return out.str();
|
||||
}
|
||||
|
||||
void Value::print(EvalState & state, std::ostream & str, PrintOptions options)
|
||||
{
|
||||
printValue(state, str, *this, options);
|
||||
}
|
||||
|
||||
const Value * getPrimOp(const Value &v) {
|
||||
const Value * primOp = &v;
|
||||
|
@ -710,6 +616,26 @@ void PrimOp::check()
|
|||
}
|
||||
|
||||
|
||||
std::ostream & operator<<(std::ostream & output, PrimOp & primOp)
|
||||
{
|
||||
output << "primop " << primOp.name;
|
||||
return output;
|
||||
}
|
||||
|
||||
|
||||
PrimOp * Value::primOpAppPrimOp() const
|
||||
{
|
||||
Value * left = primOpApp.left;
|
||||
while (left && !left->isPrimOp()) {
|
||||
left = left->primOpApp.left;
|
||||
}
|
||||
|
||||
if (!left)
|
||||
return nullptr;
|
||||
return left->primOp;
|
||||
}
|
||||
|
||||
|
||||
void Value::mkPrimOp(PrimOp * p)
|
||||
{
|
||||
p->check();
|
||||
|
|
|
@ -84,6 +84,8 @@ struct PrimOp
|
|||
void check();
|
||||
};
|
||||
|
||||
std::ostream & operator<<(std::ostream & output, PrimOp & primOp);
|
||||
|
||||
/**
|
||||
* Info about a constant
|
||||
*/
|
||||
|
@ -127,7 +129,7 @@ std::unique_ptr<ValMap> mapStaticEnvBindings(const SymbolTable & st, const Stati
|
|||
void copyContext(const Value & v, NixStringContext & context);
|
||||
|
||||
|
||||
std::string printValue(const EvalState & state, const Value & v);
|
||||
std::string printValue(EvalState & state, Value & v);
|
||||
std::ostream & operator << (std::ostream & os, const ValueType t);
|
||||
|
||||
|
||||
|
|
|
@ -130,7 +130,7 @@ else { return ELSE; }
|
|||
assert { return ASSERT; }
|
||||
with { return WITH; }
|
||||
let { return LET; }
|
||||
in { return IN; }
|
||||
in { return IN_KW; }
|
||||
rec { return REC; }
|
||||
inherit { return INHERIT; }
|
||||
or { return OR_KW; }
|
||||
|
@ -156,7 +156,7 @@ or { return OR_KW; }
|
|||
.errPos = data->state.positions[CUR_POS],
|
||||
});
|
||||
}
|
||||
return INT;
|
||||
return INT_LIT;
|
||||
}
|
||||
{FLOAT} { errno = 0;
|
||||
yylval->nf = strtod(yytext, 0);
|
||||
|
@ -165,7 +165,7 @@ or { return OR_KW; }
|
|||
.msg = hintfmt("invalid float '%1%'", yytext),
|
||||
.errPos = data->state.positions[CUR_POS],
|
||||
});
|
||||
return FLOAT;
|
||||
return FLOAT_LIT;
|
||||
}
|
||||
|
||||
\$\{ { PUSH_STATE(DEFAULT); return DOLLAR_CURLY; }
|
||||
|
|
|
@ -365,11 +365,11 @@ void yyerror(YYLTYPE * loc, yyscan_t scanner, ParseData * data, const char * err
|
|||
%type <id> attr
|
||||
%token <id> ID
|
||||
%token <str> STR IND_STR
|
||||
%token <n> INT
|
||||
%token <nf> FLOAT
|
||||
%token <n> INT_LIT
|
||||
%token <nf> FLOAT_LIT
|
||||
%token <path> PATH HPATH SPATH PATH_END
|
||||
%token <uri> URI
|
||||
%token IF THEN ELSE ASSERT WITH LET IN REC INHERIT EQ NEQ AND OR IMPL OR_KW
|
||||
%token IF THEN ELSE ASSERT WITH LET IN_KW REC INHERIT EQ NEQ AND OR IMPL OR_KW
|
||||
%token DOLLAR_CURLY /* == ${ */
|
||||
%token IND_STRING_OPEN IND_STRING_CLOSE
|
||||
%token ELLIPSIS
|
||||
|
@ -412,7 +412,7 @@ expr_function
|
|||
{ $$ = new ExprAssert(CUR_POS, $2, $4); }
|
||||
| WITH expr ';' expr_function
|
||||
{ $$ = new ExprWith(CUR_POS, $2, $4); }
|
||||
| LET binds IN expr_function
|
||||
| LET binds IN_KW expr_function
|
||||
{ if (!$2->dynamicAttrs.empty())
|
||||
throw ParseError({
|
||||
.msg = hintfmt("dynamic attributes not allowed in let"),
|
||||
|
@ -482,8 +482,8 @@ expr_simple
|
|||
else
|
||||
$$ = new ExprVar(CUR_POS, data->symbols.create($1));
|
||||
}
|
||||
| INT { $$ = new ExprInt($1); }
|
||||
| FLOAT { $$ = new ExprFloat($1); }
|
||||
| INT_LIT { $$ = new ExprInt($1); }
|
||||
| FLOAT_LIT { $$ = new ExprFloat($1); }
|
||||
| '"' string_parts '"' { $$ = $2; }
|
||||
| IND_STRING_OPEN ind_string_parts IND_STRING_CLOSE {
|
||||
$$ = stripIndentation(CUR_POS, data->symbols, std::move(*$2));
|
||||
|
|
|
@ -3712,9 +3712,6 @@ static RegisterPrimOp primop_toString({
|
|||
static void prim_substring(EvalState & state, const PosIdx pos, Value * * args, Value & v)
|
||||
{
|
||||
int start = state.forceInt(*args[0], pos, "while evaluating the first argument (the start offset) passed to builtins.substring");
|
||||
int len = state.forceInt(*args[1], pos, "while evaluating the second argument (the substring length) passed to builtins.substring");
|
||||
NixStringContext context;
|
||||
auto s = state.coerceToString(pos, *args[2], context, "while evaluating the third argument (the string) passed to builtins.substring");
|
||||
|
||||
if (start < 0)
|
||||
state.debugThrowLastTrace(EvalError({
|
||||
|
@ -3722,6 +3719,22 @@ static void prim_substring(EvalState & state, const PosIdx pos, Value * * args,
|
|||
.errPos = state.positions[pos]
|
||||
}));
|
||||
|
||||
|
||||
int len = state.forceInt(*args[1], pos, "while evaluating the second argument (the substring length) passed to builtins.substring");
|
||||
|
||||
// Special-case on empty substring to avoid O(n) strlen
|
||||
// This allows for the use of empty substrings to efficently capture string context
|
||||
if (len == 0) {
|
||||
state.forceValue(*args[2], pos);
|
||||
if (args[2]->type() == nString) {
|
||||
v.mkString("", args[2]->context());
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
NixStringContext context;
|
||||
auto s = state.coerceToString(pos, *args[2], context, "while evaluating the third argument (the string) passed to builtins.substring");
|
||||
|
||||
v.mkString((unsigned int) start >= s->size() ? "" : s->substr(start, len), context);
|
||||
}
|
||||
|
||||
|
|
100
src/libexpr/print-ambiguous.cc
Normal file
100
src/libexpr/print-ambiguous.cc
Normal file
|
@ -0,0 +1,100 @@
|
|||
#include "print-ambiguous.hh"
|
||||
#include "print.hh"
|
||||
#include "signals.hh"
|
||||
|
||||
namespace nix {
|
||||
|
||||
// See: https://github.com/NixOS/nix/issues/9730
|
||||
void printAmbiguous(
|
||||
Value &v,
|
||||
const SymbolTable &symbols,
|
||||
std::ostream &str,
|
||||
std::set<const void *> *seen,
|
||||
int depth)
|
||||
{
|
||||
checkInterrupt();
|
||||
|
||||
if (depth <= 0) {
|
||||
str << "«too deep»";
|
||||
return;
|
||||
}
|
||||
switch (v.type()) {
|
||||
case nInt:
|
||||
str << v.integer;
|
||||
break;
|
||||
case nBool:
|
||||
printLiteralBool(str, v.boolean);
|
||||
break;
|
||||
case nString:
|
||||
printLiteralString(str, v.string_view());
|
||||
break;
|
||||
case nPath:
|
||||
str << v.path().to_string(); // !!! escaping?
|
||||
break;
|
||||
case nNull:
|
||||
str << "null";
|
||||
break;
|
||||
case nAttrs: {
|
||||
if (seen && !v.attrs->empty() && !seen->insert(v.attrs).second)
|
||||
str << "«repeated»";
|
||||
else {
|
||||
str << "{ ";
|
||||
for (auto & i : v.attrs->lexicographicOrder(symbols)) {
|
||||
str << symbols[i->name] << " = ";
|
||||
printAmbiguous(*i->value, symbols, str, seen, depth - 1);
|
||||
str << "; ";
|
||||
}
|
||||
str << "}";
|
||||
}
|
||||
break;
|
||||
}
|
||||
case nList:
|
||||
if (seen && v.listSize() && !seen->insert(v.listElems()).second)
|
||||
str << "«repeated»";
|
||||
else {
|
||||
str << "[ ";
|
||||
for (auto v2 : v.listItems()) {
|
||||
if (v2)
|
||||
printAmbiguous(*v2, symbols, str, seen, depth - 1);
|
||||
else
|
||||
str << "(nullptr)";
|
||||
str << " ";
|
||||
}
|
||||
str << "]";
|
||||
}
|
||||
break;
|
||||
case nThunk:
|
||||
if (!v.isBlackhole()) {
|
||||
str << "<CODE>";
|
||||
} else {
|
||||
// Although we know for sure that it's going to be an infinite recursion
|
||||
// when this value is accessed _in the current context_, it's likely
|
||||
// that the user will misinterpret a simpler «infinite recursion» output
|
||||
// as a definitive statement about the value, while in fact it may be
|
||||
// a valid value after `builtins.trace` and perhaps some other steps
|
||||
// have completed.
|
||||
str << "«potential infinite recursion»";
|
||||
}
|
||||
break;
|
||||
case nFunction:
|
||||
if (v.isLambda()) {
|
||||
str << "<LAMBDA>";
|
||||
} else if (v.isPrimOp()) {
|
||||
str << "<PRIMOP>";
|
||||
} else if (v.isPrimOpApp()) {
|
||||
str << "<PRIMOP-APP>";
|
||||
}
|
||||
break;
|
||||
case nExternal:
|
||||
str << *v.external;
|
||||
break;
|
||||
case nFloat:
|
||||
str << v.fpoint;
|
||||
break;
|
||||
default:
|
||||
printError("Nix evaluator internal error: printAmbiguous: invalid value type");
|
||||
abort();
|
||||
}
|
||||
}
|
||||
|
||||
}
|
24
src/libexpr/print-ambiguous.hh
Normal file
24
src/libexpr/print-ambiguous.hh
Normal file
|
@ -0,0 +1,24 @@
|
|||
#pragma once
|
||||
|
||||
#include "value.hh"
|
||||
|
||||
namespace nix {
|
||||
|
||||
/**
|
||||
* Print a value in the deprecated format used by `nix-instantiate --eval` and
|
||||
* `nix-env` (for manifests).
|
||||
*
|
||||
* This output can't be changed because it's part of the `nix-instantiate` API,
|
||||
* but it produces ambiguous output; unevaluated thunks and lambdas (and a few
|
||||
* other types) are printed as Nix path syntax like `<CODE>`.
|
||||
*
|
||||
* See: https://github.com/NixOS/nix/issues/9730
|
||||
*/
|
||||
void printAmbiguous(
|
||||
Value &v,
|
||||
const SymbolTable &symbols,
|
||||
std::ostream &str,
|
||||
std::set<const void *> *seen,
|
||||
int depth);
|
||||
|
||||
}
|
52
src/libexpr/print-options.hh
Normal file
52
src/libexpr/print-options.hh
Normal file
|
@ -0,0 +1,52 @@
|
|||
#pragma once
|
||||
/**
|
||||
* @file
|
||||
* @brief Options for printing Nix values.
|
||||
*/
|
||||
|
||||
#include <limits>
|
||||
|
||||
namespace nix {
|
||||
|
||||
/**
|
||||
* Options for printing Nix values.
|
||||
*/
|
||||
struct PrintOptions
|
||||
{
|
||||
/**
|
||||
* If true, output ANSI color sequences.
|
||||
*/
|
||||
bool ansiColors = false;
|
||||
/**
|
||||
* If true, force values.
|
||||
*/
|
||||
bool force = false;
|
||||
/**
|
||||
* If true and `force` is set, print derivations as
|
||||
* `«derivation /nix/store/...»` instead of as attribute sets.
|
||||
*/
|
||||
bool derivationPaths = false;
|
||||
/**
|
||||
* If true, track which values have been printed and skip them on
|
||||
* subsequent encounters. Useful for self-referential values.
|
||||
*/
|
||||
bool trackRepeated = true;
|
||||
/**
|
||||
* Maximum depth to evaluate to.
|
||||
*/
|
||||
size_t maxDepth = std::numeric_limits<size_t>::max();
|
||||
/**
|
||||
* Maximum number of attributes in an attribute set to print.
|
||||
*/
|
||||
size_t maxAttrs = std::numeric_limits<size_t>::max();
|
||||
/**
|
||||
* Maximum number of list items to print.
|
||||
*/
|
||||
size_t maxListItems = std::numeric_limits<size_t>::max();
|
||||
/**
|
||||
* Maximum string length to print.
|
||||
*/
|
||||
size_t maxStringLength = std::numeric_limits<size_t>::max();
|
||||
};
|
||||
|
||||
}
|
|
@ -1,24 +1,66 @@
|
|||
#include "print.hh"
|
||||
#include <limits>
|
||||
#include <unordered_set>
|
||||
|
||||
#include "print.hh"
|
||||
#include "ansicolor.hh"
|
||||
#include "signals.hh"
|
||||
#include "store-api.hh"
|
||||
#include "terminal.hh"
|
||||
#include "english.hh"
|
||||
|
||||
namespace nix {
|
||||
|
||||
std::ostream &
|
||||
printLiteralString(std::ostream & str, const std::string_view string)
|
||||
void printElided(
|
||||
std::ostream & output,
|
||||
unsigned int value,
|
||||
const std::string_view single,
|
||||
const std::string_view plural,
|
||||
bool ansiColors)
|
||||
{
|
||||
if (ansiColors)
|
||||
output << ANSI_FAINT;
|
||||
output << " «";
|
||||
pluralize(output, value, single, plural);
|
||||
output << " elided»";
|
||||
if (ansiColors)
|
||||
output << ANSI_NORMAL;
|
||||
}
|
||||
|
||||
|
||||
std::ostream &
|
||||
printLiteralString(std::ostream & str, const std::string_view string, size_t maxLength, bool ansiColors)
|
||||
{
|
||||
size_t charsPrinted = 0;
|
||||
if (ansiColors)
|
||||
str << ANSI_MAGENTA;
|
||||
str << "\"";
|
||||
for (auto i = string.begin(); i != string.end(); ++i) {
|
||||
if (charsPrinted >= maxLength) {
|
||||
str << "\"";
|
||||
printElided(str, string.length() - charsPrinted, "byte", "bytes", ansiColors);
|
||||
return str;
|
||||
}
|
||||
|
||||
if (*i == '\"' || *i == '\\') str << "\\" << *i;
|
||||
else if (*i == '\n') str << "\\n";
|
||||
else if (*i == '\r') str << "\\r";
|
||||
else if (*i == '\t') str << "\\t";
|
||||
else if (*i == '$' && *(i+1) == '{') str << "\\" << *i;
|
||||
else str << *i;
|
||||
charsPrinted++;
|
||||
}
|
||||
str << "\"";
|
||||
if (ansiColors)
|
||||
str << ANSI_NORMAL;
|
||||
return str;
|
||||
}
|
||||
|
||||
std::ostream &
|
||||
printLiteralString(std::ostream & str, const std::string_view string)
|
||||
{
|
||||
return printLiteralString(str, string, std::numeric_limits<size_t>::max(), false);
|
||||
}
|
||||
|
||||
std::ostream &
|
||||
printLiteralBool(std::ostream & str, bool boolean)
|
||||
{
|
||||
|
@ -90,5 +132,373 @@ printAttributeName(std::ostream & str, std::string_view name) {
|
|||
return str;
|
||||
}
|
||||
|
||||
bool isImportantAttrName(const std::string& attrName)
|
||||
{
|
||||
return attrName == "type" || attrName == "_type";
|
||||
}
|
||||
|
||||
typedef std::pair<std::string, Value *> AttrPair;
|
||||
|
||||
struct ImportantFirstAttrNameCmp
|
||||
{
|
||||
|
||||
bool operator()(const AttrPair& lhs, const AttrPair& rhs) const
|
||||
{
|
||||
auto lhsIsImportant = isImportantAttrName(lhs.first);
|
||||
auto rhsIsImportant = isImportantAttrName(rhs.first);
|
||||
return std::forward_as_tuple(!lhsIsImportant, lhs.first)
|
||||
< std::forward_as_tuple(!rhsIsImportant, rhs.first);
|
||||
}
|
||||
};
|
||||
|
||||
typedef std::set<Value *> ValuesSeen;
|
||||
|
||||
class Printer
|
||||
{
|
||||
private:
|
||||
std::ostream & output;
|
||||
EvalState & state;
|
||||
PrintOptions options;
|
||||
std::optional<ValuesSeen> seen;
|
||||
|
||||
void printRepeated()
|
||||
{
|
||||
if (options.ansiColors)
|
||||
output << ANSI_MAGENTA;
|
||||
output << "«repeated»";
|
||||
if (options.ansiColors)
|
||||
output << ANSI_NORMAL;
|
||||
}
|
||||
|
||||
void printNullptr()
|
||||
{
|
||||
if (options.ansiColors)
|
||||
output << ANSI_MAGENTA;
|
||||
output << "«nullptr»";
|
||||
if (options.ansiColors)
|
||||
output << ANSI_NORMAL;
|
||||
}
|
||||
|
||||
void printElided(unsigned int value, const std::string_view single, const std::string_view plural)
|
||||
{
|
||||
::nix::printElided(output, value, single, plural, options.ansiColors);
|
||||
}
|
||||
|
||||
void printInt(Value & v)
|
||||
{
|
||||
if (options.ansiColors)
|
||||
output << ANSI_CYAN;
|
||||
output << v.integer;
|
||||
if (options.ansiColors)
|
||||
output << ANSI_NORMAL;
|
||||
}
|
||||
|
||||
void printFloat(Value & v)
|
||||
{
|
||||
if (options.ansiColors)
|
||||
output << ANSI_CYAN;
|
||||
output << v.fpoint;
|
||||
if (options.ansiColors)
|
||||
output << ANSI_NORMAL;
|
||||
}
|
||||
|
||||
void printBool(Value & v)
|
||||
{
|
||||
if (options.ansiColors)
|
||||
output << ANSI_CYAN;
|
||||
printLiteralBool(output, v.boolean);
|
||||
if (options.ansiColors)
|
||||
output << ANSI_NORMAL;
|
||||
}
|
||||
|
||||
void printString(Value & v)
|
||||
{
|
||||
printLiteralString(output, v.string_view(), options.maxStringLength, options.ansiColors);
|
||||
}
|
||||
|
||||
void printPath(Value & v)
|
||||
{
|
||||
if (options.ansiColors)
|
||||
output << ANSI_GREEN;
|
||||
output << v.path().to_string(); // !!! escaping?
|
||||
if (options.ansiColors)
|
||||
output << ANSI_NORMAL;
|
||||
}
|
||||
|
||||
void printNull()
|
||||
{
|
||||
if (options.ansiColors)
|
||||
output << ANSI_CYAN;
|
||||
output << "null";
|
||||
if (options.ansiColors)
|
||||
output << ANSI_NORMAL;
|
||||
}
|
||||
|
||||
void printDerivation(Value & v)
|
||||
{
|
||||
try {
|
||||
Bindings::iterator i = v.attrs->find(state.sDrvPath);
|
||||
NixStringContext context;
|
||||
std::string storePath;
|
||||
if (i != v.attrs->end())
|
||||
storePath = state.store->printStorePath(state.coerceToStorePath(i->pos, *i->value, context, "while evaluating the drvPath of a derivation"));
|
||||
|
||||
if (options.ansiColors)
|
||||
output << ANSI_GREEN;
|
||||
output << "«derivation";
|
||||
if (!storePath.empty()) {
|
||||
output << " " << storePath;
|
||||
}
|
||||
output << "»";
|
||||
if (options.ansiColors)
|
||||
output << ANSI_NORMAL;
|
||||
} catch (BaseError & e) {
|
||||
printError_(e);
|
||||
}
|
||||
}
|
||||
|
||||
void printAttrs(Value & v, size_t depth)
|
||||
{
|
||||
if (seen && !seen->insert(&v).second) {
|
||||
printRepeated();
|
||||
return;
|
||||
}
|
||||
|
||||
if (options.force && options.derivationPaths && state.isDerivation(v)) {
|
||||
printDerivation(v);
|
||||
} else if (depth < options.maxDepth) {
|
||||
output << "{ ";
|
||||
|
||||
std::vector<std::pair<std::string, Value *>> sorted;
|
||||
for (auto & i : *v.attrs)
|
||||
sorted.emplace_back(std::pair(state.symbols[i.name], i.value));
|
||||
|
||||
if (options.maxAttrs == std::numeric_limits<size_t>::max())
|
||||
std::sort(sorted.begin(), sorted.end());
|
||||
else
|
||||
std::sort(sorted.begin(), sorted.end(), ImportantFirstAttrNameCmp());
|
||||
|
||||
size_t attrsPrinted = 0;
|
||||
for (auto & i : sorted) {
|
||||
if (attrsPrinted >= options.maxAttrs) {
|
||||
printElided(sorted.size() - attrsPrinted, "attribute", "attributes");
|
||||
break;
|
||||
}
|
||||
|
||||
printAttributeName(output, i.first);
|
||||
output << " = ";
|
||||
print(*i.second, depth + 1);
|
||||
output << "; ";
|
||||
attrsPrinted++;
|
||||
}
|
||||
|
||||
output << "}";
|
||||
} else
|
||||
output << "{ ... }";
|
||||
}
|
||||
|
||||
void printList(Value & v, size_t depth)
|
||||
{
|
||||
if (seen && v.listSize() && !seen->insert(&v).second) {
|
||||
printRepeated();
|
||||
return;
|
||||
}
|
||||
|
||||
output << "[ ";
|
||||
if (depth < options.maxDepth) {
|
||||
size_t listItemsPrinted = 0;
|
||||
for (auto elem : v.listItems()) {
|
||||
if (listItemsPrinted >= options.maxListItems) {
|
||||
printElided(v.listSize() - listItemsPrinted, "item", "items");
|
||||
break;
|
||||
}
|
||||
|
||||
if (elem) {
|
||||
print(*elem, depth + 1);
|
||||
} else {
|
||||
printNullptr();
|
||||
}
|
||||
output << " ";
|
||||
listItemsPrinted++;
|
||||
}
|
||||
}
|
||||
else
|
||||
output << "... ";
|
||||
output << "]";
|
||||
}
|
||||
|
||||
void printFunction(Value & v)
|
||||
{
|
||||
if (options.ansiColors)
|
||||
output << ANSI_BLUE;
|
||||
output << "«";
|
||||
|
||||
if (v.isLambda()) {
|
||||
output << "lambda";
|
||||
if (v.lambda.fun) {
|
||||
if (v.lambda.fun->name) {
|
||||
output << " " << state.symbols[v.lambda.fun->name];
|
||||
}
|
||||
|
||||
std::ostringstream s;
|
||||
s << state.positions[v.lambda.fun->pos];
|
||||
output << " @ " << filterANSIEscapes(s.str());
|
||||
}
|
||||
} else if (v.isPrimOp()) {
|
||||
if (v.primOp)
|
||||
output << *v.primOp;
|
||||
else
|
||||
output << "primop";
|
||||
} else if (v.isPrimOpApp()) {
|
||||
output << "partially applied ";
|
||||
auto primOp = v.primOpAppPrimOp();
|
||||
if (primOp)
|
||||
output << *primOp;
|
||||
else
|
||||
output << "primop";
|
||||
} else {
|
||||
abort();
|
||||
}
|
||||
|
||||
output << "»";
|
||||
if (options.ansiColors)
|
||||
output << ANSI_NORMAL;
|
||||
}
|
||||
|
||||
void printThunk(Value & v)
|
||||
{
|
||||
if (v.isBlackhole()) {
|
||||
// Although we know for sure that it's going to be an infinite recursion
|
||||
// when this value is accessed _in the current context_, it's likely
|
||||
// that the user will misinterpret a simpler «infinite recursion» output
|
||||
// as a definitive statement about the value, while in fact it may be
|
||||
// a valid value after `builtins.trace` and perhaps some other steps
|
||||
// have completed.
|
||||
if (options.ansiColors)
|
||||
output << ANSI_RED;
|
||||
output << "«potential infinite recursion»";
|
||||
if (options.ansiColors)
|
||||
output << ANSI_NORMAL;
|
||||
} else if (v.isThunk() || v.isApp()) {
|
||||
if (options.ansiColors)
|
||||
output << ANSI_MAGENTA;
|
||||
output << "«thunk»";
|
||||
if (options.ansiColors)
|
||||
output << ANSI_NORMAL;
|
||||
} else {
|
||||
abort();
|
||||
}
|
||||
}
|
||||
|
||||
void printExternal(Value & v)
|
||||
{
|
||||
v.external->print(output);
|
||||
}
|
||||
|
||||
void printUnknown()
|
||||
{
|
||||
if (options.ansiColors)
|
||||
output << ANSI_RED;
|
||||
output << "«unknown»";
|
||||
if (options.ansiColors)
|
||||
output << ANSI_NORMAL;
|
||||
}
|
||||
|
||||
void printError_(BaseError & e)
|
||||
{
|
||||
if (options.ansiColors)
|
||||
output << ANSI_RED;
|
||||
output << "«" << e.msg() << "»";
|
||||
if (options.ansiColors)
|
||||
output << ANSI_NORMAL;
|
||||
}
|
||||
|
||||
void print(Value & v, size_t depth)
|
||||
{
|
||||
output.flush();
|
||||
checkInterrupt();
|
||||
|
||||
if (options.force) {
|
||||
try {
|
||||
state.forceValue(v, v.determinePos(noPos));
|
||||
} catch (BaseError & e) {
|
||||
printError_(e);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
switch (v.type()) {
|
||||
|
||||
case nInt:
|
||||
printInt(v);
|
||||
break;
|
||||
|
||||
case nFloat:
|
||||
printFloat(v);
|
||||
break;
|
||||
|
||||
case nBool:
|
||||
printBool(v);
|
||||
break;
|
||||
|
||||
case nString:
|
||||
printString(v);
|
||||
break;
|
||||
|
||||
case nPath:
|
||||
printPath(v);
|
||||
break;
|
||||
|
||||
case nNull:
|
||||
printNull();
|
||||
break;
|
||||
|
||||
case nAttrs:
|
||||
printAttrs(v, depth);
|
||||
break;
|
||||
|
||||
case nList:
|
||||
printList(v, depth);
|
||||
break;
|
||||
|
||||
case nFunction:
|
||||
printFunction(v);
|
||||
break;
|
||||
|
||||
case nThunk:
|
||||
printThunk(v);
|
||||
break;
|
||||
|
||||
case nExternal:
|
||||
printExternal(v);
|
||||
break;
|
||||
|
||||
default:
|
||||
printUnknown();
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
public:
|
||||
Printer(std::ostream & output, EvalState & state, PrintOptions options)
|
||||
: output(output), state(state), options(options) { }
|
||||
|
||||
void print(Value & v)
|
||||
{
|
||||
if (options.trackRepeated) {
|
||||
seen.emplace();
|
||||
} else {
|
||||
seen.reset();
|
||||
}
|
||||
|
||||
ValuesSeen seen;
|
||||
print(v, 0);
|
||||
}
|
||||
};
|
||||
|
||||
void printValue(EvalState & state, std::ostream & output, Value & v, PrintOptions options)
|
||||
{
|
||||
Printer(output, state, options).print(v);
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -9,46 +9,54 @@
|
|||
|
||||
#include <iostream>
|
||||
|
||||
#include "eval.hh"
|
||||
#include "print-options.hh"
|
||||
|
||||
namespace nix {
|
||||
/**
|
||||
* Print a string as a Nix string literal.
|
||||
*
|
||||
* Quotes and fairly minimal escaping are added.
|
||||
*
|
||||
* @param s The logical string
|
||||
*/
|
||||
std::ostream & printLiteralString(std::ostream & o, std::string_view s);
|
||||
inline std::ostream & printLiteralString(std::ostream & o, const char * s) {
|
||||
return printLiteralString(o, std::string_view(s));
|
||||
}
|
||||
inline std::ostream & printLiteralString(std::ostream & o, const std::string & s) {
|
||||
return printLiteralString(o, std::string_view(s));
|
||||
}
|
||||
|
||||
/** Print `true` or `false`. */
|
||||
std::ostream & printLiteralBool(std::ostream & o, bool b);
|
||||
|
||||
/**
|
||||
* Print a string as an attribute name in the Nix expression language syntax.
|
||||
*
|
||||
* Prints a quoted string if necessary.
|
||||
*/
|
||||
std::ostream & printAttributeName(std::ostream & o, std::string_view s);
|
||||
|
||||
/**
|
||||
* Returns `true' is a string is a reserved keyword which requires quotation
|
||||
* when printing attribute set field names.
|
||||
*/
|
||||
bool isReservedKeyword(const std::string_view str);
|
||||
|
||||
/**
|
||||
* Print a string as an identifier in the Nix expression language syntax.
|
||||
*
|
||||
* FIXME: "identifier" is ambiguous. Identifiers do not have a single
|
||||
* textual representation. They can be used in variable references,
|
||||
* let bindings, left-hand sides or attribute names in a select
|
||||
* expression, or something else entirely, like JSON. Use one of the
|
||||
* `print*` functions instead.
|
||||
*/
|
||||
std::ostream & printIdentifier(std::ostream & o, std::string_view s);
|
||||
/**
|
||||
* Print a string as a Nix string literal.
|
||||
*
|
||||
* Quotes and fairly minimal escaping are added.
|
||||
*
|
||||
* @param o The output stream to print to
|
||||
* @param s The logical string
|
||||
*/
|
||||
std::ostream & printLiteralString(std::ostream & o, std::string_view s);
|
||||
inline std::ostream & printLiteralString(std::ostream & o, const char * s) {
|
||||
return printLiteralString(o, std::string_view(s));
|
||||
}
|
||||
inline std::ostream & printLiteralString(std::ostream & o, const std::string & s) {
|
||||
return printLiteralString(o, std::string_view(s));
|
||||
}
|
||||
|
||||
/** Print `true` or `false`. */
|
||||
std::ostream & printLiteralBool(std::ostream & o, bool b);
|
||||
|
||||
/**
|
||||
* Print a string as an attribute name in the Nix expression language syntax.
|
||||
*
|
||||
* Prints a quoted string if necessary.
|
||||
*/
|
||||
std::ostream & printAttributeName(std::ostream & o, std::string_view s);
|
||||
|
||||
/**
|
||||
* Returns `true' is a string is a reserved keyword which requires quotation
|
||||
* when printing attribute set field names.
|
||||
*/
|
||||
bool isReservedKeyword(const std::string_view str);
|
||||
|
||||
/**
|
||||
* Print a string as an identifier in the Nix expression language syntax.
|
||||
*
|
||||
* FIXME: "identifier" is ambiguous. Identifiers do not have a single
|
||||
* textual representation. They can be used in variable references,
|
||||
* let bindings, left-hand sides or attribute names in a select
|
||||
* expression, or something else entirely, like JSON. Use one of the
|
||||
* `print*` functions instead.
|
||||
*/
|
||||
std::ostream & printIdentifier(std::ostream & o, std::string_view s);
|
||||
|
||||
void printValue(EvalState & state, std::ostream & str, Value & v, PrintOptions options = PrintOptions {});
|
||||
|
||||
}
|
||||
|
|
|
@ -9,6 +9,7 @@
|
|||
#include "value/context.hh"
|
||||
#include "input-accessor.hh"
|
||||
#include "source-path.hh"
|
||||
#include "print-options.hh"
|
||||
|
||||
#if HAVE_BOEHMGC
|
||||
#include <gc/gc_allocator.h>
|
||||
|
@ -70,7 +71,7 @@ struct Pos;
|
|||
class StorePath;
|
||||
class EvalState;
|
||||
class XMLWriter;
|
||||
|
||||
class Printer;
|
||||
|
||||
typedef int64_t NixInt;
|
||||
typedef double NixFloat;
|
||||
|
@ -82,6 +83,7 @@ typedef double NixFloat;
|
|||
class ExternalValueBase
|
||||
{
|
||||
friend std::ostream & operator << (std::ostream & str, const ExternalValueBase & v);
|
||||
friend class Printer;
|
||||
protected:
|
||||
/**
|
||||
* Print out the value
|
||||
|
@ -139,11 +141,9 @@ private:
|
|||
|
||||
friend std::string showType(const Value & v);
|
||||
|
||||
void print(const SymbolTable &symbols, std::ostream &str, std::set<const void *> *seen, int depth) const;
|
||||
|
||||
public:
|
||||
|
||||
void print(const SymbolTable &symbols, std::ostream &str, bool showRepeated = false, int depth = INT_MAX) const;
|
||||
void print(EvalState &state, std::ostream &str, PrintOptions options = PrintOptions {});
|
||||
|
||||
// Functions needed to distinguish the type
|
||||
// These should be removed eventually, by putting the functionality that's
|
||||
|
@ -364,10 +364,15 @@ public:
|
|||
inline void mkPrimOpApp(Value * l, Value * r)
|
||||
{
|
||||
internalType = tPrimOpApp;
|
||||
app.left = l;
|
||||
app.right = r;
|
||||
primOpApp.left = l;
|
||||
primOpApp.right = r;
|
||||
}
|
||||
|
||||
/**
|
||||
* For a `tPrimOpApp` value, get the original `PrimOp` value.
|
||||
*/
|
||||
PrimOp * primOpAppPrimOp() const;
|
||||
|
||||
inline void mkExternal(ExternalValueBase * e)
|
||||
{
|
||||
clearValue();
|
||||
|
|
|
@ -1495,7 +1495,7 @@ void LocalDerivationGoal::startDaemon()
|
|||
daemon::processConnection(store, from, to,
|
||||
NotTrusted, daemon::Recursive);
|
||||
debug("terminated daemon connection");
|
||||
} catch (SysError &) {
|
||||
} catch (SystemError &) {
|
||||
ignoreException();
|
||||
}
|
||||
});
|
||||
|
@ -1707,7 +1707,7 @@ void LocalDerivationGoal::runChild()
|
|||
try {
|
||||
if (drv->isBuiltin() && drv->builder == "builtin:fetchurl")
|
||||
netrcData = readFile(settings.netrcFile);
|
||||
} catch (SysError &) { }
|
||||
} catch (SystemError &) { }
|
||||
|
||||
#if __linux__
|
||||
if (useChroot) {
|
||||
|
|
|
@ -449,7 +449,7 @@ void Worker::waitForInput()
|
|||
} else {
|
||||
printMsg(lvlVomit, "%1%: read %2% bytes",
|
||||
goal->getName(), rd);
|
||||
std::string data((char *) buffer.data(), rd);
|
||||
std::string_view data((char *) buffer.data(), rd);
|
||||
j->lastOutput = after;
|
||||
goal->handleChildOutput(k, data);
|
||||
}
|
||||
|
|
|
@ -413,7 +413,7 @@ void LocalStore::findRuntimeRoots(Roots & roots, bool censor)
|
|||
auto env_end = std::sregex_iterator{};
|
||||
for (auto i = std::sregex_iterator{envString.begin(), envString.end(), storePathRegex}; i != env_end; ++i)
|
||||
unchecked[i->str()].emplace(envFile);
|
||||
} catch (SysError & e) {
|
||||
} catch (SystemError & e) {
|
||||
if (errno == ENOENT || errno == EACCES || errno == ESRCH)
|
||||
continue;
|
||||
throw;
|
||||
|
|
|
@ -118,7 +118,7 @@ void loadConfFile()
|
|||
try {
|
||||
std::string contents = readFile(path);
|
||||
globalConfig.applyConfig(contents, path);
|
||||
} catch (SysError &) { }
|
||||
} catch (SystemError &) { }
|
||||
};
|
||||
|
||||
applyConfigFile(settings.nixConfDir + "/nix.conf");
|
||||
|
|
|
@ -19,7 +19,7 @@ PublicKeys getDefaultPublicKeys()
|
|||
try {
|
||||
SecretKey secretKey(readFile(secretKeyFile));
|
||||
publicKeys.emplace(secretKey.name, secretKey.toPublicKey());
|
||||
} catch (SysError & e) {
|
||||
} catch (SystemError & e) {
|
||||
/* Ignore unreadable key files. That's normal in a
|
||||
multi-user installation. */
|
||||
}
|
||||
|
|
|
@ -276,7 +276,7 @@ LocalStore::LocalStore(const Params & params)
|
|||
[[gnu::unused]] auto res2 = ftruncate(fd.get(), settings.reservedSize);
|
||||
}
|
||||
}
|
||||
} catch (SysError & e) { /* don't care about errors */
|
||||
} catch (SystemError & e) { /* don't care about errors */
|
||||
}
|
||||
|
||||
/* Acquire the big fat lock in shared mode to make sure that no
|
||||
|
|
|
@ -242,7 +242,7 @@ void LocalStore::optimisePath_(Activity * act, OptimiseStats & stats,
|
|||
/* Atomically replace the old file with the new hard link. */
|
||||
try {
|
||||
renameFile(tempLink, path);
|
||||
} catch (SysError & e) {
|
||||
} catch (SystemError & e) {
|
||||
if (unlink(tempLink.c_str()) == -1)
|
||||
printError("unable to unlink '%1%'", tempLink);
|
||||
if (errno == EMLINK) {
|
||||
|
|
|
@ -87,13 +87,13 @@ std::pair<ref<SourceAccessor>, CanonPath> RemoteFSAccessor::fetch(const CanonPat
|
|||
nars.emplace(storePath.hashPart(), narAccessor);
|
||||
return {narAccessor, restPath};
|
||||
|
||||
} catch (SysError &) { }
|
||||
} catch (SystemError &) { }
|
||||
|
||||
try {
|
||||
auto narAccessor = makeNarAccessor(nix::readFile(cacheFile));
|
||||
nars.emplace(storePath.hashPart(), narAccessor);
|
||||
return {narAccessor, restPath};
|
||||
} catch (SysError &) { }
|
||||
} catch (SystemError &) { }
|
||||
}
|
||||
|
||||
StringSink sink;
|
||||
|
|
|
@ -304,7 +304,7 @@ void RootArgs::parseCmdline(const Strings & _cmdline, bool allowShebang)
|
|||
for (auto pos = savedArgs.begin(); pos != savedArgs.end();pos++)
|
||||
cmdline.push_back(*pos);
|
||||
}
|
||||
} catch (SysError &) { }
|
||||
} catch (SystemError &) { }
|
||||
}
|
||||
for (auto pos = cmdline.begin(); pos != cmdline.end(); ) {
|
||||
|
||||
|
|
|
@ -6,11 +6,11 @@ namespace nix {
|
|||
CanonPath CanonPath::root = CanonPath("/");
|
||||
|
||||
CanonPath::CanonPath(std::string_view raw)
|
||||
: path(absPath((Path) raw, "/"))
|
||||
: path(absPath(raw, "/"))
|
||||
{ }
|
||||
|
||||
CanonPath::CanonPath(std::string_view raw, const CanonPath & root)
|
||||
: path(absPath((Path) raw, root.abs()))
|
||||
: path(absPath(raw, root.abs()))
|
||||
{ }
|
||||
|
||||
CanonPath::CanonPath(const std::vector<std::string> & elems)
|
||||
|
@ -22,7 +22,7 @@ CanonPath::CanonPath(const std::vector<std::string> & elems)
|
|||
|
||||
CanonPath CanonPath::fromCwd(std::string_view path)
|
||||
{
|
||||
return CanonPath(unchecked_t(), absPath((Path) path));
|
||||
return CanonPath(unchecked_t(), absPath(path));
|
||||
}
|
||||
|
||||
std::optional<CanonPath> CanonPath::parent() const
|
||||
|
|
|
@ -95,7 +95,7 @@ static CgroupStats destroyCgroup(const Path & cgroup, bool returnStats)
|
|||
using namespace std::string_literals;
|
||||
warn("killing stray builder process %d (%s)...",
|
||||
pid, trim(replaceStrings(cmdline, "\0"s, " ")));
|
||||
} catch (SysError &) {
|
||||
} catch (SystemError &) {
|
||||
}
|
||||
}
|
||||
// FIXME: pid wraparound
|
||||
|
|
|
@ -124,7 +124,7 @@ static void applyConfigInner(const std::string & contents, const std::string & p
|
|||
try {
|
||||
std::string includedContents = readFile(path);
|
||||
applyConfigInner(includedContents, p, parsedContents);
|
||||
} catch (SysError &) {
|
||||
} catch (SystemError &) {
|
||||
// TODO: Do we actually want to ignore this? Or is it better to fail?
|
||||
}
|
||||
} else if (!ignoreMissing) {
|
||||
|
|
18
src/libutil/english.cc
Normal file
18
src/libutil/english.cc
Normal file
|
@ -0,0 +1,18 @@
|
|||
#include "english.hh"
|
||||
|
||||
namespace nix {
|
||||
|
||||
std::ostream & pluralize(
|
||||
std::ostream & output,
|
||||
unsigned int count,
|
||||
const std::string_view single,
|
||||
const std::string_view plural)
|
||||
{
|
||||
if (count == 1)
|
||||
output << "1 " << single;
|
||||
else
|
||||
output << count << " " << plural;
|
||||
return output;
|
||||
}
|
||||
|
||||
}
|
18
src/libutil/english.hh
Normal file
18
src/libutil/english.hh
Normal file
|
@ -0,0 +1,18 @@
|
|||
#pragma once
|
||||
|
||||
#include <iostream>
|
||||
|
||||
namespace nix {
|
||||
|
||||
/**
|
||||
* Pluralize a given value.
|
||||
*
|
||||
* If `count == 1`, prints `1 {single}` to `output`, otherwise prints `{count} {plural}`.
|
||||
*/
|
||||
std::ostream & pluralize(
|
||||
std::ostream & output,
|
||||
unsigned int count,
|
||||
const std::string_view single,
|
||||
const std::string_view plural);
|
||||
|
||||
}
|
|
@ -178,20 +178,50 @@ MakeError(Error, BaseError);
|
|||
MakeError(UsageError, Error);
|
||||
MakeError(UnimplementedError, Error);
|
||||
|
||||
class SysError : public Error
|
||||
/**
|
||||
* To use in catch-blocks.
|
||||
*/
|
||||
MakeError(SystemError, Error);
|
||||
|
||||
/**
|
||||
* POSIX system error, created using `errno`, `strerror` friends.
|
||||
*
|
||||
* Throw this, but prefer not to catch this, and catch `SystemError`
|
||||
* instead. This allows implementations to freely switch between this
|
||||
* and `WinError` without breaking catch blocks.
|
||||
*
|
||||
* However, it is permissible to catch this and rethrow so long as
|
||||
* certain conditions are not met (e.g. to catch only if `errNo =
|
||||
* EFooBar`). In that case, try to also catch the equivalent `WinError`
|
||||
* code.
|
||||
*
|
||||
* @todo Rename this to `PosixError` or similar. At this point Windows
|
||||
* support is too WIP to justify the code churn, but if it is finished
|
||||
* then a better identifier becomes moe worth it.
|
||||
*/
|
||||
class SysError : public SystemError
|
||||
{
|
||||
public:
|
||||
int errNo;
|
||||
|
||||
/**
|
||||
* Construct using the explicitly-provided error number. `strerror`
|
||||
* will be used to try to add additional information to the message.
|
||||
*/
|
||||
template<typename... Args>
|
||||
SysError(int errNo_, const Args & ... args)
|
||||
: Error("")
|
||||
SysError(int errNo, const Args & ... args)
|
||||
: SystemError(""), errNo(errNo)
|
||||
{
|
||||
errNo = errNo_;
|
||||
auto hf = hintfmt(args...);
|
||||
err.msg = hintfmt("%1%: %2%", normaltxt(hf.str()), strerror(errNo));
|
||||
}
|
||||
|
||||
/**
|
||||
* Construct using the ambient `errno`.
|
||||
*
|
||||
* Be sure to not perform another `errno`-modifying operation before
|
||||
* calling this constructor!
|
||||
*/
|
||||
template<typename... Args>
|
||||
SysError(const Args & ... args)
|
||||
: SysError(errno, args ...)
|
||||
|
@ -199,7 +229,9 @@ public:
|
|||
}
|
||||
};
|
||||
|
||||
/** Throw an exception for the purpose of checking that exception handling works; see 'initLibUtil()'.
|
||||
/**
|
||||
* Throw an exception for the purpose of checking that exception
|
||||
* handling works; see 'initLibUtil()'.
|
||||
*/
|
||||
void throwExceptionSelfCheck();
|
||||
|
||||
|
|
|
@ -231,7 +231,7 @@ void closeMostFDs(const std::set<int> & exceptions)
|
|||
}
|
||||
}
|
||||
return;
|
||||
} catch (SysError &) {
|
||||
} catch (SystemError &) {
|
||||
}
|
||||
#endif
|
||||
|
||||
|
|
|
@ -21,9 +21,16 @@ namespace fs = std::filesystem;
|
|||
|
||||
namespace nix {
|
||||
|
||||
Path absPath(Path path, std::optional<PathView> dir, bool resolveSymlinks)
|
||||
Path absPath(PathView path, std::optional<PathView> dir, bool resolveSymlinks)
|
||||
{
|
||||
std::string scratch;
|
||||
|
||||
if (path[0] != '/') {
|
||||
// In this case we need to call `canonPath` on a newly-created
|
||||
// string. We set `scratch` to that string first, and then set
|
||||
// `path` to `scratch`. This ensures the newly-created string
|
||||
// lives long enough for the call to `canonPath`, and allows us
|
||||
// to just accept a `std::string_view`.
|
||||
if (!dir) {
|
||||
#ifdef __GNU__
|
||||
/* GNU (aka. GNU/Hurd) doesn't have any limitation on path
|
||||
|
@ -35,12 +42,13 @@ Path absPath(Path path, std::optional<PathView> dir, bool resolveSymlinks)
|
|||
if (!getcwd(buf, sizeof(buf)))
|
||||
#endif
|
||||
throw SysError("cannot get cwd");
|
||||
path = concatStrings(buf, "/", path);
|
||||
scratch = concatStrings(buf, "/", path);
|
||||
#ifdef __GNU__
|
||||
free(buf);
|
||||
#endif
|
||||
} else
|
||||
path = concatStrings(*dir, "/", path);
|
||||
scratch = concatStrings(*dir, "/", path);
|
||||
path = scratch;
|
||||
}
|
||||
return canonPath(path, resolveSymlinks);
|
||||
}
|
||||
|
|
|
@ -41,7 +41,7 @@ struct Source;
|
|||
* specified directory, or the current directory otherwise. The path
|
||||
* is also canonicalised.
|
||||
*/
|
||||
Path absPath(Path path,
|
||||
Path absPath(PathView path,
|
||||
std::optional<PathView> dir = {},
|
||||
bool resolveSymlinks = false);
|
||||
|
||||
|
|
|
@ -116,7 +116,7 @@ void writeToStderr(std::string_view s)
|
|||
{
|
||||
try {
|
||||
writeFull(STDERR_FILENO, s, false);
|
||||
} catch (SysError & e) {
|
||||
} catch (SystemError & e) {
|
||||
/* Ignore failing writes to stderr. We need to ignore write
|
||||
errors to ensure that cleanup code that logs to stderr runs
|
||||
to completion if the other side of stderr has been closed
|
||||
|
|
|
@ -53,7 +53,7 @@ void FdSink::writeUnbuffered(std::string_view data)
|
|||
written += data.size();
|
||||
try {
|
||||
writeFull(fd, data);
|
||||
} catch (SysError & e) {
|
||||
} catch (SystemError & e) {
|
||||
_good = false;
|
||||
throw;
|
||||
}
|
||||
|
|
|
@ -20,7 +20,7 @@ void initLibUtil() {
|
|||
// When exception handling fails, the message tends to be printed by the
|
||||
// C++ runtime, followed by an abort.
|
||||
// For example on macOS we might see an error such as
|
||||
// libc++abi: terminating with uncaught exception of type nix::SysError: error: C++ exception handling is broken. This would appear to be a problem with the way Nix was compiled and/or linked and/or loaded.
|
||||
// libc++abi: terminating with uncaught exception of type nix::SystemError: error: C++ exception handling is broken. This would appear to be a problem with the way Nix was compiled and/or linked and/or loaded.
|
||||
bool caught = false;
|
||||
try {
|
||||
throwExceptionSelfCheck();
|
||||
|
|
|
@ -148,7 +148,7 @@ static void main_nix_build(int argc, char * * argv)
|
|||
args.push_back(word);
|
||||
}
|
||||
}
|
||||
} catch (SysError &) { }
|
||||
} catch (SystemError &) { }
|
||||
}
|
||||
|
||||
struct MyArgs : LegacyArgs, MixEvalArgs
|
||||
|
|
|
@ -8,6 +8,8 @@
|
|||
#include "eval.hh"
|
||||
#include "eval-inline.hh"
|
||||
#include "profiles.hh"
|
||||
#include "print-ambiguous.hh"
|
||||
#include <limits>
|
||||
|
||||
|
||||
namespace nix {
|
||||
|
@ -106,7 +108,7 @@ bool createUserEnv(EvalState & state, DrvInfos & elems,
|
|||
environment. */
|
||||
auto manifestFile = ({
|
||||
std::ostringstream str;
|
||||
manifest.print(state.symbols, str, true);
|
||||
printAmbiguous(manifest, state.symbols, str, nullptr, std::numeric_limits<int>::max());
|
||||
// TODO with C++20 we can use str.view() instead and avoid copy.
|
||||
std::string str2 = str.str();
|
||||
StringSource source { str2 };
|
||||
|
|
|
@ -1,9 +1,11 @@
|
|||
#include "globals.hh"
|
||||
#include "print-ambiguous.hh"
|
||||
#include "shared.hh"
|
||||
#include "eval.hh"
|
||||
#include "eval-inline.hh"
|
||||
#include "get-drvs.hh"
|
||||
#include "attr-path.hh"
|
||||
#include "signals.hh"
|
||||
#include "value-to-xml.hh"
|
||||
#include "value-to-json.hh"
|
||||
#include "store-api.hh"
|
||||
|
@ -24,7 +26,6 @@ static int rootNr = 0;
|
|||
|
||||
enum OutputKind { okPlain, okXML, okJSON };
|
||||
|
||||
|
||||
void processExpr(EvalState & state, const Strings & attrPaths,
|
||||
bool parseOnly, bool strict, Bindings & autoArgs,
|
||||
bool evalOnly, OutputKind output, bool location, Expr * e)
|
||||
|
@ -56,7 +57,8 @@ void processExpr(EvalState & state, const Strings & attrPaths,
|
|||
std::cout << std::endl;
|
||||
} else {
|
||||
if (strict) state.forceValueDeep(vRes);
|
||||
vRes.print(state.symbols, std::cout);
|
||||
std::set<const void *> seen;
|
||||
printAmbiguous(vRes, state.symbols, std::cout, &seen, std::numeric_limits<int>::max());
|
||||
std::cout << std::endl;
|
||||
}
|
||||
} else {
|
||||
|
|
|
@ -107,7 +107,7 @@ struct CmdConfigCheck : StoreCommand
|
|||
if (profileDir.find("/profiles/") == std::string::npos)
|
||||
dirs.insert(dir);
|
||||
}
|
||||
} catch (SysError &) {}
|
||||
} catch (SystemError &) {}
|
||||
}
|
||||
|
||||
if (!dirs.empty()) {
|
||||
|
|
|
@ -297,7 +297,6 @@ struct Common : InstallableCommand, MixProfile
|
|||
"NIX_LOG_FD",
|
||||
"NIX_REMOTE",
|
||||
"PPID",
|
||||
"SHELL",
|
||||
"SHELLOPTS",
|
||||
"SSL_CERT_FILE", // FIXME: only want to ignore /no-cert-file.crt
|
||||
"TEMP",
|
||||
|
@ -604,7 +603,7 @@ struct CmdDevelop : Common, MixEnvironment
|
|||
|
||||
setEnviron();
|
||||
// prevent garbage collection until shell exits
|
||||
setenv("NIX_GCROOT", gcroot.data(), 1);
|
||||
setenv("NIX_GCROOT", gcroot.c_str(), 1);
|
||||
|
||||
Path shell = "bash";
|
||||
|
||||
|
@ -647,6 +646,10 @@ struct CmdDevelop : Common, MixEnvironment
|
|||
ignoreException();
|
||||
}
|
||||
|
||||
// Override SHELL with the one chosen for this environment.
|
||||
// This is to make sure the system shell doesn't leak into the build environment.
|
||||
setenv("SHELL", shell.c_str(), 1);
|
||||
|
||||
// If running a phase or single command, don't want an interactive shell running after
|
||||
// Ctrl-C, so don't pass --rcfile
|
||||
auto args = phase || !command.empty() ? Strings{std::string(baseNameOf(shell)), rcFilePath}
|
||||
|
|
|
@ -7,14 +7,12 @@ R""(
|
|||
```console
|
||||
# nix profile list
|
||||
Name: gdb
|
||||
Index: 0
|
||||
Flake attribute: legacyPackages.x86_64-linux.gdb
|
||||
Original flake URL: flake:nixpkgs
|
||||
Locked flake URL: github:NixOS/nixpkgs/7b38b03d76ab71bdc8dc325e3f6338d984cc35ca
|
||||
Store paths: /nix/store/indzcw5wvlhx6vwk7k4iq29q15chvr3d-gdb-11.1
|
||||
|
||||
Name: blender-bin
|
||||
Index: 1
|
||||
Flake attribute: packages.x86_64-linux.default
|
||||
Original flake URL: flake:blender-bin
|
||||
Locked flake URL: github:edolstra/nix-warez/91f2ffee657bf834e4475865ae336e2379282d34?dir=blender
|
||||
|
|
|
@ -8,13 +8,6 @@ R""(
|
|||
# nix profile remove hello
|
||||
```
|
||||
|
||||
* Remove a package by index
|
||||
*(deprecated, will be removed in a future version)*:
|
||||
|
||||
```console
|
||||
# nix profile remove 3
|
||||
```
|
||||
|
||||
* Remove all packages:
|
||||
|
||||
```console
|
||||
|
|
|
@ -15,13 +15,6 @@ R""(
|
|||
# nix profile upgrade hello
|
||||
```
|
||||
|
||||
* Upgrade a specific package by index
|
||||
*(deprecated, will be removed in a future version)*:
|
||||
|
||||
```console
|
||||
# nix profile upgrade 0
|
||||
```
|
||||
|
||||
# Description
|
||||
|
||||
This command upgrades a previously installed package in a Nix profile,
|
||||
|
|
|
@ -45,7 +45,6 @@ const int defaultPriority = 5;
|
|||
struct ProfileElement
|
||||
{
|
||||
StorePathSet storePaths;
|
||||
std::string name;
|
||||
std::optional<ProfileElementSource> source;
|
||||
bool active = true;
|
||||
int priority = defaultPriority;
|
||||
|
@ -82,11 +81,6 @@ struct ProfileElement
|
|||
return showVersions(versions);
|
||||
}
|
||||
|
||||
bool operator < (const ProfileElement & other) const
|
||||
{
|
||||
return std::tuple(identifier(), storePaths) < std::tuple(other.identifier(), other.storePaths);
|
||||
}
|
||||
|
||||
void updateStorePaths(
|
||||
ref<Store> evalStore,
|
||||
ref<Store> store,
|
||||
|
@ -109,7 +103,9 @@ struct ProfileElement
|
|||
|
||||
struct ProfileManifest
|
||||
{
|
||||
std::vector<ProfileElement> elements;
|
||||
using ProfileElementName = std::string;
|
||||
|
||||
std::map<ProfileElementName, ProfileElement> elements;
|
||||
|
||||
ProfileManifest() { }
|
||||
|
||||
|
@ -119,8 +115,6 @@ struct ProfileManifest
|
|||
|
||||
if (pathExists(manifestPath)) {
|
||||
auto json = nlohmann::json::parse(readFile(manifestPath));
|
||||
/* Keep track of already found names to allow preventing duplicates. */
|
||||
std::set<std::string> foundNames;
|
||||
|
||||
auto version = json.value("version", 0);
|
||||
std::string sUrl;
|
||||
|
@ -131,6 +125,7 @@ struct ProfileManifest
|
|||
sOriginalUrl = "originalUri";
|
||||
break;
|
||||
case 2:
|
||||
case 3:
|
||||
sUrl = "url";
|
||||
sOriginalUrl = "originalUrl";
|
||||
break;
|
||||
|
@ -138,7 +133,9 @@ struct ProfileManifest
|
|||
throw Error("profile manifest '%s' has unsupported version %d", manifestPath, version);
|
||||
}
|
||||
|
||||
for (auto & e : json["elements"]) {
|
||||
auto elems = json["elements"];
|
||||
for (auto & elem : elems.items()) {
|
||||
auto & e = elem.value();
|
||||
ProfileElement element;
|
||||
for (auto & p : e["storePaths"])
|
||||
element.storePaths.insert(state.store->parseStorePath((std::string) p));
|
||||
|
@ -155,25 +152,14 @@ struct ProfileManifest
|
|||
};
|
||||
}
|
||||
|
||||
std::string nameCandidate = element.identifier();
|
||||
if (e.contains("name")) {
|
||||
nameCandidate = e["name"];
|
||||
}
|
||||
else if (element.source) {
|
||||
auto url = parseURL(element.source->to_string());
|
||||
auto name = getNameFromURL(url);
|
||||
if (name)
|
||||
nameCandidate = *name;
|
||||
}
|
||||
std::string name =
|
||||
elems.is_object()
|
||||
? elem.key()
|
||||
: element.source
|
||||
? getNameFromURL(parseURL(element.source->to_string())).value_or(element.identifier())
|
||||
: element.identifier();
|
||||
|
||||
auto finalName = nameCandidate;
|
||||
for (int i = 1; foundNames.contains(finalName); ++i) {
|
||||
finalName = nameCandidate + std::to_string(i);
|
||||
}
|
||||
element.name = finalName;
|
||||
foundNames.insert(element.name);
|
||||
|
||||
elements.emplace_back(std::move(element));
|
||||
addElement(name, std::move(element));
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -187,16 +173,34 @@ struct ProfileManifest
|
|||
for (auto & drvInfo : drvInfos) {
|
||||
ProfileElement element;
|
||||
element.storePaths = {drvInfo.queryOutPath()};
|
||||
element.name = element.identifier();
|
||||
elements.emplace_back(std::move(element));
|
||||
addElement(std::move(element));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void addElement(std::string_view nameCandidate, ProfileElement element)
|
||||
{
|
||||
std::string finalName(nameCandidate);
|
||||
for (int i = 1; elements.contains(finalName); ++i)
|
||||
finalName = nameCandidate + "-" + std::to_string(i);
|
||||
|
||||
elements.insert_or_assign(finalName, std::move(element));
|
||||
}
|
||||
|
||||
void addElement(ProfileElement element)
|
||||
{
|
||||
auto name =
|
||||
element.source
|
||||
? getNameFromURL(parseURL(element.source->to_string()))
|
||||
: std::nullopt;
|
||||
auto name2 = name ? *name : element.identifier();
|
||||
addElement(name2, std::move(element));
|
||||
}
|
||||
|
||||
nlohmann::json toJSON(Store & store) const
|
||||
{
|
||||
auto array = nlohmann::json::array();
|
||||
for (auto & element : elements) {
|
||||
auto es = nlohmann::json::object();
|
||||
for (auto & [name, element] : elements) {
|
||||
auto paths = nlohmann::json::array();
|
||||
for (auto & path : element.storePaths)
|
||||
paths.push_back(store.printStorePath(path));
|
||||
|
@ -210,11 +214,11 @@ struct ProfileManifest
|
|||
obj["attrPath"] = element.source->attrPath;
|
||||
obj["outputs"] = element.source->outputs;
|
||||
}
|
||||
array.push_back(obj);
|
||||
es[name] = obj;
|
||||
}
|
||||
nlohmann::json json;
|
||||
json["version"] = 2;
|
||||
json["elements"] = array;
|
||||
json["version"] = 3;
|
||||
json["elements"] = es;
|
||||
return json;
|
||||
}
|
||||
|
||||
|
@ -225,7 +229,7 @@ struct ProfileManifest
|
|||
StorePathSet references;
|
||||
|
||||
Packages pkgs;
|
||||
for (auto & element : elements) {
|
||||
for (auto & [name, element] : elements) {
|
||||
for (auto & path : element.storePaths) {
|
||||
if (element.active)
|
||||
pkgs.emplace_back(store->printStorePath(path), true, element.priority);
|
||||
|
@ -267,33 +271,27 @@ struct ProfileManifest
|
|||
|
||||
static void printDiff(const ProfileManifest & prev, const ProfileManifest & cur, std::string_view indent)
|
||||
{
|
||||
auto prevElems = prev.elements;
|
||||
std::sort(prevElems.begin(), prevElems.end());
|
||||
|
||||
auto curElems = cur.elements;
|
||||
std::sort(curElems.begin(), curElems.end());
|
||||
|
||||
auto i = prevElems.begin();
|
||||
auto j = curElems.begin();
|
||||
auto i = prev.elements.begin();
|
||||
auto j = cur.elements.begin();
|
||||
|
||||
bool changes = false;
|
||||
|
||||
while (i != prevElems.end() || j != curElems.end()) {
|
||||
if (j != curElems.end() && (i == prevElems.end() || i->identifier() > j->identifier())) {
|
||||
logger->cout("%s%s: ∅ -> %s", indent, j->identifier(), j->versions());
|
||||
while (i != prev.elements.end() || j != cur.elements.end()) {
|
||||
if (j != cur.elements.end() && (i == prev.elements.end() || i->first > j->first)) {
|
||||
logger->cout("%s%s: ∅ -> %s", indent, j->second.identifier(), j->second.versions());
|
||||
changes = true;
|
||||
++j;
|
||||
}
|
||||
else if (i != prevElems.end() && (j == curElems.end() || i->identifier() < j->identifier())) {
|
||||
logger->cout("%s%s: %s -> ∅", indent, i->identifier(), i->versions());
|
||||
else if (i != prev.elements.end() && (j == cur.elements.end() || i->first < j->first)) {
|
||||
logger->cout("%s%s: %s -> ∅", indent, i->second.identifier(), i->second.versions());
|
||||
changes = true;
|
||||
++i;
|
||||
}
|
||||
else {
|
||||
auto v1 = i->versions();
|
||||
auto v2 = j->versions();
|
||||
auto v1 = i->second.versions();
|
||||
auto v2 = j->second.versions();
|
||||
if (v1 != v2) {
|
||||
logger->cout("%s%s: %s -> %s", indent, i->identifier(), v1, v2);
|
||||
logger->cout("%s%s: %s -> %s", indent, i->second.identifier(), v1, v2);
|
||||
changes = true;
|
||||
}
|
||||
++i;
|
||||
|
@ -392,7 +390,7 @@ struct CmdProfileInstall : InstallablesCommand, MixDefaultProfile
|
|||
|
||||
element.updateStorePaths(getEvalStore(), store, res);
|
||||
|
||||
manifest.elements.push_back(std::move(element));
|
||||
manifest.addElement(std::move(element));
|
||||
}
|
||||
|
||||
try {
|
||||
|
@ -402,7 +400,7 @@ struct CmdProfileInstall : InstallablesCommand, MixDefaultProfile
|
|||
// See https://github.com/NixOS/nix/compare/3efa476c5439f8f6c1968a6ba20a31d1239c2f04..1fe5d172ece51a619e879c4b86f603d9495cc102
|
||||
auto findRefByFilePath = [&]<typename Iterator>(Iterator begin, Iterator end) {
|
||||
for (auto it = begin; it != end; it++) {
|
||||
auto profileElement = *it;
|
||||
auto & profileElement = it->second;
|
||||
for (auto & storePath : profileElement.storePaths) {
|
||||
if (conflictError.fileA.starts_with(store->printStorePath(storePath))) {
|
||||
return std::pair(conflictError.fileA, profileElement.toInstallables(*store));
|
||||
|
@ -470,43 +468,35 @@ public:
|
|||
std::string pattern;
|
||||
std::regex reg;
|
||||
};
|
||||
typedef std::variant<size_t, Path, RegexPattern> Matcher;
|
||||
typedef std::variant<Path, RegexPattern> Matcher;
|
||||
|
||||
std::vector<Matcher> getMatchers(ref<Store> store)
|
||||
{
|
||||
std::vector<Matcher> res;
|
||||
|
||||
auto anyIndexMatchers = false;
|
||||
|
||||
for (auto & s : _matchers) {
|
||||
if (auto n = string2Int<size_t>(s)) {
|
||||
res.push_back(*n);
|
||||
anyIndexMatchers = true;
|
||||
}
|
||||
if (auto n = string2Int<size_t>(s))
|
||||
throw Error("'nix profile' no longer supports indices ('%d')", *n);
|
||||
else if (store->isStorePath(s))
|
||||
res.push_back(s);
|
||||
else
|
||||
res.push_back(RegexPattern{s,std::regex(s, std::regex::extended | std::regex::icase)});
|
||||
}
|
||||
|
||||
if (anyIndexMatchers) {
|
||||
warn("Indices are deprecated and will be removed in a future version!\n"
|
||||
" Refer to packages by their `Name` as printed by `nix profile list`.\n"
|
||||
" See https://github.com/NixOS/nix/issues/9171 for more information.");
|
||||
}
|
||||
|
||||
return res;
|
||||
}
|
||||
|
||||
bool matches(const Store & store, const ProfileElement & element, size_t pos, const std::vector<Matcher> & matchers)
|
||||
bool matches(
|
||||
const Store & store,
|
||||
const std::string & name,
|
||||
const ProfileElement & element,
|
||||
const std::vector<Matcher> & matchers)
|
||||
{
|
||||
for (auto & matcher : matchers) {
|
||||
if (auto n = std::get_if<size_t>(&matcher)) {
|
||||
if (*n == pos) return true;
|
||||
} else if (auto path = std::get_if<Path>(&matcher)) {
|
||||
if (auto path = std::get_if<Path>(&matcher)) {
|
||||
if (element.storePaths.count(store.parseStorePath(*path))) return true;
|
||||
} else if (auto regex = std::get_if<RegexPattern>(&matcher)) {
|
||||
if (std::regex_match(element.name, regex->reg))
|
||||
if (std::regex_match(name, regex->reg))
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
@ -537,10 +527,9 @@ struct CmdProfileRemove : virtual EvalCommand, MixDefaultProfile, MixProfileElem
|
|||
|
||||
ProfileManifest newManifest;
|
||||
|
||||
for (size_t i = 0; i < oldManifest.elements.size(); ++i) {
|
||||
auto & element(oldManifest.elements[i]);
|
||||
if (!matches(*store, element, i, matchers)) {
|
||||
newManifest.elements.push_back(std::move(element));
|
||||
for (auto & [name, element] : oldManifest.elements) {
|
||||
if (!matches(*store, name, element, matchers)) {
|
||||
newManifest.elements.insert_or_assign(name, std::move(element));
|
||||
} else {
|
||||
notice("removing '%s'", element.identifier());
|
||||
}
|
||||
|
@ -553,11 +542,9 @@ struct CmdProfileRemove : virtual EvalCommand, MixDefaultProfile, MixProfileElem
|
|||
|
||||
if (removedCount == 0) {
|
||||
for (auto matcher: matchers) {
|
||||
if (const size_t * index = std::get_if<size_t>(&matcher)){
|
||||
warn("'%d' is not a valid index", *index);
|
||||
} else if (const Path * path = std::get_if<Path>(&matcher)){
|
||||
if (const Path * path = std::get_if<Path>(&matcher)) {
|
||||
warn("'%s' does not match any paths", *path);
|
||||
} else if (const RegexPattern * regex = std::get_if<RegexPattern>(&matcher)){
|
||||
} else if (const RegexPattern * regex = std::get_if<RegexPattern>(&matcher)) {
|
||||
warn("'%s' does not match any packages", regex->pattern);
|
||||
}
|
||||
}
|
||||
|
@ -588,14 +575,13 @@ struct CmdProfileUpgrade : virtual SourceExprCommand, MixDefaultProfile, MixProf
|
|||
auto matchers = getMatchers(store);
|
||||
|
||||
Installables installables;
|
||||
std::vector<size_t> indices;
|
||||
std::vector<ProfileElement *> elems;
|
||||
|
||||
auto matchedCount = 0;
|
||||
auto upgradedCount = 0;
|
||||
|
||||
for (size_t i = 0; i < manifest.elements.size(); ++i) {
|
||||
auto & element(manifest.elements[i]);
|
||||
if (!matches(*store, element, i, matchers)) {
|
||||
for (auto & [name, element] : manifest.elements) {
|
||||
if (!matches(*store, name, element, matchers)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
|
@ -651,17 +637,15 @@ struct CmdProfileUpgrade : virtual SourceExprCommand, MixDefaultProfile, MixProf
|
|||
};
|
||||
|
||||
installables.push_back(installable);
|
||||
indices.push_back(i);
|
||||
elems.push_back(&element);
|
||||
}
|
||||
|
||||
if (upgradedCount == 0) {
|
||||
if (matchedCount == 0) {
|
||||
for (auto & matcher : matchers) {
|
||||
if (const size_t * index = std::get_if<size_t>(&matcher)){
|
||||
warn("'%d' is not a valid index", *index);
|
||||
} else if (const Path * path = std::get_if<Path>(&matcher)){
|
||||
if (const Path * path = std::get_if<Path>(&matcher)) {
|
||||
warn("'%s' does not match any paths", *path);
|
||||
} else if (const RegexPattern * regex = std::get_if<RegexPattern>(&matcher)){
|
||||
} else if (const RegexPattern * regex = std::get_if<RegexPattern>(&matcher)) {
|
||||
warn("'%s' does not match any packages", regex->pattern);
|
||||
}
|
||||
}
|
||||
|
@ -677,7 +661,7 @@ struct CmdProfileUpgrade : virtual SourceExprCommand, MixDefaultProfile, MixProf
|
|||
|
||||
for (size_t i = 0; i < installables.size(); ++i) {
|
||||
auto & installable = installables.at(i);
|
||||
auto & element = manifest.elements[indices.at(i)];
|
||||
auto & element = *elems.at(i);
|
||||
element.updateStorePaths(
|
||||
getEvalStore(),
|
||||
store,
|
||||
|
@ -709,13 +693,12 @@ struct CmdProfileList : virtual EvalCommand, virtual StoreCommand, MixDefaultPro
|
|||
if (json) {
|
||||
std::cout << manifest.toJSON(*store).dump() << "\n";
|
||||
} else {
|
||||
for (size_t i = 0; i < manifest.elements.size(); ++i) {
|
||||
auto & element(manifest.elements[i]);
|
||||
for (const auto & [i, e] : enumerate(manifest.elements)) {
|
||||
auto & [name, element] = e;
|
||||
if (i) logger->cout("");
|
||||
logger->cout("Name: " ANSI_BOLD "%s" ANSI_NORMAL "%s",
|
||||
element.name,
|
||||
name,
|
||||
element.active ? "" : " " ANSI_RED "(inactive)" ANSI_NORMAL);
|
||||
logger->cout("Index: %s", i);
|
||||
if (element.source) {
|
||||
logger->cout("Flake attribute: %s%s", element.source->attrPath, element.source->outputs.to_string());
|
||||
logger->cout("Original flake URL: %s", element.source->originalRef.to_string());
|
||||
|
|
|
@ -45,6 +45,7 @@ if [[ -n "${NIX_DAEMON_PACKAGE:-}" ]]; then
|
|||
DAEMON_PATH="${NIX_DAEMON_PACKAGE}/bin:$DAEMON_PATH"
|
||||
fi
|
||||
coreutils=@coreutils@
|
||||
lsof=@lsof@
|
||||
|
||||
export dot=@dot@
|
||||
export SHELL="@bash@"
|
||||
|
|
67
tests/functional/flakes/develop.sh
Normal file
67
tests/functional/flakes/develop.sh
Normal file
|
@ -0,0 +1,67 @@
|
|||
source ../common.sh
|
||||
|
||||
clearStore
|
||||
rm -rf $TEST_HOME/.cache $TEST_HOME/.config $TEST_HOME/.local
|
||||
|
||||
# Create flake under test.
|
||||
cp ../shell-hello.nix ../config.nix $TEST_HOME/
|
||||
cat <<EOF >$TEST_HOME/flake.nix
|
||||
{
|
||||
inputs.nixpkgs.url = "$TEST_HOME/nixpkgs";
|
||||
outputs = {self, nixpkgs}: {
|
||||
packages.$system.hello = (import ./config.nix).mkDerivation {
|
||||
name = "hello";
|
||||
outputs = [ "out" "dev" ];
|
||||
meta.outputsToInstall = [ "out" ];
|
||||
buildCommand = "";
|
||||
};
|
||||
};
|
||||
}
|
||||
EOF
|
||||
|
||||
# Create fake nixpkgs flake.
|
||||
mkdir -p $TEST_HOME/nixpkgs
|
||||
cp ../config.nix ../shell.nix $TEST_HOME/nixpkgs
|
||||
cat <<EOF >$TEST_HOME/nixpkgs/flake.nix
|
||||
{
|
||||
outputs = {self}: {
|
||||
legacyPackages.$system.bashInteractive = (import ./shell.nix {}).bashInteractive;
|
||||
};
|
||||
}
|
||||
EOF
|
||||
|
||||
cd $TEST_HOME
|
||||
|
||||
# Test whether `nix develop` passes through environment variables.
|
||||
[[ "$(
|
||||
ENVVAR=a nix develop --no-write-lock-file .#hello <<EOF
|
||||
echo "\$ENVVAR"
|
||||
EOF
|
||||
)" = "a" ]]
|
||||
|
||||
# Test whether `nix develop --ignore-environment` does _not_ pass through environment variables.
|
||||
[[ -z "$(
|
||||
ENVVAR=a nix develop --ignore-environment --no-write-lock-file .#hello <<EOF
|
||||
echo "\$ENVVAR"
|
||||
EOF
|
||||
)" ]]
|
||||
|
||||
# Determine the bashInteractive executable.
|
||||
nix build --no-write-lock-file './nixpkgs#bashInteractive' --out-link ./bash-interactive
|
||||
BASH_INTERACTIVE_EXECUTABLE="$PWD/bash-interactive/bin/bash"
|
||||
|
||||
# Test whether `nix develop` sets `SHELL` to nixpkgs#bashInteractive shell.
|
||||
[[ "$(
|
||||
SHELL=custom nix develop --no-write-lock-file .#hello <<EOF
|
||||
echo "\$SHELL"
|
||||
EOF
|
||||
)" -ef "$BASH_INTERACTIVE_EXECUTABLE" ]]
|
||||
|
||||
# Test whether `nix develop` with ignore environment sets `SHELL` to nixpkgs#bashInteractive shell.
|
||||
[[ "$(
|
||||
SHELL=custom nix develop --ignore-environment --no-write-lock-file .#hello <<EOF
|
||||
echo "\$SHELL"
|
||||
EOF
|
||||
)" -ef "$BASH_INTERACTIVE_EXECUTABLE" ]]
|
||||
|
||||
clearStore
|
|
@ -1 +1 @@
|
|||
trace: [ <CODE> ]
|
||||
trace: [ «thunk» ]
|
||||
|
|
1
tests/functional/lang/eval-okay-repeated-empty-attrs.exp
Normal file
1
tests/functional/lang/eval-okay-repeated-empty-attrs.exp
Normal file
|
@ -0,0 +1 @@
|
|||
[ { } { } ]
|
2
tests/functional/lang/eval-okay-repeated-empty-attrs.nix
Normal file
2
tests/functional/lang/eval-okay-repeated-empty-attrs.nix
Normal file
|
@ -0,0 +1,2 @@
|
|||
# Tests that empty attribute sets are not printed as `«repeated»`.
|
||||
[ {} {} ]
|
1
tests/functional/lang/eval-okay-repeated-empty-list.exp
Normal file
1
tests/functional/lang/eval-okay-repeated-empty-list.exp
Normal file
|
@ -0,0 +1 @@
|
|||
[ [ ] [ ] ]
|
1
tests/functional/lang/eval-okay-repeated-empty-list.nix
Normal file
1
tests/functional/lang/eval-okay-repeated-empty-list.nix
Normal file
|
@ -0,0 +1 @@
|
|||
[ [] [] ]
|
1
tests/functional/lang/eval-okay-substring-context.exp
Normal file
1
tests/functional/lang/eval-okay-substring-context.exp
Normal file
|
@ -0,0 +1 @@
|
|||
"okay"
|
11
tests/functional/lang/eval-okay-substring-context.nix
Normal file
11
tests/functional/lang/eval-okay-substring-context.nix
Normal file
|
@ -0,0 +1,11 @@
|
|||
with builtins;
|
||||
|
||||
let
|
||||
|
||||
s = "${builtins.derivation { name = "test"; builder = "/bin/sh"; system = "x86_64-linux"; }}";
|
||||
|
||||
in
|
||||
|
||||
if getContext s == getContext "${substring 0 0 s + unsafeDiscardStringContext s}"
|
||||
then "okay"
|
||||
else throw "empty substring should preserve context"
|
|
@ -1 +1 @@
|
|||
"ooxfoobarybarzobaabbc"
|
||||
"ooxfoobarybarzobaabbc_bad"
|
||||
|
|
|
@ -19,3 +19,5 @@ substring 1 2 s
|
|||
+ substring 3 1 s
|
||||
+ "c"
|
||||
+ substring 5 10 "perl"
|
||||
+ "_"
|
||||
+ substring 3 (-1) "tebbad"
|
||||
|
|
|
@ -2,6 +2,7 @@ nix_tests = \
|
|||
test-infra.sh \
|
||||
init.sh \
|
||||
flakes/flakes.sh \
|
||||
flakes/develop.sh \
|
||||
flakes/run.sh \
|
||||
flakes/mercurial.sh \
|
||||
flakes/circular.sh \
|
||||
|
|
|
@ -49,7 +49,7 @@ cp ./config.nix $flake1Dir/
|
|||
nix-env -f ./user-envs.nix -i foo-1.0
|
||||
nix profile list | grep -A2 'Name:.*foo' | grep 'Store paths:.*foo-1.0'
|
||||
nix profile install $flake1Dir -L
|
||||
nix profile list | grep -A4 'Index:.*1' | grep 'Locked flake URL:.*narHash'
|
||||
nix profile list | grep -A4 'Name:.*flake1' | grep 'Locked flake URL:.*narHash'
|
||||
[[ $($TEST_HOME/.nix-profile/bin/hello) = "Hello World" ]]
|
||||
[ -e $TEST_HOME/.nix-profile/share/man ]
|
||||
(! [ -e $TEST_HOME/.nix-profile/include ])
|
||||
|
@ -58,9 +58,8 @@ nix profile history | grep "packages.$system.default: ∅ -> 1.0"
|
|||
nix profile diff-closures | grep 'env-manifest.nix: ε → ∅'
|
||||
|
||||
# Test XDG Base Directories support
|
||||
|
||||
export NIX_CONFIG="use-xdg-base-directories = true"
|
||||
nix profile remove 1
|
||||
nix profile remove flake1 2>&1 | grep 'removed 1 packages'
|
||||
nix profile install $flake1Dir
|
||||
[[ $($TEST_HOME/.local/state/nix/profile/bin/hello) = "Hello World" ]]
|
||||
unset NIX_CONFIG
|
||||
|
@ -68,7 +67,7 @@ unset NIX_CONFIG
|
|||
# Test upgrading a package.
|
||||
printf NixOS > $flake1Dir/who
|
||||
printf 2.0 > $flake1Dir/version
|
||||
nix profile upgrade 1
|
||||
nix profile upgrade flake1
|
||||
[[ $($TEST_HOME/.nix-profile/bin/hello) = "Hello NixOS" ]]
|
||||
nix profile history | grep "packages.$system.default: 1.0, 1.0-man -> 2.0, 2.0-man"
|
||||
|
||||
|
@ -81,7 +80,7 @@ nix profile rollback
|
|||
|
||||
# Test uninstall.
|
||||
[ -e $TEST_HOME/.nix-profile/bin/foo ]
|
||||
nix profile remove foo
|
||||
nix profile remove foo 2>&1 | grep 'removed 1 packages'
|
||||
(! [ -e $TEST_HOME/.nix-profile/bin/foo ])
|
||||
nix profile history | grep 'foo: 1.0 -> ∅'
|
||||
nix profile diff-closures | grep 'Version 3 -> 4'
|
||||
|
@ -89,7 +88,7 @@ nix profile diff-closures | grep 'Version 3 -> 4'
|
|||
# Test installing a non-flake package.
|
||||
nix profile install --file ./simple.nix ''
|
||||
[[ $(cat $TEST_HOME/.nix-profile/hello) = "Hello World!" ]]
|
||||
nix profile remove 1
|
||||
nix profile remove simple 2>&1 | grep 'removed 1 packages'
|
||||
nix profile install $(nix-build --no-out-link ./simple.nix)
|
||||
[[ $(cat $TEST_HOME/.nix-profile/hello) = "Hello World!" ]]
|
||||
|
||||
|
@ -97,8 +96,9 @@ nix profile install $(nix-build --no-out-link ./simple.nix)
|
|||
mkdir $TEST_ROOT/simple-too
|
||||
cp ./simple.nix ./config.nix simple.builder.sh $TEST_ROOT/simple-too
|
||||
nix profile install --file $TEST_ROOT/simple-too/simple.nix ''
|
||||
nix profile list | grep -A4 'Name:.*simple' | grep 'Name:.*simple1'
|
||||
nix profile remove simple1
|
||||
nix profile list | grep -A4 'Name:.*simple' | grep 'Name:.*simple-1'
|
||||
nix profile remove simple 2>&1 | grep 'removed 1 packages'
|
||||
nix profile remove simple-1 2>&1 | grep 'removed 1 packages'
|
||||
|
||||
# Test wipe-history.
|
||||
nix profile wipe-history
|
||||
|
@ -107,11 +107,11 @@ nix profile wipe-history
|
|||
# Test upgrade to CA package.
|
||||
printf true > $flake1Dir/ca.nix
|
||||
printf 3.0 > $flake1Dir/version
|
||||
nix profile upgrade 0
|
||||
nix profile upgrade flake1
|
||||
nix profile history | grep "packages.$system.default: 1.0, 1.0-man -> 3.0, 3.0-man"
|
||||
|
||||
# Test new install of CA package.
|
||||
nix profile remove flake1
|
||||
nix profile remove flake1 2>&1 | grep 'removed 1 packages'
|
||||
printf 4.0 > $flake1Dir/version
|
||||
printf Utrecht > $flake1Dir/who
|
||||
nix profile install $flake1Dir
|
||||
|
@ -132,14 +132,14 @@ nix profile upgrade flake1
|
|||
[ -e $TEST_HOME/.nix-profile/share/man ]
|
||||
[ -e $TEST_HOME/.nix-profile/include ]
|
||||
|
||||
nix profile remove flake1
|
||||
nix profile remove flake1 2>&1 | grep 'removed 1 packages'
|
||||
nix profile install "$flake1Dir^man"
|
||||
(! [ -e $TEST_HOME/.nix-profile/bin/hello ])
|
||||
[ -e $TEST_HOME/.nix-profile/share/man ]
|
||||
(! [ -e $TEST_HOME/.nix-profile/include ])
|
||||
|
||||
# test priority
|
||||
nix profile remove flake1
|
||||
nix profile remove flake1 2>&1 | grep 'removed 1 packages'
|
||||
|
||||
# Make another flake.
|
||||
flake2Dir=$TEST_ROOT/flake2
|
||||
|
@ -193,3 +193,12 @@ nix profile install $flake2Dir --priority 0
|
|||
clearProfiles
|
||||
nix profile install $(nix build $flake1Dir --no-link --print-out-paths)
|
||||
expect 1 nix profile install --impure --expr "(builtins.getFlake ''$flake2Dir'').packages.$system.default"
|
||||
|
||||
# Test upgrading from profile version 2.
|
||||
clearProfiles
|
||||
mkdir -p $TEST_ROOT/import-profile
|
||||
outPath=$(nix build --no-link --print-out-paths $flake1Dir/flake.nix^out)
|
||||
printf '{ "version": 2, "elements": [ { "active": true, "attrPath": "legacyPackages.x86_64-linux.hello", "originalUrl": "flake:nixpkgs", "outputs": null, "priority": 5, "storePaths": [ "%s" ], "url": "github:NixOS/nixpkgs/aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa" } ] }' "$outPath" > $TEST_ROOT/import-profile/manifest.json
|
||||
nix build --profile $TEST_HOME/.nix-profile $(nix store add-path $TEST_ROOT/import-profile) --no-link
|
||||
nix profile list | grep -A4 'Name:.*hello' | grep "Store paths:.*$outPath"
|
||||
nix profile remove hello 2>&1 | grep 'removed 1 packages, kept 0 packages'
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
#include "tests/libexpr.hh"
|
||||
|
||||
#include "value.hh"
|
||||
#include "print.hh"
|
||||
|
||||
namespace nix {
|
||||
|
||||
|
@ -12,7 +13,7 @@ struct ValuePrintingTests : LibExprTest
|
|||
void test(Value v, std::string_view expected, A... args)
|
||||
{
|
||||
std::stringstream out;
|
||||
v.print(state.symbols, out, args...);
|
||||
v.print(state, out, args...);
|
||||
ASSERT_EQ(out.str(), expected);
|
||||
}
|
||||
};
|
||||
|
@ -84,7 +85,7 @@ TEST_F(ValuePrintingTests, tList)
|
|||
vList.bigList.elems[1] = &vTwo;
|
||||
vList.bigList.size = 3;
|
||||
|
||||
test(vList, "[ 1 2 (nullptr) ]");
|
||||
test(vList, "[ 1 2 «nullptr» ]");
|
||||
}
|
||||
|
||||
TEST_F(ValuePrintingTests, vThunk)
|
||||
|
@ -92,7 +93,7 @@ TEST_F(ValuePrintingTests, vThunk)
|
|||
Value vThunk;
|
||||
vThunk.mkThunk(nullptr, nullptr);
|
||||
|
||||
test(vThunk, "<CODE>");
|
||||
test(vThunk, "«thunk»");
|
||||
}
|
||||
|
||||
TEST_F(ValuePrintingTests, vApp)
|
||||
|
@ -100,32 +101,55 @@ TEST_F(ValuePrintingTests, vApp)
|
|||
Value vApp;
|
||||
vApp.mkApp(nullptr, nullptr);
|
||||
|
||||
test(vApp, "<CODE>");
|
||||
test(vApp, "«thunk»");
|
||||
}
|
||||
|
||||
TEST_F(ValuePrintingTests, vLambda)
|
||||
{
|
||||
Value vLambda;
|
||||
vLambda.mkLambda(nullptr, nullptr);
|
||||
Env env {
|
||||
.up = nullptr,
|
||||
.values = { }
|
||||
};
|
||||
PosTable::Origin origin((std::monostate()));
|
||||
auto posIdx = state.positions.add(origin, 1, 1);
|
||||
auto body = ExprInt(0);
|
||||
auto formals = Formals {};
|
||||
|
||||
test(vLambda, "<LAMBDA>");
|
||||
ExprLambda eLambda(posIdx, createSymbol("a"), &formals, &body);
|
||||
|
||||
Value vLambda;
|
||||
vLambda.mkLambda(&env, &eLambda);
|
||||
|
||||
test(vLambda, "«lambda @ «none»:1:1»");
|
||||
|
||||
eLambda.setName(createSymbol("puppy"));
|
||||
|
||||
test(vLambda, "«lambda puppy @ «none»:1:1»");
|
||||
}
|
||||
|
||||
TEST_F(ValuePrintingTests, vPrimOp)
|
||||
{
|
||||
Value vPrimOp;
|
||||
PrimOp primOp{};
|
||||
PrimOp primOp{
|
||||
.name = "puppy"
|
||||
};
|
||||
vPrimOp.mkPrimOp(&primOp);
|
||||
|
||||
test(vPrimOp, "<PRIMOP>");
|
||||
test(vPrimOp, "«primop puppy»");
|
||||
}
|
||||
|
||||
TEST_F(ValuePrintingTests, vPrimOpApp)
|
||||
{
|
||||
Value vPrimOpApp;
|
||||
vPrimOpApp.mkPrimOpApp(nullptr, nullptr);
|
||||
PrimOp primOp{
|
||||
.name = "puppy"
|
||||
};
|
||||
Value vPrimOp;
|
||||
vPrimOp.mkPrimOp(&primOp);
|
||||
|
||||
test(vPrimOpApp, "<PRIMOP-APP>");
|
||||
Value vPrimOpApp;
|
||||
vPrimOpApp.mkPrimOpApp(&vPrimOp, nullptr);
|
||||
|
||||
test(vPrimOpApp, "«partially applied primop puppy»");
|
||||
}
|
||||
|
||||
TEST_F(ValuePrintingTests, vExternal)
|
||||
|
@ -176,9 +200,14 @@ TEST_F(ValuePrintingTests, depthAttrs)
|
|||
Value vTwo;
|
||||
vTwo.mkInt(2);
|
||||
|
||||
BindingsBuilder builderEmpty(state, state.allocBindings(0));
|
||||
Value vAttrsEmpty;
|
||||
vAttrsEmpty.mkAttrs(builderEmpty.finish());
|
||||
|
||||
BindingsBuilder builder(state, state.allocBindings(10));
|
||||
builder.insert(state.symbols.create("one"), &vOne);
|
||||
builder.insert(state.symbols.create("two"), &vTwo);
|
||||
builder.insert(state.symbols.create("nested"), &vAttrsEmpty);
|
||||
|
||||
Value vAttrs;
|
||||
vAttrs.mkAttrs(builder.finish());
|
||||
|
@ -191,10 +220,10 @@ TEST_F(ValuePrintingTests, depthAttrs)
|
|||
Value vNested;
|
||||
vNested.mkAttrs(builder2.finish());
|
||||
|
||||
test(vNested, "{ nested = «too deep»; one = «too deep»; two = «too deep»; }", false, 1);
|
||||
test(vNested, "{ nested = { one = «too deep»; two = «too deep»; }; one = 1; two = 2; }", false, 2);
|
||||
test(vNested, "{ nested = { one = 1; two = 2; }; one = 1; two = 2; }", false, 3);
|
||||
test(vNested, "{ nested = { one = 1; two = 2; }; one = 1; two = 2; }", false, 4);
|
||||
test(vNested, "{ nested = { ... }; one = 1; two = 2; }", PrintOptions { .maxDepth = 1 });
|
||||
test(vNested, "{ nested = { nested = { ... }; one = 1; two = 2; }; one = 1; two = 2; }", PrintOptions { .maxDepth = 2 });
|
||||
test(vNested, "{ nested = { nested = { }; one = 1; two = 2; }; one = 1; two = 2; }", PrintOptions { .maxDepth = 3 });
|
||||
test(vNested, "{ nested = { nested = { }; one = 1; two = 2; }; one = 1; two = 2; }", PrintOptions { .maxDepth = 4 });
|
||||
}
|
||||
|
||||
TEST_F(ValuePrintingTests, depthList)
|
||||
|
@ -227,11 +256,561 @@ TEST_F(ValuePrintingTests, depthList)
|
|||
vList.bigList.elems[2] = &vNested;
|
||||
vList.bigList.size = 3;
|
||||
|
||||
test(vList, "[ «too deep» «too deep» «too deep» ]", false, 1);
|
||||
test(vList, "[ 1 2 { nested = «too deep»; one = «too deep»; two = «too deep»; } ]", false, 2);
|
||||
test(vList, "[ 1 2 { nested = { one = «too deep»; two = «too deep»; }; one = 1; two = 2; } ]", false, 3);
|
||||
test(vList, "[ 1 2 { nested = { one = 1; two = 2; }; one = 1; two = 2; } ]", false, 4);
|
||||
test(vList, "[ 1 2 { nested = { one = 1; two = 2; }; one = 1; two = 2; } ]", false, 5);
|
||||
test(vList, "[ 1 2 { ... } ]", PrintOptions { .maxDepth = 1 });
|
||||
test(vList, "[ 1 2 { nested = { ... }; one = 1; two = 2; } ]", PrintOptions { .maxDepth = 2 });
|
||||
test(vList, "[ 1 2 { nested = { one = 1; two = 2; }; one = 1; two = 2; } ]", PrintOptions { .maxDepth = 3 });
|
||||
test(vList, "[ 1 2 { nested = { one = 1; two = 2; }; one = 1; two = 2; } ]", PrintOptions { .maxDepth = 4 });
|
||||
test(vList, "[ 1 2 { nested = { one = 1; two = 2; }; one = 1; two = 2; } ]", PrintOptions { .maxDepth = 5 });
|
||||
}
|
||||
|
||||
struct StringPrintingTests : LibExprTest
|
||||
{
|
||||
template<class... A>
|
||||
void test(std::string_view literal, std::string_view expected, unsigned int maxLength, A... args)
|
||||
{
|
||||
Value v;
|
||||
v.mkString(literal);
|
||||
|
||||
std::stringstream out;
|
||||
printValue(state, out, v, PrintOptions {
|
||||
.maxStringLength = maxLength
|
||||
});
|
||||
ASSERT_EQ(out.str(), expected);
|
||||
}
|
||||
};
|
||||
|
||||
TEST_F(StringPrintingTests, maxLengthTruncation)
|
||||
{
|
||||
test("abcdefghi", "\"abcdefghi\"", 10);
|
||||
test("abcdefghij", "\"abcdefghij\"", 10);
|
||||
test("abcdefghijk", "\"abcdefghij\" «1 byte elided»", 10);
|
||||
test("abcdefghijkl", "\"abcdefghij\" «2 bytes elided»", 10);
|
||||
test("abcdefghijklm", "\"abcdefghij\" «3 bytes elided»", 10);
|
||||
}
|
||||
|
||||
// Check that printing an attrset shows 'important' attributes like `type`
|
||||
// first, but only reorder the attrs when we have a maxAttrs budget.
|
||||
TEST_F(ValuePrintingTests, attrsTypeFirst)
|
||||
{
|
||||
Value vType;
|
||||
vType.mkString("puppy");
|
||||
|
||||
Value vApple;
|
||||
vApple.mkString("apple");
|
||||
|
||||
BindingsBuilder builder(state, state.allocBindings(10));
|
||||
builder.insert(state.symbols.create("type"), &vType);
|
||||
builder.insert(state.symbols.create("apple"), &vApple);
|
||||
|
||||
Value vAttrs;
|
||||
vAttrs.mkAttrs(builder.finish());
|
||||
|
||||
test(vAttrs,
|
||||
"{ type = \"puppy\"; apple = \"apple\"; }",
|
||||
PrintOptions {
|
||||
.maxAttrs = 100
|
||||
});
|
||||
|
||||
test(vAttrs,
|
||||
"{ apple = \"apple\"; type = \"puppy\"; }",
|
||||
PrintOptions { });
|
||||
}
|
||||
|
||||
TEST_F(ValuePrintingTests, ansiColorsInt)
|
||||
{
|
||||
Value v;
|
||||
v.mkInt(10);
|
||||
|
||||
test(v,
|
||||
ANSI_CYAN "10" ANSI_NORMAL,
|
||||
PrintOptions {
|
||||
.ansiColors = true
|
||||
});
|
||||
}
|
||||
|
||||
TEST_F(ValuePrintingTests, ansiColorsFloat)
|
||||
{
|
||||
Value v;
|
||||
v.mkFloat(1.6);
|
||||
|
||||
test(v,
|
||||
ANSI_CYAN "1.6" ANSI_NORMAL,
|
||||
PrintOptions {
|
||||
.ansiColors = true
|
||||
});
|
||||
}
|
||||
|
||||
TEST_F(ValuePrintingTests, ansiColorsBool)
|
||||
{
|
||||
Value v;
|
||||
v.mkBool(true);
|
||||
|
||||
test(v,
|
||||
ANSI_CYAN "true" ANSI_NORMAL,
|
||||
PrintOptions {
|
||||
.ansiColors = true
|
||||
});
|
||||
}
|
||||
|
||||
TEST_F(ValuePrintingTests, ansiColorsString)
|
||||
{
|
||||
Value v;
|
||||
v.mkString("puppy");
|
||||
|
||||
test(v,
|
||||
ANSI_MAGENTA "\"puppy\"" ANSI_NORMAL,
|
||||
PrintOptions {
|
||||
.ansiColors = true
|
||||
});
|
||||
}
|
||||
|
||||
TEST_F(ValuePrintingTests, ansiColorsStringElided)
|
||||
{
|
||||
Value v;
|
||||
v.mkString("puppy");
|
||||
|
||||
test(v,
|
||||
ANSI_MAGENTA "\"pup\"" ANSI_FAINT " «2 bytes elided»" ANSI_NORMAL,
|
||||
PrintOptions {
|
||||
.ansiColors = true,
|
||||
.maxStringLength = 3
|
||||
});
|
||||
}
|
||||
|
||||
TEST_F(ValuePrintingTests, ansiColorsPath)
|
||||
{
|
||||
Value v;
|
||||
v.mkPath(state.rootPath(CanonPath("puppy")));
|
||||
|
||||
test(v,
|
||||
ANSI_GREEN "/puppy" ANSI_NORMAL,
|
||||
PrintOptions {
|
||||
.ansiColors = true
|
||||
});
|
||||
}
|
||||
|
||||
TEST_F(ValuePrintingTests, ansiColorsNull)
|
||||
{
|
||||
Value v;
|
||||
v.mkNull();
|
||||
|
||||
test(v,
|
||||
ANSI_CYAN "null" ANSI_NORMAL,
|
||||
PrintOptions {
|
||||
.ansiColors = true
|
||||
});
|
||||
}
|
||||
|
||||
TEST_F(ValuePrintingTests, ansiColorsAttrs)
|
||||
{
|
||||
Value vOne;
|
||||
vOne.mkInt(1);
|
||||
|
||||
Value vTwo;
|
||||
vTwo.mkInt(2);
|
||||
|
||||
BindingsBuilder builder(state, state.allocBindings(10));
|
||||
builder.insert(state.symbols.create("one"), &vOne);
|
||||
builder.insert(state.symbols.create("two"), &vTwo);
|
||||
|
||||
Value vAttrs;
|
||||
vAttrs.mkAttrs(builder.finish());
|
||||
|
||||
test(vAttrs,
|
||||
"{ one = " ANSI_CYAN "1" ANSI_NORMAL "; two = " ANSI_CYAN "2" ANSI_NORMAL "; }",
|
||||
PrintOptions {
|
||||
.ansiColors = true
|
||||
});
|
||||
}
|
||||
|
||||
TEST_F(ValuePrintingTests, ansiColorsDerivation)
|
||||
{
|
||||
Value vDerivation;
|
||||
vDerivation.mkString("derivation");
|
||||
|
||||
BindingsBuilder builder(state, state.allocBindings(10));
|
||||
builder.insert(state.sType, &vDerivation);
|
||||
|
||||
Value vAttrs;
|
||||
vAttrs.mkAttrs(builder.finish());
|
||||
|
||||
test(vAttrs,
|
||||
ANSI_GREEN "«derivation»" ANSI_NORMAL,
|
||||
PrintOptions {
|
||||
.ansiColors = true,
|
||||
.force = true,
|
||||
.derivationPaths = true
|
||||
});
|
||||
|
||||
test(vAttrs,
|
||||
"{ type = " ANSI_MAGENTA "\"derivation\"" ANSI_NORMAL "; }",
|
||||
PrintOptions {
|
||||
.ansiColors = true,
|
||||
.force = true
|
||||
});
|
||||
}
|
||||
|
||||
TEST_F(ValuePrintingTests, ansiColorsError)
|
||||
{
|
||||
Value throw_ = state.getBuiltin("throw");
|
||||
Value message;
|
||||
message.mkString("uh oh!");
|
||||
Value vError;
|
||||
vError.mkApp(&throw_, &message);
|
||||
|
||||
test(vError,
|
||||
ANSI_RED
|
||||
"«"
|
||||
ANSI_RED
|
||||
"error:"
|
||||
ANSI_NORMAL
|
||||
"\n … while calling the '"
|
||||
ANSI_MAGENTA
|
||||
"throw"
|
||||
ANSI_NORMAL
|
||||
"' builtin\n\n "
|
||||
ANSI_RED
|
||||
"error:"
|
||||
ANSI_NORMAL
|
||||
" uh oh!»"
|
||||
ANSI_NORMAL,
|
||||
PrintOptions {
|
||||
.ansiColors = true,
|
||||
.force = true,
|
||||
});
|
||||
}
|
||||
|
||||
TEST_F(ValuePrintingTests, ansiColorsDerivationError)
|
||||
{
|
||||
Value throw_ = state.getBuiltin("throw");
|
||||
Value message;
|
||||
message.mkString("uh oh!");
|
||||
Value vError;
|
||||
vError.mkApp(&throw_, &message);
|
||||
|
||||
Value vDerivation;
|
||||
vDerivation.mkString("derivation");
|
||||
|
||||
BindingsBuilder builder(state, state.allocBindings(10));
|
||||
builder.insert(state.sType, &vDerivation);
|
||||
builder.insert(state.sDrvPath, &vError);
|
||||
|
||||
Value vAttrs;
|
||||
vAttrs.mkAttrs(builder.finish());
|
||||
|
||||
test(vAttrs,
|
||||
"{ drvPath = "
|
||||
ANSI_RED
|
||||
"«"
|
||||
ANSI_RED
|
||||
"error:"
|
||||
ANSI_NORMAL
|
||||
"\n … while calling the '"
|
||||
ANSI_MAGENTA
|
||||
"throw"
|
||||
ANSI_NORMAL
|
||||
"' builtin\n\n "
|
||||
ANSI_RED
|
||||
"error:"
|
||||
ANSI_NORMAL
|
||||
" uh oh!»"
|
||||
ANSI_NORMAL
|
||||
"; type = "
|
||||
ANSI_MAGENTA
|
||||
"\"derivation\""
|
||||
ANSI_NORMAL
|
||||
"; }",
|
||||
PrintOptions {
|
||||
.ansiColors = true,
|
||||
.force = true
|
||||
});
|
||||
|
||||
test(vAttrs,
|
||||
ANSI_RED
|
||||
"«"
|
||||
ANSI_RED
|
||||
"error:"
|
||||
ANSI_NORMAL
|
||||
"\n … while calling the '"
|
||||
ANSI_MAGENTA
|
||||
"throw"
|
||||
ANSI_NORMAL
|
||||
"' builtin\n\n "
|
||||
ANSI_RED
|
||||
"error:"
|
||||
ANSI_NORMAL
|
||||
" uh oh!»"
|
||||
ANSI_NORMAL,
|
||||
PrintOptions {
|
||||
.ansiColors = true,
|
||||
.force = true,
|
||||
.derivationPaths = true,
|
||||
});
|
||||
}
|
||||
|
||||
TEST_F(ValuePrintingTests, ansiColorsAssert)
|
||||
{
|
||||
ExprVar eFalse(state.symbols.create("false"));
|
||||
eFalse.bindVars(state, state.staticBaseEnv);
|
||||
ExprInt eInt(1);
|
||||
|
||||
ExprAssert expr(noPos, &eFalse, &eInt);
|
||||
|
||||
Value v;
|
||||
state.mkThunk_(v, &expr);
|
||||
|
||||
test(v,
|
||||
ANSI_RED "«" ANSI_RED "error:" ANSI_NORMAL " assertion '" ANSI_MAGENTA "false" ANSI_NORMAL "' failed»" ANSI_NORMAL,
|
||||
PrintOptions {
|
||||
.ansiColors = true,
|
||||
.force = true
|
||||
});
|
||||
}
|
||||
|
||||
TEST_F(ValuePrintingTests, ansiColorsList)
|
||||
{
|
||||
Value vOne;
|
||||
vOne.mkInt(1);
|
||||
|
||||
Value vTwo;
|
||||
vTwo.mkInt(2);
|
||||
|
||||
Value vList;
|
||||
state.mkList(vList, 5);
|
||||
vList.bigList.elems[0] = &vOne;
|
||||
vList.bigList.elems[1] = &vTwo;
|
||||
vList.bigList.size = 3;
|
||||
|
||||
test(vList,
|
||||
"[ " ANSI_CYAN "1" ANSI_NORMAL " " ANSI_CYAN "2" ANSI_NORMAL " " ANSI_MAGENTA "«nullptr»" ANSI_NORMAL " ]",
|
||||
PrintOptions {
|
||||
.ansiColors = true
|
||||
});
|
||||
}
|
||||
|
||||
TEST_F(ValuePrintingTests, ansiColorsLambda)
|
||||
{
|
||||
Env env {
|
||||
.up = nullptr,
|
||||
.values = { }
|
||||
};
|
||||
PosTable::Origin origin((std::monostate()));
|
||||
auto posIdx = state.positions.add(origin, 1, 1);
|
||||
auto body = ExprInt(0);
|
||||
auto formals = Formals {};
|
||||
|
||||
ExprLambda eLambda(posIdx, createSymbol("a"), &formals, &body);
|
||||
|
||||
Value vLambda;
|
||||
vLambda.mkLambda(&env, &eLambda);
|
||||
|
||||
test(vLambda,
|
||||
ANSI_BLUE "«lambda @ «none»:1:1»" ANSI_NORMAL,
|
||||
PrintOptions {
|
||||
.ansiColors = true,
|
||||
.force = true
|
||||
});
|
||||
|
||||
eLambda.setName(createSymbol("puppy"));
|
||||
|
||||
test(vLambda,
|
||||
ANSI_BLUE "«lambda puppy @ «none»:1:1»" ANSI_NORMAL,
|
||||
PrintOptions {
|
||||
.ansiColors = true,
|
||||
.force = true
|
||||
});
|
||||
}
|
||||
|
||||
TEST_F(ValuePrintingTests, ansiColorsPrimOp)
|
||||
{
|
||||
PrimOp primOp{
|
||||
.name = "puppy"
|
||||
};
|
||||
Value v;
|
||||
v.mkPrimOp(&primOp);
|
||||
|
||||
test(v,
|
||||
ANSI_BLUE "«primop puppy»" ANSI_NORMAL,
|
||||
PrintOptions {
|
||||
.ansiColors = true
|
||||
});
|
||||
}
|
||||
|
||||
TEST_F(ValuePrintingTests, ansiColorsPrimOpApp)
|
||||
{
|
||||
PrimOp primOp{
|
||||
.name = "puppy"
|
||||
};
|
||||
Value vPrimOp;
|
||||
vPrimOp.mkPrimOp(&primOp);
|
||||
|
||||
Value v;
|
||||
v.mkPrimOpApp(&vPrimOp, nullptr);
|
||||
|
||||
test(v,
|
||||
ANSI_BLUE "«partially applied primop puppy»" ANSI_NORMAL,
|
||||
PrintOptions {
|
||||
.ansiColors = true
|
||||
});
|
||||
}
|
||||
|
||||
TEST_F(ValuePrintingTests, ansiColorsThunk)
|
||||
{
|
||||
Value v;
|
||||
v.mkThunk(nullptr, nullptr);
|
||||
|
||||
test(v,
|
||||
ANSI_MAGENTA "«thunk»" ANSI_NORMAL,
|
||||
PrintOptions {
|
||||
.ansiColors = true
|
||||
});
|
||||
}
|
||||
|
||||
TEST_F(ValuePrintingTests, ansiColorsBlackhole)
|
||||
{
|
||||
Value v;
|
||||
v.mkBlackhole();
|
||||
|
||||
test(v,
|
||||
ANSI_RED "«potential infinite recursion»" ANSI_NORMAL,
|
||||
PrintOptions {
|
||||
.ansiColors = true
|
||||
});
|
||||
}
|
||||
|
||||
TEST_F(ValuePrintingTests, ansiColorsAttrsRepeated)
|
||||
{
|
||||
BindingsBuilder emptyBuilder(state, state.allocBindings(1));
|
||||
|
||||
Value vEmpty;
|
||||
vEmpty.mkAttrs(emptyBuilder.finish());
|
||||
|
||||
BindingsBuilder builder(state, state.allocBindings(10));
|
||||
builder.insert(state.symbols.create("a"), &vEmpty);
|
||||
builder.insert(state.symbols.create("b"), &vEmpty);
|
||||
|
||||
Value vAttrs;
|
||||
vAttrs.mkAttrs(builder.finish());
|
||||
|
||||
test(vAttrs,
|
||||
"{ a = { }; b = " ANSI_MAGENTA "«repeated»" ANSI_NORMAL "; }",
|
||||
PrintOptions {
|
||||
.ansiColors = true
|
||||
});
|
||||
}
|
||||
|
||||
TEST_F(ValuePrintingTests, ansiColorsListRepeated)
|
||||
{
|
||||
BindingsBuilder emptyBuilder(state, state.allocBindings(1));
|
||||
|
||||
Value vEmpty;
|
||||
vEmpty.mkAttrs(emptyBuilder.finish());
|
||||
|
||||
Value vList;
|
||||
state.mkList(vList, 3);
|
||||
vList.bigList.elems[0] = &vEmpty;
|
||||
vList.bigList.elems[1] = &vEmpty;
|
||||
vList.bigList.size = 2;
|
||||
|
||||
test(vList,
|
||||
"[ { } " ANSI_MAGENTA "«repeated»" ANSI_NORMAL " ]",
|
||||
PrintOptions {
|
||||
.ansiColors = true
|
||||
});
|
||||
}
|
||||
|
||||
TEST_F(ValuePrintingTests, listRepeated)
|
||||
{
|
||||
BindingsBuilder emptyBuilder(state, state.allocBindings(1));
|
||||
|
||||
Value vEmpty;
|
||||
vEmpty.mkAttrs(emptyBuilder.finish());
|
||||
|
||||
Value vList;
|
||||
state.mkList(vList, 3);
|
||||
vList.bigList.elems[0] = &vEmpty;
|
||||
vList.bigList.elems[1] = &vEmpty;
|
||||
vList.bigList.size = 2;
|
||||
|
||||
test(vList, "[ { } «repeated» ]", PrintOptions { });
|
||||
test(vList,
|
||||
"[ { } { } ]",
|
||||
PrintOptions {
|
||||
.trackRepeated = false
|
||||
});
|
||||
}
|
||||
|
||||
TEST_F(ValuePrintingTests, ansiColorsAttrsElided)
|
||||
{
|
||||
Value vOne;
|
||||
vOne.mkInt(1);
|
||||
|
||||
Value vTwo;
|
||||
vTwo.mkInt(2);
|
||||
|
||||
BindingsBuilder builder(state, state.allocBindings(10));
|
||||
builder.insert(state.symbols.create("one"), &vOne);
|
||||
builder.insert(state.symbols.create("two"), &vTwo);
|
||||
|
||||
Value vAttrs;
|
||||
vAttrs.mkAttrs(builder.finish());
|
||||
|
||||
test(vAttrs,
|
||||
"{ one = " ANSI_CYAN "1" ANSI_NORMAL "; " ANSI_FAINT " «1 attribute elided»" ANSI_NORMAL "}",
|
||||
PrintOptions {
|
||||
.ansiColors = true,
|
||||
.maxAttrs = 1
|
||||
});
|
||||
|
||||
Value vThree;
|
||||
vThree.mkInt(3);
|
||||
|
||||
builder.insert(state.symbols.create("three"), &vThree);
|
||||
vAttrs.mkAttrs(builder.finish());
|
||||
|
||||
test(vAttrs,
|
||||
"{ one = " ANSI_CYAN "1" ANSI_NORMAL "; " ANSI_FAINT " «2 attributes elided»" ANSI_NORMAL "}",
|
||||
PrintOptions {
|
||||
.ansiColors = true,
|
||||
.maxAttrs = 1
|
||||
});
|
||||
}
|
||||
|
||||
TEST_F(ValuePrintingTests, ansiColorsListElided)
|
||||
{
|
||||
BindingsBuilder emptyBuilder(state, state.allocBindings(1));
|
||||
|
||||
Value vOne;
|
||||
vOne.mkInt(1);
|
||||
|
||||
Value vTwo;
|
||||
vTwo.mkInt(2);
|
||||
|
||||
Value vList;
|
||||
state.mkList(vList, 4);
|
||||
vList.bigList.elems[0] = &vOne;
|
||||
vList.bigList.elems[1] = &vTwo;
|
||||
vList.bigList.size = 2;
|
||||
|
||||
test(vList,
|
||||
"[ " ANSI_CYAN "1" ANSI_NORMAL " " ANSI_FAINT " «1 item elided»" ANSI_NORMAL "]",
|
||||
PrintOptions {
|
||||
.ansiColors = true,
|
||||
.maxListItems = 1
|
||||
});
|
||||
|
||||
Value vThree;
|
||||
vThree.mkInt(3);
|
||||
|
||||
vList.bigList.elems[2] = &vThree;
|
||||
vList.bigList.size = 3;
|
||||
|
||||
test(vList,
|
||||
"[ " ANSI_CYAN "1" ANSI_NORMAL " " ANSI_FAINT " «2 items elided»" ANSI_NORMAL "]",
|
||||
PrintOptions {
|
||||
.ansiColors = true,
|
||||
.maxListItems = 1
|
||||
});
|
||||
}
|
||||
|
||||
} // namespace nix
|
||||
|
|
|
@ -73,7 +73,7 @@ namespace nix {
|
|||
|
||||
}
|
||||
|
||||
TEST(logEI, picksUpSysErrorExitCode) {
|
||||
TEST(logEI, picksUpSystemErrorExitCode) {
|
||||
|
||||
MakeError(TestError, Error);
|
||||
ErrorInfo::programName = std::optional("error-unit-test");
|
||||
|
@ -81,12 +81,12 @@ namespace nix {
|
|||
try {
|
||||
auto x = readFile(-1);
|
||||
}
|
||||
catch (SysError &e) {
|
||||
catch (SystemError &e) {
|
||||
testing::internal::CaptureStderr();
|
||||
logError(e.info());
|
||||
auto str = testing::internal::GetCapturedStderr();
|
||||
|
||||
ASSERT_STREQ(str.c_str(), "\x1B[31;1merror:\x1B[0m\x1B[34;1m --- SysError --- error-unit-test\x1B[0m\nstatting file: \x1B[33;1mBad file descriptor\x1B[0m\n");
|
||||
ASSERT_STREQ(str.c_str(), "\x1B[31;1merror:\x1B[0m\x1B[34;1m --- SystemError --- error-unit-test\x1B[0m\nstatting file: \x1B[33;1mBad file descriptor\x1B[0m\n");
|
||||
}
|
||||
}
|
||||
|
||||
|
|
Loading…
Reference in a new issue