mirror of
https://github.com/privatevoid-net/nix-super.git
synced 2024-11-28 16:46:16 +02:00
Merge remote-tracking branch 'nixos/master'
This commit is contained in:
commit
2d0b0537fe
278 changed files with 4665 additions and 1472 deletions
|
@ -28,3 +28,5 @@ EmptyLineBeforeAccessModifier: Leave
|
|||
#PackConstructorInitializers: BinPack
|
||||
BreakBeforeBinaryOperators: NonAssignment
|
||||
AlwaysBreakBeforeMultilineStrings: true
|
||||
IndentPPDirectives: AfterHash
|
||||
PPIndentWidth: 2
|
||||
|
|
13
.github/labeler.yml
vendored
13
.github/labeler.yml
vendored
|
@ -1,3 +1,16 @@
|
|||
"c api":
|
||||
- changed-files:
|
||||
- any-glob-to-any-file: "src/lib*-c/**/*"
|
||||
- any-glob-to-any-file: "test/unit/**/nix_api_*"
|
||||
- any-glob-to-any-file: "doc/external-api/**/*"
|
||||
|
||||
"contributor-experience":
|
||||
- changed-files:
|
||||
- any-glob-to-any-file: "CONTRIBUTING.md"
|
||||
- any-glob-to-any-file: ".github/ISSUE_TEMPLATE/*"
|
||||
- any-glob-to-any-file: ".github/PULL_REQUEST_TEMPLATE.md"
|
||||
- any-glob-to-any-file: "doc/manual/src/contributing/**"
|
||||
|
||||
"documentation":
|
||||
- changed-files:
|
||||
- any-glob-to-any-file: "doc/manual/*"
|
||||
|
|
2
.gitignore
vendored
2
.gitignore
vendored
|
@ -118,8 +118,6 @@ perl/Makefile.config
|
|||
/misc/systemd/nix-daemon.conf
|
||||
/misc/upstart/nix-daemon.conf
|
||||
|
||||
/src/resolve-system-dependencies/resolve-system-dependencies
|
||||
|
||||
outputs/
|
||||
|
||||
*.a
|
||||
|
|
14
Makefile
14
Makefile
|
@ -7,6 +7,8 @@ clean-files += $(buildprefix)Makefile.config
|
|||
|
||||
# List makefiles
|
||||
|
||||
include mk/platform.mk
|
||||
|
||||
ifeq ($(ENABLE_BUILD), yes)
|
||||
makefiles = \
|
||||
mk/precompiled-headers.mk \
|
||||
|
@ -20,8 +22,10 @@ makefiles = \
|
|||
src/nix/local.mk \
|
||||
src/libutil-c/local.mk \
|
||||
src/libstore-c/local.mk \
|
||||
src/libexpr-c/local.mk \
|
||||
src/resolve-system-dependencies/local.mk \
|
||||
src/libexpr-c/local.mk
|
||||
|
||||
ifdef HOST_UNIX
|
||||
makefiles += \
|
||||
scripts/local.mk \
|
||||
misc/bash/local.mk \
|
||||
misc/fish/local.mk \
|
||||
|
@ -30,6 +34,7 @@ makefiles = \
|
|||
misc/launchd/local.mk \
|
||||
misc/upstart/local.mk
|
||||
endif
|
||||
endif
|
||||
|
||||
ifeq ($(ENABLE_UNIT_TESTS), yes)
|
||||
makefiles += \
|
||||
|
@ -43,14 +48,17 @@ makefiles += \
|
|||
endif
|
||||
|
||||
ifeq ($(ENABLE_FUNCTIONAL_TESTS), yes)
|
||||
ifdef HOST_UNIX
|
||||
makefiles += \
|
||||
tests/functional/local.mk \
|
||||
tests/functional/ca/local.mk \
|
||||
tests/functional/git-hashing/local.mk \
|
||||
tests/functional/dyn-drv/local.mk \
|
||||
tests/functional/local-overlay-store/local.mk \
|
||||
tests/functional/test-libstoreconsumer/local.mk \
|
||||
tests/functional/plugins/local.mk
|
||||
endif
|
||||
endif
|
||||
|
||||
# Some makefiles require access to built programs and must be included late.
|
||||
makefiles-late =
|
||||
|
@ -79,8 +87,6 @@ else
|
|||
unexport NIX_HARDENING_ENABLE
|
||||
endif
|
||||
|
||||
include mk/platform.mk
|
||||
|
||||
ifdef HOST_WINDOWS
|
||||
# Windows DLLs are stricter about symbol visibility than Unix shared
|
||||
# objects --- see https://gcc.gnu.org/wiki/Visibility for details.
|
||||
|
|
|
@ -40,20 +40,33 @@ Nix expression `builtins.nixVersion`.
|
|||
#include <nix_api_expr.h>
|
||||
#include <nix_api_value.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
// NOTE: This example lacks all error handling. Production code must check for
|
||||
// errors, as some return values will be undefined.
|
||||
int main() {
|
||||
|
||||
void my_get_string_cb(const char * start, unsigned int n, char ** user_data)
|
||||
{
|
||||
*user_data = strdup(start);
|
||||
}
|
||||
|
||||
int main()
|
||||
{
|
||||
nix_libexpr_init(NULL);
|
||||
|
||||
Store* store = nix_store_open(NULL, "dummy://", NULL);
|
||||
EvalState* state = nix_state_create(NULL, NULL, store); // empty search path (NIX_PATH)
|
||||
Value *value = nix_alloc_value(NULL, state);
|
||||
Store * store = nix_store_open(NULL, "dummy://", NULL);
|
||||
EvalState * state = nix_state_create(NULL, NULL, store); // empty search path (NIX_PATH)
|
||||
Value * value = nix_alloc_value(NULL, state);
|
||||
|
||||
nix_expr_eval_from_string(NULL, state, "builtins.nixVersion", ".", value);
|
||||
nix_value_force(NULL, state, value);
|
||||
printf("Nix version: %s\n", nix_get_string(NULL, value));
|
||||
|
||||
char * version;
|
||||
nix_get_string(NULL, value, my_get_string_cb, version);
|
||||
printf("Nix version: %s\n", version);
|
||||
|
||||
free(version);
|
||||
nix_gc_decref(NULL, value);
|
||||
nix_state_free(state);
|
||||
nix_store_free(store);
|
||||
|
|
|
@ -110,6 +110,7 @@
|
|||
- [Derivation](protocols/json/derivation.md)
|
||||
- [Serving Tarball Flakes](protocols/tarball-fetcher.md)
|
||||
- [Store Path Specification](protocols/store-path.md)
|
||||
- [Nix Archive (NAR) Format](protocols/nix-archive.md)
|
||||
- [Derivation "ATerm" file format](protocols/derivation-aterm.md)
|
||||
- [Glossary](glossary.md)
|
||||
- [Contributing](contributing/index.md)
|
||||
|
|
|
@ -69,7 +69,7 @@ It can also execute build plans to produce new data, which are made available to
|
|||
A build plan itself is a series of *build tasks*, together with their build inputs.
|
||||
|
||||
> **Important**
|
||||
> A build task in Nix is called [derivation](../glossary.md#gloss-derivation).
|
||||
> A build task in Nix is called [derivation](@docroot@/glossary.md#gloss-derivation).
|
||||
|
||||
Each build task has a special build input executed as *build instructions* in order to perform the build.
|
||||
The result of a build task can be input to another build task.
|
||||
|
|
|
@ -41,7 +41,7 @@ expression to a low-level [store derivation]) and [`nix-store
|
|||
--realise`](@docroot@/command-ref/nix-store/realise.md) (to build the store
|
||||
derivation).
|
||||
|
||||
[store derivation]: ../glossary.md#gloss-store-derivation
|
||||
[store derivation]: @docroot@/glossary.md#gloss-store-derivation
|
||||
|
||||
> **Warning**
|
||||
>
|
||||
|
|
|
@ -49,7 +49,7 @@ authentication, you can avoid typing the passphrase with `ssh-agent`.
|
|||
- `--include-outputs`\
|
||||
Also copy the outputs of [store derivation]s included in the closure.
|
||||
|
||||
[store derivation]: ../glossary.md#gloss-store-derivation
|
||||
[store derivation]: @docroot@/glossary.md#gloss-store-derivation
|
||||
|
||||
- `--use-substitutes` / `-s`\
|
||||
Attempt to download missing paths on the target machine using Nix’s
|
||||
|
|
|
@ -23,7 +23,7 @@ It evaluates the Nix expressions in each of *files* (which defaults to
|
|||
derivation, a list of derivations, or a set of derivations. The paths
|
||||
of the resulting store derivations are printed on standard output.
|
||||
|
||||
[store derivation]: ../glossary.md#gloss-store-derivation
|
||||
[store derivation]: @docroot@/glossary.md#gloss-store-derivation
|
||||
|
||||
If *files* is the character `-`, then a Nix expression will be read from
|
||||
standard input.
|
||||
|
|
|
@ -40,12 +40,12 @@ symlink.
|
|||
derivations *paths*. These are the paths that will be produced when
|
||||
the derivation is built.
|
||||
|
||||
[output paths]: ../../glossary.md#gloss-output-path
|
||||
[output paths]: @docroot@/glossary.md#gloss-output-path
|
||||
|
||||
- `--requisites`; `-R`\
|
||||
Prints out the [closure] of the store path *paths*.
|
||||
|
||||
[closure]: ../../glossary.md#gloss-closure
|
||||
[closure]: @docroot@/glossary.md#gloss-closure
|
||||
|
||||
This query has one option:
|
||||
|
||||
|
@ -66,7 +66,7 @@ symlink.
|
|||
*paths*, that is, their immediate dependencies. (For *all*
|
||||
dependencies, use `--requisites`.)
|
||||
|
||||
[references]: ../../glossary.md#gloss-reference
|
||||
[references]: @docroot@/glossary.md#gloss-reference
|
||||
|
||||
- `--referrers`\
|
||||
Prints the set of *referrers* of the store paths *paths*, that is,
|
||||
|
@ -90,7 +90,7 @@ symlink.
|
|||
example when *paths* were substituted from a binary cache.
|
||||
Use `--valid-derivers` instead to obtain valid paths only.
|
||||
|
||||
[deriver]: ../../glossary.md#gloss-deriver
|
||||
[deriver]: @docroot@/glossary.md#gloss-deriver
|
||||
|
||||
- `--valid-derivers`\
|
||||
Prints a set of derivation files (`.drv`) which are supposed produce
|
||||
|
|
|
@ -206,3 +206,22 @@ or inside `nix-shell` or `nix develop`:
|
|||
# make internal-api-html
|
||||
# xdg-open ./outputs/doc/share/doc/nix/internal-api/html/index.html
|
||||
```
|
||||
|
||||
## C API documentation (experimental)
|
||||
|
||||
[C API documentation] is available online.
|
||||
You can also build and view it yourself:
|
||||
|
||||
[C API documentation]: https://hydra.nixos.org/job/nix/master/external-api-docs/latest/download-by-type/doc/external-api-docs
|
||||
|
||||
```console
|
||||
# nix build .#hydraJobs.external-api-docs
|
||||
# xdg-open ./result/share/doc/nix/external-api/html/index.html
|
||||
```
|
||||
|
||||
or inside `nix-shell` or `nix develop`:
|
||||
|
||||
```
|
||||
# make external-api-html
|
||||
# xdg-open ./outputs/doc/share/doc/nix/external-api/html/index.html
|
||||
```
|
||||
|
|
|
@ -144,6 +144,7 @@ Nix can be built for various platforms, as specified in [`flake.nix`]:
|
|||
- `aarch64-darwin`
|
||||
- `armv6l-linux`
|
||||
- `armv7l-linux`
|
||||
- `riscv64-linux`
|
||||
|
||||
In order to build Nix for a different platform than the one you're currently
|
||||
on, you need a way for your current Nix installation to build code for that
|
||||
|
@ -166,7 +167,10 @@ or for Nix with the [`flakes`] and [`nix-command`] experimental features enabled
|
|||
$ nix build .#packages.aarch64-linux.default
|
||||
```
|
||||
|
||||
Cross-compiled builds are available for ARMv6 (`armv6l-linux`) and ARMv7 (`armv7l-linux`).
|
||||
Cross-compiled builds are available for:
|
||||
- `armv6l-linux`
|
||||
- `armv7l-linux`
|
||||
- `riscv64-linux`
|
||||
Add more [system types](#system-type) to `crossSystems` in `flake.nix` to bootstrap Nix on unsupported platforms.
|
||||
|
||||
### Building for multiple platforms at once
|
||||
|
@ -196,7 +200,7 @@ In order to facilitate this, Nix has some support for being built out of tree
|
|||
|
||||
## System type
|
||||
|
||||
Nix uses a string with he following format to identify the *system type* or *platform* it runs on:
|
||||
Nix uses a string with the following format to identify the *system type* or *platform* it runs on:
|
||||
|
||||
```
|
||||
<cpu>-<os>[-<abi>]
|
||||
|
|
|
@ -215,6 +215,9 @@
|
|||
|
||||
[output path]: #gloss-output-path
|
||||
|
||||
- [output closure]{#gloss-output-closure}\
|
||||
The [closure] of an [output path]. It only contains what is [reachable] from the output.
|
||||
|
||||
- [deriver]{#gloss-deriver}
|
||||
|
||||
The [store derivation] that produced an [output path].
|
||||
|
|
|
@ -207,12 +207,17 @@ Derivations can declare some infrequently used optional attributes.
|
|||
|
||||
This is the default.
|
||||
|
||||
- `"recursive"`\
|
||||
The hash is computed over the NAR archive dump of the output
|
||||
- `"recursive"` or `"nar"`\
|
||||
The hash is computed over the [NAR archive](@docroot@/glossary.md#gloss-nar) dump of the output
|
||||
(i.e., the result of [`nix-store --dump`](@docroot@/command-ref/nix-store/dump.md)). In
|
||||
this case, the output can be anything, including a directory
|
||||
tree.
|
||||
|
||||
`"recursive"` is the traditional way of indicating this,
|
||||
and is supported since 2005 (virtually the entire history of Nix).
|
||||
`"nar"` is more clear, and consistent with other parts of Nix (such as the CLI),
|
||||
however support for it is only added in Nix version 2.21.
|
||||
|
||||
- [`__contentAddressed`]{#adv-attr-__contentAddressed}
|
||||
> **Warning**
|
||||
> This attribute is part of an [experimental feature](@docroot@/contributing/experimental-features.md).
|
||||
|
@ -298,7 +303,7 @@ Derivations can declare some infrequently used optional attributes.
|
|||
[`disallowedReferences`](#adv-attr-disallowedReferences) and [`disallowedRequisites`](#adv-attr-disallowedRequisites),
|
||||
the following attributes are available:
|
||||
|
||||
- `maxSize` defines the maximum size of the resulting [store object](../glossary.md#gloss-store-object).
|
||||
- `maxSize` defines the maximum size of the resulting [store object](@docroot@/glossary.md#gloss-store-object).
|
||||
- `maxClosureSize` defines the maximum size of the output's closure.
|
||||
- `ignoreSelfRefs` controls whether self-references should be considered when
|
||||
checking for allowed references/requisites.
|
||||
|
|
|
@ -128,8 +128,8 @@ The result is a string.
|
|||
> The file or directory at *path* must exist and is copied to the [store].
|
||||
> The path appears in the result as the corresponding [store path].
|
||||
|
||||
[store path]: ../glossary.md#gloss-store-path
|
||||
[store]: ../glossary.md#gloss-store
|
||||
[store path]: @docroot@/glossary.md#gloss-store-path
|
||||
[store]: @docroot@/glossary.md#gloss-store
|
||||
|
||||
[String and path concatenation]: #string-and-path-concatenation
|
||||
|
||||
|
|
|
@ -20,7 +20,7 @@ Rather than writing
|
|||
|
||||
(where `freetype` is a [derivation]), you can instead write
|
||||
|
||||
[derivation]: ../glossary.md#gloss-derivation
|
||||
[derivation]: @docroot@/glossary.md#gloss-derivation
|
||||
|
||||
```nix
|
||||
"--with-freetype2-library=${freetype}/lib"
|
||||
|
@ -107,9 +107,9 @@ An expression that is interpolated must evaluate to one of the following:
|
|||
|
||||
A string interpolates to itself.
|
||||
|
||||
A path in an interpolated expression is first copied into the Nix store, and the resulting string is the [store path] of the newly created [store object](../glossary.md#gloss-store-object).
|
||||
A path in an interpolated expression is first copied into the Nix store, and the resulting string is the [store path] of the newly created [store object](@docroot@/glossary.md#gloss-store-object).
|
||||
|
||||
[store path]: ../glossary.md#gloss-store-path
|
||||
[store path]: @docroot@/glossary.md#gloss-store-path
|
||||
|
||||
> **Example**
|
||||
>
|
||||
|
|
|
@ -113,7 +113,7 @@
|
|||
For example, assume you used a file path in an interpolated string during a `nix repl` session.
|
||||
Later in the same session, after having changed the file contents, evaluating the interpolated string with the file path again might not return a new [store path], since Nix might not re-read the file contents.
|
||||
|
||||
[store path]: ../glossary.md#gloss-store-path
|
||||
[store path]: @docroot@/glossary.md#gloss-store-path
|
||||
|
||||
Paths can include [string interpolation] and can themselves be [interpolated in other expressions].
|
||||
|
||||
|
|
|
@ -83,7 +83,7 @@ This information is not intrinsic to the store object, but about how it is store
|
|||
|
||||
## Computed closure fields
|
||||
|
||||
These fields are not stored at all, but computed by traverising the other other fields across all the store objects in a [closure].
|
||||
These fields are not stored at all, but computed by traversing the other fields across all the store objects in a [closure].
|
||||
|
||||
* `closureSize`:
|
||||
|
||||
|
|
42
doc/manual/src/protocols/nix-archive.md
Normal file
42
doc/manual/src/protocols/nix-archive.md
Normal file
|
@ -0,0 +1,42 @@
|
|||
# Nix Archive (NAR) format
|
||||
|
||||
This is the complete specification of the Nix Archive format.
|
||||
The Nix Archive format closely follows the abstract specification of a [file system object] tree,
|
||||
because it is designed to serialize exactly that data structure.
|
||||
|
||||
[file system object]: @docroot@/store/file-system-object.md
|
||||
|
||||
The format of this specification is close to [Extended Backus–Naur form](https://en.wikipedia.org/wiki/Extended_Backus%E2%80%93Naur_form), with the exception of the `str(..)` function / parameterized rule, which length-prefixes and pads strings.
|
||||
This makes the resulting binary format easier to parse.
|
||||
|
||||
Regular users do *not* need to know this information.
|
||||
But for those interested in exactly how Nix works, e.g. if they are reimplementing it, this information can be useful.
|
||||
|
||||
```ebnf
|
||||
nar = str("nix-archive-1"), nar-obj;
|
||||
|
||||
nar-obj = str("("), nar-obj-inner, str(")");
|
||||
|
||||
nar-obj-inner
|
||||
= str("type"), str("regular") regular
|
||||
| str("type"), str("symlink") symlink
|
||||
| str("type"), str("directory") directory
|
||||
;
|
||||
|
||||
regular = [ str("executable"), str("") ], str("contents"), str(contents);
|
||||
|
||||
symlink = str("target"), str(target);
|
||||
|
||||
(* side condition: directory entries must be ordered by their names *)
|
||||
directory = str("type"), str("directory") { directory-entry };
|
||||
|
||||
directory-entry = str("entry"), str("("), str("name"), str(name), str("node"), nar-obj, str(")");
|
||||
```
|
||||
|
||||
The `str` function / parameterized rule is defined as follows:
|
||||
|
||||
- `str(s)` = `int(|s|), pad(s);`
|
||||
|
||||
- `int(n)` = the 64-bit little endian representation of the number `n`
|
||||
|
||||
- `pad(s)` = the byte sequence `s`, padded with 0s to a multiple of 8 byte
|
|
@ -11,7 +11,7 @@
|
|||
As the choice of hash formats is no longer binary, the `--base16` flag is also added
|
||||
to explicitly specify the Base16 format, which is still the default.
|
||||
|
||||
* The special handling of an [installable](../command-ref/new-cli/nix.md#installables) with `.drv` suffix being interpreted as all of the given [store derivation](../glossary.md#gloss-store-derivation)'s output paths is removed, and instead taken as the literal store path that it represents.
|
||||
* The special handling of an [installable](../command-ref/new-cli/nix.md#installables) with `.drv` suffix being interpreted as all of the given [store derivation](@docroot@/glossary.md#gloss-store-derivation)'s output paths is removed, and instead taken as the literal store path that it represents.
|
||||
|
||||
The new `^` syntax for store paths introduced in Nix 2.13 allows explicitly referencing output paths of a derivation.
|
||||
Using this is better and more clear than relying on the now-removed `.drv` special handling.
|
||||
|
|
|
@ -200,3 +200,9 @@
|
|||
while performing various operations (including `nix develop`, `nix flake
|
||||
update`, and so on). With several fixes to Nix's signal handlers, Nix
|
||||
commands will now exit quickly after Ctrl-C is pressed.
|
||||
|
||||
- `nix copy` to a `ssh-ng` store now needs `--substitute-on-destination` (a.k.a. `-s`)
|
||||
in order to substitute paths on the remote store instead of copying them.
|
||||
The behavior is consistent with `nix copy` to a different kind of remote store.
|
||||
Previously this behavior was controlled by the
|
||||
`builders-use-substitutes` setting and `--substitute-on-destination` was ignored.
|
||||
|
|
|
@ -46,7 +46,7 @@ But if the store has a file system representation, the store directory contains
|
|||
|
||||
[file system objects]: ./file-system-object.md
|
||||
|
||||
This means a store path is not just derived from the referenced store object itself, but depends on the store the store object is in.
|
||||
This means a store path is not just derived from the referenced store object itself, but depends on the store that the store object is in.
|
||||
|
||||
> **Note**
|
||||
>
|
||||
|
|
15
flake.nix
15
flake.nix
|
@ -36,13 +36,8 @@
|
|||
crossSystems = [
|
||||
"armv6l-unknown-linux-gnueabihf"
|
||||
"armv7l-unknown-linux-gnueabihf"
|
||||
"riscv64-unknown-linux-gnu"
|
||||
"x86_64-unknown-netbsd"
|
||||
];
|
||||
|
||||
# Nix doesn't yet build on this platform, so we put it in a
|
||||
# separate list. We just use this for `devShells` and
|
||||
# `nixpkgsFor`, which this depends on.
|
||||
shellCrossSystems = crossSystems ++ [
|
||||
"x86_64-w64-mingw32"
|
||||
];
|
||||
|
||||
|
@ -88,7 +83,7 @@
|
|||
in {
|
||||
inherit stdenvs native;
|
||||
static = native.pkgsStatic;
|
||||
cross = lib.genAttrs shellCrossSystems (crossSystem: make-pkgs crossSystem "stdenv");
|
||||
cross = forAllCrossSystems (crossSystem: make-pkgs crossSystem "stdenv");
|
||||
});
|
||||
|
||||
installScriptFor = tarballs:
|
||||
|
@ -264,6 +259,7 @@
|
|||
# Cross
|
||||
self.hydraJobs.binaryTarballCross."x86_64-linux"."armv6l-unknown-linux-gnueabihf"
|
||||
self.hydraJobs.binaryTarballCross."x86_64-linux"."armv7l-unknown-linux-gnueabihf"
|
||||
self.hydraJobs.binaryTarballCross."x86_64-linux"."riscv64-unknown-linux-gnu"
|
||||
];
|
||||
installerScriptForGHA = installScriptFor [
|
||||
# Native
|
||||
|
@ -272,6 +268,7 @@
|
|||
# Cross
|
||||
self.hydraJobs.binaryTarballCross."x86_64-linux"."armv6l-unknown-linux-gnueabihf"
|
||||
self.hydraJobs.binaryTarballCross."x86_64-linux"."armv7l-unknown-linux-gnueabihf"
|
||||
self.hydraJobs.binaryTarballCross."x86_64-linux"."riscv64-unknown-linux-gnu"
|
||||
];
|
||||
|
||||
# docker image with Nix inside
|
||||
|
@ -431,8 +428,8 @@
|
|||
in
|
||||
(makeShells "native" nixpkgsFor.${system}.native) //
|
||||
(lib.optionalAttrs (!nixpkgsFor.${system}.native.stdenv.isDarwin)
|
||||
(makeShells "static" nixpkgsFor.${system}.static)) //
|
||||
(lib.genAttrs shellCrossSystems (crossSystem: let pkgs = nixpkgsFor.${system}.cross.${crossSystem}; in makeShell pkgs pkgs.stdenv)) //
|
||||
(makeShells "static" nixpkgsFor.${system}.static) //
|
||||
(forAllCrossSystems (crossSystem: let pkgs = nixpkgsFor.${system}.cross.${crossSystem}; in makeShell pkgs pkgs.stdenv))) //
|
||||
{
|
||||
default = self.devShells.${system}.native-stdenvPackages;
|
||||
}
|
||||
|
|
2
local.mk
2
local.mk
|
@ -2,7 +2,7 @@ GLOBAL_CXXFLAGS += -Wno-deprecated-declarations -Werror=switch
|
|||
# Allow switch-enum to be overridden for files that do not support it, usually because of dependency headers.
|
||||
ERROR_SWITCH_ENUM = -Werror=switch-enum
|
||||
|
||||
$(foreach i, config.h $(wildcard src/lib*/*.hh) $(wildcard src/lib*/*.h $(filter-out %_internal.h, $(wildcard src/lib*c/*.h))), \
|
||||
$(foreach i, config.h $(wildcard src/lib*/*.hh) $(filter-out %_internal.h, $(wildcard src/lib*c/*.h)), \
|
||||
$(eval $(call install-file-in, $(i), $(includedir)/nix, 0644)))
|
||||
|
||||
ifdef HOST_UNIX
|
||||
|
|
|
@ -46,11 +46,13 @@ AC_DEFUN([ENSURE_NO_GCC_BUG_80431],
|
|||
]])],
|
||||
[status_80431=0],
|
||||
[status_80431=$?],
|
||||
[
|
||||
# Assume we're bug-free when cross-compiling
|
||||
])
|
||||
[status_80431=''])
|
||||
AC_LANG_POP(C++)
|
||||
AS_CASE([$status_80431],
|
||||
[''],[
|
||||
AC_MSG_RESULT(cannot check because cross compiling)
|
||||
AC_MSG_NOTICE(assume we are bug free)
|
||||
],
|
||||
[0],[
|
||||
AC_MSG_RESULT(yes)
|
||||
],
|
||||
|
|
|
@ -171,6 +171,10 @@ eval {
|
|||
downloadFile("binaryTarballCross.x86_64-linux.armv7l-unknown-linux-gnueabihf", "1");
|
||||
};
|
||||
warn "$@" if $@;
|
||||
eval {
|
||||
downloadFile("binaryTarballCross.x86_64-linux.riscv64-unknown-linux-gnu", "1");
|
||||
};
|
||||
warn "$@" if $@;
|
||||
downloadFile("installerScript", "1");
|
||||
|
||||
# Upload docker images to dockerhub.
|
||||
|
|
|
@ -94,8 +94,8 @@
|
|||
|
||||
# Whether to build the internal/external API docs, can be done separately from
|
||||
# everything else.
|
||||
, enableInternalAPIDocs ? false
|
||||
, enableExternalAPIDocs ? false
|
||||
, enableInternalAPIDocs ? forDevShell
|
||||
, enableExternalAPIDocs ? forDevShell
|
||||
|
||||
# Whether to install unit tests. This is useful when cross compiling
|
||||
# since we cannot run them natively during the build, but can do so
|
||||
|
|
|
@ -42,19 +42,22 @@
|
|||
#include <dirent.h>
|
||||
#include <errno.h>
|
||||
#include <fcntl.h>
|
||||
#include <grp.h>
|
||||
#include <netdb.h>
|
||||
#include <pwd.h>
|
||||
#include <signal.h>
|
||||
#include <sys/resource.h>
|
||||
#include <sys/select.h>
|
||||
#include <sys/socket.h>
|
||||
#include <sys/stat.h>
|
||||
#include <sys/time.h>
|
||||
#include <sys/types.h>
|
||||
#include <sys/utsname.h>
|
||||
#include <sys/wait.h>
|
||||
#include <termios.h>
|
||||
#include <unistd.h>
|
||||
|
||||
#ifndef _WIN32
|
||||
# include <grp.h>
|
||||
# include <netdb.h>
|
||||
# include <pwd.h>
|
||||
# include <sys/resource.h>
|
||||
# include <sys/select.h>
|
||||
# include <sys/socket.h>
|
||||
# include <sys/utsname.h>
|
||||
# include <sys/wait.h>
|
||||
# include <termios.h>
|
||||
#endif
|
||||
|
||||
#include <nlohmann/json.hpp>
|
||||
|
|
|
@ -69,4 +69,4 @@ else
|
|||
fi
|
||||
|
||||
export PATH="$NIX_LINK/bin:@localstatedir@/nix/profiles/default/bin:$PATH"
|
||||
unset NIX_LINK
|
||||
unset NIX_LINK NIX_LINK_NEW
|
||||
|
|
|
@ -22,6 +22,7 @@
|
|||
#include "experimental-features.hh"
|
||||
|
||||
using namespace nix;
|
||||
using namespace nix::unix;
|
||||
using std::cin;
|
||||
|
||||
static void handleAlarm(int sig) {
|
||||
|
|
|
@ -148,7 +148,7 @@ MixOperateOnOptions::MixOperateOnOptions()
|
|||
{
|
||||
addFlag({
|
||||
.longName = "derivation",
|
||||
.description = "Operate on the [store derivation](../../glossary.md#gloss-store-derivation) rather than its outputs.",
|
||||
.description = "Operate on the [store derivation](@docroot@/glossary.md#gloss-store-derivation) rather than its outputs.",
|
||||
.category = installablesCategory,
|
||||
.handler = {&operateOn, OperateOn::Derivation},
|
||||
});
|
||||
|
|
|
@ -181,7 +181,7 @@ Bindings * MixEvalArgs::getAutoArgs(EvalState & state)
|
|||
v->mkString(arg.s);
|
||||
},
|
||||
[&](const AutoArgFile & arg) {
|
||||
v->mkString(readFile(arg.path));
|
||||
v->mkString(readFile(arg.path.string()));
|
||||
},
|
||||
[&](const AutoArgStdin & arg) {
|
||||
v->mkString(readFile(STDIN_FILENO));
|
||||
|
|
|
@ -49,7 +49,7 @@ Value * InstallableFlake::getFlakeOutputs(EvalState & state, const flake::Locked
|
|||
|
||||
callFlake(state, lockedFlake, *vFlake);
|
||||
|
||||
auto aOutputs = vFlake->attrs->get(state.symbols.create("outputs"));
|
||||
auto aOutputs = vFlake->attrs()->get(state.symbols.create("outputs"));
|
||||
assert(aOutputs);
|
||||
|
||||
state.forceValue(*aOutputs->value, aOutputs->value->determinePos(noPos));
|
||||
|
|
|
@ -354,7 +354,7 @@ void SourceExprCommand::completeInstallable(AddCompletions & completions, std::s
|
|||
state->autoCallFunction(*autoArgs, v1, v2);
|
||||
|
||||
if (v2.type() == nAttrs) {
|
||||
for (auto & i : *v2.attrs) {
|
||||
for (auto & i : *v2.attrs()) {
|
||||
std::string name = state->symbols[i.name];
|
||||
if (name.find(searchWord) == 0) {
|
||||
if (prefix_ == "")
|
||||
|
@ -538,7 +538,7 @@ ref<eval_cache::EvalCache> openEvalCache(
|
|||
|
||||
state.forceAttrs(*vFlake, noPos, "while parsing cached flake data");
|
||||
|
||||
auto aOutputs = vFlake->attrs->get(state.symbols.create("outputs"));
|
||||
auto aOutputs = vFlake->attrs()->get(state.symbols.create("outputs"));
|
||||
assert(aOutputs);
|
||||
|
||||
return aOutputs->value;
|
||||
|
|
|
@ -3,9 +3,9 @@
|
|||
#include "finally.hh"
|
||||
#include "terminal.hh"
|
||||
|
||||
#include <sys/queue.h>
|
||||
#if HAVE_LOWDOWN
|
||||
#include <lowdown.h>
|
||||
# include <sys/queue.h>
|
||||
# include <lowdown.h>
|
||||
#endif
|
||||
|
||||
namespace nix {
|
||||
|
|
|
@ -137,6 +137,7 @@ static constexpr const char * promptForType(ReplPromptType promptType)
|
|||
|
||||
bool ReadlineLikeInteracter::getLine(std::string & input, ReplPromptType promptType)
|
||||
{
|
||||
#ifndef _WIN32 // TODO use more signals.hh for this
|
||||
struct sigaction act, old;
|
||||
sigset_t savedSignalMask, set;
|
||||
|
||||
|
@ -161,9 +162,12 @@ bool ReadlineLikeInteracter::getLine(std::string & input, ReplPromptType promptT
|
|||
};
|
||||
|
||||
setupSignals();
|
||||
#endif
|
||||
char * s = readline(promptForType(promptType));
|
||||
Finally doFree([&]() { free(s); });
|
||||
#ifndef _WIN32 // TODO use more signals.hh for this
|
||||
restoreSignals();
|
||||
#endif
|
||||
|
||||
if (g_signal_received) {
|
||||
g_signal_received = 0;
|
||||
|
|
|
@ -290,7 +290,7 @@ StringSet NixRepl::completePrefix(const std::string & prefix)
|
|||
e->eval(*state, *env, v);
|
||||
state->forceAttrs(v, noPos, "while evaluating an attrset for the purpose of completion (this error should not be displayed; file an issue?)");
|
||||
|
||||
for (auto & i : *v.attrs) {
|
||||
for (auto & i : *v.attrs()) {
|
||||
std::string_view name = state->symbols[i.name];
|
||||
if (name.substr(0, cur2.size()) != cur2) continue;
|
||||
completions.insert(concatStrings(prev, expr, ".", name));
|
||||
|
@ -490,7 +490,7 @@ ProcessLineResult NixRepl::processLine(std::string line)
|
|||
auto path = state->coerceToPath(noPos, v, context, "while evaluating the filename to edit");
|
||||
return {path, 0};
|
||||
} else if (v.isLambda()) {
|
||||
auto pos = state->positions[v.lambda.fun->pos];
|
||||
auto pos = state->positions[v.payload.lambda.fun->pos];
|
||||
if (auto path = std::get_if<SourcePath>(&pos.origin))
|
||||
return {*path, pos.line};
|
||||
else
|
||||
|
@ -742,17 +742,17 @@ void NixRepl::loadFiles()
|
|||
void NixRepl::addAttrsToScope(Value & attrs)
|
||||
{
|
||||
state->forceAttrs(attrs, [&]() { return attrs.determinePos(noPos); }, "while evaluating an attribute set to be merged in the global scope");
|
||||
if (displ + attrs.attrs->size() >= envSize)
|
||||
if (displ + attrs.attrs()->size() >= envSize)
|
||||
throw Error("environment full; cannot add more variables");
|
||||
|
||||
for (auto & i : *attrs.attrs) {
|
||||
for (auto & i : *attrs.attrs()) {
|
||||
staticEnv->vars.emplace_back(i.name, displ);
|
||||
env->values[displ++] = i.value;
|
||||
varNames.emplace(state->symbols[i.name]);
|
||||
}
|
||||
staticEnv->sort();
|
||||
staticEnv->deduplicate();
|
||||
notice("Added %1% variables.", attrs.attrs->size());
|
||||
notice("Added %1% variables.", attrs.attrs()->size());
|
||||
}
|
||||
|
||||
|
||||
|
|
|
@ -93,6 +93,8 @@ nix_err nix_expr_eval_from_string(
|
|||
* @param[in] arg The argument to pass to the function.
|
||||
* @param[out] value The result of the function call.
|
||||
* @return NIX_OK if the function call was successful, an error code otherwise.
|
||||
* @see nix_init_apply() for a similar function that does not performs the call immediately, but stores it as a thunk.
|
||||
* Note the different argument order.
|
||||
*/
|
||||
nix_err nix_value_call(nix_c_context * context, EvalState * state, Value * fn, Value * arg, Value * value);
|
||||
|
||||
|
|
|
@ -35,4 +35,10 @@ struct nix_string_context
|
|||
nix::NixStringContext & ctx;
|
||||
};
|
||||
|
||||
struct nix_realised_string
|
||||
{
|
||||
std::string str;
|
||||
std::vector<StorePath> storePaths;
|
||||
};
|
||||
|
||||
#endif // NIX_API_EXPR_INTERNAL_H
|
||||
|
|
|
@ -2,6 +2,7 @@
|
|||
#include "config.hh"
|
||||
#include "eval.hh"
|
||||
#include "globals.hh"
|
||||
#include "path.hh"
|
||||
#include "primops.hh"
|
||||
#include "value.hh"
|
||||
|
||||
|
@ -9,7 +10,9 @@
|
|||
#include "nix_api_expr_internal.h"
|
||||
#include "nix_api_util.h"
|
||||
#include "nix_api_util_internal.h"
|
||||
#include "nix_api_store_internal.h"
|
||||
#include "nix_api_value.h"
|
||||
#include "value/context.hh"
|
||||
|
||||
#ifdef HAVE_BOEHMGC
|
||||
# include "gc/gc.h"
|
||||
|
@ -158,21 +161,21 @@ bool nix_get_bool(nix_c_context * context, const Value * value)
|
|||
try {
|
||||
auto & v = check_value_not_null(value);
|
||||
assert(v.type() == nix::nBool);
|
||||
return v.boolean;
|
||||
return v.boolean();
|
||||
}
|
||||
NIXC_CATCH_ERRS_RES(false);
|
||||
}
|
||||
|
||||
const char * nix_get_string(nix_c_context * context, const Value * value)
|
||||
nix_err nix_get_string(nix_c_context * context, const Value * value, nix_get_string_callback callback, void * user_data)
|
||||
{
|
||||
if (context)
|
||||
context->last_err_code = NIX_OK;
|
||||
try {
|
||||
auto & v = check_value_not_null(value);
|
||||
assert(v.type() == nix::nString);
|
||||
return v.c_str();
|
||||
call_nix_get_string_callback(v.c_str(), callback, user_data);
|
||||
}
|
||||
NIXC_CATCH_ERRS_NULL
|
||||
NIXC_CATCH_ERRS
|
||||
}
|
||||
|
||||
const char * nix_get_path_string(nix_c_context * context, const Value * value)
|
||||
|
@ -189,7 +192,7 @@ const char * nix_get_path_string(nix_c_context * context, const Value * value)
|
|||
// We could use v.path().to_string().c_str(), but I'm concerned this
|
||||
// crashes. Looks like .path() allocates a CanonPath with a copy of the
|
||||
// string, then it gets the underlying data from that.
|
||||
return v._path.path;
|
||||
return v.payload.path.path;
|
||||
}
|
||||
NIXC_CATCH_ERRS_NULL
|
||||
}
|
||||
|
@ -213,7 +216,7 @@ unsigned int nix_get_attrs_size(nix_c_context * context, const Value * value)
|
|||
try {
|
||||
auto & v = check_value_not_null(value);
|
||||
assert(v.type() == nix::nAttrs);
|
||||
return v.attrs->size();
|
||||
return v.attrs()->size();
|
||||
}
|
||||
NIXC_CATCH_ERRS_RES(0);
|
||||
}
|
||||
|
@ -225,7 +228,7 @@ double nix_get_float(nix_c_context * context, const Value * value)
|
|||
try {
|
||||
auto & v = check_value_not_null(value);
|
||||
assert(v.type() == nix::nFloat);
|
||||
return v.fpoint;
|
||||
return v.fpoint();
|
||||
}
|
||||
NIXC_CATCH_ERRS_RES(0.0);
|
||||
}
|
||||
|
@ -237,7 +240,7 @@ int64_t nix_get_int(nix_c_context * context, const Value * value)
|
|||
try {
|
||||
auto & v = check_value_not_null(value);
|
||||
assert(v.type() == nix::nInt);
|
||||
return v.integer;
|
||||
return v.integer();
|
||||
}
|
||||
NIXC_CATCH_ERRS_RES(0);
|
||||
}
|
||||
|
@ -249,7 +252,7 @@ ExternalValue * nix_get_external(nix_c_context * context, Value * value)
|
|||
try {
|
||||
auto & v = check_value_not_null(value);
|
||||
assert(v.type() == nix::nExternal);
|
||||
return (ExternalValue *) v.external;
|
||||
return (ExternalValue *) v.external();
|
||||
}
|
||||
NIXC_CATCH_ERRS_NULL;
|
||||
}
|
||||
|
@ -278,7 +281,7 @@ Value * nix_get_attr_byname(nix_c_context * context, const Value * value, EvalSt
|
|||
auto & v = check_value_not_null(value);
|
||||
assert(v.type() == nix::nAttrs);
|
||||
nix::Symbol s = state->state.symbols.create(name);
|
||||
auto attr = v.attrs->get(s);
|
||||
auto attr = v.attrs()->get(s);
|
||||
if (attr) {
|
||||
nix_gc_incref(nullptr, attr->value);
|
||||
state->state.forceValue(*attr->value, nix::noPos);
|
||||
|
@ -298,7 +301,7 @@ bool nix_has_attr_byname(nix_c_context * context, const Value * value, EvalState
|
|||
auto & v = check_value_not_null(value);
|
||||
assert(v.type() == nix::nAttrs);
|
||||
nix::Symbol s = state->state.symbols.create(name);
|
||||
auto attr = v.attrs->get(s);
|
||||
auto attr = v.attrs()->get(s);
|
||||
if (attr)
|
||||
return true;
|
||||
return false;
|
||||
|
@ -313,7 +316,7 @@ nix_get_attr_byidx(nix_c_context * context, const Value * value, EvalState * sta
|
|||
context->last_err_code = NIX_OK;
|
||||
try {
|
||||
auto & v = check_value_not_null(value);
|
||||
const nix::Attr & a = (*v.attrs)[i];
|
||||
const nix::Attr & a = (*v.attrs())[i];
|
||||
*name = ((const std::string &) (state->state.symbols[a.name])).c_str();
|
||||
nix_gc_incref(nullptr, a.value);
|
||||
state->state.forceValue(*a.value, nix::noPos);
|
||||
|
@ -328,7 +331,7 @@ const char * nix_get_attr_name_byidx(nix_c_context * context, const Value * valu
|
|||
context->last_err_code = NIX_OK;
|
||||
try {
|
||||
auto & v = check_value_not_null(value);
|
||||
const nix::Attr & a = (*v.attrs)[i];
|
||||
const nix::Attr & a = (*v.attrs())[i];
|
||||
return ((const std::string &) (state->state.symbols[a.name])).c_str();
|
||||
}
|
||||
NIXC_CATCH_ERRS_NULL
|
||||
|
@ -401,6 +404,19 @@ nix_err nix_init_null(nix_c_context * context, Value * value)
|
|||
NIXC_CATCH_ERRS
|
||||
}
|
||||
|
||||
nix_err nix_init_apply(nix_c_context * context, Value * value, Value * fn, Value * arg)
|
||||
{
|
||||
if (context)
|
||||
context->last_err_code = NIX_OK;
|
||||
try {
|
||||
auto & v = check_value_not_null(value);
|
||||
auto & f = check_value_not_null(fn);
|
||||
auto & a = check_value_not_null(arg);
|
||||
v.mkApp(&f, &a);
|
||||
}
|
||||
NIXC_CATCH_ERRS
|
||||
}
|
||||
|
||||
nix_err nix_init_external(nix_c_context * context, Value * value, ExternalValue * val)
|
||||
{
|
||||
if (context)
|
||||
|
@ -528,3 +544,52 @@ void nix_bindings_builder_free(BindingsBuilder * bb)
|
|||
delete (nix::BindingsBuilder *) bb;
|
||||
#endif
|
||||
}
|
||||
|
||||
nix_realised_string * nix_string_realise(nix_c_context * context, EvalState * state, Value * value, bool isIFD)
|
||||
{
|
||||
if (context)
|
||||
context->last_err_code = NIX_OK;
|
||||
try {
|
||||
auto & v = check_value_not_null(value);
|
||||
nix::NixStringContext stringContext;
|
||||
auto rawStr = state->state.coerceToString(nix::noPos, v, stringContext, "while realising a string").toOwned();
|
||||
nix::StorePathSet storePaths;
|
||||
auto rewrites = state->state.realiseContext(stringContext, &storePaths);
|
||||
|
||||
auto s = nix::rewriteStrings(rawStr, rewrites);
|
||||
|
||||
// Convert to the C API StorePath type and convert to vector for index-based access
|
||||
std::vector<StorePath> vec;
|
||||
for (auto & sp : storePaths) {
|
||||
vec.push_back(StorePath{sp});
|
||||
}
|
||||
|
||||
return new nix_realised_string{.str = s, .storePaths = vec};
|
||||
}
|
||||
NIXC_CATCH_ERRS_NULL
|
||||
}
|
||||
|
||||
void nix_realised_string_free(nix_realised_string * s)
|
||||
{
|
||||
delete s;
|
||||
}
|
||||
|
||||
size_t nix_realised_string_get_buffer_size(nix_realised_string * s)
|
||||
{
|
||||
return s->str.size();
|
||||
}
|
||||
|
||||
const char * nix_realised_string_get_buffer_start(nix_realised_string * s)
|
||||
{
|
||||
return s->str.data();
|
||||
}
|
||||
|
||||
size_t nix_realised_string_get_store_path_count(nix_realised_string * s)
|
||||
{
|
||||
return s->storePaths.size();
|
||||
}
|
||||
|
||||
const StorePath * nix_realised_string_get_store_path(nix_realised_string * s, size_t i)
|
||||
{
|
||||
return &s->storePaths[i];
|
||||
}
|
||||
|
|
|
@ -9,6 +9,7 @@
|
|||
*/
|
||||
|
||||
#include "nix_api_util.h"
|
||||
#include "nix_api_store.h"
|
||||
#include "stdbool.h"
|
||||
#include "stddef.h"
|
||||
#include "stdint.h"
|
||||
|
@ -69,6 +70,10 @@ typedef struct PrimOp PrimOp;
|
|||
*/
|
||||
typedef struct ExternalValue ExternalValue;
|
||||
|
||||
/** @brief String without placeholders, and realised store paths
|
||||
*/
|
||||
typedef struct nix_realised_string nix_realised_string;
|
||||
|
||||
/** @defgroup primops
|
||||
* @brief Create your own primops
|
||||
* @{
|
||||
|
@ -167,13 +172,19 @@ const char * nix_get_typename(nix_c_context * context, const Value * value);
|
|||
*/
|
||||
bool nix_get_bool(nix_c_context * context, const Value * value);
|
||||
|
||||
/** @brief Get string
|
||||
/** @brief Get the raw string
|
||||
*
|
||||
* This may contain placeholders.
|
||||
*
|
||||
* @param[out] context Optional, stores error information
|
||||
* @param[in] value Nix value to inspect
|
||||
* @param[in] callback Called with the string value.
|
||||
* @param[in] user_data optional, arbitrary data, passed to the callback when it's called.
|
||||
* @return string
|
||||
* @return NULL in case of error.
|
||||
* @return error code, NIX_OK on success.
|
||||
*/
|
||||
const char * nix_get_string(nix_c_context * context, const Value * value);
|
||||
nix_err
|
||||
nix_get_string(nix_c_context * context, const Value * value, nix_get_string_callback callback, void * user_data);
|
||||
|
||||
/** @brief Get path as string
|
||||
* @param[out] context Optional, stores error information
|
||||
|
@ -331,8 +342,24 @@ nix_err nix_init_int(nix_c_context * context, Value * value, int64_t i);
|
|||
* @param[out] value Nix value to modify
|
||||
* @return error code, NIX_OK on success.
|
||||
*/
|
||||
|
||||
nix_err nix_init_null(nix_c_context * context, Value * value);
|
||||
|
||||
/** @brief Set the value to a thunk that will perform a function application when needed.
|
||||
*
|
||||
* Thunks may be put into attribute sets and lists to perform some computation lazily; on demand.
|
||||
* However, note that in some places, a thunk must not be returned, such as in the return value of a PrimOp.
|
||||
* In such cases, you may use nix_value_call() instead (but note the different argument order).
|
||||
*
|
||||
* @param[out] context Optional, stores error information
|
||||
* @param[out] value Nix value to modify
|
||||
* @param[in] fn function to call
|
||||
* @param[in] arg argument to pass
|
||||
* @return error code, NIX_OK on successful initialization.
|
||||
* @see nix_value_call() for a similar function that performs the call immediately and only stores the return value.
|
||||
* Note the different argument order.
|
||||
*/
|
||||
nix_err nix_init_apply(nix_c_context * context, Value * value, Value * fn, Value * arg);
|
||||
|
||||
/** @brief Set an external value
|
||||
* @param[out] context Optional, stores error information
|
||||
* @param[out] value Nix value to modify
|
||||
|
@ -410,7 +437,7 @@ BindingsBuilder * nix_make_bindings_builder(nix_c_context * context, EvalState *
|
|||
/** @brief Insert bindings into a builder
|
||||
* @param[out] context Optional, stores error information
|
||||
* @param[in] builder BindingsBuilder to insert into
|
||||
* @param[in] name attribute name, copied into the symbol store
|
||||
* @param[in] name attribute name, only used for the duration of the call.
|
||||
* @param[in] value value to give the binding
|
||||
* @return error code, NIX_OK on success.
|
||||
*/
|
||||
|
@ -425,6 +452,55 @@ nix_bindings_builder_insert(nix_c_context * context, BindingsBuilder * builder,
|
|||
void nix_bindings_builder_free(BindingsBuilder * builder);
|
||||
/**@}*/
|
||||
|
||||
/** @brief Realise a string context.
|
||||
*
|
||||
* This will
|
||||
* - realise the store paths referenced by the string's context, and
|
||||
* - perform the replacement of placeholders.
|
||||
* - create temporary garbage collection roots for the store paths, for
|
||||
* the lifetime of the current process.
|
||||
* - log to stderr
|
||||
*
|
||||
* @param[out] context Optional, stores error information
|
||||
* @param[in] value Nix value, which must be a string
|
||||
* @param[in] state Nix evaluator state
|
||||
* @param[in] isIFD If true, disallow derivation outputs if setting `allow-import-from-derivation` is false.
|
||||
You should set this to true when this call is part of a primop.
|
||||
You should set this to false when building for your application's purpose.
|
||||
* @return NULL if failed, are a new nix_realised_string, which must be freed with nix_realised_string_free
|
||||
*/
|
||||
nix_realised_string * nix_string_realise(nix_c_context * context, EvalState * state, Value * value, bool isIFD);
|
||||
|
||||
/** @brief Start of the string
|
||||
* @param[in] realised_string
|
||||
* @return pointer to the start of the string. It may not be null-terminated.
|
||||
*/
|
||||
const char * nix_realised_string_get_buffer_start(nix_realised_string * realised_string);
|
||||
|
||||
/** @brief Length of the string
|
||||
* @param[in] realised_string
|
||||
* @return length of the string in bytes
|
||||
*/
|
||||
size_t nix_realised_string_get_buffer_size(nix_realised_string * realised_string);
|
||||
|
||||
/** @brief Number of realised store paths
|
||||
* @param[in] realised_string
|
||||
* @return number of realised store paths that were referenced by the string via its context
|
||||
*/
|
||||
size_t nix_realised_string_get_store_path_count(nix_realised_string * realised_string);
|
||||
|
||||
/** @brief Get a store path. The store paths are stored in an arbitrary order.
|
||||
* @param[in] realised_string
|
||||
* @param[in] index index of the store path, must be less than the count
|
||||
* @return store path
|
||||
*/
|
||||
const StorePath * nix_realised_string_get_store_path(nix_realised_string * realised_string, size_t index);
|
||||
|
||||
/** @brief Free a realised string
|
||||
* @param[in] realised_string
|
||||
*/
|
||||
void nix_realised_string_free(nix_realised_string * realised_string);
|
||||
|
||||
// cffi end
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
|
|
|
@ -72,10 +72,10 @@ std::pair<Value *, PosIdx> findAlongAttrPath(EvalState & state, const std::strin
|
|||
if (attr.empty())
|
||||
throw Error("empty attribute name in selection path '%1%'", attrPath);
|
||||
|
||||
Bindings::iterator a = v->attrs->find(state.symbols.create(attr));
|
||||
if (a == v->attrs->end()) {
|
||||
auto a = v->attrs()->get(state.symbols.create(attr));
|
||||
if (!a) {
|
||||
std::set<std::string> attrNames;
|
||||
for (auto & attr : *v->attrs)
|
||||
for (auto & attr : *v->attrs())
|
||||
attrNames.insert(state.symbols[attr.name]);
|
||||
|
||||
auto suggestions = Suggestions::bestMatches(attrNames, attr);
|
||||
|
|
|
@ -23,23 +23,6 @@ Bindings * EvalState::allocBindings(size_t capacity)
|
|||
}
|
||||
|
||||
|
||||
/* Create a new attribute named 'name' on an existing attribute set stored
|
||||
in 'vAttrs' and return the newly allocated Value which is associated with
|
||||
this attribute. */
|
||||
Value * EvalState::allocAttr(Value & vAttrs, Symbol name)
|
||||
{
|
||||
Value * v = allocValue();
|
||||
vAttrs.attrs->push_back(Attr(name, v));
|
||||
return v;
|
||||
}
|
||||
|
||||
|
||||
Value * EvalState::allocAttr(Value & vAttrs, std::string_view name)
|
||||
{
|
||||
return allocAttr(vAttrs, symbols.create(name));
|
||||
}
|
||||
|
||||
|
||||
Value & BindingsBuilder::alloc(Symbol name, PosIdx pos)
|
||||
{
|
||||
auto value = state.allocValue();
|
||||
|
|
|
@ -65,24 +65,26 @@ public:
|
|||
|
||||
typedef Attr * iterator;
|
||||
|
||||
typedef const Attr * const_iterator;
|
||||
|
||||
void push_back(const Attr & attr)
|
||||
{
|
||||
assert(size_ < capacity_);
|
||||
attrs[size_++] = attr;
|
||||
}
|
||||
|
||||
iterator find(Symbol name)
|
||||
const_iterator find(Symbol name) const
|
||||
{
|
||||
Attr key(name, 0);
|
||||
iterator i = std::lower_bound(begin(), end(), key);
|
||||
const_iterator i = std::lower_bound(begin(), end(), key);
|
||||
if (i != end() && i->name == name) return i;
|
||||
return end();
|
||||
}
|
||||
|
||||
Attr * get(Symbol name)
|
||||
const Attr * get(Symbol name) const
|
||||
{
|
||||
Attr key(name, 0);
|
||||
iterator i = std::lower_bound(begin(), end(), key);
|
||||
const_iterator i = std::lower_bound(begin(), end(), key);
|
||||
if (i != end() && i->name == name) return &*i;
|
||||
return nullptr;
|
||||
}
|
||||
|
@ -90,14 +92,22 @@ public:
|
|||
iterator begin() { return &attrs[0]; }
|
||||
iterator end() { return &attrs[size_]; }
|
||||
|
||||
const_iterator begin() const { return &attrs[0]; }
|
||||
const_iterator end() const { return &attrs[size_]; }
|
||||
|
||||
Attr & operator[](size_t pos)
|
||||
{
|
||||
return attrs[pos];
|
||||
}
|
||||
|
||||
const Attr & operator[](size_t pos) const
|
||||
{
|
||||
return attrs[pos];
|
||||
}
|
||||
|
||||
void sort();
|
||||
|
||||
size_t capacity() { return capacity_; }
|
||||
size_t capacity() const { return capacity_; }
|
||||
|
||||
/**
|
||||
* Returns the attributes in lexicographically sorted order.
|
||||
|
@ -166,6 +176,20 @@ public:
|
|||
{
|
||||
return bindings;
|
||||
}
|
||||
|
||||
size_t capacity()
|
||||
{
|
||||
return bindings->capacity();
|
||||
}
|
||||
|
||||
void grow(Bindings * newBindings)
|
||||
{
|
||||
for (auto & i : *bindings)
|
||||
newBindings->push_back(i);
|
||||
bindings = newBindings;
|
||||
}
|
||||
|
||||
friend struct ExprAttrs;
|
||||
};
|
||||
|
||||
}
|
||||
|
|
|
@ -387,7 +387,7 @@ Value & AttrCursor::getValue()
|
|||
if (parent) {
|
||||
auto & vParent = parent->first->getValue();
|
||||
root->state.forceAttrs(vParent, noPos, "while searching for an attribute");
|
||||
auto attr = vParent.attrs->get(parent->second);
|
||||
auto attr = vParent.attrs()->get(parent->second);
|
||||
if (!attr)
|
||||
throw Error("attribute '%s' is unexpectedly missing", getAttrPathStr());
|
||||
_value = allocRootValue(attr->value);
|
||||
|
@ -448,9 +448,9 @@ Value & AttrCursor::forceValue()
|
|||
cachedValue = {root->db->setString(getKey(), path.abs()), string_t{path.abs(), {}}};
|
||||
}
|
||||
else if (v.type() == nBool)
|
||||
cachedValue = {root->db->setBool(getKey(), v.boolean), v.boolean};
|
||||
cachedValue = {root->db->setBool(getKey(), v.boolean()), v.boolean()};
|
||||
else if (v.type() == nInt)
|
||||
cachedValue = {root->db->setInt(getKey(), v.integer), int_t{v.integer}};
|
||||
cachedValue = {root->db->setInt(getKey(), v.integer()), int_t{v.integer()}};
|
||||
else if (v.type() == nAttrs)
|
||||
; // FIXME: do something?
|
||||
else
|
||||
|
@ -510,7 +510,7 @@ std::shared_ptr<AttrCursor> AttrCursor::maybeGetAttr(Symbol name, bool forceErro
|
|||
return nullptr;
|
||||
//error<TypeError>("'%s' is not an attribute set", getAttrPathStr()).debugThrow();
|
||||
|
||||
auto attr = v.attrs->get(name);
|
||||
auto attr = v.attrs()->get(name);
|
||||
|
||||
if (!attr) {
|
||||
if (root->db) {
|
||||
|
@ -652,7 +652,7 @@ bool AttrCursor::getBool()
|
|||
if (v.type() != nBool)
|
||||
root->state.error<TypeError>("'%s' is not a Boolean", getAttrPathStr()).debugThrow();
|
||||
|
||||
return v.boolean;
|
||||
return v.boolean();
|
||||
}
|
||||
|
||||
NixInt AttrCursor::getInt()
|
||||
|
@ -674,7 +674,7 @@ NixInt AttrCursor::getInt()
|
|||
if (v.type() != nInt)
|
||||
root->state.error<TypeError>("'%s' is not an integer", getAttrPathStr()).debugThrow();
|
||||
|
||||
return v.integer;
|
||||
return v.integer();
|
||||
}
|
||||
|
||||
std::vector<std::string> AttrCursor::getListOfStrings()
|
||||
|
@ -730,7 +730,7 @@ std::vector<Symbol> AttrCursor::getAttrs()
|
|||
root->state.error<TypeError>("'%s' is not an attribute set", getAttrPathStr()).debugThrow();
|
||||
|
||||
std::vector<Symbol> attrs;
|
||||
for (auto & attr : *getValue().attrs)
|
||||
for (auto & attr : *getValue().attrs())
|
||||
attrs.push_back(attr.name);
|
||||
std::sort(attrs.begin(), attrs.end(), [&](Symbol a, Symbol b) {
|
||||
std::string_view sa = root->state.symbols[a], sb = root->state.symbols[b];
|
||||
|
|
|
@ -85,8 +85,8 @@ Env & EvalState::allocEnv(size_t size)
|
|||
void EvalState::forceValue(Value & v, const PosIdx pos)
|
||||
{
|
||||
if (v.isThunk()) {
|
||||
Env * env = v.thunk.env;
|
||||
Expr * expr = v.thunk.expr;
|
||||
Env * env = v.payload.thunk.env;
|
||||
Expr * expr = v.payload.thunk.expr;
|
||||
try {
|
||||
v.mkBlackhole();
|
||||
//checkInterrupt();
|
||||
|
@ -98,7 +98,7 @@ void EvalState::forceValue(Value & v, const PosIdx pos)
|
|||
}
|
||||
}
|
||||
else if (v.isApp())
|
||||
callFunction(*v.app.left, *v.app.right, v, pos);
|
||||
callFunction(*v.payload.app.left, *v.payload.app.right, v, pos);
|
||||
}
|
||||
|
||||
|
||||
|
|
|
@ -33,15 +33,17 @@
|
|||
#include <optional>
|
||||
#include <unistd.h>
|
||||
#include <sys/time.h>
|
||||
#include <sys/resource.h>
|
||||
#include <fstream>
|
||||
#include <functional>
|
||||
#include <iostream>
|
||||
|
||||
#include <sys/resource.h>
|
||||
#include <nlohmann/json.hpp>
|
||||
#include <boost/container/small_vector.hpp>
|
||||
|
||||
#ifndef _WIN32 // TODO use portable implementation
|
||||
# include <sys/resource.h>
|
||||
#endif
|
||||
|
||||
#if HAVE_BOEHMGC
|
||||
|
||||
#define GC_INCLUDE_NEW
|
||||
|
@ -131,7 +133,7 @@ void Value::print(EvalState & state, std::ostream & str, PrintOptions options)
|
|||
const Value * getPrimOp(const Value &v) {
|
||||
const Value * primOp = &v;
|
||||
while (primOp->isPrimOpApp()) {
|
||||
primOp = primOp->primOpApp.left;
|
||||
primOp = primOp->payload.primOpApp.left;
|
||||
}
|
||||
assert(primOp->isPrimOp());
|
||||
return primOp;
|
||||
|
@ -163,12 +165,12 @@ std::string showType(const Value & v)
|
|||
#pragma GCC diagnostic push
|
||||
#pragma GCC diagnostic ignored "-Wswitch-enum"
|
||||
switch (v.internalType) {
|
||||
case tString: return v.string.context ? "a string with context" : "a string";
|
||||
case tString: return v.payload.string.context ? "a string with context" : "a string";
|
||||
case tPrimOp:
|
||||
return fmt("the built-in function '%s'", std::string(v.primOp->name));
|
||||
return fmt("the built-in function '%s'", std::string(v.payload.primOp->name));
|
||||
case tPrimOpApp:
|
||||
return fmt("the partially applied built-in function '%s'", std::string(getPrimOp(v)->primOp->name));
|
||||
case tExternal: return v.external->showType();
|
||||
return fmt("the partially applied built-in function '%s'", std::string(getPrimOp(v)->payload.primOp->name));
|
||||
case tExternal: return v.external()->showType();
|
||||
case tThunk: return v.isBlackhole() ? "a black hole" : "a thunk";
|
||||
case tApp: return "a function application";
|
||||
default:
|
||||
|
@ -183,9 +185,9 @@ PosIdx Value::determinePos(const PosIdx pos) const
|
|||
#pragma GCC diagnostic push
|
||||
#pragma GCC diagnostic ignored "-Wswitch-enum"
|
||||
switch (internalType) {
|
||||
case tAttrs: return attrs->pos;
|
||||
case tLambda: return lambda.fun->pos;
|
||||
case tApp: return app.left->determinePos(pos);
|
||||
case tAttrs: return attrs()->pos;
|
||||
case tLambda: return payload.lambda.fun->pos;
|
||||
case tApp: return payload.app.left->determinePos(pos);
|
||||
default: return pos;
|
||||
}
|
||||
#pragma GCC diagnostic pop
|
||||
|
@ -197,10 +199,10 @@ bool Value::isTrivial() const
|
|||
internalType != tApp
|
||||
&& internalType != tPrimOpApp
|
||||
&& (internalType != tThunk
|
||||
|| (dynamic_cast<ExprAttrs *>(thunk.expr)
|
||||
&& ((ExprAttrs *) thunk.expr)->dynamicAttrs.empty())
|
||||
|| dynamic_cast<ExprLambda *>(thunk.expr)
|
||||
|| dynamic_cast<ExprList *>(thunk.expr));
|
||||
|| (dynamic_cast<ExprAttrs *>(payload.thunk.expr)
|
||||
&& ((ExprAttrs *) payload.thunk.expr)->dynamicAttrs.empty())
|
||||
|| dynamic_cast<ExprLambda *>(payload.thunk.expr)
|
||||
|| dynamic_cast<ExprList *>(payload.thunk.expr));
|
||||
}
|
||||
|
||||
|
||||
|
@ -584,7 +586,7 @@ void EvalState::addConstant(const std::string & name, Value * v, Constant info)
|
|||
/* Install value the base environment. */
|
||||
staticBaseEnv->vars.emplace_back(symbols.create(name), baseEnvDispl);
|
||||
baseEnv.values[baseEnvDispl++] = v;
|
||||
baseEnv.values[0]->attrs->push_back(Attr(symbols.create(name2), v));
|
||||
baseEnv.values[0]->payload.attrs->push_back(Attr(symbols.create(name2), v));
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -597,32 +599,30 @@ void PrimOp::check()
|
|||
}
|
||||
|
||||
|
||||
std::ostream & operator<<(std::ostream & output, PrimOp & primOp)
|
||||
std::ostream & operator<<(std::ostream & output, const PrimOp & primOp)
|
||||
{
|
||||
output << "primop " << primOp.name;
|
||||
return output;
|
||||
}
|
||||
|
||||
|
||||
PrimOp * Value::primOpAppPrimOp() const
|
||||
const PrimOp * Value::primOpAppPrimOp() const
|
||||
{
|
||||
Value * left = primOpApp.left;
|
||||
Value * left = payload.primOpApp.left;
|
||||
while (left && !left->isPrimOp()) {
|
||||
left = left->primOpApp.left;
|
||||
left = left->payload.primOpApp.left;
|
||||
}
|
||||
|
||||
if (!left)
|
||||
return nullptr;
|
||||
return left->primOp;
|
||||
return left->primOp();
|
||||
}
|
||||
|
||||
|
||||
void Value::mkPrimOp(PrimOp * p)
|
||||
{
|
||||
p->check();
|
||||
clearValue();
|
||||
internalType = tPrimOp;
|
||||
primOp = p;
|
||||
finishValue(tPrimOp, { .primOp = p });
|
||||
}
|
||||
|
||||
|
||||
|
@ -650,14 +650,14 @@ Value * EvalState::addPrimOp(PrimOp && primOp)
|
|||
v->mkPrimOp(new PrimOp(primOp));
|
||||
staticBaseEnv->vars.emplace_back(envName, baseEnvDispl);
|
||||
baseEnv.values[baseEnvDispl++] = v;
|
||||
baseEnv.values[0]->attrs->push_back(Attr(symbols.create(primOp.name), v));
|
||||
baseEnv.values[0]->payload.attrs->push_back(Attr(symbols.create(primOp.name), v));
|
||||
return v;
|
||||
}
|
||||
|
||||
|
||||
Value & EvalState::getBuiltin(const std::string & name)
|
||||
{
|
||||
return *baseEnv.values[0]->attrs->find(symbols.create(name))->value;
|
||||
return *baseEnv.values[0]->attrs()->find(symbols.create(name))->value;
|
||||
}
|
||||
|
||||
|
||||
|
@ -665,12 +665,12 @@ std::optional<EvalState::Doc> EvalState::getDoc(Value & v)
|
|||
{
|
||||
if (v.isPrimOp()) {
|
||||
auto v2 = &v;
|
||||
if (auto * doc = v2->primOp->doc)
|
||||
if (auto * doc = v2->primOp()->doc)
|
||||
return Doc {
|
||||
.pos = {},
|
||||
.name = v2->primOp->name,
|
||||
.arity = v2->primOp->arity,
|
||||
.args = v2->primOp->args,
|
||||
.name = v2->primOp()->name,
|
||||
.arity = v2->primOp()->arity,
|
||||
.args = v2->primOp()->args,
|
||||
.doc = doc,
|
||||
};
|
||||
}
|
||||
|
@ -694,8 +694,8 @@ void printWithBindings(const SymbolTable & st, const Env & env)
|
|||
if (!env.values[0]->isThunk()) {
|
||||
std::cout << "with: ";
|
||||
std::cout << ANSI_MAGENTA;
|
||||
Bindings::iterator j = env.values[0]->attrs->begin();
|
||||
while (j != env.values[0]->attrs->end()) {
|
||||
auto j = env.values[0]->attrs()->begin();
|
||||
while (j != env.values[0]->attrs()->end()) {
|
||||
std::cout << st[j->name] << " ";
|
||||
++j;
|
||||
}
|
||||
|
@ -749,11 +749,8 @@ void mapStaticEnvBindings(const SymbolTable & st, const StaticEnv & se, const En
|
|||
|
||||
if (se.isWith && !env.values[0]->isThunk()) {
|
||||
// add 'with' bindings.
|
||||
Bindings::iterator j = env.values[0]->attrs->begin();
|
||||
while (j != env.values[0]->attrs->end()) {
|
||||
vm[st[j->name]] = j->value;
|
||||
++j;
|
||||
}
|
||||
for (auto & j : *env.values[0]->attrs())
|
||||
vm[st[j.name]] = j.value;
|
||||
} else {
|
||||
// iterate through staticenv bindings and add them.
|
||||
for (auto & i : se.vars)
|
||||
|
@ -873,28 +870,28 @@ void Value::mkString(std::string_view s)
|
|||
}
|
||||
|
||||
|
||||
static void copyContextToValue(Value & v, const NixStringContext & context)
|
||||
static const char * * encodeContext(const NixStringContext & context)
|
||||
{
|
||||
if (!context.empty()) {
|
||||
size_t n = 0;
|
||||
v.string.context = (const char * *)
|
||||
auto ctx = (const char * *)
|
||||
allocBytes((context.size() + 1) * sizeof(char *));
|
||||
for (auto & i : context)
|
||||
v.string.context[n++] = dupString(i.to_string().c_str());
|
||||
v.string.context[n] = 0;
|
||||
}
|
||||
ctx[n++] = dupString(i.to_string().c_str());
|
||||
ctx[n] = 0;
|
||||
return ctx;
|
||||
} else
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
void Value::mkString(std::string_view s, const NixStringContext & context)
|
||||
{
|
||||
mkString(s);
|
||||
copyContextToValue(*this, context);
|
||||
mkString(makeImmutableString(s), encodeContext(context));
|
||||
}
|
||||
|
||||
void Value::mkStringMove(const char * s, const NixStringContext & context)
|
||||
{
|
||||
mkString(s);
|
||||
copyContextToValue(*this, context);
|
||||
mkString(s, encodeContext(context));
|
||||
}
|
||||
|
||||
void Value::mkPath(const SourcePath & path)
|
||||
|
@ -917,8 +914,7 @@ inline Value * EvalState::lookupVar(Env * env, const ExprVar & var, bool noEval)
|
|||
auto * fromWith = var.fromWith;
|
||||
while (1) {
|
||||
forceAttrs(*env->values[0], fromWith->pos, "while evaluating the first subexpression of a with expression");
|
||||
Bindings::iterator j = env->values[0]->attrs->find(var.name);
|
||||
if (j != env->values[0]->attrs->end()) {
|
||||
if (auto j = env->values[0]->attrs()->get(var.name)) {
|
||||
if (countCalls) attrSelects[j->pos]++;
|
||||
return j->value;
|
||||
}
|
||||
|
@ -1166,7 +1162,7 @@ inline bool EvalState::evalBool(Env & env, Expr * e, const PosIdx pos, std::stri
|
|||
showType(v),
|
||||
ValuePrinter(*this, v, errorPrintOptions)
|
||||
).atPos(pos).withFrame(env, *e).debugThrow();
|
||||
return v.boolean;
|
||||
return v.boolean();
|
||||
} catch (Error & e) {
|
||||
e.addTrace(positions[pos], errorCtx);
|
||||
throw;
|
||||
|
@ -1234,8 +1230,9 @@ Env * ExprAttrs::buildInheritFromEnv(EvalState & state, Env & up)
|
|||
|
||||
void ExprAttrs::eval(EvalState & state, Env & env, Value & v)
|
||||
{
|
||||
v.mkAttrs(state.buildBindings(attrs.size() + dynamicAttrs.size()).finish());
|
||||
auto bindings = state.buildBindings(attrs.size() + dynamicAttrs.size());
|
||||
auto dynamicEnv = &env;
|
||||
bool sort = false;
|
||||
|
||||
if (recursive) {
|
||||
/* Create a new environment that contains the attributes in
|
||||
|
@ -1260,7 +1257,7 @@ void ExprAttrs::eval(EvalState & state, Env & env, Value & v)
|
|||
} else
|
||||
vAttr = i.second.e->maybeThunk(state, *i.second.chooseByKind(&env2, &env, inheritEnv));
|
||||
env2.values[displ++] = vAttr;
|
||||
v.attrs->push_back(Attr(i.first, vAttr, i.second.pos));
|
||||
bindings.insert(i.first, vAttr, i.second.pos);
|
||||
}
|
||||
|
||||
/* If the rec contains an attribute called `__overrides', then
|
||||
|
@ -1272,32 +1269,28 @@ void ExprAttrs::eval(EvalState & state, Env & env, Value & v)
|
|||
been substituted into the bodies of the other attributes.
|
||||
Hence we need __overrides.) */
|
||||
if (hasOverrides) {
|
||||
Value * vOverrides = (*v.attrs)[overrides->second.displ].value;
|
||||
Value * vOverrides = (*bindings.bindings)[overrides->second.displ].value;
|
||||
state.forceAttrs(*vOverrides, [&]() { return vOverrides->determinePos(noPos); }, "while evaluating the `__overrides` attribute");
|
||||
Bindings * newBnds = state.allocBindings(v.attrs->capacity() + vOverrides->attrs->size());
|
||||
for (auto & i : *v.attrs)
|
||||
newBnds->push_back(i);
|
||||
for (auto & i : *vOverrides->attrs) {
|
||||
bindings.grow(state.allocBindings(bindings.capacity() + vOverrides->attrs()->size()));
|
||||
for (auto & i : *vOverrides->attrs()) {
|
||||
AttrDefs::iterator j = attrs.find(i.name);
|
||||
if (j != attrs.end()) {
|
||||
(*newBnds)[j->second.displ] = i;
|
||||
(*bindings.bindings)[j->second.displ] = i;
|
||||
env2.values[j->second.displ] = i.value;
|
||||
} else
|
||||
newBnds->push_back(i);
|
||||
bindings.push_back(i);
|
||||
}
|
||||
newBnds->sort();
|
||||
v.attrs = newBnds;
|
||||
sort = true;
|
||||
}
|
||||
}
|
||||
|
||||
else {
|
||||
Env * inheritEnv = inheritFromExprs ? buildInheritFromEnv(state, env) : nullptr;
|
||||
for (auto & i : attrs) {
|
||||
v.attrs->push_back(Attr(
|
||||
for (auto & i : attrs)
|
||||
bindings.insert(
|
||||
i.first,
|
||||
i.second.e->maybeThunk(state, *i.second.chooseByKind(&env, &env, inheritEnv)),
|
||||
i.second.pos));
|
||||
}
|
||||
i.second.pos);
|
||||
}
|
||||
|
||||
/* Dynamic attrs apply *after* rec and __overrides. */
|
||||
|
@ -1309,17 +1302,21 @@ void ExprAttrs::eval(EvalState & state, Env & env, Value & v)
|
|||
continue;
|
||||
state.forceStringNoCtx(nameVal, i.pos, "while evaluating the name of a dynamic attribute");
|
||||
auto nameSym = state.symbols.create(nameVal.string_view());
|
||||
Bindings::iterator j = v.attrs->find(nameSym);
|
||||
if (j != v.attrs->end())
|
||||
if (sort)
|
||||
// FIXME: inefficient
|
||||
bindings.bindings->sort();
|
||||
if (auto j = bindings.bindings->get(nameSym))
|
||||
state.error<EvalError>("dynamic attribute '%1%' already defined at %2%", state.symbols[nameSym], state.positions[j->pos]).atPos(i.pos).withFrame(env, *this).debugThrow();
|
||||
|
||||
i.valueExpr->setName(nameSym);
|
||||
/* Keep sorted order so find can catch duplicates */
|
||||
v.attrs->push_back(Attr(nameSym, i.valueExpr->maybeThunk(state, *dynamicEnv), i.pos));
|
||||
v.attrs->sort(); // FIXME: inefficient
|
||||
bindings.insert(nameSym, i.valueExpr->maybeThunk(state, *dynamicEnv), i.pos);
|
||||
sort = true;
|
||||
}
|
||||
|
||||
v.attrs->pos = pos;
|
||||
bindings.bindings->pos = pos;
|
||||
|
||||
v.mkAttrs(sort ? bindings.finish() : bindings.alreadySorted());
|
||||
}
|
||||
|
||||
|
||||
|
@ -1425,21 +1422,21 @@ void ExprSelect::eval(EvalState & state, Env & env, Value & v)
|
|||
|
||||
for (auto & i : attrPath) {
|
||||
state.nrLookups++;
|
||||
Bindings::iterator j;
|
||||
const Attr * j;
|
||||
auto name = getName(i, state, env);
|
||||
if (def) {
|
||||
state.forceValue(*vAttrs, pos);
|
||||
if (vAttrs->type() != nAttrs ||
|
||||
(j = vAttrs->attrs->find(name)) == vAttrs->attrs->end())
|
||||
!(j = vAttrs->attrs()->get(name)))
|
||||
{
|
||||
def->eval(state, env, v);
|
||||
return;
|
||||
}
|
||||
} else {
|
||||
state.forceAttrs(*vAttrs, pos, "while selecting an attribute");
|
||||
if ((j = vAttrs->attrs->find(name)) == vAttrs->attrs->end()) {
|
||||
if (!(j = vAttrs->attrs()->get(name))) {
|
||||
std::set<std::string> allAttrNames;
|
||||
for (auto & attr : *vAttrs->attrs)
|
||||
for (auto & attr : *vAttrs->attrs())
|
||||
allAttrNames.insert(state.symbols[attr.name]);
|
||||
auto suggestions = Suggestions::bestMatches(allAttrNames, state.symbols[name]);
|
||||
state.error<EvalError>("attribute '%1%' missing", state.symbols[name])
|
||||
|
@ -1477,15 +1474,15 @@ void ExprOpHasAttr::eval(EvalState & state, Env & env, Value & v)
|
|||
|
||||
for (auto & i : attrPath) {
|
||||
state.forceValue(*vAttrs, getPos());
|
||||
Bindings::iterator j;
|
||||
const Attr * j;
|
||||
auto name = getName(i, state, env);
|
||||
if (vAttrs->type() != nAttrs ||
|
||||
(j = vAttrs->attrs->find(name)) == vAttrs->attrs->end())
|
||||
if (vAttrs->type() == nAttrs &&
|
||||
(j = vAttrs->attrs()->get(name)))
|
||||
{
|
||||
vAttrs = j->value;
|
||||
} else {
|
||||
v.mkBool(false);
|
||||
return;
|
||||
} else {
|
||||
vAttrs = j->value;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -1537,19 +1534,19 @@ void EvalState::callFunction(Value & fun, size_t nrArgs, Value * * args, Value &
|
|||
}
|
||||
};
|
||||
|
||||
Attr * functor;
|
||||
const Attr * functor;
|
||||
|
||||
while (nrArgs > 0) {
|
||||
|
||||
if (vCur.isLambda()) {
|
||||
|
||||
ExprLambda & lambda(*vCur.lambda.fun);
|
||||
ExprLambda & lambda(*vCur.payload.lambda.fun);
|
||||
|
||||
auto size =
|
||||
(!lambda.arg ? 0 : 1) +
|
||||
(lambda.hasFormals() ? lambda.formals->formals.size() : 0);
|
||||
Env & env2(allocEnv(size));
|
||||
env2.up = vCur.lambda.env;
|
||||
env2.up = vCur.payload.lambda.env;
|
||||
|
||||
Displacement displ = 0;
|
||||
|
||||
|
@ -1571,7 +1568,7 @@ void EvalState::callFunction(Value & fun, size_t nrArgs, Value * * args, Value &
|
|||
argument has a default, use the default. */
|
||||
size_t attrsUsed = 0;
|
||||
for (auto & i : lambda.formals->formals) {
|
||||
auto j = args[0]->attrs->get(i.name);
|
||||
auto j = args[0]->attrs()->get(i.name);
|
||||
if (!j) {
|
||||
if (!i.def) {
|
||||
error<TypeError>("function '%1%' called without required argument '%2%'",
|
||||
|
@ -1579,7 +1576,7 @@ void EvalState::callFunction(Value & fun, size_t nrArgs, Value * * args, Value &
|
|||
symbols[i.name])
|
||||
.atPos(lambda.pos)
|
||||
.withTrace(pos, "from call site")
|
||||
.withFrame(*fun.lambda.env, lambda)
|
||||
.withFrame(*fun.payload.lambda.env, lambda)
|
||||
.debugThrow();
|
||||
}
|
||||
env2.values[displ++] = i.def->maybeThunk(*this, env2);
|
||||
|
@ -1591,10 +1588,10 @@ void EvalState::callFunction(Value & fun, size_t nrArgs, Value * * args, Value &
|
|||
|
||||
/* Check that each actual argument is listed as a formal
|
||||
argument (unless the attribute match specifies a `...'). */
|
||||
if (!lambda.formals->ellipsis && attrsUsed != args[0]->attrs->size()) {
|
||||
if (!lambda.formals->ellipsis && attrsUsed != args[0]->attrs()->size()) {
|
||||
/* Nope, so show the first unexpected argument to the
|
||||
user. */
|
||||
for (auto & i : *args[0]->attrs)
|
||||
for (auto & i : *args[0]->attrs())
|
||||
if (!lambda.formals->has(i.name)) {
|
||||
std::set<std::string> formalNames;
|
||||
for (auto & formal : lambda.formals->formals)
|
||||
|
@ -1606,7 +1603,7 @@ void EvalState::callFunction(Value & fun, size_t nrArgs, Value * * args, Value &
|
|||
.atPos(lambda.pos)
|
||||
.withTrace(pos, "from call site")
|
||||
.withSuggestions(suggestions)
|
||||
.withFrame(*fun.lambda.env, lambda)
|
||||
.withFrame(*fun.payload.lambda.env, lambda)
|
||||
.debugThrow();
|
||||
}
|
||||
abort(); // can't happen
|
||||
|
@ -1648,7 +1645,7 @@ void EvalState::callFunction(Value & fun, size_t nrArgs, Value * * args, Value &
|
|||
|
||||
else if (vCur.isPrimOp()) {
|
||||
|
||||
size_t argsLeft = vCur.primOp->arity;
|
||||
size_t argsLeft = vCur.primOp()->arity;
|
||||
|
||||
if (nrArgs < argsLeft) {
|
||||
/* We don't have enough arguments, so create a tPrimOpApp chain. */
|
||||
|
@ -1656,7 +1653,7 @@ void EvalState::callFunction(Value & fun, size_t nrArgs, Value * * args, Value &
|
|||
return;
|
||||
} else {
|
||||
/* We have all the arguments, so call the primop. */
|
||||
auto * fn = vCur.primOp;
|
||||
auto * fn = vCur.primOp();
|
||||
|
||||
nrPrimOpCalls++;
|
||||
if (countCalls) primOpCalls[fn->name]++;
|
||||
|
@ -1680,10 +1677,10 @@ void EvalState::callFunction(Value & fun, size_t nrArgs, Value * * args, Value &
|
|||
Value * primOp = &vCur;
|
||||
while (primOp->isPrimOpApp()) {
|
||||
argsDone++;
|
||||
primOp = primOp->primOpApp.left;
|
||||
primOp = primOp->payload.primOpApp.left;
|
||||
}
|
||||
assert(primOp->isPrimOp());
|
||||
auto arity = primOp->primOp->arity;
|
||||
auto arity = primOp->primOp()->arity;
|
||||
auto argsLeft = arity - argsDone;
|
||||
|
||||
if (nrArgs < argsLeft) {
|
||||
|
@ -1696,13 +1693,13 @@ void EvalState::callFunction(Value & fun, size_t nrArgs, Value * * args, Value &
|
|||
|
||||
Value * vArgs[maxPrimOpArity];
|
||||
auto n = argsDone;
|
||||
for (Value * arg = &vCur; arg->isPrimOpApp(); arg = arg->primOpApp.left)
|
||||
vArgs[--n] = arg->primOpApp.right;
|
||||
for (Value * arg = &vCur; arg->isPrimOpApp(); arg = arg->payload.primOpApp.left)
|
||||
vArgs[--n] = arg->payload.primOpApp.right;
|
||||
|
||||
for (size_t i = 0; i < argsLeft; ++i)
|
||||
vArgs[argsDone + i] = args[i];
|
||||
|
||||
auto fn = primOp->primOp;
|
||||
auto fn = primOp->primOp();
|
||||
nrPrimOpCalls++;
|
||||
if (countCalls) primOpCalls[fn->name]++;
|
||||
|
||||
|
@ -1723,7 +1720,7 @@ void EvalState::callFunction(Value & fun, size_t nrArgs, Value * * args, Value &
|
|||
}
|
||||
}
|
||||
|
||||
else if (vCur.type() == nAttrs && (functor = vCur.attrs->get(sFunctor))) {
|
||||
else if (vCur.type() == nAttrs && (functor = vCur.attrs()->get(sFunctor))) {
|
||||
/* 'vCur' may be allocated on the stack of the calling
|
||||
function, but for functors we may keep a reference, so
|
||||
heap-allocate a copy and use that instead. */
|
||||
|
@ -1798,8 +1795,8 @@ void EvalState::autoCallFunction(Bindings & args, Value & fun, Value & res)
|
|||
forceValue(fun, pos);
|
||||
|
||||
if (fun.type() == nAttrs) {
|
||||
auto found = fun.attrs->find(sFunctor);
|
||||
if (found != fun.attrs->end()) {
|
||||
auto found = fun.attrs()->find(sFunctor);
|
||||
if (found != fun.attrs()->end()) {
|
||||
Value * v = allocValue();
|
||||
callFunction(*found->value, fun, *v, pos);
|
||||
forceValue(*v, pos);
|
||||
|
@ -1807,14 +1804,14 @@ void EvalState::autoCallFunction(Bindings & args, Value & fun, Value & res)
|
|||
}
|
||||
}
|
||||
|
||||
if (!fun.isLambda() || !fun.lambda.fun->hasFormals()) {
|
||||
if (!fun.isLambda() || !fun.payload.lambda.fun->hasFormals()) {
|
||||
res = fun;
|
||||
return;
|
||||
}
|
||||
|
||||
auto attrs = buildBindings(std::max(static_cast<uint32_t>(fun.lambda.fun->formals->formals.size()), args.size()));
|
||||
auto attrs = buildBindings(std::max(static_cast<uint32_t>(fun.payload.lambda.fun->formals->formals.size()), args.size()));
|
||||
|
||||
if (fun.lambda.fun->formals->ellipsis) {
|
||||
if (fun.payload.lambda.fun->formals->ellipsis) {
|
||||
// If the formals have an ellipsis (eg the function accepts extra args) pass
|
||||
// all available automatic arguments (which includes arguments specified on
|
||||
// the command line via --arg/--argstr)
|
||||
|
@ -1822,9 +1819,9 @@ void EvalState::autoCallFunction(Bindings & args, Value & fun, Value & res)
|
|||
attrs.insert(v);
|
||||
} else {
|
||||
// Otherwise, only pass the arguments that the function accepts
|
||||
for (auto & i : fun.lambda.fun->formals->formals) {
|
||||
Bindings::iterator j = args.find(i.name);
|
||||
if (j != args.end()) {
|
||||
for (auto & i : fun.payload.lambda.fun->formals->formals) {
|
||||
auto j = args.get(i.name);
|
||||
if (j) {
|
||||
attrs.insert(*j);
|
||||
} else if (!i.def) {
|
||||
error<MissingArgumentError>(R"(cannot evaluate a function that has an argument without a value ('%1%')
|
||||
|
@ -1832,7 +1829,7 @@ Nix attempted to evaluate a function as a top level expression; in
|
|||
this case it must have its arguments supplied either by default
|
||||
values, or passed explicitly with '--arg' or '--argstr'. See
|
||||
https://nixos.org/manual/nix/stable/language/constructs.html#functions.)", symbols[i.name])
|
||||
.atPos(i.pos).withFrame(*fun.lambda.env, *fun.lambda.fun).debugThrow();
|
||||
.atPos(i.pos).withFrame(*fun.payload.lambda.env, *fun.payload.lambda.fun).debugThrow();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -1917,17 +1914,17 @@ void ExprOpUpdate::eval(EvalState & state, Env & env, Value & v)
|
|||
|
||||
state.nrOpUpdates++;
|
||||
|
||||
if (v1.attrs->size() == 0) { v = v2; return; }
|
||||
if (v2.attrs->size() == 0) { v = v1; return; }
|
||||
if (v1.attrs()->size() == 0) { v = v2; return; }
|
||||
if (v2.attrs()->size() == 0) { v = v1; return; }
|
||||
|
||||
auto attrs = state.buildBindings(v1.attrs->size() + v2.attrs->size());
|
||||
auto attrs = state.buildBindings(v1.attrs()->size() + v2.attrs()->size());
|
||||
|
||||
/* Merge the sets, preferring values from the second set. Make
|
||||
sure to keep the resulting vector in sorted order. */
|
||||
Bindings::iterator i = v1.attrs->begin();
|
||||
Bindings::iterator j = v2.attrs->begin();
|
||||
auto i = v1.attrs()->begin();
|
||||
auto j = v2.attrs()->begin();
|
||||
|
||||
while (i != v1.attrs->end() && j != v2.attrs->end()) {
|
||||
while (i != v1.attrs()->end() && j != v2.attrs()->end()) {
|
||||
if (i->name == j->name) {
|
||||
attrs.insert(*j);
|
||||
++i; ++j;
|
||||
|
@ -1938,12 +1935,12 @@ void ExprOpUpdate::eval(EvalState & state, Env & env, Value & v)
|
|||
attrs.insert(*j++);
|
||||
}
|
||||
|
||||
while (i != v1.attrs->end()) attrs.insert(*i++);
|
||||
while (j != v2.attrs->end()) attrs.insert(*j++);
|
||||
while (i != v1.attrs()->end()) attrs.insert(*i++);
|
||||
while (j != v2.attrs()->end()) attrs.insert(*j++);
|
||||
|
||||
v.mkAttrs(attrs.alreadySorted());
|
||||
|
||||
state.nrOpUpdateValuesCopied += v.attrs->size();
|
||||
state.nrOpUpdateValuesCopied += v.attrs()->size();
|
||||
}
|
||||
|
||||
|
||||
|
@ -2035,19 +2032,19 @@ void ExprConcatStrings::eval(EvalState & state, Env & env, Value & v)
|
|||
|
||||
if (firstType == nInt) {
|
||||
if (vTmp.type() == nInt) {
|
||||
n += vTmp.integer;
|
||||
n += vTmp.integer();
|
||||
} else if (vTmp.type() == nFloat) {
|
||||
// Upgrade the type from int to float;
|
||||
firstType = nFloat;
|
||||
nf = n;
|
||||
nf += vTmp.fpoint;
|
||||
nf += vTmp.fpoint();
|
||||
} else
|
||||
state.error<EvalError>("cannot add %1% to an integer", showType(vTmp)).atPos(i_pos).withFrame(env, *this).debugThrow();
|
||||
} else if (firstType == nFloat) {
|
||||
if (vTmp.type() == nInt) {
|
||||
nf += vTmp.integer;
|
||||
nf += vTmp.integer();
|
||||
} else if (vTmp.type() == nFloat) {
|
||||
nf += vTmp.fpoint;
|
||||
nf += vTmp.fpoint();
|
||||
} else
|
||||
state.error<EvalError>("cannot add %1% to a float", showType(vTmp)).atPos(i_pos).withFrame(env, *this).debugThrow();
|
||||
} else {
|
||||
|
@ -2120,11 +2117,11 @@ void EvalState::forceValueDeep(Value & v)
|
|||
forceValue(v, v.determinePos(noPos));
|
||||
|
||||
if (v.type() == nAttrs) {
|
||||
for (auto & i : *v.attrs)
|
||||
for (auto & i : *v.attrs())
|
||||
try {
|
||||
// If the value is a thunk, we're evaling. Otherwise no trace necessary.
|
||||
auto dts = debugRepl && i.value->isThunk()
|
||||
? makeDebugTraceStacker(*this, *i.value->thunk.expr, *i.value->thunk.env, positions[i.pos],
|
||||
? makeDebugTraceStacker(*this, *i.value->payload.thunk.expr, *i.value->payload.thunk.env, positions[i.pos],
|
||||
"while evaluating the attribute '%1%'", symbols[i.name])
|
||||
: nullptr;
|
||||
|
||||
|
@ -2155,13 +2152,13 @@ NixInt EvalState::forceInt(Value & v, const PosIdx pos, std::string_view errorCt
|
|||
showType(v),
|
||||
ValuePrinter(*this, v, errorPrintOptions)
|
||||
).atPos(pos).debugThrow();
|
||||
return v.integer;
|
||||
return v.integer();
|
||||
} catch (Error & e) {
|
||||
e.addTrace(positions[pos], errorCtx);
|
||||
throw;
|
||||
}
|
||||
|
||||
return v.integer;
|
||||
return v.integer();
|
||||
}
|
||||
|
||||
|
||||
|
@ -2170,14 +2167,14 @@ NixFloat EvalState::forceFloat(Value & v, const PosIdx pos, std::string_view err
|
|||
try {
|
||||
forceValue(v, pos);
|
||||
if (v.type() == nInt)
|
||||
return v.integer;
|
||||
return v.integer();
|
||||
else if (v.type() != nFloat)
|
||||
error<TypeError>(
|
||||
"expected a float but found %1%: %2%",
|
||||
showType(v),
|
||||
ValuePrinter(*this, v, errorPrintOptions)
|
||||
).atPos(pos).debugThrow();
|
||||
return v.fpoint;
|
||||
return v.fpoint();
|
||||
} catch (Error & e) {
|
||||
e.addTrace(positions[pos], errorCtx);
|
||||
throw;
|
||||
|
@ -2195,19 +2192,19 @@ bool EvalState::forceBool(Value & v, const PosIdx pos, std::string_view errorCtx
|
|||
showType(v),
|
||||
ValuePrinter(*this, v, errorPrintOptions)
|
||||
).atPos(pos).debugThrow();
|
||||
return v.boolean;
|
||||
return v.boolean();
|
||||
} catch (Error & e) {
|
||||
e.addTrace(positions[pos], errorCtx);
|
||||
throw;
|
||||
}
|
||||
|
||||
return v.boolean;
|
||||
return v.boolean();
|
||||
}
|
||||
|
||||
|
||||
bool EvalState::isFunctor(Value & fun)
|
||||
{
|
||||
return fun.type() == nAttrs && fun.attrs->find(sFunctor) != fun.attrs->end();
|
||||
return fun.type() == nAttrs && fun.attrs()->find(sFunctor) != fun.attrs()->end();
|
||||
}
|
||||
|
||||
|
||||
|
@ -2248,8 +2245,8 @@ std::string_view EvalState::forceString(Value & v, const PosIdx pos, std::string
|
|||
|
||||
void copyContext(const Value & v, NixStringContext & context)
|
||||
{
|
||||
if (v.string.context)
|
||||
for (const char * * p = v.string.context; *p; ++p)
|
||||
if (v.payload.string.context)
|
||||
for (const char * * p = v.payload.string.context; *p; ++p)
|
||||
context.insert(NixStringContextElem::parse(*p));
|
||||
}
|
||||
|
||||
|
@ -2275,8 +2272,8 @@ std::string_view EvalState::forceStringNoCtx(Value & v, const PosIdx pos, std::s
|
|||
bool EvalState::isDerivation(Value & v)
|
||||
{
|
||||
if (v.type() != nAttrs) return false;
|
||||
Bindings::iterator i = v.attrs->find(sType);
|
||||
if (i == v.attrs->end()) return false;
|
||||
auto i = v.attrs()->get(sType);
|
||||
if (!i) return false;
|
||||
forceValue(*i->value, i->pos);
|
||||
if (i->value->type() != nString) return false;
|
||||
return i->value->string_view().compare("derivation") == 0;
|
||||
|
@ -2286,8 +2283,8 @@ bool EvalState::isDerivation(Value & v)
|
|||
std::optional<std::string> EvalState::tryAttrsToString(const PosIdx pos, Value & v,
|
||||
NixStringContext & context, bool coerceMore, bool copyToStore)
|
||||
{
|
||||
auto i = v.attrs->find(sToString);
|
||||
if (i != v.attrs->end()) {
|
||||
auto i = v.attrs()->find(sToString);
|
||||
if (i != v.attrs()->end()) {
|
||||
Value v1;
|
||||
callFunction(*i->value, v, v1, pos);
|
||||
return coerceToString(pos, v1, context,
|
||||
|
@ -2319,7 +2316,7 @@ BackedStringView EvalState::coerceToString(
|
|||
!canonicalizePath && !copyToStore
|
||||
? // FIXME: hack to preserve path literals that end in a
|
||||
// slash, as in /foo/${x}.
|
||||
v._path.path
|
||||
v.payload.path.path
|
||||
: copyToStore
|
||||
? store->printStorePath(copyPathToStore(context, v.path()))
|
||||
: std::string(v.path().path.abs());
|
||||
|
@ -2329,8 +2326,8 @@ BackedStringView EvalState::coerceToString(
|
|||
auto maybeString = tryAttrsToString(pos, v, context, coerceMore, copyToStore);
|
||||
if (maybeString)
|
||||
return std::move(*maybeString);
|
||||
auto i = v.attrs->find(sOutPath);
|
||||
if (i == v.attrs->end()) {
|
||||
auto i = v.attrs()->find(sOutPath);
|
||||
if (i == v.attrs()->end()) {
|
||||
error<TypeError>(
|
||||
"cannot coerce %1% to a string: %2%",
|
||||
showType(v),
|
||||
|
@ -2345,7 +2342,7 @@ BackedStringView EvalState::coerceToString(
|
|||
|
||||
if (v.type() == nExternal) {
|
||||
try {
|
||||
return v.external->coerceToString(*this, pos, context, coerceMore, copyToStore);
|
||||
return v.external()->coerceToString(*this, pos, context, coerceMore, copyToStore);
|
||||
} catch (Error & e) {
|
||||
e.addTrace(nullptr, errorCtx);
|
||||
throw;
|
||||
|
@ -2355,10 +2352,10 @@ BackedStringView EvalState::coerceToString(
|
|||
if (coerceMore) {
|
||||
/* Note that `false' is represented as an empty string for
|
||||
shell scripting convenience, just like `null'. */
|
||||
if (v.type() == nBool && v.boolean) return "1";
|
||||
if (v.type() == nBool && !v.boolean) return "";
|
||||
if (v.type() == nInt) return std::to_string(v.integer);
|
||||
if (v.type() == nFloat) return std::to_string(v.fpoint);
|
||||
if (v.type() == nBool && v.boolean()) return "1";
|
||||
if (v.type() == nBool && !v.boolean()) return "";
|
||||
if (v.type() == nInt) return std::to_string(v.integer());
|
||||
if (v.type() == nFloat) return std::to_string(v.fpoint());
|
||||
if (v.type() == nNull) return "";
|
||||
|
||||
if (v.isList()) {
|
||||
|
@ -2437,8 +2434,8 @@ SourcePath EvalState::coerceToPath(const PosIdx pos, Value & v, NixStringContext
|
|||
/* Similarly, handle __toString where the result may be a path
|
||||
value. */
|
||||
if (v.type() == nAttrs) {
|
||||
auto i = v.attrs->find(sToString);
|
||||
if (i != v.attrs->end()) {
|
||||
auto i = v.attrs()->find(sToString);
|
||||
if (i != v.attrs()->end()) {
|
||||
Value v1;
|
||||
callFunction(*i->value, v, v1, pos);
|
||||
return coerceToPath(pos, v1, context, errorCtx);
|
||||
|
@ -2532,19 +2529,19 @@ bool EvalState::eqValues(Value & v1, Value & v2, const PosIdx pos, std::string_v
|
|||
|
||||
// Special case type-compatibility between float and int
|
||||
if (v1.type() == nInt && v2.type() == nFloat)
|
||||
return v1.integer == v2.fpoint;
|
||||
return v1.integer() == v2.fpoint();
|
||||
if (v1.type() == nFloat && v2.type() == nInt)
|
||||
return v1.fpoint == v2.integer;
|
||||
return v1.fpoint() == v2.integer();
|
||||
|
||||
// All other types are not compatible with each other.
|
||||
if (v1.type() != v2.type()) return false;
|
||||
|
||||
switch (v1.type()) {
|
||||
case nInt:
|
||||
return v1.integer == v2.integer;
|
||||
return v1.integer() == v2.integer();
|
||||
|
||||
case nBool:
|
||||
return v1.boolean == v2.boolean;
|
||||
return v1.boolean() == v2.boolean();
|
||||
|
||||
case nString:
|
||||
return strcmp(v1.c_str(), v2.c_str()) == 0;
|
||||
|
@ -2552,8 +2549,8 @@ bool EvalState::eqValues(Value & v1, Value & v2, const PosIdx pos, std::string_v
|
|||
case nPath:
|
||||
return
|
||||
// FIXME: compare accessors by their fingerprint.
|
||||
v1._path.accessor == v2._path.accessor
|
||||
&& strcmp(v1._path.path, v2._path.path) == 0;
|
||||
v1.payload.path.accessor == v2.payload.path.accessor
|
||||
&& strcmp(v1.payload.path.path, v2.payload.path.path) == 0;
|
||||
|
||||
case nNull:
|
||||
return true;
|
||||
|
@ -2568,17 +2565,17 @@ bool EvalState::eqValues(Value & v1, Value & v2, const PosIdx pos, std::string_v
|
|||
/* If both sets denote a derivation (type = "derivation"),
|
||||
then compare their outPaths. */
|
||||
if (isDerivation(v1) && isDerivation(v2)) {
|
||||
Bindings::iterator i = v1.attrs->find(sOutPath);
|
||||
Bindings::iterator j = v2.attrs->find(sOutPath);
|
||||
if (i != v1.attrs->end() && j != v2.attrs->end())
|
||||
auto i = v1.attrs()->get(sOutPath);
|
||||
auto j = v2.attrs()->get(sOutPath);
|
||||
if (i && j)
|
||||
return eqValues(*i->value, *j->value, pos, errorCtx);
|
||||
}
|
||||
|
||||
if (v1.attrs->size() != v2.attrs->size()) return false;
|
||||
if (v1.attrs()->size() != v2.attrs()->size()) return false;
|
||||
|
||||
/* Otherwise, compare the attributes one by one. */
|
||||
Bindings::iterator i, j;
|
||||
for (i = v1.attrs->begin(), j = v2.attrs->begin(); i != v1.attrs->end(); ++i, ++j)
|
||||
Bindings::const_iterator i, j;
|
||||
for (i = v1.attrs()->begin(), j = v2.attrs()->begin(); i != v1.attrs()->end(); ++i, ++j)
|
||||
if (i->name != j->name || !eqValues(*i->value, *j->value, pos, errorCtx))
|
||||
return false;
|
||||
|
||||
|
@ -2590,10 +2587,10 @@ bool EvalState::eqValues(Value & v1, Value & v2, const PosIdx pos, std::string_v
|
|||
return false;
|
||||
|
||||
case nExternal:
|
||||
return *v1.external == *v2.external;
|
||||
return *v1.external() == *v2.external();
|
||||
|
||||
case nFloat:
|
||||
return v1.fpoint == v2.fpoint;
|
||||
return v1.fpoint() == v2.fpoint();
|
||||
|
||||
case nThunk: // Must not be left by forceValue
|
||||
default:
|
||||
|
@ -2632,9 +2629,11 @@ void EvalState::maybePrintStats()
|
|||
|
||||
void EvalState::printStatistics()
|
||||
{
|
||||
#ifndef _WIN32 // TODO use portable implementation
|
||||
struct rusage buf;
|
||||
getrusage(RUSAGE_SELF, &buf);
|
||||
float cpuTime = buf.ru_utime.tv_sec + ((float) buf.ru_utime.tv_usec / 1000000);
|
||||
#endif
|
||||
|
||||
uint64_t bEnvs = nrEnvs * sizeof(Env) + nrValuesInEnvs * sizeof(Value *);
|
||||
uint64_t bLists = nrListElems * sizeof(Value *);
|
||||
|
@ -2651,7 +2650,9 @@ void EvalState::printStatistics()
|
|||
if (outPath != "-")
|
||||
fs.open(outPath, std::fstream::out);
|
||||
json topObj = json::object();
|
||||
#ifndef _WIN32 // TODO implement
|
||||
topObj["cpuTime"] = cpuTime;
|
||||
#endif
|
||||
topObj["envs"] = {
|
||||
{"number", nrEnvs},
|
||||
{"elements", nrValuesInEnvs},
|
||||
|
|
|
@ -94,7 +94,7 @@ struct PrimOp
|
|||
void check();
|
||||
};
|
||||
|
||||
std::ostream & operator<<(std::ostream & output, PrimOp & primOp);
|
||||
std::ostream & operator<<(std::ostream & output, const PrimOp & primOp);
|
||||
|
||||
/**
|
||||
* Info about a constant
|
||||
|
@ -161,6 +161,8 @@ struct DebugTrace {
|
|||
bool isError;
|
||||
};
|
||||
|
||||
// Don't want Windows function
|
||||
#undef SearchPath
|
||||
|
||||
class EvalState : public std::enable_shared_from_this<EvalState>
|
||||
{
|
||||
|
@ -643,9 +645,6 @@ public:
|
|||
inline Value * allocValue();
|
||||
inline Env & allocEnv(size_t size);
|
||||
|
||||
Value * allocAttr(Value & vAttrs, Symbol name);
|
||||
Value * allocAttr(Value & vAttrs, std::string_view name);
|
||||
|
||||
Bindings * allocBindings(size_t capacity);
|
||||
|
||||
BindingsBuilder buildBindings(size_t capacity)
|
||||
|
@ -733,10 +732,12 @@ public:
|
|||
bool fullGC();
|
||||
|
||||
/**
|
||||
* Realise the given context, and return a mapping from the placeholders
|
||||
* used to construct the associated value to their final store path
|
||||
* Realise the given context
|
||||
* @param[in] context the context to realise
|
||||
* @param[out] maybePaths if not nullptr, all built or referenced store paths will be added to this set
|
||||
* @return a mapping from the placeholders used to construct the associated value to their final store path.
|
||||
*/
|
||||
[[nodiscard]] StringMap realiseContext(const NixStringContext & context);
|
||||
[[nodiscard]] StringMap realiseContext(const NixStringContext & context, StorePathSet * maybePaths = nullptr, bool isIFD = true);
|
||||
|
||||
/* Call the binary path filter predicate used builtins.path etc. */
|
||||
bool callPathFilter(
|
||||
|
|
|
@ -111,7 +111,7 @@ static FlakeInput parseFlakeInput(EvalState & state,
|
|||
fetchers::Attrs attrs;
|
||||
std::optional<std::string> url;
|
||||
|
||||
for (nix::Attr attr : *(value->attrs)) {
|
||||
for (auto & attr : *value->attrs()) {
|
||||
try {
|
||||
if (attr.name == sUrl) {
|
||||
expectType(state, nString, *attr.value, attr.pos);
|
||||
|
@ -119,7 +119,7 @@ static FlakeInput parseFlakeInput(EvalState & state,
|
|||
attrs.emplace("url", *url);
|
||||
} else if (attr.name == sFlake) {
|
||||
expectType(state, nBool, *attr.value, attr.pos);
|
||||
input.isFlake = attr.value->boolean;
|
||||
input.isFlake = attr.value->boolean();
|
||||
} else if (attr.name == sInputs) {
|
||||
input.overrides = parseFlakeInputs(state, attr.value, attr.pos, baseDir, lockRootPath);
|
||||
} else if (attr.name == sFollows) {
|
||||
|
@ -136,10 +136,10 @@ static FlakeInput parseFlakeInput(EvalState & state,
|
|||
attrs.emplace(state.symbols[attr.name], attr.value->c_str());
|
||||
break;
|
||||
case nBool:
|
||||
attrs.emplace(state.symbols[attr.name], Explicit<bool> { attr.value->boolean });
|
||||
attrs.emplace(state.symbols[attr.name], Explicit<bool> { attr.value->boolean() });
|
||||
break;
|
||||
case nInt:
|
||||
attrs.emplace(state.symbols[attr.name], (long unsigned int) attr.value->integer);
|
||||
attrs.emplace(state.symbols[attr.name], (long unsigned int) attr.value->integer());
|
||||
break;
|
||||
default:
|
||||
if (attr.name == state.symbols.create("publicKeys")) {
|
||||
|
@ -189,7 +189,7 @@ static std::map<FlakeId, FlakeInput> parseFlakeInputs(
|
|||
|
||||
expectType(state, nAttrs, *value, pos);
|
||||
|
||||
for (nix::Attr & inputAttr : *(*value).attrs) {
|
||||
for (auto & inputAttr : *value->attrs()) {
|
||||
inputs.emplace(state.symbols[inputAttr.name],
|
||||
parseFlakeInput(state,
|
||||
state.symbols[inputAttr.name],
|
||||
|
@ -223,23 +223,23 @@ static Flake readFlake(
|
|||
.path = flakePath,
|
||||
};
|
||||
|
||||
if (auto description = vInfo.attrs->get(state.sDescription)) {
|
||||
if (auto description = vInfo.attrs()->get(state.sDescription)) {
|
||||
expectType(state, nString, *description->value, description->pos);
|
||||
flake.description = description->value->c_str();
|
||||
}
|
||||
|
||||
auto sInputs = state.symbols.create("inputs");
|
||||
|
||||
if (auto inputs = vInfo.attrs->get(sInputs))
|
||||
if (auto inputs = vInfo.attrs()->get(sInputs))
|
||||
flake.inputs = parseFlakeInputs(state, inputs->value, inputs->pos, flakePath.parent().path.abs(), lockRootPath); // FIXME
|
||||
|
||||
auto sOutputs = state.symbols.create("outputs");
|
||||
|
||||
if (auto outputs = vInfo.attrs->get(sOutputs)) {
|
||||
if (auto outputs = vInfo.attrs()->get(sOutputs)) {
|
||||
expectType(state, nFunction, *outputs->value, outputs->pos);
|
||||
|
||||
if (outputs->value->isLambda() && outputs->value->lambda.fun->hasFormals()) {
|
||||
for (auto & formal : outputs->value->lambda.fun->formals->formals) {
|
||||
if (outputs->value->isLambda() && outputs->value->payload.lambda.fun->hasFormals()) {
|
||||
for (auto & formal : outputs->value->payload.lambda.fun->formals->formals) {
|
||||
if (formal.name != state.sSelf)
|
||||
flake.inputs.emplace(state.symbols[formal.name], FlakeInput {
|
||||
.ref = parseFlakeRef(state.symbols[formal.name])
|
||||
|
@ -252,10 +252,10 @@ static Flake readFlake(
|
|||
|
||||
auto sNixConfig = state.symbols.create("nixConfig");
|
||||
|
||||
if (auto nixConfig = vInfo.attrs->get(sNixConfig)) {
|
||||
if (auto nixConfig = vInfo.attrs()->get(sNixConfig)) {
|
||||
expectType(state, nAttrs, *nixConfig->value, nixConfig->pos);
|
||||
|
||||
for (auto & setting : *nixConfig->value->attrs) {
|
||||
for (auto & setting : *nixConfig->value->attrs()) {
|
||||
forceTrivialValue(state, *setting.value, setting.pos);
|
||||
if (setting.value->type() == nString)
|
||||
flake.config.settings.emplace(
|
||||
|
@ -291,7 +291,7 @@ static Flake readFlake(
|
|||
}
|
||||
}
|
||||
|
||||
for (auto & attr : *vInfo.attrs) {
|
||||
for (auto & attr : *vInfo.attrs()) {
|
||||
if (attr.name != state.sDescription &&
|
||||
attr.name != sInputs &&
|
||||
attr.name != sOutputs &&
|
||||
|
@ -867,10 +867,13 @@ static RegisterPrimOp r3({
|
|||
Parse a flake reference, and return its exploded form.
|
||||
|
||||
For example:
|
||||
|
||||
```nix
|
||||
builtins.parseFlakeRef "github:NixOS/nixpkgs/23.05?dir=lib"
|
||||
```
|
||||
|
||||
evaluates to:
|
||||
|
||||
```nix
|
||||
{ dir = "lib"; owner = "NixOS"; ref = "23.05"; repo = "nixpkgs"; type = "github"; }
|
||||
```
|
||||
|
@ -889,14 +892,14 @@ static void prim_flakeRefToString(
|
|||
state.forceAttrs(*args[0], noPos,
|
||||
"while evaluating the argument passed to builtins.flakeRefToString");
|
||||
fetchers::Attrs attrs;
|
||||
for (const auto & attr : *args[0]->attrs) {
|
||||
for (const auto & attr : *args[0]->attrs()) {
|
||||
auto t = attr.value->type();
|
||||
if (t == nInt) {
|
||||
attrs.emplace(state.symbols[attr.name],
|
||||
(uint64_t) attr.value->integer);
|
||||
(uint64_t) attr.value->integer());
|
||||
} else if (t == nBool) {
|
||||
attrs.emplace(state.symbols[attr.name],
|
||||
Explicit<bool> { attr.value->boolean });
|
||||
Explicit<bool> { attr.value->boolean() });
|
||||
} else if (t == nString) {
|
||||
attrs.emplace(state.symbols[attr.name],
|
||||
std::string(attr.value->string_view()));
|
||||
|
@ -919,12 +922,15 @@ static RegisterPrimOp r4({
|
|||
Convert a flake reference from attribute set format to URL format.
|
||||
|
||||
For example:
|
||||
|
||||
```nix
|
||||
builtins.flakeRefToString {
|
||||
dir = "lib"; owner = "NixOS"; ref = "23.05"; repo = "nixpkgs"; type = "github";
|
||||
}
|
||||
```
|
||||
|
||||
evaluates to
|
||||
|
||||
```nix
|
||||
"github:NixOS/nixpkgs/23.05?dir=lib"
|
||||
```
|
||||
|
|
|
@ -11,7 +11,7 @@
|
|||
namespace nix {
|
||||
|
||||
|
||||
PackageInfo::PackageInfo(EvalState & state, std::string attrPath, Bindings * attrs)
|
||||
PackageInfo::PackageInfo(EvalState & state, std::string attrPath, const Bindings * attrs)
|
||||
: state(&state), attrs(attrs), attrPath(std::move(attrPath))
|
||||
{
|
||||
}
|
||||
|
@ -69,12 +69,11 @@ std::string PackageInfo::querySystem() const
|
|||
std::optional<StorePath> PackageInfo::queryDrvPath() const
|
||||
{
|
||||
if (!drvPath && attrs) {
|
||||
Bindings::iterator i = attrs->find(state->sDrvPath);
|
||||
NixStringContext context;
|
||||
if (i == attrs->end())
|
||||
drvPath = {std::nullopt};
|
||||
else
|
||||
if (auto i = attrs->get(state->sDrvPath))
|
||||
drvPath = {state->coerceToStorePath(i->pos, *i->value, context, "while evaluating the 'drvPath' attribute of a derivation")};
|
||||
else
|
||||
drvPath = {std::nullopt};
|
||||
}
|
||||
return drvPath.value_or(std::nullopt);
|
||||
}
|
||||
|
@ -91,7 +90,7 @@ StorePath PackageInfo::requireDrvPath() const
|
|||
StorePath PackageInfo::queryOutPath() const
|
||||
{
|
||||
if (!outPath && attrs) {
|
||||
Bindings::iterator i = attrs->find(state->sOutPath);
|
||||
auto i = attrs->find(state->sOutPath);
|
||||
NixStringContext context;
|
||||
if (i != attrs->end())
|
||||
outPath = state->coerceToStorePath(i->pos, *i->value, context, "while evaluating the output path of a derivation");
|
||||
|
@ -106,8 +105,8 @@ PackageInfo::Outputs PackageInfo::queryOutputs(bool withPaths, bool onlyOutputsT
|
|||
{
|
||||
if (outputs.empty()) {
|
||||
/* Get the ‘outputs’ list. */
|
||||
Bindings::iterator i;
|
||||
if (attrs && (i = attrs->find(state->sOutputs)) != attrs->end()) {
|
||||
const Attr * i;
|
||||
if (attrs && (i = attrs->get(state->sOutputs))) {
|
||||
state->forceList(*i->value, i->pos, "while evaluating the 'outputs' attribute of a derivation");
|
||||
|
||||
/* For each output... */
|
||||
|
@ -116,13 +115,13 @@ PackageInfo::Outputs PackageInfo::queryOutputs(bool withPaths, bool onlyOutputsT
|
|||
|
||||
if (withPaths) {
|
||||
/* Evaluate the corresponding set. */
|
||||
Bindings::iterator out = attrs->find(state->symbols.create(output));
|
||||
if (out == attrs->end()) continue; // FIXME: throw error?
|
||||
auto out = attrs->get(state->symbols.create(output));
|
||||
if (!out) continue; // FIXME: throw error?
|
||||
state->forceAttrs(*out->value, i->pos, "while evaluating an output of a derivation");
|
||||
|
||||
/* And evaluate its ‘outPath’ attribute. */
|
||||
Bindings::iterator outPath = out->value->attrs->find(state->sOutPath);
|
||||
if (outPath == out->value->attrs->end()) continue; // FIXME: throw error?
|
||||
auto outPath = out->value->attrs()->get(state->sOutPath);
|
||||
if (!outPath) continue; // FIXME: throw error?
|
||||
NixStringContext context;
|
||||
outputs.emplace(output, state->coerceToStorePath(outPath->pos, *outPath->value, context, "while evaluating an output path of a derivation"));
|
||||
} else
|
||||
|
@ -135,8 +134,8 @@ PackageInfo::Outputs PackageInfo::queryOutputs(bool withPaths, bool onlyOutputsT
|
|||
if (!onlyOutputsToInstall || !attrs)
|
||||
return outputs;
|
||||
|
||||
Bindings::iterator i;
|
||||
if (attrs && (i = attrs->find(state->sOutputSpecified)) != attrs->end() && state->forceBool(*i->value, i->pos, "while evaluating the 'outputSpecified' attribute of a derivation")) {
|
||||
const Attr * i;
|
||||
if (attrs && (i = attrs->get(state->sOutputSpecified)) && state->forceBool(*i->value, i->pos, "while evaluating the 'outputSpecified' attribute of a derivation")) {
|
||||
Outputs result;
|
||||
auto out = outputs.find(queryOutputName());
|
||||
if (out == outputs.end())
|
||||
|
@ -167,21 +166,21 @@ PackageInfo::Outputs PackageInfo::queryOutputs(bool withPaths, bool onlyOutputsT
|
|||
std::string PackageInfo::queryOutputName() const
|
||||
{
|
||||
if (outputName == "" && attrs) {
|
||||
Bindings::iterator i = attrs->find(state->sOutputName);
|
||||
outputName = i != attrs->end() ? state->forceStringNoCtx(*i->value, noPos, "while evaluating the output name of a derivation") : "";
|
||||
auto i = attrs->get(state->sOutputName);
|
||||
outputName = i ? state->forceStringNoCtx(*i->value, noPos, "while evaluating the output name of a derivation") : "";
|
||||
}
|
||||
return outputName;
|
||||
}
|
||||
|
||||
|
||||
Bindings * PackageInfo::getMeta()
|
||||
const Bindings * PackageInfo::getMeta()
|
||||
{
|
||||
if (meta) return meta;
|
||||
if (!attrs) return 0;
|
||||
Bindings::iterator a = attrs->find(state->sMeta);
|
||||
if (a == attrs->end()) return 0;
|
||||
auto a = attrs->get(state->sMeta);
|
||||
if (!a) return 0;
|
||||
state->forceAttrs(*a->value, a->pos, "while evaluating the 'meta' attribute of a derivation");
|
||||
meta = a->value->attrs;
|
||||
meta = a->value->attrs();
|
||||
return meta;
|
||||
}
|
||||
|
||||
|
@ -205,9 +204,8 @@ bool PackageInfo::checkMeta(Value & v)
|
|||
return true;
|
||||
}
|
||||
else if (v.type() == nAttrs) {
|
||||
Bindings::iterator i = v.attrs->find(state->sOutPath);
|
||||
if (i != v.attrs->end()) return false;
|
||||
for (auto & i : *v.attrs)
|
||||
if (v.attrs()->get(state->sOutPath)) return false;
|
||||
for (auto & i : *v.attrs())
|
||||
if (!checkMeta(*i.value)) return false;
|
||||
return true;
|
||||
}
|
||||
|
@ -219,8 +217,8 @@ bool PackageInfo::checkMeta(Value & v)
|
|||
Value * PackageInfo::queryMeta(const std::string & name)
|
||||
{
|
||||
if (!getMeta()) return 0;
|
||||
Bindings::iterator a = meta->find(state->symbols.create(name));
|
||||
if (a == meta->end() || !checkMeta(*a->value)) return 0;
|
||||
auto a = meta->get(state->symbols.create(name));
|
||||
if (!a || !checkMeta(*a->value)) return 0;
|
||||
return a->value;
|
||||
}
|
||||
|
||||
|
@ -237,7 +235,7 @@ NixInt PackageInfo::queryMetaInt(const std::string & name, NixInt def)
|
|||
{
|
||||
Value * v = queryMeta(name);
|
||||
if (!v) return def;
|
||||
if (v->type() == nInt) return v->integer;
|
||||
if (v->type() == nInt) return v->integer();
|
||||
if (v->type() == nString) {
|
||||
/* Backwards compatibility with before we had support for
|
||||
integer meta fields. */
|
||||
|
@ -251,7 +249,7 @@ NixFloat PackageInfo::queryMetaFloat(const std::string & name, NixFloat def)
|
|||
{
|
||||
Value * v = queryMeta(name);
|
||||
if (!v) return def;
|
||||
if (v->type() == nFloat) return v->fpoint;
|
||||
if (v->type() == nFloat) return v->fpoint();
|
||||
if (v->type() == nString) {
|
||||
/* Backwards compatibility with before we had support for
|
||||
float meta fields. */
|
||||
|
@ -266,7 +264,7 @@ bool PackageInfo::queryMetaBool(const std::string & name, bool def)
|
|||
{
|
||||
Value * v = queryMeta(name);
|
||||
if (!v) return def;
|
||||
if (v->type() == nBool) return v->boolean;
|
||||
if (v->type() == nBool) return v->boolean();
|
||||
if (v->type() == nString) {
|
||||
/* Backwards compatibility with before we had support for
|
||||
Boolean meta fields. */
|
||||
|
@ -292,7 +290,7 @@ void PackageInfo::setMeta(const std::string & name, Value * v)
|
|||
|
||||
|
||||
/* Cache for already considered attrsets. */
|
||||
typedef std::set<Bindings *> Done;
|
||||
typedef std::set<const Bindings *> Done;
|
||||
|
||||
|
||||
/* Evaluate value `v'. If it evaluates to a set of type `derivation',
|
||||
|
@ -309,9 +307,9 @@ static bool getDerivation(EvalState & state, Value & v,
|
|||
|
||||
/* Remove spurious duplicates (e.g., a set like `rec { x =
|
||||
derivation {...}; y = x;}'. */
|
||||
if (!done.insert(v.attrs).second) return false;
|
||||
if (!done.insert(v.attrs()).second) return false;
|
||||
|
||||
PackageInfo drv(state, attrPath, v.attrs);
|
||||
PackageInfo drv(state, attrPath, v.attrs());
|
||||
|
||||
drv.queryName();
|
||||
|
||||
|
@ -361,14 +359,14 @@ static void getDerivations(EvalState & state, Value & vIn,
|
|||
|
||||
/* !!! undocumented hackery to support combining channels in
|
||||
nix-env.cc. */
|
||||
bool combineChannels = v.attrs->find(state.symbols.create("_combineChannels")) != v.attrs->end();
|
||||
bool combineChannels = v.attrs()->get(state.symbols.create("_combineChannels"));
|
||||
|
||||
/* Consider the attributes in sorted order to get more
|
||||
deterministic behaviour in nix-env operations (e.g. when
|
||||
there are names clashes between derivations, the derivation
|
||||
bound to the attribute with the "lower" name should take
|
||||
precedence). */
|
||||
for (auto & i : v.attrs->lexicographicOrder(state.symbols)) {
|
||||
for (auto & i : v.attrs()->lexicographicOrder(state.symbols)) {
|
||||
debug("evaluating attribute '%1%'", state.symbols[i->name]);
|
||||
if (!std::regex_match(std::string(state.symbols[i->name]), attrRegex))
|
||||
continue;
|
||||
|
@ -380,8 +378,8 @@ static void getDerivations(EvalState & state, Value & vIn,
|
|||
should we recurse into it? => Only if it has a
|
||||
`recurseForDerivations = true' attribute. */
|
||||
if (i->value->type() == nAttrs) {
|
||||
Bindings::iterator j = i->value->attrs->find(state.sRecurseForDerivations);
|
||||
if (j != i->value->attrs->end() && state.forceBool(*j->value, j->pos, "while evaluating the attribute `recurseForDerivations`"))
|
||||
auto j = i->value->attrs()->get(state.sRecurseForDerivations);
|
||||
if (j && state.forceBool(*j->value, j->pos, "while evaluating the attribute `recurseForDerivations`"))
|
||||
getDerivations(state, *i->value, pathPrefix2, autoArgs, drvs, done, ignoreAssertionFailures);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -33,9 +33,9 @@ private:
|
|||
*/
|
||||
bool failed = false;
|
||||
|
||||
Bindings * attrs = nullptr, * meta = nullptr;
|
||||
const Bindings * attrs = nullptr, * meta = nullptr;
|
||||
|
||||
Bindings * getMeta();
|
||||
const Bindings * getMeta();
|
||||
|
||||
bool checkMeta(Value & v);
|
||||
|
||||
|
@ -46,7 +46,7 @@ public:
|
|||
std::string attrPath;
|
||||
|
||||
PackageInfo(EvalState & state) : state(&state) { };
|
||||
PackageInfo(EvalState & state, std::string attrPath, Bindings * attrs);
|
||||
PackageInfo(EvalState & state, std::string attrPath, const Bindings * attrs);
|
||||
PackageInfo(EvalState & state, ref<Store> store, const std::string & drvPathWithOutputs);
|
||||
|
||||
std::string queryName() const;
|
||||
|
|
|
@ -28,12 +28,12 @@ void Expr::show(const SymbolTable & symbols, std::ostream & str) const
|
|||
|
||||
void ExprInt::show(const SymbolTable & symbols, std::ostream & str) const
|
||||
{
|
||||
str << v.integer;
|
||||
str << v.integer();
|
||||
}
|
||||
|
||||
void ExprFloat::show(const SymbolTable & symbols, std::ostream & str) const
|
||||
{
|
||||
str << v.fpoint;
|
||||
str << v.fpoint();
|
||||
}
|
||||
|
||||
void ExprString::show(const SymbolTable & symbols, std::ostream & str) const
|
||||
|
|
|
@ -28,7 +28,10 @@
|
|||
#include <algorithm>
|
||||
#include <cstring>
|
||||
#include <regex>
|
||||
#include <dlfcn.h>
|
||||
|
||||
#ifndef _WIN32
|
||||
# include <dlfcn.h>
|
||||
#endif
|
||||
|
||||
#include <cmath>
|
||||
|
||||
|
@ -39,7 +42,7 @@ namespace nix {
|
|||
* Miscellaneous
|
||||
*************************************************************/
|
||||
|
||||
StringMap EvalState::realiseContext(const NixStringContext & context)
|
||||
StringMap EvalState::realiseContext(const NixStringContext & context, StorePathSet * maybePathsOut, bool isIFD)
|
||||
{
|
||||
std::vector<DerivedPath::Built> drvs;
|
||||
StringMap res;
|
||||
|
@ -59,21 +62,23 @@ StringMap EvalState::realiseContext(const NixStringContext & context)
|
|||
},
|
||||
[&](const NixStringContextElem::Opaque & o) {
|
||||
auto ctxS = store->printStorePath(o.path);
|
||||
res.insert_or_assign(ctxS, ctxS);
|
||||
ensureValid(o.path);
|
||||
if (maybePathsOut)
|
||||
maybePathsOut->emplace(o.path);
|
||||
},
|
||||
[&](const NixStringContextElem::DrvDeep & d) {
|
||||
/* Treat same as Opaque */
|
||||
auto ctxS = store->printStorePath(d.drvPath);
|
||||
res.insert_or_assign(ctxS, ctxS);
|
||||
ensureValid(d.drvPath);
|
||||
if (maybePathsOut)
|
||||
maybePathsOut->emplace(d.drvPath);
|
||||
},
|
||||
}, c.raw);
|
||||
}
|
||||
|
||||
if (drvs.empty()) return {};
|
||||
|
||||
if (!evalSettings.enableImportFromDerivation)
|
||||
if (isIFD && !evalSettings.enableImportFromDerivation)
|
||||
error<EvalError>(
|
||||
"cannot build '%1%' during evaluation because the option 'allow-import-from-derivation' is disabled",
|
||||
drvs.begin()->to_string(*store)
|
||||
|
@ -90,6 +95,8 @@ StringMap EvalState::realiseContext(const NixStringContext & context)
|
|||
auto outputs = resolveDerivedPath(*buildStore, drv, &*store);
|
||||
for (auto & [outputName, outputPath] : outputs) {
|
||||
outputsToCopyAndAllow.insert(outputPath);
|
||||
if (maybePathsOut)
|
||||
maybePathsOut->emplace(outputPath);
|
||||
|
||||
/* Get all the output paths corresponding to the placeholders we had */
|
||||
if (experimentalFeatureSettings.isEnabled(Xp::CaDerivations)) {
|
||||
|
@ -106,11 +113,14 @@ StringMap EvalState::realiseContext(const NixStringContext & context)
|
|||
}
|
||||
|
||||
if (store != buildStore) copyClosure(*buildStore, *store, outputsToCopyAndAllow);
|
||||
|
||||
if (isIFD) {
|
||||
for (auto & outputPath : outputsToCopyAndAllow) {
|
||||
/* Add the output of this derivations to the allowed
|
||||
paths. */
|
||||
allowPath(outputPath);
|
||||
}
|
||||
}
|
||||
|
||||
return res;
|
||||
}
|
||||
|
@ -216,13 +226,13 @@ static void import(EvalState & state, const PosIdx pos, Value & vPath, Value * v
|
|||
else {
|
||||
state.forceAttrs(*vScope, pos, "while evaluating the first argument passed to builtins.scopedImport");
|
||||
|
||||
Env * env = &state.allocEnv(vScope->attrs->size());
|
||||
Env * env = &state.allocEnv(vScope->attrs()->size());
|
||||
env->up = &state.baseEnv;
|
||||
|
||||
auto staticEnv = std::make_shared<StaticEnv>(nullptr, state.staticBaseEnv.get(), vScope->attrs->size());
|
||||
auto staticEnv = std::make_shared<StaticEnv>(nullptr, state.staticBaseEnv.get(), vScope->attrs()->size());
|
||||
|
||||
unsigned int displ = 0;
|
||||
for (auto & attr : *vScope->attrs) {
|
||||
for (auto & attr : *vScope->attrs()) {
|
||||
staticEnv->vars.emplace_back(attr.name, displ);
|
||||
env->values[displ++] = attr.value;
|
||||
}
|
||||
|
@ -324,6 +334,8 @@ static RegisterPrimOp primop_import({
|
|||
}
|
||||
});
|
||||
|
||||
#ifndef _WIN32 // TODO implement via DLL loading on Windows
|
||||
|
||||
/* Want reasonable symbol names, so extern C */
|
||||
/* !!! Should we pass the Pos or the file name too? */
|
||||
extern "C" typedef void (*ValueInitializer)(EvalState & state, Value & v);
|
||||
|
@ -396,6 +408,8 @@ void prim_exec(EvalState & state, const PosIdx pos, Value * * args, Value & v)
|
|||
}
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
/* Return a string representing the type of the expression. */
|
||||
static void prim_typeOf(EvalState & state, const PosIdx pos, Value * * args, Value & v)
|
||||
{
|
||||
|
@ -411,7 +425,7 @@ static void prim_typeOf(EvalState & state, const PosIdx pos, Value * * args, Val
|
|||
case nList: t = "list"; break;
|
||||
case nFunction: t = "lambda"; break;
|
||||
case nExternal:
|
||||
t = args[0]->external->typeOf();
|
||||
t = args[0]->external()->typeOf();
|
||||
break;
|
||||
case nFloat: t = "float"; break;
|
||||
case nThunk: abort();
|
||||
|
@ -575,9 +589,9 @@ struct CompareValues
|
|||
{
|
||||
try {
|
||||
if (v1->type() == nFloat && v2->type() == nInt)
|
||||
return v1->fpoint < v2->integer;
|
||||
return v1->fpoint() < v2->integer();
|
||||
if (v1->type() == nInt && v2->type() == nFloat)
|
||||
return v1->integer < v2->fpoint;
|
||||
return v1->integer() < v2->fpoint();
|
||||
if (v1->type() != v2->type())
|
||||
state.error<EvalError>("cannot compare %s with %s", showType(*v1), showType(*v2)).debugThrow();
|
||||
// Allow selecting a subset of enum values
|
||||
|
@ -585,16 +599,16 @@ struct CompareValues
|
|||
#pragma GCC diagnostic ignored "-Wswitch-enum"
|
||||
switch (v1->type()) {
|
||||
case nInt:
|
||||
return v1->integer < v2->integer;
|
||||
return v1->integer() < v2->integer();
|
||||
case nFloat:
|
||||
return v1->fpoint < v2->fpoint;
|
||||
return v1->fpoint() < v2->fpoint();
|
||||
case nString:
|
||||
return strcmp(v1->c_str(), v2->c_str()) < 0;
|
||||
case nPath:
|
||||
// Note: we don't take the accessor into account
|
||||
// since it's not obvious how to compare them in a
|
||||
// reproducible way.
|
||||
return strcmp(v1->_path.path, v2->_path.path) < 0;
|
||||
return strcmp(v1->payload.path.path, v2->payload.path.path) < 0;
|
||||
case nList:
|
||||
// Lexicographic comparison
|
||||
for (size_t i = 0;; i++) {
|
||||
|
@ -626,13 +640,13 @@ typedef std::list<Value *> ValueList;
|
|||
#endif
|
||||
|
||||
|
||||
static Bindings::iterator getAttr(
|
||||
static Bindings::const_iterator getAttr(
|
||||
EvalState & state,
|
||||
Symbol attrSym,
|
||||
Bindings * attrSet,
|
||||
const Bindings * attrSet,
|
||||
std::string_view errorCtx)
|
||||
{
|
||||
Bindings::iterator value = attrSet->find(attrSym);
|
||||
auto value = attrSet->find(attrSym);
|
||||
if (value == attrSet->end()) {
|
||||
state.error<TypeError>("attribute '%s' missing", state.symbols[attrSym]).withTrace(noPos, errorCtx).debugThrow();
|
||||
}
|
||||
|
@ -644,7 +658,7 @@ static void prim_genericClosure(EvalState & state, const PosIdx pos, Value * * a
|
|||
state.forceAttrs(*args[0], noPos, "while evaluating the first argument passed to builtins.genericClosure");
|
||||
|
||||
/* Get the start set. */
|
||||
Bindings::iterator startSet = getAttr(state, state.sStartSet, args[0]->attrs, "in the attrset passed as argument to builtins.genericClosure");
|
||||
auto startSet = getAttr(state, state.sStartSet, args[0]->attrs(), "in the attrset passed as argument to builtins.genericClosure");
|
||||
|
||||
state.forceList(*startSet->value, noPos, "while evaluating the 'startSet' attribute passed as argument to builtins.genericClosure");
|
||||
|
||||
|
@ -658,7 +672,7 @@ static void prim_genericClosure(EvalState & state, const PosIdx pos, Value * * a
|
|||
}
|
||||
|
||||
/* Get the operator. */
|
||||
Bindings::iterator op = getAttr(state, state.sOperator, args[0]->attrs, "in the attrset passed as argument to builtins.genericClosure");
|
||||
auto op = getAttr(state, state.sOperator, args[0]->attrs(), "in the attrset passed as argument to builtins.genericClosure");
|
||||
state.forceFunction(*op->value, noPos, "while evaluating the 'operator' attribute passed as argument to builtins.genericClosure");
|
||||
|
||||
/* Construct the closure by applying the operator to elements of
|
||||
|
@ -675,7 +689,7 @@ static void prim_genericClosure(EvalState & state, const PosIdx pos, Value * * a
|
|||
|
||||
state.forceAttrs(*e, noPos, "while evaluating one of the elements generated by (or initially passed to) builtins.genericClosure");
|
||||
|
||||
Bindings::iterator key = getAttr(state, state.sKey, e->attrs, "in one of the attrsets generated by (or initially passed to) builtins.genericClosure");
|
||||
auto key = getAttr(state, state.sKey, e->attrs(), "in one of the attrsets generated by (or initially passed to) builtins.genericClosure");
|
||||
state.forceValue(*key->value, noPos);
|
||||
|
||||
if (!doneKeys.insert(key->value).second) continue;
|
||||
|
@ -1043,7 +1057,11 @@ static void prim_second(EvalState & state, const PosIdx pos, Value * * args, Val
|
|||
* Derivations
|
||||
*************************************************************/
|
||||
|
||||
static void derivationStrictInternal(EvalState & state, const std::string & name, Bindings * attrs, Value & v);
|
||||
static void derivationStrictInternal(
|
||||
EvalState & state,
|
||||
const std::string & name,
|
||||
const Bindings * attrs,
|
||||
Value & v);
|
||||
|
||||
/* Construct (as a unobservable side effect) a Nix derivation
|
||||
expression that performs the derivation described by the argument
|
||||
|
@ -1056,10 +1074,10 @@ static void prim_derivationStrict(EvalState & state, const PosIdx pos, Value * *
|
|||
{
|
||||
state.forceAttrs(*args[0], pos, "while evaluating the argument passed to builtins.derivationStrict");
|
||||
|
||||
Bindings * attrs = args[0]->attrs;
|
||||
auto attrs = args[0]->attrs();
|
||||
|
||||
/* Figure out the name first (for stack backtraces). */
|
||||
Bindings::iterator nameAttr = getAttr(state, state.sName, attrs, "in the attrset passed as argument to builtins.derivationStrict");
|
||||
auto nameAttr = getAttr(state, state.sName, attrs, "in the attrset passed as argument to builtins.derivationStrict");
|
||||
|
||||
std::string drvName;
|
||||
try {
|
||||
|
@ -1098,8 +1116,11 @@ static void prim_derivationStrict(EvalState & state, const PosIdx pos, Value * *
|
|||
}
|
||||
}
|
||||
|
||||
static void derivationStrictInternal(EvalState & state, const std::string &
|
||||
drvName, Bindings * attrs, Value & v)
|
||||
static void derivationStrictInternal(
|
||||
EvalState & state,
|
||||
const std::string & drvName,
|
||||
const Bindings * attrs,
|
||||
Value & v)
|
||||
{
|
||||
/* Check whether attributes should be passed as a JSON file. */
|
||||
using nlohmann::json;
|
||||
|
@ -1139,18 +1160,20 @@ drvName, Bindings * attrs, Value & v)
|
|||
vomit("processing attribute '%1%'", key);
|
||||
|
||||
auto handleHashMode = [&](const std::string_view s) {
|
||||
if (s == "recursive") ingestionMethod = FileIngestionMethod::Recursive;
|
||||
else if (s == "flat") ingestionMethod = FileIngestionMethod::Flat;
|
||||
else if (s == "git") {
|
||||
experimentalFeatureSettings.require(Xp::GitHashing);
|
||||
ingestionMethod = FileIngestionMethod::Git;
|
||||
} else if (s == "text") {
|
||||
experimentalFeatureSettings.require(Xp::DynamicDerivations);
|
||||
ingestionMethod = TextIngestionMethod {};
|
||||
} else
|
||||
if (s == "recursive") {
|
||||
// back compat, new name is "nar"
|
||||
ingestionMethod = FileIngestionMethod::Recursive;
|
||||
} else try {
|
||||
ingestionMethod = ContentAddressMethod::parse(s);
|
||||
} catch (UsageError &) {
|
||||
state.error<EvalError>(
|
||||
"invalid value '%s' for 'outputHashMode' attribute", s
|
||||
).atPos(v).debugThrow();
|
||||
}
|
||||
if (ingestionMethod == TextIngestionMethod {})
|
||||
experimentalFeatureSettings.require(Xp::DynamicDerivations);
|
||||
if (ingestionMethod == FileIngestionMethod::Git)
|
||||
experimentalFeatureSettings.require(Xp::GitHashing);
|
||||
};
|
||||
|
||||
auto handleOutputs = [&](const Strings & ss) {
|
||||
|
@ -1699,11 +1722,11 @@ static void prim_findFile(EvalState & state, const PosIdx pos, Value * * args, V
|
|||
state.forceAttrs(*v2, pos, "while evaluating an element of the list passed to builtins.findFile");
|
||||
|
||||
std::string prefix;
|
||||
Bindings::iterator i = v2->attrs->find(state.sPrefix);
|
||||
if (i != v2->attrs->end())
|
||||
auto i = v2->attrs()->find(state.sPrefix);
|
||||
if (i != v2->attrs()->end())
|
||||
prefix = state.forceStringNoCtx(*i->value, pos, "while evaluating the `prefix` attribute of an element of the list passed to builtins.findFile");
|
||||
|
||||
i = getAttr(state, state.sPath, v2->attrs, "in an element of the __nixPath");
|
||||
i = getAttr(state, state.sPath, v2->attrs(), "in an element of the __nixPath");
|
||||
|
||||
NixStringContext context;
|
||||
auto path = state.coerceToString(pos, *i->value, context,
|
||||
|
@ -1915,11 +1938,13 @@ static RegisterPrimOp primop_outputOf({
|
|||
*`derivation reference`* must be a string that may contain a regular store path to a derivation, or may be a placeholder reference. If the derivation is produced by a derivation, you must explicitly select `drv.outPath`.
|
||||
This primop can be chained arbitrarily deeply.
|
||||
For instance,
|
||||
|
||||
```nix
|
||||
builtins.outputOf
|
||||
(builtins.outputOf myDrv "out")
|
||||
"out"
|
||||
```
|
||||
|
||||
will return a placeholder for the output of the output of `myDrv`.
|
||||
|
||||
This primop corresponds to the `^` sigil for derivable paths, e.g. as part of installable syntax on the command line.
|
||||
|
@ -2375,7 +2400,7 @@ static void prim_path(EvalState & state, const PosIdx pos, Value * * args, Value
|
|||
|
||||
state.forceAttrs(*args[0], pos, "while evaluating the argument passed to 'builtins.path'");
|
||||
|
||||
for (auto & attr : *args[0]->attrs) {
|
||||
for (auto & attr : *args[0]->attrs()) {
|
||||
auto n = state.symbols[attr.name];
|
||||
if (n == "path")
|
||||
path.emplace(state.coerceToPath(attr.pos, *attr.value, context, "while evaluating the 'path' attribute passed to 'builtins.path'"));
|
||||
|
@ -2450,9 +2475,9 @@ static void prim_attrNames(EvalState & state, const PosIdx pos, Value * * args,
|
|||
{
|
||||
state.forceAttrs(*args[0], pos, "while evaluating the argument passed to builtins.attrNames");
|
||||
|
||||
auto list = state.buildList(args[0]->attrs->size());
|
||||
auto list = state.buildList(args[0]->attrs()->size());
|
||||
|
||||
for (const auto & [n, i] : enumerate(*args[0]->attrs))
|
||||
for (const auto & [n, i] : enumerate(*args[0]->attrs()))
|
||||
(list[n] = state.allocValue())->mkString(state.symbols[i.name]);
|
||||
|
||||
std::sort(list.begin(), list.end(),
|
||||
|
@ -2478,9 +2503,9 @@ static void prim_attrValues(EvalState & state, const PosIdx pos, Value * * args,
|
|||
{
|
||||
state.forceAttrs(*args[0], pos, "while evaluating the argument passed to builtins.attrValues");
|
||||
|
||||
auto list = state.buildList(args[0]->attrs->size());
|
||||
auto list = state.buildList(args[0]->attrs()->size());
|
||||
|
||||
for (const auto & [n, i] : enumerate(*args[0]->attrs))
|
||||
for (const auto & [n, i] : enumerate(*args[0]->attrs()))
|
||||
list[n] = (Value *) &i;
|
||||
|
||||
std::sort(list.begin(), list.end(),
|
||||
|
@ -2511,10 +2536,10 @@ void prim_getAttr(EvalState & state, const PosIdx pos, Value * * args, Value & v
|
|||
{
|
||||
auto attr = state.forceStringNoCtx(*args[0], pos, "while evaluating the first argument passed to builtins.getAttr");
|
||||
state.forceAttrs(*args[1], pos, "while evaluating the second argument passed to builtins.getAttr");
|
||||
Bindings::iterator i = getAttr(
|
||||
auto i = getAttr(
|
||||
state,
|
||||
state.symbols.create(attr),
|
||||
args[1]->attrs,
|
||||
args[1]->attrs(),
|
||||
"in the attribute set under consideration"
|
||||
);
|
||||
// !!! add to stack trace?
|
||||
|
@ -2540,8 +2565,8 @@ static void prim_unsafeGetAttrPos(EvalState & state, const PosIdx pos, Value * *
|
|||
{
|
||||
auto attr = state.forceStringNoCtx(*args[0], pos, "while evaluating the first argument passed to builtins.unsafeGetAttrPos");
|
||||
state.forceAttrs(*args[1], pos, "while evaluating the second argument passed to builtins.unsafeGetAttrPos");
|
||||
Bindings::iterator i = args[1]->attrs->find(state.symbols.create(attr));
|
||||
if (i == args[1]->attrs->end())
|
||||
auto i = args[1]->attrs()->find(state.symbols.create(attr));
|
||||
if (i == args[1]->attrs()->end())
|
||||
v.mkNull();
|
||||
else
|
||||
state.mkPos(v, i->pos);
|
||||
|
@ -2569,13 +2594,13 @@ static struct LazyPosAcessors {
|
|||
PrimOp primop_lineOfPos{
|
||||
.arity = 1,
|
||||
.fun = [] (EvalState & state, PosIdx pos, Value * * args, Value & v) {
|
||||
v.mkInt(state.positions[PosIdx(args[0]->integer)].line);
|
||||
v.mkInt(state.positions[PosIdx(args[0]->integer())].line);
|
||||
}
|
||||
};
|
||||
PrimOp primop_columnOfPos{
|
||||
.arity = 1,
|
||||
.fun = [] (EvalState & state, PosIdx pos, Value * * args, Value & v) {
|
||||
v.mkInt(state.positions[PosIdx(args[0]->integer)].column);
|
||||
v.mkInt(state.positions[PosIdx(args[0]->integer())].column);
|
||||
}
|
||||
};
|
||||
|
||||
|
@ -2606,7 +2631,7 @@ static void prim_hasAttr(EvalState & state, const PosIdx pos, Value * * args, Va
|
|||
{
|
||||
auto attr = state.forceStringNoCtx(*args[0], pos, "while evaluating the first argument passed to builtins.hasAttr");
|
||||
state.forceAttrs(*args[1], pos, "while evaluating the second argument passed to builtins.hasAttr");
|
||||
v.mkBool(args[1]->attrs->find(state.symbols.create(attr)) != args[1]->attrs->end());
|
||||
v.mkBool(args[1]->attrs()->find(state.symbols.create(attr)) != args[1]->attrs()->end());
|
||||
}
|
||||
|
||||
static RegisterPrimOp primop_hasAttr({
|
||||
|
@ -2656,9 +2681,9 @@ static void prim_removeAttrs(EvalState & state, const PosIdx pos, Value * * args
|
|||
/* Copy all attributes not in that set. Note that we don't need
|
||||
to sort v.attrs because it's a subset of an already sorted
|
||||
vector. */
|
||||
auto attrs = state.buildBindings(args[0]->attrs->size());
|
||||
auto attrs = state.buildBindings(args[0]->attrs()->size());
|
||||
std::set_difference(
|
||||
args[0]->attrs->begin(), args[0]->attrs->end(),
|
||||
args[0]->attrs()->begin(), args[0]->attrs()->end(),
|
||||
names.begin(), names.end(),
|
||||
std::back_inserter(attrs));
|
||||
v.mkAttrs(attrs.alreadySorted());
|
||||
|
@ -2696,13 +2721,13 @@ static void prim_listToAttrs(EvalState & state, const PosIdx pos, Value * * args
|
|||
for (auto v2 : args[0]->listItems()) {
|
||||
state.forceAttrs(*v2, pos, "while evaluating an element of the list passed to builtins.listToAttrs");
|
||||
|
||||
Bindings::iterator j = getAttr(state, state.sName, v2->attrs, "in a {name=...; value=...;} pair");
|
||||
auto j = getAttr(state, state.sName, v2->attrs(), "in a {name=...; value=...;} pair");
|
||||
|
||||
auto name = state.forceStringNoCtx(*j->value, j->pos, "while evaluating the `name` attribute of an element of the list passed to builtins.listToAttrs");
|
||||
|
||||
auto sym = state.symbols.create(name);
|
||||
if (seen.insert(sym).second) {
|
||||
Bindings::iterator j2 = getAttr(state, state.sValue, v2->attrs, "in a {name=...; value=...;} pair");
|
||||
auto j2 = getAttr(state, state.sValue, v2->attrs(), "in a {name=...; value=...;} pair");
|
||||
attrs.insert(sym, j2->value, j2->pos);
|
||||
}
|
||||
}
|
||||
|
@ -2746,8 +2771,8 @@ static void prim_intersectAttrs(EvalState & state, const PosIdx pos, Value * * a
|
|||
state.forceAttrs(*args[0], pos, "while evaluating the first argument passed to builtins.intersectAttrs");
|
||||
state.forceAttrs(*args[1], pos, "while evaluating the second argument passed to builtins.intersectAttrs");
|
||||
|
||||
Bindings &left = *args[0]->attrs;
|
||||
Bindings &right = *args[1]->attrs;
|
||||
auto & left = *args[0]->attrs();
|
||||
auto & right = *args[1]->attrs();
|
||||
|
||||
auto attrs = state.buildBindings(std::min(left.size(), right.size()));
|
||||
|
||||
|
@ -2791,14 +2816,14 @@ static void prim_intersectAttrs(EvalState & state, const PosIdx pos, Value * * a
|
|||
|
||||
if (left.size() < right.size()) {
|
||||
for (auto & l : left) {
|
||||
Bindings::iterator r = right.find(l.name);
|
||||
auto r = right.find(l.name);
|
||||
if (r != right.end())
|
||||
attrs.insert(*r);
|
||||
}
|
||||
}
|
||||
else {
|
||||
for (auto & r : right) {
|
||||
Bindings::iterator l = left.find(r.name);
|
||||
auto l = left.find(r.name);
|
||||
if (l != left.end())
|
||||
attrs.insert(r);
|
||||
}
|
||||
|
@ -2829,8 +2854,7 @@ static void prim_catAttrs(EvalState & state, const PosIdx pos, Value * * args, V
|
|||
|
||||
for (auto v2 : args[1]->listItems()) {
|
||||
state.forceAttrs(*v2, pos, "while evaluating an element in the list passed as second argument to builtins.catAttrs");
|
||||
Bindings::iterator i = v2->attrs->find(attrName);
|
||||
if (i != v2->attrs->end())
|
||||
if (auto i = v2->attrs()->get(attrName))
|
||||
res[found++] = i->value;
|
||||
}
|
||||
|
||||
|
@ -2867,13 +2891,13 @@ static void prim_functionArgs(EvalState & state, const PosIdx pos, Value * * arg
|
|||
if (!args[0]->isLambda())
|
||||
state.error<TypeError>("'functionArgs' requires a function").atPos(pos).debugThrow();
|
||||
|
||||
if (!args[0]->lambda.fun->hasFormals()) {
|
||||
if (!args[0]->payload.lambda.fun->hasFormals()) {
|
||||
v.mkAttrs(&state.emptyBindings);
|
||||
return;
|
||||
}
|
||||
|
||||
auto attrs = state.buildBindings(args[0]->lambda.fun->formals->formals.size());
|
||||
for (auto & i : args[0]->lambda.fun->formals->formals)
|
||||
auto attrs = state.buildBindings(args[0]->payload.lambda.fun->formals->formals.size());
|
||||
for (auto & i : args[0]->payload.lambda.fun->formals->formals)
|
||||
attrs.insert(i.name, state.getBool(i.def), i.pos);
|
||||
v.mkAttrs(attrs);
|
||||
}
|
||||
|
@ -2900,9 +2924,9 @@ static void prim_mapAttrs(EvalState & state, const PosIdx pos, Value * * args, V
|
|||
{
|
||||
state.forceAttrs(*args[1], pos, "while evaluating the second argument passed to builtins.mapAttrs");
|
||||
|
||||
auto attrs = state.buildBindings(args[1]->attrs->size());
|
||||
auto attrs = state.buildBindings(args[1]->attrs()->size());
|
||||
|
||||
for (auto & i : *args[1]->attrs) {
|
||||
for (auto & i : *args[1]->attrs()) {
|
||||
Value * vName = state.allocValue();
|
||||
Value * vFun2 = state.allocValue();
|
||||
vName->mkString(state.symbols[i.name]);
|
||||
|
@ -2952,7 +2976,7 @@ static void prim_zipAttrsWith(EvalState & state, const PosIdx pos, Value * * arg
|
|||
|
||||
for (auto & vElem : listItems) {
|
||||
state.forceAttrs(*vElem, noPos, "while evaluating a value of the list passed as second argument to builtins.zipAttrsWith");
|
||||
for (auto & attr : *vElem->attrs)
|
||||
for (auto & attr : *vElem->attrs())
|
||||
attrsSeen.try_emplace(attr.name).first->second.size++;
|
||||
}
|
||||
|
||||
|
@ -2960,7 +2984,7 @@ static void prim_zipAttrsWith(EvalState & state, const PosIdx pos, Value * * arg
|
|||
elem.list.emplace(state.buildList(elem.size));
|
||||
|
||||
for (auto & vElem : listItems) {
|
||||
for (auto & attr : *vElem->attrs) {
|
||||
for (auto & attr : *vElem->attrs()) {
|
||||
auto & item = attrsSeen.at(attr.name);
|
||||
(*item.list)[item.pos++] = attr.value;
|
||||
}
|
||||
|
@ -3402,10 +3426,8 @@ static void prim_sort(EvalState & state, const PosIdx pos, Value * * args, Value
|
|||
auto comparator = [&](Value * a, Value * b) {
|
||||
/* Optimization: if the comparator is lessThan, bypass
|
||||
callFunction. */
|
||||
/* TODO: (layus) this is absurd. An optimisation like this
|
||||
should be outside the lambda creation */
|
||||
if (args[0]->isPrimOp()) {
|
||||
auto ptr = args[0]->primOp->fun.target<decltype(&prim_lessThan)>();
|
||||
auto ptr = args[0]->primOp()->fun.target<decltype(&prim_lessThan)>();
|
||||
if (ptr && *ptr == prim_lessThan)
|
||||
return CompareValues(state, noPos, "while evaluating the ordering function passed to builtins.sort")(a, b);
|
||||
}
|
||||
|
@ -3898,18 +3920,17 @@ static RegisterPrimOp primop_hashString({
|
|||
static void prim_convertHash(EvalState & state, const PosIdx pos, Value * * args, Value & v)
|
||||
{
|
||||
state.forceAttrs(*args[0], pos, "while evaluating the first argument passed to builtins.convertHash");
|
||||
auto &inputAttrs = args[0]->attrs;
|
||||
auto inputAttrs = args[0]->attrs();
|
||||
|
||||
Bindings::iterator iteratorHash = getAttr(state, state.symbols.create("hash"), inputAttrs, "while locating the attribute 'hash'");
|
||||
auto iteratorHash = getAttr(state, state.symbols.create("hash"), inputAttrs, "while locating the attribute 'hash'");
|
||||
auto hash = state.forceStringNoCtx(*iteratorHash->value, pos, "while evaluating the attribute 'hash'");
|
||||
|
||||
Bindings::iterator iteratorHashAlgo = inputAttrs->find(state.symbols.create("hashAlgo"));
|
||||
auto iteratorHashAlgo = inputAttrs->get(state.symbols.create("hashAlgo"));
|
||||
std::optional<HashAlgorithm> ha = std::nullopt;
|
||||
if (iteratorHashAlgo != inputAttrs->end()) {
|
||||
if (iteratorHashAlgo)
|
||||
ha = parseHashAlgo(state.forceStringNoCtx(*iteratorHashAlgo->value, pos, "while evaluating the attribute 'hashAlgo'"));
|
||||
}
|
||||
|
||||
Bindings::iterator iteratorToHashFormat = getAttr(state, state.symbols.create("toHashFormat"), args[0]->attrs, "while locating the attribute 'toHashFormat'");
|
||||
auto iteratorToHashFormat = getAttr(state, state.symbols.create("toHashFormat"), args[0]->attrs(), "while locating the attribute 'toHashFormat'");
|
||||
HashFormat hf = parseHashFormat(state.forceStringNoCtx(*iteratorToHashFormat->value, pos, "while evaluating the attribute 'toHashFormat'"));
|
||||
|
||||
v.mkString(Hash::parseAny(hash, ha).to_string(hf, hf == HashFormat::SRI));
|
||||
|
@ -4587,6 +4608,7 @@ void EvalState::createBaseEnv()
|
|||
)",
|
||||
});
|
||||
|
||||
#ifndef _WIN32 // TODO implement on Windows
|
||||
// Miscellaneous
|
||||
if (evalSettings.enableNativeCode) {
|
||||
addPrimOp({
|
||||
|
@ -4600,6 +4622,7 @@ void EvalState::createBaseEnv()
|
|||
.fun = prim_exec,
|
||||
});
|
||||
}
|
||||
#endif
|
||||
|
||||
addPrimOp({
|
||||
.name = "__traceVerbose",
|
||||
|
@ -4663,7 +4686,7 @@ void EvalState::createBaseEnv()
|
|||
|
||||
/* Now that we've added all primops, sort the `builtins' set,
|
||||
because attribute lookups expect it to be sorted. */
|
||||
baseEnv.values[0]->attrs->sort();
|
||||
baseEnv.values[0]->payload.attrs->sort();
|
||||
|
||||
staticBaseEnv->sort();
|
||||
|
||||
|
|
|
@ -258,7 +258,7 @@ static void prim_appendContext(EvalState & state, const PosIdx pos, Value * * ar
|
|||
|
||||
auto sPath = state.symbols.create("path");
|
||||
auto sAllOutputs = state.symbols.create("allOutputs");
|
||||
for (auto & i : *args[1]->attrs) {
|
||||
for (auto & i : *args[1]->attrs()) {
|
||||
const auto & name = state.symbols[i.name];
|
||||
if (!state.store->isStorePath(name))
|
||||
state.error<EvalError>(
|
||||
|
@ -269,17 +269,16 @@ static void prim_appendContext(EvalState & state, const PosIdx pos, Value * * ar
|
|||
if (!settings.readOnlyMode)
|
||||
state.store->ensurePath(namePath);
|
||||
state.forceAttrs(*i.value, i.pos, "while evaluating the value of a string context");
|
||||
auto iter = i.value->attrs->find(sPath);
|
||||
if (iter != i.value->attrs->end()) {
|
||||
if (state.forceBool(*iter->value, iter->pos, "while evaluating the `path` attribute of a string context"))
|
||||
|
||||
if (auto attr = i.value->attrs()->get(sPath)) {
|
||||
if (state.forceBool(*attr->value, attr->pos, "while evaluating the `path` attribute of a string context"))
|
||||
context.emplace(NixStringContextElem::Opaque {
|
||||
.path = namePath,
|
||||
});
|
||||
}
|
||||
|
||||
iter = i.value->attrs->find(sAllOutputs);
|
||||
if (iter != i.value->attrs->end()) {
|
||||
if (state.forceBool(*iter->value, iter->pos, "while evaluating the `allOutputs` attribute of a string context")) {
|
||||
if (auto attr = i.value->attrs()->get(sAllOutputs)) {
|
||||
if (state.forceBool(*attr->value, attr->pos, "while evaluating the `allOutputs` attribute of a string context")) {
|
||||
if (!isDerivation(name)) {
|
||||
state.error<EvalError>(
|
||||
"tried to add all-outputs context of %s, which is not a derivation, to a string",
|
||||
|
@ -292,17 +291,16 @@ static void prim_appendContext(EvalState & state, const PosIdx pos, Value * * ar
|
|||
}
|
||||
}
|
||||
|
||||
iter = i.value->attrs->find(state.sOutputs);
|
||||
if (iter != i.value->attrs->end()) {
|
||||
state.forceList(*iter->value, iter->pos, "while evaluating the `outputs` attribute of a string context");
|
||||
if (iter->value->listSize() && !isDerivation(name)) {
|
||||
if (auto attr = i.value->attrs()->get(state.sOutputs)) {
|
||||
state.forceList(*attr->value, attr->pos, "while evaluating the `outputs` attribute of a string context");
|
||||
if (attr->value->listSize() && !isDerivation(name)) {
|
||||
state.error<EvalError>(
|
||||
"tried to add derivation output context of %s, which is not a derivation, to a string",
|
||||
name
|
||||
).atPos(i.pos).debugThrow();
|
||||
}
|
||||
for (auto elem : iter->value->listItems()) {
|
||||
auto outputName = state.forceStringNoCtx(*elem, iter->pos, "while evaluating an output name within a string context");
|
||||
for (auto elem : attr->value->listItems()) {
|
||||
auto outputName = state.forceStringNoCtx(*elem, attr->pos, "while evaluating an output name within a string context");
|
||||
context.emplace(NixStringContextElem::Built {
|
||||
.drvPath = makeConstantStorePathRef(namePath),
|
||||
.output = std::string { outputName },
|
||||
|
|
|
@ -121,7 +121,7 @@ static void prim_fetchClosure(EvalState & state, const PosIdx pos, Value * * arg
|
|||
std::optional<StorePathOrGap> toPath;
|
||||
std::optional<bool> inputAddressedMaybe;
|
||||
|
||||
for (auto & attr : *args[0]->attrs) {
|
||||
for (auto & attr : *args[0]->attrs()) {
|
||||
const auto & attrName = state.symbols[attr.name];
|
||||
auto attrHint = [&]() -> std::string {
|
||||
return "while evaluating the '" + attrName + "' attribute passed to builtins.fetchClosure";
|
||||
|
|
|
@ -20,7 +20,7 @@ static void prim_fetchMercurial(EvalState & state, const PosIdx pos, Value * * a
|
|||
|
||||
if (args[0]->type() == nAttrs) {
|
||||
|
||||
for (auto & attr : *args[0]->attrs) {
|
||||
for (auto & attr : *args[0]->attrs()) {
|
||||
std::string_view n(state.symbols[attr.name]);
|
||||
if (n == "url")
|
||||
url = state.coerceToString(attr.pos, *attr.value, context,
|
||||
|
|
|
@ -97,7 +97,7 @@ static void fetchTree(
|
|||
|
||||
fetchers::Attrs attrs;
|
||||
|
||||
if (auto aType = args[0]->attrs->get(state.sType)) {
|
||||
if (auto aType = args[0]->attrs()->get(state.sType)) {
|
||||
if (type)
|
||||
state.error<EvalError>(
|
||||
"unexpected attribute 'type'"
|
||||
|
@ -110,7 +110,7 @@ static void fetchTree(
|
|||
|
||||
attrs.emplace("type", type.value());
|
||||
|
||||
for (auto & attr : *args[0]->attrs) {
|
||||
for (auto & attr : *args[0]->attrs()) {
|
||||
if (attr.name == state.sType) continue;
|
||||
state.forceValue(*attr.value, attr.pos);
|
||||
if (attr.value->type() == nPath || attr.value->type() == nString) {
|
||||
|
@ -121,9 +121,9 @@ static void fetchTree(
|
|||
: s);
|
||||
}
|
||||
else if (attr.value->type() == nBool)
|
||||
attrs.emplace(state.symbols[attr.name], Explicit<bool>{attr.value->boolean});
|
||||
attrs.emplace(state.symbols[attr.name], Explicit<bool>{attr.value->boolean()});
|
||||
else if (attr.value->type() == nInt)
|
||||
attrs.emplace(state.symbols[attr.name], uint64_t(attr.value->integer));
|
||||
attrs.emplace(state.symbols[attr.name], uint64_t(attr.value->integer()));
|
||||
else if (state.symbols[attr.name] == "publicKeys") {
|
||||
experimentalFeatureSettings.require(Xp::VerifiedFetches);
|
||||
attrs.emplace(state.symbols[attr.name], printValueAsJSON(state, true, *attr.value, pos, context).dump());
|
||||
|
@ -422,7 +422,7 @@ static void fetch(EvalState & state, const PosIdx pos, Value * * args, Value & v
|
|||
|
||||
if (args[0]->type() == nAttrs) {
|
||||
|
||||
for (auto & attr : *args[0]->attrs) {
|
||||
for (auto & attr : *args[0]->attrs()) {
|
||||
std::string_view n(state.symbols[attr.name]);
|
||||
if (n == "url")
|
||||
url = state.forceStringNoCtx(*attr.value, attr.pos, "while evaluating the url we should fetch");
|
||||
|
@ -473,7 +473,7 @@ static void fetch(EvalState & state, const PosIdx pos, Value * * args, Value & v
|
|||
auto storePath =
|
||||
unpack
|
||||
? fetchToStore(*state.store, fetchers::downloadTarball(*url).accessor, FetchMode::Copy, name)
|
||||
: fetchers::downloadFile(state.store, *url, name, (bool) expectedHash).storePath;
|
||||
: fetchers::downloadFile(state.store, *url, name).storePath;
|
||||
|
||||
if (expectedHash) {
|
||||
auto hash = unpack
|
||||
|
@ -650,12 +650,14 @@ static RegisterPrimOp primop_fetchGit({
|
|||
|
||||
The public keys against which `rev` is verified if `verifyCommit` is enabled.
|
||||
Must be given as a list of attribute sets with the following form:
|
||||
|
||||
```nix
|
||||
{
|
||||
key = "<public key>";
|
||||
type = "<key type>"; # optional, default: "ssh-ed25519"
|
||||
}
|
||||
```
|
||||
|
||||
Requires the [`verified-fetches` experimental feature](@docroot@/contributing/experimental-features.md#xp-feature-verified-fetches).
|
||||
|
||||
|
||||
|
|
|
@ -21,10 +21,10 @@ void printAmbiguous(
|
|||
}
|
||||
switch (v.type()) {
|
||||
case nInt:
|
||||
str << v.integer;
|
||||
str << v.integer();
|
||||
break;
|
||||
case nBool:
|
||||
printLiteralBool(str, v.boolean);
|
||||
printLiteralBool(str, v.boolean());
|
||||
break;
|
||||
case nString:
|
||||
printLiteralString(str, v.string_view());
|
||||
|
@ -36,11 +36,11 @@ void printAmbiguous(
|
|||
str << "null";
|
||||
break;
|
||||
case nAttrs: {
|
||||
if (seen && !v.attrs->empty() && !seen->insert(v.attrs).second)
|
||||
if (seen && !v.attrs()->empty() && !seen->insert(v.attrs()).second)
|
||||
str << "«repeated»";
|
||||
else {
|
||||
str << "{ ";
|
||||
for (auto & i : v.attrs->lexicographicOrder(symbols)) {
|
||||
for (auto & i : v.attrs()->lexicographicOrder(symbols)) {
|
||||
str << symbols[i->name] << " = ";
|
||||
printAmbiguous(*i->value, symbols, str, seen, depth - 1);
|
||||
str << "; ";
|
||||
|
@ -87,10 +87,10 @@ void printAmbiguous(
|
|||
}
|
||||
break;
|
||||
case nExternal:
|
||||
str << *v.external;
|
||||
str << *v.external();
|
||||
break;
|
||||
case nFloat:
|
||||
str << v.fpoint;
|
||||
str << v.fpoint();
|
||||
break;
|
||||
default:
|
||||
printError("Nix evaluator internal error: printAmbiguous: invalid value type");
|
||||
|
|
|
@ -223,7 +223,7 @@ private:
|
|||
{
|
||||
if (options.ansiColors)
|
||||
output << ANSI_CYAN;
|
||||
output << v.integer;
|
||||
output << v.integer();
|
||||
if (options.ansiColors)
|
||||
output << ANSI_NORMAL;
|
||||
}
|
||||
|
@ -232,7 +232,7 @@ private:
|
|||
{
|
||||
if (options.ansiColors)
|
||||
output << ANSI_CYAN;
|
||||
output << v.fpoint;
|
||||
output << v.fpoint();
|
||||
if (options.ansiColors)
|
||||
output << ANSI_NORMAL;
|
||||
}
|
||||
|
@ -241,7 +241,7 @@ private:
|
|||
{
|
||||
if (options.ansiColors)
|
||||
output << ANSI_CYAN;
|
||||
printLiteralBool(output, v.boolean);
|
||||
printLiteralBool(output, v.boolean());
|
||||
if (options.ansiColors)
|
||||
output << ANSI_NORMAL;
|
||||
}
|
||||
|
@ -271,10 +271,9 @@ private:
|
|||
|
||||
void printDerivation(Value & v)
|
||||
{
|
||||
Bindings::iterator i = v.attrs->find(state.sDrvPath);
|
||||
NixStringContext context;
|
||||
std::string storePath;
|
||||
if (i != v.attrs->end())
|
||||
if (auto i = v.attrs()->get(state.sDrvPath))
|
||||
storePath = state.store->printStorePath(state.coerceToStorePath(i->pos, *i->value, context, "while evaluating the drvPath of a derivation"));
|
||||
|
||||
if (options.ansiColors)
|
||||
|
@ -312,7 +311,7 @@ private:
|
|||
|
||||
void printAttrs(Value & v, size_t depth)
|
||||
{
|
||||
if (seen && !seen->insert(v.attrs).second) {
|
||||
if (seen && !seen->insert(v.attrs()).second) {
|
||||
printRepeated();
|
||||
return;
|
||||
}
|
||||
|
@ -324,7 +323,7 @@ private:
|
|||
output << "{";
|
||||
|
||||
AttrVec sorted;
|
||||
for (auto & i : *v.attrs)
|
||||
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())
|
||||
|
@ -423,18 +422,18 @@ private:
|
|||
|
||||
if (v.isLambda()) {
|
||||
output << "lambda";
|
||||
if (v.lambda.fun) {
|
||||
if (v.lambda.fun->name) {
|
||||
output << " " << state.symbols[v.lambda.fun->name];
|
||||
if (v.payload.lambda.fun) {
|
||||
if (v.payload.lambda.fun->name) {
|
||||
output << " " << state.symbols[v.payload.lambda.fun->name];
|
||||
}
|
||||
|
||||
std::ostringstream s;
|
||||
s << state.positions[v.lambda.fun->pos];
|
||||
s << state.positions[v.payload.lambda.fun->pos];
|
||||
output << " @ " << filterANSIEscapes(s.str());
|
||||
}
|
||||
} else if (v.isPrimOp()) {
|
||||
if (v.primOp)
|
||||
output << *v.primOp;
|
||||
if (v.primOp())
|
||||
output << *v.primOp();
|
||||
else
|
||||
output << "primop";
|
||||
} else if (v.isPrimOpApp()) {
|
||||
|
@ -480,7 +479,7 @@ private:
|
|||
|
||||
void printExternal(Value & v)
|
||||
{
|
||||
v.external->print(output);
|
||||
v.external()->print(output);
|
||||
}
|
||||
|
||||
void printUnknown()
|
||||
|
|
|
@ -8,6 +8,9 @@
|
|||
|
||||
namespace nix {
|
||||
|
||||
// Do not want the windows macro (alias to `SearchPathA`)
|
||||
#undef SearchPath
|
||||
|
||||
/**
|
||||
* A "search path" is a list of ways look for something, used with
|
||||
* `builtins.findFile` and `< >` lookup expressions.
|
||||
|
|
|
@ -22,11 +22,11 @@ json printValueAsJSON(EvalState & state, bool strict,
|
|||
switch (v.type()) {
|
||||
|
||||
case nInt:
|
||||
out = v.integer;
|
||||
out = v.integer();
|
||||
break;
|
||||
|
||||
case nBool:
|
||||
out = v.boolean;
|
||||
out = v.boolean();
|
||||
break;
|
||||
|
||||
case nString:
|
||||
|
@ -52,24 +52,20 @@ json printValueAsJSON(EvalState & state, bool strict,
|
|||
out = *maybeString;
|
||||
break;
|
||||
}
|
||||
auto i = v.attrs->find(state.sOutPath);
|
||||
if (i == v.attrs->end()) {
|
||||
if (auto i = v.attrs()->get(state.sOutPath))
|
||||
return printValueAsJSON(state, strict, *i->value, i->pos, context, copyToStore);
|
||||
else {
|
||||
out = json::object();
|
||||
StringSet names;
|
||||
for (auto & j : *v.attrs)
|
||||
names.emplace(state.symbols[j.name]);
|
||||
for (auto & j : names) {
|
||||
Attr & a(*v.attrs->find(state.symbols.create(j)));
|
||||
for (auto & a : v.attrs()->lexicographicOrder(state.symbols)) {
|
||||
try {
|
||||
out[j] = printValueAsJSON(state, strict, *a.value, a.pos, context, copyToStore);
|
||||
out[state.symbols[a->name]] = printValueAsJSON(state, strict, *a->value, a->pos, context, copyToStore);
|
||||
} catch (Error & e) {
|
||||
e.addTrace(state.positions[a.pos],
|
||||
HintFmt("while evaluating attribute '%1%'", j));
|
||||
e.addTrace(state.positions[a->pos],
|
||||
HintFmt("while evaluating attribute '%1%'", state.symbols[a->name]));
|
||||
throw;
|
||||
}
|
||||
}
|
||||
} else
|
||||
return printValueAsJSON(state, strict, *i->value, i->pos, context, copyToStore);
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
|
@ -90,11 +86,11 @@ json printValueAsJSON(EvalState & state, bool strict,
|
|||
}
|
||||
|
||||
case nExternal:
|
||||
return v.external->printValueAsJSON(state, strict, context, copyToStore);
|
||||
return v.external()->printValueAsJSON(state, strict, context, copyToStore);
|
||||
break;
|
||||
|
||||
case nFloat:
|
||||
out = v.fpoint;
|
||||
out = v.fpoint();
|
||||
break;
|
||||
|
||||
case nThunk:
|
||||
|
|
|
@ -32,23 +32,18 @@ static void posToXML(EvalState & state, XMLAttrs & xmlAttrs, const Pos & pos)
|
|||
|
||||
|
||||
static void showAttrs(EvalState & state, bool strict, bool location,
|
||||
Bindings & attrs, XMLWriter & doc, NixStringContext & context, PathSet & drvsSeen)
|
||||
const Bindings & attrs, XMLWriter & doc, NixStringContext & context, PathSet & drvsSeen)
|
||||
{
|
||||
StringSet names;
|
||||
|
||||
for (auto & i : attrs)
|
||||
names.emplace(state.symbols[i.name]);
|
||||
|
||||
for (auto & i : names) {
|
||||
Attr & a(*attrs.find(state.symbols.create(i)));
|
||||
|
||||
for (auto & a : attrs.lexicographicOrder(state.symbols)) {
|
||||
XMLAttrs xmlAttrs;
|
||||
xmlAttrs["name"] = i;
|
||||
if (location && a.pos) posToXML(state, xmlAttrs, state.positions[a.pos]);
|
||||
xmlAttrs["name"] = state.symbols[a->name];
|
||||
if (location && a->pos) posToXML(state, xmlAttrs, state.positions[a->pos]);
|
||||
|
||||
XMLOpenElement _(doc, "attr", xmlAttrs);
|
||||
printValueAsXML(state, strict, location,
|
||||
*a.value, doc, context, drvsSeen, a.pos);
|
||||
*a->value, doc, context, drvsSeen, a->pos);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -64,11 +59,11 @@ static void printValueAsXML(EvalState & state, bool strict, bool location,
|
|||
switch (v.type()) {
|
||||
|
||||
case nInt:
|
||||
doc.writeEmptyElement("int", singletonAttrs("value", fmt("%1%", v.integer)));
|
||||
doc.writeEmptyElement("int", singletonAttrs("value", fmt("%1%", v.integer())));
|
||||
break;
|
||||
|
||||
case nBool:
|
||||
doc.writeEmptyElement("bool", singletonAttrs("value", v.boolean ? "true" : "false"));
|
||||
doc.writeEmptyElement("bool", singletonAttrs("value", v.boolean() ? "true" : "false"));
|
||||
break;
|
||||
|
||||
case nString:
|
||||
|
@ -89,18 +84,14 @@ static void printValueAsXML(EvalState & state, bool strict, bool location,
|
|||
if (state.isDerivation(v)) {
|
||||
XMLAttrs xmlAttrs;
|
||||
|
||||
Bindings::iterator a = v.attrs->find(state.symbols.create("derivation"));
|
||||
|
||||
Path drvPath;
|
||||
a = v.attrs->find(state.sDrvPath);
|
||||
if (a != v.attrs->end()) {
|
||||
if (auto a = v.attrs()->get(state.sDrvPath)) {
|
||||
if (strict) state.forceValue(*a->value, a->pos);
|
||||
if (a->value->type() == nString)
|
||||
xmlAttrs["drvPath"] = drvPath = a->value->c_str();
|
||||
}
|
||||
|
||||
a = v.attrs->find(state.sOutPath);
|
||||
if (a != v.attrs->end()) {
|
||||
if (auto a = v.attrs()->get(state.sOutPath)) {
|
||||
if (strict) state.forceValue(*a->value, a->pos);
|
||||
if (a->value->type() == nString)
|
||||
xmlAttrs["outPath"] = a->value->c_str();
|
||||
|
@ -109,14 +100,14 @@ static void printValueAsXML(EvalState & state, bool strict, bool location,
|
|||
XMLOpenElement _(doc, "derivation", xmlAttrs);
|
||||
|
||||
if (drvPath != "" && drvsSeen.insert(drvPath).second)
|
||||
showAttrs(state, strict, location, *v.attrs, doc, context, drvsSeen);
|
||||
showAttrs(state, strict, location, *v.attrs(), doc, context, drvsSeen);
|
||||
else
|
||||
doc.writeEmptyElement("repeated");
|
||||
}
|
||||
|
||||
else {
|
||||
XMLOpenElement _(doc, "attrs");
|
||||
showAttrs(state, strict, location, *v.attrs, doc, context, drvsSeen);
|
||||
showAttrs(state, strict, location, *v.attrs(), doc, context, drvsSeen);
|
||||
}
|
||||
|
||||
break;
|
||||
|
@ -135,28 +126,28 @@ static void printValueAsXML(EvalState & state, bool strict, bool location,
|
|||
break;
|
||||
}
|
||||
XMLAttrs xmlAttrs;
|
||||
if (location) posToXML(state, xmlAttrs, state.positions[v.lambda.fun->pos]);
|
||||
if (location) posToXML(state, xmlAttrs, state.positions[v.payload.lambda.fun->pos]);
|
||||
XMLOpenElement _(doc, "function", xmlAttrs);
|
||||
|
||||
if (v.lambda.fun->hasFormals()) {
|
||||
if (v.payload.lambda.fun->hasFormals()) {
|
||||
XMLAttrs attrs;
|
||||
if (v.lambda.fun->arg) attrs["name"] = state.symbols[v.lambda.fun->arg];
|
||||
if (v.lambda.fun->formals->ellipsis) attrs["ellipsis"] = "1";
|
||||
if (v.payload.lambda.fun->arg) attrs["name"] = state.symbols[v.payload.lambda.fun->arg];
|
||||
if (v.payload.lambda.fun->formals->ellipsis) attrs["ellipsis"] = "1";
|
||||
XMLOpenElement _(doc, "attrspat", attrs);
|
||||
for (auto & i : v.lambda.fun->formals->lexicographicOrder(state.symbols))
|
||||
for (auto & i : v.payload.lambda.fun->formals->lexicographicOrder(state.symbols))
|
||||
doc.writeEmptyElement("attr", singletonAttrs("name", state.symbols[i.name]));
|
||||
} else
|
||||
doc.writeEmptyElement("varpat", singletonAttrs("name", state.symbols[v.lambda.fun->arg]));
|
||||
doc.writeEmptyElement("varpat", singletonAttrs("name", state.symbols[v.payload.lambda.fun->arg]));
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
case nExternal:
|
||||
v.external->printValueAsXML(state, strict, location, doc, context, drvsSeen, pos);
|
||||
v.external()->printValueAsXML(state, strict, location, doc, context, drvsSeen, pos);
|
||||
break;
|
||||
|
||||
case nFloat:
|
||||
doc.writeEmptyElement("float", singletonAttrs("value", fmt("%1%", v.fpoint)));
|
||||
doc.writeEmptyElement("float", singletonAttrs("value", fmt("%1%", v.fpoint())));
|
||||
break;
|
||||
|
||||
case nThunk:
|
||||
|
|
|
@ -234,14 +234,14 @@ public:
|
|||
ExprLambda * fun;
|
||||
};
|
||||
|
||||
union
|
||||
using Payload = union
|
||||
{
|
||||
NixInt integer;
|
||||
bool boolean;
|
||||
|
||||
StringWithContext string;
|
||||
|
||||
Path _path;
|
||||
Path path;
|
||||
|
||||
Bindings * attrs;
|
||||
struct {
|
||||
|
@ -258,6 +258,8 @@ public:
|
|||
NixFloat fpoint;
|
||||
};
|
||||
|
||||
Payload payload;
|
||||
|
||||
/**
|
||||
* Returns the normal type of a Value. This only returns nThunk if
|
||||
* the Value hasn't been forceValue'd
|
||||
|
@ -286,34 +288,25 @@ public:
|
|||
abort();
|
||||
}
|
||||
|
||||
/**
|
||||
* After overwriting an app node, be sure to clear pointers in the
|
||||
* Value to ensure that the target isn't kept alive unnecessarily.
|
||||
*/
|
||||
inline void clearValue()
|
||||
inline void finishValue(InternalType newType, Payload newPayload)
|
||||
{
|
||||
app.left = app.right = 0;
|
||||
payload = newPayload;
|
||||
internalType = newType;
|
||||
}
|
||||
|
||||
inline void mkInt(NixInt n)
|
||||
{
|
||||
clearValue();
|
||||
internalType = tInt;
|
||||
integer = n;
|
||||
finishValue(tInt, { .integer = n });
|
||||
}
|
||||
|
||||
inline void mkBool(bool b)
|
||||
{
|
||||
clearValue();
|
||||
internalType = tBool;
|
||||
boolean = b;
|
||||
finishValue(tBool, { .boolean = b });
|
||||
}
|
||||
|
||||
inline void mkString(const char * s, const char * * context = 0)
|
||||
{
|
||||
internalType = tString;
|
||||
string.c_str = s;
|
||||
string.context = context;
|
||||
finishValue(tString, { .string = { .c_str = s, .context = context } });
|
||||
}
|
||||
|
||||
void mkString(std::string_view s);
|
||||
|
@ -332,63 +325,44 @@ public:
|
|||
|
||||
inline void mkPath(InputAccessor * accessor, const char * path)
|
||||
{
|
||||
clearValue();
|
||||
internalType = tPath;
|
||||
_path.accessor = accessor;
|
||||
_path.path = path;
|
||||
finishValue(tPath, { .path = { .accessor = accessor, .path = path } });
|
||||
}
|
||||
|
||||
inline void mkNull()
|
||||
{
|
||||
clearValue();
|
||||
internalType = tNull;
|
||||
finishValue(tNull, {});
|
||||
}
|
||||
|
||||
inline void mkAttrs(Bindings * a)
|
||||
{
|
||||
clearValue();
|
||||
internalType = tAttrs;
|
||||
attrs = a;
|
||||
finishValue(tAttrs, { .attrs = a });
|
||||
}
|
||||
|
||||
Value & mkAttrs(BindingsBuilder & bindings);
|
||||
|
||||
void mkList(const ListBuilder & builder)
|
||||
{
|
||||
clearValue();
|
||||
if (builder.size == 1) {
|
||||
smallList[0] = builder.inlineElems[0];
|
||||
internalType = tList1;
|
||||
} else if (builder.size == 2) {
|
||||
smallList[0] = builder.inlineElems[0];
|
||||
smallList[1] = builder.inlineElems[1];
|
||||
internalType = tList2;
|
||||
} else {
|
||||
bigList.size = builder.size;
|
||||
bigList.elems = builder.elems;
|
||||
internalType = tListN;
|
||||
}
|
||||
if (builder.size == 1)
|
||||
finishValue(tList1, { .smallList = { builder.inlineElems[0] } });
|
||||
else if (builder.size == 2)
|
||||
finishValue(tList2, { .smallList = { builder.inlineElems[0], builder.inlineElems[1] } });
|
||||
else
|
||||
finishValue(tListN, { .bigList = { .size = builder.size, .elems = builder.elems } });
|
||||
}
|
||||
|
||||
inline void mkThunk(Env * e, Expr * ex)
|
||||
{
|
||||
internalType = tThunk;
|
||||
thunk.env = e;
|
||||
thunk.expr = ex;
|
||||
finishValue(tThunk, { .thunk = { .env = e, .expr = ex } });
|
||||
}
|
||||
|
||||
inline void mkApp(Value * l, Value * r)
|
||||
{
|
||||
internalType = tApp;
|
||||
app.left = l;
|
||||
app.right = r;
|
||||
finishValue(tApp, { .app = { .left = l, .right = r } });
|
||||
}
|
||||
|
||||
inline void mkLambda(Env * e, ExprLambda * f)
|
||||
{
|
||||
internalType = tLambda;
|
||||
lambda.env = e;
|
||||
lambda.fun = f;
|
||||
finishValue(tLambda, { .lambda = { .env = e, .fun = f } });
|
||||
}
|
||||
|
||||
inline void mkBlackhole();
|
||||
|
@ -397,28 +371,22 @@ public:
|
|||
|
||||
inline void mkPrimOpApp(Value * l, Value * r)
|
||||
{
|
||||
internalType = tPrimOpApp;
|
||||
primOpApp.left = l;
|
||||
primOpApp.right = r;
|
||||
finishValue(tPrimOpApp, { .primOpApp = { .left = l, .right = r } });
|
||||
}
|
||||
|
||||
/**
|
||||
* For a `tPrimOpApp` value, get the original `PrimOp` value.
|
||||
*/
|
||||
PrimOp * primOpAppPrimOp() const;
|
||||
const PrimOp * primOpAppPrimOp() const;
|
||||
|
||||
inline void mkExternal(ExternalValueBase * e)
|
||||
{
|
||||
clearValue();
|
||||
internalType = tExternal;
|
||||
external = e;
|
||||
finishValue(tExternal, { .external = e });
|
||||
}
|
||||
|
||||
inline void mkFloat(NixFloat n)
|
||||
{
|
||||
clearValue();
|
||||
internalType = tFloat;
|
||||
fpoint = n;
|
||||
finishValue(tFloat, { .fpoint = n });
|
||||
}
|
||||
|
||||
bool isList() const
|
||||
|
@ -428,7 +396,7 @@ public:
|
|||
|
||||
Value * const * listElems()
|
||||
{
|
||||
return internalType == tList1 || internalType == tList2 ? smallList : bigList.elems;
|
||||
return internalType == tList1 || internalType == tList2 ? payload.smallList : payload.bigList.elems;
|
||||
}
|
||||
|
||||
std::span<Value * const> listItems() const
|
||||
|
@ -439,12 +407,12 @@ public:
|
|||
|
||||
Value * const * listElems() const
|
||||
{
|
||||
return internalType == tList1 || internalType == tList2 ? smallList : bigList.elems;
|
||||
return internalType == tList1 || internalType == tList2 ? payload.smallList : payload.bigList.elems;
|
||||
}
|
||||
|
||||
size_t listSize() const
|
||||
{
|
||||
return internalType == tList1 ? 1 : internalType == tList2 ? 2 : bigList.size;
|
||||
return internalType == tList1 ? 1 : internalType == tList2 ? 2 : payload.bigList.size;
|
||||
}
|
||||
|
||||
PosIdx determinePos(const PosIdx pos) const;
|
||||
|
@ -460,26 +428,44 @@ public:
|
|||
{
|
||||
assert(internalType == tPath);
|
||||
return SourcePath(
|
||||
ref(_path.accessor->shared_from_this()),
|
||||
CanonPath(CanonPath::unchecked_t(), _path.path));
|
||||
ref(payload.path.accessor->shared_from_this()),
|
||||
CanonPath(CanonPath::unchecked_t(), payload.path.path));
|
||||
}
|
||||
|
||||
std::string_view string_view() const
|
||||
{
|
||||
assert(internalType == tString);
|
||||
return std::string_view(string.c_str);
|
||||
return std::string_view(payload.string.c_str);
|
||||
}
|
||||
|
||||
const char * const c_str() const
|
||||
{
|
||||
assert(internalType == tString);
|
||||
return string.c_str;
|
||||
return payload.string.c_str;
|
||||
}
|
||||
|
||||
const char * * context() const
|
||||
{
|
||||
return string.context;
|
||||
return payload.string.context;
|
||||
}
|
||||
|
||||
ExternalValueBase * external() const
|
||||
{ return payload.external; }
|
||||
|
||||
const Bindings * attrs() const
|
||||
{ return payload.attrs; }
|
||||
|
||||
const PrimOp * primOp() const
|
||||
{ return payload.primOp; }
|
||||
|
||||
bool boolean() const
|
||||
{ return payload.boolean; }
|
||||
|
||||
NixInt integer() const
|
||||
{ return payload.integer; }
|
||||
|
||||
NixFloat fpoint() const
|
||||
{ return payload.fpoint; }
|
||||
};
|
||||
|
||||
|
||||
|
@ -487,13 +473,12 @@ extern ExprBlackHole eBlackHole;
|
|||
|
||||
bool Value::isBlackhole() const
|
||||
{
|
||||
return internalType == tThunk && thunk.expr == (Expr*) &eBlackHole;
|
||||
return internalType == tThunk && payload.thunk.expr == (Expr*) &eBlackHole;
|
||||
}
|
||||
|
||||
void Value::mkBlackhole()
|
||||
{
|
||||
internalType = tThunk;
|
||||
thunk.expr = (Expr*) &eBlackHole;
|
||||
mkThunk(nullptr, (Expr *) &eBlackHole);
|
||||
}
|
||||
|
||||
|
||||
|
|
|
@ -3,6 +3,7 @@
|
|||
#include "input-accessor.hh"
|
||||
#include "source-path.hh"
|
||||
#include "fetch-to-store.hh"
|
||||
#include "json-utils.hh"
|
||||
|
||||
#include <nlohmann/json.hpp>
|
||||
|
||||
|
@ -412,3 +413,20 @@ std::string publicKeys_to_string(const std::vector<PublicKey>& publicKeys)
|
|||
}
|
||||
|
||||
}
|
||||
|
||||
namespace nlohmann {
|
||||
|
||||
using namespace nix;
|
||||
|
||||
fetchers::PublicKey adl_serializer<fetchers::PublicKey>::from_json(const json & json) {
|
||||
auto type = optionalValueAt(json, "type").value_or("ssh-ed25519");
|
||||
auto key = valueAt(json, "key");
|
||||
return fetchers::PublicKey { getString(type), getString(key) };
|
||||
}
|
||||
|
||||
void adl_serializer<fetchers::PublicKey>::to_json(json & json, fetchers::PublicKey p) {
|
||||
json["type"] = p.type;
|
||||
json["key"] = p.key;
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -4,6 +4,7 @@
|
|||
#include "types.hh"
|
||||
#include "hash.hh"
|
||||
#include "canon-path.hh"
|
||||
#include "json-impls.hh"
|
||||
#include "attrs.hh"
|
||||
#include "url.hh"
|
||||
|
||||
|
@ -230,8 +231,9 @@ struct PublicKey
|
|||
std::string type = "ssh-ed25519";
|
||||
std::string key;
|
||||
};
|
||||
NLOHMANN_DEFINE_TYPE_NON_INTRUSIVE_WITH_DEFAULT(PublicKey, type, key)
|
||||
|
||||
std::string publicKeys_to_string(const std::vector<PublicKey>&);
|
||||
|
||||
}
|
||||
|
||||
JSON_IMPL(fetchers::PublicKey)
|
||||
|
|
|
@ -38,7 +38,7 @@ std::string FilteringInputAccessor::readLink(const CanonPath & path)
|
|||
|
||||
std::string FilteringInputAccessor::showPath(const CanonPath & path)
|
||||
{
|
||||
return next->showPath(prefix / path);
|
||||
return displayPrefix + next->showPath(prefix / path) + displaySuffix;
|
||||
}
|
||||
|
||||
void FilteringInputAccessor::checkAccess(const CanonPath & path)
|
||||
|
|
|
@ -27,7 +27,9 @@ struct FilteringInputAccessor : InputAccessor
|
|||
: next(src.accessor)
|
||||
, prefix(src.path)
|
||||
, makeNotAllowedError(std::move(makeNotAllowedError))
|
||||
{ }
|
||||
{
|
||||
displayPrefix.clear();
|
||||
}
|
||||
|
||||
std::string readFile(const CanonPath & path) override;
|
||||
|
||||
|
|
|
@ -24,7 +24,10 @@ ref<InputAccessor> makeStorePathAccessor(
|
|||
const StorePath & storePath)
|
||||
{
|
||||
// FIXME: should use `store->getFSAccessor()`
|
||||
return makeFSInputAccessor(std::filesystem::path { store->toRealPath(storePath) });
|
||||
auto root = std::filesystem::path { store->toRealPath(storePath) };
|
||||
auto accessor = makeFSInputAccessor(root);
|
||||
accessor->setPathDisplay(root.string());
|
||||
return accessor;
|
||||
}
|
||||
|
||||
SourcePath getUnfilteredRootPath(CanonPath path)
|
||||
|
|
|
@ -151,11 +151,11 @@ struct GitRepoImpl : GitRepo, std::enable_shared_from_this<GitRepoImpl>
|
|||
{
|
||||
initLibGit2();
|
||||
|
||||
if (pathExists(path.native())) {
|
||||
if (git_repository_open(Setter(repo), path.c_str()))
|
||||
if (pathExists(path.string())) {
|
||||
if (git_repository_open(Setter(repo), path.string().c_str()))
|
||||
throw Error("opening Git repository '%s': %s", path, git_error_last()->message);
|
||||
} else {
|
||||
if (git_repository_init(Setter(repo), path.c_str(), bare))
|
||||
if (git_repository_init(Setter(repo), path.string().c_str(), bare))
|
||||
throw Error("creating Git repository '%s': %s", path, git_error_last()->message);
|
||||
}
|
||||
}
|
||||
|
@ -198,6 +198,12 @@ struct GitRepoImpl : GitRepo, std::enable_shared_from_this<GitRepoImpl>
|
|||
return git_repository_is_shallow(*this);
|
||||
}
|
||||
|
||||
void setRemote(const std::string & name, const std::string & url) override
|
||||
{
|
||||
if (git_remote_set_url(*this, name.c_str(), url.c_str()))
|
||||
throw Error("setting remote '%s' URL to '%s': %s", name, url, git_error_last()->message);
|
||||
}
|
||||
|
||||
Hash resolveRef(std::string ref) override
|
||||
{
|
||||
Object object;
|
||||
|
@ -210,7 +216,7 @@ struct GitRepoImpl : GitRepo, std::enable_shared_from_this<GitRepoImpl>
|
|||
std::vector<Submodule> parseSubmodules(const std::filesystem::path & configFile)
|
||||
{
|
||||
GitConfig config;
|
||||
if (git_config_open_ondisk(Setter(config), configFile.c_str()))
|
||||
if (git_config_open_ondisk(Setter(config), configFile.string().c_str()))
|
||||
throw Error("parsing .gitmodules file: %s", git_error_last()->message);
|
||||
|
||||
ConfigIterator it;
|
||||
|
@ -282,7 +288,7 @@ struct GitRepoImpl : GitRepo, std::enable_shared_from_this<GitRepoImpl>
|
|||
|
||||
/* Get submodule info. */
|
||||
auto modulesFile = path / ".gitmodules";
|
||||
if (pathExists(modulesFile))
|
||||
if (pathExists(modulesFile.string()))
|
||||
info.submodules = parseSubmodules(modulesFile);
|
||||
|
||||
return info;
|
||||
|
@ -302,9 +308,7 @@ struct GitRepoImpl : GitRepo, std::enable_shared_from_this<GitRepoImpl>
|
|||
|
||||
std::vector<std::tuple<Submodule, Hash>> getSubmodules(const Hash & rev, bool exportIgnore) override;
|
||||
|
||||
std::string resolveSubmoduleUrl(
|
||||
const std::string & url,
|
||||
const std::string & base) override
|
||||
std::string resolveSubmoduleUrl(const std::string & url) override
|
||||
{
|
||||
git_buf buf = GIT_BUF_INIT;
|
||||
if (git_submodule_resolve_url(&buf, *this, url.c_str()))
|
||||
|
@ -312,10 +316,6 @@ struct GitRepoImpl : GitRepo, std::enable_shared_from_this<GitRepoImpl>
|
|||
Finally cleanup = [&]() { git_buf_dispose(&buf); };
|
||||
|
||||
std::string res(buf.ptr);
|
||||
|
||||
if (!hasPrefix(res, "/") && res.find("://") == res.npos)
|
||||
res = parseURL(base + "/" + res).canonicalise().to_string();
|
||||
|
||||
return res;
|
||||
}
|
||||
|
||||
|
@ -377,10 +377,10 @@ struct GitRepoImpl : GitRepo, std::enable_shared_from_this<GitRepoImpl>
|
|||
auto dir = this->path;
|
||||
Strings gitArgs;
|
||||
if (shallow) {
|
||||
gitArgs = { "-C", dir, "fetch", "--quiet", "--force", "--depth", "1", "--", url, refspec };
|
||||
gitArgs = { "-C", dir.string(), "fetch", "--quiet", "--force", "--depth", "1", "--", url, refspec };
|
||||
}
|
||||
else {
|
||||
gitArgs = { "-C", dir, "fetch", "--quiet", "--force", "--", url, refspec };
|
||||
gitArgs = { "-C", dir.string(), "fetch", "--quiet", "--force", "--", url, refspec };
|
||||
}
|
||||
|
||||
runProgram(RunOptions {
|
||||
|
@ -426,7 +426,7 @@ struct GitRepoImpl : GitRepo, std::enable_shared_from_this<GitRepoImpl>
|
|||
.args = {
|
||||
"-c",
|
||||
"gpg.ssh.allowedSignersFile=" + allowedSignersFile,
|
||||
"-C", path,
|
||||
"-C", path.string(),
|
||||
"verify-commit",
|
||||
rev.gitRev()
|
||||
},
|
||||
|
|
|
@ -32,6 +32,8 @@ struct GitRepo
|
|||
/* Return the commit hash to which a ref points. */
|
||||
virtual Hash resolveRef(std::string ref) = 0;
|
||||
|
||||
virtual void setRemote(const std::string & name, const std::string & url) = 0;
|
||||
|
||||
/**
|
||||
* Info about a submodule.
|
||||
*/
|
||||
|
@ -69,9 +71,7 @@ struct GitRepo
|
|||
*/
|
||||
virtual std::vector<std::tuple<Submodule, Hash>> getSubmodules(const Hash & rev, bool exportIgnore) = 0;
|
||||
|
||||
virtual std::string resolveSubmoduleUrl(
|
||||
const std::string & url,
|
||||
const std::string & base) = 0;
|
||||
virtual std::string resolveSubmoduleUrl(const std::string & url) = 0;
|
||||
|
||||
virtual bool hasObject(const Hash & oid) = 0;
|
||||
|
||||
|
|
|
@ -357,7 +357,7 @@ struct GitHubInputScheme : GitArchiveInputScheme
|
|||
auto json = nlohmann::json::parse(
|
||||
readFile(
|
||||
store->toRealPath(
|
||||
downloadFile(store, url, "source", false, headers).storePath)));
|
||||
downloadFile(store, url, "source", headers).storePath)));
|
||||
|
||||
return RefInfo {
|
||||
.rev = Hash::parseAny(std::string { json["sha"] }, HashAlgorithm::SHA1),
|
||||
|
@ -431,7 +431,7 @@ struct GitLabInputScheme : GitArchiveInputScheme
|
|||
auto json = nlohmann::json::parse(
|
||||
readFile(
|
||||
store->toRealPath(
|
||||
downloadFile(store, url, "source", false, headers).storePath)));
|
||||
downloadFile(store, url, "source", headers).storePath)));
|
||||
|
||||
return RefInfo {
|
||||
.rev = Hash::parseAny(std::string(json[0]["id"]), HashAlgorithm::SHA1)
|
||||
|
@ -495,7 +495,7 @@ struct SourceHutInputScheme : GitArchiveInputScheme
|
|||
std::string refUri;
|
||||
if (ref == "HEAD") {
|
||||
auto file = store->toRealPath(
|
||||
downloadFile(store, fmt("%s/HEAD", base_url), "source", false, headers).storePath);
|
||||
downloadFile(store, fmt("%s/HEAD", base_url), "source", headers).storePath);
|
||||
std::ifstream is(file);
|
||||
std::string line;
|
||||
getline(is, line);
|
||||
|
@ -511,7 +511,7 @@ struct SourceHutInputScheme : GitArchiveInputScheme
|
|||
std::regex refRegex(refUri);
|
||||
|
||||
auto file = store->toRealPath(
|
||||
downloadFile(store, fmt("%s/info/refs", base_url), "source", false, headers).storePath);
|
||||
downloadFile(store, fmt("%s/info/refs", base_url), "source", headers).storePath);
|
||||
std::ifstream is(file);
|
||||
|
||||
std::string line;
|
||||
|
|
|
@ -9,6 +9,8 @@ struct MountedInputAccessor : InputAccessor
|
|||
MountedInputAccessor(std::map<CanonPath, ref<InputAccessor>> _mounts)
|
||||
: mounts(std::move(_mounts))
|
||||
{
|
||||
displayPrefix.clear();
|
||||
|
||||
// Currently we require a root filesystem. This could be relaxed.
|
||||
assert(mounts.contains(CanonPath::root));
|
||||
|
||||
|
@ -48,7 +50,7 @@ struct MountedInputAccessor : InputAccessor
|
|||
std::string showPath(const CanonPath & path) override
|
||||
{
|
||||
auto [accessor, subpath] = resolve(path);
|
||||
return accessor->showPath(subpath);
|
||||
return displayPrefix + accessor->showPath(subpath) + displaySuffix;
|
||||
}
|
||||
|
||||
std::pair<ref<InputAccessor>, CanonPath> resolve(CanonPath path)
|
||||
|
|
|
@ -158,7 +158,7 @@ static std::shared_ptr<Registry> getGlobalRegistry(ref<Store> store)
|
|||
}
|
||||
|
||||
if (!hasPrefix(path, "/")) {
|
||||
auto storePath = downloadFile(store, path, "flake-registry.json", false).storePath;
|
||||
auto storePath = downloadFile(store, path, "flake-registry.json").storePath;
|
||||
if (auto store2 = store.dynamic_pointer_cast<LocalFSStore>())
|
||||
store2->addPermRoot(storePath, getCacheDir() + "/nix/flake-registry.json");
|
||||
path = store->toRealPath(storePath);
|
||||
|
|
|
@ -19,7 +19,6 @@ DownloadFileResult downloadFile(
|
|||
ref<Store> store,
|
||||
const std::string & url,
|
||||
const std::string & name,
|
||||
bool locked,
|
||||
const Headers & headers)
|
||||
{
|
||||
// FIXME: check store
|
||||
|
@ -101,7 +100,7 @@ DownloadFileResult downloadFile(
|
|||
inAttrs,
|
||||
infoAttrs,
|
||||
*storePath,
|
||||
locked);
|
||||
false);
|
||||
}
|
||||
|
||||
return {
|
||||
|
@ -306,7 +305,7 @@ struct FileInputScheme : CurlInputScheme
|
|||
the Nix store directly, since there is little deduplication
|
||||
benefit in using the Git cache for single big files like
|
||||
tarballs. */
|
||||
auto file = downloadFile(store, getStrAttr(input.attrs, "url"), input.getName(), false);
|
||||
auto file = downloadFile(store, getStrAttr(input.attrs, "url"), input.getName());
|
||||
|
||||
auto narHash = store->queryPathInfo(file.storePath)->narHash;
|
||||
input.attrs.insert_or_assign("narHash", narHash.to_string(HashFormat::SRI, true));
|
||||
|
|
|
@ -25,7 +25,6 @@ DownloadFileResult downloadFile(
|
|||
ref<Store> store,
|
||||
const std::string & url,
|
||||
const std::string & name,
|
||||
bool locked,
|
||||
const Headers & headers = {});
|
||||
|
||||
struct DownloadTarballResult
|
||||
|
|
|
@ -527,6 +527,9 @@ struct GitInputScheme : InputScheme
|
|||
|
||||
auto repo = GitRepo::openRepo(cacheDir, true, true);
|
||||
|
||||
// We need to set the origin so resolving submodule URLs works
|
||||
repo->setRemote("origin", repoInfo.url);
|
||||
|
||||
Path localRefFile =
|
||||
ref.compare(0, 5, "refs/") == 0
|
||||
? cacheDir + "/" + ref
|
||||
|
@ -630,7 +633,7 @@ struct GitInputScheme : InputScheme
|
|||
std::map<CanonPath, nix::ref<InputAccessor>> mounts;
|
||||
|
||||
for (auto & [submodule, submoduleRev] : repo->getSubmodules(rev, exportIgnore)) {
|
||||
auto resolved = repo->resolveSubmoduleUrl(submodule.url, repoInfo.url);
|
||||
auto resolved = repo->resolveSubmoduleUrl(submodule.url);
|
||||
debug("Git submodule %s: %s %s %s -> %s",
|
||||
submodule.path, submodule.url, submodule.branch, submoduleRev.gitRev(), resolved);
|
||||
fetchers::Attrs attrs;
|
||||
|
@ -643,6 +646,7 @@ struct GitInputScheme : InputScheme
|
|||
auto submoduleInput = fetchers::Input::fromAttrs(std::move(attrs));
|
||||
auto [submoduleAccessor, submoduleInput2] =
|
||||
submoduleInput.getAccessor(store);
|
||||
submoduleAccessor->setPathDisplay("«" + submoduleInput.to_string() + "»");
|
||||
mounts.insert_or_assign(submodule.path, submoduleAccessor);
|
||||
}
|
||||
|
||||
|
@ -679,6 +683,8 @@ struct GitInputScheme : InputScheme
|
|||
exportIgnore,
|
||||
makeNotAllowedError(repoInfo.url));
|
||||
|
||||
accessor->setPathDisplay(repoInfo.url);
|
||||
|
||||
/* If the repo has submodules, return a mounted input accessor
|
||||
consisting of the accessor for the top-level repo and the
|
||||
accessors for the submodule workdirs. */
|
||||
|
@ -695,6 +701,7 @@ struct GitInputScheme : InputScheme
|
|||
auto submoduleInput = fetchers::Input::fromAttrs(std::move(attrs));
|
||||
auto [submoduleAccessor, submoduleInput2] =
|
||||
submoduleInput.getAccessor(store);
|
||||
submoduleAccessor->setPathDisplay("«" + submoduleInput.to_string() + "»");
|
||||
|
||||
/* If the submodule is dirty, mark this repo dirty as
|
||||
well. */
|
||||
|
|
|
@ -352,7 +352,11 @@ struct MercurialInputScheme : InputScheme
|
|||
|
||||
auto storePath = fetchToStore(store, input);
|
||||
|
||||
return {makeStorePathAccessor(store, storePath), input};
|
||||
auto accessor = makeStorePathAccessor(store, storePath);
|
||||
|
||||
accessor->setPathDisplay("«" + input.to_string() + "»");
|
||||
|
||||
return {accessor, input};
|
||||
}
|
||||
|
||||
bool isLocked(const Input & input) const override
|
||||
|
|
|
@ -108,7 +108,9 @@ std::string getArg(const std::string & opt,
|
|||
return *i;
|
||||
}
|
||||
|
||||
#ifndef _WIN32
|
||||
static void sigHandler(int signo) { }
|
||||
#endif
|
||||
|
||||
|
||||
void initNix()
|
||||
|
@ -121,6 +123,7 @@ void initNix()
|
|||
|
||||
initLibStore();
|
||||
|
||||
#ifndef _WIN32
|
||||
unix::startSignalHandlerThread();
|
||||
|
||||
/* Reset SIGCHLD to its default. */
|
||||
|
@ -135,6 +138,7 @@ void initNix()
|
|||
/* Install a dummy SIGUSR1 handler for use with pthread_kill(). */
|
||||
act.sa_handler = sigHandler;
|
||||
if (sigaction(SIGUSR1, &act, 0)) throw SysError("handling SIGUSR1");
|
||||
#endif
|
||||
|
||||
#if __APPLE__
|
||||
/* HACK: on darwin, we need can’t use sigprocmask with SIGWINCH.
|
||||
|
@ -156,21 +160,26 @@ void initNix()
|
|||
if (sigaction(SIGTRAP, &act, 0)) throw SysError("handling SIGTRAP");
|
||||
#endif
|
||||
|
||||
#ifndef _WIN32
|
||||
/* Register a SIGSEGV handler to detect stack overflows.
|
||||
Why not initLibExpr()? initGC() is essentially that, but
|
||||
detectStackOverflow is not an instance of the init function concept, as
|
||||
it may have to be invoked more than once per process. */
|
||||
detectStackOverflow();
|
||||
#endif
|
||||
|
||||
/* There is no privacy in the Nix system ;-) At least not for
|
||||
now. In particular, store objects should be readable by
|
||||
everybody. */
|
||||
umask(0022);
|
||||
|
||||
#ifndef _WIN32
|
||||
/* Initialise the PRNG. */
|
||||
struct timeval tv;
|
||||
gettimeofday(&tv, 0);
|
||||
srandom(tv.tv_usec);
|
||||
#endif
|
||||
|
||||
|
||||
}
|
||||
|
||||
|
@ -368,6 +377,9 @@ RunPager::RunPager()
|
|||
Pipe toPager;
|
||||
toPager.create();
|
||||
|
||||
#ifdef _WIN32 // TODO re-enable on Windows, once we can start processes.
|
||||
throw Error("Commit signature verification not implemented on Windows yet");
|
||||
#else
|
||||
pid = startProcess([&]() {
|
||||
if (dup2(toPager.readSide.get(), STDIN_FILENO) == -1)
|
||||
throw SysError("dupping stdin");
|
||||
|
@ -386,17 +398,20 @@ RunPager::RunPager()
|
|||
std_out = fcntl(STDOUT_FILENO, F_DUPFD_CLOEXEC, 0);
|
||||
if (dup2(toPager.writeSide.get(), STDOUT_FILENO) == -1)
|
||||
throw SysError("dupping standard output");
|
||||
#endif
|
||||
}
|
||||
|
||||
|
||||
RunPager::~RunPager()
|
||||
{
|
||||
try {
|
||||
#ifndef _WIN32 // TODO re-enable on Windows, once we can start processes.
|
||||
if (pid != -1) {
|
||||
std::cout.flush();
|
||||
dup2(std_out, STDOUT_FILENO);
|
||||
pid.wait();
|
||||
}
|
||||
#endif
|
||||
} catch (...) {
|
||||
ignoreException();
|
||||
}
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
#pragma once
|
||||
///@file
|
||||
|
||||
#include "file-descriptor.hh"
|
||||
#include "processes.hh"
|
||||
#include "args.hh"
|
||||
#include "args/root.hh"
|
||||
|
@ -89,8 +90,10 @@ public:
|
|||
~RunPager();
|
||||
|
||||
private:
|
||||
#ifndef _WIN32 // TODO re-enable on Windows, once we can start processes.
|
||||
Pid pid;
|
||||
int std_out;
|
||||
#endif
|
||||
Descriptor std_out;
|
||||
};
|
||||
|
||||
extern volatile ::sig_atomic_t blockInt;
|
||||
|
@ -112,6 +115,7 @@ struct PrintFreed
|
|||
};
|
||||
|
||||
|
||||
#ifndef _WIN32
|
||||
/**
|
||||
* Install a SIGSEGV handler to detect stack overflows.
|
||||
*/
|
||||
|
@ -141,5 +145,6 @@ extern std::function<void(siginfo_t * info, void * ctx)> stackOverflowHandler;
|
|||
* logger. Exits the process immediately after.
|
||||
*/
|
||||
void defaultStackOverflowHandler(siginfo_t * info, void * ctx);
|
||||
#endif
|
||||
|
||||
}
|
||||
|
|
|
@ -56,7 +56,7 @@ void nix_store_free(Store * store)
|
|||
delete store;
|
||||
}
|
||||
|
||||
nix_err nix_store_get_uri(nix_c_context * context, Store * store, void * callback, void * user_data)
|
||||
nix_err nix_store_get_uri(nix_c_context * context, Store * store, nix_get_string_callback callback, void * user_data)
|
||||
{
|
||||
if (context)
|
||||
context->last_err_code = NIX_OK;
|
||||
|
@ -67,7 +67,8 @@ nix_err nix_store_get_uri(nix_c_context * context, Store * store, void * callbac
|
|||
NIXC_CATCH_ERRS
|
||||
}
|
||||
|
||||
nix_err nix_store_get_version(nix_c_context * context, Store * store, void * callback, void * user_data)
|
||||
nix_err
|
||||
nix_store_get_version(nix_c_context * context, Store * store, nix_get_string_callback callback, void * user_data)
|
||||
{
|
||||
if (context)
|
||||
context->last_err_code = NIX_OK;
|
||||
|
@ -128,7 +129,18 @@ nix_err nix_store_realise(
|
|||
NIXC_CATCH_ERRS
|
||||
}
|
||||
|
||||
void nix_store_path_name(const StorePath * store_path, nix_get_string_callback callback, void * user_data)
|
||||
{
|
||||
std::string_view name = store_path->path.name();
|
||||
callback(name.data(), name.size(), user_data);
|
||||
}
|
||||
|
||||
void nix_store_path_free(StorePath * sp)
|
||||
{
|
||||
delete sp;
|
||||
}
|
||||
|
||||
StorePath * nix_store_path_clone(const StorePath * p)
|
||||
{
|
||||
return new StorePath{p->path};
|
||||
}
|
||||
|
|
|
@ -76,7 +76,7 @@ void nix_store_free(Store * store);
|
|||
* @see nix_get_string_callback
|
||||
* @return error code, NIX_OK on success.
|
||||
*/
|
||||
nix_err nix_store_get_uri(nix_c_context * context, Store * store, void * callback, void * user_data);
|
||||
nix_err nix_store_get_uri(nix_c_context * context, Store * store, nix_get_string_callback callback, void * user_data);
|
||||
|
||||
// returns: owned StorePath*
|
||||
/**
|
||||
|
@ -90,6 +90,23 @@ nix_err nix_store_get_uri(nix_c_context * context, Store * store, void * callbac
|
|||
*/
|
||||
StorePath * nix_store_parse_path(nix_c_context * context, Store * store, const char * path);
|
||||
|
||||
/**
|
||||
* @brief Get the path name (e.g. "name" in /nix/store/...-name)
|
||||
*
|
||||
* @param[in] store_path the path to get the name from
|
||||
* @param[in] callback called with the name
|
||||
* @param[in] user_data arbitrary data, passed to the callback when it's called.
|
||||
*/
|
||||
void nix_store_path_name(const StorePath * store_path, nix_get_string_callback callback, void * user_data);
|
||||
|
||||
/**
|
||||
* @brief Copy a StorePath
|
||||
*
|
||||
* @param[in] p the path to copy
|
||||
* @return a new StorePath
|
||||
*/
|
||||
StorePath * nix_store_path_clone(const StorePath * p);
|
||||
|
||||
/** @brief Deallocate a StorePath
|
||||
*
|
||||
* Does not fail.
|
||||
|
@ -111,7 +128,10 @@ bool nix_store_is_valid_path(nix_c_context * context, Store * store, StorePath *
|
|||
/**
|
||||
* @brief Realise a Nix store path
|
||||
*
|
||||
* Blocking, calls callback once for each realised output
|
||||
* Blocking, calls callback once for each realised output.
|
||||
*
|
||||
* @note When working with expressions, consider using e.g. nix_string_realise to get the output. `.drvPath` may not be
|
||||
* accurate or available in the future. See https://github.com/NixOS/nix/issues/6507
|
||||
*
|
||||
* @param[out] context Optional, stores error information
|
||||
* @param[in] store Nix Store reference
|
||||
|
@ -136,7 +156,8 @@ nix_err nix_store_realise(
|
|||
* @see nix_get_string_callback
|
||||
* @return error code, NIX_OK on success.
|
||||
*/
|
||||
nix_err nix_store_get_version(nix_c_context * context, Store * store, void * callback, void * user_data);
|
||||
nix_err
|
||||
nix_store_get_version(nix_c_context * context, Store * store, nix_get_string_callback callback, void * user_data);
|
||||
|
||||
// cffi end
|
||||
#ifdef __cplusplus
|
||||
|
|
|
@ -76,7 +76,11 @@ static void createLinks(State & state, const Path & srcDir, const Path & dstDir,
|
|||
throw Error("collision between '%1%' and non-directory '%2%'", srcFile, target);
|
||||
if (unlink(dstFile.c_str()) == -1)
|
||||
throw SysError("unlinking '%1%'", dstFile);
|
||||
if (mkdir(dstFile.c_str(), 0755) == -1)
|
||||
if (mkdir(dstFile.c_str()
|
||||
#ifndef _WIN32 // TODO abstract mkdir perms for Windows
|
||||
, 0755
|
||||
#endif
|
||||
) == -1)
|
||||
throw SysError("creating directory '%1%'", dstFile);
|
||||
createLinks(state, target, dstFile, state.priorities[dstFile]);
|
||||
createLinks(state, srcFile, dstFile, priority);
|
||||
|
|
|
@ -1,5 +1,4 @@
|
|||
#include "daemon.hh"
|
||||
#include "monitor-fd.hh"
|
||||
#include "signals.hh"
|
||||
#include "worker-protocol.hh"
|
||||
#include "worker-protocol-impl.hh"
|
||||
|
@ -16,6 +15,10 @@
|
|||
#include "args.hh"
|
||||
#include "git.hh"
|
||||
|
||||
#ifndef _WIN32 // TODO need graceful async exit support on Windows?
|
||||
# include "monitor-fd.hh"
|
||||
#endif
|
||||
|
||||
namespace nix::daemon {
|
||||
|
||||
Sink & operator << (Sink & sink, const Logger::Fields & fields)
|
||||
|
@ -1018,7 +1021,9 @@ void processConnection(
|
|||
TrustedFlag trusted,
|
||||
RecursiveFlag recursive)
|
||||
{
|
||||
#ifndef _WIN32 // TODO need graceful async exit support on Windows?
|
||||
auto monitor = !recursive ? std::make_unique<MonitorFdHup>(from.fd) : nullptr;
|
||||
#endif
|
||||
|
||||
/* Exchange the greeting. */
|
||||
unsigned int magic = readInt(from);
|
||||
|
|
|
@ -516,10 +516,12 @@ struct curlFileTransfer : public FileTransfer
|
|||
|
||||
Sync<State> state_;
|
||||
|
||||
#ifndef _WIN32 // TODO need graceful async exit support on Windows?
|
||||
/* We can't use a std::condition_variable to wake up the curl
|
||||
thread, because it only monitors file descriptors. So use a
|
||||
pipe instead. */
|
||||
Pipe wakeupPipe;
|
||||
#endif
|
||||
|
||||
std::thread workerThread;
|
||||
|
||||
|
@ -539,8 +541,10 @@ struct curlFileTransfer : public FileTransfer
|
|||
fileTransferSettings.httpConnections.get());
|
||||
#endif
|
||||
|
||||
#ifndef _WIN32 // TODO need graceful async exit support on Windows?
|
||||
wakeupPipe.create();
|
||||
fcntl(wakeupPipe.readSide.get(), F_SETFL, O_NONBLOCK);
|
||||
#endif
|
||||
|
||||
workerThread = std::thread([&]() { workerThreadEntry(); });
|
||||
}
|
||||
|
@ -561,15 +565,19 @@ struct curlFileTransfer : public FileTransfer
|
|||
auto state(state_.lock());
|
||||
state->quit = true;
|
||||
}
|
||||
#ifndef _WIN32 // TODO need graceful async exit support on Windows?
|
||||
writeFull(wakeupPipe.writeSide.get(), " ", false);
|
||||
#endif
|
||||
}
|
||||
|
||||
void workerThreadMain()
|
||||
{
|
||||
/* Cause this thread to be notified on SIGINT. */
|
||||
#ifndef _WIN32 // TODO need graceful async exit support on Windows?
|
||||
auto callback = createInterruptCallback([&]() {
|
||||
stopWorkerThread();
|
||||
});
|
||||
#endif
|
||||
|
||||
#if __linux__
|
||||
unshareFilesystem();
|
||||
|
@ -607,9 +615,11 @@ struct curlFileTransfer : public FileTransfer
|
|||
/* Wait for activity, including wakeup events. */
|
||||
int numfds = 0;
|
||||
struct curl_waitfd extraFDs[1];
|
||||
#ifndef _WIN32 // TODO need graceful async exit support on Windows?
|
||||
extraFDs[0].fd = wakeupPipe.readSide.get();
|
||||
extraFDs[0].events = CURL_WAIT_POLLIN;
|
||||
extraFDs[0].revents = 0;
|
||||
#endif
|
||||
long maxSleepTimeMs = items.empty() ? 10000 : 100;
|
||||
auto sleepTimeMs =
|
||||
nextWakeup != std::chrono::steady_clock::time_point()
|
||||
|
@ -693,7 +703,9 @@ struct curlFileTransfer : public FileTransfer
|
|||
throw nix::Error("cannot enqueue download request because the download thread is shutting down");
|
||||
state->incoming.push(item);
|
||||
}
|
||||
#ifndef _WIN32 // TODO need graceful async exit support on Windows?
|
||||
writeFull(wakeupPipe.writeSide.get(), " ");
|
||||
#endif
|
||||
}
|
||||
|
||||
#if ENABLE_S3
|
||||
|
|
|
@ -9,11 +9,14 @@
|
|||
#include <map>
|
||||
#include <mutex>
|
||||
#include <thread>
|
||||
#include <dlfcn.h>
|
||||
#include <sys/utsname.h>
|
||||
|
||||
#include <nlohmann/json.hpp>
|
||||
|
||||
#ifndef _WIN32
|
||||
# include <dlfcn.h>
|
||||
# include <sys/utsname.h>
|
||||
#endif
|
||||
|
||||
#ifdef __GLIBC__
|
||||
# include <gnu/lib-names.h>
|
||||
# include <nss.h>
|
||||
|
@ -46,7 +49,13 @@ static GlobalConfig::Register rSettings(&settings);
|
|||
|
||||
Settings::Settings()
|
||||
: nixPrefix(NIX_PREFIX)
|
||||
, nixStore(canonPath(getEnvNonEmpty("NIX_STORE_DIR").value_or(getEnvNonEmpty("NIX_STORE").value_or(NIX_STORE_DIR))))
|
||||
, nixStore(
|
||||
#ifndef _WIN32
|
||||
// On Windows `/nix/store` is not a canonical path, but we dont'
|
||||
// want to deal with that yet.
|
||||
canonPath
|
||||
#endif
|
||||
(getEnvNonEmpty("NIX_STORE_DIR").value_or(getEnvNonEmpty("NIX_STORE").value_or(NIX_STORE_DIR))))
|
||||
, nixDataDir(canonPath(getEnvNonEmpty("NIX_DATA_DIR").value_or(NIX_DATA_DIR)))
|
||||
, nixLogDir(canonPath(getEnvNonEmpty("NIX_LOG_DIR").value_or(NIX_LOG_DIR)))
|
||||
, nixStateDir(canonPath(getEnvNonEmpty("NIX_STATE_DIR").value_or(NIX_STATE_DIR)))
|
||||
|
@ -56,7 +65,9 @@ Settings::Settings()
|
|||
, nixManDir(canonPath(NIX_MAN_DIR))
|
||||
, nixDaemonSocketFile(canonPath(getEnvNonEmpty("NIX_DAEMON_SOCKET_PATH").value_or(nixStateDir + DEFAULT_SOCKET_PATH)))
|
||||
{
|
||||
#ifndef _WIN32
|
||||
buildUsersGroup = isRootUser() ? "nixbld" : "";
|
||||
#endif
|
||||
allowSymlinkedStore = getEnv("NIX_IGNORE_SYMLINK_STORE") == "1";
|
||||
|
||||
auto sslOverride = getEnv("NIX_SSL_CERT_FILE").value_or(getEnv("SSL_CERT_FILE").value_or(""));
|
||||
|
@ -239,11 +250,15 @@ StringSet Settings::getDefaultExtraPlatforms()
|
|||
|
||||
bool Settings::isWSL1()
|
||||
{
|
||||
#if __linux__
|
||||
struct utsname utsbuf;
|
||||
uname(&utsbuf);
|
||||
// WSL1 uses -Microsoft suffix
|
||||
// WSL2 uses -microsoft-standard suffix
|
||||
return hasSuffix(utsbuf.release, "-Microsoft");
|
||||
#else
|
||||
return false;
|
||||
#endif
|
||||
}
|
||||
|
||||
Path Settings::getDefaultSSLCertFile()
|
||||
|
@ -341,6 +356,7 @@ void initPlugins()
|
|||
for (const auto & file : pluginFiles) {
|
||||
/* handle is purposefully leaked as there may be state in the
|
||||
DSO needed by the action of the plugin. */
|
||||
#ifndef _WIN32 // TODO implement via DLL loading on Windows
|
||||
void *handle =
|
||||
dlopen(file.c_str(), RTLD_LAZY | RTLD_LOCAL);
|
||||
if (!handle)
|
||||
|
@ -351,6 +367,9 @@ void initPlugins()
|
|||
void (*nix_plugin_entry)() = (void (*)())dlsym(handle, "nix_plugin_entry");
|
||||
if (nix_plugin_entry)
|
||||
nix_plugin_entry();
|
||||
#else
|
||||
throw Error("could not dynamically open plugin file '%s'", file);
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -666,6 +666,7 @@ public:
|
|||
Setting<bool> sandboxFallback{this, true, "sandbox-fallback",
|
||||
"Whether to disable sandboxing when the kernel doesn't allow it."};
|
||||
|
||||
#ifndef _WIN32
|
||||
Setting<bool> requireDropSupplementaryGroups{this, isRootUser(), "require-drop-supplementary-groups",
|
||||
R"(
|
||||
Following the principle of least privilege,
|
||||
|
@ -683,6 +684,7 @@ public:
|
|||
(since `root` usually has permissions to call setgroups)
|
||||
and `false` otherwise.
|
||||
)"};
|
||||
#endif
|
||||
|
||||
#if __linux__
|
||||
Setting<std::string> sandboxShmSize{
|
||||
|
|
34
src/libstore/linux/fchmodat2-compat.hh
Normal file
34
src/libstore/linux/fchmodat2-compat.hh
Normal file
|
@ -0,0 +1,34 @@
|
|||
/*
|
||||
* Determine the syscall number for `fchmodat2`.
|
||||
*
|
||||
* On most platforms this is 452. Exceptions can be found on
|
||||
* a glibc git checkout via `rg --pcre2 'define __NR_fchmodat2 (?!452)'`.
|
||||
*
|
||||
* The problem is that glibc 2.39 and libseccomp 2.5.5 are needed to
|
||||
* get the syscall number. However, a Nix built against nixpkgs 23.11
|
||||
* (glibc 2.38) should still have the issue fixed without depending
|
||||
* on the build environment.
|
||||
*
|
||||
* To achieve that, the macros below try to determine the platform and
|
||||
* set the syscall number which is platform-specific, but
|
||||
* in most cases 452.
|
||||
*
|
||||
* TODO: remove this when 23.11 is EOL and the entire (supported) ecosystem
|
||||
* is on glibc 2.39.
|
||||
*/
|
||||
|
||||
#if HAVE_SECCOMP
|
||||
# if defined(__alpha__)
|
||||
# define NIX_SYSCALL_FCHMODAT2 562
|
||||
# elif defined(__x86_64__) && SIZE_MAX == 0xFFFFFFFF // x32
|
||||
# define NIX_SYSCALL_FCHMODAT2 1073742276
|
||||
# elif defined(__mips__) && defined(__mips64) && defined(_ABIN64) // mips64/n64
|
||||
# define NIX_SYSCALL_FCHMODAT2 5452
|
||||
# elif defined(__mips__) && defined(__mips64) && defined(_ABIN32) // mips64/n32
|
||||
# define NIX_SYSCALL_FCHMODAT2 6452
|
||||
# elif defined(__mips__) && defined(_ABIO32) // mips32
|
||||
# define NIX_SYSCALL_FCHMODAT2 4452
|
||||
# else
|
||||
# define NIX_SYSCALL_FCHMODAT2 452
|
||||
# endif
|
||||
#endif // HAVE_SECCOMP
|
|
@ -1,18 +1,15 @@
|
|||
#include "personality.hh"
|
||||
#include "globals.hh"
|
||||
|
||||
#if __linux__
|
||||
#include <sys/utsname.h>
|
||||
#include <sys/personality.h>
|
||||
#endif
|
||||
|
||||
#include <cstring>
|
||||
|
||||
namespace nix {
|
||||
namespace nix::linux {
|
||||
|
||||
void setPersonality(std::string_view system)
|
||||
{
|
||||
#if __linux__
|
||||
/* Change the personality to 32-bit if we're doing an
|
||||
i686-linux build on an x86_64-linux machine. */
|
||||
struct utsname utsbuf;
|
||||
|
@ -39,7 +36,6 @@ void setPersonality(std::string_view system)
|
|||
determinism. */
|
||||
int cur = personality(0xffffffff);
|
||||
if (cur != -1) personality(cur | ADDR_NO_RANDOMIZE);
|
||||
#endif
|
||||
}
|
||||
|
||||
}
|
|
@ -3,7 +3,7 @@
|
|||
|
||||
#include <string>
|
||||
|
||||
namespace nix {
|
||||
namespace nix::linux {
|
||||
|
||||
void setPersonality(std::string_view system);
|
||||
|
|
@ -33,6 +33,10 @@ struct LocalStoreAccessor : PosixSourceAccessor
|
|||
|
||||
std::optional<Stat> maybeLstat(const CanonPath & path) override
|
||||
{
|
||||
/* Handle the case where `path` is (a parent of) the store. */
|
||||
if (isDirOrInDir(store->storeDir, path.abs()))
|
||||
return Stat{ .type = tDirectory };
|
||||
|
||||
return PosixSourceAccessor::maybeLstat(toRealPath(path));
|
||||
}
|
||||
|
||||
|
|
|
@ -4,9 +4,15 @@ libstore_NAME = libnixstore
|
|||
|
||||
libstore_DIR := $(d)
|
||||
|
||||
libstore_SOURCES := $(wildcard $(d)/*.cc $(d)/builtins/*.cc $(d)/build/*.cc)
|
||||
libstore_SOURCES := $(wildcard $(d)/*.cc $(d)/builtins/*.cc)
|
||||
ifdef HOST_UNIX
|
||||
libstore_SOURCES += $(wildcard $(d)/unix/*.cc)
|
||||
libstore_SOURCES += $(wildcard $(d)/unix/*.cc $(d)/unix/builtins/*.cc $(d)/unix/build/*.cc)
|
||||
endif
|
||||
ifdef HOST_LINUX
|
||||
libstore_SOURCES += $(wildcard $(d)/linux/*.cc)
|
||||
endif
|
||||
ifdef HOST_WINDOWS
|
||||
libstore_SOURCES += $(wildcard $(d)/windows/*.cc)
|
||||
endif
|
||||
|
||||
libstore_LIBS = libutil
|
||||
|
@ -36,25 +42,42 @@ INCLUDE_libstore := -I $(d) -I $(d)/build
|
|||
ifdef HOST_UNIX
|
||||
INCLUDE_libstore += -I $(d)/unix
|
||||
endif
|
||||
ifdef HOST_LINUX
|
||||
INCLUDE_libstore += -I $(d)/linux
|
||||
endif
|
||||
ifdef HOST_WINDOWS
|
||||
INCLUDE_libstore += -I $(d)/windows
|
||||
endif
|
||||
|
||||
ifdef HOST_WINDOWS
|
||||
NIX_ROOT = N:\\\\
|
||||
else
|
||||
NIX_ROOT =
|
||||
endif
|
||||
|
||||
# Prefix all but `NIX_STORE_DIR`, since we aren't doing a local store
|
||||
# yet so a "logical" store dir that is the same as unix is prefered.
|
||||
#
|
||||
# Also, it keeps the unit tests working.
|
||||
|
||||
libstore_CXXFLAGS += \
|
||||
$(INCLUDE_libutil) $(INCLUDE_libstore) $(INCLUDE_libstore) \
|
||||
-DNIX_PREFIX=\"$(prefix)\" \
|
||||
-DNIX_PREFIX=\"$(NIX_ROOT)$(prefix)\" \
|
||||
-DNIX_STORE_DIR=\"$(storedir)\" \
|
||||
-DNIX_DATA_DIR=\"$(datadir)\" \
|
||||
-DNIX_STATE_DIR=\"$(localstatedir)/nix\" \
|
||||
-DNIX_LOG_DIR=\"$(localstatedir)/log/nix\" \
|
||||
-DNIX_CONF_DIR=\"$(sysconfdir)/nix\" \
|
||||
-DNIX_BIN_DIR=\"$(bindir)\" \
|
||||
-DNIX_MAN_DIR=\"$(mandir)\" \
|
||||
-DLSOF=\"$(lsof)\"
|
||||
-DNIX_DATA_DIR=\"$(NIX_ROOT)$(datadir)\" \
|
||||
-DNIX_STATE_DIR=\"$(NIX_ROOT)$(localstatedir)/nix\" \
|
||||
-DNIX_LOG_DIR=\"$(NIX_ROOT)$(localstatedir)/log/nix\" \
|
||||
-DNIX_CONF_DIR=\"$(NIX_ROOT)$(sysconfdir)/nix\" \
|
||||
-DNIX_BIN_DIR=\"$(NIX_ROOT)$(bindir)\" \
|
||||
-DNIX_MAN_DIR=\"$(NIX_ROOT)$(mandir)\" \
|
||||
-DLSOF=\"$(NIX_ROOT)$(lsof)\"
|
||||
|
||||
ifeq ($(embedded_sandbox_shell),yes)
|
||||
libstore_CXXFLAGS += -DSANDBOX_SHELL=\"__embedded_sandbox_shell__\"
|
||||
|
||||
$(d)/build/local-derivation-goal.cc: $(d)/embedded-sandbox-shell.gen.hh
|
||||
$(d)/unix/build/local-derivation-goal.cc: $(d)/unix/embedded-sandbox-shell.gen.hh
|
||||
|
||||
$(d)/embedded-sandbox-shell.gen.hh: $(sandbox_shell)
|
||||
$(d)/unix/embedded-sandbox-shell.gen.hh: $(sandbox_shell)
|
||||
$(trace-gen) hexdump -v -e '1/1 "0x%x," "\n"' < $< > $@.tmp
|
||||
@mv $@.tmp $@
|
||||
else
|
||||
|
@ -63,11 +86,11 @@ else
|
|||
endif
|
||||
endif
|
||||
|
||||
$(d)/local-store.cc: $(d)/schema.sql.gen.hh $(d)/ca-specific-schema.sql.gen.hh
|
||||
$(d)/unix/local-store.cc: $(d)/unix/schema.sql.gen.hh $(d)/unix/ca-specific-schema.sql.gen.hh
|
||||
|
||||
$(d)/build.cc:
|
||||
$(d)/unix/build.cc:
|
||||
|
||||
clean-files += $(d)/schema.sql.gen.hh $(d)/ca-specific-schema.sql.gen.hh
|
||||
clean-files += $(d)/unix/schema.sql.gen.hh $(d)/unix/ca-specific-schema.sql.gen.hh
|
||||
|
||||
$(eval $(call install-file-in, $(buildprefix)$(d)/nix-store.pc, $(libdir)/pkgconfig, 0644))
|
||||
|
||||
|
|
|
@ -1,7 +1,6 @@
|
|||
#include "derivations.hh"
|
||||
#include "parsed-derivations.hh"
|
||||
#include "globals.hh"
|
||||
#include "local-store.hh"
|
||||
#include "store-api.hh"
|
||||
#include "thread-pool.hh"
|
||||
#include "realisation.hh"
|
||||
|
|
|
@ -186,7 +186,7 @@ std::optional<nlohmann::json> ParsedDerivation::prepareStructuredAttrs(Store & s
|
|||
for (auto i = e->begin(); i != e->end(); ++i) {
|
||||
StorePathSet storePaths;
|
||||
for (auto & p : *i)
|
||||
storePaths.insert(store.parseStorePath(p.get<std::string>()));
|
||||
storePaths.insert(store.toStorePath(p.get<std::string>()).first);
|
||||
json[i.key()] = pathInfoToJSON(store,
|
||||
store.exportReferences(storePaths, inputPaths));
|
||||
}
|
||||
|
|
|
@ -63,7 +63,18 @@ StorePath StorePath::random(std::string_view name)
|
|||
|
||||
StorePath StoreDirConfig::parseStorePath(std::string_view path) const
|
||||
{
|
||||
auto p = canonPath(std::string(path));
|
||||
// On Windows, `/nix/store` is not a canonical path. More broadly it
|
||||
// is unclear whether this function should be using the native
|
||||
// notion of a canonical path at all. For example, it makes to
|
||||
// support remote stores whose store dir is a non-native path (e.g.
|
||||
// Windows <-> Unix ssh-ing).
|
||||
auto p =
|
||||
#ifdef _WIN32
|
||||
path
|
||||
#else
|
||||
canonPath(std::string(path))
|
||||
#endif
|
||||
;
|
||||
if (dirOf(p) != storeDir)
|
||||
throw BadStorePath("path '%s' is not in the Nix store", p);
|
||||
return StorePath(baseNameOf(p));
|
||||
|
|
|
@ -6,69 +6,9 @@
|
|||
#include <cerrno>
|
||||
#include <cstdlib>
|
||||
|
||||
#include <fcntl.h>
|
||||
#include <sys/types.h>
|
||||
#include <sys/stat.h>
|
||||
#include <sys/file.h>
|
||||
|
||||
|
||||
namespace nix {
|
||||
|
||||
|
||||
AutoCloseFD openLockFile(const Path & path, bool create)
|
||||
{
|
||||
AutoCloseFD fd;
|
||||
|
||||
fd = open(path.c_str(), O_CLOEXEC | O_RDWR | (create ? O_CREAT : 0), 0600);
|
||||
if (!fd && (create || errno != ENOENT))
|
||||
throw SysError("opening lock file '%1%'", path);
|
||||
|
||||
return fd;
|
||||
}
|
||||
|
||||
|
||||
void deleteLockFile(const Path & path, int fd)
|
||||
{
|
||||
/* Get rid of the lock file. Have to be careful not to introduce
|
||||
races. Write a (meaningless) token to the file to indicate to
|
||||
other processes waiting on this lock that the lock is stale
|
||||
(deleted). */
|
||||
unlink(path.c_str());
|
||||
writeFull(fd, "d");
|
||||
/* Note that the result of unlink() is ignored; removing the lock
|
||||
file is an optimisation, not a necessity. */
|
||||
}
|
||||
|
||||
|
||||
bool lockFile(int fd, LockType lockType, bool wait)
|
||||
{
|
||||
int type;
|
||||
if (lockType == ltRead) type = LOCK_SH;
|
||||
else if (lockType == ltWrite) type = LOCK_EX;
|
||||
else if (lockType == ltNone) type = LOCK_UN;
|
||||
else abort();
|
||||
|
||||
if (wait) {
|
||||
while (flock(fd, type) != 0) {
|
||||
checkInterrupt();
|
||||
if (errno != EINTR)
|
||||
throw SysError("acquiring/releasing lock");
|
||||
else
|
||||
return false;
|
||||
}
|
||||
} else {
|
||||
while (flock(fd, type | LOCK_NB) != 0) {
|
||||
checkInterrupt();
|
||||
if (errno == EWOULDBLOCK) return false;
|
||||
if (errno != EINTR)
|
||||
throw SysError("acquiring/releasing lock");
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
PathLocks::PathLocks()
|
||||
: deletePaths(false)
|
||||
{
|
||||
|
@ -82,68 +22,6 @@ PathLocks::PathLocks(const PathSet & paths, const std::string & waitMsg)
|
|||
}
|
||||
|
||||
|
||||
bool PathLocks::lockPaths(const PathSet & paths,
|
||||
const std::string & waitMsg, bool wait)
|
||||
{
|
||||
assert(fds.empty());
|
||||
|
||||
/* Note that `fds' is built incrementally so that the destructor
|
||||
will only release those locks that we have already acquired. */
|
||||
|
||||
/* Acquire the lock for each path in sorted order. This ensures
|
||||
that locks are always acquired in the same order, thus
|
||||
preventing deadlocks. */
|
||||
for (auto & path : paths) {
|
||||
checkInterrupt();
|
||||
Path lockPath = path + ".lock";
|
||||
|
||||
debug("locking path '%1%'", path);
|
||||
|
||||
AutoCloseFD fd;
|
||||
|
||||
while (1) {
|
||||
|
||||
/* Open/create the lock file. */
|
||||
fd = openLockFile(lockPath, true);
|
||||
|
||||
/* Acquire an exclusive lock. */
|
||||
if (!lockFile(fd.get(), ltWrite, false)) {
|
||||
if (wait) {
|
||||
if (waitMsg != "") printError(waitMsg);
|
||||
lockFile(fd.get(), ltWrite, true);
|
||||
} else {
|
||||
/* Failed to lock this path; release all other
|
||||
locks. */
|
||||
unlock();
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
debug("lock acquired on '%1%'", lockPath);
|
||||
|
||||
/* Check that the lock file hasn't become stale (i.e.,
|
||||
hasn't been unlinked). */
|
||||
struct stat st;
|
||||
if (fstat(fd.get(), &st) == -1)
|
||||
throw SysError("statting lock file '%1%'", lockPath);
|
||||
if (st.st_size != 0)
|
||||
/* This lock file has been unlinked, so we're holding
|
||||
a lock on a deleted file. This means that other
|
||||
processes may create and acquire a lock on
|
||||
`lockPath', and proceed. So we must retry. */
|
||||
debug("open lock file '%1%' has become stale", lockPath);
|
||||
else
|
||||
break;
|
||||
}
|
||||
|
||||
/* Use borrow so that the descriptor isn't closed. */
|
||||
fds.push_back(FDPair(fd.release(), lockPath));
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
PathLocks::~PathLocks()
|
||||
{
|
||||
try {
|
||||
|
@ -154,40 +32,10 @@ PathLocks::~PathLocks()
|
|||
}
|
||||
|
||||
|
||||
void PathLocks::unlock()
|
||||
{
|
||||
for (auto & i : fds) {
|
||||
if (deletePaths) deleteLockFile(i.second, i.first);
|
||||
|
||||
if (close(i.first) == -1)
|
||||
printError(
|
||||
"error (ignored): cannot close lock file on '%1%'",
|
||||
i.second);
|
||||
|
||||
debug("lock released on '%1%'", i.second);
|
||||
}
|
||||
|
||||
fds.clear();
|
||||
}
|
||||
|
||||
|
||||
void PathLocks::setDeletion(bool deletePaths)
|
||||
{
|
||||
this->deletePaths = deletePaths;
|
||||
}
|
||||
|
||||
|
||||
FdLock::FdLock(int fd, LockType lockType, bool wait, std::string_view waitMsg)
|
||||
: fd(fd)
|
||||
{
|
||||
if (wait) {
|
||||
if (!lockFile(fd, lockType, false)) {
|
||||
printInfo("%s", waitMsg);
|
||||
acquired = lockFile(fd, lockType, true);
|
||||
}
|
||||
} else
|
||||
acquired = lockFile(fd, lockType, false);
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
|
|
@ -5,22 +5,6 @@
|
|||
|
||||
namespace nix {
|
||||
|
||||
/**
|
||||
* Open (possibly create) a lock file and return the file descriptor.
|
||||
* -1 is returned if create is false and the lock could not be opened
|
||||
* because it doesn't exist. Any other error throws an exception.
|
||||
*/
|
||||
AutoCloseFD openLockFile(const Path & path, bool create);
|
||||
|
||||
/**
|
||||
* Delete an open lock file.
|
||||
*/
|
||||
void deleteLockFile(const Path & path, int fd);
|
||||
|
||||
enum LockType { ltRead, ltWrite, ltNone };
|
||||
|
||||
bool lockFile(int fd, LockType lockType, bool wait);
|
||||
|
||||
class PathLocks
|
||||
{
|
||||
private:
|
||||
|
@ -40,18 +24,6 @@ public:
|
|||
void setDeletion(bool deletePaths);
|
||||
};
|
||||
|
||||
struct FdLock
|
||||
{
|
||||
int fd;
|
||||
bool acquired = false;
|
||||
|
||||
FdLock(int fd, LockType lockType, bool wait, std::string_view waitMsg);
|
||||
|
||||
~FdLock()
|
||||
{
|
||||
if (acquired)
|
||||
lockFile(fd, ltNone, false);
|
||||
}
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
#include "pathlocks-impl.hh"
|
||||
|
|
|
@ -8,6 +8,7 @@
|
|||
#include "types.hh"
|
||||
#include "pathlocks.hh"
|
||||
|
||||
#include <optional>
|
||||
#include <time.h>
|
||||
|
||||
|
||||
|
|
|
@ -71,11 +71,15 @@ std::pair<ref<SourceAccessor>, CanonPath> RemoteFSAccessor::fetch(const CanonPat
|
|||
auto narAccessor = makeLazyNarAccessor(listing,
|
||||
[cacheFile](uint64_t offset, uint64_t length) {
|
||||
|
||||
AutoCloseFD fd = open(cacheFile.c_str(), O_RDONLY | O_CLOEXEC);
|
||||
AutoCloseFD fd = toDescriptor(open(cacheFile.c_str(), O_RDONLY
|
||||
#ifndef _WIN32
|
||||
| O_CLOEXEC
|
||||
#endif
|
||||
));
|
||||
if (!fd)
|
||||
throw SysError("opening NAR cache file '%s'", cacheFile);
|
||||
|
||||
if (lseek(fd.get(), offset, SEEK_SET) != (off_t) offset)
|
||||
if (lseek(fromDescriptorReadOnly(fd.get()), offset, SEEK_SET) != (off_t) offset)
|
||||
throw SysError("seeking in '%s'", cacheFile);
|
||||
|
||||
std::string buf(length, 0);
|
||||
|
|
Some files were not shown because too many files have changed in this diff Show more
Loading…
Reference in a new issue