forked from lix-project/lix
Merge branch 'master' into debug-exploratory-PR
This commit is contained in:
commit
a47de1ac37
7
.github/PULL_REQUEST_TEMPLATE/pull_request_template.md
vendored
Normal file
7
.github/PULL_REQUEST_TEMPLATE/pull_request_template.md
vendored
Normal file
|
@ -0,0 +1,7 @@
|
||||||
|
**Release Notes**
|
||||||
|
Please include relevant [release notes](https://github.com/NixOS/nix/blob/master/doc/manual/src/release-notes/rl-next.md) as needed.
|
||||||
|
|
||||||
|
|
||||||
|
**Testing**
|
||||||
|
|
||||||
|
If this issue is a regression or something that should block release, please consider including a test either in the [testsuite](https://github.com/NixOS/nix/tree/master/tests) or as a [hydraJob]( https://github.com/NixOS/nix/blob/master/flake.nix#L396) so that it can be part of the [automatic checks](https://hydra.nixos.org/jobset/nix/master).
|
16
.github/workflows/hydra_status.yml
vendored
Normal file
16
.github/workflows/hydra_status.yml
vendored
Normal file
|
@ -0,0 +1,16 @@
|
||||||
|
name: Hydra status
|
||||||
|
on:
|
||||||
|
schedule:
|
||||||
|
- cron: "12,42 * * * *"
|
||||||
|
workflow_dispatch:
|
||||||
|
jobs:
|
||||||
|
check_hydra_status:
|
||||||
|
name: Check Hydra status
|
||||||
|
if: github.repository_owner == 'NixOS'
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
steps:
|
||||||
|
- uses: actions/checkout@v2.4.0
|
||||||
|
with:
|
||||||
|
fetch-depth: 0
|
||||||
|
- run: bash scripts/check-hydra-status.sh
|
||||||
|
|
13
configure.ac
13
configure.ac
|
@ -188,17 +188,24 @@ PKG_CHECK_MODULES([EDITLINE], [libeditline], [CXXFLAGS="$EDITLINE_CFLAGS $CXXFLA
|
||||||
[AC_MSG_ERROR([Nix requires libeditline; it was not found via pkg-config, but via its header, but required functions do not work. Maybe it is too old? >= 1.14 is required.])])
|
[AC_MSG_ERROR([Nix requires libeditline; it was not found via pkg-config, but via its header, but required functions do not work. Maybe it is too old? >= 1.14 is required.])])
|
||||||
])
|
])
|
||||||
|
|
||||||
# Look for libsodium, an optional dependency.
|
# Look for libsodium.
|
||||||
PKG_CHECK_MODULES([SODIUM], [libsodium], [CXXFLAGS="$SODIUM_CFLAGS $CXXFLAGS"])
|
PKG_CHECK_MODULES([SODIUM], [libsodium], [CXXFLAGS="$SODIUM_CFLAGS $CXXFLAGS"])
|
||||||
|
|
||||||
# Look for libbrotli{enc,dec}.
|
# Look for libbrotli{enc,dec}.
|
||||||
PKG_CHECK_MODULES([LIBBROTLI], [libbrotlienc libbrotlidec], [CXXFLAGS="$LIBBROTLI_CFLAGS $CXXFLAGS"])
|
PKG_CHECK_MODULES([LIBBROTLI], [libbrotlienc libbrotlidec], [CXXFLAGS="$LIBBROTLI_CFLAGS $CXXFLAGS"])
|
||||||
|
|
||||||
# Look for libcpuid.
|
# Look for libcpuid.
|
||||||
|
have_libcpuid=
|
||||||
if test "$machine_name" = "x86_64"; then
|
if test "$machine_name" = "x86_64"; then
|
||||||
PKG_CHECK_MODULES([LIBCPUID], [libcpuid], [CXXFLAGS="$LIBCPUID_CFLAGS $CXXFLAGS"])
|
AC_ARG_ENABLE([cpuid],
|
||||||
|
AS_HELP_STRING([--disable-cpuid], [Do not determine microarchitecture levels with libcpuid (relevant to x86_64 only)]))
|
||||||
|
if test "x$enable_cpuid" != "xno"; then
|
||||||
|
PKG_CHECK_MODULES([LIBCPUID], [libcpuid],
|
||||||
|
[CXXFLAGS="$LIBCPUID_CFLAGS $CXXFLAGS"
|
||||||
have_libcpuid=1
|
have_libcpuid=1
|
||||||
AC_DEFINE([HAVE_LIBCPUID], [1], [Use libcpuid])
|
AC_DEFINE([HAVE_LIBCPUID], [1], [Use libcpuid])]
|
||||||
|
)
|
||||||
|
fi
|
||||||
fi
|
fi
|
||||||
AC_SUBST(HAVE_LIBCPUID, [$have_libcpuid])
|
AC_SUBST(HAVE_LIBCPUID, [$have_libcpuid])
|
||||||
|
|
||||||
|
|
|
@ -8,7 +8,8 @@ concatStrings (map
|
||||||
let option = options.${name}; in
|
let option = options.${name}; in
|
||||||
" - `${name}` \n\n"
|
" - `${name}` \n\n"
|
||||||
+ concatStrings (map (s: " ${s}\n") (splitLines option.description)) + "\n\n"
|
+ concatStrings (map (s: " ${s}\n") (splitLines option.description)) + "\n\n"
|
||||||
+ " **Default:** " + (
|
+ (if option.documentDefault
|
||||||
|
then " **Default:** " + (
|
||||||
if option.value == "" || option.value == []
|
if option.value == "" || option.value == []
|
||||||
then "*empty*"
|
then "*empty*"
|
||||||
else if isBool option.value
|
else if isBool option.value
|
||||||
|
@ -19,6 +20,7 @@ concatStrings (map
|
||||||
# JSON, but that converts to "{ }" here.
|
# JSON, but that converts to "{ }" here.
|
||||||
(if isAttrs option.value then "`\"\"`"
|
(if isAttrs option.value then "`\"\"`"
|
||||||
else "`" + toString option.value + "`")) + "\n\n"
|
else "`" + toString option.value + "`")) + "\n\n"
|
||||||
|
else " **Default:** *machine-specific*")
|
||||||
+ (if option.aliases != []
|
+ (if option.aliases != []
|
||||||
then " **Deprecated alias:** " + (concatStringsSep ", " (map (s: "`${s}`") option.aliases)) + "\n\n"
|
then " **Deprecated alias:** " + (concatStringsSep ", " (map (s: "`${s}`") option.aliases)) + "\n\n"
|
||||||
else "")
|
else "")
|
||||||
|
|
|
@ -12,11 +12,13 @@ man-pages := $(foreach n, \
|
||||||
clean-files += $(d)/*.1 $(d)/*.5 $(d)/*.8
|
clean-files += $(d)/*.1 $(d)/*.5 $(d)/*.8
|
||||||
|
|
||||||
# Provide a dummy environment for nix, so that it will not access files outside the macOS sandbox.
|
# Provide a dummy environment for nix, so that it will not access files outside the macOS sandbox.
|
||||||
|
# Set cores to 0 because otherwise nix show-config resolves the cores based on the current machine
|
||||||
dummy-env = env -i \
|
dummy-env = env -i \
|
||||||
HOME=/dummy \
|
HOME=/dummy \
|
||||||
NIX_CONF_DIR=/dummy \
|
NIX_CONF_DIR=/dummy \
|
||||||
NIX_SSL_CERT_FILE=/dummy/no-ca-bundle.crt \
|
NIX_SSL_CERT_FILE=/dummy/no-ca-bundle.crt \
|
||||||
NIX_STATE_DIR=/dummy
|
NIX_STATE_DIR=/dummy \
|
||||||
|
NIX_CONFIG='cores = 0'
|
||||||
|
|
||||||
nix-eval = $(dummy-env) $(bindir)/nix eval --experimental-features nix-command -I nix/corepkgs=corepkgs --store dummy:// --impure --raw
|
nix-eval = $(dummy-env) $(bindir)/nix eval --experimental-features nix-command -I nix/corepkgs=corepkgs --store dummy:// --impure --raw
|
||||||
|
|
||||||
|
|
|
@ -72,6 +72,7 @@
|
||||||
- [CLI guideline](contributing/cli-guideline.md)
|
- [CLI guideline](contributing/cli-guideline.md)
|
||||||
- [Release Notes](release-notes/release-notes.md)
|
- [Release Notes](release-notes/release-notes.md)
|
||||||
- [Release X.Y (202?-??-??)](release-notes/rl-next.md)
|
- [Release X.Y (202?-??-??)](release-notes/rl-next.md)
|
||||||
|
- [Release 2.5 (2021-12-13)](release-notes/rl-2.5.md)
|
||||||
- [Release 2.4 (2021-11-01)](release-notes/rl-2.4.md)
|
- [Release 2.4 (2021-11-01)](release-notes/rl-2.4.md)
|
||||||
- [Release 2.3 (2019-09-04)](release-notes/rl-2.3.md)
|
- [Release 2.3 (2019-09-04)](release-notes/rl-2.3.md)
|
||||||
- [Release 2.2 (2019-01-11)](release-notes/rl-2.2.md)
|
- [Release 2.2 (2019-01-11)](release-notes/rl-2.2.md)
|
||||||
|
|
|
@ -53,8 +53,8 @@ example, the following command allows you to build a derivation for
|
||||||
$ uname
|
$ uname
|
||||||
Linux
|
Linux
|
||||||
|
|
||||||
$ nix build \
|
$ nix build --impure \
|
||||||
'(with import <nixpkgs> { system = "x86_64-darwin"; }; runCommand "foo" {} "uname > $out")' \
|
--expr '(with import <nixpkgs> { system = "x86_64-darwin"; }; runCommand "foo" {} "uname > $out")' \
|
||||||
--builders 'ssh://mac x86_64-darwin'
|
--builders 'ssh://mac x86_64-darwin'
|
||||||
[1/0/1 built, 0.0 MiB DL] building foo on ssh://mac
|
[1/0/1 built, 0.0 MiB DL] building foo on ssh://mac
|
||||||
|
|
||||||
|
|
|
@ -35,6 +35,25 @@ variables are set up so that those dependencies can be found:
|
||||||
$ nix-shell
|
$ nix-shell
|
||||||
```
|
```
|
||||||
|
|
||||||
|
or if you have a flake-enabled nix:
|
||||||
|
|
||||||
|
```console
|
||||||
|
$ nix develop
|
||||||
|
```
|
||||||
|
|
||||||
|
To get a shell with a different compilation environment (e.g. stdenv,
|
||||||
|
gccStdenv, clangStdenv, clang11Stdenv):
|
||||||
|
|
||||||
|
```console
|
||||||
|
$ nix-shell -A devShells.x86_64-linux.clang11StdenvPackages
|
||||||
|
```
|
||||||
|
|
||||||
|
or if you have a flake-enabled nix:
|
||||||
|
|
||||||
|
```console
|
||||||
|
$ nix develop .#clang11StdenvPackages
|
||||||
|
```
|
||||||
|
|
||||||
To build Nix itself in this shell:
|
To build Nix itself in this shell:
|
||||||
|
|
||||||
```console
|
```console
|
||||||
|
|
|
@ -98,3 +98,7 @@
|
||||||
store. It can contain regular files, directories and symbolic
|
store. It can contain regular files, directories and symbolic
|
||||||
links. NARs are generated and unpacked using `nix-store --dump`
|
links. NARs are generated and unpacked using `nix-store --dump`
|
||||||
and `nix-store --restore`.
|
and `nix-store --restore`.
|
||||||
|
- `∅` \
|
||||||
|
The empty set symbol. In the context of profile history, this denotes a package is not present in a particular version of the profile.
|
||||||
|
- `ε` \
|
||||||
|
The epsilon symbol. In the context of a package, this means the version is empty. More precisely, the derivation does not have a version attribute.
|
||||||
|
|
|
@ -119,6 +119,30 @@ this to run the installer, but it may help if you run into trouble:
|
||||||
- update `/etc/synthetic.conf` to direct macOS to create a "synthetic"
|
- update `/etc/synthetic.conf` to direct macOS to create a "synthetic"
|
||||||
empty root directory to mount your volume
|
empty root directory to mount your volume
|
||||||
- specify mount options for the volume in `/etc/fstab`
|
- specify mount options for the volume in `/etc/fstab`
|
||||||
|
- `rw`: read-write
|
||||||
|
- `noauto`: prevent the system from auto-mounting the volume (so the
|
||||||
|
LaunchDaemon mentioned below can control mounting it, and to avoid
|
||||||
|
masking problems with that mounting service).
|
||||||
|
- `nobrowse`: prevent the Nix Store volume from showing up on your
|
||||||
|
desktop; also keeps Spotlight from spending resources to index
|
||||||
|
this volume
|
||||||
|
<!-- TODO:
|
||||||
|
- `suid`: honor setuid? surely not? ...
|
||||||
|
- `owners`: honor file ownership on the volume
|
||||||
|
|
||||||
|
For now I'll avoid pretending to understand suid/owners more
|
||||||
|
than I do. There've been some vague reports of file-ownership
|
||||||
|
and permission issues, particularly in cloud/VM/headless setups.
|
||||||
|
My pet theory is that this has something to do with these setups
|
||||||
|
not having a token that gets delegated to initial/admin accounts
|
||||||
|
on macOS. See scripts/create-darwin-volume.sh for a little more.
|
||||||
|
|
||||||
|
In any case, by Dec 4 2021, it _seems_ like some combination of
|
||||||
|
suid, owners, and calling diskutil enableOwnership have stopped
|
||||||
|
new reports from coming in. But I hesitate to celebrate because we
|
||||||
|
haven't really named and catalogued the behavior, understood what
|
||||||
|
we're fixing, and validated that all 3 components are essential.
|
||||||
|
-->
|
||||||
- if you have FileVault enabled
|
- if you have FileVault enabled
|
||||||
- generate an encryption password
|
- generate an encryption password
|
||||||
- put it in your system Keychain
|
- put it in your system Keychain
|
||||||
|
|
|
@ -3,7 +3,7 @@
|
||||||
To run the latest stable release of Nix with Docker run the following command:
|
To run the latest stable release of Nix with Docker run the following command:
|
||||||
|
|
||||||
```console
|
```console
|
||||||
$ docker -ti run nixos/nix
|
$ docker run -ti nixos/nix
|
||||||
Unable to find image 'nixos/nix:latest' locally
|
Unable to find image 'nixos/nix:latest' locally
|
||||||
latest: Pulling from nixos/nix
|
latest: Pulling from nixos/nix
|
||||||
5843afab3874: Pull complete
|
5843afab3874: Pull complete
|
||||||
|
@ -16,7 +16,7 @@ nix (Nix) 2.3.12
|
||||||
35ca4ada6e96:/# exit
|
35ca4ada6e96:/# exit
|
||||||
```
|
```
|
||||||
|
|
||||||
# What is included in Nix' Docker image?
|
# What is included in Nix's Docker image?
|
||||||
|
|
||||||
The official Docker image is created using `pkgs.dockerTools.buildLayeredImage`
|
The official Docker image is created using `pkgs.dockerTools.buildLayeredImage`
|
||||||
(and not with `Dockerfile` as it is usual with Docker images). You can still
|
(and not with `Dockerfile` as it is usual with Docker images). You can still
|
||||||
|
@ -54,6 +54,6 @@ You can also build a Docker image from source yourself:
|
||||||
|
|
||||||
```console
|
```console
|
||||||
$ nix build ./\#hydraJobs.dockerImage.x86_64-linux
|
$ nix build ./\#hydraJobs.dockerImage.x86_64-linux
|
||||||
$ docker load -i ./result
|
$ docker load -i ./result/image.tar.gz
|
||||||
$ docker run -ti nix:2.5pre20211105
|
$ docker run -ti nix:2.5pre20211105
|
||||||
```
|
```
|
||||||
|
|
|
@ -44,6 +44,11 @@
|
||||||
obtained from the its repository
|
obtained from the its repository
|
||||||
<https://github.com/troglobit/editline>.
|
<https://github.com/troglobit/editline>.
|
||||||
|
|
||||||
|
- The `libsodium` library for verifying cryptographic signatures
|
||||||
|
of contents fetched from binary caches.
|
||||||
|
It can be obtained from the official web site
|
||||||
|
<https://libsodium.org>.
|
||||||
|
|
||||||
- Recent versions of Bison and Flex to build the parser. (This is
|
- Recent versions of Bison and Flex to build the parser. (This is
|
||||||
because Nix needs GLR support in Bison and reentrancy support in
|
because Nix needs GLR support in Bison and reentrancy support in
|
||||||
Flex.) For Bison, you need version 2.6, which can be obtained from
|
Flex.) For Bison, you need version 2.6, which can be obtained from
|
||||||
|
@ -58,3 +63,11 @@
|
||||||
`--disable-seccomp-sandboxing` option to the `configure` script (Not
|
`--disable-seccomp-sandboxing` option to the `configure` script (Not
|
||||||
recommended unless your system doesn't support `libseccomp`). To get
|
recommended unless your system doesn't support `libseccomp`). To get
|
||||||
the library, visit <https://github.com/seccomp/libseccomp>.
|
the library, visit <https://github.com/seccomp/libseccomp>.
|
||||||
|
|
||||||
|
- On 64-bit x86 machines only, `libcpuid` library
|
||||||
|
is used to determine which microarchitecture levels are supported
|
||||||
|
(e.g., as whether to have `x86_64-v2-linux` among additional system types).
|
||||||
|
The library is available from its homepage
|
||||||
|
<http://libcpuid.sourceforge.net>.
|
||||||
|
This is an optional dependency and can be disabled
|
||||||
|
by providing a `--disable-cpuid` to the `configure` script.
|
||||||
|
|
|
@ -4,4 +4,4 @@ Nix is currently supported on the following platforms:
|
||||||
|
|
||||||
- Linux (i686, x86\_64, aarch64).
|
- Linux (i686, x86\_64, aarch64).
|
||||||
|
|
||||||
- macOS (x86\_64).
|
- macOS (x86\_64, aarch64).
|
||||||
|
|
|
@ -40,7 +40,7 @@ $ nix-channel --update
|
||||||
>
|
>
|
||||||
> On NixOS, you’re automatically subscribed to a NixOS channel
|
> On NixOS, you’re automatically subscribed to a NixOS channel
|
||||||
> corresponding to your NixOS major release (e.g.
|
> corresponding to your NixOS major release (e.g.
|
||||||
> <http://nixos.org/channels/nixos-14.12>). A NixOS channel is identical
|
> <http://nixos.org/channels/nixos-21.11>). A NixOS channel is identical
|
||||||
> to the Nixpkgs channel, except that it contains only Linux binaries
|
> to the Nixpkgs channel, except that it contains only Linux binaries
|
||||||
> and is updated only if a set of regression tests succeed.
|
> and is updated only if a set of regression tests succeed.
|
||||||
|
|
||||||
|
|
|
@ -395,6 +395,7 @@ dramforever,
|
||||||
Dustin DeWeese,
|
Dustin DeWeese,
|
||||||
edef,
|
edef,
|
||||||
Eelco Dolstra,
|
Eelco Dolstra,
|
||||||
|
Ellie Hermaszewska,
|
||||||
Emilio Karakey,
|
Emilio Karakey,
|
||||||
Emily,
|
Emily,
|
||||||
Eric Culp,
|
Eric Culp,
|
||||||
|
@ -405,7 +406,7 @@ Federico Pellegrin,
|
||||||
Finn Behrens,
|
Finn Behrens,
|
||||||
Florian Franzen,
|
Florian Franzen,
|
||||||
Félix Baylac-Jacqué,
|
Félix Baylac-Jacqué,
|
||||||
Gabriel Gonzalez,
|
Gabriella Gonzalez,
|
||||||
Geoff Reedy,
|
Geoff Reedy,
|
||||||
Georges Dubus,
|
Georges Dubus,
|
||||||
Graham Christensen,
|
Graham Christensen,
|
||||||
|
@ -428,7 +429,6 @@ Jaroslavas Pocepko,
|
||||||
Jarrett Keifer,
|
Jarrett Keifer,
|
||||||
Jeremy Schlatter,
|
Jeremy Schlatter,
|
||||||
Joachim Breitner,
|
Joachim Breitner,
|
||||||
Joe Hermaszewski,
|
|
||||||
Joe Pea,
|
Joe Pea,
|
||||||
John Ericson,
|
John Ericson,
|
||||||
Jonathan Ringer,
|
Jonathan Ringer,
|
||||||
|
|
16
doc/manual/src/release-notes/rl-2.5.md
Normal file
16
doc/manual/src/release-notes/rl-2.5.md
Normal file
|
@ -0,0 +1,16 @@
|
||||||
|
# Release 2.5 (2021-12-13)
|
||||||
|
|
||||||
|
* The garbage collector no longer blocks new builds, so the message
|
||||||
|
`waiting for the big garbage collector lock...` is a thing of the
|
||||||
|
past.
|
||||||
|
|
||||||
|
* Binary cache stores now have a setting `compression-level`.
|
||||||
|
|
||||||
|
* `nix develop` now has a flag `--unpack` to run `unpackPhase`.
|
||||||
|
|
||||||
|
* Lists can now be compared lexicographically using the `<` operator.
|
||||||
|
|
||||||
|
* New built-in function: `builtins.groupBy`, with the same functionality as
|
||||||
|
Nixpkgs' `lib.groupBy`, but faster.
|
||||||
|
|
||||||
|
* `nix repl` now has a `:log` command.
|
|
@ -1,7 +1,6 @@
|
||||||
# Release 2.5 (2021-XX-XX)
|
# Release X.Y (202?-??-??)
|
||||||
|
|
||||||
* Binary cache stores now have a setting `compression-level`.
|
* The TOML parser used by `builtins.fromTOML` has been replaced by [a
|
||||||
|
more compliant one](https://github.com/ToruNiina/toml11).
|
||||||
* `nix develop` now has a flag `--unpack` to run `unpackPhase`.
|
* Added `:st`/`:show-trace` commands to nix repl, which are used to
|
||||||
|
set or toggle display of error traces.
|
||||||
* Lists can now be compared lexicographically using the `<` operator.
|
|
||||||
|
|
18
docker.nix
18
docker.nix
|
@ -137,11 +137,8 @@ let
|
||||||
name = "root-profile-env";
|
name = "root-profile-env";
|
||||||
paths = defaultPkgs;
|
paths = defaultPkgs;
|
||||||
};
|
};
|
||||||
profile = pkgs.buildPackages.runCommand "user-environment" { } ''
|
manifest = pkgs.buildPackages.runCommand "manifest.nix" { } ''
|
||||||
mkdir $out
|
cat > $out <<EOF
|
||||||
cp -a ${rootEnv}/* $out/
|
|
||||||
|
|
||||||
cat > $out/manifest.nix <<EOF
|
|
||||||
[
|
[
|
||||||
${lib.concatStringsSep "\n" (builtins.map (drv: let
|
${lib.concatStringsSep "\n" (builtins.map (drv: let
|
||||||
outputs = drv.outputsToInstall or [ "out" ];
|
outputs = drv.outputsToInstall or [ "out" ];
|
||||||
|
@ -161,6 +158,11 @@ let
|
||||||
]
|
]
|
||||||
EOF
|
EOF
|
||||||
'';
|
'';
|
||||||
|
profile = pkgs.buildPackages.runCommand "user-environment" { } ''
|
||||||
|
mkdir $out
|
||||||
|
cp -a ${rootEnv}/* $out/
|
||||||
|
ln -s ${manifest} $out/manifest.nix
|
||||||
|
'';
|
||||||
in
|
in
|
||||||
pkgs.runCommand "base-system"
|
pkgs.runCommand "base-system"
|
||||||
{
|
{
|
||||||
|
@ -178,6 +180,9 @@ let
|
||||||
set -x
|
set -x
|
||||||
mkdir -p $out/etc
|
mkdir -p $out/etc
|
||||||
|
|
||||||
|
mkdir -p $out/etc/ssl/certs
|
||||||
|
ln -s /nix/var/nix/profiles/default/etc/ssl/certs/ca-bundle.crt $out/etc/ssl/certs
|
||||||
|
|
||||||
cat $passwdContentsPath > $out/etc/passwd
|
cat $passwdContentsPath > $out/etc/passwd
|
||||||
echo "" >> $out/etc/passwd
|
echo "" >> $out/etc/passwd
|
||||||
|
|
||||||
|
@ -227,6 +232,9 @@ pkgs.dockerTools.buildLayeredImageWithNixDb {
|
||||||
rm -rf nix-support
|
rm -rf nix-support
|
||||||
ln -s /nix/var/nix/profiles nix/var/nix/gcroots/profiles
|
ln -s /nix/var/nix/profiles nix/var/nix/gcroots/profiles
|
||||||
'';
|
'';
|
||||||
|
fakeRootCommands = ''
|
||||||
|
chmod 1777 tmp
|
||||||
|
'';
|
||||||
|
|
||||||
config = {
|
config = {
|
||||||
Cmd = [ "/root/.nix-profile/bin/bash" ];
|
Cmd = [ "/root/.nix-profile/bin/bash" ];
|
||||||
|
|
77
flake.nix
77
flake.nix
|
@ -22,15 +22,36 @@
|
||||||
|
|
||||||
crossSystems = [ "armv6l-linux" "armv7l-linux" ];
|
crossSystems = [ "armv6l-linux" "armv7l-linux" ];
|
||||||
|
|
||||||
|
stdenvs = [ "gccStdenv" "clangStdenv" "clang11Stdenv" "stdenv" ];
|
||||||
|
|
||||||
forAllSystems = f: nixpkgs.lib.genAttrs systems (system: f system);
|
forAllSystems = f: nixpkgs.lib.genAttrs systems (system: f system);
|
||||||
|
forAllSystemsAndStdenvs = f: forAllSystems (system:
|
||||||
|
nixpkgs.lib.listToAttrs
|
||||||
|
(map
|
||||||
|
(n:
|
||||||
|
nixpkgs.lib.nameValuePair "${n}Packages" (
|
||||||
|
f system n
|
||||||
|
)) stdenvs
|
||||||
|
)
|
||||||
|
);
|
||||||
|
|
||||||
|
forAllStdenvs = stdenvs: f: nixpkgs.lib.genAttrs stdenvs (stdenv: f stdenv);
|
||||||
|
|
||||||
# Memoize nixpkgs for different platforms for efficiency.
|
# Memoize nixpkgs for different platforms for efficiency.
|
||||||
nixpkgsFor = forAllSystems (system:
|
nixpkgsFor =
|
||||||
|
let stdenvsPackages = forAllSystemsAndStdenvs
|
||||||
|
(system: stdenv:
|
||||||
import nixpkgs {
|
import nixpkgs {
|
||||||
inherit system;
|
inherit system;
|
||||||
overlays = [ self.overlay ];
|
overlays = [
|
||||||
|
(overlayFor (p: p.${stdenv}))
|
||||||
|
];
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
|
in
|
||||||
|
# Add the `stdenvPackages` at toplevel, both because these are the ones
|
||||||
|
# we want most of the time and for backwards compatibility
|
||||||
|
forAllSystems (system: stdenvsPackages.${system} // stdenvsPackages.${system}.stdenvPackages);
|
||||||
|
|
||||||
commonDeps = pkgs: with pkgs; rec {
|
commonDeps = pkgs: with pkgs; rec {
|
||||||
# Use "busybox-sandbox-shell" if present,
|
# Use "busybox-sandbox-shell" if present,
|
||||||
|
@ -75,7 +96,7 @@
|
||||||
buildPackages.mdbook
|
buildPackages.mdbook
|
||||||
buildPackages.autoconf-archive
|
buildPackages.autoconf-archive
|
||||||
buildPackages.autoreconfHook
|
buildPackages.autoreconfHook
|
||||||
buildPackages.pkgconfig
|
buildPackages.pkg-config
|
||||||
|
|
||||||
# Tests
|
# Tests
|
||||||
buildPackages.git
|
buildPackages.git
|
||||||
|
@ -255,18 +276,15 @@
|
||||||
$(cat ${installerClosureInfo}/store-paths)
|
$(cat ${installerClosureInfo}/store-paths)
|
||||||
'';
|
'';
|
||||||
|
|
||||||
in {
|
overlayFor = getStdenv: final: prev:
|
||||||
|
let currentStdenv = getStdenv final; in
|
||||||
# A Nixpkgs overlay that overrides the 'nix' and
|
{
|
||||||
# 'nix.perl-bindings' packages.
|
|
||||||
overlay = final: prev: {
|
|
||||||
|
|
||||||
nixStable = prev.nix;
|
nixStable = prev.nix;
|
||||||
|
|
||||||
# Forward from the previous stage as we don’t want it to pick the lowdown override
|
# Forward from the previous stage as we don’t want it to pick the lowdown override
|
||||||
nixUnstable = prev.nixUnstable;
|
nixUnstable = prev.nixUnstable;
|
||||||
|
|
||||||
nix = with final; with commonDeps pkgs; stdenv.mkDerivation {
|
nix = with final; with commonDeps pkgs; currentStdenv.mkDerivation {
|
||||||
name = "nix-${version}";
|
name = "nix-${version}";
|
||||||
inherit version;
|
inherit version;
|
||||||
|
|
||||||
|
@ -288,9 +306,9 @@
|
||||||
mkdir -p $out/lib
|
mkdir -p $out/lib
|
||||||
cp -pd ${boost}/lib/{libboost_context*,libboost_thread*,libboost_system*} $out/lib
|
cp -pd ${boost}/lib/{libboost_context*,libboost_thread*,libboost_system*} $out/lib
|
||||||
rm -f $out/lib/*.a
|
rm -f $out/lib/*.a
|
||||||
${lib.optionalString stdenv.isLinux ''
|
${lib.optionalString currentStdenv.isLinux ''
|
||||||
chmod u+w $out/lib/*.so.*
|
chmod u+w $out/lib/*.so.*
|
||||||
patchelf --set-rpath $out/lib:${stdenv.cc.cc.lib}/lib $out/lib/libboost_thread.so.*
|
patchelf --set-rpath $out/lib:${currentStdenv.cc.cc.lib}/lib $out/lib/libboost_thread.so.*
|
||||||
''}
|
''}
|
||||||
'';
|
'';
|
||||||
|
|
||||||
|
@ -317,7 +335,7 @@
|
||||||
|
|
||||||
strictDeps = true;
|
strictDeps = true;
|
||||||
|
|
||||||
passthru.perl-bindings = with final; stdenv.mkDerivation {
|
passthru.perl-bindings = with final; currentStdenv.mkDerivation {
|
||||||
name = "nix-perl-${version}";
|
name = "nix-perl-${version}";
|
||||||
|
|
||||||
src = self;
|
src = self;
|
||||||
|
@ -325,7 +343,7 @@
|
||||||
nativeBuildInputs =
|
nativeBuildInputs =
|
||||||
[ buildPackages.autoconf-archive
|
[ buildPackages.autoconf-archive
|
||||||
buildPackages.autoreconfHook
|
buildPackages.autoreconfHook
|
||||||
buildPackages.pkgconfig
|
buildPackages.pkg-config
|
||||||
];
|
];
|
||||||
|
|
||||||
buildInputs =
|
buildInputs =
|
||||||
|
@ -336,8 +354,8 @@
|
||||||
pkgs.perl
|
pkgs.perl
|
||||||
boost
|
boost
|
||||||
]
|
]
|
||||||
++ lib.optional (stdenv.isLinux || stdenv.isDarwin) libsodium
|
++ lib.optional (currentStdenv.isLinux || currentStdenv.isDarwin) libsodium
|
||||||
++ lib.optional stdenv.isDarwin darwin.apple_sdk.frameworks.Security;
|
++ lib.optional currentStdenv.isDarwin darwin.apple_sdk.frameworks.Security;
|
||||||
|
|
||||||
configureFlags = ''
|
configureFlags = ''
|
||||||
--with-dbi=${perlPackages.DBI}/${pkgs.perl.libPrefix}
|
--with-dbi=${perlPackages.DBI}/${pkgs.perl.libPrefix}
|
||||||
|
@ -351,7 +369,7 @@
|
||||||
|
|
||||||
};
|
};
|
||||||
|
|
||||||
lowdown-nix = with final; stdenv.mkDerivation rec {
|
lowdown-nix = with final; currentStdenv.mkDerivation rec {
|
||||||
name = "lowdown-0.9.0";
|
name = "lowdown-0.9.0";
|
||||||
|
|
||||||
src = lowdown-src;
|
src = lowdown-src;
|
||||||
|
@ -361,15 +379,20 @@
|
||||||
nativeBuildInputs = [ buildPackages.which ];
|
nativeBuildInputs = [ buildPackages.which ];
|
||||||
|
|
||||||
configurePhase = ''
|
configurePhase = ''
|
||||||
${if (stdenv.isDarwin && stdenv.isAarch64) then "echo \"HAVE_SANDBOX_INIT=false\" > configure.local" else ""}
|
${if (currentStdenv.isDarwin && currentStdenv.isAarch64) then "echo \"HAVE_SANDBOX_INIT=false\" > configure.local" else ""}
|
||||||
./configure \
|
./configure \
|
||||||
PREFIX=${placeholder "dev"} \
|
PREFIX=${placeholder "dev"} \
|
||||||
BINDIR=${placeholder "bin"}/bin
|
BINDIR=${placeholder "bin"}/bin
|
||||||
'';
|
'';
|
||||||
};
|
};
|
||||||
|
|
||||||
};
|
};
|
||||||
|
|
||||||
|
in {
|
||||||
|
|
||||||
|
# A Nixpkgs overlay that overrides the 'nix' and
|
||||||
|
# 'nix.perl-bindings' packages.
|
||||||
|
overlay = overlayFor (p: p.stdenv);
|
||||||
|
|
||||||
hydraJobs = {
|
hydraJobs = {
|
||||||
|
|
||||||
# Binary package for various platforms.
|
# Binary package for various platforms.
|
||||||
|
@ -610,15 +633,22 @@
|
||||||
doInstallCheck = true;
|
doInstallCheck = true;
|
||||||
installCheckFlags = "sysconfdir=$(out)/etc";
|
installCheckFlags = "sysconfdir=$(out)/etc";
|
||||||
};
|
};
|
||||||
}) crossSystems)));
|
}) crossSystems)) // (builtins.listToAttrs (map (stdenvName:
|
||||||
|
nixpkgsFor.${system}.lib.nameValuePair
|
||||||
|
"nix-${stdenvName}"
|
||||||
|
nixpkgsFor.${system}."${stdenvName}Packages".nix
|
||||||
|
) stdenvs))
|
||||||
|
);
|
||||||
|
|
||||||
defaultPackage = forAllSystems (system: self.packages.${system}.nix);
|
defaultPackage = forAllSystems (system: self.packages.${system}.nix);
|
||||||
|
|
||||||
devShell = forAllSystems (system:
|
devShell = forAllSystems (system: self.devShells.${system}.stdenvPackages);
|
||||||
|
|
||||||
|
devShells = forAllSystemsAndStdenvs (system: stdenv:
|
||||||
with nixpkgsFor.${system};
|
with nixpkgsFor.${system};
|
||||||
with commonDeps pkgs;
|
with commonDeps pkgs;
|
||||||
|
|
||||||
stdenv.mkDerivation {
|
nixpkgsFor.${system}.${stdenv}.mkDerivation {
|
||||||
name = "nix";
|
name = "nix";
|
||||||
|
|
||||||
outputs = [ "out" "dev" "doc" ];
|
outputs = [ "out" "dev" "doc" ];
|
||||||
|
@ -637,6 +667,9 @@
|
||||||
PATH=$prefix/bin:$PATH
|
PATH=$prefix/bin:$PATH
|
||||||
unset PYTHONPATH
|
unset PYTHONPATH
|
||||||
export MANPATH=$out/share/man:$MANPATH
|
export MANPATH=$out/share/man:$MANPATH
|
||||||
|
|
||||||
|
# Make bash completion work.
|
||||||
|
XDG_DATA_DIRS+=:$out/share
|
||||||
'';
|
'';
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
|
@ -7,13 +7,15 @@ function _complete_nix {
|
||||||
local completion=${line%% *}
|
local completion=${line%% *}
|
||||||
if [[ -z $have_type ]]; then
|
if [[ -z $have_type ]]; then
|
||||||
have_type=1
|
have_type=1
|
||||||
if [[ $completion = filenames ]]; then
|
if [[ $completion == filenames ]]; then
|
||||||
compopt -o filenames
|
compopt -o filenames
|
||||||
|
elif [[ $completion == attrs ]]; then
|
||||||
|
compopt -o nospace
|
||||||
fi
|
fi
|
||||||
else
|
else
|
||||||
COMPREPLY+=("$completion")
|
COMPREPLY+=("$completion")
|
||||||
fi
|
fi
|
||||||
done < <(NIX_GET_COMPLETIONS=$cword "${words[@]}")
|
done < <(NIX_GET_COMPLETIONS=$cword "${words[@]/#\~/$HOME}")
|
||||||
__ltrim_colon_completions "$cur"
|
__ltrim_colon_completions "$cur"
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -19,7 +19,6 @@ end
|
||||||
|
|
||||||
function _nix_accepts_files
|
function _nix_accepts_files
|
||||||
set -l response (_nix_complete)
|
set -l response (_nix_complete)
|
||||||
# First line is either filenames or no-filenames.
|
|
||||||
test $response[1] = 'filenames'
|
test $response[1] = 'filenames'
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
|
@ -25,5 +25,10 @@
|
||||||
<string>/var/log/nix-daemon.log</string>
|
<string>/var/log/nix-daemon.log</string>
|
||||||
<key>StandardOutPath</key>
|
<key>StandardOutPath</key>
|
||||||
<string>/dev/null</string>
|
<string>/dev/null</string>
|
||||||
|
<key>SoftResourceLimits</key>
|
||||||
|
<dict>
|
||||||
|
<key>NumberOfFiles</key>
|
||||||
|
<integer>4096</integer>
|
||||||
|
</dict>
|
||||||
</dict>
|
</dict>
|
||||||
</plist>
|
</plist>
|
||||||
|
|
|
@ -41,7 +41,7 @@ perlarchname=$($perl -e 'use Config; print $Config{archname};')
|
||||||
AC_SUBST(perllibdir, [${libdir}/perl5/site_perl/$perlversion/$perlarchname])
|
AC_SUBST(perllibdir, [${libdir}/perl5/site_perl/$perlversion/$perlarchname])
|
||||||
AC_MSG_RESULT($perllibdir)
|
AC_MSG_RESULT($perllibdir)
|
||||||
|
|
||||||
# Look for libsodium, an optional dependency.
|
# Look for libsodium.
|
||||||
PKG_CHECK_MODULES([SODIUM], [libsodium], [CXXFLAGS="$SODIUM_CFLAGS $CXXFLAGS"])
|
PKG_CHECK_MODULES([SODIUM], [libsodium], [CXXFLAGS="$SODIUM_CFLAGS $CXXFLAGS"])
|
||||||
|
|
||||||
# Check for the required Perl dependencies (DBI and DBD::SQLite).
|
# Check for the required Perl dependencies (DBI and DBD::SQLite).
|
||||||
|
|
28
scripts/check-hydra-status.sh
Normal file
28
scripts/check-hydra-status.sh
Normal file
|
@ -0,0 +1,28 @@
|
||||||
|
#!/usr/bin/env bash
|
||||||
|
|
||||||
|
set -euo pipefail
|
||||||
|
# set -x
|
||||||
|
|
||||||
|
|
||||||
|
# mapfile BUILDS_FOR_LATEST_EVAL < <(
|
||||||
|
# curl -H 'Accept: application/json' https://hydra.nixos.org/jobset/nix/master/evals | \
|
||||||
|
# jq -r '.evals[0].builds[] | @sh')
|
||||||
|
BUILDS_FOR_LATEST_EVAL=$(
|
||||||
|
curl -sS -H 'Accept: application/json' https://hydra.nixos.org/jobset/nix/master/evals | \
|
||||||
|
jq -r '.evals[0].builds[]')
|
||||||
|
|
||||||
|
someBuildFailed=0
|
||||||
|
|
||||||
|
for buildId in $BUILDS_FOR_LATEST_EVAL; do
|
||||||
|
buildInfo=$(curl -sS -H 'Accept: application/json' "https://hydra.nixos.org/build/$buildId")
|
||||||
|
|
||||||
|
buildStatus=$(echo "$buildInfo" | \
|
||||||
|
jq -r '.buildstatus')
|
||||||
|
|
||||||
|
if [[ "$buildStatus" -ne 0 ]]; then
|
||||||
|
someBuildFailed=1
|
||||||
|
echo "Job “$(echo "$buildInfo" | jq -r '.job')” failed on hydra"
|
||||||
|
fi
|
||||||
|
done
|
||||||
|
|
||||||
|
exit "$someBuildFailed"
|
|
@ -440,7 +440,22 @@ add_nix_vol_fstab_line() {
|
||||||
# shellcheck disable=SC1003,SC2026
|
# shellcheck disable=SC1003,SC2026
|
||||||
local escaped_mountpoint="${NIX_ROOT/ /'\\\'040}"
|
local escaped_mountpoint="${NIX_ROOT/ /'\\\'040}"
|
||||||
shift
|
shift
|
||||||
EDITOR="/usr/bin/ex" _sudo "to add nix to fstab" "$@" <<EOF
|
|
||||||
|
# wrap `ex` to work around a problem with vim plugins breaking exit codes;
|
||||||
|
# (see https://github.com/NixOS/nix/issues/5468)
|
||||||
|
# we'd prefer EDITOR="/usr/bin/ex --noplugin" but vifs doesn't word-split
|
||||||
|
# the EDITOR env.
|
||||||
|
#
|
||||||
|
# TODO: at some point we should switch to `--clean`, but it wasn't added
|
||||||
|
# until https://github.com/vim/vim/releases/tag/v8.0.1554 while the macOS
|
||||||
|
# minver 10.12.6 seems to have released with vim 7.4
|
||||||
|
cat > "$SCRATCH/ex_cleanroom_wrapper" <<EOF
|
||||||
|
#!/bin/sh
|
||||||
|
/usr/bin/ex --noplugin "\$@"
|
||||||
|
EOF
|
||||||
|
chmod 755 "$SCRATCH/ex_cleanroom_wrapper"
|
||||||
|
|
||||||
|
EDITOR="$SCRATCH/ex_cleanroom_wrapper" _sudo "to add nix to fstab" "$@" <<EOF
|
||||||
:a
|
:a
|
||||||
UUID=$uuid $escaped_mountpoint apfs rw,noauto,nobrowse,suid,owners
|
UUID=$uuid $escaped_mountpoint apfs rw,noauto,nobrowse,suid,owners
|
||||||
.
|
.
|
||||||
|
@ -631,7 +646,7 @@ EOF
|
||||||
# technically /etc/synthetic.d/nix is supported in Big Sur+
|
# technically /etc/synthetic.d/nix is supported in Big Sur+
|
||||||
# but handling both takes even more code...
|
# but handling both takes even more code...
|
||||||
_sudo "to add Nix to /etc/synthetic.conf" \
|
_sudo "to add Nix to /etc/synthetic.conf" \
|
||||||
/usr/bin/ex /etc/synthetic.conf <<EOF
|
/usr/bin/ex --noplugin /etc/synthetic.conf <<EOF
|
||||||
:a
|
:a
|
||||||
${NIX_ROOT:1}
|
${NIX_ROOT:1}
|
||||||
.
|
.
|
||||||
|
@ -794,7 +809,7 @@ setup_volume_daemon() {
|
||||||
local volume_uuid="$2"
|
local volume_uuid="$2"
|
||||||
if ! test_voldaemon; then
|
if ! test_voldaemon; then
|
||||||
task "Configuring LaunchDaemon to mount '$NIX_VOLUME_LABEL'" >&2
|
task "Configuring LaunchDaemon to mount '$NIX_VOLUME_LABEL'" >&2
|
||||||
_sudo "to install the Nix volume mounter" /usr/bin/ex "$NIX_VOLUME_MOUNTD_DEST" <<EOF
|
_sudo "to install the Nix volume mounter" /usr/bin/ex --noplugin "$NIX_VOLUME_MOUNTD_DEST" <<EOF
|
||||||
:a
|
:a
|
||||||
$(generate_mount_daemon "$cmd_type" "$volume_uuid")
|
$(generate_mount_daemon "$cmd_type" "$volume_uuid")
|
||||||
.
|
.
|
||||||
|
|
|
@ -218,7 +218,7 @@ EOF
|
||||||
setup_darwin_volume
|
setup_darwin_volume
|
||||||
fi
|
fi
|
||||||
|
|
||||||
if [ "$(diskutil info -plist /nix | xmllint --xpath "(/plist/dict/key[text()='GlobalPermissionsEnabled'])/following-sibling::*[1]" -)" = "<false/>" ]; then
|
if [ "$(/usr/sbin/diskutil info -plist /nix | xmllint --xpath "(/plist/dict/key[text()='GlobalPermissionsEnabled'])/following-sibling::*[1]" -)" = "<false/>" ]; then
|
||||||
failure "This script needs a /nix volume with global permissions! This may require running sudo diskutil enableOwnership /nix."
|
failure "This script needs a /nix volume with global permissions! This may require running sudo /usr/sbin/diskutil enableOwnership /nix."
|
||||||
fi
|
fi
|
||||||
}
|
}
|
||||||
|
|
|
@ -377,6 +377,11 @@ cure_artifacts() {
|
||||||
}
|
}
|
||||||
|
|
||||||
validate_starting_assumptions() {
|
validate_starting_assumptions() {
|
||||||
|
task "Checking for artifacts of previous installs"
|
||||||
|
cat <<EOF
|
||||||
|
Before I try to install, I'll check for signs Nix already is or has
|
||||||
|
been installed on this system.
|
||||||
|
EOF
|
||||||
if type nix-env 2> /dev/null >&2; then
|
if type nix-env 2> /dev/null >&2; then
|
||||||
warning <<EOF
|
warning <<EOF
|
||||||
Nix already appears to be installed. This installer may run into issues.
|
Nix already appears to be installed. This installer may run into issues.
|
||||||
|
@ -386,6 +391,11 @@ $(uninstall_directions)
|
||||||
EOF
|
EOF
|
||||||
fi
|
fi
|
||||||
|
|
||||||
|
# TODO: I think it would be good for this step to accumulate more
|
||||||
|
# knowledge of older obsolete artifacts, if there are any.
|
||||||
|
# We could issue a "reminder" here that the user might want
|
||||||
|
# to clean them up?
|
||||||
|
|
||||||
for profile_target in "${PROFILE_TARGETS[@]}"; do
|
for profile_target in "${PROFILE_TARGETS[@]}"; do
|
||||||
# TODO: I think it would be good to accumulate a list of all
|
# TODO: I think it would be good to accumulate a list of all
|
||||||
# of the copies so that people don't hit this 2 or 3x in
|
# of the copies so that people don't hit this 2 or 3x in
|
||||||
|
|
|
@ -38,7 +38,7 @@ fi
|
||||||
|
|
||||||
# Determine if we could use the multi-user installer or not
|
# Determine if we could use the multi-user installer or not
|
||||||
if [ "$(uname -s)" = "Linux" ]; then
|
if [ "$(uname -s)" = "Linux" ]; then
|
||||||
echo "Note: a multi-user installation is possible. See https://nixos.org/nix/manual/#sect-multi-user-installation" >&2
|
echo "Note: a multi-user installation is possible. See https://nixos.org/manual/nix/stable/installation/installing-binary.html#multi-user-installation" >&2
|
||||||
fi
|
fi
|
||||||
|
|
||||||
case "$(uname -s)" in
|
case "$(uname -s)" in
|
||||||
|
@ -98,7 +98,7 @@ while [ $# -gt 0 ]; do
|
||||||
echo " providing multi-user support and better isolation for local builds."
|
echo " providing multi-user support and better isolation for local builds."
|
||||||
echo " Both for security and reproducibility, this method is recommended if"
|
echo " Both for security and reproducibility, this method is recommended if"
|
||||||
echo " supported on your platform."
|
echo " supported on your platform."
|
||||||
echo " See https://nixos.org/nix/manual/#sect-multi-user-installation"
|
echo " See https://nixos.org/manual/nix/stable/installation/installing-binary.html#multi-user-installation"
|
||||||
echo ""
|
echo ""
|
||||||
echo " --no-daemon: Simple, single-user installation that does not require root and is"
|
echo " --no-daemon: Simple, single-user installation that does not require root and is"
|
||||||
echo " trivial to uninstall."
|
echo " trivial to uninstall."
|
||||||
|
@ -144,7 +144,7 @@ if ! [ -e "$dest" ]; then
|
||||||
fi
|
fi
|
||||||
|
|
||||||
if ! [ -w "$dest" ]; then
|
if ! [ -w "$dest" ]; then
|
||||||
echo "$0: directory $dest exists, but is not writable by you. This could indicate that another user has already performed a single-user installation of Nix on this system. If you wish to enable multi-user support see https://nixos.org/nix/manual/#ssec-multi-user. If you wish to continue with a single-user install for $USER please run 'chown -R $USER $dest' as root." >&2
|
echo "$0: directory $dest exists, but is not writable by you. This could indicate that another user has already performed a single-user installation of Nix on this system. If you wish to enable multi-user support see https://nixos.org/manual/nix/stable/installation/multi-user.html. If you wish to continue with a single-user install for $USER please run 'chown -R $USER $dest' as root." >&2
|
||||||
exit 1
|
exit 1
|
||||||
fi
|
fi
|
||||||
|
|
||||||
|
|
|
@ -1,18 +0,0 @@
|
||||||
Copyright (c) 2014 Chase Geigle
|
|
||||||
|
|
||||||
Permission is hereby granted, free of charge, to any person obtaining a copy of
|
|
||||||
this software and associated documentation files (the "Software"), to deal in
|
|
||||||
the Software without restriction, including without limitation the rights to
|
|
||||||
use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
|
|
||||||
the Software, and to permit persons to whom the Software is furnished to do so,
|
|
||||||
subject to the following conditions:
|
|
||||||
|
|
||||||
The above copyright notice and this permission notice shall be included in all
|
|
||||||
copies or substantial portions of the Software.
|
|
||||||
|
|
||||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
||||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
|
|
||||||
FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
|
|
||||||
COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
|
|
||||||
IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
|
|
||||||
CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
|
File diff suppressed because it is too large
Load diff
|
@ -96,6 +96,18 @@ ref<Store> EvalCommand::getEvalStore()
|
||||||
return ref<Store>(evalStore);
|
return ref<Store>(evalStore);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
ref<EvalState> EvalCommand::getEvalState()
|
||||||
|
{
|
||||||
|
if (!evalState) evalState =
|
||||||
|
#if HAVE_BOEHMGC
|
||||||
|
std::allocate_shared<EvalState>(traceable_allocator<EvalState>(),
|
||||||
|
#else
|
||||||
|
std::make_shared<EvalState>(
|
||||||
|
#endif
|
||||||
|
searchPath, getEvalStore(), getStore());
|
||||||
|
return ref<EvalState>(evalState);
|
||||||
|
}
|
||||||
|
|
||||||
BuiltPathsCommand::BuiltPathsCommand(bool recursive)
|
BuiltPathsCommand::BuiltPathsCommand(bool recursive)
|
||||||
: recursive(recursive)
|
: recursive(recursive)
|
||||||
{
|
{
|
||||||
|
|
|
@ -191,7 +191,7 @@ void SourceExprCommand::completeInstallable(std::string_view prefix)
|
||||||
auto sep = prefix_.rfind('.');
|
auto sep = prefix_.rfind('.');
|
||||||
std::string searchWord;
|
std::string searchWord;
|
||||||
if (sep != std::string::npos) {
|
if (sep != std::string::npos) {
|
||||||
searchWord = prefix_.substr(sep, std::string::npos);
|
searchWord = prefix_.substr(sep + 1, std::string::npos);
|
||||||
prefix_ = prefix_.substr(0, sep);
|
prefix_ = prefix_.substr(0, sep);
|
||||||
} else {
|
} else {
|
||||||
searchWord = prefix_;
|
searchWord = prefix_;
|
||||||
|
@ -203,6 +203,8 @@ void SourceExprCommand::completeInstallable(std::string_view prefix)
|
||||||
Value v2;
|
Value v2;
|
||||||
state->autoCallFunction(*autoArgs, v1, v2);
|
state->autoCallFunction(*autoArgs, v1, v2);
|
||||||
|
|
||||||
|
completionType = ctAttrs;
|
||||||
|
|
||||||
if (v2.type() == nAttrs) {
|
if (v2.type() == nAttrs) {
|
||||||
for (auto & i : *v2.attrs) {
|
for (auto & i : *v2.attrs) {
|
||||||
std::string name = i.name;
|
std::string name = i.name;
|
||||||
|
@ -232,7 +234,9 @@ void completeFlakeRefWithFragment(
|
||||||
prefix. */
|
prefix. */
|
||||||
try {
|
try {
|
||||||
auto hash = prefix.find('#');
|
auto hash = prefix.find('#');
|
||||||
if (hash != std::string::npos) {
|
if (hash == std::string::npos) {
|
||||||
|
completeFlakeRef(evalState->store, prefix);
|
||||||
|
} else {
|
||||||
auto fragment = prefix.substr(hash + 1);
|
auto fragment = prefix.substr(hash + 1);
|
||||||
auto flakeRefS = std::string(prefix.substr(0, hash));
|
auto flakeRefS = std::string(prefix.substr(0, hash));
|
||||||
// FIXME: do tilde expansion.
|
// FIXME: do tilde expansion.
|
||||||
|
@ -248,6 +252,8 @@ void completeFlakeRefWithFragment(
|
||||||
flake. */
|
flake. */
|
||||||
attrPathPrefixes.push_back("");
|
attrPathPrefixes.push_back("");
|
||||||
|
|
||||||
|
completionType = ctAttrs;
|
||||||
|
|
||||||
for (auto & attrPathPrefixS : attrPathPrefixes) {
|
for (auto & attrPathPrefixS : attrPathPrefixes) {
|
||||||
auto attrPathPrefix = parseAttrPath(*evalState, attrPathPrefixS);
|
auto attrPathPrefix = parseAttrPath(*evalState, attrPathPrefixS);
|
||||||
auto attrPathS = attrPathPrefixS + std::string(fragment);
|
auto attrPathS = attrPathPrefixS + std::string(fragment);
|
||||||
|
@ -285,12 +291,13 @@ void completeFlakeRefWithFragment(
|
||||||
} catch (Error & e) {
|
} catch (Error & e) {
|
||||||
warn(e.msg());
|
warn(e.msg());
|
||||||
}
|
}
|
||||||
|
|
||||||
completeFlakeRef(evalState->store, prefix);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void completeFlakeRef(ref<Store> store, std::string_view prefix)
|
void completeFlakeRef(ref<Store> store, std::string_view prefix)
|
||||||
{
|
{
|
||||||
|
if (!settings.isExperimentalFeatureEnabled(Xp::Flakes))
|
||||||
|
return;
|
||||||
|
|
||||||
if (prefix == "")
|
if (prefix == "")
|
||||||
completions->add(".");
|
completions->add(".");
|
||||||
|
|
||||||
|
|
|
@ -28,7 +28,6 @@ extern "C" {
|
||||||
#include "common-eval-args.hh"
|
#include "common-eval-args.hh"
|
||||||
#include "get-drvs.hh"
|
#include "get-drvs.hh"
|
||||||
#include "derivations.hh"
|
#include "derivations.hh"
|
||||||
#include "affinity.hh"
|
|
||||||
#include "globals.hh"
|
#include "globals.hh"
|
||||||
#include "command.hh"
|
#include "command.hh"
|
||||||
#include "finally.hh"
|
#include "finally.hh"
|
||||||
|
@ -284,6 +283,7 @@ bool NixRepl::getLine(string & input, const std::string &prompt)
|
||||||
};
|
};
|
||||||
|
|
||||||
setupSignals();
|
setupSignals();
|
||||||
|
Finally resetTerminal([&]() { rl_deprep_terminal(); });
|
||||||
char * s = readline(prompt.c_str());
|
char * s = readline(prompt.c_str());
|
||||||
Finally doFree([&]() { free(s); });
|
Finally doFree([&]() { free(s); });
|
||||||
restoreSignals();
|
restoreSignals();
|
||||||
|
@ -361,6 +361,8 @@ StringSet NixRepl::completePrefix(string prefix)
|
||||||
// Quietly ignore evaluation errors.
|
// Quietly ignore evaluation errors.
|
||||||
} catch (UndefinedVarError & e) {
|
} catch (UndefinedVarError & e) {
|
||||||
// Quietly ignore undefined variable errors.
|
// Quietly ignore undefined variable errors.
|
||||||
|
} catch (BadURL & e) {
|
||||||
|
// Quietly ignore BadURL flake-related errors.
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -432,6 +434,8 @@ bool NixRepl::processLine(string line)
|
||||||
<< " :t <expr> Describe result of evaluation\n"
|
<< " :t <expr> Describe result of evaluation\n"
|
||||||
<< " :u <expr> Build derivation, then start nix-shell\n"
|
<< " :u <expr> Build derivation, then start nix-shell\n"
|
||||||
<< " :doc <expr> Show documentation of a builtin function\n"
|
<< " :doc <expr> Show documentation of a builtin function\n"
|
||||||
|
<< " :log <expr> Show logs for a derivation\n"
|
||||||
|
<< " :st [bool] Enable, disable or toggle showing traces for errors\n";
|
||||||
<< " :d <cmd> Debug mode commands\n"
|
<< " :d <cmd> Debug mode commands\n"
|
||||||
<< " :d stack Show call stack\n"
|
<< " :d stack Show call stack\n"
|
||||||
<< " :d env Show env stack\n"
|
<< " :d env Show env stack\n"
|
||||||
|
@ -543,7 +547,7 @@ bool NixRepl::processLine(string line)
|
||||||
runNix("nix-shell", {state->store->printStorePath(drvPath)});
|
runNix("nix-shell", {state->store->printStorePath(drvPath)});
|
||||||
}
|
}
|
||||||
|
|
||||||
else if (command == ":b" || command == ":i" || command == ":s") {
|
else if (command == ":b" || command == ":i" || command == ":s" || command == ":log") {
|
||||||
Value v;
|
Value v;
|
||||||
evalString(arg, v);
|
evalString(arg, v);
|
||||||
StorePath drvPath = getDerivationPath(v);
|
StorePath drvPath = getDerivationPath(v);
|
||||||
|
@ -557,6 +561,27 @@ bool NixRepl::processLine(string line)
|
||||||
logger->cout(" %s -> %s", outputName, state->store->printStorePath(outputPath));
|
logger->cout(" %s -> %s", outputName, state->store->printStorePath(outputPath));
|
||||||
} else if (command == ":i") {
|
} else if (command == ":i") {
|
||||||
runNix("nix-env", {"-i", drvPathRaw});
|
runNix("nix-env", {"-i", drvPathRaw});
|
||||||
|
} else if (command == ":log") {
|
||||||
|
settings.readOnlyMode = true;
|
||||||
|
Finally roModeReset([&]() {
|
||||||
|
settings.readOnlyMode = false;
|
||||||
|
});
|
||||||
|
auto subs = getDefaultSubstituters();
|
||||||
|
|
||||||
|
subs.push_front(state->store);
|
||||||
|
|
||||||
|
bool foundLog = false;
|
||||||
|
RunPager pager;
|
||||||
|
for (auto & sub : subs) {
|
||||||
|
auto log = sub->getBuildLog(drvPath);
|
||||||
|
if (log) {
|
||||||
|
printInfo("got build log for '%s' from '%s'", drvPathRaw, sub->getUri());
|
||||||
|
logger->writeToStdout(*log);
|
||||||
|
foundLog = true;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (!foundLog) throw Error("build log of '%s' is not available", drvPathRaw);
|
||||||
} else {
|
} else {
|
||||||
runNix("nix-shell", {drvPathRaw});
|
runNix("nix-shell", {drvPathRaw});
|
||||||
}
|
}
|
||||||
|
@ -594,6 +619,18 @@ bool NixRepl::processLine(string line)
|
||||||
throw Error("value does not have documentation");
|
throw Error("value does not have documentation");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
else if (command == ":st" || command == ":show-trace") {
|
||||||
|
if (arg == "false" || (arg == "" && loggerSettings.showTrace)) {
|
||||||
|
std::cout << "not showing error traces\n";
|
||||||
|
loggerSettings.showTrace = false;
|
||||||
|
} else if (arg == "true" || (arg == "" && !loggerSettings.showTrace)) {
|
||||||
|
std::cout << "showing error traces\n";
|
||||||
|
loggerSettings.showTrace = true;
|
||||||
|
} else {
|
||||||
|
throw Error("unexpected argument '%s' to %s", arg, command);
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
else if (command != "")
|
else if (command != "")
|
||||||
throw Error("unknown command '%1%'", command);
|
throw Error("unknown command '%1%'", command);
|
||||||
|
|
||||||
|
@ -689,8 +726,16 @@ void NixRepl::loadFiles()
|
||||||
void NixRepl::addAttrsToScope(Value & attrs)
|
void NixRepl::addAttrsToScope(Value & attrs)
|
||||||
{
|
{
|
||||||
state->forceAttrs(attrs);
|
state->forceAttrs(attrs);
|
||||||
for (auto & i : *attrs.attrs)
|
if (displ + attrs.attrs->size() >= envSize)
|
||||||
addVarToScope(i.name, *i.value);
|
throw Error("environment full; cannot add more variables");
|
||||||
|
|
||||||
|
for (auto & i : *attrs.attrs) {
|
||||||
|
staticEnv.vars.emplace_back(i.name, displ);
|
||||||
|
env->values[displ++] = i.value;
|
||||||
|
varNames.insert((string) i.name);
|
||||||
|
}
|
||||||
|
staticEnv.sort();
|
||||||
|
staticEnv.deduplicate();
|
||||||
notice("Added %1% variables.", attrs.attrs->size());
|
notice("Added %1% variables.", attrs.attrs->size());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -824,12 +869,12 @@ std::ostream & NixRepl::printValue(std::ostream & str, Value & v, unsigned int m
|
||||||
|
|
||||||
str << "[ ";
|
str << "[ ";
|
||||||
if (maxDepth > 0)
|
if (maxDepth > 0)
|
||||||
for (unsigned int n = 0; n < v.listSize(); ++n) {
|
for (auto elem : v.listItems()) {
|
||||||
if (seen.find(v.listElems()[n]) != seen.end())
|
if (seen.count(elem))
|
||||||
str << "«repeated»";
|
str << "«repeated»";
|
||||||
else
|
else
|
||||||
try {
|
try {
|
||||||
printValue(str, *v.listElems()[n], maxDepth - 1, seen);
|
printValue(str, *elem, maxDepth - 1, seen);
|
||||||
} catch (AssertionError & e) {
|
} catch (AssertionError & e) {
|
||||||
str << ANSI_RED "«error: " << e.msg() << "»" ANSI_NORMAL;
|
str << ANSI_RED "«error: " << e.msg() << "»" ANSI_NORMAL;
|
||||||
}
|
}
|
||||||
|
|
|
@ -121,8 +121,8 @@ void printValue(std::ostream & str, std::set<const Value *> & active, const Valu
|
||||||
case tList2:
|
case tList2:
|
||||||
case tListN:
|
case tListN:
|
||||||
str << "[ ";
|
str << "[ ";
|
||||||
for (unsigned int n = 0; n < v.listSize(); ++n) {
|
for (auto v2 : v.listItems()) {
|
||||||
printValue(str, active, *v.listElems()[n]);
|
printValue(str, active, *v2);
|
||||||
str << " ";
|
str << " ";
|
||||||
}
|
}
|
||||||
str << "]";
|
str << "]";
|
||||||
|
@ -521,8 +521,12 @@ Path EvalState::checkSourcePath(const Path & path_)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!found)
|
if (!found) {
|
||||||
throw RestrictedPathError("access to absolute path '%1%' is forbidden in restricted mode", abspath);
|
auto modeInformation = evalSettings.pureEval
|
||||||
|
? "in pure eval mode (use '--impure' to override)"
|
||||||
|
: "in restricted mode";
|
||||||
|
throw RestrictedPathError("access to absolute path '%1%' is forbidden %2%", abspath, modeInformation);
|
||||||
|
}
|
||||||
|
|
||||||
/* Resolve symlinks. */
|
/* Resolve symlinks. */
|
||||||
debug(format("checking access to '%s'") % abspath);
|
debug(format("checking access to '%s'") % abspath);
|
||||||
|
@ -960,8 +964,23 @@ inline Value * EvalState::lookupVar(Env * env, const ExprVar & var, bool noEval)
|
||||||
|
|
||||||
Value * EvalState::allocValue()
|
Value * EvalState::allocValue()
|
||||||
{
|
{
|
||||||
|
/* We use the boehm batch allocator to speed up allocations of Values (of which there are many).
|
||||||
|
GC_malloc_many returns a linked list of objects of the given size, where the first word
|
||||||
|
of each object is also the pointer to the next object in the list. This also means that we
|
||||||
|
have to explicitly clear the first word of every object we take. */
|
||||||
|
if (!valueAllocCache) {
|
||||||
|
valueAllocCache = GC_malloc_many(sizeof(Value));
|
||||||
|
if (!valueAllocCache) throw std::bad_alloc();
|
||||||
|
}
|
||||||
|
|
||||||
|
/* GC_NEXT is a convenience macro for accessing the first word of an object.
|
||||||
|
Take the first list item, advance the list to the next item, and clear the next pointer. */
|
||||||
|
void * p = valueAllocCache;
|
||||||
|
GC_PTR_STORE_AND_DIRTY(&valueAllocCache, GC_NEXT(p));
|
||||||
|
GC_NEXT(p) = nullptr;
|
||||||
|
|
||||||
nrValues++;
|
nrValues++;
|
||||||
auto v = (Value *) allocBytes(sizeof(Value));
|
auto v = (Value *) p;
|
||||||
return v;
|
return v;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1314,8 +1333,8 @@ void ExprLet::eval(EvalState & state, Env & env, Value & v)
|
||||||
void ExprList::eval(EvalState & state, Env & env, Value & v)
|
void ExprList::eval(EvalState & state, Env & env, Value & v)
|
||||||
{
|
{
|
||||||
state.mkList(v, elems.size());
|
state.mkList(v, elems.size());
|
||||||
for (size_t n = 0; n < elems.size(); ++n)
|
for (auto [n, v2] : enumerate(v.listItems()))
|
||||||
v.listElems()[n] = elems[n]->maybeThunk(state, env);
|
const_cast<Value * &>(v2) = elems[n]->maybeThunk(state, env);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@ -1834,7 +1853,7 @@ void ExprConcatStrings::eval(EvalState & state, Env & env, Value & v)
|
||||||
bool first = !forceString;
|
bool first = !forceString;
|
||||||
ValueType firstType = nString;
|
ValueType firstType = nString;
|
||||||
|
|
||||||
for (auto & i : *es) {
|
for (auto & [i_pos, i] : *es) {
|
||||||
Value vTmp;
|
Value vTmp;
|
||||||
i->eval(state, env, vTmp);
|
i->eval(state, env, vTmp);
|
||||||
|
|
||||||
|
@ -1855,7 +1874,7 @@ void ExprConcatStrings::eval(EvalState & state, Env & env, Value & v)
|
||||||
nf = n;
|
nf = n;
|
||||||
nf += vTmp.fpoint;
|
nf += vTmp.fpoint;
|
||||||
} else {
|
} else {
|
||||||
throwEvalError(pos, "cannot add %1% to an integer", showType(vTmp), env, this);
|
throwEvalError(i_pos, "cannot add %1% to an integer", showType(vTmp), env, this);
|
||||||
}
|
}
|
||||||
} else if (firstType == nFloat) {
|
} else if (firstType == nFloat) {
|
||||||
if (vTmp.type() == nInt) {
|
if (vTmp.type() == nInt) {
|
||||||
|
@ -1863,12 +1882,12 @@ void ExprConcatStrings::eval(EvalState & state, Env & env, Value & v)
|
||||||
} else if (vTmp.type() == nFloat) {
|
} else if (vTmp.type() == nFloat) {
|
||||||
nf += vTmp.fpoint;
|
nf += vTmp.fpoint;
|
||||||
} else
|
} else
|
||||||
throwEvalError(pos, "cannot add %1% to a float", showType(vTmp), env, this);
|
throwEvalError(i_pos, "cannot add %1% to a float", showType(vTmp), env, this);
|
||||||
} else
|
} else
|
||||||
/* skip canonization of first path, which would only be not
|
/* skip canonization of first path, which would only be not
|
||||||
canonized in the first place if it's coming from a ./${foo} type
|
canonized in the first place if it's coming from a ./${foo} type
|
||||||
path */
|
path */
|
||||||
s << state.coerceToString(pos, vTmp, context, false, firstType == nString, !first);
|
s << state.coerceToString(i_pos, vTmp, context, false, firstType == nString, !first);
|
||||||
|
|
||||||
first = false;
|
first = false;
|
||||||
}
|
}
|
||||||
|
@ -1925,8 +1944,8 @@ void EvalState::forceValueDeep(Value & v)
|
||||||
}
|
}
|
||||||
|
|
||||||
else if (v.isList()) {
|
else if (v.isList()) {
|
||||||
for (size_t n = 0; n < v.listSize(); ++n)
|
for (auto v2 : v.listItems())
|
||||||
recurse(*v.listElems()[n]);
|
recurse(*v2);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -2113,12 +2132,12 @@ string EvalState::coerceToString(const Pos & pos, Value & v, PathSet & context,
|
||||||
|
|
||||||
if (v.isList()) {
|
if (v.isList()) {
|
||||||
string result;
|
string result;
|
||||||
for (size_t n = 0; n < v.listSize(); ++n) {
|
for (auto [n, v2] : enumerate(v.listItems())) {
|
||||||
result += coerceToString(pos, *v.listElems()[n],
|
result += coerceToString(pos, *v2,
|
||||||
context, coerceMore, copyToStore);
|
context, coerceMore, copyToStore);
|
||||||
if (n < v.listSize() - 1
|
if (n < v.listSize() - 1
|
||||||
/* !!! not quite correct */
|
/* !!! not quite correct */
|
||||||
&& (!v.listElems()[n]->isList() || v.listElems()[n]->listSize() != 0))
|
&& (!v2->isList() || v2->listSize() != 0))
|
||||||
result += " ";
|
result += " ";
|
||||||
}
|
}
|
||||||
return result;
|
return result;
|
||||||
|
|
|
@ -145,6 +145,9 @@ private:
|
||||||
/* Cache used by prim_match(). */
|
/* Cache used by prim_match(). */
|
||||||
std::shared_ptr<RegexCache> regexCache;
|
std::shared_ptr<RegexCache> regexCache;
|
||||||
|
|
||||||
|
/* Allocation cache for GC'd Value objects. */
|
||||||
|
void * valueAllocCache = nullptr;
|
||||||
|
|
||||||
public:
|
public:
|
||||||
|
|
||||||
EvalState(
|
EvalState(
|
||||||
|
@ -362,7 +365,10 @@ public:
|
||||||
/* Print statistics. */
|
/* Print statistics. */
|
||||||
void printStats();
|
void printStats();
|
||||||
|
|
||||||
void realiseContext(const PathSet & context);
|
/* Realise the given context, and return a mapping from the placeholders
|
||||||
|
* used to construct the associated value to their final store path
|
||||||
|
*/
|
||||||
|
[[nodiscard]] StringMap realiseContext(const PathSet & context);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
|
||||||
|
|
|
@ -38,11 +38,11 @@ void ConfigFile::apply()
|
||||||
|
|
||||||
// FIXME: Move into libutil/config.cc.
|
// FIXME: Move into libutil/config.cc.
|
||||||
std::string valueS;
|
std::string valueS;
|
||||||
if (auto s = std::get_if<std::string>(&value))
|
if (auto* s = std::get_if<std::string>(&value))
|
||||||
valueS = *s;
|
valueS = *s;
|
||||||
else if (auto n = std::get_if<int64_t>(&value))
|
else if (auto* n = std::get_if<int64_t>(&value))
|
||||||
valueS = fmt("%d", n);
|
valueS = fmt("%d", *n);
|
||||||
else if (auto b = std::get_if<Explicit<bool>>(&value))
|
else if (auto* b = std::get_if<Explicit<bool>>(&value))
|
||||||
valueS = b->t ? "true" : "false";
|
valueS = b->t ? "true" : "false";
|
||||||
else if (auto ss = std::get_if<std::vector<std::string>>(&value))
|
else if (auto ss = std::get_if<std::vector<std::string>>(&value))
|
||||||
valueS = concatStringsSep(" ", *ss); // FIXME: evil
|
valueS = concatStringsSep(" ", *ss); // FIXME: evil
|
||||||
|
|
|
@ -155,7 +155,7 @@ static FlakeInput parseFlakeInput(EvalState & state,
|
||||||
if (!attrs.empty())
|
if (!attrs.empty())
|
||||||
throw Error("unexpected flake input attribute '%s', at %s", attrs.begin()->first, pos);
|
throw Error("unexpected flake input attribute '%s', at %s", attrs.begin()->first, pos);
|
||||||
if (url)
|
if (url)
|
||||||
input.ref = parseFlakeRef(*url, baseDir, true);
|
input.ref = parseFlakeRef(*url, baseDir, true, input.isFlake);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!input.follows && !input.ref)
|
if (!input.follows && !input.ref)
|
||||||
|
@ -194,8 +194,8 @@ static Flake getFlake(
|
||||||
state, originalRef, allowLookup, flakeCache);
|
state, originalRef, allowLookup, flakeCache);
|
||||||
|
|
||||||
// Guard against symlink attacks.
|
// Guard against symlink attacks.
|
||||||
auto flakeDir = canonPath(sourceInfo.actualPath + "/" + lockedRef.subdir);
|
auto flakeDir = canonPath(sourceInfo.actualPath + "/" + lockedRef.subdir, true);
|
||||||
auto flakeFile = canonPath(flakeDir + "/flake.nix");
|
auto flakeFile = canonPath(flakeDir + "/flake.nix", true);
|
||||||
if (!isInDir(flakeFile, sourceInfo.actualPath))
|
if (!isInDir(flakeFile, sourceInfo.actualPath))
|
||||||
throw Error("'flake.nix' file of flake '%s' escapes from '%s'",
|
throw Error("'flake.nix' file of flake '%s' escapes from '%s'",
|
||||||
lockedRef, state.store->printStorePath(sourceInfo.storePath));
|
lockedRef, state.store->printStorePath(sourceInfo.storePath));
|
||||||
|
@ -254,11 +254,10 @@ static Flake getFlake(
|
||||||
else if (setting.value->type() == nInt)
|
else if (setting.value->type() == nInt)
|
||||||
flake.config.settings.insert({setting.name, state.forceInt(*setting.value, *setting.pos)});
|
flake.config.settings.insert({setting.name, state.forceInt(*setting.value, *setting.pos)});
|
||||||
else if (setting.value->type() == nBool)
|
else if (setting.value->type() == nBool)
|
||||||
flake.config.settings.insert({setting.name, state.forceBool(*setting.value, *setting.pos)});
|
flake.config.settings.insert({setting.name, Explicit<bool> { state.forceBool(*setting.value, *setting.pos) }});
|
||||||
else if (setting.value->type() == nList) {
|
else if (setting.value->type() == nList) {
|
||||||
std::vector<std::string> ss;
|
std::vector<std::string> ss;
|
||||||
for (unsigned int n = 0; n < setting.value->listSize(); ++n) {
|
for (auto elem : setting.value->listItems()) {
|
||||||
auto elem = setting.value->listElems()[n];
|
|
||||||
if (elem->type() != nString)
|
if (elem->type() != nString)
|
||||||
throw TypeError("list element in flake configuration setting '%s' is %s while a string is expected",
|
throw TypeError("list element in flake configuration setting '%s' is %s while a string is expected",
|
||||||
setting.name, showType(*setting.value));
|
setting.name, showType(*setting.value));
|
||||||
|
@ -345,7 +344,8 @@ LockedFlake lockFlake(
|
||||||
const InputPath & inputPathPrefix,
|
const InputPath & inputPathPrefix,
|
||||||
std::shared_ptr<const Node> oldNode,
|
std::shared_ptr<const Node> oldNode,
|
||||||
const LockParent & parent,
|
const LockParent & parent,
|
||||||
const Path & parentPath)>
|
const Path & parentPath,
|
||||||
|
bool trustLock)>
|
||||||
computeLocks;
|
computeLocks;
|
||||||
|
|
||||||
computeLocks = [&](
|
computeLocks = [&](
|
||||||
|
@ -354,7 +354,8 @@ LockedFlake lockFlake(
|
||||||
const InputPath & inputPathPrefix,
|
const InputPath & inputPathPrefix,
|
||||||
std::shared_ptr<const Node> oldNode,
|
std::shared_ptr<const Node> oldNode,
|
||||||
const LockParent & parent,
|
const LockParent & parent,
|
||||||
const Path & parentPath)
|
const Path & parentPath,
|
||||||
|
bool trustLock)
|
||||||
{
|
{
|
||||||
debug("computing lock file node '%s'", printInputPath(inputPathPrefix));
|
debug("computing lock file node '%s'", printInputPath(inputPathPrefix));
|
||||||
|
|
||||||
|
@ -465,15 +466,21 @@ LockedFlake lockFlake(
|
||||||
.isFlake = (*lockedNode)->isFlake,
|
.isFlake = (*lockedNode)->isFlake,
|
||||||
});
|
});
|
||||||
} else if (auto follows = std::get_if<1>(&i.second)) {
|
} else if (auto follows = std::get_if<1>(&i.second)) {
|
||||||
auto o = input.overrides.find(i.first);
|
if (! trustLock) {
|
||||||
|
// It is possible that the flake has changed,
|
||||||
|
// so we must confirm all the follows that are in the lockfile are also in the flake.
|
||||||
|
auto overridePath(inputPath);
|
||||||
|
overridePath.push_back(i.first);
|
||||||
|
auto o = overrides.find(overridePath);
|
||||||
// If the override disappeared, we have to refetch the flake,
|
// If the override disappeared, we have to refetch the flake,
|
||||||
// since some of the inputs may not be present in the lockfile.
|
// since some of the inputs may not be present in the lockfile.
|
||||||
if (o == input.overrides.end()) {
|
if (o == overrides.end()) {
|
||||||
mustRefetch = true;
|
mustRefetch = true;
|
||||||
// There's no point populating the rest of the fake inputs,
|
// There's no point populating the rest of the fake inputs,
|
||||||
// since we'll refetch the flake anyways.
|
// since we'll refetch the flake anyways.
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
}
|
||||||
fakeInputs.emplace(i.first, FlakeInput {
|
fakeInputs.emplace(i.first, FlakeInput {
|
||||||
.follows = *follows,
|
.follows = *follows,
|
||||||
});
|
});
|
||||||
|
@ -481,11 +488,16 @@ LockedFlake lockFlake(
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
LockParent newParent {
|
||||||
|
.path = inputPath,
|
||||||
|
.absolute = true
|
||||||
|
};
|
||||||
|
|
||||||
computeLocks(
|
computeLocks(
|
||||||
mustRefetch
|
mustRefetch
|
||||||
? getFlake(state, oldLock->lockedRef, false, flakeCache).inputs
|
? getFlake(state, oldLock->lockedRef, false, flakeCache).inputs
|
||||||
: fakeInputs,
|
: fakeInputs,
|
||||||
childNode, inputPath, oldLock, parent, parentPath);
|
childNode, inputPath, oldLock, newParent, parentPath, !mustRefetch);
|
||||||
|
|
||||||
} else {
|
} else {
|
||||||
/* We need to create a new lock file entry. So fetch
|
/* We need to create a new lock file entry. So fetch
|
||||||
|
@ -542,7 +554,7 @@ LockedFlake lockFlake(
|
||||||
? std::dynamic_pointer_cast<const Node>(oldLock)
|
? std::dynamic_pointer_cast<const Node>(oldLock)
|
||||||
: LockFile::read(
|
: LockFile::read(
|
||||||
inputFlake.sourceInfo->actualPath + "/" + inputFlake.lockedRef.subdir + "/flake.lock").root,
|
inputFlake.sourceInfo->actualPath + "/" + inputFlake.lockedRef.subdir + "/flake.lock").root,
|
||||||
newParent, localPath);
|
newParent, localPath, false);
|
||||||
}
|
}
|
||||||
|
|
||||||
else {
|
else {
|
||||||
|
@ -566,11 +578,11 @@ LockedFlake lockFlake(
|
||||||
};
|
};
|
||||||
|
|
||||||
// Bring in the current ref for relative path resolution if we have it
|
// Bring in the current ref for relative path resolution if we have it
|
||||||
auto parentPath = canonPath(flake.sourceInfo->actualPath + "/" + flake.lockedRef.subdir);
|
auto parentPath = canonPath(flake.sourceInfo->actualPath + "/" + flake.lockedRef.subdir, true);
|
||||||
|
|
||||||
computeLocks(
|
computeLocks(
|
||||||
flake.inputs, newLockFile.root, {},
|
flake.inputs, newLockFile.root, {},
|
||||||
lockFlags.recreateLockFile ? nullptr : oldLockFile.root, parent, parentPath);
|
lockFlags.recreateLockFile ? nullptr : oldLockFile.root, parent, parentPath, false);
|
||||||
|
|
||||||
for (auto & i : lockFlags.inputOverrides)
|
for (auto & i : lockFlags.inputOverrides)
|
||||||
if (!overridesUsed.count(i.first))
|
if (!overridesUsed.count(i.first))
|
||||||
|
|
|
@ -48,9 +48,12 @@ FlakeRef FlakeRef::resolve(ref<Store> store) const
|
||||||
}
|
}
|
||||||
|
|
||||||
FlakeRef parseFlakeRef(
|
FlakeRef parseFlakeRef(
|
||||||
const std::string & url, const std::optional<Path> & baseDir, bool allowMissing)
|
const std::string & url,
|
||||||
|
const std::optional<Path> & baseDir,
|
||||||
|
bool allowMissing,
|
||||||
|
bool isFlake)
|
||||||
{
|
{
|
||||||
auto [flakeRef, fragment] = parseFlakeRefWithFragment(url, baseDir, allowMissing);
|
auto [flakeRef, fragment] = parseFlakeRefWithFragment(url, baseDir, allowMissing, isFlake);
|
||||||
if (fragment != "")
|
if (fragment != "")
|
||||||
throw Error("unexpected fragment '%s' in flake reference '%s'", fragment, url);
|
throw Error("unexpected fragment '%s' in flake reference '%s'", fragment, url);
|
||||||
return flakeRef;
|
return flakeRef;
|
||||||
|
@ -67,7 +70,10 @@ std::optional<FlakeRef> maybeParseFlakeRef(
|
||||||
}
|
}
|
||||||
|
|
||||||
std::pair<FlakeRef, std::string> parseFlakeRefWithFragment(
|
std::pair<FlakeRef, std::string> parseFlakeRefWithFragment(
|
||||||
const std::string & url, const std::optional<Path> & baseDir, bool allowMissing)
|
const std::string & url,
|
||||||
|
const std::optional<Path> & baseDir,
|
||||||
|
bool allowMissing,
|
||||||
|
bool isFlake)
|
||||||
{
|
{
|
||||||
using namespace fetchers;
|
using namespace fetchers;
|
||||||
|
|
||||||
|
@ -112,7 +118,9 @@ std::pair<FlakeRef, std::string> parseFlakeRefWithFragment(
|
||||||
to 'baseDir'). If so, search upward to the root of the
|
to 'baseDir'). If so, search upward to the root of the
|
||||||
repo (i.e. the directory containing .git). */
|
repo (i.e. the directory containing .git). */
|
||||||
|
|
||||||
path = absPath(path, baseDir, true);
|
path = absPath(path, baseDir);
|
||||||
|
|
||||||
|
if (isFlake) {
|
||||||
|
|
||||||
if (!S_ISDIR(lstat(path).st_mode))
|
if (!S_ISDIR(lstat(path).st_mode))
|
||||||
throw BadURL("path '%s' is not a flake (because it's not a directory)", path);
|
throw BadURL("path '%s' is not a flake (because it's not a directory)", path);
|
||||||
|
@ -153,6 +161,7 @@ std::pair<FlakeRef, std::string> parseFlakeRefWithFragment(
|
||||||
subdir = std::string(baseNameOf(flakeRoot)) + (subdir.empty() ? "" : "/" + subdir);
|
subdir = std::string(baseNameOf(flakeRoot)) + (subdir.empty() ? "" : "/" + subdir);
|
||||||
flakeRoot = dirOf(flakeRoot);
|
flakeRoot = dirOf(flakeRoot);
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
} else {
|
} else {
|
||||||
if (!hasPrefix(path, "/"))
|
if (!hasPrefix(path, "/"))
|
||||||
|
|
|
@ -62,13 +62,19 @@ struct FlakeRef
|
||||||
std::ostream & operator << (std::ostream & str, const FlakeRef & flakeRef);
|
std::ostream & operator << (std::ostream & str, const FlakeRef & flakeRef);
|
||||||
|
|
||||||
FlakeRef parseFlakeRef(
|
FlakeRef parseFlakeRef(
|
||||||
const std::string & url, const std::optional<Path> & baseDir = {}, bool allowMissing = false);
|
const std::string & url,
|
||||||
|
const std::optional<Path> & baseDir = {},
|
||||||
|
bool allowMissing = false,
|
||||||
|
bool isFlake = true);
|
||||||
|
|
||||||
std::optional<FlakeRef> maybeParseFlake(
|
std::optional<FlakeRef> maybeParseFlake(
|
||||||
const std::string & url, const std::optional<Path> & baseDir = {});
|
const std::string & url, const std::optional<Path> & baseDir = {});
|
||||||
|
|
||||||
std::pair<FlakeRef, std::string> parseFlakeRefWithFragment(
|
std::pair<FlakeRef, std::string> parseFlakeRefWithFragment(
|
||||||
const std::string & url, const std::optional<Path> & baseDir = {}, bool allowMissing = false);
|
const std::string & url,
|
||||||
|
const std::optional<Path> & baseDir = {},
|
||||||
|
bool allowMissing = false,
|
||||||
|
bool isFlake = true);
|
||||||
|
|
||||||
std::optional<std::pair<FlakeRef, std::string>> maybeParseFlakeRefWithFragment(
|
std::optional<std::pair<FlakeRef, std::string>> maybeParseFlakeRefWithFragment(
|
||||||
const std::string & url, const std::optional<Path> & baseDir = {});
|
const std::string & url, const std::optional<Path> & baseDir = {});
|
||||||
|
|
|
@ -102,9 +102,9 @@ DrvInfo::Outputs DrvInfo::queryOutputs(bool onlyOutputsToInstall)
|
||||||
state->forceList(*i->value, *i->pos);
|
state->forceList(*i->value, *i->pos);
|
||||||
|
|
||||||
/* For each output... */
|
/* For each output... */
|
||||||
for (unsigned int j = 0; j < i->value->listSize(); ++j) {
|
for (auto elem : i->value->listItems()) {
|
||||||
/* Evaluate the corresponding set. */
|
/* Evaluate the corresponding set. */
|
||||||
string name = state->forceStringNoCtx(*i->value->listElems()[j], *i->pos);
|
string name = state->forceStringNoCtx(*elem, *i->pos);
|
||||||
Bindings::iterator out = attrs->find(state->symbols.create(name));
|
Bindings::iterator out = attrs->find(state->symbols.create(name));
|
||||||
if (out == attrs->end()) continue; // FIXME: throw error?
|
if (out == attrs->end()) continue; // FIXME: throw error?
|
||||||
state->forceAttrs(*out->value);
|
state->forceAttrs(*out->value);
|
||||||
|
@ -128,9 +128,9 @@ DrvInfo::Outputs DrvInfo::queryOutputs(bool onlyOutputsToInstall)
|
||||||
/* ^ this shows during `nix-env -i` right under the bad derivation */
|
/* ^ this shows during `nix-env -i` right under the bad derivation */
|
||||||
if (!outTI->isList()) throw errMsg;
|
if (!outTI->isList()) throw errMsg;
|
||||||
Outputs result;
|
Outputs result;
|
||||||
for (auto i = outTI->listElems(); i != outTI->listElems() + outTI->listSize(); ++i) {
|
for (auto elem : outTI->listItems()) {
|
||||||
if ((*i)->type() != nString) throw errMsg;
|
if (elem->type() != nString) throw errMsg;
|
||||||
auto out = outputs.find((*i)->string.s);
|
auto out = outputs.find(elem->string.s);
|
||||||
if (out == outputs.end()) throw errMsg;
|
if (out == outputs.end()) throw errMsg;
|
||||||
result.insert(*out);
|
result.insert(*out);
|
||||||
}
|
}
|
||||||
|
@ -174,8 +174,8 @@ bool DrvInfo::checkMeta(Value & v)
|
||||||
{
|
{
|
||||||
state->forceValue(v);
|
state->forceValue(v);
|
||||||
if (v.type() == nList) {
|
if (v.type() == nList) {
|
||||||
for (unsigned int n = 0; n < v.listSize(); ++n)
|
for (auto elem : v.listItems())
|
||||||
if (!checkMeta(*v.listElems()[n])) return false;
|
if (!checkMeta(*elem)) return false;
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
else if (v.type() == nAttrs) {
|
else if (v.type() == nAttrs) {
|
||||||
|
@ -364,10 +364,10 @@ static void getDerivations(EvalState & state, Value & vIn,
|
||||||
}
|
}
|
||||||
|
|
||||||
else if (v.type() == nList) {
|
else if (v.type() == nList) {
|
||||||
for (unsigned int n = 0; n < v.listSize(); ++n) {
|
for (auto [n, elem] : enumerate(v.listItems())) {
|
||||||
string pathPrefix2 = addToPath(pathPrefix, (format("%1%") % n).str());
|
string pathPrefix2 = addToPath(pathPrefix, fmt("%d", n));
|
||||||
if (getDerivation(state, *v.listElems()[n], pathPrefix2, drvs, done, ignoreAssertionFailures))
|
if (getDerivation(state, *elem, pathPrefix2, drvs, done, ignoreAssertionFailures))
|
||||||
getDerivations(state, *v.listElems()[n], pathPrefix2, autoArgs, drvs, done, ignoreAssertionFailures);
|
getDerivations(state, *elem, pathPrefix2, autoArgs, drvs, done, ignoreAssertionFailures);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -190,7 +190,7 @@ void ExprConcatStrings::show(std::ostream & str) const
|
||||||
str << "(";
|
str << "(";
|
||||||
for (auto & i : *es) {
|
for (auto & i : *es) {
|
||||||
if (first) first = false; else str << " + ";
|
if (first) first = false; else str << " + ";
|
||||||
str << *i;
|
str << i.second;
|
||||||
}
|
}
|
||||||
str << ")";
|
str << ")";
|
||||||
}
|
}
|
||||||
|
@ -490,7 +490,7 @@ void ExprConcatStrings::bindVars(const std::shared_ptr<const StaticEnv> &env)
|
||||||
staticenv = env;
|
staticenv = env;
|
||||||
|
|
||||||
for (auto & i : *es)
|
for (auto & i : *es)
|
||||||
i->bindVars(env);
|
i.second->bindVars(env);
|
||||||
}
|
}
|
||||||
|
|
||||||
void ExprPos::bindVars(const std::shared_ptr<const StaticEnv> &env)
|
void ExprPos::bindVars(const std::shared_ptr<const StaticEnv> &env)
|
||||||
|
|
|
@ -354,8 +354,8 @@ struct ExprConcatStrings : Expr
|
||||||
{
|
{
|
||||||
Pos pos;
|
Pos pos;
|
||||||
bool forceString;
|
bool forceString;
|
||||||
vector<Expr *> * es;
|
vector<std::pair<Pos, Expr *> > * es;
|
||||||
ExprConcatStrings(const Pos & pos, bool forceString, vector<Expr *> * es)
|
ExprConcatStrings(const Pos & pos, bool forceString, vector<std::pair<Pos, Expr *> > * es)
|
||||||
: pos(pos), forceString(forceString), es(es) { };
|
: pos(pos), forceString(forceString), es(es) { };
|
||||||
Pos* getPos() { return &pos; }
|
Pos* getPos() { return &pos; }
|
||||||
COMMON_METHODS
|
COMMON_METHODS
|
||||||
|
@ -392,6 +392,13 @@ struct StaticEnv
|
||||||
[](const Vars::value_type & a, const Vars::value_type & b) { return a.first < b.first; });
|
[](const Vars::value_type & a, const Vars::value_type & b) { return a.first < b.first; });
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void deduplicate()
|
||||||
|
{
|
||||||
|
const auto last = std::unique(vars.begin(), vars.end(),
|
||||||
|
[] (const Vars::value_type & a, const Vars::value_type & b) { return a.first == b.first; });
|
||||||
|
vars.erase(last, vars.end());
|
||||||
|
}
|
||||||
|
|
||||||
Vars::const_iterator find(const Symbol & name) const
|
Vars::const_iterator find(const Symbol & name) const
|
||||||
{
|
{
|
||||||
Vars::value_type key(name, 0);
|
Vars::value_type key(name, 0);
|
||||||
|
|
|
@ -153,7 +153,7 @@ static void addFormal(const Pos & pos, Formals * formals, const Formal & formal)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
static Expr * stripIndentation(const Pos & pos, SymbolTable & symbols, vector<Expr *> & es)
|
static Expr * stripIndentation(const Pos & pos, SymbolTable & symbols, vector<std::pair<Pos, Expr *> > & es)
|
||||||
{
|
{
|
||||||
if (es.empty()) return new ExprString(symbols.create(""));
|
if (es.empty()) return new ExprString(symbols.create(""));
|
||||||
|
|
||||||
|
@ -163,7 +163,7 @@ static Expr * stripIndentation(const Pos & pos, SymbolTable & symbols, vector<Ex
|
||||||
bool atStartOfLine = true; /* = seen only whitespace in the current line */
|
bool atStartOfLine = true; /* = seen only whitespace in the current line */
|
||||||
size_t minIndent = 1000000;
|
size_t minIndent = 1000000;
|
||||||
size_t curIndent = 0;
|
size_t curIndent = 0;
|
||||||
for (auto & i : es) {
|
for (auto & [i_pos, i] : es) {
|
||||||
ExprIndStr * e = dynamic_cast<ExprIndStr *>(i);
|
ExprIndStr * e = dynamic_cast<ExprIndStr *>(i);
|
||||||
if (!e) {
|
if (!e) {
|
||||||
/* Anti-quotations end the current start-of-line whitespace. */
|
/* Anti-quotations end the current start-of-line whitespace. */
|
||||||
|
@ -193,12 +193,12 @@ static Expr * stripIndentation(const Pos & pos, SymbolTable & symbols, vector<Ex
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Strip spaces from each line. */
|
/* Strip spaces from each line. */
|
||||||
vector<Expr *> * es2 = new vector<Expr *>;
|
vector<std::pair<Pos, Expr *> > * es2 = new vector<std::pair<Pos, Expr *> >;
|
||||||
atStartOfLine = true;
|
atStartOfLine = true;
|
||||||
size_t curDropped = 0;
|
size_t curDropped = 0;
|
||||||
size_t n = es.size();
|
size_t n = es.size();
|
||||||
for (vector<Expr *>::iterator i = es.begin(); i != es.end(); ++i, --n) {
|
for (vector<std::pair<Pos, Expr *> >::iterator i = es.begin(); i != es.end(); ++i, --n) {
|
||||||
ExprIndStr * e = dynamic_cast<ExprIndStr *>(*i);
|
ExprIndStr * e = dynamic_cast<ExprIndStr *>(i->second);
|
||||||
if (!e) {
|
if (!e) {
|
||||||
atStartOfLine = false;
|
atStartOfLine = false;
|
||||||
curDropped = 0;
|
curDropped = 0;
|
||||||
|
@ -235,11 +235,11 @@ static Expr * stripIndentation(const Pos & pos, SymbolTable & symbols, vector<Ex
|
||||||
s2 = string(s2, 0, p + 1);
|
s2 = string(s2, 0, p + 1);
|
||||||
}
|
}
|
||||||
|
|
||||||
es2->push_back(new ExprString(symbols.create(s2)));
|
es2->emplace_back(i->first, new ExprString(symbols.create(s2)));
|
||||||
}
|
}
|
||||||
|
|
||||||
/* If this is a single string, then don't do a concatenation. */
|
/* If this is a single string, then don't do a concatenation. */
|
||||||
return es2->size() == 1 && dynamic_cast<ExprString *>((*es2)[0]) ? (*es2)[0] : new ExprConcatStrings(pos, true, es2);
|
return es2->size() == 1 && dynamic_cast<ExprString *>((*es2)[0].second) ? (*es2)[0].second : new ExprConcatStrings(pos, true, es2);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@ -278,7 +278,7 @@ void yyerror(YYLTYPE * loc, yyscan_t scanner, ParseData * data, const char * err
|
||||||
char * path;
|
char * path;
|
||||||
char * uri;
|
char * uri;
|
||||||
std::vector<nix::AttrName> * attrNames;
|
std::vector<nix::AttrName> * attrNames;
|
||||||
std::vector<nix::Expr *> * string_parts;
|
std::vector<std::pair<nix::Pos, nix::Expr *> > * string_parts;
|
||||||
}
|
}
|
||||||
|
|
||||||
%type <e> start expr expr_function expr_if expr_op
|
%type <e> start expr expr_function expr_if expr_op
|
||||||
|
@ -365,7 +365,7 @@ expr_op
|
||||||
| expr_op UPDATE expr_op { $$ = new ExprOpUpdate(CUR_POS, $1, $3); }
|
| expr_op UPDATE expr_op { $$ = new ExprOpUpdate(CUR_POS, $1, $3); }
|
||||||
| expr_op '?' attrpath { $$ = new ExprOpHasAttr($1, *$3); }
|
| expr_op '?' attrpath { $$ = new ExprOpHasAttr($1, *$3); }
|
||||||
| expr_op '+' expr_op
|
| expr_op '+' expr_op
|
||||||
{ $$ = new ExprConcatStrings(CUR_POS, false, new vector<Expr *>({$1, $3})); }
|
{ $$ = new ExprConcatStrings(CUR_POS, false, new vector<std::pair<Pos, Expr *> >({{makeCurPos(@1, data), $1}, {makeCurPos(@3, data), $3}})); }
|
||||||
| expr_op '-' expr_op { $$ = new ExprCall(CUR_POS, new ExprVar(data->symbols.create("__sub")), {$1, $3}); }
|
| expr_op '-' expr_op { $$ = new ExprCall(CUR_POS, new ExprVar(data->symbols.create("__sub")), {$1, $3}); }
|
||||||
| expr_op '*' expr_op { $$ = new ExprCall(CUR_POS, new ExprVar(data->symbols.create("__mul")), {$1, $3}); }
|
| expr_op '*' expr_op { $$ = new ExprCall(CUR_POS, new ExprVar(data->symbols.create("__mul")), {$1, $3}); }
|
||||||
| expr_op '/' expr_op { $$ = new ExprCall(CUR_POS, new ExprVar(data->symbols.create("__div")), {$1, $3}); }
|
| expr_op '/' expr_op { $$ = new ExprCall(CUR_POS, new ExprVar(data->symbols.create("__div")), {$1, $3}); }
|
||||||
|
@ -411,7 +411,7 @@ expr_simple
|
||||||
}
|
}
|
||||||
| path_start PATH_END { $$ = $1; }
|
| path_start PATH_END { $$ = $1; }
|
||||||
| path_start string_parts_interpolated PATH_END {
|
| path_start string_parts_interpolated PATH_END {
|
||||||
$2->insert($2->begin(), $1);
|
$2->insert($2->begin(), {makeCurPos(@1, data), $1});
|
||||||
$$ = new ExprConcatStrings(CUR_POS, false, $2);
|
$$ = new ExprConcatStrings(CUR_POS, false, $2);
|
||||||
}
|
}
|
||||||
| SPATH {
|
| SPATH {
|
||||||
|
@ -449,13 +449,13 @@ string_parts
|
||||||
;
|
;
|
||||||
|
|
||||||
string_parts_interpolated
|
string_parts_interpolated
|
||||||
: string_parts_interpolated STR { $$ = $1; $1->push_back($2); }
|
: string_parts_interpolated STR { $$ = $1; $1->emplace_back(makeCurPos(@2, data), $2); }
|
||||||
| string_parts_interpolated DOLLAR_CURLY expr '}' { $$ = $1; $1->push_back($3); }
|
| string_parts_interpolated DOLLAR_CURLY expr '}' { $$ = $1; $1->emplace_back(makeCurPos(@2, data), $3); }
|
||||||
| DOLLAR_CURLY expr '}' { $$ = new vector<Expr *>; $$->push_back($2); }
|
| DOLLAR_CURLY expr '}' { $$ = new vector<std::pair<Pos, Expr *> >; $$->emplace_back(makeCurPos(@1, data), $2); }
|
||||||
| STR DOLLAR_CURLY expr '}' {
|
| STR DOLLAR_CURLY expr '}' {
|
||||||
$$ = new vector<Expr *>;
|
$$ = new vector<std::pair<Pos, Expr *> >;
|
||||||
$$->push_back($1);
|
$$->emplace_back(makeCurPos(@1, data), $1);
|
||||||
$$->push_back($3);
|
$$->emplace_back(makeCurPos(@2, data), $3);
|
||||||
}
|
}
|
||||||
;
|
;
|
||||||
|
|
||||||
|
@ -474,9 +474,9 @@ path_start
|
||||||
;
|
;
|
||||||
|
|
||||||
ind_string_parts
|
ind_string_parts
|
||||||
: ind_string_parts IND_STR { $$ = $1; $1->push_back($2); }
|
: ind_string_parts IND_STR { $$ = $1; $1->emplace_back(makeCurPos(@2, data), $2); }
|
||||||
| ind_string_parts DOLLAR_CURLY expr '}' { $$ = $1; $1->push_back($3); }
|
| ind_string_parts DOLLAR_CURLY expr '}' { $$ = $1; $1->emplace_back(makeCurPos(@2, data), $3); }
|
||||||
| { $$ = new vector<Expr *>; }
|
| { $$ = new vector<std::pair<Pos, Expr *> >; }
|
||||||
;
|
;
|
||||||
|
|
||||||
binds
|
binds
|
||||||
|
|
|
@ -35,9 +35,10 @@ namespace nix {
|
||||||
InvalidPathError::InvalidPathError(const Path & path) :
|
InvalidPathError::InvalidPathError(const Path & path) :
|
||||||
EvalError("path '%s' is not valid", path), path(path) {}
|
EvalError("path '%s' is not valid", path), path(path) {}
|
||||||
|
|
||||||
void EvalState::realiseContext(const PathSet & context)
|
StringMap EvalState::realiseContext(const PathSet & context)
|
||||||
{
|
{
|
||||||
std::vector<DerivedPath::Built> drvs;
|
std::vector<DerivedPath::Built> drvs;
|
||||||
|
StringMap res;
|
||||||
|
|
||||||
for (auto & i : context) {
|
for (auto & i : context) {
|
||||||
auto [ctxS, outputName] = decodeContext(i);
|
auto [ctxS, outputName] = decodeContext(i);
|
||||||
|
@ -46,10 +47,12 @@ void EvalState::realiseContext(const PathSet & context)
|
||||||
throw InvalidPathError(store->printStorePath(ctx));
|
throw InvalidPathError(store->printStorePath(ctx));
|
||||||
if (!outputName.empty() && ctx.isDerivation()) {
|
if (!outputName.empty() && ctx.isDerivation()) {
|
||||||
drvs.push_back({ctx, {outputName}});
|
drvs.push_back({ctx, {outputName}});
|
||||||
|
} else {
|
||||||
|
res.insert_or_assign(ctxS, ctxS);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (drvs.empty()) return;
|
if (drvs.empty()) return {};
|
||||||
|
|
||||||
if (!evalSettings.enableImportFromDerivation)
|
if (!evalSettings.enableImportFromDerivation)
|
||||||
throw Error(
|
throw Error(
|
||||||
|
@ -61,21 +64,55 @@ void EvalState::realiseContext(const PathSet & context)
|
||||||
for (auto & d : drvs) buildReqs.emplace_back(DerivedPath { d });
|
for (auto & d : drvs) buildReqs.emplace_back(DerivedPath { d });
|
||||||
store->buildPaths(buildReqs);
|
store->buildPaths(buildReqs);
|
||||||
|
|
||||||
/* Add the output of this derivations to the allowed
|
/* Get all the output paths corresponding to the placeholders we had */
|
||||||
paths. */
|
|
||||||
if (allowedPaths) {
|
|
||||||
for (auto & [drvPath, outputs] : drvs) {
|
for (auto & [drvPath, outputs] : drvs) {
|
||||||
auto outputPaths = store->queryDerivationOutputMap(drvPath);
|
auto outputPaths = store->queryDerivationOutputMap(drvPath);
|
||||||
for (auto & outputName : outputs) {
|
for (auto & outputName : outputs) {
|
||||||
if (outputPaths.count(outputName) == 0)
|
if (outputPaths.count(outputName) == 0)
|
||||||
throw Error("derivation '%s' does not have an output named '%s'",
|
throw Error("derivation '%s' does not have an output named '%s'",
|
||||||
store->printStorePath(drvPath), outputName);
|
store->printStorePath(drvPath), outputName);
|
||||||
allowPath(outputPaths.at(outputName));
|
res.insert_or_assign(
|
||||||
|
downstreamPlaceholder(*store, drvPath, outputName),
|
||||||
|
store->printStorePath(outputPaths.at(outputName))
|
||||||
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* Add the output of this derivations to the allowed
|
||||||
|
paths. */
|
||||||
|
if (allowedPaths) {
|
||||||
|
for (auto & [_placeholder, outputPath] : res) {
|
||||||
|
allowPath(store->toRealPath(outputPath));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
return res;
|
||||||
|
}
|
||||||
|
|
||||||
|
struct RealisePathFlags {
|
||||||
|
// Whether to check whether the path is a valid absolute path
|
||||||
|
bool requireAbsolutePath = true;
|
||||||
|
// Whether to check that the path is allowed in pure eval mode
|
||||||
|
bool checkForPureEval = true;
|
||||||
|
};
|
||||||
|
|
||||||
|
static Path realisePath(EvalState & state, const Pos & pos, Value & v, const RealisePathFlags flags = {})
|
||||||
|
{
|
||||||
|
PathSet context;
|
||||||
|
|
||||||
|
Path path = flags.requireAbsolutePath
|
||||||
|
? state.coerceToPath(pos, v, context)
|
||||||
|
: state.coerceToString(pos, v, context, false, false);
|
||||||
|
|
||||||
|
StringMap rewrites = state.realiseContext(context);
|
||||||
|
|
||||||
|
auto realPath = state.toRealPath(rewriteStrings(path, rewrites), context);
|
||||||
|
|
||||||
|
return flags.checkForPureEval
|
||||||
|
? state.checkSourcePath(realPath)
|
||||||
|
: realPath;
|
||||||
|
}
|
||||||
|
|
||||||
/* Add and attribute to the given attribute map from the output name to
|
/* Add and attribute to the given attribute map from the output name to
|
||||||
the output path, or a placeholder.
|
the output path, or a placeholder.
|
||||||
|
|
||||||
|
@ -109,11 +146,9 @@ static void mkOutputString(EvalState & state, Value & v,
|
||||||
argument. */
|
argument. */
|
||||||
static void import(EvalState & state, const Pos & pos, Value & vPath, Value * vScope, Value & v)
|
static void import(EvalState & state, const Pos & pos, Value & vPath, Value * vScope, Value & v)
|
||||||
{
|
{
|
||||||
PathSet context;
|
Path path;
|
||||||
Path path = state.coerceToPath(pos, vPath, context);
|
|
||||||
|
|
||||||
try {
|
try {
|
||||||
state.realiseContext(context);
|
path = realisePath(state, pos, vPath);
|
||||||
} catch (InvalidPathError & e) {
|
} catch (InvalidPathError & e) {
|
||||||
throw EvalError({
|
throw EvalError({
|
||||||
.msg = hintfmt("cannot import '%1%', since path '%2%' is not valid", path, e.path),
|
.msg = hintfmt("cannot import '%1%', since path '%2%' is not valid", path, e.path),
|
||||||
|
@ -124,8 +159,6 @@ static void import(EvalState & state, const Pos & pos, Value & vPath, Value * vS
|
||||||
throw;
|
throw;
|
||||||
}
|
}
|
||||||
|
|
||||||
Path realPath = state.checkSourcePath(state.toRealPath(path, context));
|
|
||||||
|
|
||||||
// FIXME
|
// FIXME
|
||||||
auto isValidDerivationInStore = [&]() -> std::optional<StorePath> {
|
auto isValidDerivationInStore = [&]() -> std::optional<StorePath> {
|
||||||
if (!state.store->isStorePath(path))
|
if (!state.store->isStorePath(path))
|
||||||
|
@ -177,7 +210,7 @@ static void import(EvalState & state, const Pos & pos, Value & vPath, Value * vS
|
||||||
|
|
||||||
else {
|
else {
|
||||||
if (!vScope)
|
if (!vScope)
|
||||||
state.evalFile(realPath, v);
|
state.evalFile(path, v);
|
||||||
else {
|
else {
|
||||||
state.forceAttrs(*vScope);
|
state.forceAttrs(*vScope);
|
||||||
|
|
||||||
|
@ -195,8 +228,8 @@ static void import(EvalState & state, const Pos & pos, Value & vPath, Value * vS
|
||||||
// No need to call staticEnv.sort(), because
|
// No need to call staticEnv.sort(), because
|
||||||
// args[0]->attrs is already sorted.
|
// args[0]->attrs is already sorted.
|
||||||
|
|
||||||
printTalkative("evaluating file '%1%'", realPath);
|
printTalkative("evaluating file '%1%'", path);
|
||||||
Expr * e = state.parseExprFromFile(resolveExprPath(realPath), staticEnv);
|
Expr * e = state.parseExprFromFile(resolveExprPath(path), staticEnv);
|
||||||
|
|
||||||
e->eval(state, *env, v);
|
e->eval(state, *env, v);
|
||||||
}
|
}
|
||||||
|
@ -281,22 +314,19 @@ extern "C" typedef void (*ValueInitializer)(EvalState & state, Value & v);
|
||||||
/* Load a ValueInitializer from a DSO and return whatever it initializes */
|
/* Load a ValueInitializer from a DSO and return whatever it initializes */
|
||||||
void prim_importNative(EvalState & state, const Pos & pos, Value * * args, Value & v)
|
void prim_importNative(EvalState & state, const Pos & pos, Value * * args, Value & v)
|
||||||
{
|
{
|
||||||
PathSet context;
|
Path path;
|
||||||
Path path = state.coerceToPath(pos, *args[0], context);
|
|
||||||
|
|
||||||
try {
|
try {
|
||||||
state.realiseContext(context);
|
path = realisePath(state, pos, *args[0]);
|
||||||
} catch (InvalidPathError & e) {
|
} catch (InvalidPathError & e) {
|
||||||
throw EvalError({
|
throw EvalError({
|
||||||
.msg = hintfmt(
|
.msg = hintfmt("cannot import '%1%', since path '%2%' is not valid", path, e.path),
|
||||||
"cannot import '%1%', since path '%2%' is not valid",
|
|
||||||
path, e.path),
|
|
||||||
.errPos = pos
|
.errPos = pos
|
||||||
});
|
});
|
||||||
|
} catch (Error & e) {
|
||||||
|
e.addTrace(pos, "while importing '%s'", path);
|
||||||
|
throw;
|
||||||
}
|
}
|
||||||
|
|
||||||
path = state.checkSourcePath(path);
|
|
||||||
|
|
||||||
string sym = state.forceStringNoCtx(*args[1], pos);
|
string sym = state.forceStringNoCtx(*args[1], pos);
|
||||||
|
|
||||||
void *handle = dlopen(path.c_str(), RTLD_LAZY | RTLD_LOCAL);
|
void *handle = dlopen(path.c_str(), RTLD_LAZY | RTLD_LOCAL);
|
||||||
|
@ -335,11 +365,10 @@ void prim_exec(EvalState & state, const Pos & pos, Value * * args, Value & v)
|
||||||
PathSet context;
|
PathSet context;
|
||||||
auto program = state.coerceToString(pos, *elems[0], context, false, false);
|
auto program = state.coerceToString(pos, *elems[0], context, false, false);
|
||||||
Strings commandArgs;
|
Strings commandArgs;
|
||||||
for (unsigned int i = 1; i < args[0]->listSize(); ++i) {
|
for (unsigned int i = 1; i < args[0]->listSize(); ++i)
|
||||||
commandArgs.emplace_back(state.coerceToString(pos, *elems[i], context, false, false));
|
commandArgs.emplace_back(state.coerceToString(pos, *elems[i], context, false, false));
|
||||||
}
|
|
||||||
try {
|
try {
|
||||||
state.realiseContext(context);
|
auto _ = state.realiseContext(context); // FIXME: Handle CA derivations
|
||||||
} catch (InvalidPathError & e) {
|
} catch (InvalidPathError & e) {
|
||||||
throw EvalError({
|
throw EvalError({
|
||||||
.msg = hintfmt("cannot execute '%1%', since path '%2%' is not valid",
|
.msg = hintfmt("cannot execute '%1%', since path '%2%' is not valid",
|
||||||
|
@ -616,8 +645,8 @@ static void prim_genericClosure(EvalState & state, const Pos & pos, Value * * ar
|
||||||
state.forceList(*startSet->value, pos);
|
state.forceList(*startSet->value, pos);
|
||||||
|
|
||||||
ValueList workSet;
|
ValueList workSet;
|
||||||
for (unsigned int n = 0; n < startSet->value->listSize(); ++n)
|
for (auto elem : startSet->value->listItems())
|
||||||
workSet.push_back(startSet->value->listElems()[n]);
|
workSet.push_back(elem);
|
||||||
|
|
||||||
/* Get the operator. */
|
/* Get the operator. */
|
||||||
Bindings::iterator op = getAttr(
|
Bindings::iterator op = getAttr(
|
||||||
|
@ -662,9 +691,9 @@ static void prim_genericClosure(EvalState & state, const Pos & pos, Value * * ar
|
||||||
state.forceList(call, pos);
|
state.forceList(call, pos);
|
||||||
|
|
||||||
/* Add the values returned by the operator to the work set. */
|
/* Add the values returned by the operator to the work set. */
|
||||||
for (unsigned int n = 0; n < call.listSize(); ++n) {
|
for (auto elem : call.listItems()) {
|
||||||
state.forceValue(*call.listElems()[n], pos);
|
state.forceValue(*elem, pos);
|
||||||
workSet.push_back(call.listElems()[n]);
|
workSet.push_back(elem);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1013,8 +1042,8 @@ static void prim_derivationStrict(EvalState & state, const Pos & pos, Value * *
|
||||||
command-line arguments to the builder. */
|
command-line arguments to the builder. */
|
||||||
else if (i->name == state.sArgs) {
|
else if (i->name == state.sArgs) {
|
||||||
state.forceList(*i->value, pos);
|
state.forceList(*i->value, pos);
|
||||||
for (unsigned int n = 0; n < i->value->listSize(); ++n) {
|
for (auto elem : i->value->listItems()) {
|
||||||
string s = state.coerceToString(posDrvName, *i->value->listElems()[n], context, true);
|
string s = state.coerceToString(posDrvName, *elem, context, true);
|
||||||
drv.args.push_back(s);
|
drv.args.push_back(s);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1044,8 +1073,8 @@ static void prim_derivationStrict(EvalState & state, const Pos & pos, Value * *
|
||||||
/* Require ‘outputs’ to be a list of strings. */
|
/* Require ‘outputs’ to be a list of strings. */
|
||||||
state.forceList(*i->value, posDrvName);
|
state.forceList(*i->value, posDrvName);
|
||||||
Strings ss;
|
Strings ss;
|
||||||
for (unsigned int n = 0; n < i->value->listSize(); ++n)
|
for (auto elem : i->value->listItems())
|
||||||
ss.emplace_back(state.forceStringNoCtx(*i->value->listElems()[n], posDrvName));
|
ss.emplace_back(state.forceStringNoCtx(*elem, posDrvName));
|
||||||
handleOutputs(ss);
|
handleOutputs(ss);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1350,10 +1379,14 @@ static RegisterPrimOp primop_storePath({
|
||||||
|
|
||||||
static void prim_pathExists(EvalState & state, const Pos & pos, Value * * args, Value & v)
|
static void prim_pathExists(EvalState & state, const Pos & pos, Value * * args, Value & v)
|
||||||
{
|
{
|
||||||
PathSet context;
|
Path path;
|
||||||
Path path = state.coerceToPath(pos, *args[0], context);
|
|
||||||
try {
|
try {
|
||||||
state.realiseContext(context);
|
// We don’t check the path right now, because we don’t want to throw if
|
||||||
|
// the path isn’t allowed, but just return false
|
||||||
|
// (and we can’t just catch the exception here because we still want to
|
||||||
|
// throw if something in the evaluation of `*args[0]` tries to access an
|
||||||
|
// unauthorized path)
|
||||||
|
path = realisePath(state, pos, *args[0], { .checkForPureEval = false });
|
||||||
} catch (InvalidPathError & e) {
|
} catch (InvalidPathError & e) {
|
||||||
throw EvalError({
|
throw EvalError({
|
||||||
.msg = hintfmt(
|
.msg = hintfmt(
|
||||||
|
@ -1427,17 +1460,16 @@ static RegisterPrimOp primop_dirOf({
|
||||||
/* Return the contents of a file as a string. */
|
/* Return the contents of a file as a string. */
|
||||||
static void prim_readFile(EvalState & state, const Pos & pos, Value * * args, Value & v)
|
static void prim_readFile(EvalState & state, const Pos & pos, Value * * args, Value & v)
|
||||||
{
|
{
|
||||||
PathSet context;
|
Path path;
|
||||||
Path path = state.coerceToPath(pos, *args[0], context);
|
|
||||||
try {
|
try {
|
||||||
state.realiseContext(context);
|
path = realisePath(state, pos, *args[0]);
|
||||||
} catch (InvalidPathError & e) {
|
} catch (InvalidPathError & e) {
|
||||||
throw EvalError({
|
throw EvalError({
|
||||||
.msg = hintfmt("cannot read '%1%', since path '%2%' is not valid", path, e.path),
|
.msg = hintfmt("cannot read '%1%', since path '%2%' is not valid", path, e.path),
|
||||||
.errPos = pos
|
.errPos = pos
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
string s = readFile(state.checkSourcePath(state.toRealPath(path, context)));
|
string s = readFile(path);
|
||||||
if (s.find((char) 0) != string::npos)
|
if (s.find((char) 0) != string::npos)
|
||||||
throw Error("the contents of the file '%1%' cannot be represented as a Nix string", path);
|
throw Error("the contents of the file '%1%' cannot be represented as a Nix string", path);
|
||||||
mkString(v, s.c_str());
|
mkString(v, s.c_str());
|
||||||
|
@ -1460,28 +1492,26 @@ static void prim_findFile(EvalState & state, const Pos & pos, Value * * args, Va
|
||||||
|
|
||||||
SearchPath searchPath;
|
SearchPath searchPath;
|
||||||
|
|
||||||
for (unsigned int n = 0; n < args[0]->listSize(); ++n) {
|
for (auto v2 : args[0]->listItems()) {
|
||||||
Value & v2(*args[0]->listElems()[n]);
|
state.forceAttrs(*v2, pos);
|
||||||
state.forceAttrs(v2, pos);
|
|
||||||
|
|
||||||
string prefix;
|
string prefix;
|
||||||
Bindings::iterator i = v2.attrs->find(state.symbols.create("prefix"));
|
Bindings::iterator i = v2->attrs->find(state.symbols.create("prefix"));
|
||||||
if (i != v2.attrs->end())
|
if (i != v2->attrs->end())
|
||||||
prefix = state.forceStringNoCtx(*i->value, pos);
|
prefix = state.forceStringNoCtx(*i->value, pos);
|
||||||
|
|
||||||
i = getAttr(
|
i = getAttr(
|
||||||
state,
|
state,
|
||||||
"findFile",
|
"findFile",
|
||||||
"path",
|
"path",
|
||||||
v2.attrs,
|
v2->attrs,
|
||||||
pos
|
pos
|
||||||
);
|
);
|
||||||
|
|
||||||
PathSet context;
|
Path path;
|
||||||
string path = state.coerceToString(pos, *i->value, context, false, false);
|
|
||||||
|
|
||||||
try {
|
try {
|
||||||
state.realiseContext(context);
|
path = realisePath(state, pos, *i->value, { .requireAbsolutePath = false });
|
||||||
} catch (InvalidPathError & e) {
|
} catch (InvalidPathError & e) {
|
||||||
throw EvalError({
|
throw EvalError({
|
||||||
.msg = hintfmt("cannot find '%1%', since path '%2%' is not valid", path, e.path),
|
.msg = hintfmt("cannot find '%1%', since path '%2%' is not valid", path, e.path),
|
||||||
|
@ -1514,15 +1544,14 @@ static void prim_hashFile(EvalState & state, const Pos & pos, Value * * args, Va
|
||||||
.errPos = pos
|
.errPos = pos
|
||||||
});
|
});
|
||||||
|
|
||||||
PathSet context;
|
Path path;
|
||||||
Path path = state.coerceToPath(pos, *args[1], context);
|
|
||||||
try {
|
try {
|
||||||
state.realiseContext(context);
|
path = realisePath(state, pos, *args[1]);
|
||||||
} catch (InvalidPathError & e) {
|
} catch (InvalidPathError & e) {
|
||||||
throw EvalError("cannot read '%s' since path '%s' is not valid, at %s", path, e.path, pos);
|
throw EvalError("cannot read '%s' since path '%s' is not valid, at %s", path, e.path, pos);
|
||||||
}
|
}
|
||||||
|
|
||||||
mkString(v, hashFile(*ht, state.checkSourcePath(state.toRealPath(path, context))).to_string(Base16, false));
|
mkString(v, hashFile(*ht, path).to_string(Base16, false));
|
||||||
}
|
}
|
||||||
|
|
||||||
static RegisterPrimOp primop_hashFile({
|
static RegisterPrimOp primop_hashFile({
|
||||||
|
@ -1539,10 +1568,9 @@ static RegisterPrimOp primop_hashFile({
|
||||||
/* Read a directory (without . or ..) */
|
/* Read a directory (without . or ..) */
|
||||||
static void prim_readDir(EvalState & state, const Pos & pos, Value * * args, Value & v)
|
static void prim_readDir(EvalState & state, const Pos & pos, Value * * args, Value & v)
|
||||||
{
|
{
|
||||||
PathSet ctx;
|
Path path;
|
||||||
Path path = state.coerceToPath(pos, *args[0], ctx);
|
|
||||||
try {
|
try {
|
||||||
state.realiseContext(ctx);
|
path = realisePath(state, pos, *args[0]);
|
||||||
} catch (InvalidPathError & e) {
|
} catch (InvalidPathError & e) {
|
||||||
throw EvalError({
|
throw EvalError({
|
||||||
.msg = hintfmt("cannot read '%1%', since path '%2%' is not valid", path, e.path),
|
.msg = hintfmt("cannot read '%1%', since path '%2%' is not valid", path, e.path),
|
||||||
|
@ -1550,7 +1578,7 @@ static void prim_readDir(EvalState & state, const Pos & pos, Value * * args, Val
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
DirEntries entries = readDirectory(state.checkSourcePath(path));
|
DirEntries entries = readDirectory(path);
|
||||||
state.mkAttrs(v, entries.size());
|
state.mkAttrs(v, entries.size());
|
||||||
|
|
||||||
for (auto & ent : entries) {
|
for (auto & ent : entries) {
|
||||||
|
@ -1877,7 +1905,8 @@ static void addPath(
|
||||||
try {
|
try {
|
||||||
// FIXME: handle CA derivation outputs (where path needs to
|
// FIXME: handle CA derivation outputs (where path needs to
|
||||||
// be rewritten to the actual output).
|
// be rewritten to the actual output).
|
||||||
state.realiseContext(context);
|
auto rewrites = state.realiseContext(context);
|
||||||
|
path = state.toRealPath(rewriteStrings(path, rewrites), context);
|
||||||
|
|
||||||
StorePathSet refs;
|
StorePathSet refs;
|
||||||
|
|
||||||
|
@ -2239,9 +2268,9 @@ static void prim_removeAttrs(EvalState & state, const Pos & pos, Value * * args,
|
||||||
|
|
||||||
/* Get the attribute names to be removed. */
|
/* Get the attribute names to be removed. */
|
||||||
std::set<Symbol> names;
|
std::set<Symbol> names;
|
||||||
for (unsigned int i = 0; i < args[1]->listSize(); ++i) {
|
for (auto elem : args[1]->listItems()) {
|
||||||
state.forceStringNoCtx(*args[1]->listElems()[i], pos);
|
state.forceStringNoCtx(*elem, pos);
|
||||||
names.insert(state.symbols.create(args[1]->listElems()[i]->string.s));
|
names.insert(state.symbols.create(elem->string.s));
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Copy all attributes not in that set. Note that we don't need
|
/* Copy all attributes not in that set. Note that we don't need
|
||||||
|
@ -2249,7 +2278,7 @@ static void prim_removeAttrs(EvalState & state, const Pos & pos, Value * * args,
|
||||||
vector. */
|
vector. */
|
||||||
state.mkAttrs(v, args[0]->attrs->size());
|
state.mkAttrs(v, args[0]->attrs->size());
|
||||||
for (auto & i : *args[0]->attrs) {
|
for (auto & i : *args[0]->attrs) {
|
||||||
if (names.find(i.name) == names.end())
|
if (!names.count(i.name))
|
||||||
v.attrs->push_back(i);
|
v.attrs->push_back(i);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -2283,15 +2312,14 @@ static void prim_listToAttrs(EvalState & state, const Pos & pos, Value * * args,
|
||||||
|
|
||||||
std::set<Symbol> seen;
|
std::set<Symbol> seen;
|
||||||
|
|
||||||
for (unsigned int i = 0; i < args[0]->listSize(); ++i) {
|
for (auto v2 : args[0]->listItems()) {
|
||||||
Value & v2(*args[0]->listElems()[i]);
|
state.forceAttrs(*v2, pos);
|
||||||
state.forceAttrs(v2, pos);
|
|
||||||
|
|
||||||
Bindings::iterator j = getAttr(
|
Bindings::iterator j = getAttr(
|
||||||
state,
|
state,
|
||||||
"listToAttrs",
|
"listToAttrs",
|
||||||
state.sName,
|
state.sName,
|
||||||
v2.attrs,
|
v2->attrs,
|
||||||
pos
|
pos
|
||||||
);
|
);
|
||||||
|
|
||||||
|
@ -2303,7 +2331,7 @@ static void prim_listToAttrs(EvalState & state, const Pos & pos, Value * * args,
|
||||||
state,
|
state,
|
||||||
"listToAttrs",
|
"listToAttrs",
|
||||||
state.sValue,
|
state.sValue,
|
||||||
v2.attrs,
|
v2->attrs,
|
||||||
pos
|
pos
|
||||||
);
|
);
|
||||||
v.attrs->push_back(Attr(sym, j2->value, j2->pos));
|
v.attrs->push_back(Attr(sym, j2->value, j2->pos));
|
||||||
|
@ -2370,11 +2398,10 @@ static void prim_catAttrs(EvalState & state, const Pos & pos, Value * * args, Va
|
||||||
Value * res[args[1]->listSize()];
|
Value * res[args[1]->listSize()];
|
||||||
unsigned int found = 0;
|
unsigned int found = 0;
|
||||||
|
|
||||||
for (unsigned int n = 0; n < args[1]->listSize(); ++n) {
|
for (auto v2 : args[1]->listItems()) {
|
||||||
Value & v2(*args[1]->listElems()[n]);
|
state.forceAttrs(*v2, pos);
|
||||||
state.forceAttrs(v2, pos);
|
Bindings::iterator i = v2->attrs->find(attrName);
|
||||||
Bindings::iterator i = v2.attrs->find(attrName);
|
if (i != v2->attrs->end())
|
||||||
if (i != v2.attrs->end())
|
|
||||||
res[found++] = i->value;
|
res[found++] = i->value;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -2649,8 +2676,8 @@ static void prim_elem(EvalState & state, const Pos & pos, Value * * args, Value
|
||||||
{
|
{
|
||||||
bool res = false;
|
bool res = false;
|
||||||
state.forceList(*args[1], pos);
|
state.forceList(*args[1], pos);
|
||||||
for (unsigned int n = 0; n < args[1]->listSize(); ++n)
|
for (auto elem : args[1]->listItems())
|
||||||
if (state.eqValues(*args[0], *args[1]->listElems()[n])) {
|
if (state.eqValues(*args[0], *elem)) {
|
||||||
res = true;
|
res = true;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
@ -2709,8 +2736,8 @@ static void prim_foldlStrict(EvalState & state, const Pos & pos, Value * * args,
|
||||||
if (args[2]->listSize()) {
|
if (args[2]->listSize()) {
|
||||||
Value * vCur = args[1];
|
Value * vCur = args[1];
|
||||||
|
|
||||||
for (unsigned int n = 0; n < args[2]->listSize(); ++n) {
|
for (auto [n, elem] : enumerate(args[2]->listItems())) {
|
||||||
Value * vs []{vCur, args[2]->listElems()[n]};
|
Value * vs []{vCur, elem};
|
||||||
vCur = n == args[2]->listSize() - 1 ? &v : state.allocValue();
|
vCur = n == args[2]->listSize() - 1 ? &v : state.allocValue();
|
||||||
state.callFunction(*args[0], 2, vs, *vCur, pos);
|
state.callFunction(*args[0], 2, vs, *vCur, pos);
|
||||||
}
|
}
|
||||||
|
@ -2740,8 +2767,8 @@ static void anyOrAll(bool any, EvalState & state, const Pos & pos, Value * * arg
|
||||||
state.forceList(*args[1], pos);
|
state.forceList(*args[1], pos);
|
||||||
|
|
||||||
Value vTmp;
|
Value vTmp;
|
||||||
for (unsigned int n = 0; n < args[1]->listSize(); ++n) {
|
for (auto elem : args[1]->listItems()) {
|
||||||
state.callFunction(*args[0], *args[1]->listElems()[n], vTmp, pos);
|
state.callFunction(*args[0], *elem, vTmp, pos);
|
||||||
bool res = state.forceBool(vTmp, pos);
|
bool res = state.forceBool(vTmp, pos);
|
||||||
if (res == any) {
|
if (res == any) {
|
||||||
mkBool(v, any);
|
mkBool(v, any);
|
||||||
|
@ -2932,6 +2959,56 @@ static RegisterPrimOp primop_partition({
|
||||||
.fun = prim_partition,
|
.fun = prim_partition,
|
||||||
});
|
});
|
||||||
|
|
||||||
|
static void prim_groupBy(EvalState & state, const Pos & pos, Value * * args, Value & v)
|
||||||
|
{
|
||||||
|
state.forceFunction(*args[0], pos);
|
||||||
|
state.forceList(*args[1], pos);
|
||||||
|
|
||||||
|
ValueVectorMap attrs;
|
||||||
|
|
||||||
|
for (auto vElem : args[1]->listItems()) {
|
||||||
|
Value res;
|
||||||
|
state.callFunction(*args[0], *vElem, res, pos);
|
||||||
|
string name = state.forceStringNoCtx(res, pos);
|
||||||
|
Symbol sym = state.symbols.create(name);
|
||||||
|
auto vector = attrs.try_emplace(sym, ValueVector()).first;
|
||||||
|
vector->second.push_back(vElem);
|
||||||
|
}
|
||||||
|
|
||||||
|
state.mkAttrs(v, attrs.size());
|
||||||
|
|
||||||
|
for (auto & i : attrs) {
|
||||||
|
Value * list = state.allocAttr(v, i.first);
|
||||||
|
auto size = i.second.size();
|
||||||
|
state.mkList(*list, size);
|
||||||
|
memcpy(list->listElems(), i.second.data(), sizeof(Value *) * size);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static RegisterPrimOp primop_groupBy({
|
||||||
|
.name = "__groupBy",
|
||||||
|
.args = {"f", "list"},
|
||||||
|
.doc = R"(
|
||||||
|
Groups elements of *list* together by the string returned from the
|
||||||
|
function *f* called on each element. It returns an attribute set
|
||||||
|
where each attribute value contains the elements of *list* that are
|
||||||
|
mapped to the same corresponding attribute name returned by *f*.
|
||||||
|
|
||||||
|
For example,
|
||||||
|
|
||||||
|
```nix
|
||||||
|
builtins.groupBy (builtins.substring 0 1) ["foo" "bar" "baz"]
|
||||||
|
```
|
||||||
|
|
||||||
|
evaluates to
|
||||||
|
|
||||||
|
```nix
|
||||||
|
{ b = [ "bar" "baz" ]; f = [ "foo" ]; }
|
||||||
|
```
|
||||||
|
)",
|
||||||
|
.fun = prim_groupBy,
|
||||||
|
});
|
||||||
|
|
||||||
static void prim_concatMap(EvalState & state, const Pos & pos, Value * * args, Value & v)
|
static void prim_concatMap(EvalState & state, const Pos & pos, Value * * args, Value & v)
|
||||||
{
|
{
|
||||||
state.forceFunction(*args[0], pos);
|
state.forceFunction(*args[0], pos);
|
||||||
|
@ -3470,9 +3547,9 @@ static void prim_concatStringsSep(EvalState & state, const Pos & pos, Value * *
|
||||||
res.reserve((args[1]->listSize() + 32) * sep.size());
|
res.reserve((args[1]->listSize() + 32) * sep.size());
|
||||||
bool first = true;
|
bool first = true;
|
||||||
|
|
||||||
for (unsigned int n = 0; n < args[1]->listSize(); ++n) {
|
for (auto elem : args[1]->listItems()) {
|
||||||
if (first) first = false; else res += sep;
|
if (first) first = false; else res += sep;
|
||||||
res += state.coerceToString(pos, *args[1]->listElems()[n], context);
|
res += state.coerceToString(pos, *elem, context);
|
||||||
}
|
}
|
||||||
|
|
||||||
mkString(v, res, context);
|
mkString(v, res, context);
|
||||||
|
@ -3501,14 +3578,14 @@ static void prim_replaceStrings(EvalState & state, const Pos & pos, Value * * ar
|
||||||
|
|
||||||
vector<string> from;
|
vector<string> from;
|
||||||
from.reserve(args[0]->listSize());
|
from.reserve(args[0]->listSize());
|
||||||
for (unsigned int n = 0; n < args[0]->listSize(); ++n)
|
for (auto elem : args[0]->listItems())
|
||||||
from.push_back(state.forceString(*args[0]->listElems()[n], pos));
|
from.push_back(state.forceString(*elem, pos));
|
||||||
|
|
||||||
vector<std::pair<string, PathSet>> to;
|
vector<std::pair<string, PathSet>> to;
|
||||||
to.reserve(args[1]->listSize());
|
to.reserve(args[1]->listSize());
|
||||||
for (unsigned int n = 0; n < args[1]->listSize(); ++n) {
|
for (auto elem : args[1]->listItems()) {
|
||||||
PathSet ctx;
|
PathSet ctx;
|
||||||
auto s = state.forceString(*args[1]->listElems()[n], ctx, pos);
|
auto s = state.forceString(*elem, ctx, pos);
|
||||||
to.push_back(std::make_pair(std::move(s), std::move(ctx)));
|
to.push_back(std::make_pair(std::move(s), std::move(ctx)));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -3736,7 +3813,7 @@ void EvalState::createBaseEnv()
|
||||||
.fun = primOp.fun,
|
.fun = primOp.fun,
|
||||||
.arity = std::max(primOp.args.size(), primOp.arity),
|
.arity = std::max(primOp.args.size(), primOp.arity),
|
||||||
.name = symbols.create(primOp.name),
|
.name = symbols.create(primOp.name),
|
||||||
.args = std::move(primOp.args),
|
.args = primOp.args,
|
||||||
.doc = primOp.doc,
|
.doc = primOp.doc,
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
|
@ -118,10 +118,9 @@ static void prim_getContext(EvalState & state, const Pos & pos, Value * * args,
|
||||||
auto & outputsVal = *state.allocAttr(infoVal, state.sOutputs);
|
auto & outputsVal = *state.allocAttr(infoVal, state.sOutputs);
|
||||||
state.mkList(outputsVal, info.second.outputs.size());
|
state.mkList(outputsVal, info.second.outputs.size());
|
||||||
size_t i = 0;
|
size_t i = 0;
|
||||||
for (const auto & output : info.second.outputs) {
|
for (const auto & output : info.second.outputs)
|
||||||
mkString(*(outputsVal.listElems()[i++] = state.allocValue()), output);
|
mkString(*(outputsVal.listElems()[i++] = state.allocValue()), output);
|
||||||
}
|
}
|
||||||
}
|
|
||||||
infoVal.attrs->sort();
|
infoVal.attrs->sort();
|
||||||
}
|
}
|
||||||
v.attrs->sort();
|
v.attrs->sort();
|
||||||
|
@ -181,8 +180,8 @@ static void prim_appendContext(EvalState & state, const Pos & pos, Value * * arg
|
||||||
.errPos = *i.pos
|
.errPos = *i.pos
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
for (unsigned int n = 0; n < iter->value->listSize(); ++n) {
|
for (auto elem : iter->value->listItems()) {
|
||||||
auto name = state.forceStringNoCtx(*iter->value->listElems()[n], *iter->pos);
|
auto name = state.forceStringNoCtx(*elem, *iter->pos);
|
||||||
context.insert("!" + name + "!" + string(i.name));
|
context.insert("!" + name + "!" + string(i.name));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,86 +1,79 @@
|
||||||
#include "primops.hh"
|
#include "primops.hh"
|
||||||
#include "eval-inline.hh"
|
#include "eval-inline.hh"
|
||||||
|
|
||||||
#include "../../cpptoml/cpptoml.h"
|
#include "../../toml11/toml.hpp"
|
||||||
|
|
||||||
namespace nix {
|
namespace nix {
|
||||||
|
|
||||||
static void prim_fromTOML(EvalState & state, const Pos & pos, Value * * args, Value & v)
|
static void prim_fromTOML(EvalState & state, const Pos & pos, Value * * args, Value & val)
|
||||||
{
|
{
|
||||||
using namespace cpptoml;
|
|
||||||
|
|
||||||
auto toml = state.forceStringNoCtx(*args[0], pos);
|
auto toml = state.forceStringNoCtx(*args[0], pos);
|
||||||
|
|
||||||
std::istringstream tomlStream(toml);
|
std::istringstream tomlStream(toml);
|
||||||
|
|
||||||
std::function<void(Value &, std::shared_ptr<base>)> visit;
|
std::function<void(Value &, toml::value)> visit;
|
||||||
|
|
||||||
visit = [&](Value & v, std::shared_ptr<base> t) {
|
visit = [&](Value & v, toml::value t) {
|
||||||
|
|
||||||
if (auto t2 = t->as_table()) {
|
switch(t.type())
|
||||||
|
{
|
||||||
|
case toml::value_t::table:
|
||||||
|
{
|
||||||
|
auto table = toml::get<toml::table>(t);
|
||||||
|
|
||||||
size_t size = 0;
|
size_t size = 0;
|
||||||
for (auto & i : *t2) { (void) i; size++; }
|
for (auto & i : table) { (void) i; size++; }
|
||||||
|
|
||||||
state.mkAttrs(v, size);
|
state.mkAttrs(v, size);
|
||||||
|
|
||||||
for (auto & i : *t2) {
|
for(auto & elem: table) {
|
||||||
auto & v2 = *state.allocAttr(v, state.symbols.create(i.first));
|
|
||||||
|
|
||||||
if (auto i2 = i.second->as_table_array()) {
|
auto & v2 = *state.allocAttr(v, state.symbols.create(elem.first));
|
||||||
size_t size2 = i2->get().size();
|
visit(v2, elem.second);
|
||||||
state.mkList(v2, size2);
|
|
||||||
for (size_t j = 0; j < size2; ++j)
|
|
||||||
visit(*(v2.listElems()[j] = state.allocValue()), i2->get()[j]);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
visit(v2, i.second);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
v.attrs->sort();
|
v.attrs->sort();
|
||||||
}
|
}
|
||||||
|
break;;
|
||||||
|
case toml::value_t::array:
|
||||||
|
{
|
||||||
|
auto array = toml::get<std::vector<toml::value>>(t);
|
||||||
|
|
||||||
else if (auto t2 = t->as_array()) {
|
size_t size = array.size();
|
||||||
size_t size = t2->get().size();
|
|
||||||
|
|
||||||
state.mkList(v, size);
|
state.mkList(v, size);
|
||||||
|
|
||||||
for (size_t i = 0; i < size; ++i)
|
for (size_t i = 0; i < size; ++i)
|
||||||
visit(*(v.listElems()[i] = state.allocValue()), t2->get()[i]);
|
visit(*(v.listElems()[i] = state.allocValue()), array[i]);
|
||||||
}
|
}
|
||||||
|
break;;
|
||||||
|
case toml::value_t::boolean:
|
||||||
|
mkBool(v, toml::get<bool>(t));
|
||||||
|
break;;
|
||||||
|
case toml::value_t::integer:
|
||||||
|
mkInt(v, toml::get<int64_t>(t));
|
||||||
|
break;;
|
||||||
|
case toml::value_t::floating:
|
||||||
|
mkFloat(v, toml::get<NixFloat>(t));
|
||||||
|
break;;
|
||||||
|
case toml::value_t::string:
|
||||||
|
mkString(v, toml::get<std::string>(t));
|
||||||
|
break;;
|
||||||
|
case toml::value_t::local_datetime:
|
||||||
|
case toml::value_t::offset_datetime:
|
||||||
|
case toml::value_t::local_date:
|
||||||
|
case toml::value_t::local_time:
|
||||||
|
// We fail since Nix doesn't have date and time types
|
||||||
|
throw std::runtime_error("Dates and times are not supported");
|
||||||
|
break;;
|
||||||
|
case toml::value_t::empty:
|
||||||
|
mkNull(v);
|
||||||
|
break;;
|
||||||
|
|
||||||
// Handle cases like 'a = [[{ a = true }]]', which IMHO should be
|
|
||||||
// parsed as a array containing an array containing a table,
|
|
||||||
// but instead are parsed as an array containing a table array
|
|
||||||
// containing a table.
|
|
||||||
else if (auto t2 = t->as_table_array()) {
|
|
||||||
size_t size = t2->get().size();
|
|
||||||
|
|
||||||
state.mkList(v, size);
|
|
||||||
|
|
||||||
for (size_t j = 0; j < size; ++j)
|
|
||||||
visit(*(v.listElems()[j] = state.allocValue()), t2->get()[j]);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
else if (t->is_value()) {
|
|
||||||
if (auto val = t->as<int64_t>())
|
|
||||||
mkInt(v, val->get());
|
|
||||||
else if (auto val = t->as<NixFloat>())
|
|
||||||
mkFloat(v, val->get());
|
|
||||||
else if (auto val = t->as<bool>())
|
|
||||||
mkBool(v, val->get());
|
|
||||||
else if (auto val = t->as<std::string>())
|
|
||||||
mkString(v, val->get());
|
|
||||||
else
|
|
||||||
throw EvalError("unsupported value type in TOML");
|
|
||||||
}
|
|
||||||
|
|
||||||
else abort();
|
|
||||||
};
|
};
|
||||||
|
|
||||||
try {
|
try {
|
||||||
visit(v, parser(tomlStream).parse());
|
visit(val, toml::parse(tomlStream, "fromTOML" /* the "filename" */));
|
||||||
} catch (std::runtime_error & e) {
|
} catch (std::exception & e) { // TODO: toml::syntax_error
|
||||||
throw EvalError({
|
throw EvalError({
|
||||||
.msg = hintfmt("while parsing a TOML string: %s", e.what()),
|
.msg = hintfmt("while parsing a TOML string: %s", e.what()),
|
||||||
.errPos = pos
|
.errPos = pos
|
||||||
|
|
|
@ -63,9 +63,9 @@ void printValueAsJSON(EvalState & state, bool strict,
|
||||||
|
|
||||||
case nList: {
|
case nList: {
|
||||||
auto list(out.list());
|
auto list(out.list());
|
||||||
for (unsigned int n = 0; n < v.listSize(); ++n) {
|
for (auto elem : v.listItems()) {
|
||||||
auto placeholder(list.placeholder());
|
auto placeholder(list.placeholder());
|
||||||
printValueAsJSON(state, strict, *v.listElems()[n], pos, placeholder, context);
|
printValueAsJSON(state, strict, *elem, pos, placeholder, context);
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
|
@ -122,8 +122,8 @@ static void printValueAsXML(EvalState & state, bool strict, bool location,
|
||||||
|
|
||||||
case nList: {
|
case nList: {
|
||||||
XMLOpenElement _(doc, "list");
|
XMLOpenElement _(doc, "list");
|
||||||
for (unsigned int n = 0; n < v.listSize(); ++n)
|
for (auto v2 : v.listItems())
|
||||||
printValueAsXML(state, strict, location, *v.listElems()[n], doc, context, drvsSeen, pos);
|
printValueAsXML(state, strict, location, *v2, doc, context, drvsSeen, pos);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1,5 +1,7 @@
|
||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
|
#include <cassert>
|
||||||
|
|
||||||
#include "symbol-table.hh"
|
#include "symbol-table.hh"
|
||||||
|
|
||||||
#if HAVE_BOEHMGC
|
#if HAVE_BOEHMGC
|
||||||
|
@ -350,6 +352,34 @@ public:
|
||||||
bool isTrivial() const;
|
bool isTrivial() const;
|
||||||
|
|
||||||
std::vector<std::pair<Path, std::string>> getContext();
|
std::vector<std::pair<Path, std::string>> getContext();
|
||||||
|
|
||||||
|
auto listItems()
|
||||||
|
{
|
||||||
|
struct ListIterable
|
||||||
|
{
|
||||||
|
typedef Value * const * iterator;
|
||||||
|
iterator _begin, _end;
|
||||||
|
iterator begin() const { return _begin; }
|
||||||
|
iterator end() const { return _end; }
|
||||||
|
};
|
||||||
|
assert(isList());
|
||||||
|
auto begin = listElems();
|
||||||
|
return ListIterable { begin, begin + listSize() };
|
||||||
|
}
|
||||||
|
|
||||||
|
auto listItems() const
|
||||||
|
{
|
||||||
|
struct ConstListIterable
|
||||||
|
{
|
||||||
|
typedef const Value * const * iterator;
|
||||||
|
iterator _begin, _end;
|
||||||
|
iterator begin() const { return _begin; }
|
||||||
|
iterator end() const { return _end; }
|
||||||
|
};
|
||||||
|
assert(isList());
|
||||||
|
auto begin = listElems();
|
||||||
|
return ConstListIterable { begin, begin + listSize() };
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
|
@ -395,9 +425,11 @@ void mkPath(Value & v, const char * s);
|
||||||
#if HAVE_BOEHMGC
|
#if HAVE_BOEHMGC
|
||||||
typedef std::vector<Value *, traceable_allocator<Value *> > ValueVector;
|
typedef std::vector<Value *, traceable_allocator<Value *> > ValueVector;
|
||||||
typedef std::map<Symbol, Value *, std::less<Symbol>, traceable_allocator<std::pair<const Symbol, Value *> > > ValueMap;
|
typedef std::map<Symbol, Value *, std::less<Symbol>, traceable_allocator<std::pair<const Symbol, Value *> > > ValueMap;
|
||||||
|
typedef std::map<Symbol, ValueVector, std::less<Symbol>, traceable_allocator<std::pair<const Symbol, ValueVector> > > ValueVectorMap;
|
||||||
#else
|
#else
|
||||||
typedef std::vector<Value *> ValueVector;
|
typedef std::vector<Value *> ValueVector;
|
||||||
typedef std::map<Symbol, Value *> ValueMap;
|
typedef std::map<Symbol, Value *> ValueMap;
|
||||||
|
typedef std::map<Symbol, ValueVector> ValueVectorMap;
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -97,7 +97,7 @@ struct PathInputScheme : InputScheme
|
||||||
// for security, ensure that if the parent is a store path, it's inside it
|
// for security, ensure that if the parent is a store path, it's inside it
|
||||||
if (store->isInStore(parent)) {
|
if (store->isInStore(parent)) {
|
||||||
auto storePath = store->printStorePath(store->toStorePath(parent).first);
|
auto storePath = store->printStorePath(store->toStorePath(parent).first);
|
||||||
if (!isInDir(absPath, storePath))
|
if (!isDirOrInDir(absPath, storePath))
|
||||||
throw BadStorePath("relative path '%s' points outside of its parent's store path '%s'", path, storePath);
|
throw BadStorePath("relative path '%s' points outside of its parent's store path '%s'", path, storePath);
|
||||||
}
|
}
|
||||||
} else
|
} else
|
||||||
|
|
|
@ -176,6 +176,7 @@ struct TarballInputScheme : InputScheme
|
||||||
|
|
||||||
if (!hasSuffix(url.path, ".zip")
|
if (!hasSuffix(url.path, ".zip")
|
||||||
&& !hasSuffix(url.path, ".tar")
|
&& !hasSuffix(url.path, ".tar")
|
||||||
|
&& !hasSuffix(url.path, ".tgz")
|
||||||
&& !hasSuffix(url.path, ".tar.gz")
|
&& !hasSuffix(url.path, ".tar.gz")
|
||||||
&& !hasSuffix(url.path, ".tar.xz")
|
&& !hasSuffix(url.path, ".tar.xz")
|
||||||
&& !hasSuffix(url.path, ".tar.bz2")
|
&& !hasSuffix(url.path, ".tar.bz2")
|
||||||
|
|
|
@ -11,7 +11,7 @@
|
||||||
|
|
||||||
namespace nix {
|
namespace nix {
|
||||||
|
|
||||||
static std::string getS(const std::vector<Logger::Field> & fields, size_t n)
|
static std::string_view getS(const std::vector<Logger::Field> & fields, size_t n)
|
||||||
{
|
{
|
||||||
assert(n < fields.size());
|
assert(n < fields.size());
|
||||||
assert(fields[n].type == Logger::Field::tString);
|
assert(fields[n].type == Logger::Field::tString);
|
||||||
|
|
|
@ -15,9 +15,14 @@
|
||||||
#include <sys/stat.h>
|
#include <sys/stat.h>
|
||||||
#include <unistd.h>
|
#include <unistd.h>
|
||||||
#include <signal.h>
|
#include <signal.h>
|
||||||
#include <sys/types.h>
|
#ifdef __linux__
|
||||||
#include <sys/socket.h>
|
#include <features.h>
|
||||||
#include <netdb.h>
|
#endif
|
||||||
|
#ifdef __GLIBC__
|
||||||
|
#include <gnu/lib-names.h>
|
||||||
|
#include <nss.h>
|
||||||
|
#include <dlfcn.h>
|
||||||
|
#endif
|
||||||
|
|
||||||
#include <openssl/crypto.h>
|
#include <openssl/crypto.h>
|
||||||
|
|
||||||
|
@ -121,21 +126,30 @@ static void preloadNSS() {
|
||||||
been loaded in the parent. So we force a lookup of an invalid domain to force the NSS machinery to
|
been loaded in the parent. So we force a lookup of an invalid domain to force the NSS machinery to
|
||||||
load its lookup libraries in the parent before any child gets a chance to. */
|
load its lookup libraries in the parent before any child gets a chance to. */
|
||||||
std::call_once(dns_resolve_flag, []() {
|
std::call_once(dns_resolve_flag, []() {
|
||||||
struct addrinfo *res = NULL;
|
#ifdef __GLIBC__
|
||||||
|
/* On linux, glibc will run every lookup through the nss layer.
|
||||||
/* nss will only force the "local" (not through nscd) dns resolution if its on the LOCALDOMAIN.
|
* That means every lookup goes, by default, through nscd, which acts as a local
|
||||||
We need the resolution to be done locally, as nscd socket will not be accessible in the
|
* cache.
|
||||||
sandbox. */
|
* Because we run builds in a sandbox, we also remove access to nscd otherwise
|
||||||
char * previous_env = getenv("LOCALDOMAIN");
|
* lookups would leak into the sandbox.
|
||||||
setenv("LOCALDOMAIN", "invalid", 1);
|
*
|
||||||
if (getaddrinfo("this.pre-initializes.the.dns.resolvers.invalid.", "http", NULL, &res) == 0) {
|
* But now we have a new problem, we need to make sure the nss_dns backend that
|
||||||
if (res) freeaddrinfo(res);
|
* does the dns lookups when nscd is not available is loaded or available.
|
||||||
}
|
*
|
||||||
if (previous_env) {
|
* We can't make it available without leaking nix's environment, so instead we'll
|
||||||
setenv("LOCALDOMAIN", previous_env, 1);
|
* load the backend, and configure nss so it does not try to run dns lookups
|
||||||
} else {
|
* through nscd.
|
||||||
unsetenv("LOCALDOMAIN");
|
*
|
||||||
}
|
* This is technically only used for builtins:fetch* functions so we only care
|
||||||
|
* about dns.
|
||||||
|
*
|
||||||
|
* All other platforms are unaffected.
|
||||||
|
*/
|
||||||
|
if (!dlopen(LIBNSS_DNS_SO, RTLD_NOW))
|
||||||
|
warn("unable to load nss_dns backend");
|
||||||
|
// FIXME: get hosts entry from nsswitch.conf.
|
||||||
|
__nss_configure_lookup("hosts", "files dns");
|
||||||
|
#endif
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -413,7 +427,7 @@ RunPager::RunPager()
|
||||||
});
|
});
|
||||||
|
|
||||||
pid.setKillSignal(SIGINT);
|
pid.setKillSignal(SIGINT);
|
||||||
|
stdout = fcntl(STDOUT_FILENO, F_DUPFD_CLOEXEC, 0);
|
||||||
if (dup2(toPager.writeSide.get(), STDOUT_FILENO) == -1)
|
if (dup2(toPager.writeSide.get(), STDOUT_FILENO) == -1)
|
||||||
throw SysError("dupping stdout");
|
throw SysError("dupping stdout");
|
||||||
}
|
}
|
||||||
|
@ -424,7 +438,7 @@ RunPager::~RunPager()
|
||||||
try {
|
try {
|
||||||
if (pid != -1) {
|
if (pid != -1) {
|
||||||
std::cout.flush();
|
std::cout.flush();
|
||||||
close(STDOUT_FILENO);
|
dup2(stdout, STDOUT_FILENO);
|
||||||
pid.wait();
|
pid.wait();
|
||||||
}
|
}
|
||||||
} catch (...) {
|
} catch (...) {
|
||||||
|
|
|
@ -88,6 +88,7 @@ public:
|
||||||
|
|
||||||
private:
|
private:
|
||||||
Pid pid;
|
Pid pid;
|
||||||
|
int stdout;
|
||||||
};
|
};
|
||||||
|
|
||||||
extern volatile ::sig_atomic_t blockInt;
|
extern volatile ::sig_atomic_t blockInt;
|
||||||
|
|
|
@ -17,6 +17,7 @@
|
||||||
#include <regex>
|
#include <regex>
|
||||||
#include <queue>
|
#include <queue>
|
||||||
|
|
||||||
|
#include <fstream>
|
||||||
#include <sys/types.h>
|
#include <sys/types.h>
|
||||||
#include <sys/socket.h>
|
#include <sys/socket.h>
|
||||||
#include <sys/un.h>
|
#include <sys/un.h>
|
||||||
|
@ -464,7 +465,6 @@ void DerivationGoal::inputsRealised()
|
||||||
Derivation drvResolved { *std::move(attempt) };
|
Derivation drvResolved { *std::move(attempt) };
|
||||||
|
|
||||||
auto pathResolved = writeDerivation(worker.store, drvResolved);
|
auto pathResolved = writeDerivation(worker.store, drvResolved);
|
||||||
resolvedDrv = drvResolved;
|
|
||||||
|
|
||||||
auto msg = fmt("Resolved derivation: '%s' -> '%s'",
|
auto msg = fmt("Resolved derivation: '%s' -> '%s'",
|
||||||
worker.store.printStorePath(drvPath),
|
worker.store.printStorePath(drvPath),
|
||||||
|
@ -475,9 +475,9 @@ void DerivationGoal::inputsRealised()
|
||||||
worker.store.printStorePath(pathResolved),
|
worker.store.printStorePath(pathResolved),
|
||||||
});
|
});
|
||||||
|
|
||||||
auto resolvedGoal = worker.makeDerivationGoal(
|
resolvedDrvGoal = worker.makeDerivationGoal(
|
||||||
pathResolved, wantedOutputs, buildMode);
|
pathResolved, wantedOutputs, buildMode);
|
||||||
addWaitee(resolvedGoal);
|
addWaitee(resolvedDrvGoal);
|
||||||
|
|
||||||
state = &DerivationGoal::resolvedFinished;
|
state = &DerivationGoal::resolvedFinished;
|
||||||
return;
|
return;
|
||||||
|
@ -655,7 +655,7 @@ void DerivationGoal::tryLocalBuild() {
|
||||||
throw Error(
|
throw Error(
|
||||||
"unable to build with a primary store that isn't a local store; "
|
"unable to build with a primary store that isn't a local store; "
|
||||||
"either pass a different '--store' or enable remote builds."
|
"either pass a different '--store' or enable remote builds."
|
||||||
"\nhttps://nixos.org/nix/manual/#chap-distributed-builds");
|
"\nhttps://nixos.org/manual/nix/stable/advanced-topics/distributed-builds.html");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@ -938,16 +938,17 @@ void DerivationGoal::buildDone()
|
||||||
}
|
}
|
||||||
|
|
||||||
void DerivationGoal::resolvedFinished() {
|
void DerivationGoal::resolvedFinished() {
|
||||||
assert(resolvedDrv);
|
assert(resolvedDrvGoal);
|
||||||
|
auto resolvedDrv = *resolvedDrvGoal->drv;
|
||||||
|
|
||||||
auto resolvedHashes = staticOutputHashes(worker.store, *resolvedDrv);
|
auto resolvedHashes = staticOutputHashes(worker.store, resolvedDrv);
|
||||||
|
|
||||||
StorePathSet outputPaths;
|
StorePathSet outputPaths;
|
||||||
|
|
||||||
// `wantedOutputs` might be empty, which means “all the outputs”
|
// `wantedOutputs` might be empty, which means “all the outputs”
|
||||||
auto realWantedOutputs = wantedOutputs;
|
auto realWantedOutputs = wantedOutputs;
|
||||||
if (realWantedOutputs.empty())
|
if (realWantedOutputs.empty())
|
||||||
realWantedOutputs = resolvedDrv->outputNames();
|
realWantedOutputs = resolvedDrv.outputNames();
|
||||||
|
|
||||||
for (auto & wantedOutput : realWantedOutputs) {
|
for (auto & wantedOutput : realWantedOutputs) {
|
||||||
assert(initialOutputs.count(wantedOutput) != 0);
|
assert(initialOutputs.count(wantedOutput) != 0);
|
||||||
|
@ -979,9 +980,17 @@ void DerivationGoal::resolvedFinished() {
|
||||||
outputPaths
|
outputPaths
|
||||||
);
|
);
|
||||||
|
|
||||||
// This is potentially a bit fishy in terms of error reporting. Not sure
|
auto status = [&]() {
|
||||||
// how to do it in a cleaner way
|
auto resolvedResult = resolvedDrvGoal->getResult();
|
||||||
amDone(nrFailed == 0 ? ecSuccess : ecFailed, ex);
|
switch (resolvedResult.status) {
|
||||||
|
case BuildResult::AlreadyValid:
|
||||||
|
return BuildResult::ResolvesToAlreadyValid;
|
||||||
|
default:
|
||||||
|
return resolvedResult.status;
|
||||||
|
}
|
||||||
|
}();
|
||||||
|
|
||||||
|
done(status);
|
||||||
}
|
}
|
||||||
|
|
||||||
HookReply DerivationGoal::tryBuildHook()
|
HookReply DerivationGoal::tryBuildHook()
|
||||||
|
@ -1329,6 +1338,13 @@ void DerivationGoal::done(BuildResult::Status status, std::optional<Error> ex)
|
||||||
}
|
}
|
||||||
|
|
||||||
worker.updateProgress();
|
worker.updateProgress();
|
||||||
|
|
||||||
|
auto traceBuiltOutputsFile = getEnv("_NIX_TRACE_BUILT_OUTPUTS").value_or("");
|
||||||
|
if (traceBuiltOutputsFile != "") {
|
||||||
|
std::fstream fs;
|
||||||
|
fs.open(traceBuiltOutputsFile, std::fstream::out);
|
||||||
|
fs << worker.store.printStorePath(drvPath) << "\t" << result.toString() << std::endl;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -50,8 +50,8 @@ struct DerivationGoal : public Goal
|
||||||
/* The path of the derivation. */
|
/* The path of the derivation. */
|
||||||
StorePath drvPath;
|
StorePath drvPath;
|
||||||
|
|
||||||
/* The path of the corresponding resolved derivation */
|
/* The goal for the corresponding resolved derivation */
|
||||||
std::optional<BasicDerivation> resolvedDrv;
|
std::shared_ptr<DerivationGoal> resolvedDrvGoal;
|
||||||
|
|
||||||
/* The specific outputs that we need to build. Empty means all of
|
/* The specific outputs that we need to build. Empty means all of
|
||||||
them. */
|
them. */
|
||||||
|
|
|
@ -1779,11 +1779,14 @@ void LocalDerivationGoal::runChild()
|
||||||
i686-linux build on an x86_64-linux machine. */
|
i686-linux build on an x86_64-linux machine. */
|
||||||
struct utsname utsbuf;
|
struct utsname utsbuf;
|
||||||
uname(&utsbuf);
|
uname(&utsbuf);
|
||||||
if (drv->platform == "i686-linux" &&
|
if ((drv->platform == "i686-linux"
|
||||||
(settings.thisSystem == "x86_64-linux" ||
|
&& (settings.thisSystem == "x86_64-linux"
|
||||||
(!strcmp(utsbuf.sysname, "Linux") && !strcmp(utsbuf.machine, "x86_64")))) {
|
|| (!strcmp(utsbuf.sysname, "Linux") && !strcmp(utsbuf.machine, "x86_64"))))
|
||||||
|
|| drv->platform == "armv7l-linux"
|
||||||
|
|| drv->platform == "armv6l-linux")
|
||||||
|
{
|
||||||
if (personality(PER_LINUX32) == -1)
|
if (personality(PER_LINUX32) == -1)
|
||||||
throw SysError("cannot set i686-linux personality");
|
throw SysError("cannot set 32-bit personality");
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Impersonate a Linux 2.6 machine to get some determinism in
|
/* Impersonate a Linux 2.6 machine to get some determinism in
|
||||||
|
|
|
@ -281,11 +281,11 @@ void Worker::run(const Goals & _topGoals)
|
||||||
if (getMachines().empty())
|
if (getMachines().empty())
|
||||||
throw Error("unable to start any build; either increase '--max-jobs' "
|
throw Error("unable to start any build; either increase '--max-jobs' "
|
||||||
"or enable remote builds."
|
"or enable remote builds."
|
||||||
"\nhttps://nixos.org/nix/manual/#chap-distributed-builds");
|
"\nhttps://nixos.org/manual/nix/stable/advanced-topics/distributed-builds.html");
|
||||||
else
|
else
|
||||||
throw Error("unable to start any build; remote machines may not have "
|
throw Error("unable to start any build; remote machines may not have "
|
||||||
"all required system features."
|
"all required system features."
|
||||||
"\nhttps://nixos.org/nix/manual/#chap-distributed-builds");
|
"\nhttps://nixos.org/manual/nix/stable/advanced-topics/distributed-builds.html");
|
||||||
|
|
||||||
}
|
}
|
||||||
assert(!awake.empty());
|
assert(!awake.empty());
|
||||||
|
|
|
@ -19,3 +19,8 @@ create table if not exists RealisationsRefs (
|
||||||
foreign key (referrer) references Realisations(id) on delete cascade,
|
foreign key (referrer) references Realisations(id) on delete cascade,
|
||||||
foreign key (realisationReference) references Realisations(id) on delete restrict
|
foreign key (realisationReference) references Realisations(id) on delete restrict
|
||||||
);
|
);
|
||||||
|
|
||||||
|
-- used by QueryRealisationReferences
|
||||||
|
create index if not exists IndexRealisationsRefs on RealisationsRefs(referrer);
|
||||||
|
-- used by cascade deletion when ValidPaths is deleted
|
||||||
|
create index if not exists IndexRealisationsRefsOnOutputPath on Realisations(outputPath);
|
||||||
|
|
|
@ -4,7 +4,6 @@
|
||||||
#include "store-api.hh"
|
#include "store-api.hh"
|
||||||
#include "path-with-outputs.hh"
|
#include "path-with-outputs.hh"
|
||||||
#include "finally.hh"
|
#include "finally.hh"
|
||||||
#include "affinity.hh"
|
|
||||||
#include "archive.hh"
|
#include "archive.hh"
|
||||||
#include "derivations.hh"
|
#include "derivations.hh"
|
||||||
#include "args.hh"
|
#include "args.hh"
|
||||||
|
@ -431,25 +430,30 @@ static void performOp(TunnelLogger * logger, ref<Store> store,
|
||||||
hashAlgo = parseHashType(hashAlgoRaw);
|
hashAlgo = parseHashType(hashAlgoRaw);
|
||||||
}
|
}
|
||||||
|
|
||||||
StringSink saved;
|
auto dumpSource = sinkToSource([&](Sink & saved) {
|
||||||
TeeSource savedNARSource(from, saved);
|
|
||||||
RetrieveRegularNARSink savedRegular { saved };
|
|
||||||
|
|
||||||
if (method == FileIngestionMethod::Recursive) {
|
if (method == FileIngestionMethod::Recursive) {
|
||||||
/* Get the entire NAR dump from the client and save it to
|
/* We parse the NAR dump through into `saved` unmodified,
|
||||||
a string so that we can pass it to
|
so why all this extra work? We still parse the NAR so
|
||||||
addToStoreFromDump(). */
|
that we aren't sending arbitrary data to `saved`
|
||||||
|
unwittingly`, and we know when the NAR ends so we don't
|
||||||
|
consume the rest of `from` and can't parse another
|
||||||
|
command. (We don't trust `addToStoreFromDump` to not
|
||||||
|
eagerly consume the entire stream it's given, past the
|
||||||
|
length of the Nar. */
|
||||||
|
TeeSource savedNARSource(from, saved);
|
||||||
ParseSink sink; /* null sink; just parse the NAR */
|
ParseSink sink; /* null sink; just parse the NAR */
|
||||||
parseDump(sink, savedNARSource);
|
parseDump(sink, savedNARSource);
|
||||||
} else
|
} else {
|
||||||
|
/* Incrementally parse the NAR file, stripping the
|
||||||
|
metadata, and streaming the sole file we expect into
|
||||||
|
`saved`. */
|
||||||
|
RetrieveRegularNARSink savedRegular { saved };
|
||||||
parseDump(savedRegular, from);
|
parseDump(savedRegular, from);
|
||||||
|
|
||||||
logger->startWork();
|
|
||||||
if (!savedRegular.regular) throw Error("regular file expected");
|
if (!savedRegular.regular) throw Error("regular file expected");
|
||||||
|
}
|
||||||
// FIXME: try to stream directly from `from`.
|
});
|
||||||
StringSource dumpSource { *saved.s };
|
logger->startWork();
|
||||||
auto path = store->addToStoreFromDump(dumpSource, baseName, method, hashAlgo);
|
auto path = store->addToStoreFromDump(*dumpSource, baseName, method, hashAlgo);
|
||||||
logger->stopWork();
|
logger->stopWork();
|
||||||
|
|
||||||
to << store->printStorePath(path);
|
to << store->printStorePath(path);
|
||||||
|
@ -951,12 +955,12 @@ void processConnection(
|
||||||
|
|
||||||
Finally finally([&]() {
|
Finally finally([&]() {
|
||||||
_isInterrupted = false;
|
_isInterrupted = false;
|
||||||
prevLogger->log(lvlDebug, fmt("%d operations", opCount));
|
printMsgUsing(prevLogger, lvlDebug, "%d operations", opCount);
|
||||||
});
|
});
|
||||||
|
|
||||||
if (GET_PROTOCOL_MINOR(clientVersion) >= 14 && readInt(from)) {
|
if (GET_PROTOCOL_MINOR(clientVersion) >= 14 && readInt(from)) {
|
||||||
auto affinity = readInt(from);
|
// Obsolete CPU affinity.
|
||||||
setAffinityTo(affinity);
|
readInt(from);
|
||||||
}
|
}
|
||||||
|
|
||||||
readInt(from); // obsolete reserveSpace
|
readInt(from); // obsolete reserveSpace
|
||||||
|
@ -984,6 +988,8 @@ void processConnection(
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
printMsgUsing(prevLogger, lvlDebug, "received daemon op %d", op);
|
||||||
|
|
||||||
opCount++;
|
opCount++;
|
||||||
|
|
||||||
try {
|
try {
|
||||||
|
|
|
@ -544,13 +544,7 @@ struct curlFileTransfer : public FileTransfer
|
||||||
stopWorkerThread();
|
stopWorkerThread();
|
||||||
});
|
});
|
||||||
|
|
||||||
#ifdef __linux__
|
unshareFilesystem();
|
||||||
/* Cause this thread to not share any FS attributes with the main thread,
|
|
||||||
because this causes setns() in restoreMountNamespace() to fail.
|
|
||||||
Ideally, this would happen in the std::thread() constructor. */
|
|
||||||
if (unshare(CLONE_FS) != 0)
|
|
||||||
throw SysError("unsharing filesystem state in download thread");
|
|
||||||
#endif
|
|
||||||
|
|
||||||
std::map<CURL *, std::shared_ptr<TransferItem>> items;
|
std::map<CURL *, std::shared_ptr<TransferItem>> items;
|
||||||
|
|
||||||
|
|
|
@ -126,7 +126,17 @@ void LocalStore::addTempRoot(const StorePath & path)
|
||||||
auto socketPath = stateDir.get() + gcSocketPath;
|
auto socketPath = stateDir.get() + gcSocketPath;
|
||||||
debug("connecting to '%s'", socketPath);
|
debug("connecting to '%s'", socketPath);
|
||||||
state->fdRootsSocket = createUnixDomainSocket();
|
state->fdRootsSocket = createUnixDomainSocket();
|
||||||
|
try {
|
||||||
nix::connect(state->fdRootsSocket.get(), socketPath);
|
nix::connect(state->fdRootsSocket.get(), socketPath);
|
||||||
|
} catch (SysError & e) {
|
||||||
|
/* The garbage collector may have exited, so we need to
|
||||||
|
restart. */
|
||||||
|
if (e.errNo == ECONNREFUSED) {
|
||||||
|
debug("GC socket connection refused");
|
||||||
|
state->fdRootsSocket.close();
|
||||||
|
goto restart;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
try {
|
try {
|
||||||
|
@ -523,6 +533,8 @@ void LocalStore::collectGarbage(const GCOptions & options, GCResults & results)
|
||||||
AutoCloseFD fdClient = accept(fdServer.get(), nullptr, nullptr);
|
AutoCloseFD fdClient = accept(fdServer.get(), nullptr, nullptr);
|
||||||
if (!fdClient) continue;
|
if (!fdClient) continue;
|
||||||
|
|
||||||
|
debug("GC roots server accepted new client");
|
||||||
|
|
||||||
/* Process the connection in a separate thread. */
|
/* Process the connection in a separate thread. */
|
||||||
auto fdClient_ = fdClient.get();
|
auto fdClient_ = fdClient.get();
|
||||||
std::thread clientThread([&, fdClient = std::move(fdClient)]() {
|
std::thread clientThread([&, fdClient = std::move(fdClient)]() {
|
||||||
|
@ -535,6 +547,12 @@ void LocalStore::collectGarbage(const GCOptions & options, GCResults & results)
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
/* On macOS, accepted sockets inherit the
|
||||||
|
non-blocking flag from the server socket, so
|
||||||
|
explicitly make it blocking. */
|
||||||
|
if (fcntl(fdServer.get(), F_SETFL, fcntl(fdServer.get(), F_GETFL) & ~O_NONBLOCK) == -1)
|
||||||
|
abort();
|
||||||
|
|
||||||
while (true) {
|
while (true) {
|
||||||
try {
|
try {
|
||||||
auto path = readLine(fdClient.get());
|
auto path = readLine(fdClient.get());
|
||||||
|
@ -559,7 +577,10 @@ void LocalStore::collectGarbage(const GCOptions & options, GCResults & results)
|
||||||
} else
|
} else
|
||||||
printError("received garbage instead of a root from client");
|
printError("received garbage instead of a root from client");
|
||||||
writeFull(fdClient.get(), "1", false);
|
writeFull(fdClient.get(), "1", false);
|
||||||
} catch (Error &) { break; }
|
} catch (Error & e) {
|
||||||
|
debug("reading GC root from client: %s", e.msg());
|
||||||
|
break;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
|
@ -122,7 +122,7 @@ StringSet Settings::getDefaultSystemFeatures()
|
||||||
/* For backwards compatibility, accept some "features" that are
|
/* For backwards compatibility, accept some "features" that are
|
||||||
used in Nixpkgs to route builds to certain machines but don't
|
used in Nixpkgs to route builds to certain machines but don't
|
||||||
actually require anything special on the machines. */
|
actually require anything special on the machines. */
|
||||||
StringSet features{"nixos-test", "benchmark", "big-parallel", "recursive-nix"};
|
StringSet features{"nixos-test", "benchmark", "big-parallel"};
|
||||||
|
|
||||||
#if __linux__
|
#if __linux__
|
||||||
if (access("/dev/kvm", R_OK | W_OK) == 0)
|
if (access("/dev/kvm", R_OK | W_OK) == 0)
|
||||||
|
|
|
@ -21,7 +21,7 @@ struct MaxBuildJobsSetting : public BaseSetting<unsigned int>
|
||||||
const std::string & name,
|
const std::string & name,
|
||||||
const std::string & description,
|
const std::string & description,
|
||||||
const std::set<std::string> & aliases = {})
|
const std::set<std::string> & aliases = {})
|
||||||
: BaseSetting<unsigned int>(def, name, description, aliases)
|
: BaseSetting<unsigned int>(def, true, name, description, aliases)
|
||||||
{
|
{
|
||||||
options->addSetting(this);
|
options->addSetting(this);
|
||||||
}
|
}
|
||||||
|
@ -38,7 +38,7 @@ struct PluginFilesSetting : public BaseSetting<Paths>
|
||||||
const std::string & name,
|
const std::string & name,
|
||||||
const std::string & description,
|
const std::string & description,
|
||||||
const std::set<std::string> & aliases = {})
|
const std::set<std::string> & aliases = {})
|
||||||
: BaseSetting<Paths>(def, name, description, aliases)
|
: BaseSetting<Paths>(def, true, name, description, aliases)
|
||||||
{
|
{
|
||||||
options->addSetting(this);
|
options->addSetting(this);
|
||||||
}
|
}
|
||||||
|
@ -130,7 +130,9 @@ public:
|
||||||
{"build-max-jobs"}};
|
{"build-max-jobs"}};
|
||||||
|
|
||||||
Setting<unsigned int> buildCores{
|
Setting<unsigned int> buildCores{
|
||||||
this, getDefaultCores(), "cores",
|
this,
|
||||||
|
getDefaultCores(),
|
||||||
|
"cores",
|
||||||
R"(
|
R"(
|
||||||
Sets the value of the `NIX_BUILD_CORES` environment variable in the
|
Sets the value of the `NIX_BUILD_CORES` environment variable in the
|
||||||
invocation of builders. Builders can use this variable at their
|
invocation of builders. Builders can use this variable at their
|
||||||
|
@ -141,7 +143,7 @@ public:
|
||||||
command line switch and defaults to `1`. The value `0` means that
|
command line switch and defaults to `1`. The value `0` means that
|
||||||
the builder should use all available CPU cores in the system.
|
the builder should use all available CPU cores in the system.
|
||||||
)",
|
)",
|
||||||
{"build-cores"}};
|
{"build-cores"}, false};
|
||||||
|
|
||||||
/* Read-only mode. Don't copy stuff to the store, don't change
|
/* Read-only mode. Don't copy stuff to the store, don't change
|
||||||
the database. */
|
the database. */
|
||||||
|
@ -583,10 +585,11 @@ public:
|
||||||
platform and generate incompatible code, so you may wish to
|
platform and generate incompatible code, so you may wish to
|
||||||
cross-check the results of using this option against proper
|
cross-check the results of using this option against proper
|
||||||
natively-built versions of your derivations.
|
natively-built versions of your derivations.
|
||||||
)"};
|
)", {}, false};
|
||||||
|
|
||||||
Setting<StringSet> systemFeatures{
|
Setting<StringSet> systemFeatures{
|
||||||
this, getDefaultSystemFeatures(),
|
this,
|
||||||
|
getDefaultSystemFeatures(),
|
||||||
"system-features",
|
"system-features",
|
||||||
R"(
|
R"(
|
||||||
A set of system “features” supported by this machine, e.g. `kvm`.
|
A set of system “features” supported by this machine, e.g. `kvm`.
|
||||||
|
@ -602,7 +605,7 @@ public:
|
||||||
This setting by default includes `kvm` if `/dev/kvm` is accessible,
|
This setting by default includes `kvm` if `/dev/kvm` is accessible,
|
||||||
and the pseudo-features `nixos-test`, `benchmark` and `big-parallel`
|
and the pseudo-features `nixos-test`, `benchmark` and `big-parallel`
|
||||||
that are used in Nixpkgs to route builds to specific machines.
|
that are used in Nixpkgs to route builds to specific machines.
|
||||||
)"};
|
)", {}, false};
|
||||||
|
|
||||||
Setting<Strings> substituters{
|
Setting<Strings> substituters{
|
||||||
this,
|
this,
|
||||||
|
@ -797,6 +800,15 @@ public:
|
||||||
may be useful in certain scenarios (e.g. to spin up containers or
|
may be useful in certain scenarios (e.g. to spin up containers or
|
||||||
set up userspace network interfaces in tests).
|
set up userspace network interfaces in tests).
|
||||||
)"};
|
)"};
|
||||||
|
|
||||||
|
Setting<StringSet> ignoredAcls{
|
||||||
|
this, {"security.selinux", "system.nfs4_acl"}, "ignored-acls",
|
||||||
|
R"(
|
||||||
|
A list of ACLs that should be ignored, normally Nix attempts to
|
||||||
|
remove all ACLs from files and directories in the Nix store, but
|
||||||
|
some ACLs like `security.selinux` or `system.nfs4_acl` can't be
|
||||||
|
removed even by root. Therefore it's best to just ignore them.
|
||||||
|
)"};
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
Setting<Strings> hashedMirrors{
|
Setting<Strings> hashedMirrors{
|
||||||
|
|
|
@ -8,6 +8,7 @@
|
||||||
#include "references.hh"
|
#include "references.hh"
|
||||||
#include "callback.hh"
|
#include "callback.hh"
|
||||||
#include "topo-sort.hh"
|
#include "topo-sort.hh"
|
||||||
|
#include "finally.hh"
|
||||||
|
|
||||||
#include <iostream>
|
#include <iostream>
|
||||||
#include <algorithm>
|
#include <algorithm>
|
||||||
|
@ -79,7 +80,7 @@ int getSchema(Path schemaPath)
|
||||||
|
|
||||||
void migrateCASchema(SQLite& db, Path schemaPath, AutoCloseFD& lockFd)
|
void migrateCASchema(SQLite& db, Path schemaPath, AutoCloseFD& lockFd)
|
||||||
{
|
{
|
||||||
const int nixCASchemaVersion = 2;
|
const int nixCASchemaVersion = 3;
|
||||||
int curCASchema = getSchema(schemaPath);
|
int curCASchema = getSchema(schemaPath);
|
||||||
if (curCASchema != nixCASchemaVersion) {
|
if (curCASchema != nixCASchemaVersion) {
|
||||||
if (curCASchema > nixCASchemaVersion) {
|
if (curCASchema > nixCASchemaVersion) {
|
||||||
|
@ -130,6 +131,17 @@ void migrateCASchema(SQLite& db, Path schemaPath, AutoCloseFD& lockFd)
|
||||||
txn.commit();
|
txn.commit();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (curCASchema < 3) {
|
||||||
|
SQLiteTxn txn(db);
|
||||||
|
// Apply new indices added in this schema update.
|
||||||
|
db.exec(R"(
|
||||||
|
-- used by QueryRealisationReferences
|
||||||
|
create index if not exists IndexRealisationsRefs on RealisationsRefs(referrer);
|
||||||
|
-- used by cascade deletion when ValidPaths is deleted
|
||||||
|
create index if not exists IndexRealisationsRefsOnOutputPath on Realisations(outputPath);
|
||||||
|
)");
|
||||||
|
txn.commit();
|
||||||
|
}
|
||||||
writeFile(schemaPath, fmt("%d", nixCASchemaVersion));
|
writeFile(schemaPath, fmt("%d", nixCASchemaVersion));
|
||||||
lockFile(lockFd.get(), ltRead, true);
|
lockFile(lockFd.get(), ltRead, true);
|
||||||
}
|
}
|
||||||
|
@ -589,9 +601,7 @@ static void canonicalisePathMetaData_(const Path & path, uid_t fromUid, InodesSe
|
||||||
throw SysError("querying extended attributes of '%s'", path);
|
throw SysError("querying extended attributes of '%s'", path);
|
||||||
|
|
||||||
for (auto & eaName: tokenizeString<Strings>(std::string(eaBuf.data(), eaSize), std::string("\000", 1))) {
|
for (auto & eaName: tokenizeString<Strings>(std::string(eaBuf.data(), eaSize), std::string("\000", 1))) {
|
||||||
/* Ignore SELinux security labels since these cannot be
|
if (settings.ignoredAcls.get().count(eaName)) continue;
|
||||||
removed even by root. */
|
|
||||||
if (eaName == "security.selinux") continue;
|
|
||||||
if (lremovexattr(path.c_str(), eaName.c_str()) == -1)
|
if (lremovexattr(path.c_str(), eaName.c_str()) == -1)
|
||||||
throw SysError("removing extended attribute '%s' from '%s'", eaName, path);
|
throw SysError("removing extended attribute '%s' from '%s'", eaName, path);
|
||||||
}
|
}
|
||||||
|
@ -1333,13 +1343,15 @@ StorePath LocalStore::addToStoreFromDump(Source & source0, const string & name,
|
||||||
auto want = std::min(chunkSize, settings.narBufferSize - oldSize);
|
auto want = std::min(chunkSize, settings.narBufferSize - oldSize);
|
||||||
dump.resize(oldSize + want);
|
dump.resize(oldSize + want);
|
||||||
auto got = 0;
|
auto got = 0;
|
||||||
|
Finally cleanup([&]() {
|
||||||
|
dump.resize(oldSize + got);
|
||||||
|
});
|
||||||
try {
|
try {
|
||||||
got = source.read(dump.data() + oldSize, want);
|
got = source.read(dump.data() + oldSize, want);
|
||||||
} catch (EndOfFile &) {
|
} catch (EndOfFile &) {
|
||||||
inMemory = true;
|
inMemory = true;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
dump.resize(oldSize + got);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
std::unique_ptr<AutoDelete> delTempDir;
|
std::unique_ptr<AutoDelete> delTempDir;
|
||||||
|
|
|
@ -7,6 +7,7 @@
|
||||||
#include "topo-sort.hh"
|
#include "topo-sort.hh"
|
||||||
#include "callback.hh"
|
#include "callback.hh"
|
||||||
#include "closure.hh"
|
#include "closure.hh"
|
||||||
|
#include "filetransfer.hh"
|
||||||
|
|
||||||
namespace nix {
|
namespace nix {
|
||||||
|
|
||||||
|
@ -100,7 +101,8 @@ void Store::queryMissing(const std::vector<DerivedPath> & targets,
|
||||||
|
|
||||||
downloadSize_ = narSize_ = 0;
|
downloadSize_ = narSize_ = 0;
|
||||||
|
|
||||||
ThreadPool pool;
|
// FIXME: make async.
|
||||||
|
ThreadPool pool(fileTransferSettings.httpConnections);
|
||||||
|
|
||||||
struct State
|
struct State
|
||||||
{
|
{
|
||||||
|
|
|
@ -42,7 +42,7 @@ DrvName::~DrvName()
|
||||||
{ }
|
{ }
|
||||||
|
|
||||||
|
|
||||||
bool DrvName::matches(DrvName & n)
|
bool DrvName::matches(const DrvName & n)
|
||||||
{
|
{
|
||||||
if (name != "*") {
|
if (name != "*") {
|
||||||
if (!regex) {
|
if (!regex) {
|
||||||
|
|
|
@ -19,7 +19,7 @@ struct DrvName
|
||||||
DrvName(std::string_view s);
|
DrvName(std::string_view s);
|
||||||
~DrvName();
|
~DrvName();
|
||||||
|
|
||||||
bool matches(DrvName & n);
|
bool matches(const DrvName & n);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
std::unique_ptr<Regex> regex;
|
std::unique_ptr<Regex> regex;
|
||||||
|
|
|
@ -5,7 +5,6 @@
|
||||||
#include "remote-store.hh"
|
#include "remote-store.hh"
|
||||||
#include "worker-protocol.hh"
|
#include "worker-protocol.hh"
|
||||||
#include "archive.hh"
|
#include "archive.hh"
|
||||||
#include "affinity.hh"
|
|
||||||
#include "globals.hh"
|
#include "globals.hh"
|
||||||
#include "derivations.hh"
|
#include "derivations.hh"
|
||||||
#include "pool.hh"
|
#include "pool.hh"
|
||||||
|
@ -184,10 +183,7 @@ void RemoteStore::initConnection(Connection & conn)
|
||||||
conn.to << PROTOCOL_VERSION;
|
conn.to << PROTOCOL_VERSION;
|
||||||
|
|
||||||
if (GET_PROTOCOL_MINOR(conn.daemonVersion) >= 14) {
|
if (GET_PROTOCOL_MINOR(conn.daemonVersion) >= 14) {
|
||||||
int cpu = sameMachine() && settings.lockCPU ? lockToCurrentCPU() : -1;
|
// Obsolete CPU affinity.
|
||||||
if (cpu != -1)
|
|
||||||
conn.to << 1 << cpu;
|
|
||||||
else
|
|
||||||
conn.to << 0;
|
conn.to << 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -684,6 +680,14 @@ void RemoteStore::queryRealisationUncached(const DrvOutput & id,
|
||||||
Callback<std::shared_ptr<const Realisation>> callback) noexcept
|
Callback<std::shared_ptr<const Realisation>> callback) noexcept
|
||||||
{
|
{
|
||||||
auto conn(getConnection());
|
auto conn(getConnection());
|
||||||
|
|
||||||
|
if (GET_PROTOCOL_MINOR(conn->daemonVersion) < 27) {
|
||||||
|
warn("the daemon is too old to support content-addressed derivations, please upgrade it to 2.4");
|
||||||
|
try {
|
||||||
|
callback(nullptr);
|
||||||
|
} catch (...) { return callback.rethrow(); }
|
||||||
|
}
|
||||||
|
|
||||||
conn->to << wopQueryRealisation;
|
conn->to << wopQueryRealisation;
|
||||||
conn->to << id.to_string();
|
conn->to << id.to_string();
|
||||||
conn.processStderr();
|
conn.processStderr();
|
||||||
|
|
|
@ -355,8 +355,13 @@ ValidPathInfo Store::addToStoreSlow(std::string_view name, const Path & srcPath,
|
||||||
StringSet StoreConfig::getDefaultSystemFeatures()
|
StringSet StoreConfig::getDefaultSystemFeatures()
|
||||||
{
|
{
|
||||||
auto res = settings.systemFeatures.get();
|
auto res = settings.systemFeatures.get();
|
||||||
|
|
||||||
if (settings.isExperimentalFeatureEnabled(Xp::CaDerivations))
|
if (settings.isExperimentalFeatureEnabled(Xp::CaDerivations))
|
||||||
res.insert("ca-derivations");
|
res.insert("ca-derivations");
|
||||||
|
|
||||||
|
if (settings.isExperimentalFeatureEnabled(Xp::RecursiveNix))
|
||||||
|
res.insert("recursive-nix");
|
||||||
|
|
||||||
return res;
|
return res;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1074,7 +1079,7 @@ std::map<StorePath, StorePath> copyPaths(
|
||||||
nrFailed++;
|
nrFailed++;
|
||||||
if (!settings.keepGoing)
|
if (!settings.keepGoing)
|
||||||
throw e;
|
throw e;
|
||||||
logger->log(lvlError, fmt("could not copy %s: %s", dstStore.printStorePath(storePath), e.what()));
|
printMsg(lvlError, "could not copy %s: %s", dstStore.printStorePath(storePath), e.what());
|
||||||
showProgress();
|
showProgress();
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
|
@ -151,9 +151,33 @@ struct BuildResult
|
||||||
DependencyFailed,
|
DependencyFailed,
|
||||||
LogLimitExceeded,
|
LogLimitExceeded,
|
||||||
NotDeterministic,
|
NotDeterministic,
|
||||||
|
ResolvesToAlreadyValid,
|
||||||
} status = MiscFailure;
|
} status = MiscFailure;
|
||||||
std::string errorMsg;
|
std::string errorMsg;
|
||||||
|
|
||||||
|
std::string toString() const {
|
||||||
|
auto strStatus = [&]() {
|
||||||
|
switch (status) {
|
||||||
|
case Built: return "Built";
|
||||||
|
case Substituted: return "Substituted";
|
||||||
|
case AlreadyValid: return "AlreadyValid";
|
||||||
|
case PermanentFailure: return "PermanentFailure";
|
||||||
|
case InputRejected: return "InputRejected";
|
||||||
|
case OutputRejected: return "OutputRejected";
|
||||||
|
case TransientFailure: return "TransientFailure";
|
||||||
|
case CachedFailure: return "CachedFailure";
|
||||||
|
case TimedOut: return "TimedOut";
|
||||||
|
case MiscFailure: return "MiscFailure";
|
||||||
|
case DependencyFailed: return "DependencyFailed";
|
||||||
|
case LogLimitExceeded: return "LogLimitExceeded";
|
||||||
|
case NotDeterministic: return "NotDeterministic";
|
||||||
|
case ResolvesToAlreadyValid: return "ResolvesToAlreadyValid";
|
||||||
|
default: return "Unknown";
|
||||||
|
};
|
||||||
|
}();
|
||||||
|
return strStatus + ((errorMsg == "") ? "" : " : " + errorMsg);
|
||||||
|
}
|
||||||
|
|
||||||
/* How many times this build was performed. */
|
/* How many times this build was performed. */
|
||||||
unsigned int timesBuilt = 0;
|
unsigned int timesBuilt = 0;
|
||||||
|
|
||||||
|
@ -170,7 +194,7 @@ struct BuildResult
|
||||||
time_t startTime = 0, stopTime = 0;
|
time_t startTime = 0, stopTime = 0;
|
||||||
|
|
||||||
bool success() {
|
bool success() {
|
||||||
return status == Built || status == Substituted || status == AlreadyValid;
|
return status == Built || status == Substituted || status == AlreadyValid || status == ResolvesToAlreadyValid;
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
@ -10,6 +10,7 @@ std::map<std::string, nlohmann::json> BaseSetting<T>::toJSONObject()
|
||||||
auto obj = AbstractSetting::toJSONObject();
|
auto obj = AbstractSetting::toJSONObject();
|
||||||
obj.emplace("value", value);
|
obj.emplace("value", value);
|
||||||
obj.emplace("defaultValue", defaultValue);
|
obj.emplace("defaultValue", defaultValue);
|
||||||
|
obj.emplace("documentDefault", documentDefault);
|
||||||
return obj;
|
return obj;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,70 +0,0 @@
|
||||||
#include "types.hh"
|
|
||||||
#include "util.hh"
|
|
||||||
#include "affinity.hh"
|
|
||||||
|
|
||||||
#if __linux__
|
|
||||||
#include <sched.h>
|
|
||||||
#endif
|
|
||||||
|
|
||||||
namespace nix {
|
|
||||||
|
|
||||||
|
|
||||||
#if __linux__
|
|
||||||
static bool didSaveAffinity = false;
|
|
||||||
static cpu_set_t savedAffinity;
|
|
||||||
|
|
||||||
std::ostream& operator<<(std::ostream &os, const cpu_set_t &cset)
|
|
||||||
{
|
|
||||||
auto count = CPU_COUNT(&cset);
|
|
||||||
for (int i=0; i < count; ++i)
|
|
||||||
{
|
|
||||||
os << (CPU_ISSET(i,&cset) ? "1" : "0");
|
|
||||||
}
|
|
||||||
|
|
||||||
return os;
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
|
|
||||||
|
|
||||||
void setAffinityTo(int cpu)
|
|
||||||
{
|
|
||||||
#if __linux__
|
|
||||||
if (sched_getaffinity(0, sizeof(cpu_set_t), &savedAffinity) == -1) return;
|
|
||||||
didSaveAffinity = true;
|
|
||||||
debug(format("locking this thread to CPU %1%") % cpu);
|
|
||||||
cpu_set_t newAffinity;
|
|
||||||
CPU_ZERO(&newAffinity);
|
|
||||||
CPU_SET(cpu, &newAffinity);
|
|
||||||
if (sched_setaffinity(0, sizeof(cpu_set_t), &newAffinity) == -1)
|
|
||||||
printError("failed to lock thread to CPU %1%", cpu);
|
|
||||||
#endif
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
int lockToCurrentCPU()
|
|
||||||
{
|
|
||||||
#if __linux__
|
|
||||||
int cpu = sched_getcpu();
|
|
||||||
if (cpu != -1) setAffinityTo(cpu);
|
|
||||||
return cpu;
|
|
||||||
#else
|
|
||||||
return -1;
|
|
||||||
#endif
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
void restoreAffinity()
|
|
||||||
{
|
|
||||||
#if __linux__
|
|
||||||
if (!didSaveAffinity) return;
|
|
||||||
if (sched_setaffinity(0, sizeof(cpu_set_t), &savedAffinity) == -1)
|
|
||||||
{
|
|
||||||
std::ostringstream oss;
|
|
||||||
oss << savedAffinity;
|
|
||||||
printError("failed to restore CPU affinity %1%", oss.str());
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
}
|
|
|
@ -1,9 +0,0 @@
|
||||||
#pragma once
|
|
||||||
|
|
||||||
namespace nix {
|
|
||||||
|
|
||||||
void setAffinityTo(int cpu);
|
|
||||||
int lockToCurrentCPU();
|
|
||||||
void restoreAffinity();
|
|
||||||
|
|
||||||
}
|
|
|
@ -39,7 +39,7 @@ void Completions::add(std::string completion, std::string description)
|
||||||
bool Completion::operator<(const Completion & other) const
|
bool Completion::operator<(const Completion & other) const
|
||||||
{ return completion < other.completion || (completion == other.completion && description < other.description); }
|
{ return completion < other.completion || (completion == other.completion && description < other.description); }
|
||||||
|
|
||||||
bool pathCompletions = false;
|
CompletionType completionType = ctNormal;
|
||||||
std::shared_ptr<Completions> completions;
|
std::shared_ptr<Completions> completions;
|
||||||
|
|
||||||
std::string completionMarker = "___COMPLETE___";
|
std::string completionMarker = "___COMPLETE___";
|
||||||
|
@ -277,7 +277,7 @@ Args::Flag Args::Flag::mkHashTypeOptFlag(std::string && longName, std::optional<
|
||||||
|
|
||||||
static void _completePath(std::string_view prefix, bool onlyDirs)
|
static void _completePath(std::string_view prefix, bool onlyDirs)
|
||||||
{
|
{
|
||||||
pathCompletions = true;
|
completionType = ctFilenames;
|
||||||
glob_t globbuf;
|
glob_t globbuf;
|
||||||
int flags = GLOB_NOESCAPE | GLOB_TILDE;
|
int flags = GLOB_NOESCAPE | GLOB_TILDE;
|
||||||
#ifdef GLOB_ONLYDIR
|
#ifdef GLOB_ONLYDIR
|
||||||
|
|
|
@ -237,7 +237,13 @@ public:
|
||||||
void add(std::string completion, std::string description = "");
|
void add(std::string completion, std::string description = "");
|
||||||
};
|
};
|
||||||
extern std::shared_ptr<Completions> completions;
|
extern std::shared_ptr<Completions> completions;
|
||||||
extern bool pathCompletions;
|
|
||||||
|
enum CompletionType {
|
||||||
|
ctNormal,
|
||||||
|
ctFilenames,
|
||||||
|
ctAttrs
|
||||||
|
};
|
||||||
|
extern CompletionType completionType;
|
||||||
|
|
||||||
std::optional<std::string> needsCompletion(std::string_view s);
|
std::optional<std::string> needsCompletion(std::string_view s);
|
||||||
|
|
||||||
|
|
|
@ -232,16 +232,19 @@ protected:
|
||||||
|
|
||||||
T value;
|
T value;
|
||||||
const T defaultValue;
|
const T defaultValue;
|
||||||
|
const bool documentDefault;
|
||||||
|
|
||||||
public:
|
public:
|
||||||
|
|
||||||
BaseSetting(const T & def,
|
BaseSetting(const T & def,
|
||||||
|
const bool documentDefault,
|
||||||
const std::string & name,
|
const std::string & name,
|
||||||
const std::string & description,
|
const std::string & description,
|
||||||
const std::set<std::string> & aliases = {})
|
const std::set<std::string> & aliases = {})
|
||||||
: AbstractSetting(name, description, aliases)
|
: AbstractSetting(name, description, aliases)
|
||||||
, value(def)
|
, value(def)
|
||||||
, defaultValue(def)
|
, defaultValue(def)
|
||||||
|
, documentDefault(documentDefault)
|
||||||
{ }
|
{ }
|
||||||
|
|
||||||
operator const T &() const { return value; }
|
operator const T &() const { return value; }
|
||||||
|
@ -288,8 +291,9 @@ public:
|
||||||
const T & def,
|
const T & def,
|
||||||
const std::string & name,
|
const std::string & name,
|
||||||
const std::string & description,
|
const std::string & description,
|
||||||
const std::set<std::string> & aliases = {})
|
const std::set<std::string> & aliases = {},
|
||||||
: BaseSetting<T>(def, name, description, aliases)
|
const bool documentDefault = true)
|
||||||
|
: BaseSetting<T>(def, documentDefault, name, description, aliases)
|
||||||
{
|
{
|
||||||
options->addSetting(this);
|
options->addSetting(this);
|
||||||
}
|
}
|
||||||
|
@ -311,7 +315,7 @@ public:
|
||||||
const std::string & name,
|
const std::string & name,
|
||||||
const std::string & description,
|
const std::string & description,
|
||||||
const std::set<std::string> & aliases = {})
|
const std::set<std::string> & aliases = {})
|
||||||
: BaseSetting<Path>(def, name, description, aliases)
|
: BaseSetting<Path>(def, true, name, description, aliases)
|
||||||
, allowEmpty(allowEmpty)
|
, allowEmpty(allowEmpty)
|
||||||
{
|
{
|
||||||
options->addSetting(this);
|
options->addSetting(this);
|
||||||
|
|
|
@ -25,7 +25,7 @@ const string & BaseError::calcWhat() const
|
||||||
err.name = sname();
|
err.name = sname();
|
||||||
|
|
||||||
std::ostringstream oss;
|
std::ostringstream oss;
|
||||||
showErrorInfo(oss, err, false);
|
showErrorInfo(oss, err, loggerSettings.showTrace);
|
||||||
what_ = oss.str();
|
what_ = oss.str();
|
||||||
|
|
||||||
return *what_;
|
return *what_;
|
||||||
|
|
|
@ -189,13 +189,14 @@ extern Verbosity verbosity; /* suppress msgs > this */
|
||||||
/* Print a string message if the current log level is at least the specified
|
/* Print a string message if the current log level is at least the specified
|
||||||
level. Note that this has to be implemented as a macro to ensure that the
|
level. Note that this has to be implemented as a macro to ensure that the
|
||||||
arguments are evaluated lazily. */
|
arguments are evaluated lazily. */
|
||||||
#define printMsg(level, args...) \
|
#define printMsgUsing(loggerParam, level, args...) \
|
||||||
do { \
|
do { \
|
||||||
auto __lvl = level; \
|
auto __lvl = level; \
|
||||||
if (__lvl <= nix::verbosity) { \
|
if (__lvl <= nix::verbosity) { \
|
||||||
logger->log(__lvl, fmt(args)); \
|
loggerParam->log(__lvl, fmt(args)); \
|
||||||
} \
|
} \
|
||||||
} while (0)
|
} while (0)
|
||||||
|
#define printMsg(level, args...) printMsgUsing(logger, level, args)
|
||||||
|
|
||||||
#define printError(args...) printMsg(lvlError, args)
|
#define printError(args...) printMsg(lvlError, args)
|
||||||
#define notice(args...) printMsg(lvlNotice, args)
|
#define notice(args...) printMsg(lvlNotice, args)
|
||||||
|
|
|
@ -93,9 +93,16 @@ static void extract_archive(TarArchive & archive, const Path & destDir)
|
||||||
else
|
else
|
||||||
archive.check(r);
|
archive.check(r);
|
||||||
|
|
||||||
archive_entry_set_pathname(entry,
|
archive_entry_copy_pathname(entry,
|
||||||
(destDir + "/" + name).c_str());
|
(destDir + "/" + name).c_str());
|
||||||
|
|
||||||
|
// Patch hardlink path
|
||||||
|
const char *original_hardlink = archive_entry_hardlink(entry);
|
||||||
|
if (original_hardlink) {
|
||||||
|
archive_entry_copy_hardlink(entry,
|
||||||
|
(destDir + "/" + original_hardlink).c_str());
|
||||||
|
}
|
||||||
|
|
||||||
archive.check(archive_read_extract(archive.archive, entry, flags));
|
archive.check(archive_read_extract(archive.archive, entry, flags));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -161,7 +161,7 @@ namespace nix {
|
||||||
Setting<std::string> setting{&config, "", "name-of-the-setting", "description"};
|
Setting<std::string> setting{&config, "", "name-of-the-setting", "description"};
|
||||||
setting.assign("value");
|
setting.assign("value");
|
||||||
|
|
||||||
ASSERT_EQ(config.toJSON().dump(), R"#({"name-of-the-setting":{"aliases":[],"defaultValue":"","description":"description\n","value":"value"}})#");
|
ASSERT_EQ(config.toJSON().dump(), R"#({"name-of-the-setting":{"aliases":[],"defaultValue":"","description":"description\n","documentDefault":true,"value":"value"}})#");
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST(Config, setSettingAlias) {
|
TEST(Config, setSettingAlias) {
|
||||||
|
|
|
@ -1,13 +1,10 @@
|
||||||
#include "thread-pool.hh"
|
#include "thread-pool.hh"
|
||||||
#include "affinity.hh"
|
|
||||||
|
|
||||||
namespace nix {
|
namespace nix {
|
||||||
|
|
||||||
ThreadPool::ThreadPool(size_t _maxThreads)
|
ThreadPool::ThreadPool(size_t _maxThreads)
|
||||||
: maxThreads(_maxThreads)
|
: maxThreads(_maxThreads)
|
||||||
{
|
{
|
||||||
restoreAffinity(); // FIXME
|
|
||||||
|
|
||||||
if (!maxThreads) {
|
if (!maxThreads) {
|
||||||
maxThreads = std::thread::hardware_concurrency();
|
maxThreads = std::thread::hardware_concurrency();
|
||||||
if (!maxThreads) maxThreads = 1;
|
if (!maxThreads) maxThreads = 1;
|
||||||
|
|
|
@ -1,5 +1,4 @@
|
||||||
#include "util.hh"
|
#include "util.hh"
|
||||||
#include "affinity.hh"
|
|
||||||
#include "sync.hh"
|
#include "sync.hh"
|
||||||
#include "finally.hh"
|
#include "finally.hh"
|
||||||
#include "serialise.hh"
|
#include "serialise.hh"
|
||||||
|
@ -512,6 +511,7 @@ std::pair<AutoCloseFD, Path> createTempFile(const Path & prefix)
|
||||||
AutoCloseFD fd(mkstemp((char *) tmpl.c_str()));
|
AutoCloseFD fd(mkstemp((char *) tmpl.c_str()));
|
||||||
if (!fd)
|
if (!fd)
|
||||||
throw SysError("creating temporary file '%s'", tmpl);
|
throw SysError("creating temporary file '%s'", tmpl);
|
||||||
|
closeOnExec(fd.get());
|
||||||
return {std::move(fd), tmpl};
|
return {std::move(fd), tmpl};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1003,7 +1003,6 @@ pid_t startProcess(std::function<void()> fun, const ProcessOptions & options)
|
||||||
if (options.dieWithParent && prctl(PR_SET_PDEATHSIG, SIGKILL) == -1)
|
if (options.dieWithParent && prctl(PR_SET_PDEATHSIG, SIGKILL) == -1)
|
||||||
throw SysError("setting death signal");
|
throw SysError("setting death signal");
|
||||||
#endif
|
#endif
|
||||||
restoreAffinity();
|
|
||||||
fun();
|
fun();
|
||||||
} catch (std::exception & e) {
|
} catch (std::exception & e) {
|
||||||
try {
|
try {
|
||||||
|
@ -1659,6 +1658,14 @@ void restoreMountNamespace()
|
||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void unshareFilesystem()
|
||||||
|
{
|
||||||
|
#ifdef __linux__
|
||||||
|
if (unshare(CLONE_FS) != 0 && errno != EPERM)
|
||||||
|
throw SysError("unsharing filesystem state in download thread");
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
void restoreProcessContext(bool restoreMounts)
|
void restoreProcessContext(bool restoreMounts)
|
||||||
{
|
{
|
||||||
restoreSignals();
|
restoreSignals();
|
||||||
|
@ -1666,8 +1673,6 @@ void restoreProcessContext(bool restoreMounts)
|
||||||
restoreMountNamespace();
|
restoreMountNamespace();
|
||||||
}
|
}
|
||||||
|
|
||||||
restoreAffinity();
|
|
||||||
|
|
||||||
#if __linux__
|
#if __linux__
|
||||||
if (savedStackSize) {
|
if (savedStackSize) {
|
||||||
struct rlimit limit;
|
struct rlimit limit;
|
||||||
|
|
|
@ -11,6 +11,7 @@
|
||||||
#include <unistd.h>
|
#include <unistd.h>
|
||||||
#include <signal.h>
|
#include <signal.h>
|
||||||
|
|
||||||
|
#include <atomic>
|
||||||
#include <functional>
|
#include <functional>
|
||||||
#include <map>
|
#include <map>
|
||||||
#include <sstream>
|
#include <sstream>
|
||||||
|
@ -299,7 +300,7 @@ void setStackSize(size_t stackSize);
|
||||||
|
|
||||||
|
|
||||||
/* Restore the original inherited Unix process context (such as signal
|
/* Restore the original inherited Unix process context (such as signal
|
||||||
masks, stack size, CPU affinity). */
|
masks, stack size). */
|
||||||
void restoreProcessContext(bool restoreMounts = true);
|
void restoreProcessContext(bool restoreMounts = true);
|
||||||
|
|
||||||
/* Save the current mount namespace. Ignored if called more than
|
/* Save the current mount namespace. Ignored if called more than
|
||||||
|
@ -310,6 +311,11 @@ void saveMountNamespace();
|
||||||
if saveMountNamespace() was never called. */
|
if saveMountNamespace() was never called. */
|
||||||
void restoreMountNamespace();
|
void restoreMountNamespace();
|
||||||
|
|
||||||
|
/* Cause this thread to not share any FS attributes with the main
|
||||||
|
thread, because this causes setns() in restoreMountNamespace() to
|
||||||
|
fail. */
|
||||||
|
void unshareFilesystem();
|
||||||
|
|
||||||
|
|
||||||
class ExecError : public Error
|
class ExecError : public Error
|
||||||
{
|
{
|
||||||
|
|
|
@ -14,7 +14,6 @@
|
||||||
#include "local-fs-store.hh"
|
#include "local-fs-store.hh"
|
||||||
#include "globals.hh"
|
#include "globals.hh"
|
||||||
#include "derivations.hh"
|
#include "derivations.hh"
|
||||||
#include "affinity.hh"
|
|
||||||
#include "util.hh"
|
#include "util.hh"
|
||||||
#include "shared.hh"
|
#include "shared.hh"
|
||||||
#include "path-with-outputs.hh"
|
#include "path-with-outputs.hh"
|
||||||
|
@ -359,6 +358,7 @@ static void main_nix_build(int argc, char * * argv)
|
||||||
is not set, then build bashInteractive from
|
is not set, then build bashInteractive from
|
||||||
<nixpkgs>. */
|
<nixpkgs>. */
|
||||||
auto shell = getEnv("NIX_BUILD_SHELL");
|
auto shell = getEnv("NIX_BUILD_SHELL");
|
||||||
|
std::optional<StorePath> shellDrv;
|
||||||
|
|
||||||
if (!shell) {
|
if (!shell) {
|
||||||
|
|
||||||
|
@ -375,8 +375,7 @@ static void main_nix_build(int argc, char * * argv)
|
||||||
auto bashDrv = store->parseStorePath(drv->queryDrvPath());
|
auto bashDrv = store->parseStorePath(drv->queryDrvPath());
|
||||||
pathsToBuild.push_back({bashDrv});
|
pathsToBuild.push_back({bashDrv});
|
||||||
pathsToCopy.insert(bashDrv);
|
pathsToCopy.insert(bashDrv);
|
||||||
|
shellDrv = bashDrv;
|
||||||
shell = drv->queryOutPath() + "/bin/bash";
|
|
||||||
|
|
||||||
} catch (Error & e) {
|
} catch (Error & e) {
|
||||||
logError(e.info());
|
logError(e.info());
|
||||||
|
@ -402,6 +401,11 @@ static void main_nix_build(int argc, char * * argv)
|
||||||
|
|
||||||
if (dryRun) return;
|
if (dryRun) return;
|
||||||
|
|
||||||
|
if (shellDrv) {
|
||||||
|
auto shellDrvOutputs = store->queryPartialDerivationOutputMap(shellDrv.value());
|
||||||
|
shell = store->printStorePath(shellDrvOutputs.at("out").value()) + "/bin/bash";
|
||||||
|
}
|
||||||
|
|
||||||
if (settings.isExperimentalFeatureEnabled(Xp::CaDerivations)) {
|
if (settings.isExperimentalFeatureEnabled(Xp::CaDerivations)) {
|
||||||
auto resolvedDrv = drv.tryResolve(*store);
|
auto resolvedDrv = drv.tryResolve(*store);
|
||||||
assert(resolvedDrv && "Successfully resolved the derivation");
|
assert(resolvedDrv && "Successfully resolved the derivation");
|
||||||
|
|
|
@ -88,15 +88,6 @@ static void update(const StringSet & channelNames)
|
||||||
for (const auto & channel : channels) {
|
for (const auto & channel : channels) {
|
||||||
auto name = channel.first;
|
auto name = channel.first;
|
||||||
auto url = channel.second;
|
auto url = channel.second;
|
||||||
if (!(channelNames.empty() || channelNames.count(name)))
|
|
||||||
continue;
|
|
||||||
|
|
||||||
// We want to download the url to a file to see if it's a tarball while also checking if we
|
|
||||||
// got redirected in the process, so that we can grab the various parts of a nix channel
|
|
||||||
// definition from a consistent location if the redirect changes mid-download.
|
|
||||||
auto result = fetchers::downloadFile(store, url, std::string(baseNameOf(url)), false);
|
|
||||||
auto filename = store->toRealPath(result.storePath);
|
|
||||||
url = result.effectiveUrl;
|
|
||||||
|
|
||||||
// If the URL contains a version number, append it to the name
|
// If the URL contains a version number, append it to the name
|
||||||
// attribute (so that "nix-env -q" on the channels profile
|
// attribute (so that "nix-env -q" on the channels profile
|
||||||
|
@ -109,6 +100,19 @@ static void update(const StringSet & channelNames)
|
||||||
|
|
||||||
std::string extraAttrs;
|
std::string extraAttrs;
|
||||||
|
|
||||||
|
if (!(channelNames.empty() || channelNames.count(name))) {
|
||||||
|
// no need to update this channel, reuse the existing store path
|
||||||
|
Path symlink = profile + "/" + name;
|
||||||
|
Path storepath = dirOf(readLink(symlink));
|
||||||
|
exprs.push_back("f: rec { name = \"" + cname + "\"; type = \"derivation\"; outputs = [\"out\"]; system = \"builtin\"; outPath = builtins.storePath \"" + storepath + "\"; out = { inherit outPath; };}");
|
||||||
|
} else {
|
||||||
|
// We want to download the url to a file to see if it's a tarball while also checking if we
|
||||||
|
// got redirected in the process, so that we can grab the various parts of a nix channel
|
||||||
|
// definition from a consistent location if the redirect changes mid-download.
|
||||||
|
auto result = fetchers::downloadFile(store, url, std::string(baseNameOf(url)), false);
|
||||||
|
auto filename = store->toRealPath(result.storePath);
|
||||||
|
url = result.effectiveUrl;
|
||||||
|
|
||||||
bool unpacked = false;
|
bool unpacked = false;
|
||||||
if (std::regex_search(filename, std::regex("\\.tar\\.(gz|bz2|xz)$"))) {
|
if (std::regex_search(filename, std::regex("\\.tar\\.(gz|bz2|xz)$"))) {
|
||||||
runProgram(settings.nixBinDir + "/nix-build", false, { "--no-out-link", "--expr", "import " + unpackChannelPath +
|
runProgram(settings.nixBinDir + "/nix-build", false, { "--no-out-link", "--expr", "import " + unpackChannelPath +
|
||||||
|
@ -124,15 +128,15 @@ static void update(const StringSet & channelNames)
|
||||||
filename = store->toRealPath(fetchers::downloadFile(store, url + "/nixexprs.tar.bz2", "nixexprs.tar.bz2", false).storePath);
|
filename = store->toRealPath(fetchers::downloadFile(store, url + "/nixexprs.tar.bz2", "nixexprs.tar.bz2", false).storePath);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Regardless of where it came from, add the expression representing this channel to accumulated expression
|
// Regardless of where it came from, add the expression representing this channel to accumulated expression
|
||||||
exprs.push_back("f: f { name = \"" + cname + "\"; channelName = \"" + name + "\"; src = builtins.storePath \"" + filename + "\"; " + extraAttrs + " }");
|
exprs.push_back("f: f { name = \"" + cname + "\"; channelName = \"" + name + "\"; src = builtins.storePath \"" + filename + "\"; " + extraAttrs + " }");
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// Unpack the channel tarballs into the Nix store and install them
|
// Unpack the channel tarballs into the Nix store and install them
|
||||||
// into the channels profile.
|
// into the channels profile.
|
||||||
std::cerr << "unpacking channels...\n";
|
std::cerr << "unpacking channels...\n";
|
||||||
Strings envArgs{ "--profile", profile, "--file", unpackChannelPath, "--install", "--from-expression" };
|
Strings envArgs{ "--profile", profile, "--file", unpackChannelPath, "--install", "--remove-all", "--from-expression" };
|
||||||
for (auto & expr : exprs)
|
for (auto & expr : exprs)
|
||||||
envArgs.push_back(std::move(expr));
|
envArgs.push_back(std::move(expr));
|
||||||
envArgs.push_back("--quiet");
|
envArgs.push_back("--quiet");
|
||||||
|
|
|
@ -224,6 +224,91 @@ static void checkSelectorUse(DrvNames & selectors)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
namespace {
|
||||||
|
|
||||||
|
std::set<std::string> searchByPrefix(const DrvInfos & allElems, std::string_view prefix) {
|
||||||
|
constexpr std::size_t maxResults = 3;
|
||||||
|
std::set<std::string> result;
|
||||||
|
for (const auto & drvInfo : allElems) {
|
||||||
|
const auto drvName = DrvName { drvInfo.queryName() };
|
||||||
|
if (hasPrefix(drvName.name, prefix)) {
|
||||||
|
result.emplace(drvName.name);
|
||||||
|
|
||||||
|
if (result.size() >= maxResults) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
struct Match
|
||||||
|
{
|
||||||
|
DrvInfo drvInfo;
|
||||||
|
std::size_t index;
|
||||||
|
|
||||||
|
Match(DrvInfo drvInfo_, std::size_t index_)
|
||||||
|
: drvInfo{std::move(drvInfo_)}
|
||||||
|
, index{index_}
|
||||||
|
{}
|
||||||
|
};
|
||||||
|
|
||||||
|
/* If a selector matches multiple derivations
|
||||||
|
with the same name, pick the one matching the current
|
||||||
|
system. If there are still multiple derivations, pick the
|
||||||
|
one with the highest priority. If there are still multiple
|
||||||
|
derivations, pick the one with the highest version.
|
||||||
|
Finally, if there are still multiple derivations,
|
||||||
|
arbitrarily pick the first one. */
|
||||||
|
std::vector<Match> pickNewestOnly(EvalState & state, std::vector<Match> matches) {
|
||||||
|
/* Map from package names to derivations. */
|
||||||
|
std::map<std::string, Match> newest;
|
||||||
|
StringSet multiple;
|
||||||
|
|
||||||
|
for (auto & match : matches) {
|
||||||
|
auto & oneDrv = match.drvInfo;
|
||||||
|
|
||||||
|
const auto drvName = DrvName { oneDrv.queryName() };
|
||||||
|
long comparison = 1;
|
||||||
|
|
||||||
|
const auto itOther = newest.find(drvName.name);
|
||||||
|
|
||||||
|
if (itOther != newest.end()) {
|
||||||
|
auto & newestDrv = itOther->second.drvInfo;
|
||||||
|
|
||||||
|
comparison =
|
||||||
|
oneDrv.querySystem() == newestDrv.querySystem() ? 0 :
|
||||||
|
oneDrv.querySystem() == settings.thisSystem ? 1 :
|
||||||
|
newestDrv.querySystem() == settings.thisSystem ? -1 : 0;
|
||||||
|
if (comparison == 0)
|
||||||
|
comparison = comparePriorities(state, oneDrv, newestDrv);
|
||||||
|
if (comparison == 0)
|
||||||
|
comparison = compareVersions(drvName.version, DrvName { newestDrv.queryName() }.version);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (comparison > 0) {
|
||||||
|
newest.erase(drvName.name);
|
||||||
|
newest.emplace(drvName.name, match);
|
||||||
|
multiple.erase(drvName.fullName);
|
||||||
|
} else if (comparison == 0) {
|
||||||
|
multiple.insert(drvName.fullName);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
matches.clear();
|
||||||
|
for (auto & [name, match] : newest) {
|
||||||
|
if (multiple.find(name) != multiple.end())
|
||||||
|
warn(
|
||||||
|
"there are multiple derivations named '%1%'; using the first one",
|
||||||
|
name);
|
||||||
|
matches.push_back(match);
|
||||||
|
}
|
||||||
|
|
||||||
|
return matches;
|
||||||
|
}
|
||||||
|
|
||||||
|
} // end namespace
|
||||||
|
|
||||||
static DrvInfos filterBySelector(EvalState & state, const DrvInfos & allElems,
|
static DrvInfos filterBySelector(EvalState & state, const DrvInfos & allElems,
|
||||||
const Strings & args, bool newestOnly)
|
const Strings & args, bool newestOnly)
|
||||||
{
|
{
|
||||||
|
@ -232,79 +317,42 @@ static DrvInfos filterBySelector(EvalState & state, const DrvInfos & allElems,
|
||||||
selectors.emplace_back("*");
|
selectors.emplace_back("*");
|
||||||
|
|
||||||
DrvInfos elems;
|
DrvInfos elems;
|
||||||
set<unsigned int> done;
|
std::set<std::size_t> done;
|
||||||
|
|
||||||
for (auto & i : selectors) {
|
for (auto & selector : selectors) {
|
||||||
typedef list<std::pair<DrvInfo, unsigned int> > Matches;
|
std::vector<Match> matches;
|
||||||
Matches matches;
|
for (const auto & [index, drvInfo] : enumerate(allElems)) {
|
||||||
unsigned int n = 0;
|
const auto drvName = DrvName { drvInfo.queryName() };
|
||||||
for (DrvInfos::const_iterator j = allElems.begin();
|
if (selector.matches(drvName)) {
|
||||||
j != allElems.end(); ++j, ++n)
|
++selector.hits;
|
||||||
{
|
matches.emplace_back(drvInfo, index);
|
||||||
DrvName drvName(j->queryName());
|
|
||||||
if (i.matches(drvName)) {
|
|
||||||
i.hits++;
|
|
||||||
matches.push_back(std::pair<DrvInfo, unsigned int>(*j, n));
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/* If `newestOnly', if a selector matches multiple derivations
|
|
||||||
with the same name, pick the one matching the current
|
|
||||||
system. If there are still multiple derivations, pick the
|
|
||||||
one with the highest priority. If there are still multiple
|
|
||||||
derivations, pick the one with the highest version.
|
|
||||||
Finally, if there are still multiple derivations,
|
|
||||||
arbitrarily pick the first one. */
|
|
||||||
if (newestOnly) {
|
if (newestOnly) {
|
||||||
|
matches = pickNewestOnly(state, std::move(matches));
|
||||||
/* Map from package names to derivations. */
|
|
||||||
typedef map<string, std::pair<DrvInfo, unsigned int> > Newest;
|
|
||||||
Newest newest;
|
|
||||||
StringSet multiple;
|
|
||||||
|
|
||||||
for (auto & j : matches) {
|
|
||||||
DrvName drvName(j.first.queryName());
|
|
||||||
long d = 1;
|
|
||||||
|
|
||||||
Newest::iterator k = newest.find(drvName.name);
|
|
||||||
|
|
||||||
if (k != newest.end()) {
|
|
||||||
d = j.first.querySystem() == k->second.first.querySystem() ? 0 :
|
|
||||||
j.first.querySystem() == settings.thisSystem ? 1 :
|
|
||||||
k->second.first.querySystem() == settings.thisSystem ? -1 : 0;
|
|
||||||
if (d == 0)
|
|
||||||
d = comparePriorities(state, j.first, k->second.first);
|
|
||||||
if (d == 0)
|
|
||||||
d = compareVersions(drvName.version, DrvName(k->second.first.queryName()).version);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (d > 0) {
|
|
||||||
newest.erase(drvName.name);
|
|
||||||
newest.insert(Newest::value_type(drvName.name, j));
|
|
||||||
multiple.erase(j.first.queryName());
|
|
||||||
} else if (d == 0) {
|
|
||||||
multiple.insert(j.first.queryName());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
matches.clear();
|
|
||||||
for (auto & j : newest) {
|
|
||||||
if (multiple.find(j.second.first.queryName()) != multiple.end())
|
|
||||||
printInfo(
|
|
||||||
"warning: there are multiple derivations named '%1%'; using the first one",
|
|
||||||
j.second.first.queryName());
|
|
||||||
matches.push_back(j.second);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Insert only those elements in the final list that we
|
/* Insert only those elements in the final list that we
|
||||||
haven't inserted before. */
|
haven't inserted before. */
|
||||||
for (auto & j : matches)
|
for (auto & match : matches)
|
||||||
if (done.insert(j.second).second)
|
if (done.insert(match.index).second)
|
||||||
elems.push_back(j.first);
|
elems.push_back(match.drvInfo);
|
||||||
}
|
|
||||||
|
|
||||||
checkSelectorUse(selectors);
|
if (selector.hits == 0 && selector.fullName != "*") {
|
||||||
|
const auto prefixHits = searchByPrefix(allElems, selector.name);
|
||||||
|
|
||||||
|
if (prefixHits.empty()) {
|
||||||
|
throw Error("selector '%1%' matches no derivations", selector.fullName);
|
||||||
|
} else {
|
||||||
|
std::string suggestionMessage = ", maybe you meant:";
|
||||||
|
for (const auto & drvName : prefixHits) {
|
||||||
|
suggestionMessage += fmt("\n%s", drvName);
|
||||||
|
}
|
||||||
|
throw Error("selector '%1%' matches no derivations" + suggestionMessage, selector.fullName);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
return elems;
|
return elems;
|
||||||
}
|
}
|
||||||
|
@ -1149,10 +1197,10 @@ static void opQuery(Globals & globals, Strings opFlags, Strings opArgs)
|
||||||
} else if (v->type() == nList) {
|
} else if (v->type() == nList) {
|
||||||
attrs2["type"] = "strings";
|
attrs2["type"] = "strings";
|
||||||
XMLOpenElement m(xml, "meta", attrs2);
|
XMLOpenElement m(xml, "meta", attrs2);
|
||||||
for (unsigned int j = 0; j < v->listSize(); ++j) {
|
for (auto elem : v->listItems()) {
|
||||||
if (v->listElems()[j]->type() != nString) continue;
|
if (elem->type() != nString) continue;
|
||||||
XMLAttrs attrs3;
|
XMLAttrs attrs3;
|
||||||
attrs3["value"] = v->listElems()[j]->string.s;
|
attrs3["value"] = elem->string.s;
|
||||||
xml.writeEmptyElement("string", attrs3);
|
xml.writeEmptyElement("string", attrs3);
|
||||||
}
|
}
|
||||||
} else if (v->type() == nAttrs) {
|
} else if (v->type() == nAttrs) {
|
||||||
|
|
|
@ -83,11 +83,14 @@ UnresolvedApp Installable::toApp(EvalState & state)
|
||||||
auto outPath = cursor->getAttr(state.sOutPath)->getString();
|
auto outPath = cursor->getAttr(state.sOutPath)->getString();
|
||||||
auto outputName = cursor->getAttr(state.sOutputName)->getString();
|
auto outputName = cursor->getAttr(state.sOutputName)->getString();
|
||||||
auto name = cursor->getAttr(state.sName)->getString();
|
auto name = cursor->getAttr(state.sName)->getString();
|
||||||
|
auto aPname = cursor->maybeGetAttr("pname");
|
||||||
auto aMeta = cursor->maybeGetAttr("meta");
|
auto aMeta = cursor->maybeGetAttr("meta");
|
||||||
auto aMainProgram = aMeta ? aMeta->maybeGetAttr("mainProgram") : nullptr;
|
auto aMainProgram = aMeta ? aMeta->maybeGetAttr("mainProgram") : nullptr;
|
||||||
auto mainProgram =
|
auto mainProgram =
|
||||||
aMainProgram
|
aMainProgram
|
||||||
? aMainProgram->getString()
|
? aMainProgram->getString()
|
||||||
|
: aPname
|
||||||
|
? aPname->getString()
|
||||||
: DrvName(name).name;
|
: DrvName(name).name;
|
||||||
auto program = outPath + "/bin/" + mainProgram;
|
auto program = outPath + "/bin/" + mainProgram;
|
||||||
return UnresolvedApp { App {
|
return UnresolvedApp { App {
|
||||||
|
|
|
@ -5,7 +5,6 @@
|
||||||
#include "store-api.hh"
|
#include "store-api.hh"
|
||||||
#include "path-with-outputs.hh"
|
#include "path-with-outputs.hh"
|
||||||
#include "derivations.hh"
|
#include "derivations.hh"
|
||||||
#include "affinity.hh"
|
|
||||||
#include "progress-bar.hh"
|
#include "progress-bar.hh"
|
||||||
#include "run.hh"
|
#include "run.hh"
|
||||||
|
|
||||||
|
|
|
@ -310,7 +310,14 @@ void mainWrapped(int argc, char * * argv)
|
||||||
Finally printCompletions([&]()
|
Finally printCompletions([&]()
|
||||||
{
|
{
|
||||||
if (completions) {
|
if (completions) {
|
||||||
std::cout << (pathCompletions ? "filenames\n" : "no-filenames\n");
|
switch (completionType) {
|
||||||
|
case ctNormal:
|
||||||
|
std::cout << "normal\n"; break;
|
||||||
|
case ctFilenames:
|
||||||
|
std::cout << "filenames\n"; break;
|
||||||
|
case ctAttrs:
|
||||||
|
std::cout << "attrs\n"; break;
|
||||||
|
}
|
||||||
for (auto & s : *completions)
|
for (auto & s : *completions)
|
||||||
std::cout << s.completion << "\t" << s.description << "\n";
|
std::cout << s.completion << "\t" << s.description << "\n";
|
||||||
}
|
}
|
||||||
|
|
|
@ -2,7 +2,7 @@ R""(
|
||||||
|
|
||||||
# Description
|
# Description
|
||||||
|
|
||||||
`nix flake` provides subcommands for managing *flake
|
`nix registry` provides subcommands for managing *flake
|
||||||
registries*. Flake registries are a convenience feature that allows
|
registries*. Flake registries are a convenience feature that allows
|
||||||
you to refer to flakes using symbolic identifiers such as `nixpkgs`,
|
you to refer to flakes using symbolic identifiers such as `nixpkgs`,
|
||||||
rather than full URLs such as `git://github.com/NixOS/nixpkgs`. You
|
rather than full URLs such as `git://github.com/NixOS/nixpkgs`. You
|
||||||
|
|
|
@ -35,14 +35,17 @@ R""(
|
||||||
nix-repl> emacs.drvPath
|
nix-repl> emacs.drvPath
|
||||||
"/nix/store/lp0sjrhgg03y2n0l10n70rg0k7hhyz0l-emacs-27.1.drv"
|
"/nix/store/lp0sjrhgg03y2n0l10n70rg0k7hhyz0l-emacs-27.1.drv"
|
||||||
|
|
||||||
nix-repl> drv = runCommand "hello" { buildInputs = [ hello ]; } "hello > $out"
|
nix-repl> drv = runCommand "hello" { buildInputs = [ hello ]; } "hello; hello > $out"
|
||||||
|
|
||||||
nix-repl> :b x
|
nix-repl> :b drv
|
||||||
this derivation produced the following outputs:
|
this derivation produced the following outputs:
|
||||||
out -> /nix/store/0njwbgwmkwls0w5dv9mpc1pq5fj39q0l-hello
|
out -> /nix/store/0njwbgwmkwls0w5dv9mpc1pq5fj39q0l-hello
|
||||||
|
|
||||||
nix-repl> builtins.readFile drv
|
nix-repl> builtins.readFile drv
|
||||||
"Hello, world!\n"
|
"Hello, world!\n"
|
||||||
|
|
||||||
|
nix-repl> :log drv
|
||||||
|
Hello, world!
|
||||||
```
|
```
|
||||||
|
|
||||||
# Description
|
# Description
|
||||||
|
|
|
@ -8,7 +8,6 @@
|
||||||
#include "finally.hh"
|
#include "finally.hh"
|
||||||
#include "fs-accessor.hh"
|
#include "fs-accessor.hh"
|
||||||
#include "progress-bar.hh"
|
#include "progress-bar.hh"
|
||||||
#include "affinity.hh"
|
|
||||||
#include "eval.hh"
|
#include "eval.hh"
|
||||||
|
|
||||||
#if __linux__
|
#if __linux__
|
||||||
|
|
|
@ -43,10 +43,15 @@ program specified by the app definition.
|
||||||
|
|
||||||
If *installable* evaluates to a derivation, it will try to execute the
|
If *installable* evaluates to a derivation, it will try to execute the
|
||||||
program `<out>/bin/<name>`, where *out* is the primary output store
|
program `<out>/bin/<name>`, where *out* is the primary output store
|
||||||
path of the derivation and *name* is the `meta.mainProgram` attribute
|
path of the derivation, and *name* is the first of the following that
|
||||||
of the derivation if it exists, and otherwise the name part of the
|
exists:
|
||||||
value of the `name` attribute of the derivation (e.g. if `name` is set
|
|
||||||
to `hello-1.10`, it will run `$out/bin/hello`).
|
* The `meta.mainProgram` attribute of the derivation.
|
||||||
|
* The `pname` attribute of the derivation.
|
||||||
|
* The name part of the value of the `name` attribute of the derivation.
|
||||||
|
|
||||||
|
For instance, if `name` is set to `hello-1.10`, `nix run` will run
|
||||||
|
`$out/bin/hello`.
|
||||||
|
|
||||||
# Flake output attributes
|
# Flake output attributes
|
||||||
|
|
||||||
|
|
|
@ -41,8 +41,8 @@ R""(
|
||||||
|
|
||||||
# Description
|
# Description
|
||||||
|
|
||||||
`nix shell` runs a command in an environment in which the `$PATH`
|
`nix shell` runs a command in an environment in which the `$PATH` variable
|
||||||
variable provides the specified *installables*. If not command is
|
provides the specified *installables*. If no command is specified, it starts the
|
||||||
specified, it starts the default shell of your user account.
|
default shell of your user account specified by `$SHELL`.
|
||||||
|
|
||||||
)""
|
)""
|
||||||
|
|
|
@ -34,8 +34,21 @@ struct CmdWhyDepends : SourceExprCommand
|
||||||
|
|
||||||
CmdWhyDepends()
|
CmdWhyDepends()
|
||||||
{
|
{
|
||||||
expectArg("package", &_package);
|
expectArgs({
|
||||||
expectArg("dependency", &_dependency);
|
.label = "package",
|
||||||
|
.handler = {&_package},
|
||||||
|
.completer = {[&](size_t, std::string_view prefix) {
|
||||||
|
completeInstallable(prefix);
|
||||||
|
}}
|
||||||
|
});
|
||||||
|
|
||||||
|
expectArgs({
|
||||||
|
.label = "dependency",
|
||||||
|
.handler = {&_dependency},
|
||||||
|
.completer = {[&](size_t, std::string_view prefix) {
|
||||||
|
completeInstallable(prefix);
|
||||||
|
}}
|
||||||
|
});
|
||||||
|
|
||||||
addFlag({
|
addFlag({
|
||||||
.longName = "all",
|
.longName = "all",
|
||||||
|
|
21
src/toml11/LICENSE
Normal file
21
src/toml11/LICENSE
Normal file
|
@ -0,0 +1,21 @@
|
||||||
|
The MIT License (MIT)
|
||||||
|
|
||||||
|
Copyright (c) 2017 Toru Niina
|
||||||
|
|
||||||
|
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
|
of this software and associated documentation files (the "Software"), to deal
|
||||||
|
in the Software without restriction, including without limitation the rights
|
||||||
|
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||||
|
copies of the Software, and to permit persons to whom the Software is
|
||||||
|
furnished to do so, subject to the following conditions:
|
||||||
|
|
||||||
|
The above copyright notice and this permission notice shall be included in
|
||||||
|
all copies or substantial portions of the Software.
|
||||||
|
|
||||||
|
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
|
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||||
|
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||||
|
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||||
|
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||||
|
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||||
|
THE SOFTWARE.
|
1966
src/toml11/README.md
Normal file
1966
src/toml11/README.md
Normal file
File diff suppressed because it is too large
Load diff
46
src/toml11/toml.hpp
Normal file
46
src/toml11/toml.hpp
Normal file
|
@ -0,0 +1,46 @@
|
||||||
|
/*
|
||||||
|
* The MIT License (MIT)
|
||||||
|
*
|
||||||
|
* Copyright (c) 2017 Toru Niina
|
||||||
|
*
|
||||||
|
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
|
* of this software and associated documentation files (the "Software"), to deal
|
||||||
|
* in the Software without restriction, including without limitation the rights
|
||||||
|
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||||
|
* copies of the Software, and to permit persons to whom the Software is
|
||||||
|
* furnished to do so, subject to the following conditions:
|
||||||
|
*
|
||||||
|
* The above copyright notice and this permission notice shall be included in
|
||||||
|
* all copies or substantial portions of the Software.
|
||||||
|
*
|
||||||
|
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
|
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||||
|
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||||
|
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||||
|
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||||
|
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||||
|
* THE SOFTWARE.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef TOML_FOR_MODERN_CPP
|
||||||
|
#define TOML_FOR_MODERN_CPP
|
||||||
|
|
||||||
|
#ifndef __cplusplus
|
||||||
|
# error "__cplusplus is not defined"
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#if __cplusplus < 201103L && _MSC_VER < 1900
|
||||||
|
# error "toml11 requires C++11 or later."
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#define TOML11_VERSION_MAJOR 3
|
||||||
|
#define TOML11_VERSION_MINOR 7
|
||||||
|
#define TOML11_VERSION_PATCH 0
|
||||||
|
|
||||||
|
#include "toml/parser.hpp"
|
||||||
|
#include "toml/literal.hpp"
|
||||||
|
#include "toml/serializer.hpp"
|
||||||
|
#include "toml/get.hpp"
|
||||||
|
#include "toml/macros.hpp"
|
||||||
|
|
||||||
|
#endif// TOML_FOR_MODERN_CPP
|
Some files were not shown because too many files have changed in this diff Show more
Loading…
Reference in a new issue