Merge branch 'master' into structured-attrs-shell
Conflicts: src/nix/develop.cc src/nix/get-env.sh tests/shell.nix
This commit is contained in:
commit
04cd2da84c
104 changed files with 1644 additions and 539 deletions
3
.github/workflows/test.yml
vendored
3
.github/workflows/test.yml
vendored
|
@ -20,8 +20,7 @@ jobs:
|
||||||
name: '${{ env.CACHIX_NAME }}'
|
name: '${{ env.CACHIX_NAME }}'
|
||||||
signingKey: '${{ secrets.CACHIX_SIGNING_KEY }}'
|
signingKey: '${{ secrets.CACHIX_SIGNING_KEY }}'
|
||||||
authToken: '${{ secrets.CACHIX_AUTH_TOKEN }}'
|
authToken: '${{ secrets.CACHIX_AUTH_TOKEN }}'
|
||||||
#- run: nix flake check
|
- run: nix-build -A checks.$(nix-instantiate --eval -E '(builtins.currentSystem)')
|
||||||
- run: nix-build -A checks.$(if [[ `uname` = Linux ]]; then echo x86_64-linux; else echo x86_64-darwin; fi)
|
|
||||||
check_cachix:
|
check_cachix:
|
||||||
name: Cachix secret present for installer tests
|
name: Cachix secret present for installer tests
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
|
|
1
.gitignore
vendored
1
.gitignore
vendored
|
@ -76,7 +76,6 @@ perl/Makefile.config
|
||||||
# /tests/
|
# /tests/
|
||||||
/tests/test-tmp
|
/tests/test-tmp
|
||||||
/tests/common.sh
|
/tests/common.sh
|
||||||
/tests/dummy
|
|
||||||
/tests/result*
|
/tests/result*
|
||||||
/tests/restricted-innocent
|
/tests/restricted-innocent
|
||||||
/tests/shell
|
/tests/shell
|
||||||
|
|
1
Makefile
1
Makefile
|
@ -12,6 +12,7 @@ makefiles = \
|
||||||
src/resolve-system-dependencies/local.mk \
|
src/resolve-system-dependencies/local.mk \
|
||||||
scripts/local.mk \
|
scripts/local.mk \
|
||||||
misc/bash/local.mk \
|
misc/bash/local.mk \
|
||||||
|
misc/fish/local.mk \
|
||||||
misc/zsh/local.mk \
|
misc/zsh/local.mk \
|
||||||
misc/systemd/local.mk \
|
misc/systemd/local.mk \
|
||||||
misc/launchd/local.mk \
|
misc/launchd/local.mk \
|
||||||
|
|
|
@ -1,3 +1,4 @@
|
||||||
|
HOST_OS = @host_os@
|
||||||
AR = @AR@
|
AR = @AR@
|
||||||
BDW_GC_LIBS = @BDW_GC_LIBS@
|
BDW_GC_LIBS = @BDW_GC_LIBS@
|
||||||
BOOST_LDFLAGS = @BOOST_LDFLAGS@
|
BOOST_LDFLAGS = @BOOST_LDFLAGS@
|
||||||
|
|
42
boehmgc-coroutine-sp-fallback.diff
Normal file
42
boehmgc-coroutine-sp-fallback.diff
Normal file
|
@ -0,0 +1,42 @@
|
||||||
|
diff --git a/pthread_stop_world.c b/pthread_stop_world.c
|
||||||
|
index 1cee6a0b..46c3acd9 100644
|
||||||
|
--- a/pthread_stop_world.c
|
||||||
|
+++ b/pthread_stop_world.c
|
||||||
|
@@ -674,6 +674,8 @@ GC_INNER void GC_push_all_stacks(void)
|
||||||
|
struct GC_traced_stack_sect_s *traced_stack_sect;
|
||||||
|
pthread_t self = pthread_self();
|
||||||
|
word total_size = 0;
|
||||||
|
+ size_t stack_limit;
|
||||||
|
+ pthread_attr_t pattr;
|
||||||
|
|
||||||
|
if (!EXPECT(GC_thr_initialized, TRUE))
|
||||||
|
GC_thr_init();
|
||||||
|
@@ -723,6 +725,28 @@ GC_INNER void GC_push_all_stacks(void)
|
||||||
|
hi = p->altstack + p->altstack_size;
|
||||||
|
/* FIXME: Need to scan the normal stack too, but how ? */
|
||||||
|
/* FIXME: Assume stack grows down */
|
||||||
|
+ } else {
|
||||||
|
+ if (pthread_getattr_np(p->id, &pattr)) {
|
||||||
|
+ ABORT("GC_push_all_stacks: pthread_getattr_np failed!");
|
||||||
|
+ }
|
||||||
|
+ if (pthread_attr_getstacksize(&pattr, &stack_limit)) {
|
||||||
|
+ ABORT("GC_push_all_stacks: pthread_attr_getstacksize failed!");
|
||||||
|
+ }
|
||||||
|
+ // When a thread goes into a coroutine, we lose its original sp until
|
||||||
|
+ // control flow returns to the thread.
|
||||||
|
+ // While in the coroutine, the sp points outside the thread stack,
|
||||||
|
+ // so we can detect this and push the entire thread stack instead,
|
||||||
|
+ // as an approximation.
|
||||||
|
+ // We assume that the coroutine has similarly added its entire stack.
|
||||||
|
+ // This could be made accurate by cooperating with the application
|
||||||
|
+ // via new functions and/or callbacks.
|
||||||
|
+ #ifndef STACK_GROWS_UP
|
||||||
|
+ if (lo >= hi || lo < hi - stack_limit) { // sp outside stack
|
||||||
|
+ lo = hi - stack_limit;
|
||||||
|
+ }
|
||||||
|
+ #else
|
||||||
|
+ #error "STACK_GROWS_UP not supported in boost_coroutine2 (as of june 2021), so we don't support it in Nix."
|
||||||
|
+ #endif
|
||||||
|
}
|
||||||
|
GC_push_all_stack_sections(lo, hi, traced_stack_sect);
|
||||||
|
# ifdef STACK_GROWS_UP
|
|
@ -231,7 +231,7 @@ AC_SUBST(HAVE_SECCOMP, [$have_seccomp])
|
||||||
# Look for aws-cpp-sdk-s3.
|
# Look for aws-cpp-sdk-s3.
|
||||||
AC_LANG_PUSH(C++)
|
AC_LANG_PUSH(C++)
|
||||||
AC_CHECK_HEADERS([aws/s3/S3Client.h],
|
AC_CHECK_HEADERS([aws/s3/S3Client.h],
|
||||||
[AC_DEFINE([ENABLE_S3], [1], [Whether to enable S3 support via aws-sdk-cpp.]) enable_s3=1],
|
[AC_DEFINE([ENABLE_S3], [1], [Whether to enable S3 support via aws-sdk-cpp.]) enable_s3=1],
|
||||||
[AC_DEFINE([ENABLE_S3], [0], [Whether to enable S3 support via aws-sdk-cpp.]) enable_s3=])
|
[AC_DEFINE([ENABLE_S3], [0], [Whether to enable S3 support via aws-sdk-cpp.]) enable_s3=])
|
||||||
AC_SUBST(ENABLE_S3, [$enable_s3])
|
AC_SUBST(ENABLE_S3, [$enable_s3])
|
||||||
AC_LANG_POP(C++)
|
AC_LANG_POP(C++)
|
||||||
|
|
|
@ -6,9 +6,11 @@ builtins:
|
||||||
concatStrings (map
|
concatStrings (map
|
||||||
(name:
|
(name:
|
||||||
let builtin = builtins.${name}; in
|
let builtin = builtins.${name}; in
|
||||||
" - `builtins.${name}` " + concatStringsSep " " (map (s: "*${s}*") builtin.args)
|
"<dt><code>${name} "
|
||||||
+ " \n\n"
|
+ concatStringsSep " " (map (s: "<var>${s}</var>") builtin.args)
|
||||||
+ concatStrings (map (s: " ${s}\n") (splitLines builtin.doc)) + "\n\n"
|
+ "</code></dt>"
|
||||||
|
+ "<dd>\n\n"
|
||||||
|
+ builtin.doc
|
||||||
|
+ "\n\n</dd>"
|
||||||
)
|
)
|
||||||
(attrNames builtins))
|
(attrNames builtins))
|
||||||
|
|
||||||
|
|
|
@ -64,6 +64,7 @@ $(d)/conf-file.json: $(bindir)/nix
|
||||||
$(d)/src/expressions/builtins.md: $(d)/builtins.json $(d)/generate-builtins.nix $(d)/src/expressions/builtins-prefix.md $(bindir)/nix
|
$(d)/src/expressions/builtins.md: $(d)/builtins.json $(d)/generate-builtins.nix $(d)/src/expressions/builtins-prefix.md $(bindir)/nix
|
||||||
@cat doc/manual/src/expressions/builtins-prefix.md > $@.tmp
|
@cat doc/manual/src/expressions/builtins-prefix.md > $@.tmp
|
||||||
$(trace-gen) $(nix-eval) --expr 'import doc/manual/generate-builtins.nix (builtins.fromJSON (builtins.readFile $<))' >> $@.tmp
|
$(trace-gen) $(nix-eval) --expr 'import doc/manual/generate-builtins.nix (builtins.fromJSON (builtins.readFile $<))' >> $@.tmp
|
||||||
|
@cat doc/manual/src/expressions/builtins-suffix.md >> $@.tmp
|
||||||
@mv $@.tmp $@
|
@mv $@.tmp $@
|
||||||
|
|
||||||
$(d)/builtins.json: $(bindir)/nix
|
$(d)/builtins.json: $(bindir)/nix
|
||||||
|
|
|
@ -10,35 +10,39 @@ Most Nix commands interpret the following environment variables:
|
||||||
A colon-separated list of directories used to look up Nix
|
A colon-separated list of directories used to look up Nix
|
||||||
expressions enclosed in angle brackets (i.e., `<path>`). For
|
expressions enclosed in angle brackets (i.e., `<path>`). For
|
||||||
instance, the value
|
instance, the value
|
||||||
|
|
||||||
/home/eelco/Dev:/etc/nixos
|
/home/eelco/Dev:/etc/nixos
|
||||||
|
|
||||||
will cause Nix to look for paths relative to `/home/eelco/Dev` and
|
will cause Nix to look for paths relative to `/home/eelco/Dev` and
|
||||||
`/etc/nixos`, in this order. It is also possible to match paths
|
`/etc/nixos`, in this order. It is also possible to match paths
|
||||||
against a prefix. For example, the value
|
against a prefix. For example, the value
|
||||||
|
|
||||||
nixpkgs=/home/eelco/Dev/nixpkgs-branch:/etc/nixos
|
nixpkgs=/home/eelco/Dev/nixpkgs-branch:/etc/nixos
|
||||||
|
|
||||||
will cause Nix to search for `<nixpkgs/path>` in
|
will cause Nix to search for `<nixpkgs/path>` in
|
||||||
`/home/eelco/Dev/nixpkgs-branch/path` and `/etc/nixos/nixpkgs/path`.
|
`/home/eelco/Dev/nixpkgs-branch/path` and `/etc/nixos/nixpkgs/path`.
|
||||||
|
|
||||||
If a path in the Nix search path starts with `http://` or
|
If a path in the Nix search path starts with `http://` or
|
||||||
`https://`, it is interpreted as the URL of a tarball that will be
|
`https://`, it is interpreted as the URL of a tarball that will be
|
||||||
downloaded and unpacked to a temporary location. The tarball must
|
downloaded and unpacked to a temporary location. The tarball must
|
||||||
consist of a single top-level directory. For example, setting
|
consist of a single top-level directory. For example, setting
|
||||||
`NIX_PATH` to
|
`NIX_PATH` to
|
||||||
|
|
||||||
nixpkgs=https://github.com/NixOS/nixpkgs/archive/nixos-15.09.tar.gz
|
nixpkgs=https://github.com/NixOS/nixpkgs/archive/master.tar.gz
|
||||||
|
|
||||||
tells Nix to download the latest revision in the Nixpkgs/NixOS 15.09
|
tells Nix to download and use the current contents of the
|
||||||
channel.
|
`master` branch in the `nixpkgs` repository.
|
||||||
|
|
||||||
A following shorthand can be used to refer to the official channels:
|
The URLs of the tarballs from the official nixos.org channels (see
|
||||||
|
[the manual for `nix-channel`](nix-channel.md)) can be abbreviated
|
||||||
nixpkgs=channel:nixos-15.09
|
as `channel:<channel-name>`. For instance, the following two
|
||||||
|
values of `NIX_PATH` are equivalent:
|
||||||
The search path can be extended using the `-I` option, which takes
|
|
||||||
precedence over `NIX_PATH`.
|
nixpkgs=channel:nixos-21.05
|
||||||
|
nixpkgs=https://nixos.org/channels/nixos-21.05/nixexprs.tar.xz
|
||||||
|
|
||||||
|
The Nix search path can also be extended using the `-I` option to
|
||||||
|
many Nix commands, which takes precedence over `NIX_PATH`.
|
||||||
|
|
||||||
- `NIX_IGNORE_SYMLINK_STORE`\
|
- `NIX_IGNORE_SYMLINK_STORE`\
|
||||||
Normally, the Nix store directory (typically `/nix/store`) is not
|
Normally, the Nix store directory (typically `/nix/store`) is not
|
||||||
|
@ -50,7 +54,7 @@ Most Nix commands interpret the following environment variables:
|
||||||
builds are deployed to machines where `/nix/store` resolves
|
builds are deployed to machines where `/nix/store` resolves
|
||||||
differently. If you are sure that you’re not going to do that, you
|
differently. If you are sure that you’re not going to do that, you
|
||||||
can set `NIX_IGNORE_SYMLINK_STORE` to `1`.
|
can set `NIX_IGNORE_SYMLINK_STORE` to `1`.
|
||||||
|
|
||||||
Note that if you’re symlinking the Nix store so that you can put it
|
Note that if you’re symlinking the Nix store so that you can put it
|
||||||
on another file system than the root file system, on Linux you’re
|
on another file system than the root file system, on Linux you’re
|
||||||
better off using `bind` mount points, e.g.,
|
better off using `bind` mount points, e.g.,
|
||||||
|
@ -59,7 +63,7 @@ Most Nix commands interpret the following environment variables:
|
||||||
$ mkdir /nix
|
$ mkdir /nix
|
||||||
$ mount -o bind /mnt/otherdisk/nix /nix
|
$ mount -o bind /mnt/otherdisk/nix /nix
|
||||||
```
|
```
|
||||||
|
|
||||||
Consult the mount 8 manual page for details.
|
Consult the mount 8 manual page for details.
|
||||||
|
|
||||||
- `NIX_STORE_DIR`\
|
- `NIX_STORE_DIR`\
|
||||||
|
|
|
@ -9,7 +9,8 @@ scope. Instead, you can access them through the `builtins` built-in
|
||||||
value, which is a set that contains all built-in functions and values.
|
value, which is a set that contains all built-in functions and values.
|
||||||
For instance, `derivation` is also available as `builtins.derivation`.
|
For instance, `derivation` is also available as `builtins.derivation`.
|
||||||
|
|
||||||
- `derivation` *attrs*; `builtins.derivation` *attrs*\
|
<dl>
|
||||||
|
<dt><code>derivation <var>attrs</var></code>;
|
||||||
`derivation` is described in [its own section](derivations.md).
|
<code>builtins.derivation <var>attrs</var></code></dt>
|
||||||
|
<dd><p><var>derivation</var> in described in
|
||||||
|
<a href="derivations.md">its own section</a>.</p></dd>
|
||||||
|
|
1
doc/manual/src/expressions/builtins-suffix.md
Normal file
1
doc/manual/src/expressions/builtins-suffix.md
Normal file
|
@ -0,0 +1 @@
|
||||||
|
</dl>
|
|
@ -19,11 +19,11 @@
|
||||||
},
|
},
|
||||||
"nixpkgs": {
|
"nixpkgs": {
|
||||||
"locked": {
|
"locked": {
|
||||||
"lastModified": 1622593737,
|
"lastModified": 1624862269,
|
||||||
"narHash": "sha256-9loxFJg85AbzJrSkU4pE/divZ1+zOxDy2FSjlrufCB8=",
|
"narHash": "sha256-JFcsh2+7QtfKdJFoPibLFPLgIW6Ycnv8Bts9a7RYme0=",
|
||||||
"owner": "NixOS",
|
"owner": "NixOS",
|
||||||
"repo": "nixpkgs",
|
"repo": "nixpkgs",
|
||||||
"rev": "bb8a5e54845012ed1375ffd5f317d2fdf434b20e",
|
"rev": "f77036342e2b690c61c97202bf48f2ce13acc022",
|
||||||
"type": "github"
|
"type": "github"
|
||||||
},
|
},
|
||||||
"original": {
|
"original": {
|
||||||
|
|
235
flake.nix
235
flake.nix
|
@ -20,6 +20,8 @@
|
||||||
linuxSystems = linux64BitSystems ++ [ "i686-linux" ];
|
linuxSystems = linux64BitSystems ++ [ "i686-linux" ];
|
||||||
systems = linuxSystems ++ [ "x86_64-darwin" "aarch64-darwin" ];
|
systems = linuxSystems ++ [ "x86_64-darwin" "aarch64-darwin" ];
|
||||||
|
|
||||||
|
crossSystems = [ "armv6l-linux" "armv7l-linux" ];
|
||||||
|
|
||||||
forAllSystems = f: nixpkgs.lib.genAttrs systems (system: f system);
|
forAllSystems = f: nixpkgs.lib.genAttrs systems (system: f system);
|
||||||
|
|
||||||
# Memoize nixpkgs for different platforms for efficiency.
|
# Memoize nixpkgs for different platforms for efficiency.
|
||||||
|
@ -79,7 +81,7 @@
|
||||||
buildPackages.mercurial
|
buildPackages.mercurial
|
||||||
buildPackages.jq
|
buildPackages.jq
|
||||||
]
|
]
|
||||||
++ lib.optionals stdenv.isLinux [(pkgs.util-linuxMinimal or pkgs.utillinuxMinimal)];
|
++ lib.optionals stdenv.hostPlatform.isLinux [(buildPackages.util-linuxMinimal or buildPackages.utillinuxMinimal)];
|
||||||
|
|
||||||
buildDeps =
|
buildDeps =
|
||||||
[ curl
|
[ curl
|
||||||
|
@ -93,7 +95,7 @@
|
||||||
]
|
]
|
||||||
++ lib.optionals stdenv.isLinux [libseccomp]
|
++ lib.optionals stdenv.isLinux [libseccomp]
|
||||||
++ lib.optional (stdenv.isLinux || stdenv.isDarwin) libsodium
|
++ lib.optional (stdenv.isLinux || stdenv.isDarwin) libsodium
|
||||||
++ lib.optional stdenv.isx86_64 libcpuid;
|
++ lib.optional stdenv.hostPlatform.isx86_64 libcpuid;
|
||||||
|
|
||||||
awsDeps = lib.optional (stdenv.isLinux || stdenv.isDarwin)
|
awsDeps = lib.optional (stdenv.isLinux || stdenv.isDarwin)
|
||||||
(aws-sdk-cpp.override {
|
(aws-sdk-cpp.override {
|
||||||
|
@ -102,7 +104,13 @@
|
||||||
});
|
});
|
||||||
|
|
||||||
propagatedDeps =
|
propagatedDeps =
|
||||||
[ (boehmgc.override { enableLargeConfig = true; })
|
[ ((boehmgc.override {
|
||||||
|
enableLargeConfig = true;
|
||||||
|
}).overrideAttrs(o: {
|
||||||
|
patches = (o.patches or []) ++ [
|
||||||
|
./boehmgc-coroutine-sp-fallback.diff
|
||||||
|
];
|
||||||
|
}))
|
||||||
];
|
];
|
||||||
|
|
||||||
perlDeps =
|
perlDeps =
|
||||||
|
@ -133,10 +141,11 @@
|
||||||
|
|
||||||
substitute ${./scripts/install.in} $out/install \
|
substitute ${./scripts/install.in} $out/install \
|
||||||
${pkgs.lib.concatMapStrings
|
${pkgs.lib.concatMapStrings
|
||||||
(system:
|
(system: let
|
||||||
'' \
|
tarball = if builtins.elem system crossSystems then self.hydraJobs.binaryTarballCross.x86_64-linux.${system} else self.hydraJobs.binaryTarball.${system};
|
||||||
--replace '@tarballHash_${system}@' $(nix --experimental-features nix-command hash-file --base16 --type sha256 ${self.hydraJobs.binaryTarball.${system}}/*.tar.xz) \
|
in '' \
|
||||||
--replace '@tarballPath_${system}@' $(tarballPath ${self.hydraJobs.binaryTarball.${system}}/*.tar.xz) \
|
--replace '@tarballHash_${system}@' $(nix --experimental-features nix-command hash-file --base16 --type sha256 ${tarball}/*.tar.xz) \
|
||||||
|
--replace '@tarballPath_${system}@' $(tarballPath ${tarball}/*.tar.xz) \
|
||||||
''
|
''
|
||||||
)
|
)
|
||||||
systems
|
systems
|
||||||
|
@ -174,6 +183,77 @@
|
||||||
|
|
||||||
};
|
};
|
||||||
|
|
||||||
|
binaryTarball = buildPackages: nix: pkgs: let
|
||||||
|
inherit (pkgs) cacert;
|
||||||
|
installerClosureInfo = buildPackages.closureInfo { rootPaths = [ nix cacert ]; };
|
||||||
|
in
|
||||||
|
|
||||||
|
buildPackages.runCommand "nix-binary-tarball-${version}"
|
||||||
|
{ #nativeBuildInputs = lib.optional (system != "aarch64-linux") shellcheck;
|
||||||
|
meta.description = "Distribution-independent Nix bootstrap binaries for ${pkgs.system}";
|
||||||
|
}
|
||||||
|
''
|
||||||
|
cp ${installerClosureInfo}/registration $TMPDIR/reginfo
|
||||||
|
cp ${./scripts/create-darwin-volume.sh} $TMPDIR/create-darwin-volume.sh
|
||||||
|
substitute ${./scripts/install-nix-from-closure.sh} $TMPDIR/install \
|
||||||
|
--subst-var-by nix ${nix} \
|
||||||
|
--subst-var-by cacert ${cacert}
|
||||||
|
|
||||||
|
substitute ${./scripts/install-darwin-multi-user.sh} $TMPDIR/install-darwin-multi-user.sh \
|
||||||
|
--subst-var-by nix ${nix} \
|
||||||
|
--subst-var-by cacert ${cacert}
|
||||||
|
substitute ${./scripts/install-systemd-multi-user.sh} $TMPDIR/install-systemd-multi-user.sh \
|
||||||
|
--subst-var-by nix ${nix} \
|
||||||
|
--subst-var-by cacert ${cacert}
|
||||||
|
substitute ${./scripts/install-multi-user.sh} $TMPDIR/install-multi-user \
|
||||||
|
--subst-var-by nix ${nix} \
|
||||||
|
--subst-var-by cacert ${cacert}
|
||||||
|
|
||||||
|
if type -p shellcheck; then
|
||||||
|
# SC1090: Don't worry about not being able to find
|
||||||
|
# $nix/etc/profile.d/nix.sh
|
||||||
|
shellcheck --exclude SC1090 $TMPDIR/install
|
||||||
|
shellcheck $TMPDIR/create-darwin-volume.sh
|
||||||
|
shellcheck $TMPDIR/install-darwin-multi-user.sh
|
||||||
|
shellcheck $TMPDIR/install-systemd-multi-user.sh
|
||||||
|
|
||||||
|
# SC1091: Don't panic about not being able to source
|
||||||
|
# /etc/profile
|
||||||
|
# SC2002: Ignore "useless cat" "error", when loading
|
||||||
|
# .reginfo, as the cat is a much cleaner
|
||||||
|
# implementation, even though it is "useless"
|
||||||
|
# SC2116: Allow ROOT_HOME=$(echo ~root) for resolving
|
||||||
|
# root's home directory
|
||||||
|
shellcheck --external-sources \
|
||||||
|
--exclude SC1091,SC2002,SC2116 $TMPDIR/install-multi-user
|
||||||
|
fi
|
||||||
|
|
||||||
|
chmod +x $TMPDIR/install
|
||||||
|
chmod +x $TMPDIR/create-darwin-volume.sh
|
||||||
|
chmod +x $TMPDIR/install-darwin-multi-user.sh
|
||||||
|
chmod +x $TMPDIR/install-systemd-multi-user.sh
|
||||||
|
chmod +x $TMPDIR/install-multi-user
|
||||||
|
dir=nix-${version}-${pkgs.system}
|
||||||
|
fn=$out/$dir.tar.xz
|
||||||
|
mkdir -p $out/nix-support
|
||||||
|
echo "file binary-dist $fn" >> $out/nix-support/hydra-build-products
|
||||||
|
tar cvfJ $fn \
|
||||||
|
--owner=0 --group=0 --mode=u+rw,uga+r \
|
||||||
|
--absolute-names \
|
||||||
|
--hard-dereference \
|
||||||
|
--transform "s,$TMPDIR/install,$dir/install," \
|
||||||
|
--transform "s,$TMPDIR/create-darwin-volume.sh,$dir/create-darwin-volume.sh," \
|
||||||
|
--transform "s,$TMPDIR/reginfo,$dir/.reginfo," \
|
||||||
|
--transform "s,$NIX_STORE,$dir/store,S" \
|
||||||
|
$TMPDIR/install \
|
||||||
|
$TMPDIR/create-darwin-volume.sh \
|
||||||
|
$TMPDIR/install-darwin-multi-user.sh \
|
||||||
|
$TMPDIR/install-systemd-multi-user.sh \
|
||||||
|
$TMPDIR/install-multi-user \
|
||||||
|
$TMPDIR/reginfo \
|
||||||
|
$(cat ${installerClosureInfo}/store-paths)
|
||||||
|
'';
|
||||||
|
|
||||||
in {
|
in {
|
||||||
|
|
||||||
# A Nixpkgs overlay that overrides the 'nix' and
|
# A Nixpkgs overlay that overrides the 'nix' and
|
||||||
|
@ -285,7 +365,7 @@
|
||||||
|
|
||||||
outputs = [ "out" "bin" "dev" ];
|
outputs = [ "out" "bin" "dev" ];
|
||||||
|
|
||||||
nativeBuildInputs = [ which ];
|
nativeBuildInputs = [ buildPackages.which ];
|
||||||
|
|
||||||
configurePhase = ''
|
configurePhase = ''
|
||||||
${if (stdenv.isDarwin && stdenv.isAarch64) then "echo \"HAVE_SANDBOX_INIT=false\" > configure.local" else ""}
|
${if (stdenv.isDarwin && stdenv.isAarch64) then "echo \"HAVE_SANDBOX_INIT=false\" > configure.local" else ""}
|
||||||
|
@ -304,92 +384,33 @@
|
||||||
|
|
||||||
buildStatic = nixpkgs.lib.genAttrs linux64BitSystems (system: self.packages.${system}.nix-static);
|
buildStatic = nixpkgs.lib.genAttrs linux64BitSystems (system: self.packages.${system}.nix-static);
|
||||||
|
|
||||||
|
buildCross = nixpkgs.lib.genAttrs crossSystems (crossSystem:
|
||||||
|
nixpkgs.lib.genAttrs ["x86_64-linux"] (system: self.packages.${system}."nix-${crossSystem}"));
|
||||||
|
|
||||||
# Perl bindings for various platforms.
|
# Perl bindings for various platforms.
|
||||||
perlBindings = nixpkgs.lib.genAttrs systems (system: self.packages.${system}.nix.perl-bindings);
|
perlBindings = nixpkgs.lib.genAttrs systems (system: self.packages.${system}.nix.perl-bindings);
|
||||||
|
|
||||||
# Binary tarball for various platforms, containing a Nix store
|
# Binary tarball for various platforms, containing a Nix store
|
||||||
# with the closure of 'nix' package, and the second half of
|
# with the closure of 'nix' package, and the second half of
|
||||||
# the installation script.
|
# the installation script.
|
||||||
binaryTarball = nixpkgs.lib.genAttrs systems (system:
|
binaryTarball = nixpkgs.lib.genAttrs systems (system: binaryTarball nixpkgsFor.${system} nixpkgsFor.${system}.nix nixpkgsFor.${system});
|
||||||
|
|
||||||
with nixpkgsFor.${system};
|
binaryTarballCross = nixpkgs.lib.genAttrs ["x86_64-linux"] (system: builtins.listToAttrs (map (crossSystem: {
|
||||||
|
name = crossSystem;
|
||||||
let
|
value = let
|
||||||
installerClosureInfo = closureInfo { rootPaths = [ nix cacert ]; };
|
nixpkgsCross = import nixpkgs {
|
||||||
in
|
inherit system crossSystem;
|
||||||
|
overlays = [ self.overlay ];
|
||||||
runCommand "nix-binary-tarball-${version}"
|
};
|
||||||
{ #nativeBuildInputs = lib.optional (system != "aarch64-linux") shellcheck;
|
in binaryTarball nixpkgsFor.${system} self.packages.${system}."nix-${crossSystem}" nixpkgsCross;
|
||||||
meta.description = "Distribution-independent Nix bootstrap binaries for ${system}";
|
}) crossSystems));
|
||||||
}
|
|
||||||
''
|
|
||||||
cp ${installerClosureInfo}/registration $TMPDIR/reginfo
|
|
||||||
cp ${./scripts/create-darwin-volume.sh} $TMPDIR/create-darwin-volume.sh
|
|
||||||
substitute ${./scripts/install-nix-from-closure.sh} $TMPDIR/install \
|
|
||||||
--subst-var-by nix ${nix} \
|
|
||||||
--subst-var-by cacert ${cacert}
|
|
||||||
|
|
||||||
substitute ${./scripts/install-darwin-multi-user.sh} $TMPDIR/install-darwin-multi-user.sh \
|
|
||||||
--subst-var-by nix ${nix} \
|
|
||||||
--subst-var-by cacert ${cacert}
|
|
||||||
substitute ${./scripts/install-systemd-multi-user.sh} $TMPDIR/install-systemd-multi-user.sh \
|
|
||||||
--subst-var-by nix ${nix} \
|
|
||||||
--subst-var-by cacert ${cacert}
|
|
||||||
substitute ${./scripts/install-multi-user.sh} $TMPDIR/install-multi-user \
|
|
||||||
--subst-var-by nix ${nix} \
|
|
||||||
--subst-var-by cacert ${cacert}
|
|
||||||
|
|
||||||
if type -p shellcheck; then
|
|
||||||
# SC1090: Don't worry about not being able to find
|
|
||||||
# $nix/etc/profile.d/nix.sh
|
|
||||||
shellcheck --exclude SC1090 $TMPDIR/install
|
|
||||||
shellcheck $TMPDIR/create-darwin-volume.sh
|
|
||||||
shellcheck $TMPDIR/install-darwin-multi-user.sh
|
|
||||||
shellcheck $TMPDIR/install-systemd-multi-user.sh
|
|
||||||
|
|
||||||
# SC1091: Don't panic about not being able to source
|
|
||||||
# /etc/profile
|
|
||||||
# SC2002: Ignore "useless cat" "error", when loading
|
|
||||||
# .reginfo, as the cat is a much cleaner
|
|
||||||
# implementation, even though it is "useless"
|
|
||||||
# SC2116: Allow ROOT_HOME=$(echo ~root) for resolving
|
|
||||||
# root's home directory
|
|
||||||
shellcheck --external-sources \
|
|
||||||
--exclude SC1091,SC2002,SC2116 $TMPDIR/install-multi-user
|
|
||||||
fi
|
|
||||||
|
|
||||||
chmod +x $TMPDIR/install
|
|
||||||
chmod +x $TMPDIR/create-darwin-volume.sh
|
|
||||||
chmod +x $TMPDIR/install-darwin-multi-user.sh
|
|
||||||
chmod +x $TMPDIR/install-systemd-multi-user.sh
|
|
||||||
chmod +x $TMPDIR/install-multi-user
|
|
||||||
dir=nix-${version}-${system}
|
|
||||||
fn=$out/$dir.tar.xz
|
|
||||||
mkdir -p $out/nix-support
|
|
||||||
echo "file binary-dist $fn" >> $out/nix-support/hydra-build-products
|
|
||||||
tar cvfJ $fn \
|
|
||||||
--owner=0 --group=0 --mode=u+rw,uga+r \
|
|
||||||
--absolute-names \
|
|
||||||
--hard-dereference \
|
|
||||||
--transform "s,$TMPDIR/install,$dir/install," \
|
|
||||||
--transform "s,$TMPDIR/create-darwin-volume.sh,$dir/create-darwin-volume.sh," \
|
|
||||||
--transform "s,$TMPDIR/reginfo,$dir/.reginfo," \
|
|
||||||
--transform "s,$NIX_STORE,$dir/store,S" \
|
|
||||||
$TMPDIR/install \
|
|
||||||
$TMPDIR/create-darwin-volume.sh \
|
|
||||||
$TMPDIR/install-darwin-multi-user.sh \
|
|
||||||
$TMPDIR/install-systemd-multi-user.sh \
|
|
||||||
$TMPDIR/install-multi-user \
|
|
||||||
$TMPDIR/reginfo \
|
|
||||||
$(cat ${installerClosureInfo}/store-paths)
|
|
||||||
'');
|
|
||||||
|
|
||||||
# The first half of the installation script. This is uploaded
|
# The first half of the installation script. This is uploaded
|
||||||
# to https://nixos.org/nix/install. It downloads the binary
|
# to https://nixos.org/nix/install. It downloads the binary
|
||||||
# tarball for the user's system and calls the second half of the
|
# tarball for the user's system and calls the second half of the
|
||||||
# installation script.
|
# installation script.
|
||||||
installerScript = installScriptFor [ "x86_64-linux" "i686-linux" "aarch64-linux" "x86_64-darwin" "aarch64-darwin" ];
|
installerScript = installScriptFor [ "x86_64-linux" "i686-linux" "aarch64-linux" "x86_64-darwin" "aarch64-darwin" "armv6l-linux" "armv7l-linux" ];
|
||||||
installerScriptForGHA = installScriptFor [ "x86_64-linux" "x86_64-darwin" ];
|
installerScriptForGHA = installScriptFor [ "x86_64-linux" "x86_64-darwin" "armv6l-linux" "armv7l-linux"];
|
||||||
|
|
||||||
# Line coverage analysis.
|
# Line coverage analysis.
|
||||||
coverage =
|
coverage =
|
||||||
|
@ -483,11 +504,14 @@
|
||||||
# `NIX_DAEMON_SOCKET_PATH` which is required for the tests to work
|
# `NIX_DAEMON_SOCKET_PATH` which is required for the tests to work
|
||||||
# againstLatestStable = testNixVersions pkgs pkgs.nix pkgs.nixStable;
|
# againstLatestStable = testNixVersions pkgs pkgs.nix pkgs.nixStable;
|
||||||
} "touch $out";
|
} "touch $out";
|
||||||
});
|
} // (if system == "x86_64-linux" then (builtins.listToAttrs (map (crossSystem: {
|
||||||
|
name = "binaryTarball-${crossSystem}";
|
||||||
|
value = self.hydraJobs.binaryTarballCross.${system}.${crossSystem};
|
||||||
|
}) crossSystems)) else {}));
|
||||||
|
|
||||||
packages = forAllSystems (system: {
|
packages = forAllSystems (system: {
|
||||||
inherit (nixpkgsFor.${system}) nix;
|
inherit (nixpkgsFor.${system}) nix;
|
||||||
} // nixpkgs.lib.optionalAttrs (builtins.elem system linux64BitSystems) {
|
} // (nixpkgs.lib.optionalAttrs (builtins.elem system linux64BitSystems) {
|
||||||
nix-static = let
|
nix-static = let
|
||||||
nixpkgs = nixpkgsFor.${system}.pkgsStatic;
|
nixpkgs = nixpkgsFor.${system}.pkgsStatic;
|
||||||
in with commonDeps nixpkgs; nixpkgs.stdenv.mkDerivation {
|
in with commonDeps nixpkgs; nixpkgs.stdenv.mkDerivation {
|
||||||
|
@ -525,8 +549,49 @@
|
||||||
stripAllList = ["bin"];
|
stripAllList = ["bin"];
|
||||||
|
|
||||||
strictDeps = true;
|
strictDeps = true;
|
||||||
|
|
||||||
|
hardeningDisable = [ "pie" ];
|
||||||
};
|
};
|
||||||
});
|
} // builtins.listToAttrs (map (crossSystem: {
|
||||||
|
name = "nix-${crossSystem}";
|
||||||
|
value = let
|
||||||
|
nixpkgsCross = import nixpkgs {
|
||||||
|
inherit system crossSystem;
|
||||||
|
overlays = [ self.overlay ];
|
||||||
|
};
|
||||||
|
in with commonDeps nixpkgsCross; nixpkgsCross.stdenv.mkDerivation {
|
||||||
|
name = "nix-${version}";
|
||||||
|
|
||||||
|
src = self;
|
||||||
|
|
||||||
|
VERSION_SUFFIX = versionSuffix;
|
||||||
|
|
||||||
|
outputs = [ "out" "dev" "doc" ];
|
||||||
|
|
||||||
|
nativeBuildInputs = nativeBuildDeps;
|
||||||
|
buildInputs = buildDeps ++ propagatedDeps;
|
||||||
|
|
||||||
|
configureFlags = [ "--sysconfdir=/etc" "--disable-doc-gen" ];
|
||||||
|
|
||||||
|
enableParallelBuilding = true;
|
||||||
|
|
||||||
|
makeFlags = "profiledir=$(out)/etc/profile.d";
|
||||||
|
|
||||||
|
doCheck = true;
|
||||||
|
|
||||||
|
installFlags = "sysconfdir=$(out)/etc";
|
||||||
|
|
||||||
|
postInstall = ''
|
||||||
|
mkdir -p $doc/nix-support
|
||||||
|
echo "doc manual $doc/share/doc/nix/manual" >> $doc/nix-support/hydra-build-products
|
||||||
|
mkdir -p $out/nix-support
|
||||||
|
echo "file binary-dist $out/bin/nix" >> $out/nix-support/hydra-build-products
|
||||||
|
'';
|
||||||
|
|
||||||
|
doInstallCheck = true;
|
||||||
|
installCheckFlags = "sysconfdir=$(out)/etc";
|
||||||
|
};
|
||||||
|
}) crossSystems)));
|
||||||
|
|
||||||
defaultPackage = forAllSystems (system: self.packages.${system}.nix);
|
defaultPackage = forAllSystems (system: self.packages.${system}.nix);
|
||||||
|
|
||||||
|
|
|
@ -110,6 +110,9 @@ downloadFile("binaryTarball.i686-linux", "1");
|
||||||
downloadFile("binaryTarball.x86_64-linux", "1");
|
downloadFile("binaryTarball.x86_64-linux", "1");
|
||||||
downloadFile("binaryTarball.aarch64-linux", "1");
|
downloadFile("binaryTarball.aarch64-linux", "1");
|
||||||
downloadFile("binaryTarball.x86_64-darwin", "1");
|
downloadFile("binaryTarball.x86_64-darwin", "1");
|
||||||
|
downloadFile("binaryTarball.aarch64-darwin", "1");
|
||||||
|
downloadFile("binaryTarballCross.x86_64-linux.armv6l-linux", "1");
|
||||||
|
downloadFile("binaryTarballCross.x86_64-linux.armv7l-linux", "1");
|
||||||
downloadFile("installerScript", "1");
|
downloadFile("installerScript", "1");
|
||||||
|
|
||||||
for my $fn (glob "$tmpDir/*") {
|
for my $fn (glob "$tmpDir/*") {
|
||||||
|
@ -153,6 +156,7 @@ write_file("$nixpkgsDir/nixos/modules/installer/tools/nix-fallback-paths.nix",
|
||||||
" i686-linux = \"" . getStorePath("build.i686-linux") . "\";\n" .
|
" i686-linux = \"" . getStorePath("build.i686-linux") . "\";\n" .
|
||||||
" aarch64-linux = \"" . getStorePath("build.aarch64-linux") . "\";\n" .
|
" aarch64-linux = \"" . getStorePath("build.aarch64-linux") . "\";\n" .
|
||||||
" x86_64-darwin = \"" . getStorePath("build.x86_64-darwin") . "\";\n" .
|
" x86_64-darwin = \"" . getStorePath("build.x86_64-darwin") . "\";\n" .
|
||||||
|
" aarch64-darwin = \"" . getStorePath("build.aarch64-darwin") . "\";\n" .
|
||||||
"}\n");
|
"}\n");
|
||||||
|
|
||||||
system("cd $nixpkgsDir && git commit -a -m 'nix-fallback-paths.nix: Update to $version'") == 0 or die;
|
system("cd $nixpkgsDir && git commit -a -m 'nix-fallback-paths.nix: Update to $version'") == 0 or die;
|
||||||
|
|
37
misc/fish/completion.fish
Normal file
37
misc/fish/completion.fish
Normal file
|
@ -0,0 +1,37 @@
|
||||||
|
function _nix_complete
|
||||||
|
# Get the current command up to a cursor.
|
||||||
|
# - Behaves correctly even with pipes and nested in commands like env.
|
||||||
|
# - TODO: Returns the command verbatim (does not interpolate variables).
|
||||||
|
# That might not be optimal for arguments like -f.
|
||||||
|
set -l nix_args (commandline --current-process --tokenize --cut-at-cursor)
|
||||||
|
# --cut-at-cursor with --tokenize removes the current token so we need to add it separately.
|
||||||
|
# https://github.com/fish-shell/fish-shell/issues/7375
|
||||||
|
# Can be an empty string.
|
||||||
|
set -l current_token (commandline --current-token --cut-at-cursor)
|
||||||
|
|
||||||
|
# Nix wants the index of the argv item to complete but the $nix_args variable
|
||||||
|
# also contains the program name (argv[0]) so we would need to subtract 1.
|
||||||
|
# But the variable also misses the current token so it cancels out.
|
||||||
|
set -l nix_arg_to_complete (count $nix_args)
|
||||||
|
|
||||||
|
env NIX_GET_COMPLETIONS=$nix_arg_to_complete $nix_args $current_token
|
||||||
|
end
|
||||||
|
|
||||||
|
function _nix_accepts_files
|
||||||
|
set -l response (_nix_complete)
|
||||||
|
# First line is either filenames or no-filenames.
|
||||||
|
test $response[1] = 'filenames'
|
||||||
|
end
|
||||||
|
|
||||||
|
function _nix
|
||||||
|
set -l response (_nix_complete)
|
||||||
|
# Skip the first line since it handled by _nix_accepts_files.
|
||||||
|
# Tail lines each contain a command followed by a tab character and, optionally, a description.
|
||||||
|
# This is also the format fish expects.
|
||||||
|
string collect -- $response[2..-1]
|
||||||
|
end
|
||||||
|
|
||||||
|
# Disable file path completion if paths do not belong in the current context.
|
||||||
|
complete --command nix --condition 'not _nix_accepts_files' --no-files
|
||||||
|
|
||||||
|
complete --command nix --arguments '(_nix)'
|
1
misc/fish/local.mk
Normal file
1
misc/fish/local.mk
Normal file
|
@ -0,0 +1 @@
|
||||||
|
$(eval $(call install-file-as, $(d)/completion.fish, $(datarootdir)/fish/vendor_completions.d/nix.fish, 0644))
|
|
@ -1,4 +1,4 @@
|
||||||
ifeq ($(OS), Darwin)
|
ifdef HOST_DARWIN
|
||||||
|
|
||||||
$(eval $(call install-data-in, $(d)/org.nixos.nix-daemon.plist, $(prefix)/Library/LaunchDaemons))
|
$(eval $(call install-data-in, $(d)/org.nixos.nix-daemon.plist, $(prefix)/Library/LaunchDaemons))
|
||||||
|
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
ifeq ($(OS), Linux)
|
ifdef HOST_LINUX
|
||||||
|
|
||||||
$(foreach n, nix-daemon.socket nix-daemon.service, $(eval $(call install-file-in, $(d)/$(n), $(prefix)/lib/systemd/system, 0644)))
|
$(foreach n, nix-daemon.socket nix-daemon.service, $(eval $(call install-file-in, $(d)/$(n), $(prefix)/lib/systemd/system, 0644)))
|
||||||
|
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
ifeq ($(OS), Linux)
|
ifdef HOST_LINUX
|
||||||
|
|
||||||
$(foreach n, nix-daemon.conf, $(eval $(call install-file-in, $(d)/$(n), $(sysconfdir)/init, 0644)))
|
$(foreach n, nix-daemon.conf, $(eval $(call install-file-in, $(d)/$(n), $(sysconfdir)/init, 0644)))
|
||||||
|
|
||||||
|
|
|
@ -1,3 +1,5 @@
|
||||||
|
#compdef nix
|
||||||
|
|
||||||
function _nix() {
|
function _nix() {
|
||||||
local ifs_bk="$IFS"
|
local ifs_bk="$IFS"
|
||||||
local input=("${(Q)words[@]}")
|
local input=("${(Q)words[@]}")
|
||||||
|
@ -18,4 +20,4 @@ function _nix() {
|
||||||
_describe 'nix' suggestions
|
_describe 'nix' suggestions
|
||||||
}
|
}
|
||||||
|
|
||||||
compdef _nix nix
|
_nix "$@"
|
||||||
|
|
27
mk/lib.mk
27
mk/lib.mk
|
@ -10,8 +10,25 @@ bin-scripts :=
|
||||||
noinst-scripts :=
|
noinst-scripts :=
|
||||||
man-pages :=
|
man-pages :=
|
||||||
install-tests :=
|
install-tests :=
|
||||||
OS = $(shell uname -s)
|
|
||||||
|
|
||||||
|
ifdef HOST_OS
|
||||||
|
HOST_KERNEL = $(firstword $(subst -, ,$(HOST_OS)))
|
||||||
|
ifeq ($(HOST_KERNEL), cygwin)
|
||||||
|
HOST_CYGWIN = 1
|
||||||
|
endif
|
||||||
|
ifeq ($(patsubst darwin%,,$(HOST_KERNEL)),)
|
||||||
|
HOST_DARWIN = 1
|
||||||
|
endif
|
||||||
|
ifeq ($(patsubst freebsd%,,$(HOST_KERNEL)),)
|
||||||
|
HOST_FREEBSD = 1
|
||||||
|
endif
|
||||||
|
ifeq ($(HOST_KERNEL), linux)
|
||||||
|
HOST_LINUX = 1
|
||||||
|
endif
|
||||||
|
ifeq ($(patsubst solaris%,,$(HOST_KERNEL)),)
|
||||||
|
HOST_SOLARIS = 1
|
||||||
|
endif
|
||||||
|
endif
|
||||||
|
|
||||||
# Hack to define a literal space.
|
# Hack to define a literal space.
|
||||||
space :=
|
space :=
|
||||||
|
@ -50,16 +67,16 @@ endif
|
||||||
BUILD_SHARED_LIBS ?= 1
|
BUILD_SHARED_LIBS ?= 1
|
||||||
|
|
||||||
ifeq ($(BUILD_SHARED_LIBS), 1)
|
ifeq ($(BUILD_SHARED_LIBS), 1)
|
||||||
ifeq (CYGWIN,$(findstring CYGWIN,$(OS)))
|
ifdef HOST_CYGWIN
|
||||||
GLOBAL_CFLAGS += -U__STRICT_ANSI__ -D_GNU_SOURCE
|
GLOBAL_CFLAGS += -U__STRICT_ANSI__ -D_GNU_SOURCE
|
||||||
GLOBAL_CXXFLAGS += -U__STRICT_ANSI__ -D_GNU_SOURCE
|
GLOBAL_CXXFLAGS += -U__STRICT_ANSI__ -D_GNU_SOURCE
|
||||||
else
|
else
|
||||||
GLOBAL_CFLAGS += -fPIC
|
GLOBAL_CFLAGS += -fPIC
|
||||||
GLOBAL_CXXFLAGS += -fPIC
|
GLOBAL_CXXFLAGS += -fPIC
|
||||||
endif
|
endif
|
||||||
ifneq ($(OS), Darwin)
|
ifndef HOST_DARWIN
|
||||||
ifneq ($(OS), SunOS)
|
ifndef HOST_SOLARIS
|
||||||
ifneq ($(OS), FreeBSD)
|
ifndef HOST_FREEBSD
|
||||||
GLOBAL_LDFLAGS += -Wl,--no-copy-dt-needed-entries
|
GLOBAL_LDFLAGS += -Wl,--no-copy-dt-needed-entries
|
||||||
endif
|
endif
|
||||||
endif
|
endif
|
||||||
|
|
|
@ -1,9 +1,9 @@
|
||||||
libs-list :=
|
libs-list :=
|
||||||
|
|
||||||
ifeq ($(OS), Darwin)
|
ifdef HOST_DARWIN
|
||||||
SO_EXT = dylib
|
SO_EXT = dylib
|
||||||
else
|
else
|
||||||
ifeq (CYGWIN,$(findstring CYGWIN,$(OS)))
|
ifdef HOST_CYGWIN
|
||||||
SO_EXT = dll
|
SO_EXT = dll
|
||||||
else
|
else
|
||||||
SO_EXT = so
|
SO_EXT = so
|
||||||
|
@ -59,7 +59,7 @@ define build-library
|
||||||
$(1)_OBJS := $$(addprefix $(buildprefix), $$(addsuffix .o, $$(basename $$(_srcs))))
|
$(1)_OBJS := $$(addprefix $(buildprefix), $$(addsuffix .o, $$(basename $$(_srcs))))
|
||||||
_libs := $$(foreach lib, $$($(1)_LIBS), $$($$(lib)_PATH))
|
_libs := $$(foreach lib, $$($(1)_LIBS), $$($$(lib)_PATH))
|
||||||
|
|
||||||
ifeq (CYGWIN,$(findstring CYGWIN,$(OS)))
|
ifdef HOST_CYGWIN
|
||||||
$(1)_INSTALL_DIR ?= $$(bindir)
|
$(1)_INSTALL_DIR ?= $$(bindir)
|
||||||
else
|
else
|
||||||
$(1)_INSTALL_DIR ?= $$(libdir)
|
$(1)_INSTALL_DIR ?= $$(libdir)
|
||||||
|
@ -73,18 +73,18 @@ define build-library
|
||||||
ifeq ($(BUILD_SHARED_LIBS), 1)
|
ifeq ($(BUILD_SHARED_LIBS), 1)
|
||||||
|
|
||||||
ifdef $(1)_ALLOW_UNDEFINED
|
ifdef $(1)_ALLOW_UNDEFINED
|
||||||
ifeq ($(OS), Darwin)
|
ifdef HOST_DARWIN
|
||||||
$(1)_LDFLAGS += -undefined suppress -flat_namespace
|
$(1)_LDFLAGS += -undefined suppress -flat_namespace
|
||||||
endif
|
endif
|
||||||
else
|
else
|
||||||
ifneq ($(OS), Darwin)
|
ifndef HOST_DARWIN
|
||||||
ifneq (CYGWIN,$(findstring CYGWIN,$(OS)))
|
ifndef HOST_CYGWIN
|
||||||
$(1)_LDFLAGS += -Wl,-z,defs
|
$(1)_LDFLAGS += -Wl,-z,defs
|
||||||
endif
|
endif
|
||||||
endif
|
endif
|
||||||
endif
|
endif
|
||||||
|
|
||||||
ifneq ($(OS), Darwin)
|
ifndef HOST_DARWIN
|
||||||
$(1)_LDFLAGS += -Wl,-soname=$$($(1)_NAME).$(SO_EXT)
|
$(1)_LDFLAGS += -Wl,-soname=$$($(1)_NAME).$(SO_EXT)
|
||||||
endif
|
endif
|
||||||
|
|
||||||
|
@ -93,7 +93,7 @@ define build-library
|
||||||
$$($(1)_PATH): $$($(1)_OBJS) $$(_libs) | $$(_d)/
|
$$($(1)_PATH): $$($(1)_OBJS) $$(_libs) | $$(_d)/
|
||||||
$$(trace-ld) $(CXX) -o $$(abspath $$@) -shared $$(LDFLAGS) $$(GLOBAL_LDFLAGS) $$($(1)_OBJS) $$($(1)_LDFLAGS) $$($(1)_LDFLAGS_PROPAGATED) $$(foreach lib, $$($(1)_LIBS), $$($$(lib)_LDFLAGS_USE)) $$($(1)_LDFLAGS_UNINSTALLED)
|
$$(trace-ld) $(CXX) -o $$(abspath $$@) -shared $$(LDFLAGS) $$(GLOBAL_LDFLAGS) $$($(1)_OBJS) $$($(1)_LDFLAGS) $$($(1)_LDFLAGS_PROPAGATED) $$(foreach lib, $$($(1)_LIBS), $$($$(lib)_LDFLAGS_USE)) $$($(1)_LDFLAGS_UNINSTALLED)
|
||||||
|
|
||||||
ifneq ($(OS), Darwin)
|
ifndef HOST_DARWIN
|
||||||
$(1)_LDFLAGS_USE += -Wl,-rpath,$$(abspath $$(_d))
|
$(1)_LDFLAGS_USE += -Wl,-rpath,$$(abspath $$(_d))
|
||||||
endif
|
endif
|
||||||
$(1)_LDFLAGS_USE += -L$$(_d) -l$$(patsubst lib%,%,$$(strip $$($(1)_NAME)))
|
$(1)_LDFLAGS_USE += -L$$(_d) -l$$(patsubst lib%,%,$$(strip $$($(1)_NAME)))
|
||||||
|
@ -108,7 +108,7 @@ define build-library
|
||||||
$$(trace-ld) $(CXX) -o $$@ -shared $$(LDFLAGS) $$(GLOBAL_LDFLAGS) $$($(1)_OBJS) $$($(1)_LDFLAGS) $$($(1)_LDFLAGS_PROPAGATED) $$(foreach lib, $$($(1)_LIBS), $$($$(lib)_LDFLAGS_USE_INSTALLED))
|
$$(trace-ld) $(CXX) -o $$@ -shared $$(LDFLAGS) $$(GLOBAL_LDFLAGS) $$($(1)_OBJS) $$($(1)_LDFLAGS) $$($(1)_LDFLAGS_PROPAGATED) $$(foreach lib, $$($(1)_LIBS), $$($$(lib)_LDFLAGS_USE_INSTALLED))
|
||||||
|
|
||||||
$(1)_LDFLAGS_USE_INSTALLED += -L$$(DESTDIR)$$($(1)_INSTALL_DIR) -l$$(patsubst lib%,%,$$(strip $$($(1)_NAME)))
|
$(1)_LDFLAGS_USE_INSTALLED += -L$$(DESTDIR)$$($(1)_INSTALL_DIR) -l$$(patsubst lib%,%,$$(strip $$($(1)_NAME)))
|
||||||
ifneq ($(OS), Darwin)
|
ifndef HOST_DARWIN
|
||||||
ifeq ($(SET_RPATH_TO_LIBS), 1)
|
ifeq ($(SET_RPATH_TO_LIBS), 1)
|
||||||
$(1)_LDFLAGS_USE_INSTALLED += -Wl,-rpath,$$($(1)_INSTALL_DIR)
|
$(1)_LDFLAGS_USE_INSTALLED += -Wl,-rpath,$$($(1)_INSTALL_DIR)
|
||||||
else
|
else
|
||||||
|
|
|
@ -11,12 +11,12 @@ libnixrust_INSTALL_PATH := $(libdir)/libnixrust.$(SO_EXT)
|
||||||
libnixrust_LDFLAGS_USE := -L$(d)/target/$(RUST_DIR) -lnixrust
|
libnixrust_LDFLAGS_USE := -L$(d)/target/$(RUST_DIR) -lnixrust
|
||||||
libnixrust_LDFLAGS_USE_INSTALLED := -L$(libdir) -lnixrust
|
libnixrust_LDFLAGS_USE_INSTALLED := -L$(libdir) -lnixrust
|
||||||
|
|
||||||
ifeq ($(OS), Linux)
|
ifdef HOST_LINUX
|
||||||
libnixrust_LDFLAGS_USE += -ldl
|
libnixrust_LDFLAGS_USE += -ldl
|
||||||
libnixrust_LDFLAGS_USE_INSTALLED += -ldl
|
libnixrust_LDFLAGS_USE_INSTALLED += -ldl
|
||||||
endif
|
endif
|
||||||
|
|
||||||
ifeq ($(OS), Darwin)
|
ifdef HOST_DARWIN
|
||||||
libnixrust_BUILD_FLAGS = NIX_LDFLAGS="-undefined dynamic_lookup"
|
libnixrust_BUILD_FLAGS = NIX_LDFLAGS="-undefined dynamic_lookup"
|
||||||
else
|
else
|
||||||
libnixrust_LDFLAGS_USE += -Wl,-rpath,$(abspath $(d)/target/$(RUST_DIR))
|
libnixrust_LDFLAGS_USE += -Wl,-rpath,$(abspath $(d)/target/$(RUST_DIR))
|
||||||
|
@ -31,7 +31,7 @@ $(libnixrust_PATH): $(call rwildcard, $(d)/src, *.rs) $(d)/Cargo.toml
|
||||||
|
|
||||||
$(libnixrust_INSTALL_PATH): $(libnixrust_PATH)
|
$(libnixrust_INSTALL_PATH): $(libnixrust_PATH)
|
||||||
$(target-gen) cp $^ $@
|
$(target-gen) cp $^ $@
|
||||||
ifeq ($(OS), Darwin)
|
ifdef HOST_DARWIN
|
||||||
install_name_tool -id $@ $@
|
install_name_tool -id $@ $@
|
||||||
endif
|
endif
|
||||||
|
|
||||||
|
@ -40,7 +40,7 @@ clean: clean-rust
|
||||||
clean-rust:
|
clean-rust:
|
||||||
$(suppress) rm -rfv nix-rust/target
|
$(suppress) rm -rfv nix-rust/target
|
||||||
|
|
||||||
ifneq ($(OS), Darwin)
|
ifndef HOST_DARWIN
|
||||||
check: rust-tests
|
check: rust-tests
|
||||||
|
|
||||||
rust-tests:
|
rust-tests:
|
||||||
|
|
|
@ -1,3 +1,4 @@
|
||||||
|
HOST_OS = @host_os@
|
||||||
CC = @CC@
|
CC = @CC@
|
||||||
CFLAGS = @CFLAGS@
|
CFLAGS = @CFLAGS@
|
||||||
CXX = @CXX@
|
CXX = @CXX@
|
||||||
|
|
|
@ -7,6 +7,8 @@ CXXFLAGS=
|
||||||
AC_PROG_CC
|
AC_PROG_CC
|
||||||
AC_PROG_CXX
|
AC_PROG_CXX
|
||||||
|
|
||||||
|
AC_CANONICAL_HOST
|
||||||
|
|
||||||
# Use 64-bit file system calls so that we can support files > 2 GiB.
|
# Use 64-bit file system calls so that we can support files > 2 GiB.
|
||||||
AC_SYS_LARGEFILE
|
AC_SYS_LARGEFILE
|
||||||
|
|
||||||
|
|
|
@ -28,7 +28,7 @@ Store_CXXFLAGS = \
|
||||||
|
|
||||||
Store_LDFLAGS := $(SODIUM_LIBS) $(NIX_LIBS)
|
Store_LDFLAGS := $(SODIUM_LIBS) $(NIX_LIBS)
|
||||||
|
|
||||||
ifeq (CYGWIN,$(findstring CYGWIN,$(OS)))
|
ifdef HOST_CYGWIN
|
||||||
archlib = $(shell perl -E 'use Config; print $$Config{archlib};')
|
archlib = $(shell perl -E 'use Config; print $$Config{archlib};')
|
||||||
libperl = $(shell perl -E 'use Config; print $$Config{libperl};')
|
libperl = $(shell perl -E 'use Config; print $$Config{libperl};')
|
||||||
Store_LDFLAGS += $(shell find ${archlib} -name ${libperl})
|
Store_LDFLAGS += $(shell find ${archlib} -name ${libperl})
|
||||||
|
|
|
@ -40,13 +40,23 @@ case "$(uname -s).$(uname -m)" in
|
||||||
path=@tarballPath_aarch64-linux@
|
path=@tarballPath_aarch64-linux@
|
||||||
system=aarch64-linux
|
system=aarch64-linux
|
||||||
;;
|
;;
|
||||||
|
Linux.armv6l_linux)
|
||||||
|
hash=@tarballHash_armv6l-linux@
|
||||||
|
path=@tarballPath_armv6l-linux@
|
||||||
|
system=armv6l-linux
|
||||||
|
;;
|
||||||
|
Linux.armv7l_linux)
|
||||||
|
hash=@tarballHash_armv7l-linux@
|
||||||
|
path=@tarballPath_armv7l-linux@
|
||||||
|
system=armv7l-linux
|
||||||
|
;;
|
||||||
Darwin.x86_64)
|
Darwin.x86_64)
|
||||||
hash=@tarballHash_x86_64-darwin@
|
hash=@tarballHash_x86_64-darwin@
|
||||||
path=@tarballPath_x86_64-darwin@
|
path=@tarballPath_x86_64-darwin@
|
||||||
system=x86_64-darwin
|
system=x86_64-darwin
|
||||||
;;
|
;;
|
||||||
Darwin.arm64|Darwin.aarch64)
|
Darwin.arm64|Darwin.aarch64)
|
||||||
hash=@binaryTarball_aarch64-darwin@
|
hash=@tarballHash_aarch64-darwin@
|
||||||
path=@tarballPath_aarch64-darwin@
|
path=@tarballPath_aarch64-darwin@
|
||||||
system=aarch64-darwin
|
system=aarch64-darwin
|
||||||
;;
|
;;
|
||||||
|
|
|
@ -277,7 +277,16 @@ connected:
|
||||||
|
|
||||||
auto drv = store->readDerivation(*drvPath);
|
auto drv = store->readDerivation(*drvPath);
|
||||||
auto outputHashes = staticOutputHashes(*store, drv);
|
auto outputHashes = staticOutputHashes(*store, drv);
|
||||||
drv.inputSrcs = store->parseStorePathSet(inputs);
|
|
||||||
|
// Hijack the inputs paths of the derivation to include all the paths
|
||||||
|
// that come from the `inputDrvs` set.
|
||||||
|
// We don’t do that for the derivations whose `inputDrvs` is empty
|
||||||
|
// because
|
||||||
|
// 1. It’s not needed
|
||||||
|
// 2. Changing the `inputSrcs` set changes the associated output ids,
|
||||||
|
// which break CA derivations
|
||||||
|
if (!drv.inputDrvs.empty())
|
||||||
|
drv.inputSrcs = store->parseStorePathSet(inputs);
|
||||||
|
|
||||||
auto result = sshStore->buildDerivation(*drvPath, drv);
|
auto result = sshStore->buildDerivation(*drvPath, drv);
|
||||||
|
|
||||||
|
|
|
@ -188,7 +188,7 @@ void MixProfile::updateProfile(const BuiltPaths & buildables)
|
||||||
}
|
}
|
||||||
|
|
||||||
if (result.size() != 1)
|
if (result.size() != 1)
|
||||||
throw Error("'--profile' requires that the arguments produce a single store path, but there are %d", result.size());
|
throw UsageError("'--profile' requires that the arguments produce a single store path, but there are %d", result.size());
|
||||||
|
|
||||||
updateProfile(result[0]);
|
updateProfile(result[0]);
|
||||||
}
|
}
|
||||||
|
|
|
@ -171,14 +171,50 @@ Strings SourceExprCommand::getDefaultFlakeAttrPathPrefixes()
|
||||||
|
|
||||||
void SourceExprCommand::completeInstallable(std::string_view prefix)
|
void SourceExprCommand::completeInstallable(std::string_view prefix)
|
||||||
{
|
{
|
||||||
if (file) return; // FIXME
|
if (file) {
|
||||||
|
evalSettings.pureEval = false;
|
||||||
|
auto state = getEvalState();
|
||||||
|
Expr *e = state->parseExprFromFile(
|
||||||
|
resolveExprPath(state->checkSourcePath(lookupFileArg(*state, *file)))
|
||||||
|
);
|
||||||
|
|
||||||
completeFlakeRefWithFragment(
|
Value root;
|
||||||
getEvalState(),
|
state->eval(e, root);
|
||||||
lockFlags,
|
|
||||||
getDefaultFlakeAttrPathPrefixes(),
|
auto autoArgs = getAutoArgs(*state);
|
||||||
getDefaultFlakeAttrPaths(),
|
|
||||||
prefix);
|
std::string prefix_ = std::string(prefix);
|
||||||
|
auto sep = prefix_.rfind('.');
|
||||||
|
std::string searchWord;
|
||||||
|
if (sep != std::string::npos) {
|
||||||
|
searchWord = prefix_.substr(sep, std::string::npos);
|
||||||
|
prefix_ = prefix_.substr(0, sep);
|
||||||
|
} else {
|
||||||
|
searchWord = prefix_;
|
||||||
|
prefix_ = "";
|
||||||
|
}
|
||||||
|
|
||||||
|
Value &v1(*findAlongAttrPath(*state, prefix_, *autoArgs, root).first);
|
||||||
|
state->forceValue(v1);
|
||||||
|
Value v2;
|
||||||
|
state->autoCallFunction(*autoArgs, v1, v2);
|
||||||
|
|
||||||
|
if (v2.type() == nAttrs) {
|
||||||
|
for (auto & i : *v2.attrs) {
|
||||||
|
std::string name = i.name;
|
||||||
|
if (name.find(searchWord) == 0) {
|
||||||
|
completions->add(i.name);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
completeFlakeRefWithFragment(
|
||||||
|
getEvalState(),
|
||||||
|
lockFlags,
|
||||||
|
getDefaultFlakeAttrPathPrefixes(),
|
||||||
|
getDefaultFlakeAttrPaths(),
|
||||||
|
prefix);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void completeFlakeRefWithFragment(
|
void completeFlakeRefWithFragment(
|
||||||
|
@ -573,10 +609,10 @@ InstallableFlake::getCursors(EvalState & state)
|
||||||
|
|
||||||
std::shared_ptr<flake::LockedFlake> InstallableFlake::getLockedFlake() const
|
std::shared_ptr<flake::LockedFlake> InstallableFlake::getLockedFlake() const
|
||||||
{
|
{
|
||||||
|
flake::LockFlags lockFlagsApplyConfig = lockFlags;
|
||||||
|
lockFlagsApplyConfig.applyNixConfig = true;
|
||||||
if (!_lockedFlake) {
|
if (!_lockedFlake) {
|
||||||
_lockedFlake = std::make_shared<flake::LockedFlake>(lockFlake(*state, flakeRef, lockFlags));
|
_lockedFlake = std::make_shared<flake::LockedFlake>(lockFlake(*state, flakeRef, lockFlagsApplyConfig));
|
||||||
_lockedFlake->flake.config.apply();
|
|
||||||
// FIXME: send new config to the daemon.
|
|
||||||
}
|
}
|
||||||
return _lockedFlake;
|
return _lockedFlake;
|
||||||
}
|
}
|
||||||
|
|
|
@ -19,7 +19,7 @@ static Strings parseAttrPath(std::string_view s)
|
||||||
++i;
|
++i;
|
||||||
while (1) {
|
while (1) {
|
||||||
if (i == s.end())
|
if (i == s.end())
|
||||||
throw Error("missing closing quote in selection path '%1%'", s);
|
throw ParseError("missing closing quote in selection path '%1%'", s);
|
||||||
if (*i == '"') break;
|
if (*i == '"') break;
|
||||||
cur.push_back(*i++);
|
cur.push_back(*i++);
|
||||||
}
|
}
|
||||||
|
@ -116,14 +116,14 @@ Pos findDerivationFilename(EvalState & state, Value & v, std::string what)
|
||||||
|
|
||||||
auto colon = pos.rfind(':');
|
auto colon = pos.rfind(':');
|
||||||
if (colon == std::string::npos)
|
if (colon == std::string::npos)
|
||||||
throw Error("cannot parse meta.position attribute '%s'", pos);
|
throw ParseError("cannot parse meta.position attribute '%s'", pos);
|
||||||
|
|
||||||
std::string filename(pos, 0, colon);
|
std::string filename(pos, 0, colon);
|
||||||
unsigned int lineno;
|
unsigned int lineno;
|
||||||
try {
|
try {
|
||||||
lineno = std::stoi(std::string(pos, colon + 1));
|
lineno = std::stoi(std::string(pos, colon + 1));
|
||||||
} catch (std::invalid_argument & e) {
|
} catch (std::invalid_argument & e) {
|
||||||
throw Error("cannot parse line number '%s'", pos);
|
throw ParseError("cannot parse line number '%s'", pos);
|
||||||
}
|
}
|
||||||
|
|
||||||
Symbol file = state.symbols.create(filename);
|
Symbol file = state.symbols.create(filename);
|
||||||
|
|
|
@ -64,7 +64,11 @@ static char * dupStringWithLen(const char * s, size_t size)
|
||||||
|
|
||||||
RootValue allocRootValue(Value * v)
|
RootValue allocRootValue(Value * v)
|
||||||
{
|
{
|
||||||
|
#if HAVE_BOEHMGC
|
||||||
return std::allocate_shared<Value *>(traceable_allocator<Value *>(), v);
|
return std::allocate_shared<Value *>(traceable_allocator<Value *>(), v);
|
||||||
|
#else
|
||||||
|
return std::make_shared<Value *>(v);
|
||||||
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@ -233,22 +237,34 @@ static void * oomHandler(size_t requested)
|
||||||
}
|
}
|
||||||
|
|
||||||
class BoehmGCStackAllocator : public StackAllocator {
|
class BoehmGCStackAllocator : public StackAllocator {
|
||||||
boost::coroutines2::protected_fixedsize_stack stack {
|
boost::coroutines2::protected_fixedsize_stack stack {
|
||||||
// We allocate 8 MB, the default max stack size on NixOS.
|
// We allocate 8 MB, the default max stack size on NixOS.
|
||||||
// A smaller stack might be quicker to allocate but reduces the stack
|
// A smaller stack might be quicker to allocate but reduces the stack
|
||||||
// depth available for source filter expressions etc.
|
// depth available for source filter expressions etc.
|
||||||
std::max(boost::context::stack_traits::default_size(), static_cast<std::size_t>(8 * 1024 * 1024))
|
std::max(boost::context::stack_traits::default_size(), static_cast<std::size_t>(8 * 1024 * 1024))
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// This is specific to boost::coroutines2::protected_fixedsize_stack.
|
||||||
|
// The stack protection page is included in sctx.size, so we have to
|
||||||
|
// subtract one page size from the stack size.
|
||||||
|
std::size_t pfss_usable_stack_size(boost::context::stack_context &sctx) {
|
||||||
|
return sctx.size - boost::context::stack_traits::page_size();
|
||||||
|
}
|
||||||
|
|
||||||
public:
|
public:
|
||||||
boost::context::stack_context allocate() override {
|
boost::context::stack_context allocate() override {
|
||||||
auto sctx = stack.allocate();
|
auto sctx = stack.allocate();
|
||||||
GC_add_roots(static_cast<char *>(sctx.sp) - sctx.size, sctx.sp);
|
|
||||||
|
// Stacks generally start at a high address and grow to lower addresses.
|
||||||
|
// Architectures that do the opposite are rare; in fact so rare that
|
||||||
|
// boost_routine does not implement it.
|
||||||
|
// So we subtract the stack size.
|
||||||
|
GC_add_roots(static_cast<char *>(sctx.sp) - pfss_usable_stack_size(sctx), sctx.sp);
|
||||||
return sctx;
|
return sctx;
|
||||||
}
|
}
|
||||||
|
|
||||||
void deallocate(boost::context::stack_context sctx) override {
|
void deallocate(boost::context::stack_context sctx) override {
|
||||||
GC_remove_roots(static_cast<char *>(sctx.sp) - sctx.size, sctx.sp);
|
GC_remove_roots(static_cast<char *>(sctx.sp) - pfss_usable_stack_size(sctx), sctx.sp);
|
||||||
stack.deallocate(sctx);
|
stack.deallocate(sctx);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -908,7 +924,7 @@ void EvalState::evalFile(const Path & path_, Value & v, bool mustBeTrivial)
|
||||||
// computation.
|
// computation.
|
||||||
if (mustBeTrivial &&
|
if (mustBeTrivial &&
|
||||||
!(dynamic_cast<ExprAttrs *>(e)))
|
!(dynamic_cast<ExprAttrs *>(e)))
|
||||||
throw Error("file '%s' must be an attribute set", path);
|
throw EvalError("file '%s' must be an attribute set", path);
|
||||||
eval(e, v);
|
eval(e, v);
|
||||||
} catch (Error & e) {
|
} catch (Error & e) {
|
||||||
addErrorTrace(e, "while evaluating the file '%1%':", path2);
|
addErrorTrace(e, "while evaluating the file '%1%':", path2);
|
||||||
|
|
|
@ -298,6 +298,11 @@ LockedFlake lockFlake(
|
||||||
|
|
||||||
auto flake = getFlake(state, topRef, lockFlags.useRegistries, flakeCache);
|
auto flake = getFlake(state, topRef, lockFlags.useRegistries, flakeCache);
|
||||||
|
|
||||||
|
if (lockFlags.applyNixConfig) {
|
||||||
|
flake.config.apply();
|
||||||
|
// FIXME: send new config to the daemon.
|
||||||
|
}
|
||||||
|
|
||||||
try {
|
try {
|
||||||
|
|
||||||
// FIXME: symlink attack
|
// FIXME: symlink attack
|
||||||
|
@ -359,7 +364,12 @@ LockedFlake lockFlake(
|
||||||
ancestors? */
|
ancestors? */
|
||||||
auto i = overrides.find(inputPath);
|
auto i = overrides.find(inputPath);
|
||||||
bool hasOverride = i != overrides.end();
|
bool hasOverride = i != overrides.end();
|
||||||
if (hasOverride) overridesUsed.insert(inputPath);
|
if (hasOverride) {
|
||||||
|
overridesUsed.insert(inputPath);
|
||||||
|
// Respect the “flakeness” of the input even if we
|
||||||
|
// override it
|
||||||
|
i->second.isFlake = input2.isFlake;
|
||||||
|
}
|
||||||
auto & input = hasOverride ? i->second : input2;
|
auto & input = hasOverride ? i->second : input2;
|
||||||
|
|
||||||
/* Resolve 'follows' later (since it may refer to an input
|
/* Resolve 'follows' later (since it may refer to an input
|
||||||
|
|
|
@ -104,6 +104,10 @@ struct LockFlags
|
||||||
references like 'nixpkgs'. */
|
references like 'nixpkgs'. */
|
||||||
bool useRegistries = true;
|
bool useRegistries = true;
|
||||||
|
|
||||||
|
/* Whether to apply flake's nixConfig attribute to the configuration */
|
||||||
|
|
||||||
|
bool applyNixConfig = false;
|
||||||
|
|
||||||
/* Whether mutable flake references (i.e. those without a Git
|
/* Whether mutable flake references (i.e. those without a Git
|
||||||
revision or similar) without a corresponding lock are
|
revision or similar) without a corresponding lock are
|
||||||
allowed. Mutable flake references with a lock are always
|
allowed. Mutable flake references with a lock are always
|
||||||
|
|
|
@ -16,7 +16,7 @@ libexpr_CXXFLAGS += -I src/libutil -I src/libstore -I src/libfetchers -I src/lib
|
||||||
libexpr_LIBS = libutil libstore libfetchers
|
libexpr_LIBS = libutil libstore libfetchers
|
||||||
|
|
||||||
libexpr_LDFLAGS = -lboost_context
|
libexpr_LDFLAGS = -lboost_context
|
||||||
ifeq ($(OS), Linux)
|
ifdef HOST_LINUX
|
||||||
libexpr_LDFLAGS += -ldl
|
libexpr_LDFLAGS += -ldl
|
||||||
endif
|
endif
|
||||||
|
|
||||||
|
|
|
@ -62,6 +62,7 @@ static void prim_fetchMercurial(EvalState & state, const Pos & pos, Value * * ar
|
||||||
fetchers::Attrs attrs;
|
fetchers::Attrs attrs;
|
||||||
attrs.insert_or_assign("type", "hg");
|
attrs.insert_or_assign("type", "hg");
|
||||||
attrs.insert_or_assign("url", url.find("://") != std::string::npos ? url : "file://" + url);
|
attrs.insert_or_assign("url", url.find("://") != std::string::npos ? url : "file://" + url);
|
||||||
|
attrs.insert_or_assign("name", name);
|
||||||
if (ref) attrs.insert_or_assign("ref", *ref);
|
if (ref) attrs.insert_or_assign("ref", *ref);
|
||||||
if (rev) attrs.insert_or_assign("rev", rev->gitRev());
|
if (rev) attrs.insert_or_assign("rev", rev->gitRev());
|
||||||
auto input = fetchers::Input::fromAttrs(std::move(attrs));
|
auto input = fetchers::Input::fromAttrs(std::move(attrs));
|
||||||
|
|
|
@ -7,6 +7,7 @@
|
||||||
|
|
||||||
#include <ctime>
|
#include <ctime>
|
||||||
#include <iomanip>
|
#include <iomanip>
|
||||||
|
#include <regex>
|
||||||
|
|
||||||
namespace nix {
|
namespace nix {
|
||||||
|
|
||||||
|
@ -60,10 +61,19 @@ void emitTreeAttrs(
|
||||||
v.attrs->sort();
|
v.attrs->sort();
|
||||||
}
|
}
|
||||||
|
|
||||||
std::string fixURI(std::string uri, EvalState &state)
|
std::string fixURI(std::string uri, EvalState &state, const std::string & defaultScheme = "file")
|
||||||
{
|
{
|
||||||
state.checkURI(uri);
|
state.checkURI(uri);
|
||||||
return uri.find("://") != std::string::npos ? uri : "file://" + uri;
|
return uri.find("://") != std::string::npos ? uri : defaultScheme + "://" + uri;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string fixURIForGit(std::string uri, EvalState & state)
|
||||||
|
{
|
||||||
|
static std::regex scp_uri("([^/].*)@(.*):(.*)");
|
||||||
|
if (uri[0] != '/' && std::regex_match(uri, scp_uri))
|
||||||
|
return fixURI(std::regex_replace(uri, scp_uri, "$1@$2/$3"), state, "ssh");
|
||||||
|
else
|
||||||
|
return fixURI(uri, state);
|
||||||
}
|
}
|
||||||
|
|
||||||
void addURI(EvalState &state, fetchers::Attrs &attrs, Symbol name, std::string v)
|
void addURI(EvalState &state, fetchers::Attrs &attrs, Symbol name, std::string v)
|
||||||
|
@ -72,13 +82,18 @@ void addURI(EvalState &state, fetchers::Attrs &attrs, Symbol name, std::string v
|
||||||
attrs.emplace(name, n == "url" ? fixURI(v, state) : v);
|
attrs.emplace(name, n == "url" ? fixURI(v, state) : v);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
struct FetchTreeParams {
|
||||||
|
bool emptyRevFallback = false;
|
||||||
|
bool allowNameArgument = false;
|
||||||
|
};
|
||||||
|
|
||||||
static void fetchTree(
|
static void fetchTree(
|
||||||
EvalState &state,
|
EvalState &state,
|
||||||
const Pos &pos,
|
const Pos &pos,
|
||||||
Value **args,
|
Value **args,
|
||||||
Value &v,
|
Value &v,
|
||||||
const std::optional<std::string> type,
|
const std::optional<std::string> type,
|
||||||
bool emptyRevFallback = false
|
const FetchTreeParams & params = FetchTreeParams{}
|
||||||
) {
|
) {
|
||||||
fetchers::Input input;
|
fetchers::Input input;
|
||||||
PathSet context;
|
PathSet context;
|
||||||
|
@ -119,17 +134,25 @@ static void fetchTree(
|
||||||
.errPos = pos
|
.errPos = pos
|
||||||
});
|
});
|
||||||
|
|
||||||
|
if (!params.allowNameArgument)
|
||||||
|
if (auto nameIter = attrs.find("name"); nameIter != attrs.end())
|
||||||
|
throw Error({
|
||||||
|
.msg = hintfmt("attribute 'name' isn’t supported in call to 'fetchTree'"),
|
||||||
|
.errPos = pos
|
||||||
|
});
|
||||||
|
|
||||||
|
|
||||||
input = fetchers::Input::fromAttrs(std::move(attrs));
|
input = fetchers::Input::fromAttrs(std::move(attrs));
|
||||||
} else {
|
} else {
|
||||||
auto url = fixURI(state.coerceToString(pos, *args[0], context, false, false), state);
|
auto url = state.coerceToString(pos, *args[0], context, false, false);
|
||||||
|
|
||||||
if (type == "git") {
|
if (type == "git") {
|
||||||
fetchers::Attrs attrs;
|
fetchers::Attrs attrs;
|
||||||
attrs.emplace("type", "git");
|
attrs.emplace("type", "git");
|
||||||
attrs.emplace("url", url);
|
attrs.emplace("url", fixURIForGit(url, state));
|
||||||
input = fetchers::Input::fromAttrs(std::move(attrs));
|
input = fetchers::Input::fromAttrs(std::move(attrs));
|
||||||
} else {
|
} else {
|
||||||
input = fetchers::Input::fromURL(url);
|
input = fetchers::Input::fromURL(fixURI(url, state));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -144,13 +167,13 @@ static void fetchTree(
|
||||||
if (state.allowedPaths)
|
if (state.allowedPaths)
|
||||||
state.allowedPaths->insert(tree.actualPath);
|
state.allowedPaths->insert(tree.actualPath);
|
||||||
|
|
||||||
emitTreeAttrs(state, tree, input2, v, emptyRevFallback);
|
emitTreeAttrs(state, tree, input2, v, params.emptyRevFallback);
|
||||||
}
|
}
|
||||||
|
|
||||||
static void prim_fetchTree(EvalState & state, const Pos & pos, Value * * args, Value & v)
|
static void prim_fetchTree(EvalState & state, const Pos & pos, Value * * args, Value & v)
|
||||||
{
|
{
|
||||||
settings.requireExperimentalFeature("flakes");
|
settings.requireExperimentalFeature("flakes");
|
||||||
fetchTree(state, pos, args, v, std::nullopt);
|
fetchTree(state, pos, args, v, std::nullopt, FetchTreeParams { .allowNameArgument = false });
|
||||||
}
|
}
|
||||||
|
|
||||||
// FIXME: document
|
// FIXME: document
|
||||||
|
@ -292,7 +315,7 @@ static RegisterPrimOp primop_fetchTarball({
|
||||||
|
|
||||||
static void prim_fetchGit(EvalState &state, const Pos &pos, Value **args, Value &v)
|
static void prim_fetchGit(EvalState &state, const Pos &pos, Value **args, Value &v)
|
||||||
{
|
{
|
||||||
fetchTree(state, pos, args, v, "git", true);
|
fetchTree(state, pos, args, v, "git", FetchTreeParams { .emptyRevFallback = true, .allowNameArgument = true });
|
||||||
}
|
}
|
||||||
|
|
||||||
static RegisterPrimOp primop_fetchGit({
|
static RegisterPrimOp primop_fetchGit({
|
||||||
|
|
|
@ -200,12 +200,17 @@ void Input::markChangedFile(
|
||||||
return scheme->markChangedFile(*this, file, commitMsg);
|
return scheme->markChangedFile(*this, file, commitMsg);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
std::string Input::getName() const
|
||||||
|
{
|
||||||
|
return maybeGetStrAttr(attrs, "name").value_or("source");
|
||||||
|
}
|
||||||
|
|
||||||
StorePath Input::computeStorePath(Store & store) const
|
StorePath Input::computeStorePath(Store & store) const
|
||||||
{
|
{
|
||||||
auto narHash = getNarHash();
|
auto narHash = getNarHash();
|
||||||
if (!narHash)
|
if (!narHash)
|
||||||
throw Error("cannot compute store path for mutable input '%s'", to_string());
|
throw Error("cannot compute store path for mutable input '%s'", to_string());
|
||||||
return store.makeFixedOutputPath(FileIngestionMethod::Recursive, *narHash, "source");
|
return store.makeFixedOutputPath(FileIngestionMethod::Recursive, *narHash, getName());
|
||||||
}
|
}
|
||||||
|
|
||||||
std::string Input::getType() const
|
std::string Input::getType() const
|
||||||
|
|
|
@ -81,6 +81,8 @@ public:
|
||||||
std::string_view file,
|
std::string_view file,
|
||||||
std::optional<std::string> commitMsg) const;
|
std::optional<std::string> commitMsg) const;
|
||||||
|
|
||||||
|
std::string getName() const;
|
||||||
|
|
||||||
StorePath computeStorePath(Store & store) const;
|
StorePath computeStorePath(Store & store) const;
|
||||||
|
|
||||||
// Convenience functions for common attributes.
|
// Convenience functions for common attributes.
|
||||||
|
|
|
@ -60,7 +60,7 @@ struct GitInputScheme : InputScheme
|
||||||
if (maybeGetStrAttr(attrs, "type") != "git") return {};
|
if (maybeGetStrAttr(attrs, "type") != "git") return {};
|
||||||
|
|
||||||
for (auto & [name, value] : attrs)
|
for (auto & [name, value] : attrs)
|
||||||
if (name != "type" && name != "url" && name != "ref" && name != "rev" && name != "shallow" && name != "submodules" && name != "lastModified" && name != "revCount" && name != "narHash" && name != "allRefs")
|
if (name != "type" && name != "url" && name != "ref" && name != "rev" && name != "shallow" && name != "submodules" && name != "lastModified" && name != "revCount" && name != "narHash" && name != "allRefs" && name != "name")
|
||||||
throw Error("unsupported Git input attribute '%s'", name);
|
throw Error("unsupported Git input attribute '%s'", name);
|
||||||
|
|
||||||
parseURL(getStrAttr(attrs, "url"));
|
parseURL(getStrAttr(attrs, "url"));
|
||||||
|
@ -167,10 +167,10 @@ struct GitInputScheme : InputScheme
|
||||||
|
|
||||||
std::pair<Tree, Input> fetch(ref<Store> store, const Input & _input) override
|
std::pair<Tree, Input> fetch(ref<Store> store, const Input & _input) override
|
||||||
{
|
{
|
||||||
auto name = "source";
|
|
||||||
|
|
||||||
Input input(_input);
|
Input input(_input);
|
||||||
|
|
||||||
|
std::string name = input.getName();
|
||||||
|
|
||||||
bool shallow = maybeGetBoolAttr(input.attrs, "shallow").value_or(false);
|
bool shallow = maybeGetBoolAttr(input.attrs, "shallow").value_or(false);
|
||||||
bool submodules = maybeGetBoolAttr(input.attrs, "submodules").value_or(false);
|
bool submodules = maybeGetBoolAttr(input.attrs, "submodules").value_or(false);
|
||||||
bool allRefs = maybeGetBoolAttr(input.attrs, "allRefs").value_or(false);
|
bool allRefs = maybeGetBoolAttr(input.attrs, "allRefs").value_or(false);
|
||||||
|
@ -270,7 +270,7 @@ struct GitInputScheme : InputScheme
|
||||||
return files.count(file);
|
return files.count(file);
|
||||||
};
|
};
|
||||||
|
|
||||||
auto storePath = store->addToStore("source", actualUrl, FileIngestionMethod::Recursive, htSHA256, filter);
|
auto storePath = store->addToStore(input.getName(), actualUrl, FileIngestionMethod::Recursive, htSHA256, filter);
|
||||||
|
|
||||||
// FIXME: maybe we should use the timestamp of the last
|
// FIXME: maybe we should use the timestamp of the last
|
||||||
// modified dirty file?
|
// modified dirty file?
|
||||||
|
|
|
@ -207,7 +207,7 @@ struct GitArchiveInputScheme : InputScheme
|
||||||
|
|
||||||
auto url = getDownloadUrl(input);
|
auto url = getDownloadUrl(input);
|
||||||
|
|
||||||
auto [tree, lastModified] = downloadTarball(store, url.url, "source", true, url.headers);
|
auto [tree, lastModified] = downloadTarball(store, url.url, input.getName(), true, url.headers);
|
||||||
|
|
||||||
input.attrs.insert_or_assign("lastModified", uint64_t(lastModified));
|
input.attrs.insert_or_assign("lastModified", uint64_t(lastModified));
|
||||||
|
|
||||||
|
|
|
@ -74,7 +74,7 @@ struct MercurialInputScheme : InputScheme
|
||||||
if (maybeGetStrAttr(attrs, "type") != "hg") return {};
|
if (maybeGetStrAttr(attrs, "type") != "hg") return {};
|
||||||
|
|
||||||
for (auto & [name, value] : attrs)
|
for (auto & [name, value] : attrs)
|
||||||
if (name != "type" && name != "url" && name != "ref" && name != "rev" && name != "revCount" && name != "narHash")
|
if (name != "type" && name != "url" && name != "ref" && name != "rev" && name != "revCount" && name != "narHash" && name != "name")
|
||||||
throw Error("unsupported Mercurial input attribute '%s'", name);
|
throw Error("unsupported Mercurial input attribute '%s'", name);
|
||||||
|
|
||||||
parseURL(getStrAttr(attrs, "url"));
|
parseURL(getStrAttr(attrs, "url"));
|
||||||
|
@ -147,10 +147,10 @@ struct MercurialInputScheme : InputScheme
|
||||||
|
|
||||||
std::pair<Tree, Input> fetch(ref<Store> store, const Input & _input) override
|
std::pair<Tree, Input> fetch(ref<Store> store, const Input & _input) override
|
||||||
{
|
{
|
||||||
auto name = "source";
|
|
||||||
|
|
||||||
Input input(_input);
|
Input input(_input);
|
||||||
|
|
||||||
|
auto name = input.getName();
|
||||||
|
|
||||||
auto [isLocal, actualUrl_] = getActualUrl(input);
|
auto [isLocal, actualUrl_] = getActualUrl(input);
|
||||||
auto actualUrl = actualUrl_; // work around clang bug
|
auto actualUrl = actualUrl_; // work around clang bug
|
||||||
|
|
||||||
|
@ -193,7 +193,7 @@ struct MercurialInputScheme : InputScheme
|
||||||
return files.count(file);
|
return files.count(file);
|
||||||
};
|
};
|
||||||
|
|
||||||
auto storePath = store->addToStore("source", actualUrl, FileIngestionMethod::Recursive, htSHA256, filter);
|
auto storePath = store->addToStore(input.getName(), actualUrl, FileIngestionMethod::Recursive, htSHA256, filter);
|
||||||
|
|
||||||
return {
|
return {
|
||||||
Tree(store->toRealPath(storePath), std::move(storePath)),
|
Tree(store->toRealPath(storePath), std::move(storePath)),
|
||||||
|
|
|
@ -124,6 +124,13 @@ std::shared_ptr<Registry> getUserRegistry()
|
||||||
return userRegistry;
|
return userRegistry;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
std::shared_ptr<Registry> getCustomRegistry(const Path & p)
|
||||||
|
{
|
||||||
|
static auto customRegistry =
|
||||||
|
Registry::read(p, Registry::Custom);
|
||||||
|
return customRegistry;
|
||||||
|
}
|
||||||
|
|
||||||
static std::shared_ptr<Registry> flagRegistry =
|
static std::shared_ptr<Registry> flagRegistry =
|
||||||
std::make_shared<Registry>(Registry::Flag);
|
std::make_shared<Registry>(Registry::Flag);
|
||||||
|
|
||||||
|
|
|
@ -14,6 +14,7 @@ struct Registry
|
||||||
User = 1,
|
User = 1,
|
||||||
System = 2,
|
System = 2,
|
||||||
Global = 3,
|
Global = 3,
|
||||||
|
Custom = 4,
|
||||||
};
|
};
|
||||||
|
|
||||||
RegistryType type;
|
RegistryType type;
|
||||||
|
@ -48,6 +49,8 @@ typedef std::vector<std::shared_ptr<Registry>> Registries;
|
||||||
|
|
||||||
std::shared_ptr<Registry> getUserRegistry();
|
std::shared_ptr<Registry> getUserRegistry();
|
||||||
|
|
||||||
|
std::shared_ptr<Registry> getCustomRegistry(const Path & p);
|
||||||
|
|
||||||
Path getUserRegistryPath();
|
Path getUserRegistryPath();
|
||||||
|
|
||||||
Registries getRegistries(ref<Store> store);
|
Registries getRegistries(ref<Store> store);
|
||||||
|
|
|
@ -196,7 +196,7 @@ struct TarballInputScheme : InputScheme
|
||||||
if (maybeGetStrAttr(attrs, "type") != "tarball") return {};
|
if (maybeGetStrAttr(attrs, "type") != "tarball") return {};
|
||||||
|
|
||||||
for (auto & [name, value] : attrs)
|
for (auto & [name, value] : attrs)
|
||||||
if (name != "type" && name != "url" && /* name != "hash" && */ name != "narHash")
|
if (name != "type" && name != "url" && /* name != "hash" && */ name != "narHash" && name != "name")
|
||||||
throw Error("unsupported tarball input attribute '%s'", name);
|
throw Error("unsupported tarball input attribute '%s'", name);
|
||||||
|
|
||||||
Input input;
|
Input input;
|
||||||
|
@ -226,7 +226,7 @@ struct TarballInputScheme : InputScheme
|
||||||
|
|
||||||
std::pair<Tree, Input> fetch(ref<Store> store, const Input & input) override
|
std::pair<Tree, Input> fetch(ref<Store> store, const Input & input) override
|
||||||
{
|
{
|
||||||
auto tree = downloadTarball(store, getStrAttr(input.attrs, "url"), "source", false).first;
|
auto tree = downloadTarball(store, getStrAttr(input.attrs, "url"), input.getName(), false).first;
|
||||||
return {std::move(tree), input};
|
return {std::move(tree), input};
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
|
@ -484,7 +484,7 @@ Logger * makeProgressBar(bool printBuildLogs)
|
||||||
{
|
{
|
||||||
return new ProgressBar(
|
return new ProgressBar(
|
||||||
printBuildLogs,
|
printBuildLogs,
|
||||||
isatty(STDERR_FILENO) && getEnv("TERM").value_or("dumb") != "dumb"
|
shouldANSI()
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -738,6 +738,63 @@ void DerivationGoal::cleanupPostOutputsRegisteredModeNonCheck()
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void runPostBuildHook(
|
||||||
|
Store & store,
|
||||||
|
Logger & logger,
|
||||||
|
const StorePath & drvPath,
|
||||||
|
StorePathSet outputPaths
|
||||||
|
)
|
||||||
|
{
|
||||||
|
auto hook = settings.postBuildHook;
|
||||||
|
if (hook == "")
|
||||||
|
return;
|
||||||
|
|
||||||
|
Activity act(logger, lvlInfo, actPostBuildHook,
|
||||||
|
fmt("running post-build-hook '%s'", settings.postBuildHook),
|
||||||
|
Logger::Fields{store.printStorePath(drvPath)});
|
||||||
|
PushActivity pact(act.id);
|
||||||
|
std::map<std::string, std::string> hookEnvironment = getEnv();
|
||||||
|
|
||||||
|
hookEnvironment.emplace("DRV_PATH", store.printStorePath(drvPath));
|
||||||
|
hookEnvironment.emplace("OUT_PATHS", chomp(concatStringsSep(" ", store.printStorePathSet(outputPaths))));
|
||||||
|
|
||||||
|
RunOptions opts(settings.postBuildHook, {});
|
||||||
|
opts.environment = hookEnvironment;
|
||||||
|
|
||||||
|
struct LogSink : Sink {
|
||||||
|
Activity & act;
|
||||||
|
std::string currentLine;
|
||||||
|
|
||||||
|
LogSink(Activity & act) : act(act) { }
|
||||||
|
|
||||||
|
void operator() (std::string_view data) override {
|
||||||
|
for (auto c : data) {
|
||||||
|
if (c == '\n') {
|
||||||
|
flushLine();
|
||||||
|
} else {
|
||||||
|
currentLine += c;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void flushLine() {
|
||||||
|
act.result(resPostBuildLogLine, currentLine);
|
||||||
|
currentLine.clear();
|
||||||
|
}
|
||||||
|
|
||||||
|
~LogSink() {
|
||||||
|
if (currentLine != "") {
|
||||||
|
currentLine += '\n';
|
||||||
|
flushLine();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
LogSink sink(act);
|
||||||
|
|
||||||
|
opts.standardOut = &sink;
|
||||||
|
opts.mergeStderrToStdout = true;
|
||||||
|
runProgram2(opts);
|
||||||
|
}
|
||||||
|
|
||||||
void DerivationGoal::buildDone()
|
void DerivationGoal::buildDone()
|
||||||
{
|
{
|
||||||
|
@ -803,57 +860,15 @@ void DerivationGoal::buildDone()
|
||||||
being valid. */
|
being valid. */
|
||||||
registerOutputs();
|
registerOutputs();
|
||||||
|
|
||||||
if (settings.postBuildHook != "") {
|
StorePathSet outputPaths;
|
||||||
Activity act(*logger, lvlInfo, actPostBuildHook,
|
for (auto & [_, path] : finalOutputs)
|
||||||
fmt("running post-build-hook '%s'", settings.postBuildHook),
|
outputPaths.insert(path);
|
||||||
Logger::Fields{worker.store.printStorePath(drvPath)});
|
runPostBuildHook(
|
||||||
PushActivity pact(act.id);
|
worker.store,
|
||||||
StorePathSet outputPaths;
|
*logger,
|
||||||
for (auto i : drv->outputs) {
|
drvPath,
|
||||||
outputPaths.insert(finalOutputs.at(i.first));
|
outputPaths
|
||||||
}
|
);
|
||||||
std::map<std::string, std::string> hookEnvironment = getEnv();
|
|
||||||
|
|
||||||
hookEnvironment.emplace("DRV_PATH", worker.store.printStorePath(drvPath));
|
|
||||||
hookEnvironment.emplace("OUT_PATHS", chomp(concatStringsSep(" ", worker.store.printStorePathSet(outputPaths))));
|
|
||||||
|
|
||||||
RunOptions opts(settings.postBuildHook, {});
|
|
||||||
opts.environment = hookEnvironment;
|
|
||||||
|
|
||||||
struct LogSink : Sink {
|
|
||||||
Activity & act;
|
|
||||||
std::string currentLine;
|
|
||||||
|
|
||||||
LogSink(Activity & act) : act(act) { }
|
|
||||||
|
|
||||||
void operator() (std::string_view data) override {
|
|
||||||
for (auto c : data) {
|
|
||||||
if (c == '\n') {
|
|
||||||
flushLine();
|
|
||||||
} else {
|
|
||||||
currentLine += c;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void flushLine() {
|
|
||||||
act.result(resPostBuildLogLine, currentLine);
|
|
||||||
currentLine.clear();
|
|
||||||
}
|
|
||||||
|
|
||||||
~LogSink() {
|
|
||||||
if (currentLine != "") {
|
|
||||||
currentLine += '\n';
|
|
||||||
flushLine();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
};
|
|
||||||
LogSink sink(act);
|
|
||||||
|
|
||||||
opts.standardOut = &sink;
|
|
||||||
opts.mergeStderrToStdout = true;
|
|
||||||
runProgram2(opts);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (buildMode == bmCheck) {
|
if (buildMode == bmCheck) {
|
||||||
cleanupPostOutputsRegisteredModeCheck();
|
cleanupPostOutputsRegisteredModeCheck();
|
||||||
|
@ -909,6 +924,8 @@ void DerivationGoal::resolvedFinished() {
|
||||||
|
|
||||||
auto resolvedHashes = staticOutputHashes(worker.store, *resolvedDrv);
|
auto resolvedHashes = staticOutputHashes(worker.store, *resolvedDrv);
|
||||||
|
|
||||||
|
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())
|
||||||
|
@ -926,8 +943,10 @@ void DerivationGoal::resolvedFinished() {
|
||||||
auto newRealisation = *realisation;
|
auto newRealisation = *realisation;
|
||||||
newRealisation.id = DrvOutput{initialOutputs.at(wantedOutput).outputHash, wantedOutput};
|
newRealisation.id = DrvOutput{initialOutputs.at(wantedOutput).outputHash, wantedOutput};
|
||||||
newRealisation.signatures.clear();
|
newRealisation.signatures.clear();
|
||||||
|
newRealisation.dependentRealisations = drvOutputReferences(worker.store, *drv, realisation->outPath);
|
||||||
signRealisation(newRealisation);
|
signRealisation(newRealisation);
|
||||||
worker.store.registerDrvOutput(newRealisation);
|
worker.store.registerDrvOutput(newRealisation);
|
||||||
|
outputPaths.insert(realisation->outPath);
|
||||||
} else {
|
} else {
|
||||||
// If we don't have a realisation, then it must mean that something
|
// If we don't have a realisation, then it must mean that something
|
||||||
// failed when building the resolved drv
|
// failed when building the resolved drv
|
||||||
|
@ -935,6 +954,13 @@ void DerivationGoal::resolvedFinished() {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
runPostBuildHook(
|
||||||
|
worker.store,
|
||||||
|
*logger,
|
||||||
|
drvPath,
|
||||||
|
outputPaths
|
||||||
|
);
|
||||||
|
|
||||||
// This is potentially a bit fishy in terms of error reporting. Not sure
|
// This is potentially a bit fishy in terms of error reporting. Not sure
|
||||||
// how to do it in a cleaner way
|
// how to do it in a cleaner way
|
||||||
amDone(nrFailed == 0 ? ecSuccess : ecFailed, ex);
|
amDone(nrFailed == 0 ? ecSuccess : ecFailed, ex);
|
||||||
|
|
|
@ -17,6 +17,13 @@ DrvOutputSubstitutionGoal::DrvOutputSubstitutionGoal(const DrvOutput& id, Worker
|
||||||
void DrvOutputSubstitutionGoal::init()
|
void DrvOutputSubstitutionGoal::init()
|
||||||
{
|
{
|
||||||
trace("init");
|
trace("init");
|
||||||
|
|
||||||
|
/* If the derivation already exists, we’re done */
|
||||||
|
if (worker.store.queryRealisation(id)) {
|
||||||
|
amDone(ecSuccess);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
subs = settings.useSubstitutes ? getDefaultSubstituters() : std::list<ref<Store>>();
|
subs = settings.useSubstitutes ? getDefaultSubstituters() : std::list<ref<Store>>();
|
||||||
tryNext();
|
tryNext();
|
||||||
}
|
}
|
||||||
|
@ -53,6 +60,26 @@ void DrvOutputSubstitutionGoal::tryNext()
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
for (const auto & [depId, depPath] : outputInfo->dependentRealisations) {
|
||||||
|
if (depId != id) {
|
||||||
|
if (auto localOutputInfo = worker.store.queryRealisation(depId);
|
||||||
|
localOutputInfo && localOutputInfo->outPath != depPath) {
|
||||||
|
warn(
|
||||||
|
"substituter '%s' has an incompatible realisation for '%s', ignoring.\n"
|
||||||
|
"Local: %s\n"
|
||||||
|
"Remote: %s",
|
||||||
|
sub->getUri(),
|
||||||
|
depId.to_string(),
|
||||||
|
worker.store.printStorePath(localOutputInfo->outPath),
|
||||||
|
worker.store.printStorePath(depPath)
|
||||||
|
);
|
||||||
|
tryNext();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
addWaitee(worker.makeDrvOutputSubstitutionGoal(depId));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
addWaitee(worker.makePathSubstitutionGoal(outputInfo->outPath));
|
addWaitee(worker.makePathSubstitutionGoal(outputInfo->outPath));
|
||||||
|
|
||||||
if (waitees.empty()) outPathValid();
|
if (waitees.empty()) outPathValid();
|
||||||
|
|
|
@ -1248,13 +1248,18 @@ struct RestrictedStore : public virtual RestrictedStoreConfig, public virtual Lo
|
||||||
std::optional<const Realisation> queryRealisation(const DrvOutput & id) override
|
std::optional<const Realisation> queryRealisation(const DrvOutput & id) override
|
||||||
// XXX: This should probably be allowed if the realisation corresponds to
|
// XXX: This should probably be allowed if the realisation corresponds to
|
||||||
// an allowed derivation
|
// an allowed derivation
|
||||||
{ throw Error("queryRealisation"); }
|
{
|
||||||
|
if (!goal.isAllowed(id))
|
||||||
|
throw InvalidPath("cannot query an unknown output id '%s' in recursive Nix", id.to_string());
|
||||||
|
return next->queryRealisation(id);
|
||||||
|
}
|
||||||
|
|
||||||
void buildPaths(const std::vector<DerivedPath> & paths, BuildMode buildMode) override
|
void buildPaths(const std::vector<DerivedPath> & paths, BuildMode buildMode) override
|
||||||
{
|
{
|
||||||
if (buildMode != bmNormal) throw Error("unsupported build mode");
|
if (buildMode != bmNormal) throw Error("unsupported build mode");
|
||||||
|
|
||||||
StorePathSet newPaths;
|
StorePathSet newPaths;
|
||||||
|
std::set<Realisation> newRealisations;
|
||||||
|
|
||||||
for (auto & req : paths) {
|
for (auto & req : paths) {
|
||||||
if (!goal.isAllowed(req))
|
if (!goal.isAllowed(req))
|
||||||
|
@ -1267,16 +1272,28 @@ struct RestrictedStore : public virtual RestrictedStoreConfig, public virtual Lo
|
||||||
auto p = std::get_if<DerivedPath::Built>(&path);
|
auto p = std::get_if<DerivedPath::Built>(&path);
|
||||||
if (!p) continue;
|
if (!p) continue;
|
||||||
auto & bfd = *p;
|
auto & bfd = *p;
|
||||||
|
auto drv = readDerivation(bfd.drvPath);
|
||||||
|
auto drvHashes = staticOutputHashes(*this, drv);
|
||||||
auto outputs = next->queryDerivationOutputMap(bfd.drvPath);
|
auto outputs = next->queryDerivationOutputMap(bfd.drvPath);
|
||||||
for (auto & [outputName, outputPath] : outputs)
|
for (auto & [outputName, outputPath] : outputs)
|
||||||
if (wantOutput(outputName, bfd.outputs))
|
if (wantOutput(outputName, bfd.outputs)) {
|
||||||
newPaths.insert(outputPath);
|
newPaths.insert(outputPath);
|
||||||
|
if (settings.isExperimentalFeatureEnabled("ca-derivations")) {
|
||||||
|
auto thisRealisation = next->queryRealisation(
|
||||||
|
DrvOutput{drvHashes.at(outputName), outputName}
|
||||||
|
);
|
||||||
|
assert(thisRealisation);
|
||||||
|
newRealisations.insert(*thisRealisation);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
StorePathSet closure;
|
StorePathSet closure;
|
||||||
next->computeFSClosure(newPaths, closure);
|
next->computeFSClosure(newPaths, closure);
|
||||||
for (auto & path : closure)
|
for (auto & path : closure)
|
||||||
goal.addDependency(path);
|
goal.addDependency(path);
|
||||||
|
for (auto & real : Realisation::closure(*next, newRealisations))
|
||||||
|
goal.addedDrvOutputs.insert(real.id);
|
||||||
}
|
}
|
||||||
|
|
||||||
BuildResult buildDerivation(const StorePath & drvPath, const BasicDerivation & drv,
|
BuildResult buildDerivation(const StorePath & drvPath, const BasicDerivation & drv,
|
||||||
|
@ -2379,6 +2396,7 @@ void LocalDerivationGoal::registerOutputs()
|
||||||
floating CA derivations and hash-mismatching fixed-output
|
floating CA derivations and hash-mismatching fixed-output
|
||||||
derivations. */
|
derivations. */
|
||||||
PathLocks dynamicOutputLock;
|
PathLocks dynamicOutputLock;
|
||||||
|
dynamicOutputLock.setDeletion(true);
|
||||||
auto optFixedPath = output.path(worker.store, drv->name, outputName);
|
auto optFixedPath = output.path(worker.store, drv->name, outputName);
|
||||||
if (!optFixedPath ||
|
if (!optFixedPath ||
|
||||||
worker.store.printStorePath(*optFixedPath) != finalDestPath)
|
worker.store.printStorePath(*optFixedPath) != finalDestPath)
|
||||||
|
|
|
@ -108,6 +108,9 @@ struct LocalDerivationGoal : public DerivationGoal
|
||||||
/* Paths that were added via recursive Nix calls. */
|
/* Paths that were added via recursive Nix calls. */
|
||||||
StorePathSet addedPaths;
|
StorePathSet addedPaths;
|
||||||
|
|
||||||
|
/* Realisations that were added via recursive Nix calls. */
|
||||||
|
std::set<DrvOutput> addedDrvOutputs;
|
||||||
|
|
||||||
/* Recursive Nix calls are only allowed to build or realize paths
|
/* Recursive Nix calls are only allowed to build or realize paths
|
||||||
in the original input closure or added via a recursive Nix call
|
in the original input closure or added via a recursive Nix call
|
||||||
(so e.g. you can't do 'nix-store -r /nix/store/<bla>' where
|
(so e.g. you can't do 'nix-store -r /nix/store/<bla>' where
|
||||||
|
@ -116,6 +119,11 @@ struct LocalDerivationGoal : public DerivationGoal
|
||||||
{
|
{
|
||||||
return inputPaths.count(path) || addedPaths.count(path);
|
return inputPaths.count(path) || addedPaths.count(path);
|
||||||
}
|
}
|
||||||
|
bool isAllowed(const DrvOutput & id)
|
||||||
|
{
|
||||||
|
return addedDrvOutputs.count(id);
|
||||||
|
}
|
||||||
|
|
||||||
bool isAllowed(const DerivedPath & req);
|
bool isAllowed(const DerivedPath & req);
|
||||||
|
|
||||||
friend struct RestrictedStore;
|
friend struct RestrictedStore;
|
||||||
|
|
|
@ -3,10 +3,19 @@
|
||||||
-- is enabled
|
-- is enabled
|
||||||
|
|
||||||
create table if not exists Realisations (
|
create table if not exists Realisations (
|
||||||
|
id integer primary key autoincrement not null,
|
||||||
drvPath text not null,
|
drvPath text not null,
|
||||||
outputName text not null, -- symbolic output id, usually "out"
|
outputName text not null, -- symbolic output id, usually "out"
|
||||||
outputPath integer not null,
|
outputPath integer not null,
|
||||||
signatures text, -- space-separated list
|
signatures text, -- space-separated list
|
||||||
primary key (drvPath, outputName),
|
|
||||||
foreign key (outputPath) references ValidPaths(id) on delete cascade
|
foreign key (outputPath) references ValidPaths(id) on delete cascade
|
||||||
);
|
);
|
||||||
|
|
||||||
|
create index if not exists IndexRealisations on Realisations(drvPath, outputName);
|
||||||
|
|
||||||
|
create table if not exists RealisationsRefs (
|
||||||
|
referrer integer not null,
|
||||||
|
realisationReference integer,
|
||||||
|
foreign key (referrer) references Realisations(id) on delete cascade,
|
||||||
|
foreign key (realisationReference) references Realisations(id) on delete restrict
|
||||||
|
);
|
||||||
|
|
|
@ -53,12 +53,15 @@ struct LocalStore::State::Stmts {
|
||||||
SQLiteStmt InvalidatePath;
|
SQLiteStmt InvalidatePath;
|
||||||
SQLiteStmt AddDerivationOutput;
|
SQLiteStmt AddDerivationOutput;
|
||||||
SQLiteStmt RegisterRealisedOutput;
|
SQLiteStmt RegisterRealisedOutput;
|
||||||
|
SQLiteStmt UpdateRealisedOutput;
|
||||||
SQLiteStmt QueryValidDerivers;
|
SQLiteStmt QueryValidDerivers;
|
||||||
SQLiteStmt QueryDerivationOutputs;
|
SQLiteStmt QueryDerivationOutputs;
|
||||||
SQLiteStmt QueryRealisedOutput;
|
SQLiteStmt QueryRealisedOutput;
|
||||||
SQLiteStmt QueryAllRealisedOutputs;
|
SQLiteStmt QueryAllRealisedOutputs;
|
||||||
SQLiteStmt QueryPathFromHashPart;
|
SQLiteStmt QueryPathFromHashPart;
|
||||||
SQLiteStmt QueryValidPaths;
|
SQLiteStmt QueryValidPaths;
|
||||||
|
SQLiteStmt QueryRealisationReferences;
|
||||||
|
SQLiteStmt AddRealisationReference;
|
||||||
};
|
};
|
||||||
|
|
||||||
int getSchema(Path schemaPath)
|
int getSchema(Path schemaPath)
|
||||||
|
@ -76,7 +79,7 @@ int getSchema(Path schemaPath)
|
||||||
|
|
||||||
void migrateCASchema(SQLite& db, Path schemaPath, AutoCloseFD& lockFd)
|
void migrateCASchema(SQLite& db, Path schemaPath, AutoCloseFD& lockFd)
|
||||||
{
|
{
|
||||||
const int nixCASchemaVersion = 1;
|
const int nixCASchemaVersion = 2;
|
||||||
int curCASchema = getSchema(schemaPath);
|
int curCASchema = getSchema(schemaPath);
|
||||||
if (curCASchema != nixCASchemaVersion) {
|
if (curCASchema != nixCASchemaVersion) {
|
||||||
if (curCASchema > nixCASchemaVersion) {
|
if (curCASchema > nixCASchemaVersion) {
|
||||||
|
@ -94,7 +97,39 @@ void migrateCASchema(SQLite& db, Path schemaPath, AutoCloseFD& lockFd)
|
||||||
#include "ca-specific-schema.sql.gen.hh"
|
#include "ca-specific-schema.sql.gen.hh"
|
||||||
;
|
;
|
||||||
db.exec(schema);
|
db.exec(schema);
|
||||||
|
curCASchema = nixCASchemaVersion;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (curCASchema < 2) {
|
||||||
|
SQLiteTxn txn(db);
|
||||||
|
// Ugly little sql dance to add a new `id` column and make it the primary key
|
||||||
|
db.exec(R"(
|
||||||
|
create table Realisations2 (
|
||||||
|
id integer primary key autoincrement not null,
|
||||||
|
drvPath text not null,
|
||||||
|
outputName text not null, -- symbolic output id, usually "out"
|
||||||
|
outputPath integer not null,
|
||||||
|
signatures text, -- space-separated list
|
||||||
|
foreign key (outputPath) references ValidPaths(id) on delete cascade
|
||||||
|
);
|
||||||
|
insert into Realisations2 (drvPath, outputName, outputPath, signatures)
|
||||||
|
select drvPath, outputName, outputPath, signatures from Realisations;
|
||||||
|
drop table Realisations;
|
||||||
|
alter table Realisations2 rename to Realisations;
|
||||||
|
)");
|
||||||
|
db.exec(R"(
|
||||||
|
create index if not exists IndexRealisations on Realisations(drvPath, outputName);
|
||||||
|
|
||||||
|
create table if not exists RealisationsRefs (
|
||||||
|
referrer integer not null,
|
||||||
|
realisationReference integer,
|
||||||
|
foreign key (referrer) references Realisations(id) on delete cascade,
|
||||||
|
foreign key (realisationReference) references Realisations(id) on delete restrict
|
||||||
|
);
|
||||||
|
)");
|
||||||
|
txn.commit();
|
||||||
|
}
|
||||||
|
|
||||||
writeFile(schemaPath, fmt("%d", nixCASchemaVersion));
|
writeFile(schemaPath, fmt("%d", nixCASchemaVersion));
|
||||||
lockFile(lockFd.get(), ltRead, true);
|
lockFile(lockFd.get(), ltRead, true);
|
||||||
}
|
}
|
||||||
|
@ -311,9 +346,18 @@ LocalStore::LocalStore(const Params & params)
|
||||||
values (?, ?, (select id from ValidPaths where path = ?), ?)
|
values (?, ?, (select id from ValidPaths where path = ?), ?)
|
||||||
;
|
;
|
||||||
)");
|
)");
|
||||||
|
state->stmts->UpdateRealisedOutput.create(state->db,
|
||||||
|
R"(
|
||||||
|
update Realisations
|
||||||
|
set signatures = ?
|
||||||
|
where
|
||||||
|
drvPath = ? and
|
||||||
|
outputName = ?
|
||||||
|
;
|
||||||
|
)");
|
||||||
state->stmts->QueryRealisedOutput.create(state->db,
|
state->stmts->QueryRealisedOutput.create(state->db,
|
||||||
R"(
|
R"(
|
||||||
select Output.path, Realisations.signatures from Realisations
|
select Realisations.id, Output.path, Realisations.signatures from Realisations
|
||||||
inner join ValidPaths as Output on Output.id = Realisations.outputPath
|
inner join ValidPaths as Output on Output.id = Realisations.outputPath
|
||||||
where drvPath = ? and outputName = ?
|
where drvPath = ? and outputName = ?
|
||||||
;
|
;
|
||||||
|
@ -325,6 +369,19 @@ LocalStore::LocalStore(const Params & params)
|
||||||
where drvPath = ?
|
where drvPath = ?
|
||||||
;
|
;
|
||||||
)");
|
)");
|
||||||
|
state->stmts->QueryRealisationReferences.create(state->db,
|
||||||
|
R"(
|
||||||
|
select drvPath, outputName from Realisations
|
||||||
|
join RealisationsRefs on realisationReference = Realisations.id
|
||||||
|
where referrer = ?;
|
||||||
|
)");
|
||||||
|
state->stmts->AddRealisationReference.create(state->db,
|
||||||
|
R"(
|
||||||
|
insert or replace into RealisationsRefs (referrer, realisationReference)
|
||||||
|
values (
|
||||||
|
?,
|
||||||
|
(select id from Realisations where drvPath = ? and outputName = ?));
|
||||||
|
)");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -661,14 +718,54 @@ void LocalStore::registerDrvOutput(const Realisation & info, CheckSigsFlag check
|
||||||
void LocalStore::registerDrvOutput(const Realisation & info)
|
void LocalStore::registerDrvOutput(const Realisation & info)
|
||||||
{
|
{
|
||||||
settings.requireExperimentalFeature("ca-derivations");
|
settings.requireExperimentalFeature("ca-derivations");
|
||||||
auto state(_state.lock());
|
|
||||||
retrySQLite<void>([&]() {
|
retrySQLite<void>([&]() {
|
||||||
state->stmts->RegisterRealisedOutput.use()
|
auto state(_state.lock());
|
||||||
(info.id.strHash())
|
if (auto oldR = queryRealisation_(*state, info.id)) {
|
||||||
(info.id.outputName)
|
if (info.isCompatibleWith(*oldR)) {
|
||||||
(printStorePath(info.outPath))
|
auto combinedSignatures = oldR->signatures;
|
||||||
(concatStringsSep(" ", info.signatures))
|
combinedSignatures.insert(info.signatures.begin(),
|
||||||
.exec();
|
info.signatures.end());
|
||||||
|
state->stmts->UpdateRealisedOutput.use()
|
||||||
|
(concatStringsSep(" ", combinedSignatures))
|
||||||
|
(info.id.strHash())
|
||||||
|
(info.id.outputName)
|
||||||
|
.exec();
|
||||||
|
} else {
|
||||||
|
throw Error("Trying to register a realisation of '%s', but we already "
|
||||||
|
"have another one locally.\n"
|
||||||
|
"Local: %s\n"
|
||||||
|
"Remote: %s",
|
||||||
|
info.id.to_string(),
|
||||||
|
printStorePath(oldR->outPath),
|
||||||
|
printStorePath(info.outPath)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
state->stmts->RegisterRealisedOutput.use()
|
||||||
|
(info.id.strHash())
|
||||||
|
(info.id.outputName)
|
||||||
|
(printStorePath(info.outPath))
|
||||||
|
(concatStringsSep(" ", info.signatures))
|
||||||
|
.exec();
|
||||||
|
}
|
||||||
|
uint64_t myId = state->db.getLastInsertedRowId();
|
||||||
|
for (auto & [outputId, depPath] : info.dependentRealisations) {
|
||||||
|
auto localRealisation = queryRealisationCore_(*state, outputId);
|
||||||
|
if (!localRealisation)
|
||||||
|
throw Error("unable to register the derivation '%s' as it "
|
||||||
|
"depends on the non existent '%s'",
|
||||||
|
info.id.to_string(), outputId.to_string());
|
||||||
|
if (localRealisation->second.outPath != depPath)
|
||||||
|
throw Error("unable to register the derivation '%s' as it "
|
||||||
|
"depends on a realisation of '%s' that doesn’t"
|
||||||
|
"match what we have locally",
|
||||||
|
info.id.to_string(), outputId.to_string());
|
||||||
|
state->stmts->AddRealisationReference.use()
|
||||||
|
(myId)
|
||||||
|
(outputId.strHash())
|
||||||
|
(outputId.outputName)
|
||||||
|
.exec();
|
||||||
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1679,22 +1776,68 @@ void LocalStore::createUser(const std::string & userName, uid_t userId)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
std::optional<const Realisation> LocalStore::queryRealisation(
|
std::optional<std::pair<int64_t, Realisation>> LocalStore::queryRealisationCore_(
|
||||||
const DrvOutput& id) {
|
LocalStore::State & state,
|
||||||
typedef std::optional<const Realisation> Ret;
|
const DrvOutput & id)
|
||||||
return retrySQLite<Ret>([&]() -> Ret {
|
{
|
||||||
auto state(_state.lock());
|
auto useQueryRealisedOutput(
|
||||||
auto use(state->stmts->QueryRealisedOutput.use()(id.strHash())(
|
state.stmts->QueryRealisedOutput.use()
|
||||||
id.outputName));
|
(id.strHash())
|
||||||
if (!use.next())
|
(id.outputName));
|
||||||
return std::nullopt;
|
if (!useQueryRealisedOutput.next())
|
||||||
auto outputPath = parseStorePath(use.getStr(0));
|
return std::nullopt;
|
||||||
auto signatures = tokenizeString<StringSet>(use.getStr(1));
|
auto realisationDbId = useQueryRealisedOutput.getInt(0);
|
||||||
return Ret{Realisation{
|
auto outputPath = parseStorePath(useQueryRealisedOutput.getStr(1));
|
||||||
.id = id, .outPath = outputPath, .signatures = signatures}};
|
auto signatures =
|
||||||
});
|
tokenizeString<StringSet>(useQueryRealisedOutput.getStr(2));
|
||||||
|
|
||||||
|
return {{
|
||||||
|
realisationDbId,
|
||||||
|
Realisation{
|
||||||
|
.id = id,
|
||||||
|
.outPath = outputPath,
|
||||||
|
.signatures = signatures,
|
||||||
|
}
|
||||||
|
}};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
std::optional<const Realisation> LocalStore::queryRealisation_(
|
||||||
|
LocalStore::State & state,
|
||||||
|
const DrvOutput & id)
|
||||||
|
{
|
||||||
|
auto maybeCore = queryRealisationCore_(state, id);
|
||||||
|
if (!maybeCore)
|
||||||
|
return std::nullopt;
|
||||||
|
auto [realisationDbId, res] = *maybeCore;
|
||||||
|
|
||||||
|
std::map<DrvOutput, StorePath> dependentRealisations;
|
||||||
|
auto useRealisationRefs(
|
||||||
|
state.stmts->QueryRealisationReferences.use()
|
||||||
|
(realisationDbId));
|
||||||
|
while (useRealisationRefs.next()) {
|
||||||
|
auto depId = DrvOutput {
|
||||||
|
Hash::parseAnyPrefixed(useRealisationRefs.getStr(0)),
|
||||||
|
useRealisationRefs.getStr(1),
|
||||||
|
};
|
||||||
|
auto dependentRealisation = queryRealisationCore_(state, depId);
|
||||||
|
assert(dependentRealisation); // Enforced by the db schema
|
||||||
|
auto outputPath = dependentRealisation->second.outPath;
|
||||||
|
dependentRealisations.insert({depId, outputPath});
|
||||||
|
}
|
||||||
|
|
||||||
|
res.dependentRealisations = dependentRealisations;
|
||||||
|
|
||||||
|
return { res };
|
||||||
|
}
|
||||||
|
|
||||||
|
std::optional<const Realisation>
|
||||||
|
LocalStore::queryRealisation(const DrvOutput & id)
|
||||||
|
{
|
||||||
|
return retrySQLite<std::optional<const Realisation>>([&]() {
|
||||||
|
auto state(_state.lock());
|
||||||
|
return queryRealisation_(*state, id);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
FixedOutputHash LocalStore::hashCAPath(
|
FixedOutputHash LocalStore::hashCAPath(
|
||||||
const FileIngestionMethod & method, const HashType & hashType,
|
const FileIngestionMethod & method, const HashType & hashType,
|
||||||
|
|
|
@ -203,6 +203,8 @@ public:
|
||||||
void registerDrvOutput(const Realisation & info, CheckSigsFlag checkSigs) override;
|
void registerDrvOutput(const Realisation & info, CheckSigsFlag checkSigs) override;
|
||||||
void cacheDrvOutputMapping(State & state, const uint64_t deriver, const string & outputName, const StorePath & output);
|
void cacheDrvOutputMapping(State & state, const uint64_t deriver, const string & outputName, const StorePath & output);
|
||||||
|
|
||||||
|
std::optional<const Realisation> queryRealisation_(State & state, const DrvOutput & id);
|
||||||
|
std::optional<std::pair<int64_t, Realisation>> queryRealisationCore_(State & state, const DrvOutput & id);
|
||||||
std::optional<const Realisation> queryRealisation(const DrvOutput&) override;
|
std::optional<const Realisation> queryRealisation(const DrvOutput&) override;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
|
|
@ -9,11 +9,11 @@ libstore_SOURCES := $(wildcard $(d)/*.cc $(d)/builtins/*.cc $(d)/build/*.cc)
|
||||||
libstore_LIBS = libutil
|
libstore_LIBS = libutil
|
||||||
|
|
||||||
libstore_LDFLAGS = $(SQLITE3_LIBS) -lbz2 $(LIBCURL_LIBS) $(SODIUM_LIBS) -pthread
|
libstore_LDFLAGS = $(SQLITE3_LIBS) -lbz2 $(LIBCURL_LIBS) $(SODIUM_LIBS) -pthread
|
||||||
ifeq ($(OS), Linux)
|
ifdef HOST_LINUX
|
||||||
libstore_LDFLAGS += -ldl
|
libstore_LDFLAGS += -ldl
|
||||||
endif
|
endif
|
||||||
|
|
||||||
ifeq ($(OS), Darwin)
|
ifdef HOST_DARWIN
|
||||||
libstore_FILES = sandbox-defaults.sb sandbox-minimal.sb sandbox-network.sb
|
libstore_FILES = sandbox-defaults.sb sandbox-minimal.sb sandbox-network.sb
|
||||||
endif
|
endif
|
||||||
|
|
||||||
|
@ -23,7 +23,7 @@ ifeq ($(ENABLE_S3), 1)
|
||||||
libstore_LDFLAGS += -laws-cpp-sdk-transfer -laws-cpp-sdk-s3 -laws-cpp-sdk-core
|
libstore_LDFLAGS += -laws-cpp-sdk-transfer -laws-cpp-sdk-s3 -laws-cpp-sdk-core
|
||||||
endif
|
endif
|
||||||
|
|
||||||
ifeq ($(OS), SunOS)
|
ifdef HOST_SOLARIS
|
||||||
libstore_LDFLAGS += -lsocket
|
libstore_LDFLAGS += -lsocket
|
||||||
endif
|
endif
|
||||||
|
|
||||||
|
|
|
@ -16,13 +16,18 @@ Machine::Machine(decltype(storeUri) storeUri,
|
||||||
decltype(mandatoryFeatures) mandatoryFeatures,
|
decltype(mandatoryFeatures) mandatoryFeatures,
|
||||||
decltype(sshPublicHostKey) sshPublicHostKey) :
|
decltype(sshPublicHostKey) sshPublicHostKey) :
|
||||||
storeUri(
|
storeUri(
|
||||||
// Backwards compatibility: if the URI is a hostname,
|
// Backwards compatibility: if the URI is schemeless, is not a path,
|
||||||
// prepend ssh://.
|
// and is not one of the special store connection words, prepend
|
||||||
|
// ssh://.
|
||||||
storeUri.find("://") != std::string::npos
|
storeUri.find("://") != std::string::npos
|
||||||
|| hasPrefix(storeUri, "local")
|
|| storeUri.find("/") != std::string::npos
|
||||||
|| hasPrefix(storeUri, "remote")
|
|| storeUri == "auto"
|
||||||
|| hasPrefix(storeUri, "auto")
|
|| storeUri == "daemon"
|
||||||
|| hasPrefix(storeUri, "/")
|
|| storeUri == "local"
|
||||||
|
|| hasPrefix(storeUri, "auto?")
|
||||||
|
|| hasPrefix(storeUri, "daemon?")
|
||||||
|
|| hasPrefix(storeUri, "local?")
|
||||||
|
|| hasPrefix(storeUri, "?")
|
||||||
? storeUri
|
? storeUri
|
||||||
: "ssh://" + storeUri),
|
: "ssh://" + storeUri),
|
||||||
systemTypes(systemTypes),
|
systemTypes(systemTypes),
|
||||||
|
|
|
@ -29,9 +29,9 @@ void Store::computeFSClosure(const StorePathSet & startPaths,
|
||||||
res.insert(i);
|
res.insert(i);
|
||||||
|
|
||||||
if (includeDerivers && path.isDerivation())
|
if (includeDerivers && path.isDerivation())
|
||||||
for (auto& i : queryDerivationOutputs(path))
|
for (auto& [_, maybeOutPath] : queryPartialDerivationOutputMap(path))
|
||||||
if (isValidPath(i) && queryPathInfo(i)->deriver == path)
|
if (maybeOutPath && isValidPath(*maybeOutPath))
|
||||||
res.insert(i);
|
res.insert(*maybeOutPath);
|
||||||
return res;
|
return res;
|
||||||
};
|
};
|
||||||
else
|
else
|
||||||
|
@ -44,9 +44,9 @@ void Store::computeFSClosure(const StorePathSet & startPaths,
|
||||||
res.insert(ref);
|
res.insert(ref);
|
||||||
|
|
||||||
if (includeOutputs && path.isDerivation())
|
if (includeOutputs && path.isDerivation())
|
||||||
for (auto& i : queryDerivationOutputs(path))
|
for (auto& [_, maybeOutPath] : queryPartialDerivationOutputMap(path))
|
||||||
if (isValidPath(i))
|
if (maybeOutPath && isValidPath(*maybeOutPath))
|
||||||
res.insert(i);
|
res.insert(*maybeOutPath);
|
||||||
|
|
||||||
if (includeDerivers && info->deriver && isValidPath(*info->deriver))
|
if (includeDerivers && info->deriver && isValidPath(*info->deriver))
|
||||||
res.insert(*info->deriver);
|
res.insert(*info->deriver);
|
||||||
|
@ -254,5 +254,44 @@ StorePaths Store::topoSortPaths(const StorePathSet & paths)
|
||||||
}});
|
}});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
std::map<DrvOutput, StorePath> drvOutputReferences(
|
||||||
|
const std::set<Realisation> & inputRealisations,
|
||||||
|
const StorePathSet & pathReferences)
|
||||||
|
{
|
||||||
|
std::map<DrvOutput, StorePath> res;
|
||||||
|
|
||||||
|
for (const auto & input : inputRealisations) {
|
||||||
|
if (pathReferences.count(input.outPath)) {
|
||||||
|
res.insert({input.id, input.outPath});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return res;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::map<DrvOutput, StorePath> drvOutputReferences(
|
||||||
|
Store & store,
|
||||||
|
const Derivation & drv,
|
||||||
|
const StorePath & outputPath)
|
||||||
|
{
|
||||||
|
std::set<Realisation> inputRealisations;
|
||||||
|
|
||||||
|
for (const auto& [inputDrv, outputNames] : drv.inputDrvs) {
|
||||||
|
auto outputHashes =
|
||||||
|
staticOutputHashes(store, store.readDerivation(inputDrv));
|
||||||
|
for (const auto& outputName : outputNames) {
|
||||||
|
auto thisRealisation = store.queryRealisation(
|
||||||
|
DrvOutput{outputHashes.at(outputName), outputName});
|
||||||
|
if (!thisRealisation)
|
||||||
|
throw Error(
|
||||||
|
"output '%s' of derivation '%s' isn’t built", outputName,
|
||||||
|
store.printStorePath(inputDrv));
|
||||||
|
inputRealisations.insert(*thisRealisation);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
auto info = store.queryPathInfo(outputPath);
|
||||||
|
|
||||||
|
return drvOutputReferences(Realisation::closure(store, inputRealisations), info->references);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -93,6 +93,8 @@ StringSet ParsedDerivation::getRequiredSystemFeatures() const
|
||||||
StringSet res;
|
StringSet res;
|
||||||
for (auto & i : getStringsAttr("requiredSystemFeatures").value_or(Strings()))
|
for (auto & i : getStringsAttr("requiredSystemFeatures").value_or(Strings()))
|
||||||
res.insert(i);
|
res.insert(i);
|
||||||
|
if (!derivationHasKnownOutputPaths(drv.type()))
|
||||||
|
res.insert("ca-derivations");
|
||||||
return res;
|
return res;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1,5 +1,6 @@
|
||||||
#include "realisation.hh"
|
#include "realisation.hh"
|
||||||
#include "store-api.hh"
|
#include "store-api.hh"
|
||||||
|
#include "closure.hh"
|
||||||
#include <nlohmann/json.hpp>
|
#include <nlohmann/json.hpp>
|
||||||
|
|
||||||
namespace nix {
|
namespace nix {
|
||||||
|
@ -21,11 +22,52 @@ std::string DrvOutput::to_string() const {
|
||||||
return strHash() + "!" + outputName;
|
return strHash() + "!" + outputName;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
std::set<Realisation> Realisation::closure(Store & store, const std::set<Realisation> & startOutputs)
|
||||||
|
{
|
||||||
|
std::set<Realisation> res;
|
||||||
|
Realisation::closure(store, startOutputs, res);
|
||||||
|
return res;
|
||||||
|
}
|
||||||
|
|
||||||
|
void Realisation::closure(Store & store, const std::set<Realisation> & startOutputs, std::set<Realisation> & res)
|
||||||
|
{
|
||||||
|
auto getDeps = [&](const Realisation& current) -> std::set<Realisation> {
|
||||||
|
std::set<Realisation> res;
|
||||||
|
for (auto& [currentDep, _] : current.dependentRealisations) {
|
||||||
|
if (auto currentRealisation = store.queryRealisation(currentDep))
|
||||||
|
res.insert(*currentRealisation);
|
||||||
|
else
|
||||||
|
throw Error(
|
||||||
|
"Unrealised derivation '%s'", currentDep.to_string());
|
||||||
|
}
|
||||||
|
return res;
|
||||||
|
};
|
||||||
|
|
||||||
|
computeClosure<Realisation>(
|
||||||
|
startOutputs, res,
|
||||||
|
[&](const Realisation& current,
|
||||||
|
std::function<void(std::promise<std::set<Realisation>>&)>
|
||||||
|
processEdges) {
|
||||||
|
std::promise<std::set<Realisation>> promise;
|
||||||
|
try {
|
||||||
|
auto res = getDeps(current);
|
||||||
|
promise.set_value(res);
|
||||||
|
} catch (...) {
|
||||||
|
promise.set_exception(std::current_exception());
|
||||||
|
}
|
||||||
|
return processEdges(promise);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
nlohmann::json Realisation::toJSON() const {
|
nlohmann::json Realisation::toJSON() const {
|
||||||
|
auto jsonDependentRealisations = nlohmann::json::object();
|
||||||
|
for (auto & [depId, depOutPath] : dependentRealisations)
|
||||||
|
jsonDependentRealisations.emplace(depId.to_string(), depOutPath.to_string());
|
||||||
return nlohmann::json{
|
return nlohmann::json{
|
||||||
{"id", id.to_string()},
|
{"id", id.to_string()},
|
||||||
{"outPath", outPath.to_string()},
|
{"outPath", outPath.to_string()},
|
||||||
{"signatures", signatures},
|
{"signatures", signatures},
|
||||||
|
{"dependentRealisations", jsonDependentRealisations},
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -51,10 +93,16 @@ Realisation Realisation::fromJSON(
|
||||||
if (auto signaturesIterator = json.find("signatures"); signaturesIterator != json.end())
|
if (auto signaturesIterator = json.find("signatures"); signaturesIterator != json.end())
|
||||||
signatures.insert(signaturesIterator->begin(), signaturesIterator->end());
|
signatures.insert(signaturesIterator->begin(), signaturesIterator->end());
|
||||||
|
|
||||||
|
std::map <DrvOutput, StorePath> dependentRealisations;
|
||||||
|
if (auto jsonDependencies = json.find("dependentRealisations"); jsonDependencies != json.end())
|
||||||
|
for (auto & [jsonDepId, jsonDepOutPath] : jsonDependencies->get<std::map<std::string, std::string>>())
|
||||||
|
dependentRealisations.insert({DrvOutput::parse(jsonDepId), StorePath(jsonDepOutPath)});
|
||||||
|
|
||||||
return Realisation{
|
return Realisation{
|
||||||
.id = DrvOutput::parse(getField("id")),
|
.id = DrvOutput::parse(getField("id")),
|
||||||
.outPath = StorePath(getField("outPath")),
|
.outPath = StorePath(getField("outPath")),
|
||||||
.signatures = signatures,
|
.signatures = signatures,
|
||||||
|
.dependentRealisations = dependentRealisations,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -92,6 +140,16 @@ StorePath RealisedPath::path() const {
|
||||||
return std::visit([](auto && arg) { return arg.getPath(); }, raw);
|
return std::visit([](auto && arg) { return arg.getPath(); }, raw);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool Realisation::isCompatibleWith(const Realisation & other) const
|
||||||
|
{
|
||||||
|
assert (id == other.id);
|
||||||
|
if (outPath == other.outPath) {
|
||||||
|
assert(dependentRealisations == other.dependentRealisations);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
void RealisedPath::closure(
|
void RealisedPath::closure(
|
||||||
Store& store,
|
Store& store,
|
||||||
const RealisedPath::Set& startPaths,
|
const RealisedPath::Set& startPaths,
|
||||||
|
|
|
@ -28,6 +28,14 @@ struct Realisation {
|
||||||
|
|
||||||
StringSet signatures;
|
StringSet signatures;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The realisations that are required for the current one to be valid.
|
||||||
|
*
|
||||||
|
* When importing this realisation, the store will first check that all its
|
||||||
|
* dependencies exist, and map to the correct output path
|
||||||
|
*/
|
||||||
|
std::map<DrvOutput, StorePath> dependentRealisations;
|
||||||
|
|
||||||
nlohmann::json toJSON() const;
|
nlohmann::json toJSON() const;
|
||||||
static Realisation fromJSON(const nlohmann::json& json, const std::string& whence);
|
static Realisation fromJSON(const nlohmann::json& json, const std::string& whence);
|
||||||
|
|
||||||
|
@ -36,6 +44,11 @@ struct Realisation {
|
||||||
bool checkSignature(const PublicKeys & publicKeys, const std::string & sig) const;
|
bool checkSignature(const PublicKeys & publicKeys, const std::string & sig) const;
|
||||||
size_t checkSignatures(const PublicKeys & publicKeys) const;
|
size_t checkSignatures(const PublicKeys & publicKeys) const;
|
||||||
|
|
||||||
|
static std::set<Realisation> closure(Store &, const std::set<Realisation> &);
|
||||||
|
static void closure(Store &, const std::set<Realisation> &, std::set<Realisation>& res);
|
||||||
|
|
||||||
|
bool isCompatibleWith(const Realisation & other) const;
|
||||||
|
|
||||||
StorePath getPath() const { return outPath; }
|
StorePath getPath() const { return outPath; }
|
||||||
|
|
||||||
GENERATE_CMP(Realisation, me->id, me->outPath);
|
GENERATE_CMP(Realisation, me->id, me->outPath);
|
||||||
|
|
|
@ -337,6 +337,13 @@ ValidPathInfo Store::addToStoreSlow(std::string_view name, const Path & srcPath,
|
||||||
return info;
|
return info;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
StringSet StoreConfig::getDefaultSystemFeatures()
|
||||||
|
{
|
||||||
|
auto res = settings.systemFeatures.get();
|
||||||
|
if (settings.isExperimentalFeatureEnabled("ca-derivations"))
|
||||||
|
res.insert("ca-derivations");
|
||||||
|
return res;
|
||||||
|
}
|
||||||
|
|
||||||
Store::Store(const Params & params)
|
Store::Store(const Params & params)
|
||||||
: StoreConfig(params)
|
: StoreConfig(params)
|
||||||
|
@ -816,20 +823,39 @@ std::map<StorePath, StorePath> copyPaths(ref<Store> srcStore, ref<Store> dstStor
|
||||||
RepairFlag repair, CheckSigsFlag checkSigs, SubstituteFlag substitute)
|
RepairFlag repair, CheckSigsFlag checkSigs, SubstituteFlag substitute)
|
||||||
{
|
{
|
||||||
StorePathSet storePaths;
|
StorePathSet storePaths;
|
||||||
std::set<Realisation> realisations;
|
std::set<Realisation> toplevelRealisations;
|
||||||
for (auto & path : paths) {
|
for (auto & path : paths) {
|
||||||
storePaths.insert(path.path());
|
storePaths.insert(path.path());
|
||||||
if (auto realisation = std::get_if<Realisation>(&path.raw)) {
|
if (auto realisation = std::get_if<Realisation>(&path.raw)) {
|
||||||
settings.requireExperimentalFeature("ca-derivations");
|
settings.requireExperimentalFeature("ca-derivations");
|
||||||
realisations.insert(*realisation);
|
toplevelRealisations.insert(*realisation);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
auto pathsMap = copyPaths(srcStore, dstStore, storePaths, repair, checkSigs, substitute);
|
auto pathsMap = copyPaths(srcStore, dstStore, storePaths, repair, checkSigs, substitute);
|
||||||
|
|
||||||
|
ThreadPool pool;
|
||||||
|
|
||||||
try {
|
try {
|
||||||
for (auto & realisation : realisations) {
|
// Copy the realisation closure
|
||||||
dstStore->registerDrvOutput(realisation, checkSigs);
|
processGraph<Realisation>(
|
||||||
}
|
pool, Realisation::closure(*srcStore, toplevelRealisations),
|
||||||
} catch (MissingExperimentalFeature & e) {
|
[&](const Realisation& current) -> std::set<Realisation> {
|
||||||
|
std::set<Realisation> children;
|
||||||
|
for (const auto& [drvOutput, _] : current.dependentRealisations) {
|
||||||
|
auto currentChild = srcStore->queryRealisation(drvOutput);
|
||||||
|
if (!currentChild)
|
||||||
|
throw Error(
|
||||||
|
"Incomplete realisation closure: '%s' is a "
|
||||||
|
"dependency of '%s' but isn’t registered",
|
||||||
|
drvOutput.to_string(), current.id.to_string());
|
||||||
|
children.insert(*currentChild);
|
||||||
|
}
|
||||||
|
return children;
|
||||||
|
},
|
||||||
|
[&](const Realisation& current) -> void {
|
||||||
|
dstStore->registerDrvOutput(current, checkSigs);
|
||||||
|
});
|
||||||
|
} catch (MissingExperimentalFeature& e) {
|
||||||
// Don't fail if the remote doesn't support CA derivations is it might
|
// Don't fail if the remote doesn't support CA derivations is it might
|
||||||
// not be within our control to change that, and we might still want
|
// not be within our control to change that, and we might still want
|
||||||
// to at least copy the output paths.
|
// to at least copy the output paths.
|
||||||
|
|
|
@ -180,6 +180,8 @@ struct StoreConfig : public Config
|
||||||
|
|
||||||
StoreConfig() = delete;
|
StoreConfig() = delete;
|
||||||
|
|
||||||
|
StringSet getDefaultSystemFeatures();
|
||||||
|
|
||||||
virtual ~StoreConfig() { }
|
virtual ~StoreConfig() { }
|
||||||
|
|
||||||
virtual const std::string name() = 0;
|
virtual const std::string name() = 0;
|
||||||
|
@ -196,7 +198,7 @@ struct StoreConfig : public Config
|
||||||
|
|
||||||
Setting<bool> wantMassQuery{this, false, "want-mass-query", "whether this substituter can be queried efficiently for path validity"};
|
Setting<bool> wantMassQuery{this, false, "want-mass-query", "whether this substituter can be queried efficiently for path validity"};
|
||||||
|
|
||||||
Setting<StringSet> systemFeatures{this, settings.systemFeatures,
|
Setting<StringSet> systemFeatures{this, getDefaultSystemFeatures(),
|
||||||
"system-features",
|
"system-features",
|
||||||
"Optional features that the system this store builds on implements (like \"kvm\")."};
|
"Optional features that the system this store builds on implements (like \"kvm\")."};
|
||||||
|
|
||||||
|
@ -869,4 +871,9 @@ std::pair<std::string, Store::Params> splitUriAndParams(const std::string & uri)
|
||||||
|
|
||||||
std::optional<ContentAddress> getDerivationCA(const BasicDerivation & drv);
|
std::optional<ContentAddress> getDerivationCA(const BasicDerivation & drv);
|
||||||
|
|
||||||
|
std::map<DrvOutput, StorePath> drvOutputReferences(
|
||||||
|
Store & store,
|
||||||
|
const Derivation & drv,
|
||||||
|
const StorePath & outputPath);
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -25,6 +25,8 @@
|
||||||
}
|
}
|
||||||
#define GENERATE_EQUAL(args...) GENERATE_ONE_CMP(==, args)
|
#define GENERATE_EQUAL(args...) GENERATE_ONE_CMP(==, args)
|
||||||
#define GENERATE_LEQ(args...) GENERATE_ONE_CMP(<, args)
|
#define GENERATE_LEQ(args...) GENERATE_ONE_CMP(<, args)
|
||||||
|
#define GENERATE_NEQ(args...) GENERATE_ONE_CMP(!=, args)
|
||||||
#define GENERATE_CMP(args...) \
|
#define GENERATE_CMP(args...) \
|
||||||
GENERATE_EQUAL(args) \
|
GENERATE_EQUAL(args) \
|
||||||
GENERATE_LEQ(args)
|
GENERATE_LEQ(args) \
|
||||||
|
GENERATE_NEQ(args)
|
||||||
|
|
|
@ -46,7 +46,7 @@ public:
|
||||||
: printBuildLogs(printBuildLogs)
|
: printBuildLogs(printBuildLogs)
|
||||||
{
|
{
|
||||||
systemd = getEnv("IN_SYSTEMD") == "1";
|
systemd = getEnv("IN_SYSTEMD") == "1";
|
||||||
tty = isatty(STDERR_FILENO);
|
tty = shouldANSI();
|
||||||
}
|
}
|
||||||
|
|
||||||
bool isVerbose() override {
|
bool isVerbose() override {
|
||||||
|
|
|
@ -32,7 +32,7 @@ ParsedURL parseURL(const std::string & url)
|
||||||
auto isFile = scheme.find("file") != std::string::npos;
|
auto isFile = scheme.find("file") != std::string::npos;
|
||||||
|
|
||||||
if (authority && *authority != "" && isFile)
|
if (authority && *authority != "" && isFile)
|
||||||
throw Error("file:// URL '%s' has unexpected authority '%s'",
|
throw BadURL("file:// URL '%s' has unexpected authority '%s'",
|
||||||
url, *authority);
|
url, *authority);
|
||||||
|
|
||||||
if (isFile && path.empty())
|
if (isFile && path.empty())
|
||||||
|
|
|
@ -1372,6 +1372,12 @@ void ignoreException()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool shouldANSI()
|
||||||
|
{
|
||||||
|
return isatty(STDERR_FILENO)
|
||||||
|
&& getEnv("TERM").value_or("dumb") != "dumb"
|
||||||
|
&& !getEnv("NO_COLOR").has_value();
|
||||||
|
}
|
||||||
|
|
||||||
std::string filterANSIEscapes(const std::string & s, bool filterAll, unsigned int width)
|
std::string filterANSIEscapes(const std::string & s, bool filterAll, unsigned int width)
|
||||||
{
|
{
|
||||||
|
|
|
@ -482,6 +482,9 @@ constexpr char treeLast[] = "└───";
|
||||||
constexpr char treeLine[] = "│ ";
|
constexpr char treeLine[] = "│ ";
|
||||||
constexpr char treeNull[] = " ";
|
constexpr char treeNull[] = " ";
|
||||||
|
|
||||||
|
/* Determine whether ANSI escape sequences are appropriate for the
|
||||||
|
present output. */
|
||||||
|
bool shouldANSI();
|
||||||
|
|
||||||
/* Truncate a string to 'width' printable characters. If 'filterAll'
|
/* Truncate a string to 'width' printable characters. If 'filterAll'
|
||||||
is true, all ANSI escape sequences are filtered out. Otherwise,
|
is true, all ANSI escape sequences are filtered out. Otherwise,
|
||||||
|
|
|
@ -392,6 +392,12 @@ static void main_nix_build(int argc, char * * argv)
|
||||||
|
|
||||||
if (dryRun) return;
|
if (dryRun) return;
|
||||||
|
|
||||||
|
if (settings.isExperimentalFeatureEnabled("ca-derivations")) {
|
||||||
|
auto resolvedDrv = drv.tryResolve(*store);
|
||||||
|
assert(resolvedDrv && "Successfully resolved the derivation");
|
||||||
|
drv = *resolvedDrv;
|
||||||
|
}
|
||||||
|
|
||||||
// Set the environment.
|
// Set the environment.
|
||||||
auto env = getEnv();
|
auto env = getEnv();
|
||||||
|
|
||||||
|
|
|
@ -8,7 +8,7 @@
|
||||||
#include "affinity.hh"
|
#include "affinity.hh"
|
||||||
#include "progress-bar.hh"
|
#include "progress-bar.hh"
|
||||||
|
|
||||||
#include <regex>
|
#include <nlohmann/json.hpp>
|
||||||
|
|
||||||
using namespace nix;
|
using namespace nix;
|
||||||
|
|
||||||
|
@ -25,94 +25,142 @@ static DevelopSettings developSettings;
|
||||||
|
|
||||||
static GlobalConfig::Register rDevelopSettings(&developSettings);
|
static GlobalConfig::Register rDevelopSettings(&developSettings);
|
||||||
|
|
||||||
struct Var
|
|
||||||
{
|
|
||||||
bool exported = true;
|
|
||||||
bool associative = false;
|
|
||||||
std::string quoted; // quoted string or array
|
|
||||||
};
|
|
||||||
|
|
||||||
struct BuildEnvironment
|
struct BuildEnvironment
|
||||||
{
|
{
|
||||||
std::map<std::string, Var> env;
|
struct String
|
||||||
std::string bashFunctions;
|
{
|
||||||
};
|
bool exported;
|
||||||
|
std::string value;
|
||||||
|
|
||||||
BuildEnvironment readEnvironment(const Path & path)
|
bool operator == (const String & other) const
|
||||||
{
|
{
|
||||||
BuildEnvironment res;
|
return exported == other.exported && value == other.value;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
std::set<std::string> exported;
|
using Array = std::vector<std::string>;
|
||||||
|
|
||||||
debug("reading environment file '%s'", path);
|
using Associative = std::map<std::string, std::string>;
|
||||||
|
|
||||||
auto file = readFile(path);
|
using Value = std::variant<String, Array, Associative>;
|
||||||
|
|
||||||
auto pos = file.cbegin();
|
std::map<std::string, Value> vars;
|
||||||
|
std::map<std::string, std::string> bashFunctions;
|
||||||
|
|
||||||
static std::string varNameRegex =
|
static BuildEnvironment fromJSON(std::string_view in)
|
||||||
R"re((?:[a-zA-Z_][a-zA-Z0-9_]*))re";
|
{
|
||||||
|
BuildEnvironment res;
|
||||||
|
|
||||||
static std::string simpleStringRegex =
|
std::set<std::string> exported;
|
||||||
R"re((?:[a-zA-Z0-9_/:\.\-\+=]*))re";
|
|
||||||
|
|
||||||
static std::string dquotedStringRegex =
|
auto json = nlohmann::json::parse(in);
|
||||||
R"re((?:\$?"(?:[^"\\]|\\[$`"\\\n])*"))re";
|
|
||||||
|
|
||||||
static std::string squotedStringRegex =
|
for (auto & [name, info] : json["variables"].items()) {
|
||||||
R"re((?:\$?(?:'(?:[^'\\]|\\[abeEfnrtv\\'"?])*'|\\')+))re";
|
std::string type = info["type"];
|
||||||
|
if (type == "var" || type == "exported")
|
||||||
static std::string indexedArrayRegex =
|
res.vars.insert({name, BuildEnvironment::String { .exported = type == "exported", .value = info["value"] }});
|
||||||
R"re((?:\(( *\[[0-9]+\]="(?:[^"\\]|\\.)*")*\)))re";
|
else if (type == "array")
|
||||||
|
res.vars.insert({name, (Array) info["value"]});
|
||||||
static std::regex declareRegex(
|
else if (type == "associative")
|
||||||
"^declare -a?x (" + varNameRegex + ")(=(" +
|
res.vars.insert({name, (Associative) info["value"]});
|
||||||
dquotedStringRegex + "|" + indexedArrayRegex + "))?\n");
|
|
||||||
|
|
||||||
static std::regex varRegex(
|
|
||||||
"^(" + varNameRegex + ")=(" + simpleStringRegex + "|" + squotedStringRegex + "|" + indexedArrayRegex + ")\n");
|
|
||||||
|
|
||||||
/* Note: we distinguish between an indexed and associative array
|
|
||||||
using the space before the closing parenthesis. Will
|
|
||||||
undoubtedly regret this some day. */
|
|
||||||
static std::regex assocArrayRegex(
|
|
||||||
"^(" + varNameRegex + ")=" + R"re((?:\(( *\[[^\]]+\]="(?:[^"\\]|\\.)*")* *\)))re" + "\n");
|
|
||||||
|
|
||||||
static std::regex functionRegex(
|
|
||||||
"^" + varNameRegex + " \\(\\) *\n");
|
|
||||||
|
|
||||||
while (pos != file.end()) {
|
|
||||||
|
|
||||||
std::smatch match;
|
|
||||||
|
|
||||||
if (std::regex_search(pos, file.cend(), match, declareRegex, std::regex_constants::match_continuous)) {
|
|
||||||
pos = match[0].second;
|
|
||||||
exported.insert(match[1]);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
else if (std::regex_search(pos, file.cend(), match, varRegex, std::regex_constants::match_continuous)) {
|
for (auto & [name, def] : json["bashFunctions"].items()) {
|
||||||
pos = match[0].second;
|
res.bashFunctions.insert({name, def});
|
||||||
res.env.insert({match[1], Var { .exported = exported.count(match[1]) > 0, .quoted = match[2] }});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
else if (std::regex_search(pos, file.cend(), match, assocArrayRegex, std::regex_constants::match_continuous)) {
|
return res;
|
||||||
pos = match[0].second;
|
|
||||||
res.env.insert({match[1], Var { .associative = true, .quoted = match[2] }});
|
|
||||||
}
|
|
||||||
|
|
||||||
else if (std::regex_search(pos, file.cend(), match, functionRegex, std::regex_constants::match_continuous)) {
|
|
||||||
res.bashFunctions = std::string(pos, file.cend());
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
else throw Error("shell environment '%s' has unexpected line '%s'",
|
|
||||||
path, file.substr(pos - file.cbegin(), 60));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
res.env.erase("__output");
|
std::string toJSON() const
|
||||||
|
{
|
||||||
|
auto res = nlohmann::json::object();
|
||||||
|
|
||||||
return res;
|
auto vars2 = nlohmann::json::object();
|
||||||
}
|
for (auto & [name, value] : vars) {
|
||||||
|
auto info = nlohmann::json::object();
|
||||||
|
if (auto str = std::get_if<String>(&value)) {
|
||||||
|
info["type"] = str->exported ? "exported" : "var";
|
||||||
|
info["value"] = str->value;
|
||||||
|
}
|
||||||
|
else if (auto arr = std::get_if<Array>(&value)) {
|
||||||
|
info["type"] = "array";
|
||||||
|
info["value"] = *arr;
|
||||||
|
}
|
||||||
|
else if (auto arr = std::get_if<Associative>(&value)) {
|
||||||
|
info["type"] = "associative";
|
||||||
|
info["value"] = *arr;
|
||||||
|
}
|
||||||
|
vars2[name] = std::move(info);
|
||||||
|
}
|
||||||
|
res["variables"] = std::move(vars2);
|
||||||
|
|
||||||
|
res["bashFunctions"] = bashFunctions;
|
||||||
|
|
||||||
|
auto json = res.dump();
|
||||||
|
|
||||||
|
assert(BuildEnvironment::fromJSON(json) == *this);
|
||||||
|
|
||||||
|
return json;
|
||||||
|
}
|
||||||
|
|
||||||
|
void toBash(std::ostream & out, const std::set<std::string> & ignoreVars) const
|
||||||
|
{
|
||||||
|
for (auto & [name, value] : vars) {
|
||||||
|
if (!ignoreVars.count(name)) {
|
||||||
|
if (auto str = std::get_if<String>(&value)) {
|
||||||
|
out << fmt("%s=%s\n", name, shellEscape(str->value));
|
||||||
|
if (str->exported)
|
||||||
|
out << fmt("export %s\n", name);
|
||||||
|
}
|
||||||
|
else if (auto arr = std::get_if<Array>(&value)) {
|
||||||
|
out << "declare -a " << name << "=(";
|
||||||
|
for (auto & s : *arr)
|
||||||
|
out << shellEscape(s) << " ";
|
||||||
|
out << ")\n";
|
||||||
|
}
|
||||||
|
else if (auto arr = std::get_if<Associative>(&value)) {
|
||||||
|
out << "declare -A " << name << "=(";
|
||||||
|
for (auto & [n, v] : *arr)
|
||||||
|
out << "[" << shellEscape(n) << "]=" << shellEscape(v) << " ";
|
||||||
|
out << ")\n";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
for (auto & [name, def] : bashFunctions) {
|
||||||
|
out << name << " ()\n{\n" << def << "}\n";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static std::string getString(const Value & value)
|
||||||
|
{
|
||||||
|
if (auto str = std::get_if<String>(&value))
|
||||||
|
return str->value;
|
||||||
|
else
|
||||||
|
throw Error("bash variable is not a string");
|
||||||
|
}
|
||||||
|
|
||||||
|
static Array getStrings(const Value & value)
|
||||||
|
{
|
||||||
|
if (auto str = std::get_if<String>(&value))
|
||||||
|
return tokenizeString<Array>(str->value);
|
||||||
|
else if (auto arr = std::get_if<Array>(&value)) {
|
||||||
|
return *arr;
|
||||||
|
} else if (auto assoc = std::get_if<Associative>(&value)) {
|
||||||
|
Array assocKeys;
|
||||||
|
std::for_each(assoc->begin(), assoc->end(), [&](auto & n) { assocKeys.push_back(n.first); });
|
||||||
|
return assocKeys;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
throw Error("bash variable is not a string or array");
|
||||||
|
}
|
||||||
|
|
||||||
|
bool operator == (const BuildEnvironment & other) const
|
||||||
|
{
|
||||||
|
return vars == other.vars && bashFunctions == other.bashFunctions;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
const static std::string getEnvSh =
|
const static std::string getEnvSh =
|
||||||
#include "get-env.sh.gen.hh"
|
#include "get-env.sh.gen.hh"
|
||||||
|
@ -144,17 +192,26 @@ StorePath getDerivationEnvironment(ref<Store> store, const StorePath & drvPath)
|
||||||
/* Rehash and write the derivation. FIXME: would be nice to use
|
/* Rehash and write the derivation. FIXME: would be nice to use
|
||||||
'buildDerivation', but that's privileged. */
|
'buildDerivation', but that's privileged. */
|
||||||
drv.name += "-env";
|
drv.name += "-env";
|
||||||
for (auto & output : drv.outputs) {
|
|
||||||
output.second = { .output = DerivationOutputInputAddressed { .path = StorePath::dummy } };
|
|
||||||
drv.env[output.first] = "";
|
|
||||||
}
|
|
||||||
drv.inputSrcs.insert(std::move(getEnvShPath));
|
drv.inputSrcs.insert(std::move(getEnvShPath));
|
||||||
Hash h = std::get<0>(hashDerivationModulo(*store, drv, true));
|
if (settings.isExperimentalFeatureEnabled("ca-derivations")) {
|
||||||
|
for (auto & output : drv.outputs) {
|
||||||
|
output.second = {
|
||||||
|
.output = DerivationOutputDeferred{},
|
||||||
|
};
|
||||||
|
drv.env[output.first] = hashPlaceholder(output.first);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
for (auto & output : drv.outputs) {
|
||||||
|
output.second = { .output = DerivationOutputInputAddressed { .path = StorePath::dummy } };
|
||||||
|
drv.env[output.first] = "";
|
||||||
|
}
|
||||||
|
Hash h = std::get<0>(hashDerivationModulo(*store, drv, true));
|
||||||
|
|
||||||
for (auto & output : drv.outputs) {
|
for (auto & output : drv.outputs) {
|
||||||
auto outPath = store->makeOutputPath(output.first, h, drv.name);
|
auto outPath = store->makeOutputPath(output.first, h, drv.name);
|
||||||
output.second = { .output = DerivationOutputInputAddressed { .path = outPath } };
|
output.second = { .output = DerivationOutputInputAddressed { .path = outPath } };
|
||||||
drv.env[output.first] = store->printStorePath(outPath);
|
drv.env[output.first] = store->printStorePath(outPath);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
auto shellDrvPath = writeDerivation(*store, drv);
|
auto shellDrvPath = writeDerivation(*store, drv);
|
||||||
|
@ -162,8 +219,7 @@ StorePath getDerivationEnvironment(ref<Store> store, const StorePath & drvPath)
|
||||||
/* Build the derivation. */
|
/* Build the derivation. */
|
||||||
store->buildPaths({DerivedPath::Built{shellDrvPath}});
|
store->buildPaths({DerivedPath::Built{shellDrvPath}});
|
||||||
|
|
||||||
for (auto & [_0, outputAndOptPath] : drv.outputsAndOptPaths(*store)) {
|
for (auto & [_0, optPath] : store->queryPartialDerivationOutputMap(shellDrvPath)) {
|
||||||
auto & [_1, optPath] = outputAndOptPath;
|
|
||||||
assert(optPath);
|
assert(optPath);
|
||||||
auto & outPath = *optPath;
|
auto & outPath = *optPath;
|
||||||
assert(store->isValidPath(outPath));
|
assert(store->isValidPath(outPath));
|
||||||
|
@ -177,19 +233,15 @@ StorePath getDerivationEnvironment(ref<Store> store, const StorePath & drvPath)
|
||||||
|
|
||||||
struct Common : InstallableCommand, MixProfile
|
struct Common : InstallableCommand, MixProfile
|
||||||
{
|
{
|
||||||
std::set<string> ignoreVars{
|
std::set<std::string> ignoreVars{
|
||||||
"BASHOPTS",
|
"BASHOPTS",
|
||||||
"EUID",
|
|
||||||
"HOME", // FIXME: don't ignore in pure mode?
|
"HOME", // FIXME: don't ignore in pure mode?
|
||||||
"HOSTNAME",
|
|
||||||
"NIX_BUILD_TOP",
|
"NIX_BUILD_TOP",
|
||||||
"NIX_ENFORCE_PURITY",
|
"NIX_ENFORCE_PURITY",
|
||||||
"NIX_LOG_FD",
|
"NIX_LOG_FD",
|
||||||
"NIX_REMOTE",
|
"NIX_REMOTE",
|
||||||
"PPID",
|
"PPID",
|
||||||
"PWD",
|
|
||||||
"SHELLOPTS",
|
"SHELLOPTS",
|
||||||
"SHLVL",
|
|
||||||
"SSL_CERT_FILE", // FIXME: only want to ignore /no-cert-file.crt
|
"SSL_CERT_FILE", // FIXME: only want to ignore /no-cert-file.crt
|
||||||
"TEMP",
|
"TEMP",
|
||||||
"TEMPDIR",
|
"TEMPDIR",
|
||||||
|
@ -225,22 +277,10 @@ struct Common : InstallableCommand, MixProfile
|
||||||
|
|
||||||
out << "nix_saved_PATH=\"$PATH\"\n";
|
out << "nix_saved_PATH=\"$PATH\"\n";
|
||||||
|
|
||||||
for (auto & i : buildEnvironment.env) {
|
buildEnvironment.toBash(out, ignoreVars);
|
||||||
if (!ignoreVars.count(i.first) && !hasPrefix(i.first, "BASH_")) {
|
|
||||||
if (i.second.associative)
|
|
||||||
out << fmt("declare -A %s=(%s)\n", i.first, i.second.quoted);
|
|
||||||
else {
|
|
||||||
out << fmt("%s=%s\n", i.first, i.second.quoted);
|
|
||||||
if (i.second.exported)
|
|
||||||
out << fmt("export %s\n", i.first);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
out << "PATH=\"$PATH:$nix_saved_PATH\"\n";
|
out << "PATH=\"$PATH:$nix_saved_PATH\"\n";
|
||||||
|
|
||||||
out << buildEnvironment.bashFunctions << "\n";
|
|
||||||
|
|
||||||
out << "export NIX_BUILD_TOP=\"$(mktemp -d -t nix-shell.XXXXXX)\"\n";
|
out << "export NIX_BUILD_TOP=\"$(mktemp -d -t nix-shell.XXXXXX)\"\n";
|
||||||
for (auto & i : {"TMP", "TMPDIR", "TEMP", "TEMPDIR"})
|
for (auto & i : {"TMP", "TMPDIR", "TEMP", "TEMPDIR"})
|
||||||
out << fmt("export %s=\"$NIX_BUILD_TOP\"\n", i);
|
out << fmt("export %s=\"$NIX_BUILD_TOP\"\n", i);
|
||||||
|
@ -250,24 +290,16 @@ struct Common : InstallableCommand, MixProfile
|
||||||
auto script = out.str();
|
auto script = out.str();
|
||||||
|
|
||||||
/* Substitute occurrences of output paths. */
|
/* Substitute occurrences of output paths. */
|
||||||
auto outputs = buildEnvironment.env.find("outputs");
|
auto outputs = buildEnvironment.vars.find("outputs");
|
||||||
assert(outputs != buildEnvironment.env.end());
|
assert(outputs != buildEnvironment.vars.end());
|
||||||
|
|
||||||
// FIXME: properly unquote 'outputs'.
|
// FIXME: properly unquote 'outputs'.
|
||||||
StringMap rewrites;
|
StringMap rewrites;
|
||||||
for (auto & outputName : tokenizeString<std::vector<std::string>>(replaceStrings(outputs->second.quoted, "'", ""))) {
|
for (auto & outputName : BuildEnvironment::getStrings(outputs->second)) {
|
||||||
// Hacky way to obtain the key of an associate array. This is needed for strctured attrs where
|
auto from = buildEnvironment.vars.find(outputName);
|
||||||
// `outputs` is an associative array. If the regex isn't matched, the non-structured-attrs behavior will
|
assert(from != buildEnvironment.vars.end());
|
||||||
// be used.
|
|
||||||
std::regex ptrn(R"re(\[([A-z0-9]+)\]=.*)re");
|
|
||||||
std::smatch match;
|
|
||||||
if (std::regex_match(outputName, match, ptrn)) {
|
|
||||||
outputName = match[1];
|
|
||||||
}
|
|
||||||
auto from = buildEnvironment.env.find(outputName);
|
|
||||||
assert(from != buildEnvironment.env.end());
|
|
||||||
// FIXME: unquote
|
// FIXME: unquote
|
||||||
rewrites.insert({from->second.quoted, outputsDir + "/" + outputName});
|
rewrites.insert({BuildEnvironment::getString(from->second), outputsDir + "/" + outputName});
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Substitute redirects. */
|
/* Substitute redirects. */
|
||||||
|
@ -321,7 +353,9 @@ struct Common : InstallableCommand, MixProfile
|
||||||
|
|
||||||
updateProfile(shellOutPath);
|
updateProfile(shellOutPath);
|
||||||
|
|
||||||
return {readEnvironment(strPath), strPath};
|
debug("reading environment file '%s'", strPath);
|
||||||
|
|
||||||
|
return {BuildEnvironment::fromJSON(readFile(strPath)), strPath};
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -443,13 +477,17 @@ struct CmdDevelop : Common, MixEnvironment
|
||||||
try {
|
try {
|
||||||
auto state = getEvalState();
|
auto state = getEvalState();
|
||||||
|
|
||||||
|
auto nixpkgsLockFlags = lockFlags;
|
||||||
|
nixpkgsLockFlags.inputOverrides = {};
|
||||||
|
nixpkgsLockFlags.inputUpdates = {};
|
||||||
|
|
||||||
auto bashInstallable = std::make_shared<InstallableFlake>(
|
auto bashInstallable = std::make_shared<InstallableFlake>(
|
||||||
this,
|
this,
|
||||||
state,
|
state,
|
||||||
installable->nixpkgsFlakeRef(),
|
installable->nixpkgsFlakeRef(),
|
||||||
Strings{"bashInteractive"},
|
Strings{"bashInteractive"},
|
||||||
Strings{"legacyPackages." + settings.thisSystem.get() + "."},
|
Strings{"legacyPackages." + settings.thisSystem.get() + "."},
|
||||||
lockFlags);
|
nixpkgsLockFlags);
|
||||||
|
|
||||||
shell = state->store->printStorePath(
|
shell = state->store->printStorePath(
|
||||||
toStorePath(state->store, Realise::Outputs, OperateOn::Output, bashInstallable)) + "/bin/bash";
|
toStorePath(state->store, Realise::Outputs, OperateOn::Output, bashInstallable)) + "/bin/bash";
|
||||||
|
@ -470,7 +508,7 @@ struct CmdDevelop : Common, MixEnvironment
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
struct CmdPrintDevEnv : Common
|
struct CmdPrintDevEnv : Common, MixJSON
|
||||||
{
|
{
|
||||||
std::string description() override
|
std::string description() override
|
||||||
{
|
{
|
||||||
|
@ -492,7 +530,10 @@ struct CmdPrintDevEnv : Common
|
||||||
|
|
||||||
stopProgressBar();
|
stopProgressBar();
|
||||||
|
|
||||||
std::cout << makeRcScript(store, buildEnvironment);
|
logger->writeToStdout(
|
||||||
|
json
|
||||||
|
? buildEnvironment.toJSON()
|
||||||
|
: makeRcScript(store, buildEnvironment));
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
@ -84,6 +84,7 @@ struct CmdFlakeUpdate : FlakeCommand
|
||||||
|
|
||||||
lockFlags.recreateLockFile = true;
|
lockFlags.recreateLockFile = true;
|
||||||
lockFlags.writeLockFile = true;
|
lockFlags.writeLockFile = true;
|
||||||
|
lockFlags.applyNixConfig = true;
|
||||||
|
|
||||||
lockFlake();
|
lockFlake();
|
||||||
}
|
}
|
||||||
|
@ -114,6 +115,7 @@ struct CmdFlakeLock : FlakeCommand
|
||||||
settings.tarballTtl = 0;
|
settings.tarballTtl = 0;
|
||||||
|
|
||||||
lockFlags.writeLockFile = true;
|
lockFlags.writeLockFile = true;
|
||||||
|
lockFlags.applyNixConfig = true;
|
||||||
|
|
||||||
lockFlake();
|
lockFlake();
|
||||||
}
|
}
|
||||||
|
@ -270,6 +272,8 @@ struct CmdFlakeCheck : FlakeCommand
|
||||||
settings.readOnlyMode = !build;
|
settings.readOnlyMode = !build;
|
||||||
|
|
||||||
auto state = getEvalState();
|
auto state = getEvalState();
|
||||||
|
|
||||||
|
lockFlags.applyNixConfig = true;
|
||||||
auto flake = lockFlake();
|
auto flake = lockFlake();
|
||||||
|
|
||||||
bool hasErrors = false;
|
bool hasErrors = false;
|
||||||
|
|
|
@ -8,6 +8,109 @@ if [[ -n $stdenv ]]; then
|
||||||
source $stdenv/setup
|
source $stdenv/setup
|
||||||
fi
|
fi
|
||||||
|
|
||||||
|
# Better to use compgen, but stdenv bash doesn't have it.
|
||||||
|
__vars="$(declare -p)"
|
||||||
|
__functions="$(declare -F)"
|
||||||
|
|
||||||
|
__dumpEnv() {
|
||||||
|
printf '{\n'
|
||||||
|
|
||||||
|
printf ' "bashFunctions": {\n'
|
||||||
|
local __first=1
|
||||||
|
while read __line; do
|
||||||
|
if ! [[ $__line =~ ^declare\ -f\ (.*) ]]; then continue; fi
|
||||||
|
__fun_name="${BASH_REMATCH[1]}"
|
||||||
|
__fun_body="$(type $__fun_name)"
|
||||||
|
if [[ $__fun_body =~ \{(.*)\} ]]; then
|
||||||
|
if [[ -z $__first ]]; then printf ',\n'; else __first=; fi
|
||||||
|
__fun_body="${BASH_REMATCH[1]}"
|
||||||
|
printf " "
|
||||||
|
__escapeString "$__fun_name"
|
||||||
|
printf ':'
|
||||||
|
__escapeString "$__fun_body"
|
||||||
|
else
|
||||||
|
printf "Cannot parse definition of function '%s'.\n" "$__fun_name" >&2
|
||||||
|
return 1
|
||||||
|
fi
|
||||||
|
done < <(printf "%s\n" "$__functions")
|
||||||
|
printf '\n },\n'
|
||||||
|
|
||||||
|
printf ' "variables": {\n'
|
||||||
|
local __first=1
|
||||||
|
while read __line; do
|
||||||
|
if ! [[ $__line =~ ^declare\ (-[^ ])\ ([^=]*) ]]; then continue; fi
|
||||||
|
local type="${BASH_REMATCH[1]}"
|
||||||
|
local __var_name="${BASH_REMATCH[2]}"
|
||||||
|
|
||||||
|
if [[ $__var_name =~ ^BASH_ || \
|
||||||
|
$__var_name = _ || \
|
||||||
|
$__var_name = DIRSTACK || \
|
||||||
|
$__var_name = EUID || \
|
||||||
|
$__var_name = FUNCNAME || \
|
||||||
|
$__var_name = HISTCMD || \
|
||||||
|
$__var_name = HOSTNAME || \
|
||||||
|
$__var_name = GROUPS || \
|
||||||
|
$__var_name = PIPESTATUS || \
|
||||||
|
$__var_name = PWD || \
|
||||||
|
$__var_name = RANDOM || \
|
||||||
|
$__var_name = SHLVL || \
|
||||||
|
$__var_name = SECONDS \
|
||||||
|
]]; then continue; fi
|
||||||
|
|
||||||
|
if [[ -z $__first ]]; then printf ',\n'; else __first=; fi
|
||||||
|
|
||||||
|
printf " "
|
||||||
|
__escapeString "$__var_name"
|
||||||
|
printf ': {'
|
||||||
|
|
||||||
|
# FIXME: handle -i, -r, -n.
|
||||||
|
if [[ $type == -x ]]; then
|
||||||
|
printf '"type": "exported", "value": '
|
||||||
|
__escapeString "${!__var_name}"
|
||||||
|
elif [[ $type == -- ]]; then
|
||||||
|
printf '"type": "var", "value": '
|
||||||
|
__escapeString "${!__var_name}"
|
||||||
|
elif [[ $type == -a ]]; then
|
||||||
|
printf '"type": "array", "value": ['
|
||||||
|
local __first2=1
|
||||||
|
__var_name="$__var_name[@]"
|
||||||
|
for __i in "${!__var_name}"; do
|
||||||
|
if [[ -z $__first2 ]]; then printf ', '; else __first2=; fi
|
||||||
|
__escapeString "$__i"
|
||||||
|
printf ' '
|
||||||
|
done
|
||||||
|
printf ']'
|
||||||
|
elif [[ $type == -A ]]; then
|
||||||
|
printf '"type": "associative", "value": {\n'
|
||||||
|
local __first2=1
|
||||||
|
declare -n __var_name2="$__var_name"
|
||||||
|
for __i in "${!__var_name2[@]}"; do
|
||||||
|
if [[ -z $__first2 ]]; then printf ',\n'; else __first2=; fi
|
||||||
|
printf " "
|
||||||
|
__escapeString "$__i"
|
||||||
|
printf ": "
|
||||||
|
__escapeString "${__var_name2[$__i]}"
|
||||||
|
done
|
||||||
|
printf '\n }'
|
||||||
|
else
|
||||||
|
printf '"type": "unknown"'
|
||||||
|
fi
|
||||||
|
|
||||||
|
printf "}"
|
||||||
|
done < <(printf "%s\n" "$__vars")
|
||||||
|
printf '\n }\n}'
|
||||||
|
}
|
||||||
|
|
||||||
|
__escapeString() {
|
||||||
|
local __s="$1"
|
||||||
|
__s="${__s//\\/\\\\}"
|
||||||
|
__s="${__s//\"/\\\"}"
|
||||||
|
__s="${__s//$'\n'/\\n}"
|
||||||
|
__s="${__s//$'\r'/\\r}"
|
||||||
|
__s="${__s//$'\t'/\\t}"
|
||||||
|
printf '"%s"' "$__s"
|
||||||
|
}
|
||||||
|
|
||||||
# In case of `__structuredAttrs = true;` the list of outputs is an associative
|
# In case of `__structuredAttrs = true;` the list of outputs is an associative
|
||||||
# array with a format like `outname => /nix/store/hash-drvname-outname`, so `__olist`
|
# array with a format like `outname => /nix/store/hash-drvname-outname`, so `__olist`
|
||||||
# must contain the array's keys (hence `${!...[@]}`) in this case.
|
# must contain the array's keys (hence `${!...[@]}`) in this case.
|
||||||
|
@ -19,8 +122,7 @@ fi
|
||||||
|
|
||||||
for __output in $__olist; do
|
for __output in $__olist; do
|
||||||
if [[ -z $__done ]]; then
|
if [[ -z $__done ]]; then
|
||||||
export > "${!__output}"
|
__dumpEnv > ${!__output}
|
||||||
set >> "${!__output}"
|
|
||||||
__done=1
|
__done=1
|
||||||
else
|
else
|
||||||
echo -n >> "${!__output}"
|
echo -n >> "${!__output}"
|
||||||
|
|
|
@ -8,12 +8,43 @@ R""(
|
||||||
# . <(nix print-dev-env nixpkgs#hello)
|
# . <(nix print-dev-env nixpkgs#hello)
|
||||||
```
|
```
|
||||||
|
|
||||||
|
* Get the build environment in JSON format:
|
||||||
|
|
||||||
|
```console
|
||||||
|
# nix print-dev-env nixpkgs#hello --json
|
||||||
|
```
|
||||||
|
|
||||||
|
The output will look like this:
|
||||||
|
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"bashFunctions": {
|
||||||
|
"buildPhase": " \n runHook preBuild;\n...",
|
||||||
|
...
|
||||||
|
},
|
||||||
|
"variables": {
|
||||||
|
"src": {
|
||||||
|
"type": "exported",
|
||||||
|
"value": "/nix/store/3x7dwzq014bblazs7kq20p9hyzz0qh8g-hello-2.10.tar.gz"
|
||||||
|
},
|
||||||
|
"postUnpackHooks": {
|
||||||
|
"type": "array",
|
||||||
|
"value": ["_updateSourceDateEpochFromSourceRoot"]
|
||||||
|
},
|
||||||
|
...
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
# Description
|
# Description
|
||||||
|
|
||||||
This command prints a shell script that can be sourced by `b`ash and
|
This command prints a shell script that can be sourced by `bash` and
|
||||||
that sets the environment variables and shell functions defined by the
|
that sets the variables and shell functions defined by the build
|
||||||
build process of *installable*. This allows you to get a similar build
|
process of *installable*. This allows you to get a similar build
|
||||||
environment in your current shell rather than in a subshell (as with
|
environment in your current shell rather than in a subshell (as with
|
||||||
`nix develop`).
|
`nix develop`).
|
||||||
|
|
||||||
|
With `--json`, the output is a JSON serialisation of the variables and
|
||||||
|
functions defined by the build process.
|
||||||
|
|
||||||
)""
|
)""
|
||||||
|
|
|
@ -15,6 +15,7 @@ R""(
|
||||||
```
|
```
|
||||||
|
|
||||||
* Remove all packages:
|
* Remove all packages:
|
||||||
|
|
||||||
```console
|
```console
|
||||||
# nix profile remove '.*'
|
# nix profile remove '.*'
|
||||||
```
|
```
|
||||||
|
|
|
@ -21,6 +21,13 @@ R""(
|
||||||
# nix registry add nixpkgs/nixos-20.03 ~/Dev/nixpkgs
|
# nix registry add nixpkgs/nixos-20.03 ~/Dev/nixpkgs
|
||||||
```
|
```
|
||||||
|
|
||||||
|
* Add `nixpkgs` pointing to `github:nixos/nixpkgs` to your custom flake
|
||||||
|
registry:
|
||||||
|
|
||||||
|
```console
|
||||||
|
nix registry add --registry ./custom-flake-registry.json nixpkgs github:nixos/nixpkgs
|
||||||
|
```
|
||||||
|
|
||||||
# Description
|
# Description
|
||||||
|
|
||||||
This command adds an entry to the user registry that maps flake
|
This command adds an entry to the user registry that maps flake
|
||||||
|
|
|
@ -24,6 +24,13 @@ R""(
|
||||||
…
|
…
|
||||||
```
|
```
|
||||||
|
|
||||||
|
* Pin `nixpkgs` in a custom registry to its most recent Git revision:
|
||||||
|
|
||||||
|
```console
|
||||||
|
# nix registry pin --registry ./custom-flake-registry.json nixpkgs
|
||||||
|
```
|
||||||
|
|
||||||
|
|
||||||
# Description
|
# Description
|
||||||
|
|
||||||
This command adds an entry to the user registry that maps flake
|
This command adds an entry to the user registry that maps flake
|
||||||
|
|
|
@ -8,6 +8,12 @@ R""(
|
||||||
# nix registry remove nixpkgs
|
# nix registry remove nixpkgs
|
||||||
```
|
```
|
||||||
|
|
||||||
|
* Remove the entry `nixpkgs` from a custom registry:
|
||||||
|
|
||||||
|
```console
|
||||||
|
# nix registry remove --registry ./custom-flake-registry.json nixpkgs
|
||||||
|
```
|
||||||
|
|
||||||
# Description
|
# Description
|
||||||
|
|
||||||
This command removes from the user registry any entry for flake
|
This command removes from the user registry any entry for flake
|
||||||
|
|
|
@ -10,6 +10,46 @@
|
||||||
using namespace nix;
|
using namespace nix;
|
||||||
using namespace nix::flake;
|
using namespace nix::flake;
|
||||||
|
|
||||||
|
|
||||||
|
class RegistryCommand : virtual Args
|
||||||
|
{
|
||||||
|
std::string registry_path;
|
||||||
|
|
||||||
|
std::shared_ptr<fetchers::Registry> registry;
|
||||||
|
|
||||||
|
public:
|
||||||
|
|
||||||
|
RegistryCommand()
|
||||||
|
{
|
||||||
|
addFlag({
|
||||||
|
.longName = "registry",
|
||||||
|
.description = "The registry to operate on.",
|
||||||
|
.labels = {"registry"},
|
||||||
|
.handler = {®istry_path},
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
std::shared_ptr<fetchers::Registry> getRegistry()
|
||||||
|
{
|
||||||
|
if (registry) return registry;
|
||||||
|
if (registry_path.empty()) {
|
||||||
|
registry = fetchers::getUserRegistry();
|
||||||
|
} else {
|
||||||
|
registry = fetchers::getCustomRegistry(registry_path);
|
||||||
|
}
|
||||||
|
return registry;
|
||||||
|
}
|
||||||
|
|
||||||
|
Path getRegistryPath()
|
||||||
|
{
|
||||||
|
if (registry_path.empty()) {
|
||||||
|
return fetchers::getUserRegistryPath();
|
||||||
|
} else {
|
||||||
|
return registry_path;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
struct CmdRegistryList : StoreCommand
|
struct CmdRegistryList : StoreCommand
|
||||||
{
|
{
|
||||||
std::string description() override
|
std::string description() override
|
||||||
|
@ -45,7 +85,7 @@ struct CmdRegistryList : StoreCommand
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
struct CmdRegistryAdd : MixEvalArgs, Command
|
struct CmdRegistryAdd : MixEvalArgs, Command, RegistryCommand
|
||||||
{
|
{
|
||||||
std::string fromUrl, toUrl;
|
std::string fromUrl, toUrl;
|
||||||
|
|
||||||
|
@ -71,16 +111,16 @@ struct CmdRegistryAdd : MixEvalArgs, Command
|
||||||
{
|
{
|
||||||
auto fromRef = parseFlakeRef(fromUrl);
|
auto fromRef = parseFlakeRef(fromUrl);
|
||||||
auto toRef = parseFlakeRef(toUrl);
|
auto toRef = parseFlakeRef(toUrl);
|
||||||
|
auto registry = getRegistry();
|
||||||
fetchers::Attrs extraAttrs;
|
fetchers::Attrs extraAttrs;
|
||||||
if (toRef.subdir != "") extraAttrs["dir"] = toRef.subdir;
|
if (toRef.subdir != "") extraAttrs["dir"] = toRef.subdir;
|
||||||
auto userRegistry = fetchers::getUserRegistry();
|
registry->remove(fromRef.input);
|
||||||
userRegistry->remove(fromRef.input);
|
registry->add(fromRef.input, toRef.input, extraAttrs);
|
||||||
userRegistry->add(fromRef.input, toRef.input, extraAttrs);
|
registry->write(getRegistryPath());
|
||||||
userRegistry->write(fetchers::getUserRegistryPath());
|
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
struct CmdRegistryRemove : virtual Args, MixEvalArgs, Command
|
struct CmdRegistryRemove : RegistryCommand, Command
|
||||||
{
|
{
|
||||||
std::string url;
|
std::string url;
|
||||||
|
|
||||||
|
@ -103,19 +143,21 @@ struct CmdRegistryRemove : virtual Args, MixEvalArgs, Command
|
||||||
|
|
||||||
void run() override
|
void run() override
|
||||||
{
|
{
|
||||||
auto userRegistry = fetchers::getUserRegistry();
|
auto registry = getRegistry();
|
||||||
userRegistry->remove(parseFlakeRef(url).input);
|
registry->remove(parseFlakeRef(url).input);
|
||||||
userRegistry->write(fetchers::getUserRegistryPath());
|
registry->write(getRegistryPath());
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
struct CmdRegistryPin : virtual Args, EvalCommand
|
struct CmdRegistryPin : RegistryCommand, EvalCommand
|
||||||
{
|
{
|
||||||
std::string url;
|
std::string url;
|
||||||
|
|
||||||
|
std::string locked;
|
||||||
|
|
||||||
std::string description() override
|
std::string description() override
|
||||||
{
|
{
|
||||||
return "pin a flake to its current version in user flake registry";
|
return "pin a flake to its current version or to the current version of a flake URL";
|
||||||
}
|
}
|
||||||
|
|
||||||
std::string doc() override
|
std::string doc() override
|
||||||
|
@ -128,18 +170,31 @@ struct CmdRegistryPin : virtual Args, EvalCommand
|
||||||
CmdRegistryPin()
|
CmdRegistryPin()
|
||||||
{
|
{
|
||||||
expectArg("url", &url);
|
expectArg("url", &url);
|
||||||
|
|
||||||
|
expectArgs({
|
||||||
|
.label = "locked",
|
||||||
|
.optional = true,
|
||||||
|
.handler = {&locked},
|
||||||
|
.completer = {[&](size_t, std::string_view prefix) {
|
||||||
|
completeFlakeRef(getStore(), prefix);
|
||||||
|
}}
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
void run(nix::ref<nix::Store> store) override
|
void run(nix::ref<nix::Store> store) override
|
||||||
{
|
{
|
||||||
|
if (locked.empty()) {
|
||||||
|
locked = url;
|
||||||
|
}
|
||||||
|
auto registry = getRegistry();
|
||||||
auto ref = parseFlakeRef(url);
|
auto ref = parseFlakeRef(url);
|
||||||
auto userRegistry = fetchers::getUserRegistry();
|
auto locked_ref = parseFlakeRef(locked);
|
||||||
userRegistry->remove(ref.input);
|
registry->remove(ref.input);
|
||||||
auto [tree, resolved] = ref.resolve(store).input.fetch(store);
|
auto [tree, resolved] = locked_ref.resolve(store).input.fetch(store);
|
||||||
fetchers::Attrs extraAttrs;
|
fetchers::Attrs extraAttrs;
|
||||||
if (ref.subdir != "") extraAttrs["dir"] = ref.subdir;
|
if (ref.subdir != "") extraAttrs["dir"] = ref.subdir;
|
||||||
userRegistry->add(ref.input, resolved, extraAttrs);
|
registry->add(ref.input, resolved, extraAttrs);
|
||||||
userRegistry->write(fetchers::getUserRegistryPath());
|
registry->write(getRegistryPath());
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
@ -104,6 +104,26 @@ NixRepl::~NixRepl()
|
||||||
write_history(historyFile.c_str());
|
write_history(historyFile.c_str());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
string runNix(Path program, const Strings & args,
|
||||||
|
const std::optional<std::string> & input = {})
|
||||||
|
{
|
||||||
|
auto experimentalFeatures = concatStringsSep(" ", settings.experimentalFeatures.get());
|
||||||
|
auto nixConf = getEnv("NIX_CONFIG").value_or("");
|
||||||
|
nixConf.append("\nexperimental-features = " + experimentalFeatures);
|
||||||
|
auto subprocessEnv = getEnv();
|
||||||
|
subprocessEnv["NIX_CONFIG"] = nixConf;
|
||||||
|
RunOptions opts(settings.nixBinDir+ "/" + program, args);
|
||||||
|
opts.input = input;
|
||||||
|
opts.environment = subprocessEnv;
|
||||||
|
|
||||||
|
auto res = runProgram(opts);
|
||||||
|
|
||||||
|
if (!statusOk(res.first))
|
||||||
|
throw ExecError(res.first, fmt("program '%1%' %2%", program, statusToString(res.first)));
|
||||||
|
|
||||||
|
return res.second;
|
||||||
|
}
|
||||||
|
|
||||||
static NixRepl * curRepl; // ugly
|
static NixRepl * curRepl; // ugly
|
||||||
|
|
||||||
static char * completionCallback(char * s, int *match) {
|
static char * completionCallback(char * s, int *match) {
|
||||||
|
@ -463,7 +483,7 @@ bool NixRepl::processLine(string line)
|
||||||
state->callFunction(f, v, result, Pos());
|
state->callFunction(f, v, result, Pos());
|
||||||
|
|
||||||
StorePath drvPath = getDerivationPath(result);
|
StorePath drvPath = getDerivationPath(result);
|
||||||
runProgram(settings.nixBinDir + "/nix-shell", true, {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") {
|
||||||
|
@ -477,7 +497,7 @@ bool NixRepl::processLine(string line)
|
||||||
but doing it in a child makes it easier to recover from
|
but doing it in a child makes it easier to recover from
|
||||||
problems / SIGINT. */
|
problems / SIGINT. */
|
||||||
try {
|
try {
|
||||||
runProgram(settings.nixBinDir + "/nix", true, {"build", "--no-link", drvPathRaw});
|
runNix("nix", {"build", "--no-link", drvPathRaw});
|
||||||
auto drv = state->store->readDerivation(drvPath);
|
auto drv = state->store->readDerivation(drvPath);
|
||||||
std::cout << std::endl << "this derivation produced the following outputs:" << std::endl;
|
std::cout << std::endl << "this derivation produced the following outputs:" << std::endl;
|
||||||
for (auto & i : drv.outputsAndOptPaths(*state->store))
|
for (auto & i : drv.outputsAndOptPaths(*state->store))
|
||||||
|
@ -485,9 +505,9 @@ bool NixRepl::processLine(string line)
|
||||||
} catch (ExecError &) {
|
} catch (ExecError &) {
|
||||||
}
|
}
|
||||||
} else if (command == ":i") {
|
} else if (command == ":i") {
|
||||||
runProgram(settings.nixBinDir + "/nix-env", true, {"-i", drvPathRaw});
|
runNix("nix-env", {"-i", drvPathRaw});
|
||||||
} else {
|
} else {
|
||||||
runProgram(settings.nixBinDir + "/nix-shell", true, {drvPathRaw});
|
runNix("nix-shell", {drvPathRaw});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
ifeq ($(OS), Darwin)
|
ifdef HOST_DARWIN
|
||||||
programs += resolve-system-dependencies
|
programs += resolve-system-dependencies
|
||||||
endif
|
endif
|
||||||
|
|
||||||
|
|
|
@ -9,7 +9,7 @@ echo $path2
|
||||||
if test "$path1" != "$path2"; then
|
if test "$path1" != "$path2"; then
|
||||||
echo "nix-store --add and --add-fixed mismatch"
|
echo "nix-store --add and --add-fixed mismatch"
|
||||||
exit 1
|
exit 1
|
||||||
fi
|
fi
|
||||||
|
|
||||||
path3=$(nix-store --add-fixed sha256 ./dummy)
|
path3=$(nix-store --add-fixed sha256 ./dummy)
|
||||||
echo $path3
|
echo $path3
|
||||||
|
|
|
@ -4,4 +4,6 @@ file=build-hook-ca-floating.nix
|
||||||
|
|
||||||
sed -i 's/experimental-features .*/& ca-derivations/' "$NIX_CONF_DIR"/nix.conf
|
sed -i 's/experimental-features .*/& ca-derivations/' "$NIX_CONF_DIR"/nix.conf
|
||||||
|
|
||||||
|
CONTENT_ADDRESSED=true
|
||||||
|
|
||||||
source build-remote.sh
|
source build-remote.sh
|
||||||
|
|
|
@ -6,12 +6,17 @@ unset NIX_STATE_DIR
|
||||||
|
|
||||||
function join_by { local d=$1; shift; echo -n "$1"; shift; printf "%s" "${@/#/$d}"; }
|
function join_by { local d=$1; shift; echo -n "$1"; shift; printf "%s" "${@/#/$d}"; }
|
||||||
|
|
||||||
|
EXTRA_SYSTEM_FEATURES=()
|
||||||
|
if [[ -n "$CONTENT_ADDRESSED" ]]; then
|
||||||
|
EXTRA_SYSTEM_FEATURES=("ca-derivations")
|
||||||
|
fi
|
||||||
|
|
||||||
builders=(
|
builders=(
|
||||||
# system-features will automatically be added to the outer URL, but not inner
|
# system-features will automatically be added to the outer URL, but not inner
|
||||||
# remote-store URL.
|
# remote-store URL.
|
||||||
"ssh://localhost?remote-store=$TEST_ROOT/machine1?system-features=foo - - 1 1 foo"
|
"ssh://localhost?remote-store=$TEST_ROOT/machine1?system-features=$(join_by "%20" foo ${EXTRA_SYSTEM_FEATURES[@]}) - - 1 1 $(join_by "," foo ${EXTRA_SYSTEM_FEATURES[@]})"
|
||||||
"$TEST_ROOT/machine2 - - 1 1 bar"
|
"$TEST_ROOT/machine2 - - 1 1 $(join_by "," bar ${EXTRA_SYSTEM_FEATURES[@]})"
|
||||||
"ssh-ng://localhost?remote-store=$TEST_ROOT/machine3?system-features=baz - - 1 1 baz"
|
"ssh-ng://localhost?remote-store=$TEST_ROOT/machine3?system-features=$(join_by "%20" baz ${EXTRA_SYSTEM_FEATURES[@]}) - - 1 1 $(join_by "," baz ${EXTRA_SYSTEM_FEATURES[@]})"
|
||||||
)
|
)
|
||||||
|
|
||||||
chmod -R +w $TEST_ROOT/machine* || true
|
chmod -R +w $TEST_ROOT/machine* || true
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
source common.sh
|
source common.sh
|
||||||
|
|
||||||
expectedJSONRegex='\[\{"drvPath":".*multiple-outputs-a.drv","outputs":\{"first":".*multiple-outputs-a-first","second":".*multiple-outputs-a-second"}},\{"drvPath":".*multiple-outputs-b.drv","outputs":\{"out":".*multiple-outputs-b"}}]'
|
expectedJSONRegex='\[\{"drvPath":".*multiple-outputs-a.drv","outputs":\{"first":".*multiple-outputs-a-first","second":".*multiple-outputs-a-second"}},\{"drvPath":".*multiple-outputs-b.drv","outputs":\{"out":".*multiple-outputs-b"}}]'
|
||||||
nix build -f multiple-outputs.nix --json a.all b.all | jq --exit-status '
|
nix build -f multiple-outputs.nix --json a.all b.all --no-link | jq --exit-status '
|
||||||
(.[0] |
|
(.[0] |
|
||||||
(.drvPath | match(".*multiple-outputs-a.drv")) and
|
(.drvPath | match(".*multiple-outputs-a.drv")) and
|
||||||
(.outputs.first | match(".*multiple-outputs-a-first")) and
|
(.outputs.first | match(".*multiple-outputs-a-first")) and
|
||||||
|
@ -10,10 +10,10 @@ nix build -f multiple-outputs.nix --json a.all b.all | jq --exit-status '
|
||||||
(.drvPath | match(".*multiple-outputs-b.drv")) and
|
(.drvPath | match(".*multiple-outputs-b.drv")) and
|
||||||
(.outputs.out | match(".*multiple-outputs-b")))
|
(.outputs.out | match(".*multiple-outputs-b")))
|
||||||
'
|
'
|
||||||
|
|
||||||
testNormalization () {
|
testNormalization () {
|
||||||
clearStore
|
clearStore
|
||||||
outPath=$(nix-build ./simple.nix)
|
outPath=$(nix-build ./simple.nix --no-out-link)
|
||||||
test "$(stat -c %Y $outPath)" -eq 1
|
test "$(stat -c %Y $outPath)" -eq 1
|
||||||
}
|
}
|
||||||
|
|
||||||
testNormalization
|
testNormalization
|
||||||
|
|
26
tests/ca/duplicate-realisation-in-closure.sh
Normal file
26
tests/ca/duplicate-realisation-in-closure.sh
Normal file
|
@ -0,0 +1,26 @@
|
||||||
|
source ./common.sh
|
||||||
|
|
||||||
|
sed -i 's/experimental-features .*/& ca-derivations ca-references/' "$NIX_CONF_DIR"/nix.conf
|
||||||
|
|
||||||
|
export REMOTE_STORE_DIR="$TEST_ROOT/remote_store"
|
||||||
|
export REMOTE_STORE="file://$REMOTE_STORE_DIR"
|
||||||
|
|
||||||
|
rm -rf $REMOTE_STORE_DIR
|
||||||
|
clearStore
|
||||||
|
|
||||||
|
# Build dep1 and push that to the binary cache.
|
||||||
|
# This entails building (and pushing) current-time.
|
||||||
|
nix copy --to "$REMOTE_STORE" -f nondeterministic.nix dep1
|
||||||
|
clearStore
|
||||||
|
sleep 2 # To make sure that `$(date)` will be different
|
||||||
|
# Build dep2.
|
||||||
|
# As we’ve cleared the cache, we’ll have to rebuild current-time. And because
|
||||||
|
# the current time isn’t the same as before, this will yield a new (different)
|
||||||
|
# realisation
|
||||||
|
nix build -f nondeterministic.nix dep2 --no-link
|
||||||
|
|
||||||
|
# Build something that depends both on dep1 and dep2.
|
||||||
|
# If everything goes right, we should rebuild dep2 rather than fetch it from
|
||||||
|
# the cache (because that would mean duplicating `current-time` in the closure),
|
||||||
|
# and have `dep1 == dep2`.
|
||||||
|
nix build --substituters "$REMOTE_STORE" -f nondeterministic.nix toplevel --no-require-sigs --no-link
|
12
tests/ca/gc.sh
Executable file
12
tests/ca/gc.sh
Executable file
|
@ -0,0 +1,12 @@
|
||||||
|
#!/usr/bin/env bash
|
||||||
|
|
||||||
|
# Ensure that garbage collection works properly with ca derivations
|
||||||
|
|
||||||
|
source common.sh
|
||||||
|
|
||||||
|
sed -i 's/experimental-features .*/& ca-derivations ca-references/' "$NIX_CONF_DIR"/nix.conf
|
||||||
|
|
||||||
|
export NIX_TESTS_CA_BY_DEFAULT=1
|
||||||
|
|
||||||
|
cd ..
|
||||||
|
source gc.sh
|
10
tests/ca/nix-shell.sh
Executable file
10
tests/ca/nix-shell.sh
Executable file
|
@ -0,0 +1,10 @@
|
||||||
|
#!/usr/bin/env bash
|
||||||
|
|
||||||
|
source common.sh
|
||||||
|
|
||||||
|
sed -i 's/experimental-features .*/& ca-derivations ca-references nix-command flakes/' "$NIX_CONF_DIR"/nix.conf
|
||||||
|
|
||||||
|
CONTENT_ADDRESSED=true
|
||||||
|
cd ..
|
||||||
|
source ./nix-shell.sh
|
||||||
|
|
35
tests/ca/nondeterministic.nix
Normal file
35
tests/ca/nondeterministic.nix
Normal file
|
@ -0,0 +1,35 @@
|
||||||
|
with import ./config.nix;
|
||||||
|
|
||||||
|
let mkCADerivation = args: mkDerivation ({
|
||||||
|
__contentAddressed = true;
|
||||||
|
outputHashMode = "recursive";
|
||||||
|
outputHashAlgo = "sha256";
|
||||||
|
} // args);
|
||||||
|
in
|
||||||
|
|
||||||
|
rec {
|
||||||
|
currentTime = mkCADerivation {
|
||||||
|
name = "current-time";
|
||||||
|
buildCommand = ''
|
||||||
|
mkdir $out
|
||||||
|
echo $(date) > $out/current-time
|
||||||
|
'';
|
||||||
|
};
|
||||||
|
dep = seed: mkCADerivation {
|
||||||
|
name = "dep";
|
||||||
|
inherit seed;
|
||||||
|
buildCommand = ''
|
||||||
|
echo ${currentTime} > $out
|
||||||
|
'';
|
||||||
|
};
|
||||||
|
dep1 = dep 1;
|
||||||
|
dep2 = dep 2;
|
||||||
|
toplevel = mkCADerivation {
|
||||||
|
name = "toplevel";
|
||||||
|
buildCommand = ''
|
||||||
|
test ${dep1} == ${dep2}
|
||||||
|
touch $out
|
||||||
|
'';
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
11
tests/ca/post-hook.sh
Executable file
11
tests/ca/post-hook.sh
Executable file
|
@ -0,0 +1,11 @@
|
||||||
|
#!/usr/bin/env bash
|
||||||
|
|
||||||
|
source common.sh
|
||||||
|
|
||||||
|
sed -i 's/experimental-features .*/& ca-derivations ca-references nix-command flakes/' "$NIX_CONF_DIR"/nix.conf
|
||||||
|
|
||||||
|
export NIX_TESTS_CA_BY_DEFAULT=1
|
||||||
|
cd ..
|
||||||
|
source ./post-hook.sh
|
||||||
|
|
||||||
|
|
11
tests/ca/recursive.sh
Executable file
11
tests/ca/recursive.sh
Executable file
|
@ -0,0 +1,11 @@
|
||||||
|
#!/usr/bin/env bash
|
||||||
|
|
||||||
|
source common.sh
|
||||||
|
|
||||||
|
sed -i 's/experimental-features .*/& ca-derivations ca-references nix-command flakes/' "$NIX_CONF_DIR"/nix.conf
|
||||||
|
|
||||||
|
export NIX_TESTS_CA_BY_DEFAULT=1
|
||||||
|
cd ..
|
||||||
|
source ./recursive.sh
|
||||||
|
|
||||||
|
|
|
@ -17,11 +17,15 @@ buildDrvs () {
|
||||||
|
|
||||||
# Populate the remote cache
|
# Populate the remote cache
|
||||||
clearStore
|
clearStore
|
||||||
buildDrvs --post-build-hook ../push-to-store.sh
|
nix copy --to $REMOTE_STORE --file ./content-addressed.nix
|
||||||
|
|
||||||
# Restart the build on an empty store, ensuring that we don't build
|
# Restart the build on an empty store, ensuring that we don't build
|
||||||
clearStore
|
clearStore
|
||||||
buildDrvs --substitute --substituters $REMOTE_STORE --no-require-sigs -j0
|
buildDrvs --substitute --substituters $REMOTE_STORE --no-require-sigs -j0 transitivelyDependentCA
|
||||||
|
# Check that the thing we’ve just substituted has its realisation stored
|
||||||
|
nix realisation info --file ./content-addressed.nix transitivelyDependentCA
|
||||||
|
# Check that its dependencies have it too
|
||||||
|
nix realisation info --file ./content-addressed.nix dependentCA rootCA
|
||||||
|
|
||||||
# Same thing, but
|
# Same thing, but
|
||||||
# 1. With non-ca derivations
|
# 1. With non-ca derivations
|
||||||
|
|
|
@ -44,7 +44,7 @@ with import ./config.nix;
|
||||||
};
|
};
|
||||||
|
|
||||||
hashmismatch = import <nix/fetchurl.nix> {
|
hashmismatch = import <nix/fetchurl.nix> {
|
||||||
url = "file://" + toString ./dummy;
|
url = "file://" + builtins.getEnv "TMPDIR" + "/dummy";
|
||||||
sha256 = "0mdqa9w1p6cmli6976v4wi0sw9r4p5prkj7lzfd1877wk11c9c73";
|
sha256 = "0mdqa9w1p6cmli6976v4wi0sw9r4p5prkj7lzfd1877wk11c9c73";
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
@ -74,12 +74,13 @@ nix-build check.nix -A fetchurl --no-out-link --check
|
||||||
nix-build check.nix -A fetchurl --no-out-link --repair
|
nix-build check.nix -A fetchurl --no-out-link --repair
|
||||||
[[ $(cat $path) != foo ]]
|
[[ $(cat $path) != foo ]]
|
||||||
|
|
||||||
|
echo 'Hello World' > $TMPDIR/dummy
|
||||||
nix-build check.nix -A hashmismatch --no-out-link || status=$?
|
nix-build check.nix -A hashmismatch --no-out-link || status=$?
|
||||||
[ "$status" = "102" ]
|
[ "$status" = "102" ]
|
||||||
|
|
||||||
echo -n > ./dummy
|
echo -n > $TMPDIR/dummy
|
||||||
nix-build check.nix -A hashmismatch --no-out-link
|
nix-build check.nix -A hashmismatch --no-out-link
|
||||||
echo 'Hello World' > ./dummy
|
echo 'Hello World' > $TMPDIR/dummy
|
||||||
|
|
||||||
nix-build check.nix -A hashmismatch --no-out-link --check || status=$?
|
nix-build check.nix -A hashmismatch --no-out-link --check || status=$?
|
||||||
[ "$status" = "102" ]
|
[ "$status" = "102" ]
|
||||||
|
|
|
@ -1,3 +1,12 @@
|
||||||
|
let
|
||||||
|
contentAddressedByDefault = builtins.getEnv "NIX_TESTS_CA_BY_DEFAULT" == "1";
|
||||||
|
caArgs = if contentAddressedByDefault then {
|
||||||
|
__contentAddressed = true;
|
||||||
|
outputHashMode = "recursive";
|
||||||
|
outputHashAlgo = "sha256";
|
||||||
|
} else {};
|
||||||
|
in
|
||||||
|
|
||||||
rec {
|
rec {
|
||||||
shell = "@bash@";
|
shell = "@bash@";
|
||||||
|
|
||||||
|
@ -13,6 +22,6 @@ rec {
|
||||||
builder = shell;
|
builder = shell;
|
||||||
args = ["-e" args.builder or (builtins.toFile "builder-${args.name}.sh" "if [ -e .attrs.sh ]; then source .attrs.sh; fi; eval \"$buildCommand\"")];
|
args = ["-e" args.builder or (builtins.toFile "builder-${args.name}.sh" "if [ -e .attrs.sh ]; then source .attrs.sh; fi; eval \"$buildCommand\"")];
|
||||||
PATH = path;
|
PATH = path;
|
||||||
} // removeAttrs args ["builder" "meta"])
|
} // caArgs // removeAttrs args ["builder" "meta"])
|
||||||
// { meta = args.meta or {}; };
|
// { meta = args.meta or {}; };
|
||||||
}
|
}
|
||||||
|
|
1
tests/dummy
Normal file
1
tests/dummy
Normal file
|
@ -0,0 +1 @@
|
||||||
|
Hello World
|
|
@ -189,3 +189,7 @@ path8=$(nix eval --impure --raw --expr "(builtins.fetchGit { url = \"file://$rep
|
||||||
rev4=$(git -C $repo rev-parse HEAD)
|
rev4=$(git -C $repo rev-parse HEAD)
|
||||||
rev4_nix=$(nix eval --impure --raw --expr "(builtins.fetchGit { url = \"file://$repo\"; ref = \"HEAD\"; }).rev")
|
rev4_nix=$(nix eval --impure --raw --expr "(builtins.fetchGit { url = \"file://$repo\"; ref = \"HEAD\"; }).rev")
|
||||||
[[ $rev4 = $rev4_nix ]]
|
[[ $rev4 = $rev4_nix ]]
|
||||||
|
|
||||||
|
# The name argument should be handled
|
||||||
|
path9=$(nix eval --impure --raw --expr "(builtins.fetchGit { url = \"file://$repo\"; ref = \"HEAD\"; name = \"foo\"; }).outPath")
|
||||||
|
[[ $path9 =~ -foo$ ]]
|
||||||
|
|
|
@ -94,3 +94,8 @@ hg commit --cwd $repo -m 'Bla3'
|
||||||
|
|
||||||
path4=$(nix eval --impure --refresh --raw --expr "(builtins.fetchMercurial file://$repo).outPath")
|
path4=$(nix eval --impure --refresh --raw --expr "(builtins.fetchMercurial file://$repo).outPath")
|
||||||
[[ $path2 = $path4 ]]
|
[[ $path2 = $path4 ]]
|
||||||
|
|
||||||
|
echo paris > $repo/hello
|
||||||
|
# Passing a `name` argument should be reflected in the output path
|
||||||
|
path5=$(nix eval -vvvvv --impure --refresh --raw --expr "(builtins.fetchMercurial { url = \"file://$repo\"; name = \"foo\"; } ).outPath")
|
||||||
|
[[ $path5 =~ -foo$ ]]
|
||||||
|
|
|
@ -90,76 +90,14 @@ EOF
|
||||||
git -C $nonFlakeDir add README.md
|
git -C $nonFlakeDir add README.md
|
||||||
git -C $nonFlakeDir commit -m 'Initial'
|
git -C $nonFlakeDir commit -m 'Initial'
|
||||||
|
|
||||||
cat > $registry <<EOF
|
# Construct a custom registry, additionally test the --registry flag
|
||||||
{
|
nix registry add --registry $registry flake1 git+file://$flake1Dir
|
||||||
"version": 2,
|
nix registry add --registry $registry flake2 git+file://$flake2Dir
|
||||||
"flakes": [
|
nix registry add --registry $registry flake3 git+file://$flake3Dir
|
||||||
{ "from": {
|
nix registry add --registry $registry flake4 flake3
|
||||||
"type": "indirect",
|
nix registry add --registry $registry flake5 hg+file://$flake5Dir
|
||||||
"id": "flake1"
|
nix registry add --registry $registry nixpkgs flake1
|
||||||
},
|
nix registry add --registry $registry templates git+file://$templatesDir
|
||||||
"to": {
|
|
||||||
"type": "git",
|
|
||||||
"url": "file://$flake1Dir"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
{ "from": {
|
|
||||||
"type": "indirect",
|
|
||||||
"id": "flake2"
|
|
||||||
},
|
|
||||||
"to": {
|
|
||||||
"type": "git",
|
|
||||||
"url": "file://$flake2Dir"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
{ "from": {
|
|
||||||
"type": "indirect",
|
|
||||||
"id": "flake3"
|
|
||||||
},
|
|
||||||
"to": {
|
|
||||||
"type": "git",
|
|
||||||
"url": "file://$flake3Dir"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
{ "from": {
|
|
||||||
"type": "indirect",
|
|
||||||
"id": "flake4"
|
|
||||||
},
|
|
||||||
"to": {
|
|
||||||
"type": "indirect",
|
|
||||||
"id": "flake3"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
{ "from": {
|
|
||||||
"type": "indirect",
|
|
||||||
"id": "flake5"
|
|
||||||
},
|
|
||||||
"to": {
|
|
||||||
"type": "hg",
|
|
||||||
"url": "file://$flake5Dir"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
{ "from": {
|
|
||||||
"type": "indirect",
|
|
||||||
"id": "nixpkgs"
|
|
||||||
},
|
|
||||||
"to": {
|
|
||||||
"type": "indirect",
|
|
||||||
"id": "flake1"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
{ "from": {
|
|
||||||
"type": "indirect",
|
|
||||||
"id": "templates"
|
|
||||||
},
|
|
||||||
"to": {
|
|
||||||
"type": "git",
|
|
||||||
"url": "file://$templatesDir"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
]
|
|
||||||
}
|
|
||||||
EOF
|
|
||||||
|
|
||||||
# Test 'nix flake list'.
|
# Test 'nix flake list'.
|
||||||
[[ $(nix registry list | wc -l) == 7 ]]
|
[[ $(nix registry list | wc -l) == 7 ]]
|
||||||
|
@ -405,6 +343,8 @@ nix registry add flake1 flake3
|
||||||
[[ $(nix registry list | wc -l) == 8 ]]
|
[[ $(nix registry list | wc -l) == 8 ]]
|
||||||
nix registry pin flake1
|
nix registry pin flake1
|
||||||
[[ $(nix registry list | wc -l) == 8 ]]
|
[[ $(nix registry list | wc -l) == 8 ]]
|
||||||
|
nix registry pin flake1 flake3
|
||||||
|
[[ $(nix registry list | wc -l) == 8 ]]
|
||||||
nix registry remove flake1
|
nix registry remove flake1
|
||||||
[[ $(nix registry list | wc -l) == 7 ]]
|
[[ $(nix registry list | wc -l) == 7 ]]
|
||||||
|
|
||||||
|
|
|
@ -12,7 +12,7 @@ ln -sf $outPath "$NIX_STATE_DIR"/gcroots/foo
|
||||||
nix-store --gc --print-roots | grep $outPath
|
nix-store --gc --print-roots | grep $outPath
|
||||||
nix-store --gc --print-live | grep $outPath
|
nix-store --gc --print-live | grep $outPath
|
||||||
nix-store --gc --print-dead | grep $drvPath
|
nix-store --gc --print-dead | grep $drvPath
|
||||||
if nix-store --gc --print-dead | grep $outPath; then false; fi
|
if nix-store --gc --print-dead | grep -E $outPath$; then false; fi
|
||||||
|
|
||||||
nix-store --gc --print-dead
|
nix-store --gc --print-dead
|
||||||
|
|
||||||
|
|
|
@ -35,5 +35,3 @@ nix-store --init
|
||||||
|
|
||||||
# Did anything happen?
|
# Did anything happen?
|
||||||
test -e "$NIX_STATE_DIR"/db/db.sqlite
|
test -e "$NIX_STATE_DIR"/db/db.sqlite
|
||||||
|
|
||||||
echo 'Hello World' > ./dummy
|
|
||||||
|
|
|
@ -2,6 +2,7 @@ nix_tests = \
|
||||||
hash.sh lang.sh add.sh simple.sh dependencies.sh \
|
hash.sh lang.sh add.sh simple.sh dependencies.sh \
|
||||||
config.sh \
|
config.sh \
|
||||||
gc.sh \
|
gc.sh \
|
||||||
|
ca/gc.sh \
|
||||||
gc-concurrent.sh \
|
gc-concurrent.sh \
|
||||||
gc-auto.sh \
|
gc-auto.sh \
|
||||||
referrers.sh user-envs.sh logging.sh nix-build.sh misc.sh fixed.sh \
|
referrers.sh user-envs.sh logging.sh nix-build.sh misc.sh fixed.sh \
|
||||||
|
@ -38,6 +39,7 @@ nix_tests = \
|
||||||
search.sh \
|
search.sh \
|
||||||
nix-copy-ssh.sh \
|
nix-copy-ssh.sh \
|
||||||
post-hook.sh \
|
post-hook.sh \
|
||||||
|
ca/post-hook.sh \
|
||||||
function-trace.sh \
|
function-trace.sh \
|
||||||
recursive.sh \
|
recursive.sh \
|
||||||
describe-stores.sh \
|
describe-stores.sh \
|
||||||
|
@ -46,9 +48,12 @@ nix_tests = \
|
||||||
compute-levels.sh \
|
compute-levels.sh \
|
||||||
ca/build.sh \
|
ca/build.sh \
|
||||||
ca/build-with-garbage-path.sh \
|
ca/build-with-garbage-path.sh \
|
||||||
|
ca/duplicate-realisation-in-closure.sh \
|
||||||
ca/substitute.sh \
|
ca/substitute.sh \
|
||||||
ca/signatures.sh \
|
ca/signatures.sh \
|
||||||
|
ca/nix-shell.sh \
|
||||||
ca/nix-run.sh \
|
ca/nix-run.sh \
|
||||||
|
ca/recursive.sh \
|
||||||
ca/nix-copy.sh
|
ca/nix-copy.sh
|
||||||
# parallel.sh
|
# parallel.sh
|
||||||
|
|
||||||
|
|
|
@ -2,6 +2,20 @@ source common.sh
|
||||||
|
|
||||||
clearStore
|
clearStore
|
||||||
|
|
||||||
|
if [[ -n ${CONTENT_ADDRESSED:-} ]]; then
|
||||||
|
nix-shell () {
|
||||||
|
command nix-shell --arg contentAddressed true "$@"
|
||||||
|
}
|
||||||
|
|
||||||
|
nix_develop() {
|
||||||
|
nix develop --arg contentAddressed true "$@"
|
||||||
|
}
|
||||||
|
else
|
||||||
|
nix_develop() {
|
||||||
|
nix develop "$@"
|
||||||
|
}
|
||||||
|
fi
|
||||||
|
|
||||||
# Test nix-shell -A
|
# Test nix-shell -A
|
||||||
export IMPURE_VAR=foo
|
export IMPURE_VAR=foo
|
||||||
export SELECTED_IMPURE_VAR=baz
|
export SELECTED_IMPURE_VAR=baz
|
||||||
|
@ -41,7 +55,7 @@ output=$(NIX_PATH=nixpkgs=shell.nix nix-shell --pure -p foo bar --run 'echo "$(f
|
||||||
[ "$output" = "foo bar" ]
|
[ "$output" = "foo bar" ]
|
||||||
|
|
||||||
# Test nix-shell shebang mode
|
# Test nix-shell shebang mode
|
||||||
sed -e "s|@ENV_PROG@|$(type -p env)|" shell.shebang.sh > $TEST_ROOT/shell.shebang.sh
|
sed -e "s|@ENV_PROG@|$(type -P env)|" shell.shebang.sh > $TEST_ROOT/shell.shebang.sh
|
||||||
chmod a+rx $TEST_ROOT/shell.shebang.sh
|
chmod a+rx $TEST_ROOT/shell.shebang.sh
|
||||||
|
|
||||||
output=$($TEST_ROOT/shell.shebang.sh abc def)
|
output=$($TEST_ROOT/shell.shebang.sh abc def)
|
||||||
|
@ -49,7 +63,7 @@ output=$($TEST_ROOT/shell.shebang.sh abc def)
|
||||||
|
|
||||||
# Test nix-shell shebang mode again with metacharacters in the filename.
|
# Test nix-shell shebang mode again with metacharacters in the filename.
|
||||||
# First word of filename is chosen to not match any file in the test root.
|
# First word of filename is chosen to not match any file in the test root.
|
||||||
sed -e "s|@ENV_PROG@|$(type -p env)|" shell.shebang.sh > $TEST_ROOT/spaced\ \\\'\"shell.shebang.sh
|
sed -e "s|@ENV_PROG@|$(type -P env)|" shell.shebang.sh > $TEST_ROOT/spaced\ \\\'\"shell.shebang.sh
|
||||||
chmod a+rx $TEST_ROOT/spaced\ \\\'\"shell.shebang.sh
|
chmod a+rx $TEST_ROOT/spaced\ \\\'\"shell.shebang.sh
|
||||||
|
|
||||||
output=$($TEST_ROOT/spaced\ \\\'\"shell.shebang.sh abc def)
|
output=$($TEST_ROOT/spaced\ \\\'\"shell.shebang.sh abc def)
|
||||||
|
@ -58,7 +72,7 @@ output=$($TEST_ROOT/spaced\ \\\'\"shell.shebang.sh abc def)
|
||||||
# Test nix-shell shebang mode for ruby
|
# Test nix-shell shebang mode for ruby
|
||||||
# This uses a fake interpreter that returns the arguments passed
|
# This uses a fake interpreter that returns the arguments passed
|
||||||
# This, in turn, verifies the `rc` script is valid and the `load()` script (given using `-e`) is as expected.
|
# This, in turn, verifies the `rc` script is valid and the `load()` script (given using `-e`) is as expected.
|
||||||
sed -e "s|@SHELL_PROG@|$(type -p nix-shell)|" shell.shebang.rb > $TEST_ROOT/shell.shebang.rb
|
sed -e "s|@SHELL_PROG@|$(type -P nix-shell)|" shell.shebang.rb > $TEST_ROOT/shell.shebang.rb
|
||||||
chmod a+rx $TEST_ROOT/shell.shebang.rb
|
chmod a+rx $TEST_ROOT/shell.shebang.rb
|
||||||
|
|
||||||
output=$($TEST_ROOT/shell.shebang.rb abc ruby)
|
output=$($TEST_ROOT/shell.shebang.rb abc ruby)
|
||||||
|
@ -66,21 +80,27 @@ output=$($TEST_ROOT/shell.shebang.rb abc ruby)
|
||||||
|
|
||||||
# Test nix-shell shebang mode for ruby again with metacharacters in the filename.
|
# Test nix-shell shebang mode for ruby again with metacharacters in the filename.
|
||||||
# Note: fake interpreter only space-separates args without adding escapes to its output.
|
# Note: fake interpreter only space-separates args without adding escapes to its output.
|
||||||
sed -e "s|@SHELL_PROG@|$(type -p nix-shell)|" shell.shebang.rb > $TEST_ROOT/spaced\ \\\'\"shell.shebang.rb
|
sed -e "s|@SHELL_PROG@|$(type -P nix-shell)|" shell.shebang.rb > $TEST_ROOT/spaced\ \\\'\"shell.shebang.rb
|
||||||
chmod a+rx $TEST_ROOT/spaced\ \\\'\"shell.shebang.rb
|
chmod a+rx $TEST_ROOT/spaced\ \\\'\"shell.shebang.rb
|
||||||
|
|
||||||
output=$($TEST_ROOT/spaced\ \\\'\"shell.shebang.rb abc ruby)
|
output=$($TEST_ROOT/spaced\ \\\'\"shell.shebang.rb abc ruby)
|
||||||
[ "$output" = '-e load(ARGV.shift) -- '"$TEST_ROOT"'/spaced \'\''"shell.shebang.rb abc ruby' ]
|
[ "$output" = '-e load(ARGV.shift) -- '"$TEST_ROOT"'/spaced \'\''"shell.shebang.rb abc ruby' ]
|
||||||
|
|
||||||
# Test 'nix develop'.
|
# Test 'nix develop'.
|
||||||
nix develop -f shell.nix shellDrv -c bash -c '[[ -n $stdenv ]]'
|
nix_develop -f shell.nix shellDrv -c bash -c '[[ -n $stdenv ]]'
|
||||||
|
|
||||||
# Ensure `nix develop -c` preserves stdin
|
# Ensure `nix develop -c` preserves stdin
|
||||||
echo foo | nix develop -f shell.nix shellDrv -c cat | grep -q foo
|
echo foo | nix develop -f shell.nix shellDrv -c cat | grep -q foo
|
||||||
|
|
||||||
# Ensure `nix develop -c` actually executes the command if stdout isn't a terminal
|
# Ensure `nix develop -c` actually executes the command if stdout isn't a terminal
|
||||||
nix develop -f shell.nix shellDrv -c echo foo |& grep -q foo
|
nix_develop -f shell.nix shellDrv -c echo foo |& grep -q foo
|
||||||
|
|
||||||
# Test 'nix print-dev-env'.
|
# Test 'nix print-dev-env'.
|
||||||
|
[[ $(nix print-dev-env -f shell.nix shellDrv --json | jq -r .variables.arr1.value[2]) = '3 4' ]]
|
||||||
|
|
||||||
source <(nix print-dev-env -f shell.nix shellDrv)
|
source <(nix print-dev-env -f shell.nix shellDrv)
|
||||||
[[ -n $stdenv ]]
|
[[ -n $stdenv ]]
|
||||||
|
[[ ${arr1[2]} = "3 4" ]]
|
||||||
|
[[ ${arr2[1]} = $'\n' ]]
|
||||||
|
[[ ${arr2[2]} = $'x\ny' ]]
|
||||||
|
[[ $(fun) = blabla ]]
|
||||||
|
|
Some files were not shown because too many files have changed in this diff Show more
Loading…
Reference in a new issue